diff -Nru connectome-workbench-1.4.2/debian/changelog connectome-workbench-1.5.0/debian/changelog --- connectome-workbench-1.4.2/debian/changelog 2021-01-13 21:04:17.000000000 +0000 +++ connectome-workbench-1.5.0/debian/changelog 2021-02-19 15:02:11.000000000 +0000 @@ -1,3 +1,10 @@ +connectome-workbench (1.5.0-1) unstable; urgency=medium + + * Fresh upstream release + - includes debian/patches/0001-Incorporate-*.patch + + -- Yaroslav Halchenko Fri, 19 Feb 2021 10:02:11 -0500 + connectome-workbench (1.4.2-2) unstable; urgency=medium * debian/patches diff -Nru connectome-workbench-1.4.2/debian/patches/0001-Incorporate-patches-from-Dimitre-T.-so-that-the-sour.patch connectome-workbench-1.5.0/debian/patches/0001-Incorporate-patches-from-Dimitre-T.-so-that-the-sour.patch --- connectome-workbench-1.4.2/debian/patches/0001-Incorporate-patches-from-Dimitre-T.-so-that-the-sour.patch 2021-01-13 21:04:17.000000000 +0000 +++ connectome-workbench-1.5.0/debian/patches/0001-Incorporate-patches-from-Dimitre-T.-so-that-the-sour.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -From ac5fcb7b05f7733ec59397a11c2545d7ea0fb289 Mon Sep 17 00:00:00 2001 -From: John Harwell -Date: Wed, 2 Dec 2020 09:20:45 -0600 -Subject: [PATCH] Incorporate patches from Dimitre T. so that the source will - compile with Qt 5.15.x. - ---- - src/GuiQt/ChartTwoOverlayViewController.cxx | 1 + - src/Qwt/qwt_null_paintdevice.cpp | 1 + - src/Qwt/qwt_painter.cpp | 1 + - src/Qwt/qwt_painter_command.h | 1 + - src/Qwt/qwt_plot_panner.cpp | 1 + - src/Qwt/qwt_plot_renderer.cpp | 1 + - src/Qwt/qwt_widget_overlay.cpp | 1 + - 7 files changed, 7 insertions(+) - ---- a/src/GuiQt/ChartTwoOverlayViewController.cxx -+++ b/src/GuiQt/ChartTwoOverlayViewController.cxx -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - #include - #include - ---- a/src/Qwt/qwt_null_paintdevice.cpp -+++ b/src/Qwt/qwt_null_paintdevice.cpp -@@ -10,6 +10,7 @@ - #include "qwt_null_paintdevice.h" - #include - #include -+#include - - class QwtNullPaintDevice::PrivateData - { ---- a/src/Qwt/qwt_painter.cpp -+++ b/src/Qwt/qwt_painter.cpp -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - - #if QT_VERSION >= 0x050000 - #include ---- a/src/Qwt/qwt_painter_command.h -+++ b/src/Qwt/qwt_painter_command.h -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - - class QPainterPath; - ---- a/src/Qwt/qwt_plot_panner.cpp -+++ b/src/Qwt/qwt_plot_panner.cpp -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - - static QBitmap qwtBorderMask( const QWidget *canvas, const QSize &size ) - { ---- a/src/Qwt/qwt_plot_renderer.cpp -+++ b/src/Qwt/qwt_plot_renderer.cpp -@@ -23,6 +23,7 @@ - #if QT_VERSION >= 0x050000 - #include - #include -+#include - #else // QT_VERSION - #include - #include ---- a/src/Qwt/qwt_widget_overlay.cpp -+++ b/src/Qwt/qwt_widget_overlay.cpp -@@ -13,6 +13,7 @@ - #include - #include - #include -+#include - - static QImage::Format qwtMaskImageFormat() - { diff -Nru connectome-workbench-1.4.2/debian/patches/series connectome-workbench-1.5.0/debian/patches/series --- connectome-workbench-1.4.2/debian/patches/series 2021-01-13 21:04:17.000000000 +0000 +++ connectome-workbench-1.5.0/debian/patches/series 2021-02-19 15:02:11.000000000 +0000 @@ -1,2 +1 @@ up_include_cpuinfo -0001-Incorporate-patches-from-Dimitre-T.-so-that-the-sour.patch diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmAnnotationResample.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmAnnotationResample.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmAnnotationResample.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmAnnotationResample.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -28,8 +28,8 @@ #include "AnnotationCoordinate.h" #include "AnnotationFile.h" #include "AnnotationGroup.h" -#include "AnnotationOneDimensionalShape.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" +#include "AnnotationOneCoordinateShape.h" #include "DataFileException.h" #include "SurfaceFile.h" @@ -114,7 +114,7 @@ std::vector sourceSurfaces; std::vector targetSurfaces; - for (auto instance : *(myParams->getRepeatableParameterInstances(3))) { + for (auto instance : myParams->getRepeatableParameterInstances(3)) { sourceSurfaces.push_back(instance->getSurface(1)); targetSurfaces.push_back(instance->getSurface(2)); } @@ -191,8 +191,8 @@ CaretAssert(ann); CaretAssert(ann->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE); - AnnotationOneDimensionalShape* oneDimAnn = ann->castToOneDimensionalShape(); - AnnotationTwoDimensionalShape* twoDimAnn = ann->castToTwoDimensionalShape(); + AnnotationTwoCoordinateShape* oneDimAnn = ann->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* twoDimAnn = ann->castToOneCoordinateShape(); std::vector coordinates; if (oneDimAnn != NULL) { diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiAverage.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiAverage.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiAverage.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiAverage.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -71,7 +71,7 @@ CiftiFile* ciftiOut = myParams->getOutputCifti(1); vector ciftiList;//this is just so that it can pass them to the algorithm vector weights; - const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); + const vector& myInstances = myParams->getRepeatableParameterInstances(3); for (int i = 0; i < (int)myInstances.size(); ++i) { ciftiList.push_back(myInstances[i]->getCifti(1)); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -145,7 +145,7 @@ cerebAreaSurf = cerebAreaSurfOpt->getSurface(1); } vector ciftiList; - const vector& ciftiInstances = *(myParams->getRepeatableParameterInstances(10)); + const vector& ciftiInstances = myParams->getRepeatableParameterInstances(10); for (int i = 0; i < (int)ciftiInstances.size(); ++i) { ciftiList.push_back(ciftiInstances[i]->getCifti(1)); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -146,7 +146,7 @@ cerebAreaSurf = cerebAreaSurfOpt->getSurface(1); } vector ciftiList; - const vector& ciftiInputs = *(myParams->getRepeatableParameterInstances(10)); + const vector& ciftiInputs = myParams->getRepeatableParameterInstances(10); if (ciftiInputs.size() == 0) throw AlgorithmException("at least one -cifti input is required"); for (int i = 0; i < (int)ciftiInputs.size(); ++i) { diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -72,10 +72,12 @@ cerebCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* presmoothSurfOpt = ret->createOptionalParameter(6, "-surface-presmooth", "smooth on the surface before computing the gradient"); - presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the sigma for the gaussian surface smoothing kernel, in mm"); + presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the size of the gaussian surface smoothing kernel in mm, as sigma by default"); OptionalParameter* presmoothVolOpt = ret->createOptionalParameter(7, "-volume-presmooth", "smooth the volume before computing the gradient"); - presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the sigma for the gaussian volume smoothing kernel, in mm"); + presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the size of the gaussian volume smoothing kernel in mm, as sigma by default"); + + ret->createOptionalParameter(15, "-presmooth-fwhm", "smoothing kernel sizes are FWHM, not sigma"); ret->createOptionalParameter(8, "-undo-fisher-z", "apply the inverse fisher small z transform to the input"); @@ -153,6 +155,11 @@ { volKern = (float)presmoothVolOpt->getDouble(1); } + if (myParams->getOptionalParameter(15)->m_present) + { + if (surfKern > 0.0f) surfKern = surfKern / (2.0f * sqrt(2.0f * log(2.0f))); + if (volKern > 0.0f) volKern = volKern / (2.0f * sqrt(2.0f * log(2.0f))); + } bool undoFisherInput = myParams->getOptionalParameter(8)->m_present; bool applyFisher = myParams->getOptionalParameter(12)->m_present; float surfaceExclude = -1.0f; @@ -786,12 +793,12 @@ int64_t offset[3]; if (mapSize > 0) {//make a voxel bounding box to minimize memory usage - int extrema[6] = { myMap[0].m_ijk[0], - myMap[0].m_ijk[0], - myMap[0].m_ijk[1], - myMap[0].m_ijk[1], - myMap[0].m_ijk[2], - myMap[0].m_ijk[2] + int64_t extrema[6] = { myMap[0].m_ijk[0], + myMap[0].m_ijk[0], + myMap[0].m_ijk[1], + myMap[0].m_ijk[1], + myMap[0].m_ijk[2], + myMap[0].m_ijk[2] }; for (int64_t i = 1; i < mapSize; ++i) { @@ -935,12 +942,12 @@ int64_t offset[3]; if (mapSize > 0) {//make a voxel bounding box to minimize memory usage - int extrema[6] = { myMap[0].m_ijk[0], - myMap[0].m_ijk[0], - myMap[0].m_ijk[1], - myMap[0].m_ijk[1], - myMap[0].m_ijk[2], - myMap[0].m_ijk[2] + int64_t extrema[6] = { myMap[0].m_ijk[0], + myMap[0].m_ijk[0], + myMap[0].m_ijk[1], + myMap[0].m_ijk[1], + myMap[0].m_ijk[2], + myMap[0].m_ijk[2] }; for (int64_t i = 1; i < mapSize; ++i) { diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiDilate.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiDilate.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiDilate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiDilate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -76,7 +76,7 @@ OptionalParameter* roiOpt = ret->createOptionalParameter(9, "-bad-brainordinate-roi", "specify an roi of brainordinates to overwrite, rather than zeros"); roiOpt->addCiftiParameter(1, "roi-cifti", "cifti dscalar or dtseries file, positive values denote brainordinates to have their values replaced"); - ret->createOptionalParameter(10, "-nearest", "use nearest value"); + ret->createOptionalParameter(10, "-nearest", "use nearest good value instead of a weighted average"); ret->createOptionalParameter(11, "-merged-volume", "treat volume components as if they were a single component"); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiExtrema.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiExtrema.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiExtrema.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiExtrema.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -30,6 +30,8 @@ #include "SurfaceFile.h" #include "VolumeFile.h" +#include + using namespace caret; using namespace std; @@ -62,11 +64,13 @@ cerebSurfaceOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* presmoothSurfOpt = ret->createOptionalParameter(9, "-surface-presmooth", "smooth on the surface before finding extrema"); - presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the sigma for the gaussian surface smoothing kernel, in mm"); + presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the size of the gaussian surface smoothing kernel in mm, as sigma by default"); OptionalParameter* presmoothVolOpt = ret->createOptionalParameter(10, "-volume-presmooth", "smooth volume components before finding extrema"); - presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the sigma for the gaussian volume smoothing kernel, in mm"); + presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the size of the gaussian volume smoothing kernel in mm, as sigma by default"); + ret->createOptionalParameter(17, "-presmooth-fwhm", "smoothing kernel distances are FWHM, not sigma"); + OptionalParameter* thresholdOpt = ret->createOptionalParameter(11, "-threshold", "ignore small extrema"); thresholdOpt->addDoubleParameter(1, "low", "the largest value to consider for being a minimum"); thresholdOpt->addDoubleParameter(2, "high", "the smallest value to consider for being a maximum"); @@ -133,6 +137,11 @@ { volPresmooth = (float)presmoothVolOpt->getDouble(1); } + if (myParams->getOptionalParameter(17)->m_present) + { + if (surfPresmooth > 0.0f) surfPresmooth = surfPresmooth / (2.0f * sqrt(2.0f * log(2.0f))); + if (volPresmooth > 0.0f) volPresmooth = volPresmooth / (2.0f * sqrt(2.0f * log(2.0f))); + } OptionalParameter* thresholdOpt = myParams->getOptionalParameter(11); bool thresholdMode = false; float lowThresh = 0.0f, highThresh = 0.0f; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiGradient.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiGradient.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiGradient.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiGradient.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -29,6 +29,7 @@ #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" +#include #include using namespace caret; @@ -69,10 +70,12 @@ cerebCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* presmoothSurfOpt = ret->createOptionalParameter(7, "-surface-presmooth", "smooth on the surface before computing the gradient"); - presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the sigma for the gaussian surface smoothing kernel, in mm"); + presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the size of the gaussian surface smoothing kernel in mm, as sigma by default"); OptionalParameter* presmoothVolOpt = ret->createOptionalParameter(8, "-volume-presmooth", "smooth on the surface before computing the gradient"); - presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the sigma for the gaussian volume smoothing kernel, in mm"); + presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the size of the gaussian volume smoothing kernel in mm, as sigma by default"); + + ret->createOptionalParameter(11, "-presmooth-fwhm", "smoothing kernel sizes are FWHM, not sigma"); ret->createOptionalParameter(9, "-average-output", "output the average of the gradient magnitude maps instead of each gradient map separately"); @@ -146,6 +149,11 @@ { volKern = (float)presmoothVolOpt->getDouble(1); } + if (myParams->getOptionalParameter(11)->m_present) + { + if (surfKern > 0.0f) surfKern = surfKern / (2.0f * sqrt(2.0f * log(2.0f))); + if (volKern > 0.0f) volKern = volKern / (2.0f * sqrt(2.0f * log(2.0f))); + } bool outputAverage = myParams->getOptionalParameter(9)->m_present; CiftiFile* ciftiVectorsOut = NULL; OptionalParameter* vectorOpt = myParams->getOptionalParameter(10); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -79,13 +79,13 @@ if (columnOpt->m_present) { //row is first dimension, 0D cifti won't load, so don't need a test - column = myCifti->getCiftiXML().getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(columnOpt->getString(2)); + column = myCifti->getCiftiXML().getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(columnOpt->getString(1)); if (column < 0) { throw AlgorithmException("invalid column specified"); } } - const vector& borderOpts = *(myParams->getRepeatableParameterInstances(4)); + const vector& borderOpts = myParams->getRepeatableParameterInstances(4); if (borderOpts.empty()) CaretLogWarning("no output requested from -cifti-label-to-border, command will do nothing"); for (int i = 0; i < (int)borderOpts.size(); ++i) { diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiMergeDense.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiMergeDense.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiMergeDense.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiMergeDense.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -50,6 +50,9 @@ ParameterComponent* ciftiOpt = ret->createRepeatableParameter(3, "-cifti", "specify an input cifti file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "a cifti file to merge"); + + OptionalParameter* collideOpt = ret->createOptionalParameter(4, "-label-collision", "how to handle conflicts between label keys"); + collideOpt->addStringParameter(1, "action", "'ERROR', 'FIRST', or 'LEGACY', default 'ERROR', use 'LEGACY' to match v1.4.2 and earlier"); ret->setHelpText( AString("The input cifti files must have matching mappings along the direction not specified, and the mapping along the specified direction must be brain models.") @@ -70,17 +73,34 @@ throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(2); - const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); + const vector& myInstances = myParams->getRepeatableParameterInstances(3); vector ciftiList; int numCifti = (int)myInstances.size(); for (int i = 0; i < numCifti; ++i) { ciftiList.push_back(myInstances[i]->getCifti(1)); } - AlgorithmCiftiMergeDense(myProgObj, myDir, ciftiList, myCiftiOut); + OptionalParameter* collideOpt = myParams->getOptionalParameter(4); + LabelConflictLogic conflictLogic = ERROR; + if (collideOpt->m_present) + { + AString collideStr = collideOpt->getString(1); + if (collideStr == "ERROR") + { + conflictLogic = ERROR; + } else if (collideStr == "FIRST") { + conflictLogic = FIRST; + } else if (collideStr == "LEGACY") { + conflictLogic = LEGACY; + } else { + throw AlgorithmException("incorrect string for label collision option"); + } + } + AlgorithmCiftiMergeDense(myProgObj, myDir, ciftiList, myCiftiOut, conflictLogic); } -AlgorithmCiftiMergeDense::AlgorithmCiftiMergeDense(ProgressObject* myProgObj, const int& myDir, const vector& ciftiList, CiftiFile* myCiftiOut) : AbstractAlgorithm(myProgObj) +AlgorithmCiftiMergeDense::AlgorithmCiftiMergeDense(ProgressObject* myProgObj, const int& myDir, const vector& ciftiList, CiftiFile* myCiftiOut, + const LabelConflictLogic conflictLogic) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (ciftiList.size() == 0) throw AlgorithmException("no input files specified"); @@ -90,20 +110,22 @@ const CiftiXMLOld& baseXML = ciftiList[0]->getCiftiXMLOld(); if (baseXML.getMappingType(myDir) != CIFTI_INDEX_TYPE_BRAIN_MODELS) throw AlgorithmException("mapping type along specified dimension is not brain models"); bool isLabel = (baseXML.getMappingType(otherDir) == CIFTI_INDEX_TYPE_LABELS); - CiftiXMLOld outXML = baseXML; - VolumeSpace baseSpace; - bool haveVolSpace = false; - if (baseXML.hasVolumeData(myDir)) + CiftiXMLOld outXML; + if (conflictLogic == FIRST) { - haveVolSpace = true; - baseXML.getVolumeSpace(baseSpace); + outXML = ciftiList.back()->getCiftiXMLOld();//reduce warning spam by making the first replace not produce a conflict + } else { + outXML = baseXML; } - vector sourceCifti(baseXML.getNumberOfBrainModels(myDir), 0); - for (int i = 1; i < (int)ciftiList.size(); ++i) + outXML.resetColumnsToBrainModels(); + VolumeSpace baseSpace; + bool haveVolSpace = false; + vector sourceCifti; + for (int i = 0; i < (int)ciftiList.size(); ++i) { CaretAssert(ciftiList[i] != NULL); const CiftiXMLOld& otherXML = ciftiList[i]->getCiftiXMLOld(); - if (!ciftiList[0]->getCiftiXML().getMap(otherDir)->approximateMatch(*(ciftiList[i]->getCiftiXML().getMap(otherDir)))) + if (i != 0 && !ciftiList[0]->getCiftiXML().getMap(otherDir)->approximateMatch(*(ciftiList[i]->getCiftiXML().getMap(otherDir)))) { throw AlgorithmException("mappings along other dimension do not match"); } @@ -169,8 +191,13 @@ } CaretAssert((int)sourceCifti.size() == outXML.getNumberOfBrainModels(myDir)); myCiftiOut->setCiftiXML(outXML); - for (int i = 0; i < (int)sourceCifti.size(); ++i) + for (int i_raw = 0; i_raw < (int)sourceCifti.size(); ++i_raw) { + int i = i_raw; + if (conflictLogic == FIRST) + { + i = int(sourceCifti.size()) - i_raw - 1;//loop through Replaces backwards to give precedence to first model (which comes from the first argument) + } CiftiBrainModelInfo myInfo = outXML.getBrainModelInfo(myDir, i); switch (myInfo.m_type) { @@ -178,9 +205,10 @@ { if (isLabel) { + //is a generic message from ciftireplace/table::append good enough? LabelFile tempFile; AlgorithmCiftiSeparate(NULL, ciftiList[sourceCifti[i]], myDir, myInfo.m_structure, &tempFile);//using this because dealing with label tables is nasty, but doesn't happen on large files - AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, myInfo.m_structure, &tempFile); + AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, myInfo.m_structure, &tempFile, false, (conflictLogic == ERROR)); } else {//for everything else, just use rows directly, because making large metric files in-memory is problematic vector inMap, outMap; const CiftiXMLOld& otherXML = ciftiList[sourceCifti[i]]->getCiftiXMLOld(); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiMergeDense.h connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiMergeDense.h --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiMergeDense.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiMergeDense.h 2021-02-16 19:46:47.000000000 +0000 @@ -35,7 +35,14 @@ static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: - AlgorithmCiftiMergeDense(ProgressObject* myProgObj, const int& myDir, const std::vector& ciftiList, CiftiFile* myCiftiOut); + enum LabelConflictLogic + { + ERROR, + FIRST, + LEGACY + }; + AlgorithmCiftiMergeDense(ProgressObject* myProgObj, const int& myDir, const std::vector& ciftiList, CiftiFile* myCiftiOut, + const LabelConflictLogic conflictLogic = ERROR); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiMergeParcels.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiMergeParcels.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiMergeParcels.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiMergeParcels.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -63,7 +63,7 @@ AString directionName = myParams->getString(1); int myDir = CiftiXML::directionFromString(directionName); CiftiFile* myCiftiOut = myParams->getOutputCifti(2); - const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); + const vector& myInstances = myParams->getRepeatableParameterInstances(3); vector ciftiList; int numCifti = (int)myInstances.size(); for (int i = 0; i < numCifti; ++i) diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -22,6 +22,7 @@ //for computing the cropped volume space #include "AlgorithmCiftiSeparate.h" #include "AlgorithmException.h" +#include "CaretLogger.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "GiftiLabelTable.h" @@ -31,6 +32,7 @@ #include "VolumeFile.h" #include +#include #include using namespace caret; @@ -71,11 +73,17 @@ volumeAllOpt->createOptionalParameter(2, "-from-cropped", "the input is cropped to the size of the data"); ret->createOptionalParameter(7, "-discard-unused-labels", "when operating on a dlabel file, drop any unused label keys from the label table"); + + OptionalParameter* collideOpt = ret->createOptionalParameter(8, "-label-collision", "how to handle conflicts between label keys"); + collideOpt->addStringParameter(1, "action", "'ERROR', 'LEFT_SURFACE_FIRST', or 'LEGACY', default 'ERROR', use 'LEGACY' to match v1.4.2 and earlier"); - AString helpText = AString("You must specify at least one of -metric, -label, -volume, or -volume-all for this command to do anything. ") + + AString helpText = AString("This is a fairly low-level command, you probably want to use -cifti-create-dense-from-template instead.\n\n") + + "You must specify at least one of -metric, -label, -volume, or -volume-all for this command to do anything. " + "Input volumes must line up with the output of -cifti-separate. " + - "For dtseries/dscalar, use COLUMN, and if your matrix will be fully symmetric, COLUMN is more efficient. " + - "The structure argument must be one of the following:\n"; + "For dtseries/dscalar, use COLUMN, and if your dconn matrix will be fully symmetric, COLUMN is more efficient. " + + "The -volume-all option must not be specified when using a -volume option. " + + "A -metric option must not be specified when using a -label option, and is not recommended on a label-type cifti file. " + + "For each argument, use one of the following strings:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) @@ -86,6 +94,40 @@ return ret; } +namespace +{ + enum DataSourceType + { + LABEL, + METRIC, + VOLUME, + VOLUME_ALL + }; + + enum LabelConflictLogic + { + ERROR, + LEFT_SURFACE_FIRST, + LEGACY + }; + + struct DataSourceInfo + { + DataSourceType type; + int64_t index;//for repeatable options + + DataSourceInfo(const DataSourceType myType, const int64_t myIndex) + { + type = myType; + index = myIndex; + }; + DataSourceInfo() + { + index = -1;//make it obviously invalid by default + }; + }; +} + void AlgorithmCiftiReplaceStructure::useParameters(OperationParameters* myParams, ProgressObject* /*myProgObj*/) { AString ciftiName = myParams->getString(1); @@ -101,9 +143,36 @@ throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } bool discardUnusedLabels = myParams->getOptionalParameter(7)->m_present; - const vector& labelInst = *(myParams->getRepeatableParameterInstances(3)); + LabelConflictLogic conflictLogic = ERROR; + OptionalParameter* collideOpt = myParams->getOptionalParameter(8); + if (collideOpt->m_present) + { + AString collideStr = collideOpt->getString(1); + if (collideStr == "ERROR") + { + conflictLogic = ERROR; + } else if (collideStr == "LEFT_SURFACE_FIRST") { + conflictLogic = LEFT_SURFACE_FIRST; + } else if (collideStr == "LEGACY") { + conflictLogic = LEGACY; + } else { + throw AlgorithmException("incorrect string for label collision option"); + } + } + const vector& labelInst = myParams->getRepeatableParameterInstances(3); + const vector& metricInst = myParams->getRepeatableParameterInstances(4); + const vector& volumeInst = myParams->getRepeatableParameterInstances(5); + OptionalParameter* volumeAllOpt = myParams->getOptionalParameter(6); + if (volumeAllOpt->m_present && volumeInst.size() != 0) + { + throw AlgorithmException("using -volume-all and -volume at the same time in -cifti-replace-structure is strongly discouraged"); + } + map surfSources, volSources; + vector legacyOrder;//label versus metric was NOT checked for metric replace on dlabel...but, we should probably still error when the arguments are very wrong + int labelMode = 0;//0 unknown, 1 label, 2 not label for (int i = 0; i < (int)labelInst.size(); ++i) { + labelMode = 1; AString structName = labelInst[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); @@ -111,12 +180,21 @@ { throw AlgorithmException("unrecognized structure name"); } - LabelFile* labelIn = labelInst[i]->getLabel(2); - AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, labelIn, discardUnusedLabels); + if (surfSources.find(myStruct) != surfSources.end()) + { + throw AlgorithmException("-label specified more than once with structure " + structName);//this wasn't previously checked either... + } + DataSourceInfo thisInfo(LABEL, i); + surfSources[myStruct] = thisInfo; + legacyOrder.push_back(thisInfo); } - const vector& metricInst = *(myParams->getRepeatableParameterInstances(4)); for (int i = 0; i < (int)metricInst.size(); ++i) { + if (labelMode == 1) + { + throw AlgorithmException("-metric and -label options cannot be used together"); + } + labelMode = 2; AString structName = metricInst[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); @@ -124,10 +202,14 @@ { throw AlgorithmException("unrecognized structure name"); } - MetricFile* metricIn = metricInst[i]->getMetric(2); - AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, metricIn); + if (surfSources.find(myStruct) != surfSources.end()) + { + throw AlgorithmException("-metric specified more than once with structure " + structName);//this wasn't previously checked either... + } + DataSourceInfo thisInfo(METRIC, i); + surfSources[myStruct] = thisInfo; + legacyOrder.push_back(thisInfo); } - const vector& volumeInst = *(myParams->getRepeatableParameterInstances(5)); for (int i = 0; i < (int)volumeInst.size(); ++i) { AString structName = volumeInst[i]->getString(1); @@ -137,27 +219,242 @@ { throw AlgorithmException("unrecognized structure name"); } - VolumeFile* volIn = volumeInst[i]->getVolume(2); - bool fromCropVol = volumeInst[i]->getOptionalParameter(3)->m_present; - AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, volIn, fromCropVol, discardUnusedLabels); + if (volSources.find(myStruct) != surfSources.end()) + { + throw AlgorithmException("-volume specified more than once with structure " + structName);//this wasn't previously checked either... + } + DataSourceInfo thisInfo(VOLUME, i); + volSources[myStruct] = thisInfo; + legacyOrder.push_back(thisInfo); + bool thisLabel = (volumeInst[i]->getVolume(2)->getType() == SubvolumeAttributes::LABEL); + int thisLabelMode = (thisLabel ? 1 : 2); + if (labelMode == 0) + { + labelMode = thisLabelMode; + } else { + if (labelMode != thisLabelMode) + { + if (thisLabel) + { + throw AlgorithmException("label volume encountered when other inputs are non-label type: " + volumeInst[i]->getVolume(2)->getFileName()); + } else { + throw AlgorithmException("non-label volume encountered when other inputs are label type: " + volumeInst[i]->getVolume(2)->getFileName()); + } + } + } } - OptionalParameter* volumeAllOpt = myParams->getOptionalParameter(6); if (volumeAllOpt->m_present) { - VolumeFile* volIn = volumeAllOpt->getVolume(1); - bool fromCropVol = volumeAllOpt->getOptionalParameter(2)->m_present; - AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, volIn, fromCropVol, discardUnusedLabels); + legacyOrder.push_back(DataSourceInfo(VOLUME_ALL, -1)); + bool thisLabel = (volumeAllOpt->getVolume(1)->getType() == SubvolumeAttributes::LABEL); + int thisLabelMode = (thisLabel ? 1 : 2); + if (labelMode == 0) + { + labelMode = thisLabelMode; + } else { + if (labelMode != thisLabelMode) + { + if (thisLabel) + { + throw AlgorithmException("label volume encountered when other inputs are non-label type: " + volumeAllOpt->getVolume(1)->getFileName()); + } else { + throw AlgorithmException("non-label volume encountered when other inputs are label type: " + volumeAllOpt->getVolume(1)->getFileName()); + } + } + } + } + bool errorOnLabelConflict = (legacyOrder.size() > 1) && (conflictLogic == ERROR);//don't error when all priority options would give the same result + if (errorOnLabelConflict && labelMode == 1)//check for replacing everything when the new label tables don't collide, as that behavior also doesn't depend on priority + { + const CiftiXML& myXML = myCifti.getCiftiXML(); + if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified dimension must contain brain models"); + const CiftiBrainModelsMap& myMap = myXML.getBrainModelsMap(myDir); + bool allReplace = true; + for (auto surfStruct : myMap.getSurfaceStructureList()) + { + if (surfSources.find(surfStruct) == surfSources.end()) + { + allReplace = false; + break; + } + } + for (auto volStruct : myMap.getVolumeStructureList()) + { + if (volSources.find(volStruct) == volSources.end()) + { + allReplace = false; + break; + } + } + if (allReplace) + { + errorOnLabelConflict = false;//replace everything means we can ignore conflicts with the labels in the original cifti + vector testTables;//but need to manually check the tables of the inputs + for (auto source : legacyOrder)//order doesn't really matter if we are just generating an error + { + CaretMappableDataFile* thisFile = NULL; + switch (source.type) + { + case LABEL: + thisFile = labelInst[source.index]->getLabel(2); + break; + case VOLUME: + thisFile = volumeInst[source.index]->getVolume(2); + break; + case VOLUME_ALL: + thisFile = volumeAllOpt->getVolume(1); + break; + default: + CaretAssert(false);//it is an error to mix -metric and -label now, so this can't happen + throw AlgorithmException("internal error, tell the developers what you tried to do"); + } + if (testTables.empty()) + { + testTables.resize(thisFile->getNumberOfMaps()); + } else { + if (thisFile->getNumberOfMaps() != int(testTables.size())) + { + throw AlgorithmException("input files have different numbers of maps"); + } + } + for (int i = 0; i < int(testTables.size()); ++i) + { + testTables[i].append(*(thisFile->getMapLabelTable(i)), true);//produce an error on conflict + } + } + if (!discardUnusedLabels)//Matt insisted on making this an error, too + { + if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("only 2D cifti are supported"); + if (myXML.getMappingType(1 - myDir) != CiftiMappingType::LABELS) throw AlgorithmException("cannot replace structure with label data in non-label cifti file"); + const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(1 - myDir); + if (myLabelsMap.getLength() != int64_t(testTables.size())) throw AlgorithmException("input files have the wrong number of maps for this cifti file"); + try + { + for (int i = 0; i < int(testTables.size()); ++i) + { + testTables[i].append(*(myLabelsMap.getMapLabelTable(i)), true); + } + } catch (CaretException& e) { + throw AlgorithmException("labels in the cifti file conflict with labels in other input files, but all structures are being replaced, so using either -label-collision or -discard-unused-labels will resolve this error"); + } + } + } + } + vector grayordOrder; + { + //most predictable is probably to do -cifti-create-label order and ignore whatever the input cifti order is + auto iter = surfSources.find(StructureEnum::CORTEX_LEFT); + if (iter != surfSources.end()) + { + grayordOrder.push_back(iter->second); + surfSources.erase(iter); + } + iter = surfSources.find(StructureEnum::CORTEX_RIGHT); + if (iter != surfSources.end()) + { + grayordOrder.push_back(iter->second); + surfSources.erase(iter); + } + //cerebellum next, because this is what -cifti-create-label does + iter = surfSources.find(StructureEnum::CEREBELLUM); + if (iter != surfSources.end()) + { + grayordOrder.push_back(iter->second); + surfSources.erase(iter); + } + //volume after main surfaces + if (volumeAllOpt->m_present) + { + grayordOrder.push_back(DataSourceInfo(VOLUME_ALL, -1)); + } + for (auto vIter : volSources) //this is already checked as mutually exclusive with -volume-all + { + grayordOrder.push_back(vIter.second); + } + volSources.clear(); + //any other surfaces last? -cifti-create-label doesn't accept these + for (auto sIter : surfSources) + { + grayordOrder.push_back(sIter.second); + } + surfSources.clear(); + CaretAssert(volSources.empty() && surfSources.empty()); + } + vector useOrder; + switch (conflictLogic) + { + case ERROR://do scalar-type replace with legacy order, in case it makes a difference + case LEGACY: + useOrder = legacyOrder; + break; + case LEFT_SURFACE_FIRST: + useOrder = vector(grayordOrder.rbegin(), grayordOrder.rend());//reverse the order so high priority goes last + break; + } + for (auto iter : useOrder) + { + switch (iter.type) + { + case LABEL: + { + AString structName = labelInst[iter.index]->getString(1); + bool ok = false; + StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); + if (!ok) + { + throw AlgorithmException("unrecognized structure name"); + } + LabelFile* labelIn = labelInst[iter.index]->getLabel(2); + AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, labelIn, discardUnusedLabels, errorOnLabelConflict); + break; + } + case METRIC: + { + AString structName = metricInst[iter.index]->getString(1); + bool ok = false; + StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); + if (!ok) + { + throw AlgorithmException("unrecognized structure name"); + } + MetricFile* metricIn = metricInst[iter.index]->getMetric(2); + AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, metricIn); + break; + } + case VOLUME: + {//can't happen with -volume-all + AString structName = volumeInst[iter.index]->getString(1); + bool ok = false; + StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); + if (!ok) + { + throw AlgorithmException("unrecognized structure name"); + } + VolumeFile* volIn = volumeInst[iter.index]->getVolume(2); + bool fromCropVol = volumeInst[iter.index]->getOptionalParameter(3)->m_present; + AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, volIn, fromCropVol, discardUnusedLabels, errorOnLabelConflict); + break; + } + case VOLUME_ALL: + {//can only exist once + VolumeFile* volIn = volumeAllOpt->getVolume(1); + bool fromCropVol = volumeAllOpt->getOptionalParameter(2)->m_present; + AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, volIn, fromCropVol, discardUnusedLabels, errorOnLabelConflict); + break; + } + } } myCifti.writeFile(ciftiName);//and write the modified file } -AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, - const StructureEnum::Enum& myStruct, const MetricFile* metricIn) : AbstractAlgorithm(myProgObj) +AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int myDir, + const StructureEnum::Enum myStruct, const MetricFile* metricIn) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = ciftiInOut->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("replace structure only supported on 2D cifti"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified dimension must contain brain models"); + if (myXML.getMappingType(1 - myDir) == CiftiMappingType::LABELS) CaretLogWarning("replacing data in label-type cifti file with a metric file!");//previously not an error, so just warn, I guess const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(myDir); vector myMap; int rowSize = ciftiInOut->getNumberOfColumns(), colSize = ciftiInOut->getNumberOfRows(); @@ -209,8 +506,9 @@ } } -AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, - const StructureEnum::Enum& myStruct, const LabelFile* labelIn, const bool& discardUnusedLabels) : AbstractAlgorithm(myProgObj) +AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int myDir, + const StructureEnum::Enum myStruct, const LabelFile* labelIn, + const bool discardUnusedLabels, const bool errorOnLabelConflict) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = ciftiInOut->getCiftiXML(); @@ -222,7 +520,7 @@ int64_t rowSize = ciftiInOut->getNumberOfColumns(), colSize = ciftiInOut->getNumberOfRows(); if (myDir == CiftiXML::ALONG_COLUMN) { - if (myXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) throw AlgorithmException("label separate requested on non-label cifti"); + if (myXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) throw AlgorithmException("label replace structure requested on non-label cifti"); const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_ROW); myMap = myDenseMap.getSurfaceMap(myStruct); int64_t numNodes = myDenseMap.getSurfaceNumberOfNodes(myStruct); @@ -241,7 +539,7 @@ for (int64_t j = 0; j < rowSize; ++j) { GiftiLabelTable myTable = *(labelIn->getLabelTable());//we remap the old label table values so that the new label table keys are unmolested - remapArray[j] = myTable.append(*(myLabelsMap.getMapLabelTable(j))); + remapArray[j] = myTable.append(*(myLabelsMap.getMapLabelTable(j)), errorOnLabelConflict); *(myLabelsMap.getMapLabelTable(j)) = myTable; } set writeRows; @@ -298,7 +596,7 @@ } } else { if (myDir != CiftiXML::ALONG_ROW) throw AlgorithmException("unsupported cifti direction"); - if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::LABELS) throw AlgorithmException("label separate requested on non-label cifti"); + if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::LABELS) throw AlgorithmException("label replace structure requested on non-label cifti"); const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_COLUMN); myMap = myDenseMap.getSurfaceMap(myStruct); { @@ -323,7 +621,7 @@ for (int64_t i = 0; i < colSize; ++i) { GiftiLabelTable myTable = *(labelIn->getLabelTable());//we remap the old label table values so that the new label table keys are unmolested - map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i))); + map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i)), errorOnLabelConflict); *(myLabelsMap.getMapLabelTable(i)) = myTable; ciftiInOut->getRow(rowScratch, i, true); set used; @@ -359,8 +657,9 @@ } } -AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, - const StructureEnum::Enum& myStruct, const VolumeFile* volIn, const bool& fromCropped, const bool& discardUnusedLabels) : AbstractAlgorithm(myProgObj) +AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int myDir, + const StructureEnum::Enum myStruct, const VolumeFile* volIn, const bool fromCropped, + const bool discardUnusedLabels, const bool errorOnLabelConflict) : AbstractAlgorithm(myProgObj) { const CiftiXML& myXML = ciftiInOut->getCiftiXML(); if (myDir != CiftiXML::ALONG_ROW && myDir != CiftiXML::ALONG_COLUMN) throw AlgorithmException("direction not supported in cifti replace structure"); @@ -410,7 +709,7 @@ for (int64_t i = 0; i < rowSize; ++i) { GiftiLabelTable myTable = *(volIn->getMapLabelTable(i));//we remap the old label table values so that the new label table keys are unmolested - remapArray[i] = myTable.append(*(myLabelsMap.getMapLabelTable(i))); + remapArray[i] = myTable.append(*(myLabelsMap.getMapLabelTable(i)), errorOnLabelConflict); *(myLabelsMap.getMapLabelTable(i)) = myTable; } set writeRows; @@ -495,7 +794,7 @@ for (int64_t i = 0; i < colSize; ++i) { GiftiLabelTable myTable = *(volIn->getMapLabelTable(i));//we remap the old label table values so that the new label table keys are unmolested - map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i))); + map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i)), errorOnLabelConflict); *(myLabelsMap.getMapLabelTable(i)) = myTable; ciftiInOut->getRow(rowScratch, i, true); set used; @@ -544,8 +843,9 @@ } } -AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, - const VolumeFile* volIn, const bool& fromCropped, const bool& discardUnusedLabels): AbstractAlgorithm(myProgObj) +AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int myDir, + const VolumeFile* volIn, const bool fromCropped, + const bool discardUnusedLabels, const bool errorOnLabelConflict): AbstractAlgorithm(myProgObj) { const CiftiXML& myXML = ciftiInOut->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("replace structure only supported on 2D cifti"); @@ -595,7 +895,7 @@ for (int64_t i = 0; i < rowSize; ++i) { GiftiLabelTable myTable = *(volIn->getMapLabelTable(i));//we remap the old label table values so that the new label table keys are unmolested - remapArray[i] = myTable.append(*(myLabelsMap.getMapLabelTable(i))); + remapArray[i] = myTable.append(*(myLabelsMap.getMapLabelTable(i)), errorOnLabelConflict); *(myLabelsMap.getMapLabelTable(i)) = myTable; } set writeRows; @@ -680,7 +980,7 @@ for (int64_t i = 0; i < colSize; ++i) { GiftiLabelTable myTable = *(volIn->getMapLabelTable(i));//we remap the old label table values so that the new label table keys are unmolested - map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i))); + map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i)), errorOnLabelConflict); *(myLabelsMap.getMapLabelTable(i)) = myTable; ciftiInOut->getRow(rowScratch, i, true); set used; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiReplaceStructure.h connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiReplaceStructure.h --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiReplaceStructure.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiReplaceStructure.h 2021-02-16 19:46:47.000000000 +0000 @@ -33,14 +33,17 @@ static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: - AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, - const StructureEnum::Enum& myStruct, const MetricFile* metricIn); - AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, - const StructureEnum::Enum& myStruct, const LabelFile* labelIn, const bool& discardUnusedLabels = false); - AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, - const StructureEnum::Enum& myStruct, const VolumeFile* volIn, const bool& fromCropped, const bool& discardUnusedLabels = false); - AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, - const VolumeFile* volIn, const bool& fromCropped, const bool& discardUnusedLabels = false); + AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int myDir, + const StructureEnum::Enum myStruct, const MetricFile* metricIn); + AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int myDir, + const StructureEnum::Enum myStruct, const LabelFile* labelIn, + const bool discardUnusedLabels = false, const bool errorOnLabelConflict = true); + AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int myDir, + const StructureEnum::Enum myStruct, const VolumeFile* volIn, const bool fromCropped, + const bool discardUnusedLabels = false, const bool errorOnLabelConflict = true); + AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int myDir, + const VolumeFile* volIn, const bool fromCropped, + const bool discardUnusedLabels = false, const bool errorOnLabelConflict = true); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiResample.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiResample.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiResample.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiResample.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -1202,7 +1202,7 @@ } } } - } + } } else {//if we don't dilate, we can use cifti to find the used voxels if (doEdgeAdjust) { diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiSeparate.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiSeparate.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiSeparate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiSeparate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -109,7 +109,7 @@ throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } bool outputRequested = false; - const vector& labelInstances = *(myParams->getRepeatableParameterInstances(3)); + const vector& labelInstances = myParams->getRepeatableParameterInstances(3); for (int i = 0; i < (int)labelInstances.size(); ++i) { outputRequested = true; @@ -129,7 +129,7 @@ } AlgorithmCiftiSeparate(NULL, ciftiIn, myDir, myStruct, labelOut, roiOut); } - const vector& metricInstances = *(myParams->getRepeatableParameterInstances(4)); + const vector& metricInstances = myParams->getRepeatableParameterInstances(4); for (int i = 0; i < (int)metricInstances.size(); ++i) { outputRequested = true; @@ -149,7 +149,7 @@ } AlgorithmCiftiSeparate(NULL, ciftiIn, myDir, myStruct, metricOut, roiOut); } - const vector& volumeInstances = *(myParams->getRepeatableParameterInstances(5)); + const vector& volumeInstances = myParams->getRepeatableParameterInstances(5); for (int i = 0; i < (int)volumeInstances.size(); ++i) { outputRequested = true; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiSmoothing.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiSmoothing.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCiftiSmoothing.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCiftiSmoothing.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -29,6 +29,8 @@ #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" +#include + using namespace caret; using namespace std; @@ -46,11 +48,13 @@ { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the input cifti"); - ret->addDoubleParameter(2, "surface-kernel", "the sigma for the gaussian surface smoothing kernel, in mm"); - ret->addDoubleParameter(3, "volume-kernel", "the sigma for the gaussian volume smoothing kernel, in mm"); + ret->addDoubleParameter(2, "surface-kernel", "the size of the gaussian surface smoothing kernel in mm, as sigma by default"); + ret->addDoubleParameter(3, "volume-kernel", "the size of the gaussian volume smoothing kernel in mm, as sigma by default"); ret->addStringParameter(4, "direction", "which dimension to smooth along, ROW or COLUMN"); ret->addCiftiOutputParameter(5, "cifti-out", "the output cifti"); + ret->createOptionalParameter(13, "-fwhm", "kernel sizes are FWHM, not sigma"); + OptionalParameter* leftSurfOpt = ret->createOptionalParameter(6, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->createOptionalParameter(2, "-left-corrected-areas", "vertex areas to use instead of computing them from the left surface"); @@ -94,6 +98,11 @@ CiftiFile* myCifti = myParams->getCifti(1); float surfKern = (float)myParams->getDouble(2); float volKern = (float)myParams->getDouble(3); + if (myParams->getOptionalParameter(13)->m_present) + { + surfKern = surfKern / (2.0f * sqrt(2.0f * log(2.0f))); + volKern = volKern / (2.0f * sqrt(2.0f * log(2.0f))); + } AString directionName = myParams->getString(4); int myDir; if (directionName == "ROW") diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -19,17 +19,21 @@ /*LICENSE_END*/ #include "AlgorithmCreateSignedDistanceVolume.h" + #include "AlgorithmException.h" -#include "VolumeFile.h" +#include "CaretLogger.h" #include "CaretOMP.h" #include "CaretHeap.h" +#include "FastStatistics.h" #include "MathFunctions.h" #include "NiftiIO.h" #include "SurfaceFile.h" +#include "VolumeFile.h" #include #include #include +#include using namespace caret; using namespace std; @@ -181,135 +185,253 @@ if (kOrthHat.dot(kvec) < 0) kOrthHat = -kOrthHat; vector myDims; myVolOut->getDimensions(myDims); - myVolOut->setValueAllVoxels(fillValue); //list all voxels to be exactly computed int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; - CaretArray volMarked(frameSize, 0); - myProgress.setTask("marking voxel to be calculated exactly"); + vector volMarked(frameSize, 0); + vector scratchFrame (frameSize, fillValue); + const float outThresh = max(exactLim, approxLim); //don't output values beyond the specified limits + vector exactVoxelList; + VolumeSpace outSpace = myVolOut->getVolumeSpace(); + const int faceNeigh[] = { 1, 0, 0, + -1, 0, 0, + 0, 1, 0, + 0, -1, 0, + 0, 0, 1, + 0, 0, -1 }; + + //fudge factor for mark and fixup method + const float smallestSpacing = min(min(ivec.length(), jvec.length()), kvec.length()); + const float exactLimFudge = exactLim + 1.0f * smallestSpacing; //spend some extra computation in the parallel section to reduce total wall time (by reducing fixup work) //compare expected runtimes of kernel based and locator based marking methods - if (2.9 * myDims[0] * myDims[1] * myDims[2] < (numNodes * exactLim * exactLim * exactLim / iOrthHat.dot(ivec) / jOrthHat.dot(jvec) / kOrthHat.dot(kvec))) + const bool useStencil = 2.9 * myDims[0] * myDims[1] * myDims[2] > (numNodes * exactLimFudge * exactLimFudge * exactLimFudge / iOrthHat.dot(ivec) / jOrthHat.dot(jvec) / kOrthHat.dot(kvec)); + + //correction for point locator screening for direct method + FastStatistics edgeStats; + mySurf->getNodesSpacingStatistics(edgeStats); + const float longestEdge = edgeStats.getMostPositiveValue(); //can bound the error from distance to node based on edge length + const float edgeCorrection = longestEdge / sqrt(3.0f); //worst case is equilateral triangle with longest edges, this is distance from vertex to barycenter + const float nodeDistFudge = sqrt(exactLim * exactLim + edgeCorrection * edgeCorrection); //vectors from barycenter to farthest point and barycenter to vertex are orthogonal + const bool uselocator = (nodeDistFudge < 2.0f * exactLim); //take a guess at the crossover point where point locator isn't specific enough to be helpful + + //fixup method is faster when it chooses to use stencil (high resolution voxels, small exact distance) + //direct method is faster on decently-shaped surfaces at larger limits (when stencil would be slow), and is far simpler code + const bool useDirect = !useStencil; + //yes, this means the fixup method never gets used in non-stencil mode + //fixup is single-threaded, like the approximation, but calls signed distance, so the fully parallel direct method generally finishes faster unless stencil saves a lot of time + if (useDirect) { -#pragma omp CARET_PARFOR schedule(dynamic) - for (int64_t k = 0; k < myDims[2]; ++k) + //exclude the easy calls with point locator, slightly faster when the surface is well-behaved, slower when the edges are long +#pragma omp CARET_PAR { - for (int64_t j = 0; j < myDims[1]; ++j) + CaretPointer myDist = mySurf->getSignedDistanceHelper(); +#pragma omp CARET_FOR schedule(dynamic) + for (int64_t k = 0; k < myDims[2]; ++k) { - for (int64_t i = 0; i < myDims[0]; ++i) + for (int64_t j = 0; j < myDims[1]; ++j) { - Vector3D voxCoord; - myVolOut->indexToSpace(i, j, k, voxCoord); - int32_t ret = mySurf->closestNode(voxCoord, exactLim); - if (ret != -1) + for (int64_t i = 0; i < myDims[0]; ++i) { - volMarked[myVolOut->getIndex(i, j, k)] = 1; + Vector3D voxCoord = outSpace.indexToSpace(i, j, k); + if (!uselocator || mySurf->closestNode(voxCoord, nodeDistFudge) != -1) + { + bool valid = false; + float tempf = myDist->distLimited(voxCoord, exactLim, valid, myWinding); + if (valid) + { + int64_t tempindex = myVolOut->getIndex(i, j, k); + scratchFrame[tempindex] = tempf; + volMarked[tempindex] = 23; //frozen, is in exact list, has valid value for pos and neg + } + } + } + } + } + } + int64_t ijk[3]; + for (ijk[2] = 0; ijk[2] < myDims[2]; ++ijk[2]) + { + for (ijk[1] = 0; ijk[1] < myDims[1]; ++ijk[1]) + { + for (ijk[0] = 0; ijk[0] < myDims[0]; ++ijk[0]) + { + if (volMarked[myVolOut->getIndex(ijk)] != 0) + { + exactVoxelList.push_back(ijk[0]); + exactVoxelList.push_back(ijk[1]); + exactVoxelList.push_back(ijk[2]); } } } } } else { -#pragma omp CARET_PARFOR schedule(dynamic) - for (int node = 0; node < numNodes; ++node) + myProgress.setTask("marking voxels to be calculated exactly"); + if (!useStencil) { - int64_t ijk[3]; - Vector3D nodeCoord = mySurf->getCoordinate(node), tempvec; - float tempf, tempf2, tempf3; - tempvec = nodeCoord - iOrthHat * exactLim; - myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3);//compute bounding box once rather than doing a convoluted sphere loop construct - int64_t imin = (int64_t)ceil(tempf); - if (imin < 0) imin = 0; - tempvec = nodeCoord + iOrthHat * exactLim; - myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); - int64_t imax = (int64_t)floor(tempf) + 1; - if (imax > myDims[0]) imax = myDims[0]; - tempvec = nodeCoord - jOrthHat * exactLim; - myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); - int64_t jmin = (int64_t)ceil(tempf2); - if (jmin < 0) jmin = 0; - tempvec = nodeCoord + jOrthHat * exactLim; - myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); - int64_t jmax = (int64_t)floor(tempf2) + 1; - if (jmax > myDims[1]) jmax = myDims[1]; - tempvec = nodeCoord - kOrthHat * exactLim; - myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); - int64_t kmin = (int64_t)ceil(tempf3); - if (kmin < 0) kmin = 0; - tempvec = nodeCoord + kOrthHat * exactLim; - myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); - int64_t kmax = (int64_t)floor(tempf3) + 1; - if (kmax > myDims[2]) kmax = myDims[2]; - for (ijk[2] = kmin; ijk[2] < kmax; ++ijk[2]) - { - for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) - { - for (ijk[0] = imin; ijk[0] < imax; ++ijk[0]) - { - myVolOut->indexToSpace(ijk, tempvec); - tempvec -= nodeCoord; - if (tempvec.length() <= exactLim) +#pragma omp CARET_PARFOR schedule(dynamic) + for (int64_t k = 0; k < myDims[2]; ++k) + { + for (int64_t j = 0; j < myDims[1]; ++j) + { + for (int64_t i = 0; i < myDims[0]; ++i) + { + Vector3D voxCoord; + myVolOut->indexToSpace(i, j, k, voxCoord); + int32_t ret = mySurf->closestNode(voxCoord, exactLimFudge); + if (ret != -1) + { + volMarked[myVolOut->getIndex(i, j, k)] = 1; //this mark only matters during approximation - if we calculate an exact distance that is beyond the exact limit, use it as long as it is within approx limit + } + } + } + } + } else { //when you only need to set a very small fraction of the total voxels, it is faster to mark everything near each vertex with a stencil and fix up holes later +#pragma omp CARET_PARFOR schedule(dynamic) + for (int node = 0; node < numNodes; ++node) + { + int64_t ijk[3]; + Vector3D nodeCoord = mySurf->getCoordinate(node), tempvec; + float tempf, tempf2, tempf3; + tempvec = nodeCoord - iOrthHat * exactLimFudge; + myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3);//compute bounding box once rather than doing a convoluted sphere loop construct + int64_t imin = (int64_t)ceil(tempf); + if (imin < 0) imin = 0; + tempvec = nodeCoord + iOrthHat * exactLimFudge; + myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); + int64_t imax = (int64_t)floor(tempf) + 1; + if (imax > myDims[0]) imax = myDims[0]; + tempvec = nodeCoord - jOrthHat * exactLimFudge; + myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); + int64_t jmin = (int64_t)ceil(tempf2); + if (jmin < 0) jmin = 0; + tempvec = nodeCoord + jOrthHat * exactLimFudge; + myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); + int64_t jmax = (int64_t)floor(tempf2) + 1; + if (jmax > myDims[1]) jmax = myDims[1]; + tempvec = nodeCoord - kOrthHat * exactLimFudge; + myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); + int64_t kmin = (int64_t)ceil(tempf3); + if (kmin < 0) kmin = 0; + tempvec = nodeCoord + kOrthHat * exactLimFudge; + myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); + int64_t kmax = (int64_t)floor(tempf3) + 1; + if (kmax > myDims[2]) kmax = myDims[2]; + for (ijk[2] = kmin; ijk[2] < kmax; ++ijk[2]) + { + for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) + { + for (ijk[0] = imin; ijk[0] < imax; ++ijk[0]) { - volMarked[myVolOut->getIndex(ijk)] = 1; + myVolOut->indexToSpace(ijk, tempvec); + tempvec -= nodeCoord; + if (tempvec.length() <= exactLimFudge) + { + volMarked[myVolOut->getIndex(ijk)] = 1; + } } } } } } - } - vector exactVoxelList; - int64_t ijk[3]; - for (ijk[2] = 0; ijk[2] < myDims[2]; ++ijk[2]) - { - for (ijk[1] = 0; ijk[1] < myDims[1]; ++ijk[1]) + int64_t ijk[3]; + for (ijk[2] = 0; ijk[2] < myDims[2]; ++ijk[2]) { - for (ijk[0] = 0; ijk[0] < myDims[0]; ++ijk[0]) + for (ijk[1] = 0; ijk[1] < myDims[1]; ++ijk[1]) { - if (volMarked[myVolOut->getIndex(ijk)] == 1) + for (ijk[0] = 0; ijk[0] < myDims[0]; ++ijk[0]) { - exactVoxelList.push_back(ijk[0]); - exactVoxelList.push_back(ijk[1]); - exactVoxelList.push_back(ijk[2]); + if (volMarked[myVolOut->getIndex(ijk)] == 1) + { + exactVoxelList.push_back(ijk[0]); + exactVoxelList.push_back(ijk[1]); + exactVoxelList.push_back(ijk[2]); + } } } } - } - myProgress.reportProgress(markweight); - myProgress.setTask("computing exact distances"); + myProgress.reportProgress(markweight); + myProgress.setTask("computing exact distances"); #pragma omp CARET_PAR - { - CaretPointer myDist = mySurf->getSignedDistanceHelper(); - int numExact = (int)exactVoxelList.size(); - Vector3D thisCoord; + { + int64_t numExact = (int64_t)exactVoxelList.size(); + CaretPointer myDist = mySurf->getSignedDistanceHelper(); + Vector3D thisCoord; #pragma omp CARET_FOR schedule(dynamic) - for (int i = 0; i < numExact; i += 3) + for (int64_t i = 0; i < numExact; i += 3) + { + const int64_t* thisVox = exactVoxelList.data() + i; + myVolOut->indexToSpace(thisVox, thisCoord); + bool valid = false; + float thisDist = myDist->distLimited(thisCoord, outThresh, valid, myWinding); + if (valid) + { + int64_t tempindex = myVolOut->getIndex(thisVox); + scratchFrame[tempindex] = thisDist; + volMarked[tempindex] |= 22;//set marked to have valid value (positive and negative), and frozen + } + } + } + //fix up missing exact distances due to vertex-based marking, should be faster for extreme cases than increasing the limit based on longest edge + //crawl neighbors of marked vertices that aren't marked, looking for any below exact threshold + CaretPointer myDist = mySurf->getSignedDistanceHelper(); + vector toVisit = exactVoxelList; + for (int64_t i = 0; i < int64_t(toVisit.size()); i += 3) { - myVolOut->indexToSpace(exactVoxelList.data() + i, thisCoord); - myVolOut->setValue(myDist->dist(thisCoord, myWinding), exactVoxelList.data() + i); - volMarked[myVolOut->getIndex(exactVoxelList.data() + i)] |= 22;//set marked to have valid value (positive and negative), and frozen + int64_t tempindex = myVolOut->getIndex(toVisit.data() + i); + if ((volMarked[tempindex] & 22) != 0 && abs(scratchFrame[tempindex]) <= exactLim) //don't grow from a voxel with an invalid value, or that is already beyond exactLim + { + //check face neighbors for being unmarked + for (int neigh = 0; neigh < 18; neigh += 3) + { + int64_t tempijk[3] = { toVisit[i] + faceNeigh[neigh], + toVisit[i + 1] + faceNeigh[neigh + 1], + toVisit[i + 2] + faceNeigh[neigh + 2] }; + if (myVolOut->indexValid(tempijk)) + { + int64_t tempindex = myVolOut->getIndex(tempijk); + if ((volMarked[tempindex] & 1) == 0) + { + volMarked[tempindex] |= 1; //mark it as having had exact signed distance run on it + Vector3D thisCoord = outSpace.indexToSpace(tempijk); + bool valid = false; + float thisDist = myDist->distLimited(thisCoord, outThresh, valid, myWinding); + if (valid) //don't throw the distance away unless it is outside the maximum distance requested + { + scratchFrame[tempindex] = thisDist; + volMarked[tempindex] |= 22; //mark it as having a valid value + exactVoxelList.push_back(tempijk[0]); + exactVoxelList.push_back(tempijk[1]); + exactVoxelList.push_back(tempijk[2]); + if (abs(thisDist) <= exactLim) //only evaluate exact distances to one neighbor past exact threshold + { + toVisit.push_back(tempijk[0]); + toVisit.push_back(tempijk[1]); + toVisit.push_back(tempijk[2]); + } + } + } + } + } + } } } myProgress.reportProgress(markweight + exactweight); if (approxLim > exactLim) { myProgress.setTask("approximating distances in extended region"); - int faceNeigh[] = { 1, 0, 0, - -1, 0, 0, - 0, 1, 0, - 0, -1, 0, - 0, 0, 1, - 0, 0, -1 }; vector neighborhood;//this will contain ONLY the shortest voxel offsets with unique 3d slopes within the neighborhood - DistVoxOffset tempIndex; + DistVoxOffset tempOffset; Vector3D tempvec; for (int i = -approxNeighborhood; i <= approxNeighborhood; ++i) { - tempIndex.m_offset[0] = i; + tempOffset.m_offset[0] = i; for (int j = -approxNeighborhood; j <= approxNeighborhood; ++j) { - tempIndex.m_offset[1] = j; + tempOffset.m_offset[1] = j; for (int k = -approxNeighborhood; k <= approxNeighborhood; ++k) { - tempIndex.m_offset[2] = k; + tempOffset.m_offset[2] = k; tempvec = ivec * i + jvec * j + kvec * k; - tempIndex.m_dist = tempvec.length(); + tempOffset.m_dist = tempvec.length(); int low, med, high; low = min(min(abs(i), abs(j)), abs(k));//stupid sort high = max(max(abs(i), abs(j)), abs(k)); @@ -327,18 +449,18 @@ { if (high == 1) { - neighborhood.push_back(tempIndex);//face neighbors + neighborhood.push_back(tempOffset);//face neighbors } } else { if (MathFunctions::gcd(med, high) == 1) { - neighborhood.push_back(tempIndex);//unique in-plane + neighborhood.push_back(tempOffset);//unique in-plane } } } else { if (MathFunctions::gcd(MathFunctions::gcd(low, med), high) == 1) { - neighborhood.push_back(tempIndex);//unique out of plane + neighborhood.push_back(tempOffset);//unique out of plane } } } @@ -349,43 +471,47 @@ CaretMinHeap posHeap; CaretArray heapIndexes(frameSize); int numExact = (int)exactVoxelList.size(); - for (int i = 0; i < numExact; i += 3) + for (int64_t i = 0; i < numExact; i += 3) { int64_t* thisVoxel = exactVoxelList.data() + i; - float tempf = myVolOut->getValue(thisVoxel); - for (int neigh = 0; neigh < neighSize; ++neigh) + int64_t thisindex = myVolOut->getIndex(thisVoxel); + if ((volMarked[thisindex] & 2) != 0) //using fixup method, there are voxels marked as "exact signed distance run" that don't have a value, because it was beyond both limits { - int64_t tempijk[3]; - tempijk[0] = thisVoxel[0] + neighborhood[neigh].m_offset[0]; - tempijk[1] = thisVoxel[1] + neighborhood[neigh].m_offset[1]; - tempijk[2] = thisVoxel[2] + neighborhood[neigh].m_offset[2]; - if (myVolOut->indexValid(tempijk)) - { - int64_t tempindex = myVolOut->getIndex(tempijk); - float tempf2 = tempf + neighborhood[neigh].m_dist; - if (abs(tempf2) <= approxLim && (volMarked[tempindex] & 4) == 0 && ((volMarked[tempindex] & 2) == 0 || tempf2 < myVolOut->getValue(tempijk))) - {//within approxlim (so no stragglers outside limit), not frozen, and either no value or worse value - volMarked[tempindex] |= 2; - myVolOut->setValue(tempf2, tempijk); - } - } - } - if (tempf > 0.0f) - {//start only from positive values - //check face neighbors for being unmarked - for (int neigh = 0; neigh < 18; neigh += 3) + float tempf = scratchFrame[thisindex]; + for (int neigh = 0; neigh < neighSize; ++neigh) { int64_t tempijk[3]; - tempijk[0] = thisVoxel[0] + faceNeigh[neigh]; - tempijk[1] = thisVoxel[1] + faceNeigh[neigh + 1]; - tempijk[2] = thisVoxel[2] + faceNeigh[neigh + 2]; + tempijk[0] = thisVoxel[0] + neighborhood[neigh].m_offset[0]; + tempijk[1] = thisVoxel[1] + neighborhood[neigh].m_offset[1]; + tempijk[2] = thisVoxel[2] + neighborhood[neigh].m_offset[2]; if (myVolOut->indexValid(tempijk)) { - int64_t tempIndex = myVolOut->getIndex(tempijk); - if ((volMarked[tempIndex] & 1) == 0) - {//only add this to the heap if it has unmarked face neighbors - posHeap.push(VoxelIndex(thisVoxel), tempf);//don't need to store the index to change the key, value is frozen - break; + int64_t tempindex = myVolOut->getIndex(tempijk); + float tempf2 = tempf + neighborhood[neigh].m_dist; + if (abs(tempf2) <= approxLim && (volMarked[tempindex] & 4) == 0 && ((volMarked[tempindex] & 2) == 0 || tempf2 < scratchFrame[tempindex])) + {//within approxlim (so no stragglers outside limit), not frozen, and either no value or worse value + volMarked[tempindex] |= 2; + scratchFrame[tempindex] = tempf2; + } + } + } + if (tempf > 0.0f) + {//start only from positive values + //check face neighbors for being unmarked + for (int neigh = 0; neigh < 18; neigh += 3) + { + int64_t tempijk[3]; + tempijk[0] = thisVoxel[0] + faceNeigh[neigh]; + tempijk[1] = thisVoxel[1] + faceNeigh[neigh + 1]; + tempijk[2] = thisVoxel[2] + faceNeigh[neigh + 2]; + if (myVolOut->indexValid(tempijk)) + { + int64_t tempIndex = myVolOut->getIndex(tempijk); + if ((volMarked[tempIndex] & 1) == 0) + {//only add this to the heap if it has unmarked face neighbors + posHeap.push(VoxelIndex(thisVoxel), tempf);//don't need to store the index to change the key, value is frozen + break; + } } } } @@ -409,10 +535,10 @@ float tempf = curDist + neighborhood[neigh].m_dist; int tempindex = myVolOut->getIndex(tempijk); int& tempmark = volMarked[tempindex]; - if (abs(tempf) <= approxLim && (tempmark & 4) == 0 && ((tempmark & 2) == 0 || myVolOut->getValue(tempijk) > tempf)) + if (abs(tempf) <= approxLim && (tempmark & 4) == 0 && ((tempmark & 2) == 0 || scratchFrame[tempindex] > tempf)) {//within range, not frozen, no value or current value is worse tempmark |= 2;//valid value - myVolOut->setValue(tempf, tempijk); + scratchFrame[tempindex] = tempf; if ((tempmark & 8) != 0)//if it is already in the heap, we must update its key (log time worst case, but changes should generally be small) { posHeap.changekey(heapIndexes[tempindex], tempf); @@ -420,7 +546,7 @@ } if ((tempmark & 12) == 0 && (tempmark & 2) != 0 && neighborhood[neigh].m_dist <= maxFaceDist) {//this neatly handles both face neighbors and any other needed neighbors to maintain dijkstra correctness under extreme scenarios - heapIndexes[tempindex] = posHeap.push(VoxelIndex(tempijk), myVolOut->getValue(tempijk)); + heapIndexes[tempindex] = posHeap.push(VoxelIndex(tempijk), scratchFrame[tempindex]); tempmark |= 8;//has a heap index } } @@ -428,43 +554,47 @@ }//negatives myProgress.reportProgress(markweight + exactweight + approxweight * 0.5f); CaretMaxHeap negHeap; - for (int i = 0; i < numExact; i += 3) + for (int64_t i = 0; i < numExact; i += 3) { int64_t* thisVoxel = exactVoxelList.data() + i; - float tempf = myVolOut->getValue(thisVoxel); - for (int neigh = 0; neigh < neighSize; ++neigh) + int64_t thisindex = myVolOut->getIndex(thisVoxel); + if ((volMarked[thisindex] & 16) != 0) //using fixup method, there are voxels marked as "exact signed distance run" that don't have a valid value, because it was beyond both limits { - int64_t tempijk[3]; - tempijk[0] = thisVoxel[0] + neighborhood[neigh].m_offset[0]; - tempijk[1] = thisVoxel[1] + neighborhood[neigh].m_offset[1]; - tempijk[2] = thisVoxel[2] + neighborhood[neigh].m_offset[2]; - if (myVolOut->indexValid(tempijk)) - { - int64_t tempindex = myVolOut->getIndex(tempijk); - float tempf2 = tempf - neighborhood[neigh].m_dist; - if (abs(tempf2) <= approxLim && (volMarked[tempindex] & 4) == 0 && ((volMarked[tempindex] & 16) == 0 || tempf2 > myVolOut->getValue(tempijk))) - {//within approxlim (so no stragglers outside limit), not frozen, and either no value or worse value - volMarked[tempindex] |= 16; - myVolOut->setValue(tempf2, tempijk); - } - } - } - if (tempf < 0.0f) - {//start only from negative values - //check face neighbors for being unmarked - for (int neigh = 0; neigh < 18; neigh += 3) + float tempf = scratchFrame[thisindex]; + for (int neigh = 0; neigh < neighSize; ++neigh) { int64_t tempijk[3]; - tempijk[0] = thisVoxel[0] + faceNeigh[neigh]; - tempijk[1] = thisVoxel[1] + faceNeigh[neigh + 1]; - tempijk[2] = thisVoxel[2] + faceNeigh[neigh + 2]; + tempijk[0] = thisVoxel[0] + neighborhood[neigh].m_offset[0]; + tempijk[1] = thisVoxel[1] + neighborhood[neigh].m_offset[1]; + tempijk[2] = thisVoxel[2] + neighborhood[neigh].m_offset[2]; if (myVolOut->indexValid(tempijk)) { - int64_t tempIndex = myVolOut->getIndex(tempijk); - if ((volMarked[tempIndex] & 1) == 0) - {//only add this to the heap if it has unmarked face neighbors - negHeap.push(VoxelIndex(thisVoxel), tempf);//don't need to store the index to change the key, value is frozen - break; + int64_t tempindex = myVolOut->getIndex(tempijk); + float tempf2 = tempf - neighborhood[neigh].m_dist; + if (abs(tempf2) <= approxLim && (volMarked[tempindex] & 4) == 0 && ((volMarked[tempindex] & 16) == 0 || tempf2 > scratchFrame[tempindex])) + {//within approxlim (so no stragglers outside limit), not frozen, and either no value or worse value + volMarked[tempindex] |= 16; + scratchFrame[tempindex] = tempf2; + } + } + } + if (tempf < 0.0f) + {//start only from negative values + //check face neighbors for being unmarked + for (int neigh = 0; neigh < 18; neigh += 3) + { + int64_t tempijk[3]; + tempijk[0] = thisVoxel[0] + faceNeigh[neigh]; + tempijk[1] = thisVoxel[1] + faceNeigh[neigh + 1]; + tempijk[2] = thisVoxel[2] + faceNeigh[neigh + 2]; + if (myVolOut->indexValid(tempijk)) + { + int64_t tempIndex = myVolOut->getIndex(tempijk); + if ((volMarked[tempIndex] & 1) == 0) + {//only add this to the heap if it has unmarked face neighbors + negHeap.push(VoxelIndex(thisVoxel), tempf);//don't need to store the index to change the key, value is frozen + break; + } } } } @@ -488,10 +618,10 @@ float tempf = curDist - neighborhood[neigh].m_dist; int tempindex = myVolOut->getIndex(tempijk); int& tempmark = volMarked[tempindex]; - if (abs(tempf) <= approxLim && (tempmark & 4) == 0 && ((tempmark & 16) == 0 || myVolOut->getValue(tempijk) < tempf)) + if (abs(tempf) <= approxLim && (tempmark & 4) == 0 && ((tempmark & 16) == 0 || scratchFrame[tempindex] < tempf)) {//within range, not frozen, no value or current value is worse tempmark |= 16;//valid value - myVolOut->setValue(tempf, tempijk); + scratchFrame[tempindex] = tempf; if ((tempmark & 8) != 0)//if it is already in the heap, we must update its key (log time worst case, but changes should generally be small) { negHeap.changekey(heapIndexes[tempindex], tempf); @@ -499,32 +629,36 @@ } if ((tempmark & 12) == 0 && (tempmark & 16) != 0 && neighborhood[neigh].m_dist <= maxFaceDist) {//this neatly handles both face neighbors and any other needed neighbors to maintain dijkstra correctness under extreme scenarios - heapIndexes[tempindex] = negHeap.push(VoxelIndex(tempijk), myVolOut->getValue(tempijk)); + heapIndexes[tempindex] = negHeap.push(VoxelIndex(tempijk), scratchFrame[tempindex]); tempmark |= 8;//has a heap index } } } } - }//now make the roi volume + } + myVolOut->setFrame(scratchFrame.data()); if (myRoiOut != NULL) - { + {//now make the roi volume myDims.resize(3); myRoiOut->reinitialize(myDims, myVolOut->getSform()); + int64_t ijk[3]; for (ijk[2] = 0; ijk[2] < myDims[2]; ++ijk[2]) { for (ijk[1] = 0; ijk[1] < myDims[1]; ++ijk[1]) { for (ijk[0] = 0; ijk[0] < myDims[0]; ++ijk[0]) { - if ((volMarked[myVolOut->getIndex(ijk)] & 4) == 0)//only mark "frozen" (4) as valid, though "have value" (2) should now be the same + int64_t tempindex = myVolOut->getIndex(ijk); + if ((volMarked[tempindex] & 4) == 0)//only mark "frozen" (4) as valid, though "have value" (2 or 16) should now be the same { - myRoiOut->setValue(0.0f, ijk); + scratchFrame[tempindex] = 0.0f; } else { - myRoiOut->setValue(1.0f, ijk); + scratchFrame[tempindex] = 1.0f; } } } } + myRoiOut->setFrame(scratchFrame.data()); } } diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmLabelToBorder.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmLabelToBorder.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmLabelToBorder.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmLabelToBorder.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -93,7 +93,7 @@ LevelProgress myProgress(myProgObj); if (mySurf->getNumberOfNodes() != myLabel->getNumberOfNodes()) throw AlgorithmException("label file does not match surface file number of vertices"); if (placement < 0.0f || placement > 1.0f || placement != placement) throw AlgorithmException("placement must be between 0 and 1"); - if (columnNum < -1 || columnNum > myLabel->getNumberOfColumns()) throw AlgorithmException("invalid column specified"); + if (columnNum < -1 || columnNum >= myLabel->getNumberOfColumns()) throw AlgorithmException("invalid column specified"); myBorderOut->setStructure(mySurf->getStructure()); myBorderOut->setNumberOfNodes(mySurf->getNumberOfNodes()); BorderTracingHelper myHelper(mySurf); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricDilate.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricDilate.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricDilate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricDilate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -77,7 +77,7 @@ OptionalParameter* corrAreaOpt = ret->createOptionalParameter(11, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); corrAreaOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); - ret->createOptionalParameter(12, "-legacy-cutoff", "use the old method of choosing how many vertices to use when calulating the dilated value with weighted method"); + ret->createOptionalParameter(12, "-legacy-cutoff", "use the v1.3.2 method of choosing how many vertices to use when calulating the dilated value with weighted method"); ret->setHelpText( AString("For all metric vertices that are designated as bad, if they neighbor a non-bad vertex with data or are within the specified distance of such a vertex, ") + diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricExtrema.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricExtrema.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricExtrema.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricExtrema.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -27,6 +27,7 @@ #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" + #include #include #include @@ -56,7 +57,8 @@ ret->addMetricOutputParameter(4, "metric-out", "the output extrema metric"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(5, "-presmooth", "smooth the metric before finding extrema"); - presmoothOpt->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); + presmoothOpt->addDoubleParameter(1, "kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); + presmoothOpt->createOptionalParameter(2, "-fwhm", "kernel size is FWHM, not sigma"); OptionalParameter* roiOpt = ret->createOptionalParameter(6, "-roi", "ignore values outside the selected area"); roiOpt->addMetricParameter(1, "roi-metric", "the area to find extrema in, as a metric"); @@ -108,6 +110,10 @@ { throw AlgorithmException("smoothing kernel must be positive"); } + if (presmoothOpt->getOptionalParameter(2)->m_present) + { + presmooth = presmooth / (2.0f * sqrt(2.0f * log(2.0f))); + } } OptionalParameter* roiOpt = myParams->getOptionalParameter(6); MetricFile* myRoi = NULL; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricGradient.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricGradient.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricGradient.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricGradient.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -58,7 +58,8 @@ ret->addMetricOutputParameter(3, "metric-out", "the magnitude of the gradient"); OptionalParameter* presmooth = ret->createOptionalParameter(4, "-presmooth", "smooth the metric before computing the gradient"); - presmooth->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); + presmooth->addDoubleParameter(1, "kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); + presmooth->createOptionalParameter(2, "-fwhm", "kernel size is FWHM, not sigma"); OptionalParameter* roiOption = ret->createOptionalParameter(5, "-roi", "select a region of interest to take the gradient of"); roiOption->addMetricParameter(1, "roi-metric", "the area to take the gradient within, as a metric"); @@ -99,11 +100,15 @@ MetricFile* myMetricIn = myParams->getMetric(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); float myPresmooth = -1.0f;//negative or zero means no smoothing - OptionalParameter* presmooth = myParams->getOptionalParameter(4); - if (presmooth->m_present) + OptionalParameter* presmoothOpt = myParams->getOptionalParameter(4); + if (presmoothOpt->m_present) { - myPresmooth = (float)presmooth->getDouble(1); + myPresmooth = (float)presmoothOpt->getDouble(1); if (myPresmooth <= 0.0f) throw AlgorithmException("presmooth kernel size must be positive"); + if (presmoothOpt->getOptionalParameter(2)->m_present) + { + myPresmooth = myPresmooth / (2.0f * sqrt(2.0f * log(2.0f))); + } } MetricFile* myRoi = NULL; bool matchRoiColumns = false; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricRegression.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricRegression.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricRegression.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricRegression.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -90,7 +90,7 @@ } } vector > remove, keep; - const vector& removeInstances = *(myParams->getRepeatableParameterInstances(5)); + const vector& removeInstances = myParams->getRepeatableParameterInstances(5); int numRemove = (int)removeInstances.size(); if (numRemove == 0) throw AlgorithmException("you must specify at least one 'remove' metric"); for (int i = 0; i < numRemove; ++i) @@ -108,7 +108,7 @@ } remove.push_back(pair(toRemove, removeCol)); } - const vector& keepInstances = *(myParams->getRepeatableParameterInstances(6)); + const vector& keepInstances = myParams->getRepeatableParameterInstances(6); int numKeep = (int)keepInstances.size(); for (int i = 0; i < numKeep; ++i) { diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricSmoothing.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricSmoothing.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricSmoothing.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricSmoothing.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -27,6 +27,7 @@ #include "PaletteColorMapping.h" #include "SurfaceFile.h" #include "TopologyHelper.h" + #include using namespace caret; @@ -49,10 +50,12 @@ ret->addMetricParameter(2, "metric-in", "the metric to smooth"); - ret->addDoubleParameter(3, "smoothing-kernel", "the sigma for the gaussian kernel function, in mm"); + ret->addDoubleParameter(3, "smoothing-kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); ret->addMetricOutputParameter(4, "metric-out", "the output metric"); + ret->createOptionalParameter(10, "-fwhm", "kernel size is FWHM, not sigma"); + OptionalParameter* roiOption = ret->createOptionalParameter(5, "-roi", "select a region of interest to smooth"); roiOption->addMetricParameter(1, "roi-metric", "the roi to smooth within, as a metric"); roiOption->createOptionalParameter(2, "-match-columns", "for each input column, use the corresponding column from the roi"); @@ -105,6 +108,10 @@ SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); double myKernel = myParams->getDouble(3); + if (myParams->getOptionalParameter(10)->m_present) + { + myKernel = myKernel / (2.0 * sqrt(2.0 * log(2.0))); + } MetricFile* myMetricOut = myParams->getOutputMetric(4); MetricFile* myRoi = NULL; bool matchRoiColumns = false; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricTFCE.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricTFCE.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmMetricTFCE.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmMetricTFCE.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -57,7 +57,8 @@ ret->addMetricOutputParameter(3, "metric-out", "the output metric"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(4, "-presmooth", "smooth the metric before running TFCE"); - presmoothOpt->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); + presmoothOpt->addDoubleParameter(1, "kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); + presmoothOpt->createOptionalParameter(2, "-fwhm", "kernel size is FWHM, not sigma"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "select a region of interest to run TFCE on"); roiOpt->addMetricParameter(1, "roi-metric", "the area to run TFCE on, as a metric"); @@ -96,6 +97,10 @@ { presmooth = (float)presmoothOpt->getDouble(1); if (presmooth <= 0.0f) throw AlgorithmException("presmooth kernel size must be positive"); + if (presmoothOpt->getOptionalParameter(2)->m_present) + { + presmooth = presmooth / (2.0f * sqrt(2.0f * log(2.0f))); + } } MetricFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(5); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmSurfaceAverage.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmSurfaceAverage.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmSurfaceAverage.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmSurfaceAverage.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -70,7 +70,7 @@ { SurfaceFile* myAvgOut = myParams->getOutputSurface(1); vector inputSurfs; - const vector& surfOpts = *(myParams->getRepeatableParameterInstances(2)); + const vector& surfOpts = myParams->getRepeatableParameterInstances(2); int numSurfs = (int)surfOpts.size(); vector surfWeights, *surfWeightPtr = NULL; for (int i = 0; i < numSurfs; ++i) diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -52,7 +52,7 @@ ret->addSurfaceOutputParameter(4, "out-surface", "the output surface"); - OptionalParameter* metricOpt = ret->createOptionalParameter(5, "-placement-out", "output the placement as a distance fraction from pial to white"); + OptionalParameter* metricOpt = ret->createOptionalParameter(5, "-placement-out", "output the placement as a volume fraction from pial to white"); metricOpt->addMetricOutputParameter(1, "placement-metric", "output metric"); //ret->createOptionalParameter(6, "-untwist", "temporary option for comparing methods, specify to use old method"); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmSurfaceDistortion.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmSurfaceDistortion.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmSurfaceDistortion.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmSurfaceDistortion.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -55,7 +55,8 @@ ret->addMetricOutputParameter(3, "metric-out", "the output distortion metric"); OptionalParameter* smoothOpt = ret->createOptionalParameter(4, "-smooth", "smooth the area data"); - smoothOpt->addDoubleParameter(1, "sigma", "the smoothing kernel sigma in mm"); + smoothOpt->addDoubleParameter(1, "sigma", "the size of the smoothing kernel in mm, as sigma by default"); + smoothOpt->createOptionalParameter(2, "-fwhm", "kernel size is FWHM, not sigma"); ret->createOptionalParameter(5, "-caret5-method", "use the surface distortion method from caret5"); @@ -89,6 +90,10 @@ { smooth = (float)smoothOpt->getDouble(1); if (smooth <= 0.0f) throw AlgorithmException("smoothing kernel must be positive if specified"); + if (smoothOpt->getOptionalParameter(2)->m_present) + { + smooth = smooth / (2.0f * sqrt(2.0f * log(2.0f))); + } } int methodCount = 0; bool caret5method = myParams->getOptionalParameter(5)->m_present; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmTemplate.cxx.txt connectome-workbench-1.5.0/src/Algorithms/AlgorithmTemplate.cxx.txt --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmTemplate.cxx.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmTemplate.cxx.txt 2021-02-16 19:46:47.000000000 +0000 @@ -1,6 +1,6 @@ /*LICENSE_START*/ /* - * Copyright (C) 2018 Washington University School of Medicine + * Copyright (C) 2020 Washington University School of Medicine * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmTemplate.h.txt connectome-workbench-1.5.0/src/Algorithms/AlgorithmTemplate.h.txt --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmTemplate.h.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmTemplate.h.txt 2021-02-16 19:46:47.000000000 +0000 @@ -3,7 +3,7 @@ /*LICENSE_START*/ /* - * Copyright (C) 2018 Washington University School of Medicine + * Copyright (C) 2020 Washington University School of Medicine * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeAffineResample.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeAffineResample.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeAffineResample.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeAffineResample.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -36,7 +36,7 @@ AString AlgorithmVolumeAffineResample::getShortDescription() { - return "RESAMPLE VOLUME USING AFFINE TRANSFORM"; + return "DEPRECATED: use -volume-resample"; } OperationParameters* AlgorithmVolumeAffineResample::getParameters() @@ -58,7 +58,8 @@ flirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); ret->setHelpText( - AString("Resample a volume file with an affine transformation. ") + + AString("DEPRECATED: this command may be removed in a future release, use -volume-resample.\n\n") + + "Resample a volume file with an affine transformation. " + "The recommended methods are CUBIC (cubic spline) for most data, and ENCLOSING_VOXEL for label data. " "The parameter must be one of:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR" diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeDilate.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeDilate.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeDilate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeDilate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -70,7 +70,7 @@ OptionalParameter* subvolSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume to dilate"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); - ret->createOptionalParameter(9, "-legacy-cutoff", "use the old method of excluding voxels further than the dilation distance when calculating the dilated value"); + ret->createOptionalParameter(9, "-legacy-cutoff", "use the v1.3.2 method of excluding voxels further than the dilation distance when calculating the dilated value"); ret->setHelpText( AString("For all voxels that are designated as bad, if they neighbor a non-bad voxel with data or are within the specified distance of such a voxel, ") + diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeExtrema.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeExtrema.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeExtrema.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeExtrema.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ #include "CaretLogger.h" #include "CaretOMP.h" #include "VolumeFile.h" + #include #include #include @@ -52,7 +53,8 @@ ret->addVolumeOutputParameter(3, "volume-out", "the output extrema volume"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(4, "-presmooth", "smooth the volume before finding extrema"); - presmoothOpt->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); + presmoothOpt->addDoubleParameter(1, "kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); + presmoothOpt->createOptionalParameter(2, "-fwhm", "kernel size is FWHM, not sigma"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "ignore values outside the selected area"); roiOpt->addVolumeParameter(1, "roi-volume", "the area to find extrema in"); @@ -101,6 +103,10 @@ { throw AlgorithmException("smoothing kernel must be positive"); } + if (presmoothOpt->getOptionalParameter(2)->m_present) + { + presmooth = presmooth / (2.0f * sqrt(2.0f * log(2.0f))); + } } OptionalParameter* roiOpt = myParams->getOptionalParameter(5); VolumeFile* myRoi = NULL; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeGradient.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeGradient.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeGradient.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeGradient.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -51,7 +51,8 @@ ret->addVolumeOutputParameter(2, "volume-out", "the output gradient magnitude volume"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(3, "-presmooth", "smooth the volume before computing the gradient"); - presmoothOpt->addDoubleParameter(1, "kernel", "sigma for gaussian weighting function, in mm"); + presmoothOpt->addDoubleParameter(1, "kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); + presmoothOpt->createOptionalParameter(2, "-fwhm", "kernel size is FWHM, not sigma"); OptionalParameter* roiOption = ret->createOptionalParameter(4, "-roi", "select a region of interest to take the gradient of"); roiOption->addVolumeParameter(1, "roi-volume", "the region to take the gradient within"); @@ -79,6 +80,10 @@ if (presmoothOpt->m_present) { presmooth = (float)presmoothOpt->getDouble(1); + if (presmoothOpt->getOptionalParameter(2)->m_present) + { + presmooth = presmooth / (2.0f * sqrt(2.0f * log(2.0f))); + } } VolumeFile* myRoi = NULL; OptionalParameter* roiOption = myParams->getOptionalParameter(4); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeParcelResampling.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeParcelResampling.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeParcelResampling.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeParcelResampling.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "Vector3D.h" + #include #include #include @@ -54,12 +55,14 @@ ret->addVolumeParameter(3, "new-parcels", "label volume of where the parcels should be"); - ret->addDoubleParameter(4, "kernel", "gaussian kernel sigma to smooth by during resampling"); + ret->addDoubleParameter(4, "kernel", "gaussian kernel size in mm to smooth by during resampling, as sigma by default"); ret->addVolumeOutputParameter(5, "volume-out", "output volume"); ret->createOptionalParameter(6, "-fix-zeros", "treat zero values as not being data"); + ret->createOptionalParameter(8, "-fwhm", "smoothing kernel size is FWHM, not sigma"); + OptionalParameter* subvolSelect = ret->createOptionalParameter(7, "-subvolume", "select a single subvolume as input"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); @@ -79,6 +82,10 @@ VolumeFile* curLabel = myParams->getVolume(2); VolumeFile* newLabel = myParams->getVolume(3); float kernel = (float)myParams->getDouble(4); + if (myParams->getOptionalParameter(8)->m_present) + { + kernel = kernel / (2.0f * sqrt(2.0f * log(2.0f))); + } VolumeFile* outVol = myParams->getOutputVolume(5); bool fixZeros = false; if (myParams->getOptionalParameter(6)->m_present) @@ -246,12 +253,12 @@ int64_t listSize = (int64_t)thisList.size(); if (listSize > 2)//NOTE: this should NEVER be something other than a multiple of 3, but check against what we will actually access anyway { - int extrema[6] = { thisList[0], - thisList[0], - thisList[1], - thisList[1], - thisList[2], - thisList[2] }; + int64_t extrema[6] = { thisList[0], + thisList[0], + thisList[1], + thisList[1], + thisList[2], + thisList[2] }; for (int64_t base = 3; base < listSize; base += 3) { if (thisList[base] < extrema[0]) extrema[0] = thisList[base]; @@ -493,12 +500,12 @@ int64_t listSize = (int64_t)thisList.size(); if (listSize > 2)//NOTE: this should NEVER be something other than a multiple of 3, but check against what we will actually access anyway { - int extrema[6] = { thisList[0], - thisList[0], - thisList[1], - thisList[1], - thisList[2], - thisList[2] }; + int64_t extrema[6] = { thisList[0], + thisList[0], + thisList[1], + thisList[1], + thisList[2], + thisList[2] }; for (int64_t base = 3; base < listSize; base += 3) { if (thisList[base] < extrema[0]) extrema[0] = thisList[base]; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ #include "CaretLogger.h" #include "Vector3D.h" #include "CaretOMP.h" + #include #include #include @@ -54,10 +55,12 @@ ret->addVolumeParameter(3, "new-parcels", "label volume of where the parcels should be"); - ret->addDoubleParameter(4, "kernel", "gaussian kernel sigma to smooth by during resampling"); + ret->addDoubleParameter(4, "kernel", "gaussian kernel size in mm to smooth by during resampling, as sigma by default"); ret->addVolumeOutputParameter(5, "volume-out", "output volume"); + ret->createOptionalParameter(8, "-fwhm", "smoothing kernel size is FWHM, not sigma"); + ret->createOptionalParameter(6, "-fix-zeros", "treat zero values as not being data"); OptionalParameter* subvolSelect = ret->createOptionalParameter(7, "-subvolume", "select a single subvolume as input"); @@ -79,6 +82,10 @@ VolumeFile* curLabel = myParams->getVolume(2); VolumeFile* newLabel = myParams->getVolume(3); float kernel = (float)myParams->getDouble(4); + if (myParams->getOptionalParameter(8)->m_present) + { + kernel = kernel / (2.0f * sqrt(2.0f * log(2.0f))); + } VolumeFile* outVol = myParams->getOutputVolume(5); bool fixZeros = false; if (myParams->getOptionalParameter(6)->m_present) diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -22,6 +22,7 @@ #include "AlgorithmException.h" #include "VolumeFile.h" #include "AlgorithmVolumeSmoothing.h" + #include #include @@ -45,10 +46,12 @@ ret->addVolumeParameter(2, "label-volume", "a label volume containing the parcels to smooth"); - ret->addDoubleParameter(3, "kernel", "the gaussian smoothing kernel sigma, in mm"); + ret->addDoubleParameter(3, "kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); ret->addVolumeOutputParameter(4, "volume-out", "the output volume"); + ret->createOptionalParameter(7, "-fwhm", "smoothing kernel size is FWHM, not sigma"); + ret->createOptionalParameter(5, "-fix-zeros", "treat zero values as not being data"); OptionalParameter* subvolSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume to smooth"); @@ -66,6 +69,10 @@ VolumeFile* myVol = myParams->getVolume(1); VolumeFile* myLabelVol = myParams->getVolume(2); float myKernel = (float)myParams->getDouble(3); + if (myParams->getOptionalParameter(7)->m_present) + { + myKernel = myKernel / (2.0f * sqrt(2.0f * log(2.0f))); + } VolumeFile* myOutVol = myParams->getOutputVolume(4); OptionalParameter* fixZerosOpt = myParams->getOptionalParameter(5); bool fixZeros = fixZerosOpt->m_present; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeResample.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeResample.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeResample.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeResample.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,334 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AlgorithmVolumeResample.h" +#include "AlgorithmException.h" + +#include "AffineFile.h" +#include "AffineSeriesFile.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "NiftiIO.h" +#include "WarpfieldFile.h" + +using namespace caret; +using namespace std; + +AString AlgorithmVolumeResample::getCommandSwitch() +{ + return "-volume-resample"; +} + +AString AlgorithmVolumeResample::getShortDescription() +{ + return "TRANSFORM AND RESAMPLE A VOLUME FILE"; +} + +OperationParameters* AlgorithmVolumeResample::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + + ret->addVolumeParameter(1, "volume-in", "volume to resample"); + + ret->addStringParameter(2, "volume-space", "a volume file in the volume space you want for the output"); + + ret->addStringParameter(3, "method", "the resampling method"); + + ret->addVolumeOutputParameter(4, "volume-out", "the output volume"); + + ParameterComponent* affineOpt = ret->createRepeatableParameter(5, "-affine", "add an affine transform"); + affineOpt->addStringParameter(1, "affine", "the affine file to use"); + OptionalParameter* flirtOpt = affineOpt->createOptionalParameter(2, "-flirt", "MUST be used if affine is a flirt affine"); + flirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the affine"); + flirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); + + ParameterComponent* affineSeriesOpt = ret->createRepeatableParameter(6, "-affine-series", "add an independent affine per-frame"); + affineSeriesOpt->addStringParameter(1, "affine-series", "text file containing 12 or 16 numbers per line, each being a row-major flattened affine"); + OptionalParameter* seriesFlirtOpt = affineSeriesOpt->createOptionalParameter(2, "-flirt", "MUST be used if the affines are flirt affines"); + seriesFlirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the affine"); + seriesFlirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); + + ParameterComponent* warpOpt = ret->createRepeatableParameter(7, "-warp", "add a nonlinear warpfield transform"); + warpOpt->addStringParameter(1, "warpfield", "the warpfield file"); + OptionalParameter* fnirtOpt = warpOpt->createOptionalParameter(2, "-fnirt", "MUST be used if using a fnirt warpfield"); + fnirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the warpfield"); + + ret->setHelpText( + AString("Resample a volume file with an arbitrary list of transformations. ") + + "You may specify -affine, -warp, and -affine-series multiple times each, and they will be used in the order specified. " + "For instance, for rigid motion correction followed by nonlinear atlas registration, specify -affine-series first, then -warp. " + "The recommended methods are CUBIC (cubic spline) for most data, and ENCLOSING_VOXEL for label data. " + "The parameter must be one of:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR" + ); + return ret; +} + +void AlgorithmVolumeResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + VolumeFile* inVol = myParams->getVolume(1); + vector voldims = inVol->getDimensions(); + AString refSpaceName = myParams->getString(2); + AString methodStr = myParams->getString(3); + VolumeFile* outVol = myParams->getOutputVolume(4); + auto& affInstances = myParams->getRepeatableParameterInstances(5); + auto& affSeriesInstances = myParams->getRepeatableParameterInstances(6); + auto& warpInstances = myParams->getRepeatableParameterInstances(7); + VolumeSpace refSpace; + { + NiftiIO myIO; + myIO.openRead(refSpaceName); + refSpace = myIO.getHeader().getVolumeSpace(); + } + VolumeFile::InterpType myMethod = VolumeFile::CUBIC; + if (methodStr == "CUBIC") + { + myMethod = VolumeFile::CUBIC; + } else if (methodStr == "TRILINEAR") { + myMethod = VolumeFile::TRILINEAR; + } else if (methodStr == "ENCLOSING_VOXEL") { + myMethod = VolumeFile::ENCLOSING_VOXEL; + } else { + throw AlgorithmException("unrecognized interpolation method"); + } + XfmStack myStack; + auto xfmOrder = myParams->getRepeatableOrder();//helper for some ugly code to resolve relative order of repeatable options + vector warpStorage(warpInstances.size());//need to keep these in scope until after the algorithm completes + for (auto iter = xfmOrder.rbegin(); iter != xfmOrder.rend(); ++iter)//because this is volume resampling, we need to transform target coords into source coords + {//so, reverse the transform order and invert affines (warpfields are harder to invert, so they work differently for surfaces) + switch (iter->key) + { + case 5: + { + OptionalParameter* flirtOpt = affInstances[iter->index]->getOptionalParameter(2); + AffineFile myAff; + if (flirtOpt->m_present) + { + myAff.readFlirt(affInstances[iter->index]->getString(1), flirtOpt->getString(1), flirtOpt->getString(2)); + } else { + myAff.readWorld(affInstances[iter->index]->getString(1)); + } + myStack.push_back(CaretPointer(new AffineXfm(myAff.getMatrix().inverse())));//invert it + break; + } + case 6: + { + OptionalParameter* flirtOpt = affSeriesInstances[iter->index]->getOptionalParameter(2); + AffineSeriesFile myAffSeries; + if (flirtOpt->m_present) + { + myAffSeries.readFlirt(affSeriesInstances[iter->index]->getString(1), flirtOpt->getString(1), flirtOpt->getString(2)); + } else { + myAffSeries.readWorld(affSeriesInstances[iter->index]->getString(1)); + } + if (myAffSeries.getMatrixList().size() != size_t(voldims[3])) + { + throw AlgorithmException("affine series file '" + affSeriesInstances[iter->index]->getString(1) + "' has different number of frames than the input volume"); + } + myStack.push_back(CaretPointer(new AffineSeriesXfm(myAffSeries.getInverseMatrixList())));//invert it + break; + } + case 7: + { + OptionalParameter* fnirtOpt = warpInstances[iter->index]->getOptionalParameter(2); + WarpfieldFile& myWarp = warpStorage[iter->index]; + if (fnirtOpt->m_present) + { + myWarp.readFnirt(warpInstances[iter->index]->getString(1), fnirtOpt->getString(1)); + } else { + myWarp.readWorld(warpInstances[iter->index]->getString(1)); + } + myStack.push_back(CaretPointer(new WarpfieldXfm(myWarp.getWarpfield())));//DON'T invert, internal warpfield convention is already inverse + break; + } + default: + CaretAssert(false); + throw AlgorithmException("internal error, tell the developers what you just tried to do"); + } + } + AlgorithmVolumeResample(myProgObj, inVol, myStack, refSpace, myMethod, outVol); +} + +AlgorithmVolumeResample::AlgorithmVolumeResample(ProgressObject* myProgObj, const VolumeFile* inVol, const XfmStack& myStack, const VolumeSpace refSpace, + const VolumeFile::InterpType& myMethod, VolumeFile* outVol) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + vector outDims = inVol->getOriginalDimensions(); + const int64_t* refDims = refSpace.getDims(); + if (outDims.size() < 3) throw AlgorithmException("input must have 3 spatial dimensions"); + outDims[0] = refDims[0]; + outDims[1] = refDims[1]; + outDims[2] = refDims[2]; + int64_t numMaps = inVol->getNumberOfMaps(), numComponents = inVol->getNumberOfComponents(); + outVol->reinitialize(outDims, refSpace.getSform(), numComponents, inVol->getType(), inVol->m_header); + vector scratchFrame(outDims[0] * outDims[1] * outDims[2], 0.0f); + if (inVol->isMappedWithLabelTable()) + { + if (myMethod != VolumeFile::ENCLOSING_VOXEL) + { + CaretLogWarning("using interpolation type other than ENCLOSING_VOXEL on a label volume"); + } + for (int64_t i = 0; i < numMaps; ++i) + { + *(outVol->getMapLabelTable(i)) = *(inVol->getMapLabelTable(i)); + } + } + for (int64_t i = 0; i < numMaps; ++i) + { + outVol->setMapName(i, inVol->getMapName(i)); + } + for (int64_t c = 0; c < numComponents; ++c) + { + for (int64_t b = 0; b < numMaps; ++b) + { + if (myMethod == VolumeFile::CUBIC) + { + inVol->validateSpline(b, c);//because deconvolve is parallel, but won't execute parallel if we are already in a parallel section + } +#pragma omp CARET_PARFOR schedule(guided, 10) + for (int64_t k = 0; k < outDims[2]; ++k) + { + for (int64_t j = 0; j < outDims[1]; ++j) + { + for (int64_t i = 0; i < outDims[0]; ++i) + { + Vector3D outCoord; + outVol->indexToSpace(i, j, k, outCoord);//start with the coords of the output voxel + bool validCoord = false; + Vector3D inCoord = myStack.xfmPoint(outCoord, b, &validCoord);//put it through the inverse transforms that are in reverse order + if (validCoord) + { + float interpVal = inVol->interpolateValue(inCoord, myMethod, NULL, b, c); + scratchFrame[outVol->getIndex(i, j, k)] = interpVal; + } else { + scratchFrame[outVol->getIndex(i, j, k)] = VolumeFile::INVALID_INTERP_VALUE; + } + } + } + } + outVol->setFrame(scratchFrame.data(), b, c); + if (myMethod == VolumeFile::CUBIC) + { + inVol->freeSpline(b, c);//release memory we no longer need, if we allocated it + } + } + } +} + +float AlgorithmVolumeResample::getAlgorithmInternalWeight() +{ + return 1.0f;//override this if needed, if the progress bar isn't smooth +} + +float AlgorithmVolumeResample::getSubAlgorithmWeight() +{ + //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm + return 0.0f; +} + +//XfmStack details +namespace +{ + void sanityCheckAffine(const FloatMatrix& affine) + { + if (affine.getNumberOfRows() != 4 || affine.getNumberOfColumns() != 4) + { + CaretAssert(false); + throw AlgorithmException("affines must be 4x4 matrices");//what exception type? + } + if (affine[3][0] != 0.0f || affine[3][1] != 0.0f || affine[3][2] != 0.0f || affine[3][3] != 1.0f) + { + CaretAssert(false); + throw AlgorithmException("affine fourth row must be 0 0 0 1"); + } + } +} + +AffineXfm::AffineXfm(const FloatMatrix& xfm) +{ + sanityCheckAffine(xfm); + m_xfm = xfm; +} + +Vector3D AffineXfm::xfmPoint(const Vector3D& coordIn, const int64_t /*frame*/, bool* validCoord) const +{ + if (validCoord != NULL) *validCoord = true; + return m_xfm.transformPoint(coordIn); +} + +AffineSeriesXfm::AffineSeriesXfm(const vector& xfmList) +{ + for (auto matrix : xfmList) + { + sanityCheckAffine(matrix); + } + m_xfmList = xfmList; +} + +Vector3D AffineSeriesXfm::xfmPoint(const Vector3D& coordIn, const int64_t frame, bool* validCoord) const +{ + CaretAssertVectorIndex(m_xfmList, frame); + if (validCoord != NULL) *validCoord = true; + return m_xfmList[frame].transformPoint(coordIn); +} + +WarpfieldXfm::WarpfieldXfm(const VolumeFile* warp) +{ + if ((warp->getDimensions())[3] != 3) + { + throw AlgorithmException("warpfields must have 3 subvolumes"); + } + m_warp = warp; +} + +Vector3D WarpfieldXfm::xfmPoint(const Vector3D& coordIn, const int64_t /*frame*/, bool* validCoord) const +{ + Vector3D offset; + bool validDisplacement = false; + offset[0] = m_warp->interpolateValue(coordIn, VolumeFile::TRILINEAR, &validDisplacement, 0); + if (validDisplacement) + { + offset[1] = m_warp->interpolateValue(coordIn, VolumeFile::TRILINEAR, NULL, 1); + offset[2] = m_warp->interpolateValue(coordIn, VolumeFile::TRILINEAR, NULL, 2); + } + if (validCoord != NULL) *validCoord = validDisplacement; + return offset + coordIn; +} + +void XfmStack::push_back(CaretPointer nextXfm) +{ + m_xfmStack.push_back(nextXfm); +} + +Vector3D XfmStack::xfmPoint(const Vector3D& coordIn, const int64_t frame, bool* validCoord) const +{ + Vector3D ret = coordIn; + bool thisValid = true; + for (auto& xfm : m_xfmStack) + { + ret = xfm->xfmPoint(ret, frame, &thisValid); + if (validCoord != NULL && !thisValid) + { + break;//short circuit only when the caller is paying attention? + } + } + if (validCoord != NULL) *validCoord = thisValid; + return ret; +} diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeResample.h connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeResample.h --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeResample.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeResample.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,92 @@ +#ifndef __ALGORITHM_VOLUME_RESAMPLE_H__ +#define __ALGORITHM_VOLUME_RESAMPLE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractAlgorithm.h" +#include "CaretPointer.h" +#include "FloatMatrix.h" +#include "Vector3D.h" +#include "VolumeFile.h" + +namespace caret { + + class XfmStack; + + class AlgorithmVolumeResample : public AbstractAlgorithm + { + AlgorithmVolumeResample(); + protected: + static float getSubAlgorithmWeight(); + static float getAlgorithmInternalWeight(); + public: + AlgorithmVolumeResample(ProgressObject* myProgObj, const VolumeFile* inVol, const XfmStack& myStack, const VolumeSpace refSpace, + const VolumeFile::InterpType& myMethod, VolumeFile* outVol); + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoAlgorithmVolumeResample; + + //might want to move these and related to a separate XfmStack header + struct XfmBase + { + virtual Vector3D xfmPoint(const Vector3D& coordIn, const int64_t frame, bool* validCoord = NULL) const = 0; + virtual ~XfmBase() {}; + }; + + class AffineXfm : public XfmBase + { + FloatMatrix m_xfm; + public: + AffineXfm(const FloatMatrix& xfm); + Vector3D xfmPoint(const Vector3D& coordIn, const int64_t frame, bool* validCoord = NULL) const; + }; + + class AffineSeriesXfm : public XfmBase + { + std::vector m_xfmList; + public: + AffineSeriesXfm(const std::vector& xfmList); + Vector3D xfmPoint(const Vector3D& coordIn, const int64_t frame, bool* validCoord = NULL) const; + }; + + class WarpfieldXfm : public XfmBase + { + const VolumeFile* m_warp;//NOTE: the warp is by reference + public: + WarpfieldXfm(const VolumeFile* warp); + Vector3D xfmPoint(const Vector3D& coordIn, const int64_t frame, bool* validCoord = NULL) const; + }; + + class XfmStack : public XfmBase //allow stacking of transform stacks, because why not + { + std::vector > m_xfmStack; + public: + Vector3D xfmPoint(const Vector3D& coordIn, const int64_t frame, bool* validCoord = NULL) const; + void push_back(CaretPointer nextXfm); + }; + +} + +#endif //__ALGORITHM_VOLUME_RESAMPLE_H__ diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeSmoothing.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeSmoothing.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeSmoothing.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeSmoothing.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ #include "CaretLogger.h" #include "CaretOMP.h" #include "CaretAssert.h" + #include using namespace caret; @@ -48,10 +49,12 @@ OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the volume to smooth"); - ret->addDoubleParameter(2, "kernel", "the gaussian smoothing kernel sigma, in mm"); + ret->addDoubleParameter(2, "kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); ret->addVolumeOutputParameter(3, "volume-out", "the output volume"); + ret->createOptionalParameter(7, "-fwhm", "kernel size is FWHM, not sigma"); + OptionalParameter* roiVolOpt = ret->createOptionalParameter(4, "-roi", "smooth only from data within an ROI"); roiVolOpt->addVolumeParameter(1, "roivol", "the volume to use as an ROI"); @@ -75,6 +78,10 @@ { VolumeFile* myVol = myParams->getVolume(1); float myKernel = (float)myParams->getDouble(2); + if (myParams->getOptionalParameter(7)->m_present) + { + myKernel = myKernel / (2.0f * sqrt(2.0f * log(2.0f))); + } VolumeFile* myOutVol = myParams->getOutputVolume(3); OptionalParameter* roiVolOpt = myParams->getOptionalParameter(4); VolumeFile* roiVol = NULL; diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeTFCE.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeTFCE.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeTFCE.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeTFCE.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -54,7 +54,8 @@ ret->addVolumeOutputParameter(2, "volume-out", "the output volume"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(3, "-presmooth", "smooth the volume before running TFCE"); - presmoothOpt->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); + presmoothOpt->addDoubleParameter(1, "kernel", "the size of the gaussian smoothing kernel in mm, as sigma by default"); + presmoothOpt->createOptionalParameter(2, "-fwhm", "smoothing kernel size is FWHM, not sigma"); OptionalParameter* roiOpt = ret->createOptionalParameter(4, "-roi", "select a region of interest to run TFCE on"); roiOpt->addVolumeParameter(1, "roi-volume", "the area to run TFCE on, as a volume"); @@ -87,6 +88,10 @@ { presmooth = (float)presmoothOpt->getDouble(1); if (presmooth <= 0.0f) throw AlgorithmException("presmooth kernel size must be positive"); + if (presmoothOpt->getOptionalParameter(2)->m_present) + { + presmooth = presmooth / (2.0f * sqrt(2.0f * log(2.0f))); + } } VolumeFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(4); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -21,10 +21,12 @@ #include "AlgorithmVolumeToSurfaceMapping.h" #include "AlgorithmException.h" +#include "CaretAssert.h" #include "CaretOMP.h" #include "FloatMatrix.h" #include "MathFunctions.h" #include "MetricFile.h" +#include "SignedDistanceHelper.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "Vector3D.h" @@ -68,12 +70,15 @@ ribbonOpt->addSurfaceParameter(1, "inner-surf", "the inner surface of the ribbon"); ribbonOpt->addSurfaceParameter(2, "outer-surf", "the outer surface of the ribbon"); OptionalParameter* roiVol = ribbonOpt->createOptionalParameter(3, "-volume-roi", "use a volume roi"); - roiVol->addVolumeParameter(1, "roi-volume", "the volume file"); + roiVol->addVolumeParameter(1, "roi-volume", "the roi volume file"); + roiVol->createOptionalParameter(2, "-weighted", "treat the roi values as weightings rather than binary"); OptionalParameter* ribbonSubdiv = ribbonOpt->createOptionalParameter(4, "-voxel-subdiv", "voxel divisions while estimating voxel weights"); ribbonSubdiv->addIntegerParameter(1, "subdiv-num", "number of subdivisions, default 3"); ribbonOpt->createOptionalParameter(7, "-thin-columns", "use non-overlapping polyhedra"); OptionalParameter* gaussianOpt = ribbonOpt->createOptionalParameter(8, "-gaussian", "reduce weight to voxels that aren't near "); gaussianOpt->addDoubleParameter(1, "scale", "value to multiply the local thickness by, to get the gaussian sigma"); + OptionalParameter* ribbonInterpOpt = ribbonOpt->createOptionalParameter(10, "-interpolate", "instead of a weighted average of voxels, interpolate at subpoints inside the ribbon"); + ribbonInterpOpt->addStringParameter(1, "method", "interpolation method, must be CUBIC, ENCLOSING_VOXEL, or TRILINEAR"); OptionalParameter* badVertOpt = ribbonOpt->createOptionalParameter(9, "-bad-vertices-out", "output an ROI of which vertices didn't intersect any valid voxels"); badVertOpt->addMetricOutputParameter(1, "roi-out", "the output metric file of vertices that have no data"); OptionalParameter* ribbonWeights = ribbonOpt->createOptionalParameter(5, "-output-weights", "write the voxel weights for a vertex to a volume file"); @@ -93,7 +98,8 @@ ret->setHelpText( AString("You must specify exactly one mapping method. Enclosing voxel uses the value from the voxel the vertex lies inside, while trilinear does a 3D ") + - "linear interpolation based on the voxels immediately on each side of the vertex's position.\n\n" + + "linear interpolation based on the voxels immediately on each side of the vertex's position." + + "\n\n" + "The ribbon mapping method constructs a polyhedron from the vertex's neighbors on each " + "surface, and estimates the amount of this polyhedron's volume that falls inside any nearby voxels, to use as the weights for sampling. " + "If -thin-columns is specified, the polyhedron uses the edge midpoints and triangle centroids, so that neighboring vertices do not have overlapping polyhedra. " + @@ -102,7 +108,11 @@ "voxels that don't have a positive value in the mask. The subdivision number specifies how it approximates the amount of the volume the polyhedron " + "intersects, by splitting each voxel into NxNxN pieces, and checking whether the center of each piece is inside the polyhedron. If you have very large " + "voxels, consider increasing this if you get zeros in your output. " + - "The -gaussian option makes it act more like the myelin method, where the distance of a voxel from is used to downweight the voxel.\n\n" + + "The -gaussian option makes it act more like the myelin method, where the distance of a voxel from is used to downweight the voxel. " + + "The -interpolate suboption, instead of doing a weighted average of voxels, interpolates from the volume at the subdivided points inside the ribbon. " + + "If using both -interpolate and the -weighted suboption to -volume-roi, the roi volume weights are linearly interpolated, " + + "unless the -interpolate method is ENCLOSING_VOXEL, in which case ENCLOSING_VOXEL is also used for sampling the roi volume weights." + + "\n\n" + "The myelin style method uses part of the caret5 myelin mapping command to do the mapping: for each surface vertex, take all voxels that are in a cylinder " + "with radius and height equal to cortical thickness, centered on the vertex and aligned with the surface normal, and that are also within the ribbon ROI, " + "and apply a gaussian kernel with the specified sigma to them to get the weights to use. " + @@ -196,9 +206,11 @@ SurfaceFile* outerSurf = ribbonOpt->getSurface(2); OptionalParameter* roiVol = ribbonOpt->getOptionalParameter(3); VolumeFile* myRoiVol = NULL; + bool weightedRoi = false; if (roiVol->m_present) { myRoiVol = roiVol->getVolume(1); + weightedRoi = roiVol->getOptionalParameter(2)->m_present; } int32_t subdivisions = 3; OptionalParameter* ribbonSubdiv = ribbonOpt->getOptionalParameter(4); @@ -218,6 +230,23 @@ gaussScale = (float)gaussianOpt->getDouble(1); if (!(gaussScale > 0.0f)) throw AlgorithmException("gaussian scale must be positive"); } + bool ribbonInterp = false; + OptionalParameter* ribbonInterpOpt = ribbonOpt->getOptionalParameter(10); + if (ribbonInterpOpt->m_present) + { + ribbonInterp = true; + AString interpMethod = ribbonInterpOpt->getString(1); + if (interpMethod == "CUBIC") + { + volInterpMethod = VolumeFile::CUBIC; + } else if (interpMethod == "TRILINEAR") { + volInterpMethod = VolumeFile::TRILINEAR; + } else if (interpMethod == "ENCLOSING_VOXEL") { + volInterpMethod = VolumeFile::ENCLOSING_VOXEL; + } else { + throw AlgorithmException("unrecognized volume interpolation method"); + } + } MetricFile* badVertices = NULL; OptionalParameter* badVertOpt = ribbonOpt->getOptionalParameter(9); if (badVertOpt->m_present) @@ -232,9 +261,19 @@ weightsOutVertex = (int)ribbonWeights->getInteger(1); weightsOut = ribbonWeights->getOutputVolume(2); } - AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, innerSurf, outerSurf, myRoiVol, subdivisions, thinColumns, - mySubVol, gaussScale, badVertices, weightsOutVertex, weightsOut); OptionalParameter* ribbonWeightsText = ribbonOpt->getOptionalParameter(6); + if (ribbonInterp) + { + if (ribbonWeightsText->m_present || weightsOut != NULL) + { + throw AlgorithmException("-output-weights options are incompatible with -interpolate"); + } + AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, innerSurf, outerSurf, volInterpMethod, myRoiVol, weightedRoi, subdivisions, thinColumns, + mySubVol, gaussScale, badVertices); + } else { + AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, innerSurf, outerSurf, myRoiVol, weightedRoi, subdivisions, thinColumns, + mySubVol, gaussScale, badVertices, weightsOutVertex, weightsOut); + } if (ribbonWeightsText->m_present) {//do this after the algorithm, to let it do the error condition checking ofstream outFile(ribbonWeightsText->getString(1).toLocal8Bit().constData()); @@ -242,7 +281,7 @@ vector > myWeights; const float* roiFrame = NULL; if (myRoiVol != NULL) roiFrame = myRoiVol->getFrame(); - AlgorithmVolumeToSurfaceMapping::precomputeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions, thinColumns, mySurface, gaussScale); + AlgorithmVolumeToSurfaceMapping::precomputeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, weightedRoi, subdivisions, thinColumns, mySurface, gaussScale); for (int i = 0; i < (int)myWeights.size(); ++i) { outFile << i << ", " << myWeights[i].size(); @@ -369,7 +408,7 @@ //ribbon mapping AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, - const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile* roiVol, + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile* roiVol, const bool roiWeights, const int32_t& subdivisions, const bool& thinColumns, const int64_t& mySubVol, const float& gaussScale, MetricFile* badVertices, const int& weightsOutVertex, VolumeFile* weightsOut) : AbstractAlgorithm(myProgObj) { @@ -415,7 +454,7 @@ vector > myWeights; const float* roiFrame = NULL; if (roiVol != NULL) roiFrame = roiVol->getFrame(); - precomputeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions, thinColumns, mySurface, gaussScale); + precomputeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, roiWeights, subdivisions, thinColumns, mySurface, gaussScale); if (weightsOut != NULL) { weightsOut->setValueAllVoxels(0.0f); @@ -511,17 +550,150 @@ } } +//interpolation ribbon mapping +AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile::InterpType interpType, const VolumeFile* roiVol, const bool roiWeights, + const int32_t& subdivisions, const bool& thinColumns, const int64_t& mySubVol, const float& gaussScale, MetricFile* badVertices) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + vector myVolDims; + myVolume->getDimensions(myVolDims); + if (mySubVol >= myVolDims[3] || mySubVol < -1) + { + throw AlgorithmException("invalid subvolume specified"); + } + if (!mySurface->hasNodeCorrespondence(*outerSurf) || !mySurface->hasNodeCorrespondence(*innerSurf)) + { + throw AlgorithmException("all surfaces must have vertex correspondence"); + } + if (roiVol != NULL && !roiVol->matchesVolumeSpace(myVolume)) + { + throw AlgorithmException("roi volume is not in the same volume space as input volume"); + } + int64_t startVol = 0, endVol = myVolDims[3]; + if (mySubVol > -1) + { + startVol = mySubVol; + endVol = mySubVol + 1; + } + int64_t numColumns = (endVol - startVol) * myVolDims[4]; + int64_t numNodes = mySurface->getNumberOfNodes(); + myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); + myMetricOut->setStructure(mySurface->getStructure()); + vector badVertScratch, myScratchArray(numNodes); + if (badVertices != NULL) + { + badVertices->setNumberOfNodesAndColumns(numNodes, 1); + badVertices->setStructure(mySurface->getStructure()); + badVertScratch.resize(numNodes, 0.0f); + } + const float* roiFrame = NULL; + if (roiVol != NULL) roiFrame = roiVol->getFrame(); + vector > pointList = RibbonMappingHelper::computePointsRibbon(myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions, thinColumns); + CaretPointer myHelpBase; + if (gaussScale > 0.0f) + { + myHelpBase.grabNew(new SignedDistanceHelperBase(mySurface)); + } + MetricFile thickness; + VolumeFile::InterpType roiInterp = VolumeFile::TRILINEAR;//TODO: should this change based on interpType? be explicit? + if (interpType == VolumeFile::ENCLOSING_VOXEL) + { + roiInterp = VolumeFile::ENCLOSING_VOXEL; + } + AlgorithmSurfaceToSurface3dDistance(NULL, innerSurf, outerSurf, &thickness); + for (int64_t i = startVol; i < endVol; ++i) + { + for (int64_t j = 0; j < myVolDims[4]; ++j) + { + if (interpType == VolumeFile::CUBIC) + { + myVolume->validateSpline(i, j);//to do spline deconvolution in parallel + } + int64_t thisCol = (i - startVol) * myVolDims[4] + j; + AString metricLabel = myVolume->getMapName(i); + if (myVolDims[4] != 1) + { + metricLabel += " component " + AString::number(j); + } + myMetricOut->setColumnName(thisCol, metricLabel); +#pragma omp CARET_PAR + { + CaretPointer distHelp; + if (gaussScale > 0.0f) + { + distHelp.grabNew(new SignedDistanceHelper(myHelpBase)); + } +#pragma omp CARET_FOR + for (int64_t node = 0; node < numNodes; ++node) + { + double accum = 0.0, weightAccum = 0.0; + float thisThick = thickness.getValue(node, 0); + for (auto& point : pointList[node]) + { + float value = myVolume->interpolateValue(point.coord, interpType, NULL, i, j); + float weight = point.weight; + if (roiWeights) + { + weight = weight * roiVol->interpolateValue(point.coord, roiInterp); + } + if (gaussScale > 0.0f) + { + float dist = distHelp->dist(point.coord, SignedDistanceHelper::EVEN_ODD);//could make a function for unsigned distance to speed this up some + float toSquare = dist / (thisThick * gaussScale); + weight = weight * exp(-toSquare * toSquare / 2); + } + accum += value * weight; + weightAccum += weight; + } + if (weightAccum > 0.0) + { + myScratchArray[node] = accum / weightAccum; + } else { + myScratchArray[node] = 0.0f; + if (badVertices != NULL) + { + badVertScratch[node] = 1.0f; + } + } + } + } + myMetricOut->setValuesForColumn(thisCol, myScratchArray.data()); + if (interpType == VolumeFile::CUBIC) + { + myVolume->freeSpline(i, j);//release memory we no longer need, if we allocated it + } + } + } + if (badVertices != NULL) + { + badVertices->setValuesForColumn(0, badVertScratch.data()); + } +} + void AlgorithmVolumeToSurfaceMapping::precomputeWeightsRibbon(vector >& myWeights, const VolumeSpace& volSpace, - const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const float* roiFrame, + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const float* roiFrame, const bool roiWeights, const int& subdivisions, const bool& thinColumns, const SurfaceFile* gaussSurf, const float& gaussScale) { RibbonMappingHelper::computeWeightsRibbon(myWeights, volSpace, innerSurf, outerSurf, roiFrame, subdivisions, thinColumns); + const int numNodes = innerSurf->getNumberOfNodes(); + if (roiWeights) + { + CaretAssert(roiFrame != NULL); + for (int i = 0; i < numNodes; ++i) + {//modify the weights from the ribbon helper + vector& nodeWeights = myWeights[i]; + for (int j = 0; j < (int)nodeWeights.size(); ++j) + { + nodeWeights[j].weight *= roiFrame[volSpace.getIndex(nodeWeights[j].ijk)]; + } + } + } if (gaussScale > 0.0f) { VolumeFile signedDistVol; MetricFile thickness; AlgorithmSurfaceToSurface3dDistance(NULL, innerSurf, outerSurf, &thickness); - int numNodes = innerSurf->getNumberOfNodes(); float maxThick = thickness.getValue(0, 0); for (int i = 1; i < numNodes; ++i) { diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,7 +37,7 @@ static void precomputeWeightsMyelin(std::vector >& myWeights, const SurfaceFile* mySurface, const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma, const bool& oldCutoffBug); static void precomputeWeightsRibbon(std::vector >& myWeights, const VolumeSpace& volSpace, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, - const float* roiFrame, const int& subdivisions, const bool& thinColumns, const SurfaceFile* gaussSurf, const float& gaussScale); + const float* roiFrame, const bool roiWeights, const int& subdivisions, const bool& thinColumns, const SurfaceFile* gaussSurf, const float& gaussScale); enum Method { TRILINEAR, @@ -54,10 +54,14 @@ const int64_t& mySubVol = -1); AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, - const VolumeFile* roiVol = NULL, const int32_t& subdivisions = 3, const bool& thinColumns = false, + const VolumeFile* roiVol = NULL, const bool roiWeights = false, const int32_t& subdivisions = 3, const bool& thinColumns = false, const int64_t& mySubVol = -1, const float& gaussScale = -1.0f, MetricFile* badVertices = NULL, const int& weightsOutVertex = -1, VolumeFile* weightsOut = NULL); AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile::InterpType interpType, + const VolumeFile* roiVol = NULL, const bool roiWeights = false, const int32_t& subdivisions = 3, const bool& thinColumns = false, + const int64_t& mySubVol = -1, const float& gaussScale = -1.0f, MetricFile* badVertices = NULL); + AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma, const int64_t& mySubVol = -1, const bool& oldCutoffBug = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); diff -Nru connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx --- connectome-workbench-1.4.2/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -37,7 +37,7 @@ AString AlgorithmVolumeWarpfieldResample::getShortDescription() { - return "RESAMPLE VOLUME USING WARPFIELD"; + return "DEPRECATED: use -volume-resample"; } OperationParameters* AlgorithmVolumeWarpfieldResample::getParameters() @@ -57,7 +57,8 @@ fnirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the warpfield"); ret->setHelpText( - AString("Resample a volume file with a warpfield. ") + + AString("DEPRECATED: this command may be removed in a future release, use -volume-resample.\n\n") + + "Resample a volume file with a warpfield. " + "The recommended methods are CUBIC (cubic spline) for most data, and ENCLOSING_VOXEL for label data. " "The parameter must be one of:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR" diff -Nru connectome-workbench-1.4.2/src/Algorithms/CMakeLists.txt connectome-workbench-1.5.0/src/Algorithms/CMakeLists.txt --- connectome-workbench-1.4.2/src/Algorithms/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Algorithms/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -134,6 +134,7 @@ AlgorithmVolumeParcelSmoothing.h AlgorithmVolumeReduce.h AlgorithmVolumeRemoveIslands.h +AlgorithmVolumeResample.h AlgorithmVolumeROIsFromExtrema.h AlgorithmVolumeSmoothing.h AlgorithmVolumeTFCE.h @@ -255,6 +256,7 @@ AlgorithmVolumeParcelSmoothing.cxx AlgorithmVolumeReduce.cxx AlgorithmVolumeRemoveIslands.cxx +AlgorithmVolumeResample.cxx AlgorithmVolumeROIsFromExtrema.cxx AlgorithmVolumeSmoothing.cxx AlgorithmVolumeTFCE.cxx diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationBox.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationBox.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -44,7 +44,7 @@ * Type for attribute defaults */ AnnotationBox::AnnotationBox(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::BOX, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::BOX, attributeDefaultType) { initializeMembersAnnotationBox(); @@ -63,7 +63,7 @@ * Object that is copied. */ AnnotationBox::AnnotationBox(const AnnotationBox& obj) -: AnnotationTwoDimensionalShape(obj) +: AnnotationOneCoordinateShape(obj) { this->initializeMembersAnnotationBox(); this->copyHelperAnnotationBox(obj); @@ -80,7 +80,7 @@ AnnotationBox::operator=(const AnnotationBox& obj) { if (this != &obj) { - AnnotationTwoDimensionalShape::operator=(obj); + AnnotationOneCoordinateShape::operator=(obj); this->copyHelperAnnotationBox(obj); } return *this; @@ -126,7 +126,7 @@ AnnotationBox::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -148,7 +148,7 @@ AnnotationBox::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationBox.h connectome-workbench-1.5.0/src/Annotations/AnnotationBox.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationBox.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationBox.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,13 +22,13 @@ /*LICENSE_END*/ -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" #include "CaretPointer.h" namespace caret { - class AnnotationBox : public AnnotationTwoDimensionalShape { + class AnnotationBox : public AnnotationOneCoordinateShape { public: AnnotationBox(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationBrowserTab.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationBrowserTab.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationBrowserTab.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationBrowserTab.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,1081 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_BROWSER_TAB_DECLARE__ +#include "AnnotationBrowserTab.h" +#undef __ANNOTATION_BROWSER_TAB_DECLARE__ + +#include + +#include "AnnotationCoordinate.h" +#include "CaretAssert.h" +#include "MathFunctions.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" +#include "TileTabsBrowserTabGeometry.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationBrowserTab + * \brief An annotation box. + * \ingroup Annotations + */ + +/** + * Constructor. + * + * @param attributeDefaultType + * Type for attribute defaults + */ +AnnotationBrowserTab::AnnotationBrowserTab(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) +: AnnotationOneCoordinateShape(AnnotationTypeEnum::BROWSER_TAB, + attributeDefaultType) +{ + initializeMembersAnnotationBrowserTab(); +} + +/** + * Destructor. + */ +AnnotationBrowserTab::~AnnotationBrowserTab() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +AnnotationBrowserTab::AnnotationBrowserTab(const AnnotationBrowserTab& obj) +: AnnotationOneCoordinateShape(obj) +{ + this->initializeMembersAnnotationBrowserTab(); + this->copyHelperAnnotationBrowserTab(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +AnnotationBrowserTab& +AnnotationBrowserTab::operator=(const AnnotationBrowserTab& obj) +{ + if (this != &obj) { + AnnotationOneCoordinateShape::operator=(obj); + this->copyHelperAnnotationBrowserTab(obj); + } + return *this; +} + +/** + * Initialize a new instance of this class. + */ +void +AnnotationBrowserTab::initializeMembersAnnotationBrowserTab() +{ + m_sceneAssistant.grabNew(new SceneClassAssistant()); + + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + + } + + setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +AnnotationBrowserTab::copyHelperAnnotationBrowserTab(const AnnotationBrowserTab& /*obj*/) +{ + /* nothing to copy here */ +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +AnnotationBrowserTab::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, + sceneClass); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +AnnotationBrowserTab::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, + sceneClass); + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + +/** + * Set the browser tab and index that owns this annotation + * + * @param browserTabContent + * Browser tab that owns this annotation + * @param tabIndex + * Index of the browser tab + */ +void +AnnotationBrowserTab::setBrowserTabContent(BrowserTabContent* browserTabContent, + const int32_t tabIndex) +{ + m_browserTabContent = browserTabContent; + m_tabIndex = tabIndex; +} + +/** + * @return The browser tab that owns this annotation + */ +BrowserTabContent* +AnnotationBrowserTab::getBrowserTabContent() +{ + return m_browserTabContent; +} + +/** + * @return The browser tab that owns this annotation (const method) + */ +const BrowserTabContent* +AnnotationBrowserTab::getBrowserTabContent() const +{ + return m_browserTabContent; +} + +/** + * @return Index of browser tab that owns this annotation + */ +int32_t +AnnotationBrowserTab::getTabIndex() const +{ + return m_tabIndex; +} + +/** + * @return Display status of tab + */ +bool +AnnotationBrowserTab::isBrowserTabDisplayed() const +{ + return m_displayStatus; +} + +/** + * Set the display status of this tab + * + * @param status + * New display status for this tab + */ +void +AnnotationBrowserTab::setBrowserTabDisplayed(const bool status) +{ + m_displayStatus = status; +} + +/** + * Set the browser tab annotation dimensions using the tile tabs geometry + * + * @param geometry + * The tile tabs geometry + */ +void +AnnotationBrowserTab::setFromTileTabsGeometry(const TileTabsBrowserTabGeometry* geometry) +{ + CaretAssert(geometry); + setBounds2D(geometry->getMinX(), + geometry->getMaxX(), + geometry->getMinY(), + geometry->getMaxY()); + m_displayStatus = geometry->isDisplayed(); + setStackingOrder(geometry->getStackingOrder()); + m_backgroundType = geometry->getBackgroundType(); +} + +/** + * Load the browser tab annotation dimensions into the tile tabs geometry + * + * @param geometry + * The tile tabs geometry + */ +void +AnnotationBrowserTab::getTileTabsGeometry(TileTabsBrowserTabGeometry* geometryOut) const +{ + CaretAssert(geometryOut); + + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + getBounds2D(minX, maxX, minY, maxY); + + geometryOut->setBounds(minX, maxX, minY, maxY); + geometryOut->setDisplayed(m_displayStatus); + geometryOut->setStackingOrder(getStackingOrder()); + geometryOut->setBackgroundType(m_backgroundType); +} + +/** + * Get the 2D bounds (note: BrowserTab is always aligned with X/Y axes) + * + * @param minX + * Output with minimum X + * @param maxX + * Output with maximum X + * @param minY + * Output with minimum X + * @param maxY + * Output with maximum X + */ +void +AnnotationBrowserTab::getBounds2D(float& minX, + float& maxX, + float& minY, + float& maxY) const +{ + float xyz[3]; + getCoordinate()->getXYZ(xyz); + + minX = xyz[0] - (getWidth() / 2.0); + maxX = minX + getWidth(); + minY = xyz[1] - (getHeight() / 2.0); + maxY = minY + getHeight(); +} + +/** + * Set the 2D bounds (note: BrowserTab is always aligned with X/Y axes) + * + * @param minX + * The minimum X + * @param maxX + * The maximum X + * @param minY + * The minimum X + * @param maxY + * The maximum X + */ +void +AnnotationBrowserTab::setBounds2D(const float minX, + const float maxX, + const float minY, + const float maxY) +{ + const float centerX = (minX + maxX) / 2.0; + const float centerY = (minY + maxY) / 2.0; + const float width = (maxX - minX); + const float height = (maxY - minY); + + getCoordinate()->setXYZ(centerX, centerY, 0.0); + setWidth(width); + setHeight(height); +} + +/** + * @return Type of background (opaque / transparent) for tab + */ +TileTabsLayoutBackgroundTypeEnum::Enum +AnnotationBrowserTab::getBackgroundType() const +{ + return m_backgroundType; +} + +/** + * Set Type of background (opaque / transparent) for tab + * + * @param backgroundType + * New value for Type of background (opaque / transparent) for tab + */ +void +AnnotationBrowserTab::setBackgroundType(const TileTabsLayoutBackgroundTypeEnum::Enum backgroundType) +{ + m_backgroundType = backgroundType; +} + +/** + * @return true if this and the given geometry intersect. + * NOTE: if 'other' is 'this' true is returned (overlaps self) but this + * could change so it is best to avoid testing overlap of self. + * NOTE: Display status is ignored + * + * @param other + * Other geometry for intersection test + * @param windowIndex + * Index of window + */ +bool +AnnotationBrowserTab::intersectionTest(const Annotation* otherAnn, + const int32_t /*windowIndex*/) const +{ + CaretAssert(otherAnn); + const AnnotationBrowserTab* other = dynamic_cast(otherAnn); + if (other == NULL) { + return false; + } + + /* + * Does self overlap + */ + if (this == other) { + return true; + } + + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + getBounds2D(minX, maxX, minY, maxY); + + float otherMinX(0.0), otherMaxX(0.0), otherMinY(0.0), otherMaxY(0.0); + other->getBounds2D(otherMinX, otherMaxX, otherMinY, otherMaxY); + + /* + * Note: Since the geometry is aligned with the X- and Y-axes, + * we only need to test for one to be above or the the right of the other + * + * https://www.geeksforgeeks.org/find-two-rectangles-overlap/ + * https://leetcode.com/articles/rectangle-overlap/ + */ + /* 'this' is on right side of 'other' */ + if (minX >= otherMaxX) { + return false; + } + + /* 'other' is on right side of 'this' */ + if (otherMinX >= maxX) { + return false; + } + + /* 'this' is above 'other */ + if (minY >= otherMaxY) { + return false; + } + + /* 'other' is above 'this' */ + if (otherMinY >= maxY) { + return false; + } + + return true; +} + +/** + * This class is used to find an expandable region + * for a browser tab + */ +class PercentageGrid { +public: + /* + * Constructor with grid size + */ + PercentageGrid(const int32_t minXY, + const int32_t maxXY) + : m_minXY(minXY), + m_maxXY(maxXY), + m_rangeXY((maxXY - minXY) + 1) /* Add 1 since inclusive range */ + { + const int32_t gridSize(m_rangeXY * m_rangeXY); + m_grid.resize(gridSize, 0); + } + + /* + * Test to see of ALL of the grid points with the given bounds + * are off + */ + bool isRangeOff(const int32_t minX, + const int32_t maxX, + const int32_t minY, + const int32_t maxY) const { + for (int32_t i = minX; i <= maxX; i++) { + for (int32_t j = minY; j <= maxY; j++) { + const int32_t offset = (m_rangeXY * j) + i; + CaretAssertVectorIndex(m_grid, offset); + if (m_grid[offset] != 0) { + return false; + } + } + } + return true; + } + + /* + * Test to see of ALL of the grid points with the given bounds + * are off + */ + bool isRangeAllOn(const int32_t minX, + const int32_t maxX, + const int32_t minY, + const int32_t maxY) const { + for (int32_t i = minX; i <= maxX; i++) { + for (int32_t j = minY; j <= maxY; j++) { + const int32_t offset = (m_rangeXY * j) + i; + CaretAssertVectorIndex(m_grid, offset); + if (m_grid[offset] == 0) { + return false; + } + } + } + return true; + } + + /* + * Test to see of ANY of the grid points with the given bounds + * are on + */ + bool isRangeOn(const int32_t minX, + const int32_t maxX, + const int32_t minY, + const int32_t maxY) const { + for (int32_t i = minX; i <= maxX; i++) { + for (int32_t j = minY; j <= maxY; j++) { + const int32_t offset = (m_rangeXY * j) + i; + CaretAssertVectorIndex(m_grid, offset); + if (m_grid[offset] != 0) { + return true; + } + } + } + return false; + } + + /** + * Set grid points on for the given range + */ + void setRange(float minXIn, + float maxXIn, + float minYIn, + float maxYIn) { + /* + * Tile Layout sizes are floating point with one decimal. Grid is integer so need + * to account for any fractional values when converting to integer. + */ + minXIn = std::floor(minXIn); + maxXIn = std::ceil(maxXIn); + minYIn = std::floor(minYIn); + maxYIn = std::ceil(maxYIn); + + const int32_t minX = MathFunctions::limitRange(static_cast(minXIn), m_minXY, m_maxXY); + const int32_t maxX = MathFunctions::limitRange(static_cast(maxXIn), m_minXY, m_maxXY); + const int32_t minY = MathFunctions::limitRange(static_cast(minYIn), m_minXY, m_maxXY); + const int32_t maxY = MathFunctions::limitRange(static_cast(maxYIn), m_minXY, m_maxXY); + + for (int32_t i = minX; i <= maxX; i++) { + for (int32_t j = minY; j <= maxY; j++) { + const int32_t offset = (m_rangeXY * j) + i; + CaretAssertVectorIndex(m_grid, offset); + m_grid[offset] = 1; + } + } + } + + const int32_t m_minXY; + const int32_t m_maxXY; + const int32_t m_rangeXY; + + std::vector m_grid; +}; + +/** + * Expand the given tab using any available space around it + * + * @param browserTabsInWindow + * Tabs in the window + * @param tabToExpand + * Tab that is expanded to fill empty space + * @param boundsOut + * Output with new bounds + * @return + * True if new bounds are valid + */ +bool +AnnotationBrowserTab::shrinkAndExpandToFillEmptySpace(const std::vector& browserTabsInWindow, + const AnnotationBrowserTab* tabToExpand, + std::array& boundsOut) +{ + float origBounds[4]; + tabToExpand->getBounds2D(origBounds[0], origBounds[1], origBounds[2], origBounds[3]); + + /* + * First, try to shrink the tab so that it does not + * overlap any other tabs + */ + std::array shrinkBounds; + shrinkBounds.fill(0.0); + const bool shrinkResultFlag = shrinkTab(browserTabsInWindow, + tabToExpand, + shrinkBounds); + + /* + * Second, try to expand the tab to fill any extra space + */ + std::array expandBounds; + expandBounds.fill(0.0); + const bool expandResultFlag = expandTab(browserTabsInWindow, + tabToExpand, + shrinkBounds, + shrinkResultFlag, + expandBounds); + + bool resultFlag(false); + if (expandResultFlag) { + boundsOut = expandBounds; + resultFlag = true; + } + else if (shrinkResultFlag) { + boundsOut = shrinkBounds; + resultFlag = true; + } + + /* + * Verify that bounds have changed + */ + if (resultFlag) { + resultFlag = false; + for (int32_t i = 0; i < 4; i++) { + if (origBounds[i] != boundsOut[i]) { + resultFlag = true; + break; + } + } + } + + return resultFlag; +} + + +/** + * Expand the given tab using any available space around it + * + * @param browserTabsInWindow + * Tabs in the window + * @param tabToExpand + * Tab that is expanded to fill empty space + * @param boundsOut + * Output with new bounds + * @return + * True if new bounds are valid + */ +bool +AnnotationBrowserTab::expandTab(const std::vector& browserTabsInWindow, + const AnnotationBrowserTab* tabToExpand, + const std::array& boundsIn, + const bool boundsInValidFlag, + std::array& boundsOut) +{ + if (tabToExpand == NULL) { + return false; + } + + /* + * Ensure tab for expansion is not in list of other tabs + */ + std::vector tabs; + for (auto bt : browserTabsInWindow) { + if (bt != tabToExpand) { + tabs.push_back(bt); + } + } + + /* + * If no other tabs, fill window + */ + if (tabs.empty()) { + boundsOut[0] = 0.0; + boundsOut[1] = 100.0; + boundsOut[2] = 0.0; + boundsOut[3] = 100.0; + return true; + } + + /* + * Since the percentage coordinates in the window range [0, 100], + * create a grid that is 101 x 101 (grid will add one in constructor) + */ + PercentageGrid grid(0, 100); + + /* + * Fill in grid points that are overlapped by + * all of the tabs + */ + for (auto t : tabs) { + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + t->getBounds2D(minX, maxX, minY, maxY); + grid.setRange(minX, maxX, minY, maxY); + } + + /* + * Get bounds of tab for expansion and clip to grid + */ + float tabMinX(0.0), tabMaxX(0.0), tabMinY(0.0), tabMaxY(0.0); + tabToExpand->getBounds2D(tabMinX, tabMaxX, tabMinY, tabMaxY); + + /* + * Override tab's bounds if input bounds are valid + */ + if (boundsInValidFlag) { + tabMinX = boundsIn[0]; + tabMaxX = boundsIn[1]; + tabMinY = boundsIn[2]; + tabMaxY = boundsIn[3]; + } + + /* + * Limit the tab to the valid range [0, 100]. + * Optionally shrink around edges. If the maximum of a tab + * is the same as a minimum of this tab, it will detect + * overlap and prevent expansion + */ + const int32_t shrinkFactor(0); + int32_t x1 = MathFunctions::limitRange(static_cast(tabMinX + shrinkFactor), 0, 100); + int32_t x2 = MathFunctions::limitRange(static_cast(tabMaxX - shrinkFactor), 0, 100); + int32_t y1 = MathFunctions::limitRange(static_cast(tabMinY + shrinkFactor), 0, 100); + int32_t y2 = MathFunctions::limitRange(static_cast(tabMaxY - shrinkFactor), 0, 100); + + bool leftDone(x1 <= 0); + bool rightDone(x2 >= 100); + bool bottomDone(y1 <= 0); + bool topDone(y2 >= 100); + + /* + * When expanding an edge, we want to exclude the "corners" since they may overlap + * the edge of another tab. For example, when expanding to the right, the bottom + * may be at Y=50 and the tab below ends at Y=50 and this will prevent expansion to + * the right. So, by excluding these corners, it allows the tab to expand to the right. + */ + const int32_t cornerOffset(1); + + bool done(leftDone && rightDone && bottomDone && topDone); + while ( ! done) { + const int32_t xl(x1); + const int32_t xr(x2); + const int32_t yb(y1); + const int32_t yt(y2); + + const int32_t cornerOffset(1); + + /* + * Try to expand at bottom by one row + */ + if ( ! bottomDone) { + if (grid.isRangeOff(xl + cornerOffset, xr - cornerOffset, yb - 1, yb - 1)) { + y1--; + } + else { + bottomDone = true; + } + } + + /* + * Try to expand at top by one row + */ + if ( ! topDone) { + if (grid.isRangeOff(xl + cornerOffset, xr - cornerOffset, yt + 1, yt + 1)) { + y2++; + } + else { + topDone = true; + } + } + + /* + * Try to expand at left by one column + */ + if ( ! leftDone) { + if (grid.isRangeOff(xl - 1, xl - 1, yb + cornerOffset, yt - cornerOffset)) { + x1--; + } + else { + leftDone = true; + } + } + + /* + * Try to expand at right by one column + */ + if ( ! rightDone) { + if (grid.isRangeOff(xr + 1, xr + 1, yb + cornerOffset, yt - cornerOffset)) { + x2++; + } + else { + rightDone = true; + } + } + + /* + * Test for any sides that no longer are expandable + */ + if (x1 <= 0) { + leftDone = true; + } + if (x2 >= 100) { + rightDone = true; + } + if (y1 <= 0) { + bottomDone = true; + } + if (y2 >= 100) { + topDone = true; + } + + /* + * Done if no expansion available + */ + done = (leftDone && rightDone && bottomDone && topDone); + } + + /* + * When ON, an edge of the tab will expand until it overlaps the edge of the neighboring tab + * When OFF, an edge will move to one row or column from the neighboring tab + */ + const bool allowOverlapFlag(true); + if (allowOverlapFlag) { + /* + * Move each edge to overlap adjacent tab(s) + */ + const int32_t xl(x1); + const int32_t xr(x2); + const int32_t yb(y1); + const int32_t yt(y2); + if (grid.isRangeOff(xl, xl, yb + cornerOffset, yt - cornerOffset)) { /* left */ + x1 = std::max(x1 - 1, 0); + } + if (grid.isRangeOff(xr, xr, yb + cornerOffset, yt - cornerOffset)) { /* right */ + x2 = std::min(x2 + 1, 100); + } + if (grid.isRangeOff(xl + cornerOffset, xr - cornerOffset, yb, yb)) { /* bottom */ + y1 = std::max(y1 - 1, 0); + } + if (grid.isRangeOff(xl + cornerOffset, xr - cornerOffset, yt, yt)) { /* top */ + y2 = std::min(y2 + 1, 100); + } + } + + /* + * If expansion available, report new bounds + */ + if ((x1 < tabMinX) + || (x2 > tabMaxX) + || (y1 < tabMinY) + || (y2 > tabMaxY)) { + boundsOut[0] = x1; + boundsOut[1] = x2; + boundsOut[2] = y1; + boundsOut[3] = y2; + return true; + } + + return false; +} + +/** + * Shrink the given tab until it does not overlay any other tabs + * + * @param browserTabsInWindow + * Tabs in the window + * @param tabToShrink + * Tab that is expanded to fill empty space + * @param boundsOut + * Output with new bounds + * @return + * True if new bounds are valid + */ +bool +AnnotationBrowserTab::shrinkTab(const std::vector& browserTabsInWindow, + const AnnotationBrowserTab* tabToShrink, + std::array& boundsOut) +{ + int32_t bounds[4] { 0, 100, 0, 100 }; + + /* + * Two passes are performed to minimize shrinking of the tab + */ + + /* + * First time, erode when an ENTIRE edge overlaps other tab(s) + */ + if ( ! shrinkTabAux(browserTabsInWindow, tabToShrink, bounds, true, boundsOut)) { + return false; + } + + bounds[0] = boundsOut[0]; + bounds[1] = boundsOut[1]; + bounds[2] = boundsOut[2]; + bounds[3] = boundsOut[3]; + + /* + * Second pass, erode an edge when ANY point in edge overlaps other tab(s) + */ + shrinkTabAux(browserTabsInWindow, tabToShrink, bounds, false, boundsOut); + + return true; +} + +/** + * Shrink the given tab until it does not overlay any other tabs + * + * @param browserTabsInWindow + * Tabs in the window + * @param tabToShrink + * Tab that is expanded to fill empty space + * @param startingBounds + * Starting bounds for tab + * @param testAllOnFlag + * If true test for all voxels on along edge, else any voxel on along edge + * @param boundsOut + * Output with new bounds + * @return + * True if new bounds are valid + */ +bool +AnnotationBrowserTab::shrinkTabAux(const std::vector& browserTabsInWindow, + const AnnotationBrowserTab* tabToShrink, + const int32_t startingBounds[4], + const bool testAllOnFlag, + std::array& boundsOut) +{ + if (tabToShrink == NULL) { + return false; + } + + /* + * Ensure tab for expansion is not in list of other tabs + */ + std::vector tabs; + for (auto bt : browserTabsInWindow) { + if (bt != tabToShrink) { + tabs.push_back(bt); + } + } + + /* + * If no other tabs, do not change siz + */ + if (tabs.empty()) { + return false; + } + + /* + * Since the percentage coordinates in the window range [0, 100], + * create a grid that is 101 x 101 (grid will add one in constructor) + */ + PercentageGrid grid(0, 100); + + /* + * Fill in grid points that are overlapped by + * all of the tabs + */ + for (auto t : tabs) { + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + t->getBounds2D(minX, maxX, minY, maxY); + grid.setRange(minX, maxX, minY, maxY); + } + + /* + * Get bounds of tab for expansion and clip to grid + */ + float tabMinX(0.0), tabMaxX(0.0), tabMinY(0.0), tabMaxY(0.0); + tabToShrink->getBounds2D(tabMinX, tabMaxX, tabMinY, tabMaxY); + + /* + * Shrink by 1 point around edges. If the maximum of a tab + * is the same as a minimum of this tab, it will detect + * overlap and prevent expansion + */ + int32_t x1 = MathFunctions::limitRange(static_cast(tabMinX), startingBounds[0], startingBounds[1]); + int32_t x2 = MathFunctions::limitRange(static_cast(tabMaxX), startingBounds[0], startingBounds[1]); + int32_t y1 = MathFunctions::limitRange(static_cast(tabMinY), startingBounds[2], startingBounds[3]); + int32_t y2 = MathFunctions::limitRange(static_cast(tabMaxY), startingBounds[2], startingBounds[3]); + + bool leftDone(x1 <= 0); + bool rightDone(x2 >= 100); + bool bottomDone(y1 <= 0); + bool topDone(y2 >= 100); + + bool done(leftDone && rightDone && bottomDone && topDone); + while ( ! done) { + const int32_t xl(x1); + const int32_t xr(x2); + const int32_t yb(y1); + const int32_t yt(y2); + + /* + * Try to shrink at bottom by one row + */ + if ( ! bottomDone) { + if (testAllOnFlag) { + if (grid.isRangeAllOn(xl, xr, yb, yb)) { + y1++; + } + else { + bottomDone = true; + } + } + else if (grid.isRangeOn(xl, xr, yb, yb)) { + y1++; + } + else { + bottomDone = true; + } + } + + /* + * Try to shrink at top by one row + */ + if ( ! topDone) { + if (testAllOnFlag) { + if (grid.isRangeAllOn(xl, xr, yt, yt)) { + y2--; + } + else { + topDone = true; + } + } + else if (grid.isRangeOn(xl, xr, yt, yt)) { + y2--; + } + else { + topDone = true; + } + } + + /* + * Try to shrink at left by one column + */ + if ( ! leftDone) { + if (testAllOnFlag) { + if (grid.isRangeAllOn(xl, xl, yb, yt)) { + x1++; + } + else { + leftDone = true; + } + } + else if (grid.isRangeOn(xl, xl, yb, yt)) { + x1++; + } + else { + leftDone = true; + } + } + + /* + * Try to shrink at right by one column + */ + if ( ! rightDone) { + if (testAllOnFlag) { + if (grid.isRangeAllOn(xr, xr, yb, yt)) { + x2--; + } + else { + rightDone = true; + } + } + else if (grid.isRangeOn(xr, xr, yb, yt)) { + x2--; + } + else { + rightDone = true; + } + } + + /* + * Test for any sides that no longer are expandable + */ + if (x1 >= 100) { + leftDone = true; + } + if (x2 <= 0) { + rightDone = true; + } + if (y1 >= 100) { + bottomDone = true; + } + if (y2 <= 0) { + topDone = true; + } + if (x1 >= x2) { + leftDone = true; + rightDone = true; + } + if (y1 >= y2) { + bottomDone = true; + topDone = true; + } + + /* + * Done if no shrinking available + */ + done = (leftDone && rightDone && bottomDone && topDone); + } + + /* + * If tab collapsed to empty, then no changes + */ + if (x1 >= x2) { + return false; + } + if (y1 >= y2) { + return false; + } + + /* + * If shrinking was performed, report new bounds + */ + if ((x1 > tabMinX) + || (x2 < tabMaxX) + || (y1 > tabMinY) + || (y2 < tabMaxY)) { + boundsOut[0] = x1; + boundsOut[1] = x2; + boundsOut[2] = y1; + boundsOut[3] = y2; + return true; + } + + return false; +} diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationBrowserTab.h connectome-workbench-1.5.0/src/Annotations/AnnotationBrowserTab.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationBrowserTab.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationBrowserTab.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,142 @@ +#ifndef __ANNOTATION_BROWSER_TAB_H__ +#define __ANNOTATION_BROWSER_TAB_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "AnnotationOneCoordinateShape.h" +#include "CaretPointer.h" +#include "TileTabsLayoutBackgroundTypeEnum.h" + +namespace caret { + + class BrowserTabContent; + class TileTabsBrowserTabGeometry; + + class AnnotationBrowserTab : public AnnotationOneCoordinateShape { + + public: + AnnotationBrowserTab(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); + + virtual ~AnnotationBrowserTab(); + + AnnotationBrowserTab(const AnnotationBrowserTab& obj); + + AnnotationBrowserTab& operator=(const AnnotationBrowserTab& obj); + + void setFromTileTabsGeometry(const TileTabsBrowserTabGeometry* geometry); + + void getTileTabsGeometry(TileTabsBrowserTabGeometry* geometryOut) const; + + void setBrowserTabContent(BrowserTabContent* browserTabContent, + const int32_t tabIndex); + + BrowserTabContent* getBrowserTabContent(); + + const BrowserTabContent* getBrowserTabContent() const; + + bool isBrowserTabDisplayed() const; + + void setBrowserTabDisplayed(const bool status); + + int32_t getTabIndex() const; + + TileTabsLayoutBackgroundTypeEnum::Enum getBackgroundType() const; + + void setBackgroundType(const TileTabsLayoutBackgroundTypeEnum::Enum backgroundType); + + virtual bool intersectionTest(const Annotation* other, + const int32_t windowIndex) const override; + + void getBounds2D(float& minX, + float& maxX, + float& minY, + float& maxY) const; + + void setBounds2D(const float minX, + const float maxX, + const float minY, + const float maxY); + + static bool shrinkAndExpandToFillEmptySpace(const std::vector& browserTabsInWindow, + const AnnotationBrowserTab* tabToExpand, + std::array& boundsOut); + + // ADD_NEW_METHODS_HERE + + + + + + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private: + void copyHelperAnnotationBrowserTab(const AnnotationBrowserTab& obj); + + void initializeMembersAnnotationBrowserTab(); + + static bool expandTab(const std::vector& browserTabsInWindow, + const AnnotationBrowserTab* tabToExpand, + const std::array& boundsIn, + const bool boundsInValidFlag, + std::array& boundsOut); + + static bool shrinkTab(const std::vector& browserTabsInWindow, + const AnnotationBrowserTab* tabToShrink, + std::array& boundsOut); + + static bool shrinkTabAux(const std::vector& browserTabsInWindow, + const AnnotationBrowserTab* tabToShrink, + const int32_t startingBounds[4], + const bool testAllOnFlag, + std::array& boundsOut); + + CaretPointer m_sceneAssistant; + + /** Browser tab that owns this annotation DO NOT DELETE */ + BrowserTabContent* m_browserTabContent = NULL; + + /** Display status of browser tab */ + bool m_displayStatus = true; + + /** Index of browser tab that owns this annotation */ + int32_t m_tabIndex = -1; + + /** Type of background (opaque / transparent) for tab*/ + TileTabsLayoutBackgroundTypeEnum::Enum m_backgroundType = TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __ANNOTATION_BROWSER_TAB_DECLARE__ + // +#endif // __ANNOTATION_BROWSER_TAB_DECLARE__ + +} // namespace +#endif //__ANNOTATION_BROWSER_TAB_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationColorBar.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationColorBar.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationColorBar.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationColorBar.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -47,7 +47,7 @@ * Type for attribute defaults */ AnnotationColorBar::AnnotationColorBar(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::COLOR_BAR, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::COLOR_BAR, attributeDefaultType), AnnotationFontAttributesInterface() { @@ -89,7 +89,7 @@ * Object that is copied. */ AnnotationColorBar::AnnotationColorBar(const AnnotationColorBar& obj) -: AnnotationTwoDimensionalShape(obj), +: AnnotationOneCoordinateShape(obj), AnnotationFontAttributesInterface() { this->copyHelperAnnotationColorBar(obj); @@ -106,7 +106,7 @@ AnnotationColorBar::operator=(const AnnotationColorBar& obj) { if (this != &obj) { - AnnotationTwoDimensionalShape::operator=(obj); + AnnotationOneCoordinateShape::operator=(obj); this->copyHelperAnnotationColorBar(obj); } return *this; @@ -537,7 +537,7 @@ AnnotationColorBar::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -559,7 +559,7 @@ AnnotationColorBar::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationColorBar.h connectome-workbench-1.5.0/src/Annotations/AnnotationColorBar.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationColorBar.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationColorBar.h 2021-02-16 19:46:47.000000000 +0000 @@ -24,14 +24,14 @@ #include "AnnotationColorBarPositionModeEnum.h" #include "AnnotationFontAttributesInterface.h" #include "AnnotationTextAlignHorizontalEnum.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" namespace caret { class AnnotationColorBarSection; class AnnotationColorBarNumericText; - class AnnotationColorBar : public AnnotationTwoDimensionalShape, public AnnotationFontAttributesInterface { + class AnnotationColorBar : public AnnotationOneCoordinateShape, public AnnotationFontAttributesInterface { public: AnnotationColorBar(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); diff -Nru connectome-workbench-1.4.2/src/Annotations/Annotation.cxx connectome-workbench-1.5.0/src/Annotations/Annotation.cxx --- connectome-workbench-1.4.2/src/Annotations/Annotation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/Annotation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -24,6 +24,7 @@ #undef __ANNOTATION_DECLARE__ #include "AnnotationBox.h" +#include "AnnotationBrowserTab.h" #include "AnnotationColorBar.h" #include "AnnotationCoordinate.h" #include "AnnotationGroup.h" @@ -32,6 +33,8 @@ #include "AnnotationOval.h" #include "AnnotationPercentSizeText.h" #include "AnnotationPointSizeText.h" +#include "AnnotationPolyLine.h" +#include "AnnotationScaleBar.h" #include "AnnotationText.h" #include "BrainConstants.h" #include "CaretAssert.h" @@ -144,6 +147,8 @@ m_customColorLine[2] = obj.m_customColorLine[2]; m_customColorLine[3] = obj.m_customColorLine[3]; + m_stackingOrder = obj.m_stackingOrder; + m_properties = obj.m_properties; *m_displayGroupAndTabItemHelper = *obj.m_displayGroupAndTabItemHelper; @@ -179,6 +184,13 @@ myClone = new AnnotationBox(*box); } break; + case AnnotationTypeEnum::BROWSER_TAB: + { + const AnnotationBrowserTab* browserTab = dynamic_cast(this); + CaretAssert(browserTab); + myClone = new AnnotationBrowserTab(*browserTab); + } + break; case AnnotationTypeEnum::COLOR_BAR: { const AnnotationColorBar* colorBar = dynamic_cast(this); @@ -207,6 +219,20 @@ myClone = new AnnotationOval(*oval); } break; + case AnnotationTypeEnum::POLY_LINE: + { + const AnnotationPolyLine* polyLine = dynamic_cast(this); + CaretAssert(polyLine); + myClone = new AnnotationPolyLine(*polyLine); + } + break; + case AnnotationTypeEnum::SCALE_BAR: + { + const AnnotationScaleBar* scaleBar = dynamic_cast(this); + CaretAssert(scaleBar); + myClone = new AnnotationScaleBar(*scaleBar); + } + break; case AnnotationTypeEnum::TEXT: { const AnnotationText* text = dynamic_cast(this); @@ -348,6 +374,9 @@ case AnnotationTypeEnum::BOX: annotation = new AnnotationBox(attributeDefaultType); break; + case AnnotationTypeEnum::BROWSER_TAB: + annotation = new AnnotationBrowserTab(attributeDefaultType); + break; case AnnotationTypeEnum::COLOR_BAR: annotation = new AnnotationColorBar(attributeDefaultType); break; @@ -360,6 +389,12 @@ case AnnotationTypeEnum::OVAL: annotation = new AnnotationOval(attributeDefaultType); break; + case AnnotationTypeEnum::POLY_LINE: + annotation = new AnnotationPolyLine(attributeDefaultType); + break; + case AnnotationTypeEnum::SCALE_BAR: + annotation = new AnnotationScaleBar(attributeDefaultType); + break; case AnnotationTypeEnum::TEXT: annotation = new AnnotationPercentSizeText(attributeDefaultType); break; @@ -418,6 +453,9 @@ switch (m_type) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + m_colorBackground = CaretColorEnum::NONE; + break; case AnnotationTypeEnum::COLOR_BAR: m_colorBackground = CaretColorEnum::BLACK; break; @@ -427,6 +465,11 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + m_colorBackground = CaretColorEnum::BLACK; + break; case AnnotationTypeEnum::TEXT: m_colorBackground = CaretColorEnum::NONE; m_colorLine = CaretColorEnum::NONE; @@ -470,6 +513,9 @@ m_colorBackground = defaultColor; } break; + case AnnotationTypeEnum::BROWSER_TAB: + m_colorBackground = CaretColorEnum::NONE; + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -484,6 +530,13 @@ m_colorBackground = defaultColor; } break; + case AnnotationTypeEnum::POLY_LINE: + if (m_colorLine == CaretColorEnum::NONE) { + m_colorLine = defaultColor; + } + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: m_colorLine = s_userDefaultForTextColorLine; m_customColorLine[0] = s_userDefaultForTextCustomColorLine[0]; @@ -515,6 +568,9 @@ switch (m_type) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + disallowLineColorNoneFlag = true; + break; case AnnotationTypeEnum::COLOR_BAR: disallowLineColorNoneFlag = true; break; @@ -525,6 +581,12 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + disallowLineColorNoneFlag = true; + break; + case AnnotationTypeEnum::SCALE_BAR: + disallowLineColorNoneFlag = true; + break; case AnnotationTypeEnum::TEXT: break; } @@ -581,6 +643,9 @@ m_sceneAssistant->add("m_colorLine", &m_colorLine); m_sceneAssistant->addArray("m_customColorLine", m_customColorLine, 4, 0.0); + + m_sceneAssistant->add("m_stackingOrder", + &m_stackingOrder); } } @@ -623,6 +688,8 @@ switch (m_type) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -631,6 +698,10 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: { const AnnotationText* textAnn = dynamic_cast(this); @@ -684,6 +755,103 @@ } /** + * @return True if the given annotation is in the same coordinate space as this + * annotation. For spacer, tab, and window they must have the same indices. For + * surface space structure and number of vertices must match. + * @param annotation + * Annotation for comparison. + */ +bool +Annotation::isInSameCoordinateSpace(const Annotation* annotation) const +{ + CaretAssert(annotation); + + if (getCoordinateSpace() != annotation->getCoordinateSpace()) { + return false; + } + + bool sameSpaceFlag(false); + + switch (annotation->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + sameSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (getSpacerTabIndex() == annotation->getSpacerTabIndex()) { + sameSpaceFlag = true; + } + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + sameSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + { + StructureEnum::Enum myStructure = StructureEnum::INVALID; + int32_t myNumberOfVertices(-1); + const AnnotationTwoCoordinateShape* oneDimAnn = castToTwoCoordinateShape(); + if (oneDimAnn != NULL) { + int32_t vertexIndex(-1); + oneDimAnn->getStartCoordinate()->getSurfaceSpace(myStructure, + myNumberOfVertices, + vertexIndex); + } + else { + const AnnotationOneCoordinateShape* twoDimAnn = castToOneCoordinateShape(); + if (twoDimAnn != NULL) { + int32_t vertexIndex(-1); + twoDimAnn->getCoordinate()->getSurfaceSpace(myStructure, + myNumberOfVertices, + vertexIndex); + } + } + + StructureEnum::Enum otherStructure = StructureEnum::INVALID; + int32_t otherSurfaceNumberOfVertices(-1); + const AnnotationTwoCoordinateShape* otherOneDimAnn = annotation->castToTwoCoordinateShape(); + if (otherOneDimAnn != NULL) { + int32_t vertexIndex(-1); + otherOneDimAnn->getStartCoordinate()->getSurfaceSpace(otherStructure, + otherSurfaceNumberOfVertices, + vertexIndex); + } + else { + const AnnotationOneCoordinateShape* otherTwoDimAnn = annotation->castToOneCoordinateShape(); + if (otherTwoDimAnn != NULL) { + int32_t vertexIndex(-1); + otherTwoDimAnn->getCoordinate()->getSurfaceSpace(otherStructure, + otherSurfaceNumberOfVertices, + vertexIndex); + } + } + + if (myNumberOfVertices > 0) { + if ((myStructure == otherStructure) + && (myNumberOfVertices == otherSurfaceNumberOfVertices)) { + sameSpaceFlag = true; + } + } + } + break; + case AnnotationCoordinateSpaceEnum::TAB: + if (getTabIndex() == annotation->getTabIndex()) { + sameSpaceFlag = true; + } + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + sameSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + if (getWindowIndex() == annotation->getWindowIndex()) { + sameSpaceFlag = true; + } + break; + } + + return sameSpaceFlag; +} + + +/** * @return Is this annotation in surface coordinate space * with tangent selected for the surface offset vector? */ @@ -729,13 +897,13 @@ { std::vector coords; if (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { - AnnotationOneDimensionalShape* oneDimAnn = castToOneDimensionalShape(); + AnnotationTwoCoordinateShape* oneDimAnn = castToTwoCoordinateShape(); if (oneDimAnn != NULL) { coords.push_back(oneDimAnn->getStartCoordinate()); coords.push_back(oneDimAnn->getEndCoordinate()); } else { - AnnotationTwoDimensionalShape* twoDimAnn = castToTwoDimensionalShape(); + AnnotationOneCoordinateShape* twoDimAnn = castToOneCoordinateShape(); if (twoDimAnn != NULL) { coords.push_back(twoDimAnn->getCoordinate()); } @@ -783,7 +951,7 @@ { float angleOut(0.0); - const AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(this); + const AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(this); if (twoDimAnn != NULL) { if (isInSurfaceSpaceWithTangentOffset()) { enum class OrientationType { @@ -922,7 +1090,7 @@ return; - AnnotationTwoDimensionalShape* twoDimAnn = castToTwoDimensionalShape(); + AnnotationOneCoordinateShape* twoDimAnn = castToOneCoordinateShape(); if (twoDimAnn != NULL) { if (isInSurfaceSpaceWithTangentOffset()) { const float angle = getSurfaceSpaceWithTangentOffsetRotation(structure, @@ -932,129 +1100,6 @@ } return; - - - - - -// AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(this); -// if (twoDimAnn != NULL) { -// if (isInSurfaceSpaceWithTangentOffset()) { -// enum class OrientationType { -// LEFT_TO_RIGHT = 0, -// RIGHT_TO_LEFT = 1, -// POSTERIOR_TO_ANTERIOR = 2, -// ANTERIOR_TO_POSTERIOR = 3, -// INFERIOR_TO_SUPERIOR = 4, -// SUPERIOR_TO_INFERIOR = 5 -// }; -// -// const OrientationType orientations[6] = { -// OrientationType::LEFT_TO_RIGHT, -// OrientationType::RIGHT_TO_LEFT, -// OrientationType::POSTERIOR_TO_ANTERIOR, -// OrientationType::ANTERIOR_TO_POSTERIOR, -// OrientationType::INFERIOR_TO_SUPERIOR, -// OrientationType::SUPERIOR_TO_INFERIOR -// }; -// const float orientationVectors[6][3] { -// { 1.0, 0.0, 0.0 }, -// { -1.0, 0.0, 0.0 }, -// { 0.0, 1.0, 0.0 }, -// { 0.0, -1.0, 0.0 }, -// { 0.0, 0.0, 1.0 }, -// { 0.0, 0.0, -1.0 } -// }; -// -// -// /* -// * Find orientation that aligns with the vertex's normal vector -// */ -// OrientationType matchingOrientation = OrientationType::LEFT_TO_RIGHT; -// float matchingAngle = 10000.0f; -// for (int32_t i = 0; i < 6; i++) { -// const float angle = MathFunctions::angleInDegreesBetweenVectors(orientationVectors[i], -// vertexNormal); -// if (angle < matchingAngle) { -// matchingAngle = angle; -// matchingOrientation = orientations[i]; -// } -// } -// -// float surfaceUpAxisVector[3] = { 0.0f, 0.0f, 1.0f }; -// switch (matchingOrientation) { -// case OrientationType::LEFT_TO_RIGHT: -// break; -// case OrientationType::RIGHT_TO_LEFT: -// break; -// case OrientationType::POSTERIOR_TO_ANTERIOR: -// break; -// case OrientationType::ANTERIOR_TO_POSTERIOR: -// break; -// case OrientationType::INFERIOR_TO_SUPERIOR: -// surfaceUpAxisVector[0] = 1.0; -// surfaceUpAxisVector[0] = 0.0; -// surfaceUpAxisVector[0] = 0.0; -// break; -// case OrientationType::SUPERIOR_TO_INFERIOR: -// surfaceUpAxisVector[0] = -1.0; -// surfaceUpAxisVector[0] = 0.0; -// surfaceUpAxisVector[0] = 0.0; -// break; -// } -// -// /* -// * Vector for annotation's Y (vector from bottom to top of annotation) -// */ -// const float annotationUpYVector[3] { -// 0.0, -// 1.0, -// 0.0 -// }; -// -// /* -// * Initialize the rotation angle so that the annotation's vertical axis -// * is aligned with the screen vertical axis when the surface is in the -// * analogous surface view. For a text annotation, the text should be -// * flowing left to right across screen. -// */ -// Matrix4x4 rotationMatrix; -// rotationMatrix.setMatrixToOpenGLRotationFromVector(vertexNormal); -// Matrix4x4 inverseMatrix(rotationMatrix); -// inverseMatrix.invert(); -// inverseMatrix.multiplyPoint3(surfaceUpAxisVector); -// const float alignRotationAngle = MathFunctions::angleInDegreesBetweenVectors(annotationUpYVector, -// surfaceUpAxisVector); -// float rotationAngle = alignRotationAngle; -// switch (matchingOrientation) { -// case OrientationType::LEFT_TO_RIGHT: -// rotationAngle = 360.0 - rotationAngle; -// break; -// case OrientationType::RIGHT_TO_LEFT: -// break; -// case OrientationType::POSTERIOR_TO_ANTERIOR: -// if (StructureEnum::isRight(structure)) { -// rotationAngle = 360.0 - rotationAngle; -// } -// break; -// case OrientationType::ANTERIOR_TO_POSTERIOR: -// if (StructureEnum::isRight(structure)) { -// rotationAngle = 360.0 - rotationAngle; -// } -// break; -// case OrientationType::INFERIOR_TO_SUPERIOR: -// if (StructureEnum::isRight(structure)) { -// rotationAngle += 180.0; -// } -// break; -// case OrientationType::SUPERIOR_TO_INFERIOR: -// rotationAngle += 90.0; -// break; -// } -// -// twoDimAnn->setRotationAngle(rotationAngle); -// } -// } } /** @@ -1575,13 +1620,20 @@ CaretAssert(m_properties.size() >= static_cast::type>(Property::COUNT_FOR_BITSET)); m_properties.set(); + bool browserTabFlag(false); bool colorBarFlag = false; bool fillColorFlag = true; bool lineArrowsFlag = false; + bool polyLineFlag(false); + bool scaleBarFlag = false; bool textFlag = false; switch (m_type) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + browserTabFlag = true; + fillColorFlag = false; + break; case AnnotationTypeEnum::COLOR_BAR: colorBarFlag = true; break; @@ -1594,6 +1646,13 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + fillColorFlag = false; + polyLineFlag = true; + break; + case AnnotationTypeEnum::SCALE_BAR: + scaleBarFlag = true; + break; case AnnotationTypeEnum::TEXT: textFlag = true; break; @@ -1603,9 +1662,9 @@ setProperty(Property::LINE_ARROWS, lineArrowsFlag); setProperty(Property::TEXT_ALIGNMENT, textFlag); setProperty(Property::TEXT_EDIT, textFlag); - setProperty(Property::TEXT_COLOR, colorBarFlag | textFlag); - setProperty(Property::TEXT_FONT_NAME, colorBarFlag | textFlag); - setProperty(Property::TEXT_FONT_SIZE, colorBarFlag | textFlag); + setProperty(Property::TEXT_COLOR, colorBarFlag | scaleBarFlag | textFlag); + setProperty(Property::TEXT_FONT_NAME, colorBarFlag | scaleBarFlag | textFlag); + setProperty(Property::TEXT_FONT_SIZE, colorBarFlag | scaleBarFlag | textFlag); setProperty(Property::TEXT_FONT_STYLE, textFlag); setProperty(Property::TEXT_ORIENTATION, textFlag); @@ -1617,6 +1676,17 @@ resetProperty(Property::INVALID); resetProperty(Property::COUNT_FOR_BITSET); + if (browserTabFlag) { + resetProperty(Property::COPY_CUT_PASTE); + resetProperty(Property::DELETION); + resetProperty(Property::DISPLAY_GROUP); + resetProperty(Property::GROUP); + resetProperty(Property::LINE_COLOR); + resetProperty(Property::LINE_THICKNESS); + resetProperty(Property::TEXT_COLOR); + resetProperty(Property::TEXT_EDIT); + } + if (colorBarFlag) { resetProperty(Property::ARRANGE); resetProperty(Property::COPY_CUT_PASTE); @@ -1629,6 +1699,22 @@ setProperty(Property::SCENE_CONTAINS_ATTRIBUTES); } + + if (polyLineFlag) { + /* Disables cut/copy for polyline until that functionality can be implemeted */ + resetProperty(Property::COPY_CUT_PASTE); + } + + if (scaleBarFlag) { + resetProperty(Property::ARRANGE); + resetProperty(Property::COPY_CUT_PASTE); + resetProperty(Property::DELETION); + resetProperty(Property::DISPLAY_GROUP); + resetProperty(Property::GROUP); + resetProperty(Property::TEXT_EDIT); + + setProperty(Property::SCENE_CONTAINS_ATTRIBUTES); + } } /** @@ -1912,6 +1998,8 @@ switch (m_type) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -1920,6 +2008,10 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: { const AnnotationText* textAnn = dynamic_cast(this); @@ -1975,7 +2067,7 @@ * the drawn status set. */ bool -Annotation::isDrawnInWindowStatus(const int32_t windowIndex) +Annotation::isDrawnInWindowStatus(const int32_t windowIndex) const { CaretAssertArrayIndex(m_drawnInWindowStatus, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); return m_drawnInWindowStatus[windowIndex]; @@ -2003,6 +2095,28 @@ } } +/** + * @return Stacking order (depth in screen) of tab, greater value is 'in front' + */ +int32_t +Annotation::getStackingOrder() const +{ + return m_stackingOrder; +} + +/** + * Set Stacking order (depth in screen) of tab, greater value is 'in front' + * + * @param stackingOrder + * New value for Stacking order (depth in screen) of tab, greater value is 'in front' + */ +void +Annotation::setStackingOrder(const int32_t stackingOrder) +{ + m_stackingOrder = stackingOrder; + setModified(); +} + /** * @return The annotation's selected for editing status. @@ -2638,3 +2752,185 @@ s_userDefaultLineWidthPercentage = lineWidthPercentage; } +/** + * @return true if this and the given annotation intersect using bounding box from when drawn in the given window + * NOTE: if 'other' is 'this' true is returned (overlaps self) but this + * could change so it is best to avoid testing overlap of self. + * NOTE: Display status is ignored + * + * @param other + * Other annotation for intersection test + * @param windowIndex + * Index of window + */ +bool +Annotation::intersectionTest(const Annotation* other, + const int32_t windowIndex) const +{ + if ( ! isInSameCoordinateSpace(other)) { + return false; + } + + if (isDrawnInWindowStatus(windowIndex) + && other->isDrawnInWindowStatus(windowIndex)) { + if (m_boundsFromDrawing[windowIndex].intersectsXY(other->m_boundsFromDrawing[windowIndex])) { + return true; + } + } + + return false; +} + +/** + * Resize the annotation so that it is the same size, in pixels, in the new viewport. + * @param oldViewport + * Current viewport of annotation + * @param newViewport + * New viewport for annotation + * @param matchPositionFlag + * If true, try to match position, but may require moving the annotion + * @param matchSizeFlag + * If true, match the size of the annotaiton + */ +void +Annotation::matchPixelPositionAndSizeInNewViewport(const int32_t oldViewport[4], + const int32_t newViewport[4], + const bool matchPositionFlag, + const bool matchSizeFlag) +{ + const float newViewportWidth(newViewport[2]); + if (newViewportWidth <= 0.0) { + return; + } + const float newViewportHeight(newViewport[3]); + if (newViewportHeight <= 0.0) { + return; + } + + bool tabOrWindowFlag(false); + switch (getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + tabOrWindowFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + tabOrWindowFlag = true; + break; + } + + if ( ! tabOrWindowFlag) { + return; + } + + AnnotationTwoCoordinateShape* oneDimAnn = castToTwoCoordinateShape(); + if (oneDimAnn != NULL) { + const AnnotationCoordinate* coordOne = oneDimAnn->getStartCoordinate(); + CaretAssert(coordOne); + const AnnotationCoordinate* coordTwo = oneDimAnn->getEndCoordinate(); + CaretAssert(coordTwo); + const float length = MathFunctions::distance3D(coordOne->getXYZ(), + coordTwo->getXYZ()); + if (length > 0.0) { + } + CaretLogWarning("Matching size not supported for one-dimensional annotations"); + } + else { + AnnotationOneCoordinateShape* twoDimAnn = castToOneCoordinateShape(); + if (twoDimAnn) { + if (twoDimAnn->isFixedAspectRatio()) { + CaretLogWarning("Matching size not supported for fixed aspect ratio two-dimensional annotations"); + } + else { + const float oldViewportWidth(oldViewport[2]); + const float oldViewportHeight(oldViewport[3]); + const float widthPixels((twoDimAnn->getWidth() / 100.0) * oldViewportWidth); + const float heightPixels((twoDimAnn->getHeight() / 100.0) * oldViewportHeight); + const float newWidthPct((widthPixels / newViewportWidth) * 100.0); + const float newHeightPct((heightPixels / newViewportHeight) * 100.0); + if (matchSizeFlag) { + twoDimAnn->setWidth(newWidthPct); + twoDimAnn->setHeight(newHeightPct); + } + + if (matchPositionFlag) { + float oldXYZ[3]; + twoDimAnn->getCoordinate()->getXYZ(oldXYZ); + const float oldPixelX(((oldXYZ[0] / 100.0) * oldViewportWidth) + oldViewport[0]); + const float oldPixelY(((oldXYZ[1] / 100.0) * oldViewportHeight) + oldViewport[1]); + const float newPixelX(oldPixelX - newViewport[0]); + const float newPixelY(oldPixelY - newViewport[1]); + float newPctX((newPixelX / newViewportWidth) * 100.0); + if (newPctX <= 0.0) { + newPctX = std::min((twoDimAnn->getWidth() / 2.0), 99.0); + } + else if (newPctX >= 100.0) { + newPctX = std::max((100.0 - (twoDimAnn->getWidth() / 2.0)), 1.0); + } + float newPctY((newPixelY / newViewportHeight) * 100.0); + if (newPctY <= 0.0) { + newPctY = std::min((twoDimAnn->getHeight() / 2.0), 99.0); + } + else if (newPctY >= 100.0) { + newPctY = std::max((100.0 - (twoDimAnn->getHeight() / 2.0)), 1.0); + } + twoDimAnn->getCoordinate()->setXYZ(newPctX, + newPctY, + oldXYZ[2]); + } + + AnnotationColorBar* colorBar = dynamic_cast(twoDimAnn); + if (colorBar != NULL) { + const float oldFontHeightPixels((colorBar->getFontPercentViewportSize() / 100.0) * oldViewport[3]); + const float newFontHeightPct((oldFontHeightPixels / newViewportHeight) * 100.0); + if (matchSizeFlag) { + colorBar->setFontPercentViewportSize(newFontHeightPct); + } + } + } + } + else { + CaretAssertMessage(0, "Annotation is neither one- nor two-dimensional"); + } + } +} + +/** + * Set the bounds from last time annotation was drawn + * @param windowIndex + * Index of window + * @param bounds + * The bounds parallel to screen axes (elements are min-x, max-x, min-y, max-y, min-z, max-z) + */ +void +Annotation::setDrawnInWindowBounds(const int32_t windowIndex, + const BoundingBox& bounds) const +{ + CaretAssert((windowIndex >= 0) + && (windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); + m_boundsFromDrawing[windowIndex] = bounds; +} + +/** + * Get the bounds from last time annotation was drawn + * @param windowIndex + * Index of window + * @@return + * The bounds parallel to screen axes (elements are min-x, max-x, min-y, max-y, average Z) + */ +BoundingBox +Annotation::getDrawnInWindowBounds(const int32_t windowIndex) const +{ + CaretAssert((windowIndex >= 0) + && (windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); + return m_boundsFromDrawing[windowIndex]; +} + diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationGroup.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationGroup.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationGroup.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationGroup.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -1059,6 +1059,67 @@ } /** + * Copy the selections for annotations that are displayed in more than one + * tab. This includes chart, stereotaxic, and surface annotations. + * + * @param sourceTabIndex + * Index of source tab (copy "from") + * @param targetTabIndex + * Index of target tab (copy "to") + */ +void +AnnotationGroup::copySelections(const int32_t sourceTabIndex, + const int32_t targetTabIndex) +{ + bool supportedSpaceFlag(false); + switch (m_coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + supportedSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + supportedSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + supportedSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + + if ( ! supportedSpaceFlag) { + return; + } + + /* + * Note: We DO NOT copy the selection status for the group. + * When getItemDisplaySelected() is called, it will determine + * the selection status based (on, partial, off) based upon + * the selection status of the annotations in the group. + */ + setItemExpanded(DisplayGroupEnum::DISPLAY_GROUP_TAB, + targetTabIndex, + isItemExpanded(DisplayGroupEnum::DISPLAY_GROUP_TAB, + sourceTabIndex)); + + for (auto ann : m_annotations) { + ann->setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, + targetTabIndex, + ann->getItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, + sourceTabIndex)); + ann->setItemExpanded(DisplayGroupEnum::DISPLAY_GROUP_TAB, + targetTabIndex, + ann->isItemExpanded(DisplayGroupEnum::DISPLAY_GROUP_TAB, + sourceTabIndex)); + } +} + +/** * Update the tab index to correspond to the tab index used for this * annotation group if it is in tab annotation space. This functionality * was added to resolve WB-831. diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationGroup.h connectome-workbench-1.5.0/src/Annotations/AnnotationGroup.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationGroup.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationGroup.h 2021-02-16 19:46:47.000000000 +0000 @@ -129,6 +129,18 @@ const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status); + /** + * Copy the selections from one tab to another tab. + * Also copies selections in all descendants. + * + * @param sourceTabIndex + * Index of source tab (copy "from") + * @param targetTabIndex + * Index of target tab (copy "to") + */ + void copySelections(const int32_t sourceTabIndex, + const int32_t targetTabIndex); + virtual bool isItemSelectedForEditingInWindow(const int32_t windowIndex); diff -Nru connectome-workbench-1.4.2/src/Annotations/Annotation.h connectome-workbench-1.5.0/src/Annotations/Annotation.h --- connectome-workbench-1.4.2/src/Annotations/Annotation.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/Annotation.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,7 +21,9 @@ */ /*LICENSE_END*/ +#include #include +#include #include "AnnotationAttributesDefaultTypeEnum.h" #include "AnnotationCoordinateSpaceEnum.h" @@ -29,6 +31,7 @@ #include "AnnotationSizingHandleTypeEnum.h" #include "AnnotationSurfaceOffsetVectorTypeEnum.h" #include "AnnotationTypeEnum.h" +#include "BoundingBox.h" #include "CaretColorEnum.h" #include "CaretObjectTracksModification.h" #include "DisplayGroupAndTabItemInterface.h" @@ -38,9 +41,12 @@ namespace caret { - class AnnotationOneDimensionalShape; - class AnnotationTwoDimensionalShape; + class AnnotationScaleBar; + + class AnnotationMultiCoordinateShape; + class AnnotationOneCoordinateShape; class AnnotationSpatialModification; + class AnnotationTwoCoordinateShape; class DisplayGroupAndTabItemHelper; class SceneClassAssistant; @@ -124,14 +130,48 @@ Annotation* clone() const; - virtual AnnotationOneDimensionalShape* castToOneDimensionalShape() = 0; + /** + * @return Cast to multi-coordinate (NULL if NOT multi-coordinate annotation + */ + virtual AnnotationMultiCoordinateShape* castToMultiCoordinateShape() { return NULL; } - virtual const AnnotationOneDimensionalShape* castToOneDimensionalShape() const = 0; - - virtual AnnotationTwoDimensionalShape* castToTwoDimensionalShape() = 0; + /** + * @return Cast to multi-coordinate (NULL if NOT multi-coordinate annotation + */ + virtual const AnnotationMultiCoordinateShape* castToMultiCoordinateShape() const { return NULL; } - virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const = 0; + /** + * @return Cast to one-coordinate (NULL if NOT one-coordinate annotation + */ + virtual AnnotationOneCoordinateShape* castToOneCoordinateShape() { return NULL; } + + /** + * @return Cast to one-coordinate (NULL if NOT one-coordinate annotation + */ + virtual const AnnotationOneCoordinateShape* castToOneCoordinateShape() const { return NULL; } + /** + * @return Cast to two-coordinate (NULL if NOT two-coordinate annotation + */ + virtual AnnotationTwoCoordinateShape* castToTwoCoordinateShape() { return NULL; } + + /** + * @return Cast to two-coordinate (NULL if NOT two-coordinate annotation + */ + virtual const AnnotationTwoCoordinateShape* castToTwoCoordinateShape() const { return NULL; } + + /** + * @return this annotation cast to AnnotationScaleBar (NULL if not a scale bar) + * Intended for overriding by the annotation type + */ + virtual AnnotationScaleBar* castToScaleBar() { return NULL; } + + /** + * @return this annotation cast to AnnotationScaleBar (NULL if not a scale bar) const method + * Intended for overriding by the annotation type + */ + virtual const AnnotationScaleBar* castToScaleBar() const { return NULL; } + bool testProperty(const Property property) const; bool testPropertiesAny(const Property propertyOne, @@ -168,6 +208,8 @@ void setCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordinateSpace); + bool isInSameCoordinateSpace(const Annotation* annotation) const; + virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const = 0; bool isInSurfaceSpaceWithTangentOffset() const; @@ -254,6 +296,10 @@ virtual float getFixedAspectRatio() const; + int32_t getStackingOrder() const; + + void setStackingOrder(const int32_t stackingOrder); + /** * Apply a spatial modification to an annotation. * @@ -340,8 +386,21 @@ virtual bool isItemSelectedForEditingInWindow(const int32_t windowIndex); void setDrawnInWindowStatus(const int32_t windowIndex); - - protected: + + void setDrawnInWindowBounds(const int32_t windowIndex, + const BoundingBox& bounds) const; + + BoundingBox getDrawnInWindowBounds(const int32_t windowIndex) const; + + virtual bool intersectionTest(const Annotation* other, + const int32_t windowIndex) const; + + void matchPixelPositionAndSizeInNewViewport(const int32_t oldViewport[4], + const int32_t newViewport[4], + const bool matchPositionFlag, + const bool matchSizeFlag); + + protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) = 0; @@ -369,7 +428,7 @@ void textAnnotationResetName(); - bool isDrawnInWindowStatus(const int32_t windowIndex); + bool isDrawnInWindowStatus(const int32_t windowIndex) const; void clearDrawnInWindowStatusForAllWindows(); @@ -421,8 +480,14 @@ AnnotationGroupKey m_annotationGroupKey; + /** Stacking order (depth in screen) of tab, greater value is 'in front'*/ + int32_t m_stackingOrder = 1; + bool m_drawnInWindowStatus[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; + /* Bounds last time annotation was drawn NOT saved to scenes or annontation file*/ + mutable BoundingBox m_boundsFromDrawing[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; + /** * Selection (NOT DISPLAY) status in each window. * diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationImage.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationImage.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationImage.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationImage.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -46,7 +46,7 @@ * Type for attribute defaults */ AnnotationImage::AnnotationImage(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::IMAGE, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::IMAGE, attributeDefaultType) { initializeMembersAnnotationImage(); @@ -67,7 +67,7 @@ * Object that is copied. */ AnnotationImage::AnnotationImage(const AnnotationImage& obj) -: AnnotationTwoDimensionalShape(obj) +: AnnotationOneCoordinateShape(obj) { initializeMembersAnnotationImage(); this->copyHelperAnnotationImage(obj); @@ -84,7 +84,7 @@ AnnotationImage::operator=(const AnnotationImage& obj) { if (this != &obj) { - AnnotationTwoDimensionalShape::operator=(obj); + AnnotationOneCoordinateShape::operator=(obj); this->copyHelperAnnotationImage(obj); } return *this; @@ -234,7 +234,9 @@ GraphicsPrimitiveV3fT3f* primitive = GraphicsPrimitive::newPrimitiveV3fT3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, &m_imageBytesRGBA[0], m_imageWidth, - m_imageHeight); + m_imageHeight, + GraphicsPrimitive::TextureWrappingType::CLAMP, + GraphicsPrimitive::TextureFilteringType::LINEAR); /* * A Triangle Strip (consisting of two triangles) is used * for drawing the image. At this time, the XYZ coordinates @@ -254,9 +256,6 @@ primitive->addVertex(0, 0, 1, 0); /* Bottom Right */ m_graphicsPrimitive.reset(primitive); - - -// create triangles above and add method to set the vertex coordintes (bottom left, bottom right, etc) } } @@ -270,7 +269,13 @@ const float topLeft[3]) { GraphicsPrimitiveV3fT3f* primitive = getGraphicsPrimitive(); - CaretAssert(primitive); + if (primitive == NULL) { + /* + * Primitive will be invalid if when user is dragging mouse + * to create the annotation. + */ + return; + } CaretAssert(primitive->getNumberOfVertices() == 4); @@ -296,7 +301,7 @@ AnnotationImage::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -318,7 +323,7 @@ AnnotationImage::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationImage.h connectome-workbench-1.5.0/src/Annotations/AnnotationImage.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationImage.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationImage.h 2021-02-16 19:46:47.000000000 +0000 @@ -23,13 +23,13 @@ #include -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" namespace caret { class GraphicsPrimitiveV3fT3f; - class AnnotationImage : public AnnotationTwoDimensionalShape { + class AnnotationImage : public AnnotationOneCoordinateShape { public: AnnotationImage(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationLine.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationLine.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationLine.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationLine.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -44,7 +44,7 @@ * Type for attribute defaults */ AnnotationLine::AnnotationLine(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationOneDimensionalShape(AnnotationTypeEnum::LINE, +: AnnotationTwoCoordinateShape(AnnotationTypeEnum::LINE, attributeDefaultType) { initializeMembersAnnotationLine(); @@ -63,7 +63,7 @@ * Object that is copied. */ AnnotationLine::AnnotationLine(const AnnotationLine& obj) -: AnnotationOneDimensionalShape(obj) +: AnnotationTwoCoordinateShape(obj) { this->initializeMembersAnnotationLine(); this->copyHelperAnnotationLine(obj); @@ -80,7 +80,7 @@ AnnotationLine::operator=(const AnnotationLine& obj) { if (this != &obj) { - AnnotationOneDimensionalShape::operator=(obj); + AnnotationTwoCoordinateShape::operator=(obj); this->copyHelperAnnotationLine(obj); } return *this; @@ -179,7 +179,7 @@ AnnotationLine::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationOneDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationTwoCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -201,7 +201,7 @@ AnnotationLine::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationOneDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationTwoCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationLine.h connectome-workbench-1.5.0/src/Annotations/AnnotationLine.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationLine.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationLine.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,12 +22,12 @@ /*LICENSE_END*/ -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "CaretPointer.h" namespace caret { - class AnnotationLine : public AnnotationOneDimensionalShape { + class AnnotationLine : public AnnotationTwoCoordinateShape { public: AnnotationLine(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationMultiCoordinateShape.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationMultiCoordinateShape.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationMultiCoordinateShape.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationMultiCoordinateShape.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,880 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_MULTI_COORDINATE_SHAPE_DECLARE__ +#include "AnnotationMultiCoordinateShape.h" +#undef __ANNOTATION_MULTI_COORDINATE_SHAPE_DECLARE__ + +#include + +#include "AnnotationCoordinate.h" +#include "AnnotationSpatialModification.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "MathFunctions.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationMultiCoordinateShape + * \brief Class for annotations that contain multiple (2 or more points to form line sequences) + * \ingroup Annotations + */ + +/** + * Constructor. + * @param type + * Type of annotation. + * @param attributeDefaultType + * Type for attribute defaults + */ +AnnotationMultiCoordinateShape::AnnotationMultiCoordinateShape(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) +: Annotation(type, + attributeDefaultType) +{ + initializeMembersAnnotationMultiCoordinateShape(); +} + +/** + * Destructor. + */ +AnnotationMultiCoordinateShape::~AnnotationMultiCoordinateShape() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +AnnotationMultiCoordinateShape::AnnotationMultiCoordinateShape(const AnnotationMultiCoordinateShape& obj) +: Annotation(obj) +{ + initializeMembersAnnotationMultiCoordinateShape(); + + this->copyHelperAnnotationMultiCoordinateShape(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +AnnotationMultiCoordinateShape& +AnnotationMultiCoordinateShape::operator=(const AnnotationMultiCoordinateShape& obj) +{ + if (this != &obj) { + Annotation::operator=(obj); + this->copyHelperAnnotationMultiCoordinateShape(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +AnnotationMultiCoordinateShape::copyHelperAnnotationMultiCoordinateShape(const AnnotationMultiCoordinateShape& obj) +{ + m_coordinates.clear(); + + for (const auto& ptr : obj.m_coordinates) { + std::unique_ptr ac(new AnnotationCoordinate(*ptr)); + m_coordinates.push_back(std::move(ac)); + } + + setModified(); +} + +/** + * Initialize members of this class. + */ +void +AnnotationMultiCoordinateShape::initializeMembersAnnotationMultiCoordinateShape() +{ + m_sceneAssistant.reset(new SceneClassAssistant()); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + } +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. + */ +AnnotationMultiCoordinateShape* +AnnotationMultiCoordinateShape::castToMultiCoordinateShape() +{ + return this; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. + */ +const AnnotationMultiCoordinateShape* +AnnotationMultiCoordinateShape::castToMultiCoordinateShape() const +{ + return this; +} + +/** + * Add a coordinate to this multi coordinate shape + * @param coord + * Coordinate that is added. + */ +void +AnnotationMultiCoordinateShape::addCoordinate(AnnotationCoordinate* coord) +{ + CaretAssert(coord); + std::unique_ptr ptr(coord); + m_coordinates.push_back(std::move(ptr)); + setModified(); +} + +/** + * @return Number of coordinates in this annotation + */ +int32_t +AnnotationMultiCoordinateShape::getNumberOfCoordinates() const +{ + return m_coordinates.size(); +} + +/** + * @return Coordinate at the given index + * @param index + * Inde of the coordinate + */ +AnnotationCoordinate* +AnnotationMultiCoordinateShape::getCoordinate(const int32_t index) +{ + CaretAssertVectorIndex(m_coordinates, index); + return m_coordinates[index].get(); +} + +/** + * @return Coordinate at the given index + * @param index + * Inde of the coordinate + */ +const AnnotationCoordinate* +AnnotationMultiCoordinateShape::getCoordinate(const int32_t index) const +{ + CaretAssertVectorIndex(m_coordinates, index); + return m_coordinates[index].get(); +} + +/** + * Get a copy of all coordinates in the annotation + * @param allCoordsOut + * Output containing copy of all coordinates + */ +void +AnnotationMultiCoordinateShape::getCopyOfAllCoordinates(std::vector>& allCoordsOut) const +{ + allCoordsOut.clear(); + for (auto& ac : m_coordinates) { + std::unique_ptr acCopy(new AnnotationCoordinate(*ac)); + allCoordsOut.push_back(std::move(acCopy)); + } +} + +/** + * Get a copy of all coordinates in the annotation in const + * @param allCoordsOut + * Output containing copy of all coordinates + */ +void +AnnotationMultiCoordinateShape::getCopyOfAllCoordinates(std::vector>& allCoordsOut) const +{ + allCoordsOut.clear(); + for (auto& ac : m_coordinates) { + std::unique_ptr acCopy(new AnnotationCoordinate(*ac)); + allCoordsOut.push_back(std::move(acCopy)); + } +} + +/** + * Remove the coordinate at the given index + * @param index + * Index of coordinate for removal + */ +void +AnnotationMultiCoordinateShape::removeCoordinateAtIndex(const int32_t index) +{ + CaretAssertVectorIndex(m_coordinates, index); + m_coordinates.erase(m_coordinates.begin() + index); + setModified(); +} + +/** + * Replace all coordinates in this annotation with copies of the given coordinates + * @param coordinates + * Coordinates that are copied into this annotation + */ +void AnnotationMultiCoordinateShape::replaceAllCoordinates(const std::vector>& coordinates) +{ + m_coordinates.clear(); + + for (const auto& coord : coordinates) { + AnnotationCoordinate* ac = new AnnotationCoordinate(*coord); + addCoordinate(ac); + } +} + + +/** + * @return The surface offset vector type for this annotation. + */ +AnnotationSurfaceOffsetVectorTypeEnum::Enum +AnnotationMultiCoordinateShape::getSurfaceOffsetVectorType() const +{ + AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetType(AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL); + if ( ! m_coordinates.empty()) { + CaretAssertVectorIndex(m_coordinates, 0); + offsetType = m_coordinates[0]->getSurfaceOffsetVectorType(); + } + + return offsetType; +} + +/** + * Is the object modified? + * @return true if modified, else false. + */ +bool +AnnotationMultiCoordinateShape::isModified() const +{ + if (Annotation::isModified()) { + return true; + } + + for (const auto& ptr : m_coordinates) { + if (ptr->isModified()) { + return true; + } + } + + return false; +} + +/** + * Set the status to unmodified. + */ +void +AnnotationMultiCoordinateShape::clearModified() +{ + Annotation::clearModified(); + + for (auto& ptr : m_coordinates) { + ptr->clearModified(); + } +} + +/** + * Apply the coordinates, size, and rotation from the given annotation + * to this annotation. + * + * @param otherAnnotationIn + * The other annotation from which attributes are obtained. + */ +void +AnnotationMultiCoordinateShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotationIn) +{ + CaretAssert(otherAnnotationIn); + const AnnotationMultiCoordinateShape* otherAnn = otherAnnotationIn->castToMultiCoordinateShape(); + CaretAssert(otherAnn); + + const int32_t otherNumCoords = otherAnn->getNumberOfCoordinates(); + const int32_t myNumCoords = getNumberOfCoordinates(); + if (myNumCoords == otherNumCoords) { + for (int32_t i = 0; i < myNumCoords; i++) { + *getCoordinate(i) = *otherAnn->getCoordinate(i); + } + } + else { + m_coordinates.clear(); + for (int32_t i = 0; i < otherNumCoords; i++) { + const AnnotationCoordinate* otherCoord = otherAnn->getCoordinate(i); + + std::unique_ptr ac(new AnnotationCoordinate(*otherCoord)); + m_coordinates.push_back(std::move(ac)); + } + } + + setCoordinateSpace(otherAnn->getCoordinateSpace()); + setTabIndex(otherAnn->getTabIndex()); + setWindowIndex(otherAnn->getWindowIndex()); + setModified(); +} + +/** + * Is the given sizing handle valid for this annotation? + * + * @sizingHandle + * The sizing handle. + * @return + * True if sizing handle valid, else false. + */ +bool +AnnotationMultiCoordinateShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const +{ + bool xyPlaneFlag = false; + + switch (getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + xyPlaneFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SPACER: + xyPlaneFlag = true; + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + xyPlaneFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + xyPlaneFlag = true; + break; + } + + bool validFlag = false; + + switch (sizingHandle) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (xyPlaneFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + validFlag = true; + break; + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in surface space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationMultiCoordinateShape::applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfNodes = -1; + int32_t surfaceNodeIndex = -1; + const int32_t coordIndex(spatialModification.m_polyLineCoordinateIndex); + if ((coordIndex >= 0) + && (coordIndex < getNumberOfCoordinates())) { + + AnnotationCoordinate* coord = getCoordinate(coordIndex); + CaretAssert(coord); + coord->getSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex); + if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { + if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) + && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { + coord->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, + spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, + spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); + validFlag = true; + } + } + } + } + break; + } + + + if (validFlag) { + setModified(); + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in spacer tab space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationMultiCoordinateShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) +{ + return applySpatialModificationTabOrWindowSpace(spatialModification); +} + + +/** + * Apply a spatial modification to an annotation in tab or window space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationMultiCoordinateShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + const int32_t numCoords(getNumberOfCoordinates()); + /* + * Get all XYZ coordinates + */ + std::vector allXYZ; + allXYZ.reserve(numCoords * 3); + for (int32_t i = 0; i < numCoords; i++) { + const AnnotationCoordinate* ac(getCoordinate(i)); + float xyz[3]; + ac->getXYZ(xyz); + float viewportXYZ[3] = { 0.0, 0.0, 0.0 }; + relativeXYZToViewportXYZ(xyz, spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, viewportXYZ); + allXYZ.push_back(viewportXYZ[0]); + allXYZ.push_back(viewportXYZ[1]); + allXYZ.push_back(viewportXYZ[2]); + } + + const float spaceDX = spatialModification.m_mouseDX; + const float spaceDY = spatialModification.m_mouseDY; + + int32_t startIndex(-1); + int32_t endIndex(-1); + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + /* + * Moving entire shape (all coordinates change) + */ + startIndex = 0; + endIndex = numCoords - 1; + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + /* + * Moving one coordinate in the shape + */ + if ((spatialModification.m_polyLineCoordinateIndex >= 0) + && (spatialModification.m_polyLineCoordinateIndex < numCoords)) { + startIndex = spatialModification.m_polyLineCoordinateIndex; + endIndex = spatialModification.m_polyLineCoordinateIndex; + validFlag = true; + } + break; + } + + if ((validFlag) + && (startIndex >= 0) + && (endIndex >= 0)) { + + /* + * Need to convert back to percentage coords + */ + for (int32_t i = 0; i < numCoords; i++) { + const int32_t i3(i * 3); + float x(allXYZ[i3]); + float y(allXYZ[i3+1]); + + /* + * Adjust selected coord(s) + */ + if ((i >= startIndex) + && (i <= endIndex)) { + x += spaceDX; + y += spaceDY; + } + + const float percentagNewX = 100.0 * (x / spatialModification.m_viewportWidth); + const float percentagNewY = 100.0 * (y / spatialModification.m_viewportHeight); + if ((percentagNewX >= 0.0) + && (percentagNewX <= 100.0) + && (percentagNewY >= 0.0) + && (percentagNewY <= 100.0)) { + allXYZ[i3] = percentagNewX; + allXYZ[i3+1] = percentagNewY; + } + else { + validFlag = false; + break; + } + } + + if (validFlag) { + /* + * Update the coordinates + */ + for (int32_t i = 0; i < numCoords; i++) { + AnnotationCoordinate* ac(getCoordinate(i)); + const int32_t i3(i * 3); + ac->setXYZ(&allXYZ[i3]); + } + } + } + + if (validFlag) { + setModified(); + } + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in chart space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationMultiCoordinateShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + const int32_t numCoords(getNumberOfCoordinates()); + int32_t startIndex(-1); + int32_t endIndex(-1); + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + /* + * Moving entire shape (all coordinates change) + */ + startIndex = 0; + endIndex = numCoords - 1; + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + /* + * Moving one coordinate in the shape + */ + if ((spatialModification.m_polyLineCoordinateIndex >= 0) + && (spatialModification.m_polyLineCoordinateIndex < numCoords)) { + startIndex = spatialModification.m_polyLineCoordinateIndex; + endIndex = spatialModification.m_polyLineCoordinateIndex; + validFlag = true; + } + break; + } + if ((validFlag) + && (startIndex >= 0) + && (endIndex >= 0)) { + validFlag = false; + + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid + && spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZValid) { + const float dx = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[0] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[0]; + const float dy = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[1] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[1]; + const float dz = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[2] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[2]; + + for (int32_t i = startIndex; i <= endIndex; i++) { + AnnotationCoordinate* ac = getCoordinate(i); + ac->addToXYZ(dx, dy, dz); + } + validFlag = true; + } + } + + if (validFlag) { + setModified(); + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in stereotaxic space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationMultiCoordinateShape::applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + const int32_t coordIndex(spatialModification.m_polyLineCoordinateIndex); + const int32_t numCoords(getNumberOfCoordinates()); + if ((coordIndex >= 0) + && (coordIndex < numCoords)) { + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + /* No dragging entire annotation in stereotaxic space */ + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + validFlag = true; + break; + } + if (validFlag) { + AnnotationCoordinate* ac = getCoordinate(coordIndex); + ac->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); + } + } + + if (validFlag) { + setModified(); + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationMultiCoordinateShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) +{ + if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { + return false; + } + + switch (getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + return applySpatialModificationChartSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::SPACER: + return applySpatialModificationSpacerTabSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + return applySpatialModificationStereotaxicSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + return applySpatialModificationSurfaceSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::TAB: + return applySpatialModificationTabOrWindowSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + return applySpatialModificationTabOrWindowSpace(spatialModification); + break; + } + + return false; +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +AnnotationMultiCoordinateShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + std::vector coordClasses; + + for (const auto& ptr : m_coordinates) { + const AString name("Coord_" + AString::number(m_coordinates.size())); + SceneClass* sc = ptr->saveToScene(sceneAttributes, name); + coordClasses.push_back(sc); + } + + SceneClassArray* coordArray = new SceneClassArray("m_coordinates", + coordClasses); + + sceneClass->addChild(coordArray); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +AnnotationMultiCoordinateShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_coordinates.clear(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + const SceneClassArray* coordArray = sceneClass->getClassArray("m_coordinates"); + if (coordArray != NULL) { + const int32_t numCoords = coordArray->getNumberOfArrayElements(); + for (int32_t i = 0; i < numCoords; i++) { + const SceneClass* coordClass = coordArray->getClassAtIndex(i); + CaretAssert(coordClass); + std::unique_ptr ac(new AnnotationCoordinate(m_attributeDefaultType)); + ac->restoreFromScene(sceneAttributes, + coordClass); + m_coordinates.push_back(std::move(ac)); + } + } +} diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationMultiCoordinateShape.h connectome-workbench-1.5.0/src/Annotations/AnnotationMultiCoordinateShape.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationMultiCoordinateShape.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationMultiCoordinateShape.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,113 @@ +#ifndef __ANNOTATION_MULTI_COORDINATE_SHAPE_H__ +#define __ANNOTATION_MULTI_COORDINATE_SHAPE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "Annotation.h" + +namespace caret { + + class AnnotationCoordinate; + + class AnnotationMultiCoordinateShape : public Annotation { + + public: + AnnotationMultiCoordinateShape(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); + + virtual ~AnnotationMultiCoordinateShape(); + + AnnotationMultiCoordinateShape(const AnnotationMultiCoordinateShape& obj); + + AnnotationMultiCoordinateShape& operator=(const AnnotationMultiCoordinateShape& obj); + + void addCoordinate(AnnotationCoordinate* coord); + + virtual AnnotationMultiCoordinateShape* castToMultiCoordinateShape() override; + + virtual const AnnotationMultiCoordinateShape* castToMultiCoordinateShape() const override; + + int32_t getNumberOfCoordinates() const; + + AnnotationCoordinate* getCoordinate(const int32_t index); + + const AnnotationCoordinate* getCoordinate(const int32_t index) const; + + void getCopyOfAllCoordinates(std::vector>& allCoordsOut) const; + + void getCopyOfAllCoordinates(std::vector>& allCoordsOut) const; + + void removeCoordinateAtIndex(const int32_t index); + + void replaceAllCoordinates(const std::vector>& coordinates); + + virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const override; + + virtual bool isModified() const; + + virtual void clearModified(); + + virtual bool isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const; + + virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); + + virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); + + // ADD_NEW_METHODS_HERE + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private: + void copyHelperAnnotationMultiCoordinateShape(const AnnotationMultiCoordinateShape& obj); + + void initializeMembersAnnotationMultiCoordinateShape(); + + bool applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification); + + std::unique_ptr m_sceneAssistant; + + std::vector> m_coordinates; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __ANNOTATION_MULTI_COORDINATE_SHAPE_DECLARE__ + // +#endif // __ANNOTATION_MULTI_COORDINATE_SHAPE_DECLARE__ + +} // namespace +#endif //__ANNOTATION_MULTI_COORDINATE_SHAPE_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationOneCoordinateShape.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationOneCoordinateShape.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationOneCoordinateShape.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationOneCoordinateShape.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,1746 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __ANNOTATION_ONE_COORDINATE_SHAPE_DECLARE__ +#include "AnnotationOneCoordinateShape.h" +#undef __ANNOTATION_ONE_COORDINATE_SHAPE_DECLARE__ + +#include "AnnotationColorBar.h" +#include "AnnotationCoordinate.h" +#include "AnnotationScaleBar.h" +#include "AnnotationSpatialModification.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "MathFunctions.h" +#include "Matrix4x4.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationOneCoordinateShape + * \brief Class for annotations that are two dimensional (width and height). + * \ingroup Annotations + */ + +/** + * Constructor. + * + * @param type + * Type of annotation + * @param attributeDefaultType + * Type for attribute defaults + */ +AnnotationOneCoordinateShape::AnnotationOneCoordinateShape(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) +: Annotation(type, + attributeDefaultType) +{ + initializeMembersAnnotationOneCoordinateShape(); +} + +/** + * Destructor. + */ +AnnotationOneCoordinateShape::~AnnotationOneCoordinateShape() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +AnnotationOneCoordinateShape::AnnotationOneCoordinateShape(const AnnotationOneCoordinateShape& obj) +: Annotation(obj) +{ + initializeMembersAnnotationOneCoordinateShape(); + this->copyHelperAnnotationOneCoordinateShape(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +AnnotationOneCoordinateShape& +AnnotationOneCoordinateShape::operator=(const AnnotationOneCoordinateShape& obj) +{ + if (this != &obj) { + Annotation::operator=(obj); + this->copyHelperAnnotationOneCoordinateShape(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +AnnotationOneCoordinateShape::copyHelperAnnotationOneCoordinateShape(const AnnotationOneCoordinateShape& obj) +{ + *m_coordinate = *obj.m_coordinate; + m_width = obj.m_width; + m_height = obj.m_height; + m_rotationAngle = obj.m_rotationAngle; +} + +/** + * Initialize members of this class. + */ +void +AnnotationOneCoordinateShape::initializeMembersAnnotationOneCoordinateShape() +{ + m_coordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); + + switch (m_attributeDefaultType) { + case AnnotationAttributesDefaultTypeEnum::NORMAL: + m_width = 25.0; + m_height = 25.0; + m_rotationAngle = 0.0; + break; + case AnnotationAttributesDefaultTypeEnum::USER: + m_width = s_userDefaultWidth; + m_height = s_userDefaultHeight; + m_rotationAngle = 0.0; + break; + } + + + + m_sceneAssistant.grabNew(new SceneClassAssistant()); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + /* + * Saves/restores color bar manual position and size mode. + */ + m_sceneAssistant->add("m_coordinate", + "AnnotationCoordinate", + m_coordinate); + m_sceneAssistant->add("m_width", + &m_width); + m_sceneAssistant->add("m_height", + &m_height); + m_sceneAssistant->add("m_rotationAngle", + &m_rotationAngle); + } +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. + */ +AnnotationOneCoordinateShape* +AnnotationOneCoordinateShape::castToOneCoordinateShape() +{ + return this; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. + */ +const AnnotationOneCoordinateShape* +AnnotationOneCoordinateShape::castToOneCoordinateShape() const +{ + return this; +} + +/** + * @return The coordinate for the two dimensional shape. + */ +AnnotationCoordinate* +AnnotationOneCoordinateShape::getCoordinate() +{ + return m_coordinate; +} + +/** + * @return The start coordinate for the two dimensional shape. + */ +const AnnotationCoordinate* +AnnotationOneCoordinateShape::getCoordinate() const +{ + return m_coordinate; +} + +/** + * @return The surface offset vector type for this annotation. + */ +AnnotationSurfaceOffsetVectorTypeEnum::Enum +AnnotationOneCoordinateShape::getSurfaceOffsetVectorType() const +{ + CaretAssert(m_coordinate); + return m_coordinate->getSurfaceOffsetVectorType(); +} + +/** + * @return Height for "two-dimensional" annotations in percentage zero to one-hundred. + */ +float +AnnotationOneCoordinateShape::getHeight() const +{ + return m_height; +} + +/** + * Set the height for "two-dimensional" annotations in percentage zero to one-hundred. + * + * @param height + * New value for height of the annotation. + */ +void +AnnotationOneCoordinateShape::setHeight(const float height) +{ + if (height != m_height) { + if (isFixedAspectRatio()) { + m_height = height; + m_width = m_height / getFixedAspectRatio(); + } + else { + m_height = height; + } + setModified(); + } +} + +/** + * @return Width for "two-dimensional" annotations in percentage zero to one-hundred. + */ +float +AnnotationOneCoordinateShape::getWidth() const +{ + return m_width; +} + +/** + * Set the width for "two-dimensional" annotations in percentage zero to one-hundred. + * + * @param width + * New value for width of the annotation. + */ +void +AnnotationOneCoordinateShape::setWidth(const float width) +{ + if (width != m_width) { + if (isFixedAspectRatio()) { + m_width = width; + m_height = m_width * getFixedAspectRatio(); + } + else { + m_width = width; + } + setModified(); + } +} + +/** + * @return The rotation angle, in degrees, clockwise, from vertical at the top (12 o'clock). + */ +float +AnnotationOneCoordinateShape::getRotationAngle() const +{ + return m_rotationAngle; +} + +/** + * The rotation angle, in degrees, clockwise, from vertical at the top (12 o'clock). + * + * @param rotationAngle + * New value rotation angle. + */ +void +AnnotationOneCoordinateShape::setRotationAngle(const float rotationAngle) +{ + if (rotationAngle != m_rotationAngle) { + m_rotationAngle = rotationAngle; + setModified(); + } +} + +/** + * Is the object modified? + * @return true if modified, else false. + */ +bool +AnnotationOneCoordinateShape::isModified() const +{ + if (Annotation::isModified()) { + return true; + } + + if (m_coordinate->isModified()) { + return true; + } + + return false; +} + +/** + * Set the status to unmodified. + */ +void +AnnotationOneCoordinateShape::clearModified() +{ + Annotation::clearModified(); + + m_coordinate->clearModified(); +} + +/** + * Apply the coordinates, size, and rotation from the given annotation + * to this annotation. + * + * @param otherAnnotation + * The other annotation from which attributes are obtained. + */ +void +AnnotationOneCoordinateShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) +{ + CaretAssert(otherAnnotation); + const AnnotationOneCoordinateShape* otherTwoDim = dynamic_cast(otherAnnotation); + CaretAssert(otherTwoDim); + + AnnotationCoordinate* coord = getCoordinate(); + const AnnotationCoordinate* otherCoord = otherTwoDim->getCoordinate(); + *coord = *otherCoord; + + setWidth(otherTwoDim->getWidth()); + setHeight(otherTwoDim->getHeight()); + setRotationAngle(otherTwoDim->getRotationAngle()); + + setCoordinateSpace(otherAnnotation->getCoordinateSpace()); + setTabIndex(otherAnnotation->getTabIndex()); + setWindowIndex(otherAnnotation->getWindowIndex()); + + /* + * Switch color or scale bar to manual positioning + */ + AnnotationColorBar* colorBar = dynamic_cast(this); + if (colorBar != NULL) { + colorBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); + } + AnnotationScaleBar* scaleBar = castToScaleBar(); + if (scaleBar != NULL) { + scaleBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); + } +} + +/** + * Is the given sizing handle valid for this annotation? + * + * @sizingHandle + * The sizing handle. + * @return + * True if sizing handle valid, else false. + */ +bool +AnnotationOneCoordinateShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const +{ + const bool viewportFlag = (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::VIEWPORT); + + const bool surfaceTangentOffsetFlag = isInSurfaceSpaceWithTangentOffset(); + + bool allowsMovingFlag = false; + bool allowsCornerResizingFlag = false; + bool allowsSideResizingFlag = false; + bool allowsRotationFlag = false; + + switch (getType()) { + case AnnotationTypeEnum::BOX: + allowsMovingFlag = true; + allowsCornerResizingFlag = true; + allowsSideResizingFlag = true; + allowsRotationFlag = true; + break; + case AnnotationTypeEnum::BROWSER_TAB: + allowsMovingFlag = true; + allowsCornerResizingFlag = true; + allowsSideResizingFlag = true; + break; + case AnnotationTypeEnum::COLOR_BAR: + allowsMovingFlag = true; + allowsCornerResizingFlag = true; + allowsSideResizingFlag = true; + allowsRotationFlag = true; + break; + case AnnotationTypeEnum::IMAGE: + allowsMovingFlag = true; + allowsSideResizingFlag = true; + allowsRotationFlag = true; + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + allowsMovingFlag = true; + allowsCornerResizingFlag = true; + allowsSideResizingFlag = true; + allowsRotationFlag = true; + break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + allowsMovingFlag = true; + break; + case AnnotationTypeEnum::TEXT: + allowsMovingFlag = true; + allowsRotationFlag = true; + break; + } + + if (surfaceTangentOffsetFlag) { + allowsCornerResizingFlag = false; + allowsSideResizingFlag = false; + } + + bool validFlag = false; + + if (! viewportFlag) { + switch (sizingHandle) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + if (allowsSideResizingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + if (allowsCornerResizingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + if (allowsCornerResizingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + if (allowsSideResizingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + if (allowsSideResizingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + if (allowsSideResizingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + if (allowsCornerResizingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + if (allowsCornerResizingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (allowsMovingFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + if (allowsRotationFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in chart space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationOneCoordinateShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + + const float rotationRadians = MathFunctions::toRadians(m_rotationAngle); + float dx = (spatialModification.m_mouseDX * std::cos(rotationRadians) + - spatialModification.m_mouseDY * std::sin(rotationRadians)); + float dy = (spatialModification.m_mouseDX * std::sin(rotationRadians) + + spatialModification.m_mouseDY * std::cos(rotationRadians)); + + + if (spatialModification.m_viewportWidth > 0) { + dx = (dx / spatialModification.m_viewportWidth) * 100.0; + } + else { + dx = 0.0; + } + if (spatialModification.m_viewportHeight > 0) { + dy = (dy / spatialModification.m_viewportHeight) * 100.0; + } + else { + dy = 0.0; + } + + bool validDxyFlag = false; + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + dx = 0.0; + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + dx = -dx; + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + dx = -dx; + dy = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + dy = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + dx = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + dx = -dx; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { + m_coordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + { + static float centerX = 0.0; + static float centerY = 0.0; + + /* + * Stereotaxic and surface rotations may be displayed in + * multiple locations if more that one surface is dislayed + * when surface montage is viewed and/or tile tabs is enabled. + * + * We don't have the window location of the annotation but + * we can estimate it as it is in the center of the annotation. + * Once we have approximated the center we can use the center + * as the rotation point and so that the rotation handle points + * to the mouse. During rotation, the center will not change + * so set its position when the user starts to drag the mouse. + */ + if (spatialModification.m_startOfDraggingFlag) { + const float height = ((m_height / 100.0) * spatialModification.m_viewportHeight); + const float halfHeight = height / 2.0; + + const float handleOffsetHeight = 15.0; + const float centerAngleRadians = MathFunctions::toRadians(m_rotationAngle - 90); + const float centerOffsetY = (halfHeight + handleOffsetHeight) * std::sin(centerAngleRadians); + const float centerOffsetX = -halfHeight * std::cos(centerAngleRadians); + centerX = spatialModification.m_mousePressX + centerOffsetX; + centerY = spatialModification.m_mousePressY + centerOffsetY; + } + + /* + * Rotation angle is a formed by the triangle + * (Mouse XY, Annotation XY, Positive X-axis). + */ + const float dy = (spatialModification.m_mouseY + - centerY); + const float dx = (spatialModification.m_mouseX + - centerX); + + const float angleRadians = std::atan2(dy, dx); + const float angleDegrees = MathFunctions::toDegrees(angleRadians); + m_rotationAngle = 90.0 - angleDegrees; + if (m_rotationAngle > 360.0) { + m_rotationAngle -= 360.0; + } + else if (m_rotationAngle < 0.0) { + m_rotationAngle += 360.0; + } + + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + if (validDxyFlag) { + if (isFixedAspectRatio()) { + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + if (std::fabs(dx) > std::fabs(dy)) { + /* + * For fixed aspect ratio, the width percentage is not a percentage + * of window width but of the window's height since the annotation + * is sized by its height (width = height / aspect). + * + * So when width is changed, need to set height. + */ + const float aspectRatio = getFixedAspectRatio(); + const float newHeight = MathFunctions::limitRange(getHeight() + dx * aspectRatio, 0.0f, 100.0f); + setHeight(newHeight); + validFlag = true; + } + else if (std::fabs(dx) < std::fabs(dy)) { + const float newHeight = MathFunctions::limitRange(getHeight() + dy, 0.0f, 100.0f); + setHeight(newHeight); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + } + else { + if (dx != 0.0) { + m_width += dx; + m_width = MathFunctions::limitRange(m_width, 1.0f, 100.0f); + validFlag = true; + } + if (dy != 0.0) { + m_height += dy; + m_height = MathFunctions::limitRange(m_height, 1.0f, 100.0f); + validFlag = true; + } + } + } + + if (validFlag) { + setModified(); + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in surface + * or stereotaxic space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @param coordinateSpace + * The coordinate space. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationOneCoordinateShape::applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, + const AnnotationCoordinateSpaceEnum::Enum coordinateSpace) +{ + bool badSpaceFlag = false; + bool stereoSpaceFlag = false; + bool surfaceSpaceFlag = false; + + switch (coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + badSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SPACER: + badSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + stereoSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + surfaceSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::TAB: + badSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + badSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + badSpaceFlag = true; + break; + } + + if (badSpaceFlag) { + CaretAssertMessage(0, ("This method should NEVER be called for " + + AnnotationCoordinateSpaceEnum::toName(coordinateSpace) + + " space annotations")); + return false; + } + + bool validFlag = false; + + const float rotationRadians = MathFunctions::toRadians(m_rotationAngle); + float dx = (spatialModification.m_mouseDX * std::cos(rotationRadians) + - spatialModification.m_mouseDY * std::sin(rotationRadians)); + float dy = (spatialModification.m_mouseDX * std::sin(rotationRadians) + + spatialModification.m_mouseDY * std::cos(rotationRadians)); + + + if (spatialModification.m_viewportWidth > 0) { + dx = (dx / spatialModification.m_viewportWidth) * 100.0; + } + else { + dx = 0.0; + } + if (spatialModification.m_viewportHeight > 0) { + dy = (dy / spatialModification.m_viewportHeight) * 100.0; + } + else { + dy = 0.0; + } + + bool validDxyFlag = false; + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + dx = 0.0; + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + dx = -dx; + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + dy = -dy; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + dx = -dx; + dy = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + dy = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + dx = 0.0; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + dx = -dx; + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + validDxyFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (stereoSpaceFlag) { + m_coordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); + validFlag = true; + } + else if (surfaceSpaceFlag) { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfNodes = -1; + int32_t surfaceNodeIndex = -1; + m_coordinate->getSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex); + + if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { + if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) + && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { + m_coordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, + spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, + spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); + validFlag = true; + } + } + } + else { + CaretAssertMessage(0, "New space???"); + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + { + static float centerX = 0.0; + static float centerY = 0.0; + + /* + * Stereotaxic and surface rotations may be displayed in + * multiple locations if more that one surface is dislayed + * when surface montage is viewed and/or tile tabs is enabled. + * + * We don't have the window location of the annotation but + * we can estimate it as it is in the center of the annotation. + * Once we have approximated the center we can use the center + * as the rotation point and so that the rotation handle points + * to the mouse. During rotation, the center will not change + * so set its position when the user starts to drag the mouse. + */ + if (spatialModification.m_startOfDraggingFlag) { + const float height = ((m_height / 100.0) * spatialModification.m_viewportHeight); + const float halfHeight = height / 2.0; + + const float handleOffsetHeight = 15.0; + const float centerAngleRadians = MathFunctions::toRadians(m_rotationAngle - 90); + const float centerOffsetY = (halfHeight + handleOffsetHeight) * std::sin(centerAngleRadians); + const float centerOffsetX = -halfHeight * std::cos(centerAngleRadians); + centerX = spatialModification.m_mousePressX + centerOffsetX; + centerY = spatialModification.m_mousePressY + centerOffsetY; + } + + /* + * Rotation angle is a formed by the triangle + * (Mouse XY, Annotation XY, Positive X-axis). + */ + const float dy = (spatialModification.m_mouseY + - centerY); + const float dx = (spatialModification.m_mouseX + - centerX); + + const float angleRadians = std::atan2(dy, dx); + const float angleDegrees = MathFunctions::toDegrees(angleRadians); + m_rotationAngle = 90.0 - angleDegrees; + if (m_rotationAngle > 360.0) { + m_rotationAngle -= 360.0; + } + else if (m_rotationAngle < 0.0) { + m_rotationAngle += 360.0; + } + + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + if (validDxyFlag) { + if (isFixedAspectRatio()) { + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + if (std::fabs(dx) > std::fabs(dy)) { + /* + * For fixed aspect ratio, the width percentage is not a percentage + * of window width but of the window's height since the annotation + * is sized by its height (width = height / aspect). + * + * So when width is changed, need to set height. + */ + const float aspectRatio = getFixedAspectRatio(); + const float newHeight = MathFunctions::limitRange(getHeight() + dx * aspectRatio, 0.0f, 100.0f); + setHeight(newHeight); + validFlag = true; + } + else if (std::fabs(dx) < std::fabs(dy)) { + const float newHeight = MathFunctions::limitRange(getHeight() + dy, 0.0f, 100.0f); + setHeight(newHeight); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + } + else { + if (dx != 0.0) { + m_width += dx; + m_width = MathFunctions::limitRange(m_width, 1.0f, 100.0f); + validFlag = true; + } + if (dy != 0.0) { + m_height += dy; + m_height = MathFunctions::limitRange(m_height, 1.0f, 100.0f); + validFlag = true; + } + } + } + + if (validFlag) { + setModified(); + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in spacer tab space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationOneCoordinateShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) +{ + return applySpatialModificationTabOrWindowSpace(spatialModification); +} + + +/** + * Apply a spatial modification to an annotation in tab or window space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationOneCoordinateShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) +{ + float xyz[3]; + m_coordinate->getXYZ(xyz); + + float viewportXYZ[3] = { 0.0, 0.0, 0.0 }; + relativeXYZToViewportXYZ(xyz, spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, viewportXYZ); + + float bottomLeftXYZ[3]; + float bottomRightXYZ[3]; + float topLeftXYZ[3]; + float topRightXYZ[3]; + const bool validBounds = getShapeBounds(spatialModification.m_viewportWidth, + spatialModification.m_viewportHeight, + viewportXYZ, + bottomLeftXYZ, + bottomRightXYZ, + topRightXYZ, + topLeftXYZ); + if ( ! validBounds) { + return false; + } + + float leftToRightUnitVector[3]; + MathFunctions::createUnitVector(bottomLeftXYZ, bottomRightXYZ, leftToRightUnitVector); + float bottomToTopUnitVector[3]; + MathFunctions::createUnitVector(bottomLeftXYZ, topLeftXYZ, bottomToTopUnitVector); + + /* + * Find size adjustment for side (not corner) sizing handles + */ + float sideHandleDX = 0.0; + float sideHandleDY = 0.0; + getSideHandleMouseDelta(spatialModification.m_sizingHandleType, + leftToRightUnitVector, + bottomToTopUnitVector, + spatialModification.m_mouseDX, + spatialModification.m_mouseDY, + sideHandleDX, + sideHandleDY); + + bool validCoordinatesFlag = false; + bool validRotationFlag = false; + + /* + * When a resize handle is moved, update the corners of the shape + */ + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + addToXYZWithXY(bottomLeftXYZ, sideHandleDX, sideHandleDY); + addToXYZWithXY(bottomRightXYZ, sideHandleDX, sideHandleDY); + + validCoordinatesFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + { + /* + * Bottom left is now at the mouse XY + */ + bottomLeftXYZ[0] = spatialModification.m_mouseX; + bottomLeftXYZ[1] = spatialModification.m_mouseY; + + /* + * Unit vector from bottom left to updated top right + */ + float bottomLeftToTopRightUnitVector[3]; + MathFunctions::createUnitVector(bottomLeftXYZ, topRightXYZ, bottomLeftToTopRightUnitVector); + + /* + * We have a right triangle where: + * The hypotnuse is from bottom left corner to new top right corner + * A right angle is at top left corner + * Want angle at bottom left but vector angle is at top right (all + * angles add up to PI=180). + */ + const float oppositeAngle = MathFunctions::angle(topLeftXYZ, + topRightXYZ, + bottomLeftXYZ); + const float angle = (M_PI / 2.0) - oppositeAngle; + const float hypotnuseLength = MathFunctions::distance3D(bottomLeftXYZ, + topRightXYZ); + + const float newWidth = std::sin(angle) * hypotnuseLength; + const float newHeight = std::cos(angle) * hypotnuseLength; + + topLeftXYZ[0] = bottomLeftXYZ[0] + bottomToTopUnitVector[0] * newHeight; + topLeftXYZ[1] = bottomLeftXYZ[1] + bottomToTopUnitVector[1] * newHeight; + + bottomRightXYZ[0] = bottomLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; + bottomRightXYZ[1] = bottomLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; + + validCoordinatesFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + { + /* + * Bottom right is now at the mouse XY + */ + bottomRightXYZ[0] = spatialModification.m_mouseX; + bottomRightXYZ[1] = spatialModification.m_mouseY; + + /* + * Unit vector from top left to updated bottom right + */ + float topLeftToBottomRightUnitVector[3]; + MathFunctions::createUnitVector(topLeftXYZ, bottomRightXYZ, topLeftToBottomRightUnitVector); + + /* + * We have a right triangle where: + * The hypotnuse is from top left corner to new bottom right corner + * A right angle is at top right corner + */ + const float angle = MathFunctions::angle(topRightXYZ, + topLeftXYZ, + bottomRightXYZ); + const float hypotnuseLength = MathFunctions::distance3D(topLeftXYZ, + bottomRightXYZ); + + const float newWidth = std::cos(angle) * hypotnuseLength; + const float newHeight = std::sin(angle) * hypotnuseLength; + + topRightXYZ[0] = topLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; + topRightXYZ[1] = topLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; + + bottomLeftXYZ[0] = topLeftXYZ[0] - bottomToTopUnitVector[0] * newHeight; + bottomLeftXYZ[1] = topLeftXYZ[1] - bottomToTopUnitVector[1] * newHeight; + + validCoordinatesFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + addToXYZWithXY(topLeftXYZ, sideHandleDX, sideHandleDY); + addToXYZWithXY(bottomLeftXYZ, sideHandleDX, sideHandleDY); + + validCoordinatesFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + addToXYZWithXY(topRightXYZ, sideHandleDX, sideHandleDY); + addToXYZWithXY(bottomRightXYZ, sideHandleDX, sideHandleDY); + + validCoordinatesFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + addToXYZWithXY(topLeftXYZ, sideHandleDX, sideHandleDY); + addToXYZWithXY(topRightXYZ, sideHandleDX, sideHandleDY); + + validCoordinatesFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + { + /* + * Top left is now at the mouse XY + */ + topLeftXYZ[0] = spatialModification.m_mouseX; + topLeftXYZ[1] = spatialModification.m_mouseY; + + /* + * Unit vector from top left to updated bottom right + */ + float topLeftToBottomRightUnitVector[3]; + MathFunctions::createUnitVector(topLeftXYZ, bottomRightXYZ, topLeftToBottomRightUnitVector); + + /* + * We have a right triangle where: + * The hypotnuse is from top left corner to new bottom right corner + * A right angle is at top right corner + */ + const float oppositeAngle = MathFunctions::angle(topLeftXYZ, + bottomRightXYZ, + bottomLeftXYZ); + const float angle = (M_PI / 2.0) - oppositeAngle; + const float hypotnuseLength = MathFunctions::distance3D(topLeftXYZ, + bottomRightXYZ); + + const float newWidth = std::sin(angle) * hypotnuseLength; + const float newHeight = std::cos(angle) * hypotnuseLength; + + topRightXYZ[0] = topLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; + topRightXYZ[1] = topLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; + + bottomLeftXYZ[0] = topLeftXYZ[0] - bottomToTopUnitVector[0] * newHeight; + bottomLeftXYZ[1] = topLeftXYZ[1] - bottomToTopUnitVector[1] * newHeight; + + validCoordinatesFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + { + /* + * Top right is now at the mouse XY + */ + topRightXYZ[0] = spatialModification.m_mouseX; + topRightXYZ[1] = spatialModification.m_mouseY; + + /* + * Unit vector from updated top right to bottom left + */ + float topRightToBottomLeftUnitVector[3]; + MathFunctions::createUnitVector(topRightXYZ, bottomLeftXYZ, topRightToBottomLeftUnitVector); + + /* + * We have a right triangle where: + * The hypotnuse is from bottom left corner to new top right corner + * A right angle is at top left corner + */ + const float oppositeAngle = MathFunctions::angle(topLeftXYZ, + bottomLeftXYZ, + topRightXYZ); + const float angle = (M_PI / 2.0) - oppositeAngle; + const float hypotnuseLength = MathFunctions::distance3D(topRightXYZ, + bottomLeftXYZ); + + const float newWidth = std::cos(angle) * hypotnuseLength; + const float newHeight = std::sin(angle) * hypotnuseLength; + + topLeftXYZ[0] = topRightXYZ[0] - leftToRightUnitVector[0] * newWidth; + topLeftXYZ[1] = topRightXYZ[1] - leftToRightUnitVector[1] * newWidth; + + bottomRightXYZ[0] = topRightXYZ[0] - bottomToTopUnitVector[0] * newHeight; + bottomRightXYZ[1] = topRightXYZ[1] - bottomToTopUnitVector[1] * newHeight; + + validCoordinatesFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + CaretAssert(0); + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + CaretAssert(0); + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + addToXYZWithXY(bottomLeftXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); + addToXYZWithXY(bottomRightXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); + addToXYZWithXY(topRightXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); + addToXYZWithXY(topLeftXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); + + validCoordinatesFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + { + /* + * Rotation angle is a formed by the triangle + * (Mouse XY, Annotation XY, Positive X-axis). + */ + const float dy = spatialModification.m_mouseY - viewportXYZ[1]; + const float dx = spatialModification.m_mouseX - viewportXYZ[0]; + + const float angleRadians = std::atan2(dy, dx); + const float angleDegrees = MathFunctions::toDegrees(angleRadians); + m_rotationAngle = 90.0 - angleDegrees; + if (m_rotationAngle > 360.0) { + m_rotationAngle -= 360.0; + } + else if (m_rotationAngle < 0.0) { + m_rotationAngle += 360.0; + } + validRotationFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + if (validCoordinatesFlag + && (getType() == AnnotationTypeEnum::SCALE_BAR)) { + validCoordinatesFlag = false; + + AnnotationScaleBar* scaleBar = castToScaleBar(); + CaretAssert(scaleBar); + scaleBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); + + /* + * Scale bar has coordinate at its bottom left + */ + const float percentagNewX = 100.0 * (bottomLeftXYZ[0] / spatialModification.m_viewportWidth); + const float percentagNewY = 100.0 * (bottomLeftXYZ[1] / spatialModification.m_viewportHeight); + if ((percentagNewX >= 0.0) + && (percentagNewX <= 100.0) + && (percentagNewY >= 0.0) + && (percentagNewY <= 100.0)) { + AnnotationCoordinate* ac = getCoordinate(); + float xyz[3]; + ac->getXYZ(xyz); + xyz[0] = percentagNewX; + xyz[1] = percentagNewY; + ac->setXYZ(xyz); + + validCoordinatesFlag = true; + } + } + else if (validCoordinatesFlag) { + /* + * Using the updated corners of the annotation, convert back to normalized x, y, width, and aspect ratio + */ + float newViewportXYZ[3]; + MathFunctions::averageOfFourCoordinates(bottomLeftXYZ, bottomRightXYZ, topRightXYZ, topLeftXYZ, newViewportXYZ); + const float newX = 100.0 * (newViewportXYZ[0] / spatialModification.m_viewportWidth); + const float newY = 100.0 * (newViewportXYZ[1] / spatialModification.m_viewportHeight); + const float newShapeViewportWidth = MathFunctions::distance3D(bottomLeftXYZ, bottomRightXYZ); + const float newWidth = 100.0 * (newShapeViewportWidth / spatialModification.m_viewportWidth); + const float newShapeViewportHeight = MathFunctions::distance3D(bottomLeftXYZ, topLeftXYZ); + const float newHeight = 100.0 * (newShapeViewportHeight / spatialModification.m_viewportHeight); + + /* + * Note: + * Coordinates are relative (range 0 to 100) + * Width is relative (range 0 to 100) + * Aspect ratio must only be greater than zero (when < 1, horizontal rectangle, when > 1 vertical rectangle) + */ + if ((newX >= 0.0) + && (newX <= 100.0) + && (newY >= 0.0) + && (newY <= 100.0)) { + + if (isFixedAspectRatio()) { + xyz[0] = newX; + xyz[1] = newY; + + m_coordinate->setXYZ(xyz); + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + if (std::fabs(sideHandleDX) > std::fabs(sideHandleDY)) { + /* + * For fixed aspect ratio, the width percentage is not a percentage + * of window width but of the window's height since the annotation + * is sized by its height (width = height / aspect). + * + * So when width is changed, need to set height. + */ + const float aspectRatio = getFixedAspectRatio(); + const float newViewportHeight = newShapeViewportWidth * aspectRatio; + const float newShapeHeight = 100.0 * (newViewportHeight / spatialModification.m_viewportHeight); + setHeight(newShapeHeight); + } + else if (std::fabs(sideHandleDX) < std::fabs(sideHandleDY)) { + setHeight(newHeight); + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + } + else { + xyz[0] = newX; + xyz[1] = newY; + m_coordinate->setXYZ(xyz); + + m_width = newWidth; + m_height = newHeight; + } + } + else { + validCoordinatesFlag = false; + } + } + + if (validCoordinatesFlag + || validRotationFlag) { + setModified(); + return true; + } + + return false; +} + +/** + * Given the previous mouse location, current mouse location, and the shape + * location, examine the angle formed to determine if rotation is allowed. + * Note that if the mouse is moved purely horizontally or vertically from + * the rotation point, the rotation angle may rapidly change causing weird + * behavior. + * + * @param previousMouseXYZ + * Previous location of mouse. + * @param shapeXYZ + * Location of shape. + * @param currentMouseXYZ + * Current location of mouse. + * @return + * True if rotation is valid, else false. + */ +bool +AnnotationOneCoordinateShape::rotationAngleTest(const float previousMouseXYZ[3], + const float shapeXYZ[3], + const float currentMouseXYZ[3]) const +{ + const float angle = MathFunctions::angle(currentMouseXYZ, + shapeXYZ, + previousMouseXYZ); + + static const float MINIMUM_ANGLE = 0.025; + + if (angle > MINIMUM_ANGLE) { + return true; + } + + return false; +} + +/** + * Apply a spatial modification to an annotation. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationOneCoordinateShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) +{ + if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { + return false; + } + + const AnnotationCoordinateSpaceEnum::Enum space = getCoordinateSpace(); + switch (space) { + case AnnotationCoordinateSpaceEnum::CHART: + return applySpatialModificationChartSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::SPACER: + return applySpatialModificationSpacerTabSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + return applySpatialModificationSurfaceOrStereotaxicSpace(spatialModification, + space); + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + return applySpatialModificationSurfaceOrStereotaxicSpace(spatialModification, + space); + break; + case AnnotationCoordinateSpaceEnum::TAB: + return applySpatialModificationTabOrWindowSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + return applySpatialModificationTabOrWindowSpace(spatialModification); + break; + } + + return false; +} + + +/** + * When adjusting one of the "side handles" of a selected shape, set and adjust the change in the shape + * so that the shape changes in the same direction as the mouse is moved. + * + * @param sizeHandle + Size handle that is being adjusted. + * @param leftToRightShapeVector + * Vector running from left to right of shape accounting for any rotation. + * @param bottomToTopShapeVector + * Vector running from bottom to top of shape accounting for any rotation. + * @param mouseDX + * Mouse movement in X. + * @param mouseDY + * Mouse movement in Y. + * @param shapeDxOut + * Suggested change in shape (signed). + * @param shapeDyOut + * Suggested change in shape (signed). + */ +void +AnnotationOneCoordinateShape::getSideHandleMouseDelta(const AnnotationSizingHandleTypeEnum::Enum sizeHandle, + const float leftToRightShapeVector[3], + const float bottomToTopShapeVector[3], + const float mouseDX, + const float mouseDY, + float& shapeDxOut, + float& shapeDyOut) +{ + shapeDxOut = 0.0; + shapeDyOut = 0.0; + + bool useLeftRightFlag = false; + bool useBottomTopFlag = false; + bool posToNegFlag = false; + switch (sizeHandle) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + useBottomTopFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + useLeftRightFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + useLeftRightFlag = true; + posToNegFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + useBottomTopFlag = true; + posToNegFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + float shapeVector[3]; + if (useLeftRightFlag) { + shapeVector[0] = leftToRightShapeVector[0]; + shapeVector[1] = leftToRightShapeVector[1]; + shapeVector[2] = 0.0; + } + else if (useBottomTopFlag) { + shapeVector[0] = bottomToTopShapeVector[0]; + shapeVector[1] = bottomToTopShapeVector[1]; + shapeVector[2] = 0.0; + } + else { + return; + } + if (posToNegFlag) { + shapeVector[0] = -shapeVector[0]; + shapeVector[1] = -shapeVector[1]; + } + + MathFunctions::normalizeVector(shapeVector); + + float mouseVector[3] = { mouseDX, mouseDY, 0.0 }; + float mouseDelta = MathFunctions::normalizeVector(mouseVector); + + const float cosineAngle = MathFunctions::dotProduct(mouseVector, + shapeVector); + if (cosineAngle < 0.0) { + mouseDelta = -mouseDelta; + } + + shapeDxOut = (shapeVector[0] * mouseDelta); + shapeDyOut = (shapeVector[1] * mouseDelta); +} + +/** + * Add the given X and Y values to the three-dimensional coordinate. + * + * @param xyz + * Coordinate that has values added to it. + * @param addX + * Value that is added coordinate's X. + * @param addY + * Value that is added coordinate's Y. + */ +void +AnnotationOneCoordinateShape::addToXYZWithXY(float xyz[3], + const float addX, + const float addY) +{ + xyz[0] += addX; + xyz[1] += addY; +} + +/** + * Get the bounds for a two-dimensional shape annotation. + * + * @param viewportWidth + * Width of the viewport. + * @param viewportHeight + * Height of the viewport. + * @param viewportXYZ + * Viewport coordinates of the annotation. + * @param bottomLeftOut + * The bottom left corner of the annotation absolute bounds. + * @param bottomRightOut + * The bottom right corner of the annotation absolute bounds. + * @param topRightOut + * The top right corner of the annotation absolute bounds. + * @param topLeftOut + * The top left corner of the annotation absolute bounds. + */ +bool +AnnotationOneCoordinateShape::getShapeBounds(const float viewportWidth, + const float viewportHeight, + const float viewportXYZ[3], + float bottomLeftOut[3], + float bottomRightOut[3], + float topRightOut[3], + float topLeftOut[3]) const +{ + /* + * NOTE: Annotation's height and width are 'relative' ([0.0, 100.0] percentage) of window size. + * So want HALF of width/height + */ + + if (getType() == AnnotationTypeEnum::SCALE_BAR) { + const AnnotationScaleBar* scaleBar = castToScaleBar(); + CaretAssert(scaleBar); + + const std::array vpXYZ { + viewportXYZ[0], + viewportXYZ[1], + viewportXYZ[2] + }; + AnnotationScaleBar::DrawingInfo scaleBarDrawingInfo; + const bool selectionFlag(false); + scaleBar->getScaleBarDrawingInfo(viewportWidth, + viewportHeight, + vpXYZ, + selectionFlag, + scaleBarDrawingInfo); + + /* + * Scale bar coordinate is at left and its length + * is set by the user (does not use annotation width) + */ + const auto& bounds = scaleBarDrawingInfo.m_backgroundBounds; + bottomLeftOut[0] = bounds[0]; + bottomLeftOut[1] = bounds[1]; + bottomLeftOut[2] = bounds[2]; + bottomRightOut[0] = bounds[3]; + bottomRightOut[1] = bounds[4]; + bottomRightOut[2] = bounds[5]; + topRightOut[0] = bounds[6]; + topRightOut[1] = bounds[7]; + topRightOut[2] = bounds[8]; + topLeftOut[0] = bounds[9]; + topLeftOut[1] = bounds[10]; + topLeftOut[2] = bounds[11]; + } + else { + const float width = getWidth(); + float halfWidth = (width / 200.0) * viewportWidth; + const float halfHeight = (getHeight() / 200.0) * viewportHeight; + if (isFixedAspectRatio()) { + halfWidth = halfHeight / getFixedAspectRatio(); + } + + bottomLeftOut[0] = viewportXYZ[0] - halfWidth; + bottomLeftOut[1] = viewportXYZ[1] - halfHeight; + bottomLeftOut[2] = viewportXYZ[2]; + bottomRightOut[0] = viewportXYZ[0] + halfWidth; + bottomRightOut[1] = viewportXYZ[1] - halfHeight; + bottomRightOut[2] = viewportXYZ[2]; + topRightOut[0] = viewportXYZ[0] + halfWidth; + topRightOut[1] = viewportXYZ[1] + halfHeight; + topRightOut[2] = viewportXYZ[2]; + topLeftOut[0] = viewportXYZ[0] - halfWidth; + topLeftOut[1] = viewportXYZ[1] + halfHeight; + topLeftOut[2] = viewportXYZ[2]; + + if (m_rotationAngle != 0) { + Matrix4x4 matrix; + matrix.translate(-viewportXYZ[0], -viewportXYZ[1], -viewportXYZ[2]); + matrix.rotateZ(-m_rotationAngle); + matrix.translate(viewportXYZ[0], viewportXYZ[1], viewportXYZ[2]); + matrix.multiplyPoint3(bottomLeftOut); + matrix.multiplyPoint3(bottomRightOut); + matrix.multiplyPoint3(topRightOut); + matrix.multiplyPoint3(topLeftOut); + } + } + + return true; +} + +/** + * Set the width and height of the shape from bounding coordinates. + * + * @param xyzOne + * First bounding coordinate in absolute tab coordinates + * @param xyzTwo + * Second bounding coordinate in absolute tab coordinates + * @param spaceWidth + * Width of space. + * @param spaceHeight + * Height of space. + */ +void +AnnotationOneCoordinateShape::setWidthAndHeightFromBounds(const float xyzOne[3], + const float xyzTwo[3], + const float spaceWidth, + const float spaceHeight) +{ + if ((spaceWidth > 0.0) + && (spaceHeight > 0.0)) { + const float minX = std::min(xyzOne[0], + xyzTwo[0]); + const float maxX = std::max(xyzOne[0], + xyzTwo[0]); + + const float minY = std::min(xyzOne[1], + xyzTwo[1]); + const float maxY = std::max(xyzOne[1], + xyzTwo[1]); + + const float width = ((maxX - minX) / spaceWidth) * 100.0; + const float height = ((maxY - minY) / spaceHeight) * 100.0; + + setWidth(width); + setHeight(height); + } +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +AnnotationOneCoordinateShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +AnnotationOneCoordinateShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + +/** + * Set the default value for height + * + * @param height + * Default for newly created text annotations. + */ +void +AnnotationOneCoordinateShape::setUserDefaultHeight(const float height) +{ + s_userDefaultHeight = height; +} + +/** + * Set the default value for width + * + * @param width + * Default for newly created annotations. + */ +void +AnnotationOneCoordinateShape::setUserDefaultWidth(const float width) +{ + s_userDefaultWidth = width; +} diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationOneCoordinateShape.h connectome-workbench-1.5.0/src/Annotations/AnnotationOneCoordinateShape.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationOneCoordinateShape.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationOneCoordinateShape.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,163 @@ +#ifndef __ANNOTATION_ONE_COORDINATE_SHAPE_H__ +#define __ANNOTATION_ONE_COORDINATE_SHAPE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "Annotation.h" +#include "CaretPointer.h" + + +namespace caret { + + class AnnotationCoordinate; + + class AnnotationOneCoordinateShape : public Annotation { + + public: + AnnotationOneCoordinateShape(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); + + virtual ~AnnotationOneCoordinateShape(); + + AnnotationOneCoordinateShape(const AnnotationOneCoordinateShape& obj); + + AnnotationOneCoordinateShape& operator=(const AnnotationOneCoordinateShape& obj); + + virtual AnnotationOneCoordinateShape* castToOneCoordinateShape() override; + + virtual const AnnotationOneCoordinateShape* castToOneCoordinateShape() const override; + + AnnotationCoordinate* getCoordinate(); + + const AnnotationCoordinate* getCoordinate() const; + + virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const override; + + float getHeight() const; + + void setHeight(const float height); + + float getWidth() const; + + void setWidth(const float width); + + float getRotationAngle() const; + + void setRotationAngle(const float rotationAngle); + + virtual bool isModified() const; + + virtual void clearModified(); + + virtual bool isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const; + + virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); + + virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); + + virtual bool getShapeBounds(const float viewportWidth, + const float viewportHeight, + const float viewportXYZ[3], + float bottomLeftOut[3], + float bottomRightOut[3], + float topRightOut[3], + float topLeftOut[3]) const; + + void getSideHandleMouseDelta(const AnnotationSizingHandleTypeEnum::Enum sizeHandle, + const float leftToRightShapeVector[3], + const float bottomToTopShapeVector[3], + const float mouseDX, + const float mouseDY, + float& shapeDxOut, + float& shapeDyOut); + + void setWidthAndHeightFromBounds(const float xyzOne[3], + const float xyzTwo[3], + const float spaceWidth, + const float spaceHeight); + + static void setUserDefaultHeight(const float height); + + static void setUserDefaultWidth(const float width); + + // ADD_NEW_METHODS_HERE + + + + + + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private: + void copyHelperAnnotationOneCoordinateShape(const AnnotationOneCoordinateShape& obj); + + void initializeMembersAnnotationOneCoordinateShape(); + + void addToXYZWithXY(float xyz[3], + const float addX, + const float addY); + + bool applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, + const AnnotationCoordinateSpaceEnum::Enum coordinateSpace); + + bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification); + + bool rotationAngleTest(const float previousMouseXYZ[3], + const float shapeXYZ[3], + const float currentMouseXYZ[3]) const; + + CaretPointer m_sceneAssistant; + + CaretPointer m_coordinate; + + float m_rotationAngle; + + float m_width; + + float m_height; + + static float s_userDefaultWidth; + + static float s_userDefaultHeight; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __ANNOTATION_ONE_COORDINATE_SHAPE_DECLARE__ + float AnnotationOneCoordinateShape::s_userDefaultWidth = 25.0; + + float AnnotationOneCoordinateShape::s_userDefaultHeight = 25.0; +#endif // __ANNOTATION_ONE_COORDINATE_SHAPE_DECLARE__ + +} // namespace +#endif //__ANNOTATION_ONE_COORDINATE_SHAPE_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationOneDimensionalShape.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationOneDimensionalShape.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationOneDimensionalShape.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationOneDimensionalShape.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,872 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ -#include "AnnotationOneDimensionalShape.h" -#undef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ - -#include - -#include "AnnotationCoordinate.h" -#include "AnnotationSpatialModification.h" -#include "CaretAssert.h" -#include "MathFunctions.h" -#include "SceneClass.h" -#include "SceneClassAssistant.h" - -using namespace caret; - - - -/** - * \class caret::AnnotationOneDimensionalShape - * \brief Class for annotations that are one-dimensional (lines) - * \ingroup Annotations - */ - -/** - * Constructor. - * @param type - * Type of annotation. - * @param attributeDefaultType - * Type for attribute defaults - */ -AnnotationOneDimensionalShape::AnnotationOneDimensionalShape(const AnnotationTypeEnum::Enum type, - const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: Annotation(type, - attributeDefaultType) -{ - initializeMembersAnnotationOneDimensionalShape(); -} - -/** - * Destructor. - */ -AnnotationOneDimensionalShape::~AnnotationOneDimensionalShape() -{ -} - -/** - * Copy constructor. - * @param obj - * Object that is copied. - */ -AnnotationOneDimensionalShape::AnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj) -: Annotation(obj) -{ - initializeMembersAnnotationOneDimensionalShape(); - - this->copyHelperAnnotationOneDimensionalShape(obj); -} - -/** - * Assignment operator. - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -AnnotationOneDimensionalShape& -AnnotationOneDimensionalShape::operator=(const AnnotationOneDimensionalShape& obj) -{ - if (this != &obj) { - Annotation::operator=(obj); - this->copyHelperAnnotationOneDimensionalShape(obj); - } - return *this; -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -AnnotationOneDimensionalShape::copyHelperAnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj) -{ - *m_startCoordinate = *obj.m_startCoordinate; - *m_endCoordinate = *obj.m_endCoordinate; -} - -/** - * Initialize members of this class. - */ -void -AnnotationOneDimensionalShape::initializeMembersAnnotationOneDimensionalShape() -{ - m_startCoordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); - m_endCoordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); - - m_sceneAssistant.grabNew(new SceneClassAssistant()); - if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { - m_sceneAssistant->add("m_startCoordinate", - "AnnotationCoordinate", - m_startCoordinate); - m_sceneAssistant->add("m_endCoordinate", - "AnnotationCoordinate", - m_endCoordinate); - } -} - -/** - * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. - */ -AnnotationOneDimensionalShape* -AnnotationOneDimensionalShape::castToOneDimensionalShape() -{ - return this; -} - -/** - * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. - */ -const AnnotationOneDimensionalShape* -AnnotationOneDimensionalShape::castToOneDimensionalShape() const -{ - return this; -} - -/** - * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. - */ -AnnotationTwoDimensionalShape* -AnnotationOneDimensionalShape::castToTwoDimensionalShape() -{ - return NULL; -} - -/** - * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. - */ -const AnnotationTwoDimensionalShape* -AnnotationOneDimensionalShape::castToTwoDimensionalShape() const -{ - return NULL; -} - -/** - * @return The start coordinate for the one dimensional shape. - */ -AnnotationCoordinate* -AnnotationOneDimensionalShape::getStartCoordinate() -{ - return m_startCoordinate; -} - -/** - * @return The start coordinate for the one dimensional shape. - */ -const AnnotationCoordinate* -AnnotationOneDimensionalShape::getStartCoordinate() const -{ - return m_startCoordinate; -} - -/** - * @return The end coordinate for the one dimensional shape. - */ -AnnotationCoordinate* -AnnotationOneDimensionalShape::getEndCoordinate() -{ - return m_endCoordinate; -} - -/** - * @return The end coordinate for the one dimensional shape. - */ -const AnnotationCoordinate* -AnnotationOneDimensionalShape::getEndCoordinate() const -{ - return m_endCoordinate; -} - -/** - * @return The surface offset vector type for this annotation. - */ -AnnotationSurfaceOffsetVectorTypeEnum::Enum -AnnotationOneDimensionalShape::getSurfaceOffsetVectorType() const -{ - CaretAssert(m_startCoordinate); - return m_startCoordinate->getSurfaceOffsetVectorType(); -} - -/** - * Is the object modified? - * @return true if modified, else false. - */ -bool -AnnotationOneDimensionalShape::isModified() const -{ - if (Annotation::isModified()) { - return true; - } - - if (m_startCoordinate->isModified()) { - return true; - } - - if (m_endCoordinate->isModified()) { - return true; - } - - return false; -} - -/** - * Set the status to unmodified. - */ -void -AnnotationOneDimensionalShape::clearModified() -{ - Annotation::clearModified(); - - m_startCoordinate->clearModified(); - m_endCoordinate->clearModified(); -} - -/** - * Apply the coordinates, size, and rotation from the given annotation - * to this annotation. - * - * @param otherAnnotation - * The other annotation from which attributes are obtained. - */ -void -AnnotationOneDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) -{ - CaretAssert(otherAnnotation); - const AnnotationOneDimensionalShape* otherOneDim = dynamic_cast(otherAnnotation); - CaretAssert(otherOneDim); - - AnnotationCoordinate* startCoord = getStartCoordinate(); - const AnnotationCoordinate* otherStartCoord = otherOneDim->getStartCoordinate(); - *startCoord = *otherStartCoord; - - AnnotationCoordinate* endCoord = getEndCoordinate(); - const AnnotationCoordinate* otherEndCoord = otherOneDim->getEndCoordinate(); - *endCoord = *otherEndCoord; - - setCoordinateSpace(otherAnnotation->getCoordinateSpace()); - setTabIndex(otherAnnotation->getTabIndex()); - setWindowIndex(otherAnnotation->getWindowIndex()); -} - -/** - * Get the rotation angle from the one-dimensional annotation. - * 0 is horizontal. - * - * @param viewportWidth - * Width of viewport. - * @param viewportHeight - * Height of viewport. - * @return - * Rotation angle of the annotation. - */ -float -AnnotationOneDimensionalShape::getRotationAngle(const float viewportWidth, - const float viewportHeight) const -{ - if ( ! isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { - return 0.0; - } - - float vpOneX = 0.0; - float vpOneY = 0.0; - float vpTwoX = 0.0; - float vpTwoY = 0.0; - getStartCoordinate()->getViewportXY(viewportWidth, viewportHeight, vpOneX, vpOneY); - getEndCoordinate()->getViewportXY(viewportWidth, viewportHeight, vpTwoX, vpTwoY); - - const float dx = vpTwoX - vpOneX; - const float dy = vpTwoY - vpOneY; - - float angle = 180.0 - MathFunctions::toDegrees(std::atan2(dy, dx)); - if (angle < 0.0) { - angle = angle + 360.0; - } - else if (angle > 360.0) { - angle = angle - 360.0; - } - return angle; -} - -/** - * Set the rotation angle from the one-dimensional annotation. - * 0 is horizontal. - * - * @param viewportWidth - * Width of viewport. - * @param viewportHeight - * Height of viewport. - * @param rotationAngle - * Rotation angle for the annotation. - */ -void -AnnotationOneDimensionalShape::setRotationAngle(const float viewportWidth, - const float viewportHeight, - const float rotationAngle) -{ - if ( ! isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { - return; - } - - float annOneX = 0.0; - float annOneY = 0.0; - float annTwoX = 0.0; - float annTwoY = 0.0; - getStartCoordinate()->getViewportXY(viewportWidth, viewportHeight, annOneX, annOneY); - getEndCoordinate()->getViewportXY(viewportWidth, viewportHeight, annTwoX, annTwoY); - - const float midPointXYZ[3] = { - (annOneX + annTwoX) / 2.0f, - (annOneY + annTwoY) / 2.0f, - 0.0f - }; - - const float vpOneXYZ[3] = { annOneX, annOneY, 0.0f }; - const float lengthMidToOne = MathFunctions::distance3D(midPointXYZ, vpOneXYZ); - const float newRotationAngle = 180.0f - rotationAngle; - - const float angleRadians = MathFunctions::toRadians(newRotationAngle); - const float dy = lengthMidToOne * std::sin(angleRadians); - const float dx = lengthMidToOne * std::cos(angleRadians); - annOneX = midPointXYZ[0] - dx; - annOneY = midPointXYZ[1] - dy; - - annTwoX = midPointXYZ[0] + dx; - annTwoY = midPointXYZ[1] + dy; - - getStartCoordinate()->setXYZFromViewportXYZ(viewportWidth, viewportHeight, annOneX, annOneY); - getEndCoordinate()->setXYZFromViewportXYZ(viewportWidth, viewportHeight, annTwoX, annTwoY); -} - -/** - * Is the given sizing handle valid for this annotation? - * - * @sizingHandle - * The sizing handle. - * @return - * True if sizing handle valid, else false. - */ -bool -AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const -{ - bool xyPlaneFlag = false; - - switch (getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::CHART: - xyPlaneFlag = true; - break; - case AnnotationCoordinateSpaceEnum::SPACER: - xyPlaneFlag = true; - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - break; - case AnnotationCoordinateSpaceEnum::TAB: - xyPlaneFlag = true; - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - xyPlaneFlag = true; - break; - } - - bool validFlag = false; - - switch (sizingHandle) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - validFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - validFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - if (xyPlaneFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - if (xyPlaneFlag) { - validFlag = true; - } - break; - } - - return validFlag; -} - -/** - * Apply a spatial modification to an annotation in surface space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationOneDimensionalShape::applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification) -{ - bool validFlag = false; - - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - { - StructureEnum::Enum structure = StructureEnum::INVALID; - int32_t surfaceNumberOfNodes = -1; - int32_t surfaceNodeIndex = -1; - - m_endCoordinate->getSurfaceSpace(structure, - surfaceNumberOfNodes, - surfaceNodeIndex); - if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { - if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) - && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { - m_endCoordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, - spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, - spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); - validFlag = true; - } - } - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - { - StructureEnum::Enum structure = StructureEnum::INVALID; - int32_t surfaceNumberOfNodes = -1; - int32_t surfaceNodeIndex = -1; - - m_startCoordinate->getSurfaceSpace(structure, - surfaceNumberOfNodes, - surfaceNodeIndex); - if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { - if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) - && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { - m_startCoordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, - spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, - spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); - validFlag = true; - } - } - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - break; - } - - - if (validFlag) { - setModified(); - } - - return validFlag; -} - -/** - * Apply a spatial modification to an annotation in spacer tab space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationOneDimensionalShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) -{ - return applySpatialModificationTabOrWindowSpace(spatialModification); -} - - -/** - * Apply a spatial modification to an annotation in tab or window space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) -{ - float xyz1[3]; - float xyz2[3]; - m_startCoordinate->getXYZ(xyz1); - m_endCoordinate->getXYZ(xyz2); - - float newX1 = xyz1[0]; - float newY1 = xyz1[1]; - float newX2 = xyz2[0]; - float newY2 = xyz2[1]; - - const float spaceDX = 100.0f * ((spatialModification.m_viewportWidth != 0.0f) - ? (spatialModification.m_mouseDX / spatialModification.m_viewportWidth) - : 0.0f); - const float spaceDY = 100.0f * ((spatialModification.m_viewportHeight != 0.0f) - ? (spatialModification.m_mouseDY / spatialModification.m_viewportHeight) - : 0.0f); - bool validFlag = false; - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - newX2 += spaceDX; - newY2 += spaceDY; - validFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - newX1 += spaceDX; - newY1 += spaceDY; - validFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - newX1 += spaceDX; - newY1 += spaceDY; - newX2 += spaceDX; - newY2 += spaceDY; - validFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - { - float vpOneXYZ[3] = { 0.0, 0.0, 0.0 }; - relativeXYZToViewportXYZ(xyz1, - spatialModification.m_viewportWidth, - spatialModification.m_viewportHeight, - vpOneXYZ); - float vpTwoXYZ[3] = { 0.0, 0.0, 0.0 }; - relativeXYZToViewportXYZ(xyz2, - spatialModification.m_viewportWidth, - spatialModification.m_viewportHeight, - vpTwoXYZ); - - float vpXYZ[3] = { - (vpOneXYZ[0] + vpTwoXYZ[0]) / 2.0f, - (vpOneXYZ[1] + vpTwoXYZ[1]) / 2.0f, - (vpOneXYZ[2] + vpTwoXYZ[2]) / 2.0f - }; - - /* - * Rotation angle is a formed by the triangle - * (Mouse XY, Annotation XY, Positive X-axis). - */ - const float dy = spatialModification.m_mouseY - vpXYZ[1]; - const float dx = spatialModification.m_mouseX - vpXYZ[0]; - - const float angleRadians = std::atan2(dy, dx); - const float angleDegrees = MathFunctions::toDegrees(angleRadians); - float rotationAngle = -angleDegrees; - if (rotationAngle > 360.0) { - rotationAngle -= 360.0; - } - else if (rotationAngle < 0.0) { - rotationAngle += 360.0; - } - - CaretPointer shapeCopy(dynamic_cast(this->clone())); - shapeCopy->setRotationAngle(spatialModification.m_viewportWidth, - spatialModification.m_viewportHeight, - rotationAngle); - - const float* xyzOne = shapeCopy->getStartCoordinate()->getXYZ(); - const float* xyzTwo = shapeCopy->getEndCoordinate()->getXYZ(); - newX1 = xyzOne[0]; - newY1 = xyzOne[1]; - newX2 = xyzTwo[0]; - newY2 = xyzTwo[1]; - validFlag = true; - } - break; - } - - if (validFlag) { - if ((newX1 >= 0.0) - && (newX1 <= 100.0) - && (newY1 >= 0.0) - && (newY1 <= 100.0) - && (newX2 >= 0.0) - && (newX2 <= 100.0) - && (newY2 >= 0.0) - && (newY2 <= 100.0)) { - xyz1[0] = newX1; - xyz1[1] = newY1; - m_startCoordinate->setXYZ(xyz1); - xyz2[0] = newX2; - xyz2[1] = newY2; - m_endCoordinate->setXYZ(xyz2); - } - else { - validFlag = false; - } - } - - return validFlag; -} - -/** - * Apply a spatial modification to an annotation in chart space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationOneDimensionalShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) -{ - bool validFlag = false; - - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { - m_endCoordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { - m_startCoordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid - && spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZValid) { - const float dx = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[0] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[0]; - const float dy = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[1] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[1]; - const float dz = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[2] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[2]; - - m_startCoordinate->addToXYZ(dx, dy, dz); - m_endCoordinate->addToXYZ(dx, dy, dz); - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - break; - } - - if (validFlag) { - setModified(); - } - - return validFlag; -} - -/** - * Apply a spatial modification to an annotation in stereotaxic space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationOneDimensionalShape::applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification) -{ - bool validFlag = false; - - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - if (spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid) { - m_endCoordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - if (spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid) { - m_startCoordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - break; - } - - if (validFlag) { - setModified(); - } - - return validFlag; -} - -/** - * Apply a spatial modification to an annotation. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationOneDimensionalShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) -{ - if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { - return false; - } - - switch (getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::CHART: - return applySpatialModificationChartSpace(spatialModification); - break; - case AnnotationCoordinateSpaceEnum::SPACER: - return applySpatialModificationSpacerTabSpace(spatialModification); - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - return applySpatialModificationStereotaxicSpace(spatialModification); - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - return applySpatialModificationSurfaceSpace(spatialModification); - break; - case AnnotationCoordinateSpaceEnum::TAB: - return applySpatialModificationTabOrWindowSpace(spatialModification); - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - return applySpatialModificationTabOrWindowSpace(spatialModification); - break; - } - - return false; -} - -/** - * Save subclass data to the scene. - * - * @param sceneAttributes - * Attributes for the scene. Scenes may be of different types - * (full, generic, etc) and the attributes should be checked when - * restoring the scene. - * - * @param sceneClass - * sceneClass to which data members should be added. Will always - * be valid (non-NULL). - */ -void -AnnotationOneDimensionalShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, - SceneClass* sceneClass) -{ - m_sceneAssistant->saveMembers(sceneAttributes, - sceneClass); -} - -/** - * Restore file data from the scene. - * - * @param sceneAttributes - * Attributes for the scene. Scenes may be of different types - * (full, generic, etc) and the attributes should be checked when - * restoring the scene. - * - * @param sceneClass - * sceneClass for the instance of a class that implements - * this interface. Will NEVER be NULL. - */ -void -AnnotationOneDimensionalShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass) -{ - m_sceneAssistant->restoreMembers(sceneAttributes, - sceneClass); -} - diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationOneDimensionalShape.h connectome-workbench-1.5.0/src/Annotations/AnnotationOneDimensionalShape.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationOneDimensionalShape.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationOneDimensionalShape.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -#ifndef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ -#define __ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "Annotation.h" -#include "CaretPointer.h" - -namespace caret { - - class AnnotationCoordinate; - - class AnnotationOneDimensionalShape : public Annotation { - - public: - AnnotationOneDimensionalShape(const AnnotationTypeEnum::Enum type, - const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); - - virtual ~AnnotationOneDimensionalShape(); - - AnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj); - - AnnotationOneDimensionalShape& operator=(const AnnotationOneDimensionalShape& obj); - - virtual AnnotationOneDimensionalShape* castToOneDimensionalShape() override; - - virtual const AnnotationOneDimensionalShape* castToOneDimensionalShape() const override; - - virtual AnnotationTwoDimensionalShape* castToTwoDimensionalShape() override; - - virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const override; - - AnnotationCoordinate* getStartCoordinate(); - - const AnnotationCoordinate* getStartCoordinate() const; - - AnnotationCoordinate* getEndCoordinate(); - - const AnnotationCoordinate* getEndCoordinate() const; - - virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const override; - - virtual bool isModified() const; - - virtual void clearModified(); - - virtual bool isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const; - - virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); - - virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); - - float getRotationAngle(const float viewportWidth, - const float viewportHeight) const; - - void setRotationAngle(const float viewportWidth, - const float viewportHeight, - const float rotationAngle); - - - // ADD_NEW_METHODS_HERE - - - - - - - protected: - virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, - SceneClass* sceneClass); - - virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass); - - private: - void copyHelperAnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj); - - void initializeMembersAnnotationOneDimensionalShape(); - - bool applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification); - - bool applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification); - - bool applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification); - - bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); - - bool applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification); - - CaretPointer m_sceneAssistant; - - CaretPointer m_startCoordinate; - - CaretPointer m_endCoordinate; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ - // -#endif // __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ - -} // namespace -#endif //__ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationOval.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationOval.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationOval.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationOval.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -44,7 +44,7 @@ * Type for attribute defaults */ AnnotationOval::AnnotationOval(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::OVAL, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::OVAL, attributeDefaultType) { initializeMembersAnnotationOval(); @@ -63,7 +63,7 @@ * Object that is copied. */ AnnotationOval::AnnotationOval(const AnnotationOval& obj) -: AnnotationTwoDimensionalShape(obj) +: AnnotationOneCoordinateShape(obj) { this->initializeMembersAnnotationOval(); this->copyHelperAnnotationOval(obj); @@ -80,7 +80,7 @@ AnnotationOval::operator=(const AnnotationOval& obj) { if (this != &obj) { - AnnotationTwoDimensionalShape::operator=(obj); + AnnotationOneCoordinateShape::operator=(obj); this->copyHelperAnnotationOval(obj); } return *this; @@ -124,7 +124,7 @@ AnnotationOval::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -146,7 +146,7 @@ AnnotationOval::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationOval.h connectome-workbench-1.5.0/src/Annotations/AnnotationOval.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationOval.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationOval.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,13 +22,13 @@ /*LICENSE_END*/ -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" #include "CaretPointer.h" namespace caret { - class AnnotationOval : public AnnotationTwoDimensionalShape { + class AnnotationOval : public AnnotationOneCoordinateShape { public: AnnotationOval(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationPolyLine.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationPolyLine.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationPolyLine.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationPolyLine.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,152 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_POLY_LINE_DECLARE__ +#include "AnnotationPolyLine.h" +#undef __ANNOTATION_POLY_LINE_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationPolyLine + * \brief An annotation poly line + * \ingroup Annotations + */ + +/** + * Constructor. + * + * @param attributeDefaultType + * Type for attribute defaults + */ +AnnotationPolyLine::AnnotationPolyLine(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) +: AnnotationMultiCoordinateShape(AnnotationTypeEnum::POLY_LINE, + attributeDefaultType) +{ + initializeMembersAnnotationPolyLine(); +} + +/** + * Destructor. + */ +AnnotationPolyLine::~AnnotationPolyLine() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +AnnotationPolyLine::AnnotationPolyLine(const AnnotationPolyLine& obj) +: AnnotationMultiCoordinateShape(obj) +{ + this->initializeMembersAnnotationPolyLine(); + this->copyHelperAnnotationPolyLine(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +AnnotationPolyLine& +AnnotationPolyLine::operator=(const AnnotationPolyLine& obj) +{ + if (this != &obj) { + AnnotationMultiCoordinateShape::operator=(obj); + this->copyHelperAnnotationPolyLine(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +AnnotationPolyLine::copyHelperAnnotationPolyLine(const AnnotationPolyLine& /*obj*/) +{ +} + +/** + * Initialize a new instance of this class. + */ +void +AnnotationPolyLine::initializeMembersAnnotationPolyLine() +{ + m_sceneAssistant.grabNew(new SceneClassAssistant()); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + } +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +AnnotationPolyLine::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + AnnotationMultiCoordinateShape::saveSubClassDataToScene(sceneAttributes, + sceneClass); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +AnnotationPolyLine::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + AnnotationMultiCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, + sceneClass); + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationPolyLine.h connectome-workbench-1.5.0/src/Annotations/AnnotationPolyLine.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationPolyLine.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationPolyLine.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,65 @@ +#ifndef __ANNOTATION_POLY_LINE_H__ +#define __ANNOTATION_POLY_LINE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "AnnotationMultiCoordinateShape.h" +#include "CaretPointer.h" + +namespace caret { + + class AnnotationPolyLine : public AnnotationMultiCoordinateShape { + + public: + AnnotationPolyLine(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); + + virtual ~AnnotationPolyLine(); + + AnnotationPolyLine(const AnnotationPolyLine& obj); + + AnnotationPolyLine& operator=(const AnnotationPolyLine& obj); + + // ADD_NEW_METHODS_HERE + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private: + void copyHelperAnnotationPolyLine(const AnnotationPolyLine& obj); + + void initializeMembersAnnotationPolyLine(); + + CaretPointer m_sceneAssistant; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __ANNOTATION_POLY_LINE_DECLARE__ +#endif // __ANNOTATION_POLY_LINE_DECLARE__ + +} // namespace +#endif //__ANNOTATION_POLY_LINE_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationRedoUndoCommand.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationRedoUndoCommand.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationRedoUndoCommand.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationRedoUndoCommand.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,13 +25,18 @@ #include +#include "AnnotationBrowserTab.h" #include "AnnotationFontAttributesInterface.h" #include "AnnotationLine.h" #include "AnnotationPercentSizeText.h" #include "AnnotationPointSizeText.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationPolyLine.h" +#include "AnnotationStackingOrderOperation.h" +#include "AnnotationOneCoordinateShape.h" +#include "CaretLogger.h" #include "EventAnnotationAddToRemoveFromFile.h" #include "EventAnnotationGrouping.h" +#include "EventAnnotationValidate.h" #include "EventGetViewportSize.h" #include "EventManager.h" #include "CaretAssert.h" @@ -88,11 +93,54 @@ const Annotation* annotationValue) const { CaretAssert(annotation); + + /* + * Test the validity of the annotation (it still exists) to + * avoid a crash. + */ + EventAnnotationValidate validateEvent(annotation); + EventManager::get()->sendEvent(validateEvent.getPointer()); + if ( ! validateEvent.isAnnotationValid()) { + CaretLogFine("Failed to validate annotation for redo/undo. Annotation was likely deleted (not an error)."); + return; + } + + const AnnotationTypeEnum::Enum annType = annotation->getType(); switch (m_mode) { case AnnotationRedoUndoCommandModeEnum::INVALID: break; + case AnnotationRedoUndoCommandModeEnum::BROWSER_TAB_BACKGROUND: + { + CaretAssert(annotation); + CaretAssert(annotationValue); + + AnnotationBrowserTab* annotationBrowserTab = dynamic_cast(annotation); + const AnnotationBrowserTab* annotationBrowserTabValue = dynamic_cast(annotationValue); + CaretAssert(annotationBrowserTab); + CaretAssert(annotationBrowserTabValue); + + const TileTabsLayoutBackgroundTypeEnum::Enum backgroundType = annotationBrowserTabValue->getBackgroundType(); + annotationBrowserTab->setBackgroundType(backgroundType); + } + break; + case AnnotationRedoUndoCommandModeEnum::BOUNDS_2D_ALL: + case AnnotationRedoUndoCommandModeEnum::BOUNDS_2D_SINGLE: + { + CaretAssert(annotation); + CaretAssert(annotationValue); + + AnnotationBrowserTab* annotationBrowserTab = dynamic_cast(annotation); + const AnnotationBrowserTab* annotationBrowserTabValue = dynamic_cast(annotationValue); + CaretAssert(annotationBrowserTab); + CaretAssert(annotationBrowserTabValue); + + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + annotationBrowserTabValue->getBounds2D(minX, maxX, minY, maxY); + annotationBrowserTab->setBounds2D(minX, maxX, minY, maxY); + } + break; case AnnotationRedoUndoCommandModeEnum::COLOR_BACKGROUND: { CaretAssert(annotationValue); @@ -113,11 +161,11 @@ break; case AnnotationRedoUndoCommandModeEnum::COORDINATE_ONE: { - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(annotation); - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(annotation); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(annotation); + AnnotationOneCoordinateShape* twoDimShape = dynamic_cast(annotation); - const AnnotationOneDimensionalShape* valueOneDimShape = dynamic_cast(annotationValue); - const AnnotationTwoDimensionalShape* valueTwoDimShape = dynamic_cast(annotationValue); + const AnnotationTwoCoordinateShape* valueOneDimShape = dynamic_cast(annotationValue); + const AnnotationOneCoordinateShape* valueTwoDimShape = dynamic_cast(annotationValue); if ((oneDimShape != NULL) && (valueOneDimShape != NULL)) { @@ -138,9 +186,9 @@ break; case AnnotationRedoUndoCommandModeEnum::COORDINATE_ONE_AND_TWO: { - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(annotation); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(annotation); - const AnnotationOneDimensionalShape* valueOneDimShape = dynamic_cast(annotationValue); + const AnnotationTwoCoordinateShape* valueOneDimShape = dynamic_cast(annotationValue); if ((oneDimShape != NULL) && (valueOneDimShape != NULL)) { @@ -159,8 +207,8 @@ break; case AnnotationRedoUndoCommandModeEnum::COORDINATE_TWO: { - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(annotation); - const AnnotationOneDimensionalShape* valueOneDimShape = dynamic_cast(annotationValue); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(annotation); + const AnnotationTwoCoordinateShape* valueOneDimShape = dynamic_cast(annotationValue); if ((oneDimShape != NULL) && (valueOneDimShape != NULL)) { @@ -173,6 +221,22 @@ } } break; + case AnnotationRedoUndoCommandModeEnum::COORDINATE_MULTI: + { + AnnotationMultiCoordinateShape* multiCoordShape = dynamic_cast(annotation); + const AnnotationMultiCoordinateShape* valueMultiCoordShape = dynamic_cast(annotationValue); + + if ((multiCoordShape != NULL) + && (valueMultiCoordShape != NULL)) { + std::vector> coords; + valueMultiCoordShape->getCopyOfAllCoordinates(coords); + multiCoordShape->replaceAllCoordinates(coords); + } + else { + CaretAssert(0); + } + } + break; case AnnotationRedoUndoCommandModeEnum::CREATE_ANNOTATION: CaretAssertMessage(0, ("This mode " + AnnotationRedoUndoCommandModeEnum::toName(m_mode) @@ -193,6 +257,11 @@ + AnnotationRedoUndoCommandModeEnum::toName(m_mode) + " is handle in the redo() and undo() functions.")); break; + case AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATIONS: + CaretAssertMessage(0, ("This mode " + + AnnotationRedoUndoCommandModeEnum::toName(m_mode) + + " is handle in the redo() and undo() functions.")); + break; case AnnotationRedoUndoCommandModeEnum::GROUPING_GROUP: CaretAssert(0); break; @@ -241,15 +310,15 @@ break; case AnnotationRedoUndoCommandModeEnum::ROTATION_ANGLE: { - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); - const AnnotationTwoDimensionalShape* twoDimValue = dynamic_cast(annotationValue); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); + const AnnotationOneCoordinateShape* twoDimValue = dynamic_cast(annotationValue); if ((twoDimAnn != NULL) && (twoDimValue != NULL)) { twoDimAnn->setRotationAngle(twoDimValue->getRotationAngle()); } else { - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); - const AnnotationOneDimensionalShape* oneDimValue = dynamic_cast(annotationValue); + AnnotationTwoCoordinateShape* oneDimAnn = dynamic_cast(annotation); + const AnnotationTwoCoordinateShape* oneDimValue = dynamic_cast(annotationValue); if ((oneDimAnn != NULL) && (oneDimValue != NULL)) { oneDimAnn->getStartCoordinate()->setXYZ(oneDimValue->getStartCoordinate()->getXYZ()); @@ -258,6 +327,47 @@ } } break; + case AnnotationRedoUndoCommandModeEnum::STACKING_ORDER_ANNOTATIONS: + { + const AnnotationTwoCoordinateShape* oneDimAnn = annotationValue->castToTwoCoordinateShape(); + const AnnotationOneCoordinateShape* twoDimAnn = annotationValue->castToOneCoordinateShape(); + if (oneDimAnn != NULL) { + float xyz[3]; + oneDimAnn->getStartCoordinate()->getXYZ(xyz); + const float z1 = xyz[2]; + oneDimAnn->getEndCoordinate()->getXYZ(xyz); + const float z2 = xyz[2]; + + AnnotationTwoCoordinateShape* ann = annotation->castToTwoCoordinateShape(); + if (ann != NULL) { + ann->getStartCoordinate()->getXYZ(xyz); + xyz[2] = z1; + ann->getStartCoordinate()->setXYZ(xyz); + + ann->getEndCoordinate()->getXYZ(xyz); + xyz[2] = z2; + ann->getEndCoordinate()->setXYZ(xyz); + } + } + else if (twoDimAnn != NULL) { + float xyz[3]; + twoDimAnn->getCoordinate()->getXYZ(xyz); + const float z = xyz[2]; + + AnnotationOneCoordinateShape* ann = annotation->castToOneCoordinateShape(); + if (ann != NULL) { + ann->getCoordinate()->getXYZ(xyz); + xyz[2] = z; + ann->getCoordinate()->setXYZ(xyz); + } + } + } + break; + case AnnotationRedoUndoCommandModeEnum::STACKING_ORDER_BROWSER_TAB: + { + annotation->setStackingOrder(annotationValue->getStackingOrder()); + } + break; case AnnotationRedoUndoCommandModeEnum::TEXT_ALIGNMENT_HORIZONTAL: { AnnotationText* textAnn = dynamic_cast(annotation); @@ -425,8 +535,8 @@ break; case AnnotationRedoUndoCommandModeEnum::TWO_DIM_HEIGHT: { - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); - const AnnotationTwoDimensionalShape* twoDimValue = dynamic_cast(annotationValue); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); + const AnnotationOneCoordinateShape* twoDimValue = dynamic_cast(annotationValue); if ((twoDimAnn != NULL) && (twoDimValue != NULL)) { twoDimAnn->setHeight(twoDimValue->getHeight()); @@ -438,8 +548,8 @@ break; case AnnotationRedoUndoCommandModeEnum::TWO_DIM_WIDTH: { - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); - const AnnotationTwoDimensionalShape* twoDimValue = dynamic_cast(annotationValue); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); + const AnnotationOneCoordinateShape* twoDimValue = dynamic_cast(annotationValue); if ((twoDimAnn != NULL) && (twoDimValue != NULL)) { twoDimAnn->setWidth(twoDimValue->getWidth()); @@ -555,7 +665,8 @@ validFlag = false; } } - else if (m_mode == AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATION) { + else if ((m_mode == AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATION) + || (m_mode == AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATIONS)) { EventAnnotationAddToRemoveFromFile duplicateEvent(EventAnnotationAddToRemoveFromFile::MODE_DUPLICATE, annMem->m_annotationFile, annMem->m_annotation); @@ -689,7 +800,8 @@ validFlag = false; } } - else if (m_mode == AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATION) { + else if ((m_mode == AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATION) + || (m_mode == AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATIONS)) { EventAnnotationAddToRemoveFromFile duplicateEvent(EventAnnotationAddToRemoveFromFile::MODE_UNDUPLICATE, annMem->m_annotationFile, annMem->m_annotation); @@ -791,6 +903,221 @@ return true; } +/** + * Set them mode to all bounds and create the redo/undo instances. + * + * @param minX + * New minimum x-value of the annotation + * @param maxX + * New maximum x-value of the annotation + * @param minY + * New minimum y-value of the annotation + * @param maxY + * New minimum y-value of the annotation + * @param annotation + * Annotation that receive this new bounds (browser tabs only). + */ +void +AnnotationRedoUndoCommand::setBoundsAll(const float minX, + const float maxX, + const float minY, + const float maxY, + Annotation* annotation) +{ + CaretAssert(annotation); + + AnnotationBrowserTab* browserTabAnnotation = dynamic_cast(annotation); + if (browserTabAnnotation != NULL) { + m_mode = AnnotationRedoUndoCommandModeEnum::BOUNDS_2D_ALL; + setDescription("Bounds 2D ALL"); + + AnnotationBrowserTab* redoAnnotation = dynamic_cast(browserTabAnnotation->clone()); + CaretAssert(redoAnnotation); + redoAnnotation->setBounds2D(minX, maxX, minY, maxY); + + Annotation* undoAnnotation = browserTabAnnotation->clone(); + AnnotationMemento* am = new AnnotationMemento(annotation, + redoAnnotation, + undoAnnotation); + m_annotationMementos.push_back(am); + } + else { + CaretLogWarning("Annotation for bounds setting must be a Browser Tab Annotation"); + } +} + +/** + * Set background type and create the redo/undo instances. + * + * @param backgroundType + * New value for background type + * @param annotations + * Annotations that receive this background type (browser tabs only). + */ +void +AnnotationRedoUndoCommand::setBrowserTabBackground(const TileTabsLayoutBackgroundTypeEnum::Enum backgroundType, + const std::vector& annotations) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::BROWSER_TAB_BACKGROUND; + setDescription("Browser Tab Background"); + + for (std::vector::const_iterator iter = annotations.begin(); + iter != annotations.end(); + iter++) { + Annotation* annotation = *iter; + CaretAssert(annotation); + + AnnotationBrowserTab* browserTabAnnotation = dynamic_cast(annotation); + if (browserTabAnnotation != NULL) { + AnnotationBrowserTab* redoAnnotation = dynamic_cast(browserTabAnnotation->clone()); + CaretAssert(redoAnnotation); + + redoAnnotation->setBackgroundType(backgroundType); + + Annotation* undoAnnotation = browserTabAnnotation->clone(); + AnnotationMemento* am = new AnnotationMemento(annotation, + redoAnnotation, + undoAnnotation); + m_annotationMementos.push_back(am); + } + else { + CaretLogWarning("Annotation for background type must be a Browser Tab Annotation"); + } + } +} + +/** + * Set them mode to x-minimum and create the redo/undo instances. + * + * @param newMinX + * New minimum x-value of the annotation + * @param annotations + * Annotations that receive this new bounds (browser tabs only). + */ +void +AnnotationRedoUndoCommand::setBoundsMinX2D(const float newMinX, + const std::vector& annotations) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::BOUNDS_2D_SINGLE; + setDescription("Bounds 2D X-Min"); + setBounds2DHelper(BoundsType2D::MIN_X, + newMinX, + annotations); +} + +/** + * Set them mode to x-maximum and create the redo/undo instances. + * + * @param newMaxX + * New minimum x-value of the annotation + * @param annotations + * Annotations that receive this new bounds (browser tabs only). + */ +void +AnnotationRedoUndoCommand::setBoundsMaxX2D(const float newMaxX, + const std::vector& annotations) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::BOUNDS_2D_SINGLE; + setDescription("Bounds 2D X-Max"); + setBounds2DHelper(BoundsType2D::MAX_X, + newMaxX, + annotations); +} + +/** + * Set them mode to y-minimum and create the redo/undo instances. + * + * @param newMinY + * New minimum y-value of the annotation + * @param annotations + * Annotations that receive this new bounds (browser tabs only). + */ +void +AnnotationRedoUndoCommand::setBoundsMinY2D(const float newMinY, + const std::vector& annotations) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::BOUNDS_2D_SINGLE; + setDescription("Bounds 2D Y-Min"); + setBounds2DHelper(BoundsType2D::MIN_Y, + newMinY, + annotations); +} + +/** + * Set them mode to y-maximum and create the redo/undo instances. + * + * @param newMaxY + * New maximum y-value of the annotation + * @param annotations + * Annotations that receive this new bounds (browser tabs only). + */ +void +AnnotationRedoUndoCommand::setBoundsMaxY2D(const float newMaxY, + const std::vector& annotations) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::BOUNDS_2D_SINGLE; + setDescription("Bounds 2D Y-Max"); + setBounds2DHelper(BoundsType2D::MAX_Y, + newMaxY, + annotations); +} + +/** + * Helper for bounds 2D + * + * @param boundsType + * The bounds value bing set + * @param value + * New value + * @param annotation + * Annotations that have bounds changed. + */ +void +AnnotationRedoUndoCommand::setBounds2DHelper(const BoundsType2D boundsType, + const float value, + const std::vector& annotations) +{ + for (std::vector::const_iterator iter = annotations.begin(); + iter != annotations.end(); + iter++) { + Annotation* annotation = *iter; + CaretAssert(annotation); + + AnnotationBrowserTab* browserTabAnnotation = dynamic_cast(annotation); + if (browserTabAnnotation != NULL) { + AnnotationBrowserTab* redoAnnotation = dynamic_cast(browserTabAnnotation->clone()); + CaretAssert(redoAnnotation); + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + redoAnnotation->getBounds2D(minX, maxX, minY, maxY); + + switch (boundsType) { + case BoundsType2D::MIN_X: + minX = value; + break; + case BoundsType2D::MAX_X: + maxX = value; + break; + case BoundsType2D::MIN_Y: + minY = value; + break; + case BoundsType2D::MAX_Y: + maxY = value; + break; + } + + redoAnnotation->setBounds2D(minX, maxX, minY, maxY); + + Annotation* undoAnnotation = browserTabAnnotation->clone(); + AnnotationMemento* am = new AnnotationMemento(annotation, + redoAnnotation, + undoAnnotation); + m_annotationMementos.push_back(am); + } + else { + CaretLogWarning("Annotation for bounds setting must be a Browser Tab Annotation"); + } + } +} /** * Set them mode to first coordinate and create the redo/undo instances. @@ -814,8 +1141,8 @@ CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation); - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(redoAnnotation); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(redoAnnotation); + AnnotationOneCoordinateShape* twoDimShape = dynamic_cast(redoAnnotation); AnnotationCoordinate* redoCoordinate = NULL; if (oneDimShape != NULL) { @@ -866,7 +1193,7 @@ CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(redoAnnotation); if (oneDimShape != NULL) { AnnotationCoordinate* redoCoordinateOne = oneDimShape->getStartCoordinate(); @@ -958,7 +1285,7 @@ CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(redoAnnotation); AnnotationCoordinate* redoCoordinate = NULL; if (oneDimShape != NULL) { @@ -982,6 +1309,51 @@ } /** + * Set them mode to second coordinate and create the redo/undo instances. + * + * @param coordinate + * New value of the coordinate. + * @param annotations + * Annotations that receive this new coordinate. + */ +void +AnnotationRedoUndoCommand::setModeCoordinateMulti(const std::vector>& coordinates, + const std::vector& annotations) +{ + std::vector multiCoordAnns; + for (auto& ann : annotations) { + CaretAssert(ann); + AnnotationMultiCoordinateShape* mc = ann->castToMultiCoordinateShape(); + if (mc != NULL) { + multiCoordAnns.push_back(mc); + } + else { + CaretLogWarning("Attempting to apply set multi-coords on annotation that is does not support multi-coordinates: " + + ann->toString()); + } + } + if (multiCoordAnns.empty()) { + CaretLogWarning("No multi-coord annotations for setting coordinates"); + return; + } + + m_mode = AnnotationRedoUndoCommandModeEnum::COORDINATE_MULTI; + setDescription("Set coordinates for multi-coordinate annotations"); + + for (auto& ann : multiCoordAnns) { + CaretAssert(ann); + AnnotationMultiCoordinateShape* redoAnnotation = dynamic_cast(ann->clone()); + ann->replaceAllCoordinates(coordinates); + Annotation* undoAnnotation = ann->clone(); + AnnotationMemento* am = new AnnotationMemento(ann, + redoAnnotation, + undoAnnotation); + m_annotationMementos.push_back(am); + } +} + + +/** * Set them mode to line arrow start and create the redo/undo instances. * * @param newStatus @@ -1381,6 +1753,43 @@ m_annotationMementos.push_back(am); } +/** + * Set the mode to duplicate annotations and create the undo/redo instances. + * + * @param fileAndAnnotations + * Pairs with file and annotation + */ +void +AnnotationRedoUndoCommand::setModeDuplicateAnnotations(std::vector>& fileAndAnnotations) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::DUPLICATE_ANNOTATIONS; + setDescription("Duplicate Annotations"); + + CaretAssert(fileAndAnnotations.size() > 0); + + for (auto& fileAnn : fileAndAnnotations) { + AnnotationFile* file(fileAnn.first); + Annotation* annotation(fileAnn.second); + CaretAssert(file); + CaretAssert(annotation); + + /* + * NOTE: We only need the pointer since the file containing + * the annotation will handle delete/undelete of the + * annotation. If we don't use NULL for the redo and + * undo annotations, copies of the annotation would be + * needed since the AnnotationMemento will delete + * the redo and undo annotations when it is deleted. + */ + AnnotationMemento* am = new AnnotationMemento(file, + annotation, + NULL, + NULL); + + m_annotationMementos.push_back(am); + } +} + /** * Set the mode to rotation angle and create the undo/redo instances @@ -1403,10 +1812,10 @@ Annotation* annotation = *iter; CaretAssert(annotation); - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); + AnnotationTwoCoordinateShape* oneDimAnn = dynamic_cast(annotation); if (twoDimAnn != NULL) { - AnnotationTwoDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); + AnnotationOneCoordinateShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setRotationAngle(newRotationAngle); Annotation* undoAnnotation = annotation->clone(); @@ -1469,7 +1878,7 @@ const bool rotateAroundMiddleFlag = true; if (rotateAroundMiddleFlag) { - AnnotationOneDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); + AnnotationTwoCoordinateShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setRotationAngle(vpWidth, vpHeight, newRotationAngle); Annotation* undoAnnotation = annotation->clone(); @@ -1489,7 +1898,7 @@ annTwoY = annOneY + dy; - AnnotationOneDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); + AnnotationTwoCoordinateShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->getEndCoordinate()->setXYZFromViewportXYZ(vpWidth, vpHeight, annTwoX, annTwoY); Annotation* undoAnnotation = annotation->clone(); @@ -1504,6 +1913,34 @@ } +/** + * Set the mode to stacking order and created the undo/redo instances + * + * @param newStackingOrder + * New stacking order value + * @param annotations + * Annotations that receive the new stacking order + */ +void +AnnotationRedoUndoCommand::setModeStackingOrderBrowserTab(const int32_t newStackingOrder, + const std::vector& annotations) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::STACKING_ORDER_BROWSER_TAB; + setDescription("Stacking Order"); + + for (auto ann : annotations) { + CaretAssert(ann); + Annotation* redoAnnotation = ann->clone(); + CaretAssert(redoAnnotation); + redoAnnotation->setStackingOrder(newStackingOrder); + + Annotation* undoAnnotation = ann->clone(); + AnnotationMemento* am = new AnnotationMemento(ann, + redoAnnotation, + undoAnnotation); + m_annotationMementos.push_back(am); + } +} /** * Set the mode to horizontal text alignment and create the undo/redo instances @@ -1601,6 +2038,8 @@ switch (annotation->getType()) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -1609,6 +2048,10 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: redoAnnotation = dynamic_cast(annotation->clone()); break; @@ -2006,9 +2449,9 @@ Annotation* annotation = *iter; CaretAssert(annotation); - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); if (twoDimAnn != NULL) { - AnnotationTwoDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); + AnnotationOneCoordinateShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setHeight(newHeight); Annotation* undoAnnotation = annotation->clone(); @@ -2041,9 +2484,9 @@ Annotation* annotation = *iter; CaretAssert(annotation); - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); if (twoDimAnn != NULL) { - AnnotationTwoDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); + AnnotationOneCoordinateShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setWidth(newWidth); Annotation* undoAnnotation = annotation->clone(); @@ -2055,6 +2498,65 @@ } } +/** + * Set the mode to and process an annotation stacking order change (but NOT for browser tab annotations) + * + * @param annotations + * The annotations that are reordered + * @param stackingOrders + * New stacking orders for each of the annotations + * @param orderType + * Type of ordering + */ +void +AnnotationRedoUndoCommand::setModeStackingOrderAnnotations(const std::vector& annotations, + const std::vector& stackingOrders, + const AnnotationStackingOrderTypeEnum::Enum orderType) +{ + m_mode = AnnotationRedoUndoCommandModeEnum::STACKING_ORDER_ANNOTATIONS; + setDescription(AnnotationStackingOrderTypeEnum::toGuiName(orderType)); + + CaretAssert(annotations.size() == stackingOrders.size()); + + const int32_t numAnn = static_cast(annotations.size()); + for (int32_t i = 0; i < numAnn; i++) { + CaretAssertVectorIndex(annotations, i); + CaretAssertVectorIndex(stackingOrders, i); + + const float newZ = stackingOrders[i]; + + Annotation* redoAnnotation = annotations[i]->clone(); + CaretAssert(redoAnnotation); + AnnotationTwoCoordinateShape* oneDimAnn = redoAnnotation->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* twoDimAnn = redoAnnotation->castToOneCoordinateShape(); + if (oneDimAnn != NULL) { + float xyz[3]; + oneDimAnn->getStartCoordinate()->getXYZ(xyz); + xyz[2] = newZ; + oneDimAnn->getStartCoordinate()->setXYZ(xyz); + oneDimAnn->getEndCoordinate()->getXYZ(xyz); + xyz[2] = newZ; + oneDimAnn->getEndCoordinate()->setXYZ(xyz); + } + else if (twoDimAnn != NULL) { + float xyz[3]; + twoDimAnn->getCoordinate()->getXYZ(xyz); + xyz[2] = newZ; + twoDimAnn->getCoordinate()->setXYZ(xyz); + } + else { + CaretAssert(0); + } + + Annotation* undoAnnotation = annotations[i]->clone(); + + AnnotationMemento* am = new AnnotationMemento(annotations[i], + redoAnnotation, + undoAnnotation); + m_annotationMementos.push_back(am); + } +} + /** * Compare the two annotation mementos using the annotation pointer. * diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationRedoUndoCommand.h connectome-workbench-1.5.0/src/Annotations/AnnotationRedoUndoCommand.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationRedoUndoCommand.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationRedoUndoCommand.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,11 +22,12 @@ /*LICENSE_END*/ #include "AnnotationCoordinate.h" +#include "AnnotationStackingOrderTypeEnum.h" #include "AnnotationRedoUndoCommandModeEnum.h" #include "AnnotationText.h" #include "CaretColorEnum.h" #include "CaretUndoCommand.h" - +#include "TileTabsLayoutBackgroundTypeEnum.h" namespace caret { @@ -49,6 +50,27 @@ virtual bool mergeWith(const CaretUndoCommand* command); + void setBoundsAll(const float minX, + const float maxX, + const float minY, + const float maxY, + Annotation* annotation); + + void setBrowserTabBackground(const TileTabsLayoutBackgroundTypeEnum::Enum backgroundType, + const std::vector& annotations); + + void setBoundsMinX2D(const float minX, + const std::vector& annotations); + + void setBoundsMaxX2D(const float maxX, + const std::vector& annotations); + + void setBoundsMinY2D(const float minY, + const std::vector& annotations); + + void setBoundsMaxY2D(const float maxY, + const std::vector& annotations); + void setModeCoordinateOne(const AnnotationCoordinate& coordinate, const std::vector& annotations); @@ -59,6 +81,9 @@ void setModeCoordinateTwo(const AnnotationCoordinate& coordinate, const std::vector& annotations); + void setModeCoordinateMulti(const std::vector>& coordinates, + const std::vector& annotations); + void setModeLineArrowStart(const bool newStatus, const std::vector& annotations); @@ -99,9 +124,14 @@ void setModeDuplicateAnnotation(AnnotationFile* annotationFile, Annotation* annotation); + void setModeDuplicateAnnotations(std::vector>& fileAndAnnotations); + void setModeRotationAngle(const float newRotationAngle, const std::vector& annotations); + void setModeStackingOrderBrowserTab(const int32_t newStackingOrder, + const std::vector& annotations); + void setModeTextAlignmentHorizontal(const AnnotationTextAlignHorizontalEnum::Enum newHorizontalAlignment, const std::vector& annotations); @@ -149,10 +179,23 @@ void setModeTwoDimWidth(const float newWidth, const std::vector& annotations); + void setModeStackingOrderAnnotations(const std::vector& annotations, + const std::vector& stackingOrders, + const AnnotationStackingOrderTypeEnum::Enum orderType); // ADD_NEW_METHODS_HERE private: /** + * Used with bounds 2D helper + */ + enum class BoundsType2D { + MIN_X, + MAX_X, + MIN_Y, + MAX_Y + }; + + /** * The annotation memento contains copies of the * annotation before and after its modification. */ @@ -247,6 +290,10 @@ static bool lessThanAnnotationMemento(const AnnotationMemento* am1, const AnnotationMemento* am2); + void setBounds2DHelper(const BoundsType2D boundsType, + const float value, + const std::vector& annotations); + AnnotationRedoUndoCommandModeEnum::Enum m_mode; mutable std::vector m_annotationMementos; diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -110,6 +110,18 @@ "INVALID", "Invalid")); + enumData.push_back(AnnotationRedoUndoCommandModeEnum(BROWSER_TAB_BACKGROUND, + "BROWSER_TAB_BACKGROUND", + "Browser Tab Background")); + + enumData.push_back(AnnotationRedoUndoCommandModeEnum(BOUNDS_2D_ALL, + "BOUNDS_2D_ALL", + "Bounds 2D All")); + + enumData.push_back(AnnotationRedoUndoCommandModeEnum(BOUNDS_2D_SINGLE, + "BOUNDS_2D_SINGLE", + "Bounds 2D Single")); + enumData.push_back(AnnotationRedoUndoCommandModeEnum(COLOR_BACKGROUND, "COLOR_BACKGROUND", "Color - Background")); @@ -142,6 +154,10 @@ "DUPLICATE_ANNOTATION", "Duplicate Annotation")); + enumData.push_back(AnnotationRedoUndoCommandModeEnum(DUPLICATE_ANNOTATIONS, + "DUPLICATE_ANNOTATIONS", + "Duplicate Annotations")); + enumData.push_back(AnnotationRedoUndoCommandModeEnum(GROUPING_GROUP, "GROUPING_GROUP", "Group Annotations")); @@ -178,6 +194,14 @@ "ROTATION_ANGLE", "Rotation Angle")); + enumData.push_back(AnnotationRedoUndoCommandModeEnum(STACKING_ORDER_ANNOTATIONS, + "STACKING_ORDER_ANNOTATIONS", + "Stacking Order for Annotation")); + + enumData.push_back(AnnotationRedoUndoCommandModeEnum(STACKING_ORDER_BROWSER_TAB, + "STACKING_ORDER_BROWSER_TAB", + "Stacking Order for Browser Tab")); + enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_ALIGNMENT_HORIZONTAL, "TEXT_ALIGNMENT_HORIZONTAL", "Text Alignment Horizontal")); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationRedoUndoCommandModeEnum.h connectome-workbench-1.5.0/src/Annotations/AnnotationRedoUndoCommandModeEnum.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationRedoUndoCommandModeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationRedoUndoCommandModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,6 +37,12 @@ enum Enum { /** Invalid mode */ INVALID, + /** background for annotation browser tab */ + BROWSER_TAB_BACKGROUND, + /** Set all of the bounds in 2D */ + BOUNDS_2D_ALL, + /** Set one of the bounds in 2D */ + BOUNDS_2D_SINGLE, /** Color - Background */ COLOR_BACKGROUND, /** Color - Foreground */ @@ -47,6 +53,8 @@ COORDINATE_ONE_AND_TWO, /** Coordinate Two */ COORDINATE_TWO, + /** Coordinates in Multi-Coord Annotation */ + COORDINATE_MULTI, /** Create an annotation */ CREATE_ANNOTATION, /** Cut Annotation */ @@ -55,6 +63,8 @@ DELETE_ANNOTATIONS, /** Duplicate Annotation */ DUPLICATE_ANNOTATION, + /** Duplicate Annotations */ + DUPLICATE_ANNOTATIONS, /** Group Annotations */ GROUPING_GROUP, /** Regroup Annotations */ @@ -73,6 +83,10 @@ PASTE_ANNOTATION, /** Rotation Angle */ ROTATION_ANGLE, + /** Stacking order for annotations (except browser tabs) */ + STACKING_ORDER_ANNOTATIONS, + /** Stacking order for browser tab */ + STACKING_ORDER_BROWSER_TAB, /** Text Alignment Horizontal */ TEXT_ALIGNMENT_HORIZONTAL, /** Text Alignment Vertical */ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBar.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBar.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBar.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBar.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,1226 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __ANNOTATION_SCALE_BAR_DECLARE__ +#include "AnnotationScaleBar.h" +#undef __ANNOTATION_SCALE_BAR_DECLARE__ + +#include "AnnotationCoordinate.h" +#include "AnnotationPercentSizeText.h" +#include "CaretAssert.h" +#include "EventAnnotationTextGetBounds.h" +#include "EventManager.h" +#include "MathFunctions.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationScaleBar + * \brief Annotation used for drawing a color bar. + * \ingroup Annotations + */ + +/** + * Constructor. + * + * @param attributeDefaultType + * Type for attribute defaults + */ +AnnotationScaleBar::AnnotationScaleBar(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) +: AnnotationOneCoordinateShape(AnnotationTypeEnum::SCALE_BAR, + attributeDefaultType), +AnnotationFontAttributesInterface() +{ + reset(); + + initializeScaleBarInstance(); + + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + m_sceneAssistant->add("m_length", + &m_length); + m_sceneAssistant->add("m_showLengthTextFlag", + &m_showLengthTextFlag); + m_sceneAssistant->add("m_showLengthUnitsTextFlag", + &m_showLengthUnitsTextFlag); + m_sceneAssistant->add("m_lengthTextLocation", + &m_lengthTextLocation); + + m_sceneAssistant->add("m_lengthUnits", + &m_lengthUnits); + + m_sceneAssistant->add("m_fontName", + &m_fontName); + m_sceneAssistant->add("m_fontPercentViewportHeight", + &m_fontPercentViewportHeight); + m_sceneAssistant->add("m_positionMode", + &m_positionMode); + m_sceneAssistant->add("m_displayedFlag", + &m_displayedFlag); + + m_sceneAssistant->add("m_showTickMarksFlag", + &m_showTickMarksFlag); + m_sceneAssistant->add("m_tickMarksSubdivisions", + &m_tickMarksSubdivisions); + m_sceneAssistant->add("m_colorText", + &m_colorText); + m_sceneAssistant->addArray("m_customColorText", + m_customColorText, 4, 1.0); + } +} +/** + * Destructor. + */ +AnnotationScaleBar::~AnnotationScaleBar() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +AnnotationScaleBar::AnnotationScaleBar(const AnnotationScaleBar& obj) +: AnnotationOneCoordinateShape(obj), +AnnotationFontAttributesInterface() +{ + initializeScaleBarInstance(); + + this->copyHelperAnnotationScaleBar(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +AnnotationScaleBar& +AnnotationScaleBar::operator=(const AnnotationScaleBar& obj) +{ + if (this != &obj) { + AnnotationOneCoordinateShape::operator=(obj); + this->copyHelperAnnotationScaleBar(obj); + } + return *this; +} + +/** + * Initialize an instance of the scale bar + */ +void +AnnotationScaleBar::initializeScaleBarInstance() +{ + m_sceneAssistant.grabNew(new SceneClassAssistant()); + m_lengthTextAnnotation.reset(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); + m_lengthTextAnnotation->setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); + m_lengthTextAnnotation->setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); +} + +/** + * Helps with copying an object of this type. + * + * @param obj + * Object that is copied. + */ +void +AnnotationScaleBar::copyHelperAnnotationScaleBar(const AnnotationScaleBar& obj) +{ + m_length = obj.m_length; + m_lengthUnits = obj.m_lengthUnits; + m_showLengthTextFlag = obj.m_showLengthTextFlag; + m_showLengthUnitsTextFlag = obj.m_showLengthUnitsTextFlag; + m_showTickMarksFlag = obj.m_showTickMarksFlag; + m_tickMarksSubdivisions = obj.m_tickMarksSubdivisions; + m_modelSpaceOrthographicWidth = obj.m_modelSpaceOrthographicWidth; + m_modelSpaceViewportWidth = obj.m_modelSpaceViewportWidth; + m_modelSpaceViewportHeight = obj.m_modelSpaceViewportHeight; + + m_positionMode = obj.m_positionMode; + m_fontName = obj.m_fontName; + m_fontPercentViewportHeight = obj.m_fontPercentViewportHeight; + m_positionMode = obj.m_positionMode; + m_displayedFlag = obj.m_displayedFlag; + m_colorText = obj.m_colorText; + m_customColorText[0] = obj.m_customColorText[0]; + m_customColorText[1] = obj.m_customColorText[1]; + m_customColorText[2] = obj.m_customColorText[2]; + m_customColorText[3] = obj.m_customColorText[3]; + m_fontTooSmallWhenLastDrawnFlag = obj.m_fontTooSmallWhenLastDrawnFlag; + + *m_lengthTextAnnotation = *obj.m_lengthTextAnnotation; +} + +/** + * Reset the annotation colorbar. + * + * DO NOT make this method virtual is it is called from constructor. + */ +void +AnnotationScaleBar::reset() +{ + resetSizeAttributes(); + setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); + setTabIndex(-1); + setLineWidthPercentage(0.5); + + m_fontName = AnnotationTextFontNameEnum::getDefaultFontName(); + m_positionMode = AnnotationColorBarPositionModeEnum::AUTOMATIC; + m_displayedFlag = false; + m_showTickMarksFlag = false; + m_tickMarksSubdivisions = 2; + + m_colorText = CaretColorEnum::WHITE; + m_customColorText[0] = 1.0; + m_customColorText[1] = 1.0; + m_customColorText[2] = 1.0; + m_customColorText[3] = 1.0; + + setLineColor(CaretColorEnum::WHITE); + setBackgroundColor(CaretColorEnum::BLACK); + setLength(25.0); + + m_fontTooSmallWhenLastDrawnFlag = false; +} + +/** + * Reset the size attributes of the color bar. + */ +void +AnnotationScaleBar::resetSizeAttributes() +{ + setWidth(25.0); + setHeight(7.0); + setRotationAngle(0.0); + m_fontPercentViewportHeight = 3.33; +} + +/** + * @return Length of scale bar + */ +float +AnnotationScaleBar::getLength() const +{ + return m_length; +} + +/** + * Sets the length of the scale bar + * @param length + * New length + */ +void +AnnotationScaleBar::setLength(const float length) +{ + m_length = length; +} + +/** + * @return Show length text + */ +bool +AnnotationScaleBar::isShowLengthText() const +{ + return m_showLengthTextFlag; +} + +/** + * Sets show the length text + * @param status + * New statius + */ +void +AnnotationScaleBar::setShowLengthText(const bool status) +{ + m_showLengthTextFlag = status; +} + +/** + * @return Show length units text + */ +bool +AnnotationScaleBar::isShowLengthUnitsText() const +{ + return m_showLengthUnitsTextFlag; +} + +/** + * Sets show the length units text + * @param status + * New status + */ +void +AnnotationScaleBar::setShowLengthUnitsText(const bool status) +{ + m_showLengthUnitsTextFlag = status; +} + +/** + * @return Showtick marks + */ +bool +AnnotationScaleBar::isShowTickMarks() const +{ + return m_showTickMarksFlag; +} + +/** + * Sets show the tick marks + * @param status + * New status + */ +void +AnnotationScaleBar::setShowTickMarks(const bool status) +{ + m_showTickMarksFlag = status; +} + +/** + * @return Number of tick marks subdivisions + */ +int32_t +AnnotationScaleBar::getTickMarksSubdivsions() const +{ + return m_tickMarksSubdivisions; +} + +/** + * Set the number of tick marks subdivisions + * @param subdivisions + * Number of subdivisions + */ +void +AnnotationScaleBar::setTickMarksSubdivisions(const int32_t subdivisions) +{ + m_tickMarksSubdivisions = subdivisions; +} + +/** + * @return Length units + */ + +float +AnnotationScaleBar::getTickMarksHeight() const +{ + const float height(std::max(1.0, + (getLineWidthPercentage() / 2.0))); + return height; +} + +/** + * @return Length units + */ +AnnotationScaleBarUnitsTypeEnum::Enum +AnnotationScaleBar::getLengthUnits() const +{ + return m_lengthUnits; +} + +/** + * Sets the length of the scale bar + * @param lengthUnits + * New length units + */ +void +AnnotationScaleBar::setLengthUnits(const AnnotationScaleBarUnitsTypeEnum::Enum lengthUnits) +{ + m_lengthUnits = lengthUnits; +} + +/** + * Set the orthographic width when the model is drawn + * @param modelSpaceOrthographicWidth + * Width of the orthographic projection when the model was drawn. + * + * Note: This width is set when the model is drawn in model space. The scale bar is drawn + * in tab space but the length of the scale bar is in model space. This length is used with the + * scale bar's length and the tab space viewport to draw the scale bar in the proper size. + */ +void +AnnotationScaleBar::setModelSpaceOrthographicWidth(const float modelSpaceOrthographicWidth) +{ + m_modelSpaceOrthographicWidth = modelSpaceOrthographicWidth; +} + +/** + * @return The orthographic width from when the model was drawn + */ +float +AnnotationScaleBar::getModelSpaceOrthographicWidth() const +{ + return m_modelSpaceOrthographicWidth; +} + +/** + * Set the viewport width when the model is drawn (viewport in which model was drawn) + * @param modelSpaceViewportWidth + * Width of the viewport when the model was drawn. + * @param modelSpaceViewportHeight + * Heighty of the viewport when the model was drawn. + * + * Note: This width and height is set when the model is drawn in model space. The scale bar is drawn + * in tab space but the length of the scale bar is in model space. This width is used with the + * scale bar's length and the tab space viewport to draw the scale bar in the proper size. + */ +void +AnnotationScaleBar::setModelSpaceViewportWidthAndHeight(const float modelSpaceViewportWidth, + const float modelSpaceViewportHeight) +{ + m_modelSpaceViewportWidth = modelSpaceViewportWidth; + m_modelSpaceViewportHeight = modelSpaceViewportHeight; +} + +/** + * @return The viewport width from when the model was drawn (viewport in which model is drawn) + */ +float +AnnotationScaleBar::getModelSpaceViewportWidth() const +{ + return m_modelSpaceViewportWidth; +} + +/** + * @return The viewport height from when the model was drawn (viewport in which model is drawn) + */ +float +AnnotationScaleBar::getModelSpaceViewportHeight() const +{ + return m_modelSpaceViewportHeight; +} + +/** + * @return The font. + */ +AnnotationTextFontNameEnum::Enum +AnnotationScaleBar::getFont() const +{ + return m_fontName; +} + +/** + * Set the font. + * + * @param font + * New value for font. + */ +void +AnnotationScaleBar::setFont(const AnnotationTextFontNameEnum::Enum font) +{ + if (font != m_fontName) { + m_fontName = font; + setModified(); + } +} + +/** + * @return THe percent viewport height for the font. + */ +float +AnnotationScaleBar::getFontPercentViewportSize() const +{ + return m_fontPercentViewportHeight; +} + +/** + * Set the percent viewport size for the font. + * + * @param fontPercentViewportHeight + * New value for percent viewport height. + */ +void +AnnotationScaleBar::setFontPercentViewportSize(const float fontPercentViewportHeight) +{ + if (fontPercentViewportHeight != m_fontPercentViewportHeight) { + m_fontPercentViewportHeight = fontPercentViewportHeight; + setModified(); + } +} + +/** + * @return The foreground color. + */ +CaretColorEnum::Enum +AnnotationScaleBar::getTextColor() const +{ + return m_colorText; +} + +/** + * Set the foreground color. + * + * @param color + * New value for foreground color. + */ +void +AnnotationScaleBar::setTextColor(const CaretColorEnum::Enum color) +{ + if (m_colorText != color) { + m_colorText = color; + setModified(); + } +} + +/** + * Get the foreground color's RGBA components regardless of + * coloring (custom color or a CaretColorEnum) selected by the user. + * + * @param rgbaOut + * RGBA components ranging 0.0 to 1.0. + */ +void +AnnotationScaleBar::getTextColorRGBA(float rgbaOut[4]) const +{ + switch (m_colorText) { + case CaretColorEnum::NONE: + rgbaOut[0] = 0.0; + rgbaOut[1] = 0.0; + rgbaOut[2] = 0.0; + rgbaOut[3] = 0.0; + break; + case CaretColorEnum::CUSTOM: + getCustomTextColor(rgbaOut); + break; + case CaretColorEnum::AQUA: + case CaretColorEnum::BLACK: + case CaretColorEnum::BLUE: + case CaretColorEnum::FUCHSIA: + case CaretColorEnum::GRAY: + case CaretColorEnum::GREEN: + case CaretColorEnum::LIME: + case CaretColorEnum::MAROON: + case CaretColorEnum::NAVY: + case CaretColorEnum::OLIVE: + case CaretColorEnum::PURPLE: + case CaretColorEnum::RED: + case CaretColorEnum::SILVER: + case CaretColorEnum::TEAL: + case CaretColorEnum::WHITE: + case CaretColorEnum::YELLOW: + CaretColorEnum::toRGBAFloat(m_colorText, + rgbaOut); + rgbaOut[3] = 1.0; + break; + } +} + +/** + * Get the foreground color's RGBA components regardless of + * coloring (custom color or a CaretColorEnum) selected by the user. + * + * @param rgbaOut + * RGBA components ranging 0 to 255. + */ +void +AnnotationScaleBar::getTextColorRGBA(uint8_t rgbaOut[4]) const +{ + float rgbaFloat[4] = { 0.0, 0.0, 0.0, 0.0 }; + getTextColorRGBA(rgbaFloat); + + rgbaOut[0] = static_cast(rgbaFloat[0] * 255.0); + rgbaOut[1] = static_cast(rgbaFloat[1] * 255.0); + rgbaOut[2] = static_cast(rgbaFloat[2] * 255.0); + rgbaOut[3] = static_cast(rgbaFloat[3] * 255.0); +} + +/** + * Get the foreground color. + * + * @param rgbaOut + * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. + */ +void +AnnotationScaleBar::getCustomTextColor(float rgbaOut[4]) const +{ + rgbaOut[0] = m_customColorText[0]; + rgbaOut[1] = m_customColorText[1]; + rgbaOut[2] = m_customColorText[2]; + rgbaOut[3] = m_customColorText[3]; +} + +/** + * Get the foreground color. + * + * @param rgbaOut + * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. + */ +void +AnnotationScaleBar::getCustomTextColor(uint8_t rgbaOut[4]) const +{ + rgbaOut[0] = static_cast(m_customColorText[0] * 255.0); + rgbaOut[1] = static_cast(m_customColorText[1] * 255.0); + rgbaOut[2] = static_cast(m_customColorText[2] * 255.0); + rgbaOut[3] = static_cast(m_customColorText[3] * 255.0); +} + +/** + * Set the foreground color with floats. + * + * @param rgba + * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. + */ +void +AnnotationScaleBar::setCustomTextColor(const float rgba[4]) +{ + for (int32_t i = 0; i < 4; i++) { + if (rgba[i] != m_customColorText[i]) { + m_customColorText[i] = rgba[i]; + setModified(); + } + } +} + +/** + * Set the foreground color with unsigned bytes. + * + * @param rgba + * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. + */ +void +AnnotationScaleBar::setCustomTextColor(const uint8_t rgba[4]) +{ + for (int32_t i = 0; i < 4; i++) { + const float component = rgba[i] / 255.0; + if (component != m_customColorText[i]) { + m_customColorText[i] = component; + setModified(); + } + } +} + +/** + * @return + * Is bold enabled ? + */ +bool +AnnotationScaleBar::isBoldStyleEnabled() const +{ + return false; +} + +/** + * Set bold enabled. + * + * @param enabled + * New status for bold enabled. + */ +void +AnnotationScaleBar::setBoldStyleEnabled(const bool /*enabled*/) +{ +} + +/** + * @return + * Is italic enabled ? + */ +bool +AnnotationScaleBar::isItalicStyleEnabled() const +{ + return false; +} + +/** + * Set italic enabled. + * + * @param enabled + * New status for italic enabled. + */ +void +AnnotationScaleBar::setItalicStyleEnabled(const bool /*enabled*/) +{ +} + +/** + * @return + * Is underline enabled ? + */ +bool +AnnotationScaleBar::isUnderlineStyleEnabled() const +{ + return false; +} + +/** + * Set underline enabled. + * + * @param enabled + * New status for underline enabled. + */ +void +AnnotationScaleBar::setUnderlineStyleEnabled(const bool /*enabled*/) +{ +} + +/** + * @return + * Is outline enabled ? + */ +bool +AnnotationScaleBar::isOutlineStyleEnabled() const +{ + return false; +} + +/** + * Set outline enabled. + * + * @param enabled + * New status for outline enabled. + */ +void +AnnotationScaleBar::setOutlineStyleEnabled(const bool /*enabled*/) +{ +} + +/** + * @return The position mode for the colorbar annotation. + */ +AnnotationColorBarPositionModeEnum::Enum +AnnotationScaleBar::getPositionMode() const +{ + return m_positionMode; +} + +/** + * Set the position mode for the colorbar. + * + * @param positionMode + * New position mode for the colorbar. + */ +void +AnnotationScaleBar::setPositionMode(const AnnotationColorBarPositionModeEnum::Enum positionMode) +{ + if (positionMode != m_positionMode) { + m_positionMode = positionMode; + setModified(); + } +} + +/** + * @return Display status of colorbar. + */ +bool +AnnotationScaleBar::isDisplayed() const +{ + return m_displayedFlag; +} + +/** + * Set the color bar annotation displayed. + * + * Note that this also sets the annotation's selection + * status to off so that if the user turns off display + * of the annotation while the annotation is selected + * the annotation does not show up as selected when + * the color bar is later displayed by the user. + * + * @param displayed + * New status for display of colorbar. + */ +void +AnnotationScaleBar::setDisplayed(const bool displayed) +{ + if (displayed != m_displayedFlag) { + m_displayedFlag = displayed; + setDeselectedForEditing(); + setModified(); + } +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +AnnotationScaleBar::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, + sceneClass); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +AnnotationScaleBar::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, + sceneClass); + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + +/** + * @return Is the font too small when it is last drawn + * that may cause an OpenGL error and, as a result, + * the text is not seen by the user. + */ +bool +AnnotationScaleBar::isFontTooSmallWhenLastDrawn() const +{ + return m_fontTooSmallWhenLastDrawnFlag; +} + +/** + * Set the font too small status + * @param tooSmallFontFlag + * New status + */ +void +AnnotationScaleBar::setFontTooSmallWhenLastDrawn(const bool tooSmallFontFlag) const +{ + m_fontTooSmallWhenLastDrawnFlag = tooSmallFontFlag; +} + +/* + * @return Location for length text + */ +AnnotationScaleBarTextLocationEnum::Enum +AnnotationScaleBar::getLengthTextLocation() const +{ + return m_lengthTextLocation; +} + +/** + * Set the location of the length text + * @param location + * New location of length text + */ +void +AnnotationScaleBar::setLengthTextLocation(const AnnotationScaleBarTextLocationEnum::Enum location) +{ + m_lengthTextLocation = location; +} + +/** + * Get drawing information for drawing a scale bar + * * + * @param tabViewportWidth + * Width of tab's viewport + * @param tabViewportHeight + * Height of tab's viewport + * @param viewportXYZ + * Bottom corner of viewport + * @param selectionFlag + * True if selection mode + * @param drawingInfoOut + * Upon exit, contains drawing information + */ +void +AnnotationScaleBar::getScaleBarDrawingInfo(const float tabViewportWidth, + const float tabViewportHeight, + const std::array& viewportXYZ, + const bool selectionFlag, + DrawingInfo& drawingInfoOut) const +{ + drawingInfoOut.reset(); + + float convertToMM(1.0); + switch (getLengthUnits()) { + case AnnotationScaleBarUnitsTypeEnum::CENTIMETERS: + convertToMM = 10.0; + break; + case AnnotationScaleBarUnitsTypeEnum::MICROMETERS: + convertToMM = 0.10; + break; + case AnnotationScaleBarUnitsTypeEnum::MILLIMETERS: + convertToMM = 1.0; + break; + } + + const float scaleBarLengthModelCoords(getLength() * convertToMM); + const float orthographicWidth(getModelSpaceOrthographicWidth()); + if (orthographicWidth <= 0.0) { + return; + } + if (m_modelSpaceViewportWidth <= 0.0) { + return; + } + if (m_modelSpaceViewportHeight <= 0.0) { + return; + } + + /* + * Get the width and height of the text + * Text size is percentage of the TAB's viewport width/height so that + * the text size is the same in and out of surface/volume montage modes + */ + EventAnnotationTextGetBounds textBoundsEvent(*getLengthTextAnnotation(), + tabViewportWidth, + tabViewportHeight); + EventManager::get()->sendEvent(textBoundsEvent.getPointer()); + const float textDrawingWidth(textBoundsEvent.getTextWidth()); + const float textDrawingHeight(textBoundsEvent.getTextHeight()); + + /* + * Scale bar uses line width for height (not annotation height). + * Width of scale bar is in MODEL space so must use the model space's viewport width + * Height of scale bar is in TAB so must use height of TAB's viewport so that + * the height of scale bar is same in and out of surface/volume montage modes + */ + const float percentageWidth(scaleBarLengthModelCoords / orthographicWidth); + const float scaleBarWidthPixels(m_modelSpaceViewportWidth * percentageWidth); + float scaleBarHeightPixels((getLineWidthPercentage() / 100.0) * tabViewportHeight); + + if (selectionFlag) { + if (getBackgroundColor() == CaretColorEnum::NONE) { + /* + * When in selection mode and there is no background, + * increase the with of the scale bar by a fex pixels. + * Otherwise, the bar may be very thin making it difficult + * for the user to select + */ + scaleBarHeightPixels += 4; + } + } + + std::vector tickMarkBounds; + float tickHeight(0.0); + float tickWidth(0.0); + if (isShowTickMarks()) { + const int32_t numSubDiv = getTickMarksSubdivsions(); + if (numSubDiv > 0) { + const int32_t tickCount(numSubDiv + 1); + tickWidth = MathFunctions::limitRange(scaleBarHeightPixels * 0.10, 2.0, 6.0); + tickHeight = MathFunctions::limitRange(scaleBarHeightPixels * 0.25, 4.0, 10.0); + + const float halfTickWidth(tickWidth / 2.0); + const float startX(0.0); + const float endX(scaleBarWidthPixels); + const float xRange((endX - startX)); + const float deltaX(xRange / numSubDiv); + + float tickX(startX); + const float y(0); + + /* + * Create bounds for each tick mark + */ + for (int32_t i = 0; i < tickCount; i++) { + int32_t x(tickX); + if (i > 0) { + if (i == (tickCount - 1)) { + x = scaleBarWidthPixels - tickWidth; + } + else { + x -= halfTickWidth; + } + } + /* bottom left */ + tickMarkBounds.push_back(x); + tickMarkBounds.push_back(y); + tickMarkBounds.push_back(0.0); + + /* bottom right */ + tickMarkBounds.push_back(x + tickWidth); + tickMarkBounds.push_back(y); + tickMarkBounds.push_back(0.0); + + /* top right */ + tickMarkBounds.push_back(x + tickWidth); + tickMarkBounds.push_back(y + tickHeight); + tickMarkBounds.push_back(0.0); + + /* top left */ + tickMarkBounds.push_back(x); + tickMarkBounds.push_back(y + tickHeight); + tickMarkBounds.push_back(0.0); + + tickX += deltaX; + } + } + } + + float textWidth(0.0); + float textHeight(0.0); + const float spaceBetweenScaleBarAndText(5.0); + if (isShowLengthText()) { + textWidth = textDrawingWidth + spaceBetweenScaleBarAndText; + textHeight = textDrawingHeight; + } + + switch (m_lengthTextLocation) { + case AnnotationScaleBarTextLocationEnum::BOTTOM: + break; + case AnnotationScaleBarTextLocationEnum::RIGHT: + break; + } + + + const float barAndTicksHeight(scaleBarHeightPixels + tickHeight); + const float margin(3.0); + + float totalWidth(0.0); + float totalHeight(0.0); + switch (m_lengthTextLocation) { + case AnnotationScaleBarTextLocationEnum::BOTTOM: + totalWidth = (std::max(scaleBarWidthPixels, + textWidth) + + (margin * 2.0)); + totalHeight = (barAndTicksHeight + + textHeight + + (margin * 2.0)); + break; + case AnnotationScaleBarTextLocationEnum::RIGHT: + totalWidth = scaleBarWidthPixels + textWidth + (margin * 2.0); + totalHeight = (std::max(barAndTicksHeight, + textHeight) + + (margin * 2.0)); + break; + } + + /* + * Overall (background) bounds + * bottom left + */ + drawingInfoOut.m_backgroundBounds[0] = viewportXYZ[0]; + drawingInfoOut.m_backgroundBounds[1] = viewportXYZ[1]; + drawingInfoOut.m_backgroundBounds[2] = viewportXYZ[2]; + + /* bottom right */ + drawingInfoOut.m_backgroundBounds[3] = viewportXYZ[0] + totalWidth; + drawingInfoOut.m_backgroundBounds[4] = viewportXYZ[1]; + drawingInfoOut.m_backgroundBounds[5] = viewportXYZ[2]; + + /* top right */ + drawingInfoOut.m_backgroundBounds[6] = viewportXYZ[0] + totalWidth; + drawingInfoOut.m_backgroundBounds[7] = viewportXYZ[1] + totalHeight; + drawingInfoOut.m_backgroundBounds[8] = viewportXYZ[2]; + + /* top left */ + drawingInfoOut.m_backgroundBounds[9] = viewportXYZ[0]; + drawingInfoOut.m_backgroundBounds[10] = viewportXYZ[1] + totalHeight; + drawingInfoOut.m_backgroundBounds[11] = viewportXYZ[2]; + + /* + * Vertically align bar with text + */ + float barOffsetX(0.0); + float barOffsetY(0.0); + switch (m_lengthTextLocation) { + case AnnotationScaleBarTextLocationEnum::BOTTOM: + if (scaleBarWidthPixels < textWidth) { + barOffsetX = (textWidth - scaleBarWidthPixels) / 2.0; + } + barOffsetY = textHeight; + break; + case AnnotationScaleBarTextLocationEnum::RIGHT: + if (barAndTicksHeight < textHeight) { + barOffsetY = (textHeight - barAndTicksHeight) / 2.0; + } + break; + } + + /* + * Bounds of the bar + * bottom left + */ + drawingInfoOut.m_barBounds[0] = viewportXYZ[0] + margin + barOffsetX; + drawingInfoOut.m_barBounds[1] = viewportXYZ[1] + margin + barOffsetY; + drawingInfoOut.m_barBounds[2] = viewportXYZ[2]; + + /* bottom right */ + drawingInfoOut.m_barBounds[3] = viewportXYZ[0] + margin + barOffsetX + scaleBarWidthPixels; + drawingInfoOut.m_barBounds[4] = viewportXYZ[1] + margin + barOffsetY; + drawingInfoOut.m_barBounds[5] = viewportXYZ[2]; + + /* top right */ + drawingInfoOut.m_barBounds[6] = viewportXYZ[0] + margin + barOffsetX + scaleBarWidthPixels; + drawingInfoOut.m_barBounds[7] = viewportXYZ[1] + margin + scaleBarHeightPixels + barOffsetY; + drawingInfoOut.m_barBounds[8] = viewportXYZ[2]; + + /* top left */ + drawingInfoOut.m_barBounds[9] = viewportXYZ[0] + margin + barOffsetX; + drawingInfoOut.m_barBounds[10] = viewportXYZ[1] + margin + scaleBarHeightPixels + barOffsetY; + drawingInfoOut.m_barBounds[11] = viewportXYZ[2]; + + if (isShowLengthText()) { + std::array textBottomLeft { 0.0, 0.0, 0.0 }; + switch (m_lengthTextLocation) { + case AnnotationScaleBarTextLocationEnum::BOTTOM: + { + const float textOffsetX(totalWidth / 2.0); + + /* + * Text is below the BAR + */ + drawingInfoOut.m_textStartXYZ[0] = viewportXYZ[0] + textOffsetX; + drawingInfoOut.m_textStartXYZ[1] = viewportXYZ[1] + margin; + drawingInfoOut.m_textStartXYZ[2] = viewportXYZ[2]; + + m_lengthTextAnnotation->setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + m_lengthTextAnnotation->setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + + /* + * Offset to bottom left corner + */ + textBottomLeft[0] = drawingInfoOut.m_textStartXYZ[0] - (textWidth / 2.0); + textBottomLeft[1] = drawingInfoOut.m_textStartXYZ[1]; + textBottomLeft[2] = drawingInfoOut.m_textStartXYZ[2]; + } + break; + case AnnotationScaleBarTextLocationEnum::RIGHT: + /* + * Text is on right side of the BAR + * X -> to the right side of the bar + * Y -> centered in BACKGROUND BOUNDS + * Z -> all Z's are same + */ + drawingInfoOut.m_textStartXYZ[0] = drawingInfoOut.m_barBounds[3] + spaceBetweenScaleBarAndText; + drawingInfoOut.m_textStartXYZ[1] = drawingInfoOut.m_backgroundBounds[4] + (totalHeight / 2.0); + drawingInfoOut.m_textStartXYZ[2] = drawingInfoOut.m_barBounds[5]; + + m_lengthTextAnnotation->setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); + m_lengthTextAnnotation->setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); + + /* + * Offset to bottom left corner + */ + textBottomLeft[0] = drawingInfoOut.m_textStartXYZ[0]; + textBottomLeft[1] = drawingInfoOut.m_textStartXYZ[1] - (textHeight / 2.0); + textBottomLeft[2] = drawingInfoOut.m_textStartXYZ[2]; + break; + } + + /* bottom left text bounds */ + drawingInfoOut.m_textBounds[0] = textBottomLeft[0]; + drawingInfoOut.m_textBounds[1] = textBottomLeft[1]; + drawingInfoOut.m_textBounds[2] = textBottomLeft[2]; + + /* bottom right text bounds */ + drawingInfoOut.m_textBounds[3] = textBottomLeft[0] + textWidth; + drawingInfoOut.m_textBounds[4] = textBottomLeft[1]; + drawingInfoOut.m_textBounds[5] = textBottomLeft[2]; + + /* top right text bounds */ + drawingInfoOut.m_textBounds[6] = textBottomLeft[0] + textWidth; + drawingInfoOut.m_textBounds[7] = textBottomLeft[1] + textHeight; + drawingInfoOut.m_textBounds[8] = textBottomLeft[2]; + + /* top left text bounds */ + drawingInfoOut.m_textBounds[9] = textBottomLeft[0]; + drawingInfoOut.m_textBounds[10] = textBottomLeft[1] + textHeight; + drawingInfoOut.m_textBounds[11] = textBottomLeft[2]; + } + + if (isShowTickMarks()) { + const float topLeftXYZ[3] = { + drawingInfoOut.m_barBounds[9], + drawingInfoOut.m_barBounds[10], + drawingInfoOut.m_barBounds[11] + }; + + const float maxBarX(drawingInfoOut.m_barBounds[3]); + + /* + * ticks are relative to top left of the bar + */ + const int32_t numTickBounds = static_cast(tickMarkBounds.size() / 12); + for (int32_t i = 0; i < numTickBounds; i++) { + const int32_t i12(i * 12); + CaretAssertVectorIndex(tickMarkBounds, i12 + 11); + std::array tb { + /* bottom left */ + tickMarkBounds[i12] + topLeftXYZ[0], + tickMarkBounds[i12+1] + topLeftXYZ[1], + tickMarkBounds[i12+2] + topLeftXYZ[2], + /* bottom right */ + tickMarkBounds[i12+3] + topLeftXYZ[0], + tickMarkBounds[i12+4] + topLeftXYZ[1], + tickMarkBounds[i12+5] + topLeftXYZ[2], + /* top right */ + tickMarkBounds[i12+6] + topLeftXYZ[0], + tickMarkBounds[i12+7] + topLeftXYZ[1], + tickMarkBounds[i12+8] + topLeftXYZ[2], + /* top left */ + tickMarkBounds[i12+9] + topLeftXYZ[0], + tickMarkBounds[i12+10] + topLeftXYZ[1], + tickMarkBounds[i12+11] + topLeftXYZ[2] + }; + + /* + * Adjust bounds of last tick so that its right-most X-coord + * is the same as the scale bar + */ + if (i == (numTickBounds - 1)) { + tb[0] = maxBarX - tickWidth; + tb[3] = maxBarX; + tb[6] = maxBarX; + tb[9] = maxBarX - tickWidth; + } + + drawingInfoOut.m_ticksBounds.push_back(tb); + } + } + + drawingInfoOut.setValid(true); +} + +/** + * @return Pointer to annotation for drawing the length text + */ +const AnnotationPercentSizeText* +AnnotationScaleBar::getLengthTextAnnotation() const +{ + m_lengthTextAnnotation->setFont(getFont()); + + m_lengthTextAnnotation->setFontPercentViewportSize(getFontPercentViewportSize()); + m_lengthTextAnnotation->setTextColor(CaretColorEnum::CUSTOM); + float rgba[4]; + getTextColorRGBA(rgba); + m_lengthTextAnnotation->setCustomTextColor(rgba); + m_lengthTextAnnotation->setRotationAngle(0.0); + + const int32_t numDecimals(1); + QString lengthText(QString::number(getLength(), 'f', numDecimals)); + if (isShowLengthUnitsText()) { + lengthText.append(AnnotationScaleBarUnitsTypeEnum::toGuiName(getLengthUnits())); + } + m_lengthTextAnnotation->setText(lengthText); + + return m_lengthTextAnnotation.get(); +} + diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBar.h connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBar.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBar.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBar.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,276 @@ +#ifndef __ANNOTATION_SCALE_BAR_H__ +#define __ANNOTATION_SCALE_BAR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#include "AnnotationColorBarPositionModeEnum.h" +#include "AnnotationScaleBarTextLocationEnum.h" +#include "AnnotationScaleBarUnitsTypeEnum.h" +#include "AnnotationFontAttributesInterface.h" +#include "AnnotationTextAlignHorizontalEnum.h" +#include "AnnotationOneCoordinateShape.h" + +namespace caret { + + class AnnotationPercentSizeText; + + class AnnotationScaleBar : public AnnotationOneCoordinateShape, public AnnotationFontAttributesInterface { + + public: + /** + * Contains information for drawing scale bar, its ticks, and text + */ + class DrawingInfo { + public: + /** + * Constructor + */ + DrawingInfo() { + reset(); + } + + /** + * Reset to invalid + */ + void reset() { + m_backgroundBounds.fill(0.0); + m_barBounds.fill(0.0); + m_textStartXYZ.fill(0.0); + m_textBounds.fill(0.0); + m_ticksBounds.clear(); + m_validFlag = false; + } + + /** Returns true if drawing info is valid */ + bool isValid() const { return m_validFlag; } + + /** Set the validity status @param status new status */ + void setValid(const bool status) { m_validFlag = status; } + + /** bounds are bottomLeft, bottomRight, topRight, topLeft */ + std::array m_backgroundBounds; + + /** bounds are bottomLeft, bottomRight, topRight, topLeft */ + std::array m_barBounds; + + /** starting XYZ for text containing length characters */ + std::array m_textStartXYZ; + + /** bounds of the text are bottomLeft, bottomRight, topRight, topLeft */ + std::array m_textBounds; + + /** bounds are bottomLeft, bottomRight, topRight, topLeft for each tick mark */ + std::vector> m_ticksBounds; + + private: + bool m_validFlag = false; + }; + + AnnotationScaleBar(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); + + virtual ~AnnotationScaleBar(); + + AnnotationScaleBar(const AnnotationScaleBar& obj); + + AnnotationScaleBar& operator=(const AnnotationScaleBar& obj); + + /** + * @return this annotation cast to AnnotationScaleBar (NULL if not a scale bar) + * Intended for overriding by the annotation type + */ + virtual AnnotationScaleBar* castToScaleBar() override { return this; } + + /** + * @return this annotation cast to AnnotationScaleBar (NULL if not a scale bar) const method + * Intended for overriding by the annotation type + */ + virtual const AnnotationScaleBar* castToScaleBar() const override { return this; } + + void reset(); + + void resetSizeAttributes(); + + float getLength() const; + + void setLength(const float length); + + bool isShowLengthText() const; + + void setShowLengthText(const bool status); + + bool isShowLengthUnitsText() const; + + void setShowLengthUnitsText(const bool status); + + bool isShowTickMarks() const; + + void setShowTickMarks(const bool status); + + int32_t getTickMarksSubdivsions() const; + + void setTickMarksSubdivisions(const int32_t subdivisions); + + float getTickMarksHeight() const; + + AnnotationScaleBarUnitsTypeEnum::Enum getLengthUnits() const; + + void setLengthUnits(const AnnotationScaleBarUnitsTypeEnum::Enum lengthUnits); + + void setModelSpaceOrthographicWidth(const float modelSpaceOrthographicWidth); + + float getModelSpaceOrthographicWidth() const; + + void setModelSpaceViewportWidthAndHeight(const float modelSpaceViewportWidth, + const float modelSpaceViewportHeight); + + float getModelSpaceViewportWidth() const; + + float getModelSpaceViewportHeight() const; + + virtual AnnotationTextFontNameEnum::Enum getFont() const; + + virtual void setFont(const AnnotationTextFontNameEnum::Enum font); + + virtual float getFontPercentViewportSize() const; + + virtual void setFontPercentViewportSize(const float fontPercentViewportHeight); + + AnnotationColorBarPositionModeEnum::Enum getPositionMode() const; + + void setPositionMode(const AnnotationColorBarPositionModeEnum::Enum positionMode); + + virtual CaretColorEnum::Enum getTextColor() const; + + virtual void setTextColor(const CaretColorEnum::Enum color); + + virtual void getTextColorRGBA(float rgbaOut[4]) const; + + virtual void getTextColorRGBA(uint8_t rgbaOut[4]) const; + + virtual void getCustomTextColor(float rgbaOut[4]) const; + + virtual void getCustomTextColor(uint8_t rgbaOut[4]) const; + + virtual void setCustomTextColor(const float rgba[4]); + + virtual void setCustomTextColor(const uint8_t rgba[4]); + + virtual bool isBoldStyleEnabled() const; + + virtual void setBoldStyleEnabled(const bool enabled); + + virtual bool isItalicStyleEnabled() const; + + virtual void setItalicStyleEnabled(const bool enabled); + + virtual bool isUnderlineStyleEnabled() const; + + virtual void setUnderlineStyleEnabled(const bool enabled); + + virtual bool isOutlineStyleEnabled() const; + + virtual void setOutlineStyleEnabled(const bool enabled); + + bool isDisplayed() const; + + void setDisplayed(const bool displayed); + + bool isFontTooSmallWhenLastDrawn() const override; + + void setFontTooSmallWhenLastDrawn(const bool tooSmallFontFlag) const override; + + void getScaleBarDrawingInfo(const float tabViewportWidth, + const float tabViewportHeight, + const std::array& viewportXYZ, + const bool selectionFlag, + DrawingInfo& drawingInfoOut) const; + + const AnnotationPercentSizeText* getLengthTextAnnotation() const; + + AnnotationScaleBarTextLocationEnum::Enum getLengthTextLocation() const; + + void setLengthTextLocation(const AnnotationScaleBarTextLocationEnum::Enum location); + + // ADD_NEW_METHODS_HERE + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private: + void copyHelperAnnotationScaleBar(const AnnotationScaleBar& obj); + + void initializeScaleBarInstance(); + + CaretPointer m_sceneAssistant; + + float m_length = 10.0; + + bool m_showLengthTextFlag = true; + + bool m_showLengthUnitsTextFlag = true; + + bool m_showTickMarksFlag = false; + + int32_t m_tickMarksSubdivisions = 2; + + AnnotationScaleBarUnitsTypeEnum::Enum m_lengthUnits = AnnotationScaleBarUnitsTypeEnum::MILLIMETERS; + + float m_modelSpaceOrthographicWidth = 0.0; + + float m_modelSpaceViewportWidth = 0.0; + + float m_modelSpaceViewportHeight = 0.0; + + AnnotationTextFontNameEnum::Enum m_fontName; + + float m_fontPercentViewportHeight; + + AnnotationColorBarPositionModeEnum::Enum m_positionMode; + + CaretColorEnum::Enum m_colorText; + + float m_customColorText[4]; + + bool m_displayedFlag; + + AnnotationScaleBarTextLocationEnum::Enum m_lengthTextLocation = AnnotationScaleBarTextLocationEnum::BOTTOM; + + mutable bool m_fontTooSmallWhenLastDrawnFlag = false; + + mutable std::unique_ptr m_lengthTextAnnotation; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __ANNOTATION_SCALE_BAR_DECLARE__ + // +#endif // __ANNOTATION_SCALE_BAR_DECLARE__ + +} // namespace +#endif //__ANNOTATION_SCALE_BAR_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBarTextLocationEnum.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBarTextLocationEnum.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBarTextLocationEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBarTextLocationEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,375 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_DECLARE__ +#include "AnnotationScaleBarTextLocationEnum.h" +#undef __ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::AnnotationScaleBarTextLocationEnum + * \brief + * + * + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_annotationScaleBarTextLocationEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void annotationScaleBarTextLocationEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "AnnotationScaleBarTextLocationEnum.h" + * + * Instatiate: + * m_annotationScaleBarTextLocationEnumComboBox = new EnumComboBoxTemplate(this); + * m_annotationScaleBarTextLocationEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_annotationScaleBarTextLocationEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(annotationScaleBarTextLocationEnumComboBoxItemActivated())); + * + * Update the selection: + * m_annotationScaleBarTextLocationEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const AnnotationScaleBarTextLocationEnum::Enum VARIABLE = m_annotationScaleBarTextLocationEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +AnnotationScaleBarTextLocationEnum::AnnotationScaleBarTextLocationEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +AnnotationScaleBarTextLocationEnum::~AnnotationScaleBarTextLocationEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +AnnotationScaleBarTextLocationEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(AnnotationScaleBarTextLocationEnum(BOTTOM, + "BOTTOM", + "Bottom")); + + enumData.push_back(AnnotationScaleBarTextLocationEnum(RIGHT, + "RIGHT", + "Right")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const AnnotationScaleBarTextLocationEnum* +AnnotationScaleBarTextLocationEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const AnnotationScaleBarTextLocationEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +AnnotationScaleBarTextLocationEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const AnnotationScaleBarTextLocationEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +AnnotationScaleBarTextLocationEnum::Enum +AnnotationScaleBarTextLocationEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationScaleBarTextLocationEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationScaleBarTextLocationEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type AnnotationScaleBarTextLocationEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +AnnotationScaleBarTextLocationEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const AnnotationScaleBarTextLocationEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +AnnotationScaleBarTextLocationEnum::Enum +AnnotationScaleBarTextLocationEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationScaleBarTextLocationEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationScaleBarTextLocationEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type AnnotationScaleBarTextLocationEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +AnnotationScaleBarTextLocationEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const AnnotationScaleBarTextLocationEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +AnnotationScaleBarTextLocationEnum::Enum +AnnotationScaleBarTextLocationEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationScaleBarTextLocationEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationScaleBarTextLocationEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type AnnotationScaleBarTextLocationEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +AnnotationScaleBarTextLocationEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +AnnotationScaleBarTextLocationEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(AnnotationScaleBarTextLocationEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +AnnotationScaleBarTextLocationEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(AnnotationScaleBarTextLocationEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBarTextLocationEnum.h connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBarTextLocationEnum.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBarTextLocationEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBarTextLocationEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,104 @@ +#ifndef __ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_H__ +#define __ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class AnnotationScaleBarTextLocationEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + BOTTOM, + /** */ + RIGHT + }; + + + ~AnnotationScaleBarTextLocationEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + AnnotationScaleBarTextLocationEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const AnnotationScaleBarTextLocationEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_DECLARE__ +std::vector AnnotationScaleBarTextLocationEnum::enumData; +bool AnnotationScaleBarTextLocationEnum::initializedFlag = false; +int32_t AnnotationScaleBarTextLocationEnum::integerCodeCounter = 0; +#endif // __ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_DECLARE__ + +} // namespace +#endif //__ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBarUnitsTypeEnum.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBarUnitsTypeEnum.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBarUnitsTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBarUnitsTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __ANNOTATION_SCALE_BAR_UNITS_TYPE_ENUM_DECLARE__ +#include "AnnotationScaleBarUnitsTypeEnum.h" +#undef __ANNOTATION_SCALE_BAR_UNITS_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::AnnotationScaleBarUnitsTypeEnum + * \brief Units type for scale bar length text + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_annotationScaleBarUnitsTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void annotationScaleBarUnitsTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "AnnotationScaleBarUnitsTypeEnum.h" + * + * Instatiate: + * m_annotationScaleBarUnitsTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_annotationScaleBarUnitsTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_annotationScaleBarUnitsTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(annotationScaleBarUnitsTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_annotationScaleBarUnitsTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const AnnotationScaleBarUnitsTypeEnum::Enum VARIABLE = m_annotationScaleBarUnitsTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +AnnotationScaleBarUnitsTypeEnum::AnnotationScaleBarUnitsTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +AnnotationScaleBarUnitsTypeEnum::~AnnotationScaleBarUnitsTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +AnnotationScaleBarUnitsTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(AnnotationScaleBarUnitsTypeEnum(MICROMETERS, + "MICROMETERS", + "um")); + + enumData.push_back(AnnotationScaleBarUnitsTypeEnum(MILLIMETERS, + "MILLIMETERS", + "mm")); + + enumData.push_back(AnnotationScaleBarUnitsTypeEnum(CENTIMETERS, + "CENTIMETERS", + "cm")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const AnnotationScaleBarUnitsTypeEnum* +AnnotationScaleBarUnitsTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const AnnotationScaleBarUnitsTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +AnnotationScaleBarUnitsTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const AnnotationScaleBarUnitsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +AnnotationScaleBarUnitsTypeEnum::Enum +AnnotationScaleBarUnitsTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationScaleBarUnitsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationScaleBarUnitsTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type AnnotationScaleBarUnitsTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +AnnotationScaleBarUnitsTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const AnnotationScaleBarUnitsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +AnnotationScaleBarUnitsTypeEnum::Enum +AnnotationScaleBarUnitsTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationScaleBarUnitsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationScaleBarUnitsTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type AnnotationScaleBarUnitsTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +AnnotationScaleBarUnitsTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const AnnotationScaleBarUnitsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +AnnotationScaleBarUnitsTypeEnum::Enum +AnnotationScaleBarUnitsTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationScaleBarUnitsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationScaleBarUnitsTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type AnnotationScaleBarUnitsTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +AnnotationScaleBarUnitsTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +AnnotationScaleBarUnitsTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(AnnotationScaleBarUnitsTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +AnnotationScaleBarUnitsTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(AnnotationScaleBarUnitsTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBarUnitsTypeEnum.h connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBarUnitsTypeEnum.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationScaleBarUnitsTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationScaleBarUnitsTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __ANNOTATION_SCALE_BAR_UNITS_TYPE_ENUM_H__ +#define __ANNOTATION_SCALE_BAR_UNITS_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class AnnotationScaleBarUnitsTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** micrometers*/ + MICROMETERS, + /** millimeters*/ + MILLIMETERS, + /** centimeters */ + CENTIMETERS + }; + + + ~AnnotationScaleBarUnitsTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + AnnotationScaleBarUnitsTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const AnnotationScaleBarUnitsTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __ANNOTATION_SCALE_BAR_UNITS_TYPE_ENUM_DECLARE__ +std::vector AnnotationScaleBarUnitsTypeEnum::enumData; +bool AnnotationScaleBarUnitsTypeEnum::initializedFlag = false; +int32_t AnnotationScaleBarUnitsTypeEnum::integerCodeCounter = 0; +#endif // __ANNOTATION_SCALE_BAR_UNITS_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__ANNOTATION_SCALE_BAR_UNITS_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationSizingHandleTypeEnum.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationSizingHandleTypeEnum.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationSizingHandleTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationSizingHandleTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -154,6 +154,9 @@ "ANNOTATION_SIZING_HANDLE_LINE_START", "Line Start")); + enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE, + "ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE", + "Coordinate in a Poly Line")); } /** diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationSizingHandleTypeEnum.h connectome-workbench-1.5.0/src/Annotations/AnnotationSizingHandleTypeEnum.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationSizingHandleTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationSizingHandleTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -58,7 +58,9 @@ /** Line End */ ANNOTATION_SIZING_HANDLE_LINE_END, /** Line Start */ - ANNOTATION_SIZING_HANDLE_LINE_START + ANNOTATION_SIZING_HANDLE_LINE_START, + /** Point in poly line */ + ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE }; diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationSpatialModification.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationSpatialModification.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationSpatialModification.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationSpatialModification.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -55,6 +55,8 @@ * Change in mouse X-coordinate. * @param mouseDY * Change in mouse Y-coordinate. + * @param polyLineCoordinateIndex + * Index of poly line coordinate * @param startOfDraggingFlag * True when user starts to drag mouse. */ @@ -67,6 +69,7 @@ const float mouseY, const float mouseDX, const float mouseDY, + const int32_t polyLineCoordinateIndex, const bool startOfDraggingFlag) : CaretObject(), m_sizingHandleType(sizingHandleType), @@ -78,6 +81,7 @@ m_mouseY(mouseY), m_mouseDX(mouseDX), m_mouseDY(mouseDY), +m_polyLineCoordinateIndex(polyLineCoordinateIndex), m_startOfDraggingFlag(startOfDraggingFlag) { m_chartCoordAtMouseXY.m_chartXYZValid = false; diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationSpatialModification.h connectome-workbench-1.5.0/src/Annotations/AnnotationSpatialModification.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationSpatialModification.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationSpatialModification.h 2021-02-16 19:46:47.000000000 +0000 @@ -42,6 +42,7 @@ const float mouseY, const float mouseDX, const float mouseDY, + const int32_t polyLineCoordinateIndex, const bool startOfDraggingFlag); void setSurfaceCoordinateAtMouseXY(const StructureEnum::Enum structure, @@ -138,6 +139,8 @@ const float m_mouseDY; + const int32_t m_polyLineCoordinateIndex; + const bool m_startOfDraggingFlag; ChartCoord m_chartCoordAtMouseXY; @@ -150,9 +153,10 @@ // ADD_NEW_MEMBERS_HERE - friend class AnnotationOneDimensionalShape; + friend class AnnotationMultiCoordinateShape; friend class AnnotationText; - friend class AnnotationTwoDimensionalShape; + friend class AnnotationOneCoordinateShape; + friend class AnnotationTwoCoordinateShape; }; #ifdef __ANNOTATION_SPATIAL_MODIFICATION_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationStackingOrderOperation.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationStackingOrderOperation.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationStackingOrderOperation.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationStackingOrderOperation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,571 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_STACKING_ORDER_OPERATION_DECLARE__ +#include "AnnotationStackingOrderOperation.h" +#undef __ANNOTATION_STACKING_ORDER_OPERATION_DECLARE__ + +#include +#include + +#include "Annotation.h" +#include "AnnotationCoordinate.h" +#include "AnnotationTwoCoordinateShape.h" +#include "AnnotationText.h" +#include "AnnotationOneCoordinateShape.h" +#include "CaretAssert.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationStackingOrderOperation + * \brief Perform a stacking order operation + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param mode + * Mode to apply or not apply new stacking order + * @param annotations + * The annotations that are reordered + * @param selectedAnnotation + * The selected annotation that causes reordering + * @param windowIndex + * Index of window + */ +AnnotationStackingOrderOperation::AnnotationStackingOrderOperation(const Mode mode, + const std::vector& annotations, + const Annotation* selectedAnnotation, + const int32_t windowIndex) +: CaretObject(), +m_mode(mode), +m_annotations(annotations), +m_selectedAnnotation(selectedAnnotation), +m_windowIndex(windowIndex) +{ + CaretAssert(m_selectedAnnotation); +} + +/** + * Destructor. + */ +AnnotationStackingOrderOperation::~AnnotationStackingOrderOperation() +{ +} + +/** + * Run an annotation reordering operation + * + * @param orderType + * Type of ordering + * @param errorMessageOut + * Output with error information + * @return + * True if successful, else false + */ +bool +AnnotationStackingOrderOperation::runOrdering(const AnnotationStackingOrderTypeEnum::Enum orderType, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + if ( ! validateInput(errorMessageOut)) { + return false; + } + + std::vector annotationOrderAndContent; + if ( ! preOrderAnnotations(annotationOrderAndContent, + errorMessageOut)) { + return false; + } + + const bool debugFlag(false); + if (debugFlag) { + std::cout << "PRE-ORDERED" << std::endl; + printOrderedAnnotations(annotationOrderAndContent); + } + /* + * Now that that annotations are sorted by stacking order, + * assign a "stack order" using only even numbers. This allows + * the stack order of the selected annotation to change by + * plus or minus one. In addition, find the annotation that + * is closest behind and closest in front of the selected + * annotation. + */ + int32_t indexOfSelectedAnnotation(-1); + int32_t indexOfAnnotationBehind(-1); + int32_t indexOfAnnotationInFront(-1); + + if ( ! findAnnotationIndices(annotationOrderAndContent, + indexOfSelectedAnnotation, + indexOfAnnotationBehind, + indexOfAnnotationInFront, + errorMessageOut)) { + return false; + } + CaretAssert(indexOfSelectedAnnotation >= 0); + + const int32_t numberOfAnnotations = static_cast(annotationOrderAndContent.size()); + + if (debugFlag) { + std::cout << "INDICES: " << std::endl; + std::cout << "In front: " << indexOfAnnotationInFront << std::endl; + std::cout << "Selected: " << indexOfSelectedAnnotation << std::endl; + std::cout << "Behind: " << indexOfAnnotationBehind << std::endl; + printOrderedAnnotations(annotationOrderAndContent); + } + + /* + * Now change the stack order of the annotation. + * Since earlier, the stacking orders were made (0, 2, 4, ...) we only need to + * add or subtract 1 to the stack order using the reference annotation that is in front or back + */ + switch (orderType) { + case AnnotationStackingOrderTypeEnum::BRING_FORWARD: + if (indexOfAnnotationInFront >= 0) { + /* + * Move forward one position + */ + CaretAssertVectorIndex(annotationOrderAndContent, indexOfAnnotationInFront); + CaretAssertVectorIndex(annotationOrderAndContent, indexOfSelectedAnnotation); + annotationOrderAndContent[indexOfSelectedAnnotation].m_stackOrder = annotationOrderAndContent[indexOfAnnotationInFront].m_stackOrder - 1; + } + break; + case AnnotationStackingOrderTypeEnum::BRING_TO_FRONT: + { + /* + * Move to in front of all annotations + */ + const int32_t frontIndex = 0; + CaretAssertVectorIndex(annotationOrderAndContent, frontIndex); + CaretAssertVectorIndex(annotationOrderAndContent, indexOfSelectedAnnotation); + annotationOrderAndContent[indexOfSelectedAnnotation].m_stackOrder = annotationOrderAndContent[frontIndex].m_stackOrder - 1; + } + break; + case AnnotationStackingOrderTypeEnum::SEND_BACKWARD: + if (indexOfAnnotationBehind >= 0) { + /* + * Move back one position + */ + CaretAssertVectorIndex(annotationOrderAndContent, indexOfAnnotationBehind); + CaretAssertVectorIndex(annotationOrderAndContent, indexOfSelectedAnnotation); + annotationOrderAndContent[indexOfSelectedAnnotation].m_stackOrder = annotationOrderAndContent[indexOfAnnotationBehind].m_stackOrder + 1; + } + break; + case AnnotationStackingOrderTypeEnum::SEND_TO_BACK: + { + /* + * Move to behind all annotations + */ + const int32_t backIndex = static_cast(annotationOrderAndContent.size() - 1); + CaretAssertVectorIndex(annotationOrderAndContent, backIndex); + CaretAssertVectorIndex(annotationOrderAndContent, indexOfSelectedAnnotation); + annotationOrderAndContent[indexOfSelectedAnnotation].m_stackOrder = annotationOrderAndContent[backIndex].m_stackOrder + 1; + } + break; + } + + /* + * Now sort again since the sort order has changed + */ + std::sort(annotationOrderAndContent.begin(), + annotationOrderAndContent.end()); + + if (debugFlag) { + std::cout << "AFTER ORDERING: " << std::endl; + printOrderedAnnotations(annotationOrderAndContent); + } + + if (debugFlag) { + std::cout << "FINAL: " << std::endl; + } + /* + * Update annotations with new sort order or Z-coordinate + */ + for (int32_t indx = 0; indx < numberOfAnnotations; indx++) { + CaretAssertVectorIndex(annotationOrderAndContent, indx); + Annotation* ann = annotationOrderAndContent[indx].m_annotation; + CaretAssert(ann); + + /* + * Order with even numbers. Users may modify the order + * and it creates "space" to reduce annotations with same + * order value. + */ + const int32_t orderValue = (indx + 1); + + if (debugFlag) { + std::cout << orderValue << " " << ann->toString() << std::endl; + } + switch (ann->getType()) { + case AnnotationTypeEnum::BROWSER_TAB: + case AnnotationTypeEnum::COLOR_BAR: + case AnnotationTypeEnum::SCALE_BAR: + switch (m_mode) { + case Mode::MODE_APPLY_NEW_ORDER_TO_ANNOTAIONS: + ann->setStackingOrder(orderValue); + break; + case Mode::MODE_REQUEST_NEW_ORDER_VALUES: + m_newStackingOrderResults.push_back(NewStackingOrder(ann, orderValue)); + break; + } + break; + case AnnotationTypeEnum::BOX: + case AnnotationTypeEnum::IMAGE: + case AnnotationTypeEnum::LINE: + case AnnotationTypeEnum::OVAL: + case AnnotationTypeEnum::POLY_LINE: + case AnnotationTypeEnum::TEXT: + { + switch (m_mode) { + case Mode::MODE_APPLY_NEW_ORDER_TO_ANNOTAIONS: + { + AnnotationTwoCoordinateShape* oneDim = ann->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* twoDim = ann->castToOneCoordinateShape(); + if (oneDim != NULL) { + setCoordinateZ(oneDim->getStartCoordinate(), orderValue); + setCoordinateZ(oneDim->getEndCoordinate(), orderValue); + } + else if (twoDim != NULL) { + setCoordinateZ(twoDim->getCoordinate(), orderValue); + } + else { + CaretAssert(0); + } + break; + } + case Mode::MODE_REQUEST_NEW_ORDER_VALUES: + m_newStackingOrderResults.push_back(NewStackingOrder(ann, orderValue)); + break; + } + } + break; + } + } + + return true; +} + +/** + * Set the Z-component of an annotation coordinate + * + * @param coordinate + * The annotation coordinate + * @param z + * New Z value + */ +void +AnnotationStackingOrderOperation::setCoordinateZ(AnnotationCoordinate* coordinate, + const float z) +{ + CaretAssert(coordinate); + + float xyz[3] { 0.0, 0.0, 0.0 }; + coordinate->getXYZ(xyz); + xyz[2] = z; + coordinate->setXYZ(xyz); +} + +/** + * Find indices of the seleted, behind, and in front annotations + * + * @param indexOfSelectedAnnotation + * Index of the selected annotation in the ordered annotations + * @param indexOfAnnotationBehind + * Index of the annotation immediately behind the selected annotation in the ordered annotations + * May be invalid (-1) if selected annotation is in back of all annotations + * @param indexOfAnnotationInFront + * Index of the annotation immediately in front of the selected annotation in the ordered annotations + * May be invalid (-1) if selected annotation is in front of all annotations + * @param errorMessageOut + * Ouput with error message + * @return True if successful, else false + */ +bool +AnnotationStackingOrderOperation::findAnnotationIndices(const std::vector& annotationOrderAndContent, + int32_t& indexOfSelectedAnnotation, + int32_t& indexOfAnnotationBehind, + int32_t& indexOfAnnotationInFront, + AString& errorMessageOut) +{ + indexOfSelectedAnnotation = -1; + indexOfAnnotationBehind = -1; + indexOfAnnotationInFront = -1; + + const int32_t numAnn = static_cast(annotationOrderAndContent.size()); + for (int32_t i = 0; i < numAnn; i++) { + if (annotationOrderAndContent[i].m_annotation == m_selectedAnnotation) { + indexOfSelectedAnnotation = i; + break; + } + } + + if (indexOfSelectedAnnotation >= 0) { + for (int32_t i = (indexOfSelectedAnnotation - 1); i >= 0; i--) { + CaretAssertVectorIndex(annotationOrderAndContent, i); + if (annotationOrderAndContent[i].m_overlapsFlag) { + indexOfAnnotationInFront = i; + break; + } + } + + for (int32_t i = (indexOfSelectedAnnotation + 1); i < numAnn; i++) { + CaretAssertVectorIndex(annotationOrderAndContent, i); + if (annotationOrderAndContent[i].m_overlapsFlag) { + indexOfAnnotationBehind = i; + break; + } + } + } + else { + errorMessageOut = "Unable to find index of selected annotation in ordered annotations"; + return false; + } + + return true; +} + +/** + * Sort the annotations front to back prior to applying the ordering operation + * + * @param annotationOrderAndContent + * Output with annotations and their ordering + * @param errorMessageOut + * Contains error information + * @return True if input is valid, else false. + */ +bool +AnnotationStackingOrderOperation::preOrderAnnotations(std::vector& annotationOrderAndContent, + AString& errorMessageOut) +{ + /* + * Sort annotations by current stacking order + */ + for (const auto ann : m_annotations) { + CaretAssert(ann); + + switch (ann->getType()) { + case AnnotationTypeEnum::BROWSER_TAB: + case AnnotationTypeEnum::COLOR_BAR: + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::BOX: + case AnnotationTypeEnum::IMAGE: + case AnnotationTypeEnum::LINE: + case AnnotationTypeEnum::OVAL: + case AnnotationTypeEnum::POLY_LINE: + case AnnotationTypeEnum::TEXT: + { + const AnnotationTwoCoordinateShape* oneDim = ann->castToTwoCoordinateShape(); + const AnnotationOneCoordinateShape* twoDim = ann->castToOneCoordinateShape(); + float xyz[3] { 0.0, 0.0, 0.0 }; + if (oneDim != NULL) { + oneDim->getStartCoordinate()->getXYZ(xyz); + } + else if (twoDim != NULL) { + twoDim->getCoordinate()->getXYZ(xyz); + } + else { + CaretAssert(0); + } + /* + * Coords are float but stack order is integer so scale the coord + */ + ann->setStackingOrder(static_cast(xyz[2] * m_coordToStackOrderScaleFactor)); + } + break; + } + + bool overlapsSelectedAnnotationFlag(false); + if (ann != m_selectedAnnotation) { + if (m_selectedAnnotation->intersectionTest(ann, m_windowIndex)) { + overlapsSelectedAnnotationFlag = true; + } + } + annotationOrderAndContent.emplace_back(ann, + ann->getStackingOrder(), + overlapsSelectedAnnotationFlag); + } + + if (annotationOrderAndContent.size() <= 1) { + errorMessageOut = "There must be at least two annotations selected for ordering"; + return false; + } + + + std::sort(annotationOrderAndContent.begin(), + annotationOrderAndContent.end()); + + /* + * Update order indices so that all are a multiple of 2. + * Later, when an annotation is moved, its order can + * simply be incremented or decremented to move in front or behind + */ + int32_t orderIndex(0); + for (auto& ao : annotationOrderAndContent) { + ao.m_stackOrder = orderIndex; + orderIndex += 2; + } + + return true; +} + +/** + * Print the current ordering of the annotations + */ +void +AnnotationStackingOrderOperation::printOrderedAnnotations(const std::vector& annotationOrderAndContent) +{ + for (auto& ao : annotationOrderAndContent) { + std::cout << ao.m_stackOrder << " " << AString::fromBool(ao.m_overlapsFlag) << " " << ao.m_annotation->toString() << std::endl; + } +} + +/** + * Validate the input to verify space of selected annotation is supported for + * reordering and to filter out annoations in other spaces + * + * @param errorMessageOut + * Contains error information + * @return True if input is valid, else false. + */ +bool +AnnotationStackingOrderOperation::validateInput(AString& errorMessageOut) +{ + bool supportedSpaceFlag(false); + CaretAssert(m_selectedAnnotation); + switch (m_selectedAnnotation->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + supportedSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + supportedSpaceFlag = true; + break; + } + + if ( ! supportedSpaceFlag) { + errorMessageOut = ("Ordering for annotation in " + + AnnotationCoordinateSpaceEnum::toGuiName(m_selectedAnnotation->getCoordinateSpace()) + + " coordinate space is not allowed"); + return false; + } + + if ( ! filterAnnotationsBySpace()) { + errorMessageOut = "No annotations in same coordinate space overlap the selected annotation"; + return false; + } + + bool found(false); + for (auto ann : m_annotations) { + CaretAssert(ann); + if (ann == m_selectedAnnotation) { + found = true; + break; + } + } + if ( ! found) { + errorMessageOut = ("Annotation for reordering " + + m_selectedAnnotation->toString() + + " not found in input annotations"); + return false; + } + + return true; +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +AnnotationStackingOrderOperation::toString() const +{ + return "AnnotationStackingOrderOperation"; +} + +/** + * Filter the annotations to find those in same space of the selected annotation + * + * @return True there are annotations remaining after filtering + */ +bool +AnnotationStackingOrderOperation::filterAnnotationsBySpace() +{ + CaretAssert(m_selectedAnnotation); + + std::vector filteredAnnotations; + + if (m_selectedAnnotation->getType() == AnnotationTypeEnum::BROWSER_TAB) { + for (auto ann : m_annotations) { + CaretAssert(ann); + if (ann->getType() == AnnotationTypeEnum::BROWSER_TAB) { + filteredAnnotations.push_back(ann); + } + } + } + else { + for (auto ann : m_annotations) { + CaretAssert(ann); + + if (m_selectedAnnotation->isInSameCoordinateSpace(ann)) { + if (m_selectedAnnotation == ann) { + filteredAnnotations.push_back(ann); + } + else { + filteredAnnotations.push_back(ann); + } + } + } + } + + + m_annotations = filteredAnnotations; + + return (m_annotations.size() > 1); +} + +/** + * @return The results with annotations and their new stack order. No modifications + * have been made to the annotations, it is up to the caller to assign the new stack + * order to the annotations. + */ +std::vector +AnnotationStackingOrderOperation::getNewStackingOrderResults() const +{ + return m_newStackingOrderResults; +} + + diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationStackingOrderOperation.h connectome-workbench-1.5.0/src/Annotations/AnnotationStackingOrderOperation.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationStackingOrderOperation.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationStackingOrderOperation.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,151 @@ +#ifndef __ANNOTATION_STACKING_ORDER_OPERATION_H__ +#define __ANNOTATION_STACKING_ORDER_OPERATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "AnnotationStackingOrderTypeEnum.h" +#include "CaretObject.h" + + + +namespace caret { + + class Annotation; + class AnnotationCoordinate; + + class AnnotationStackingOrderOperation : public CaretObject { + + public: + /** + * Mode of the alogorithm + */ + enum class Mode { + /* + * Any stacking order modifications are applied to the annotations + */ + MODE_APPLY_NEW_ORDER_TO_ANNOTAIONS, + /* + * No stacking order changes are applied to the annotations. Instead, + * the caller must call "getNewStackingOrderResults()" to get the + * unmodified annotations and their new stacking orders. The + * caller is responsible for applying the new stacking order to + * the annotations. + */ + MODE_REQUEST_NEW_ORDER_VALUES + }; + + class NewStackingOrder { + public: + NewStackingOrder(Annotation* annotation, + const int32_t newStackOrder) + : m_annotation(annotation), + m_newStackOrder(newStackOrder) { } + + Annotation* m_annotation; + const int32_t m_newStackOrder; + }; + + AnnotationStackingOrderOperation(const Mode mode, + const std::vector& annotations, + const Annotation* selectedAnnotation, + const int32_t windowIndex); + + virtual ~AnnotationStackingOrderOperation(); + + AnnotationStackingOrderOperation(const AnnotationStackingOrderOperation&) = delete; + + AnnotationStackingOrderOperation& operator=(const AnnotationStackingOrderOperation&) = delete; + + bool runOrdering(const AnnotationStackingOrderTypeEnum::Enum orderType, + AString& errorMessageOut); + + std::vector getNewStackingOrderResults() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + /** + * Sort annotations by stack order + */ + class OrderInfo { + public: + OrderInfo(Annotation* annotation, + const int32_t stackOrder, + const bool overlapsSelectedAnnotationFlag) + : m_annotation(annotation), + m_stackOrder(stackOrder), + m_overlapsFlag(overlapsSelectedAnnotationFlag) { } + + bool operator<(const OrderInfo& orderInfo) const { + return m_stackOrder < orderInfo.m_stackOrder; + } + + Annotation* m_annotation = NULL; + int32_t m_stackOrder = -1; + bool m_overlapsFlag = false; + }; + + bool filterAnnotationsBySpace(); + + bool preOrderAnnotations(std::vector& annotationOrderAndContent, + AString& errorMessageOut); + + bool validateInput(AString& errorMessageOut); + + bool findAnnotationIndices(const std::vector& annotationOrderAndContent, + int32_t& indexOfSelectedAnnotation, + int32_t& indexOfAnnotationBehind, + int32_t& indexOfAnnotationInFront, + AString& errorMessageOut); + + void printOrderedAnnotations(const std::vector& annotationOrderAndContent); + + void setCoordinateZ(AnnotationCoordinate* coordinate, + const float z); + + const Mode m_mode; + + std::vector m_annotations; + + const Annotation* m_selectedAnnotation; + + const int32_t m_windowIndex; + + const float m_coordToStackOrderScaleFactor = 100.0; + + std::vector m_newStackingOrderResults; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __ANNOTATION_STACKING_ORDER_OPERATION_DECLARE__ + // +#endif // __ANNOTATION_STACKING_ORDER_OPERATION_DECLARE__ + +} // namespace +#endif //__ANNOTATION_STACKING_ORDER_OPERATION_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationStackingOrderTypeEnum.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationStackingOrderTypeEnum.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationStackingOrderTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationStackingOrderTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,381 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __ANNOTATION_STACKING_ORDER_TYPE_ENUM_DECLARE__ +#include "AnnotationStackingOrderTypeEnum.h" +#undef __ANNOTATION_STACKING_ORDER_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::AnnotationStackingOrderTypeEnum + * \brief Enumerated type for stacking order operations + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_annotationStackingOrderTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void annotationStackingOrderTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "AnnotationStackingOrderTypeEnum.h" + * + * Instatiate: + * m_annotationStackingOrderTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_annotationStackingOrderTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_annotationStackingOrderTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(annotationStackingOrderTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_annotationStackingOrderTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const AnnotationStackingOrderTypeEnum::Enum VARIABLE = m_annotationStackingOrderTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +AnnotationStackingOrderTypeEnum::AnnotationStackingOrderTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +AnnotationStackingOrderTypeEnum::~AnnotationStackingOrderTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +AnnotationStackingOrderTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(AnnotationStackingOrderTypeEnum(BRING_TO_FRONT, + "BRING_TO_FRONT", + "Bring To Front")); + + enumData.push_back(AnnotationStackingOrderTypeEnum(BRING_FORWARD, + "BRING_FORWARD", + "Bring Forward")); + + enumData.push_back(AnnotationStackingOrderTypeEnum(SEND_TO_BACK, + "SEND_TO_BACK", + "Send To Back")); + + enumData.push_back(AnnotationStackingOrderTypeEnum(SEND_BACKWARD, + "SEND_BACKWARD", + "Send Backward")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const AnnotationStackingOrderTypeEnum* +AnnotationStackingOrderTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const AnnotationStackingOrderTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +AnnotationStackingOrderTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const AnnotationStackingOrderTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +AnnotationStackingOrderTypeEnum::Enum +AnnotationStackingOrderTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationStackingOrderTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationStackingOrderTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type AnnotationStackingOrderTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +AnnotationStackingOrderTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const AnnotationStackingOrderTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +AnnotationStackingOrderTypeEnum::Enum +AnnotationStackingOrderTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationStackingOrderTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationStackingOrderTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type AnnotationStackingOrderTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +AnnotationStackingOrderTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const AnnotationStackingOrderTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +AnnotationStackingOrderTypeEnum::Enum +AnnotationStackingOrderTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = AnnotationStackingOrderTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const AnnotationStackingOrderTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type AnnotationStackingOrderTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +AnnotationStackingOrderTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +AnnotationStackingOrderTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(AnnotationStackingOrderTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +AnnotationStackingOrderTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(AnnotationStackingOrderTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationStackingOrderTypeEnum.h connectome-workbench-1.5.0/src/Annotations/AnnotationStackingOrderTypeEnum.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationStackingOrderTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationStackingOrderTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,108 @@ +#ifndef __ANNOTATION_STACKING_ORDER_TYPE_ENUM_H__ +#define __ANNOTATION_STACKING_ORDER_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class AnnotationStackingOrderTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Puts annotation in front of all other annotations in same space */ + BRING_TO_FRONT, + /** Moves the annotation one position forward in same space after depth sorting */ + BRING_FORWARD, + /** Moves annotation to behind all other annotations in same space*/ + SEND_TO_BACK, + /** Moves the annotation one position backward in same space after depth sorting */ + SEND_BACKWARD + }; + + + ~AnnotationStackingOrderTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + AnnotationStackingOrderTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const AnnotationStackingOrderTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __ANNOTATION_STACKING_ORDER_TYPE_ENUM_DECLARE__ +std::vector AnnotationStackingOrderTypeEnum::enumData; +bool AnnotationStackingOrderTypeEnum::initializedFlag = false; +int32_t AnnotationStackingOrderTypeEnum::integerCodeCounter = 0; +#endif // __ANNOTATION_STACKING_ORDER_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__ANNOTATION_STACKING_ORDER_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationText.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationText.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationText.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationText.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -53,7 +53,7 @@ */ AnnotationText::AnnotationText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, const AnnotationTextFontSizeTypeEnum::Enum fontSizeType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::TEXT, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::TEXT, attributeDefaultType), AnnotationFontAttributesInterface(), m_fontSizeType(fontSizeType) @@ -74,7 +74,7 @@ AnnotationText::AnnotationText(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, const AnnotationTextFontSizeTypeEnum::Enum fontSizeType) -: AnnotationTwoDimensionalShape(type, +: AnnotationOneCoordinateShape(type, attributeDefaultType), AnnotationFontAttributesInterface(), m_fontSizeType(fontSizeType) @@ -95,7 +95,7 @@ * Object that is copied. */ AnnotationText::AnnotationText(const AnnotationText& obj) -: AnnotationTwoDimensionalShape(obj), +: AnnotationOneCoordinateShape(obj), AnnotationFontAttributesInterface(), m_fontSizeType(obj.m_fontSizeType) { @@ -914,7 +914,7 @@ void AnnotationText::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) { - AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(otherAnnotation); + AnnotationOneCoordinateShape::applyCoordinatesSizeAndRotationFromOther(otherAnnotation); /* * Text size may change when an annotation is moved to a different @@ -1034,11 +1034,13 @@ case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: operationSupportedFlag = true; break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } bool validFlag = false; if (operationSupportedFlag) { - validFlag = AnnotationTwoDimensionalShape::applySpatialModification(spatialModification); + validFlag = AnnotationOneCoordinateShape::applySpatialModification(spatialModification); } return validFlag; @@ -1060,7 +1062,7 @@ AnnotationText::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { sceneClass->addBoolean("hasAttributesFlag", true); @@ -1085,7 +1087,7 @@ AnnotationText::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { /* diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationText.h connectome-workbench-1.5.0/src/Annotations/AnnotationText.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationText.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationText.h 2021-02-16 19:46:47.000000000 +0000 @@ -28,12 +28,12 @@ #include "AnnotationTextFontSizeTypeEnum.h" #include "AnnotationTextConnectTypeEnum.h" #include "AnnotationTextOrientationEnum.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" #include "CaretPointer.h" namespace caret { - class AnnotationText : public AnnotationTwoDimensionalShape, public AnnotationFontAttributesInterface { + class AnnotationText : public AnnotationOneCoordinateShape, public AnnotationFontAttributesInterface { public: /** diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationTwoCoordinateShape.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationTwoCoordinateShape.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationTwoCoordinateShape.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationTwoCoordinateShape.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,864 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_TWO_COORDINATE_SHAPE_DECLARE__ +#include "AnnotationTwoCoordinateShape.h" +#undef __ANNOTATION_TWO_COORDINATE_SHAPE_DECLARE__ + +#include + +#include "AnnotationCoordinate.h" +#include "AnnotationSpatialModification.h" +#include "CaretAssert.h" +#include "MathFunctions.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationTwoCoordinateShape + * \brief Class for annotations that are one-dimensional (lines) + * \ingroup Annotations + */ + +/** + * Constructor. + * @param type + * Type of annotation. + * @param attributeDefaultType + * Type for attribute defaults + */ +AnnotationTwoCoordinateShape::AnnotationTwoCoordinateShape(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) +: Annotation(type, + attributeDefaultType) +{ + initializeMembersAnnotationTwoCoordinateShape(); +} + +/** + * Destructor. + */ +AnnotationTwoCoordinateShape::~AnnotationTwoCoordinateShape() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +AnnotationTwoCoordinateShape::AnnotationTwoCoordinateShape(const AnnotationTwoCoordinateShape& obj) +: Annotation(obj) +{ + initializeMembersAnnotationTwoCoordinateShape(); + + this->copyHelperAnnotationTwoCoordinateShape(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +AnnotationTwoCoordinateShape& +AnnotationTwoCoordinateShape::operator=(const AnnotationTwoCoordinateShape& obj) +{ + if (this != &obj) { + Annotation::operator=(obj); + this->copyHelperAnnotationTwoCoordinateShape(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +AnnotationTwoCoordinateShape::copyHelperAnnotationTwoCoordinateShape(const AnnotationTwoCoordinateShape& obj) +{ + *m_startCoordinate = *obj.m_startCoordinate; + *m_endCoordinate = *obj.m_endCoordinate; +} + +/** + * Initialize members of this class. + */ +void +AnnotationTwoCoordinateShape::initializeMembersAnnotationTwoCoordinateShape() +{ + m_startCoordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); + m_endCoordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); + + m_sceneAssistant.grabNew(new SceneClassAssistant()); + if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { + m_sceneAssistant->add("m_startCoordinate", + "AnnotationCoordinate", + m_startCoordinate); + m_sceneAssistant->add("m_endCoordinate", + "AnnotationCoordinate", + m_endCoordinate); + } +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. + */ +AnnotationTwoCoordinateShape* +AnnotationTwoCoordinateShape::castToTwoCoordinateShape() +{ + return this; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. + */ +const AnnotationTwoCoordinateShape* +AnnotationTwoCoordinateShape::castToTwoCoordinateShape() const +{ + return this; +} + +/** + * @return The start coordinate for the one dimensional shape. + */ +AnnotationCoordinate* +AnnotationTwoCoordinateShape::getStartCoordinate() +{ + return m_startCoordinate; +} + +/** + * @return The start coordinate for the one dimensional shape. + */ +const AnnotationCoordinate* +AnnotationTwoCoordinateShape::getStartCoordinate() const +{ + return m_startCoordinate; +} + +/** + * @return The end coordinate for the one dimensional shape. + */ +AnnotationCoordinate* +AnnotationTwoCoordinateShape::getEndCoordinate() +{ + return m_endCoordinate; +} + +/** + * @return The end coordinate for the one dimensional shape. + */ +const AnnotationCoordinate* +AnnotationTwoCoordinateShape::getEndCoordinate() const +{ + return m_endCoordinate; +} + +/** + * @return The surface offset vector type for this annotation. + */ +AnnotationSurfaceOffsetVectorTypeEnum::Enum +AnnotationTwoCoordinateShape::getSurfaceOffsetVectorType() const +{ + CaretAssert(m_startCoordinate); + return m_startCoordinate->getSurfaceOffsetVectorType(); +} + +/** + * Is the object modified? + * @return true if modified, else false. + */ +bool +AnnotationTwoCoordinateShape::isModified() const +{ + if (Annotation::isModified()) { + return true; + } + + if (m_startCoordinate->isModified()) { + return true; + } + + if (m_endCoordinate->isModified()) { + return true; + } + + return false; +} + +/** + * Set the status to unmodified. + */ +void +AnnotationTwoCoordinateShape::clearModified() +{ + Annotation::clearModified(); + + m_startCoordinate->clearModified(); + m_endCoordinate->clearModified(); +} + +/** + * Apply the coordinates, size, and rotation from the given annotation + * to this annotation. + * + * @param otherAnnotation + * The other annotation from which attributes are obtained. + */ +void +AnnotationTwoCoordinateShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) +{ + CaretAssert(otherAnnotation); + const AnnotationTwoCoordinateShape* otherOneDim = dynamic_cast(otherAnnotation); + CaretAssert(otherOneDim); + + AnnotationCoordinate* startCoord = getStartCoordinate(); + const AnnotationCoordinate* otherStartCoord = otherOneDim->getStartCoordinate(); + *startCoord = *otherStartCoord; + + AnnotationCoordinate* endCoord = getEndCoordinate(); + const AnnotationCoordinate* otherEndCoord = otherOneDim->getEndCoordinate(); + *endCoord = *otherEndCoord; + + setCoordinateSpace(otherAnnotation->getCoordinateSpace()); + setTabIndex(otherAnnotation->getTabIndex()); + setWindowIndex(otherAnnotation->getWindowIndex()); +} + +/** + * Get the rotation angle from the one-dimensional annotation. + * 0 is horizontal. + * + * @param viewportWidth + * Width of viewport. + * @param viewportHeight + * Height of viewport. + * @return + * Rotation angle of the annotation. + */ +float +AnnotationTwoCoordinateShape::getRotationAngle(const float viewportWidth, + const float viewportHeight) const +{ + if ( ! isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { + return 0.0; + } + + float vpOneX = 0.0; + float vpOneY = 0.0; + float vpTwoX = 0.0; + float vpTwoY = 0.0; + getStartCoordinate()->getViewportXY(viewportWidth, viewportHeight, vpOneX, vpOneY); + getEndCoordinate()->getViewportXY(viewportWidth, viewportHeight, vpTwoX, vpTwoY); + + const float dx = vpTwoX - vpOneX; + const float dy = vpTwoY - vpOneY; + + float angle = 180.0 - MathFunctions::toDegrees(std::atan2(dy, dx)); + if (angle < 0.0) { + angle = angle + 360.0; + } + else if (angle > 360.0) { + angle = angle - 360.0; + } + return angle; +} + +/** + * Set the rotation angle from the one-dimensional annotation. + * 0 is horizontal. + * + * @param viewportWidth + * Width of viewport. + * @param viewportHeight + * Height of viewport. + * @param rotationAngle + * Rotation angle for the annotation. + */ +void +AnnotationTwoCoordinateShape::setRotationAngle(const float viewportWidth, + const float viewportHeight, + const float rotationAngle) +{ + if ( ! isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { + return; + } + + float annOneX = 0.0; + float annOneY = 0.0; + float annTwoX = 0.0; + float annTwoY = 0.0; + getStartCoordinate()->getViewportXY(viewportWidth, viewportHeight, annOneX, annOneY); + getEndCoordinate()->getViewportXY(viewportWidth, viewportHeight, annTwoX, annTwoY); + + const float midPointXYZ[3] = { + (annOneX + annTwoX) / 2.0f, + (annOneY + annTwoY) / 2.0f, + 0.0f + }; + + const float vpOneXYZ[3] = { annOneX, annOneY, 0.0f }; + const float lengthMidToOne = MathFunctions::distance3D(midPointXYZ, vpOneXYZ); + const float newRotationAngle = 180.0f - rotationAngle; + + const float angleRadians = MathFunctions::toRadians(newRotationAngle); + const float dy = lengthMidToOne * std::sin(angleRadians); + const float dx = lengthMidToOne * std::cos(angleRadians); + annOneX = midPointXYZ[0] - dx; + annOneY = midPointXYZ[1] - dy; + + annTwoX = midPointXYZ[0] + dx; + annTwoY = midPointXYZ[1] + dy; + + getStartCoordinate()->setXYZFromViewportXYZ(viewportWidth, viewportHeight, annOneX, annOneY); + getEndCoordinate()->setXYZFromViewportXYZ(viewportWidth, viewportHeight, annTwoX, annTwoY); +} + +/** + * Is the given sizing handle valid for this annotation? + * + * @sizingHandle + * The sizing handle. + * @return + * True if sizing handle valid, else false. + */ +bool +AnnotationTwoCoordinateShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const +{ + bool xyPlaneFlag = false; + + switch (getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + xyPlaneFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SPACER: + xyPlaneFlag = true; + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + xyPlaneFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + xyPlaneFlag = true; + break; + } + + bool validFlag = false; + + switch (sizingHandle) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (xyPlaneFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + if (xyPlaneFlag) { + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in surface space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationTwoCoordinateShape::applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfNodes = -1; + int32_t surfaceNodeIndex = -1; + + m_endCoordinate->getSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex); + if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { + if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) + && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { + m_endCoordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, + spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, + spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); + validFlag = true; + } + } + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfNodes = -1; + int32_t surfaceNodeIndex = -1; + + m_startCoordinate->getSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex); + if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { + if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) + && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { + m_startCoordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, + spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, + spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); + validFlag = true; + } + } + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + + if (validFlag) { + setModified(); + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in spacer tab space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationTwoCoordinateShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) +{ + return applySpatialModificationTabOrWindowSpace(spatialModification); +} + + +/** + * Apply a spatial modification to an annotation in tab or window space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationTwoCoordinateShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) +{ + float xyz1[3]; + float xyz2[3]; + m_startCoordinate->getXYZ(xyz1); + m_endCoordinate->getXYZ(xyz2); + + float newX1 = xyz1[0]; + float newY1 = xyz1[1]; + float newX2 = xyz2[0]; + float newY2 = xyz2[1]; + + const float spaceDX = 100.0f * ((spatialModification.m_viewportWidth != 0.0f) + ? (spatialModification.m_mouseDX / spatialModification.m_viewportWidth) + : 0.0f); + const float spaceDY = 100.0f * ((spatialModification.m_viewportHeight != 0.0f) + ? (spatialModification.m_mouseDY / spatialModification.m_viewportHeight) + : 0.0f); + bool validFlag = false; + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + newX2 += spaceDX; + newY2 += spaceDY; + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + newX1 += spaceDX; + newY1 += spaceDY; + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + newX1 += spaceDX; + newY1 += spaceDY; + newX2 += spaceDX; + newY2 += spaceDY; + validFlag = true; + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + { + float vpOneXYZ[3] = { 0.0, 0.0, 0.0 }; + relativeXYZToViewportXYZ(xyz1, + spatialModification.m_viewportWidth, + spatialModification.m_viewportHeight, + vpOneXYZ); + float vpTwoXYZ[3] = { 0.0, 0.0, 0.0 }; + relativeXYZToViewportXYZ(xyz2, + spatialModification.m_viewportWidth, + spatialModification.m_viewportHeight, + vpTwoXYZ); + + float vpXYZ[3] = { + (vpOneXYZ[0] + vpTwoXYZ[0]) / 2.0f, + (vpOneXYZ[1] + vpTwoXYZ[1]) / 2.0f, + (vpOneXYZ[2] + vpTwoXYZ[2]) / 2.0f + }; + + /* + * Rotation angle is a formed by the triangle + * (Mouse XY, Annotation XY, Positive X-axis). + */ + const float dy = spatialModification.m_mouseY - vpXYZ[1]; + const float dx = spatialModification.m_mouseX - vpXYZ[0]; + + const float angleRadians = std::atan2(dy, dx); + const float angleDegrees = MathFunctions::toDegrees(angleRadians); + float rotationAngle = -angleDegrees; + if (rotationAngle > 360.0) { + rotationAngle -= 360.0; + } + else if (rotationAngle < 0.0) { + rotationAngle += 360.0; + } + + CaretPointer shapeCopy(dynamic_cast(this->clone())); + shapeCopy->setRotationAngle(spatialModification.m_viewportWidth, + spatialModification.m_viewportHeight, + rotationAngle); + + const float* xyzOne = shapeCopy->getStartCoordinate()->getXYZ(); + const float* xyzTwo = shapeCopy->getEndCoordinate()->getXYZ(); + newX1 = xyzOne[0]; + newY1 = xyzOne[1]; + newX2 = xyzTwo[0]; + newY2 = xyzTwo[1]; + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + if (validFlag) { + if ((newX1 >= 0.0) + && (newX1 <= 100.0) + && (newY1 >= 0.0) + && (newY1 <= 100.0) + && (newX2 >= 0.0) + && (newX2 <= 100.0) + && (newY2 >= 0.0) + && (newY2 <= 100.0)) { + xyz1[0] = newX1; + xyz1[1] = newY1; + m_startCoordinate->setXYZ(xyz1); + xyz2[0] = newX2; + xyz2[1] = newY2; + m_endCoordinate->setXYZ(xyz2); + } + else { + validFlag = false; + } + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in chart space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationTwoCoordinateShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { + m_endCoordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { + m_startCoordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid + && spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZValid) { + const float dx = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[0] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[0]; + const float dy = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[1] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[1]; + const float dz = spatialModification.m_chartCoordAtMouseXY.m_chartXYZ[2] - spatialModification.m_chartCoordAtPreviousMouseXY.m_chartXYZ[2]; + + m_startCoordinate->addToXYZ(dx, dy, dz); + m_endCoordinate->addToXYZ(dx, dy, dz); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + if (validFlag) { + setModified(); + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation in stereotaxic space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationTwoCoordinateShape::applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification) +{ + bool validFlag = false; + + switch (spatialModification.m_sizingHandleType) { + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: + if (spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid) { + m_endCoordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: + if (spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid) { + m_startCoordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); + validFlag = true; + } + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: + break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; + } + + if (validFlag) { + setModified(); + } + + return validFlag; +} + +/** + * Apply a spatial modification to an annotation. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationTwoCoordinateShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) +{ + if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { + return false; + } + + switch (getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + return applySpatialModificationChartSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::SPACER: + return applySpatialModificationSpacerTabSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + return applySpatialModificationStereotaxicSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + return applySpatialModificationSurfaceSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::TAB: + return applySpatialModificationTabOrWindowSpace(spatialModification); + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + return applySpatialModificationTabOrWindowSpace(spatialModification); + break; + } + + return false; +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +AnnotationTwoCoordinateShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +AnnotationTwoCoordinateShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationTwoCoordinateShape.h connectome-workbench-1.5.0/src/Annotations/AnnotationTwoCoordinateShape.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationTwoCoordinateShape.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationTwoCoordinateShape.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,120 @@ +#ifndef __ANNOTATION_TWO_COORDINATE_SHAPE_H__ +#define __ANNOTATION_TWO_COORDINATE_SHAPE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "Annotation.h" +#include "CaretPointer.h" + +namespace caret { + + class AnnotationCoordinate; + + class AnnotationTwoCoordinateShape : public Annotation { + + public: + AnnotationTwoCoordinateShape(const AnnotationTypeEnum::Enum type, + const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); + + virtual ~AnnotationTwoCoordinateShape(); + + AnnotationTwoCoordinateShape(const AnnotationTwoCoordinateShape& obj); + + AnnotationTwoCoordinateShape& operator=(const AnnotationTwoCoordinateShape& obj); + + virtual AnnotationTwoCoordinateShape* castToTwoCoordinateShape() override; + + virtual const AnnotationTwoCoordinateShape* castToTwoCoordinateShape() const override; + + AnnotationCoordinate* getStartCoordinate(); + + const AnnotationCoordinate* getStartCoordinate() const; + + AnnotationCoordinate* getEndCoordinate(); + + const AnnotationCoordinate* getEndCoordinate() const; + + virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const override; + + virtual bool isModified() const; + + virtual void clearModified(); + + virtual bool isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const; + + virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); + + virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); + + float getRotationAngle(const float viewportWidth, + const float viewportHeight) const; + + void setRotationAngle(const float viewportWidth, + const float viewportHeight, + const float rotationAngle); + + + // ADD_NEW_METHODS_HERE + + + + + + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private: + void copyHelperAnnotationTwoCoordinateShape(const AnnotationTwoCoordinateShape& obj); + + void initializeMembersAnnotationTwoCoordinateShape(); + + bool applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); + + bool applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification); + + CaretPointer m_sceneAssistant; + + CaretPointer m_startCoordinate; + + CaretPointer m_endCoordinate; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __ANNOTATION_TWO_COORDINATE_SHAPE_DECLARE__ + // +#endif // __ANNOTATION_TWO_COORDINATE_SHAPE_DECLARE__ + +} // namespace +#endif //__ANNOTATION_TWO_COORDINATE_SHAPE_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationTwoDimensionalShape.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationTwoDimensionalShape.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationTwoDimensionalShape.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationTwoDimensionalShape.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,1667 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -#define __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ -#include "AnnotationTwoDimensionalShape.h" -#undef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ - -#include "AnnotationColorBar.h" -#include "AnnotationCoordinate.h" -#include "AnnotationSpatialModification.h" -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "MathFunctions.h" -#include "Matrix4x4.h" -#include "SceneClass.h" -#include "SceneClassAssistant.h" - -using namespace caret; - - - -/** - * \class caret::AnnotationTwoDimensionalShape - * \brief Class for annotations that are two dimensional (width and height). - * \ingroup Annotations - */ - -/** - * Constructor. - * - * @param type - * Type of annotation - * @param attributeDefaultType - * Type for attribute defaults - */ -AnnotationTwoDimensionalShape::AnnotationTwoDimensionalShape(const AnnotationTypeEnum::Enum type, - const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: Annotation(type, - attributeDefaultType) -{ - initializeMembersAnnotationTwoDimensionalShape(); -} - -/** - * Destructor. - */ -AnnotationTwoDimensionalShape::~AnnotationTwoDimensionalShape() -{ -} - -/** - * Copy constructor. - * @param obj - * Object that is copied. - */ -AnnotationTwoDimensionalShape::AnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj) -: Annotation(obj) -{ - initializeMembersAnnotationTwoDimensionalShape(); - this->copyHelperAnnotationTwoDimensionalShape(obj); -} - -/** - * Assignment operator. - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -AnnotationTwoDimensionalShape& -AnnotationTwoDimensionalShape::operator=(const AnnotationTwoDimensionalShape& obj) -{ - if (this != &obj) { - Annotation::operator=(obj); - this->copyHelperAnnotationTwoDimensionalShape(obj); - } - return *this; -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -AnnotationTwoDimensionalShape::copyHelperAnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj) -{ - *m_coordinate = *obj.m_coordinate; - m_width = obj.m_width; - m_height = obj.m_height; - m_rotationAngle = obj.m_rotationAngle; -} - -/** - * Initialize members of this class. - */ -void -AnnotationTwoDimensionalShape::initializeMembersAnnotationTwoDimensionalShape() -{ - m_coordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); - - switch (m_attributeDefaultType) { - case AnnotationAttributesDefaultTypeEnum::NORMAL: - m_width = 25.0; - m_height = 25.0; - m_rotationAngle = 0.0; - break; - case AnnotationAttributesDefaultTypeEnum::USER: - m_width = s_userDefaultWidth; - m_height = s_userDefaultHeight; - m_rotationAngle = 0.0; - break; - } - - - - m_sceneAssistant.grabNew(new SceneClassAssistant()); - if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { - /* - * Saves/restores color bar manual position and size mode. - */ - m_sceneAssistant->add("m_coordinate", - "AnnotationCoordinate", - m_coordinate); - m_sceneAssistant->add("m_width", - &m_width); - m_sceneAssistant->add("m_height", - &m_height); - m_sceneAssistant->add("m_rotationAngle", - &m_rotationAngle); - } -} - -/** - * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. - */ -AnnotationOneDimensionalShape* -AnnotationTwoDimensionalShape::castToOneDimensionalShape() -{ - return NULL; -} - -/** - * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. - */ -const AnnotationOneDimensionalShape* -AnnotationTwoDimensionalShape::castToOneDimensionalShape() const -{ - return NULL; -} - -/** - * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. - */ -AnnotationTwoDimensionalShape* -AnnotationTwoDimensionalShape::castToTwoDimensionalShape() -{ - return this; -} - -/** - * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. - */ -const AnnotationTwoDimensionalShape* -AnnotationTwoDimensionalShape::castToTwoDimensionalShape() const -{ - return this; -} - -/** - * @return The coordinate for the two dimensional shape. - */ -AnnotationCoordinate* -AnnotationTwoDimensionalShape::getCoordinate() -{ - return m_coordinate; -} - -/** - * @return The start coordinate for the two dimensional shape. - */ -const AnnotationCoordinate* -AnnotationTwoDimensionalShape::getCoordinate() const -{ - return m_coordinate; -} - -/** - * @return The surface offset vector type for this annotation. - */ -AnnotationSurfaceOffsetVectorTypeEnum::Enum -AnnotationTwoDimensionalShape::getSurfaceOffsetVectorType() const -{ - CaretAssert(m_coordinate); - return m_coordinate->getSurfaceOffsetVectorType(); -} - -/** - * @return Height for "two-dimensional" annotations in percentage zero to one-hundred. - */ -float -AnnotationTwoDimensionalShape::getHeight() const -{ - return m_height; -} - -/** - * Set the height for "two-dimensional" annotations in percentage zero to one-hundred. - * - * @param height - * New value for height of the annotation. - */ -void -AnnotationTwoDimensionalShape::setHeight(const float height) -{ - if (height != m_height) { - if (isFixedAspectRatio()) { - m_height = height; - m_width = m_height / getFixedAspectRatio(); - } - else { - m_height = height; - } - setModified(); - } -} - -/** - * @return Width for "two-dimensional" annotations in percentage zero to one-hundred. - */ -float -AnnotationTwoDimensionalShape::getWidth() const -{ - return m_width; -} - -/** - * Set the width for "two-dimensional" annotations in percentage zero to one-hundred. - * - * @param width - * New value for width of the annotation. - */ -void -AnnotationTwoDimensionalShape::setWidth(const float width) -{ - if (width != m_width) { - if (isFixedAspectRatio()) { - m_width = width; - m_height = m_width * getFixedAspectRatio(); - } - else { - m_width = width; - } - setModified(); - } -} - -/** - * @return The rotation angle, in degrees, clockwise, from vertical at the top (12 o'clock). - */ -float -AnnotationTwoDimensionalShape::getRotationAngle() const -{ - return m_rotationAngle; -} - -/** - * The rotation angle, in degrees, clockwise, from vertical at the top (12 o'clock). - * - * @param rotationAngle - * New value rotation angle. - */ -void -AnnotationTwoDimensionalShape::setRotationAngle(const float rotationAngle) -{ - if (rotationAngle != m_rotationAngle) { - m_rotationAngle = rotationAngle; - setModified(); - } -} - -/** - * Is the object modified? - * @return true if modified, else false. - */ -bool -AnnotationTwoDimensionalShape::isModified() const -{ - if (Annotation::isModified()) { - return true; - } - - if (m_coordinate->isModified()) { - return true; - } - - return false; -} - -/** - * Set the status to unmodified. - */ -void -AnnotationTwoDimensionalShape::clearModified() -{ - Annotation::clearModified(); - - m_coordinate->clearModified(); -} - -/** - * Apply the coordinates, size, and rotation from the given annotation - * to this annotation. - * - * @param otherAnnotation - * The other annotation from which attributes are obtained. - */ -void -AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) -{ - CaretAssert(otherAnnotation); - const AnnotationTwoDimensionalShape* otherTwoDim = dynamic_cast(otherAnnotation); - CaretAssert(otherTwoDim); - - AnnotationCoordinate* coord = getCoordinate(); - const AnnotationCoordinate* otherCoord = otherTwoDim->getCoordinate(); - *coord = *otherCoord; - - setWidth(otherTwoDim->getWidth()); - setHeight(otherTwoDim->getHeight()); - setRotationAngle(otherTwoDim->getRotationAngle()); - - setCoordinateSpace(otherAnnotation->getCoordinateSpace()); - setTabIndex(otherAnnotation->getTabIndex()); - setWindowIndex(otherAnnotation->getWindowIndex()); - - /* - * Switch color bar to manual positioning - */ - AnnotationColorBar* colorBar = dynamic_cast(this); - if (colorBar != NULL) { - colorBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); - } -} - -/** - * Is the given sizing handle valid for this annotation? - * - * @sizingHandle - * The sizing handle. - * @return - * True if sizing handle valid, else false. - */ -bool -AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const -{ - const bool viewportFlag = (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::VIEWPORT); - - const bool surfaceTangentOffsetFlag = isInSurfaceSpaceWithTangentOffset(); - - bool allowsMovingFlag = false; - bool allowsCornerResizingFlag = false; - bool allowsSideResizingFlag = false; - bool allowsRotationFlag = false; - - switch (getType()) { - case AnnotationTypeEnum::BOX: - allowsMovingFlag = true; - allowsCornerResizingFlag = true; - allowsSideResizingFlag = true; - allowsRotationFlag = true; - break; - case AnnotationTypeEnum::COLOR_BAR: - allowsMovingFlag = true; - allowsCornerResizingFlag = true; - allowsSideResizingFlag = true; - allowsRotationFlag = true; - break; - case AnnotationTypeEnum::IMAGE: - allowsMovingFlag = true; - allowsSideResizingFlag = true; - allowsRotationFlag = true; - break; - case AnnotationTypeEnum::LINE: - break; - case AnnotationTypeEnum::OVAL: - allowsMovingFlag = true; - allowsCornerResizingFlag = true; - allowsSideResizingFlag = true; - allowsRotationFlag = true; - break; - case AnnotationTypeEnum::TEXT: - allowsMovingFlag = true; - allowsRotationFlag = true; - break; - } - - if (surfaceTangentOffsetFlag) { - allowsCornerResizingFlag = false; - allowsSideResizingFlag = false; - } - - bool validFlag = false; - - if (! viewportFlag) { - switch (sizingHandle) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - if (allowsSideResizingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - if (allowsCornerResizingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - if (allowsCornerResizingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - if (allowsSideResizingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - if (allowsSideResizingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - if (allowsSideResizingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - if (allowsCornerResizingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - if (allowsCornerResizingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - if (allowsMovingFlag) { - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - if (allowsRotationFlag) { - validFlag = true; - } - break; - } - } - - return validFlag; -} - -/** - * Apply a spatial modification to an annotation in chart space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationTwoDimensionalShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) -{ - bool validFlag = false; - - const float rotationRadians = MathFunctions::toRadians(m_rotationAngle); - float dx = (spatialModification.m_mouseDX * std::cos(rotationRadians) - - spatialModification.m_mouseDY * std::sin(rotationRadians)); - float dy = (spatialModification.m_mouseDX * std::sin(rotationRadians) - + spatialModification.m_mouseDY * std::cos(rotationRadians)); - - - if (spatialModification.m_viewportWidth > 0) { - dx = (dx / spatialModification.m_viewportWidth) * 100.0; - } - else { - dx = 0.0; - } - if (spatialModification.m_viewportHeight > 0) { - dy = (dy / spatialModification.m_viewportHeight) * 100.0; - } - else { - dy = 0.0; - } - - bool validDxyFlag = false; - - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - dx = 0.0; - dy = -dy; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - dx = -dx; - dy = -dy; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - dy = -dy; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - dx = -dx; - dy = 0.0; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - dy = 0.0; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - dx = 0.0; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - dx = -dx; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - if (spatialModification.m_chartCoordAtMouseXY.m_chartXYZValid) { - m_coordinate->setXYZ(spatialModification.m_chartCoordAtMouseXY.m_chartXYZ); - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - { - static float centerX = 0.0; - static float centerY = 0.0; - - /* - * Stereotaxic and surface rotations may be displayed in - * multiple locations if more that one surface is dislayed - * when surface montage is viewed and/or tile tabs is enabled. - * - * We don't have the window location of the annotation but - * we can estimate it as it is in the center of the annotation. - * Once we have approximated the center we can use the center - * as the rotation point and so that the rotation handle points - * to the mouse. During rotation, the center will not change - * so set its position when the user starts to drag the mouse. - */ - if (spatialModification.m_startOfDraggingFlag) { - const float height = ((m_height / 100.0) * spatialModification.m_viewportHeight); - const float halfHeight = height / 2.0; - - const float handleOffsetHeight = 15.0; - const float centerAngleRadians = MathFunctions::toRadians(m_rotationAngle - 90); - const float centerOffsetY = (halfHeight + handleOffsetHeight) * std::sin(centerAngleRadians); - const float centerOffsetX = -halfHeight * std::cos(centerAngleRadians); - centerX = spatialModification.m_mousePressX + centerOffsetX; - centerY = spatialModification.m_mousePressY + centerOffsetY; - } - - /* - * Rotation angle is a formed by the triangle - * (Mouse XY, Annotation XY, Positive X-axis). - */ - const float dy = (spatialModification.m_mouseY - - centerY); - const float dx = (spatialModification.m_mouseX - - centerX); - - const float angleRadians = std::atan2(dy, dx); - const float angleDegrees = MathFunctions::toDegrees(angleRadians); - m_rotationAngle = 90.0 - angleDegrees; - if (m_rotationAngle > 360.0) { - m_rotationAngle -= 360.0; - } - else if (m_rotationAngle < 0.0) { - m_rotationAngle += 360.0; - } - - validFlag = true; - } - break; - } - - if (validDxyFlag) { - if (isFixedAspectRatio()) { - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - if (std::fabs(dx) > std::fabs(dy)) { - /* - * For fixed aspect ratio, the width percentage is not a percentage - * of window width but of the window's height since the annotation - * is sized by its height (width = height / aspect). - * - * So when width is changed, need to set height. - */ - const float aspectRatio = getFixedAspectRatio(); - const float newHeight = MathFunctions::limitRange(getHeight() + dx * aspectRatio, 0.0f, 100.0f); - setHeight(newHeight); - validFlag = true; - } - else if (std::fabs(dx) < std::fabs(dy)) { - const float newHeight = MathFunctions::limitRange(getHeight() + dy, 0.0f, 100.0f); - setHeight(newHeight); - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - break; - } - } - else { - if (dx != 0.0) { - m_width += dx; - m_width = MathFunctions::limitRange(m_width, 1.0f, 100.0f); - validFlag = true; - } - if (dy != 0.0) { - m_height += dy; - m_height = MathFunctions::limitRange(m_height, 1.0f, 100.0f); - validFlag = true; - } - } - } - - if (validFlag) { - setModified(); - } - - return validFlag; -} - -/** - * Apply a spatial modification to an annotation in surface - * or stereotaxic space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @param coordinateSpace - * The coordinate space. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, - const AnnotationCoordinateSpaceEnum::Enum coordinateSpace) -{ - bool badSpaceFlag = false; - bool stereoSpaceFlag = false; - bool surfaceSpaceFlag = false; - - switch (coordinateSpace) { - case AnnotationCoordinateSpaceEnum::CHART: - badSpaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::SPACER: - badSpaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - stereoSpaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - surfaceSpaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::TAB: - badSpaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - badSpaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - badSpaceFlag = true; - break; - } - - if (badSpaceFlag) { - CaretAssertMessage(0, ("This method should NEVER be called for " - + AnnotationCoordinateSpaceEnum::toName(coordinateSpace) - + " space annotations")); - return false; - } - - bool validFlag = false; - - const float rotationRadians = MathFunctions::toRadians(m_rotationAngle); - float dx = (spatialModification.m_mouseDX * std::cos(rotationRadians) - - spatialModification.m_mouseDY * std::sin(rotationRadians)); - float dy = (spatialModification.m_mouseDX * std::sin(rotationRadians) - + spatialModification.m_mouseDY * std::cos(rotationRadians)); - - - if (spatialModification.m_viewportWidth > 0) { - dx = (dx / spatialModification.m_viewportWidth) * 100.0; - } - else { - dx = 0.0; - } - if (spatialModification.m_viewportHeight > 0) { - dy = (dy / spatialModification.m_viewportHeight) * 100.0; - } - else { - dy = 0.0; - } - - bool validDxyFlag = false; - - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - dx = 0.0; - dy = -dy; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - dx = -dx; - dy = -dy; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - dy = -dy; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - dx = -dx; - dy = 0.0; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - dy = 0.0; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - dx = 0.0; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - dx = -dx; - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - validDxyFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - if (stereoSpaceFlag) { - m_coordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); - validFlag = true; - } - else if (surfaceSpaceFlag) { - StructureEnum::Enum structure = StructureEnum::INVALID; - int32_t surfaceNumberOfNodes = -1; - int32_t surfaceNodeIndex = -1; - m_coordinate->getSurfaceSpace(structure, - surfaceNumberOfNodes, - surfaceNodeIndex); - - if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { - if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) - && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { - m_coordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, - spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, - spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); - validFlag = true; - } - } - } - else { - CaretAssertMessage(0, "New space???"); - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - { - static float centerX = 0.0; - static float centerY = 0.0; - - /* - * Stereotaxic and surface rotations may be displayed in - * multiple locations if more that one surface is dislayed - * when surface montage is viewed and/or tile tabs is enabled. - * - * We don't have the window location of the annotation but - * we can estimate it as it is in the center of the annotation. - * Once we have approximated the center we can use the center - * as the rotation point and so that the rotation handle points - * to the mouse. During rotation, the center will not change - * so set its position when the user starts to drag the mouse. - */ - if (spatialModification.m_startOfDraggingFlag) { - const float height = ((m_height / 100.0) * spatialModification.m_viewportHeight); - const float halfHeight = height / 2.0; - - const float handleOffsetHeight = 15.0; - const float centerAngleRadians = MathFunctions::toRadians(m_rotationAngle - 90); - const float centerOffsetY = (halfHeight + handleOffsetHeight) * std::sin(centerAngleRadians); - const float centerOffsetX = -halfHeight * std::cos(centerAngleRadians); - centerX = spatialModification.m_mousePressX + centerOffsetX; - centerY = spatialModification.m_mousePressY + centerOffsetY; - } - - /* - * Rotation angle is a formed by the triangle - * (Mouse XY, Annotation XY, Positive X-axis). - */ - const float dy = (spatialModification.m_mouseY - - centerY); - const float dx = (spatialModification.m_mouseX - - centerX); - - const float angleRadians = std::atan2(dy, dx); - const float angleDegrees = MathFunctions::toDegrees(angleRadians); - m_rotationAngle = 90.0 - angleDegrees; - if (m_rotationAngle > 360.0) { - m_rotationAngle -= 360.0; - } - else if (m_rotationAngle < 0.0) { - m_rotationAngle += 360.0; - } - - validFlag = true; - } - break; - } - - if (validDxyFlag) { - if (isFixedAspectRatio()) { - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - if (std::fabs(dx) > std::fabs(dy)) { - /* - * For fixed aspect ratio, the width percentage is not a percentage - * of window width but of the window's height since the annotation - * is sized by its height (width = height / aspect). - * - * So when width is changed, need to set height. - */ - const float aspectRatio = getFixedAspectRatio(); - const float newHeight = MathFunctions::limitRange(getHeight() + dx * aspectRatio, 0.0f, 100.0f); - setHeight(newHeight); - validFlag = true; - } - else if (std::fabs(dx) < std::fabs(dy)) { - const float newHeight = MathFunctions::limitRange(getHeight() + dy, 0.0f, 100.0f); - setHeight(newHeight); - validFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - break; - } - } - else { - if (dx != 0.0) { - m_width += dx; - m_width = MathFunctions::limitRange(m_width, 1.0f, 100.0f); - validFlag = true; - } - if (dy != 0.0) { - m_height += dy; - m_height = MathFunctions::limitRange(m_height, 1.0f, 100.0f); - validFlag = true; - } - } - } - - if (validFlag) { - setModified(); - } - - return validFlag; -} - -/** - * Apply a spatial modification to an annotation in spacer tab space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationTwoDimensionalShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) -{ - return applySpatialModificationTabOrWindowSpace(spatialModification); -} - - -/** - * Apply a spatial modification to an annotation in tab or window space. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationTwoDimensionalShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) -{ - float xyz[3]; - m_coordinate->getXYZ(xyz); - - float viewportXYZ[3] = { 0.0, 0.0, 0.0 }; - relativeXYZToViewportXYZ(xyz, spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, viewportXYZ); - - float bottomLeftXYZ[3]; - float bottomRightXYZ[3]; - float topLeftXYZ[3]; - float topRightXYZ[3]; - const bool validBounds = getShapeBounds(spatialModification.m_viewportWidth, - spatialModification.m_viewportHeight, - viewportXYZ, - bottomLeftXYZ, - bottomRightXYZ, - topRightXYZ, - topLeftXYZ); - if ( ! validBounds) { - return false; - } - - float leftToRightUnitVector[3]; - MathFunctions::createUnitVector(bottomLeftXYZ, bottomRightXYZ, leftToRightUnitVector); - float bottomToTopUnitVector[3]; - MathFunctions::createUnitVector(bottomLeftXYZ, topLeftXYZ, bottomToTopUnitVector); - - /* - * Find size adjustment for side (not corner) sizing handles - */ - float sideHandleDX = 0.0; - float sideHandleDY = 0.0; - getSideHandleMouseDelta(spatialModification.m_sizingHandleType, - leftToRightUnitVector, - bottomToTopUnitVector, - spatialModification.m_mouseDX, - spatialModification.m_mouseDY, - sideHandleDX, - sideHandleDY); - - bool validCoordinatesFlag = false; - bool validRotationFlag = false; - - /* - * When a resize handle is moved, update the corners of the shape - */ - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - addToXYZWithXY(bottomLeftXYZ, sideHandleDX, sideHandleDY); - addToXYZWithXY(bottomRightXYZ, sideHandleDX, sideHandleDY); - - validCoordinatesFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - { - /* - * Bottom left is now at the mouse XY - */ - bottomLeftXYZ[0] = spatialModification.m_mouseX; - bottomLeftXYZ[1] = spatialModification.m_mouseY; - - /* - * Unit vector from bottom left to updated top right - */ - float bottomLeftToTopRightUnitVector[3]; - MathFunctions::createUnitVector(bottomLeftXYZ, topRightXYZ, bottomLeftToTopRightUnitVector); - - /* - * We have a right triangle where: - * The hypotnuse is from bottom left corner to new top right corner - * A right angle is at top left corner - * Want angle at bottom left but vector angle is at top right (all - * angles add up to PI=180). - */ - const float oppositeAngle = MathFunctions::angle(topLeftXYZ, - topRightXYZ, - bottomLeftXYZ); - const float angle = (M_PI / 2.0) - oppositeAngle; - const float hypotnuseLength = MathFunctions::distance3D(bottomLeftXYZ, - topRightXYZ); - - const float newWidth = std::sin(angle) * hypotnuseLength; - const float newHeight = std::cos(angle) * hypotnuseLength; - - topLeftXYZ[0] = bottomLeftXYZ[0] + bottomToTopUnitVector[0] * newHeight; - topLeftXYZ[1] = bottomLeftXYZ[1] + bottomToTopUnitVector[1] * newHeight; - - bottomRightXYZ[0] = bottomLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; - bottomRightXYZ[1] = bottomLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; - - validCoordinatesFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - { - /* - * Bottom right is now at the mouse XY - */ - bottomRightXYZ[0] = spatialModification.m_mouseX; - bottomRightXYZ[1] = spatialModification.m_mouseY; - - /* - * Unit vector from top left to updated bottom right - */ - float topLeftToBottomRightUnitVector[3]; - MathFunctions::createUnitVector(topLeftXYZ, bottomRightXYZ, topLeftToBottomRightUnitVector); - - /* - * We have a right triangle where: - * The hypotnuse is from top left corner to new bottom right corner - * A right angle is at top right corner - */ - const float angle = MathFunctions::angle(topRightXYZ, - topLeftXYZ, - bottomRightXYZ); - const float hypotnuseLength = MathFunctions::distance3D(topLeftXYZ, - bottomRightXYZ); - - const float newWidth = std::cos(angle) * hypotnuseLength; - const float newHeight = std::sin(angle) * hypotnuseLength; - - topRightXYZ[0] = topLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; - topRightXYZ[1] = topLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; - - bottomLeftXYZ[0] = topLeftXYZ[0] - bottomToTopUnitVector[0] * newHeight; - bottomLeftXYZ[1] = topLeftXYZ[1] - bottomToTopUnitVector[1] * newHeight; - - validCoordinatesFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - addToXYZWithXY(topLeftXYZ, sideHandleDX, sideHandleDY); - addToXYZWithXY(bottomLeftXYZ, sideHandleDX, sideHandleDY); - - validCoordinatesFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - addToXYZWithXY(topRightXYZ, sideHandleDX, sideHandleDY); - addToXYZWithXY(bottomRightXYZ, sideHandleDX, sideHandleDY); - - validCoordinatesFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - addToXYZWithXY(topLeftXYZ, sideHandleDX, sideHandleDY); - addToXYZWithXY(topRightXYZ, sideHandleDX, sideHandleDY); - - validCoordinatesFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - { - /* - * Top left is now at the mouse XY - */ - topLeftXYZ[0] = spatialModification.m_mouseX; - topLeftXYZ[1] = spatialModification.m_mouseY; - - /* - * Unit vector from top left to updated bottom right - */ - float topLeftToBottomRightUnitVector[3]; - MathFunctions::createUnitVector(topLeftXYZ, bottomRightXYZ, topLeftToBottomRightUnitVector); - - /* - * We have a right triangle where: - * The hypotnuse is from top left corner to new bottom right corner - * A right angle is at top right corner - */ - const float oppositeAngle = MathFunctions::angle(topLeftXYZ, - bottomRightXYZ, - bottomLeftXYZ); - const float angle = (M_PI / 2.0) - oppositeAngle; - const float hypotnuseLength = MathFunctions::distance3D(topLeftXYZ, - bottomRightXYZ); - - const float newWidth = std::sin(angle) * hypotnuseLength; - const float newHeight = std::cos(angle) * hypotnuseLength; - - topRightXYZ[0] = topLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; - topRightXYZ[1] = topLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; - - bottomLeftXYZ[0] = topLeftXYZ[0] - bottomToTopUnitVector[0] * newHeight; - bottomLeftXYZ[1] = topLeftXYZ[1] - bottomToTopUnitVector[1] * newHeight; - - validCoordinatesFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - { - /* - * Top right is now at the mouse XY - */ - topRightXYZ[0] = spatialModification.m_mouseX; - topRightXYZ[1] = spatialModification.m_mouseY; - - /* - * Unit vector from updated top right to bottom left - */ - float topRightToBottomLeftUnitVector[3]; - MathFunctions::createUnitVector(topRightXYZ, bottomLeftXYZ, topRightToBottomLeftUnitVector); - - /* - * We have a right triangle where: - * The hypotnuse is from bottom left corner to new top right corner - * A right angle is at top left corner - */ - const float oppositeAngle = MathFunctions::angle(topLeftXYZ, - bottomLeftXYZ, - topRightXYZ); - const float angle = (M_PI / 2.0) - oppositeAngle; - const float hypotnuseLength = MathFunctions::distance3D(topRightXYZ, - bottomLeftXYZ); - - const float newWidth = std::cos(angle) * hypotnuseLength; - const float newHeight = std::sin(angle) * hypotnuseLength; - - topLeftXYZ[0] = topRightXYZ[0] - leftToRightUnitVector[0] * newWidth; - topLeftXYZ[1] = topRightXYZ[1] - leftToRightUnitVector[1] * newWidth; - - bottomRightXYZ[0] = topRightXYZ[0] - bottomToTopUnitVector[0] * newHeight; - bottomRightXYZ[1] = topRightXYZ[1] - bottomToTopUnitVector[1] * newHeight; - - validCoordinatesFlag = true; - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - CaretAssert(0); - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - CaretAssert(0); - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - addToXYZWithXY(bottomLeftXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); - addToXYZWithXY(bottomRightXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); - addToXYZWithXY(topRightXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); - addToXYZWithXY(topLeftXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); - - validCoordinatesFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - { - /* - * Rotation angle is a formed by the triangle - * (Mouse XY, Annotation XY, Positive X-axis). - */ - const float dy = spatialModification.m_mouseY - viewportXYZ[1]; - const float dx = spatialModification.m_mouseX - viewportXYZ[0]; - - const float angleRadians = std::atan2(dy, dx); - const float angleDegrees = MathFunctions::toDegrees(angleRadians); - m_rotationAngle = 90.0 - angleDegrees; - if (m_rotationAngle > 360.0) { - m_rotationAngle -= 360.0; - } - else if (m_rotationAngle < 0.0) { - m_rotationAngle += 360.0; - } - validRotationFlag = true; - } - break; - } - - if (validCoordinatesFlag) { - /* - * Using the updated corners of the annotation, convert back to normalized x, y, width, and aspect ratio - */ - float newViewportXYZ[3]; - MathFunctions::averageOfFourCoordinates(bottomLeftXYZ, bottomRightXYZ, topRightXYZ, topLeftXYZ, newViewportXYZ); - const float newX = 100.0 * (newViewportXYZ[0] / spatialModification.m_viewportWidth); - const float newY = 100.0 * (newViewportXYZ[1] / spatialModification.m_viewportHeight); - const float newShapeViewportWidth = MathFunctions::distance3D(bottomLeftXYZ, bottomRightXYZ); - const float newWidth = 100.0 * (newShapeViewportWidth / spatialModification.m_viewportWidth); - const float newShapeViewportHeight = MathFunctions::distance3D(bottomLeftXYZ, topLeftXYZ); - const float newHeight = 100.0 * (newShapeViewportHeight / spatialModification.m_viewportHeight); - - /* - * Note: - * Coordinates are relative (range 0 to 100) - * Width is relative (range 0 to 100) - * Aspect ratio must only be greater than zero (when < 1, horizontal rectangle, when > 1 vertical rectangle) - */ - if ((newX >= 0.0) - && (newX <= 100.0) - && (newY >= 0.0) - && (newY <= 100.0)) { - - if (isFixedAspectRatio()) { - xyz[0] = newX; - xyz[1] = newY; - - m_coordinate->setXYZ(xyz); - - switch (spatialModification.m_sizingHandleType) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - if (std::fabs(sideHandleDX) > std::fabs(sideHandleDY)) { - /* - * For fixed aspect ratio, the width percentage is not a percentage - * of window width but of the window's height since the annotation - * is sized by its height (width = height / aspect). - * - * So when width is changed, need to set height. - */ - const float aspectRatio = getFixedAspectRatio(); - const float newViewportHeight = newShapeViewportWidth * aspectRatio; - const float newShapeHeight = 100.0 * (newViewportHeight / spatialModification.m_viewportHeight); - setHeight(newShapeHeight); - } - else if (std::fabs(sideHandleDX) < std::fabs(sideHandleDY)) { - setHeight(newHeight); - } - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - break; - } - } - else { - xyz[0] = newX; - xyz[1] = newY; - m_coordinate->setXYZ(xyz); - - m_width = newWidth; - m_height = newHeight; - } - } - else { - validCoordinatesFlag = false; - } - } - - if (validCoordinatesFlag - || validRotationFlag) { - setModified(); - return true; - } - - return false; -} - -/** - * Given the previous mouse location, current mouse location, and the shape - * location, examine the angle formed to determine if rotation is allowed. - * Note that if the mouse is moved purely horizontally or vertically from - * the rotation point, the rotation angle may rapidly change causing weird - * behavior. - * - * @param previousMouseXYZ - * Previous location of mouse. - * @param shapeXYZ - * Location of shape. - * @param currentMouseXYZ - * Current location of mouse. - * @return - * True if rotation is valid, else false. - */ -bool -AnnotationTwoDimensionalShape::rotationAngleTest(const float previousMouseXYZ[3], - const float shapeXYZ[3], - const float currentMouseXYZ[3]) const -{ - const float angle = MathFunctions::angle(currentMouseXYZ, - shapeXYZ, - previousMouseXYZ); - - static const float MINIMUM_ANGLE = 0.025; //0.01; - - if (angle > MINIMUM_ANGLE) { - return true; - } - - return false; -} - -/** - * Apply a spatial modification to an annotation. - * - * @param spatialModification - * Contains information about the spatial modification. - * @return - * True if the annotation was modified, else false. - */ -bool -AnnotationTwoDimensionalShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) -{ - if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { - return false; - } - - const AnnotationCoordinateSpaceEnum::Enum space = getCoordinateSpace(); - switch (space) { - case AnnotationCoordinateSpaceEnum::CHART: - return applySpatialModificationChartSpace(spatialModification); - break; - case AnnotationCoordinateSpaceEnum::SPACER: - return applySpatialModificationSpacerTabSpace(spatialModification); - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - return applySpatialModificationSurfaceOrStereotaxicSpace(spatialModification, - space); - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - return applySpatialModificationSurfaceOrStereotaxicSpace(spatialModification, - space); - break; - case AnnotationCoordinateSpaceEnum::TAB: - return applySpatialModificationTabOrWindowSpace(spatialModification); - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - return applySpatialModificationTabOrWindowSpace(spatialModification); - break; - } - - return false; -} - - -/** - * When adjusting one of the "side handles" of a selected shape, set and adjust the change in the shape - * so that the shape changes in the same direction as the mouse is moved. - * - * @param sizeHandle - Size handle that is being adjusted. - * @param leftToRightShapeVector - * Vector running from left to right of shape accounting for any rotation. - * @param bottomToTopShapeVector - * Vector running from bottom to top of shape accounting for any rotation. - * @param mouseDX - * Mouse movement in X. - * @param mouseDY - * Mouse movement in Y. - * @param shapeDxOut - * Suggested change in shape (signed). - * @param shapeDyOut - * Suggested change in shape (signed). - */ -void -AnnotationTwoDimensionalShape::getSideHandleMouseDelta(const AnnotationSizingHandleTypeEnum::Enum sizeHandle, - const float leftToRightShapeVector[3], - const float bottomToTopShapeVector[3], - const float mouseDX, - const float mouseDY, - float& shapeDxOut, - float& shapeDyOut) -{ - shapeDxOut = 0.0; - shapeDyOut = 0.0; - - bool useLeftRightFlag = false; - bool useBottomTopFlag = false; - bool posToNegFlag = false; - switch (sizeHandle) { - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: - useBottomTopFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: - useLeftRightFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: - useLeftRightFlag = true; - posToNegFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: - useBottomTopFlag = true; - posToNegFlag = true; - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - break; - case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - break; - } - - float shapeVector[3]; - if (useLeftRightFlag) { - shapeVector[0] = leftToRightShapeVector[0]; - shapeVector[1] = leftToRightShapeVector[1]; - shapeVector[2] = 0.0; - } - else if (useBottomTopFlag) { - shapeVector[0] = bottomToTopShapeVector[0]; - shapeVector[1] = bottomToTopShapeVector[1]; - shapeVector[2] = 0.0; - } - else { - return; - } - if (posToNegFlag) { - shapeVector[0] = -shapeVector[0]; - shapeVector[1] = -shapeVector[1]; - } - - MathFunctions::normalizeVector(shapeVector); - - float mouseVector[3] = { mouseDX, mouseDY, 0.0 }; - float mouseDelta = MathFunctions::normalizeVector(mouseVector); - - const float cosineAngle = MathFunctions::dotProduct(mouseVector, - shapeVector); - if (cosineAngle < 0.0) { - mouseDelta = -mouseDelta; - } - - shapeDxOut = (shapeVector[0] * mouseDelta); - shapeDyOut = (shapeVector[1] * mouseDelta); -} - -/** - * Add the given X and Y values to the three-dimensional coordinate. - * - * @param xyz - * Coordinate that has values added to it. - * @param addX - * Value that is added coordinate's X. - * @param addY - * Value that is added coordinate's Y. - */ -void -AnnotationTwoDimensionalShape::addToXYZWithXY(float xyz[3], - const float addX, - const float addY) -{ - xyz[0] += addX; - xyz[1] += addY; -} - -/** - * Get the bounds for a two-dimensional shape annotation. - * - * @param viewportWidth - * Width of the viewport. - * @param viewportHeight - * Height of the viewport. - * @param viewportXYZ - * Viewport coordinates of the annotation. - * @param bottomLeftOut - * The bottom left corner of the annotation absolute bounds. - * @param bottomRightOut - * The bottom right corner of the annotation absolute bounds. - * @param topRightOut - * The top right corner of the annotation absolute bounds. - * @param topLeftOut - * The top left corner of the annotation absolute bounds. - */ -bool -AnnotationTwoDimensionalShape::getShapeBounds(const float viewportWidth, - const float viewportHeight, - const float viewportXYZ[3], - float bottomLeftOut[3], - float bottomRightOut[3], - float topRightOut[3], - float topLeftOut[3]) const -{ - /* - * NOTE: Annotation's height and width are 'relative' ([0.0, 100.0] percentage) of window size. - * So want HALF of width/height - */ - const float width = getWidth(); - float halfWidth = (width / 200.0) * viewportWidth; - const float halfHeight = (getHeight() / 200.0) * viewportHeight; - if (isFixedAspectRatio()) { - halfWidth = halfHeight / getFixedAspectRatio(); - } - - bottomLeftOut[0] = viewportXYZ[0] - halfWidth; - bottomLeftOut[1] = viewportXYZ[1] - halfHeight; - bottomLeftOut[2] = viewportXYZ[2]; - bottomRightOut[0] = viewportXYZ[0] + halfWidth; - bottomRightOut[1] = viewportXYZ[1] - halfHeight; - bottomRightOut[2] = viewportXYZ[2]; - topRightOut[0] = viewportXYZ[0] + halfWidth; - topRightOut[1] = viewportXYZ[1] + halfHeight; - topRightOut[2] = viewportXYZ[2]; - topLeftOut[0] = viewportXYZ[0] - halfWidth; - topLeftOut[1] = viewportXYZ[1] + halfHeight; - topLeftOut[2] = viewportXYZ[2]; - - if (m_rotationAngle != 0) { - Matrix4x4 matrix; - matrix.translate(-viewportXYZ[0], -viewportXYZ[1], -viewportXYZ[2]); - matrix.rotateZ(-m_rotationAngle); - matrix.translate(viewportXYZ[0], viewportXYZ[1], viewportXYZ[2]); - matrix.multiplyPoint3(bottomLeftOut); - matrix.multiplyPoint3(bottomRightOut); - matrix.multiplyPoint3(topRightOut); - matrix.multiplyPoint3(topLeftOut); - } - return true; -} - -/** - * Set the width and height of the shape from bounding coordinates. - * - * @param xyzOne - * First bounding coordinate in absolute tab coordinates - * @param xyzTwo - * Second bounding coordinate in absolute tab coordinates - * @param spaceWidth - * Width of space. - * @param spaceHeight - * Height of space. - */ -void -AnnotationTwoDimensionalShape::setWidthAndHeightFromBounds(const float xyzOne[3], - const float xyzTwo[3], - const float spaceWidth, - const float spaceHeight) -{ - if ((spaceWidth > 0.0) - && (spaceHeight > 0.0)) { - const float minX = std::min(xyzOne[0], - xyzTwo[0]); - const float maxX = std::max(xyzOne[0], - xyzTwo[0]); - - const float minY = std::min(xyzOne[1], - xyzTwo[1]); - const float maxY = std::max(xyzOne[1], - xyzTwo[1]); - - const float width = ((maxX - minX) / spaceWidth) * 100.0; - const float height = ((maxY - minY) / spaceHeight) * 100.0; - - setWidth(width); - setHeight(height); - } -} - -/** - * Save subclass data to the scene. - * - * @param sceneAttributes - * Attributes for the scene. Scenes may be of different types - * (full, generic, etc) and the attributes should be checked when - * restoring the scene. - * - * @param sceneClass - * sceneClass to which data members should be added. Will always - * be valid (non-NULL). - */ -void -AnnotationTwoDimensionalShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, - SceneClass* sceneClass) -{ - m_sceneAssistant->saveMembers(sceneAttributes, - sceneClass); -} - -/** - * Restore file data from the scene. - * - * @param sceneAttributes - * Attributes for the scene. Scenes may be of different types - * (full, generic, etc) and the attributes should be checked when - * restoring the scene. - * - * @param sceneClass - * sceneClass for the instance of a class that implements - * this interface. Will NEVER be NULL. - */ -void -AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass) -{ - m_sceneAssistant->restoreMembers(sceneAttributes, - sceneClass); -} - -/** - * Set the default value for height - * - * @param height - * Default for newly created text annotations. - */ -void -AnnotationTwoDimensionalShape::setUserDefaultHeight(const float height) -{ - s_userDefaultHeight = height; -} - -/** - * Set the default value for width - * - * @param width - * Default for newly created annotations. - */ -void -AnnotationTwoDimensionalShape::setUserDefaultWidth(const float width) -{ - s_userDefaultWidth = width; -} diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationTwoDimensionalShape.h connectome-workbench-1.5.0/src/Annotations/AnnotationTwoDimensionalShape.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationTwoDimensionalShape.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationTwoDimensionalShape.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,167 +0,0 @@ -#ifndef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ -#define __ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "Annotation.h" -#include "CaretPointer.h" - - -namespace caret { - - class AnnotationCoordinate; - - class AnnotationTwoDimensionalShape : public Annotation { - - public: - AnnotationTwoDimensionalShape(const AnnotationTypeEnum::Enum type, - const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); - - virtual ~AnnotationTwoDimensionalShape(); - - AnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj); - - AnnotationTwoDimensionalShape& operator=(const AnnotationTwoDimensionalShape& obj); - - virtual AnnotationOneDimensionalShape* castToOneDimensionalShape() override; - - virtual const AnnotationOneDimensionalShape* castToOneDimensionalShape() const override; - - virtual AnnotationTwoDimensionalShape* castToTwoDimensionalShape() override; - - virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const override; - - AnnotationCoordinate* getCoordinate(); - - const AnnotationCoordinate* getCoordinate() const; - - virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const override; - - float getHeight() const; - - void setHeight(const float height); - - float getWidth() const; - - void setWidth(const float width); - - float getRotationAngle() const; - - void setRotationAngle(const float rotationAngle); - - virtual bool isModified() const; - - virtual void clearModified(); - - virtual bool isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const; - - virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); - - virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); - - virtual bool getShapeBounds(const float viewportWidth, - const float viewportHeight, - const float viewportXYZ[3], - float bottomLeftOut[3], - float bottomRightOut[3], - float topRightOut[3], - float topLeftOut[3]) const; - - void getSideHandleMouseDelta(const AnnotationSizingHandleTypeEnum::Enum sizeHandle, - const float leftToRightShapeVector[3], - const float bottomToTopShapeVector[3], - const float mouseDX, - const float mouseDY, - float& shapeDxOut, - float& shapeDyOut); - - void setWidthAndHeightFromBounds(const float xyzOne[3], - const float xyzTwo[3], - const float spaceWidth, - const float spaceHeight); - - static void setUserDefaultHeight(const float height); - - static void setUserDefaultWidth(const float width); - - // ADD_NEW_METHODS_HERE - - - - - - - protected: - virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, - SceneClass* sceneClass); - - virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass); - - private: - void copyHelperAnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj); - - void initializeMembersAnnotationTwoDimensionalShape(); - - void addToXYZWithXY(float xyz[3], - const float addX, - const float addY); - - bool applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification); - - bool applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, - const AnnotationCoordinateSpaceEnum::Enum coordinateSpace); - - bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); - - bool applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification); - - bool rotationAngleTest(const float previousMouseXYZ[3], - const float shapeXYZ[3], - const float currentMouseXYZ[3]) const; - - CaretPointer m_sceneAssistant; - - CaretPointer m_coordinate; - - float m_rotationAngle; - - float m_width; - - float m_height; - - static float s_userDefaultWidth; - - static float s_userDefaultHeight; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ - float AnnotationTwoDimensionalShape::s_userDefaultWidth = 25.0; - - float AnnotationTwoDimensionalShape::s_userDefaultHeight = 25.0; -#endif // __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ - -} // namespace -#endif //__ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationTypeEnum.cxx connectome-workbench-1.5.0/src/Annotations/AnnotationTypeEnum.cxx --- connectome-workbench-1.4.2/src/Annotations/AnnotationTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -110,6 +110,10 @@ "BOX", "Box")); + enumData.push_back(AnnotationTypeEnum(BROWSER_TAB, + "BROWSER_TAB", + "Browser Tab")); + enumData.push_back(AnnotationTypeEnum(COLOR_BAR, "COLOR_BAR", "Color Bar")); @@ -126,6 +130,14 @@ "OVAL", "Oval")); + enumData.push_back(AnnotationTypeEnum(POLY_LINE, + "POLY_LINE", + "Poly Line")); + + enumData.push_back(AnnotationTypeEnum(SCALE_BAR, + "SCALE_BAR", + "Scale Bar")); + enumData.push_back(AnnotationTypeEnum(TEXT, "TEXT", "Text")); diff -Nru connectome-workbench-1.4.2/src/Annotations/AnnotationTypeEnum.h connectome-workbench-1.5.0/src/Annotations/AnnotationTypeEnum.h --- connectome-workbench-1.4.2/src/Annotations/AnnotationTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/AnnotationTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,6 +37,8 @@ enum Enum { /** Box */ BOX, + /** Browser Tab */ + BROWSER_TAB, /** Colorbar */ COLOR_BAR, /** Image */ @@ -45,6 +47,10 @@ LINE, /** Oval */ OVAL, + /** Poly Line */ + POLY_LINE, + /** Scale Bar */ + SCALE_BAR, /** Text */ TEXT }; diff -Nru connectome-workbench-1.4.2/src/Annotations/CMakeLists.txt connectome-workbench-1.5.0/src/Annotations/CMakeLists.txt --- connectome-workbench-1.4.2/src/Annotations/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -27,6 +27,7 @@ AnnotationAlignmentEnum.h AnnotationAttributesDefaultTypeEnum.h AnnotationBox.h +AnnotationBrowserTab.h AnnotationColorBar.h AnnotationColorBarNumericText.h AnnotationColorBarPositionModeEnum.h @@ -34,6 +35,7 @@ AnnotationCoordinate.h AnnotationCoordinateSpaceEnum.h AnnotationDistributeEnum.h +AnnotationEditingSelectionInformation.h AnnotationFontAttributesInterface.h AnnotationGroup.h AnnotationGroupKey.h @@ -41,15 +43,21 @@ AnnotationGroupingModeEnum.h AnnotationImage.h AnnotationLine.h -AnnotationOneDimensionalShape.h +AnnotationMultiCoordinateShape.h +AnnotationOneCoordinateShape.h AnnotationOval.h AnnotationPercentSizeText.h AnnotationPointSizeText.h +AnnotationPolyLine.h AnnotationRedoUndoCommand.h AnnotationRedoUndoCommandModeEnum.h -AnnotationEditingSelectionInformation.h +AnnotationScaleBar.h +AnnotationScaleBarTextLocationEnum.h +AnnotationScaleBarUnitsTypeEnum.h AnnotationSizingHandleTypeEnum.h AnnotationSpatialModification.h +AnnotationStackingOrderOperation.h +AnnotationStackingOrderTypeEnum.h AnnotationSurfaceOffsetVectorTypeEnum.h AnnotationText.h AnnotationTextAlignHorizontalEnum.h @@ -59,19 +67,22 @@ AnnotationTextFontPointSizeEnum.h AnnotationTextFontSizeTypeEnum.h AnnotationTextOrientationEnum.h -AnnotationTwoDimensionalShape.h +AnnotationTwoCoordinateShape.h AnnotationTypeEnum.h EventAnnotationAddToRemoveFromFile.h EventAnnotationChartLabelGet.h EventAnnotationGroupGetWithKey.h EventAnnotationGrouping.h +EventAnnotationTextGetBounds.h EventAnnotationTextSubstitutionGet.h EventAnnotationTextSubstitutionInvalidate.h +EventAnnotationValidate.h Annotation.cxx AnnotationAlignmentEnum.cxx AnnotationAttributesDefaultTypeEnum.cxx AnnotationBox.cxx +AnnotationBrowserTab.cxx AnnotationColorBar.cxx AnnotationColorBarNumericText.cxx AnnotationColorBarPositionModeEnum.cxx @@ -79,21 +90,28 @@ AnnotationCoordinate.cxx AnnotationCoordinateSpaceEnum.cxx AnnotationDistributeEnum.cxx +AnnotationEditingSelectionInformation.cxx AnnotationGroup.cxx AnnotationGroupKey.cxx AnnotationGroupTypeEnum.cxx AnnotationGroupingModeEnum.cxx AnnotationImage.cxx AnnotationLine.cxx -AnnotationOneDimensionalShape.cxx +AnnotationMultiCoordinateShape.cxx +AnnotationOneCoordinateShape.cxx AnnotationOval.cxx AnnotationPercentSizeText.cxx AnnotationPointSizeText.cxx +AnnotationPolyLine.cxx AnnotationRedoUndoCommand.cxx AnnotationRedoUndoCommandModeEnum.cxx -AnnotationEditingSelectionInformation.cxx +AnnotationScaleBar.cxx +AnnotationScaleBarTextLocationEnum.cxx +AnnotationScaleBarUnitsTypeEnum.cxx AnnotationSizingHandleTypeEnum.cxx AnnotationSpatialModification.cxx +AnnotationStackingOrderOperation.cxx +AnnotationStackingOrderTypeEnum.cxx AnnotationSurfaceOffsetVectorTypeEnum.cxx AnnotationText.cxx AnnotationTextAlignHorizontalEnum.cxx @@ -103,14 +121,16 @@ AnnotationTextFontPointSizeEnum.cxx AnnotationTextFontSizeTypeEnum.cxx AnnotationTextOrientationEnum.cxx -AnnotationTwoDimensionalShape.cxx +AnnotationTwoCoordinateShape.cxx AnnotationTypeEnum.cxx EventAnnotationAddToRemoveFromFile.cxx EventAnnotationChartLabelGet.cxx EventAnnotationGroupGetWithKey.cxx EventAnnotationGrouping.cxx +EventAnnotationTextGetBounds.cxx EventAnnotationTextSubstitutionGet.cxx EventAnnotationTextSubstitutionInvalidate.cxx +EventAnnotationValidate.cxx ) TARGET_LINK_LIBRARIES(Annotations ${CARET_QT5_LINK}) diff -Nru connectome-workbench-1.4.2/src/Annotations/EventAnnotationTextGetBounds.cxx connectome-workbench-1.5.0/src/Annotations/EventAnnotationTextGetBounds.cxx --- connectome-workbench-1.4.2/src/Annotations/EventAnnotationTextGetBounds.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/EventAnnotationTextGetBounds.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,134 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_ANNOTATION_TEXT_GET_BOUNDS_DECLARE__ +#include "EventAnnotationTextGetBounds.h" +#undef __EVENT_ANNOTATION_TEXT_GET_BOUNDS_DECLARE__ + +#include "AnnotationText.h" +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + +/** + * \class caret::EventAnnotationTextGetBounds + * \brief Event to get bounds for drawing texty + * \ingroup Annotations + */ + +/** + * Constructor. + * @param text + * The text + * @param viewportWidthForPercentageHeightText + * The text + * @param viewportHeightForPercentageHeightText + * The text + */ +EventAnnotationTextGetBounds::EventAnnotationTextGetBounds(const AnnotationText& annotationText, + const int32_t viewportWidthForPercentageHeightText, + const int32_t viewportHeightForPercentageHeightText) +: Event(EventTypeEnum::EVENT_ANNOTATION_TEXT_GET_BOUNDS), +m_annotationText(annotationText), +m_viewportWidth(viewportWidthForPercentageHeightText), +m_viewportHeight(viewportHeightForPercentageHeightText) +{ + +} + +/** + * Destructor. + */ +EventAnnotationTextGetBounds::~EventAnnotationTextGetBounds() +{ +} + +/** + * @return The text + */ +const AnnotationText& +EventAnnotationTextGetBounds::getAnnotationText() const +{ + return m_annotationText; +} + +/** + * @return Width of viewport + */ +int32_t +EventAnnotationTextGetBounds::getViewportWidth() const +{ + return m_viewportWidth; +} + +/** + * @return Height of viewport + */ +int32_t +EventAnnotationTextGetBounds::getViewportHeight() const +{ + return m_viewportHeight; +} + +/** + * @return Width of text + */ +float +EventAnnotationTextGetBounds::getTextWidth() const +{ + return m_textWidth; +} + +/** + * @return Height of text + */ +float +EventAnnotationTextGetBounds::getTextHeight() const +{ + return m_textHeight; +} + +/** + * Set the width and height of text and sets validity to true. + * + * @param textWidth + * Width of text + * @param textHeight + * Height of text + */ +void +EventAnnotationTextGetBounds::setTextWidthHeight(const float textWidth, + const float textHeight) +{ + m_textWidth = textWidth; + m_textHeight = textHeight; + m_validFlag = true; +} + +/** + * @return True if text width/height has been set + */ +bool +EventAnnotationTextGetBounds::isValid() const +{ + return m_validFlag; +} diff -Nru connectome-workbench-1.4.2/src/Annotations/EventAnnotationTextGetBounds.h connectome-workbench-1.5.0/src/Annotations/EventAnnotationTextGetBounds.h --- connectome-workbench-1.4.2/src/Annotations/EventAnnotationTextGetBounds.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/EventAnnotationTextGetBounds.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,89 @@ +#ifndef __EVENT_ANNOTATION_TEXT_GET_BOUNDS_H__ +#define __EVENT_ANNOTATION_TEXT_GET_BOUNDS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class AnnotationText; + + class EventAnnotationTextGetBounds : public Event { + + public: + EventAnnotationTextGetBounds(const AnnotationText& annotationText, + const int32_t viewportWidthForPercentageHeightText, + const int32_t viewportHeightForPercentageHeightText); + + const AnnotationText& getAnnotationText() const; + + int32_t getViewportWidth() const; + + int32_t getViewportHeight() const; + + float getTextWidth() const; + + float getTextHeight() const; + + void setTextWidthHeight(const float textWidth, + const float textHeight); + + bool isValid() const; + + virtual ~EventAnnotationTextGetBounds(); + + EventAnnotationTextGetBounds(const EventAnnotationTextGetBounds&) = delete; + + EventAnnotationTextGetBounds& operator=(const EventAnnotationTextGetBounds&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + const AnnotationText& m_annotationText; + + const int32_t m_viewportWidth; + + const int32_t m_viewportHeight; + + float m_textWidth = 0.0; + + float m_textHeight = 0.0; + + bool m_validFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_ANNOTATION_TEXT_GET_BOUNDS_DECLARE__ + // +#endif // __EVENT_ANNOTATION_TEXT_GET_BOUNDS_DECLARE__ + +} // namespace +#endif //__EVENT_ANNOTATION_TEXT_GET_BOUNDS_H__ diff -Nru connectome-workbench-1.4.2/src/Annotations/EventAnnotationValidate.cxx connectome-workbench-1.5.0/src/Annotations/EventAnnotationValidate.cxx --- connectome-workbench-1.4.2/src/Annotations/EventAnnotationValidate.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/EventAnnotationValidate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,86 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_ANNOTATION_VALIDATE_DECLARE__ +#include "EventAnnotationValidate.h" +#undef __EVENT_ANNOTATION_VALIDATE_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventAnnotationValidate + * \brief Verify that an annotation pointer is valid (annotation exists) + * \ingroup Annotations + */ + +/** + * Constructor. + * + * @param annotation + * Annotation tested for validity + */ +EventAnnotationValidate::EventAnnotationValidate(const Annotation* annotation) +: Event(EventTypeEnum::EVENT_ANNOTATION_VALIDATE), +m_annotation(annotation) +{ + CaretAssert(m_annotation); + m_validFlag = false; +} + +/** + * Destructor. + */ +EventAnnotationValidate::~EventAnnotationValidate() +{ +} + +/** + * @return Pointer to annotation being tested for validity + */ +const Annotation* +EventAnnotationValidate::getAnnotation() const +{ + return m_annotation; +} + +/** + * @return Validity status of annotation + */ +bool +EventAnnotationValidate::isAnnotationValid() const +{ + return m_validFlag; +} + +/** + * Set the annotation as valid + */ +void +EventAnnotationValidate::setAnnotationValid() +{ + m_validFlag = true; +} + diff -Nru connectome-workbench-1.4.2/src/Annotations/EventAnnotationValidate.h connectome-workbench-1.5.0/src/Annotations/EventAnnotationValidate.h --- connectome-workbench-1.4.2/src/Annotations/EventAnnotationValidate.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Annotations/EventAnnotationValidate.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,70 @@ +#ifndef __EVENT_ANNOTATION_VALIDATE_H__ +#define __EVENT_ANNOTATION_VALIDATE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class Annotation; + + class EventAnnotationValidate : public Event { + + public: + EventAnnotationValidate(const Annotation* annotation); + + virtual ~EventAnnotationValidate(); + + EventAnnotationValidate(const EventAnnotationValidate&) = delete; + + EventAnnotationValidate& operator=(const EventAnnotationValidate&) = delete; + + const Annotation* getAnnotation() const; + + bool isAnnotationValid() const; + + void setAnnotationValid(); + + // ADD_NEW_METHODS_HERE + + private: + + const Annotation* m_annotation; + + bool m_validFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_ANNOTATION_VALIDATE_DECLARE__ + // +#endif // __EVENT_ANNOTATION_VALIDATE_DECLARE__ + +} // namespace +#endif //__EVENT_ANNOTATION_VALIDATE_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/AnnotationArrangerExecutor.cxx connectome-workbench-1.5.0/src/Brain/AnnotationArrangerExecutor.cxx --- connectome-workbench-1.4.2/src/Brain/AnnotationArrangerExecutor.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/AnnotationArrangerExecutor.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -31,7 +31,7 @@ #include "AnnotationArrangerInputs.h" #include "AnnotationCoordinate.h" #include "AnnotationManager.h" -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationText.h" #include "BrainOpenGLTextRenderInterface.h" @@ -54,8 +54,9 @@ /** * Constructor. */ -AnnotationArrangerExecutor::AnnotationArrangerExecutor() +AnnotationArrangerExecutor::AnnotationArrangerExecutor(const UserInputModeEnum::Enum userInputMode) : CaretObject(), +m_userInputMode(userInputMode), m_mode(MODE_NONE), m_annotationManager(NULL), m_alignment(AnnotationAlignmentEnum::ALIGN_BOTTOM), @@ -75,6 +76,8 @@ /** * Apply alignment modification to selected annotations * + * @param userInputMode + * The current user input mode which MUST be ANNOTATIONS or TILE_TABS_MANUAL_LAYOUT_EDITING * @param annotationManager * The annotation manager. * @param arrangerInputs @@ -115,6 +118,8 @@ /** * Apply distribute modification to selected annotations * + * @param userInputMode + * The current user input mode which MUST be ANNOTATIONS or TILE_TABS_MANUAL_LAYOUT_EDITING * @param annotationManager * The annotation manager. * @param arrangerInputs @@ -342,7 +347,8 @@ afterMoving); undoCommand->setDescription(AnnotationDistributeEnum::toGuiName(m_distribute)); - validFlag = m_annotationManager->applyCommand(undoCommand, + validFlag = m_annotationManager->applyCommand(m_userInputMode, + undoCommand, errorMessage); } @@ -452,7 +458,8 @@ afterMoving); undoCommand->setDescription(AnnotationAlignmentEnum::toGuiName(m_alignment)); - validFlag = m_annotationManager->applyCommand(undoCommand, + validFlag = m_annotationManager->applyCommand(m_userInputMode, + undoCommand, errorMessage); } @@ -520,8 +527,8 @@ annInfoIter++) { const AnnotationInfo& annInfo = *annInfoIter; - m_allAnnotationsBoundingBox.update(annInfo.m_windowBoundingBox.getMinXYZ()); - m_allAnnotationsBoundingBox.update(annInfo.m_windowBoundingBox.getMaxXYZ()); + m_allAnnotationsBoundingBox.update(annInfo.m_windowBoundingBox.getMinXYZ().data()); + m_allAnnotationsBoundingBox.update(annInfo.m_windowBoundingBox.getMaxXYZ().data()); } } @@ -642,8 +649,8 @@ break; } - const AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); - const AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); + const AnnotationTwoCoordinateShape* oneDimAnn = dynamic_cast(annotation); + const AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); float viewportPixelOneXYZ[3] = { 0.0, 0.0, 0.0 }; float viewportPixelTwoXYZ[3] = { 0.0, 0.0, 0.0 }; @@ -1037,8 +1044,8 @@ const float dy) const { CaretAssert(m_annotation); - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); + AnnotationTwoCoordinateShape* oneDimAnn = dynamic_cast(annotation); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); const float newPixelOneX = m_viewportPixelOneXY[0] + dx; const float relativeOneX = (newPixelOneX / m_viewport[2]) * 100.0; diff -Nru connectome-workbench-1.4.2/src/Brain/AnnotationArrangerExecutor.h connectome-workbench-1.5.0/src/Brain/AnnotationArrangerExecutor.h --- connectome-workbench-1.4.2/src/Brain/AnnotationArrangerExecutor.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/AnnotationArrangerExecutor.h 2021-02-16 19:46:47.000000000 +0000 @@ -28,6 +28,7 @@ #include "BoundingBox.h" #include "CaretObject.h" #include "SpacerTabIndex.h" +#include "UserInputModeEnum.h" namespace caret { @@ -38,7 +39,7 @@ class AnnotationArrangerExecutor : public CaretObject { public: - AnnotationArrangerExecutor(); + AnnotationArrangerExecutor(const UserInputModeEnum::Enum userInputMode); virtual ~AnnotationArrangerExecutor(); @@ -138,6 +139,8 @@ void printAnnotationInfo(const QString& title); + const UserInputModeEnum::Enum m_userInputMode; + Mode m_mode; AnnotationManager* m_annotationManager; diff -Nru connectome-workbench-1.4.2/src/Brain/AnnotationManager.cxx connectome-workbench-1.5.0/src/Brain/AnnotationManager.cxx --- connectome-workbench-1.4.2/src/Brain/AnnotationManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/AnnotationManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,26 +25,34 @@ #include "Annotation.h" #include "AnnotationArrangerExecutor.h" +#include "AnnotationBrowserTab.h" #include "AnnotationColorBar.h" #include "AnnotationFile.h" #include "AnnotationGroup.h" -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationEditingSelectionInformation.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationScaleBar.h" +#include "AnnotationStackingOrderOperation.h" +#include "AnnotationOneCoordinateShape.h" +#include "BrowserTabContent.h" +#include "BrowserWindowContent.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretUndoStack.h" #include "DisplayPropertiesAnnotation.h" #include "EventAnnotationChartLabelGet.h" -#include "EventAnnotationColorBarGet.h" +#include "EventAnnotationBarsGet.h" #include "EventAnnotationGroupGetWithKey.h" +#include "EventAnnotationValidate.h" +#include "EventBrowserTabGetAll.h" +#include "EventBrowserWindowContent.h" #include "EventGetDisplayedDataFiles.h" #include "EventManager.h" #include "SceneClass.h" #include "SceneClassAssistant.h" - +#include "EventUserInputModeGet.h" using namespace caret; @@ -68,8 +76,11 @@ m_clipboardAnnotationFile = NULL; m_clipboardAnnotation.grabNew(NULL); - m_annotationRedoUndoStack.grabNew(new CaretUndoStack()); - m_annotationRedoUndoStack->setUndoLimit(500); + m_annotationsExceptBrowserTabsRedoUndoStack.grabNew(new CaretUndoStack()); + m_annotationsExceptBrowserTabsRedoUndoStack->setUndoLimit(500); + + m_browserTabAnnotationsRedoUndoStack.grabNew(new CaretUndoStack()); + m_browserTabAnnotationsRedoUndoStack->setUndoLimit(100); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { m_annotationBeingDrawnInWindow[i] = NULL; @@ -77,6 +88,8 @@ } m_sceneAssistant = new SceneClassAssistant(); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_VALIDATE); } /** @@ -94,7 +107,8 @@ delete m_selectionInformation[i]; } - m_annotationRedoUndoStack->clear(); + m_annotationsExceptBrowserTabsRedoUndoStack->clear(); + m_browserTabAnnotationsRedoUndoStack->clear(); delete m_sceneAssistant; } @@ -105,7 +119,8 @@ void AnnotationManager::reset() { - m_annotationRedoUndoStack->clear(); + m_annotationsExceptBrowserTabsRedoUndoStack->clear(); + m_browserTabAnnotationsRedoUndoStack->clear(); } /** @@ -113,16 +128,22 @@ * the command, the command is placed on the undo stack so that the * user can undo or redo the command. * + * @param userInputMode + * The current user input mode which MUST be ANNOTATIONS or TILE_TABS_MANUAL_LAYOUT_EDITING * @param command * Command that will be applied to the selected annotations. * Annotation manager will take ownership of the command and * destroy it at the appropriate time. + * @param errorMessageOut + * Output with error information if command fails. */ bool -AnnotationManager::applyCommand(AnnotationRedoUndoCommand* command, +AnnotationManager::applyCommand(const UserInputModeEnum::Enum userInputMode, + AnnotationRedoUndoCommand* command, AString& errorMessageOut) { - return applyCommandInWindow(command, + return applyCommandInWindow(userInputMode, + command, -1, errorMessageOut); } @@ -132,15 +153,20 @@ * the command, the command is placed on the undo stack so that the * user can undo or redo the command. * + * @param userInputMode + * The current user input mode which MUST be ANNOTATIONS or TILE_TABS_MANUAL_LAYOUT_EDITING * @param command * Command that will be applied to the selected annotations. * Annotation manager will take ownership of the command and * destroy it at the appropriate time. * @param windowIndex * Index of window in which command was requested. + * @param errorMessageOut + * Output with error information if command fails. */ bool -AnnotationManager::applyCommandInWindow(AnnotationRedoUndoCommand* command, +AnnotationManager::applyCommandInWindow(const UserInputModeEnum::Enum userInputMode, + AnnotationRedoUndoCommand* command, const int32_t windowIndex, AString& errorMessageOut) { @@ -160,9 +186,9 @@ /* * "Redo" the command and add it to the undo stack */ - const bool result = m_annotationRedoUndoStack->pushAndRedo(command, - windowIndex, - errorMessageOut); + const bool result = getCommandRedoUndoStack(userInputMode)->pushAndRedo(command, + windowIndex, + errorMessageOut); return result; } @@ -188,9 +214,9 @@ false); } - EventAnnotationColorBarGet colorBarEvent; - EventManager::get()->sendEvent(colorBarEvent.getPointer()); - std::vector colorBars = colorBarEvent.getAnnotationColorBars(); + EventAnnotationBarsGet barsEvent; + EventManager::get()->sendEvent(barsEvent.getPointer()); + std::vector colorBars = barsEvent.getAnnotationColorBars(); for (std::vector::iterator iter = colorBars.begin(); iter != colorBars.end(); @@ -200,6 +226,12 @@ false); } + std::vector scaleBars = barsEvent.getAnnotationScaleBars(); + for (auto sb : scaleBars) { + sb->setSelectedForEditing(windowIndex, + false); + } + EventAnnotationChartLabelGet chartLabelEvent; EventManager::get()->sendEvent(chartLabelEvent.getPointer()); std::vector chartLabels = chartLabelEvent.getAnnotationChartLabels(); @@ -207,6 +239,14 @@ label->setSelectedForEditing(windowIndex, false); } + + EventBrowserTabGetAll allTabsEvent; + EventManager::get()->sendEvent(allTabsEvent.getPointer()); + std::vector allTabs = allTabsEvent.getAllBrowserTabs(); + for (auto tab : allTabs) { + tab->getManualLayoutBrowserTabAnnotation()->setSelectedForEditing(windowIndex, + false); + } } /** @@ -433,15 +473,19 @@ } } - EventAnnotationColorBarGet colorBarEvent; - EventManager::get()->sendEvent(colorBarEvent.getPointer()); - std::vector colorBars = colorBarEvent.getAnnotationColorBars(); - + EventAnnotationBarsGet barsEvent; + EventManager::get()->sendEvent(barsEvent.getPointer()); + std::vector colorBars = barsEvent.getAnnotationColorBars(); for (std::vector::iterator iter = colorBars.begin(); iter != colorBars.end(); iter++) { allAnnotations.push_back(*iter); } + + std::vector scaleBars = barsEvent.getAnnotationScaleBars(); + for (auto sb : scaleBars) { + allAnnotations.push_back(sb); + } EventAnnotationChartLabelGet chartLabelEvent; EventManager::get()->sendEvent(chartLabelEvent.getPointer()); @@ -450,10 +494,48 @@ chartLabels.begin(), chartLabels.end()); + EventBrowserTabGetAll allTabsEvent; + EventManager::get()->sendEvent(allTabsEvent.getPointer()); + std::vector allTabs = allTabsEvent.getAllBrowserTabs(); + for (auto tab : allTabs) { + allAnnotations.push_back(tab->getManualLayoutBrowserTabAnnotation()); + } + return allAnnotations; } /** + * Get all annotations that were drawn in the window with the given index and are in the same + * coordinate space (for tab/window also same tab/window). + * + * @param annotation + * Annotation for space comparison + * @param windowIndex + * Index of window + */ +std::vector +AnnotationManager::getAnnotationsDrawnInSameWindowAndSpace(const Annotation* annotation, + const int32_t windowIndex) const +{ + CaretAssert(annotation); + + std::vector allAnnotations = getAllAnnotations(); + + std::vector annotationsOut; + for (auto a : allAnnotations) { + if (a == annotation) { + continue; + } + if (a->isDrawnInWindowStatus(windowIndex)) { + if (annotation->isInSameCoordinateSpace(a)) { + annotationsOut.push_back(a); + } + } + } + return annotationsOut; +} + +/** * Get the annotation editing selection information for the given window. * * @param windowIndex @@ -467,6 +549,10 @@ { CaretAssertArrayIndex(m_selectionInformation, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); + EventUserInputModeGet modeEvent(windowIndex); + EventManager::get()->sendEvent(modeEvent.getPointer()); + const bool tileModeFlag = (modeEvent.getUserInputMode() == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); + AnnotationEditingSelectionInformation* asi = m_selectionInformation[windowIndex]; std::vector allAnnotations = getAllAnnotations(); @@ -474,10 +560,16 @@ for (std::vector::iterator annIter = allAnnotations.begin(); annIter != allAnnotations.end(); annIter++) { - Annotation* annotation = *annIter; - + Annotation* annotation = *annIter; if (annotation->isSelectedForEditing(windowIndex)) { - selectedAnnotations.push_back(annotation); + if (tileModeFlag) { + if (annotation->getType() == AnnotationTypeEnum::BROWSER_TAB) { + selectedAnnotations.push_back(annotation); + } + } + else if (annotation->getType() != AnnotationTypeEnum::BROWSER_TAB) { + selectedAnnotations.push_back(annotation); + } } } @@ -546,11 +638,18 @@ * A 'pair' containing a selected annotation and the file that contains the annotation. */ void -AnnotationManager::getAnnotationsSelectedForEditing(const int32_t windowIndex, +AnnotationManager::getAnnotationsAndFilesSelectedForEditing(const int32_t windowIndex, std::vector >& annotationsAndFileOut) const { annotationsAndFileOut.clear(); + EventUserInputModeGet modeEvent(windowIndex); + EventManager::get()->sendEvent(modeEvent.getPointer()); + if (modeEvent.getUserInputMode() == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING) { + /* In Tile Editing mode and browser tabs are not in files */ + return; + } + std::vector annotationFiles; m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); @@ -571,7 +670,6 @@ } } } - } /** @@ -584,10 +682,19 @@ * A 'pair' containing a selected annotation and the file that contains the annotation. */ void -AnnotationManager::getAnnotationsSelectedForEditingIncludingLabels(const int32_t windowIndex, +AnnotationManager::getAnnotationsAndFilesSelectedForEditingIncludingLabels(const int32_t windowIndex, std::vector >& annotationsAndFileOut) const { - getAnnotationsSelectedForEditing(windowIndex, + annotationsAndFileOut.clear(); + + EventUserInputModeGet modeEvent(windowIndex); + EventManager::get()->sendEvent(modeEvent.getPointer()); + if (modeEvent.getUserInputMode() == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING) { + /* In Tile Editing mode and browser tabs are not in files */ + return; + } + + getAnnotationsAndFilesSelectedForEditing(windowIndex, annotationsAndFileOut); EventAnnotationChartLabelGet chartLabelEvent; @@ -604,6 +711,8 @@ /** * Align annotations. * + * @param userInputMode + * The current user input mode which MUST be ANNOTATIONS or TILE_TABS_MANUAL_LAYOUT_EDITING * @param arrangerInputs * Inputs to algorithm that aligns the annotations. * @param errorMessageOut @@ -612,11 +721,12 @@ * True if successful, false if error. */ bool -AnnotationManager::alignAnnotations(const AnnotationArrangerInputs& arrangerInputs, +AnnotationManager::alignAnnotations(const UserInputModeEnum::Enum userInputMode, + const AnnotationArrangerInputs& arrangerInputs, const AnnotationAlignmentEnum::Enum alignment, AString& errorMessageOut) { - AnnotationArrangerExecutor arranger; + AnnotationArrangerExecutor arranger(userInputMode); return arranger.alignAnnotations(this, arrangerInputs, @@ -627,6 +737,8 @@ /** * Align annotations. * + * @param userInputMode + * The current user input mode which MUST be ANNOTATIONS or TILE_TABS_MANUAL_LAYOUT_EDITING * @param arrangerInputs * Inputs to algorithm that aligns the annotations. * @param errorMessageOut @@ -635,11 +747,12 @@ * True if successful, false if error. */ bool -AnnotationManager::distributeAnnotations(const AnnotationArrangerInputs& arrangerInputs, +AnnotationManager::distributeAnnotations(const UserInputModeEnum::Enum userInputMode, + const AnnotationArrangerInputs& arrangerInputs, const AnnotationDistributeEnum::Enum distribute, AString& errorMessageOut) { - AnnotationArrangerExecutor arranger; + AnnotationArrangerExecutor arranger(userInputMode); return arranger.distributeAnnotations(this, arrangerInputs, @@ -650,6 +763,8 @@ /** * Apply given grouping mode valid in the given window. * + * @param userInputMode + * The current user input mode which MUST be ANNOTATIONS or TILE_TABS_MANUAL_LAYOUT_EDITING * @param windowIndex * Index of the window. * @param groupingMode @@ -660,7 +775,8 @@ * True if successful, else false. */ bool -AnnotationManager::applyGroupingMode(const int32_t windowIndex, +AnnotationManager::applyGroupingMode(const UserInputModeEnum::Enum userInputMode, + const int32_t windowIndex, const AnnotationGroupingModeEnum::Enum groupingMode, AString& errorMessageOut) { @@ -699,7 +815,8 @@ AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingGroupAnnotations(annotationGroupKey, annotations); - validFlag = applyCommandInWindow(command, + validFlag = applyCommandInWindow(userInputMode, + command, windowIndex, errorMessageOut); } @@ -719,7 +836,8 @@ AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingRegroupAnnotations(annotationGroupKey); - validFlag = applyCommandInWindow(command, + validFlag = applyCommandInWindow(userInputMode, + command, windowIndex, errorMessageOut); } @@ -740,7 +858,8 @@ AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingUngroupAnnotations(annotationGroupKey); - validFlag = applyCommandInWindow(command, + validFlag = applyCommandInWindow(userInputMode, + command, windowIndex, errorMessageOut); } @@ -765,6 +884,215 @@ return getAnnotationEditingSelectionInformation(windowIndex)->isGroupingModeValid(groupingMode); } +/** + * Apply a stacking order change + * + * @param annotations + * The annotations that are reordered + * @param selectedAnnotation + * The selected annotation that is moved in order relative to other annotations + * @param orderType + * Type of ordering + * @param windowIndex + * Index of window + * @param errorMessageOut + * Output with error information + * @return True is successful, else false + */ +bool +AnnotationManager::applyStackingOrder(const std::vector& annotations, + const Annotation* selectedAnnotation, + const AnnotationStackingOrderTypeEnum::Enum orderType, + const int32_t windowIndex, + AString& errorMessageOut) +{ + AnnotationStackingOrderOperation operation(AnnotationStackingOrderOperation::Mode::MODE_REQUEST_NEW_ORDER_VALUES, + annotations, + selectedAnnotation, + windowIndex); + if ( ! operation.runOrdering(orderType, + errorMessageOut)) { + return false; + } + + std::vector results = operation.getNewStackingOrderResults(); + if (results.empty()) { + return true; + } + + std::vector stackAnns; + std::vector stackOrders; + for (auto& nso : results) { + stackAnns.push_back(nso.m_annotation); + stackOrders.push_back(nso.m_newStackOrder); + } + + AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); + command->setModeStackingOrderAnnotations(stackAnns, + stackOrders, + orderType); + + const float resultFlag = applyCommandInWindow(UserInputModeEnum::Enum::ANNOTATIONS, + command, + windowIndex, + errorMessageOut); + return resultFlag; +} + +/** + * @return All annotations in the same space as the given annotation. If the annotation + * is a browser tab annotation, ALL browser tab annotations are returned. + * @param annotation + * The annotation for space matching and NOT in the return annotations. + */ +std::vector +AnnotationManager::getAnnotationsInSameSpace(const Annotation* annotation) +{ + std::vector sameSpaceAnns; + std::vector allAnns = getAllAnnotations(); + if (annotation->getType() == AnnotationTypeEnum::BROWSER_TAB) { + EventBrowserTabGetAll tabEvent; + EventManager::get()->sendEvent(tabEvent.getPointer()); + std::vector allTabs = tabEvent.getAllBrowserTabs(); + for (auto tab : allTabs) { + AnnotationBrowserTab* abt = tab->getManualLayoutBrowserTabAnnotation(); + if (abt != annotation) { + sameSpaceAnns.push_back(abt); + } + } + } + else { + switch (annotation->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + case AnnotationCoordinateSpaceEnum::SPACER: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::SURFACE: + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert("Supports only annotations in Tab or Window Space"); + return sameSpaceAnns; + break; + case AnnotationCoordinateSpaceEnum::TAB: + { + const int32_t tabIndex = annotation->getTabIndex(); + for (auto a : allAnns) { + if (a == annotation) { + continue; + } + if (a->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { + if (a->getTabIndex() == tabIndex) { + sameSpaceAnns.push_back(a); + } + } + } + } + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + { + const int32_t windowIndex = annotation->getWindowIndex(); + for (auto a : allAnns) { + if (a == annotation) { + continue; + } + if (a->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::WINDOW) { + if (a->getWindowIndex() == windowIndex) { + sameSpaceAnns.push_back(a); + } + } + } + } + break; + } + } + + return sameSpaceAnns; +} + +/** + * Move the given window or tab annotation so that it is in front of all other annotations in the same tab or window. + * The stack ordering algorithm uses the undo system and we do want this operation to be undoable. + * + * @param annotation + * The annotation that is moved in front + * @param errorMessageOut + * Output with error information + * @return True is successful, else false + */ +bool +AnnotationManager::moveTabOrWindowAnnotationToFront(Annotation* annotation, + AString& errorMessageOut) +{ + CaretAssert(annotation); + errorMessageOut.clear(); + + std::vector sameSpaceAnns = getAnnotationsInSameSpace(annotation); + if (sameSpaceAnns.empty()) { + return true; + } + + std::map stackOrderAnnotationMap; + for (auto a : sameSpaceAnns) { + AnnotationBrowserTab* abt = dynamic_cast(a); + if (abt != NULL) { + stackOrderAnnotationMap.emplace(static_cast(abt->getStackingOrder()), + a); + } + else { + float z(1.0); + AnnotationTwoCoordinateShape* oneDim = a->castToTwoCoordinateShape(); + if (oneDim != NULL) { + float xyz[3]; + oneDim->getStartCoordinate()->getXYZ(xyz); + z = xyz[2]; + } + else { + AnnotationOneCoordinateShape* twoDim = a->castToOneCoordinateShape(); + if (twoDim != NULL) { + float xyz[3]; + twoDim->getCoordinate()->getXYZ(xyz); + z = xyz[2]; + } + } + stackOrderAnnotationMap.emplace(z, + a); + } + } + + stackOrderAnnotationMap.emplace(-1, + annotation); + int32_t orderIndex(1); + for (auto& soa : stackOrderAnnotationMap) { + Annotation* a(soa.second); + AnnotationBrowserTab* abt = dynamic_cast(a); + if (abt != NULL) { + abt->setStackingOrder(orderIndex); + } + else { + AnnotationTwoCoordinateShape* oneDim = a->castToTwoCoordinateShape(); + if (oneDim != NULL) { + float xyz[3]; + oneDim->getStartCoordinate()->getXYZ(xyz); + xyz[2] = orderIndex; + oneDim->getStartCoordinate()->setXYZ(xyz); + oneDim->getEndCoordinate()->getXYZ(xyz); + xyz[2] = orderIndex; + oneDim->getEndCoordinate()->setXYZ(xyz); + } + else { + AnnotationOneCoordinateShape* twoDim = a->castToOneCoordinateShape(); + if (twoDim != NULL) { + float xyz[3]; + twoDim->getCoordinate()->getXYZ(xyz); + xyz[2] = orderIndex; + twoDim->getCoordinate()->setXYZ(xyz); + } + } + } + + orderIndex++; + } + + return true; +} /** * Receive an event. @@ -773,8 +1101,21 @@ * An event for which this instance is listening. */ void -AnnotationManager::receiveEvent(Event* /*event*/) +AnnotationManager::receiveEvent(Event* event) { + if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_VALIDATE) { + EventAnnotationValidate* annEvent = dynamic_cast(event); + CaretAssert(annEvent); + const Annotation* validateAnnotation = annEvent->getAnnotation(); + + std::vector allAnns = getAllAnnotations(); + for (const auto ann : allAnns) { + if (ann == validateAnnotation) { + annEvent->setAnnotationValid(); + annEvent->setEventProcessed(); + } + } + } } /** @@ -961,11 +1302,32 @@ /** * @return Pointer to the command redo undo stack + * + * @param userInputMode + * The current user input mode which MUST be ANNOTATIONS or TILE_TABS_MANUAL_LAYOUT_EDITING */ CaretUndoStack* -AnnotationManager::getCommandRedoUndoStack() +AnnotationManager::getCommandRedoUndoStack(const UserInputModeEnum::Enum userInputMode) { - return m_annotationRedoUndoStack; + CaretUndoStack* undoStackOut(NULL); + switch (userInputMode) { + case UserInputModeEnum::Enum::ANNOTATIONS: + undoStackOut = m_annotationsExceptBrowserTabsRedoUndoStack; + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + undoStackOut = m_browserTabAnnotationsRedoUndoStack; + break; + case UserInputModeEnum::Enum::BORDERS: + case UserInputModeEnum::Enum::FOCI: + case UserInputModeEnum::Enum::IMAGE: + case UserInputModeEnum::Enum::INVALID: + case UserInputModeEnum::Enum::VIEW: + case UserInputModeEnum::Enum::VOLUME_EDIT: + CaretAssert(0); + break; + } + + return undoStackOut; } /** @@ -1024,3 +1386,70 @@ } +/** + * Shrink and expand selected browser tab to fill available space in the window. MANUAL TILE LAYOUT MODE ONLY ! + * + * @param tabsInWindow + * Tabs displayed in the window (may or may not include selected tab) + * @param windowIndex + * Index of window + * @param userInputMode + * The current user input mode (browser tab always) + * @param errorMessageOut + * Contains error information if expansion has error (inability to expand is NOT an error) + * @return + * True no error, else false. + */ +bool +AnnotationManager::shrinkAndExpandSelectedBrowserTabAnnotation(const std::vector& tabsInWindow, + const int32_t windowIndex, + const UserInputModeEnum::Enum userInputMode, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + AnnotationBrowserTab* selectedTabAnnotation(NULL); + std::vector selectedAnnotations = getAnnotationsSelectedForEditing(windowIndex); + if (selectedAnnotations.size() == 1) { + CaretAssertVectorIndex(selectedAnnotations, 0); + selectedTabAnnotation = dynamic_cast(selectedAnnotations[0]); + } + + CaretAssert(selectedTabAnnotation); + + if (selectedTabAnnotation != NULL) { + std::vector tabAnnotations; + for (auto btc : tabsInWindow) { + const AnnotationBrowserTab* ta = btc->getManualLayoutBrowserTabAnnotation(); + CaretAssert(ta); + if (ta != selectedTabAnnotation) { + tabAnnotations.push_back(ta); + } + } + + std::array newBounds; + if (AnnotationBrowserTab::shrinkAndExpandToFillEmptySpace(tabAnnotations, + selectedTabAnnotation, + newBounds)) { + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + undoCommand->setBoundsAll(newBounds[0], + newBounds[1], + newBounds[2], + newBounds[3], + selectedTabAnnotation); + if (applyCommand(userInputMode, + undoCommand, + errorMessageOut)) { + return true; + } + } + else { + errorMessageOut = "Unable to move/resize tab"; + } + } + else { + errorMessageOut = "Either no annotation is selected or selected annotation is not a browser tab"; + } + + return false; +} diff -Nru connectome-workbench-1.4.2/src/Brain/AnnotationManager.h connectome-workbench-1.5.0/src/Brain/AnnotationManager.h --- connectome-workbench-1.4.2/src/Brain/AnnotationManager.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/AnnotationManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -25,22 +25,25 @@ #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationDistributeEnum.h" #include "AnnotationGroupingModeEnum.h" +#include "AnnotationStackingOrderTypeEnum.h" #include "BrainConstants.h" #include "CaretObject.h" #include "CaretUndoCommand.h" #include "CaretPointer.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" - +#include "UserInputModeEnum.h" namespace caret { class Annotation; class AnnotationArrangerInputs; + class AnnotationBrowserTab; class AnnotationFile; class AnnotationGroupKey; class AnnotationRedoUndoCommand; class AnnotationEditingSelectionInformation; class Brain; + class BrowserTabContent; class CaretUndoStack; class EventGetDisplayedDataFiles; class SceneClassAssistant; @@ -70,10 +73,12 @@ virtual ~AnnotationManager(); - bool applyCommand(AnnotationRedoUndoCommand* command, + bool applyCommand(const UserInputModeEnum::Enum userInputMode, + AnnotationRedoUndoCommand* command, AString& errorMessageOut); - bool applyCommandInWindow(AnnotationRedoUndoCommand* command, + bool applyCommandInWindow(const UserInputModeEnum::Enum userInputMode, + AnnotationRedoUndoCommand* command, const int32_t windowIndex, AString& errorMessageOut); @@ -93,6 +98,9 @@ std::vector getAllAnnotations() const; + std::vector getAnnotationsDrawnInSameWindowAndSpace(const Annotation* annotation, + const int32_t windowIndex) const; + const AnnotationEditingSelectionInformation* getAnnotationEditingSelectionInformation(const int32_t windowIndex) const; std::vector getAnnotationsSelectedForEditing(const int32_t windowIndex) const; @@ -100,10 +108,10 @@ std::vector getAnnotationsSelectedForEditingInSpaces(const int32_t windowIndex, const std::vector& spaces) const; - void getAnnotationsSelectedForEditing(const int32_t windowIndex, + void getAnnotationsAndFilesSelectedForEditing(const int32_t windowIndex, std::vector >& annotationsAndFileOut) const; - void getAnnotationsSelectedForEditingIncludingLabels(const int32_t windowIndex, + void getAnnotationsAndFilesSelectedForEditingIncludingLabels(const int32_t windowIndex, std::vector >& annotationsAndFileOut) const; bool isAnnotationOnClipboardValid() const; @@ -122,26 +130,45 @@ void setAnnotationBeingDrawnInWindow(const int32_t windowIndex, const Annotation* annotation); - CaretUndoStack* getCommandRedoUndoStack(); + CaretUndoStack* getCommandRedoUndoStack(const UserInputModeEnum::Enum userInputMode); void getDisplayedAnnotationFiles(EventGetDisplayedDataFiles* displayedFilesEvent, std::vector& displayedAnnotationFilesOut) const; - bool alignAnnotations(const AnnotationArrangerInputs& arrangerInputs, + bool alignAnnotations(const UserInputModeEnum::Enum userInputMode, + const AnnotationArrangerInputs& arrangerInputs, const AnnotationAlignmentEnum::Enum alignment, AString& errorMessageOut); - bool distributeAnnotations(const AnnotationArrangerInputs& arrangerInputs, - const AnnotationDistributeEnum::Enum distribute, - AString& errorMessageOut); - - bool applyGroupingMode(const int32_t windowIndex, + bool distributeAnnotations(const UserInputModeEnum::Enum userInputMode, + const AnnotationArrangerInputs& arrangerInputs, + const AnnotationDistributeEnum::Enum distribute, + AString& errorMessageOut); + + bool applyGroupingMode(const UserInputModeEnum::Enum userInputMode, + const int32_t windowIndex, const AnnotationGroupingModeEnum::Enum groupingMode, AString& errorMessageOut); bool isGroupingModeValid(const int32_t windowIndex, const AnnotationGroupingModeEnum::Enum groupingMode) const; + bool applyStackingOrder(const std::vector& annotations, + const Annotation* selectedAnnotation, + const AnnotationStackingOrderTypeEnum::Enum orderType, + const int32_t windowIndex, + AString& errorMessageOut); + + bool moveTabOrWindowAnnotationToFront(Annotation* annotation, + AString& errorMessageOut); + + bool shrinkAndExpandSelectedBrowserTabAnnotation(const std::vector& tabsInWindow, + const int32_t windowIndex, + const UserInputModeEnum::Enum userInputMode, + AString& errorMessageOut); + + std::vector getAnnotationsInSameSpace(const Annotation* annotation); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -204,8 +231,9 @@ CaretPointer m_clipboardAnnotation; - CaretPointer m_annotationRedoUndoStack; + CaretPointer m_annotationsExceptBrowserTabsRedoUndoStack; + CaretPointer m_browserTabAnnotationsRedoUndoStack; // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/Brain/Brain.cxx connectome-workbench-1.5.0/src/Brain/Brain.cxx --- connectome-workbench-1.4.2/src/Brain/Brain.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/Brain.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,7 +23,7 @@ #include #include -#include "CaretAssert.h" +#include #include "AnnotationFile.h" #include "AnnotationManager.h" @@ -35,9 +35,13 @@ #include "BrainordinateRegionOfInterest.h" #include "BrainStructure.h" #include "BrowserTabContent.h" +#include "CaretAssert.h" #include "CaretDataFileHelper.h" +#include "CaretHttpManager.h" #include "CaretLogger.h" #include "CaretPreferences.h" +#include "CaretResult.h" +#include "ChartTwoCartesianOrientedAxesYokingManager.h" #include "ChartingDataManager.h" #include "ChartableTwoFileDelegate.h" #include "ChartableTwoFileMatrixChart.h" @@ -74,13 +78,16 @@ #include "EventDataFileDelete.h" #include "EventDataFileRead.h" #include "EventDataFileReload.h" +#include "EventDataFileReloadAll.h" #include "EventCaretDataFilesGet.h" #include "EventGetDisplayedDataFiles.h" +#include "EventMediaFilesGet.h" #include "EventModelAdd.h" #include "EventModelDelete.h" #include "EventModelGetAll.h" #include "EventModelGetAllDisplayed.h" #include "EventPaletteGetByName.h" +#include "EventPaletteGroupsGet.h" #include "EventProgressUpdate.h" #include "EventSceneActive.h" #include "EventSpecFileReadDataFiles.h" @@ -97,6 +104,7 @@ #include "MetricFile.h" #include "ModelChart.h" #include "ModelChartTwo.h" +#include "ModelMedia.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelVolume.h" @@ -105,6 +113,8 @@ #include "Overlay.h" #include "OverlaySet.h" #include "PaletteFile.h" +#include "PaletteGroupStandardPalettes.h" +#include "PaletteGroupUserCustomPalettes.h" #include "RgbaFile.h" #include "Scene.h" #include "SceneAttributes.h" @@ -129,17 +139,39 @@ using namespace caret; /** + * Sort data files by the filename and excluding the path + * + * @param dataFiles + * All files of a particular data type that are loaded. + */ +template +static void +sortDataFileTypeByFileNameNoPath(std::vector& dataFiles) +{ + QCollator collator; + collator.setNumericMode(true); + collator.setCaseSensitivity(Qt::CaseInsensitive); + + std::sort(dataFiles.begin(), + dataFiles.end(), + [&collator](DFT* a, DFT* b) -> bool { + return (collator.compare(a->getFileNameNoPath(), b->getFileNameNoPath()) < 0); + + }); +} + +/** * Constructor. * * @param caretPreferences * The caret preferencers used to initialize some components. */ -Brain::Brain(const CaretPreferences* caretPreferences) +Brain::Brain(CaretPreferences* caretPreferences) { m_annotationManager = new AnnotationManager(this); - m_chartingDataManager = new ChartingDataManager(this); m_fiberOrientationSamplesLoader = new FiberOrientationSamplesLoader(); + m_chartTwoCartesianAxesYokingManager.reset(new ChartTwoCartesianOrientedAxesYokingManager()); m_paletteFile = new PaletteFile(); m_paletteFile->setFileName(convertFilePathNameToAbsolutePathName(m_paletteFile->getFileName())); @@ -158,6 +190,7 @@ m_surfaceMontageModel = NULL; m_volumeSliceModel = NULL; m_wholeBrainModel = NULL; + m_mediaModel = NULL; m_displayPropertiesAnnotation = new DisplayPropertiesAnnotation(this); m_displayProperties.push_back(m_displayPropertiesAnnotation); @@ -168,7 +201,7 @@ m_displayPropertiesBorders = new DisplayPropertiesBorders(); m_displayProperties.push_back(m_displayPropertiesBorders); - m_displayPropertiesFiberOrientation = new DisplayPropertiesFiberOrientation(); + m_displayPropertiesFiberOrientation = new DisplayPropertiesFiberOrientation(this); m_displayProperties.push_back(m_displayPropertiesFiberOrientation); m_displayPropertiesFoci = new DisplayPropertiesFoci(); @@ -198,15 +231,21 @@ EventTypeEnum::EVENT_DATA_FILE_READ); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_DATA_FILE_RELOAD); + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_DATA_FILE_RELOAD_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES); EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_MEDIA_FILES_GET); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_PALETTE_GET_BY_NAME); EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_PALETTE_GROUPS_GET); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SCENE_ACTIVE); m_isSpecFileBeingRead = false; @@ -259,6 +298,9 @@ m_gapsAndMargins); m_sceneAssistant->add("m_surfaceMatchingToAnatomicalFlag", &m_surfaceMatchingToAnatomicalFlag); + m_sceneAssistant->add("m_chartTwoCartesianAxesYokingManager", + "ChartTwoCartesianOrientedAxesYokingManager", + m_chartTwoCartesianAxesYokingManager.get()); m_selectionManager = new SelectionManager(); @@ -266,6 +308,11 @@ m_brainordinateHighlightRegionOfInterest = new BrainordinateRegionOfInterest(); + m_palettesStandardGroup = std::make_shared(); + m_palettesStandardGroup->loadPalettes(); + + m_palettesUserCustomGroup = std::make_shared(caretPreferences); + updateChartModel(); } @@ -308,6 +355,9 @@ if (m_wholeBrainModel != NULL) { delete m_wholeBrainModel; } + if (m_mediaModel != NULL) { + delete m_mediaModel; + } delete m_selectionManager; delete m_identificationManager; @@ -457,9 +507,6 @@ m_duplicateFileNameCounter[dataFileType] = counterValue; -// m_duplicateFileNameCounter.insert(std::make_pair(dataFileType, -// counterValue)); - return counterValue; } @@ -502,6 +549,7 @@ { m_isSpecFileBeingRead = false; m_activeScene = NULL; + SessionManager::get()->resetSceneWithChartOld(); m_surfaceMatchingToAnatomicalFlag = false; @@ -539,7 +587,6 @@ m_annotationSubstitutionFiles.clear(); m_sceneAnnotationFile->clear(); - //m_sceneAnnotationFile->setFileName("Scene Annotations"); m_sceneAnnotationFile->clearModified(); for (std::vector::iterator bfi = m_borderFiles.begin(); @@ -880,8 +927,8 @@ * Index of target tab. */ void -Brain::copyDisplayProperties(const int32_t sourceTabIndex, - const int32_t targetTabIndex) +Brain::copyDisplayPropertiesToTab(const int32_t sourceTabIndex, + const int32_t targetTabIndex) { for (std::vector::iterator iter = m_displayProperties.begin(); iter != m_displayProperties.end(); @@ -892,6 +939,57 @@ } /** + * Copy data file properties from the source tab to the target tab. + * @param sourceTabIndex + * Index of source tab. + * @param targetTabIndex + * Index of target tab. + */ +void +Brain::copyFilePropertiesToTab(const int32_t sourceTabIndex, + const int32_t targetTabIndex) +{ + const int32_t numberOfBrainStructures = getNumberOfBrainStructures(); + for (int32_t i = 0; i < numberOfBrainStructures; i++) { + BrainStructure* bs = getBrainStructure(i); + const int32_t numLabelFiles = bs->getNumberOfLabelFiles(); + for (int32_t j = 0; j < numLabelFiles; j++) { + LabelFile* labelFile = bs->getLabelFile(j); + labelFile->getGroupAndNameHierarchyModel()->copySelections(sourceTabIndex, + targetTabIndex); + } + } + + const int32_t numBorderFiles = getNumberOfBorderFiles(); + for (int32_t i = 0; i < numBorderFiles; i++) { + BorderFile* bf = getBorderFile(i); + bf->getGroupAndNameHierarchyModel()->copySelections(sourceTabIndex, + targetTabIndex); + } + + std::vector ciftiMapFiles; + getAllCiftiMappableDataFiles(ciftiMapFiles); + for (auto cmf : ciftiMapFiles) { + cmf->getGroupAndNameHierarchyModel()->copySelections(sourceTabIndex, + targetTabIndex); + } + + const int32_t numFociFiles = getNumberOfFociFiles(); + for (int32_t i = 0; i < numFociFiles; i++) { + FociFile* ff = getFociFile(i); + ff->getGroupAndNameHierarchyModel()->copySelections(sourceTabIndex, + targetTabIndex); + } + + const int32_t numVolumeFiles = getNumberOfVolumeFiles(); + for (int32_t i = 0; i < numVolumeFiles; i++) { + VolumeFile* vf = getVolumeFile(i); + vf->getGroupAndNameHierarchyModel()->copySelections(sourceTabIndex, + targetTabIndex); + } +} + +/** * Read a surface file. * * @param fileMode @@ -3432,14 +3530,6 @@ if (readFlag) { try { try { - /* - * Add to recent scene files - */ - if (FileInformation(filename).exists()) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSceneFiles(filename); - } - sf->readFile(filename); } catch (const std::bad_alloc&) { @@ -5016,6 +5106,40 @@ } /** + * Update the multi-meda model. + */ +void +Brain::updateMediaModel() +{ + bool isValid = false; + if ((getNumberOfImageFiles() > 0)) { + isValid = true; + } + + if (isValid) { + if (m_mediaModel == NULL) { + m_mediaModel = new ModelMedia(this); + EventModelAdd eventAddModel(m_mediaModel); + EventManager::get()->sendEvent(eventAddModel.getPointer()); + + if ( ! m_isSpecFileBeingRead) { + m_mediaModel->initializeOverlays(); + } + } + + m_mediaModel->updateModel(); + } + else { + if (m_mediaModel != NULL) { + EventModelDelete eventDeleteModel(m_mediaModel); + EventManager::get()->sendEvent(eventDeleteModel.getPointer()); + delete m_mediaModel; + m_mediaModel = NULL; + } + } +} + +/** * Update the volume slice model. */ void @@ -5168,7 +5292,168 @@ updateAfterFilesAddedOrRemoved(); } -#include "CaretHttpManager.h" +/** + * @return All reloadable data files. Surfaces and Volume files are + * at the beginning of the vector and followed by all other files. + */ +std::vector +Brain::getReloadableDataFiles() const +{ + std::vector allDataFiles; + getAllDataFiles(allDataFiles); + + std::deque filesToReload; + for (auto& df : allDataFiles) { + /* Surfaces and volume must be read before other files */ + bool loadFirstFlag(false); + bool reloadFlag(true); + + switch (df->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + reloadFlag = false; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::METRIC_DYNAMIC: + reloadFlag = false; + break; + case DataFileTypeEnum::PALETTE: + reloadFlag = false; + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + reloadFlag = false; + break; + case DataFileTypeEnum::SURFACE: + loadFirstFlag = true; + break; + case DataFileTypeEnum::UNKNOWN: + reloadFlag = false; + break; + case DataFileTypeEnum::VOLUME: + loadFirstFlag = true; + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + reloadFlag = false; + break; + } + + if (reloadFlag) { + if (loadFirstFlag) { + filesToReload.push_front(df); + } + else { + filesToReload.push_back(df); + } + } + } + + std::vector filesOut(filesToReload.begin(), + filesToReload.end()); + return filesOut; +} + + +/** + * Process a reload all data files event. + * @param reloadAllDataFilesEvent + * Event containing forreloading all files and my be updated with error messages. + */ +void +Brain::processReloadAllDataFilesEvent(EventDataFileReloadAll* reloadAllDataFilesEvent) +{ + CaretAssert(reloadAllDataFilesEvent); + + std::vector filesToReload(getReloadableDataFiles()); + + switch (reloadAllDataFilesEvent->getMode()) { + case EventDataFileReloadAll::Mode::GET_FILES: + { + for (auto& df : filesToReload) { + reloadAllDataFilesEvent->addReloadableFile(df); + } + } + break; + case EventDataFileReloadAll::Mode::RELOAD_FILES: + { + const int32_t numFiles = static_cast(filesToReload.size()); + EventProgressUpdate progressEvent(1, numFiles, 1, "Reloading Files"); + EventManager::get()->sendEvent(progressEvent.getPointer()); + + int32_t progressCount(1); + AString errorMessage; + for (auto& df : filesToReload) { + progressEvent.setProgress(progressCount, "Reloading " + df->getFileNameNoPath()); + EventManager::get()->sendEvent(progressEvent.getPointer()); + + try { + addReadOrReloadDataFile(FILE_MODE_RELOAD, + df, + df->getDataFileType(), + df->getStructure(), + df->getFileName(), + false); + } + catch (const DataFileException& dfe) { + errorMessage.appendWithNewLine(dfe.whatString()); + } + } + + if ( ! errorMessage.isEmpty()) { + reloadAllDataFilesEvent->setErrorMessage(errorMessage); + } + + progressEvent.setProgress(progressCount, "Finishing reloading of files"); + EventManager::get()->sendEvent(progressEvent.getPointer()); + + updateAfterFilesAddedOrRemoved(); + } + break; + } + + reloadAllDataFilesEvent->setEventProcessed(); +} /** * Process a read data file event. @@ -5562,6 +5847,7 @@ updateVolumeSliceModel(); updateWholeBrainModel(); updateChartModel(); + updateMediaModel(); updateFiberTrajectoryMatchingFiberOrientationFiles(); } @@ -5591,14 +5877,14 @@ const AString specFileName = sf->getFileName(); if (DataFile::isFileOnNetwork(specFileName)) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSpecFiles(specFileName); + prefs->addToRecentFilesAndOrDirectories(specFileName); } else { FileInformation specFileInfo(specFileName); if (specFileInfo.exists() && specFileInfo.isAbsolute()) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSpecFiles(specFileName); + prefs->addToRecentFilesAndOrDirectories(specFileName); } } @@ -5703,10 +5989,8 @@ // testingAnnFile->setFileName("Testing." + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::ANNOTATION)); // addDataFile(testingAnnFile); - - - - + sortDataFilesByFileNameNoPath(); + /* * Reset the primary anatomical surfaces since they can get set * incorrectly when loading files @@ -5755,6 +6039,15 @@ } /** + * Some files are sorted by name + */ +void +Brain::sortDataFilesByFileNameNoPath() +{ + sortDataFileTypeByFileNameNoPath(m_imageFiles); +} + +/** * Load files from the given spec file. * @param specFileToLoad * Spec file from which selected files are read. @@ -5794,6 +6087,13 @@ FileInformation oldSpecFileInfo(previousSpecFileName); setCurrentDirectory(oldSpecFileInfo.getPathName()); } + + const AString sceneFileName = sceneAttributes->getSceneFileName(); + FileInformation sceneFileInfo(sceneFileName); + if (sceneFileInfo.exists()) { + AString sceneFileDirectory = sceneFileInfo.getAbsolutePath(); + setCurrentDirectory(sceneFileDirectory); + } } /* @@ -5890,6 +6190,8 @@ /* * Load new files and add existing files that were previously loaded. */ + const int64_t numberOfFilesToLoad(specFileToLoad->getNumberOfFilesSelectedForLoading()); + int64_t fileLoadingCounter(1); const int32_t numFileGroups = specFileToLoad->getNumberOfDataFileTypeGroups(); for (int32_t ig = 0; ig < numFileGroups; ig++) { const SpecFileDataFileTypeGroup* group = specFileToLoad->getDataFileTypeGroupByIndex(ig); @@ -5925,8 +6227,14 @@ else { const StructureEnum::Enum structure = fileInfo->getStructure(); - const QString msg = ("Loading " + const QString msg = ("Loading (" + + AString::number(fileLoadingCounter) + + " of " + + AString::number(numberOfFilesToLoad) + + ") " + FileInformation(filename).getFileName()); + ++fileLoadingCounter; + progressEvent.setProgressMessage(msg); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { @@ -6078,6 +6386,8 @@ addDataFileEvent->setErrorMessage(dfe.whatString()); } addDataFileEvent->setEventProcessed(); + + sortDataFilesByFileNameNoPath(); } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_DELETE) { EventDataFileDelete* deleteDataFileEvent = @@ -6100,6 +6410,8 @@ if (readDataFileEvent->getLoadIntoBrain() == this) { readDataFileEvent->setEventProcessed(); processReadDataFileEvent(readDataFileEvent); + + sortDataFilesByFileNameNoPath(); } } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_RELOAD) { @@ -6112,6 +6424,15 @@ processReloadDataFileEvent(reloadDataFileEvent); } } + else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_RELOAD_ALL) { + EventDataFileReloadAll* reloadAllEvent = dynamic_cast(event); + CaretAssert(reloadAllEvent); + + if (reloadAllEvent->getBrain() == this) { + reloadAllEvent->setEventProcessed(); + processReloadAllDataFilesEvent(reloadAllEvent); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_CARET_DATA_FILES_GET) { EventCaretDataFilesGet* filesEvent = dynamic_cast(event); CaretAssert(filesEvent); @@ -6137,6 +6458,16 @@ dataFilesEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_MEDIA_FILES_GET) { + EventMediaFilesGet* mediaEvent = dynamic_cast(event); + CaretAssert(mediaEvent); + + for (auto f : m_imageFiles) { + mediaEvent->addMediaFile(f); + } + + mediaEvent->setEventProcessed(); + } else if (event->getEventType() == EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES) { EventSpecFileReadDataFiles* readSpecFileDataFilesEvent = dynamic_cast(event); @@ -6149,6 +6480,7 @@ if (readSpecFileDataFilesEvent->getLoadIntoBrain() == this) { readSpecFileDataFilesEvent->setEventProcessed(); loadFilesSelectedInSpecFile(readSpecFileDataFilesEvent); + m_displayPropertiesFiberOrientation->setMaximumUncertaintyFromFiles(); } } else if (event->getEventType() == EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES) { @@ -6233,6 +6565,15 @@ } } } + else if (event->getEventType() == EventTypeEnum::EVENT_PALETTE_GROUPS_GET) { + EventPaletteGroupsGet* palettesEvent = dynamic_cast(event); + CaretAssert(palettesEvent); + + palettesEvent->addPaletteGroup(m_palettesStandardGroup); + palettesEvent->addPaletteGroup(m_palettesUserCustomGroup); + + palettesEvent->setEventProcessed(); + } else if (event->getEventType() == EventTypeEnum::EVENT_SCENE_ACTIVE) { EventSceneActive* sceneEvent = dynamic_cast(event); CaretAssert(sceneEvent); @@ -6309,6 +6650,24 @@ } /** + * @return The multi-media model (warning may be NULL) + */ +ModelMedia* +Brain::getMediaModel() +{ + return m_mediaModel; +} + +/** + * @return The multi-media model (warning may be NULL) const method + */ +const ModelMedia* +Brain::getMediaModel() const +{ + return m_mediaModel; +} + +/** * @return The annotation manager. */ AnnotationManager* @@ -6496,21 +6855,6 @@ getAllDataFilesWithDataFileTypes(dataFileTypes, caretDataFilesOut); - -// caretDataFilesOut.clear(); -// -// std::vector allDataFiles; -// getAllDataFiles(allDataFiles, -// true); -// -// for (std::vector::iterator iter = allDataFiles.begin(); -// iter != allDataFiles.end(); -// iter++) { -// CaretDataFile* cdf = *iter; -// if (cdf->getDataFileType() == dataFileType) { -// caretDataFilesOut.push_back(cdf); -// } -// } } @@ -6566,10 +6910,6 @@ m_connectivityMatrixDenseFiles.begin(), m_connectivityMatrixDenseFiles.end()); -// allDataFilesOut.insert(allDataFilesOut.end(), -// m_connectivityDataSeriesFiles.begin(), -// m_connectivityDataSeriesFiles.end()); - /* * By placing the dynamic connectivity file immediately after * its parent data-series file, they will appear in this @@ -6589,12 +6929,6 @@ } } -// std::vector denseDynFiles; -// getConnectivityMatrixDenseDynamicFiles(denseDynFiles); -// allDataFilesOut.insert(allDataFilesOut.end(), -// denseDynFiles.begin(), -// denseDynFiles.end()); - allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityDenseLabelFiles.begin(), m_connectivityDenseLabelFiles.end()); @@ -6718,48 +7052,6 @@ } } - -///** -// * Are any data files modified (including spec file)? -// * @param excludeTheseDataTypes -// * Do not check the modification status of any data files whose -// * data type is contained in this parameter. -// */ -//bool -//Brain::areFilesModified(const std::vector& excludeTheseDataTypes) -//{ -// if (std::find(excludeTheseDataTypes.begin(), -// excludeTheseDataTypes.end(), -// DataFileTypeEnum::SPECIFICATION) == excludeTheseDataTypes.end()) { -// if (m_specFile->isModified()) { -// return true; -// } -// } -// -// std::vector dataFiles; -// getAllDataFiles(dataFiles); -// -// for (std::vector::iterator iter = dataFiles.begin(); -// iter != dataFiles.end(); -// iter++) { -// CaretDataFile* cdf = *iter; -// -// /** -// * Ignore files whose data type is excluded. -// */ -// if (std::find(excludeTheseDataTypes.begin(), -// excludeTheseDataTypes.end(), -// cdf->getDataFileType()) == excludeTheseDataTypes.end()) { -// if (cdf->isModified()) { -// return true; -// } -// } -// } -// -// return false; -//} - - /** * Write a data file. * @param caretDataFile @@ -6855,10 +7147,10 @@ case DataFileTypeEnum::SCENE: { /* - * Add to recent scene files + * Add to recent scene/spec files */ CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSceneFiles(caretDataFile->getFileName()); + prefs->addToRecentFilesAndOrDirectories(caretDataFile->getFileName()); } break; case DataFileTypeEnum::SPECIFICATION: @@ -7504,7 +7796,11 @@ sceneClass->addClass(ff->getGroupAndNameHierarchyModel()->saveToScene(sceneAttributes, ff->getFileNameNoPath())); } - + for (auto lf : m_volumeFiles) { + sceneClass->addClass(lf->getGroupAndNameHierarchyModel()->saveToScene(sceneAttributes, + lf->getFileNameNoPath())); + } + sceneClass->addClass(m_identificationManager->saveToScene(sceneAttributes, "m_identificationManager")); @@ -7647,8 +7943,6 @@ bestMatchingSceneClass = const_cast(fileSceneClass); bestMatchingCount = matchCount; } -// caretDataFile->restoreFromScene(sceneAttributes, -// fileSceneClass); } } @@ -7674,6 +7968,11 @@ } /* + * Some files are sorted by name + */ + sortDataFilesByFileNameNoPath(); + + /* * Restore members */ m_sceneAssistant->restoreMembers(sceneAttributes, @@ -7780,6 +8079,10 @@ ff->getGroupAndNameHierarchyModel()->restoreFromScene(sceneAttributes, sceneClass->getClass(ff->getFileNameNoPath())); } + for (auto vf : m_volumeFiles) { + vf->getGroupAndNameHierarchyModel()->restoreFromScene(sceneAttributes, + sceneClass->getClass(vf->getFileNameNoPath())); + } m_identificationManager->restoreFromScene(sceneAttributes, sceneClass->getClass("m_identificationManager")); @@ -7795,6 +8098,77 @@ } /** + * @return The base directory for the loaded data files excluding Scene and Spec files + * @param validFlagOut + * On output, it will be true if the returne + */ +std::unique_ptr +Brain::getBaseDirectoryForLoadedDataFiles(AString& baseDirectoryOut) const +{ + baseDirectoryOut.clear(); + + std::vector allDataFilesOrig; + getAllDataFiles(allDataFilesOrig); + + std::set fileInfo; + + AString modifiedFileNames; + std::vector allDataFiles; + for (auto& df : allDataFilesOrig) { + CaretAssert(df); + switch (df->getDataFileType()) { + case DataFileTypeEnum::SCENE: + break; + default: + if (df->supportsWriting()) { + if (df->isModified()) { + modifiedFileNames.appendWithNewLine(df->getFileNameNoPath()); + } + allDataFiles.push_back(df); + + /* + * Test to see if this file is already in the output file info + */ + const float sceneIndex(1); + const AString pathName(FileInformation(df->getFileName()).getAbsoluteFilePath()); + bool foundFlag = false; + for (auto& dfi : fileInfo) { + if (dfi.m_dataFileName == pathName) { + dfi.addSceneIndex(sceneIndex); + foundFlag = true; + break; + } + } + + if ( ! foundFlag) { + fileInfo.insert(SceneFile::FileAndSceneIndicesInfo(pathName, + sceneIndex)); + } + } + break; + } + } + + if (fileInfo.empty()) { + return CaretResult::newInstanceError("No data files (excluding Scene and Spec) are loaded for finding base path"); + } + + std::vector missingFileNames; + AString errorMessage; + AString emptySceneFileName; + + if (SceneFile::findBaseDirectoryForDataFiles(emptySceneFileName, + fileInfo, + baseDirectoryOut, + missingFileNames, + errorMessage)) { + return CaretResult::newInstanceSuccess(); + } + return CaretResult::newInstanceError(errorMessage); +} + + +/** * Load the default row/column for a matrix charting file. * If the file is not matrix chartable, no action is taken. * @@ -7879,6 +8253,15 @@ return m_identificationManager; } +/** + * @return The identification manager. + */ +const IdentificationManager* +Brain::getIdentificationManager() const +{ + return m_identificationManager; +} + /** Region of interest for highlighting brainordinates */ BrainordinateRegionOfInterest* Brain::getBrainordinateHighlightRegionOfInterest() @@ -7949,6 +8332,15 @@ } } +/** + * @return The active scene (NULL if no active scene) + */ +const Scene* +Brain::getActiveScene() const +{ + return m_activeScene; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/Brain.h connectome-workbench-1.5.0/src/Brain/Brain.h --- connectome-workbench-1.4.2/src/Brain/Brain.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/Brain.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include #include @@ -49,6 +50,8 @@ class CaretDataFile; class CaretMappableDataFile; class CaretPreferences; + class CaretResult; + class ChartTwoCartesianOrientedAxesYokingManager; class ChartingDataManager; class ChartableLineSeriesBrainordinateInterface; class ChartableMatrixInterface; @@ -80,6 +83,7 @@ class DisplayPropertiesVolume; class EventDataFileRead; class EventDataFileReload; + class EventDataFileReloadAll; class EventSpecFileReadDataFiles; class GapsAndMargins; class IdentificationManager; @@ -89,10 +93,13 @@ class MetricDynamicConnectivityFile; class ModelChart; class ModelChartTwo; + class ModelMedia; class ModelSurfaceMontage; class ModelVolume; class ModelWholeBrain; class PaletteFile; + class PaletteGroupStandardPalettes; + class PaletteGroupUserCustomPalettes; class RgbaFile; class SceneClassAssistant; class Scene; @@ -108,7 +115,7 @@ class Brain : public CaretObject, public EventListenerInterface, public SceneableInterface { public: - Brain(const CaretPreferences* caretPreferences); + Brain(CaretPreferences* caretPreferences); ~Brain(); @@ -216,6 +223,10 @@ const ChartingDataManager* getChartingDataManager() const; + ModelMedia* getMediaModel(); + + const ModelMedia* getMediaModel() const; + void getAllCiftiMappableDataFiles(std::vector& allCiftiMappableDataFilesOut) const; int32_t getNumberOfConnectivityMatrixDenseFiles() const; @@ -414,8 +425,11 @@ const DisplayPropertiesLabels* getDisplayPropertiesLabels() const; - void copyDisplayProperties(const int32_t sourceTabIndex, - const int32_t targetTabIndex); + void copyDisplayPropertiesToTab(const int32_t sourceTabIndex, + const int32_t targetTabIndex); + + void copyFilePropertiesToTab(const int32_t sourceTabIndex, + const int32_t targetTabIndex); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -429,6 +443,8 @@ IdentificationManager* getIdentificationManager(); + const IdentificationManager* getIdentificationManager() const; + SelectionManager* getSelectionManager(); BrainordinateRegionOfInterest* getBrainordinateHighlightRegionOfInterest(); @@ -447,6 +463,10 @@ void setSurfaceMatchingToAnatomical(const bool matchStatus); + std::unique_ptr getBaseDirectoryForLoadedDataFiles(AString& baseDirectoryOut) const; + + const Scene* getActiveScene() const; + private: /** * Reset the brain scene file mode @@ -502,11 +522,15 @@ void processReloadDataFileEvent(EventDataFileReload* reloadDataFileEvent); + void processReloadAllDataFilesEvent(EventDataFileReloadAll* reloadAllDataFilesEvent); + CaretDataFile* readDataFile(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& dataFileName, const bool markDataFileAsModified); + void sortDataFilesByFileNameNoPath(); + void createModelChartTwo(); /** @@ -728,6 +752,8 @@ void updateSurfaceMontageModel(); + void updateMediaModel(); + void updateBrainStructures(); void updateFiberTrajectoryMatchingFiberOrientationFiles(); @@ -738,6 +764,8 @@ void resetDuplicateFileNameCounter(const bool preserveSceneFileCounter); + std::vector getReloadableDataFiles() const; + std::vector m_brainStructures; std::vector m_annotationFiles; @@ -798,12 +826,16 @@ ModelWholeBrain* m_wholeBrainModel; + ModelMedia* m_mediaModel = NULL; + ModelSurfaceMontage* m_surfaceMontageModel; ChartingDataManager* m_chartingDataManager; AnnotationManager* m_annotationManager; + std::unique_ptr m_chartTwoCartesianAxesYokingManager; + /** contains all display properties */ std::vector m_displayProperties; @@ -885,6 +917,10 @@ Scene* m_activeScene = NULL; bool m_surfaceMatchingToAnatomicalFlag = false; + + std::shared_ptr m_palettesStandardGroup; + + std::shared_ptr m_palettesUserCustomGroup; }; } // namespace diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -19,6 +19,7 @@ */ /*LICENSE_END*/ +#include #include #include @@ -29,6 +30,7 @@ #undef __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_DECLARE__ #include "AnnotationBox.h" +#include "AnnotationBrowserTab.h" #include "AnnotationColorBar.h" #include "AnnotationColorBarSection.h" #include "AnnotationColorBarNumericText.h" @@ -38,7 +40,9 @@ #include "AnnotationLine.h" #include "AnnotationManager.h" #include "AnnotationOval.h" +#include "AnnotationPolyLine.h" #include "AnnotationPercentSizeText.h" +#include "AnnotationScaleBar.h" #include "AnnotationText.h" #include "Brain.h" #include "BrainOpenGLFixedPipeline.h" @@ -56,6 +60,7 @@ #include "GraphicsEngineDataOpenGL.h" #include "GraphicsPrimitiveV3f.h" #include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsPrimitiveV3fC4ub.h" #include "GraphicsPrimitiveV3fN3f.h" #include "GraphicsPrimitiveV3fT3f.h" #include "GraphicsShape.h" @@ -93,8 +98,8 @@ { CaretAssert(brainOpenGLFixedPipeline); - m_dummyAnnotationFile = new AnnotationFile(); - m_dummyAnnotationFile->setFileName("DummyFileForDrawing" + m_dummyAnnotationFile = new AnnotationFile(AnnotationFile::ANNOTATION_FILE_DUMMY_FOR_DRAWING); + m_dummyAnnotationFile->setFileName("DummyFileForDrawing." + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::ANNOTATION)); float unusedLineWidthMaximum(0); @@ -333,7 +338,6 @@ */ if (convertModelToWindowCoordinate(modelXYZ, drawingSpaceXYZ)) { - modelXYZValid = false; drawingSpaceXYZValid = true; } else { @@ -388,7 +392,7 @@ * The top left corner of the annotation bounds. */ bool -BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationTwoDimShapeBounds(const AnnotationTwoDimensionalShape* annotation2D, +BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationTwoDimShapeBounds(const AnnotationOneCoordinateShape* annotation2D, const float windowXYZ[3], float bottomLeftOut[3], float bottomRightOut[3], @@ -413,10 +417,10 @@ if (textFlag) { m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextAtViewportCoords(*textAnnotation, m_textDrawingFlags, - windowXYZ[0], windowXYZ[1], windowXYZ[2], - viewportWidth, viewportHeight, - bottomLeftOut, bottomRightOut, topRightOut, topLeftOut); - + windowXYZ[0], windowXYZ[1], windowXYZ[2], + viewportWidth, viewportHeight, + bottomLeftOut, bottomRightOut, topRightOut, topLeftOut); + boundsValid = true; } else { @@ -427,8 +431,15 @@ bottomRightOut, topRightOut, topLeftOut); + } + BoundingBox bb; + if (boundsValid) { + bb.set(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut); + } + annotation2D->setDrawnInWindowBounds(m_inputs->m_windowIndex, bb); + return boundsValid; } @@ -491,9 +502,11 @@ m_volumeSpacePlaneValid = true; std::vector emptyColorBars; + std::vector emptyScaleBars; std::vector emptyNotInFileAnnotations; drawAnnotationsInternal(AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, + emptyScaleBars, emptyNotInFileAnnotations, NULL, sliceThickness); @@ -512,6 +525,8 @@ * Coordinate space of annotation that are drawn. * @param colorbars * Colorbars that will be drawn. + * @param scalebars + * Scalebars that will be drawn. * @param notInFileAnnotations * Annotations that are not in a file but need to be drawn. * @param surfaceDisplayed @@ -523,6 +538,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, + std::vector& scaleBars, std::vector& notInFileAnnotations, const Surface* surfaceDisplayed, const float surfaceViewScaling) @@ -537,6 +553,7 @@ drawAnnotationsInternal(drawingCoordinateSpace, colorBars, + scaleBars, notInFileAnnotations, surfaceDisplayed, sliceThickness); @@ -551,6 +568,8 @@ * Coordinate space of annotation that are drawn. * @param colorbars * Colorbars that will be drawn. + * @param scalebars + * Scale that will be drawn. * @param notInFileAnnotations * Annotations that are not in a file but need to be drawn. * @param surfaceDisplayed @@ -563,6 +582,7 @@ void BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, + std::vector& scaleBars, std::vector& notInFileAnnotations, const Surface* surfaceDisplayed, const float sliceThickness) @@ -581,8 +601,6 @@ m_volumeSliceThickness = sliceThickness; - setSelectionBoxColor(); - m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, ("At beginning of annotation drawing in space " + AnnotationCoordinateSpaceEnum::toName(drawingCoordinateSpace))); @@ -704,7 +722,7 @@ /* * Draw annotations from all files. - * NOTE: iFile == numAnnFiles, the annotation colorbars + * NOTE: iFile == numAnnFiles, the annotation colorbars and scale bars * and annotation chart labels are drawn */ const int32_t numAnnFiles = static_cast(allAnnotationFiles.size()); @@ -736,17 +754,39 @@ * Note: Positions are in percentages ranging [0.0, 100.0] */ const float yStart = 4.0; - float x = 14.0; float y = yStart; int32_t lastTabIndex = -1; bool firstColorBarFlag = true; - if ( ! colorBars.empty()) { - for (std::vector::iterator cbIter = colorBars.begin(); - cbIter != colorBars.end(); - cbIter++) { - AnnotationColorBar* cb = *cbIter; - if (cb->getCoordinateSpace() == drawingCoordinateSpace) { - switch (cb->getPositionMode()) { + std::vector colorAndScaleBars; + colorAndScaleBars.insert(colorAndScaleBars.end(), + colorBars.begin(), colorBars.end()); + colorAndScaleBars.insert(colorAndScaleBars.end(), + scaleBars.begin(), scaleBars.end()); + if ( ! colorAndScaleBars.empty()) { + for (auto annBar : colorAndScaleBars) { + if (annBar->getCoordinateSpace() == drawingCoordinateSpace) { + AnnotationColorBarPositionModeEnum::Enum positionMode = AnnotationColorBarPositionModeEnum::AUTOMATIC; + AnnotationColorBar* colorBar = dynamic_cast(annBar); + AnnotationScaleBar* scaleBar = annBar->castToScaleBar(); + float x = 14.0; + if (colorBar != NULL) { + positionMode = colorBar->getPositionMode(); + } + else { + if (scaleBar != NULL) { + positionMode = scaleBar->getPositionMode(); + + /* + * When drawn, color bars are at pixel X=18.5 + * so try to align left side of scale bars + */ + x = ((18.5 / m_modelSpaceViewport[2]) * 100.0); + } + else { + CaretAssert(0); + } + } + switch (positionMode) { case AnnotationColorBarPositionModeEnum::AUTOMATIC: { /* @@ -755,7 +795,31 @@ * colorbar or bottom of screen. Second time to move the * Y to the top of this annotation. */ - const float halfHeight = cb->getHeight() / 2.0; + float halfHeight(0.0); + float yOffset(0.0); + if (scaleBar != NULL) { + /* + * Need to get scale bar height + * Will need something other than half height since + * Y=0 is at bottom of scale bar + * Half Height => yOffset, yHeight + */ + float bottomLeft[3], bottomRight[3], topRight[3], topLeft[3]; + float dummyXYZ[3] { 0.0, 0.0, 0.0 }; + scaleBar->getShapeBounds(m_modelSpaceViewport[2], m_modelSpaceViewport[3], + dummyXYZ, + bottomLeft, bottomRight, topRight, topLeft); + const float scaleBarHeightPixels(topLeft[1] - bottomLeft[1]); + const float halfHeightPixels(scaleBarHeightPixels / 2.0); + halfHeight = (halfHeightPixels / m_modelSpaceViewport[3]) * 100.0; + + /* scale bar has origin at bottom left*/ + yOffset = -halfHeight; + } + else { + /* color bar has origin in center-Y */ + halfHeight = annBar->getHeight() / 2.0; + } if (firstColorBarFlag) { firstColorBarFlag = false; y = 4; @@ -764,28 +828,28 @@ } if (drawingCoordinateSpace == AnnotationCoordinateSpaceEnum::TAB) { - lastTabIndex = cb->getTabIndex(); + lastTabIndex = annBar->getTabIndex(); } } else { y += halfHeight; if (drawingCoordinateSpace == AnnotationCoordinateSpaceEnum::TAB) { - if (cb->getTabIndex() != lastTabIndex) { + if (annBar->getTabIndex() != lastTabIndex) { /* * First color bar in tab is at bottom of tab */ y = yStart; } - lastTabIndex = cb->getTabIndex(); + lastTabIndex = annBar->getTabIndex(); } } float xyz[3]; - cb->getCoordinate()->getXYZ(xyz); + annBar->getCoordinate()->getXYZ(xyz); xyz[0] = x; - xyz[1] = y; - cb->getCoordinate()->setXYZ(xyz); + xyz[1] = y + yOffset; + annBar->getCoordinate()->setXYZ(xyz); y += halfHeight; } break; @@ -797,6 +861,9 @@ annotationsFromFile.insert(annotationsFromFile.end(), colorBars.begin(), colorBars.end()); + annotationsFromFile.insert(annotationsFromFile.end(), + scaleBars.begin(), + scaleBars.end()); } } break; @@ -834,9 +901,9 @@ continue; } - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); - + AnnotationTwoCoordinateShape* oneDimAnn = dynamic_cast(annotation); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(annotation); + AnnotationMultiCoordinateShape* multiCoordAnn = dynamic_cast(annotation); /* * Limit drawing of annotations to those in the * selected coordinate space. @@ -850,6 +917,8 @@ } else if (twoDimAnn != NULL) { } + else if (multiCoordAnn != NULL) { + } else { CaretAssertMessage(0, ("Annotation is not derived from One or Two Dim Annotation classes: " + annotation->toString())); @@ -905,41 +974,9 @@ } break; } - -// /* -// * Skip annotation in a different window -// */ -// if (annotationCoordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { -// const int32_t annotationWindowIndex = annotation->getWindowIndex(); -// if ((annotationWindowIndex < 0) -// || (annotationWindowIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)) { -// CaretLogSevere("Annotation has invalid window index=" -// + AString::number(annotationWindowIndex) -// + " " -// + annotation->toString()); -// } -// if (m_inputs->m_windowIndex != annotationWindowIndex) { -// continue; -// } -// } -// -// /* -// * Skip annotations in a different tab -// */ -// if (annotationCoordinateSpace == AnnotationCoordinateSpaceEnum::TAB) { -// const int32_t annotationTabIndex = annotation->getTabIndex(); -// if ((annotationTabIndex < 0) -// || (annotationTabIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { -// CaretLogSevere("Annotation has invalid tab index=" -// + AString::number(annotationTabIndex) -// + " " -// + annotation->toString()); -// } -// if (m_inputs->m_tabIndex != annotationTabIndex) { -// continue; -// } -// } - + + setSelectionBoxColor(annotation); + drawAnnotation(annotationFile, annotation, surfaceDisplayed); @@ -968,7 +1005,8 @@ annotationID->setAnnotation(selectionInfo.m_annotationFile, selectionInfo.m_annotation, - selectionInfo.m_sizingHandle); + selectionInfo.m_sizingHandle, + selectionInfo.m_polyLineCoordinateIndex); annotationID->setBrain(m_inputs->m_brain); annotationID->setScreenXYZ(selectionInfo.m_windowXYZ); annotationID->setScreenDepth(depth); @@ -1145,17 +1183,53 @@ bool drawnFlag = false; if (annotation->isInSurfaceSpaceWithTangentOffset()) { - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); - if (twoDimAnn != NULL) { - drawnFlag = drawTwoDimAnnotationSurfaceTextureOffset(annotationFile, - twoDimAnn, - surfaceDisplayed); + AnnotationOneCoordinateShape* oneCoordAnn = annotation->castToOneCoordinateShape(); + AnnotationTwoCoordinateShape* twoCoordAnn = annotation->castToTwoCoordinateShape(); + AnnotationMultiCoordinateShape* multiCoordAnn = annotation->castToMultiCoordinateShape(); + if (oneCoordAnn != NULL) { + drawnFlag = drawOneCoordinateAnnotationSurfaceTextureOffset(annotationFile, + oneCoordAnn, + surfaceDisplayed); + } + else if (twoCoordAnn != NULL) { + drawnFlag = drawTwoCoordinateAnnotationSurfaceTextureOffset(annotationFile, + twoCoordAnn, + surfaceDisplayed); + } + else if (multiCoordAnn != NULL) { + drawnFlag = drawMultiCoordinateAnnotationSurfaceTextureOffset(annotationFile, + multiCoordAnn, + surfaceDisplayed); } else { - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); - drawnFlag = drawOneDimAnnotationSurfaceTextureOffset(annotationFile, - oneDimAnn, - surfaceDisplayed); + CaretAssertMessage(0, "Has new annotation type been added"); + } + + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + case AnnotationTypeEnum::IMAGE: + case AnnotationTypeEnum::OVAL: + case AnnotationTypeEnum::TEXT: + drawnFlag = drawOneCoordinateAnnotationSurfaceTextureOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + case AnnotationTypeEnum::BROWSER_TAB: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::LINE: + drawnFlag = drawTwoCoordinateAnnotationSurfaceTextureOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + case AnnotationTypeEnum::POLY_LINE: + drawnFlag = drawMultiCoordinateAnnotationSurfaceTextureOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + case AnnotationTypeEnum::SCALE_BAR: + break; } } else { @@ -1165,6 +1239,10 @@ dynamic_cast(annotation), surfaceDisplayed); break; + case AnnotationTypeEnum::BROWSER_TAB: + drawnFlag = drawBrowserTab(annotationFile, + dynamic_cast(annotation)); + break; case AnnotationTypeEnum::COLOR_BAR: drawColorBar(annotationFile, dynamic_cast(annotation)); @@ -1184,6 +1262,15 @@ dynamic_cast(annotation), surfaceDisplayed); break; + case AnnotationTypeEnum::POLY_LINE: + drawnFlag = drawPolyLine(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + case AnnotationTypeEnum::SCALE_BAR: + drawScaleBar(annotationFile, + annotation->castToScaleBar()); + break; case AnnotationTypeEnum::TEXT: drawnFlag = drawText(annotationFile, dynamic_cast(annotation), @@ -1210,8 +1297,8 @@ * True if the anntotation was drawn, else false. */ bool -BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, - AnnotationTwoDimensionalShape* annotation, +BrainOpenGLAnnotationDrawingFixedPipeline::drawOneCoordinateAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationOneCoordinateShape* annotation, const Surface* surfaceDisplayed) { bool drawnFlag = false; @@ -1410,6 +1497,9 @@ surfaceExtentZ, vertexXYZ); break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssertMessage(0, "Browser Tab is NEVER drawn in surface space"); + break; case AnnotationTypeEnum::COLOR_BAR: CaretAssertMessage(0, "Color Bar is NEVER drawn in surface space"); break; @@ -1428,6 +1518,12 @@ surfaceExtentZ, vertexXYZ); break; + case AnnotationTypeEnum::POLY_LINE: + CaretAssert(0); + break; + case AnnotationTypeEnum::SCALE_BAR: + CaretAssertMessage(0, "Scale Bar is NEVER drawn in surface space"); + break; case AnnotationTypeEnum::TEXT: drawnFlag = drawTextSurfaceTangentOffset(annotationFile, dynamic_cast(annotation), @@ -1465,8 +1561,8 @@ * True if the anntotation was drawn, else false. */ bool -BrainOpenGLAnnotationDrawingFixedPipeline::drawOneDimAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, - AnnotationOneDimensionalShape* annotation, +BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoCoordinateAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationTwoCoordinateShape* annotation, const Surface* surfaceDisplayed) { CaretAssert(annotationFile); @@ -1501,9 +1597,12 @@ glPushMatrix(); switch (annotation->getType()) { case AnnotationTypeEnum::BOX: + case AnnotationTypeEnum::BROWSER_TAB: case AnnotationTypeEnum::COLOR_BAR: case AnnotationTypeEnum::IMAGE: case AnnotationTypeEnum::OVAL: + case AnnotationTypeEnum::POLY_LINE: + case AnnotationTypeEnum::SCALE_BAR: case AnnotationTypeEnum::TEXT: CaretAssert(0); break; @@ -1531,6 +1630,88 @@ } /** + * Draw a one-dimensional annotation that is in surface space with a texture offset. + * + * @param annotationFile + * File containing the annotation. + * @param annotation + * Annotation to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @return + * True if the anntotation was drawn, else false. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawMultiCoordinateAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationMultiCoordinateShape* annotation, + const Surface* surfaceDisplayed) +{ + CaretAssert(annotationFile); + CaretAssert(annotation); + CaretAssert(surfaceDisplayed); + + + bool drawnFlag = false; + + + const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); + const float surfaceExtentZ = boundingBox->getDifferenceZ(); + + /* + * Need to restore model space + * Recall that all other annotation spaces are drawn in window space + */ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceProjectionMatrix); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceModelMatrix); + int32_t savedViewport[4]; + glGetIntegerv(GL_VIEWPORT, + savedViewport); + glViewport(m_modelSpaceViewport[0], + m_modelSpaceViewport[1], + m_modelSpaceViewport[2], + m_modelSpaceViewport[3]); + + glPushMatrix(); + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + case AnnotationTypeEnum::BROWSER_TAB: + case AnnotationTypeEnum::COLOR_BAR: + case AnnotationTypeEnum::IMAGE: + case AnnotationTypeEnum::OVAL: + case AnnotationTypeEnum::SCALE_BAR: + case AnnotationTypeEnum::TEXT: + case AnnotationTypeEnum::LINE: + CaretAssert(0); + break; + case AnnotationTypeEnum::POLY_LINE: + drawnFlag = drawPolyLineSurfaceTextureOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed, + surfaceExtentZ); + break; + } + + glPopMatrix(); /* restore MODELVIEW */ + + glViewport(savedViewport[0], + savedViewport[1], + savedViewport[2], + savedViewport[3]); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + return drawnFlag; +} + +/** * Draw an annotation box. * * @param annotationFile @@ -1590,10 +1771,12 @@ const bool drawForegroundFlag = (foregroundRGBA[3] > 0); const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); + const int32_t invalidPolyLineCoordinateIndex(-1); bool drawnFlag = false; if (drawAnnotationFlag) { - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); @@ -1627,6 +1810,7 @@ m_selectionInfo.push_back(SelectionInfo(annotationFile, box, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -1651,7 +1835,7 @@ } } if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, box, bottomLeft, bottomRight, @@ -1706,7 +1890,8 @@ float lineThickness = ((box->getLineWidthPercentage() / 100.0) * surfaceExtentZ); - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { lineThickness = std::max(lineThickness, s_selectionLineMinimumPixelWidth); } @@ -1723,7 +1908,8 @@ bool drawnFlag = false; if (drawAnnotationFlag) { - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); @@ -1753,9 +1939,11 @@ lineThickness); } + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, box, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -1782,7 +1970,7 @@ } } if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, box, bottomLeft, bottomRight, @@ -1796,136 +1984,459 @@ return drawnFlag; } - /** - * Draw an annotation color bar. + * Draw an annotation browser tab. * * @param annotationFile * File containing the annotation. - * @param colorBar - * Color bar to draw. + * @param browserTab + * Browser tab to draw. */ -void -BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotationFile, - AnnotationColorBar* colorBar) +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawBrowserTab(AnnotationFile* annotationFile, + AnnotationBrowserTab* browserTab) { - CaretAssert(colorBar); - CaretAssert(colorBar->getType() == AnnotationTypeEnum::COLOR_BAR); + CaretAssert(browserTab); + CaretAssert(browserTab->getType() == AnnotationTypeEnum::BROWSER_TAB); float annXYZ[3]; - if ( ! getAnnotationDrawingSpaceCoordinate(colorBar, - colorBar->getCoordinate(), - NULL, - annXYZ)) { - return; + Surface* nullSurface(NULL); + if ( ! getAnnotationDrawingSpaceCoordinate(browserTab, + browserTab->getCoordinate(), + nullSurface, + annXYZ)) { + return false; } float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; - if ( ! getAnnotationTwoDimShapeBounds(colorBar, annXYZ, + if ( ! getAnnotationTwoDimShapeBounds(browserTab, annXYZ, bottomLeft, bottomRight, topRight, topLeft)) { - return; + return false; } - const float viewportHeight = m_modelSpaceViewport[3]; - const float viewportWidth = m_modelSpaceViewport[2]; + const float selectionCenterXYZ[3] = { + (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, + (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, + (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0f + }; - /* - * The user sets the total height of the colorbar and the - * height of the text. - * - * There are three items in the colorbar from top to bottom: - * (1) The numeric values - * (2) The tick marks - * (3) The palettes color sections - */ - float totalHeightPercent = colorBar->getHeight(); - float textHeightPercent = colorBar->getFontPercentViewportSize(); - float totalHeightPixels = (viewportHeight - * (totalHeightPercent / 100.0f)); + if (browserTab->getLineWidthPercentage() <= 0.0f) { + convertObsoleteLineWidthPixelsToPercentageWidth(browserTab); + } - /* - * Text is aligned at the top of the characters - */ - float textHeightPixels = (viewportHeight - * (textHeightPercent / 100.0f)); + const Surface* surfaceDisplayed(NULL); + const bool depthTestFlag = isDrawnWithDepthTesting(browserTab, + surfaceDisplayed); + const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); - if (debugFlag) { - std::cout << "Color bar heights before corrections (pixels) " << std::endl; - std::cout << " Text: " << textHeightPixels << std::endl; - } + uint8_t backgroundRGBA[4]; + browserTab->getBackgroundColorRGBA(backgroundRGBA); + uint8_t foregroundRGBA[4]; + browserTab->getLineColorRGBA(foregroundRGBA); - switch (colorBar->getPositionMode()) { - case AnnotationColorBarPositionModeEnum::AUTOMATIC: - { - const float startingX = (bottomLeft[0] + topLeft[0]) / 2.0f; - float estimatedWidth = estimateColorBarWidth(colorBar, - textHeightPixels); + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0); + const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); + + bool drawnFlag = false; + + if (drawAnnotationFlag) { + if (m_selectionModeFlag + && m_inputs->m_tileTabsManualLayoutUserInputModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + + const bool alwaysDrawBackgroundForSelectionFlag(true); + if (drawBackgroundFlag + || alwaysDrawBackgroundForSelectionFlag) { /* - * Maximum width is set so that margins on left and right sides - * of the color bar are identical. + * When selecting draw only background if it is enabled + * since it is opaque and prevents "behind" annotations + * from being selected */ - const float maximumWidth = viewportWidth - (startingX * 2.0f); - if (estimatedWidth > maximumWidth) { - /* - * Since the width of the text is a function of the text height, - * gradually reduce the height of the text until the color bar - * width is less than the maximum allowable width. Note that - * the font heights are not a continuous function so that a - * range of "requested font heights" may correspond to one - * font height. - */ - const float tooBigTextHeightPixels = textHeightPixels; - const float onePercentPixelHeight = textHeightPixels * 0.01; - for (int32_t pctSteps = 0; pctSteps < 100; pctSteps++) { - textHeightPixels -= onePercentPixelHeight; - estimatedWidth = estimateColorBarWidth(colorBar, - textHeightPixels); - if (estimatedWidth <= maximumWidth) { - break; - } - } - if (estimatedWidth > maximumWidth) { - estimatedWidth = maximumWidth; - } - - /* - * Since the height of the text has been reduced, we must also reduce the - * height of the color bar. Otherwise, the color swatches will become taller - * (color swatches fill the region of color bar not used by the text). - */ - const float textHeightPixelChangePercent = ((tooBigTextHeightPixels - textHeightPixels) - / tooBigTextHeightPixels); - if (debugFlag) { - std::cout << "Too big text height: " << tooBigTextHeightPixels << std::endl; - std::cout << " Reduced to: " << textHeightPixels << std::endl; - std::cout << "Text height percentage change: " << textHeightPixelChangePercent << std::endl; - } - - const float tooBigTextHeightPercent = textHeightPercent; - textHeightPercent = (textHeightPixels / viewportHeight) * 100.0; - const float textPercentChange = tooBigTextHeightPercent - textHeightPercent; - totalHeightPercent -= textPercentChange; - totalHeightPixels = (viewportHeight - * (totalHeightPercent / 100.0f)); - } - - const float shapeWidth = MathFunctions::distance3D(bottomLeft, bottomRight); - if (estimatedWidth > shapeWidth) { - /* - * The corners of the color bar must now be recomputed to account - * for the reduced dimensions of the color bar. - */ - float bottomUnitVector[3]; - MathFunctions::createUnitVector(bottomLeft, bottomRight, bottomUnitVector); - float topUnitVector[3]; - MathFunctions::createUnitVector(topLeft, topRight, topUnitVector); - float leftUnitVector[3]; - MathFunctions::createUnitVector(bottomLeft, topLeft, leftUnitVector); - if (debugFlag) { - std::cout << "Too short bottom right: " << AString::fromNumbers(bottomRight, 3, ", ") << std::endl; + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA); + } + else { + /* + * Drawing foreground as line will still allow user to + * select annotation that are inside of the box + */ + const float percentHeight = getLineWidthPercentageInSelectionMode(browserTab); + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + percentHeight); + } + + const int32_t invalidPolyLineCoordinateIndex(-1); + m_selectionInfo.push_back(SelectionInfo(annotationFile, + browserTab, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, + selectionCenterXYZ)); + } + else { + if (drawBackgroundFlag) { + drawnFlag = true; + } + + if (drawForegroundFlag) { + drawnFlag = true; + } + } + if (browserTab->isSelectedForEditing(m_inputs->m_windowIndex)) { + drawAnnotationTwoDimShapeSizingHandles(annotationFile, + browserTab, + bottomLeft, + bottomRight, + topRight, + topLeft, + s_sizingHandleLineWidthInPixels, + browserTab->getRotationAngle()); + } + else if (m_inputs->m_tileTabsManualLayoutUserInputModeFlag) { + glPushAttrib(GL_LINE_BIT); + glLineStipple(1, 0xf000); + glEnable(GL_LINE_STIPPLE); + glLineWidth(1); + glColor4ubv(foregroundRGBA); + glBegin(GL_LINE_LOOP); + glVertex3fv(bottomLeft); + glVertex3fv(bottomRight); + glVertex3fv(topRight); + glVertex3fv(topLeft); + glEnd(); + glPopAttrib(); + } + } + + setDepthTestingStatus(savedDepthTestStatus); + + return drawnFlag; +} + +/** + * Draw an annotation scale bar. + * + * @param annotationFile + * File containing the annotation. + * @param scaleBar + * Scale bar to draw. + */ +void +BrainOpenGLAnnotationDrawingFixedPipeline::drawScaleBar(AnnotationFile* annotationFile, + AnnotationScaleBar* scaleBar) +{ + CaretAssert(scaleBar); + + if ( ! scaleBar->isDisplayed()) { + return; + } + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, + viewport); + + std::array startXYZ; + if ( ! getAnnotationDrawingSpaceCoordinate(scaleBar, + scaleBar->getCoordinate(), + NULL, + startXYZ.data())) { + return; + } + + uint8_t backgroundRGBA[4]; + scaleBar->getBackgroundColorRGBA(backgroundRGBA); + uint8_t foregroundRGBA[4]; + scaleBar->getLineColorRGBA(foregroundRGBA); + + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0); + const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); + + bool drawnFlag = false; + + bool fontTooSmallFlag(false); + + BrainOpenGLTextRenderInterface::DrawingFlags textDrawingFlags; + textDrawingFlags.setDrawSubstitutedText(false); + + const AnnotationPercentSizeText* annLengthText = scaleBar->getLengthTextAnnotation(); + CaretAssert(annLengthText); + + AnnotationScaleBar::DrawingInfo scaleBarDrawingInfo; + scaleBar->getScaleBarDrawingInfo(viewport[2], + viewport[3], + startXYZ, + m_selectionModeFlag, + scaleBarDrawingInfo); + + if ( ! scaleBarDrawingInfo.isValid()) { + return; + } + + const bool depthTestFlag(false); + const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); + + if (drawAnnotationFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + + if (drawBackgroundFlag) { + /* + * When selecting draw only background if it is enabled + * since it is opaque and prevents "behind" annotations + * from being selected + */ + GraphicsShape::drawBoxFilledByteColor(&scaleBarDrawingInfo.m_backgroundBounds[0], + &scaleBarDrawingInfo.m_backgroundBounds[3], + &scaleBarDrawingInfo.m_backgroundBounds[6], + &scaleBarDrawingInfo.m_backgroundBounds[9], + selectionColorRGBA); + } + else { + /* + * Drawing foreground as line will still allow user to + * select annotation that are inside of the box + */ + GraphicsShape::drawBoxFilledByteColor(&scaleBarDrawingInfo.m_barBounds[0], + &scaleBarDrawingInfo.m_barBounds[3], + &scaleBarDrawingInfo.m_barBounds[6], + &scaleBarDrawingInfo.m_barBounds[9], + selectionColorRGBA); + + if (scaleBar->isShowLengthText()) { + /* + * Drawing box where text is located so text + * can be clicked to cause selection + */ + GraphicsShape::drawBoxFilledByteColor(&scaleBarDrawingInfo.m_textBounds[0], + &scaleBarDrawingInfo.m_textBounds[3], + &scaleBarDrawingInfo.m_textBounds[6], + &scaleBarDrawingInfo.m_textBounds[9], + selectionColorRGBA); + } + } + + const float selectionCenterXYZ[3] = { + (scaleBarDrawingInfo.m_backgroundBounds[0] + scaleBarDrawingInfo.m_backgroundBounds[4]) / 2.0f, + (scaleBarDrawingInfo.m_backgroundBounds[1] + scaleBarDrawingInfo.m_backgroundBounds[10]) / 2.0f, + scaleBarDrawingInfo.m_backgroundBounds[2] + }; + const int32_t invalidPolyLineCoordinateIndex(-1); + m_selectionInfo.push_back(SelectionInfo(annotationFile, + scaleBar, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, + selectionCenterXYZ)); + } + else { + if (drawBackgroundFlag) { + /* + * Draws background for bar and text + */ + GraphicsShape::drawBoxFilledByteColor(&scaleBarDrawingInfo.m_backgroundBounds[0], + &scaleBarDrawingInfo.m_backgroundBounds[3], + &scaleBarDrawingInfo.m_backgroundBounds[6], + &scaleBarDrawingInfo.m_backgroundBounds[9], + backgroundRGBA); + drawnFlag = true; + } + + if (drawForegroundFlag) { + /* + * Draws bar only + */ + GraphicsShape::drawBoxFilledByteColor(&scaleBarDrawingInfo.m_barBounds[0], + &scaleBarDrawingInfo.m_barBounds[3], + &scaleBarDrawingInfo.m_barBounds[6], + &scaleBarDrawingInfo.m_barBounds[9], + foregroundRGBA); + + if (scaleBar->isShowTickMarks()) { + for (const auto& tickBounds : scaleBarDrawingInfo.m_ticksBounds) { + GraphicsShape::drawBoxFilledByteColor(&tickBounds[0], + &tickBounds[3], + &tickBounds[6], + &tickBounds[9], + foregroundRGBA); + } + } + + drawnFlag = true; + } + + if (drawnFlag) { + if (scaleBar->isShowLengthText()) { + m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextAtViewportCoords(scaleBarDrawingInfo.m_textStartXYZ[0], + scaleBarDrawingInfo.m_textStartXYZ[1], + scaleBarDrawingInfo.m_textStartXYZ[2], + *annLengthText, + textDrawingFlags); + if (annLengthText->isFontTooSmallWhenLastDrawn()) { + fontTooSmallFlag = true; + } + } + } + } + + if (scaleBar->isSelectedForEditing(m_inputs->m_windowIndex)) { + drawAnnotationTwoDimShapeSizingHandles(annotationFile, + scaleBar, + &scaleBarDrawingInfo.m_backgroundBounds[0], + &scaleBarDrawingInfo.m_backgroundBounds[3], + &scaleBarDrawingInfo.m_backgroundBounds[6], + &scaleBarDrawingInfo.m_backgroundBounds[9], + s_sizingHandleLineWidthInPixels, + scaleBar->getRotationAngle()); + } + } + + scaleBar->setFontTooSmallWhenLastDrawn(fontTooSmallFlag); + setDepthTestingStatus(savedDepthTestStatus); +} + + +/** + * Draw an annotation color bar. + * + * @param annotationFile + * File containing the annotation. + * @param colorBar + * Color bar to draw. + */ +void +BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotationFile, + AnnotationColorBar* colorBar) +{ + CaretAssert(colorBar); + + float annXYZ[3]; + if ( ! getAnnotationDrawingSpaceCoordinate(colorBar, + colorBar->getCoordinate(), + NULL, + annXYZ)) { + return; + } + + float bottomLeft[3]; + float bottomRight[3]; + float topRight[3]; + float topLeft[3]; + if ( ! getAnnotationTwoDimShapeBounds(colorBar, annXYZ, + bottomLeft, bottomRight, topRight, topLeft)) { + return; + } + + const float viewportHeight = m_modelSpaceViewport[3]; + const float viewportWidth = m_modelSpaceViewport[2]; + + /* + * The user sets the total height of the colorbar and the + * height of the text. + * + * There are three items in the colorbar from top to bottom: + * (1) The numeric values + * (2) The tick marks + * (3) The palettes color sections + */ + float totalHeightPercent = colorBar->getHeight(); + float textHeightPercent = colorBar->getFontPercentViewportSize(); + float totalHeightPixels = (viewportHeight + * (totalHeightPercent / 100.0f)); + + /* + * Text is aligned at the top of the characters + */ + float textHeightPixels = (viewportHeight + * (textHeightPercent / 100.0f)); + + if (debugFlag) { + std::cout << "Color bar heights before corrections (pixels) " << std::endl; + std::cout << " Text: " << textHeightPixels << std::endl; + } + + switch (colorBar->getPositionMode()) { + case AnnotationColorBarPositionModeEnum::AUTOMATIC: + { + const float startingX = (bottomLeft[0] + topLeft[0]) / 2.0f; + float estimatedWidth = estimateColorBarWidth(colorBar, + textHeightPixels); + /* + * Maximum width is set so that margins on left and right sides + * of the color bar are identical. + */ + const float maximumWidth = viewportWidth - (startingX * 2.0f); + if (estimatedWidth > maximumWidth) { + /* + * Since the width of the text is a function of the text height, + * gradually reduce the height of the text until the color bar + * width is less than the maximum allowable width. Note that + * the font heights are not a continuous function so that a + * range of "requested font heights" may correspond to one + * font height. + */ + const float tooBigTextHeightPixels = textHeightPixels; + const float onePercentPixelHeight = textHeightPixels * 0.01; + for (int32_t pctSteps = 0; pctSteps < 100; pctSteps++) { + textHeightPixels -= onePercentPixelHeight; + estimatedWidth = estimateColorBarWidth(colorBar, + textHeightPixels); + if (estimatedWidth <= maximumWidth) { + break; + } + } + if (estimatedWidth > maximumWidth) { + estimatedWidth = maximumWidth; + } + + /* + * Since the height of the text has been reduced, we must also reduce the + * height of the color bar. Otherwise, the color swatches will become taller + * (color swatches fill the region of color bar not used by the text). + */ + const float textHeightPixelChangePercent = ((tooBigTextHeightPixels - textHeightPixels) + / tooBigTextHeightPixels); + if (debugFlag) { + std::cout << "Too big text height: " << tooBigTextHeightPixels << std::endl; + std::cout << " Reduced to: " << textHeightPixels << std::endl; + std::cout << "Text height percentage change: " << textHeightPixelChangePercent << std::endl; + } + + const float tooBigTextHeightPercent = textHeightPercent; + textHeightPercent = (textHeightPixels / viewportHeight) * 100.0; + const float textPercentChange = tooBigTextHeightPercent - textHeightPercent; + totalHeightPercent -= textPercentChange; + totalHeightPixels = (viewportHeight + * (totalHeightPercent / 100.0f)); + } + + const float shapeWidth = MathFunctions::distance3D(bottomLeft, bottomRight); + if (estimatedWidth > shapeWidth) { + /* + * The corners of the color bar must now be recomputed to account + * for the reduced dimensions of the color bar. + */ + float bottomUnitVector[3]; + MathFunctions::createUnitVector(bottomLeft, bottomRight, bottomUnitVector); + float topUnitVector[3]; + MathFunctions::createUnitVector(topLeft, topRight, topUnitVector); + float leftUnitVector[3]; + MathFunctions::createUnitVector(bottomLeft, topLeft, leftUnitVector); + if (debugFlag) { + std::cout << "Too short bottom right: " << AString::fromNumbers(bottomRight, 3, ", ") << std::endl; } for (int32_t i = 0; i < 3; i++) { topLeft[i] = bottomLeft[i] + leftUnitVector[i] * totalHeightPixels; @@ -1981,14 +2492,17 @@ const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0f); - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); GraphicsShape::drawBoxFilledByteColor(bottomLeft, bottomRight, topRight, topLeft, selectionColorRGBA); + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, colorBar, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -2047,7 +2561,7 @@ tickMarksOffsetFromBotom); } if (colorBar->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, colorBar, bottomLeft, bottomRight, @@ -2650,7 +3164,8 @@ glRotatef(-rotationAngle, 0.0f, 0.0f, 1.0f); } - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); @@ -2677,9 +3192,11 @@ percentHeight); } + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, oval, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -2702,7 +3219,7 @@ glPopMatrix(); if (oval->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, oval, bottomLeft, bottomRight, @@ -2757,7 +3274,8 @@ float lineThickness = ((oval->getLineWidthPercentage() / 100.0) * surfaceExtentZ); - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { lineThickness = std::max(lineThickness, s_selectionLineMinimumPixelWidth); } @@ -2777,7 +3295,8 @@ const float minorAxis = ((oval->getHeight() / 100.0f) * surfaceExtentZ); if (drawAnnotationFlag) { - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); @@ -2803,9 +3322,11 @@ lineThickness); } + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, oval, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -2823,17 +3344,12 @@ minorAxis, foregroundRGBA, lineThickness); -// GraphicsShape::drawEllipseOutlineByteColor(majorAxis, -// minorAxis, -// foregroundRGBA, -// GraphicsPrimitive::LineWidthType::PIXELS, -// lineThickness); glDisable(GL_POLYGON_OFFSET_FILL); drawnFlag = true; } } if (oval->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, oval, bottomLeft, bottomRight, @@ -3175,7 +3691,8 @@ underlineStart, underlineEnd); bool textDrawnFlag = false; - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4] = { 0, 0, 0, 0 }; getIdentificationColor(selectionColorRGBA); GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, @@ -3190,9 +3707,12 @@ primitive.addVertex(topRight, doubleNormalXYZ); primitive.addVertex(bottomRight, doubleNormalXYZ); GraphicsEngineDataOpenGL::draw(&primitive); + + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, text, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, vertexXYZ)); } else { @@ -3231,7 +3751,7 @@ floatTopLeft[i] = topLeft[i]; floatTopRight[i] = topRight[i]; } - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, text, floatBottomLeft, floatBottomRight, @@ -3306,6 +3826,10 @@ m_modelSpaceViewport[2], m_modelSpaceViewport[3], bottomLeft, bottomRight, topRight, topLeft); + BoundingBox bb; + bb.set(bottomLeft, bottomRight, topRight, topLeft); + text->setDrawnInWindowBounds(m_inputs->m_windowIndex, bb); + const float selectionCenterXYZ[3] = { (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, @@ -3330,14 +3854,18 @@ bool drawnFlag = false; if (drawAnnotationFlag) { - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); GraphicsShape::drawBoxFilledByteColor(bottomLeft, bottomRight, topRight, topLeft, selectionColorRGBA); + + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, text, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -3421,7 +3949,7 @@ } if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, text, bottomLeft, bottomRight, @@ -3491,10 +4019,16 @@ if ((imageWidth > 0) && (imageHeight > 0) && (imageRgbaBytes != NULL)) { - + /* OK */ } else { - CaretLogWarning("Attempt to draw invalid image annotation."); + /* + * This will occur when the user is dragging the mouse to ceate + * an image annotation. Continue processing so that a box + * outline is drawn. + */ + CaretLogFine("Image annotation is invalid for drawing. This will " + "occur when user is dragging mouse to create image annotation. "); } float annXYZ[3]; @@ -3534,14 +4068,17 @@ bool drawnFlag = false; if (drawAnnotationFlag) { - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); GraphicsShape::drawBoxFilledByteColor(bottomLeft, bottomRight, topRight, topLeft, selectionColorRGBA); + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, image, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -3580,7 +4117,7 @@ } if (image->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, image, bottomLeft, bottomRight, @@ -3635,7 +4172,8 @@ float lineThickness = ((image->getLineWidthPercentage() / 100.0) * surfaceExtentZ); - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { lineThickness = std::max(lineThickness, s_selectionLineMinimumPixelWidth); } @@ -3651,7 +4189,8 @@ bool drawnFlag = false; if (drawAnnotationFlag) { - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); @@ -3665,9 +4204,11 @@ topRight, topLeft, selectionColorRGBA); + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, image, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -3697,7 +4238,7 @@ } } if (image->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, image, bottomLeft, bottomRight, @@ -3912,7 +4453,8 @@ bool drawnFlag = false; if (drawForegroundFlag) { - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); @@ -3933,9 +4475,11 @@ GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, line->getLineWidthPercentage()); } + const int32_t invalidPolyLineCoordinateIndex(-1); m_selectionInfo.push_back(SelectionInfo(annotationFile, line, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -3961,7 +4505,7 @@ } if (line->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationOneDimSizingHandles(annotationFile, + drawAnnotationLineSizingHandles(annotationFile, line, lineHeadXYZ, lineTailXYZ, @@ -3971,6 +4515,13 @@ setDepthTestingStatus(savedDepthTestStatus); + BoundingBox bb; + if (drawnFlag) { + /* Note head and tail intentionally used twice since line only has two points, unlike box or other shapes */ + bb.set(lineHeadXYZ, lineTailXYZ, lineHeadXYZ, lineTailXYZ); + } + line->setDrawnInWindowBounds(m_inputs->m_windowIndex, bb); + return drawnFlag; } @@ -4046,12 +4597,14 @@ float lineThickness = ((line->getLineWidthPercentage() / 100.0) * surfaceExtentZ); lineThickness *= m_surfaceViewScaling; - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { lineThickness = std::max(lineThickness, s_selectionLineMinimumPixelWidth); } lineThickness = GraphicsUtilitiesOpenGL::convertMillimetersToPixels(lineThickness); + int32_t invalidPolyLineCoordinateIndex(-1); bool drawnFlag = false; std::vector lineCoordinates; @@ -4072,7 +4625,8 @@ const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0f); if (drawForegroundFlag) { - if (m_selectionModeFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); @@ -4080,59 +4634,18 @@ selectionColorRGBA, GraphicsPrimitive::LineWidthType::PIXELS, lineThickness); - // if ( ! startArrowCoordinates.empty()) { - // GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, - // selectionColorRGBA, - // GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, - // lineThickness); - // } - // if ( ! endArrowCoordinates.empty()) { - // GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, - // selectionColorRGBA, - // GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, - // lineThickness); - // } m_selectionInfo.push_back(SelectionInfo(annotationFile, line, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { if (drawForegroundFlag) { - // float floatRGBA[4]; - // line->getLineColorRGBA(floatRGBA); - // m_brainOpenGLFixedPipeline->drawCylinder(floatRGBA, - // lineHeadXYZ, - // lineTailXYZ, - // lineThickness / 2); GraphicsShape::drawLinesByteColor(lineCoordinates, foregroundRGBA, GraphicsPrimitive::LineWidthType::PIXELS, lineThickness); - // if ( ! startArrowCoordinates.empty()) { - // if (startArrowCoordinates.size() == 9) { - // m_brainOpenGLFixedPipeline->drawCylinder(floatRGBA, - // &startArrowCoordinates[0], - // &startArrowCoordinates[3], - // lineThickness / 2); - // m_brainOpenGLFixedPipeline->drawCylinder(floatRGBA, - // &startArrowCoordinates[3], - // &startArrowCoordinates[6], - // lineThickness / 2); - // } - // else { - // GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, - // foregroundRGBA, - // GraphicsPrimitive::LineWidthType::PIXELS, - // lineThickness); - // } - // } - // if ( ! endArrowCoordinates.empty()) { - // GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, - // foregroundRGBA, - // GraphicsPrimitive::LineWidthType::PIXELS, - // lineThickness); - // } drawnFlag = true; } } @@ -4143,18 +4656,227 @@ * m_surfaceViewScaling); const float handleThickness = std::max(minSizeMM, lineThickness * 2.0f); - drawAnnotationOneDimSizingHandles(annotationFile, + drawAnnotationLineSizingHandles(annotationFile, line, lineHeadXYZ, lineTailXYZ, - handleThickness); //lineThickness * 2.0); //s_sizingHandleLineWidthInPixels); + handleThickness); + } + } + + return drawnFlag; +} + +/** + * Draw an annotation poly line. + * + * @param annotationFile + * File containing the annotation. + * @param polyLine + * Annotation poly line to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawPolyLine(AnnotationFile* annotationFile, + AnnotationPolyLine* polyLine, + const Surface* surfaceDisplayed) +{ + CaretAssert(polyLine); + CaretAssert(polyLine->getType() == AnnotationTypeEnum::POLY_LINE); + + uint8_t foregroundRGBA[4]; + polyLine->getLineColorRGBA(foregroundRGBA); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0f); + + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN, + foregroundRGBA)); + const int32_t numCoords = polyLine->getNumberOfCoordinates(); + for (int32_t i = 0; i < numCoords; i++) { + float xyz[3]; + if ( ! getAnnotationDrawingSpaceCoordinate(polyLine, + polyLine->getCoordinate(i), + surfaceDisplayed, + xyz)) { + return false; + } + primitive->addVertex(xyz); + } + + if (polyLine->getLineWidthPercentage() <= 0.0) { + convertObsoleteLineWidthPixelsToPercentageWidth(polyLine); + } + primitive->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + polyLine->getLineWidthPercentage()); + + BoundingBox boundingBox; + primitive->getVertexBounds(boundingBox); + float selectionCenterXYZ[3]; + boundingBox.getCenter(selectionCenterXYZ); + + const bool depthTestFlag = isDrawnWithDepthTesting(polyLine, + surfaceDisplayed); + const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); + + + bool drawnFlag = false; + + if (drawForegroundFlag) { + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + primitive->replaceAllVertexSolidByteRGBA(selectionColorRGBA); + const int32_t invalidPolyLineCoordinateIndex(-1); + m_selectionInfo.push_back(SelectionInfo(annotationFile, + polyLine, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, + selectionCenterXYZ)); + } + + GraphicsEngineDataOpenGL::draw(primitive.get()); + drawnFlag = true; + + if (polyLine->isSelectedForEditing(m_inputs->m_windowIndex)) { + drawAnnotationPolyLineSizingHandles(annotationFile, + polyLine, + primitive.get(), + s_sizingHandleLineWidthInPixels); } } + setDepthTestingStatus(savedDepthTestStatus); + + polyLine->setDrawnInWindowBounds(m_inputs->m_windowIndex, + boundingBox); + return drawnFlag; } /** + * Draw an annotation poly line that is in surface space with tangent offset. + * + * @param annotationFile + * File containing the annotation. + * @param polyLine + * Annotation poly line to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawPolyLineSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationPolyLine* polyLine, + const Surface* surfaceDisplayed, + const float surfaceExtentZ) +{ + CaretAssert(polyLine); + CaretAssert(polyLine->getType() == AnnotationTypeEnum::POLY_LINE); + CaretAssert(surfaceDisplayed); + + const int32_t numCoords = polyLine->getNumberOfCoordinates(); + if (numCoords < 2) { + return false; + } + + uint8_t rgba[4]; + polyLine->getLineColorRGBA(rgba); + if (rgba[3] <= 0) { + return false; + } + + const StructureEnum::Enum surfaceStructure(surfaceDisplayed->getStructure()); + const int32_t surfaceVertexCount(surfaceDisplayed->getNumberOfNodes()); + + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN, + rgba)); + for (int32_t i = 0; i < numCoords; i++) { + const AnnotationCoordinate* ac = polyLine->getCoordinate(i); + StructureEnum::Enum structure(StructureEnum::INVALID); + int32_t vertexCount(-1); + int32_t vertexIndex(-1); + float offsetLength(0.0); + AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVector(AnnotationSurfaceOffsetVectorTypeEnum::TANGENT); + ac->getSurfaceSpace(structure, vertexCount, vertexIndex, + offsetLength, offsetVector); + if (structure != surfaceStructure) { + CaretLogWarning("Polyline vertex does not map to displayed surface"); + return false; + } + else if (vertexCount != surfaceVertexCount) { + CaretLogWarning("Polyline vertex maps to surface with different vertex count"); + return false; + } + + float xyz[3]; + surfaceDisplayed->getCoordinate(vertexIndex, xyz); + + float normalVector[3]; + surfaceDisplayed->getNormalVector(vertexIndex, normalVector); + + for (int32_t n = 0; n < 3; n++) { + xyz[n] += (normalVector[n] * offsetLength); + } + + primitive->addVertex(xyz); + } + + if (polyLine->getLineWidthPercentage() <= 0.0) { + convertObsoleteLineWidthPixelsToPercentageWidth(polyLine); + } + float lineThickness = ((polyLine->getLineWidthPercentage() / 100.0) + * surfaceExtentZ); + lineThickness *= m_surfaceViewScaling; + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { + lineThickness = std::max(lineThickness, + s_selectionLineMinimumPixelWidth); + } + lineThickness = GraphicsUtilitiesOpenGL::convertMillimetersToPixels(lineThickness); + primitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); + + if (m_selectionModeFlag + && m_inputs->m_annotationUserInputModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + + primitive->replaceAllVertexSolidByteRGBA(selectionColorRGBA); + BoundingBox bb; + primitive->getVertexBounds(bb); + float selectionCenterXYZ[3]; + bb.getCenter(selectionCenterXYZ); + + const int32_t invalidPolyLineCoordinateIndex(0); + m_selectionInfo.push_back(SelectionInfo(annotationFile, + polyLine, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, + selectionCenterXYZ)); + } + + GraphicsEngineDataOpenGL::draw(primitive.get()); + + if (polyLine->isSelectedForEditing(m_inputs->m_windowIndex)) { + const float minPixelSize = 30.0; + const float minSizeMM = (GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(minPixelSize) + * m_surfaceViewScaling); + const float handleThickness = std::max(minSizeMM, + lineThickness * 2.0f); + drawAnnotationPolyLineSizingHandles(annotationFile, + polyLine, + primitive.get(), + handleThickness); + } + + return true; +} + +/** * When lines are very thin, they can be difficult to select. This method will * return the line thickness percentage, adjusted so that is thick enough that * the user will be able to select the annotation. @@ -4194,6 +4916,8 @@ * Half Width/height of square. * @param rotationAngle * Rotation angle for the sizing handle. + * @param polyLineCoordinateIndex + * Index of poly line coordinate */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizingHandleTypeEnum::Enum handleType, @@ -4201,7 +4925,8 @@ Annotation* annotation, const float xyz[3], const float halfWidthHeight, - const float rotationAngle) + const float rotationAngle, + const int32_t polyLineCoordinateIndex) { glPushMatrix(); @@ -4266,55 +4991,95 @@ case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: drawOutlineCircleFlag = true; break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + if (annotation->isInSurfaceSpaceWithTangentOffset()) { + drawSphereFlag = true; + } + else { + drawFilledCircleFlag = true; + } + break; + } + + if (annotation->getType() == AnnotationTypeEnum::BROWSER_TAB) { + drawFilledCircleFlag = false; + drawOutlineCircleFlag = false; + drawSphereFlag = false; + drawSquareFlag = true; } + bool selectionFlag(false); if (m_selectionModeFlag) { - uint8_t identificationRGBA[4]; - getIdentificationColor(identificationRGBA); - GraphicsShape::drawBoxFilledByteColor(bottomLeft, - bottomRight, - topRight, - topLeft, - identificationRGBA); + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + case AnnotationTypeEnum::COLOR_BAR: + case AnnotationTypeEnum::IMAGE: + case AnnotationTypeEnum::LINE: + case AnnotationTypeEnum::OVAL: + case AnnotationTypeEnum::POLY_LINE: + case AnnotationTypeEnum::SCALE_BAR: + case AnnotationTypeEnum::TEXT: + selectionFlag = m_inputs->m_annotationUserInputModeFlag; + break; + case AnnotationTypeEnum::BROWSER_TAB: + selectionFlag = m_inputs->m_tileTabsManualLayoutUserInputModeFlag; + break; + } + } + + uint8_t symbolRGBA[4] { + m_selectionBoxRGBA[0], + m_selectionBoxRGBA[1], + m_selectionBoxRGBA[2], + m_selectionBoxRGBA[3] + }; + + if (selectionFlag) { + getIdentificationColor(symbolRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, annotation, handleType, + polyLineCoordinateIndex, xyz)); - } - else { - if (drawFilledCircleFlag) { - GraphicsShape::drawCircleFilled(NULL, - m_selectionBoxRGBA, - halfWidthHeight * 2); - } - else if (drawSquareFlag) { - GraphicsShape::drawBoxFilledByteColor(bottomLeft, - bottomRight, - topRight, - topLeft, - m_selectionBoxRGBA); - } - else if (drawOutlineCircleFlag) { - const float diameter = halfWidthHeight; - glPushMatrix(); - glScaled(diameter, diameter, 1.0f); - GraphicsShape::drawRing(NULL, - m_selectionBoxRGBA, - 0.7f, - 1.0f); - glPopMatrix(); - } - else if (drawSphereFlag) { - float zeros[3] { 0.0f, 0.0f, 0.0f }; - GraphicsShape::drawSphereByteColor(zeros, m_selectionBoxRGBA, halfWidthHeight); + if (drawOutlineCircleFlag) { + drawFilledCircleFlag = true; } } + + if (drawFilledCircleFlag) { + GraphicsShape::drawCircleFilled(NULL, + symbolRGBA, + halfWidthHeight * 2); + } + else if (drawSquareFlag) { + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + symbolRGBA); + } + else if (drawOutlineCircleFlag) { + const float diameter = halfWidthHeight; + glPushMatrix(); + glScaled(diameter, diameter, 1.0f); + GraphicsShape::drawRing(NULL, + symbolRGBA, + 0.7f, + 1.0f); + glPopMatrix(); + } + else if (drawSphereFlag) { + float zeros[3] { 0.0f, 0.0f, 0.0f }; + GraphicsShape::drawSphereByteColor(zeros, + symbolRGBA, + halfWidthHeight); + } glPopMatrix(); } /** - * Draw sizing handles around a one-dimensional annotation. + * Draw sizing handles around a two-coordinate annotation. * * @param annotationFile * File containing the annotation. @@ -4328,7 +5093,7 @@ * Thickness of line (when enabled). */ void -BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(AnnotationFile* annotationFile, +BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationLineSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float firstPoint[3], const float secondPoint[3], @@ -4350,7 +5115,7 @@ float cornerSquareSize = 3.0 + lineThickness; if (tangentSurfaceOffsetFlag) { - cornerSquareSize = lineThickness;// * 4.0; + cornerSquareSize = lineThickness; cornerSquareSize = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(cornerSquareSize); } @@ -4379,20 +5144,22 @@ rotationAngle = MathFunctions::toDegrees(angleRadians); } + const int32_t invalidPolyLineCoordinateIndex(-1); if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START)) { /* * Symbol for first coordinate is a little bigger */ float startSquareSize = cornerSquareSize + 2.0; if (tangentSurfaceOffsetFlag) { - startSquareSize = cornerSquareSize;// * 1.5; + startSquareSize = cornerSquareSize; } drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START, annotationFile, annotation, firstPointSymbolXYZ, startSquareSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END)) { @@ -4401,7 +5168,8 @@ annotation, secondPointSymbolXYZ, cornerSquareSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { @@ -4415,7 +5183,8 @@ annotation, midPointXYZ, cornerSquareSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } } @@ -4440,7 +5209,7 @@ * Rotation of the annotation. */ void -BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(AnnotationFile* annotationFile, +BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimShapeSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float bottomLeft[3], const float bottomRight[3], @@ -4449,11 +5218,29 @@ const float lineThickness, const float rotationAngle) { - if ( ! m_inputs->m_annotationUserInputModeFlag) { - return; - } CaretAssert(annotation); + + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + case AnnotationTypeEnum::COLOR_BAR: + case AnnotationTypeEnum::IMAGE: + case AnnotationTypeEnum::LINE: + case AnnotationTypeEnum::OVAL: + case AnnotationTypeEnum::POLY_LINE: + case AnnotationTypeEnum::SCALE_BAR: + case AnnotationTypeEnum::TEXT: + if ( ! m_inputs->m_annotationUserInputModeFlag) { + return; + } + break; + case AnnotationTypeEnum::BROWSER_TAB: + if ( ! m_inputs->m_tileTabsManualLayoutUserInputModeFlag) { + return; + } + break; + } + AnnotationText* textAnn = dynamic_cast(annotation); const bool modelSpaceTangentTextFlag = annotation->isInSurfaceSpaceWithTangentOffset(); @@ -4465,6 +5252,15 @@ if (modelSpaceTangentTextFlag) { innerSpacing = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(innerSpacing); } + + /* + * Must shrink box around browser tab or else the box is outside of the + * browser tab and not seen + */ + if (annotation->getType() == AnnotationTypeEnum::BROWSER_TAB) { + innerSpacing = -innerSpacing; + } + float handleTopLeft[3]; float handleTopRight[3]; float handleBottomRight[3]; @@ -4483,42 +5279,54 @@ m_selectionBoxRGBA, GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); } + float sizeHandleSize = 5.0; + if (modelSpaceTangentTextFlag) { + sizeHandleSize = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(sizeHandleSize); + } + + float handleOffset(0.0); + if (annotation->getType() == AnnotationTypeEnum::BROWSER_TAB) { + if (m_selectionModeFlag) { + sizeHandleSize *= 4.0; + } + else { + sizeHandleSize *= 2.0; + MathFunctions::expandBox(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, -sizeHandleSize, -sizeHandleSize); + } + } const float handleLeft[3] = { - (handleBottomLeft[0] + handleTopLeft[0]) / 2.0f, + (handleBottomLeft[0] + handleTopLeft[0]) / 2.0f + handleOffset, (handleBottomLeft[1] + handleTopLeft[1]) / 2.0f, (handleBottomLeft[2] + handleTopLeft[2]) / 2.0f, }; const float handleRight[3] = { - (handleBottomRight[0] + handleTopRight[0]) / 2.0f, + (handleBottomRight[0] + handleTopRight[0]) / 2.0f - handleOffset, (handleBottomRight[1] + handleTopRight[1]) / 2.0f, (handleBottomRight[2] + handleTopRight[2]) / 2.0f, }; const float handleBottom[3] = { (handleBottomLeft[0] + handleBottomRight[0]) / 2.0f, - (handleBottomLeft[1] + handleBottomRight[1]) / 2.0f, + (handleBottomLeft[1] + handleBottomRight[1]) / 2.0f + handleOffset, (handleBottomLeft[2] + handleBottomRight[2]) / 2.0f, }; const float handleTop[3] = { (handleTopLeft[0] + handleTopRight[0]) / 2.0f, - (handleTopLeft[1] + handleTopRight[1]) / 2.0f, + (handleTopLeft[1] + handleTopRight[1]) / 2.0f - handleOffset, (handleTopLeft[2] + handleTopRight[2]) / 2.0f, }; - float sizeHandleSize = 5.0; - if (modelSpaceTangentTextFlag) { - sizeHandleSize = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(sizeHandleSize); - } - + const int32_t invalidPolyLineCoordinateIndex(-1); if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT, annotationFile, annotation, handleBottomLeft, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT, @@ -4526,7 +5334,8 @@ annotation, handleBottomRight, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT, @@ -4534,7 +5343,8 @@ annotation, handleTopRight, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT, @@ -4542,7 +5352,8 @@ annotation, handleTopLeft, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP, @@ -4550,7 +5361,8 @@ annotation, handleTop, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM, @@ -4558,7 +5370,8 @@ annotation, handleBottom, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT, @@ -4566,7 +5379,8 @@ annotation, handleRight, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT, @@ -4574,7 +5388,8 @@ annotation, handleLeft, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { @@ -4631,16 +5446,89 @@ annotation, handleRotation, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); + } +} + +/** + * Draw sizing handles around a poly-line annotation. + * + * @param annotationFile + * File containing the annotation. + * @param annotation + * Annotation to draw. + * @param primitive + * Primitive that draws the annotation + * @param lineThickness + * Thickness of lines (when enabled). + */ +void +BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationPolyLineSizingHandles(AnnotationFile* annotationFile, + Annotation* annotation, + const GraphicsPrimitive* primitive, + const float lineThickness) +{ + const bool tangentSurfaceOffsetFlag = annotation->isInSurfaceSpaceWithTangentOffset(); + + float cornerSquareSize = 3.0 + lineThickness; + if (tangentSurfaceOffsetFlag) { + cornerSquareSize = lineThickness; + cornerSquareSize = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(cornerSquareSize); + } + + + const float rotationAngle(0.0); + + const int32_t numberOfVertices = primitive->getNumberOfVertices(); + for (int32_t i = 0; i < numberOfVertices; i++) { + float xyz[3]; + primitive->getVertexFloatXYZ(i, xyz); + if (i == 0) { + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE)) { + /* + * Symbol for first coordinate is a little bigger + */ + float startSquareSize = cornerSquareSize + 2.0; + if (tangentSurfaceOffsetFlag) { + startSquareSize = cornerSquareSize; + } + + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE, + annotationFile, + annotation, + xyz, + startSquareSize, + rotationAngle, + i); + } + } + else { + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE, + annotationFile, + annotation, + xyz, + cornerSquareSize, + rotationAngle, + i); + } + } } } + /** * Set the color for drawing the selection box and handles. + * + * @param annotation + * Annotation this is being drawn. */ void -BrainOpenGLAnnotationDrawingFixedPipeline::setSelectionBoxColor() +BrainOpenGLAnnotationDrawingFixedPipeline::setSelectionBoxColor(const Annotation* annotation) { + CaretAssert(annotation); + /* * Use the foreground color but reduce the intensity and saturation. */ @@ -4649,6 +5537,31 @@ m_selectionBoxRGBA[2] = m_brainOpenGLFixedPipeline->m_foregroundColorByte[2]; m_selectionBoxRGBA[3] = m_brainOpenGLFixedPipeline->m_foregroundColorByte[3]; + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + /* + * Foreground color is loaded into browser tab by BrainOpenGLFixedPipeline + */ + annotation->getLineColorRGBA(m_selectionBoxRGBA); + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } + QColor color(m_selectionBoxRGBA[0], m_selectionBoxRGBA[1], m_selectionBoxRGBA[2], diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h connectome-workbench-1.5.0/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h 2021-02-16 19:46:47.000000000 +0000 @@ -39,18 +39,23 @@ class Annotation; class AnnotationBox; + class AnnotationBrowserTab; class AnnotationColorBar; class AnnotationCoordinate; class AnnotationFile; class AnnotationImage; class AnnotationLine; - class AnnotationOneDimensionalShape; + class AnnotationMultiCoordinateShape; + class AnnotationTwoCoordinateShape; class AnnotationOval; + class AnnotationPolyLine; + class AnnotationScaleBar; class AnnotationText; - class AnnotationTwoDimensionalShape; + class AnnotationOneCoordinateShape; class Brain; class BrainOpenGLFixedPipeline; class EventOpenGLObjectToWindowTransform; + class GraphicsPrimitive; class Surface; class BrainOpenGLAnnotationDrawingFixedPipeline : public CaretObject { @@ -70,7 +75,8 @@ const int32_t tabIndex, const SpacerTabIndex &spacerTabIndex, const WindowDrawingMode windowDrawingMode, - const bool annotationUserInputModeFlag) + const bool annotationUserInputModeFlag, + const bool tileTabsManualLayoutUserInputModeFlag) : m_brain(brain), m_drawingMode(drawingMode), m_centerToEyeDistance(centerToEyeDistance), @@ -78,8 +84,9 @@ m_tabIndex(tabIndex), m_spacerTabIndex(spacerTabIndex), m_windowDrawingMode(windowDrawingMode), - m_annotationUserInputModeFlag(annotationUserInputModeFlag) { - } + m_annotationUserInputModeFlag(annotationUserInputModeFlag), + m_tileTabsManualLayoutUserInputModeFlag(tileTabsManualLayoutUserInputModeFlag) + { } Brain* m_brain; const BrainOpenGLFixedPipeline::Mode m_drawingMode; @@ -89,6 +96,7 @@ const SpacerTabIndex m_spacerTabIndex; const WindowDrawingMode m_windowDrawingMode; const bool m_annotationUserInputModeFlag; + const bool m_tileTabsManualLayoutUserInputModeFlag; }; BrainOpenGLAnnotationDrawingFixedPipeline(BrainOpenGLFixedPipeline* brainOpenGLFixedPipeline); @@ -98,6 +106,7 @@ void drawAnnotations(Inputs* inputs, const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, + std::vector& scaleBars, std::vector& notInFileAnnotations, const Surface* surfaceDisplayed, const float surfaceViewScaling); @@ -116,10 +125,12 @@ SelectionInfo(AnnotationFile* annotationFile, Annotation* annotation, AnnotationSizingHandleTypeEnum::Enum sizingHandle, + int32_t polyLineCoordinateIndex, const float windowXYZ[3]) { m_annotationFile = annotationFile; m_annotation = annotation; m_sizingHandle = sizingHandle; + m_polyLineCoordinateIndex = polyLineCoordinateIndex; m_windowXYZ[0] = windowXYZ[0]; m_windowXYZ[1] = windowXYZ[1]; m_windowXYZ[2] = windowXYZ[2]; @@ -131,6 +142,8 @@ AnnotationSizingHandleTypeEnum::Enum m_sizingHandle; + int32_t m_polyLineCoordinateIndex; + double m_windowXYZ[3]; }; @@ -169,6 +182,7 @@ void drawAnnotationsInternal(const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, + std::vector& scaleBars, std::vector& viewportAnnotations, const Surface* surfaceDisplayed, const float sliceThickness); @@ -178,7 +192,7 @@ const Surface* surfaceDisplayed, float xyzOut[3]) const; - bool getAnnotationTwoDimShapeBounds(const AnnotationTwoDimensionalShape* annotation2D, + bool getAnnotationTwoDimShapeBounds(const AnnotationOneCoordinateShape* annotation2D, const float windowXYZ[3], float bottomLeftOut[3], float bottomRightOut[3], @@ -196,14 +210,18 @@ Annotation* annotation, const Surface* surfaceDisplayed); - bool drawTwoDimAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, - AnnotationTwoDimensionalShape* annotation, + bool drawOneCoordinateAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationOneCoordinateShape* annotation, const Surface* surfaceDisplayed); - bool drawOneDimAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, - AnnotationOneDimensionalShape* annotation, + bool drawTwoCoordinateAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationTwoCoordinateShape* annotation, const Surface* surfaceDisplayed); + bool drawMultiCoordinateAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationMultiCoordinateShape* annotation, + const Surface* surfaceDisplayed); + void drawColorBar(AnnotationFile* annotationFile, AnnotationColorBar* colorBar); @@ -211,6 +229,9 @@ AnnotationBox* box, const Surface* surfaceDisplayed); + bool drawBrowserTab(AnnotationFile* annotationFile, + AnnotationBrowserTab* browserTab); + bool drawBoxSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationBox* box, const float surfaceExtentZ, @@ -238,11 +259,23 @@ AnnotationOval* oval, const Surface* surfaceDisplayed); + bool drawPolyLine(AnnotationFile* annotationFile, + AnnotationPolyLine* polyLine, + const Surface* surfaceDisplayed); + + bool drawPolyLineSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationPolyLine* polyLine, + const Surface* surfaceDisplayed, + const float surfaceExtentZ); + bool drawOvalSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationOval* oval, const float surfaceExtentZ, const float vertexXYZ[3]); + void drawScaleBar(AnnotationFile* annotationFile, + AnnotationScaleBar* scaleBar); + bool drawText(AnnotationFile* annotationFile, AnnotationText* text, const Surface* surfaceDisplayed); @@ -281,9 +314,10 @@ Annotation* annotation, const float xyz[3], const float halfWidthHeight, - const float rotationAngle); + const float rotationAngle, + const int32_t polyLineCoordinateIndex); - void drawAnnotationTwoDimSizingHandles(AnnotationFile* annotationFile, + void drawAnnotationTwoDimShapeSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float bottomLeft[3], const float bottomRight[3], @@ -292,12 +326,17 @@ const float lineThickness, const float rotationAngle); - void drawAnnotationOneDimSizingHandles(AnnotationFile* annotationFile, + void drawAnnotationLineSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float firstPoint[3], const float secondPoint[3], const float lineThickness); + void drawAnnotationPolyLineSizingHandles(AnnotationFile* annotationFile, + Annotation* annotation, + const GraphicsPrimitive* primitive, + const float lineThickness); + bool isDrawnWithDepthTesting(const Annotation* annotation, const Surface* surface); @@ -329,7 +368,7 @@ std::vector& lineCoordinatesOut, std::vector& arrowCoordinatesOut) const; - void setSelectionBoxColor(); + void setSelectionBoxColor(const Annotation* annotation); void startOpenGLForDrawing(GLint* savedShadeModelOut, GLboolean* savedLightingEnabledOut); diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -38,6 +38,7 @@ #include "CaretLogger.h" #include "CaretPreferences.h" #include "ChartTwoCartesianAxis.h" +#include "ChartTwoCartesianOrientedAxes.h" #include "ChartTwoDataCartesian.h" #include "ChartTwoLineSeriesHistory.h" #include "ChartTwoMatrixDisplayProperties.h" @@ -47,15 +48,20 @@ #include "ChartableTwoFileDelegate.h" #include "ChartableTwoFileHistogramChart.h" #include "ChartableTwoFileMatrixChart.h" +#include "ChartableTwoFileLineLayerChart.h" #include "ChartableTwoFileLineSeriesChart.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "DeveloperFlagsEnum.h" +#include "EventManager.h" +#include "EventOpenGLObjectToWindowTransform.h" #include "FastStatistics.h" #include "GraphicsEngineDataOpenGL.h" #include "GraphicsPrimitiveV3f.h" #include "GraphicsPrimitiveV3fC4f.h" #include "GraphicsPrimitiveV3fC4ub.h" #include "GraphicsShape.h" +#include "GraphicsUtilitiesOpenGL.h" +#include "IdentificationManager.h" #include "IdentificationWithColor.h" #include "MathFunctions.h" #include "ModelChartTwo.h" @@ -65,6 +71,8 @@ #include "SelectionItemAnnotation.h" #include "SelectionItemChartTwoHistogram.h" #include "SelectionItemChartTwoLabel.h" +#include "SelectionItemChartTwoLineLayer.h" +#include "SelectionItemChartTwoLineLayerVerticalNearest.h" #include "SelectionItemChartTwoLineSeries.h" #include "SelectionItemChartTwoMatrix.h" #include "SelectionManager.h" @@ -158,6 +166,8 @@ m_selectionItemAnnotation = m_brain->getSelectionManager()->getAnnotationIdentification(); m_selectionItemHistogram = m_brain->getSelectionManager()->getChartTwoHistogramIdentification(); m_selectionItemLineSeries = m_brain->getSelectionManager()->getChartTwoLineSeriesIdentification(); + m_selectionItemLineLayer = m_brain->getSelectionManager()->getChartTwoLineLayerIdentification(); + m_selectionItemLineLayerVerticalNearest = m_brain->getSelectionManager()->getChartTwoLineLayerVerticalNearestIdentification(); m_selectionItemMatrix = m_brain->getSelectionManager()->getChartTwoMatrixIdentification(); m_selectionItemChartLabel = m_brain->getSelectionManager()->getChartTwoLabelIdentification(); @@ -216,6 +226,7 @@ bool drawHistogramFlag = true; bool drawLineSeriesFlag = true; + bool drawLineLayerFlag = true; bool drawMatrixFlag = true; /* @@ -231,6 +242,11 @@ && (! m_selectionItemChartLabel->isEnabledForSelection()) ) { drawHistogramFlag = false; } + if ( (! m_selectionItemLineLayer->isEnabledForSelection()) + && (! m_selectionItemAnnotation->isEnabledForSelection()) + && (! m_selectionItemChartLabel->isEnabledForSelection()) ) { + drawLineLayerFlag = false; + } if ( (! m_selectionItemLineSeries->isEnabledForSelection()) && (! m_selectionItemAnnotation->isEnabledForSelection()) && (! m_selectionItemChartLabel->isEnabledForSelection()) ) { @@ -275,17 +291,22 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: if (drawHistogramFlag) { - drawHistogramOrLineSeriesChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM); + drawHistogramOrLineChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + if (drawLineLayerFlag) { + drawHistogramOrLineChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER); } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: if (drawLineSeriesFlag) { - drawHistogramOrLineSeriesChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES); + drawHistogramOrLineChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES); } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: if (drawMatrixFlag) { - drawMatrixChart(); + drawHistogramOrLineChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX); } break; } @@ -305,9 +326,11 @@ * Type of chart to draw. */ void -BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const ChartTwoDataTypeEnum::Enum chartDataType) +BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineChart(const ChartTwoDataTypeEnum::Enum chartDataType) { bool drawHistogramFlag = false; + bool drawLineLayerFlag = false; + bool drawMatrixFlag = false; bool drawLineSeriesFlag = false; switch (chartDataType) { case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: @@ -317,12 +340,14 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: drawHistogramFlag = true; break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + drawLineLayerFlag = true; + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: drawLineSeriesFlag = true; break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: - CaretAssert(0); - return; + drawMatrixFlag = true; break; } @@ -340,13 +365,19 @@ const int32_t numberOfOverlays = m_chartOverlaySet->getNumberOfDisplayedOverlays(); CaretAssert(numberOfOverlays > 0); - const ChartTwoOverlay* topOverlay = m_chartOverlaySet->getOverlay(0); - const ChartTwoCompoundDataType cdt = topOverlay->getChartTwoCompoundDataType(); + CaretUsedInDebugCompileOnly(const ChartTwoOverlay* topOverlay = m_chartOverlaySet->getOverlay(0)); + CaretUsedInDebugCompileOnly(const ChartTwoCompoundDataType* cdt = topOverlay->getChartTwoCompoundDataType()); if (drawHistogramFlag) { - CaretAssert(cdt.getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM); + CaretAssert(cdt->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM); } else if (drawLineSeriesFlag) { - CaretAssert(cdt.getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES); + CaretAssert(cdt->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES); + } + else if (drawLineLayerFlag) { + CaretAssert(cdt->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER); + } + else if (drawMatrixFlag) { + CaretAssert(cdt->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX); } else { CaretAssert(0); @@ -354,18 +385,19 @@ std::vector> histogramDrawingInfo; std::deque lineSeriesChartsToDraw; + std::deque lineLayerChartsToDraw; + std::deque matrixChartsToDraw; /* * Get the histogram drawing information and overall extent */ - float xMinBottom = std::numeric_limits::max(); - float xMaxBottom = -std::numeric_limits::max(); - float xMinTop = std::numeric_limits::max(); - float xMaxTop = -std::numeric_limits::max(); - float yMinLeft = std::numeric_limits::max(); - float yMaxLeft = -std::numeric_limits::max(); - float yMinRight = std::numeric_limits::max(); - float yMaxRight = -std::numeric_limits::max(); + float xMinBottomTop(0.0), xMaxBottomTop(0.0); + m_chartOverlaySet->getHorizontalAxes()->getUserScaleMinimumMaximumValues(xMinBottomTop, + xMaxBottomTop); + + float yMinLeftRight(0.0), yMaxLeftRight(0.0); + m_chartOverlaySet->getVerticalAxes()->getUserScaleMinimumMaximumValues(yMinLeftRight, + yMaxLeftRight); /* * Find histograms or line-series charts for drawing @@ -397,7 +429,6 @@ AString errorMessage; histogramDrawingInfo.push_back(std::unique_ptr(new HistogramChartDrawingInfo(histogramChart, selectedIndex, - chartOverlay->getCartesianVerticalAxisLocation(), (chartOverlay->isAllMapsSupported() && chartOverlay->isAllMapsSelected())))); const Histogram* histogram = histogramChart->getHistogramForChartDrawing(selectedIndex, @@ -407,32 +438,6 @@ histogramDrawingInfo.pop_back(); continue; } - - float histogramMinX = 0.0, histogramMaxX = 0.0, histogramMaxY = 0.0; - histogram->getRangeAndMaxDisplayHeight(histogramMinX, histogramMaxX, histogramMaxY); - if (histogramMaxX > histogramMinX) { - xMinBottom = std::min(xMinBottom, histogramMinX); - xMaxBottom = std::max(xMaxBottom, histogramMaxX); - xMinTop = std::min(xMinTop, histogramMinX); - xMaxTop = std::max(xMaxTop, histogramMaxX); - - switch (chartOverlay->getCartesianVerticalAxisLocation()) { - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - CaretAssertMessage(0, "TOP axis not allowed for vertical axis"); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - yMinRight = std::min(yMinRight, 0.0f); - yMaxRight = std::max(yMaxRight, histogramMaxY); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - yMinLeft = std::min(yMinLeft, 0.0f); - yMaxLeft = std::max(yMaxLeft, histogramMaxY); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - CaretAssertMessage(0, "BOTTOM axis not allowed for vertical axis"); - break; - } - } } } @@ -446,31 +451,46 @@ if (data->isSelected()) { BoundingBox boundingBox; if (data->getBounds(boundingBox)) { - xMinBottom = std::min(xMinBottom, boundingBox.getMinX()); - xMaxBottom = std::max(xMaxBottom, boundingBox.getMaxX()); - xMinTop = std::min(xMinTop, boundingBox.getMinX()); - xMaxTop = std::max(xMaxTop, boundingBox.getMaxX()); - - switch (chartOverlay->getCartesianVerticalAxisLocation()) { - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - CaretAssertMessage(0, "TOP axis not allowed for vertical axis"); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - yMinRight = std::min(yMinRight, boundingBox.getMinY()); - yMaxRight = std::max(yMaxRight, boundingBox.getMaxY()); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - yMinLeft = std::min(yMinLeft, boundingBox.getMinY()); - yMaxLeft = std::max(yMaxLeft, boundingBox.getMaxY()); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - CaretAssertMessage(0, "BOTTOM axis not allowed for vertical axis"); - break; - } - lineSeriesChartsToDraw.push_back(LineSeriesChartDrawingInfo(lineSeriesChart, - data, - chartOverlay->getCartesianVerticalAxisLocation())); + data)); + } + } + } + } + + if (drawLineLayerFlag) { + ChartableTwoFileLineLayerChart* lineLayerChart = chartDelegate->getLineLayerCharting(); + if (lineLayerChart->isValid()) { + ChartTwoDataCartesian* data = chartOverlay->getLineLayerChartDisplayedCartesianData(); + CaretAssert(data); + if (data->isSelected()) { + BoundingBox boundingBox; + if (data->getBounds(boundingBox)) { + lineLayerChartsToDraw.push_back(LineLayerChartDrawingInfo(lineLayerChart, + data, + chartOverlay, + chartOverlay->getLineLayerColor(), + chartOverlay->getLineLayerLineWidth())); + } + } + } + } + + if (drawMatrixFlag) { + ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); + if (matrixChart->isValid()) { + ChartTwoMatrixTriangularViewingModeEnum::Enum triangleMode = chartOverlay->getMatrixTriangularViewingMode(); + GraphicsPrimitive* primitive = matrixChart->getMatrixChartingGraphicsPrimitive(triangleMode, + CiftiMappableDataFile::MatrixGridMode::FILLED_TEXTURE, + chartOverlay->getMatrixOpacity()); + if (primitive->isValid()) { + BoundingBox boundingBox; + if (primitive->getVertexBounds(boundingBox)) { + matrixChartsToDraw.push_back(MatrixChartDrawingInfo(matrixChart, + primitive, + chartOverlay, + triangleMode, + chartOverlay->getMatrixOpacity())); } } } @@ -483,31 +503,19 @@ * For Y, maximum value must be greater than or equal to minimum value since * all data points may be zero. */ - const bool xBottomValid = (xMinBottom < xMaxBottom); - const bool xTopValid = (xMinTop < xMaxTop); - const bool xValid = (xBottomValid || xTopValid); - const bool yLeftValid = (yMinLeft <= yMaxLeft); - const bool yRightValid = (yMinRight <= yMaxRight); - const bool yValid = (yLeftValid || yRightValid); - if (xValid && yValid) { + const bool xBottomTopValid = (xMinBottomTop < xMaxBottomTop); + const bool yLeftRightValid = (yMinLeftRight <= yMaxLeftRight); + if (xBottomTopValid && yLeftRightValid) { /* * Make invalid ranges zero */ - if ( ! yLeftValid) { - yMinLeft = 0.0; - yMaxLeft = 0.0; - } - if ( ! yRightValid) { - yMinRight = 0.0; - yMaxRight = 0.0; - } - if ( ! xTopValid) { - xMinTop = 0.0; - xMaxTop = 0.0; - } - if ( ! xBottomValid) { - xMinBottom = 0.0; - xMaxBottom = 0.0; + if ( ! yLeftRightValid) { + yMinLeftRight = 0.0; + yMaxLeftRight = 0.0; + } + if ( ! xBottomTopValid) { + xMinBottomTop = 0.0; + xMaxBottomTop = 0.0; } ChartTwoCartesianAxis* leftAxis = NULL; @@ -567,41 +575,45 @@ */ AxisDrawingInfo leftAxisInfo(m_textRenderer, m_viewport, - xMinBottom, - xMaxBottom, - yMinLeft, - yMaxLeft, + xMinBottomTop, + xMaxBottomTop, + yMinLeftRight, + yMaxLeftRight, ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT, + m_chartOverlaySet->getVerticalAxes(), leftAxis, m_chartOverlaySet->getAxisLabel(leftAxis), lineWidthPercentage); AxisDrawingInfo rightAxisInfo(m_textRenderer, m_viewport, - xMinTop, - xMaxTop, - yMinRight, - yMaxRight, + xMinBottomTop, + xMaxBottomTop, + yMinLeftRight, + yMaxLeftRight, ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT, + m_chartOverlaySet->getVerticalAxes(), rightAxis, m_chartOverlaySet->getAxisLabel(rightAxis), lineWidthPercentage); AxisDrawingInfo bottomAxisInfo(m_textRenderer, m_viewport, - xMinBottom, - xMaxBottom, - yMinLeft, - yMaxLeft, + xMinBottomTop, + xMaxBottomTop, + yMinLeftRight, + yMaxLeftRight, ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM, + m_chartOverlaySet->getHorizontalAxes(), bottomAxis, m_chartOverlaySet->getAxisLabel(bottomAxis), lineWidthPercentage); AxisDrawingInfo topAxisInfo(m_textRenderer, m_viewport, - xMinTop, - xMaxTop, - yMinLeft, - yMaxLeft, + xMinBottomTop, + xMaxBottomTop, + yMinLeftRight, + yMaxLeftRight, ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP, + m_chartOverlaySet->getHorizontalAxes(), topAxis, m_chartOverlaySet->getAxisLabel(topAxis), lineWidthPercentage); @@ -609,7 +621,7 @@ float topTitleHeight = titleInfo.m_titleHeight; const float topAxisHeight = topAxisInfo.m_axisHeight; if (titleInfo.m_titleDisplayedFlag) { - topAxisInfo.m_axisHeight = 0.0f; + /* nothing */ } else { topTitleHeight = 0.0f; @@ -681,30 +693,22 @@ m_chartOverlaySet, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, - foregroundRGBA, - yMinLeft, - yMaxLeft); + foregroundRGBA); rightAxisInfo.drawAxis(this, m_chartOverlaySet, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, - foregroundRGBA, - yMinRight, - yMaxRight); + foregroundRGBA); topAxisInfo.drawAxis(this, m_chartOverlaySet, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, - foregroundRGBA, - xMinTop, - xMaxTop); + foregroundRGBA); bottomAxisInfo.drawAxis(this, m_chartOverlaySet, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, - foregroundRGBA, - xMinBottom, - xMaxBottom); + foregroundRGBA); drawChartGraphicsBoxAndSetViewport(tabViewportX, tabViewportY, @@ -726,10 +730,10 @@ tabViewportHeight, m_chartOverlaySet->getAxisLineThickness(), topTitleHeight, - 0.0f, // bottomAxisHeight, - 0.0f, //topAxisHeight, - 0.0f, //leftAxisWidth, - 0.0f, //rightAxisWidth, + 0.0f, /* bottom axis height */ + 0.0f, /* top axis height */ + 0.0f, /* left axis width */ + 0.0f, /* right axis width */ true, /* draw the box */ chartGraphicsDrawingViewport); } @@ -742,17 +746,11 @@ * slightly larger than the minimum value. */ const float smallRange = 0.01; - if (xMinBottom >= xMaxBottom) { - xMaxBottom = xMinBottom + smallRange; - } - if (xMinTop >= xMaxTop) { - xMaxTop = xMinBottom + smallRange; + if (xMinBottomTop >= xMaxBottomTop) { + xMaxBottomTop = xMinBottomTop + smallRange; } - if (yMinLeft >= yMaxLeft) { - yMaxLeft = yMinLeft + smallRange; - } - if (yMinRight >= yMaxRight) { - yMaxRight = yMinRight + smallRange; + if (yMinLeftRight >= yMaxLeftRight) { + yMaxLeftRight = yMinLeftRight + smallRange; } glViewport(chartGraphicsDrawingViewport[0], @@ -776,33 +774,14 @@ if (drawBarsFlag || drawEnvelopeFlag) { - bool leftVerticalAxisFlag = true; - switch (drawInfo->m_verticalAxisLocation) { - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - leftVerticalAxisFlag = false; - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - break; - } glMatrixMode(GL_PROJECTION); glLoadIdentity(); - CaretAssert(xMinBottom < xMaxBottom); - if (leftVerticalAxisFlag) { - glOrtho(xMinBottom, xMaxBottom, - yMinLeft, yMaxLeft, - -10.0, 10.0); - } - else { - glOrtho(xMinBottom, xMaxBottom, - yMinRight, yMaxRight, - -10.0, 10.0); - } - + CaretAssert(xMinBottomTop < xMaxBottomTop); + glOrtho(xMinBottomTop, xMaxBottomTop, + yMinLeftRight, yMaxLeftRight, + -10.0, 10.0); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -908,37 +887,17 @@ } } } + if (drawLineSeriesFlag) { - for (const auto lineChart : lineSeriesChartsToDraw) { - bool leftVerticalAxisFlag = true; - switch (lineChart.m_verticalAxisLocation) { - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - leftVerticalAxisFlag = false; - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - break; - } - + for (const auto& lineChart : lineSeriesChartsToDraw) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - CaretAssert(xMinBottom < xMaxBottom); - if (leftVerticalAxisFlag) { - CaretAssert(yMinLeft < yMaxLeft); - glOrtho(xMinBottom, xMaxBottom, - yMinLeft, yMaxLeft, - -10.0, 10.0); - } - else { - CaretAssert(yMinRight < yMaxRight); - glOrtho(xMinBottom, xMaxBottom, - yMinRight, yMaxRight, - -10.0, 10.0); - } - + CaretAssert(xMinBottomTop < xMaxBottomTop); + CaretAssert(yMinLeftRight < yMaxLeftRight); + glOrtho(xMinBottomTop, xMaxBottomTop, + yMinLeftRight, yMaxLeftRight, + -10.0, 10.0); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -995,230 +954,453 @@ } } } -} -/** - * Draw a matrix chart. - */ -void -BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChart() -{ - const int32_t tabViewportX = m_viewport[0]; - const int32_t tabViewportY = m_viewport[1]; - const int32_t tabViewportWidth = m_viewport[2]; - const int32_t tabViewportHeight = m_viewport[3]; - - int32_t chartGraphicsDrawingViewport[4] = { - tabViewportX, - tabViewportY, - tabViewportWidth, - tabViewportHeight - }; - - /* - * Estimate size of title - */ - float titleHeight = 0.0f; - ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); - TitleDrawingInfo titleInfo(m_textRenderer, - m_viewport, - chartTitle); - - float bottomMargin = 3.0f; - float topMargin = 3.0f; - float leftMargin = 3.0f; - float rightMargin = 3.0f; - if (titleInfo.m_titleDisplayedFlag) { - topMargin = 0.0f; - titleHeight = titleInfo.m_titleHeight; - } - - titleInfo.setTitleViewport(leftMargin, - rightMargin); - - glViewport(tabViewportX, - tabViewportY, - tabViewportWidth, - tabViewportHeight); - - const float foregroundRGBA[4] = { - m_fixedPipelineDrawing->m_foregroundColorFloat[0], - m_fixedPipelineDrawing->m_foregroundColorFloat[1], - m_fixedPipelineDrawing->m_foregroundColorFloat[2], - 1.0f - }; - titleInfo.drawTitle(foregroundRGBA); - - drawChartGraphicsBoxAndSetViewport(tabViewportX, - tabViewportY, - tabViewportWidth, - tabViewportHeight, - m_chartOverlaySet->getAxisLineThickness(), - titleHeight, - bottomMargin, - topMargin, - leftMargin, - rightMargin, - false, /* do not draw box */ - chartGraphicsDrawingViewport); - - glViewport(chartGraphicsDrawingViewport[0], - chartGraphicsDrawingViewport[1], - chartGraphicsDrawingViewport[2], - chartGraphicsDrawingViewport[3]); - - bool applyTransformationsFlag = true; - float cellWidth = 1.0; - float cellHeight = 1.0; - - /* - * Setup width/height of area in which matrix is drawn with a - * small margin along all of the edges - */ - float margin = 10.0; - if ((m_viewport[2] < (margin * 3.0)) - || (m_viewport[3] < (margin * 3.0))) { - margin = 0.0; - } - const float graphicsWidth = chartGraphicsDrawingViewport[2]; - const float graphicsHeight = chartGraphicsDrawingViewport[3]; - - /* - * First overlay is ALWAYS ON and since all matrices must have - * same number of rows/columns, use first matrix for rows/columns - */ - const int32_t numberOfOverlays = m_chartOverlaySet->getNumberOfDisplayedOverlays(); - CaretAssert(numberOfOverlays > 0); - const ChartTwoOverlay* topOverlay = m_chartOverlaySet->getOverlay(0); - const ChartTwoCompoundDataType cdt = topOverlay->getChartTwoCompoundDataType(); - CaretAssert(cdt.getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX); - const int32_t numberOfRows = cdt.getMatrixNumberOfRows(); - const int32_t numberOfCols = cdt.getMatrixNumberOfColumns(); - if ((numberOfRows > 0) - && (numberOfCols > 0)) { - cellWidth = graphicsWidth / numberOfCols; - cellHeight = graphicsHeight / numberOfRows; - } - else { - return; - } - - /* - * Set the width and neight of each matrix cell. - */ - const ChartTwoMatrixDisplayProperties* matrixProperties = m_browserTabContent->getChartTwoMatrixDisplayProperties(); - CaretAssert(matrixProperties); - const float cellWidthZoom = matrixProperties->getCellPercentageZoomWidth() / 100.0; - const float cellHeightZoom = matrixProperties->getCellPercentageZoomHeight() / 100.0; - if ((cellWidthZoom > 0.0) - && (cellHeightZoom > 0.0)) { - cellWidth *= cellWidthZoom; - cellHeight *= cellHeightZoom; - } - - /* - * Set the coordinates for the area in which the matrix is drawn. - */ - margin = 0.0; - const float xMin = -margin; - const float xMax = graphicsWidth + margin; - const float yMin = -margin; - const float yMax = graphicsHeight + margin; - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(xMin, xMax, - yMin, yMax, - -1.0, 1.0); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - if (applyTransformationsFlag) { - glTranslatef(m_translation[0], - m_translation[1], - 0.0); - - const float chartWidth = cellWidth * numberOfCols; - const float chartHeight = cellHeight * numberOfRows; - const float halfWidth = chartWidth / 2.0; - const float halfHeight = chartHeight / 2.0; - glTranslatef(halfWidth, - halfHeight, - 0.0); - glScalef(m_zooming, - m_zooming, - 1.0); - glTranslatef(-halfWidth, - -halfHeight, - 0.0); - } - - /* - * Save the transformation matrices and the viewport - * If there is more than one line chart, this code will be executed - * several times but since the top overlay is drawn last, the contents - * of the top overlay will be used. - */ - updateViewportContentForCharting(chartGraphicsDrawingViewport); - - std::vector rowColumnHighlighting; - - for (int32_t iOverlay = (numberOfOverlays - 1); iOverlay >= 0; iOverlay--) { - ChartTwoOverlay* chartOverlay = m_chartOverlaySet->getOverlay(iOverlay); - if ( ! chartOverlay->isEnabled()) { - continue; - } + if (drawLineLayerFlag) { - CaretMappableDataFile* mapFile = NULL; - ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; - int32_t selectedIndex = -1; - chartOverlay->getSelectionData(mapFile, - selectedIndexType, - selectedIndex); - if (mapFile == NULL) { - continue; - } + /* + * Need to draw tooltips last so they are not under other chart lines + */ + std::vector> textToolTips; - const ChartableTwoFileMatrixChart* matrixChart = mapFile->getChartingDelegate()->getMatrixCharting(); - int32_t overlayRows = 0; - int32_t overlayColumns = 0; - matrixChart->getMatrixDimensions(overlayRows, - overlayColumns); + for (const auto& lineChart : lineLayerChartsToDraw) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + const float xMin(xMinBottomTop); + const float xMax(xMaxBottomTop); + const float yMin = yMinLeftRight; + const float yMax = yMaxLeftRight; + CaretAssert(xMin <= xMax); + CaretAssert(yMin <= yMax); + glOrtho(xMin, xMax, + yMin, yMax, + -10.0, 10.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + bool applyTransformationsFlag = false; + if (applyTransformationsFlag) { + glTranslatef(m_translation[0], + m_translation[1], + 0.0f); + + const float chartWidth = chartGraphicsDrawingViewport[2]; + const float chartHeight = chartGraphicsDrawingViewport[3]; + const float halfWidth = chartWidth / 2.0f; + const float halfHeight = chartHeight / 2.0f; + glTranslatef(halfWidth, + halfHeight, + 0.0f); + glScalef(m_zooming, + m_zooming, + 1.0f); + glTranslatef(-halfWidth, + -halfHeight, + 0.0f); + } + + if (m_identificationModeFlag) { + /* + * First find line nearest to mouse in vertical distance + */ + bool performNearestVerticalIdentificationFlag(false); + switch (lineChart.m_chartTwoOverlay->getLineChartActiveMode()) { + case ChartTwoOverlayActiveModeEnum::ACTIVE: + performNearestVerticalIdentificationFlag = true; + break; + case ChartTwoOverlayActiveModeEnum::OFF: + break; + case ChartTwoOverlayActiveModeEnum::ON: + performNearestVerticalIdentificationFlag = true; + break; + } + if (performNearestVerticalIdentificationFlag) { + /* + * Identify point on line that is neartest to mouse + * in a vertical direction + */ + EventOpenGLObjectToWindowTransform windowTransform(EventOpenGLObjectToWindowTransform::SpaceType::MODEL); + EventManager::get()->sendEvent(windowTransform.getPointer()); + if (windowTransform.isValid()) { + const float mouseWindowXYZ[3] { + static_cast(m_fixedPipelineDrawing->mouseX), + static_cast(m_fixedPipelineDrawing->mouseY), + 0.0f + }; + float mouseModelXYZ[3]; + windowTransform.inverseTransformPoint(mouseWindowXYZ, + mouseModelXYZ); + + if ((mouseModelXYZ[0] >= xMin) + && (mouseModelXYZ[0] <= xMax) + && (mouseModelXYZ[1] >= yMin) + && (mouseModelXYZ[1] <= yMax)) { + float distance(0.0); + int32_t pointIndex(-1); + float chartXYZ[3]; + if (lineChart.m_chartTwoCartesianData->getVerticalDistanceToXY(mouseModelXYZ[0], + mouseModelXYZ[1], + distance, + pointIndex, + chartXYZ)) { + bool debugFlag(false); + if (debugFlag) { + std::cout << "Distance: " << distance << std::endl; + std::cout << "Chart XYZ: " << AString::fromNumbers(chartXYZ, 3, ",") << std::endl; + float chartWindowXYZ[3]; + windowTransform.transformPoint(chartXYZ, chartWindowXYZ); + std::cout << " Chart Window XYZ: " << AString::fromNumbers(chartWindowXYZ, 3, ",") << std::endl; + std::cout << " Window XYZ: " << AString::fromNumbers(mouseWindowXYZ, 3, ",") << std::endl; + std::cout << " Window Distance: " << MathFunctions::distance3D(chartWindowXYZ, mouseWindowXYZ) << std::endl; + float pointModelXYZ[3]; + lineChart.m_chartTwoCartesianData->getPointXYZ(pointIndex, pointModelXYZ); + float pointWindowXYZ[3]; + windowTransform.transformPoint(pointModelXYZ, pointWindowXYZ); + std::cout << " Point Distance: " << MathFunctions::distance3D(pointWindowXYZ, mouseWindowXYZ) << std::endl; + } + + + m_selectionItemLineLayerVerticalNearest->setLineLayerChart(const_cast(lineChart.m_lineLayerChart), + const_cast(lineChart.m_chartTwoCartesianData), + const_cast(lineChart.m_chartTwoOverlay), + distance, + pointIndex); + } + } + else { + /* + * Outside chart coordinate bounds + */ + m_selectionItemLineLayerVerticalNearest->setOutsideChartBound(true); + } + } + } + else { + /* + * Identify line segment point under mouse + */ + int32_t primitiveIndex = -1; + float primitiveDepth = 0.0f; + + /* + * Increase width as thin lines are difficult to select + */ + const float minWidth(8.0); + const float lineWidthPercentage(std::max(lineChart.m_lineWidth, minWidth)); + lineChart.m_chartTwoCartesianData->setLineWidth(lineWidthPercentage); + GraphicsEngineDataOpenGL::drawWithSelection(lineChart.m_chartTwoCartesianData->getGraphicsPrimitive(), + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + primitiveIndex, + primitiveDepth); + + if (primitiveIndex >= 0) { + if (m_selectionItemLineLayer->isOtherScreenDepthCloserToViewer(primitiveDepth)) { + m_selectionItemLineLayer->setLineLayerChart(const_cast(lineChart.m_lineLayerChart), + const_cast(lineChart.m_chartTwoCartesianData), + const_cast(lineChart.m_chartTwoOverlay), + primitiveIndex); + m_selectionItemLineLayer->setScreenDepth(primitiveDepth); + } + } + } + } + else { + IdentificationManager* idManager = m_brain->getIdentificationManager(); + const float symbolSize = idManager->getChartLineLayerSymbolSize(); + const float textSize = idManager->getChartLineLayerToolTipTextSize(); + + if (lineChart.m_lineChartColor != lineChart.m_chartTwoCartesianData->getColor()) { + lineChart.m_chartTwoCartesianData->setColor(lineChart.m_lineChartColor); + } + + /* + * Prevent zero line width that causes OpenGL error + */ + const float lineWidth((lineChart.m_lineWidth > 0.0) + ? lineChart.m_lineWidth + : 0.1); + lineChart.m_chartTwoCartesianData->setLineWidth(lineWidth); + + GraphicsEngineDataOpenGL::draw(lineChart.m_chartTwoCartesianData->getGraphicsPrimitive()); + + bool showCircleFlag(false); + bool showRingFlag(false); + switch (lineChart.m_chartTwoOverlay->getLineChartActiveMode()) { + case ChartTwoOverlayActiveModeEnum::ACTIVE: + showCircleFlag = true; + break; + case ChartTwoOverlayActiveModeEnum::OFF: + break; + case ChartTwoOverlayActiveModeEnum::ON: + showRingFlag = true; + break; + } + if (showCircleFlag + | showRingFlag) { + std::array xyz; + if (lineChart.m_chartTwoOverlay->getSelectedLineChartPointXYZ(xyz)) { + const uint8_t foregroundRGBA[4] = { + m_fixedPipelineDrawing->m_foregroundColorByte[0], + m_fixedPipelineDrawing->m_foregroundColorByte[1], + m_fixedPipelineDrawing->m_foregroundColorByte[2], + 255 + }; + + /* + * May have NaN of Inf in the Y-component so + * play Y at middle of screen. Need to leave + * the NaN/Inf in the xyz for conversion to text + * so that user sees NaN/Inf + */ + auto xyzSymbol(xyz); + if ( ! MathFunctions::isNumeric(xyz[1])) { + xyzSymbol[1] = (yMinLeftRight + yMaxLeftRight) / 2.0; + } + + std::array windowXYZ; + if (showCircleFlag) { + GraphicsShape::drawCircleFilledPercentViewportHeight(xyzSymbol.data(), + foregroundRGBA, + symbolSize, + &windowXYZ); + } + else if (showRingFlag) { + GraphicsShape::drawRingPercentViewportHeight(xyzSymbol.data(), + foregroundRGBA, + symbolSize * 0.65, /* inner diameter */ + symbolSize, /* diameter */ + &windowXYZ); + } + else { + CaretAssertMessage(0, "Does new shape need to be drawn?"); + } + { + const QString info("Index: " + + QString::number(lineChart.m_chartTwoOverlay->getSelectedLineChartPointIndex()) + + "\nX: " + + QString::number(xyz[0], 'f') + + "\nY: " + + QString::number(xyz[1], 'f')); + AnnotationPercentSizeText text(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); + text.setText(info); + text.setFontPercentViewportSize(textSize); + text.setBoldStyleEnabled(true); + text.setTextColor(CaretColorEnum::BLACK); + text.setBackgroundColor(CaretColorEnum::CUSTOM); + const uint8_t backgroundColor[4] { 200, 200, 200, 255 }; + text.setCustomBackgroundColor(backgroundColor); + text.setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); + text.getCoordinate()->setXYZ(windowXYZ.data()); + + const float vpWidth(chartGraphicsDrawingViewport[2]); + const float vpHeight(chartGraphicsDrawingViewport[3]); + float vpX(windowXYZ[0] - chartGraphicsDrawingViewport[0]); + float vpY(windowXYZ[1] - chartGraphicsDrawingViewport[1]); + const float offsetXY(10.0); + + double textWidth(0.0); + double textHeight(0.0); + m_textRenderer->getTextWidthHeightInPixels(text,BrainOpenGLTextRenderInterface::DrawingFlags(), + vpWidth, vpHeight, + textWidth, textHeight); + const double halfTextWidth(textWidth / 2.0); + + text.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + switch (lineChart.m_chartTwoOverlay->getSelectedLineChartTextOffset()) { + case CardinalDirectionEnum::AUTO: + if (vpX > (vpWidth / 2.0)) { + /* + * Place text to the left of the selected point. + * Need additional offset of text width since the text is + * aligned on the left. Right alignment looks bad when + * the lines of text are different lengths and aligned on right. + */ + vpX -= (offsetXY + textWidth); + } + else { + /* + * Place text to the right of the selected point + */ + vpX += offsetXY; + } + if (vpY > (vpHeight / 2.0)) { + /* + * Place text below selected point + */ + vpY -= offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); + } + else { + /* + * Place text above selected point + */ + vpY += offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + } + break; + case CardinalDirectionEnum::EAST: + vpX += offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); + break; + case CardinalDirectionEnum::NORTH: + vpX -= halfTextWidth; + vpY += offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + break; + case CardinalDirectionEnum::NORTHEAST: + vpX += offsetXY; + vpY += offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + break; + case CardinalDirectionEnum::NORTHWEST: + vpX -= (offsetXY + textWidth); + vpY += offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + break; + case CardinalDirectionEnum::SOUTH: + vpX -= (halfTextWidth); + vpY -= offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); + break; + case CardinalDirectionEnum::SOUTHEAST: + vpX += offsetXY; + vpY -= offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); + break; + case CardinalDirectionEnum::SOUTHWEST: + vpX -= (offsetXY + textWidth); + vpY -= offsetXY; + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); + break; + case CardinalDirectionEnum::WEST: + vpX -= (offsetXY + textWidth); + text.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); + break; + } + + const float vpZ(0.0); + textToolTips.push_back(std::make_tuple(vpX, vpY, vpZ, text)); + } + } + } + } + + /* + * Save the transformation matrices and the viewport + * If there is more than one line chart, this code will be executed + * several times but since the top overlay is drawn last, the contents + * of the top overlay will be used. + */ + updateViewportContentForCharting(chartGraphicsDrawingViewport); + } /* - * All matrices must have same rows/columns + * Draw tooltips AFTER lines to that the tooltips are + * not behind any lines */ - if ((overlayRows == numberOfRows) - && (overlayColumns == numberOfCols)) { - drawMatrixChartContent(matrixChart, - chartOverlay->getMatrixTriangularViewingMode(), - cellWidth, - cellHeight, - m_zooming, - rowColumnHighlighting); + for (auto tt : textToolTips) { + m_textRenderer->drawTextAtViewportCoords(std::get<0>(tt), + std::get<1>(tt), + std::get<2>(tt), + std::get<3>(tt), + BrainOpenGLTextRenderInterface::DrawingFlags()); } } - /* - * Row/column highlighting must be drawn after matrices to that - * the highlights are over any and all displayed matrices - */ - const int32_t numHighlights = static_cast(rowColumnHighlighting.size()); - if (numHighlights > 0) { - for (int32_t ctr = 0; ctr < numHighlights; ctr++) { - MatrixRowColumnHighight* mrch = rowColumnHighlighting[ctr]; - CaretAssert(mrch); + if (drawMatrixFlag) { + std::vector rowColumnHighlighting; + + /* + * Blending (alpha/opacity) is not allowed on the bottom-most + * layer since there is nothing under it for blending + */ + bool allowBlendingFlag(false); + + for (const auto& matrixChart : matrixChartsToDraw) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + const float xMin(xMinBottomTop); + const float xMax(xMaxBottomTop); + const float yMin = yMinLeftRight; + const float yMax = yMaxLeftRight; + CaretAssert(xMin <= xMax); + CaretAssert(yMin <= yMax); + glOrtho(xMin, xMax, + yMin, yMax, + -10.0, 10.0); - glPushMatrix(); - glLoadMatrixf(mrch->m_modelViewMatrix); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - drawPrimitivePrivate(mrch->m_graphicsPrimitive.get()); - delete mrch; + drawMatrixChartContent(matrixChart.m_matrixChart, + matrixChart.m_triangularMode, + matrixChart.m_opacity, + rowColumnHighlighting, + allowBlendingFlag); + allowBlendingFlag = true; - glPopMatrix(); + /* + * Save the transformation matrices and the viewport + * If there is more than one line chart, this code will be executed + * several times but since the top overlay is drawn last, the contents + * of the top overlay will be used. + */ + updateViewportContentForCharting(chartGraphicsDrawingViewport); + } + + /* + * Row/column highlighting must be drawn after matrices to that + * the highlights are over any and all displayed matrices + */ + const int32_t numHighlights = static_cast(rowColumnHighlighting.size()); + if (numHighlights > 0) { + for (int32_t ctr = 0; ctr < numHighlights; ctr++) { + MatrixRowColumnHighight* mrch = rowColumnHighlighting[ctr]; + CaretAssert(mrch); + + glPushMatrix(); + glLoadMatrixf(mrch->m_modelViewMatrix); + + drawPrimitivePrivate(mrch->m_graphicsPrimitive.get()); + delete mrch; + + glPopMatrix(); + } + rowColumnHighlighting.clear(); + } + } + + if ((xMinBottomTop < xMaxBottomTop) + && (yMinLeftRight < yMaxLeftRight)) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + const float xMin(xMinBottomTop); + const float xMax(xMaxBottomTop); + const float yMin = yMinLeftRight; + const float yMax = yMaxLeftRight; + CaretAssert(xMin <= xMax); + CaretAssert(yMin <= yMax); + glOrtho(xMin, xMax, + yMin, yMax, + -10.0, 10.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + if (m_chartOverlaySet->getChartSelectionBounds(minX, minY, maxX, maxY)) { + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN, + this->m_fixedPipelineDrawing->m_foregroundColorFloat)); + primitive->addVertex(minX, minY); + primitive->addVertex(maxX, minY); + primitive->addVertex(maxX, maxY); + primitive->addVertex(minX, maxY); + primitive->setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, 1.5); + drawPrimitivePrivate(primitive.get()); } - rowColumnHighlighting.clear(); } } @@ -1229,23 +1411,33 @@ * Matrix chart that is drawn. * @param chartViewingType * Type of chart viewing. - * @param cellWidth - * Width of cell. - * @param cellHeight - * Height of cell. - * @param zooming - * Current zooming. + * @param opacity + * Opacity for matrix drawing. + * @param rowColumnHighlightingOut + * Current Output with row/column highlighting. + * @param blendingEnabled + * Allow blending. */ void BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChartContent(const ChartableTwoFileMatrixChart* matrixChart, const ChartTwoMatrixTriangularViewingModeEnum::Enum chartViewingType, - const float cellWidth, - const float cellHeight, - const float /*zooming*/, - std::vector& rowColumnHighlightingOut) -{ - GraphicsPrimitiveV3fC4f* matrixPrimitive = matrixChart->getMatrixChartingGraphicsPrimitive(chartViewingType, - CiftiMappableDataFile::MatrixGridMode::FILLED); + const float opacity, + std::vector& rowColumnHighlightingOut, + const bool blendingEnabled) +{ + GraphicsPrimitive* matrixPrimitive(NULL); + const bool useTextureFlag(true); + if (useTextureFlag) { + matrixPrimitive = matrixChart->getMatrixChartingGraphicsPrimitive(chartViewingType, + CiftiMappableDataFile::MatrixGridMode::FILLED_TEXTURE, + opacity); + } + else { + matrixPrimitive = matrixChart->getMatrixChartingGraphicsPrimitive(chartViewingType, + CiftiMappableDataFile::MatrixGridMode::FILLED_TRIANGLES, + opacity); + } + if (matrixPrimitive == NULL) { return; } @@ -1255,38 +1447,42 @@ } glPushMatrix(); - glScalef(cellWidth, cellHeight, 1.0); /* - * Enable alpha blending so voxels that are not drawn from higher layers - * allow voxels from lower layers to be seen. + * Blending is disabled for the bottom-most + * layer since there is nothing to blend with. If allowed, + * the bottom layer would blend with the background. */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (blendingEnabled) { + BrainOpenGLFixedPipeline::setupBlending(BrainOpenGLFixedPipeline::BlendDataType::CHART_TWO_MATRIX); + } + else { + glDisable(GL_BLEND); + } if (m_identificationModeFlag) { - int32_t primitiveIndex = -1; - float primitiveDepth = 0.0; - GraphicsEngineDataOpenGL::drawWithSelection(matrixPrimitive, - m_fixedPipelineDrawing->mouseX, - m_fixedPipelineDrawing->mouseY, - primitiveIndex, - primitiveDepth); - if (primitiveIndex >= 0) { - /* - * Two triangles per cell so divided the primitive index by two - */ - CaretAssert(matrixPrimitive->getPrimitiveType() == GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES); - primitiveIndex /= 2; + EventOpenGLObjectToWindowTransform transformEvent(EventOpenGLObjectToWindowTransform::SpaceType::MODEL); + EventManager::get()->sendEvent(transformEvent.getPointer()); + if (transformEvent.isValid()) { + const float windowPos[3] { + static_cast(m_fixedPipelineDrawing->mouseX), + static_cast(m_fixedPipelineDrawing->mouseY), + 0.0f + }; + float modelPos[3]; + transformEvent.inverseTransformPoint(windowPos, + modelPos); - int32_t numberOfRows = 0; - int32_t numberOfColumns = 0; + int32_t numberOfRows(0); + int32_t numberOfColumns(0); matrixChart->getMatrixDimensions(numberOfRows, numberOfColumns); + const int32_t rowIndex(numberOfRows - modelPos[1]); + const int32_t colIndex(modelPos[0]); - const int32_t rowIndex = primitiveIndex / numberOfColumns; - const int32_t colIndex = primitiveIndex % numberOfColumns; - - if (m_selectionItemMatrix->isOtherScreenDepthCloserToViewer(primitiveDepth)) { + if ((rowIndex >= 0) + && (rowIndex < numberOfRows) + && (colIndex >= 0) + && (colIndex < numberOfColumns)) { m_selectionItemMatrix->setMatrixChart(const_cast(matrixChart), rowIndex, colIndex); @@ -1300,8 +1496,9 @@ CaretAssert(matrixProperties); if (matrixProperties->isGridLinesDisplayed()) { - GraphicsPrimitiveV3fC4f* matrixGridPrimitive = matrixChart->getMatrixChartingGraphicsPrimitive(chartViewingType, - CiftiMappableDataFile::MatrixGridMode::OUTLINE); + GraphicsPrimitive* matrixGridPrimitive = matrixChart->getMatrixChartingGraphicsPrimitive(chartViewingType, + CiftiMappableDataFile::MatrixGridMode::OUTLINE, + opacity); drawPrimitivePrivate(matrixGridPrimitive); } @@ -1335,7 +1532,7 @@ * Disabled by WB-741 */ const bool limitSelectionToTriangularFlag = false; - const float lineWidthPercentageHeight = 1.0f; + const float lineWidthPercentageHeight(0.5f); for (auto rowIndex : selectedRowIndices) { float minX = 0; @@ -1394,8 +1591,6 @@ } } -// const float highlightLineWidth = std::max(((zooming) * 0.20), 3.0); - GraphicsPrimitiveV3fC4f* columnOutlineData = GraphicsPrimitiveV3fC4f::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN); columnOutlineData->reserveForNumberOfVertices(4); columnOutlineData->addVertex(minX, minY, highlightRGBA); @@ -1485,9 +1680,6 @@ if (pixelsWidthOrHeight < s_minimumLineWidthOpenGL) { pixelsWidthOrHeight = s_minimumLineWidthOpenGL; } -// if (pixelsWidthOrHeight > s_maximumLineWidthOpenGL) { -// pixelsWidthOrHeight = s_maximumLineWidthOpenGL; -// } return pixelsWidthOrHeight; } @@ -1746,19 +1938,15 @@ * The file's histogram charting * @param mapIndex * Index of the map for which histogram is displayed. - * @param verticalAxisLocation - * Location of vertical axis for the histogram * @param allMapsSelected * True if ALL MAPS selected for histogram, else false. */ BrainOpenGLChartTwoDrawingFixedPipeline::HistogramChartDrawingInfo::HistogramChartDrawingInfo(ChartableTwoFileHistogramChart* histogramChart, int32_t mapIndex, - ChartAxisLocationEnum::Enum verticalAxisLocation, const bool allMapsSelected) : m_histogramChart(histogramChart), m_mapIndex(mapIndex), -m_verticalAxisLocation(verticalAxisLocation), m_allMapsSelected(allMapsSelected) { } @@ -1787,6 +1975,8 @@ * Maximum Y-data value. * @param axisLocation * Location of axis as 'axis' may be NULL. + * @param orientedAxes + * The oriented axes parent of the 'axis' * @param axis * Axis being setup. * @param labelText @@ -1801,10 +1991,12 @@ const float dataMinY, const float dataMaxY, const ChartAxisLocationEnum::Enum axisLocation, + const ChartTwoCartesianOrientedAxes* orientedAxes, const ChartTwoCartesianAxis* axis, const AString& labelText, const float lineWidthPercentage) : m_axisLocation(axisLocation), +m_orientedAxes(orientedAxes), m_axis(axis), m_textRenderer(textRenderer), m_tabViewportWidth(tabViewport[2]), @@ -1831,8 +2023,7 @@ m_axisDisplayedFlag = false; if (m_axis != NULL) { CaretAssert(m_axis->getAxisLocation() == axisLocation); - m_axisDisplayedFlag = (axis->isEnabledByChart() - && axis->isDisplayedByUser()); + m_axisDisplayedFlag = axis->isDisplayedByUser(); } if ( ! m_axisDisplayedFlag) { /* @@ -1858,16 +2049,18 @@ * This is necessary so that the correct axis minimum and * maximum values (m_axisMinimumValue and m_axisMaximumValue). */ - if (m_axis != NULL) { + if ((m_axis != NULL) + && (m_orientedAxes != NULL)) { std::vector scaleValuePositions; std::vector scaleValuesText; - m_axis->getScaleValuesAndOffsets(dataMinimumValue, - dataMaximumValue, - 1.0, - m_axisMinimumValue, - m_axisMaximumValue, - scaleValuePositions, - scaleValuesText); + m_orientedAxes->getScaleValuesAndOffsets(m_axis, + dataMinimumValue, + dataMaximumValue, + 1.0, + m_axisMinimumValue, + m_axisMaximumValue, + scaleValuePositions, + scaleValuesText); } return; @@ -1907,7 +2100,6 @@ if (m_axis->isShowTickmarks()) { const float tickLengthPercentage = 1.0f; m_tickLength = convertPercentageOfViewportToOpenGLLineWidth(tickLengthPercentage, m_tabViewportHeight); - //m_tickLength = m_lineDrawingWidth; // * 2.0f; } /* @@ -2051,14 +2243,15 @@ const float axisLength = 1.0; std::vector scaleValuePositions; std::vector scaleValuesText; - m_axis->getScaleValuesAndOffsets(dataMinimumDataValue, - dataMaximumDataValue, - axisLength, - m_axisMinimumValue, - m_axisMaximumValue, - scaleValuePositions, - scaleValuesText); - + m_orientedAxes->getScaleValuesAndOffsets(m_axis, + dataMinimumDataValue, + dataMaximumDataValue, + axisLength, + m_axisMinimumValue, + m_axisMaximumValue, + scaleValuePositions, + scaleValuesText); + /* * For each numeric value, create a text annotation and determine the * width and height of the text. Also, set either the X or Y-coordinate @@ -2067,8 +2260,6 @@ */ CaretAssert(scaleValuePositions.size() == scaleValuesText.size()); const int32_t numValues = static_cast(scaleValuesText.size()); - const int32_t firstIndex = 0; - const int32_t lastIndex = (numValues - 1); for (int32_t i = 0; i < numValues; i++) { AnnotationPercentSizeText* text = new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); @@ -2086,109 +2277,120 @@ xyz[0] = scaleValuePositions[i]; if (rotateNumericFlag) { horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; - if (i == firstIndex) { - verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; - } - else if (i == lastIndex) { - verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; - } - else { - verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; - } +// if (i == firstIndex) { +// verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; +// } +// else if (i == lastIndex) { +// verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; +// } +// else { +// verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; +// } + verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; } else { verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; - if (i == firstIndex) { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; - } - else if (i == lastIndex) { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; - } - else { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; - } +// if (i == firstIndex) { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; +// } +// else if (i == lastIndex) { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; +// } +// else { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; +// } + horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; } break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: xyz[0] = scaleValuePositions[i]; if (rotateNumericFlag) { horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; - if (i == firstIndex) { - verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; - } - else if (i == lastIndex) { - verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; - } - else { - verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; - } +// if (i == firstIndex) { +// verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; +// } +// else if (i == lastIndex) { +// verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; +// } +// else { +// verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; +// } + verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; } else { verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; - if (i == firstIndex) { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; - } - else if (i == lastIndex) { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; - } - else { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; - } +// if (i == firstIndex) { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; +// } +// else if (i == lastIndex) { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; +// } +// else { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; +// } + horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; } break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: xyz[1] = scaleValuePositions[i]; if (rotateNumericFlag) { verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; - if (i == firstIndex) { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; - } - else if (i == lastIndex) { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; - } - else { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; - } +// if (i == firstIndex) { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; +// } +// else if (i == lastIndex) { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; +// } +// else { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; +// } + horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; } else { horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; - if (i == firstIndex) { - verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; - } - else if (i == lastIndex) { - verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; - } - else { - verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; - } +// if (i == firstIndex) { +// verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; +// } +// else if (i == lastIndex) { +// verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; +// } +// else { +// verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; +// } + verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; } + + break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: xyz[1] = scaleValuePositions[i]; if (rotateNumericFlag) { verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; - if (i == firstIndex) { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; - } - else if (i == lastIndex) { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; - } - else { - horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; - } +// if (i == firstIndex) { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; +// } +// else if (i == lastIndex) { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::RIGHT; +// } +// else { +// horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; +// } + horizontalAlignment = AnnotationTextAlignHorizontalEnum::CENTER; } else { horizontalAlignment = AnnotationTextAlignHorizontalEnum::LEFT; - if (i == firstIndex) { - verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; - } - else if (i == lastIndex) { - verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; - } - else { - verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; - } +// if (i == firstIndex) { +// verticalAlignment = AnnotationTextAlignVerticalEnum::BOTTOM; +// } +// else if (i == lastIndex) { +// verticalAlignment = AnnotationTextAlignVerticalEnum::TOP; +// } +// else { +// verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; +// } + verticalAlignment = AnnotationTextAlignVerticalEnum::MIDDLE; } + break; } text->setHorizontalAlignment(horizontalAlignment); @@ -2215,7 +2417,6 @@ maxWidthOut = std::max(maxWidthOut, static_cast(textWidth)); maxHeightOut = std::max(maxHeightOut, static_cast(textHeight)); - //text->setText(scaleValuesText[i]); m_numericsText.push_back(std::unique_ptr(text)); } } @@ -2422,23 +2623,14 @@ * Y-coordinate of the mouse. * @param foregroundFloatRGBA * Color of the foreground. - * @param axisMinimumValueOut - * Output containing the minimum value for the axis - * @param axisMaximumValueOut - * Output containing the maximum value for the axis */ void BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::drawAxis(BrainOpenGLChartTwoDrawingFixedPipeline* chartDrawing, ChartTwoOverlaySet* chartTwoOverlaySet, const int32_t mouseX, const int32_t mouseY, - const float foregroundFloatRGBA[4], - float& axisMinimumValueOut, - float& axisMaximumValueOut) + const float foregroundFloatRGBA[4]) { - axisMinimumValueOut = m_axisMinimumValue; - axisMaximumValueOut = m_axisMaximumValue; - if (m_axis == NULL) { return; } @@ -2472,8 +2664,8 @@ AnnotationPercentSizeText* text = m_numericsText[i].get(); float xyz[3]; text->getCoordinate()->getXYZ(xyz); - const float textX = xyz[0]; - const float textY = xyz[1]; + float textX = xyz[0]; + float textY = xyz[1]; /* * Tick starts at an offset from its corresponding numerical value. @@ -2509,14 +2701,79 @@ break; } + const float viewportX = m_axisViewport[0]; + const float viewportY = m_axisViewport[1]; + const float viewportWidth = m_axisViewport[2]; + const float viewportHeight = m_axisViewport[3]; + + double textWidth(0.0); + double textHeight(0.0); + m_textRenderer->getTextWidthHeightInPixels(*text, + BrainOpenGLTextRenderInterface::DrawingFlags(), + m_tabViewportWidth, m_tabViewportHeight, + textWidth, textHeight); if (showTicksEnabledFlag) { - if ((i != firstTickIndex) - && (i != lastTickIndex)) { + bool showTicksFlag(true); + const bool hideFirstAndLastTicksFlag(false); + if (hideFirstAndLastTicksFlag) { + if ((i == firstTickIndex) + || (i == lastTickIndex)) { + } + } + if (showTicksFlag) { ticksData->addVertex(tickStartX, tickStartY); ticksData->addVertex(tickEndX, tickEndY); } } + switch (m_axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + { + /* + * Text alignment is center; + */ + const float viewportLeft(viewportX); + const float viewportRight(viewportX + viewportWidth); + const float halfTextSize(m_axis->isNumericsTextRotated() + ? (textHeight / 2.0) + : (textWidth / 2.0)); +// const float halfTextWidth(textWidth / 2.0); + const float textRight(textX + halfTextSize); + if (textRight > viewportRight) { + textX = viewportRight - halfTextSize; + } + + const float textLeft = (textX - halfTextSize); + if (textLeft < viewportLeft) { + textX = viewportLeft + halfTextSize; + } + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + { + /* + * Text alignment is middle; + */ + const float viewportBottom(viewportY); + const float viewportTop(viewportY + viewportHeight); + const float halfTextSize(m_axis->isNumericsTextRotated() + ? (textWidth / 2.0) + : (textHeight / 2.0)); + const float textTop(textY + halfTextSize); + if (textTop > viewportTop) { + textY = viewportTop - halfTextSize; + } + + const float textBottom = (textY - halfTextSize); + if (textBottom < viewportBottom) { + textY = viewportBottom + halfTextSize; + } + } + break; + } + /* * Draw the numeric value. */ diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h connectome-workbench-1.5.0/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h 2021-02-16 19:46:47.000000000 +0000 @@ -24,7 +24,7 @@ #include #include #include "BrainOpenGLChartTwoDrawingInterface.h" -#include "CaretColorEnum.h" +#include "CaretColor.h" #include "ChartAxisLocationEnum.h" #include "ChartTwoDataTypeEnum.h" #include "ChartTwoMatrixTriangularViewingModeEnum.h" @@ -38,15 +38,20 @@ class BrowserTabContent; class CaretPreferences; class ChartTwoCartesianAxis; + class ChartTwoCartesianOrientedAxes; class ChartTwoDataCartesian; + class ChartTwoOverlay; class ChartTwoOverlaySet; class ChartTwoTitle; class ChartableTwoFileHistogramChart; + class ChartableTwoFileLineLayerChart; class ChartableTwoFileLineSeriesChart; class ChartableTwoFileMatrixChart; class SelectionItemAnnotation; class SelectionItemChartTwoHistogram; class SelectionItemChartTwoLabel; + class SelectionItemChartTwoLineLayer; + class SelectionItemChartTwoLineLayerVerticalNearest; class SelectionItemChartTwoLineSeries; class SelectionItemChartTwoMatrix; @@ -74,34 +79,71 @@ public: HistogramChartDrawingInfo(ChartableTwoFileHistogramChart* histogramChart, int32_t mapIndex, - ChartAxisLocationEnum::Enum verticalAxisLocation, const bool allMapsSelected); ChartableTwoFileHistogramChart* m_histogramChart; int32_t m_mapIndex; - ChartAxisLocationEnum::Enum m_verticalAxisLocation; const bool m_allMapsSelected; ~HistogramChartDrawingInfo(); }; /** - * Contains line chart for drawing + * Contains line chart for drawing line layer + */ + class LineLayerChartDrawingInfo { + public: + LineLayerChartDrawingInfo(const ChartableTwoFileLineLayerChart* lineLayerChart, + ChartTwoDataCartesian* chartTwoCartesianData, + const ChartTwoOverlay* chartTwoOverlay, + const CaretColor& lineChartColor, + const float lineWidth) + : m_lineLayerChart(lineLayerChart), + m_chartTwoCartesianData(chartTwoCartesianData), + m_chartTwoOverlay(chartTwoOverlay), + m_lineChartColor(lineChartColor), + m_lineWidth(lineWidth) { } + + const ChartableTwoFileLineLayerChart* m_lineLayerChart; + ChartTwoDataCartesian* m_chartTwoCartesianData; + const ChartTwoOverlay* m_chartTwoOverlay; + const CaretColor m_lineChartColor; + const float m_lineWidth; + }; + + /** + * Contains line chart for drawing line series */ class LineSeriesChartDrawingInfo { public: LineSeriesChartDrawingInfo(const ChartableTwoFileLineSeriesChart* lineSeriesChart, - const ChartTwoDataCartesian* chartTwoCartesianData, - const ChartAxisLocationEnum::Enum verticalAxisLocation) + const ChartTwoDataCartesian* chartTwoCartesianData) : m_lineSeriesChart(lineSeriesChart), - m_chartTwoCartesianData(chartTwoCartesianData), - m_verticalAxisLocation(verticalAxisLocation) { } + m_chartTwoCartesianData(chartTwoCartesianData) { } const ChartableTwoFileLineSeriesChart* m_lineSeriesChart; const ChartTwoDataCartesian* m_chartTwoCartesianData; - const ChartAxisLocationEnum::Enum m_verticalAxisLocation; }; + class MatrixChartDrawingInfo { + public: + MatrixChartDrawingInfo(const ChartableTwoFileMatrixChart* matrixChart, + const GraphicsPrimitive* matrixPrimitive, + const ChartTwoOverlay* chartTwoOverlay, + const ChartTwoMatrixTriangularViewingModeEnum::Enum triangularMode, + const float opacity) + : m_matrixChart(matrixChart), + m_matrixPrimitive(matrixPrimitive), + m_chartTwoOverlay(chartTwoOverlay), + m_triangularMode(triangularMode), + m_opacity(opacity) { } + + const ChartableTwoFileMatrixChart* m_matrixChart; + const GraphicsPrimitive* m_matrixPrimitive; + const ChartTwoOverlay* m_chartTwoOverlay; + const ChartTwoMatrixTriangularViewingModeEnum::Enum m_triangularMode; + const float m_opacity; + }; /** * Determines size of title and draws the title */ @@ -143,6 +185,7 @@ const float dataMinY, const float dataMaxY, const ChartAxisLocationEnum::Enum axisLocation, + const ChartTwoCartesianOrientedAxes* orientedAxes, const ChartTwoCartesianAxis* axis, const AString& labelText, const float lineWidthPercentage); @@ -160,9 +203,7 @@ ChartTwoOverlaySet* chartTwoOverlaySet, const int32_t mouseX, const int32_t mouseY, - const float foregroundFloatRGBA[4], - float& axisMinimumValueOut, - float& axisMaximumValueOut); + const float foregroundFloatRGBA[4]); std::unique_ptr m_labelText; @@ -184,6 +225,7 @@ private: const ChartAxisLocationEnum::Enum m_axisLocation; + const ChartTwoCartesianOrientedAxes* m_orientedAxes; const ChartTwoCartesianAxis* m_axis; BrainOpenGLTextRenderInterface* m_textRenderer; const float m_tabViewportWidth = 0.0f; @@ -226,16 +268,13 @@ void saveStateOfOpenGL(); - void drawMatrixChart(); - void drawMatrixChartContent(const ChartableTwoFileMatrixChart* matrixChart, const ChartTwoMatrixTriangularViewingModeEnum::Enum chartViewingType, - const float cellWidth, - const float cellHeight, - const float zooming, - std::vector& rowColumnHighlightingOut); + const float opacity, + std::vector& rowColumnHighlightingOut, + const bool blendingEnabled); - void drawHistogramOrLineSeriesChart(const ChartTwoDataTypeEnum::Enum chartDataType); + void drawHistogramOrLineChart(const ChartTwoDataTypeEnum::Enum chartDataType); void drawChartGraphicsBoxAndSetViewport(const float vpX, const float vpY, @@ -302,8 +341,12 @@ SelectionItemChartTwoHistogram* m_selectionItemHistogram; - SelectionItemChartTwoLineSeries* m_selectionItemLineSeries; + SelectionItemChartTwoLineLayer* m_selectionItemLineLayer; + SelectionItemChartTwoLineLayerVerticalNearest* m_selectionItemLineLayerVerticalNearest; + + SelectionItemChartTwoLineSeries* m_selectionItemLineSeries; + SelectionItemChartTwoMatrix* m_selectionItemMatrix; SelectionItemChartTwoLabel* m_selectionItemChartLabel; diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGL.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGL.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGL.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGL.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -32,6 +32,7 @@ #include "CaretLogger.h" #include "CaretPreferences.h" #include "DummyFontTextRenderer.h" +#include "EventAnnotationTextGetBounds.h" #include "EventGetBrainOpenGLTextRenderer.h" #include "EventGraphicsOpenGLCreateBufferObject.h" #include "EventGraphicsOpenGLCreateTextureName.h" @@ -64,6 +65,7 @@ this->borderBeingDrawn = NULL; m_drawHighlightedEndPoints = false; + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_TEXT_GET_BOUNDS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME); @@ -94,7 +96,30 @@ void BrainOpenGL::receiveEvent(Event* event) { - if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT) { + if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_TEXT_GET_BOUNDS) { + EventAnnotationTextGetBounds* textBoundsEvent = dynamic_cast(event); + CaretAssert(textBoundsEvent); + + if (m_textRenderer != NULL) { + double textWidth(0.0); + double textHeight(0.0); + BrainOpenGLTextRenderInterface::DrawingFlags textDrawingFlags; + textDrawingFlags.setDrawSubstitutedText(false); + + m_textRenderer->getTextWidthHeightInPixels(textBoundsEvent->getAnnotationText(), + textDrawingFlags, + textBoundsEvent->getViewportWidth(), + textBoundsEvent->getViewportHeight(), + textWidth, + textHeight); + + textBoundsEvent->setTextWidthHeight(textWidth, + textHeight); + } + + textBoundsEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT) { EventGraphicsOpenGLCreateBufferObject* createBufferEvent = dynamic_cast(event); CaretAssert(createBufferEvent); @@ -720,6 +745,17 @@ * Call to validate the draw mode selection logic. */ getBestDrawingMode(); + + GraphicsUtilitiesOpenGL::setMajorMinorVersion(s_runtimeLibraryMajorVersionOfOpenGL.toInt(), + s_runtimeLibraryMinorVersionOfOpenGL.toInt()); + + GLint textureMax, texture3DMax; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, + &textureMax); + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, + &texture3DMax); + GraphicsUtilitiesOpenGL::setMaximumTextureDimension(textureMax, + texture3DMax); } /** diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLFixedPipeline.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLFixedPipeline.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLFixedPipeline.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLFixedPipeline.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -35,15 +35,18 @@ #include #include +#include "AnnotationBrowserTab.h" #include "AnnotationColorBar.h" #include "AnnotationManager.h" #include "AnnotationPointSizeText.h" +#include "AnnotationScaleBar.h" #include "Border.h" #include "BorderFile.h" #include "Brain.h" #include "BrainOpenGLAnnotationDrawingFixedPipeline.h" #include "BrainOpenGLChartDrawingFixedPipeline.h" #include "BrainOpenGLChartTwoDrawingFixedPipeline.h" +#include "BrainOpenGLMediaDrawing.h" #include "BrainOpenGLPrimitiveDrawing.h" #include "BrainOpenGLVolumeObliqueSliceDrawing.h" #include "BrainOpenGLVolumeSliceDrawing.h" @@ -56,6 +59,7 @@ #include "BrainOpenGLViewportContent.h" #include "BrainStructure.h" #include "BrowserTabContent.h" +#include "BrowserWindowContent.h" #include "BoundingBox.h" #include "CaretAssert.h" #include "CaretDataFileSelectionModel.h" @@ -85,7 +89,8 @@ #include "DisplayPropertiesSurface.h" #include "DisplayPropertiesVolume.h" #include "ElapsedTimer.h" -#include "EventAnnotationColorBarGet.h" +#include "EventAnnotationBarsGet.h" +#include "EventBrowserWindowContent.h" #include "EventManager.h" #include "EventModelSurfaceGet.h" #include "EventNodeIdentificationColorsGetFromCharts.h" @@ -102,7 +107,9 @@ #include "GiftiLabelTable.h" #include "GraphicsEngineDataOpenGL.h" #include "GraphicsPrimitiveV3fC4ub.h" +#include "GraphicsPrimitiveV3fN3fC4ub.h" #include "GraphicsPrimitiveV3f.h" +#include "GraphicsPrimitiveV3fT3f.h" #include "GraphicsShape.h" #include "GroupAndNameHierarchyModel.h" #include "IdentifiedItemNode.h" @@ -127,6 +134,7 @@ #include "MathFunctions.h" #include "ModelChart.h" #include "ModelChartTwo.h" +#include "ModelMedia.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelVolume.h" @@ -266,9 +274,11 @@ m_specialCaseGraphicsAnnotations.clear(); + const bool selectionModeFlag(true); std::vector viewportContentsVector; viewportContentsVector.push_back(viewportContent); - setAnnotationColorBarsForDrawing(viewportContentsVector); + setAnnotationColorBarsAndBrowserTabsForDrawing(viewportContentsVector, + selectionModeFlag); m_clippingPlaneGroup = NULL; @@ -310,7 +320,7 @@ m_brain = NULL; m_windowIndex = -1; - m_windowUserInputMode = UserInputModeEnum::INVALID; + m_windowUserInputMode = UserInputModeEnum::Enum::INVALID; } /** @@ -379,7 +389,7 @@ this->modeProjectionData = NULL; m_brain = NULL; m_windowIndex = -1; - m_windowUserInputMode = UserInputModeEnum::INVALID; + m_windowUserInputMode = UserInputModeEnum::Enum::INVALID; } /** @@ -448,6 +458,10 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + prefs->getBackgroundAndForegroundColors()->getColorForegroundMediaView(m_foregroundColorByte); + prefs->getBackgroundAndForegroundColors()->getColorBackgroundMediaView(m_backgroundColorByte); + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: prefs->getBackgroundAndForegroundColors()->getColorForegroundSurfaceView(m_foregroundColorByte); prefs->getBackgroundAndForegroundColors()->getColorBackgroundSurfaceView(m_backgroundColorByte); @@ -499,23 +513,28 @@ } /** - * Get colorbars in window space. + * Get colorbars and browser tabs for drawing * * @param viewportContents * Contents of the viewports. + *@param selectionModeFlag + * True if in selection mode, else false. */ void -BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(const std::vector& viewportContents) +BrainOpenGLFixedPipeline::setAnnotationColorBarsAndBrowserTabsForDrawing(const std::vector& viewportContents, + const bool selectionModeFlag) { m_annotationColorBarsForDrawing.clear(); + m_annotationScaleBarsForDrawing.clear(); /* * This event gets EVERY color bar, even those * in other windows */ - EventAnnotationColorBarGet colorBarEvent; - EventManager::get()->sendEvent(colorBarEvent.getPointer()); - std::vector allColorBars = colorBarEvent.getAnnotationColorBars(); + EventAnnotationBarsGet barsEvent; + EventManager::get()->sendEvent(barsEvent.getPointer()); + std::vector allColorBars = barsEvent.getAnnotationColorBars(); + std::vector allScaleBars = barsEvent.getAnnotationScaleBars(); /* * Find the color bars contained in the viewports and @@ -524,17 +543,143 @@ * tab view). */ for (auto colorBar : allColorBars) { + bool windowSpaceSelectionFlag(false); const int32_t tabIndex = colorBar->getTabIndex(); + + if (selectionModeFlag) { + switch (colorBar->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + /* + * Need to include colorbars in window space since they + * can outside of current tab + */ + for (auto vc : viewportContents) { + if (colorBar->getWindowIndex() == vc->getWindowIndex()) { + windowSpaceSelectionFlag = true; + } + } + break; + } + } + if (windowSpaceSelectionFlag) { + m_annotationColorBarsForDrawing.push_back(colorBar); + } + else { + for (auto vc : viewportContents) { + if (vc->getTabIndex() == tabIndex) { + /* + * While color bars are associated with tabs, the color bar + * may be in window space so always update the window index + * (users can move tabs to other windows). + */ + colorBar->setWindowIndex(vc->getWindowIndex()); + m_annotationColorBarsForDrawing.push_back(colorBar); + break; + } + } + } + } + + for (auto scaleBar : allScaleBars) { + const int32_t tabIndex = scaleBar->getTabIndex(); for (auto vc : viewportContents) { if (vc->getTabIndex() == tabIndex) { + scaleBar->setWindowIndex(vc->getWindowIndex()); + m_annotationScaleBarsForDrawing.push_back(scaleBar); + } + } + } + + std::unique_ptr windowContentEvent = EventBrowserWindowContent::getWindowContent(m_windowIndex); + EventManager::get()->sendEvent(windowContentEvent->getPointer()); + const BrowserWindowContent* windowContent = windowContentEvent->getBrowserWindowContent(); + bool drawBrowserTabAnnotationsFlag(false); + if (windowContent != NULL) { + switch (windowContent->getTileTabsConfigurationMode()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + drawBrowserTabAnnotationsFlag = true; + break; + } + } + + m_annotationBrowserTabsForDrawing.clear(); + + if (drawBrowserTabAnnotationsFlag) { + const CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + const BackgroundAndForegroundColors* colors = preferences->getBackgroundAndForegroundColors(); + for (const auto vc : viewportContents) { + BrowserTabContent* tabContent = vc->getBrowserTabContent(); + if (tabContent != NULL) { + uint8_t foregroundColor[4] = { 255, 255, 255, 255 }; + uint8_t backgroundColor[4] = { 0, 0, 0, 255 }; + colors->getColorBackgroundWindow(backgroundColor); + colors->getColorForegroundWindow(foregroundColor); + + switch (tabContent->getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + colors->getColorBackgroundChartView(backgroundColor); + colors->getColorForegroundChartView(foregroundColor); + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + colors->getColorBackgroundChartView(backgroundColor); + colors->getColorForegroundChartView(foregroundColor); + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + colors->getColorBackgroundMediaView(backgroundColor); + colors->getColorForegroundMediaView(foregroundColor); + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + colors->getColorBackgroundSurfaceView(backgroundColor); + colors->getColorForegroundSurfaceView(foregroundColor); + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + colors->getColorBackgroundSurfaceView(backgroundColor); + colors->getColorForegroundSurfaceView(foregroundColor); + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + colors->getColorBackgroundVolumeView(backgroundColor); + colors->getColorForegroundVolumeView(foregroundColor); + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + colors->getColorBackgroundAllView(backgroundColor); + colors->getColorForegroundAllView(foregroundColor); + break; + } + /* - * While color bars are associated with tabs, the color bar - * may be in window space so always update the window index - * (users can move tabs to other windows). + * Update the line and background colors using the + * appropriate model color from preferences */ - colorBar->setWindowIndex(vc->getWindowIndex()); - m_annotationColorBarsForDrawing.push_back(colorBar); - break; + AnnotationBrowserTab* abt = tabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(abt); + const bool modStatus = abt->isModified(); + abt->setCustomLineColor(foregroundColor); + abt->setLineColor(CaretColorEnum::CUSTOM); + abt->setCustomBackgroundColor(backgroundColor); + abt->setBackgroundColor(CaretColorEnum::CUSTOM); + if ( ! modStatus) { + abt->clearModified(); + } + abt->setWindowIndex(vc->getWindowIndex()); + m_annotationBrowserTabsForDrawing.push_back(abt); } } } @@ -568,8 +713,10 @@ setTabViewport(NULL); + const bool selectionModeFlag(false); m_specialCaseGraphicsAnnotations.clear(); - setAnnotationColorBarsForDrawing(viewportContents); + setAnnotationColorBarsAndBrowserTabsForDrawing(viewportContents, + selectionModeFlag); m_tileTabsActiveFlag = (viewportContents.size() > 1); @@ -605,7 +752,20 @@ this->checkForOpenGLError(NULL, "At middle of drawModels()"); - for (int32_t i = 0; i < static_cast(viewportContents.size()); i++) { + std::unique_ptr windowContentEvent = EventBrowserWindowContent::getWindowContent(m_windowIndex); + EventManager::get()->sendEvent(windowContentEvent->getPointer()); + const BrowserWindowContent* windowContent = windowContentEvent->getBrowserWindowContent(); + CaretAssert(windowContent); + const bool manualLayoutFlag = windowContent->isManualModeTileTabsConfigurationEnabled(); + + bool windowAnnotationsDrawnFlag(false); + const int32_t windowAnnotationsStackOrder(manualLayoutFlag + ? windowContent->getWindowAnnotationsStackingOrder() + : -1000); /* smaller numbers are in front */ + int32_t lastTabStackOrder(std::numeric_limits::max()); + + const int32_t numberOfTabs = static_cast(viewportContents.size()); + for (int32_t i = 0; i < numberOfTabs; i++) { const BrainOpenGLViewportContent* vpContent = viewportContents[i]; /* * Don't draw if off-screen @@ -632,18 +792,23 @@ continue; } + const bool logInvalidViewportFlag(false); if (tabViewport[2] <= 0) { - CaretLogSevere("Invalid TAB width=" - + AString::number(tabViewport[2]) - + " for index=" - + AString::number(i)); + if (logInvalidViewportFlag) { + CaretLogSevere("Invalid TAB width=" + + AString::number(tabViewport[2]) + + " for index=" + + AString::number(i)); + } continue; } if (tabViewport[3] <= 0) { - CaretLogSevere("Invalid TAB height=" - + AString::number(tabViewport[3]) - + " for index=" - + AString::number(i)); + if (logInvalidViewportFlag) { + CaretLogSevere("Invalid TAB height=" + + AString::number(tabViewport[3]) + + " for index=" + + AString::number(i)); + } continue; } } @@ -659,40 +824,114 @@ */ updateForegroundAndBackgroundColors(vpContent); - /* - * If the background color for this viewport content is - * different that the clear color, THEN - * draw a rectangle with the background color. - */ - if ((m_backgroundColorByte[0] != clearColorByte[0]) - || (m_backgroundColorByte[1] != clearColorByte[1]) - || (m_backgroundColorByte[2] != clearColorByte[2])) { - GLboolean depthEnabledFlag; - glGetBooleanv(GL_DEPTH_TEST, - &depthEnabledFlag); - glDisable(GL_DEPTH_TEST); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glColor3ubv(m_backgroundColorByte); - glBegin(GL_POLYGON); - glVertex2f(0.0, 0.0); - glVertex2f(1.0, 0.0); - glVertex2f(1.0, 1.0); - glVertex2f(0.0, 1.0); - glEnd(); - - if (depthEnabledFlag) { - glEnable(GL_DEPTH_TEST); + const BrowserTabContent* tabContent = vpContent->getBrowserTabContent(); + + if ( ! windowAnnotationsDrawnFlag) { + if (tabContent != NULL) { + const int32_t tabStackOrder = tabContent->getManualLayoutBrowserTabAnnotation()->getStackingOrder(); + if ((windowAnnotationsStackOrder < lastTabStackOrder) + && (windowAnnotationsStackOrder >= tabStackOrder)) { + CaretAssertVectorIndex(viewportContents, 0); + int windowViewport[4]; + viewportContents[0]->getWindowViewport(windowViewport); + CaretAssert(m_windowIndex == viewportContents[0]->getWindowIndex()); + drawWindowAnnotations(windowViewport); + windowAnnotationsDrawnFlag = true; + } + + lastTabStackOrder = tabStackOrder; + } + } + + bool tileTabsFlag(false); + if (tabContent != NULL) { + if (numberOfTabs > 1) { + bool opaqueFlag(true); + if (manualLayoutFlag) { + switch (tabContent->getManualLayoutBrowserTabAnnotation()->getBackgroundType()) { + case TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG: + opaqueFlag = true; + break; + case TileTabsLayoutBackgroundTypeEnum::TRANSPARENT_BG: + opaqueFlag = false; + break; + } + } + + glPushAttrib(GL_COLOR_BUFFER_BIT + | GL_DEPTH_BUFFER_BIT + | GL_SCISSOR_BIT); + + GLint tabViewport[4]; + vpContent->getTabViewportBeforeApplyingMargins(tabViewport); + glClearColor(m_backgroundColorFloat[0], + m_backgroundColorFloat[1], + m_backgroundColorFloat[2], + 1.0); + + glEnable(GL_SCISSOR_TEST); + glScissor(tabViewport[0], + tabViewport[1], + tabViewport[2], + tabViewport[3]); + + if (opaqueFlag) { + glClear(GL_COLOR_BUFFER_BIT + | GL_DEPTH_BUFFER_BIT); + } + else { + glClear(GL_DEPTH_BUFFER_BIT); + } + + glPopAttrib(); + + tileTabsFlag = true; + } + } + + if ( ! tileTabsFlag) { + /* + * If the background color for this viewport content is + * different that the clear color, THEN + * draw a rectangle with the background color. + */ + if ((m_backgroundColorByte[0] != clearColorByte[0]) + || (m_backgroundColorByte[1] != clearColorByte[1]) + || (m_backgroundColorByte[2] != clearColorByte[2])) { + GLboolean depthEnabledFlag; + glGetBooleanv(GL_DEPTH_TEST, + &depthEnabledFlag); + glDisable(GL_DEPTH_TEST); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glColor3ubv(m_backgroundColorByte); + glBegin(GL_POLYGON); + glVertex2f(0.0, 0.0); + glVertex2f(1.0, 0.0); + glVertex2f(1.0, 1.0); + glVertex2f(0.0, 1.0); + glEnd(); + + if (depthEnabledFlag) { + glEnable(GL_DEPTH_TEST); + } } } this->drawModelInternal(MODE_DRAWING, vpContent); + if (vpContent->getSpacerTabContent() != NULL) { + drawSpacerAnnotations(vpContent); + } + else { + drawTabAnnotations(vpContent); + } + /* * Draw border in foreground color around tab that is highlighted * in Tile Tabs when user selects a tab. @@ -711,34 +950,42 @@ * everything else. */ glClear(GL_DEPTH_BUFFER_BIT); - - for (int32_t i = 0; i < static_cast(viewportContents.size()); i++) { - /* - * Viewport of window. - */ - const BrainOpenGLViewportContent* vpContent = viewportContents[i]; - setTabViewport(vpContent); - - /* - * Update foreground and background colors for model - */ - updateForegroundAndBackgroundColors(vpContent); - - if (vpContent->getSpacerTabContent() != NULL) { - drawSpacerAnnotations(vpContent); - } - else { - drawTabAnnotations(vpContent); - } - } - + /* * Draw window viewport annotations */ - int windowViewport[4]; - viewportContents[0]->getWindowViewport(windowViewport); - CaretAssert(m_windowIndex == viewportContents[0]->getWindowIndex()); - drawWindowAnnotations(windowViewport); + if ( ! windowAnnotationsDrawnFlag) { + int windowViewport[4]; + viewportContents[0]->getWindowViewport(windowViewport); + CaretAssert(m_windowIndex == viewportContents[0]->getWindowIndex()); + drawWindowAnnotations(windowViewport); + } + + if ( ! viewportContents.empty()) { + if (windowContent->isWindowAspectLocked()) { + /* + * When window aspect is locked, the window viewport may be smaller in one dimension + * than the OpenGL widget. So, fill the regions outside the window viewport with the + * window background color. This is probably only needed for manual tab configuration + * since a tab may be partially outside the window viewport. + */ + CaretAssertVectorIndex(viewportContents, 0); + const BrainOpenGLViewportContent* vpContent = viewportContents[0]; + int32_t windowViewportAfterAspectLocking[4]; + vpContent->getWindowViewport(windowViewportAfterAspectLocking); + int32_t windowViewportBeforeAspectLocking[4]; + vpContent->getWindowBeforeAspectLockingViewport(windowViewportBeforeAspectLocking); + + if (m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING) { + drawStippledBackgroundInAreasOutsideWindowAspectLocking(windowViewportBeforeAspectLocking, + windowViewportAfterAspectLocking); + } + else { + drawSolidBackgroundInAreasOutsideWindowAspectLocking(windowViewportBeforeAspectLocking, + windowViewportAfterAspectLocking); + } + } + } } m_specialCaseGraphicsAnnotations.clear(); @@ -747,7 +994,7 @@ m_brain = NULL; m_windowIndex = -1; - m_windowUserInputMode = UserInputModeEnum::INVALID; + m_windowUserInputMode = UserInputModeEnum::Enum::INVALID; } /** @@ -875,7 +1122,8 @@ * Draw annotations for this surface and maybe draw * the model annotations. */ - const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -883,12 +1131,15 @@ this->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); std::vector emptyColorBars; + std::vector emptyScaleBars; std::vector emptyViewportAnnotations; m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::CHART, emptyColorBars, + emptyScaleBars, emptyViewportAnnotations, NULL, 1.0); @@ -919,6 +1170,15 @@ int tabViewport[4]; tabContent->getModelViewport(tabViewport); + + /* + * Is viewport width/height invalid? + */ + if ((tabViewport[2] <= 0) + || (tabViewport[3] <= 0)) { + return; + } + CaretAssertMessage(m_brain, "m_brain must NOT be NULL for drawing spacer tab annotations."); glViewport(tabViewport[0], tabViewport[1], @@ -933,7 +1193,7 @@ CaretAssert(m_windowIndex == tabContent->getWindowIndex()); this->browserTabContent = NULL; - m_clippingPlaneGroup = NULL; //const_cast(tabContent->getBrowserTabContent()->getClippingPlaneGroup()); + m_clippingPlaneGroup = NULL; this->windowTabIndex = -1; @@ -942,7 +1202,8 @@ CaretAssert(spacerTabContent); spacerTabIndex = spacerTabContent->getSpacerTabIndex(); - const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -950,10 +1211,12 @@ this->windowTabIndex, spacerTabIndex, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SPACER, m_annotationColorBarsForDrawing, + m_annotationScaleBarsForDrawing, m_specialCaseGraphicsAnnotations, annotationDrawingNullSurface, annotationDrawingUnusedSurfaceScaling); @@ -979,6 +1242,11 @@ int tabViewport[4]; tabContent->getModelViewport(tabViewport); + if ((tabViewport[2] <= 0) + || (tabViewport[3] <= 0)) { + /* Viewport may be invalid when tabs edited by user */ + return; + } CaretAssertMessage(m_brain, "m_brain must NOT be NULL for drawing window annotations."); glViewport(tabViewport[0], tabViewport[1], @@ -998,7 +1266,8 @@ this->windowTabIndex = this->browserTabContent->getTabNumber(); - const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -1006,10 +1275,36 @@ this->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); + + /* + * Scale bars on drawn on brain models + */ + bool drawScaleBarsFlag(false); + switch (tabContent->getBrowserTabContent()->getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + case ModelTypeEnum::MODEL_TYPE_INVALID: + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + drawScaleBarsFlag = false; + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + drawScaleBarsFlag = true; + break; + } + + std::vector annotationScaleBarsForDrawing; + if (drawScaleBarsFlag) { + annotationScaleBarsForDrawing = m_annotationScaleBarsForDrawing; + } m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::TAB, m_annotationColorBarsForDrawing, + annotationScaleBarsForDrawing, m_specialCaseGraphicsAnnotations, annotationDrawingNullSurface, annotationDrawingUnusedSurfaceScaling); @@ -1061,7 +1356,17 @@ */ this->windowTabIndex = -1; - const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + std::unique_ptr windowContentEvent = EventBrowserWindowContent::getWindowContent(m_windowIndex); + EventManager::get()->sendEvent(windowContentEvent->getPointer()); + const BrowserWindowContent* windowContent = windowContentEvent->getBrowserWindowContent(); + bool tileTabsEnabledFlag(false); + if (windowContent != NULL) { + tileTabsEnabledFlag = windowContent->isTileTabsEnabled(); + } + + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (tileTabsEnabledFlag + && (m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING)); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -1069,12 +1374,21 @@ this->windowTabIndex, SpacerTabIndex(), windowDrawingMode, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); + std::vector notInFileAnnotations; + notInFileAnnotations.insert(notInFileAnnotations.end(), + m_specialCaseGraphicsAnnotations.begin(), + m_specialCaseGraphicsAnnotations.end()); + notInFileAnnotations.insert(notInFileAnnotations.end(), + m_annotationBrowserTabsForDrawing.begin(), + m_annotationBrowserTabsForDrawing.end()); m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::WINDOW, m_annotationColorBarsForDrawing, - m_specialCaseGraphicsAnnotations, + m_annotationScaleBarsForDrawing, + notInFileAnnotations, annotationDrawingNullSurface, annotationDrawingUnusedSurfaceScaling); @@ -1141,6 +1455,7 @@ ModelChart* modelChart = dynamic_cast(model); ModelChartTwo* modelTwoChart = dynamic_cast(model); + ModelMedia* mediaModel = dynamic_cast(model); ModelSurface* surfaceModel = dynamic_cast(model); ModelSurfaceMontage* surfaceMontageModel = dynamic_cast(model); ModelVolume* volumeModel = dynamic_cast(model); @@ -1151,6 +1466,11 @@ else if (modelTwoChart != NULL) { drawChartTwoData(viewportContent, modelTwoChart, viewport); } + else if (mediaModel != NULL) { + drawMediaModel(browserTabContent, + mediaModel, + viewport); + } else if (surfaceModel != NULL) { m_mirroredClippingEnabled = true; this->drawSurfaceModel(browserTabContent, @@ -1317,6 +1637,10 @@ CaretAssert(m_clippingPlaneGroup); + if ( ! m_clippingPlaneGroup->isEnabled()) { + return; + } + float rotation[3]; m_clippingPlaneGroup->getRotationAngles(rotation); @@ -1861,9 +2185,6 @@ if (glIsEnabled(GL_MULTISAMPLE)) { return; } - -// glDisable(GL_LINE_SMOOTH); -// glDisable(GL_BLEND); } /** @@ -1896,6 +2217,8 @@ surface, this->windowTabIndex); + setupScaleBarDrawingInformation(browserTabContent); + this->drawSurface(surface, browserTabContent->getScaling(), nodeColoringRGBA, @@ -1953,7 +2276,13 @@ surface->getStructure()); this->enableLighting(); - + + /* + * WB-911 Foci are completely obscured by not fully opaque surfaces + * Need to draw the foci before drawing the surface + */ + this->drawSurfaceFoci(surface); + const SurfaceDrawingTypeEnum::Enum drawingType = dps->getSurfaceDrawingType(); switch (this->mode) { case MODE_DRAWING: @@ -1964,7 +2293,7 @@ case SurfaceDrawingTypeEnum::DRAW_AS_LINKS: /* * Draw first as triangles without coloring which uses - * the background color. This prevents edges on back + * the background color. This prevents edges on back * from being seen. */ glPolygonOffset(1.0, 1.0); @@ -1984,20 +2313,45 @@ nodeColoringRGBA); glPolygonMode(GL_FRONT, GL_FILL); break; + case SurfaceDrawingTypeEnum::DRAW_AS_LINKS_TRANSPARENT: + { + /* + * Enable alpha blending so that surface transparency + * (using first overlay opacity) will function. + */ + GLboolean blendingEnabled = false; + glGetBooleanv(GL_BLEND, &blendingEnabled); + setupBlending(BlendDataType::SURFACE_PROPERTIES_OPACITY); + + /* + * Now draw as polygon but outline only, do not fill. + */ + enableLighting(); + setLineWidth(dps->getLinkSize()); + glPolygonMode(GL_FRONT, GL_LINE); + this->drawSurfaceTrianglesWithVertexArrays(surface, + nodeColoringRGBA); + glPolygonMode(GL_FRONT, GL_FILL); + + if ( ! blendingEnabled) { + glDisable(GL_BLEND); + } + } + break; case SurfaceDrawingTypeEnum::DRAW_AS_NODES: this->drawSurfaceNodes(surface, nodeColoringRGBA); break; case SurfaceDrawingTypeEnum::DRAW_AS_TRIANGLES: + { /* * Enable alpha blending so that surface transparency * (using first overlay opacity) will function. */ GLboolean blendingEnabled = false; glGetBooleanv(GL_BLEND, &blendingEnabled); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - + setupBlending(BlendDataType::SURFACE_PROPERTIES_OPACITY); + const DisplayPropertiesBorders* dpb = m_brain->getDisplayPropertiesBorders(); const float borderAboveSurfaceOffset = dpb->getAboveSurfaceOffset(); if (borderAboveSurfaceOffset != 0.0) { @@ -2008,7 +2362,12 @@ } glPushAttrib(GL_ENABLE_BIT); - glDisable(GL_CULL_FACE); + if (dps->isBackfaceCullingEnabled()) { + glEnable(GL_CULL_FACE); + } + else { + glDisable(GL_CULL_FACE); + } this->drawSurfaceTrianglesWithVertexArrays(surface, nodeColoringRGBA); glPopAttrib(); @@ -2017,9 +2376,10 @@ glDisable(GL_POLYGON_OFFSET_FILL); } - if (blendingEnabled == false) { + if ( ! blendingEnabled) { glDisable(GL_BLEND); } + } break; } @@ -2029,7 +2389,6 @@ drawSurfaceNormalVectors(surface); } this->drawSurfaceBorders(surface); - this->drawSurfaceFoci(surface); this->drawSurfaceNodeAttributes(surface); this->drawSurfaceBorderBeingDrawn(surface); @@ -2037,7 +2396,8 @@ * Draw annotations for this surface and maybe draw * the model annotations. */ - const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -2045,14 +2405,17 @@ this->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); std::vector emptyColorBars; std::vector emptyViewportAnnotations; - + std::vector emptyScaleBars; + m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, + emptyScaleBars, emptyViewportAnnotations, surface, surfaceScaling); @@ -2060,6 +2423,7 @@ m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, + emptyScaleBars, emptyViewportAnnotations, annotationDrawingNullSurface, annotationDrawingUnusedSurfaceScaling); @@ -2073,10 +2437,17 @@ */ glShadeModel(GL_FLAT); if (drawingType != SurfaceDrawingTypeEnum::DRAW_HIDE) { + /* + * 15sep2020 - Disable culling to fix identification + * problems on surfaces with clockwise oriented triangles + */ + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_CULL_FACE); this->drawSurfaceNodes(surface, nodeColoringRGBA); this->drawSurfaceTriangles(surface, nodeColoringRGBA); + glPopAttrib(); } this->disableClippingPlanes(); @@ -2092,7 +2463,8 @@ * Draw annotations for this surface and maybe draw * the model annotations. */ - const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -2100,12 +2472,15 @@ this->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); std::vector emptyColorBars; std::vector emptyViewportAnnotations; + std::vector emptyScaleBars; m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, + emptyScaleBars, emptyViewportAnnotations, surface, surfaceScaling); @@ -2113,6 +2488,7 @@ m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, + emptyScaleBars, emptyViewportAnnotations, annotationDrawingNullSurface, annotationDrawingUnusedSurfaceScaling); @@ -2744,8 +3120,6 @@ } } - const float symbolDiameter = nodeID.getSymbolSize(); - uint8_t symbolRGBA[4]; if (isSelect) { @@ -2766,6 +3140,20 @@ } symbolRGBA[3] = 255; + float symbolDiameter = nodeID.getSymbolSize(); + switch (nodeID.getIdentificationSymbolSizeType()) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + { + BoundingBox boundingBox; + surface->getBounds(boundingBox); + const float maxDiff(boundingBox.getMaximumDifferenceOfXYZ()); + symbolDiameter = maxDiff * (symbolDiameter / 100.0); + } + break; + } + /* * Need to draw each symbol independently since each symbol * contains a unique size (diameter) @@ -3133,39 +3521,47 @@ glDisable(GL_LIGHTING); if (pointsIdentificationPrimitive) { - pointsIdentificationPrimitive->setPointDiameter(GraphicsPrimitive::PointSizeType::MILLIMETERS, - pointDiameter); - pointsIdentificationPrimitive->setSphereDiameter(GraphicsPrimitive::SphereSizeType::MILLIMETERS, - pointDiameter); - GraphicsEngineDataOpenGL::draw(pointsIdentificationPrimitive.get()); + if (pointsIdentificationPrimitive->isValid()) { + pointsIdentificationPrimitive->setPointDiameter(GraphicsPrimitive::PointSizeType::MILLIMETERS, + pointDiameter); + pointsIdentificationPrimitive->setSphereDiameter(GraphicsPrimitive::SphereSizeType::MILLIMETERS, + pointDiameter); + GraphicsEngineDataOpenGL::draw(pointsIdentificationPrimitive.get()); + } } else if (pointsPrimitive) { - pointsPrimitive->setPointDiameter(GraphicsPrimitive::PointSizeType::MILLIMETERS, - pointDiameter); - pointsPrimitive->setSphereDiameter(GraphicsPrimitive::SphereSizeType::MILLIMETERS, - pointDiameter); - if (pointsPrimitive->getPrimitiveType() == GraphicsPrimitive::PrimitiveType::SPHERES) { - glEnable(GL_LIGHTING); - } - GraphicsEngineDataOpenGL::draw(pointsPrimitive.get()); - if (firstPointPrimitive->isValid()) { - GraphicsEngineDataOpenGL::draw(firstPointPrimitive.get()); - } - if (lastPointPrimitive->isValid()) { - GraphicsEngineDataOpenGL::draw(lastPointPrimitive.get()); + if (pointsPrimitive->isValid()) { + pointsPrimitive->setPointDiameter(GraphicsPrimitive::PointSizeType::MILLIMETERS, + pointDiameter); + pointsPrimitive->setSphereDiameter(GraphicsPrimitive::SphereSizeType::MILLIMETERS, + pointDiameter); + if (pointsPrimitive->getPrimitiveType() == GraphicsPrimitive::PrimitiveType::SPHERES) { + glEnable(GL_LIGHTING); + } + GraphicsEngineDataOpenGL::draw(pointsPrimitive.get()); + if (firstPointPrimitive->isValid()) { + GraphicsEngineDataOpenGL::draw(firstPointPrimitive.get()); + } + if (lastPointPrimitive->isValid()) { + GraphicsEngineDataOpenGL::draw(lastPointPrimitive.get()); + } } } if (linesIdentificationPrimitive) { - glDisable(GL_LIGHTING); - linesIdentificationPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, - lineWidth); - GraphicsEngineDataOpenGL::draw(linesIdentificationPrimitive.get()); + if (linesIdentificationPrimitive->isValid()) { + glDisable(GL_LIGHTING); + linesIdentificationPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, + lineWidth); + GraphicsEngineDataOpenGL::draw(linesIdentificationPrimitive.get()); + } } else if (linesPrimitive) { - glDisable(GL_LIGHTING); - linesPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, - lineWidth); - GraphicsEngineDataOpenGL::draw(linesPrimitive.get()); + if (linesPrimitive->isValid()) { + glDisable(GL_LIGHTING); + linesPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, + lineWidth); + GraphicsEngineDataOpenGL::draw(linesPrimitive.get()); + } } glPopAttrib(); @@ -3213,11 +3609,30 @@ /** * Draw foci on a surface. * @param surface - * Surface on which foci are drawn. + * Surface on which foci are drawn (may be NULL) */ void BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) { + const DisplayPropertiesFoci* fociDisplayProperties = m_brain->getDisplayPropertiesFoci(); + const DisplayGroupEnum::Enum displayGroup = fociDisplayProperties->getDisplayGroupForTab(this->windowTabIndex); + const FociDrawingProjectionTypeEnum::Enum drawingProjectionType = fociDisplayProperties->getDrawingProjectionType(displayGroup, + this->windowTabIndex); + switch (drawingProjectionType) { + case FociDrawingProjectionTypeEnum::PROJECTED: + if (surface == NULL) { + return; + } + break; + case FociDrawingProjectionTypeEnum::STEREOTAXIC: + break; + } + + if (fociDisplayProperties->isDisplayed(displayGroup, + this->windowTabIndex) == false) { + return; + } + SelectionItemFocusSurface* idFocus = m_brain->getSelectionManager()->getSurfaceFocusIdentification(); /* @@ -3241,20 +3656,30 @@ break; } - Brain* brain = surface->getBrainStructure()->getBrain(); - const DisplayPropertiesFoci* fociDisplayProperties = brain->getDisplayPropertiesFoci(); - const DisplayGroupEnum::Enum displayGroup = fociDisplayProperties->getDisplayGroupForTab(this->windowTabIndex); - - if (fociDisplayProperties->isDisplayed(displayGroup, - this->windowTabIndex) == false) { - return; + float focusDiameter(1.0); + switch (fociDisplayProperties->getFociSymbolSizeType(displayGroup, + this->windowTabIndex)) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + focusDiameter = fociDisplayProperties->getFociSizeMillimeters(displayGroup, + this->windowTabIndex); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + { + focusDiameter = fociDisplayProperties->getFociSizePercentage(displayGroup, + this->windowTabIndex); + BoundingBox boundingBox; + surface->getBounds(boundingBox); + const float maxDiff(boundingBox.getMaximumDifferenceOfXYZ()); + focusDiameter = maxDiff * (focusDiameter / 100.0); + } + break; } - const float focusDiameter = fociDisplayProperties->getFociSize(displayGroup, - this->windowTabIndex); const FeatureColoringTypeEnum::Enum fociColoringType = fociDisplayProperties->getColoringType(displayGroup, this->windowTabIndex); - const StructureEnum::Enum surfaceStructure = surface->getStructure(); + const StructureEnum::Enum surfaceStructure = ((surface != NULL) + ? surface->getStructure() + : StructureEnum::INVALID); const StructureEnum::Enum surfaceContralateralStructure = StructureEnum::getContralateralStructure(surfaceStructure); const bool doClipping = isFeatureClippingEnabled(); @@ -3288,9 +3713,9 @@ const bool isContralateralEnabled = fociDisplayProperties->isContralateralDisplayed(displayGroup, this->windowTabIndex); - const int32_t numFociFiles = brain->getNumberOfFociFiles(); + const int32_t numFociFiles = m_brain->getNumberOfFociFiles(); for (int32_t i = 0; i < numFociFiles; i++) { - FociFile* fociFile = brain->getFociFile(i); + FociFile* fociFile = m_brain->getFociFile(i); const GroupAndNameHierarchyModel* classAndNameSelection = fociFile->getGroupAndNameHierarchyModel(); if (classAndNameSelection->isSelected(displayGroup, @@ -3358,52 +3783,70 @@ for (int32_t k = 0; k < numProjections; k++) { const SurfaceProjectedItem* spi = focus->getProjection(k); float xyz[3]; - if (spi->getProjectedPosition(*surface, - xyz, - isPasteOntoSurface)) { - const StructureEnum::Enum focusStructure = spi->getStructure(); - bool drawIt = false; - if (focusStructure == surfaceStructure) { - drawIt = true; - } - else if (focusStructure == StructureEnum::INVALID) { - drawIt = true; - } - else if (isContralateralEnabled) { - if (focusStructure == surfaceContralateralStructure) { + bool drawIt = false; + + switch (drawingProjectionType) { + case FociDrawingProjectionTypeEnum::PROJECTED: + if (spi->getProjectedPosition(surface, /* NULL is okay for this method */ + xyz, + isPasteOntoSurface)) { + const StructureEnum::Enum focusStructure = spi->getStructure(); + if (focusStructure == surfaceStructure) { + drawIt = true; + } + else if (focusStructure == StructureEnum::INVALID) { + drawIt = true; + } + else if (isContralateralEnabled) { + if (focusStructure == surfaceContralateralStructure) { + drawIt = true; + } + } + else if (surface == NULL) { + /* + * This is a special case for ALL view with + * neither surfaces nor volumes displayed + */ + drawIt = true; + } + + if (doClipping) { + if ( ! isCoordinateInsideClippingPlanesForStructure(surfaceStructure, + xyz)) { + drawIt = false; + } + } + } + break; + case FociDrawingProjectionTypeEnum::STEREOTAXIC: + if (spi->isStereotaxicXYZValid()) { + spi->getStereotaxicXYZ(xyz); drawIt = true; } - } + break; + } - if (doClipping) { - if ( ! isCoordinateInsideClippingPlanesForStructure(surfaceStructure, - xyz)) { - drawIt = false; - } + if (drawIt) { + if (isSelect) { + uint8_t idRGBA[4]; + this->colorIdentification->addItem(idRGBA, + SelectionItemDataTypeEnum::FOCUS_SURFACE, + i, /* file index */ + j, /* focus index */ + k);/* projection index */ + idRGBA[3] = 255; + fociPrimitive->addVertex(xyz, idRGBA); } - - if (drawIt) { - if (isSelect) { - uint8_t idRGBA[4]; - this->colorIdentification->addItem(idRGBA, - SelectionItemDataTypeEnum::FOCUS_SURFACE, - i, /* file index */ - j, /* focus index */ - k);/* projection index */ - idRGBA[3] = 255; - fociPrimitive->addVertex(xyz, idRGBA); - } - else { - const uint8_t rgbaByte[4] = { - static_cast(rgbaFloat[0] * 255), - static_cast(rgbaFloat[1] * 255), - static_cast(rgbaFloat[2] * 255), - static_cast(rgbaFloat[3] * 255) - }; - fociPrimitive->addVertex(xyz, rgbaByte); - } + else { + const uint8_t rgbaByte[4] = { + static_cast(rgbaFloat[0] * 255), + static_cast(rgbaFloat[1] * 255), + static_cast(rgbaFloat[2] * 255), + static_cast(rgbaFloat[3] * 255) + }; + fociPrimitive->addVertex(xyz, rgbaByte); } - } + } } } } @@ -3416,8 +3859,12 @@ disableLighting(); } - GraphicsEngineDataOpenGL::draw(fociPrimitive.get()); - + if (fociPrimitive) { + if (fociPrimitive->isValid()) { + GraphicsEngineDataOpenGL::draw(fociPrimitive.get()); + } + } + if (isSelect) { int32_t fociFileIndex = -1; int32_t focusIndex = -1; @@ -3432,10 +3879,10 @@ depth); if (fociFileIndex >= 0) { if (idFocus->isOtherScreenDepthCloserToViewer(depth)) { - Focus* focus = brain->getFociFile(fociFileIndex)->getFocus(focusIndex); - idFocus->setBrain(brain); + Focus* focus = m_brain->getFociFile(fociFileIndex)->getFocus(focusIndex); + idFocus->setBrain(m_brain); idFocus->setFocus(focus); - idFocus->setFociFile(brain->getFociFile(fociFileIndex)); + idFocus->setFociFile(m_brain->getFociFile(fociFileIndex)); idFocus->setFocusIndex(focusIndex); idFocus->setFocusProjectionIndex(focusProjectionIndex); idFocus->setSurface(surface); @@ -3454,7 +3901,6 @@ glPopAttrib(); } - /** * Draw borders on a surface. * @param surface @@ -3814,12 +4260,6 @@ break; } - /* - * Allow blending of volume slices and volume surface outline - */ - glPushAttrib(GL_COLOR_BUFFER_BIT); - applyVolumePropertiesOpacity(); - if (useNewDrawingFlag) { if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TEXTURE_VOLUME)) { BrainOpenGLVolumeTextureSliceDrawing textureSliceDrawing; @@ -3863,8 +4303,6 @@ viewport); } } - - glPopAttrib(); } /** @@ -3881,6 +4319,7 @@ * are to be drawn as 3D Voxel Cubes. */ std::vector volumeDrawInfo; + std::vector volumeDrawInfoOutsideFaces; for (std::vector::iterator iter = volumeDrawInfoIn.begin(); iter != volumeDrawInfoIn.end(); iter++) { @@ -3895,10 +4334,35 @@ break; } if (useIt) { - volumeDrawInfo.push_back(vdi); + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_VOXEL_CUBES_TEST)) { + if (vdi.opacity >= 1.0) { + /* + * When opacity is one, there is no need to draw "interior" + * voxels because they cannot be seen and this may result + * in faster drawing + */ + volumeDrawInfoOutsideFaces.push_back(vdi); + } + else { + /* + * Need interior voxels when opacity less than one + * sincd we can see through the exterior voxels + */ + volumeDrawInfo.push_back(vdi); + } + } + else { + volumeDrawInfo.push_back(vdi); + } } } + if ( ! volumeDrawInfoOutsideFaces.empty()) { + glPushAttrib(GL_ENABLE_BIT); + drawVolumeVoxelsAsCubesWholeBrainOutsideFaces(volumeDrawInfoOutsideFaces); + glPopAttrib(); + } + const int32_t numberOfVolumesToDraw = static_cast(volumeDrawInfo.size()); if (numberOfVolumesToDraw <= 0) { return; @@ -3963,15 +4427,24 @@ identificationIndices.reserve(10000 * idPerVoxelCount); } + const DisplayPropertiesVolume* dsv = m_brain->getDisplayPropertiesVolume(); + CaretAssert(dsv); + const bool transparencyActiveFlag(dsv->getOpacity() < 1.0); + if (transparencyActiveFlag) { + applyVolumePropertiesOpacity(); + } + for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { VolumeDrawInfo& volInfo = volumeDrawInfo[iVol]; - if (volInfo.opacity < 1.0) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - else { - glDisable(GL_BLEND); + if ( ! transparencyActiveFlag) { + if (volInfo.opacity < 1.0) { + setupBlending(BlendDataType::VOLUME_ALL_VIEW_CUBES); + } + else { + glDisable(GL_BLEND); + } } + const VolumeMappableInterface* volumeFile = volInfo.volumeFile; int64_t dimI, dimJ, dimK, numMaps, numComponents; volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); @@ -3991,7 +4464,7 @@ * Scale the cube slightly larger to avoid cracks, particularly if * a single slice is drawn. */ - const float cubeScale = 1.10; + const float cubeScale = 1.01; const float cubeSizeDX = std::fabs(dx) * cubeScale; const float cubeSizeDY = std::fabs(dy) * cubeScale; const float cubeSizeDZ = std::fabs(dz) * cubeScale; @@ -4009,6 +4482,7 @@ glDisable(GL_LIGHTING); } + int64_t count(0); uint8_t rgba[4]; for (int64_t iVoxel = 0; iVoxel < dimI; iVoxel++) { for (int64_t jVoxel = 0; jVoxel < dimJ; jVoxel++) { @@ -4033,57 +4507,731 @@ rgba); } if (rgba[3] > 0) { - if (volInfo.opacity < 1.0) { - rgba[3] *= volInfo.opacity; + if (volInfo.opacity < 1.0) { + rgba[3] *= volInfo.opacity; + } + if (rgba[3] > 0) { + if (isSelect) { + const int32_t idIndex = identificationIndices.size() / idPerVoxelCount; + this->colorIdentification->addItem(rgba, + SelectionItemDataTypeEnum::VOXEL, + idIndex); + identificationIndices.push_back(iVol); + identificationIndices.push_back(volInfo.mapIndex); + identificationIndices.push_back(iVoxel); + identificationIndices.push_back(jVoxel); + identificationIndices.push_back(kVoxel); + } + + float x = 0, y = 0.0, z = 0.0; + volumeFile->indexToSpace(iVoxel, jVoxel, kVoxel, x, y, z); + + bool drawIt = true; + if (doClipping) { + const float xyz[3] = { x, y, z }; + if ( ! isCoordinateInsideClippingPlanesForStructure(StructureEnum::ALL, + xyz)) { + drawIt = false; + } + } + + if (drawIt) { + glPushMatrix(); + glTranslatef(x, y, z); + switch (volInfo.wholeBrainVoxelDrawingMode) { + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_THREE_D_CUBES: + drawCuboid(rgba, cubeSizeDX, cubeSizeDY, cubeSizeDZ); + break; + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES: + drawRoundedCuboid(rgba, cubeSizeDX, cubeSizeDY, cubeSizeDZ); + break; + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES: + break; + } + glPopMatrix(); + count++; + } + } + } + } + } + } + } + + if (isSelect) { + int32_t identifiedItemIndex; + float depth = -1.0; + this->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL, + this->mouseX, + this->mouseY, + identifiedItemIndex, + depth); + if (identifiedItemIndex >= 0) { + const int32_t idIndex = identifiedItemIndex * idPerVoxelCount; + const int32_t volDrawInfoIndex = identificationIndices[idIndex]; + CaretAssertVectorIndex(volumeDrawInfo, volDrawInfoIndex); + VolumeMappableInterface* vf = volumeDrawInfo[volDrawInfoIndex].volumeFile; + const int64_t voxelIndices[3] = { + identificationIndices[idIndex + 2], + identificationIndices[idIndex + 3], + identificationIndices[idIndex + 4] + }; + + if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { + voxelID->setVoxelIdentification(m_brain, + vf, + voxelIndices, + depth); + + float voxelCoordinates[3]; + vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], + voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); + + this->setSelectedItemScreenXYZ(voxelID, + voxelCoordinates); + CaretLogFine("Selected Voxel (3D): " + AString::fromNumbers(voxelIndices, 3, ",")); + } + } + } + + this->disableLighting(); + glShadeModel(GL_SMOOTH); + glDisable(GL_BLEND); +} + +//#define NORMAL_TEST 1 +#ifdef NORMAL_TEST +/* + * Verify normal pointing correctly + */ +static void nt(float a1, float a2, float a3, + float b1, float b2, float b3, + float c1, float c2, float c3, + const float normal[3], + const AString& name) +{ + const float a[3] = {a1,a2,a3}; + const float b[3] = {b1,b2,b3}; + const float c[3] = {c1,c2,c3}; + float n[3]; + MathFunctions::normalVector(a, b, c, n); + float t1 = normal[0] * n[0]; + float t2 = normal[1] * n[1]; + float t3 = normal[2] * n[2]; + if (t1 < 0.0) { + std::cout << name << " X bad" << std::endl; + } + if (t2 < 0.0) { + std::cout << name << " Y bad" << std::endl; + } + if (t3 < 0.0) { + std::cout << name << " Z bad" << std::endl; + } + float n2[3] = { normal[0], normal[1], normal[2] }; + if (MathFunctions::normalizeVector(n2) <= 0.0) { + std::cout << name << " Input normal invalid" << std::endl; + } + if (MathFunctions::normalizeVector(n) <= 0.0) { + std::cout << name << " Calculated normal invalid" << std::endl; + } +} +#endif + +/** + * Draw volumes a voxel cubes for whole brain view but only outside faces + * + * @param volumeDrawInfoIn + * Describes volumes that are drawn. + */ +void +BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrainOutsideFaces(std::vector& volumeDrawInfoIn) +{ + /* + * Filter volumes for drawing and only draw those volumes that + * are to be drawn as 3D Voxel Cubes. + */ + std::vector volumeDrawInfo; + for (std::vector::iterator iter = volumeDrawInfoIn.begin(); + iter != volumeDrawInfoIn.end(); + iter++) { + bool useIt = false; + VolumeDrawInfo& vdi = *iter; + switch (vdi.wholeBrainVoxelDrawingMode) { + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_THREE_D_CUBES: + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES: + useIt = true; + break; + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES: + break; + } + if (useIt) { + volumeDrawInfo.push_back(vdi); + } + } + + const int32_t numberOfVolumesToDraw = static_cast(volumeDrawInfo.size()); + if (numberOfVolumesToDraw <= 0) { + return; + } + + /* + * Check for a 'selection' type mode + */ + SelectionItemVoxel* voxelID = + m_brain->getSelectionManager()->getVoxelIdentification(); + bool isSelect = false; + switch (this->mode) { + case MODE_DRAWING: + break; + case MODE_IDENTIFICATION: + if (voxelID->isEnabledForSelection()) { + isSelect = true; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + else { + return; + } + break; + case MODE_PROJECTION: + return; + break; + } + + /* + * When selecting turn on lighting and shading since + * colors are used for identification. + */ + if (isSelect) { + this->disableLighting(); + glShadeModel(GL_FLAT); + } + else { + this->enableLighting(); + glShadeModel(GL_SMOOTH); + } + + glEnable(GL_CULL_FACE); + + const bool doClipping = isFeatureClippingEnabled(); + + const DisplayPropertiesLabels* dsl = m_brain->getDisplayPropertiesLabels(); + const DisplayGroupEnum::Enum displayGroup = dsl->getDisplayGroupForTab(this->windowTabIndex); + + /* + * For identification, five items per voxel + * 1) volume index + * 2) map index + * 3) index I + * 4) index J + * 5) index K + */ + const int32_t idPerVoxelCount = 5; + std::vector identificationIndices; + if (isSelect) { + identificationIndices.reserve(10000 * idPerVoxelCount); + } + + /* + * Normals pointing along XYZ axes + */ + const float minXNormal[3] = { -1.0, 0.0, 0.0 }; + const float maxXNormal[3] = { 1.0, 0.0, 0.0 }; + const float minYNormal[3] = { 0.0, -1.0, 0.0 }; + const float maxYNormal[3] = { 0.0, 1.0, 0.0 }; + const float minZNormal[3] = { 0.0, 0.0, -1.0 }; + const float maxZNormal[3] = { 0.0, 0.0, 1.0 }; + + /* + * Normals starting in center of cube and pointing + * out through corners. Last three letters are + * the orientations (L=Left, R=Right, P=Posterior, + * A=Anterior, I=Inferior, S=Superior). + */ + float normalLPI[3] { -1.0, -1.0, -1.0 }; + MathFunctions::normalizeVector(normalLPI); + float normalLAI[3] { -1.0, 1.0, -1.0 }; + MathFunctions::normalizeVector(normalLAI); + float normalLPS[3] { -1.0, -1.0, 1.0 }; + MathFunctions::normalizeVector(normalLPS); + float normalLAS[3] { -1.0, 1.0, 1.0 }; + MathFunctions::normalizeVector(normalLAS); + float normalRPI[3] { 1.0, -1.0, -1.0 }; + MathFunctions::normalizeVector(normalRPI); + float normalRAI[3] { 1.0, 1.0, -1.0 }; + MathFunctions::normalizeVector(normalRAI); + float normalRPS[3] { 1.0, -1.0, 1.0 }; + MathFunctions::normalizeVector(normalRPS); + float normalRAS[3] { 1.0, 1.0, 1.0 }; + MathFunctions::normalizeVector(normalRAS); + + const DisplayPropertiesVolume* dsv = m_brain->getDisplayPropertiesVolume(); + CaretAssert(dsv); + const bool transparencyActiveFlag(dsv->getOpacity() < 1.0); + if (transparencyActiveFlag) { + applyVolumePropertiesOpacity(); + } + + for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { + VolumeDrawInfo& volInfo = volumeDrawInfo[iVol]; + if ( ! transparencyActiveFlag) { + if (volInfo.opacity < 1.0) { + setupBlending(BlendDataType::VOLUME_ALL_VIEW_CUBES); + } + else { + glDisable(GL_BLEND); + } + } + + bool roundedCubesFlag(false); + switch (volInfo.wholeBrainVoxelDrawingMode) { + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_THREE_D_CUBES: + break; + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES: + roundedCubesFlag = true; + break; + case WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES: + CaretAssert(0); + break; + } + + const VolumeMappableInterface* volumeFile = volInfo.volumeFile; + int64_t dimI, dimJ, dimK, numMaps, numComponents; + volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); + + /* + * Cube size for voxel drawing. Some volumes may have a right to left + * orientation in which case dx may be negative. + * + * Scale the cube slightly larger to avoid cracks, particularly if + * a single slice is drawn. To match the old drawing, cubeScale's + * decimal portion should be double (old draws a cube and this + * method draws triangles) + */ + const float cubeScale = 1.02; + + float originX, originY, originZ; + float x1, y1, z1; + volumeFile->indexToSpace(0, 0, 0, originX, originY, originZ); + volumeFile->indexToSpace(1, 1, 1, x1, y1, z1); + const float dx = x1 - originX; + const float dy = y1 - originY; + const float dz = z1 - originZ; + const float halfAbsX(std::fabs((dx * cubeScale) / 2.0)); + const float halfAbsY(std::fabs((dy * cubeScale) / 2.0)); + const float halfAbsZ(std::fabs((dz * cubeScale) / 2.0)); + + const bool xRightFlag(dx > 0.0); + const bool yAnteriorFlag(dy > 0.0); + const bool zSuperiorFlag(dz > 0.0); + + if (xRightFlag + && yAnteriorFlag + && zSuperiorFlag) { + glEnable(GL_CULL_FACE); + } + else { + /* + * 19 Nov 2020 + * Disable culling as there are problems with Right-to-Left orientations + * and to truly fix it requires rewriting drawing voxels as cubes. + * Another reason to rewrite drawing voxels as cubes as it probably will + * not work for oblique volumes. + */ + glDisable(GL_CULL_FACE); + } + + std::vector labelMapData; + const CiftiBrainordinateLabelFile* ciftiLabelFile = dynamic_cast(volumeFile); + if (ciftiLabelFile != NULL) { + ciftiLabelFile->getMapData(volInfo.mapIndex, + labelMapData); + } + + if ((dimI == 1) + || (dimJ == 1) + || (dimK == 1)) { + glDisable(GL_LIGHTING); + } + + const int64_t numAxialSliceVoxels(dimI * dimJ); + const int64_t numVoxels(numAxialSliceVoxels * dimK); + const int64_t numAxialSizeRGBA(numAxialSliceVoxels * 4); + const int64_t numRGBA(numVoxels * 4); + std::vector axialSliceRGBA(numAxialSizeRGBA); + std::vector volumeRGBA(numRGBA, 0); + + /* + * Get coloring for all voxels in volume + */ + for (int64_t kVoxel = 0; kVoxel < dimK; kVoxel++) { + /* + * Voxel coloring for axial slice + */ + volumeFile->getVoxelColorsForSliceInMap(volInfo.mapIndex, + VolumeSliceViewPlaneEnum::AXIAL, + kVoxel, + displayGroup, + this->windowTabIndex, + axialSliceRGBA.data()); + /* + * Apply layer opacity + */ + if (volInfo.opacity < 1.0) { + for (int64_t m = 0; m < numAxialSliceVoxels; m++) { + CaretAssertVectorIndex(axialSliceRGBA, m * 4 + 3); + axialSliceRGBA[m * 4 + 3] *= volInfo.opacity; + } + } + + /* + * Copy axial slice RGBA to Volume RGBA + */ + const int64_t sliceOffset(numAxialSizeRGBA * kVoxel); + std::copy(axialSliceRGBA.begin(), + axialSliceRGBA.end(), + volumeRGBA.begin() + sliceOffset); + } + + /* + * Graphics Primitive for drawing voxels + */ + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3fN3fC4ub(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES)); + + /* + * Loop through the voxels + */ + int64_t count(0); + uint8_t rgba[4]; + for (int64_t iVoxel = 0; iVoxel < dimI; iVoxel++) { + for (int64_t jVoxel = 0; jVoxel < dimJ; jVoxel++) { + for (int64_t kVoxel = 0; kVoxel < dimK; kVoxel++) { + const int64_t offsetRGBA((numAxialSizeRGBA * kVoxel) + + (dimI * jVoxel * 4) + + (iVoxel * 4)); + CaretAssertVectorIndex(volumeRGBA, offsetRGBA + 3); + rgba[3] = volumeRGBA[offsetRGBA+3]; + + if (rgba[3] > 0) { + rgba[0] = volumeRGBA[offsetRGBA]; + rgba[1] = volumeRGBA[offsetRGBA+1]; + rgba[2] = volumeRGBA[offsetRGBA+2]; + + float x = 0, y = 0.0, z = 0.0; + volumeFile->indexToSpace(iVoxel, jVoxel, kVoxel, x, y, z); + + if (doClipping) { + const float xyz[3] = { x, y, z }; + if ( ! isCoordinateInsideClippingPlanesForStructure(StructureEnum::ALL, + xyz)) { + continue; + } } - if (rgba[3] > 0) { - if (isSelect) { - const int32_t idIndex = identificationIndices.size() / idPerVoxelCount; - this->colorIdentification->addItem(rgba, - SelectionItemDataTypeEnum::VOXEL, - idIndex); - identificationIndices.push_back(iVol); - identificationIndices.push_back(volInfo.mapIndex); - identificationIndices.push_back(iVoxel); - identificationIndices.push_back(jVoxel); - identificationIndices.push_back(kVoxel); + + bool drawMinXFlag(iVoxel == 0); + bool drawMaxXFlag(iVoxel == (dimI - 1)); + bool drawMinYFlag(jVoxel == 0); + bool drawMaxYFlag(jVoxel == (dimJ - 1)); + bool drawMinZFlag(kVoxel == 0); + bool drawMaxZFlag(kVoxel == (dimK - 1)); + + if (iVoxel > 0) { + const int64_t offset(offsetRGBA - 4); + CaretAssertVectorIndex(volumeRGBA, offset + 3); + if (volumeRGBA[offset + 3] == 0) { + /* Draw side since voxel on left side is off */ + if (xRightFlag) { + drawMinXFlag = true; + } + else { + drawMaxXFlag = true; + } } - - float x = 0, y = 0.0, z = 0.0; - volumeFile->indexToSpace(iVoxel, jVoxel, kVoxel, x, y, z); - - bool drawIt = true; - if (doClipping) { - const float xyz[3] = { x, y, z }; - if ( ! isCoordinateInsideClippingPlanesForStructure(StructureEnum::ALL, - xyz)) { - drawIt = false; + } + if (iVoxel < (dimI - 1)) { + const int64_t offset(offsetRGBA + 4); + CaretAssertVectorIndex(volumeRGBA, offset + 3); + if (volumeRGBA[offset + 3] == 0) { + /* Draw side since voxel on right side is off */ + if (xRightFlag) { + drawMaxXFlag = true; + } + else { + drawMinXFlag = true; } } - - if (drawIt) { - glPushMatrix(); - glTranslatef(x, y, z); - switch (volInfo.wholeBrainVoxelDrawingMode) { - case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_THREE_D_CUBES: - drawCuboid(rgba, cubeSizeDX, cubeSizeDY, cubeSizeDZ); - break; - case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES: - drawRoundedCuboid(rgba, cubeSizeDX, cubeSizeDY, cubeSizeDZ); - break; - case WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES: - break; + } + + if (jVoxel > 0) { + const int64_t offset(offsetRGBA - (dimI * 4)); + CaretAssertVectorIndex(volumeRGBA, offset + 3); + if (volumeRGBA[offset + 3] == 0) { + /* Draw side since voxel on bottom side is off */ + if (yAnteriorFlag) { + drawMinYFlag = true; + } + else { + drawMaxYFlag = true; + } + } + } + if (jVoxel < (dimJ - 1)) { + const int64_t offset(offsetRGBA + (dimI * 4)); + CaretAssertVectorIndex(volumeRGBA, offset + 3); + if (volumeRGBA[offset + 3] == 0) { + /* Draw side since voxel on top side is off */ + if (yAnteriorFlag) { + drawMaxYFlag = true; + } + else { + drawMinYFlag = true; } - glPopMatrix(); } } + + if (kVoxel > 0) { + const int64_t offset(offsetRGBA - numAxialSizeRGBA); + CaretAssertVectorIndex(volumeRGBA, offset + 3); + if (volumeRGBA[offset + 3] == 0) { + /* Draw side since voxel below is off */ + if (zSuperiorFlag) { + drawMinZFlag = true; + } + else { + drawMaxZFlag = true; + } + } + } + if (kVoxel < (dimK - 1)) { + const int64_t offset(offsetRGBA + numAxialSizeRGBA); + CaretAssertVectorIndex(volumeRGBA, offset + 3); + if (volumeRGBA[offset + 3] == 0) { + /* Draw side since voxel above is off */ + if (zSuperiorFlag) { + drawMaxZFlag = true; + } + else { + drawMinZFlag = true; + } + } + } + + if (drawMinXFlag + || drawMaxZFlag + || drawMinYFlag + || drawMaxYFlag + || drawMinZFlag + || drawMaxZFlag) { + count++; + } + const float minX(x - halfAbsX); + const float maxX(x + halfAbsX); + const float minY(y - halfAbsY); + const float maxY(y + halfAbsY); + const float minZ(z - halfAbsZ); + const float maxZ(z + halfAbsZ); + + if (isSelect) { + const int32_t idIndex = identificationIndices.size() / idPerVoxelCount; + this->colorIdentification->addItem(rgba, + SelectionItemDataTypeEnum::VOXEL, + idIndex); + identificationIndices.push_back(iVol); + identificationIndices.push_back(volInfo.mapIndex); + identificationIndices.push_back(iVoxel); + identificationIndices.push_back(jVoxel); + identificationIndices.push_back(kVoxel); + } + +//#define DRAW_SIDE_COLOR_TEST 1 + if (drawMinXFlag) { +#ifdef DRAW_SIDE_COLOR_TEST + uint8_t rgba[4] = { 255, 0, 0, 255 }; +#endif + primitive->addVertex(minX, minY, minZ, + (roundedCubesFlag ? normalLPI : minXNormal), + rgba); + primitive->addVertex(minX, minY, maxZ, + (roundedCubesFlag ? normalLPS : minXNormal), + rgba); + primitive->addVertex(minX, maxY, minZ, + (roundedCubesFlag ? normalLAI :minXNormal), + rgba); +#ifdef NORMAL_TEST + nt(minX, minY, minZ, minX, minY, maxZ, minX, maxY, maxZ, minXNormal, "XMin1"); +#endif + primitive->addVertex(minX, maxY, minZ, + (roundedCubesFlag ? normalLAI : minXNormal), + rgba); + primitive->addVertex(minX, minY, maxZ, + (roundedCubesFlag ? normalLPS : minXNormal), + rgba); + primitive->addVertex(minX, maxY, maxZ, + (roundedCubesFlag ? normalLAS : minXNormal), + rgba); +#ifdef NORMAL_TEST + nt(minX, minY, minZ, minX, maxY, maxZ, minX, maxY, minZ, minXNormal, "XMin2"); +#endif + + } + if (drawMaxXFlag) { +#ifdef DRAW_SIDE_COLOR_TEST + uint8_t rgba[4] = { 0, 255, 0, 255 }; +#endif + primitive->addVertex(maxX, minY, minZ, + (roundedCubesFlag ? normalRPI : maxXNormal), + rgba); + primitive->addVertex(maxX, maxY, minZ, + (roundedCubesFlag ? normalRAI : maxXNormal), + rgba); + primitive->addVertex(maxX, maxY, maxZ, + (roundedCubesFlag ? normalRAS : maxXNormal), + rgba); + // nt(maxX, minY, minZ, maxX, maxY, minZ, maxX, minY, maxZ, maxXNormal, "XMax1"); + primitive->addVertex(maxX, maxY, maxZ, + (roundedCubesFlag ? normalRAS : maxXNormal), + rgba); + primitive->addVertex(maxX, minY, maxZ, + (roundedCubesFlag ? normalRPS : maxXNormal), + rgba); + primitive->addVertex(maxX, minY, minZ, + (roundedCubesFlag ? normalRPI : maxXNormal), + rgba); +#ifdef NORMAL_TEST + nt(maxX, minY, maxZ, maxX, maxY, minZ, maxX, maxY, maxZ, maxXNormal, "XMax2"); +#endif + } + + if (drawMinYFlag) { +#ifdef DRAW_SIDE_COLOR_TEST + uint8_t rgba[4] = { 0, 0, 255, 255 }; +#endif + primitive->addVertex(minX, minY, minZ, + (roundedCubesFlag ? normalLPI : minYNormal), + rgba); + primitive->addVertex(maxX, minY, minZ, + (roundedCubesFlag ? normalRPI : minYNormal), + rgba); + primitive->addVertex(minX, minY, maxZ, + (roundedCubesFlag ? normalLPS : minYNormal), + rgba); +#ifdef NORMAL_TEST + nt(minX, minY, minZ, maxX, minY, minZ, maxX, minY, maxZ, minYNormal, "YMin1"); +#endif + primitive->addVertex(minX, minY, maxZ, + (roundedCubesFlag ? normalLPS : minYNormal), + rgba); + primitive->addVertex(maxX, minY, minZ, + (roundedCubesFlag ? normalRPI : minYNormal), + rgba); + primitive->addVertex(maxX, minY, maxZ, + (roundedCubesFlag ? normalRPS : minYNormal), + rgba); +#ifdef NORMAL_TEST + nt(minX, minY, minZ, maxX, minY, maxZ, minX, minY, maxZ, minYNormal, "YMin2"); +#endif + } + if (drawMaxYFlag) { +#ifdef DRAW_SIDE_COLOR_TEST + uint8_t rgba[4] = { 255, 255, 0, 255 }; +#endif + primitive->addVertex(maxX, maxY, minZ, + (roundedCubesFlag ? normalRAI : maxYNormal), + rgba); + primitive->addVertex(minX, maxY, minZ, + (roundedCubesFlag ? normalLAI : maxYNormal), + rgba); + primitive->addVertex(minX, maxY, maxZ, + (roundedCubesFlag ? normalLAS : maxYNormal), + rgba); +#ifdef NORMAL_TEST + nt(maxX, maxY, minZ, minX, maxY, minZ, minX, maxY, maxZ, maxYNormal, "YMax1"); +#endif + primitive->addVertex(maxX, maxY, minZ, + (roundedCubesFlag ? normalRAI : maxYNormal), + rgba); + primitive->addVertex(minX, maxY, maxZ, + (roundedCubesFlag ? normalLAS : maxYNormal), + rgba); + primitive->addVertex(maxX, maxY, maxZ, + (roundedCubesFlag ? normalRAS : maxYNormal), + rgba); +#ifdef NORMAL_TEST + nt(maxX, maxY, minZ, minX, maxY, maxZ, maxX, maxY, maxZ, maxYNormal, "YMax2"); +#endif + } + + if (drawMinZFlag) { +#ifdef DRAW_SIDE_COLOR_TEST + uint8_t rgba[4] = { 255, 0, 255, 255 }; +#endif + primitive->addVertex(minX, minY, minZ, + (roundedCubesFlag ? normalLPI : minZNormal), + rgba); + primitive->addVertex(minX, maxY, minZ, + (roundedCubesFlag ? normalLAI : minZNormal), + rgba); + primitive->addVertex(maxX, minY, minZ, + (roundedCubesFlag ? normalRPI : minZNormal), + rgba); +#ifdef NORMAL_TEST + nt(minX, minY, minZ,maxX, maxY, minZ,maxX, minY, minZ, minZNormal, "ZMin1"); +#endif + primitive->addVertex(minX, maxY, minZ, + (roundedCubesFlag ? normalLAI : minZNormal), + rgba); + primitive->addVertex(maxX, maxY, minZ, + (roundedCubesFlag ? normalRAI : minZNormal), + rgba); + primitive->addVertex(maxX, minY, minZ, + (roundedCubesFlag ? normalRPI : minZNormal), + rgba); +#ifdef NORMAL_TEST + nt(minX, maxY, minZ, maxX, maxY, minZ, minX, minY, minZ, minZNormal, "ZMin2"); +#endif + } + if (drawMaxZFlag) { +#ifdef DRAW_SIDE_COLOR_TEST + uint8_t rgba[4] = { 0, 255, 255, 255 }; +#endif + primitive->addVertex(minX, minY, maxZ, + (roundedCubesFlag ? normalLPS : maxZNormal), + rgba); + primitive->addVertex(maxX, minY, maxZ, + (roundedCubesFlag ? normalRPS : maxZNormal), + rgba); + primitive->addVertex(maxX, maxY, maxZ, + (roundedCubesFlag ? normalRAS : maxZNormal), + rgba); +#ifdef NORMAL_TEST + nt(minX, minY, maxZ, maxX, minY, maxZ, maxX, maxY, maxZ, maxZNormal, "ZMax1"); +#endif + primitive->addVertex(minX, minY, maxZ, + (roundedCubesFlag ? normalLPS : maxZNormal), + rgba); + primitive->addVertex(maxX, maxY, maxZ, + (roundedCubesFlag ? normalRAS : maxZNormal), + rgba); + primitive->addVertex(minX, maxY, maxZ, + (roundedCubesFlag ? normalLAS : maxZNormal), + rgba); +#ifdef NORMAL_TEST + nt(minX, minY, maxZ, maxX, maxY, maxZ, minX, maxY, maxZ, maxZNormal, "ZMax2"); +#endif + } } } } } + + if (primitive->isValid()) { + GraphicsEngineDataOpenGL::draw(primitive.get()); + } } if (isSelect) { + /* + * Process selection + */ int32_t identifiedItemIndex; float depth = -1.0; this->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL, @@ -4124,6 +5272,7 @@ glDisable(GL_BLEND); } + void BrainOpenGLFixedPipeline::setFiberOrientationDisplayInfo(const DisplayPropertiesFiberOrientation* dpfo, const DisplayGroupEnum::Enum displayGroup, @@ -4140,6 +5289,7 @@ dispInfo.fanMultiplier = dpfo->getFanMultiplier(displayGroup, tabIndex); dispInfo.isDrawWithMagnitude = dpfo->isDrawWithMagnitude(displayGroup, tabIndex); dispInfo.minimumMagnitude = dpfo->getMinimumMagnitude(displayGroup, tabIndex); + dispInfo.maximumUncertainty = dpfo->getMaximumUncertainty(displayGroup, tabIndex); dispInfo.magnitudeMultiplier = dpfo->getLengthMultiplier(displayGroup, tabIndex); dispInfo.plane = plane; dispInfo.structure = structure; @@ -4390,6 +5540,9 @@ if (fiber->m_meanF < fodi->minimumMagnitude) { drawIt = false; } + if (fiber->m_varF > fodi->maximumUncertainty) { + drawIt = false; + } if (drawIt) { float alpha = 1.0; @@ -4663,8 +5816,7 @@ glDisable(GL_CLIP_PLANE4); glDisable(GL_CLIP_PLANE5); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + setupBlending(BlendDataType::FIBER_TRAJECTORIES); CaretAssert(this->browserTabContent); OverlaySet* overlaySet = this->browserTabContent->getOverlaySet(); @@ -5229,6 +6381,10 @@ center, mvp->getProjectionViewType()); + if (ivp == 0) { + setupScaleBarDrawingInformation(browserTabContent); + } + this->drawSurface(mvp->getSurface(), browserTabContent->getScaling(), nodeColoringRGBA, @@ -5242,6 +6398,56 @@ } /** + * While drawing in model space provide information to the scale bar so that it + * can be drawn in the proper size when it is drawn in tab space + * + * @param browserTabContent + * Content of browser tab + */ +void +BrainOpenGLFixedPipeline::setupScaleBarDrawingInformation(BrowserTabContent* browserTabContent) +{ + setupScaleBarDrawingInformation(browserTabContent, + this->orthographicLeft, + this->orthographicRight); +} + +/** + * While drawing in model space provide information to the scale bar so that it + * can be drawn in the proper size when it is drawn in tab space' + * + * @param browserTabContent + * Content of browser tab + * @param orthographicProjectionLeft + * Left side or orthographic projection + * @param orthographicProjectionRight + * Right side or orthographic projection + */ +void +BrainOpenGLFixedPipeline::setupScaleBarDrawingInformation(BrowserTabContent* browserTabContent, + const float orthographicProjectionLeft, + const float orthographicProjectionRight) +{ + CaretAssert(browserTabContent); + + const float scaling = browserTabContent->getScaling(); + if (scaling <= 0.0) { + return; + } + + const float orthoWidth = (std::abs(orthographicProjectionRight - orthographicProjectionLeft) + / browserTabContent->getScaling()); + + browserTabContent->getScaleBar()->setModelSpaceOrthographicWidth(orthoWidth); + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, + viewport); + browserTabContent->getScaleBar()->setModelSpaceViewportWidthAndHeight(viewport[2], + viewport[3]); +} + +/** * Draw the whole brain. * @param browserTabContent * Content of the window. @@ -5337,6 +6543,8 @@ center, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL); + setupScaleBarDrawingInformation(browserTabContent); + const SurfaceTypeEnum::Enum surfaceType = wholeBrainModel->getSelectedSurfaceType(tabNumberIndex); /* @@ -5540,6 +6748,13 @@ glPopMatrix(); } } + + /* + * Special case to draw foci when surfaces are not displayed + */ + if (numSurfaceToDraw <= 0) { + drawSurfaceFoci(NULL); + } } /** @@ -5848,11 +7063,15 @@ break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: - case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: glOrtho(this->orthographicRight, this->orthographicLeft, this->orthographicBottom, this->orthographicTop, this->orthographicFar, this->orthographicNear); break; + case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: + glOrtho(this->orthographicLeft, this->orthographicRight, + this->orthographicBottom, this->orthographicTop, + this->orthographicNear, this->orthographicFar); + break; } // std::cout << "Viewport: " << AString::fromNumbers(viewport, 4, ",") << std::endl; @@ -5908,11 +7127,15 @@ break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: - case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: glOrtho(this->orthographicRight, this->orthographicLeft, this->orthographicBottom, this->orthographicTop, this->orthographicFar, this->orthographicNear); break; + case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: + glOrtho(this->orthographicLeft, this->orthographicRight, + this->orthographicBottom, this->orthographicTop, + this->orthographicNear, this->orthographicFar); + break; } } @@ -6767,8 +7990,7 @@ glGetBooleanv(GL_BLEND, &blendingEnabled); if (useBlendingFlag) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + BrainOpenGLFixedPipeline::setupBlending(BrainOpenGLFixedPipeline::BlendDataType::FEATURE_IMAGE); } /* @@ -6982,6 +8204,249 @@ } } +/** + * When the window aspect is locked, there may be region(s) around the window edges + * that are outside the viewport for the aspect locked window. A manual tile tab + * configuration allows tabs to be partially outside the window. The tab viewport cannot + * be altered because the tab's aspect ratio must be respected. So, after the tabs are + * drawn, fill any regions outside the window aspect locked viewport. + * + * @param windowBeforeAspectLockingViewport + * Window viewport before aspect locking (full size of the OpenGL widget) + * @param windowAfterAspectLockingViewport + * Window viewport after aspect locking + */ +void +BrainOpenGLFixedPipeline::drawSolidBackgroundInAreasOutsideWindowAspectLocking(const int32_t windowBeforeAspectLockingViewport[4], + const int32_t windowAfterAspectLockingViewport[4]) +{ + const int32_t beforeLeft = windowBeforeAspectLockingViewport[0]; + const int32_t beforeRight = beforeLeft + windowBeforeAspectLockingViewport[2]; + + const int32_t afterLeft = windowAfterAspectLockingViewport[0]; + const int32_t afterRight = afterLeft + windowAfterAspectLockingViewport[2]; + + const int32_t beforeBottom = windowBeforeAspectLockingViewport[1]; + const int32_t beforeTop = beforeBottom + windowBeforeAspectLockingViewport[3]; + + const int32_t afterBottom = windowAfterAspectLockingViewport[1]; + const int32_t afterTop = afterBottom + windowAfterAspectLockingViewport[3]; + + /* + * Null will result in window colors for background and foreground + */ + updateForegroundAndBackgroundColors(NULL); + + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES, + m_backgroundColorFloat)); + + if (afterLeft > beforeLeft) { + /* Left */ + primitive->addVertex(beforeLeft, beforeBottom); + primitive->addVertex(afterLeft, beforeBottom); + primitive->addVertex(beforeLeft, beforeTop); + primitive->addVertex(beforeLeft, beforeTop); + primitive->addVertex(afterLeft, beforeBottom); + primitive->addVertex(afterLeft, beforeTop); + } + if (beforeRight > afterRight) { + /* Right */ + primitive->addVertex(afterRight, beforeBottom); + primitive->addVertex(beforeRight, beforeBottom); + primitive->addVertex(afterRight, beforeTop); + primitive->addVertex(afterRight, beforeTop); + primitive->addVertex(beforeRight, beforeBottom); + primitive->addVertex(beforeRight, beforeTop); + } + if (afterBottom > beforeBottom) { + /* Bottom */ + primitive->addVertex(beforeLeft, beforeBottom); + primitive->addVertex(beforeRight, beforeBottom); + primitive->addVertex(beforeLeft, afterBottom); + primitive->addVertex(beforeLeft, afterBottom); + primitive->addVertex(beforeRight, beforeBottom); + primitive->addVertex(beforeRight, afterBottom); + } + if (beforeTop > afterTop) { + /* Top */ + primitive->addVertex(beforeLeft, afterTop); + primitive->addVertex(beforeRight, afterTop); + primitive->addVertex(beforeLeft, beforeTop); + primitive->addVertex(beforeLeft, beforeTop); + primitive->addVertex(beforeRight, afterTop); + primitive->addVertex(beforeRight, beforeTop); + } + + if (primitive->getNumberOfVertices() > 0) { + glPushAttrib(GL_DEPTH_BUFFER_BIT + | GL_PIXEL_MODE_BIT + | GL_POLYGON_BIT + | GL_POLYGON_STIPPLE_BIT + | GL_VIEWPORT_BIT + | GL_TRANSFORM_BIT); + glEnable(GL_DEPTH_TEST); + glViewport(windowBeforeAspectLockingViewport[0], + windowBeforeAspectLockingViewport[1], + windowBeforeAspectLockingViewport[2], + windowBeforeAspectLockingViewport[3]); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(beforeLeft, beforeRight, beforeBottom, beforeTop, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glDepthFunc(GL_ALWAYS); + GraphicsEngineDataOpenGL::draw(primitive.get()); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopAttrib(); + } +} + +/** + * When the window aspect is locked, there may be region(s) around the window edges + * that are outside the viewport for the aspect locked window. A manual tile tab + * configuration allows tabs to be partially outside the window. The tab viewport cannot + * be altered because the tab's aspect ratio must be respected. So, after the tabs are + * drawn, fill any regions outside the window aspect locked viewport. + * + * @param windowBeforeAspectLockingViewport + * Window viewport before aspect locking (full size of the OpenGL widget) + * @param windowAfterAspectLockingViewport + * Window viewport after aspect locking + */ +void +BrainOpenGLFixedPipeline::drawStippledBackgroundInAreasOutsideWindowAspectLocking(const int32_t windowBeforeAspectLockingViewport[4], + const int32_t windowAfterAspectLockingViewport[4]) +{ + const int32_t beforeLeft = windowBeforeAspectLockingViewport[0]; + const int32_t beforeRight = beforeLeft + windowBeforeAspectLockingViewport[2]; + + const int32_t afterLeft = windowAfterAspectLockingViewport[0]; + const int32_t afterRight = afterLeft + windowAfterAspectLockingViewport[2]; + + const int32_t beforeBottom = windowBeforeAspectLockingViewport[1]; + const int32_t beforeTop = beforeBottom + windowBeforeAspectLockingViewport[3]; + + const int32_t afterBottom = windowAfterAspectLockingViewport[1]; + const int32_t afterTop = afterBottom + windowAfterAspectLockingViewport[3]; + + /* + * Null will result in window colors for background and foreground + */ + updateForegroundAndBackgroundColors(NULL); + + + const int32_t invertedRGBA[4] = { + 255 - m_backgroundColorByte[0], + 255 - m_backgroundColorByte[1], + 255 - m_backgroundColorByte[2], + m_backgroundColorByte[3], + }; + + const int32_t textureDim = 8; + const int32_t textureSize = textureDim * textureDim * 4; + std::vector textureRGBA(textureSize); + for (int32_t j = 0; j < textureDim; j++) { + for (int32_t i = 0; i < textureDim; i++) { + const int32_t offset(((textureDim * j) + i) * 4); + CaretAssertArrayIndex(textureRGB, textureSize, offset+2); + if (i == j) { + textureRGBA[offset] = invertedRGBA[0]; + textureRGBA[offset+1] = invertedRGBA[1]; + textureRGBA[offset+2] = invertedRGBA[2]; + textureRGBA[offset+3] = invertedRGBA[3]; + } + else { + textureRGBA[offset] = m_backgroundColorByte[0]; + textureRGBA[offset+1] = m_backgroundColorByte[1]; + textureRGBA[offset+2] = m_backgroundColorByte[2]; + textureRGBA[offset+3] = m_backgroundColorByte[3]; + } + } + } + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3fT3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES, + &textureRGBA[0], + textureDim, + textureDim, + GraphicsPrimitive::TextureWrappingType::REPEAT, + GraphicsPrimitive::TextureFilteringType::LINEAR)); + + if (afterLeft > beforeLeft) { + /* Left */ + const float maxS = static_cast((afterLeft - beforeLeft)) / textureDim; + const float maxT = static_cast((beforeTop - beforeBottom)) / textureDim; + primitive->addVertex(beforeLeft, beforeBottom, 0, 0); + primitive->addVertex(afterLeft, beforeBottom, maxS, 0); + primitive->addVertex(beforeLeft, beforeTop, 0, maxT); + primitive->addVertex(beforeLeft, beforeTop, 0, maxT); + primitive->addVertex(afterLeft, beforeBottom, maxS, 0); + primitive->addVertex(afterLeft, beforeTop, maxS, maxT); + } + if (beforeRight > afterRight) { + /* Right */ + const float maxS = static_cast((beforeRight - afterRight)) / textureDim; + const float maxT = static_cast((beforeTop - beforeBottom)) / textureDim; + primitive->addVertex(afterRight, beforeBottom, 0, 0); + primitive->addVertex(beforeRight, beforeBottom, maxS, 0); + primitive->addVertex(afterRight, beforeTop, 0, maxT); + primitive->addVertex(afterRight, beforeTop, 0, maxT); + primitive->addVertex(beforeRight, beforeBottom, maxS, 0); + primitive->addVertex(beforeRight, beforeTop, maxS, maxT); + } + if (afterBottom > beforeBottom) { + /* Bottom */ + const float maxS = static_cast((beforeRight - beforeBottom)) / textureDim; + const float maxT = static_cast((afterBottom - beforeBottom)) / textureDim; + primitive->addVertex(beforeLeft, beforeBottom, 0, 0); + primitive->addVertex(beforeRight, beforeBottom, maxS, 0); + primitive->addVertex(beforeLeft, afterBottom, 0, maxT); + primitive->addVertex(beforeLeft, afterBottom, 0, maxT); + primitive->addVertex(beforeRight, beforeBottom, maxS, 0); + primitive->addVertex(beforeRight, afterBottom, maxS, maxT); + } + if (beforeTop > afterTop) { + /* Top */ + const float maxS = static_cast((beforeRight - beforeBottom)) / textureDim; + const float maxT = static_cast((beforeTop - afterTop)) / textureDim; + primitive->addVertex(beforeLeft, afterTop, 0, 0); + primitive->addVertex(beforeRight, afterTop, maxS, 0); + primitive->addVertex(beforeLeft, beforeTop, 0, maxT); + primitive->addVertex(beforeLeft, beforeTop, 0, maxT); + primitive->addVertex(beforeRight, afterTop, maxS, 0); + primitive->addVertex(beforeRight, beforeTop, maxS, maxT); + } + + if (primitive->getNumberOfVertices() > 0) { + glPushAttrib(GL_DEPTH_BUFFER_BIT + | GL_PIXEL_MODE_BIT + | GL_POLYGON_BIT + | GL_VIEWPORT_BIT + | GL_TRANSFORM_BIT); + glEnable(GL_DEPTH_TEST); + glViewport(windowBeforeAspectLockingViewport[0], + windowBeforeAspectLockingViewport[1], + windowBeforeAspectLockingViewport[2], + windowBeforeAspectLockingViewport[3]); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(beforeLeft, beforeRight, beforeBottom, beforeTop, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glDepthFunc(GL_ALWAYS); + GraphicsEngineDataOpenGL::draw(primitive.get()); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopAttrib(); + } +} /** * @return A string containing the state of OpenGL (depth testing, lighting, etc.) @@ -7060,7 +8525,94 @@ return s; } +/** + * Draw a medial model + * + * @param browserTabContent + * Content of the browser tab + * @param mediaModel + * The medial model for drawing + * @param viewport + * The viewport + */ +void +BrainOpenGLFixedPipeline::drawMediaModel(BrowserTabContent* browserTabContent, + ModelMedia* mediaModel, + const int32_t viewport[4]) +{ + BrainOpenGLMediaDrawing mediaDrawing; + mediaDrawing.draw(this, + browserTabContent, + mediaModel, + { viewport[0], viewport[1], viewport[2], viewport[3] }); +} +/** + * Setup opengl blending + * @param blendDataType + * Type of data that is blended + */ +void +BrainOpenGLFixedPipeline::setupBlending(const BlendDataType blendDataType) +{ + bool separateBlendingFlag(false); + + /* + * May want or not want separate blending for some data types + */ + switch (blendDataType) { + case BlendDataType::CHART_TWO_MATRIX: + separateBlendingFlag = true; + break; + case BlendDataType::FEATURE_IMAGE: + separateBlendingFlag = true; + break; + case BlendDataType::FIBER_TRAJECTORIES: + separateBlendingFlag = true; + break; + case BlendDataType::SURFACE_PROPERTIES_OPACITY: + separateBlendingFlag = true; + break; + case BlendDataType::VOLUME_ALL_VIEW_CUBES: + separateBlendingFlag = true; + break; + case BlendDataType::VOLUME_ALL_VIEW_SLICES: + separateBlendingFlag = true; + break; + case BlendDataType::VOLUME_ORTHOGONAL_SLICES: + separateBlendingFlag = true; + break; + } + + if ( ! DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_BLENDING)) { + separateBlendingFlag = false; + } + + if (separateBlendingFlag) { + glEnable(GL_BLEND); + + /* + * Equivelent of glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + */ + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* + * Blend RGB but DO NOT blend Alpha; Alpha is always one + */ + glBlendFuncSeparate(GL_SRC_ALPHA, /* source (incoming) RGB blending factor */ + GL_ONE_MINUS_SRC_ALPHA, /* destination (frame buffer) RGB blending factor */ + GL_ZERO, /* source (incoming) Alpha blending factor */ + GL_ONE); /* destination (frame buffer) Alpha blending factor */ + } + else { + /* + * Used prior to 06 Jan 2021 that places alpha + * values less than one in the frame buffer + */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } +} /* ============================================================================ */ /** diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLFixedPipeline.h connectome-workbench-1.5.0/src/Brain/BrainOpenGLFixedPipeline.h --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLFixedPipeline.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLFixedPipeline.h 2021-02-16 19:46:47.000000000 +0000 @@ -43,6 +43,8 @@ namespace caret { class Annotation; + class AnnotationBrowserTab; + class AnnotationScaleBar; class AnnotationText; class BoundingBox; class Brain; @@ -63,17 +65,18 @@ class SelectionManager; class IdentificationWithColor; class ImageFile; - class Plane; - class Surface; class Model; class ModelChart; class ModelChartTwo; + class ModelMedia; class ModelSurface; class ModelSurfaceMontage; class ModelVolume; class ModelWholeBrain; class Palette; class PaletteColorMapping; + class Plane; + class Surface; class SurfaceFile; class SurfaceMontageConfigurationCerebellar; class SurfaceMontageConfigurationCerebral; @@ -168,6 +171,7 @@ float fanMultiplier; bool isDrawWithMagnitude; float minimumMagnitude; + float maximumUncertainty; float magnitudeMultiplier; Plane* plane; FiberOrientationSymbolTypeEnum::Enum symbolType; @@ -263,6 +267,10 @@ void drawFiberTrajectories(const Plane* plane, const StructureEnum::Enum structure); + void drawMediaModel(BrowserTabContent* browserTabContent, + ModelMedia* mediaModel, + const int32_t viewport[4]); + void drawVolumeModel(BrowserTabContent* browserTabContent, ModelVolume* volumeModel, const int32_t viewport[4]); @@ -276,6 +284,8 @@ void drawVolumeVoxelsAsCubesWholeBrain(std::vector& volumeDrawInfoIn); + void drawVolumeVoxelsAsCubesWholeBrainOutsideFaces(std::vector& volumeDrawInfoIn); + void drawVolumeOrthogonalSliceWholeBrain(const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, std::vector& volumeDrawInfoIn); @@ -507,7 +517,8 @@ void setTabViewport(const BrainOpenGLViewportContent* vpContent); - void setAnnotationColorBarsForDrawing(const std::vector& viewportContents); + void setAnnotationColorBarsAndBrowserTabsForDrawing(const std::vector& viewportContents, + const bool selectionModeFlag); void drawTabHighlighting(const float width, const float height, @@ -515,6 +526,30 @@ void applyVolumePropertiesOpacity(); + void drawSolidBackgroundInAreasOutsideWindowAspectLocking(const int32_t windowBeforeAspectLockingViewport[4], + const int32_t windowAfterAspectLockingViewport[4]); + + void drawStippledBackgroundInAreasOutsideWindowAspectLocking(const int32_t windowBeforeAspectLockingViewport[4], + const int32_t windowAfterAspectLockingViewport[4]); + + void setupScaleBarDrawingInformation(BrowserTabContent* browserTabContent); + + void setupScaleBarDrawingInformation(BrowserTabContent* browserTabContent, + const float orthographicProjectionLeft, + const float orthographicProjectionRight); + + enum class BlendDataType { + CHART_TWO_MATRIX, + FEATURE_IMAGE, /* older image display selected in Features ToolBox*/ + FIBER_TRAJECTORIES, + SURFACE_PROPERTIES_OPACITY, + VOLUME_ALL_VIEW_CUBES, + VOLUME_ALL_VIEW_SLICES, + VOLUME_ORTHOGONAL_SLICES + }; + + static void setupBlending(const BlendDataType blendDataType); + /** Index of window */ int32_t m_windowIndex = -1; @@ -606,8 +641,12 @@ CaretPointer m_annotationDrawing; + std::vector m_annotationBrowserTabsForDrawing; + std::vector m_annotationColorBarsForDrawing; + std::vector m_annotationScaleBarsForDrawing; + /** Some graphics using annotations for some elements so user can select and edit them */ std::vector m_specialCaseGraphicsAnnotations; @@ -622,6 +661,7 @@ friend class BrainOpenGLAnnotationDrawingFixedPipeline; friend class BrainOpenGLChartDrawingFixedPipeline; friend class BrainOpenGLChartTwoDrawingFixedPipeline; + friend class BrainOpenGLMediaDrawing; friend class BrainOpenGLVolumeObliqueSliceDrawing; friend class BrainOpenGLVolumeSliceDrawing; friend class BrainOpenGLVolumeTextureSliceDrawing; diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLMediaDrawing.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLMediaDrawing.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLMediaDrawing.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLMediaDrawing.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,290 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_OPEN_G_L_MEDIA_DRAWING_DECLARE__ +#include "BrainOpenGLMediaDrawing.h" +#undef __BRAIN_OPEN_G_L_MEDIA_DRAWING_DECLARE__ + +#include "Brain.h" +#include "BrainOpenGLFixedPipeline.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventManager.h" +#include "EventOpenGLObjectToWindowTransform.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitiveV3fT3f.h" +#include "ImageFile.h" +#include "ModelMedia.h" +#include "MediaOverlay.h" +#include "MediaOverlaySet.h" +#include "SelectionItemImage.h" +#include "SelectionManager.h" + +using namespace caret; + + + +/** + * \class caret::BrainOpenGLMediaDrawing + * \brief Draw media data + * \ingroup Brain + */ + +/** + * Constructor. + */ +BrainOpenGLMediaDrawing::BrainOpenGLMediaDrawing() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +BrainOpenGLMediaDrawing::~BrainOpenGLMediaDrawing() +{ +} + +/** + * @param fixedPipelineDrawing + * The fixed pipeline drawing + * @param browserTabContent + * Content of the browser tab + * @param mediaModel + * Media model for drawing + * @param viewport + * Size of the viewport + */ +void +BrainOpenGLMediaDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + BrowserTabContent* browserTabContent, + ModelMedia* mediaModel, + const std::array& viewport) +{ + CaretAssert(fixedPipelineDrawing); + CaretAssert(browserTabContent); + CaretAssert(mediaModel); + + m_fixedPipelineDrawing = fixedPipelineDrawing; + m_browserTabContent = browserTabContent; + m_mediaModel = mediaModel; + m_viewport = viewport; + + if ((m_viewport[2] < 1) + || (m_viewport[3] < 1)) { + return; + } + glViewport(m_viewport[0], + m_viewport[1], + m_viewport[2], + m_viewport[3]); + + + const float halfHeight(500); + const float aspectRatio = (static_cast(m_viewport[3]) + / static_cast(m_viewport[2])); + const float halfWidth(halfHeight / aspectRatio); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-halfWidth, halfWidth, + -halfHeight, halfHeight, + -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + float translation[3]; + m_browserTabContent->getTranslation(translation); + const float scaling = m_browserTabContent->getScaling(); + + glTranslatef(translation[0], translation[1], 0.0); + glScalef(scaling, scaling, 1.0); + + drawModelLayers(); +} + +/** + * Draw the media models layers + */ +void +BrainOpenGLMediaDrawing::drawModelLayers() +{ + SelectionItemImage* idImage = m_fixedPipelineDrawing->m_brain->getSelectionManager()->getImageIdentification(); + + /* + * Check for a 'selection' type mode + */ + bool selectImageFlag(false); + switch (m_fixedPipelineDrawing->mode) { + case BrainOpenGLFixedPipeline::MODE_DRAWING: + break; + case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: + if (idImage->isEnabledForSelection()) { + selectImageFlag = true; + } + else { + return; + } + break; + case BrainOpenGLFixedPipeline::MODE_PROJECTION: + return; + break; + } + + MediaOverlaySet* mediaOverlaySet = m_mediaModel->getMediaOverlaySet(m_browserTabContent->getTabNumber()); + CaretAssert(mediaOverlaySet); + const int32_t numberOfOverlays = mediaOverlaySet->getNumberOfDisplayedOverlays(); + + for (int32_t i = (numberOfOverlays - 1); i >= 0; i--) { + MediaOverlay* overlay = mediaOverlaySet->getOverlay(i); + CaretAssert(overlay); + + if (overlay->isEnabled()) { + MediaFile* selectedFile(NULL); + int32_t selectedIndex(-1); + overlay->getSelectionData(selectedFile, + selectedIndex); + + if (selectedFile != NULL) { + ImageFile* imageFile = selectedFile->castToImageFile(); + if (imageFile != NULL) { + /* + * Image is drawn using a primitive in which + * the image is a texture + */ + GraphicsPrimitiveV3fT3f* primitive = imageFile->getGraphicsPrimitiveForMediaDrawing(); + if (primitive) { + GraphicsEngineDataOpenGL::draw(primitive); + + if (selectImageFlag) { + processImageFileSelection(imageFile, + primitive); + } + } + } + else { + CaretAssertMessage(0, ("Unrecogized file type " + + DataFileTypeEnum::toName(selectedFile->getDataFileType()) + + " for media drawing.")); + } + } + } + } +} + +/** + * Process selection in an image file + * @param imageFile + * The image file + * @param primitive + * Primitive that draws image file + */ +void +BrainOpenGLMediaDrawing::processImageFileSelection(ImageFile* imageFile, + GraphicsPrimitiveV3fT3f* primitive) +{ + SelectionItemImage* idImage = m_fixedPipelineDrawing->m_brain->getSelectionManager()->getImageIdentification(); + EventOpenGLObjectToWindowTransform xform(EventOpenGLObjectToWindowTransform::SpaceType::MODEL); + EventManager::get()->sendEvent(xform.getPointer()); + if (xform.isValid()) { + BoundingBox bounds; + primitive->getVertexBounds(bounds); + + /* + * Transform bottom left and top right coordinates of + * primitive containing image to window coordinates + */ + const auto minXYZ = bounds.getMinXYZ(); + float windowMinXYZ[3]; + xform.transformPoint(minXYZ.data(), + windowMinXYZ); + + const auto maxXYZ = bounds.getMaxXYZ(); + float windowMaxXYZ[3]; + xform.transformPoint(maxXYZ.data(), + windowMaxXYZ); + + const float drawnImageWidth(bounds.getDifferenceX()); + const float drawnImageHeight(bounds.getDifferenceY()); + if ((drawnImageWidth > 0.0) + && (drawnImageHeight > 0.0)) { + const float imageWidth(imageFile->getWidth()); + const float imageHeight(imageFile->getHeight()); + + const float mouseX(this->m_fixedPipelineDrawing->mouseX); + const float mouseY(this->m_fixedPipelineDrawing->mouseY); + + /* + * Offset of mouse from bottom left of image in window coordinates + */ + const float relativeX = mouseX - windowMinXYZ[0]; + const float relativeY = mouseY - windowMinXYZ[1]; + + /* + * Normalized coordinate in image (range is 0 to 1 if inside image) + */ + const float normalizedX = relativeX / static_cast(windowMaxXYZ[0] - windowMinXYZ[0]); + const float normalizedY = relativeY / static_cast(windowMaxXYZ[1] - windowMinXYZ[1]); + + /* + * Pixel X&Y in image + */ + const int32_t pixelX = static_cast(normalizedX * + static_cast(imageWidth)); + const int32_t pixelY = static_cast(normalizedY * + static_cast(imageHeight)); + + /* + * Verify clicked location is inside image + */ + if ((pixelX >= 0) + && (pixelX < imageWidth) + && (pixelY >= 0) + && (pixelY < imageHeight)) { + idImage->setImageFile(imageFile); + idImage->setPixelI(pixelX); + idImage->setPixelJ(pixelY); + + uint8_t pixelByteRGBA[4]; + if (imageFile->getImagePixelRGBA(ImageFile::IMAGE_DATA_ORIGIN_AT_BOTTOM, + pixelX, + pixelY, + pixelByteRGBA)) { + idImage->setImageFile(imageFile); + idImage->setPixelRGBA(pixelByteRGBA); + } + } + } + } +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +BrainOpenGLMediaDrawing::toString() const +{ + return "BrainOpenGLMediaDrawing"; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLMediaDrawing.h connectome-workbench-1.5.0/src/Brain/BrainOpenGLMediaDrawing.h --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLMediaDrawing.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLMediaDrawing.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,84 @@ +#ifndef __BRAIN_OPEN_G_L_MEDIA_DRAWING_H__ +#define __BRAIN_OPEN_G_L_MEDIA_DRAWING_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "CaretObject.h" + + + +namespace caret { + + class BrainOpenGLFixedPipeline; + class BrowserTabContent; + class GraphicsPrimitiveV3fT3f; + class ImageFile; + class ModelMedia; + + class BrainOpenGLMediaDrawing : public CaretObject { + + public: + BrainOpenGLMediaDrawing(); + + virtual ~BrainOpenGLMediaDrawing(); + + void draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + BrowserTabContent* browserTabContent, + ModelMedia* mediaModel, + const std::array& viewport); + + BrainOpenGLMediaDrawing(const BrainOpenGLMediaDrawing&) = delete; + + BrainOpenGLMediaDrawing& operator=(const BrainOpenGLMediaDrawing&) = delete; + + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void drawModelLayers(); + + void processImageFileSelection(ImageFile* imageFile, + GraphicsPrimitiveV3fT3f* primitive); + + BrainOpenGLFixedPipeline* m_fixedPipelineDrawing = NULL; + + BrowserTabContent* m_browserTabContent = NULL; + + ModelMedia* m_mediaModel; + + std::array m_viewport; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_OPEN_G_L_MEDIA_DRAWING_DECLARE__ + // +#endif // __BRAIN_OPEN_G_L_MEDIA_DRAWING_DECLARE__ + +} // namespace +#endif //__BRAIN_OPEN_G_L_MEDIA_DRAWING_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLViewportContent.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLViewportContent.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLViewportContent.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLViewportContent.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -28,6 +28,7 @@ #include #include +#include "AnnotationBrowserTab.h" #include "BrowserTabContent.h" #include "BrowserWindowContent.h" #include "CaretAssert.h" @@ -40,7 +41,9 @@ #include "ModelSurfaceMontage.h" #include "SpacerTabContent.h" #include "SurfaceMontageConfigurationAbstract.h" -#include "TileTabsConfiguration.h" +#include "TileTabsBrowserTabGeometry.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "TileTabsLayoutManualConfiguration.h" using namespace caret; @@ -57,8 +60,12 @@ /** * Constructor. * + * @param windowBeforeAspectLockingViewport + * Viewport of window before aspect locking is applied * @param windowViewport * Viewport of WINDOW in which drawing takes place. + * @param tabViewportManualLayoutBeforeAspectLocking + * Viewport before locking in a manual layout (for grid, same as tab viewport) * @param tabViewport * Viewport for TAB in which drawing takes place. * @param modelViewport @@ -74,7 +81,9 @@ * @param spacerTabContent * Spacer Tab content that is being drawn (if not NULL) */ -BrainOpenGLViewportContent::BrainOpenGLViewportContent(const int windowViewport[4], +BrainOpenGLViewportContent::BrainOpenGLViewportContent(const int32_t windowBeforeAspectLockingViewport[4], + const int windowViewport[4], + const int tabViewportManualLayoutBeforeAspectLocking[4], const int tabViewport[4], const int modelViewport[4], const int windowIndex, @@ -85,7 +94,11 @@ m_windowIndex(windowIndex), m_highlightTab(highlightTabFlag), m_browserTabContent(browserTabContent), -m_spacerTabContent(spacerTabContent) +m_spacerTabContent(spacerTabContent), +m_windowBeforeAspectLockingX(windowBeforeAspectLockingViewport[0]), +m_windowBeforeAspectLockingY(windowBeforeAspectLockingViewport[1]), +m_windowBeforeAspectLockingWidth(windowBeforeAspectLockingViewport[2]), +m_windowBeforeAspectLockingHeight(windowBeforeAspectLockingViewport[3]) { m_windowX = windowViewport[0]; m_windowY = windowViewport[1]; @@ -97,6 +110,10 @@ m_tabWidth = tabViewport[2]; m_tabHeight = tabViewport[3]; + for (int32_t i = 0; i < 4; i++) { + m_tabViewportManualLayoutBeforeAspectLocking[i] = tabViewportManualLayoutBeforeAspectLocking[i]; + } + m_modelX = modelViewport[0]; m_modelY = modelViewport[1]; m_modelWidth = modelViewport[2]; @@ -127,7 +144,11 @@ BrainOpenGLViewportContent::BrainOpenGLViewportContent(const BrainOpenGLViewportContent& obj) : CaretObject(obj), m_windowIndex(obj.m_windowIndex), -m_highlightTab(obj.m_highlightTab) +m_highlightTab(obj.m_highlightTab), +m_windowBeforeAspectLockingX(obj.m_windowBeforeAspectLockingX), +m_windowBeforeAspectLockingY(obj.m_windowBeforeAspectLockingY), +m_windowBeforeAspectLockingWidth(obj.m_windowBeforeAspectLockingWidth), +m_windowBeforeAspectLockingHeight(obj.m_windowBeforeAspectLockingHeight) { this->initializeMembersBrainOpenGLViewportContent(); this->copyHelperBrainOpenGLViewportContent(obj); @@ -175,8 +196,15 @@ m_windowY = 0; m_windowWidth = 0; m_windowHeight = 0; + m_windowBeforeAspectLockingX = 0; + m_windowBeforeAspectLockingY = 0; + m_windowBeforeAspectLockingWidth = 0; + m_windowBeforeAspectLockingHeight = 0; m_browserTabContent = NULL; m_spacerTabContent = NULL; + for (int32_t i = 0; i < 4; i++) { + m_tabViewportManualLayoutBeforeAspectLocking[i] = 0; + } } /** @@ -206,7 +234,15 @@ m_windowY = obj.m_windowY; m_windowWidth = obj.m_windowWidth; m_windowHeight = obj.m_windowHeight; - + m_windowBeforeAspectLockingX = obj.m_windowBeforeAspectLockingX; + m_windowBeforeAspectLockingY = obj.m_windowBeforeAspectLockingY; + m_windowBeforeAspectLockingWidth = obj.m_windowBeforeAspectLockingWidth; + m_windowBeforeAspectLockingHeight = obj.m_windowBeforeAspectLockingHeight; + + for (int32_t i = 0; i < 4; i++) { + m_tabViewportManualLayoutBeforeAspectLocking[i] = obj.m_tabViewportManualLayoutBeforeAspectLocking[i]; + } + m_browserTabContent = obj.m_browserTabContent; m_spacerTabContent = obj.m_spacerTabContent; } @@ -399,13 +435,25 @@ tabViewportOut[2] = m_tabWidth; tabViewportOut[3] = m_tabHeight; } - /** - * @return Pointer to the viewport for the window. + * Get the viewport for drawing a tab in a manual layout before application of aspect locking * + * @param tabViewportOut + * Output into which tab viewport dimensions are loaded. + * (x, y, width, height) + */ +void +BrainOpenGLViewportContent::getTabViewportManualLayoutBeforeAspectLocking(int tabViewportOut[4]) const +{ + for (int32_t i = 0; i < 4; i++) { + tabViewportOut[i] = m_tabViewportManualLayoutBeforeAspectLocking[i]; + } +} + +/** * @param windowViewportOut * Output into which window viewport dimensions are loaded. - * (x, y, width, height) + * (x, y, width, height). Viewport is AFTER aspect locking. */ void BrainOpenGLViewportContent::getWindowViewport(int windowViewportOut[4]) const @@ -417,6 +465,20 @@ } /** + * @param windowBeforeAspectLockingViewportOut + * Output into which window viewport dimensions are loaded. + * (x, y, width, height). Viewport is BEFORE aspect locking. + */ +void +BrainOpenGLViewportContent::getWindowBeforeAspectLockingViewport(int windowBeforeAspectLockingViewportOut[4]) const +{ + windowBeforeAspectLockingViewportOut[0] = m_windowBeforeAspectLockingX; + windowBeforeAspectLockingViewportOut[1] = m_windowBeforeAspectLockingY; + windowBeforeAspectLockingViewportOut[2] = m_windowBeforeAspectLockingWidth; + windowBeforeAspectLockingViewportOut[3] = m_windowBeforeAspectLockingHeight; +} + +/** * @return The window index. */ int @@ -464,10 +526,11 @@ AString BrainOpenGLViewportContent::toString() const { + const QString windowBeforeLockMsg = QString(" Window Before Lock x=%1 y=%2 w=%3 h=%4").arg(m_windowBeforeAspectLockingX).arg(m_windowBeforeAspectLockingY).arg(m_windowBeforeAspectLockingWidth).arg(m_windowBeforeAspectLockingHeight); const QString windowMsg = QString(" Window x=%1 y=%2 w=%3 h=%4").arg(m_windowX).arg(m_windowY).arg(m_windowWidth).arg(m_windowHeight); const QString tabMsg = QString(" Tab x=%1 y=%2 w=%3 h=%4").arg(m_tabX).arg(m_tabY).arg(m_tabWidth).arg(m_tabHeight); const QString modelMsg = QString(" Model x=%1 y=%2 w=%3 h=%4").arg(m_modelX).arg(m_modelY).arg(m_modelWidth).arg(m_modelHeight); - + const QString manMsg = QString(" Tab Before Lock x=%1 y=%2 w=%3 h=%4").arg(m_tabViewportManualLayoutBeforeAspectLocking[0]).arg(m_tabViewportManualLayoutBeforeAspectLocking[1]).arg(m_tabViewportManualLayoutBeforeAspectLocking[2]).arg(m_tabViewportManualLayoutBeforeAspectLocking[3]); AString msgOut; if (m_chartDataViewportValidFlag) { const QString chartProjectionMsg = QString(" Chart Projection=" + m_chartDataProjectionMatrix.toFormattedString(" ")); @@ -480,9 +543,11 @@ else { msgOut.appendWithNewLine(" Chart Invalid."); } + msgOut.appendWithNewLine(windowBeforeLockMsg); msgOut.appendWithNewLine(windowMsg); msgOut.appendWithNewLine(tabMsg); msgOut.appendWithNewLine(modelMsg); + msgOut.appendWithNewLine(manMsg); return msgOut; } @@ -496,6 +561,8 @@ * Gaps and margins * @param windowIndex * Index of browser window. + * @param windowBeforeAspectLockingViewport + * Viewport of window before aspect locking is applied * @param windowViewport * Viewport of WINDOW in which drawing takes place. * @return @@ -506,8 +573,16 @@ BrowserTabContent* selectedTabContent, const GapsAndMargins* gapsAndMargins, const int32_t windowIndex, + const int32_t windowBeforeAspectLockingViewport[4], const int32_t windowViewport[4]) { + const int tabViewportBeforeAspectLocking[4] = { + windowViewport[0], + windowViewport[1], + windowViewport[2], + windowViewport[3] + }; + int tabViewport[4] = { windowViewport[0], windowViewport[1], @@ -522,6 +597,7 @@ tabViewport[3] }; + std::unique_ptr eventContent = EventBrowserWindowContent::getWindowContent(windowIndex); EventManager::get()->sendEvent(eventContent->getPointer()); @@ -564,7 +640,9 @@ - BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowViewport, + BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowBeforeAspectLockingViewport, + windowViewport, + tabViewportBeforeAspectLocking, tabViewport, modelViewport, windowIndex, @@ -584,6 +662,8 @@ * Content of window. * @param gapsAndMargins * Contains margins around edges of tabs + * @param windowBeforeAspectLockingViewport + * Viewport of window before aspect locking is applied * @param windowViewport * The window's viewport. * @param windowIndex @@ -597,6 +677,7 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector& tabContents, BrowserWindowContent* browserWindowContent, const GapsAndMargins* gapsAndMargins, + const int32_t windowBeforeAspectLockingViewport[4], const int32_t windowViewport[4], const int32_t windowIndex, const int32_t highlightTabIndex) @@ -611,6 +692,75 @@ return viewportContentsOut; } + switch (browserWindowContent->getTileTabsConfigurationMode()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + { + TileTabsLayoutBaseConfiguration* tileTabsConfiguration = browserWindowContent->getSelectedTileTabsGridConfiguration(); + CaretAssert(tileTabsConfiguration); + viewportContentsOut = createViewportContentForGridTileTabs(tabContents, + browserWindowContent, + tileTabsConfiguration->castToGridConfiguration(), + gapsAndMargins, + windowBeforeAspectLockingViewport, + windowViewport, + windowIndex, + highlightTabIndex); + } + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + viewportContentsOut = createViewportContentForManualTileTabs(tabContents, + browserWindowContent, + gapsAndMargins, + windowBeforeAspectLockingViewport, + windowViewport, + windowIndex, + highlightTabIndex); + break; + } + + return viewportContentsOut; +} + +/** + * Create Viewport Contents for the given tab contents, window sizes, and tile sizes + * for a grid configuration. + * + * @param tabContents + * Content of each tab. + * @param browserWindowContent + * Content of window. + * @param gridConfiguration + * The grid configuration + * @param gapsAndMargins + * Contains margins around edges of tabs + * @param windowBeforeAspectLockingViewport + * Viewport of window before aspect locking is applied + * @param windowViewport + * The window's viewport. + * @param windowIndex + * Index of the window. + * @param hightlightTabIndex + * Index of tab that is highlighted when selected by user. + * @return + * Vector containing data for drawing each model. + */ +std::vector +BrainOpenGLViewportContent::createViewportContentForGridTileTabs(std::vector& tabContents, + BrowserWindowContent* browserWindowContent, + TileTabsLayoutGridConfiguration* gridConfiguration, + const GapsAndMargins* gapsAndMargins, + const int32_t windowBeforeAspectLockingViewport[4], + const int32_t windowViewport[4], + const int32_t windowIndex, + const int32_t highlightTabIndex) +{ + std::vector viewportContentsOut; + + CaretAssert(browserWindowContent); + CaretAssert(gapsAndMargins); + CaretAssert(gridConfiguration); + int32_t windowX = windowViewport[0]; int32_t windowY = windowViewport[1]; const int32_t windowWidth = windowViewport[2]; @@ -623,8 +773,9 @@ */ std::vector rowHeights; std::vector columnWidths; - TileTabsConfiguration* tileTabsConfiguration = browserWindowContent->getSelectedTileTabsConfiguration(); - tileTabsConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, + + const int32_t numberOfTabs = static_cast(tabContents.size()); + gridConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, windowHeight, numberOfTabs, browserWindowContent->getTileTabsConfigurationMode(), @@ -654,7 +805,7 @@ for (int32_t jCol = 0; jCol < numColumns; jCol++) { bool spacerTabFlag = false; - const TileTabsGridRowColumnContentTypeEnum::Enum rowContentType = tileTabsConfiguration->getRow(iRowFromTop)->getContentType(); + const TileTabsGridRowColumnContentTypeEnum::Enum rowContentType = gridConfiguration->getRow(iRowFromTop)->getContentType(); switch (rowContentType) { case TileTabsGridRowColumnContentTypeEnum::SPACE: spacerTabFlag = true; @@ -663,7 +814,7 @@ break; } - const TileTabsGridRowColumnContentTypeEnum::Enum tabContentType = tileTabsConfiguration->getColumn(jCol)->getContentType(); + const TileTabsGridRowColumnContentTypeEnum::Enum tabContentType = gridConfiguration->getColumn(jCol)->getContentType(); switch (tabContentType) { case TileTabsGridRowColumnContentTypeEnum::SPACE: spacerTabFlag = true; @@ -740,7 +891,7 @@ } - if (tileTabsConfiguration->isCenteringCorrectionEnabled()) { + if (gridConfiguration->isCenteringCorrectionEnabled()) { /* * Kludge that fixes old scenes * NEED TO ADJUST FOR X too ? @@ -856,7 +1007,9 @@ gapsAndMargins, modelViewport); - BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowViewport, + BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowBeforeAspectLockingViewport, + windowViewport, + tabViewport, tabViewport, modelViewport, browserWindowContent->getWindowIndex(), @@ -877,7 +1030,9 @@ columnWidths[jCol], rowHeights[iRow] }; - BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowViewport, + BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowBeforeAspectLockingViewport, + windowViewport, + tabViewport, tabViewport, tabViewport, browserWindowContent->getWindowIndex(), @@ -891,12 +1046,182 @@ vpX += columnWidths[jCol]; } } + + const bool printViewportsFlag(false); + if (printViewportsFlag) { + for (auto vpc : viewportContentsOut) { + if (vpc->getTabIndex() == 0) { + std::cout << "TAB 1: " << vpc->toString() << std::endl << std::endl; + } + } + } + + return viewportContentsOut; +} + +/** + * Create Viewport Contents for the given tab contents, window sizes, and tile sizes + * for a manual configuration. + * + * @param tabContents + * Content of each tab. + * @param browserWindowContent + * Content of window. + * @param gapsAndMargins + * Contains margins around edges of tabs + * @param windowBeforeAspectLockingViewport + * Viewport of window before aspect locking is applied + * @param windowViewport + * The window's viewport. + * @param windowIndex + * Index of the window. + * @param hightlightTabIndex + * Index of tab that is highlighted when selected by user. + * @return + * Vector containing data for drawing each model. + */ +std::vector +BrainOpenGLViewportContent::createViewportContentForManualTileTabs(std::vector& tabContents, + BrowserWindowContent* browserWindowContent, + const GapsAndMargins* gapsAndMargins, + const int32_t windowBeforeAspectLockingViewport[4], + const int32_t windowViewport[4], + const int32_t /*windowIndex*/, + const int32_t highlightTabIndex) +{ + const bool allTabsAspectLockedFlag = browserWindowContent->isAllTabsInWindowAspectRatioLocked(); + + std::vector viewportContentsOut; + + CaretAssert(browserWindowContent); + CaretAssert(gapsAndMargins); + + int32_t windowX = windowViewport[0]; + int32_t windowY = windowViewport[1]; + const int32_t windowWidth = windowViewport[2]; + const int32_t windowHeight = windowViewport[3]; + + if (tabContents.size() > 1) { + /* + * Sort tabs by stacking order + */ + std::sort(tabContents.begin(), + tabContents.end(), + [](BrowserTabContent* a, BrowserTabContent* b) { return (a->getManualLayoutBrowserTabAnnotation()->getStackingOrder() + > b->getManualLayoutBrowserTabAnnotation()->getStackingOrder()); } ); + } + + for (const auto tab : tabContents) { + CaretAssert(tab); + + const AnnotationBrowserTab* tabAnnotation = tab->getManualLayoutBrowserTabAnnotation(); + if ( ! tabAnnotation->isBrowserTabDisplayed()) { + continue; + } + + /* + * Tab Geometry is in percentages ranging [0.0, 100.0] + */ + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + tabAnnotation->getBounds2D(minX, maxX, minY, maxY); + const float tabAnnWidth(maxX - minX); + const float tabAnnHeight(maxY - minY); + int32_t tabX(((minX / 100.0) * windowWidth) + windowX); + int32_t tabY(((minY / 100.0) * windowHeight) + windowY); + int32_t tabWidth((tabAnnWidth / 100.0) * windowWidth); + int32_t tabHeight((tabAnnHeight / 100.0) * windowHeight); + + /* + * Is aspect ratio locked for all tabs ? + */ + if (allTabsAspectLockedFlag) { + /** + * Update aspect locking for any tabs that have not been locked + * Locking is done here so that it will work for new tabs and + * for tabs from old scenes before "lock all" + * Aspect locking is also performed here so that it works + * with both GUI and Command Line Show Scene + */ + if ( ! tab->isAspectRatioLocked()) { + if (tabWidth > 0) { + const float aspectRatio = (static_cast(tabHeight) + / static_cast(tabWidth)); + + tab->setAspectRatio(aspectRatio); + tab->setAspectRatioLocked(true); + } + } + } + + int tabViewportBeforeAspectLocking[4] { + tabX, + tabY, + tabWidth, + tabHeight + }; + + /* + * Adjust size of tab to match aspect ratio + */ + if (tab->isAspectRatioLocked()) { + const int32_t nonLockedWidth(tabWidth); + const int32_t nonLockedHeight(tabHeight); + BrainOpenGLViewportContent::adjustWidthHeightForAspectRatio(tab->getAspectRatio(), + tabWidth, + tabHeight); + + /* + * Center tab within its region + */ + const int32_t offsetX = nonLockedWidth - tabWidth; + if (offsetX > 0) { + tabX += (offsetX / 2); + } + + const int32_t offsetY = nonLockedHeight - tabHeight; + if (offsetY > 0) { + tabY += (offsetY / 2); + } + } + + /* + * Tab viewport may be invalid (width or height) when the user + * is editing the layout in tile tags configuration dialog + */ + if ((tabWidth > 0) + && (tabHeight > 0)) { + const int tabViewport[4] = { + tabX, + tabY, + tabWidth, + tabHeight + }; + + /* + * Model is drawn in the model viewport inside any margins. + */ + const int32_t tabIndex = tab->getTabNumber(); + const bool highlightTabFlag(highlightTabIndex == tabIndex); + int modelViewport[4] = { 0, 0, 0, 0 }; + createModelViewport(tabViewport, + tabIndex, + gapsAndMargins, + modelViewport); + + SpacerTabContent* invalidSpaceTabContent(NULL); + BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowBeforeAspectLockingViewport, + windowViewport, + tabViewportBeforeAspectLocking, + tabViewport, + modelViewport, + browserWindowContent->getWindowIndex(), + highlightTabFlag, + tab, + invalidSpaceTabContent); + viewportContentsOut.push_back(vpContent); + } + } -// for (auto vpc : viewportContentsOut) { -// if (vpc->getTabIndex() == 0) { -// std::cout << "TAB 1: " << vpc->toString() << std::endl << std::endl; -// } -// } return viewportContentsOut; } diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLViewportContent.h connectome-workbench-1.5.0/src/Brain/BrainOpenGLViewportContent.h --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLViewportContent.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLViewportContent.h 2021-02-16 19:46:47.000000000 +0000 @@ -32,7 +32,8 @@ class BrowserWindowContent; class GapsAndMargins; class SpacerTabContent; - class TileTabsConfiguration; + class TileTabsLayoutGridConfiguration; + class TileTabsLayoutManualConfiguration; class BrainOpenGLViewportContent : public CaretObject { @@ -59,8 +60,12 @@ void getTabViewportBeforeApplyingMargins(int tabViewportOut[4]) const; + void getTabViewportManualLayoutBeforeAspectLocking(int tabViewportOut[4]) const; + void getWindowViewport(int windowViewportOut[4]) const; + void getWindowBeforeAspectLockingViewport(int windowBeforeAspectLockingViewportOut[4]) const; + int getWindowIndex() const; BrowserTabContent* getBrowserTabContent() const; @@ -81,6 +86,7 @@ static std::vector createViewportContentForTileTabs(std::vector& tabContents, BrowserWindowContent* browserWindowContent, const GapsAndMargins* gapsAndMargins, + const int32_t windowBeforeAspectLockingViewport[4], const int32_t windowViewport[4], const int32_t windowIndex, const int32_t highlightTabIndex); @@ -89,6 +95,7 @@ BrowserTabContent* selectedTabContent, const GapsAndMargins* gapsAndMargins, const int32_t windowIndex, + const int32_t windowBeforeAspectLockingViewport[4], const int32_t windowViewport[4]); static void getSliceAllViewViewport(const int32_t tabViewport[4], @@ -133,7 +140,9 @@ int32_t m_height; }; - BrainOpenGLViewportContent(const int windowViewport[4], + BrainOpenGLViewportContent(const int32_t windowBeforeAspectLockingViewport[4], + const int windowViewport[4], + const int tabViewportManualLayoutBeforeAspectLocking[4], const int tabViewport[4], const int modelViewport[4], const int windowIndex, @@ -153,6 +162,23 @@ void updateTabLockedAspectRatios(const int32_t windowIndex, const int32_t windowViewport[4]); + static std::vector createViewportContentForGridTileTabs(std::vector& tabContents, + BrowserWindowContent* browserWindowContent, + TileTabsLayoutGridConfiguration* gridConfiguration, + const GapsAndMargins* gapsAndMargins, + const int32_t windowBeforeAspectLockingViewport[4], + const int32_t windowViewport[4], + const int32_t windowIndex, + const int32_t highlightTabIndex); + + static std::vector createViewportContentForManualTileTabs(std::vector& tabContents, + BrowserWindowContent* browserWindowContent, + const GapsAndMargins* gapsAndMargins, + const int32_t windowBeforeAspectLockingViewport[4], + const int32_t windowViewport[4], + const int32_t windowIndex, + const int32_t highlightTabIndex); + const int m_windowIndex; const bool m_highlightTab; @@ -166,6 +192,9 @@ /** Tab viewport's Height */ int m_tabHeight; + /** Manual layout tab viewport before aspect locking is applied */ + int m_tabViewportManualLayoutBeforeAspectLocking[4]; + /** Chart data viewport's X-coordinate */ mutable int m_chartDataX; /** Chart data viewport's Y-coordinate */ @@ -203,6 +232,15 @@ SpacerTabContent* m_spacerTabContent; + /** Window viewport's X-coordinate */ + int m_windowBeforeAspectLockingX; + /** Window viewport's Y-coordinate */ + int m_windowBeforeAspectLockingY; + /** Window viewport's Width */ + int m_windowBeforeAspectLockingWidth; + /** Window viewport's Height */ + int m_windowBeforeAspectLockingHeight; + public: virtual AString toString() const; }; diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -43,7 +43,6 @@ #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesFoci.h" #include "DisplayPropertiesLabels.h" -#include "DisplayPropertiesVolume.h" #include "ElapsedTimer.h" #include "FociFile.h" #include "Focus.h" @@ -741,8 +740,14 @@ sliceViewPlane, slicePlane, sliceCoordinates); + /* + * Only set for two-d view, 3D view (VIEW -> ALL) is set when surfaces are drawn + */ + m_fixedPipelineDrawing->setupScaleBarDrawingInformation(m_browserTabContent, + m_orthographicBounds[0], + m_orthographicBounds[1]); } - + SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); SelectionItemVoxelEditing* voxelEditingID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); @@ -846,8 +851,18 @@ break; } } + + CaretAssertVectorIndex(m_volumeDrawInfo, 0); + BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(m_fixedPipelineDrawing, + this->m_brain, + m_volumeDrawInfo[0].volumeFile, + slicePlane, + sliceThickness); + } - const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + + const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, m_fixedPipelineDrawing->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -855,7 +870,8 @@ m_fixedPipelineDrawing->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, sliceThickness); @@ -1211,7 +1227,7 @@ m_fixedPipelineDrawing->windowTabIndex) == false) { return; } - const float focusDiameter = fociDisplayProperties->getFociSize(displayGroup, + const float focusDiameter = fociDisplayProperties->getFociSizeMillimeters(displayGroup, m_fixedPipelineDrawing->windowTabIndex); const FeatureColoringTypeEnum::Enum fociColoringType = fociDisplayProperties->getColoringType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); @@ -3747,13 +3763,11 @@ uint8_t sliceAlpha = 255; bool drawWithBlendingFlag(false); -// if (m_bottomLayerFlag) { - if (m_opacity < 1.0) { - sliceAlpha = static_cast(m_opacity * 255.0); - drawWithBlendingFlag = true; - } -// } - + if (m_opacity < 1.0) { + sliceAlpha = static_cast(m_opacity * 255.0); + drawWithBlendingFlag = true; + } + std::vector selectionIJK; for (int32_t iRow = 0; iRow < m_numberOfRows; iRow++) { float voxelXYZ[3] = { @@ -3909,18 +3923,7 @@ } } else { - /* - * Only allow layer blending when overall volume opacity is off (>= 1.0) - */ - //const DisplayPropertiesVolume* dpv = brain->getDisplayPropertiesVolume(); - const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); - glPushAttrib(GL_COLOR_BUFFER_BIT); - if (drawWithBlendingFlag - && allowBlendingFlag) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } GraphicsEngineDataOpenGL::draw(primitive.get()); glPopAttrib(); } diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -44,7 +44,6 @@ #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesFoci.h" #include "DisplayPropertiesLabels.h" -#include "DisplayPropertiesVolume.h" #include "ElapsedTimer.h" #include "FociFile.h" #include "Focus.h" @@ -742,8 +741,14 @@ setVolumeSliceViewingAndModelingTransformations(sliceProjectionType, sliceViewPlane, slicePlane); + /* + * Only set for two-d view, 3D view (VIEW -> ALL) is set when surfaces are drawn + */ + m_fixedPipelineDrawing->setupScaleBarDrawingInformation(m_browserTabContent, + m_orthographicBounds[0], + m_orthographicBounds[1]); } - + SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); SelectionItemVoxelEditing* voxelEditingID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); @@ -787,8 +792,6 @@ glDisable(GL_CULL_FACE); const bool cullingSliceViewFlag = true; - const bool nonCulledLpiSliceViewFlag = false; // culling only works in a view looking along an axis (any rotation and slices disappear) - switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: if (m_modelVolume != NULL) { @@ -798,36 +801,22 @@ slicePlane); } else { - if (nonCulledLpiSliceViewFlag) { - drawOrthogonalSlice_LPI_ONLY(sliceViewPlane, - sliceCoordinates, - slicePlane); - } - else { - drawOrthogonalSlice(sliceViewPlane, - sliceCoordinates, - slicePlane); - } + drawOrthogonalSlice(sliceViewPlane, + sliceCoordinates, + slicePlane); } } else if (m_modelWholeBrain != NULL) { - if (nonCulledLpiSliceViewFlag) { - drawOrthogonalSlice_LPI_ONLY(sliceViewPlane, - sliceCoordinates, - slicePlane); + const bool allOrientationsFlag = true; + if (allOrientationsFlag) { + drawOrthogonalSliceAllView(sliceViewPlane, + sliceCoordinates, + slicePlane); } else { - const bool allOrientationsFlag = true; - if (allOrientationsFlag) { - drawOrthogonalSliceAllView(sliceViewPlane, - sliceCoordinates, - slicePlane); - } - else { - drawOrthogonalSlice(sliceViewPlane, - sliceCoordinates, - slicePlane); - } + drawOrthogonalSlice(sliceViewPlane, + sliceCoordinates, + slicePlane); } } break; @@ -835,25 +824,6 @@ CaretAssert(0); break; } - - /* - * Process selection - */ - if (m_identificationModeFlag) { - processIdentification(); - } - } - - drawIdentificationSymbols(slicePlane); - - if ( ! m_identificationModeFlag) { - if (slicePlane.isValidPlane()) { - drawLayers(sliceDrawingType, - sliceProjectionType, - sliceViewPlane, - slicePlane, - sliceCoordinates); - } } /* @@ -880,8 +850,25 @@ break; } } + + CaretAssertVectorIndex(m_volumeDrawInfo, 0); + drawIdentificationSymbols(m_volumeDrawInfo[0].volumeFile, + slicePlane, + sliceThickness); + } + + if ( ! m_identificationModeFlag) { + if (slicePlane.isValidPlane()) { + drawLayers(sliceDrawingType, + sliceProjectionType, + sliceViewPlane, + slicePlane, + sliceCoordinates); + } } - const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + + const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, m_fixedPipelineDrawing->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -889,7 +876,8 @@ m_fixedPipelineDrawing->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, sliceThickness); @@ -902,341 +890,6 @@ } /** - * Draw an orthogonal slice. - * - * NOTE: THIS METHOD ONLY DRAWS CORRECTLY IF THE VOLUME IS IN AN LPI OR RPI ORIENTATION. - * - * @param sliceViewPlane - * The plane for slice drawing. - * @param sliceCoordinates - * Coordinates of the selected slice. - * @param plane - * Plane equation for the selected slice. - */ -void -BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice_LPI_ONLY(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const float sliceCoordinates[3], - const Plane& plane) -{ - const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); - const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); - const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); - - /* - * Enable alpha blending so voxels that are not drawn from higher layers - * allow voxels from lower layers to be seen. - */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - /* - * Flat shading voxels not interpolated - */ - glShadeModel(GL_FLAT); - - CaretAssert(plane.isValidPlane()); - if (plane.isValidPlane() == false) { - return; - } - - /* - * Compute coordinate of point in center of first slice - */ - float selectedSliceCoordinate = 0.0; - float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; - plane.getNormalVector(sliceNormalVector); - switch (sliceViewPlane) { - case VolumeSliceViewPlaneEnum::ALL: - CaretAssert(0); - break; - case VolumeSliceViewPlaneEnum::AXIAL: - selectedSliceCoordinate = sliceCoordinates[2]; - break; - case VolumeSliceViewPlaneEnum::CORONAL: - selectedSliceCoordinate = sliceCoordinates[1]; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - selectedSliceCoordinate = sliceCoordinates[0]; - break; - } - - /* - * Holds colors for voxels in the slice - * Outside of loop to minimize allocations - * It is faster to make one call to - * NodeAndVoxelColoring::colorScalarsWithPalette() with - * all voxels in the slice than it is to call it - * separately for each voxel. - */ - std::vector sliceVoxelsRgbaVector; - - /* - * Draw each of the volumes separately so that each - * is drawn with the correct voxel slices. - */ - const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); - for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { - const BrainOpenGLFixedPipeline::VolumeDrawInfo& volInfo = m_volumeDrawInfo[iVol]; - const VolumeMappableInterface* volumeFile = volInfo.volumeFile; - int64_t dimI, dimJ, dimK, numMaps, numComponents; - volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); - const int64_t mapIndex = volInfo.mapIndex; - - float originX, originY, originZ; - volumeFile->indexToSpace(0, 0, 0, originX, originY, originZ); - - float x1, y1, z1; - volumeFile->indexToSpace(1, 1, 1, x1, y1, z1); - const float voxelStepX = x1 - originX; - const float voxelStepY = y1 - originY; - const float voxelStepZ = z1 - originZ; - - - /* - * Determine index of slice being viewed for the volume - */ - float coordinateOnSlice[3] = { - originX, - originY, - originZ - }; - switch (sliceViewPlane) { - case VolumeSliceViewPlaneEnum::ALL: - CaretAssert(0); - break; - case VolumeSliceViewPlaneEnum::AXIAL: - coordinateOnSlice[2] = selectedSliceCoordinate; - break; - case VolumeSliceViewPlaneEnum::CORONAL: - coordinateOnSlice[1] = selectedSliceCoordinate; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - coordinateOnSlice[0] = selectedSliceCoordinate; - break; - } - - int64_t sliceIndicesForCoordinateOnSlice[3]; - volumeFile->enclosingVoxel(coordinateOnSlice[0], - coordinateOnSlice[1], - coordinateOnSlice[2], - sliceIndicesForCoordinateOnSlice[0], - sliceIndicesForCoordinateOnSlice[1], - sliceIndicesForCoordinateOnSlice[2]); - - int64_t sliceIndexForDrawing = -1; - int64_t numVoxelsInSlice = 0; - switch (sliceViewPlane) { - case VolumeSliceViewPlaneEnum::ALL: - CaretAssert(0); - break; - case VolumeSliceViewPlaneEnum::AXIAL: - sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[2]; - if ((sliceIndexForDrawing < 0) - || (sliceIndexForDrawing >= dimK)) { - continue; - } - numVoxelsInSlice = dimI * dimJ; - break; - case VolumeSliceViewPlaneEnum::CORONAL: - sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[1]; - if ((sliceIndexForDrawing < 0) - || (sliceIndexForDrawing >= dimJ)) { - continue; - } - numVoxelsInSlice = dimI * dimK; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[0]; - if ((sliceIndexForDrawing < 0) - || (sliceIndexForDrawing >= dimI)) { - continue; - } - numVoxelsInSlice = dimJ * dimK; - break; - } - - /* - * Stores RGBA values for each voxel. - * Use a vector for voxel colors so no worries about memory being freed. - */ - const int64_t numVoxelsInSliceRGBA = numVoxelsInSlice * 4; - if (numVoxelsInSliceRGBA > static_cast(sliceVoxelsRgbaVector.size())) { - sliceVoxelsRgbaVector.resize(numVoxelsInSliceRGBA); - } - uint8_t* sliceVoxelsRGBA = &sliceVoxelsRgbaVector[0]; - - /* - * Get colors for all voxels in the slice. - */ - const int64_t validVoxelCount = - volumeFile->getVoxelColorsForSliceInMap(mapIndex, - sliceViewPlane, - sliceIndexForDrawing, - displayGroup, - browserTabIndex, - sliceVoxelsRGBA); - - /* - * Is label outline mode? - */ - if (m_volumeDrawInfo[iVol].mapFile->isMappedWithLabelTable()) { - int64_t xdim = 0; - int64_t ydim = 0; - switch (sliceViewPlane) { - case VolumeSliceViewPlaneEnum::ALL: - CaretAssert(0); - break; - case VolumeSliceViewPlaneEnum::AXIAL: - xdim = dimI; - ydim = dimJ; - break; - case VolumeSliceViewPlaneEnum::CORONAL: - xdim = dimI; - ydim = dimK; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - xdim = dimJ; - ydim = dimK; - break; - } - - LabelDrawingTypeEnum::Enum labelDrawingType = LabelDrawingTypeEnum::DRAW_FILLED; - CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; - const CaretMappableDataFile* mapFile = dynamic_cast(volumeFile); - if (mapFile != NULL) { - if (mapFile->isMappedWithLabelTable()) { - const LabelDrawingProperties* props = mapFile->getLabelDrawingProperties(); - labelDrawingType = props->getDrawingType(); - outlineColor = props->getOutlineColor(); - } - } - NodeAndVoxelColoring::convertSliceColoringToOutlineMode(sliceVoxelsRGBA, - labelDrawingType, - outlineColor, - xdim, - ydim); - } - - int64_t selectedSliceIndices[3]; - volumeFile->enclosingVoxel(sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2], - selectedSliceIndices[0], - selectedSliceIndices[1], - selectedSliceIndices[2]); - - const uint8_t volumeDrawingOpacity = static_cast(volInfo.opacity * 255.0); - - /* - * Setup for drawing the voxels in the slice. - */ - float startCoordinate[3] = { - originX - (voxelStepX / 2.0f), - originY - (voxelStepY / 2.0f), - originZ - (voxelStepZ / 2.0f) - }; - - float rowStep[3] = { - 0.0f, - 0.0f, - 0.0f - }; - - float columnStep[3] = { - 0.0f, - 0.0f, - 0.0f - }; - - - int64_t numberOfRows = 0; - int64_t numberOfColumns = 0; - switch (sliceViewPlane) { - case VolumeSliceViewPlaneEnum::ALL: - CaretAssert(0); - break; - case VolumeSliceViewPlaneEnum::AXIAL: - startCoordinate[2] = selectedSliceCoordinate; - rowStep[1] = voxelStepY; - columnStep[0] = voxelStepX; - numberOfRows = dimJ; - numberOfColumns = dimI; - break; - case VolumeSliceViewPlaneEnum::CORONAL: - startCoordinate[1] = selectedSliceCoordinate; - rowStep[2] = voxelStepZ; - columnStep[0] = voxelStepX; - numberOfRows = dimK; - numberOfColumns = dimI; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - startCoordinate[0] = selectedSliceCoordinate; - rowStep[2] = voxelStepZ; - columnStep[1] = voxelStepY; - numberOfRows = dimK; - numberOfColumns = dimJ; - break; - } - - if (m_modelWholeBrain != NULL) { - /* - * After the a slice is drawn in ALL view, some layers - * (volume surface outline) may be drawn in lines. As the - * view is rotated, lines will partially appear and disappear - * due to the lines having the same (extremely close) depth - * values as the voxel polygons. OpenGL's Polygon Offset - * only works with polygons and NOT with lines or points. - * So, polygon offset cannot be used to move the depth - * values for the lines and points "a little closer" to - * the user. Instead, polygon offset is used to push - * the underlaying slices "a little bit away" from the - * user. - * - * Resolves WB-414 - */ - const float inverseSliceIndex = numberOfVolumesToDraw - iVol; - const float factor = inverseSliceIndex * 1.0 + 1.0; - const float units = inverseSliceIndex * 1.0 + 1.0; - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(factor, units); - } - - /* - * Draw the voxels in the slice. - */ -// const AString drawMsg("OLD LPI:" -// "\n Axis: " + VolumeSliceViewPlaneEnum::toName(sliceViewPlane) -// + "\n Start XYZ: " + AString::fromNumbers(startCoordinate, 3, ",") -// + "\n Row Step: " + AString::fromNumbers(rowStep, 3, ",") -// + "\n Column Step: " + AString::fromNumbers(columnStep, 3, ",") -// + "\n Num Cols: " + AString::number(numberOfColumns) -// + "\n Num Rows: " + AString::number(numberOfRows) -// + "\n"); -// std::cout << qPrintable(drawMsg) << std::endl; - drawOrthogonalSliceVoxels(sliceNormalVector, - startCoordinate, - rowStep, - columnStep, - numberOfColumns, - numberOfRows, - sliceVoxelsRgbaVector, - validVoxelCount, - volumeFile, - iVol, - mapIndex, - volumeDrawingOpacity); - glDisable(GL_POLYGON_OFFSET_FILL); - } - - showBrainordinateHighlightRegionOfInterest(sliceViewPlane, - sliceCoordinates, - sliceNormalVector); - - glDisable(GL_BLEND); - glShadeModel(GL_SMOOTH); -} - -/** * Find the given orientation in the given volume, find its corresponding * dimesion index. * @@ -1476,8 +1129,6 @@ } axisInfoOut.valid = true; - - //axisInfoOut.print(); } /** @@ -1507,14 +1158,11 @@ /* * Enable alpha blending so voxels that are not drawn from higher layers * allow voxels from lower layers to be seen. - * - * Only allow layer blending when overall volume opacity is off (>= 1.0) */ const bool allowBlendingFlag(true); glPushAttrib(GL_COLOR_BUFFER_BIT); if (allowBlendingFlag) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + BrainOpenGLFixedPipeline::setupBlending(BrainOpenGLFixedPipeline::BlendDataType::VOLUME_ORTHOGONAL_SLICES); } /* @@ -1787,13 +1435,15 @@ volumeDrawingOpacity); glDisable(GL_POLYGON_OFFSET_FILL); + if (m_identificationModeFlag) { + processIdentification(true); + } } showBrainordinateHighlightRegionOfInterest(sliceViewingPlane, sliceCoordinates, sliceNormalVector); glPopAttrib(); - //glDisable(GL_BLEND); glShadeModel(GL_SMOOTH); } @@ -1955,23 +1605,63 @@ /** * Draw identification symbols on volume slice with the given plane. * + * @param volume + * The underlay volume + * @param plane + * The plane equation. + * @param sliceThickess + * Thickness of the slice + */ +void +BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const VolumeMappableInterface* volume, + const Plane& plane, + const float sliceThickness) +{ + drawIdentificationSymbols(m_fixedPipelineDrawing, + m_brain, + volume, + plane, + sliceThickness); +} + +/** + * Draw identification symbols on volume slice with the given plane. + * @param fixedPipelineDrawing + * The fixed pipeline drawing + * @param brain + * The brain + * @param volume + * The underlay volume * @param plane - * The plane equation. + * The plane equation. + * @param sliceThickess + * Thickness of the slice */ void -BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const Plane& plane) +BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + Brain* brain, + const VolumeMappableInterface* volume, + const Plane& plane, + const float sliceThickness) { - IdentificationManager* idManager = m_brain->getIdentificationManager(); + CaretAssert(fixedPipelineDrawing); + CaretAssert(brain); + CaretAssert(volume); + + const float halfSliceThickness(sliceThickness > 0.0 + ? (sliceThickness * 0.55) /* ensure symbol falls within a slice*/ + : 1.0); + IdentificationManager* idManager = brain->getIdentificationManager(); + + SelectionItemVoxelIdentificationSymbol* symbolID = brain->getSelectionManager()->getVoxelIdentificationSymbol(); - SelectionItemVoxelIdentificationSymbol* symbolID = m_brain->getSelectionManager()->getVoxelIdentificationSymbol(); - const std::vector voxelIDs = idManager->getIdentifiedItemsForVolume(); /* * Check for a 'selection' type mode */ bool isSelect = false; - switch (m_fixedPipelineDrawing->mode) { + switch (fixedPipelineDrawing->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: @@ -1987,7 +1677,7 @@ return; break; } - + if (idManager->isShowVolumeIdentificationSymbols()) { uint8_t rgba[4]; const int32_t numVoxelIdSymbols = static_cast(voxelIDs.size()); @@ -1998,13 +1688,25 @@ float xyz[3]; voxel.getXYZ(xyz); - const float symbolDiameter = voxel.getSymbolSize(); - const float halfSymbolSize = symbolDiameter / 2.0; + float symbolDiameter = voxel.getSymbolSize(); - const float dist = plane.signedDistanceToPlane(xyz); - if (dist < halfSymbolSize) { + const float dist = std::fabs(plane.signedDistanceToPlane(xyz)); + if (dist < halfSliceThickness) { + switch (voxel.getIdentificationSymbolSizeType()) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + { + BoundingBox boundingBox; + volume->getVoxelSpaceBoundingBox(boundingBox); + const float maxDimension = boundingBox.getMaximumDifferenceOfXYZ(); + symbolDiameter = maxDimension * (symbolDiameter / 100.0); + } + break; + } + if (isSelect) { - m_fixedPipelineDrawing->colorIdentification->addItem(rgba, + fixedPipelineDrawing->colorIdentification->addItem(rgba, SelectionItemDataTypeEnum::VOXEL_IDENTIFICATION_SYMBOL, iVoxel); rgba[3] = 255; @@ -2015,7 +1717,7 @@ glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); - m_fixedPipelineDrawing->drawSphereWithDiameter(rgba, + fixedPipelineDrawing->drawSphereWithDiameter(rgba, symbolDiameter); glPopMatrix(); } @@ -2026,11 +1728,11 @@ if (isSelect) { int voxelIdIndex = -1; float depth = -1.0; - m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL_IDENTIFICATION_SYMBOL, - m_fixedPipelineDrawing->mouseX, - m_fixedPipelineDrawing->mouseY, - voxelIdIndex, - depth); + fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL_IDENTIFICATION_SYMBOL, + fixedPipelineDrawing->mouseX, + fixedPipelineDrawing->mouseY, + voxelIdIndex, + depth); if (voxelIdIndex >= 0) { if (symbolID->isOtherScreenDepthCloserToViewer(depth)) { CaretAssertVectorIndex(voxelIDs, voxelIdIndex); @@ -2038,16 +1740,15 @@ float xyz[3]; voxel.getXYZ(xyz); symbolID->setVoxelXYZ(xyz); - symbolID->setBrain(m_brain); + symbolID->setBrain(brain); symbolID->setScreenDepth(depth); - m_fixedPipelineDrawing->setSelectedItemScreenXYZ(symbolID, xyz); + fixedPipelineDrawing->setSelectedItemScreenXYZ(symbolID, xyz); CaretLogFine("Selected Vertex Identification Symbol: " + QString::number(voxelIdIndex)); } } } } - /** * Draw an orthogonal slice with culling to avoid drawing * voxels not visible in the viewport and reduce drawing time. @@ -2081,14 +1782,11 @@ /* * Enable alpha blending so voxels that are not drawn from higher layers * allow voxels from lower layers to be seen. - * - * Only allow layer blending when overall volume opacity is off (>= 1.0) */ const bool allowBlendingFlag(true); glPushAttrib(GL_COLOR_BUFFER_BIT); if (allowBlendingFlag) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + BrainOpenGLFixedPipeline::setupBlending(BrainOpenGLFixedPipeline::BlendDataType::VOLUME_ORTHOGONAL_SLICES); } /* @@ -2144,9 +1842,6 @@ /* volume does not have slice within the culled region */ continue; } - /*const int64_t numVoxelsI = std::abs(culledLastVoxelIJK[0] - culledFirstVoxelIJK[0]) + 1; - const int64_t numVoxelsJ = std::abs(culledLastVoxelIJK[1] - culledFirstVoxelIJK[1]) + 1; - const int64_t numVoxelsK = std::abs(culledLastVoxelIJK[2] - culledFirstVoxelIJK[2]) + 1;//*/ int64_t numVoxelsX = -1, numVoxelsY = -1, numVoxelsZ = -1; int64_t sliceIndexForDrawing = -1; @@ -2438,6 +2133,10 @@ volumeDrawingOpacity); glDisable(GL_POLYGON_OFFSET_FILL); + + if (m_identificationModeFlag) { + processIdentification(true); + } } showBrainordinateHighlightRegionOfInterest(sliceViewPlane, @@ -2862,6 +2561,8 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: @@ -3039,6 +2740,8 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: @@ -3108,7 +2811,6 @@ colorSourceBrowserTabIndex); } - //const float thicknessPercentage = GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(thicknessMillimeters); SurfacePlaneIntersectionToContour contour(surface, plane, outlineColor, @@ -3200,8 +2902,27 @@ m_fixedPipelineDrawing->windowTabIndex) == false) { return; } - const float focusDiameter = fociDisplayProperties->getFociSize(displayGroup, - m_fixedPipelineDrawing->windowTabIndex); +// const float focusDiameter = fociDisplayProperties->getFociSizeMillimeters(displayGroup, +// m_fixedPipelineDrawing->windowTabIndex); + float focusDiameter(1.0); + switch (fociDisplayProperties->getFociSymbolSizeType(displayGroup, + m_fixedPipelineDrawing->windowTabIndex)) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + focusDiameter = fociDisplayProperties->getFociSizeMillimeters(displayGroup, + m_fixedPipelineDrawing->windowTabIndex); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + { + focusDiameter = fociDisplayProperties->getFociSizePercentage(displayGroup, + m_fixedPipelineDrawing->windowTabIndex); + BoundingBox boundingBox; + underlayVolume->getVoxelSpaceBoundingBox(boundingBox); + const float maxDimension = boundingBox.getMaximumDifferenceOfXYZ(); + focusDiameter = maxDimension * (focusDiameter / 100.0); + } + break; + } + const FeatureColoringTypeEnum::Enum fociColoringType = fociDisplayProperties->getColoringType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); @@ -3279,7 +3000,12 @@ focus->getNameRgba(rgba); break; } - + + /* + * Some label tables may have alpha at zero, so correct it + */ + rgba[3] = 1.0; + const int32_t numProjections = focus->getNumberOfProjections(); for (int32_t k = 0; k < numProjections; k++) { const SurfaceProjectedItem* spi = focus->getProjection(k); @@ -4785,9 +4511,12 @@ /** * Process voxel identification. + * + * @param doNotReplaceUnderlayFlag + * If true, do not replace identification from a lower layer */ void -BrainOpenGLVolumeSliceDrawing::processIdentification() +BrainOpenGLVolumeSliceDrawing::processIdentification(const bool doNotReplaceUnderlayFlag) { int32_t identifiedItemIndex; float depth = -1.0; @@ -4811,7 +4540,12 @@ SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); if (voxelID->isEnabledForSelection()) { - if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { + const bool idVoxelValid(doNotReplaceUnderlayFlag + && voxelID->isValid()); + if (idVoxelValid) { + /* do not replace the identified voxel */ + } + else if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { voxelID->setVoxelIdentification(m_brain, vf, voxelIndices, @@ -5552,15 +5286,9 @@ /* * Enable alpha blending so voxels that are not drawn from higher layers * allow voxels from lower layers to be seen. - * - * Only allow layer blending when overall volume opacity is off (>= 1.0) */ const bool allowBlendingFlag(true); glPushAttrib(GL_COLOR_BUFFER_BIT); - if (allowBlendingFlag) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } /* * Flat shading voxels not interpolated @@ -5868,6 +5596,16 @@ << " rowstep XYZ: " << AString::fromNumbers(rowStepXYZ, 3, ",") << " colstep XYZ: " << AString::fromNumbers(columnStepXYZ, 3, ",") << std::endl; } + + glPushAttrib(GL_COLOR_BUFFER_BIT); + if (volInfo.opacity < 1.0) { + if (allowBlendingFlag) { + if ( ! m_identificationModeFlag) { + BrainOpenGLFixedPipeline::setupBlending(BrainOpenGLFixedPipeline::BlendDataType::VOLUME_ALL_VIEW_SLICES); + } + } + } + drawOrthogonalSliceVoxels(sliceNormalVector, startCoordinateXYZ, rowStepXYZ, @@ -5880,15 +5618,20 @@ iVol, volInfo.mapIndex, volumeDrawingOpacity); + glDisable(GL_POLYGON_OFFSET_FILL); + glPopAttrib(); + + if (m_identificationModeFlag) { + processIdentification(false); + } } showBrainordinateHighlightRegionOfInterest(sliceViewingPlane, sliceCoordinates, sliceNormalVector); -// glDisable(GL_BLEND); glPopAttrib(); glShadeModel(GL_SMOOTH); } diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLVolumeSliceDrawing.h connectome-workbench-1.5.0/src/Brain/BrainOpenGLVolumeSliceDrawing.h --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLVolumeSliceDrawing.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLVolumeSliceDrawing.h 2021-02-16 19:46:47.000000000 +0000 @@ -83,6 +83,12 @@ BrainOpenGLFixedPipeline* fixedPipelineDrawing, const bool useNegativePolygonOffsetFlag); + static void drawIdentificationSymbols(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + Brain* brain, + const VolumeMappableInterface* volume, + const Plane& plane, + const float sliceThickness); + // ADD_NEW_METHODS_HERE private: @@ -228,10 +234,6 @@ const float sliceCoordinates[3], const int32_t viewport[4]); - void drawOrthogonalSlice_LPI_ONLY(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const float sliceCoordinates[3], - const Plane& plane); - void drawOrthogonalSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane); @@ -340,7 +342,9 @@ bool getVoxelCoordinateBoundsAndSpacing(float boundsOut[6], float spacingOut[3]); - void drawIdentificationSymbols(const Plane& plane); + void drawIdentificationSymbols(const VolumeMappableInterface* volume, + const Plane& plane, + const float sliceThickness); void addVoxelToIdentification(const int32_t volumeIndex, const int32_t mapIndex, @@ -361,7 +365,7 @@ const float sliceCoordinates[3], const float sliceNormalVector[3]); - void processIdentification(); + void processIdentification(const bool doNotReplaceUnderlayFlag); void resetIdentification(); diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -894,7 +894,8 @@ } } } - const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::Enum::ANNOTATIONS); + const bool tileTabsEditModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, m_fixedPipelineDrawing->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, @@ -902,7 +903,8 @@ m_fixedPipelineDrawing->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, sliceThickness); @@ -1401,7 +1403,7 @@ m_fixedPipelineDrawing->windowTabIndex) == false) { return; } - const float focusDiameter = fociDisplayProperties->getFociSize(displayGroup, + const float focusDiameter = fociDisplayProperties->getFociSizeMillimeters(displayGroup, m_fixedPipelineDrawing->windowTabIndex); const FeatureColoringTypeEnum::Enum fociColoringType = fociDisplayProperties->getColoringType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); @@ -3164,9 +3166,14 @@ if ((bottomLeftToTopLeftDistance > 0) && (bottomRightToTopRightDistance > 0)) { + const DisplayPropertiesVolume* dsv = m_brain->getDisplayPropertiesVolume(); + const bool allowBlendingFlag(dsv->getOpacity() >= 1.0); + glPushAttrib(GL_COLOR_BUFFER_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (allowBlendingFlag) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } if (m_modelWholeBrain != NULL) { glAlphaFunc(GL_GEQUAL, 0.95); @@ -3191,11 +3198,15 @@ * Using GL_ONE prevents an edge artifact * (narrow line on texture edges). */ - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + if (allowBlendingFlag) { + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } firstFlag = false; } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (allowBlendingFlag) { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } } std::array maxStr = { 1.0, 1.0, 1.0 }; GLuint textureID = 0; diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLWindowContent.cxx connectome-workbench-1.5.0/src/Brain/BrainOpenGLWindowContent.cxx --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLWindowContent.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLWindowContent.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,7 +23,10 @@ #include "BrainOpenGLWindowContent.h" #undef __BRAIN_OPEN_G_L_WINDOW_CONTENT_DECLARE__ +#include "AnnotationBrowserTab.h" +#include "BrowserTabContent.h" #include "CaretAssert.h" + using namespace caret; @@ -157,6 +160,8 @@ BrainOpenGLWindowContent::getTabViewportWithLockAspectXY(const int32_t x, const int32_t y) const { + BrainOpenGLViewportContent* viewportContentOut(NULL); + for (const auto& vp : m_tabViewports) { int32_t viewport[4]; vp->getTabViewportBeforeApplyingMargins(viewport); @@ -164,12 +169,81 @@ && (x < (viewport[0] + viewport[2])) && (y >= viewport[1]) && (y < (viewport[1] + viewport[3]))) { - return vp.get(); + if (viewportContentOut == NULL) { + viewportContentOut = vp.get(); + } + else { + /* + * Note: The tabs ONLY overlay in a manual tile tabs layout + */ + const BrowserTabContent* btc = vp.get()->getBrowserTabContent(); + const BrowserTabContent* btcOut = viewportContentOut->getBrowserTabContent(); + if ((btc != NULL) + && (btcOut != NULL)) { + /* + * Smaller stack order is in front (closer to viewer) + */ + if (btc->getManualLayoutBrowserTabAnnotation()->getStackingOrder() + < btcOut->getManualLayoutBrowserTabAnnotation()->getStackingOrder()) { + viewportContentOut = vp.get(); + } + } + } } } - return NULL; + + return viewportContentOut; } +/** + * Get the tab viewport containing the given X/Y coordinates + * for a manual layout prior to aspect locking. + * + * @param x + * The X-coordinate + * @param y + * The Y-coordinate + * @return + * Tab viewport containing the coordinate or NULL if not found. + */ +const BrainOpenGLViewportContent* +BrainOpenGLWindowContent::getTabViewportManualLayoutWithoutAspectLocking(const int32_t x, + const int32_t y) const +{ + BrainOpenGLViewportContent* viewportContentOut(NULL); + + for (const auto& vp : m_tabViewports) { + int32_t viewport[4]; + vp->getTabViewportManualLayoutBeforeAspectLocking(viewport); + if ((x >= viewport[0]) + && (x < (viewport[0] + viewport[2])) + && (y >= viewport[1]) + && (y < (viewport[1] + viewport[3]))) { + if (viewportContentOut == NULL) { + viewportContentOut = vp.get(); + } + else { + /* + * Note: The tabs ONLY overlay in a manual tile tabs layout + */ + const BrowserTabContent* btc = vp.get()->getBrowserTabContent(); + const BrowserTabContent* btcOut = viewportContentOut->getBrowserTabContent(); + if ((btc != NULL) + && (btcOut != NULL)) { + /* + * Smaller stack order is in front (closer to viewer) + */ + if (btc->getManualLayoutBrowserTabAnnotation()->getStackingOrder() + < btcOut->getManualLayoutBrowserTabAnnotation()->getStackingOrder()) { + viewportContentOut = vp.get(); + } + } + } + } + } + + return viewportContentOut; +} /** * All viewports for all tabs. diff -Nru connectome-workbench-1.4.2/src/Brain/BrainOpenGLWindowContent.h connectome-workbench-1.5.0/src/Brain/BrainOpenGLWindowContent.h --- connectome-workbench-1.4.2/src/Brain/BrainOpenGLWindowContent.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrainOpenGLWindowContent.h 2021-02-16 19:46:47.000000000 +0000 @@ -56,6 +56,9 @@ const BrainOpenGLViewportContent* getTabViewportWithLockAspectXY(const int32_t x, const int32_t y) const; + const BrainOpenGLViewportContent* getTabViewportManualLayoutWithoutAspectLocking(const int32_t x, + const int32_t y) const; + const BrainOpenGLViewportContent* getWindowViewport() const; std::vector getAllTabViewports() const; diff -Nru connectome-workbench-1.4.2/src/Brain/BrowserTabContent.cxx connectome-workbench-1.5.0/src/Brain/BrowserTabContent.cxx --- connectome-workbench-1.4.2/src/Brain/BrowserTabContent.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrowserTabContent.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -26,7 +26,10 @@ #include "BrowserTabContent.h" #undef __BROWSER_TAB_CONTENT_DECLARE__ +#include "AnnotationBrowserTab.h" #include "AnnotationColorBar.h" +#include "AnnotationCoordinate.h" +#include "AnnotationScaleBar.h" #include "BorderFile.h" #include "Brain.h" #include "BrainOpenGLViewportContent.h" @@ -40,6 +43,7 @@ #include "CaretPreferences.h" #include "ChartableMatrixInterface.h" #include "ChartModelDataSeries.h" +#include "ChartTwoCartesianOrientedAxes.h" #include "ChartTwoMatrixDisplayProperties.h" #include "ChartTwoOverlay.h" #include "ChartTwoOverlaySet.h" @@ -52,7 +56,7 @@ #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesBorders.h" #include "DisplayPropertiesFoci.h" -#include "EventAnnotationColorBarGet.h" +#include "EventAnnotationBarsGet.h" #include "EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h" #include "EventCaretMappableDataFileMapsViewedInOverlays.h" #include "EventIdentificationHighlightLocation.h" @@ -63,9 +67,13 @@ #include "LabelFile.h" #include "MathFunctions.h" #include "Matrix4x4.h" +#include "MediaFile.h" +#include "MediaOverlay.h" +#include "MediaOverlaySet.h" #include "MetricDynamicConnectivityFile.h" #include "ModelChart.h" #include "ModelChartTwo.h" +#include "ModelMedia.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelSurfaceSelector.h" @@ -85,9 +93,12 @@ #include "SurfaceMontageConfigurationFlatMaps.h" #include "SurfaceSelectionModel.h" #include "StructureEnum.h" +#include "TileTabsBrowserTabGeometry.h" +#include "TileTabsBrowserTabGeometrySceneHelper.h" #include "VolumeFile.h" #include "ViewingTransformations.h" #include "ViewingTransformationsCerebellum.h" +#include "ViewingTransformationsMedia.h" #include "ViewingTransformationsVolume.h" #include "VolumeDynamicConnectivityFile.h" #include "VolumeSliceSettings.h" @@ -119,6 +130,7 @@ m_surfaceMontageModel = NULL; m_chartModel = NULL; m_chartTwoModel = NULL; + m_mediaModel = NULL; m_guiName = ""; m_userName = ""; m_volumeSurfaceOutlineSetModel = new VolumeSurfaceOutlineSetModel(); @@ -131,6 +143,10 @@ m_displayVolumeMontageAxesCoordinates = prefs->isVolumeMontageAxesCoordinatesDisplayed(); m_volumeMontageCoordinatePrecision = prefs->getVolumeMontageCoordinatePrecision(); + m_scaleBar.reset(new AnnotationScaleBar(AnnotationAttributesDefaultTypeEnum::NORMAL)); + initializeScaleBar(); + CaretAssert(m_scaleBar.get()); + m_lightingEnabled = true; m_aspectRatio = 1.0; @@ -139,6 +155,7 @@ m_cerebellumViewingTransformation = new ViewingTransformationsCerebellum(); m_flatSurfaceViewingTransformation = new ViewingTransformations(); m_viewingTransformation = new ViewingTransformations(); + m_mediaViewingTransformation = new ViewingTransformationsMedia(); m_volumeSliceViewingTransformation = new ViewingTransformationsVolume(); m_chartTwoMatrixViewingTranformation = new ViewingTransformations(); m_chartTwoMatrixDisplayProperties = new ChartTwoMatrixDisplayProperties(); @@ -153,6 +170,10 @@ m_clippingPlaneGroup = new ClippingPlaneGroup(); + m_manualLayoutBrowserTabAnnotation.reset(new AnnotationBrowserTab(AnnotationAttributesDefaultTypeEnum::NORMAL)); + m_manualLayoutBrowserTabAnnotation->setBrowserTabContent(this, + m_tabNumber); + m_sceneClassAssistant = new SceneClassAssistant(); m_sceneClassAssistant->add("m_tabNumber", &m_tabNumber); @@ -182,6 +203,10 @@ "ViewingTransformations", m_viewingTransformation); + m_sceneClassAssistant->add("m_mediaViewingTransformation", + "ViewingTransformationsMedia", + m_mediaViewingTransformation); + m_sceneClassAssistant->add("m_volumeSliceViewingTransformation", "ViewingTransformations", m_volumeSliceViewingTransformation); @@ -225,8 +250,13 @@ &m_brainModelYokingGroup); m_sceneClassAssistant->add("m_chartModelYokingGroup", &m_chartModelYokingGroup); + + m_sceneClassAssistant->add("m_scaleBar", + "AnnotationScaleBar", + m_scaleBar.get()); + EventManager::get()->addEventListener(this, - EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET); + EventTypeEnum::EVENT_ANNOTATION_BARS_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION); EventManager::get()->addEventListener(this, @@ -241,6 +271,31 @@ setBrainModelYokingGroup(YokingGroupEnum::YOKING_GROUP_A); setChartModelYokingGroup(YokingGroupEnum::YOKING_GROUP_OFF); } + + /* + * Initialize the manual layout's default positions + * These values are used when a Manual Layout is selected to determine if + * the manual layout contains the defaults to that the bounds can be + * changed the first time manual layout is selected. + */ + AnnotationBrowserTab* annotationBrowserTab = getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + + TileTabsBrowserTabGeometry tabGeom(m_tabNumber); + m_defaultManualTabGeometryBounds[0] = tabGeom.getMinX(); + m_defaultManualTabGeometryBounds[1] = tabGeom.getMaxX(); + m_defaultManualTabGeometryBounds[2] = tabGeom.getMinY(); + m_defaultManualTabGeometryBounds[3] = tabGeom.getMaxY(); + + annotationBrowserTab->setBounds2D(m_defaultManualTabGeometryBounds[0], + m_defaultManualTabGeometryBounds[1], + m_defaultManualTabGeometryBounds[2], + m_defaultManualTabGeometryBounds[3]); + const float annWidth = (m_defaultManualTabGeometryBounds[1] - m_defaultManualTabGeometryBounds[0]); + const float annHeight = (m_defaultManualTabGeometryBounds[3] - m_defaultManualTabGeometryBounds[2]); + if (annWidth > 0.0) { + m_aspectRatio = annHeight / annWidth; + } } /** @@ -256,6 +311,7 @@ delete m_flatSurfaceViewingTransformation; delete m_cerebellumViewingTransformation; delete m_viewingTransformation; + delete m_mediaViewingTransformation; delete m_volumeSliceViewingTransformation; delete m_chartTwoMatrixViewingTranformation; delete m_chartTwoMatrixDisplayProperties; @@ -313,6 +369,7 @@ *m_cerebellumViewingTransformation = *tabToClone->m_cerebellumViewingTransformation; *m_flatSurfaceViewingTransformation = *tabToClone->m_flatSurfaceViewingTransformation; *m_viewingTransformation = *tabToClone->m_viewingTransformation; + *m_mediaViewingTransformation = *tabToClone->m_mediaViewingTransformation; *m_volumeSliceViewingTransformation = *tabToClone->m_volumeSliceViewingTransformation; *m_chartTwoMatrixViewingTranformation = *tabToClone->m_chartTwoMatrixViewingTranformation; *m_chartTwoMatrixDisplayProperties = *tabToClone->m_chartTwoMatrixDisplayProperties; @@ -335,36 +392,31 @@ if (model != NULL) { Brain* brain = model->getBrain(); - brain->copyDisplayProperties(tabToClone->getTabNumber(), - getTabNumber()); - - const int32_t numberOfBrainStructures = brain->getNumberOfBrainStructures(); - for (int32_t i = 0; i < numberOfBrainStructures; i++) { - BrainStructure* bs = brain->getBrainStructure(i); - const int32_t numLabelFiles = bs->getNumberOfLabelFiles(); - for (int32_t j = 0; j < numLabelFiles; j++) { - LabelFile* labelFile = bs->getLabelFile(j); - labelFile->getGroupAndNameHierarchyModel()->copySelections(tabToClone->getTabNumber(), - getTabNumber()); - } - } - - const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); - for (int32_t i = 0; i < numBorderFiles; i++) { - BorderFile* bf = brain->getBorderFile(i); - bf->getGroupAndNameHierarchyModel()->copySelections(tabToClone->getTabNumber(), - getTabNumber()); - } + brain->copyDisplayPropertiesToTab(tabToClone->getTabNumber(), + getTabNumber()); - const int32_t numFociFiles = brain->getNumberOfFociFiles(); - for (int32_t i = 0; i < numFociFiles; i++) { - FociFile* ff = brain->getFociFile(i); - ff->getGroupAndNameHierarchyModel()->copySelections(tabToClone->getTabNumber(), - getTabNumber()); - } + brain->copyFilePropertiesToTab(tabToClone->getTabNumber(), + getTabNumber()); } m_volumeSurfaceOutlineSetModel->copyVolumeSurfaceOutlineSetModel(tabToClone->getVolumeSurfaceOutlineSet()); + + /* + * For manual layout, make tab same size but put in bottom left corner + */ + const AnnotationBrowserTab* cloneAnnotationBrowerTab = tabToClone->getManualLayoutBrowserTabAnnotation(); + CaretAssert(cloneAnnotationBrowerTab); + const float minXY(10.0f); + const float maxWidthHeight(100.0f - (minXY * 2)); + float tabWidth = MathFunctions::limitRange(cloneAnnotationBrowerTab->getWidth(), minXY, maxWidthHeight); + float tabHeight = MathFunctions::limitRange(cloneAnnotationBrowerTab->getHeight(), minXY, maxWidthHeight); + + AnnotationBrowserTab* annotationBrowserTab = getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + annotationBrowserTab->setBounds2D(minXY, + minXY + tabWidth, + minXY, + minXY + tabHeight); } /** @@ -375,11 +427,13 @@ { AString s = getTabNamePrefix(); - const Model* displayedModel = - getModelForDisplay(); - if (displayedModel != NULL) { - const AString name = displayedModel->getNameForBrowserTab(); - s += name; + if ( ! m_closedFlag) { + const Model* displayedModel = + getModelForDisplay(); + if (displayedModel != NULL) { + const AString name = displayedModel->getNameForBrowserTab(); + s += name; + } } return s; @@ -470,6 +524,7 @@ if (model != NULL) { bool chartOneFlag = false; bool chartTwoFlag = false; + bool mediaFlag = false; bool surfaceFlag = false; bool surfaceMontageFlag = false; bool wholeBrainFlag = false; @@ -483,6 +538,9 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + mediaFlag = true; + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: surfaceFlag = true; break; @@ -540,6 +598,10 @@ model->getDescriptionOfContent(tabIndex, descriptionOut); } + else if (mediaFlag) { + model->getDescriptionOfContent(tabIndex, + descriptionOut); + } if (wholeBrainFlag || volumeFlag) { @@ -555,6 +617,9 @@ else if (chartTwoFlag) { getChartTwoOverlaySet()->getDescriptionOfContent(descriptionOut); } + else if (mediaFlag) { + getMediaOverlaySet()->getDescriptionOfContent(descriptionOut); + } else { getOverlaySet()->getDescriptionOfContent(descriptionOut); } @@ -601,6 +666,9 @@ switch (m_selectedModelType) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + mdc = m_mediaModel; + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: mdc = m_surfaceModelSelector->getSelectedSurfaceModel(); break; @@ -638,6 +706,9 @@ switch (m_selectedModelType) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + mdc = m_mediaModel; + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: mdc = m_surfaceModelSelector->getSelectedSurfaceModel(); break; @@ -718,6 +789,35 @@ } /** + * Get the displayed media model + * + * @return Pointer to displayed media model or + * NULL if the displayed model is NOT a + * media. + */ +ModelMedia* +BrowserTabContent::getDisplayedMediaModel() +{ + ModelMedia* mm = dynamic_cast(getModelForDisplay()); + return mm; +} + +/** + * Get the displayed media model + * + * @return Pointer to displayed media model or + * NULL if the displayed model is NOT a + * media. + */ +const ModelMedia* +BrowserTabContent::getDisplayedMediaModel() const +{ + const ModelMedia* mm = dynamic_cast(getModelForDisplay()); + return mm; +} + + +/** * Get the displayed surface model. * * @return Pointer to displayed surface model or @@ -850,6 +950,17 @@ return false; } +/** + * @return Is the displayed model a media model? + */ +bool +BrowserTabContent::isMediaDisplayed() const +{ + const ModelMedia* mm = dynamic_cast(getModelForDisplay()); + + const bool mediaFlag = (mm != NULL); + return mediaFlag; +} /** * @return Is the displayed model a volume slice model? @@ -995,6 +1106,69 @@ } /** + * @return Media overlay set for this tab. + */ +MediaOverlaySet* +BrowserTabContent::getMediaOverlaySet() +{ + if (m_mediaModel == NULL) { + return NULL; + } + + CaretAssert(m_mediaModel); + return m_mediaModel->getMediaOverlaySet(m_tabNumber); +} + +/** + * @return Media overlay set for this tab. + */ +const MediaOverlaySet* +BrowserTabContent::getMediaOverlaySet() const +{ + if (m_mediaModel == NULL) { + return NULL; + } + CaretAssert(m_mediaModel); + return m_mediaModel->getMediaOverlaySet(m_tabNumber); +} + +/** + * Get all axes for chart two models (histogram, lines matrix) that are yoked with the given axes and range mode + * @param axisOrientation + * The axes orientation + * @param yokingRangeMode + * The yoking range mode + * @return Vector containing all chart axes for the given orientation yoked to the given yoking range mode + */ +std::vector +BrowserTabContent::getYokedAxes(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode) const +{ + std::vector axesOut; + + if (m_chartTwoModel != NULL) { + std::vector overlaySets(m_chartTwoModel->getAllChartTwoOverlaySets(m_tabNumber)); + for (auto os : overlaySets) { + switch (axisOrientation) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + if (os->getHorizontalAxes()->getScaleRangeMode() == yokingRangeMode) { + axesOut.push_back(os->getHorizontalAxes()); + } + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + if (os->getVerticalAxes()->getScaleRangeMode() == yokingRangeMode) { + axesOut.push_back(os->getVerticalAxes()); + } + break; + } + } + } + + return axesOut; +} + + +/** * @return Chart two matrix display properties. */ ChartTwoMatrixDisplayProperties* @@ -1042,6 +1216,7 @@ m_surfaceMontageModel = NULL; m_chartModel = NULL; m_chartTwoModel = NULL; + m_mediaModel = NULL; for (int i = 0; i < numModels; i++) { Model* mdc = models[i]; @@ -1052,6 +1227,7 @@ ModelSurfaceMontage* mdcsm = dynamic_cast(mdc); ModelChart* mdch = dynamic_cast(mdc); ModelChartTwo* mdchTwo = dynamic_cast(mdc); + ModelMedia* mdmm = dynamic_cast(mdc); if (mdcs != NULL) { /* nothing to do since the surface model selector handles surfaces */ @@ -1076,6 +1252,9 @@ CaretAssertMessage((m_chartTwoModel == NULL), "There is more than one chart two model."); m_chartTwoModel = mdchTwo; } + else if (mdmm != NULL) { + m_mediaModel = mdmm; + } else { CaretAssertMessage(0, (AString("Unknown type of brain model.") + mdc->getNameForGUI(true))); } @@ -1084,6 +1263,11 @@ switch (m_selectedModelType) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + if (m_mediaModel == NULL) { + m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; + } + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: if (m_surfaceModelSelector->getSelectedSurfaceModel() == NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; @@ -1135,6 +1319,9 @@ else if (m_chartModel != NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_CHART; } + else if (m_mediaModel != NULL) { + m_selectedModelType = ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA; + } } if (m_volumeModel != NULL) { @@ -1206,6 +1393,17 @@ } /** + * Is the multi-medial model selection valid? + * @return True if valid, else false. + */ +bool +BrowserTabContent::isMediaModelValid() const +{ + bool valid(m_mediaModel != NULL); + return valid; +} + +/** * Is the volume model selection valid? * * @return bool indicating validity. @@ -1293,14 +1491,29 @@ void BrowserTabContent::receiveEvent(Event* event) { - if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET) { - EventAnnotationColorBarGet* colorBarEvent = dynamic_cast(event); - CaretAssert(colorBarEvent); + /* + * Ignore events while closed but available for reopening + */ + if (m_closedFlag) { + return; + } + + if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_BARS_GET) { + EventAnnotationBarsGet* barsEvent = dynamic_cast(event); + CaretAssert(barsEvent); - if (colorBarEvent->isGetAnnotationColorBarsForTabIndex(m_tabNumber)) { + if (barsEvent->isGetAnnotationColorBarsForTabIndex(m_tabNumber)) { std::vector colorBars; getAnnotationColorBars(colorBars); - colorBarEvent->addAnnotationColorBars(colorBars); + + barsEvent->addAnnotationColorBars(colorBars); + + if (m_scaleBar->isDisplayed()) { + /* + * Scale bar is derived from color bar + */ + barsEvent->addAnnotationScaleBar(m_scaleBar.get()); + } } } else if (event->getEventType() == EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION) { @@ -1339,7 +1552,6 @@ } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - //keepSliceCoordinateForSelectedAxis = true; break; } switch (m_volumeSliceSettings->getSliceDrawingType()) { @@ -1371,7 +1583,6 @@ } selectSlicesAtCoordinate(volumeSliceXYZ); - //m_volumeSliceSettings->selectSlicesAtCoordinate(volumeSliceXYZ); } } } @@ -1431,6 +1642,8 @@ case ModelTypeEnum::MODEL_TYPE_CHART_TWO: useChartTwoFlag = true; break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: useOverlayFlag = true; break; @@ -1544,6 +1757,8 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -1652,6 +1867,8 @@ case ModelTypeEnum::MODEL_TYPE_CHART_TWO: useChartTwoFlag = true; break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: useOverlayFlag = true; break; @@ -1770,6 +1987,8 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -1883,9 +2102,46 @@ mapIndex); if (overlayDataFile != NULL) { + switch (indexType) { + case ChartTwoOverlay::SelectedIndexType::INVALID: + if (overlay->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES) { + mapIndex = 0; + } + break; + case ChartTwoOverlay::SelectedIndexType::COLUMN: + break; + case ChartTwoOverlay::SelectedIndexType::MAP: + break; + case ChartTwoOverlay::SelectedIndexType::ROW: + break; + } if (mapIndex >= 0) { - fileAndMapsEvent->addChartFileAndMap(overlayDataFile, - mapIndex); + fileAndMapsEvent->addChartTwoFileAndMap(overlayDataFile, + mapIndex, + m_tabNumber); + } + } + } + } + } + break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + { + MediaOverlaySet* overlaySet = model->getMediaOverlaySet(tabIndex); + const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + MediaOverlay* overlay = overlaySet->getOverlay(i); + if (overlay->isEnabled()) { + MediaFile* mediaFile = NULL; + int32_t frameIndex; + overlay->getSelectionData(mediaFile, + frameIndex); + + if (mediaFile != NULL) { + if (frameIndex >= 0) { + fileAndMapsEvent->addMediaFileAndFrame(mediaFile, + frameIndex, + m_tabNumber); } } } @@ -1910,7 +2166,8 @@ if (overlayDataFile != NULL) { if (mapIndex >= 0) { fileAndMapsEvent->addBrainordinateFileAndMap(overlayDataFile, - mapIndex); + mapIndex, + m_tabNumber); } } } @@ -1942,6 +2199,27 @@ switch (getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + { + MediaOverlaySet* overlaySet = m_mediaModel->getMediaOverlaySet(tabIndex); + if (overlaySet != NULL) { + const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + MediaOverlay* mediaOverlay = overlaySet->getOverlay(i); + if (mediaOverlay->isEnabled()) { + MediaFile* mediaFile = NULL; + int32_t selectedIndex = -1; + mediaOverlay->getSelectionData(mediaFile, + selectedIndex); + + if (mediaFile != NULL) { + displayedDataFiles.insert(mediaFile); + } + } + } + } + } + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: { ModelSurface* ms = getDisplayedSurfaceModel(); @@ -2233,6 +2511,9 @@ else if (isChartTwoDisplayed()) { return m_chartTwoMatrixViewingTranformation; } + else if (isMediaDisplayed()) { + return m_mediaViewingTransformation; + } return m_viewingTransformation; } @@ -2251,6 +2532,9 @@ else if (isChartTwoDisplayed()) { return m_chartTwoMatrixViewingTranformation; } + else if (isMediaDisplayed()) { + return m_mediaViewingTransformation; + } return m_viewingTransformation; } @@ -2376,6 +2660,20 @@ updateBrainModelYokedBrowserTabs(); } +Matrix4x4 +BrowserTabContent::getFlatRotationMatrix() const +{ + return getViewingTransformation()->getFlatRotationMatrix(); +} + +void +BrowserTabContent::setFlatRotationMatrix(const Matrix4x4& flatRotationMatrix) +{ + getViewingTransformation()->setFlatRotationMatrix(flatRotationMatrix); + updateYokedModelBrowserTabs(); +} + + /** * Get the offset for the right flat map offset. * @@ -2742,14 +3040,6 @@ float mouseDelta = std::sqrt(static_cast((mouseDeltaX * mouseDeltaX) + (mouseDeltaY * mouseDeltaY))); - // /* - // * Rotation needs to be oppposite for newer - // * oblique slice drawing for volumes that - // * do not have a voxel corresponding to - // * the origin. - // */ - // mouseDelta = -mouseDelta; - switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: { @@ -2808,6 +3098,9 @@ || isChartTwoDisplayed()) { /* no rotation for chart */ } + else if (isMediaDisplayed()) { + /* no rotation for media */ + } else if (isCerebellumDisplayed()) { const float screenDX = mouseDeltaX; const float screenDY = mouseDeltaY; @@ -2815,6 +3108,8 @@ float rotateDX = 0.0; float rotateDY = 0.0; float rotateDZ = 0.0; + float rotateFlat = 0.0; + bool flatFlag(false); ModelSurfaceMontage* montageModel = getDisplayedSurfaceMontageModel(); if (montageModel != NULL) { @@ -2874,15 +3169,87 @@ } } else { - rotateDX = -screenDY; - rotateDY = screenDX; + ModelSurface* modelSurface = getDisplayedSurfaceModel(); + if (modelSurface != NULL) { + if (isFlatSurfaceDisplayed()) { + flatFlag = true; + rotateFlat = -screenDY; + } + else { + rotateDX = -screenDY; + rotateDY = screenDX; + } + } } - Matrix4x4 rotationMatrix = m_cerebellumViewingTransformation->getRotationMatrix(); - rotationMatrix.rotateX(rotateDX); - rotationMatrix.rotateY(rotateDY); - rotationMatrix.rotateZ(rotateDZ); - m_cerebellumViewingTransformation->setRotationMatrix(rotationMatrix); + if (flatFlag) { + if (rotateFlat != 0.0) { + int viewport[4]; + viewportContent->getModelViewport(viewport); + if ((viewport[2] > 0) + && (viewport[3] > 0)) { + if ((mouseDeltaX != 0) + || (mouseDeltaY != 0)) { + + const int previousMouseX = mouseX - mouseDeltaX; + const int previousMouseY = mouseY - mouseDeltaY; + + /* + * Need to account for the quadrants!!!! + */ + const float viewportCenter[3] = { + (float)(viewport[0] + viewport[2] / 2), + ((float)viewport[1] + viewport[3] / 2), + 0.0 + }; + + const float oldPos[3] = { + (float)previousMouseX, + (float)previousMouseY, + 0.0 + }; + + const float newPos[3] = { + (float)mouseX, + (float)mouseY, + 0.0 + }; + + /* + * Compute normal vector from viewport center to + * old mouse position to new mouse position. + * If normal-Z is positive, mouse has been moved + * in a counter clockwise motion relative to center. + * If normal-Z is negative, mouse has moved clockwise. + */ + float normalDirection[3]; + MathFunctions::normalVectorDirection(viewportCenter, + oldPos, + newPos, + normalDirection); + rotateFlat = std::fabs(rotateFlat); + if (normalDirection[2] > 0.0) { + /* mouse movied counter-clockwise */ + } + else if (normalDirection[2] < 0.0) { + /* mouse movied clockwise */ + rotateFlat = -rotateFlat; + } + } + } + ViewingTransformations* viewingTransform = getViewingTransformation(); + Matrix4x4 flatRotationMatrix = viewingTransform->getFlatRotationMatrix(); + flatRotationMatrix.rotateZ(rotateFlat); + viewingTransform->setFlatRotationMatrix(flatRotationMatrix); + } + } + else { + Matrix4x4 rotationMatrix = m_cerebellumViewingTransformation->getRotationMatrix(); + rotationMatrix.rotateX(rotateDX); + rotationMatrix.rotateY(rotateDY); + rotationMatrix.rotateZ(rotateDZ); + m_cerebellumViewingTransformation->setRotationMatrix(rotationMatrix); + } } else { ViewingTransformations* viewingTransform = getViewingTransformation(); @@ -2896,6 +3263,10 @@ float dx = mouseDeltaX; float dy = mouseDeltaY; + StructureEnum::Enum flatStructure(StructureEnum::INVALID); + float flatRotate(0.0); + int32_t flatViewport[4] { -1, -1, -1, -1 }; + ModelSurfaceMontage* montageModel = getDisplayedSurfaceMontageModel(); if (montageModel != NULL) { std::vector montageViewports; @@ -2921,6 +3292,9 @@ case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: + isFlat = true; + smv->getViewport(flatViewport); + flatStructure = StructureEnum::CEREBELLUM; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: isLeft = true; @@ -2933,6 +3307,8 @@ case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: isLeft = true; isFlat = true; + smv->getViewport(flatViewport); + flatStructure = StructureEnum::CORTEX_LEFT; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: isLeft = false; @@ -2945,6 +3321,8 @@ case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: isLeft = false; isFlat = true; + smv->getViewport(flatViewport); + flatStructure = StructureEnum::CORTEX_RIGHT; break; } isValid = true; @@ -2957,7 +3335,12 @@ if (isFlat) { /* - * No rotation. + * Special flat map rotation + */ + flatRotate = dy; + + /* + * No 3D rotation. */ dx = 0.0; dy = 0.0; @@ -2972,10 +3355,95 @@ } } + ModelSurface* modelSurface = getDisplayedSurfaceModel(); + if (modelSurface != NULL) { + const Surface* surface = modelSurface->getSurface(); + if (surface != NULL) { + switch (surface->getSurfaceType()) { + case SurfaceTypeEnum::ANATOMICAL: + case SurfaceTypeEnum::ELLIPSOID: + case SurfaceTypeEnum::HULL: + case SurfaceTypeEnum::INFLATED: + case SurfaceTypeEnum::RECONSTRUCTION: + case SurfaceTypeEnum::SEMI_SPHERICAL: + case SurfaceTypeEnum::SPHERICAL: + case SurfaceTypeEnum::UNKNOWN: + case SurfaceTypeEnum::VERY_INFLATED: + break; + case SurfaceTypeEnum::FLAT: + viewportContent->getModelViewport(flatViewport); + flatRotate = dy; + flatStructure = surface->getStructure(); + break; + } + } + } + Matrix4x4 rotationMatrix = viewingTransform->getRotationMatrix(); rotationMatrix.rotateX(-dy); rotationMatrix.rotateY(dx); viewingTransform->setRotationMatrix(rotationMatrix); + + if (flatRotate != 0.0) { + if ((flatViewport[2] > 0) + && (flatViewport[3] > 0)) { + if ((mouseDeltaX != 0) + || (mouseDeltaY != 0)) { + + const int previousMouseX = mouseX - mouseDeltaX; + const int previousMouseY = mouseY - mouseDeltaY; + + /* + * Need to account for the quadrants!!!! + */ + const float viewportCenter[3] = { + (float)(flatViewport[0] + flatViewport[2] / 2), + ((float)flatViewport[1] + flatViewport[3] / 2), + 0.0 + }; + + const float oldPos[3] = { + (float)previousMouseX, + (float)previousMouseY, + 0.0 + }; + + const float newPos[3] = { + (float)mouseX, + (float)mouseY, + 0.0 + }; + + /* + * Compute normal vector from viewport center to + * old mouse position to new mouse position. + * If normal-Z is positive, mouse has been moved + * in a counter clockwise motion relative to center. + * If normal-Z is negative, mouse has moved clockwise. + */ + float normalDirection[3]; + MathFunctions::normalVectorDirection(viewportCenter, + oldPos, + newPos, + normalDirection); + flatRotate = std::fabs(flatRotate); + if (normalDirection[2] > 0.0) { + /* mouse movied counter-clockwise */ + } + else if (normalDirection[2] < 0.0) { + /* mouse movied clockwise */ + flatRotate = -flatRotate; + } + + if (StructureEnum::isRight(flatStructure)) { + flatRotate = -flatRotate; + } + } + } + Matrix4x4 flatRotationMatrix = viewingTransform->getFlatRotationMatrix(); + flatRotationMatrix.rotateZ(flatRotate); + viewingTransform->setFlatRotationMatrix(flatRotationMatrix); + } } } updateYokedModelBrowserTabs(); @@ -2984,13 +3452,28 @@ /** * Apply mouse scaling to the displayed model. * + * @param viewportContent + * Content of the viewport + * @param mousePressX + * X coordinate of where mouse was pressed. + * @param mousePressY + * X coordinate of where mouse was pressed. + * @param mouseX + * Mouse X coordinate. + * @param mouseY + * Mouse Y coordinate. * @param mouseDX * Change in mouse X coordinate. * @param mouseDY * Change in mouse Y coordinate. */ void -BrowserTabContent::applyMouseScaling(const int32_t /*mouseDX*/, +BrowserTabContent::applyMouseScaling(BrainOpenGLViewportContent* viewportContent, + const int32_t /*mousePressX*/, + const int32_t /*mousePressY*/, + const float mouseX, + const float mouseY, + const int32_t /*mouseDX*/, const int32_t mouseDY) { if (isChartOneDisplayed()) { @@ -3022,14 +3505,17 @@ } } else if (isChartTwoDisplayed()) { - float scaling = getViewingTransformation()->getScaling(); - if (mouseDY != 0.0) { - scaling *= (1.0f + (mouseDY * 0.01)); - } - if (scaling < 0.01) { - scaling = 0.01; + ChartTwoOverlaySet* overlaySet = getChartTwoOverlaySet(); + if (overlaySet != NULL) { + int32_t viewport[4]; + Matrix4x4 m1, m2; + if (viewportContent->getChartDataMatricesAndViewport(m1, m2, viewport)) { + overlaySet->applyMouseScaling(viewport, + mouseX, + mouseY, + mouseDY); + } } - getViewingTransformation()->setScaling(scaling); } else { float scaling = getViewingTransformation()->getScaling(); @@ -3145,12 +3631,24 @@ } } else if (isChartTwoDisplayed()) { - float translation[3]; - m_chartTwoMatrixViewingTranformation->getTranslation(translation); - translation[0] += mouseDX; - translation[1] += mouseDY; - translation[2] = 0; // NO Z-translation - m_chartTwoMatrixViewingTranformation->setTranslation(translation); + ChartTwoOverlaySet* overlaySet = getChartTwoOverlaySet(); + if (overlaySet != NULL) { + int32_t viewport[4]; + Matrix4x4 m1, m2; + if (viewportContent->getChartDataMatricesAndViewport(m1, m2, viewport)) { + overlaySet->applyMouseTranslation(viewport, + mouseDX, + mouseDY); + } + } + } + else if (isMediaDisplayed()) { + float txyz[3]; + m_mediaViewingTransformation->getTranslation(txyz); + const float accelerate(3.0); + txyz[0] += (mouseDX * accelerate); + txyz[1] += (mouseDY * accelerate); + m_mediaViewingTransformation->setTranslation(txyz); } else if (isCerebellumDisplayed()) { const float screenDX = mouseDX; @@ -3328,6 +3826,60 @@ } /** + * Apply chart two bounds selection as user drags the mouse + * @param viewport + * Chart viewport + * @param x1 + * X from first pair of coordinates + * @param y1 + * Y from first pair of coordinates + * @param x2 + * X from second pair of coordinates + * @param y2 + * Y from second pair of coordinates + */ +void +BrowserTabContent::applyChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2) +{ + ChartTwoOverlaySet* ctos = getChartTwoOverlaySet(); + if (ctos != NULL) { + ctos->applyChartTwoAxesBoundSelection(viewport, + x1, y1, x2, y2); + } +} + +/** + * Finalize chart two bounds selection to set the bounds of the chart + * @param viewport + * Chart viewport + * @param x1 + * X from first pair of coordinates + * @param y1 + * Y from first pair of coordinates + * @param x2 + * X from second pair of coordinates + * @param y2 + * Y from second pair of coordinates + */ +void +BrowserTabContent::finalizeChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2) +{ + ChartTwoOverlaySet* ctos = getChartTwoOverlaySet(); + if (ctos != NULL) { + ctos->finalizeChartTwoAxesBoundSelection(viewport, + x1, y1, x2, y2); + } +} + +/** * Get the transformations for drawing a model. * * @param projectionViewType @@ -3424,18 +3976,18 @@ } break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: - rotationX = 0.0; - rotationY = 0.0; - rotationZ = 0.0; + getFlatRotationMatrix().getRotation(rotationX, + rotationY, + rotationZ); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: - rotationX = 0.0; - rotationY = 0.0; - rotationZ = 0.0; + getFlatRotationMatrix().getRotation(rotationX, + rotationY, + rotationZ); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: rotationX = rotationFlippedX; @@ -3446,9 +3998,11 @@ rotationY = rotationFlippedY; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: - rotationX = 0.0; - rotationY = 180.0; - rotationZ = 0.0; + translationOut[0] = -translationOut[0]; + getFlatRotationMatrix().getRotation(rotationX, + rotationY, + rotationZ); + rotationZ = -rotationZ; break; } @@ -3482,6 +4036,11 @@ obliqueRotationMatrix.getMatrix(mob); modelTransform.setObliqueRotation(mob); + const Matrix4x4 flatRotationMatrix = getFlatRotationMatrix(); + float fm[4][4]; + flatRotationMatrix.getMatrix(fm); + modelTransform.setFlatRotation(fm); + float rightFlatX, rightFlatY; getRightCortexFlatMapOffset(rightFlatX, rightFlatY); modelTransform.setRightCortexFlatMapOffset(rightFlatX, rightFlatY); @@ -3520,6 +4079,12 @@ obliqueRotationMatrix.setMatrix(mob); setObliqueVolumeRotationMatrix(obliqueRotationMatrix); + float fm[4][4]; + modelTransform.getFlatRotation(fm); + Matrix4x4 flatRotationMatrix; + flatRotationMatrix.setMatrix(fm); + setFlatRotationMatrix(flatRotationMatrix); + const float scale = modelTransform.getScaling(); setScaling(scale); @@ -3555,7 +4120,8 @@ { SceneClass* sceneClass = new SceneClass(instanceName, "BrowserTabContent", - 6); // WB-491 Flat Fixes + 7); // matrices no longer support translation/zooming + //6); // WB-491 Flat Fixes //5); // WB-576 //4); // WB-491, 1/28/2015 //3); // version 3 as of 4/22/2014 @@ -3564,7 +4130,13 @@ m_obliqueVolumeRotationMatrix->getMatrixForOpenGL(obliqueMatrix); sceneClass->addFloatArray("m_obliqueVolumeRotationMatrix", obliqueMatrix, 16); - m_sceneClassAssistant->saveMembers(sceneAttributes, + TileTabsBrowserTabGeometry manualLayoutTabGeometry(m_tabNumber); + m_manualLayoutBrowserTabAnnotation->getTileTabsGeometry(&manualLayoutTabGeometry); + TileTabsBrowserTabGeometrySceneHelper geometryHelper(&manualLayoutTabGeometry); + sceneClass->addClass(geometryHelper.saveToScene(sceneAttributes, + "m_manualLayoutTabGeometry")); + + m_sceneClassAssistant->saveMembers(sceneAttributes, sceneClass); return sceneClass; @@ -3593,9 +4165,19 @@ m_brainModelYokingGroup = YokingGroupEnum::YOKING_GROUP_A; m_chartModelYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + initializeScaleBar(); + m_sceneClassAssistant->restoreMembers(sceneAttributes, sceneClass); + TileTabsBrowserTabGeometry manualLayoutTabGeometry(m_tabNumber); + TileTabsBrowserTabGeometrySceneHelper geometryHelper(&manualLayoutTabGeometry); + geometryHelper.restoreFromScene(sceneAttributes, + sceneClass->getClass("m_manualLayoutTabGeometry")); + m_manualLayoutBrowserTabAnnotation->setFromTileTabsGeometry(&manualLayoutTabGeometry); + m_manualLayoutBrowserTabAnnotation->setBrowserTabContent(this, + m_tabNumber); + /* * With charting version two, yoking was split into chart and non-chart yoking * If old yoking group is found, apply it to the brain model yoking group @@ -3652,8 +4234,6 @@ m.getRotation(rotationX, rotationY, rotationZ); - //rotationX = -rotationX; - //rotationY = 180.0 - rotationY; rotationY = 90 + rotationY; rotationZ = -rotationZ; m.identity(); @@ -3760,6 +4340,8 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: { ModelSurface* modelSurface = getDisplayedSurfaceModel(); @@ -3811,7 +4393,7 @@ boundingBox->getDifferenceY()), zDiff); if (zDiff > 0.0) { - const float scaleAdjustment = zDiff / maxDim; //maxDim / zDiff; + const float scaleAdjustment = zDiff / maxDim; float scaling = getScaling(); scaling *= scaleAdjustment; setScaling(scaling); @@ -3848,9 +4430,38 @@ } } + + testForRestoreSceneWarnings(sceneAttributes, + sceneClass->getVersionNumber()); } /** + * Test for scene warnings + */ +void +BrowserTabContent::testForRestoreSceneWarnings(const SceneAttributes* sceneAttributes, + const int32_t sceneVersion) +{ + if (sceneVersion <= 6) { + ModelChartTwo* chartModel = getDisplayedChartTwoModel(); + if (chartModel != NULL) { + if (chartModel->getSelectedChartTwoDataType(m_tabNumber) == ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX) { + float translation[3]; + getTranslation(translation); + const float zoom = getScaling(); + if (MathFunctions::compareValuesEqual(translation, 2, 0.0, 0.001) + && MathFunctions::compareValuesEqual(&zoom, 1, 1.0, 0.001)) { + /* OK */ + } + else { + sceneAttributes->setSceneRestoreWarningCode(SceneRestoreWarningCodesEnum::CHART_TWO_MATRIX_TRANSFORM); + } + } + } + } +} + +/** * Get the clipping planes enabled attributes * * @param xEnabled @@ -3970,6 +4581,7 @@ updateYokedModelBrowserTabs(); } + /** * Get the clipping plane group (const method). * @@ -3994,6 +4606,25 @@ } /** + * @return Clipping planes enabled + */ +bool BrowserTabContent::isClippingPlanesEnabled() +{ + return m_clippingPlaneGroup->isEnabled(); +} + +/** + * Set clipping planes enabled + * @param status + * New enabled status + */ +void +BrowserTabContent::setClippingPlanesEnabled(const bool status) +{ + m_clippingPlaneGroup->setEnabled(status); +} + +/** * @return the projection view type (view from left/right) */ ProjectionViewTypeEnum::Enum @@ -4092,12 +4723,72 @@ } /** - * @return Type of slice projection (oblique/orthogonal) + * @return Selected type of slice projection (oblique/orthogonal) */ VolumeSliceProjectionTypeEnum::Enum BrowserTabContent::getSliceProjectionType() const { - return m_volumeSliceSettings->getSliceProjectionType(); + std::vector sliceProjectionTypes; + getValidSliceProjectionTypes(sliceProjectionTypes); + + /* + * Selected projection type may not be valid and needs to be set to a valid projection type + */ + VolumeSliceProjectionTypeEnum::Enum projType = m_volumeSliceSettings->getSliceProjectionType(); + if (std::find(sliceProjectionTypes.begin(), + sliceProjectionTypes.end(), + projType) == sliceProjectionTypes.end()) { + if ( ! sliceProjectionTypes.empty()) { + CaretAssertVectorIndex(sliceProjectionTypes, 0); + projType = sliceProjectionTypes[0]; + const_cast(m_volumeSliceSettings)->setSliceProjectionType(projType); + } + } + + return projType; +} + +/** + * Get valid slice projection types (ortho is NOT valid if any volume is oblique only) + * @param sliceProjectionTypesOut + * Output containing valid slice projection types based upon selected files in overlays + */ +void +BrowserTabContent::getValidSliceProjectionTypes(std::vector& sliceProjectionTypesOut) const +{ + sliceProjectionTypesOut.clear(); + sliceProjectionTypesOut.push_back(VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); + + bool orthoValidFlag(false); + switch (getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + case ModelTypeEnum::MODEL_TYPE_INVALID: + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + orthoValidFlag = true; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + { + /* + * Note: OverlaySet will be NULL if loading a scene + * and the volume file(s) in the scene are missing (do not exist) + */ + const OverlaySet* overlaySet = getOverlaySet(); + if (overlaySet != NULL) { + if ( ! overlaySet->hasObliqueOnlyVolumeSelected()) { + orthoValidFlag = true; + } + } + } + break; + } + + if (orthoValidFlag) { + sliceProjectionTypesOut.push_back(VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL); + } } /** @@ -4735,10 +5426,8 @@ * Find another browser tab using the same yoking as 'me' and copy * yoked data from the other browser tab. */ - for (std::set::iterator iter = s_allBrowserTabContent.begin(); - iter != s_allBrowserTabContent.end(); - iter++) { - BrowserTabContent* btc = *iter; + std::vector activeTabs = BrowserTabContent::getOpenBrowserTabs(); + for (auto btc : activeTabs) { if (btc != this) { if (btc->getChartModelYokingGroup() == m_chartModelYokingGroup) { copyFromTabIndex = btc->getTabNumber(); @@ -4791,10 +5480,8 @@ * Find another browser tab using the same yoking as 'me' and copy * yoked data from the other browser tab. */ - for (std::set::iterator iter = s_allBrowserTabContent.begin(); - iter != s_allBrowserTabContent.end(); - iter++) { - BrowserTabContent* btc = *iter; + std::vector activeTabs = BrowserTabContent::getOpenBrowserTabs(); + for (auto btc : activeTabs) { if (btc != this) { if (btc->getBrainModelYokingGroup() == m_brainModelYokingGroup) { /* @@ -4804,6 +5491,7 @@ *m_flatSurfaceViewingTransformation = *btc->m_flatSurfaceViewingTransformation; *m_cerebellumViewingTransformation = *btc->m_cerebellumViewingTransformation; *m_volumeSliceViewingTransformation = *btc->m_volumeSliceViewingTransformation; + *m_mediaViewingTransformation = *btc->m_mediaViewingTransformation; const VolumeSliceViewPlaneEnum::Enum slicePlane = m_volumeSliceSettings->getSliceViewPlane(); *m_volumeSliceSettings = *btc->m_volumeSliceSettings; m_volumeSliceSettings->setSliceViewPlane(slicePlane); // do not yoke the slice plane @@ -4861,6 +5549,8 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: @@ -4896,10 +5586,8 @@ /* * Copy yoked data from 'me' to all other yoked browser tabs */ - for (std::set::iterator iter = s_allBrowserTabContent.begin(); - iter != s_allBrowserTabContent.end(); - iter++) { - BrowserTabContent* btc = *iter; + std::vector activeTabs = BrowserTabContent::getOpenBrowserTabs(); + for (auto btc : activeTabs) { if (btc != this) { /* * If anything is added, also need to update setYokingGroup() @@ -4919,6 +5607,12 @@ btc->m_displayVolumeAxesCrosshairLabels = m_displayVolumeAxesCrosshairLabels; btc->m_displayVolumeMontageAxesCoordinates = m_displayVolumeMontageAxesCoordinates; btc->m_volumeMontageCoordinatePrecision = m_volumeMontageCoordinatePrecision; + + /* + * DO NOT YOKE MEDIA TRANSFORMATION (but might have its own yoking in the future + * *btc->m_mediaViewingTransformation = *m_mediaViewingTransformation; + */ + /** * lighting enabled NOT yoked * btc->m_lightingEnabled = m_lightingEnabled; @@ -4945,10 +5639,8 @@ /* * Copy yoked data from 'me' to all other yoked browser tabs */ - for (std::set::iterator iter = s_allBrowserTabContent.begin(); - iter != s_allBrowserTabContent.end(); - iter++) { - BrowserTabContent* btc = *iter; + std::vector activeTabs = BrowserTabContent::getOpenBrowserTabs(); + for (auto btc : activeTabs) { if (btc != this) { /* * If anything is added, also need to update setYokingGroup() @@ -4961,6 +5653,160 @@ } } +/** + * @return Pointer to the manual layout browser tab annotation + */ +AnnotationBrowserTab* +BrowserTabContent::getManualLayoutBrowserTabAnnotation() +{ + return m_manualLayoutBrowserTabAnnotation.get(); +} + +/** + * @return Pointer to the manual layout browser tab annotation (const method) + */ +const +AnnotationBrowserTab* +BrowserTabContent::getManualLayoutBrowserTabAnnotation() const +{ + return m_manualLayoutBrowserTabAnnotation.get(); +} +/** + * @return True if the manual tab geometry bounds are still + * set to the default bounds. + */ +bool +BrowserTabContent::isDefaultManualTabGeometryBounds() const +{ + float xMin(0.0), xMax(0.0), yMin(0.0), yMax(0.0); + m_manualLayoutBrowserTabAnnotation->getBounds2D(xMin, xMax, yMin, yMax); + + if (xMin != m_defaultManualTabGeometryBounds[0]) { + return false; + } + if (xMax != m_defaultManualTabGeometryBounds[1]) { + return false; + } + if (yMin != m_defaultManualTabGeometryBounds[2]) { + return false; + } + if (yMax != m_defaultManualTabGeometryBounds[3]) { + return false; + } + + return true; +} + +/** + * @return Pointer to the scale bar + */ +AnnotationScaleBar* +BrowserTabContent::getScaleBar() +{ + return m_scaleBar.get(); +} + +/** + * @return Pointer to the scale bar + */ +const AnnotationScaleBar* +BrowserTabContent::getScaleBar() const +{ + return m_scaleBar.get(); +} + +/** + * Initialize a scale bar + */ +void +BrowserTabContent::initializeScaleBar() +{ + m_scaleBar->setTabIndex(m_tabNumber); + m_scaleBar->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); + m_scaleBar->getCoordinate()->setXYZ(10.0, 10.0, 0.0); +} + +/** + * This method should be called by SessionManager and NOTHING ELSE. + * Set the closed status (tab has been closed by user but is available for reopening. + * @param closedStatus + * New closed status + */ +void +BrowserTabContent::setClosedStatusFromSessionManager(const bool closedStatus) +{ + m_closedFlag = closedStatus; + + /* + * If reopening, may need to update from yoking + */ + if ( ! m_closedFlag) { + /* + * May need to update yoking + */ + setChartModelYokingGroup(getChartModelYokingGroup()); + setBrainModelYokingGroup(getBrainModelYokingGroup()); + } +} + +/** + * Set the position in the toolbar of a tab from when the tab was closed + * @param tabBarPosition + * Position (index) of the tab in the tab bar + */ +void +BrowserTabContent::setClosedTabWindowTabBarPositionIndex(const int32_t tabBarPosition) +{ + m_closedTabBarPosition = tabBarPosition; +} + +/** + * @return Position in the toolbar of a tab from when the tab was closed + */ +int32_t +BrowserTabContent::getClosedTabWindowTabBarPositionIndex() const +{ + return m_closedTabBarPosition; +} + +/** + * Set the position in the toolbar of a tab from when the tab was closed + * @param tabBarPosition + * Position (index) of the tab in the tab bar + */ +void +BrowserTabContent::setClosedTabWindowIndex(const int32_t windowIndex) +{ + m_closedWindowIndex = windowIndex; +} + +/** + * @return Position in the toolbar of a tab from when the tab was closed + */ +int32_t +BrowserTabContent::getClosedTabWindowIndex() const +{ + return m_closedWindowIndex; +} + + +/** + *@return Browser tabs the are open and in use by the user. + * Any tabs that are closed but available for reopening are excluded + */ +std::vector +BrowserTabContent::getOpenBrowserTabs() +{ + std::vector openTabs; + + for (auto tab : s_allBrowserTabContent) { + if ( ! tab->m_closedFlag) { + openTabs.push_back(tab); + } + } + + return openTabs; +} diff -Nru connectome-workbench-1.4.2/src/Brain/BrowserTabContent.h connectome-workbench-1.5.0/src/Brain/BrowserTabContent.h --- connectome-workbench-1.4.2/src/Brain/BrowserTabContent.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrowserTabContent.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,9 +21,12 @@ */ /*LICENSE_END*/ +#include #include #include "CaretObject.h" +#include "ChartTwoAxisOrientationTypeEnum.h" +#include "ChartTwoAxisScaleRangeModeEnum.h" #include "EventListenerInterface.h" #include "Model.h" #include "ModelTypeEnum.h" @@ -41,17 +44,22 @@ namespace caret { + class AnnotationBrowserTab; class AnnotationColorBar; + class AnnotationScaleBar; class BrainOpenGLViewportContent; class CaretDataFile; class CaretMappableDataFile; + class ChartTwoCartesianOrientedAxes; class ChartTwoMatrixDisplayProperties; class ChartTwoOverlaySet; class ClippingPlaneGroup; class EventCaretMappableDataFilesAndMapsInDisplayedOverlays; class Matrix4x4; + class MediaOverlaySet; class ModelChart; class ModelChartTwo; + class ModelMedia; class ModelSurface; class ModelSurfaceMontage; class ModelSurfaceSelector; @@ -65,6 +73,7 @@ class Surface; class ViewingTransformations; class ViewingTransformationsCerebellum; + class ViewingTransformationsMedia; class ViewingTransformationsVolume; class VolumeMappableInterface; class VolumeSliceSettings; @@ -101,6 +110,13 @@ const ChartTwoOverlaySet* getChartTwoOverlaySet() const; + std::vector getYokedAxes(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode) const; + + MediaOverlaySet* getMediaOverlaySet(); + + const MediaOverlaySet* getMediaOverlaySet() const; + int32_t getTabNumber() const; ModelTypeEnum::Enum getSelectedModelType() const; @@ -119,6 +135,10 @@ const ModelChartTwo* getDisplayedChartTwoModel() const; + ModelMedia* getDisplayedMediaModel(); + + const ModelMedia* getDisplayedMediaModel() const; + ModelSurface* getDisplayedSurfaceModel(); const ModelSurface* getDisplayedSurfaceModel() const; @@ -149,6 +169,8 @@ bool isVolumeSlicesDisplayed() const; + bool isMediaDisplayed() const; + bool isWholeBrainDisplayed() const; void getFilesDisplayedInTab(std::vector& displayedDataFilesOut); @@ -169,6 +191,8 @@ bool isSurfaceMontageModelValid() const; + bool isMediaModelValid() const; + void getAnnotationColorBars(std::vector& colorBarsOut); void getDisplayedPaletteMapFiles(std::vector& mapFiles, @@ -215,6 +239,10 @@ void resetClippingPlaneTransformation(); + bool isClippingPlanesEnabled(); + + void setClippingPlanesEnabled(const bool status); + const float* getTranslation() const; void getTranslation(float translationOut[3]) const; @@ -237,6 +265,10 @@ void setObliqueVolumeRotationMatrix(const Matrix4x4& obliqueRotationMatrix); + Matrix4x4 getFlatRotationMatrix() const; + + void setFlatRotationMatrix(const Matrix4x4& flatRotationMatrix); + void getRightCortexFlatMapOffset(float& offsetX, float& offsetY) const; @@ -276,7 +308,12 @@ const int32_t mouseDeltaX, const int32_t mouseDeltaY); - void applyMouseScaling(const int32_t mouseDX, + void applyMouseScaling(BrainOpenGLViewportContent* viewportContent, + const int32_t mousePressX, + const int32_t mousePressY, + const float mouseX, + const float mouseY, + const int32_t mouseDX, const int32_t mouseDY); void applyMouseTranslation(BrainOpenGLViewportContent* viewportContent, @@ -290,6 +327,18 @@ double rotationMatrixOut[16], float& scalingOut) const; + void applyChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2); + + void finalizeChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2); + void getTransformationsInModelTransform(ModelTransform& modelTransform) const; void setTransformationsFromModelTransform(const ModelTransform& modelTransform); @@ -310,6 +359,8 @@ void setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType); + void getValidSliceProjectionTypes(std::vector& sliceProjectionTypesOut) const; + VolumeSliceProjectionTypeEnum::Enum getSliceProjectionType() const; void setSliceProjectionType(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType); @@ -441,11 +492,32 @@ const ViewingTransformations* getViewingTransformation() const; + AnnotationBrowserTab* getManualLayoutBrowserTabAnnotation(); + + const AnnotationBrowserTab* getManualLayoutBrowserTabAnnotation() const; + + bool isDefaultManualTabGeometryBounds() const; + + AnnotationScaleBar* getScaleBar(); + + const AnnotationScaleBar* getScaleBar() const; + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); + + void setClosedStatusFromSessionManager(const bool closedStatus); + + void setClosedTabWindowTabBarPositionIndex(const int32_t tabBarPosition); + + int32_t getClosedTabWindowTabBarPositionIndex() const; + + void setClosedTabWindowIndex(const int32_t windowIndex); + + int32_t getClosedTabWindowIndex() const; + private: class ColorBarFileMap { public: @@ -467,11 +539,6 @@ BrowserTabContent& operator=(const BrowserTabContent&); -// VolumeSliceViewPlaneEnum::Enum getSliceViewPlaneForVolumeAllSliceView(const int viewport[4], -// const int32_t mousePressX, -// const int32_t mousePressY, -// int sliceViewportOut[4]) const; - void updateBrainModelYokedBrowserTabs(); void updateYokedModelBrowserTabs(); @@ -480,6 +547,13 @@ AString getTabNamePrefix() const override; + void initializeScaleBar(); + + void testForRestoreSceneWarnings(const SceneAttributes* sceneAttributes, + const int32_t sceneVersion); + + static std::vector getOpenBrowserTabs(); + /** Number of this tab */ int32_t m_tabNumber; @@ -507,6 +581,9 @@ /** The chart two model */ ModelChartTwo* m_chartTwoModel; + /** The multi-media model */ + ModelMedia* m_mediaModel; + /** * Name requested by user interface - reflects contents * such as Surface, Volume Slices, etc @@ -549,6 +626,9 @@ /** Transformation for surface/all viewing */ ViewingTransformations* m_flatSurfaceViewingTransformation; + /** Transformation for media viewing */ + ViewingTransformationsMedia* m_mediaViewingTransformation; + /** Transformation for volume slices viewing */ ViewingTransformationsVolume* m_volumeSliceViewingTransformation; @@ -564,6 +644,8 @@ /** Whole brain surface settings. */ WholeBrainSurfaceSettings* m_wholeBrainSurfaceSettings; + std::unique_ptr m_scaleBar; + /** aspect ratio */ float m_aspectRatio; @@ -596,7 +678,26 @@ */ bool isExecutingConstructor; - /** Contains all active browser tab content instances */ + /** Manual layout brower tab annotation */ + std::unique_ptr m_manualLayoutBrowserTabAnnotation; + + /** Default bounds of manual tab geometry */ + float m_defaultManualTabGeometryBounds[4]; + + /** True if browser tab content has been closed but is available for reopening */ + bool m_closedFlag = false; + + /** Position in the tab bar befpre a tab that was closed */ + int32_t m_closedTabBarPosition = -1; + + /** Index of window before tab was closed */ + int32_t m_closedWindowIndex = -1; + + /** + * NEVER access this directly as it may contain tabs that are closed but available for reopening. + * Instead, call getOpenBrowserTabs(). + * Contains all active browser tab content instances + */ static std::set s_allBrowserTabContent; }; diff -Nru connectome-workbench-1.4.2/src/Brain/BrowserWindowContent.cxx connectome-workbench-1.5.0/src/Brain/BrowserWindowContent.cxx --- connectome-workbench-1.4.2/src/Brain/BrowserWindowContent.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrowserWindowContent.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,12 +23,18 @@ #include "BrowserWindowContent.h" #undef __BROWSER_WINDOW_CONTENT_DECLARE__ +#include + +#include "AnnotationBrowserTab.h" +#include "BrowserTabContent.h" #include "CaretAssert.h" +#include "EventBrowserWindowGetTabs.h" +#include "EventManager.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SceneIntegerArray.h" #include "ScenePrimitive.h" -#include "TileTabsConfiguration.h" +#include "TileTabsLayoutGridConfiguration.h" using namespace caret; @@ -47,8 +53,8 @@ : CaretObject(), m_windowIndex(windowIndex) { - m_automaticTileTabsConfiguration.reset(new TileTabsConfiguration()); - m_customTileTabsConfiguration.reset(new TileTabsConfiguration()); + m_automaticGridTileTabsConfiguration.reset(TileTabsLayoutGridConfiguration::newInstanceAutomaticGrid()); + m_customGridTileTabsConfiguration.reset(TileTabsLayoutGridConfiguration::newInstanceCustomGrid()); m_validFlag = false; reset(); @@ -58,11 +64,12 @@ m_sceneAssistant->add("m_windowAspectLockedRatio", &m_windowAspectLockedRatio); m_sceneAssistant->add("m_allTabsInWindowAspectRatioLocked", &m_allTabsInWindowAspectRatioLocked); m_sceneAssistant->add("m_tileTabsEnabled", &m_tileTabsEnabled); - m_sceneAssistant->add("m_tileTabsConfigurationMode", - &m_tileTabsConfigurationMode); + m_sceneAssistant->add("m_tileTabsConfigurationMode", + &m_tileTabsConfigurationMode); m_sceneAssistant->add("m_sceneGraphicsWidth", &m_sceneGraphicsWidth); m_sceneAssistant->add("m_sceneGraphicsHeight", &m_sceneGraphicsHeight); m_sceneAssistant->add("m_sceneSelectedTabIndex", &m_sceneSelectedTabIndex); + m_sceneAssistant->add("m_windowAnnotationsStackingOrder", &m_windowAnnotationsStackingOrder); } /** @@ -108,14 +115,24 @@ m_tileTabsEnabled = false; m_sceneGraphicsHeight = 0; m_sceneGraphicsWidth = 0; - m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; - m_automaticTileTabsConfiguration->updateAutomaticConfigurationRowsAndColumns(1); - /* sets rows/columns/factors to defaults */ - m_customTileTabsConfiguration->updateAutomaticConfigurationRowsAndColumns(1); + m_tileTabsConfigurationMode = TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID; + m_automaticGridTileTabsConfiguration->updateAutomaticConfigurationRowsAndColumns(1); + setCustomGridConfigurationToDefault(); m_sceneSelectedTabIndex = 0; m_sceneTabIndices.clear(); + m_windowAnnotationsStackingOrder = -1000; /* way in front */ } +/** + * Set the custom grid configuration to the default + */ +void +BrowserWindowContent::setCustomGridConfigurationToDefault() +{ + /* sets rows/columns/factors to defaults */ + m_customGridTileTabsConfiguration->updateAutomaticConfigurationRowsAndColumns(1); + m_customGridTileTabsConfiguration->setCustomDefaultFlag(true); +} /** * Get a description of this object's content. @@ -215,18 +232,23 @@ * @return The selected tile tabs configuration. * This will be the automatic configuration when automatic is selected, * otherwise it is the custom configuration. + * Will return NULL if the selected tile tabs configuration is + * neither AUTO nor CUSTOM grid. */ -TileTabsConfiguration* -BrowserWindowContent::getSelectedTileTabsConfiguration() +TileTabsLayoutBaseConfiguration* +BrowserWindowContent::getSelectedTileTabsGridConfiguration() { - TileTabsConfiguration* configMode = NULL; + TileTabsLayoutBaseConfiguration* configMode = NULL; switch (m_tileTabsConfigurationMode) { - case TileTabsGridModeEnum::AUTOMATIC: - configMode = m_automaticTileTabsConfiguration.get(); + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + configMode = m_automaticGridTileTabsConfiguration.get(); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + configMode = m_customGridTileTabsConfiguration.get(); break; - case TileTabsGridModeEnum::CUSTOM: - configMode = m_customTileTabsConfiguration.get(); + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + return NULL; break; } CaretAssert(configMode); @@ -238,18 +260,23 @@ * @return The selected tile tabs configuration (const method) * This will be the automatic configuration when automatic is selected, * otherwise it is the custom configuration. + * Will return NULL if the selected tile tabs configuration is + * neither AUTO nor CUSTOM grid. */ -const TileTabsConfiguration* -BrowserWindowContent::getSelectedTileTabsConfiguration() const +const TileTabsLayoutBaseConfiguration* +BrowserWindowContent::getSelectedTileTabsGridConfiguration() const { - TileTabsConfiguration* configMode = NULL; + TileTabsLayoutBaseConfiguration* configMode = NULL; switch (m_tileTabsConfigurationMode) { - case TileTabsGridModeEnum::AUTOMATIC: - configMode = m_automaticTileTabsConfiguration.get(); + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + configMode = m_automaticGridTileTabsConfiguration.get(); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + configMode = m_customGridTileTabsConfiguration.get(); break; - case TileTabsGridModeEnum::CUSTOM: - configMode = m_customTileTabsConfiguration.get(); + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + return NULL; break; } CaretAssert(configMode); @@ -258,45 +285,62 @@ } /** - * @return The automatic tile tabs configuration. + * @return The automatic grid tile tabs configuration + */ +TileTabsLayoutGridConfiguration* +BrowserWindowContent::getAutomaticGridTileTabsConfiguration() +{ + return m_automaticGridTileTabsConfiguration.get(); +} + +/** + * @return The automatic grid tile tabs configuration (const method) */ -TileTabsConfiguration* -BrowserWindowContent::getAutomaticTileTabsConfiguration() +const TileTabsLayoutGridConfiguration* +BrowserWindowContent::getAutomaticGridTileTabsConfiguration() const { - return m_automaticTileTabsConfiguration.get(); + return m_automaticGridTileTabsConfiguration.get(); } /** - * @return The automatic tile tabs configuration (const method) + * @return The custom grid tile tabs configuration */ -const TileTabsConfiguration* -BrowserWindowContent::getAutomaticTileTabsConfiguration() const +TileTabsLayoutGridConfiguration* +BrowserWindowContent::getCustomGridTileTabsConfiguration() { - return m_automaticTileTabsConfiguration.get(); + return m_customGridTileTabsConfiguration.get(); } /** - * @return The custom tile tabs configuration + * @return The custom grid tile tabs configuration (const method) */ -TileTabsConfiguration* -BrowserWindowContent::getCustomTileTabsConfiguration() +const TileTabsLayoutGridConfiguration* +BrowserWindowContent::getCustomGridTileTabsConfiguration() const { - return m_customTileTabsConfiguration.get(); + return m_customGridTileTabsConfiguration.get(); } /** - * @return The custom tile tabs configuration (const method) + * Set the custom grid tile tabs configuration + * + * @param gridConfiguration + * Grid configuration that is copied to the custom grid */ -const TileTabsConfiguration* -BrowserWindowContent::getCustomTileTabsConfiguration() const +void +BrowserWindowContent::setCustomGridTileTabsConfiguration(const TileTabsLayoutGridConfiguration* gridConfiguration) { - return m_customTileTabsConfiguration.get(); + CaretAssert(gridConfiguration); + if (gridConfiguration == NULL) { + return; + } + + m_customGridTileTabsConfiguration->copy(*gridConfiguration); } /** * @return The tile tabs configuration mode. */ -TileTabsGridModeEnum::Enum +TileTabsLayoutConfigurationTypeEnum::Enum BrowserWindowContent::getTileTabsConfigurationMode() const { return m_tileTabsConfigurationMode; @@ -309,11 +353,183 @@ * New value for configuration mode. */ void -BrowserWindowContent::setTileTabsConfigurationMode(const TileTabsGridModeEnum::Enum configMode) +BrowserWindowContent::setTileTabsConfigurationMode(const TileTabsLayoutConfigurationTypeEnum::Enum configMode) { + const TileTabsLayoutConfigurationTypeEnum::Enum previousMode = m_tileTabsConfigurationMode; m_tileTabsConfigurationMode = configMode; + + switch (m_tileTabsConfigurationMode) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + if (m_customGridTileTabsConfiguration->isCustomDefaultFlag()) { + m_customGridTileTabsConfiguration->setCustomDefaultFlag(false); + + EventBrowserWindowGetTabs windowTabsEvent(m_windowIndex); + EventManager::get()->sendEvent(windowTabsEvent.getPointer()); + int32_t numTabs = static_cast(windowTabsEvent.getBrowserTabIndices().size()); + if (numTabs > 0) { + m_customGridTileTabsConfiguration->updateAutomaticConfigurationRowsAndColumns(numTabs); + } + } + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + { + EventBrowserWindowGetTabs tabsEvent(m_windowIndex); + EventManager::get()->sendEvent(tabsEvent.getPointer()); + + std::vector allTabs = tabsEvent.getBrowserTabs(); + bool allDefaultFlag(true); + for (auto tab : allTabs) { + if ( ! tab->isDefaultManualTabGeometryBounds()) { + allDefaultFlag = false; + break; + } + } + if (allDefaultFlag) { + switch (previousMode) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + setManualConfigurationFromGridConfiguration(getAutomaticGridTileTabsConfiguration()); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + setManualConfigurationFromGridConfiguration(getCustomGridTileTabsConfiguration()); + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + /* manual was not initialized so use automatic grid */ + setManualConfigurationFromGridConfiguration(getAutomaticGridTileTabsConfiguration()); + break; + } + } + } + break; + } +} + +/** + * Set the manual configuration from the given grid configuratin + * + * @param gridConfiguration + * Grid configuration copied to manual configuration. + */ +void +BrowserWindowContent::setManualConfigurationFromGridConfiguration(TileTabsLayoutGridConfiguration* gridConfiguration) +{ + EventBrowserWindowGetTabs tabsEvent(m_windowIndex); + EventManager::get()->sendEvent(tabsEvent.getPointer()); + + std::vector allTabs = tabsEvent.getBrowserTabs(); + const int32_t numTabs = static_cast(allTabs.size()); + + const int32_t windowWidth(100); + const int32_t windowHeight(100); + std::vector rowHeightsInt; + std::vector columnWidthsInt; + + if (gridConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, + windowHeight, + numTabs, + gridConfiguration->getLayoutType(), + rowHeightsInt, + columnWidthsInt)) { + const int32_t numRows = static_cast(rowHeightsInt.size()); + const int32_t numCols = static_cast(columnWidthsInt.size()); + + /* + * No longer computer the row sum as it causes problems when + * the tabs do not fill the entire width and/or height of window + */ + const float rowSum = 0.0; + std::vector rowHeights; + for (int32_t iRow = 0; iRow < numRows; iRow++) { + if (rowSum > 0.0) { + rowHeights.push_back((rowHeightsInt[iRow] / rowSum) * 100.0); + } + else { + rowHeights.push_back(rowHeightsInt[iRow]); + } + } + CaretAssert(rowHeights.size() == rowHeightsInt.size()); + + const float columnSum = 0.0; + std::vector columnWidths; + for (int32_t iCol = 0; iCol < numCols; iCol++) { + if (columnSum > 0.0) { + columnWidths.push_back((columnWidthsInt[iCol] / columnSum) * 100.0); + } + else { + columnWidths.push_back(columnWidthsInt[iCol]); + } + } + CaretAssert(columnWidths.size() == columnWidthsInt.size()); + + int32_t tabCounter(0); + float yBottom(windowHeight); + for (int32_t i = 0; i < numRows; i++) { + CaretAssertVectorIndex(rowHeights, i); + const float height = rowHeights[i]; + yBottom -= height; + + switch (gridConfiguration->getRow(i)->getContentType()) { + case TileTabsGridRowColumnContentTypeEnum::SPACE: + break; + case TileTabsGridRowColumnContentTypeEnum::TAB: + { + float xLeft(0.0); + for (int32_t j = 0; j < numCols; j++) { + CaretAssertVectorIndex(columnWidths, j); + const float width = columnWidths[j]; + + switch (gridConfiguration->getColumn(j)->getContentType()) { + case TileTabsGridRowColumnContentTypeEnum::SPACE: + break; + case TileTabsGridRowColumnContentTypeEnum::TAB: + if (tabCounter < numTabs) { + CaretAssertVectorIndex(allTabs, tabCounter); + BrowserTabContent* tabContent(allTabs[tabCounter]); + AnnotationBrowserTab* browserTabAnnotation = tabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(browserTabAnnotation); + browserTabAnnotation->setBounds2D((xLeft / windowWidth) * 100.0, + ((xLeft + width) / windowWidth) * 100.0, + (yBottom / windowHeight) * 100.0, + ((yBottom + height) / windowHeight) * 100.0); + browserTabAnnotation->setStackingOrder(tabCounter + 1); + browserTabAnnotation->setBackgroundType(TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG); + + tabCounter++; + } + break; + } + + xLeft += width; + } + } + break; + } + } + } + +} +/** + * @return True if tile tabs is enabled AND a manual configuration is selected + */ +bool +BrowserWindowContent::isManualModeTileTabsConfigurationEnabled() const +{ + if (isTileTabsEnabled()) { + switch (getTileTabsConfigurationMode()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + return true; + break; + } + } + return false; } + /** * @return Width of the graphics region from scene. */ @@ -403,6 +619,26 @@ } /** + * @return The window annotations stacking order for a manual tile tabs layout + */ +int32_t +BrowserWindowContent::getWindowAnnotationsStackingOrder() const +{ + return m_windowAnnotationsStackingOrder; +} + +/** + * Set the window annotations stacking order for a manual tile tabs layout + * @param stackingOrder + * The new stacking order + */ +void +BrowserWindowContent::setWindowAnnotationsStackingOrder(const int32_t stackingOrder) +{ + m_windowAnnotationsStackingOrder = stackingOrder; +} + +/** * Save information specific to this type of model to the scene. * * @param sceneAttributes @@ -427,14 +663,14 @@ sceneClass); sceneClass->addString("m_customTileTabsConfigurationLatest", - m_customTileTabsConfiguration->encodeInXML()); + m_customGridTileTabsConfiguration->encodeInXML()); /* * Add a tile tabs version one so older versions of wb_view * may still load the scene correctly */ sceneClass->addString("m_customTileTabsConfiguration", - m_customTileTabsConfiguration->encodeVersionInXML(1)); + m_customGridTileTabsConfiguration->encodeVersionInXML(1)); /* * Write the tile tabs configuration a second time using @@ -443,7 +679,7 @@ * tile tabs correctly. */ sceneClass->addString("m_sceneTileTabsConfiguration", - m_customTileTabsConfiguration->encodeVersionInXML(1)); + m_customGridTileTabsConfiguration->encodeVersionInXML(1)); sceneClass->addChild(new SceneIntegerArray("m_sceneTabIndices", m_sceneTabIndices)); @@ -502,17 +738,36 @@ /* Restore an old name for custom configuration */ tileTabsConfig = sceneClass->getStringValue("m_tileTabsConfiguration"); } + if (tileTabsConfig.isEmpty()) { + /* + * "m_sceneTileTabsConfiguration" is from scenes before "m_customTileTabsConfiguration" was added + */ + tileTabsConfig = sceneClass->getStringValue("m_sceneTileTabsConfiguration"); + } if ( ! tileTabsConfig.isEmpty()) { AString errorMessage; - const bool valid = m_customTileTabsConfiguration->decodeFromXML(tileTabsConfig, - errorMessage); - if ( ! valid) { + TileTabsLayoutBaseConfiguration* config = TileTabsLayoutBaseConfiguration::decodeFromXML(tileTabsConfig, + errorMessage); + if (config != NULL) { + switch (config->getLayoutType()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + CaretAssert(0); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + m_customGridTileTabsConfiguration.reset(config->castToGridConfiguration()); + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + CaretAssert(0); + break; + } + } + else { sceneAttributes->addToErrorMessage("Failed to decode custom tile tabs configuration with error \"" + errorMessage + "\" from BrowserWindowContent: \"" + tileTabsConfig + "\""); - m_customTileTabsConfiguration.reset(new TileTabsConfiguration()); + m_customGridTileTabsConfiguration.reset(TileTabsLayoutGridConfiguration::newInstanceCustomGrid()); } /* @@ -522,36 +777,16 @@ * versions before before WB-735. */ (void)sceneClass->getStringValue("m_sceneTileTabsConfiguration"); - } - else { - /* - * "m_sceneTileTabsConfiguration" is from scenes before "m_customTileTabsConfiguration" was added - */ - const AString stringTileTabsConfig = sceneClass->getStringValue("m_sceneTileTabsConfiguration"); - if ( ! stringTileTabsConfig.isEmpty()) { - AString errorMessage; - const bool valid = m_customTileTabsConfiguration->decodeFromXML(stringTileTabsConfig, - errorMessage); - if ( ! valid) { - sceneAttributes->addToErrorMessage("Failed to decode custom tile tabs configuration with error \"" - + errorMessage - + "\" from BrowserWindowContent: \"" - + stringTileTabsConfig - + "\""); - m_customTileTabsConfiguration.reset(new TileTabsConfiguration()); - } - } - } - + } const ScenePrimitive* oldTileTabsAutoPrimitive = sceneClass->getPrimitive("m_tileTabsAutomaticConfigurationEnabled"); if (oldTileTabsAutoPrimitive != NULL) { const bool autoModeSelected = oldTileTabsAutoPrimitive->booleanValue(); if (autoModeSelected) { - m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; + m_tileTabsConfigurationMode = TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID; } else { - m_tileTabsConfigurationMode = TileTabsGridModeEnum::CUSTOM; + m_tileTabsConfigurationMode = TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID; } } @@ -561,13 +796,30 @@ * If tile tabs was enabled, use CUSTOM, otherwise AUTOMATIC */ if (m_tileTabsEnabled) { - m_tileTabsConfigurationMode = TileTabsGridModeEnum::CUSTOM; + m_tileTabsConfigurationMode = TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID; } else { - m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; + m_tileTabsConfigurationMode = TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID; } } + /* + * If NOT grid configuration mode for tile tabs, reset the + * grid configuration to its default. When the user selects + * grid configuration, it will default to the automatic grid + * so that all tabs are displayed. + */ + switch (m_tileTabsConfigurationMode) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + setCustomGridConfigurationToDefault(); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + setCustomGridConfigurationToDefault(); + break; + } + //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); @@ -620,15 +872,28 @@ const AString tileTabsConfigString = browserClass->getStringValue("m_sceneTileTabsConfiguration"); if ( ! tileTabsConfigString.isEmpty()) { AString errorMessage; - const bool valid = m_customTileTabsConfiguration->decodeFromXML(tileTabsConfigString, - errorMessage); - if ( ! valid) { + TileTabsLayoutBaseConfiguration* config = TileTabsLayoutBaseConfiguration::decodeFromXML(tileTabsConfigString, + errorMessage); + if (config != NULL) { + switch (config->getLayoutType()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + CaretAssert(0); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + m_customGridTileTabsConfiguration.reset(config->castToGridConfiguration()); + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + CaretAssert(0); + break; + } + } + else { sceneAttributes->addToErrorMessage("Failed to decode custom tile tabs configuration with error \"" + errorMessage - + "\" from OLD BrowserWindowContent: \"" + + "\" from BrowserWindowContent: \"" + tileTabsConfigString + "\""); - m_customTileTabsConfiguration.reset(new TileTabsConfiguration()); + m_customGridTileTabsConfiguration.reset(TileTabsLayoutGridConfiguration::newInstanceCustomGrid()); } } @@ -650,10 +915,10 @@ * If tile tabs was enabled, use CUSTOM, otherwise AUTOMATIC */ if (m_tileTabsEnabled) { - m_tileTabsConfigurationMode = TileTabsGridModeEnum::CUSTOM; + m_tileTabsConfigurationMode = TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID; } else { - m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; + m_tileTabsConfigurationMode = TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID; } } diff -Nru connectome-workbench-1.4.2/src/Brain/BrowserWindowContent.h connectome-workbench-1.5.0/src/Brain/BrowserWindowContent.h --- connectome-workbench-1.4.2/src/Brain/BrowserWindowContent.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/BrowserWindowContent.h 2021-02-16 19:46:47.000000000 +0000 @@ -28,11 +28,12 @@ #include "CaretObject.h" #include "SceneableInterface.h" -#include "TileTabsGridModeEnum.h" +#include "TileTabsLayoutConfigurationTypeEnum.h" namespace caret { class SceneClassAssistant; - class TileTabsConfiguration; + class TileTabsLayoutBaseConfiguration; + class TileTabsLayoutGridConfiguration; class BrowserWindowContent : public CaretObject, public SceneableInterface { @@ -65,21 +66,27 @@ void setTileTabsEnabled(const bool tileTabsEnabled); - TileTabsConfiguration* getSelectedTileTabsConfiguration(); + TileTabsLayoutBaseConfiguration* getSelectedTileTabsGridConfiguration(); - const TileTabsConfiguration* getSelectedTileTabsConfiguration() const; + const TileTabsLayoutBaseConfiguration* getSelectedTileTabsGridConfiguration() const; - TileTabsConfiguration* getAutomaticTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* getAutomaticGridTileTabsConfiguration(); - const TileTabsConfiguration* getAutomaticTileTabsConfiguration() const; + const TileTabsLayoutGridConfiguration* getAutomaticGridTileTabsConfiguration() const; - TileTabsConfiguration* getCustomTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* getCustomGridTileTabsConfiguration(); - const TileTabsConfiguration* getCustomTileTabsConfiguration() const; + const TileTabsLayoutGridConfiguration* getCustomGridTileTabsConfiguration() const; - TileTabsGridModeEnum::Enum getTileTabsConfigurationMode() const; + void setCustomGridTileTabsConfiguration(const TileTabsLayoutGridConfiguration* gridConfiguration); - void setTileTabsConfigurationMode(const TileTabsGridModeEnum::Enum configMode); + TileTabsLayoutConfigurationTypeEnum::Enum getTileTabsConfigurationMode() const; + + void setTileTabsConfigurationMode(const TileTabsLayoutConfigurationTypeEnum::Enum configMode); + + bool isManualModeTileTabsConfigurationEnabled() const; + + void setManualConfigurationFromGridConfiguration(TileTabsLayoutGridConfiguration* gridConfiguration); int32_t getSceneGraphicsWidth() const; @@ -97,6 +104,10 @@ void setSceneWindowTabIndices(const std::vector& sceneTabIndices); + int32_t getWindowAnnotationsStackingOrder() const; + + void setWindowAnnotationsStackingOrder(const int32_t stackingOrder); + // ADD_NEW_METHODS_HERE virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, @@ -128,6 +139,8 @@ BrowserWindowContent& operator=(const BrowserWindowContent&); + void setCustomGridConfigurationToDefault(); + std::unique_ptr m_sceneAssistant; bool m_validFlag; @@ -142,20 +155,22 @@ bool m_tileTabsEnabled = false; - TileTabsGridModeEnum::Enum m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; + TileTabsLayoutConfigurationTypeEnum::Enum m_tileTabsConfigurationMode = TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID; int32_t m_sceneGraphicsWidth = 0; int32_t m_sceneGraphicsHeight = 0; - std::unique_ptr m_automaticTileTabsConfiguration; + std::unique_ptr m_automaticGridTileTabsConfiguration; - std::unique_ptr m_customTileTabsConfiguration; + std::unique_ptr m_customGridTileTabsConfiguration; int32_t m_sceneSelectedTabIndex = 0; std::vector m_sceneTabIndices; + int32_t m_windowAnnotationsStackingOrder = -1000; + friend class BrainBrowserWindow; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/Brain/ChartTwoOverlayActiveModeEnum.cxx connectome-workbench-1.5.0/src/Brain/ChartTwoOverlayActiveModeEnum.cxx --- connectome-workbench-1.4.2/src/Brain/ChartTwoOverlayActiveModeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ChartTwoOverlayActiveModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_OVERLAY_ACTIVE_MODE_ENUM_DECLARE__ +#include "ChartTwoOverlayActiveModeEnum.h" +#undef __CHART_TWO_OVERLAY_ACTIVE_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoOverlayActiveModeEnum + * \brief Active mode for lines in chart two overlays + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartTwoOverlayActiveModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartTwoOverlayActiveModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoOverlayActiveModeEnum.h" + * + * Instatiate: + * m_chartTwoOverlayActiveModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartTwoOverlayActiveModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartTwoOverlayActiveModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartTwoOverlayActiveModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartTwoOverlayActiveModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoOverlayActiveModeEnum::Enum VARIABLE = m_chartTwoOverlayActiveModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoOverlayActiveModeEnum::ChartTwoOverlayActiveModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoOverlayActiveModeEnum::~ChartTwoOverlayActiveModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoOverlayActiveModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoOverlayActiveModeEnum(OFF, + "OFF", + "Off")); + + enumData.push_back(ChartTwoOverlayActiveModeEnum(ON, + "ON", + "On")); + + enumData.push_back(ChartTwoOverlayActiveModeEnum(ACTIVE, + "ACTIVE", + "Active")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoOverlayActiveModeEnum* +ChartTwoOverlayActiveModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoOverlayActiveModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoOverlayActiveModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoOverlayActiveModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoOverlayActiveModeEnum::Enum +ChartTwoOverlayActiveModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoOverlayActiveModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoOverlayActiveModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoOverlayActiveModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoOverlayActiveModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoOverlayActiveModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoOverlayActiveModeEnum::Enum +ChartTwoOverlayActiveModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoOverlayActiveModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoOverlayActiveModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoOverlayActiveModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoOverlayActiveModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoOverlayActiveModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoOverlayActiveModeEnum::Enum +ChartTwoOverlayActiveModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoOverlayActiveModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoOverlayActiveModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoOverlayActiveModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoOverlayActiveModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoOverlayActiveModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoOverlayActiveModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoOverlayActiveModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoOverlayActiveModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Brain/ChartTwoOverlayActiveModeEnum.h connectome-workbench-1.5.0/src/Brain/ChartTwoOverlayActiveModeEnum.h --- connectome-workbench-1.4.2/src/Brain/ChartTwoOverlayActiveModeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ChartTwoOverlayActiveModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __CHART_TWO_OVERLAY_ACTIVE_MODE_ENUM_H__ +#define __CHART_TWO_OVERLAY_ACTIVE_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoOverlayActiveModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Off */ + OFF, + /** On */ + ON, + /** Active */ + ACTIVE + }; + + + ~ChartTwoOverlayActiveModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoOverlayActiveModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoOverlayActiveModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_OVERLAY_ACTIVE_MODE_ENUM_DECLARE__ +std::vector ChartTwoOverlayActiveModeEnum::enumData; +bool ChartTwoOverlayActiveModeEnum::initializedFlag = false; +int32_t ChartTwoOverlayActiveModeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_OVERLAY_ACTIVE_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_OVERLAY_ACTIVE_MODE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/ChartTwoOverlay.cxx connectome-workbench-1.5.0/src/Brain/ChartTwoOverlay.cxx --- connectome-workbench-1.4.2/src/Brain/ChartTwoOverlay.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ChartTwoOverlay.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -28,16 +28,20 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" +#include "ChartTwoDataCartesian.h" #include "ChartableTwoFileDelegate.h" #include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineLayerChart.h" #include "ChartableTwoFileLineSeriesChart.h" #include "ChartableTwoFileMatrixChart.h" #include "ChartTwoLineSeriesHistory.h" #include "ChartTwoOverlaySet.h" #include "EventCaretMappableDataFilesGet.h" -#include "EventChartOverlayValidate.h" +#include "EventChartTwoOverlayValidate.h" #include "EventManager.h" +#include "GraphicsPrimitiveV3f.h" #include "Histogram.h" +#include "Matrix4x4.h" #include "PlainTextStringBuilder.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -82,12 +86,27 @@ m_colorBar->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); m_matrixTriangularViewingMode = ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL; + m_cartesianHorizontalAxisLocation = ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM; m_cartesianVerticalAxisLocation = ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT; - + m_matrixOpacity = 1.0; + m_selectedMapFile = NULL; m_selectedHistogramMapIndex = -1; m_allHistogramMapsSelectedFlag = false; + m_selectedLineLayerMapIndex = -1; + m_lineLayerColor.setCaretColorEnum(generateDefaultColor()); + m_lineLayerLineWidth = ChartTwoDataCartesian::getDefaultLineWidth(); + m_selectedLineChartPointIndex = 0; + m_lineChartActiveMode = ChartTwoOverlayActiveModeEnum::OFF; + m_lineChartNewMeanEnabled = false; + m_lineChartNewMeanValue = 0.0; + m_lineChartNewDeviationEnabled = false; + m_lineChartNormalizationAbsoluteValueEnabled = false; + m_lineChartNewDeviationValue = 1.0; + + m_selectedLineChartTextOffset = CardinalDirectionEnum::AUTO; + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); m_sceneAssistant->add("m_enabled", &m_enabled); m_sceneAssistant->add("m_mapYokingGroup", @@ -95,13 +114,34 @@ m_sceneAssistant->add("m_colorBar", "AnnotationColorBar", m_colorBar.get()); m_sceneAssistant->add("m_matrixTriangularViewingMode", &m_matrixTriangularViewingMode); + m_sceneAssistant->add("m_matrixOpacity", + &m_matrixOpacity); + m_sceneAssistant->add("m_cartesianHorizontalAxisLocation", + &m_cartesianHorizontalAxisLocation); m_sceneAssistant->add("m_cartesianVerticalAxisLocation", &m_cartesianVerticalAxisLocation); m_sceneAssistant->add("m_selectedHistogramMapIndex", &m_selectedHistogramMapIndex); m_sceneAssistant->add("m_allHistogramMapsSelectedFlag", &m_allHistogramMapsSelectedFlag); - + m_sceneAssistant->add("m_selectedLineLayerMapIndex", &m_selectedLineLayerMapIndex); + m_sceneAssistant->add("m_lineLayerLineWidth", &m_lineLayerLineWidth); + m_sceneAssistant->add("m_selectedLineChartPointIndex", &m_selectedLineChartPointIndex); + m_sceneAssistant->add("m_lineChartActiveMode", + &m_lineChartActiveMode); + m_sceneAssistant->add("m_selectedLineChartTextOffset", + &m_selectedLineChartTextOffset); + m_sceneAssistant->add("m_lineChartNewMeanEnabled", + &m_lineChartNewMeanEnabled); + m_sceneAssistant->add("m_lineChartNewMeanValue", + &m_lineChartNewMeanValue); + m_sceneAssistant->add("m_lineChartNewDeviationEnabled", + &m_lineChartNewDeviationEnabled); + m_sceneAssistant->add("m_lineChartNormalizationAbsoluteValueEnabled", + &m_lineChartNormalizationAbsoluteValueEnabled); + m_sceneAssistant->add("m_lineChartNewDeviationValue", + &m_lineChartNewDeviationValue); + EventManager::get()->addEventListener(this, - EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE); + EventTypeEnum::EVENT_CHART_TWO_OVERLAY_VALIDATE); } /** @@ -147,8 +187,8 @@ void ChartTwoOverlay::receiveEvent(Event* event) { - if (event->getEventType() == EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE) { - EventChartOverlayValidate* eov = dynamic_cast(event); + if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_OVERLAY_VALIDATE) { + EventChartTwoOverlayValidate* eov = dynamic_cast(event); CaretAssert(eov); eov->testValidChartOverlay(this); eov->setEventProcessed(); @@ -235,15 +275,15 @@ /** * Get the chart compound data type */ -ChartTwoCompoundDataType +const ChartTwoCompoundDataType* ChartTwoOverlay::getChartTwoCompoundDataType() const { - return m_chartCompoundDataType; + return &m_chartCompoundDataType; } /** * Set the compound chart type for charts displayed in this overlay. - * MUST match simplae data type for this chart unless invalid. + * MUST match simple data type for this chart unless invalid. * Note that overlay index zero, allows any chart type. * * @param chartCompoundDataType @@ -257,7 +297,6 @@ " for first overlay"); return; } -// do for overlay zero ?? if (chartCompoundDataType.getChartTwoDataType() != ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID) { CaretAssert(m_chartDataType == chartCompoundDataType.getChartTwoDataType()); @@ -358,11 +397,24 @@ *m_colorBar = *overlay->m_colorBar; m_matrixTriangularViewingMode = overlay->m_matrixTriangularViewingMode; + m_matrixOpacity = overlay->m_matrixOpacity; m_cartesianVerticalAxisLocation = overlay->m_cartesianVerticalAxisLocation; - + m_cartesianHorizontalAxisLocation = overlay->m_cartesianHorizontalAxisLocation; + m_selectedMapFile = overlay->m_selectedMapFile; m_selectedHistogramMapIndex = overlay->m_selectedHistogramMapIndex; m_allHistogramMapsSelectedFlag = overlay->m_allHistogramMapsSelectedFlag; + m_selectedLineLayerMapIndex = overlay->m_selectedLineLayerMapIndex; + m_lineLayerColor = overlay->m_lineLayerColor; + m_lineLayerLineWidth = overlay->m_lineLayerLineWidth; + m_selectedLineChartPointIndex = overlay->m_selectedLineChartPointIndex; + m_lineChartActiveMode = overlay->m_lineChartActiveMode; + m_selectedLineChartTextOffset = overlay->m_selectedLineChartTextOffset; + m_lineChartNewMeanEnabled = overlay->m_lineChartNewMeanEnabled; + m_lineChartNewMeanValue = overlay->m_lineChartNewMeanValue; + m_lineChartNewDeviationEnabled = overlay->m_lineChartNewDeviationEnabled; + m_lineChartNormalizationAbsoluteValueEnabled = overlay->m_lineChartNormalizationAbsoluteValueEnabled; + m_lineChartNewDeviationValue = overlay->m_lineChartNewDeviationValue; } /** @@ -396,6 +448,8 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: supportedFlag = true; break; @@ -456,6 +510,9 @@ supportedFlag = true; } break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + supportedFlag = true; + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { supportedFlag = true; @@ -550,7 +607,6 @@ const Histogram* histogram = histogramChart->getHistogramForChartDrawing(selectedIndex, (isAllMapsSupported() && isAllMapsSelected())); - //CaretAssert(histogram); if (histogram != NULL) { float histogramMinX = 0.0, histogramMaxX = 0.0, histogramMaxY = 0.0; histogram->getRangeAndMaxDisplayHeight(histogramMinX, histogramMaxX, histogramMaxY); @@ -565,6 +621,12 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + const ChartTwoDataCartesian* cartesianLine = getLineLayerChartDisplayedCartesianData(); + validFlag = cartesianLine->getBounds(boundingBoxOut); + } + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: { const ChartableTwoFileLineSeriesChart* lineSeriesChart = chartDelegate->getLineSeriesCharting(); @@ -573,6 +635,12 @@ } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + const ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); + validFlag = matrixChart->getMatrixChartingGraphicsPrimitive(getMatrixTriangularViewingMode(), + CiftiMappableDataFile::MatrixGridMode::FILLED_TEXTURE, + getMatrixOpacity())->getVertexBounds(boundingBoxOut); + } break; } @@ -580,6 +648,200 @@ } /** + * @return The displayed cartesian data for a line layer chart (ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER). + * This data may be normalized (if layer's new mean,deviation is enabled), otherwise the data as from the map file + * NULL may be returned. NULL always returned for types other than line layer. + */ +const ChartTwoDataCartesian* +ChartTwoOverlay::getLineLayerChartDisplayedCartesianData() const +{ + ChartTwoOverlay* nonConstThis = const_cast(this); + return nonConstThis->getLineLayerChartDisplayedCartesianData(); +} + +/** + * @return The displayed cartesian data for a line layer chart (ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER). + * This data may be normalized (if layer's new mean,deviation is enabled), otherwise the data as from the map file + * NULL may be returned. NULL always returned for types other than line layer. + */ +ChartTwoDataCartesian* +ChartTwoOverlay::getLineLayerChartDisplayedCartesianData() +{ + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType selectedIndexType = SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile == NULL) { + return NULL; + } + + ChartTwoDataCartesian* dataOut(NULL); + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + + + ChartableTwoFileDelegate* chartDelegate = mapFile->getChartingDelegate(); + ChartableTwoFileLineLayerChart* layerChart = chartDelegate->getLineLayerCharting(); + ChartTwoDataCartesian* cartesianLineData = layerChart->getChartMapLineForChartTwoOverlay(selectedIndex); + CaretAssert(cartesianLineData); + + /* + * Cache the data (not done yet) + */ + if ((m_previousLineChartSettings.m_mapFile != mapFile) + || (m_previousLineChartSettings.m_mapIndex != selectedIndex) + || (m_previousLineChartSettings.m_numberOfMaps != mapFile->getNumberOfMaps()) + || (m_previousLineChartSettings.m_cartesianLineData != cartesianLineData) + || (m_previousLineChartSettings.m_numberOfCartesianVertices != cartesianLineData->getGraphicsPrimitive()->getNumberOfVertices()) + || (m_previousLineChartSettings.m_newMean != m_lineChartNewMeanValue) + || (m_previousLineChartSettings.m_newDeviation != m_lineChartNewDeviationValue) + || (m_previousLineChartSettings.m_newMeanEnabled != m_lineChartNewMeanEnabled) + || (m_previousLineChartSettings.m_newDeviationEnabled != m_lineChartNewDeviationEnabled) + || (m_previousLineChartSettings.m_lineChartNormalizationAbsoluteValueEnabled != m_lineChartNormalizationAbsoluteValueEnabled)) { + m_lineChartNormalizedCartesianData.reset(); + } + + m_previousLineChartSettings.m_mapFile = mapFile; + m_previousLineChartSettings.m_mapIndex = selectedIndex; + m_previousLineChartSettings.m_numberOfMaps = mapFile->getNumberOfMaps(); + m_previousLineChartSettings.m_cartesianLineData = cartesianLineData; + m_previousLineChartSettings.m_numberOfCartesianVertices = cartesianLineData->getGraphicsPrimitive()->getNumberOfVertices(); + m_previousLineChartSettings.m_newMean = m_lineChartNewMeanValue; + m_previousLineChartSettings.m_newDeviation = m_lineChartNewDeviationValue; + m_previousLineChartSettings.m_newMeanEnabled = m_lineChartNewMeanEnabled; + m_previousLineChartSettings.m_newDeviationEnabled = m_lineChartNewDeviationEnabled; + m_previousLineChartSettings.m_lineChartNormalizationAbsoluteValueEnabled = m_lineChartNormalizationAbsoluteValueEnabled; + + if ( ! m_lineChartNormalizedCartesianData) { + /* + * Need to clone the line + */ + m_lineChartNormalizedCartesianData.reset(cartesianLineData->clone()); + } + else { + /* + * Just need to replace the Y-components with original Y-components + */ + std::vector yComponents; + cartesianLineData->getGraphicsPrimitive()->getFloatYComponents(yComponents); + m_lineChartNormalizedCartesianData->getGraphicsPrimitive()->setFloatYComponents(yComponents); + } + + + if (m_lineChartNewMeanEnabled + || m_lineChartNewDeviationEnabled + || m_lineChartNormalizationAbsoluteValueEnabled) { + /* + * Apply new mean and deviation to Y-components + */ + bool haveNanInfFlag(false); + CaretAssert(m_lineChartNormalizedCartesianData->getGraphicsPrimitive()); + m_lineChartNormalizedCartesianData->getGraphicsPrimitive()->applyNewMeanAndDeviationToYComponents(m_lineChartNewMeanEnabled, + m_lineChartNewMeanValue, + m_lineChartNewDeviationEnabled, + m_lineChartNewDeviationValue, + m_lineChartNormalizationAbsoluteValueEnabled, + haveNanInfFlag); + + if (haveNanInfFlag) { + /* + * Issue warning but only once per file/map + */ + auto fileMap(std::make_pair(mapFile, selectedIndex)); + if (m_normalizedMapFilesWithNanInf.find(fileMap) == m_normalizedMapFilesWithNanInf.end()) { + m_normalizedMapFilesWithNanInf.insert(fileMap); + CaretLogWarning(mapFile->getFileName() + + " Contains NaNs and/or Infs in map index=" + + QString::number(selectedIndex + 1) + + " " + mapFile->getMapName(selectedIndex)); + } + } + } + + dataOut = m_lineChartNormalizedCartesianData.get(); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + const QString msg("This method should only be called when chart " + "type is CHART_DATA_TYPE_LINE_LAYER. Was called with: " + + ChartTwoDataTypeEnum::toName(m_chartDataType)); + CaretAssertMessage(0, msg); + CaretLogWarning(msg); + } + break; + } + + return dataOut; +} + +/** + * @return The cartesian data from the map file in the layer for a line layer chart (ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER). + * NULL may be returned. NULL always returned for types other than line layer. + */ +const ChartTwoDataCartesian* +ChartTwoOverlay::getLineLayerChartMapFileCartesianData() const +{ + ChartTwoOverlay* nonConstThis = const_cast(this); + return nonConstThis->getLineLayerChartMapFileCartesianData(); +} + +/** + * @return The cartesian data from the map file in the layer for a line layer chart (ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER). + * NULL may be returned. NULL always returned for types other than line layer. + */ +ChartTwoDataCartesian* +ChartTwoOverlay::getLineLayerChartMapFileCartesianData() +{ + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType selectedIndexType = SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile == NULL) { + return NULL; + } + + ChartTwoDataCartesian* dataOut(NULL); + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + ChartableTwoFileDelegate* chartDelegate = mapFile->getChartingDelegate(); + ChartableTwoFileLineLayerChart* layerChart = chartDelegate->getLineLayerCharting(); + dataOut = layerChart->getChartMapLineForChartTwoOverlay(selectedIndex); + CaretAssert(dataOut); + CaretAssert(dataOut->getGraphicsPrimitive()); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + const QString msg("This method should only be called when chart " + "type is CHART_DATA_TYPE_LINE_LAYER. Was called with: " + + ChartTwoDataTypeEnum::toName(m_chartDataType)); + CaretAssertMessage(0, msg); + CaretLogWarning(msg); + } + break; + } + + return dataOut; +} + +/** * @return The selected map file (NULL if not map file available) */ CaretMappableDataFile* @@ -720,11 +982,11 @@ if (chartingFile->isChartingTwoSupported()) { bool useIt = false; - std::vector chartCompoundDataTypes; + std::vector chartCompoundDataTypes; chartingFile->getSupportedChartTwoCompoundDataTypes(chartCompoundDataTypes); for (auto& compoundType : chartCompoundDataTypes) { - if (m_chartDataType == compoundType.getChartTwoDataType()) { + if (m_chartDataType == compoundType->getChartTwoDataType()) { if (m_overlayIndex == 0) { /* * The first overlay displays ALL files that match the @@ -733,7 +995,7 @@ useIt = true; } else { - if (m_chartCompoundDataType == compoundType) { + if (m_chartCompoundDataType == *compoundType) { /* * If not the first overlay, the enumerated type * and dimensions must also match @@ -802,6 +1064,9 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: m_selectedHistogramMapIndex = 0; break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + m_selectedLineLayerMapIndex = 0; + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -838,6 +1103,25 @@ selectedIndexOut = m_selectedHistogramMapIndex; } break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + ChartableTwoFileLineLayerChart* lineLayerChart = + m_selectedMapFile->getChartingDelegate()->getLineLayerCharting(); + CaretAssert(lineLayerChart); + numMaps = lineLayerChart->getNumberOfChartMaps(); + if (selectedFileMapNamesOut != NULL) { + lineLayerChart->getChartMapNames(*selectedFileMapNamesOut); + } + if (m_selectedLineLayerMapIndex >= numMaps) { + m_selectedLineLayerMapIndex = numMaps - 1; + } + if (m_selectedLineLayerMapIndex < 0) { + m_selectedLineLayerMapIndex = 0; + } + selectedIndexTypeOut = SelectedIndexType::MAP; + selectedIndexOut = m_selectedLineLayerMapIndex; + } + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: { /* @@ -862,50 +1146,6 @@ { ChartableTwoFileDelegate* chartDelegate = m_selectedMapFile->getChartingDelegate(); matrixChart = chartDelegate->getMatrixCharting(); -// const ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); -// CaretAssert(matrixChart); -// int32_t numRows = 0; -// int32_t numCols = 0; -// matrixChart->getMatrixDimensions(numRows, numCols); -// -// ChartTwoMatrixLoadingDimensionEnum::Enum rowColumnDimension; -// std::vector columnIndices; -// std::vector rowIndices; -// matrixChart->getSelectedRowColumnIndices(m_parentChartTwoOverlaySet->m_tabIndex, -// rowColumnDimension, -// rowIndices, -// columnIndices); -// -// switch (rowColumnDimension) { -// case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: -// numMaps = numCols; -// if ( ! columnIndices.empty()) { -// selectedIndexTypeOut = SelectedIndexType::COLUMN; -// selectedIndexOut = columnIndices[0]; -// } -// if (matrixChart->hasColumnSelection()) { -// if (selectedFileMapNamesOut != NULL) { -// for (int32_t i = 0; i < numMaps; i++) { -// selectedFileMapNamesOut->push_back(matrixChart->getColumnName(i)); -// } -// } -// } -// break; -// case ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: -// numMaps = numRows; -// if ( ! rowIndices.empty()) { -// selectedIndexTypeOut = SelectedIndexType::ROW; -// selectedIndexOut = rowIndices[0]; -// } -// if (matrixChart->hasRowSelection()) { -// if (selectedFileMapNamesOut != NULL) { -// for (int32_t i = 0; i < numMaps; i++) { -// selectedFileMapNamesOut->push_back(matrixChart->getRowName(i)); -// } -// } -// } -// break; -// } } break; } @@ -1006,6 +1246,11 @@ m_selectedHistogramMapIndex = selectedMapIndex; } break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + if (selectedMapIndex >= 0) { + m_selectedLineLayerMapIndex = selectedMapIndex; + } + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: { /* @@ -1181,6 +1426,8 @@ } } break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -1249,6 +1496,8 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -1263,26 +1512,33 @@ } /** - * @return Location of vertical cartesian axis + * @return The matrix opacity */ -ChartAxisLocationEnum::Enum -ChartTwoOverlay::getCartesianVerticalAxisLocation() const +float +ChartTwoOverlay::getMatrixOpacity() const { - validateCartesianVerticalAxisLocation(); - return m_cartesianVerticalAxisLocation; + return m_matrixOpacity; } /** - * Set Location of vertical cartesian axis - * - * @param cartesianVerticalAxisLocation - * New value for Location of vertical cartesian axis + * Set the matrix opacity + * @param opacity + * New opacity value */ void -ChartTwoOverlay::setCartesianVerticalAxisLocation(const ChartAxisLocationEnum::Enum cartesianVerticalAxisLocation) +ChartTwoOverlay::setMatrixOpacity(const float opacity) +{ + m_matrixOpacity = opacity; +} + +/** + * @return Location of vertical cartesian axis from OLD scenes + */ +ChartAxisLocationEnum::Enum +ChartTwoOverlay::getSceneCartesianVerticalAxisLocation() const { - m_cartesianVerticalAxisLocation = cartesianVerticalAxisLocation; validateCartesianVerticalAxisLocation(); + return m_cartesianVerticalAxisLocation; } /** @@ -1319,6 +1575,9 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: return true; break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + return true; + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: return true; break; @@ -1329,6 +1588,432 @@ return false; } +/** + * @return Location of horizontal cartesian axis + */ +ChartAxisLocationEnum::Enum +ChartTwoOverlay::getCartesianHorizontalAxisLocation() const +{ + validateCartesianHorizontalAxisLocation(); + return m_cartesianHorizontalAxisLocation; +} + +/** + * Set Location of horizontal cartesian axis + * + * @param cartesianHorizontalAxisLocation + * New value for Location of horizontal cartesian axis + */ +void +ChartTwoOverlay::setCartesianHorizontalAxisLocation(const ChartAxisLocationEnum::Enum cartesianHorizontalAxisLocation) +{ + m_cartesianHorizontalAxisLocation = cartesianHorizontalAxisLocation; + validateCartesianHorizontalAxisLocation(); +} + +/** + * Validate cartesian horizontal axis to valid locations (bottom and top) + */ +void +ChartTwoOverlay::validateCartesianHorizontalAxisLocation() const +{ + + switch (m_cartesianHorizontalAxisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + m_cartesianHorizontalAxisLocation = ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + m_cartesianHorizontalAxisLocation = ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + break; + } +} + +/** + * @return Is the cartesian horizontal axis location supported? + */ +bool +ChartTwoOverlay::isCartesianHorizontalAxisLocationSupported() const +{ + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + return true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + return true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + return true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + return false; +} + +/** + * @return The line layer color + */ +CaretColor +ChartTwoOverlay::getLineLayerColor() const +{ + return m_lineLayerColor; +} + +/** + * Set the line layer color + * @param color + * New color for line layer + */ +void +ChartTwoOverlay::setLineLayerColor(const CaretColor& color) +{ + m_lineLayerColor = color; +} + +/** + * The line layer line width + */ +float +ChartTwoOverlay::getLineLayerLineWidth() const +{ + return m_lineLayerLineWidth; +} + +/** + * Set the line width for a line layer + * @param lineWidth + * New value for line width + */ +void +ChartTwoOverlay::setLineLayerLineWidth(const float lineWidth) +{ + m_lineLayerLineWidth = lineWidth; +} + +/** + * Generate the default color. + */ +CaretColorEnum::Enum +ChartTwoOverlay::generateDefaultColor() +{ + /* + * No black or white since they are used for backgrounds + */ + std::vector colors; + CaretColorEnum::getColorEnumsNoBlackOrWhite(colors); + CaretAssert( ! colors.empty()); + CaretColorEnum::Enum color = colors[0]; + + const int32_t numColors = static_cast(colors.size()); + CaretAssert(numColors > 0); + if (s_defaultColorIndexGenerator < 0) { + s_defaultColorIndexGenerator = 0; + } + else if (s_defaultColorIndexGenerator >= numColors) { + s_defaultColorIndexGenerator = 0; + } + + CaretAssertVectorIndex(colors, s_defaultColorIndexGenerator); + color = colors[s_defaultColorIndexGenerator]; + + /* move to next color */ + ++s_defaultColorIndexGenerator; + + return color; +} + +/** + * @return Number of points in line layer chart + */ +int32_t +ChartTwoOverlay::getSelectedLineChartNumberOfPoints() const +{ + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType selectedIndexType = SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile == NULL) { + return false; + } + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + const ChartTwoDataCartesian* cd = getLineLayerChartDisplayedCartesianData(); + CaretAssert(cd); + const GraphicsPrimitive* gp(cd->getGraphicsPrimitive()); + CaretAssert(gp); + return gp->getNumberOfVertices(); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + return 0; +} + +/** + * @return Index of selected point in line chart + */ +int32_t +ChartTwoOverlay::getSelectedLineChartPointIndex() const +{ + validateSelectedLineChartPointIndex(); + + return m_selectedLineChartPointIndex; +} + +/** + * Validate the selected line chart point index (in value range) + */ +void +ChartTwoOverlay::validateSelectedLineChartPointIndex() const +{ + if (m_selectedLineChartPointIndex >= getSelectedLineChartNumberOfPoints()) { + m_selectedLineChartPointIndex = getSelectedLineChartNumberOfPoints() - 1; + } + if (m_selectedLineChartPointIndex < 0) { + m_selectedLineChartPointIndex = 0; + } +} + +/** + * Set the index of the selected point in the line chart + * @param pointIndex + * Index of selected point + */ +void +ChartTwoOverlay::setSelectedLineChartPointIndex(const int32_t pointIndex) +{ + m_selectedLineChartPointIndex = pointIndex; +} + +/** + * Increment the selected line chart point index by the given amout + * @param incrementValue + * Amount to increment, may be negative to decrement + */ +void +ChartTwoOverlay::incrementSelectedLineChartPointIndex(const int32_t incrementValue) +{ + m_selectedLineChartPointIndex += incrementValue; + validateSelectedLineChartPointIndex(); +} + +/** + * @return Active mode for this overlay + */ +ChartTwoOverlayActiveModeEnum::Enum +ChartTwoOverlay::getLineChartActiveMode() const +{ + return m_lineChartActiveMode;; +} + +/** + * Set the active mode for this overlay + * @param lineChartActiveMode + * New active mode for this overlay + */ +void +ChartTwoOverlay::setLineChartActiveMode(const ChartTwoOverlayActiveModeEnum::Enum lineChartActiveMode) +{ + m_lineChartActiveMode = lineChartActiveMode; +} + +/** + * Get the XYZ-coordinate of the selected line chart point + * @param xyzOut + * Output with selected point coordinate + * @return True if the selected point is displayed and the index is within + * the range of the lines points and thus the XYZ is valid for display + */ +bool +ChartTwoOverlay::getSelectedLineChartPointXYZ(std::array& xyzOut) const +{ + bool validFlag(false); + switch (m_lineChartActiveMode) { + case ChartTwoOverlayActiveModeEnum::ACTIVE: + validFlag = true; + break; + case ChartTwoOverlayActiveModeEnum::OFF: + break; + case ChartTwoOverlayActiveModeEnum::ON: + validFlag = true; + break; + } + + if (validFlag) { + CaretMappableDataFile* mapFile = NULL; + SelectedIndexType selectedIndexType = SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + + if (mapFile == NULL) { + return false; + } + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + const ChartTwoDataCartesian* cd = getLineLayerChartDisplayedCartesianData(); + CaretAssert(cd); + cd->getPointXYZ(m_selectedLineChartPointIndex, + xyzOut.data()); + return true; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + } + + return false; +} + +/** + * @return The text offset for selected line chart point + */ +CardinalDirectionEnum::Enum +ChartTwoOverlay::getSelectedLineChartTextOffset() const +{ + return m_selectedLineChartTextOffset; +} + +/** + * Set text offset for selected line chart point + * @param offset + * New offset + */ +void +ChartTwoOverlay::setSelectedLineChartTextOffset(const CardinalDirectionEnum::Enum offset) +{ + m_selectedLineChartTextOffset = offset; +} + +/** + * @return Line chart new mean enabled + */ +bool +ChartTwoOverlay::isLineChartNewMeanEnabled() const +{ + return m_lineChartNewMeanEnabled; +} + +/** + * Set line chart new mean enabled + * @param enabled + * New enabled status + */ +void +ChartTwoOverlay::setLineChartNewMeanEnabled(const bool enabled) +{ + m_lineChartNewMeanEnabled = enabled; +} + +/** + * @return Line chart new mean value + */ +float +ChartTwoOverlay::getLineChartNewMeanValue() const +{ + return m_lineChartNewMeanValue; +} + +/** + * Set the line chart new mean value + * @param value + * New demean value + */ +void +ChartTwoOverlay::setLineChartNewMeanValue(const float value) +{ + m_lineChartNewMeanValue = value; +} + +/** + * @return Line chart new deviation enabled + */ +bool +ChartTwoOverlay::isLineChartNewDeviationEnabled() const +{ + return m_lineChartNewDeviationEnabled; +} + +/** + * Set line chart new deviation enabled + * @param enabled + * New enabled status + */ +void +ChartTwoOverlay::setLineChartNewDeviationEnabled(const bool enabled) +{ + m_lineChartNewDeviationEnabled = enabled; +} + +/** + * @return Line chart normalization absolute value enabled + */ +bool +ChartTwoOverlay::isLineChartNormalizationAbsoluteValueEnabled() const +{ + return m_lineChartNormalizationAbsoluteValueEnabled; +} + +/** + * Set line chart normalization absolute value enabled + * @param enable + * New status + */ +void +ChartTwoOverlay::setLineChartNormalizationAbsoluteValueEnabled(const bool enabled) +{ + m_lineChartNormalizationAbsoluteValueEnabled = enabled; +} + +/** + * @return Line chart new deviation value + */ +float +ChartTwoOverlay::getLineChartNewDeviationValue() const +{ + return m_lineChartNewDeviationValue; +} + +/** + * Set the line chart new deviation value + * @param value + * New deviation value + */ +void +ChartTwoOverlay::setLineChartNewDeviationValue(const float value) +{ + m_lineChartNewDeviationValue = value; +} /** * Save information specific to this type of model to the scene. @@ -1350,10 +2035,11 @@ 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); - + sceneClass->addString("m_lineLayerColor", + m_lineLayerColor.encodeInXML()); + std::vector mapFiles; CaretMappableDataFile* selectedMapFile = NULL; - //AString selectedMapUniqueID; SelectedIndexType selectedIndexType = SelectedIndexType::INVALID; int32_t selectedMapIndex; getSelectionData(mapFiles, @@ -1413,9 +2099,40 @@ if (sceneClass == NULL) { return; } - m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); + + /* + * Was briefly used for point display before it became OFF/ON/ACTIVE + */ + const SceneObject* pointDisplayObject = sceneClass->getObjectWithName("m_selectedLineChartPointDisplayed"); + if (pointDisplayObject != NULL) { + if (sceneClass->getBooleanValue("m_selectedLineChartPointDisplayed")) { + m_lineChartActiveMode = ChartTwoOverlayActiveModeEnum::ON; + } + else { + m_lineChartActiveMode = ChartTwoOverlayActiveModeEnum::OFF; + } + } + + /* + * m_caretColor (instance of CaretColor) replaced + * m_color (instance CaretColorEnum) + */ + const QString caretColorText = sceneClass->getStringValue("m_lineLayerColor", ""); + if ( ! caretColorText.isEmpty()) { + AString errorMessage; + if ( ! m_lineLayerColor.decodeFromXML(caretColorText, + errorMessage)) { + sceneAttributes->addToErrorMessage(errorMessage); + } + } + else { + const CaretColorEnum::Enum color = sceneClass->getEnumeratedTypeValue("m_color", + CaretColorEnum::BLUE); + m_lineLayerColor.setCaretColorEnum(color); + } + const MapYokingGroupEnum::Enum mapYokingGroupFromScene = m_mapYokingGroup; /* @@ -1493,20 +2210,52 @@ CaretMappableDataFile* mapFile = *iter; matchedMapFile = mapFile; - if (foundMapNameIndex < 0) { - if ( ! selectedMapName.isEmpty()) { - const int mapNameIndex = mapFile->getMapIndexFromName(selectedMapName); - if (mapNameIndex >= 0) { - foundMapNameFile = mapFile; - foundMapNameIndex = mapNameIndex; + bool useMapsFlag(false); + bool useLineLayerFlag(false); + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + useMapsFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + useLineLayerFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + useMapsFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + useMapsFlag = true; + break; + } + + if (useMapsFlag) { + if (foundMapNameIndex < 0) { + if ( ! selectedMapName.isEmpty()) { + const int mapNameIndex = mapFile->getMapIndexFromName(selectedMapName); + if (mapNameIndex >= 0) { + foundMapNameFile = mapFile; + foundMapNameIndex = mapNameIndex; + } } + } + if (foundMapIndex < 0) { + if (selectedMapIndex >= 0) { + if (selectedMapIndex < mapFile->getNumberOfMaps()) { + foundMapIndexFile = mapFile; + foundMapIndex = selectedMapIndex; + } + } + } } - - if (foundMapIndex < 0) { - if (selectedMapIndex >= 0) { - if (selectedMapIndex < mapFile->getNumberOfMaps()) { + else if (useLineLayerFlag) { + /* + * Line layers are not file maps + */ + if (foundMapIndex < 0) { + if (selectedMapIndex >= 0) { foundMapIndexFile = mapFile; foundMapIndex = selectedMapIndex; } @@ -1576,6 +2325,9 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + defaultMapIndex = 0; + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: defaultMapIndex = 0; break; diff -Nru connectome-workbench-1.4.2/src/Brain/ChartTwoOverlay.h connectome-workbench-1.5.0/src/Brain/ChartTwoOverlay.h --- connectome-workbench-1.4.2/src/Brain/ChartTwoOverlay.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ChartTwoOverlay.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,12 +21,17 @@ */ /*LICENSE_END*/ +#include #include +#include +#include "CardinalDirectionEnum.h" +#include "CaretColor.h" #include "CaretObject.h" #include "ChartAxisLocationEnum.h" #include "ChartTwoCompoundDataType.h" #include "ChartTwoMatrixTriangularViewingModeEnum.h" +#include "ChartTwoOverlayActiveModeEnum.h" #include "EventListenerInterface.h" #include "MapYokingGroupEnum.h" #include "SceneableInterface.h" @@ -36,7 +41,9 @@ class AnnotationColorBar; class BoundingBox; class CaretMappableDataFile; + class ChartTwoDataCartesian; class ChartTwoOverlaySet; + class Matrix4x4; class PlainTextStringBuilder; class SceneClassAssistant; @@ -61,7 +68,7 @@ ChartTwoDataTypeEnum::Enum getChartTwoDataType() const; - ChartTwoCompoundDataType getChartTwoCompoundDataType() const; + const ChartTwoCompoundDataType* getChartTwoCompoundDataType() const; void setChartTwoCompoundDataType(const ChartTwoCompoundDataType& chartCompoundDataType); @@ -101,12 +108,20 @@ bool isMatrixTriangularViewingModeSupported() const; - ChartAxisLocationEnum::Enum getCartesianVerticalAxisLocation() const; + float getMatrixOpacity() const; - void setCartesianVerticalAxisLocation(const ChartAxisLocationEnum::Enum cartesianVerticalAxisLocation); + void setMatrixOpacity(const float opacity); + + ChartAxisLocationEnum::Enum getSceneCartesianVerticalAxisLocation() const; bool isCartesianVerticalAxisLocationSupported() const; + ChartAxisLocationEnum::Enum getCartesianHorizontalAxisLocation() const; + + void setCartesianHorizontalAxisLocation(const ChartAxisLocationEnum::Enum cartesianHorizontalAxisLocation); + + bool isCartesianHorizontalAxisLocationSupported() const; + CaretMappableDataFile* getSelectedMapFile() const; void getSelectionData(std::vector& mapFilesOut, @@ -135,7 +150,61 @@ bool getBounds(BoundingBox& boundingBoxOut) const; - virtual void receiveEvent(Event* event); + ChartTwoDataCartesian* getLineLayerChartDisplayedCartesianData(); + + const ChartTwoDataCartesian* getLineLayerChartDisplayedCartesianData() const; + + ChartTwoDataCartesian* getLineLayerChartMapFileCartesianData(); + + const ChartTwoDataCartesian* getLineLayerChartMapFileCartesianData() const; + + CaretColor getLineLayerColor() const; + + void setLineLayerColor(const CaretColor& color); + + float getLineLayerLineWidth() const; + + void setLineLayerLineWidth(const float lineWidth); + + int32_t getSelectedLineChartNumberOfPoints() const; + + int32_t getSelectedLineChartPointIndex() const; + + void setSelectedLineChartPointIndex(const int32_t pointIndex); + + void incrementSelectedLineChartPointIndex(const int32_t incrementValue); + + ChartTwoOverlayActiveModeEnum::Enum getLineChartActiveMode() const; + + void setLineChartActiveMode(const ChartTwoOverlayActiveModeEnum::Enum lineChartActiveMode); + + bool getSelectedLineChartPointXYZ(std::array& xyzOut) const; + + CardinalDirectionEnum::Enum getSelectedLineChartTextOffset() const; + + void setSelectedLineChartTextOffset(const CardinalDirectionEnum::Enum offset); + + bool isLineChartNewMeanEnabled() const; + + void setLineChartNewMeanEnabled(const bool enabled); + + float getLineChartNewMeanValue() const; + + void setLineChartNewMeanValue(const float value); + + bool isLineChartNewDeviationEnabled() const; + + void setLineChartNewDeviationEnabled(const bool enabled); + + bool isLineChartNormalizationAbsoluteValueEnabled() const; + + void setLineChartNormalizationAbsoluteValueEnabled(const bool enabled); + + float getLineChartNewDeviationValue() const; + + void setLineChartNewDeviationValue(const float value); + + virtual void receiveEvent(Event* event); // ADD_NEW_METHODS_HERE @@ -172,6 +241,12 @@ void validateCartesianVerticalAxisLocation() const; + void validateCartesianHorizontalAxisLocation() const; + + void validateSelectedLineChartPointIndex() const; + + static CaretColorEnum::Enum generateDefaultColor(); + /** Parent chart overlay set (only used by first overlay in the set */ ChartTwoOverlaySet* m_parentChartTwoOverlaySet; @@ -185,7 +260,7 @@ std::unique_ptr m_sceneAssistant; - /** Current 'compound chart type' of charts allowed in this overlay */ + /** Current 'compound chart type' of charts allowed in this overlay DO NOT COPY */ mutable ChartTwoCompoundDataType m_chartCompoundDataType; /** Name of overlay (DO NOT COPY)*/ @@ -206,23 +281,72 @@ /** histogram selected map index */ mutable int32_t m_selectedHistogramMapIndex = -1; + mutable int32_t m_selectedLineLayerMapIndex = -1; + bool m_allHistogramMapsSelectedFlag = false; mutable ChartTwoMatrixTriangularViewingModeEnum::Enum m_matrixTriangularViewingMode; + float m_matrixOpacity = 1.0; + /** Location of vertical cartesian axis*/ mutable ChartAxisLocationEnum::Enum m_cartesianVerticalAxisLocation; + /** Location of horizontal cartesian axis*/ + mutable ChartAxisLocationEnum::Enum m_cartesianHorizontalAxisLocation; + + CaretColor m_lineLayerColor; + + float m_lineLayerLineWidth; + + mutable int32_t m_selectedLineChartPointIndex = 0; + + ChartTwoOverlayActiveModeEnum::Enum m_lineChartActiveMode = ChartTwoOverlayActiveModeEnum::OFF; + + CardinalDirectionEnum::Enum m_selectedLineChartTextOffset = CardinalDirectionEnum::AUTO; + + bool m_lineChartNewMeanEnabled = false; + + float m_lineChartNewMeanValue = 0.0; + + bool m_lineChartNewDeviationEnabled = false; + + bool m_lineChartNormalizationAbsoluteValueEnabled = false; + + float m_lineChartNewDeviationValue = 1.0; + + mutable std::unique_ptr m_lineChartNormalizedCartesianData; + + struct LineChartNormalizedSettings { + CaretMappableDataFile* m_mapFile = NULL; + int32_t m_mapIndex = -1; + int32_t m_numberOfMaps = -1; + ChartTwoDataCartesian* m_cartesianLineData = NULL; + int32_t m_numberOfCartesianVertices = -1; + float m_newMean = std::numeric_limits::max(); + float m_newDeviation = 1.0; + bool m_newMeanEnabled = false; + bool m_newDeviationEnabled = false; + bool m_lineChartNormalizationAbsoluteValueEnabled = false; + }; + + LineChartNormalizedSettings m_previousLineChartSettings; + /** A weak pointer to 'self' so that can be stored to safely test instance is valid and can be accessed */ std::weak_ptr m_weakPointerToSelf; + /** prevents excessing warnings when NaNs and/or Inf found in normalization */ + std::set> m_normalizedMapFilesWithNanInf; + + static int32_t s_defaultColorIndexGenerator; + // ADD_NEW_MEMBERS_HERE friend class ChartTwoOverlaySet; }; #ifdef __CHART_TWO_OVERLAY_DECLARE__ - // + int32_t ChartTwoOverlay::s_defaultColorIndexGenerator = 0; #endif // __CHART_TWO_OVERLAY_DECLARE__ } // namespace diff -Nru connectome-workbench-1.4.2/src/Brain/ChartTwoOverlaySet.cxx connectome-workbench-1.5.0/src/Brain/ChartTwoOverlaySet.cxx --- connectome-workbench-1.4.2/src/Brain/ChartTwoOverlaySet.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ChartTwoOverlaySet.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -28,16 +28,17 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretMappableDataFile.h" +#include "ChartTwoCartesianOrientedAxes.h" #include "ChartTwoCartesianAxis.h" #include "ChartTwoOverlay.h" #include "ChartTwoTitle.h" #include "ChartableTwoFileDelegate.h" #include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineLayerChart.h" #include "ChartableTwoFileLineSeriesChart.h" -#include "EventAnnotationChartLabelGet.h" +#include "ChartableTwoFileMatrixChart.h" #include "EventBrowserTabGet.h" -#include "EventChartTwoAttributesChanged.h" -#include "EventChartTwoAxisGetDataRange.h" +#include "EventChartTwoCartesianAxisDisplayGroup.h" #include "EventManager.h" #include "EventMapYokingSelectMap.h" #include "EventMapYokingValidation.h" @@ -72,67 +73,92 @@ const AString& name, const int32_t tabIndex) : CaretObject(), +ChartTwoOverlaySetInterface(), m_chartDataType(chartDataType), m_name(name), m_tabIndex(tabIndex) { m_numberOfDisplayedOverlays = BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS; - m_chartAxisLeft = std::unique_ptr(new ChartTwoCartesianAxis(this, ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT)); - m_chartAxisRight = std::unique_ptr(new ChartTwoCartesianAxis(this, ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT)); - m_chartAxisBottom = std::unique_ptr(new ChartTwoCartesianAxis(this, ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM)); - - m_chartAxisLeft->setEnabledByChart(false); - m_chartAxisRight->setEnabledByChart(false); - m_chartAxisBottom->setEnabledByChart(false); + m_horizontalAxes.reset(new ChartTwoCartesianOrientedAxes(this, + ChartTwoAxisOrientationTypeEnum::HORIZONTAL)); + m_verticalAxes.reset(new ChartTwoCartesianOrientedAxes(this, + ChartTwoAxisOrientationTypeEnum::VERTICAL)); + + ChartTwoCartesianAxis* chartAxisLeft = m_verticalAxes->getLeftOrBottomAxis(); + ChartTwoCartesianAxis* chartAxisRight = m_verticalAxes->getRightOrTopAxis(); + ChartTwoCartesianAxis* chartAxisBottom = m_horizontalAxes->getLeftOrBottomAxis(); + ChartTwoCartesianAxis* chartAxisTop = m_horizontalAxes->getRightOrTopAxis(); + + chartAxisLeft->setDisplayedByUser(true); + chartAxisRight->setDisplayedByUser(false); + chartAxisBottom->setDisplayedByUser(true); + chartAxisTop->setDisplayedByUser(false); + switch (m_chartDataType) { case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: { - m_chartAxisLeft->setEnabledByChart(true); - m_chartAxisLeft->setUnits(CaretUnitsTypeEnum::NONE); - - m_chartAxisRight->setEnabledByChart(false); - m_chartAxisRight->setUnits(CaretUnitsTypeEnum::NONE); + m_horizontalAxes->initializeScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::DATA); + m_verticalAxes->initializeScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::AUTO); + chartAxisLeft->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisRight->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisBottom->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisTop->setUnits(CaretUnitsTypeEnum::NONE); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + chartAxisLeft->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisRight->setUnits(CaretUnitsTypeEnum::NONE); - m_chartAxisBottom->setEnabledByChart(true); - m_chartAxisBottom->setUnits(CaretUnitsTypeEnum::NONE); + /* + * X-axis for line series shows full extent of data + */ + m_horizontalAxes->initializeScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::DATA); + m_verticalAxes->initializeScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::AUTO); + chartAxisBottom->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisTop->setUnits(CaretUnitsTypeEnum::NONE); } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: { - m_chartAxisLeft->setEnabledByChart(true); - m_chartAxisLeft->setUnits(CaretUnitsTypeEnum::NONE); - - m_chartAxisRight->setEnabledByChart(false); - m_chartAxisRight->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisLeft->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisRight->setUnits(CaretUnitsTypeEnum::NONE); /* * X-axis for line series shows full extent of data */ - m_chartAxisBottom->setScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::DATA); - m_chartAxisBottom->setEnabledByChart(true); - m_chartAxisBottom->setUnits(CaretUnitsTypeEnum::NONE); + m_horizontalAxes->initializeScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::DATA); + m_verticalAxes->initializeScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::AUTO); + chartAxisBottom->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisTop->setUnits(CaretUnitsTypeEnum::NONE); } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + chartAxisLeft->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisRight->setUnits(CaretUnitsTypeEnum::NONE); + + /* + * X- and Y-axis for line series shows full extent of data + */ + m_horizontalAxes->initializeScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::DATA); + m_verticalAxes->initializeScaleRangeMode(ChartTwoAxisScaleRangeModeEnum::DATA); + chartAxisBottom->setUnits(CaretUnitsTypeEnum::NONE); + chartAxisTop->setUnits(CaretUnitsTypeEnum::NONE); break; } m_title = std::unique_ptr(new ChartTwoTitle()); m_sceneAssistant = new SceneClassAssistant(); - m_sceneAssistant->add("m_chartAxisLeft", - "ChartTwoCartesianAxis", - m_chartAxisLeft.get()); - m_sceneAssistant->add("m_chartAxisRight", - "ChartTwoCartesianAxis", - m_chartAxisRight.get()); - m_sceneAssistant->add("m_chartAxisBottom", - "ChartTwoCartesianAxis", - m_chartAxisBottom.get()); - + m_sceneAssistant->add("m_horizontalAxes", + "ChartTwoCartesianOrientedAxes", + m_horizontalAxes.get()); + m_sceneAssistant->add("m_verticalAxes", + "ChartTwoCartesianOrientedAxes", + m_verticalAxes.get()); m_sceneAssistant->add("m_title", "ChartTwoTitle", m_title.get()); @@ -149,10 +175,8 @@ CaretAssertVectorIndex(m_overlays, i); m_overlays[i]->setWeakPointerToSelf(m_overlays[i]); } - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_CHART_LABEL_GET); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_TWO_ATTRIBUTES_CHANGED); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_TWO_AXIS_GET_DATA_RANGE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_VALIDATION); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP); } @@ -179,9 +203,9 @@ CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i); m_overlays[i]->copyData(overlaySet->getOverlay(i)); } - *m_chartAxisLeft = *overlaySet->m_chartAxisLeft; - *m_chartAxisRight = *overlaySet->m_chartAxisRight; - *m_chartAxisBottom = *overlaySet->m_chartAxisBottom; + *m_horizontalAxes = *overlaySet->m_horizontalAxes; + *m_verticalAxes = *overlaySet->m_verticalAxes; + *m_title = *overlaySet->m_title; m_axisLineThickness = overlaySet->m_axisLineThickness; @@ -199,13 +223,22 @@ { CaretAssert(m_chartDataType == overlaySet->m_chartDataType); - *m_chartAxisLeft = *overlaySet->m_chartAxisLeft; - *m_chartAxisRight = *overlaySet->m_chartAxisRight; - *m_chartAxisBottom = *overlaySet->m_chartAxisBottom; + m_horizontalAxes->copyAxes(overlaySet->m_horizontalAxes.get()); + m_verticalAxes->copyAxes(overlaySet->m_verticalAxes.get()); + m_axisLineThickness = overlaySet->m_axisLineThickness; } /** + * @return The chart data type for this chart overlay. + */ +ChartTwoDataTypeEnum::Enum +ChartTwoOverlaySet::getChartTwoDataType() const +{ + return m_chartDataType; +} + +/** * @return Returns the top-most overlay regardless of its enabled status. */ ChartTwoOverlay* @@ -404,6 +437,48 @@ } /** + * Assign to this overlay that is not used by any other overlays. + * Note: The maximum number of overlay is greater than the number + * of CaretColors so when many overlays are enabled, a unque + * color may not be available. + * + * @param chartOverlay + * Overlay that is assigned a color not used by other overlays + */ +void +ChartTwoOverlaySet::assignUnusedColor(ChartTwoOverlay* chartOverlay) +{ + CaretAssert(chartOverlay); + + /* + * Get colors used by other overlays + */ + std::set usedColors; + for (int32_t i = 0; i < m_numberOfDisplayedOverlays; i++) { + const ChartTwoOverlay* cto = getOverlay(i); + CaretAssert(cto); + if (cto != chartOverlay) { + usedColors.insert(cto->getLineLayerColor().getCaretColorEnum()); + } + } + + /* + * Assing a color not used by any other chart layers + */ + std::vector allColors; + CaretColorEnum::getColorEnumsNoBlackOrWhite(allColors); + CaretAssert( ! allColors.empty()); + for (const auto color : allColors) { + if (usedColors.find(color) == usedColors.end()) { + CaretColor caretColor; + caretColor.setCaretColorEnum(color); + chartOverlay->setLineLayerColor(caretColor); + break; + } + } +} + +/** * Insert an overlay below this overlay * @param overlayIndex * Index of overlay for which an overlay is added below @@ -417,6 +492,10 @@ for (int32_t i = (m_numberOfDisplayedOverlays - 2); i >= overlayIndex; i--) { moveDisplayedOverlayDown(i); } + + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, overlayIndex+1); + m_overlays[overlayIndex]->copyData(m_overlays[overlayIndex+1].get()); + assignUnusedColor(m_overlays[overlayIndex].get()); } } @@ -434,6 +513,10 @@ for (int32_t i = (m_numberOfDisplayedOverlays - 2); i > overlayIndex; i--) { moveDisplayedOverlayDown(i); } + + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, overlayIndex+1); + m_overlays[overlayIndex+1]->copyData(m_overlays[overlayIndex].get()); + assignUnusedColor(m_overlays[overlayIndex+1].get()); } } @@ -524,16 +607,13 @@ m_inFirstOverlayChangedMethodFlag = true; - ChartTwoCompoundDataType cdt = m_overlays[0]->getChartTwoCompoundDataType(); + const ChartTwoCompoundDataType* cdt = m_overlays[0]->getChartTwoCompoundDataType(); for (int32_t i = 1; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i); - m_overlays[i]->setChartTwoCompoundDataType(cdt); + m_overlays[i]->setChartTwoCompoundDataType(*cdt); } - PlainTextStringBuilder description; - getDescriptionOfContent(description); - m_inFirstOverlayChangedMethodFlag = false; } @@ -569,123 +649,25 @@ void ChartTwoOverlaySet::receiveEvent(Event* event) { - if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_ATTRIBUTES_CHANGED) { - EventChartTwoAttributesChanged* attributeEvent = dynamic_cast(event); - CaretAssert(attributeEvent); - - switch (attributeEvent->getMode()) { - case EventChartTwoAttributesChanged::Mode::INVALID: - break; - case EventChartTwoAttributesChanged::Mode::CARTESIAN_AXIS: - { - YokingGroupEnum::Enum yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; - ChartTwoDataTypeEnum::Enum chartTwoDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; - ChartTwoCartesianAxis* cartesianAxis = NULL; - attributeEvent->getCartesianAxisChanged(yokingGroup, - chartTwoDataType, - cartesianAxis); - - /* - * Only tabs in the windows are valid - */ - EventBrowserTabGet tabEvent(m_tabIndex); - EventManager::get()->sendEvent(tabEvent.getPointer()); - const BrowserTabContent* btc = tabEvent.getBrowserTab(); - if (btc != NULL) { - const YokingGroupEnum::Enum tabYoking = btc->getChartModelYokingGroup(); - - if ((yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) - && (yokingGroup == tabYoking) - && (m_chartDataType == chartTwoDataType)) { - switch (cartesianAxis->getAxisLocation()) { - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - *m_chartAxisBottom = *cartesianAxis; - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - *m_chartAxisLeft = *cartesianAxis; - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - *m_chartAxisRight = *cartesianAxis; - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - CaretAssert(0); - break; - } - } - } - } - break; - case EventChartTwoAttributesChanged::Mode::LINE_THICKESS: + if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP) { + EventChartTwoCartesianAxisDisplayGroup* dgEvent = dynamic_cast(event); + CaretAssert(event); + switch (dgEvent->getMode()) { + case EventChartTwoCartesianAxisDisplayGroup::Mode::GET_ALL_YOKED_AXES: { - YokingGroupEnum::Enum yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; - ChartTwoDataTypeEnum::Enum chartTwoDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; - float lineThickness = 0.0f; - attributeEvent->getLineThicknessChanged(yokingGroup, - chartTwoDataType, - lineThickness); - - /* - * Only tabs in the windows are valid - */ - EventBrowserTabGet tabEvent(m_tabIndex); - EventManager::get()->sendEvent(tabEvent.getPointer()); - const BrowserTabContent* btc = tabEvent.getBrowserTab(); - if (btc != NULL) { - const YokingGroupEnum::Enum tabYoking = btc->getChartModelYokingGroup(); - - if ((yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) - && (yokingGroup == tabYoking) - && (m_chartDataType == chartTwoDataType)) { - m_axisLineThickness = lineThickness; + std::vector axes; + getDisplayedChartAxes(axes); + for (auto& a : axes) { + if (a->getDisplayGroup() == dgEvent->getDisplayGroup()) { + dgEvent->addToYokedAxes(a); } } + event->setEventProcessed(); } break; - case EventChartTwoAttributesChanged::Mode::TITLE: - { - YokingGroupEnum::Enum yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; - ChartTwoDataTypeEnum::Enum chartTwoDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; - ChartTwoTitle* chartTitle = NULL; - attributeEvent->getTitleChanged(yokingGroup, - chartTwoDataType, - chartTitle); - - /* - * Only tabs in the windows are valid - */ - EventBrowserTabGet tabEvent(m_tabIndex); - EventManager::get()->sendEvent(tabEvent.getPointer()); - const BrowserTabContent* btc = tabEvent.getBrowserTab(); - if (btc != NULL) { - const YokingGroupEnum::Enum tabYoking = btc->getChartModelYokingGroup(); - - if ((yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) - && (yokingGroup == tabYoking) - && (m_chartDataType == chartTwoDataType)) { - *m_title = *chartTitle; - } - } - } + case EventChartTwoCartesianAxisDisplayGroup::Mode::GET_DISPLAY_GROUP_AXIS: break; } - - attributeEvent->setEventProcessed(); - } - else if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_AXIS_GET_DATA_RANGE) { - EventChartTwoAxisGetDataRange* rangeEvent = dynamic_cast(event); - CaretAssert(rangeEvent); - - if (rangeEvent->getChartOverlaySet() == this) { - float minimumValue = 0.0f; - float maximumValue = 0.0f; - if (getDataRangeForAxis(rangeEvent->getChartAxisLocation(), - minimumValue, - maximumValue)) { - rangeEvent->setMinimumAndMaximumValues(minimumValue, - maximumValue); - rangeEvent->setEventProcessed(); - } - } } else if (event->getEventType() == EventTypeEnum::EVENT_MAP_YOKING_VALIDATION) { /* @@ -762,6 +744,12 @@ yokingGroupMapIndex); } break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + if (yokingGroupMapIndex < mapFile->getNumberOfMaps()) { + overlay->setSelectionData(mapFile, + yokingGroupMapIndex); + } + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -780,16 +768,6 @@ selectMapEvent->setEventProcessed(); } } - else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_CHART_LABEL_GET) { - /* - * The events intended for overlays are received here so that - * only DISPLAYED overlays are updated. - */ - //EventAnnotationChartLabelGet* chartLabelEvent = dynamic_cast(event); - //CaretAssert(chartLabelEvent); - - //chartLabelEvent->addAnnotationChartLabel(m_chartTitle.get()); - } } /** @@ -831,22 +809,19 @@ if (mapFile != NULL) { BoundingBox boundingBox; if (overlay->getBounds(boundingBox)) { + switch (chartAxisLocation) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: minimumValueOut = std::min(minimumValueOut, boundingBox.getMinX()); maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxX()); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - if (overlay->getCartesianVerticalAxisLocation() == ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT) { - minimumValueOut = std::min(minimumValueOut, boundingBox.getMinY()); - maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxY()); - } + minimumValueOut = std::min(minimumValueOut, boundingBox.getMinY()); + maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxY()); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - if (overlay->getCartesianVerticalAxisLocation() == ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT) { - minimumValueOut = std::min(minimumValueOut, boundingBox.getMinY()); - maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxY()); - } + minimumValueOut = std::min(minimumValueOut, boundingBox.getMinY()); + maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxY()); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: minimumValueOut = std::min(minimumValueOut, boundingBox.getMinX()); @@ -869,6 +844,71 @@ } /** + * Get the minimum and maximum values for the given chart axis orientation + * from the ENABLED overlays in this chart overlay set. + * + * @param axisOrientationType + * Location of axis. + * @param minimumValueOut + * Output with minimum value for axis. + * @param maximumValueOut + * Output with maximum value for axis. + * @return + * True if output values are valid, else false. + */ +bool +ChartTwoOverlaySet::getDataRangeForAxisOrientation(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientationType, + float& minimumValueOut, + float& maximumValueOut) const +{ + minimumValueOut = 0.0f; + maximumValueOut = 0.0f; + if ( ! isAxesSupportedByChartDataType()) { + return false; + } + + minimumValueOut = std::numeric_limits::max(); + maximumValueOut = -std::numeric_limits::max(); + + std::vector enabledOverlays = getEnabledOverlays(); + for (auto overlay : enabledOverlays) { + if (overlay->isEnabled()) { + CaretMappableDataFile* mapFile = NULL; + ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; + int32_t selectedIndex = -1; + overlay->getSelectionData(mapFile, + selectedIndexType, + selectedIndex); + if (mapFile != NULL) { + BoundingBox boundingBox; + if (overlay->getBounds(boundingBox)) { + switch (axisOrientationType) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + minimumValueOut = std::min(minimumValueOut, boundingBox.getMinX()); + maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxX()); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + minimumValueOut = std::min(minimumValueOut, boundingBox.getMinY()); + maximumValueOut = std::max(maximumValueOut, boundingBox.getMaxY()); + break; + } + } + } + } + } + + if (minimumValueOut < maximumValueOut) { + return true; + } + + minimumValueOut = 0.0f; + maximumValueOut = 0.0f; + + return false; +} + + +/** * @return Are axes supported by the chart data type for this overlay set? */ bool @@ -881,10 +921,14 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: axisSupportedFlag = true; break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + axisSupportedFlag = true; + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: axisSupportedFlag = true; break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + axisSupportedFlag = true; break; } @@ -903,36 +947,266 @@ { axesOut.clear(); - AString lineSeriesDataTypeName; - - - float xMin = 0.0f; - float xMax = 0.0f; + float xMinBottom = 0.0f; + float xMaxBottom = 0.0f; float yMinLeft = 0.0f; float yMaxLeft = 0.0f; - float yMinRight = 0.0f; - float yMaxRight = 0.0f; - bool showBottomFlag = getDataRangeForAxis(ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM, - xMin, xMax); - bool showLeftFlag = getDataRangeForAxis(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT, - yMinLeft, yMaxLeft); - bool showRightFlag = getDataRangeForAxis(ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT, - yMinRight, yMaxRight); - - m_chartAxisBottom->setEnabledByChart(showBottomFlag); - m_chartAxisLeft->setEnabledByChart(showLeftFlag); - m_chartAxisRight->setEnabledByChart(showRightFlag); - - if (m_chartAxisBottom->isEnabledByChart()) { - axesOut.push_back(m_chartAxisBottom.get()); + if (getDataRangeForAxisOrientation(ChartTwoAxisOrientationTypeEnum::HORIZONTAL, + xMinBottom, + xMaxBottom)) { + + if (m_horizontalAxes->getLeftOrBottomAxis()->isDisplayedByUser()) { + axesOut.push_back(m_horizontalAxes->getLeftOrBottomAxis()); + } + if (m_horizontalAxes->getRightOrTopAxis()->isDisplayedByUser()) { + axesOut.push_back(m_horizontalAxes->getRightOrTopAxis()); + } + } + if (getDataRangeForAxisOrientation(ChartTwoAxisOrientationTypeEnum::VERTICAL, + yMinLeft, + yMaxLeft)) { + if (m_verticalAxes->getLeftOrBottomAxis()->isDisplayedByUser()) { + axesOut.push_back(m_verticalAxes->getLeftOrBottomAxis()); + } + if (m_verticalAxes->getRightOrTopAxis()->isDisplayedByUser()) { + axesOut.push_back(m_verticalAxes->getRightOrTopAxis()); + } + } +} + +/** + * @return Pointer to the horizontal axes + */ +ChartTwoCartesianOrientedAxes* +ChartTwoOverlaySet::getHorizontalAxes() +{ + return m_horizontalAxes.get(); +} + +/** + * @return Pointer to the horizontal axes (const method) + */ +const ChartTwoCartesianOrientedAxes* +ChartTwoOverlaySet::getHorizontalAxes() const +{ + return m_horizontalAxes.get(); +} + +/** + * @return Pointer to the vertical axes + */ +ChartTwoCartesianOrientedAxes* +ChartTwoOverlaySet::getVerticalAxes() +{ + return m_verticalAxes.get(); +} + +/** + * @return Pointer to the vertical axes (const method) + */ +const ChartTwoCartesianOrientedAxes* +ChartTwoOverlaySet::getVerticalAxes() const +{ + return m_verticalAxes.get(); +} + +/** + * Apply mouse translation to the current chart's axes + * @param viewport + * Viewport containing chart + * @param mouseDX + * The change in mouse X + * @param mouseDY + * The change in mouse Y + */ +void +ChartTwoOverlaySet::applyMouseTranslation(const int32_t viewport[4], + const float mouseDX, + const float mouseDY) +{ + m_horizontalAxes->applyMouseTranslation(viewport, + mouseDX, + mouseDY); + m_verticalAxes->applyMouseTranslation(viewport, + mouseDX, + mouseDY); +} + +/** + * Apply mouse scaling to the current chart's axes + * @param viewport + * Viewport containing chart + * @param mouseX + * The position of mouse along x-axis + * @param mouseY + * The position of mouse along y-axis + * @param mouseDY + * The change in mouse Y + */ +void +ChartTwoOverlaySet::applyMouseScaling(const int32_t viewport[4], + const float mouseX, + const float mouseY, + const float mouseDY) +{ + m_horizontalAxes->applyMouseScaling(viewport, + mouseX, + mouseDY); + m_verticalAxes->applyMouseScaling(viewport, + mouseY, + mouseDY); +} + +/** + * Apply chart two bounds selection as user drags the mouse + * @param viewport + * Chart viewport + * @param x1 + * X from first pair of coordinates + * @param y1 + * Y from first pair of coordinates + * @param x2 + * X from second pair of coordinates + * @param y2 + * Y from second pair of coordinates + */ +void +ChartTwoOverlaySet::applyChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2) +{ + setChartTwoAxesBoundSelection(viewport, + x1, y1, x2, y2); + m_chartSelectionBoundsValid = true; +} + +/** + * Finalize chart two bounds selection to set the bounds of the chart + * @param viewport + * Chart viewport + * @param x1 + * X from first pair of coordinates + * @param y1 + * Y from first pair of coordinates + * @param x2 + * X from second pair of coordinates + * @param y2 + * Y from second pair of coordinates + */ +void +ChartTwoOverlaySet::finalizeChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2) +{ + if ( ! m_chartSelectionBoundsValid) { + return; } - if (m_chartAxisLeft->isEnabledByChart()) { - axesOut.push_back(m_chartAxisLeft.get()); + + /* + * x2 and y2 are coordinates of where mouse button was released. + * Test to verify this coordinate is inside the chart axes. + * User can cancel this operation by releasing the mouse outside + * of the axes. + */ + const int32_t extraPixels(3); + const int32_t vpMinX(viewport[0] - extraPixels); + const int32_t vpMaxX(viewport[0] + viewport[2] + extraPixels); + const int32_t vpMinY(viewport[1] - extraPixels); + const int32_t vpMaxY(viewport[1] + viewport[3] + extraPixels); + if ((x2 >= vpMinX) + && (x2 <= vpMaxX) + && (y2 >= vpMinY) + && (y2 <= vpMaxY)) { + setChartTwoAxesBoundSelection(viewport, + x1, y1, x2, y2); + const float minX(m_chartSelectionBounds[0]); + const float minY(m_chartSelectionBounds[1]); + const float maxX(m_chartSelectionBounds[2]); + const float maxY(m_chartSelectionBounds[3]); + + if ((minX < maxX) + && (minY < maxY)) { + if (m_horizontalAxes->isTransformationEnabled()) { + m_horizontalAxes->setUserScaleMinimumValueFromGUI(minX); + m_horizontalAxes->setUserScaleMaximumValueFromGUI(maxX); + } + if (m_verticalAxes->isTransformationEnabled()) { + m_verticalAxes->setUserScaleMinimumValueFromGUI(minY); + m_verticalAxes->setUserScaleMaximumValueFromGUI(maxY); + } + } } - if (m_chartAxisRight->isEnabledByChart()) { - axesOut.push_back(m_chartAxisRight.get()); + else { + /* + * Nothing, outside chart axes + */ } + + m_chartSelectionBoundsValid = false; +} + +void +ChartTwoOverlaySet::setChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2) +{ + const int32_t vpX(viewport[0]); + const int32_t vpY(viewport[1]); + const int32_t viewportWidth(viewport[2]); + const int32_t viewportHeight(viewport[3]); + float xMin(m_horizontalAxes->getAxesCoordinateFromViewportCoordinate(viewportWidth, + viewportHeight, + x1 - vpX)); + float xMax(m_horizontalAxes->getAxesCoordinateFromViewportCoordinate(viewportWidth, + viewportHeight, + x2 - vpX)); + float yMin(m_verticalAxes->getAxesCoordinateFromViewportCoordinate(viewportWidth, + viewportHeight, + y1 - vpY)); + float yMax(m_verticalAxes->getAxesCoordinateFromViewportCoordinate(viewportWidth, + viewportHeight, + y2 - vpY)); + + if (xMax < xMin) std::swap(xMax, xMin); + if (yMax < yMin) std::swap(yMax, yMin); + + m_chartSelectionBounds[0] = xMin; + m_chartSelectionBounds[1] = yMin; + m_chartSelectionBounds[2] = xMax; + m_chartSelectionBounds[3] = yMax; +} + +/** + * Get chart two bounds selection as user drags the mouse + * @param minX + * X from first pair of coordinates + * @param minY + * Y from first pair of coordinates + * @param maxX + * X from second pair of coordinates + * @param maxY + * Y from second pair of coordinates + * @return True if valid + */ +bool +ChartTwoOverlaySet::getChartSelectionBounds(float& minX, + float& minY, + float& maxX, + float& maxY) const +{ + minX = m_chartSelectionBounds[0]; + minY = m_chartSelectionBounds[1]; + maxX = m_chartSelectionBounds[2]; + maxY = m_chartSelectionBounds[3]; + + return m_chartSelectionBoundsValid; } /** @@ -962,7 +1236,7 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: switch (axis->getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - label = mapFile->getChartingDelegate()->getHistogramCharting()->getBottomAxisTitle(); + label = mapFile->getChartingDelegate()->getHistogramCharting()->getBottomTopAxisTitle(); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: label = mapFile->getChartingDelegate()->getHistogramCharting()->getLeftRightAxisTitle(); @@ -971,14 +1245,30 @@ label = mapFile->getChartingDelegate()->getHistogramCharting()->getLeftRightAxisTitle(); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - CaretAssert(0); + label = mapFile->getChartingDelegate()->getHistogramCharting()->getBottomTopAxisTitle(); + break; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + label = mapFile->getChartingDelegate()->getLineLayerCharting()->getBottomTopAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + label = mapFile->getChartingDelegate()->getLineLayerCharting()->getLeftRightAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + label = mapFile->getChartingDelegate()->getLineLayerCharting()->getLeftRightAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + label = mapFile->getChartingDelegate()->getLineLayerCharting()->getBottomTopAxisTitle(); break; } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: switch (axis->getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - label = mapFile->getChartingDelegate()->getLineSeriesCharting()->getBottomAxisTitle(); + label = mapFile->getChartingDelegate()->getLineSeriesCharting()->getBottomTopAxisTitle(); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: label = mapFile->getChartingDelegate()->getLineSeriesCharting()->getLeftRightAxisTitle(); @@ -987,11 +1277,25 @@ label = mapFile->getChartingDelegate()->getLineSeriesCharting()->getLeftRightAxisTitle(); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - CaretAssert(0); + label = mapFile->getChartingDelegate()->getLineSeriesCharting()->getBottomTopAxisTitle(); break; } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + label = mapFile->getChartingDelegate()->getMatrixCharting()->getBottomTopAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + label = mapFile->getChartingDelegate()->getMatrixCharting()->getLeftRightAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + label = mapFile->getChartingDelegate()->getMatrixCharting()->getLeftRightAxisTitle(); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + label = mapFile->getChartingDelegate()->getMatrixCharting()->getBottomTopAxisTitle(); + break; + } break; } } @@ -1021,7 +1325,7 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: switch (axis->getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - mapFile->getChartingDelegate()->getHistogramCharting()->setBottomAxisTitle(label); + mapFile->getChartingDelegate()->getHistogramCharting()->setBottomTopAxisTitle(label); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: mapFile->getChartingDelegate()->getHistogramCharting()->setLeftRightAxisTitle(label); @@ -1030,14 +1334,30 @@ mapFile->getChartingDelegate()->getHistogramCharting()->setLeftRightAxisTitle(label); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - CaretAssert(0); + mapFile->getChartingDelegate()->getHistogramCharting()->setBottomTopAxisTitle(label); + break; + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + mapFile->getChartingDelegate()->getLineLayerCharting()->setBottomTopAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + mapFile->getChartingDelegate()->getLineLayerCharting()->setLeftRightAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + mapFile->getChartingDelegate()->getLineLayerCharting()->setLeftRightAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + mapFile->getChartingDelegate()->getLineLayerCharting()->setBottomTopAxisTitle(label); break; } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: switch (axis->getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - mapFile->getChartingDelegate()->getLineSeriesCharting()->setBottomAxisTitle(label); + mapFile->getChartingDelegate()->getLineSeriesCharting()->setBottomTopAxisTitle(label); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: mapFile->getChartingDelegate()->getLineSeriesCharting()->setLeftRightAxisTitle(label); @@ -1046,11 +1366,25 @@ mapFile->getChartingDelegate()->getLineSeriesCharting()->setLeftRightAxisTitle(label); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - CaretAssert(0); + mapFile->getChartingDelegate()->getLineSeriesCharting()->setBottomTopAxisTitle(label); break; } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + switch (axis->getAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + mapFile->getChartingDelegate()->getMatrixCharting()->setBottomTopAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + mapFile->getChartingDelegate()->getMatrixCharting()->setLeftRightAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + mapFile->getChartingDelegate()->getMatrixCharting()->setLeftRightAxisTitle(label); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + mapFile->getChartingDelegate()->getMatrixCharting()->setBottomTopAxisTitle(label); + break; + } break; } } @@ -1097,6 +1431,69 @@ } /** + * Increment (decrement if negative) any line chart layer points the are active + * @param incrementValue + * Amount to advance point index. + */ +void +ChartTwoOverlaySet::incrementOverlayActiveLineChartPoint(const int32_t incrementValue) +{ + const int32_t numOverlays = static_cast(m_overlays.size()); + for (int32_t i = 0; i < numOverlays; i++) { + if (m_overlays[i] != NULL) { + switch (m_overlays[i]->getLineChartActiveMode()) { + case ChartTwoOverlayActiveModeEnum::ACTIVE: + m_overlays[i]->incrementSelectedLineChartPointIndex(incrementValue); + break; + case ChartTwoOverlayActiveModeEnum::OFF: + break; + case ChartTwoOverlayActiveModeEnum::ON: + break; + } + } + } +} + +/** + * Select or deselect the active line chart layers + * @param chartTwoOverlay + * If non NULL, set the given layer as the active layer and change any other active layers to on. + * If NULL, change any active layer to on (no layers are active) + * @param lineSegmentPointIndex + * Index of selected line segment point + */ +void +ChartTwoOverlaySet::selectOverlayActiveLineChart(ChartTwoOverlay* chartTwoOverlay, + const int32_t lineSegmentPointIndex) +{ + const int32_t numOverlays = static_cast(m_overlays.size()); + for (int32_t i = 0; i < numOverlays; i++) { + if (m_overlays[i] != NULL) { + /* + * Change any active overlays to on + */ + switch (m_overlays[i]->getLineChartActiveMode()) { + case ChartTwoOverlayActiveModeEnum::ACTIVE: + m_overlays[i]->setLineChartActiveMode(ChartTwoOverlayActiveModeEnum::ON); + break; + case ChartTwoOverlayActiveModeEnum::OFF: + break; + case ChartTwoOverlayActiveModeEnum::ON: + break; + } + + /* + * Make given chart overlay active + */ + if (chartTwoOverlay == m_overlays[i].get()) { + chartTwoOverlay->setLineChartActiveMode(ChartTwoOverlayActiveModeEnum::ACTIVE); + chartTwoOverlay->setSelectedLineChartPointIndex(lineSegmentPointIndex); + } + } + } +} + +/** * Save information specific to this type of model to the scene. * * @param sceneAttributes @@ -1113,7 +1510,7 @@ { SceneClass* sceneClass = new SceneClass(instanceName, "ChartTwoOverlaySet", - 1); + 2); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -1136,6 +1533,118 @@ } /** + * Restore the range scale from an old scene + * @param axisOrientationType + * The axis orientation + * @param leftOrBottomAxis + * The left or bottom axis + * @param rightOrTopAxis + * The right or top axis + */ +void +ChartTwoOverlaySet::updateRangeScaleFromVersionOneScene(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientationType, + ChartTwoCartesianAxis* leftOrBottomAxis, + ChartTwoCartesianAxis* rightOrTopAxis) +{ + ChartTwoAxisScaleRangeModeEnum::Enum rangeMode = ChartTwoAxisScaleRangeModeEnum::AUTO; + float minRange(0.0); + float maxRange(0.0); + + /* + * Restore from left/bottom first + */ + bool leftBottomValidFlag(false); + if (leftOrBottomAxis != NULL) { + /* + * Neither axis may be on, so default to left's range + */ + rangeMode = leftOrBottomAxis->getSceneScaleRangeMode(); + minRange = leftOrBottomAxis->getSceneUserScaleMinimumValue(); + maxRange = leftOrBottomAxis->getSceneUserScaleMaximumValue(); + if (leftOrBottomAxis->isDisplayedByUser()) { + /* + * Use the left axis for range + */ + leftBottomValidFlag = true; + } + } + + /* + * If left NOT valid + */ + if ( ! leftBottomValidFlag) { + /* + * Only use right axis if left is not used (NULL) + * or left axis is off and right is on + */ + if (rightOrTopAxis != NULL) { + if (rightOrTopAxis->isDisplayedByUser() + || (leftOrBottomAxis == NULL)) { + rangeMode = rightOrTopAxis->getSceneScaleRangeMode(); + minRange = rightOrTopAxis->getSceneUserScaleMinimumValue(); + maxRange = rightOrTopAxis->getSceneUserScaleMaximumValue(); + } + } + } + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + /* + * Use 'data' for range for matrices in version one scenes + */ + rangeMode = ChartTwoAxisScaleRangeModeEnum::DATA; + break; + } + + switch (axisOrientationType) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + m_horizontalAxes->setRangeModeAndUserScaleFromVersionOneScene(rangeMode, + minRange, + maxRange); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + m_verticalAxes->setRangeModeAndUserScaleFromVersionOneScene(rangeMode, + minRange, + maxRange); + break; + } + + switch (m_chartDataType) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + /* + * Version one matrices did not have axes + */ + switch (axisOrientationType) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + m_horizontalAxes->getLeftOrBottomAxis()->setDisplayedByUser(false); + m_horizontalAxes->getRightOrTopAxis()->setDisplayedByUser(false); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + m_verticalAxes->getLeftOrBottomAxis()->setDisplayedByUser(false); + m_verticalAxes->getRightOrTopAxis()->setDisplayedByUser(false); + break; + } + break; + } +} + +/** * Restore information specific to the type of model from the scene. * * @param sceneAttributes @@ -1155,24 +1664,31 @@ } m_title->reset(); + m_verticalAxes->reset(); + m_horizontalAxes->reset(); + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); - /* - * Title was originally saved as text and boolean - */ - const SceneClass* oldChartTitleClass = sceneClass->getClass("m_chartTitle"); - if (oldChartTitleClass != NULL) { - if (oldChartTitleClass->getClassName() == "AnnotationPercentSizeText") { - std::unique_ptr title = std::unique_ptr(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, - AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT)); - title->setText(""); - title->restoreFromScene(sceneAttributes, oldChartTitleClass); - - m_title->setText(title->getText()); - m_title->setDisplayed(sceneClass->getBooleanValue("m_chartTitleDisplayedFlag", - false)); + if (sceneClass->getVersionNumber() >= 2) { + } + else { + /* + * Title was originally saved as text and boolean + */ + const SceneClass* oldChartTitleClass = sceneClass->getClass("m_chartTitle"); + if (oldChartTitleClass != NULL) { + if (oldChartTitleClass->getClassName() == "AnnotationPercentSizeText") { + std::unique_ptr title = std::unique_ptr(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT)); + title->setText(""); + title->restoreFromScene(sceneAttributes, oldChartTitleClass); + + m_title->setText(title->getText()); + m_title->setDisplayed(sceneClass->getBooleanValue("m_chartTitleDisplayedFlag", + false)); + } } } @@ -1186,6 +1702,96 @@ overlayClassArray->getClassAtIndex(i)); } } + + /** + * For version 1 need use obsolete axis location (right or left) to set display + * status of left and right axes + */ + if (sceneClass->getVersionNumber() == 1) { + bool leftAxisOnFlag(false); + bool rightAxisOnFlag(false); + for (int32_t i = 0; i < getNumberOfDisplayedOverlays(); i++) { + const ChartTwoOverlay* overlay = getOverlay(i); + if (overlay->isEnabled()) { + switch (overlay->getSceneCartesianVerticalAxisLocation()) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + leftAxisOnFlag = true; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + rightAxisOnFlag = true; + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + break; + } + } + } + + /* + * Restore the axes from the older separate axes + */ + ChartTwoCartesianAxis* rightAxis(NULL); + const SceneClass* v1RightAxis = sceneClass->getClass("m_chartAxisRight"); + if (v1RightAxis != NULL) { + rightAxis = m_verticalAxes->getRightOrTopAxis(); + rightAxis->restoreFromScene(sceneAttributes, + v1RightAxis); + } + + const SceneClass* v1LeftAxis = sceneClass->getClass("m_chartAxisLeft"); + ChartTwoCartesianAxis* leftAxis(NULL); + if (v1LeftAxis != NULL) { + leftAxis = m_verticalAxes->getLeftOrBottomAxis(); + leftAxis->restoreFromScene(sceneAttributes, + v1LeftAxis); + } + + /* + * Must do after restoring axes + */ + m_verticalAxes->getLeftOrBottomAxis()->setDisplayedByUser(leftAxisOnFlag); + m_verticalAxes->getRightOrTopAxis()->setDisplayedByUser(rightAxisOnFlag); + if (leftAxisOnFlag + && (leftAxis != NULL)) { + updateRangeScaleFromVersionOneScene(ChartTwoAxisOrientationTypeEnum::VERTICAL, + leftAxis, + NULL); /* right axis */ + } + else if (rightAxisOnFlag + && (rightAxis != NULL)) { + updateRangeScaleFromVersionOneScene(ChartTwoAxisOrientationTypeEnum::VERTICAL, + NULL, /* left axis */ + rightAxis); + } + + const SceneClass* v1TopAxis = sceneClass->getClass("m_chartAxisTop"); + ChartTwoCartesianAxis* topAxis(NULL); + if (v1TopAxis != NULL) { + topAxis = m_horizontalAxes->getRightOrTopAxis(); + topAxis->restoreFromScene(sceneAttributes, + v1TopAxis); + } + + /* + * There was no top axis option in version 1 scenes + * so turn top axis off + */ + m_horizontalAxes->getRightOrTopAxis()->setDisplayedByUser(false); + + const SceneClass* v1BottomAxis = sceneClass->getClass("m_chartAxisBottom"); + ChartTwoCartesianAxis* bottomAxis(NULL); + if (v1BottomAxis != NULL) { + bottomAxis = m_horizontalAxes->getLeftOrBottomAxis(); + bottomAxis->restoreFromScene(sceneAttributes, + v1BottomAxis); + } + + updateRangeScaleFromVersionOneScene(ChartTwoAxisOrientationTypeEnum::HORIZONTAL, + bottomAxis, + topAxis); + + } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); diff -Nru connectome-workbench-1.4.2/src/Brain/ChartTwoOverlaySet.h connectome-workbench-1.5.0/src/Brain/ChartTwoOverlaySet.h --- connectome-workbench-1.4.2/src/Brain/ChartTwoOverlaySet.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ChartTwoOverlaySet.h 2021-02-16 19:46:47.000000000 +0000 @@ -20,12 +20,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*LICENSE_END*/ + +#include #include #include "BrainConstants.h" #include "CaretObject.h" -#include "ChartAxisLocationEnum.h" #include "ChartTwoDataTypeEnum.h" +#include "ChartTwoOverlaySetInterface.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" @@ -33,13 +35,14 @@ namespace caret { class AnnotationPercentSizeText; class CaretMappableDataFile; + class ChartTwoCartesianOrientedAxes; class ChartTwoCartesianAxis; class ChartTwoOverlay; class ChartTwoTitle; class PlainTextStringBuilder; class SceneClassAssistant; - class ChartTwoOverlaySet : public CaretObject, public EventListenerInterface, public SceneableInterface { + class ChartTwoOverlaySet : public CaretObject, public ChartTwoOverlaySetInterface, public EventListenerInterface, public SceneableInterface { public: ChartTwoOverlaySet(const ChartTwoDataTypeEnum::Enum chartDataType, @@ -52,6 +55,8 @@ void copyCartesianAxes(const ChartTwoOverlaySet* overlaySet); + ChartTwoDataTypeEnum::Enum getChartTwoDataType() const; + ChartTwoOverlay* getPrimaryOverlay(); const ChartTwoOverlay* getPrimaryOverlay() const; @@ -74,6 +79,40 @@ void getDisplayedChartAxes(std::vector& axesOut) const; + ChartTwoCartesianOrientedAxes* getHorizontalAxes(); + + const ChartTwoCartesianOrientedAxes* getHorizontalAxes() const; + + ChartTwoCartesianOrientedAxes* getVerticalAxes(); + + const ChartTwoCartesianOrientedAxes* getVerticalAxes() const; + + void applyMouseTranslation(const int32_t viewport[4], + const float mouseDX, + const float mouseDY); + + void applyMouseScaling(const int32_t viewport[4], + const float mouseX, + const float mouseY, + const float mouseDY); + + void applyChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2); + + void finalizeChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2); + + bool getChartSelectionBounds(float& minX, + float& minY, + float& maxX, + float& maxY) const; + AString getAxisLabel(const ChartTwoCartesianAxis* axis) const; void setAxisLabel(const ChartTwoCartesianAxis* axis, @@ -81,10 +120,14 @@ bool isAxesSupportedByChartDataType() const; - bool getDataRangeForAxis(const ChartAxisLocationEnum::Enum chartAxisLocation, - float& minimumValueOut, - float& maximumValueOut) const; - + virtual bool getDataRangeForAxis(const ChartAxisLocationEnum::Enum chartAxisLocation, + float& minimumValueOut, + float& maximumValueOut) const override; + + virtual bool getDataRangeForAxisOrientation(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientationType, + float& minimumValueOut, + float& maximumValueOut) const override; + ChartTwoTitle* getChartTitle(); const ChartTwoTitle* getChartTitle() const; @@ -107,6 +150,11 @@ void setAxisLineThickness(const float axisLineThickness); + void incrementOverlayActiveLineChartPoint(const int32_t incrementValue); + + void selectOverlayActiveLineChart(ChartTwoOverlay* chartTwoOverlay, + const int32_t lineSegmentPointIndex); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -122,10 +170,6 @@ const SceneClass* sceneClass); - - - - // If there will be sub-classes of this class that need to save // and restore data from scenes, these pure virtual methods can // be uncommented to force their implementation by sub-classes. @@ -143,17 +187,27 @@ void firstOverlaySelectionChanged(); + void assignUnusedColor(ChartTwoOverlay* chartOverlay); + + void updateRangeScaleFromVersionOneScene(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientationType, + ChartTwoCartesianAxis* leftOrBottomAxis, + ChartTwoCartesianAxis* rightOrTopAxis); + + void setChartTwoAxesBoundSelection(const int32_t viewport[4], + const int32_t x1, + const int32_t y1, + const int32_t x2, + const int32_t y2); + SceneClassAssistant* m_sceneAssistant; std::vector> m_overlays; const ChartTwoDataTypeEnum::Enum m_chartDataType; - std::unique_ptr m_chartAxisLeft; - - std::unique_ptr m_chartAxisRight; + std::unique_ptr m_horizontalAxes; - std::unique_ptr m_chartAxisBottom; + std::unique_ptr m_verticalAxes; const AString m_name; @@ -168,6 +222,10 @@ /** Thickness of box around chart and tick marks on axes*/ float m_axisLineThickness = 0.5; + std::array m_chartSelectionBounds; + + bool m_chartSelectionBoundsValid = false; + // ADD_NEW_MEMBERS_HERE friend class ChartTwoOverlay; diff -Nru connectome-workbench-1.4.2/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx connectome-workbench-1.5.0/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx --- connectome-workbench-1.4.2/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -31,6 +31,7 @@ #include "EventGetDisplayedDataFiles.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" +#include "HtmlTableBuilder.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" @@ -146,7 +147,8 @@ CiftiConnectivityMatrixParcelFile* parcelFile, const int32_t rowIndex, const int32_t columnIndex, - std::vector& rowColumnInformationOut) + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder) { CaretAssert(parcelFile); @@ -194,6 +196,8 @@ rowColumnInformationOut.push_back(pf->getFileNameNoPath() + " column index=" + AString::number(rowColumnIndexToLoad + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Column Index: " + AString::number(rowColumnIndexToLoad + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + pf->getFileNameNoPath()); break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: pf->loadDataForRowIndex(rowColumnIndexToLoad); @@ -201,6 +205,8 @@ rowColumnInformationOut.push_back(pf->getFileNameNoPath() + " row index=" + AString::number(rowColumnIndexToLoad + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Row Index: " + AString::number(rowColumnIndexToLoad + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + pf->getFileNameNoPath()); break; } } @@ -213,7 +219,8 @@ CiftiMappableConnectivityMatrixDataFile* ciftiConnMatrixFile, const int32_t rowIndex, const int32_t columnIndex, - std::vector& rowColumnInformationOut) + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder) { const int32_t mapIndex = 0; @@ -223,6 +230,8 @@ rowColumnInformationOut.push_back(ciftiConnMatrixFile->getFileNameNoPath() + " row index=" + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Row Index: " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + ciftiConnMatrixFile->getFileNameNoPath()); } else if (columnIndex >= 0) { ciftiConnMatrixFile->loadDataForColumnIndex(columnIndex); @@ -230,6 +239,8 @@ rowColumnInformationOut.push_back(ciftiConnMatrixFile->getFileNameNoPath() + " column index=" + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Column Index: " + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + ciftiConnMatrixFile->getFileNameNoPath()); } return true; @@ -253,7 +264,8 @@ CiftiConnectivityMatrixDataFileManager::loadDataForSurfaceNode(Brain* brain, const SurfaceFile* surfaceFile, const int32_t nodeIndex, - std::vector& rowColumnInformationOut) + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder) { std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, @@ -287,6 +299,8 @@ + AString::number(nodeIndex) + ", row index=" + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Row Index: " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + cmf->getFileNameNoPath()); } else if (columnIndex >= 0) { /* @@ -297,6 +311,8 @@ + AString::number(nodeIndex) + ", column index=" + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Column Index: " + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + cmf->getFileNameNoPath()); } } } @@ -365,7 +381,8 @@ bool CiftiConnectivityMatrixDataFileManager::loadDataForVoxelAtCoordinate(Brain* brain, const float xyz[3], - std::vector& rowColumnInformationOut) + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder) { std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, @@ -396,6 +413,8 @@ + AString::fromNumbers(xyz, 3, ",") + ", row index=" + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Row Index: " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + cmf->getFileNameNoPath()); } else if (columnIndex >= 0) { /* @@ -406,6 +425,8 @@ + AString::fromNumbers(xyz, 3, ",") + ", column index=" + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Column Index: " + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + cmf->getFileNameNoPath()); } } } diff -Nru connectome-workbench-1.4.2/src/Brain/CiftiConnectivityMatrixDataFileManager.h connectome-workbench-1.5.0/src/Brain/CiftiConnectivityMatrixDataFileManager.h --- connectome-workbench-1.4.2/src/Brain/CiftiConnectivityMatrixDataFileManager.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/CiftiConnectivityMatrixDataFileManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -30,6 +30,7 @@ class Brain; class CiftiConnectivityMatrixParcelFile; class CiftiMappableConnectivityMatrixDataFile; + class HtmlTableBuilder; class SurfaceFile; class CiftiConnectivityMatrixDataFileManager @@ -44,7 +45,8 @@ bool loadDataForSurfaceNode(Brain* brain, const SurfaceFile* surfaceFile, const int32_t nodeIndex, - std::vector& rowColumnInformationOut); + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder); bool loadAverageDataForSurfaceNodes(Brain* brain, const SurfaceFile* surfaceFile, @@ -52,7 +54,8 @@ bool loadDataForVoxelAtCoordinate(Brain* brain, const float xyz[3], - std::vector& rowColumnInformationOut); + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder); bool loadAverageDataForVoxelIndices(Brain* brain, const int64_t volumeDimensionIJK[3], @@ -62,12 +65,14 @@ CiftiConnectivityMatrixParcelFile* ciftiConnMatrixFile, const int32_t rowIndex, const int32_t columnIndex, - std::vector& rowColumnInformationOut); + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder); bool loadRowOrColumnFromConnectivityMatrixFile(CiftiMappableConnectivityMatrixDataFile* parcelFile, const int32_t rowIndex, const int32_t columnIndex, - std::vector& rowColumnInformationOut); + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder); bool hasNetworkFiles(Brain* brain) const; diff -Nru connectome-workbench-1.4.2/src/Brain/CiftiFiberTrajectoryManager.cxx connectome-workbench-1.5.0/src/Brain/CiftiFiberTrajectoryManager.cxx --- connectome-workbench-1.4.2/src/Brain/CiftiFiberTrajectoryManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/CiftiFiberTrajectoryManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -27,6 +27,8 @@ #include "CaretAssert.h" #include "CiftiFiberOrientationFile.h" #include "CiftiFiberTrajectoryFile.h" +#include "CiftiMappableDataFile.h" +#include "HtmlTableBuilder.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SurfaceFile.h" @@ -59,10 +61,16 @@ /** * Load data for the given surface node index. * + * @param brain + * The brain * @param surfaceFile * Surface File that contains the node (uses its structure). * @param nodeIndex * Index of the surface node. + * @param rowColumnInformationOut + * Appends one string for each row/column loaded + * @param htmlTableBuilder + * Html table builder for cifti loading info * @return * true if any data was loaded, else false. */ @@ -70,7 +78,8 @@ CiftiFiberTrajectoryManager::loadDataForSurfaceNode(Brain* brain, const SurfaceFile* surfaceFile, const int32_t nodeIndex, - std::vector& rowColumnInformationOut) + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder) { bool dataWasLoaded = false; @@ -91,7 +100,9 @@ + " nodeIndex=" + AString::number(nodeIndex) + ", row index= " - + AString::number(rowIndex)); + + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Row Index: " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + trajFile->getFileNameNoPath()); dataWasLoaded = true; } } @@ -102,6 +113,8 @@ /** * Load data for the given surface node index. * + * @param brain + * The brain * @param surfaceFile * Surface File that contains the node (uses its structure). * @param nodeIndex @@ -144,17 +157,22 @@ /** * Load data for the voxel near the given coordinate. + * @param brain + * The brain * @param xyz * Coordinate of voxel. * @param rowColumnInformationOut * Appends one string for each row/column loaded + * @param htmlTableBuilder + * Html table builder for cifti loading info * @return * true if any connectivity loaders are active, else false. */ bool CiftiFiberTrajectoryManager::loadDataForVoxelAtCoordinate(Brain* brain, const float xyz[3], - std::vector& rowColumnInformationOut) + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder) { std::vector ciftiTrajFiles; brain->getConnectivityFiberTrajectoryFiles(ciftiTrajFiles); @@ -175,6 +193,8 @@ + AString::fromNumbers(xyz, 3, ",") + ", row index= " + AString::number(rowIndex)); + htmlTableBuilder.addRow(("Row Index: " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + trajFile->getFileNameNoPath()); haveData = true; } } @@ -186,6 +206,8 @@ /** * Load average data for the given voxel indices. * + * @param brain + * The brain * @param volumeDimensionIJK * Dimensions of the volume. * @param voxelIndices diff -Nru connectome-workbench-1.4.2/src/Brain/CiftiFiberTrajectoryManager.h connectome-workbench-1.5.0/src/Brain/CiftiFiberTrajectoryManager.h --- connectome-workbench-1.4.2/src/Brain/CiftiFiberTrajectoryManager.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/CiftiFiberTrajectoryManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -28,6 +28,7 @@ namespace caret { class Brain; + class HtmlTableBuilder; class SurfaceFile; class CiftiFiberTrajectoryManager : public CaretObject { @@ -40,14 +41,16 @@ bool loadDataForSurfaceNode(Brain* brain, const SurfaceFile* surfaceFile, const int32_t nodeIndex, - std::vector& rowColumnInformationOut); + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder); bool loadDataAverageForSurfaceNodes(Brain* brain, const SurfaceFile* surfaceFile, const std::vector& nodeIndices); bool loadDataForVoxelAtCoordinate(Brain* brain, const float xyz[3], - std::vector& rowColumnInformationOut); + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder); bool loadAverageDataForVoxelIndices(Brain* brain, const int64_t volumeDimensionIJK[3], diff -Nru connectome-workbench-1.4.2/src/Brain/ClippingPlaneGroup.cxx connectome-workbench-1.5.0/src/Brain/ClippingPlaneGroup.cxx --- connectome-workbench-1.4.2/src/Brain/ClippingPlaneGroup.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ClippingPlaneGroup.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -50,6 +50,7 @@ resetToDefaultValues(); m_sceneAssistant = new SceneClassAssistant(); + m_sceneAssistant->add("m_enabledStatus", &m_enabledStatus); m_sceneAssistant->add("m_displayClippingBoxStatus", &m_displayClippingBoxStatus); m_sceneAssistant->addArray("m_translation", m_translation, 3, 0.0); m_sceneAssistant->addArray("m_thickness", m_thickness, 3, 20.0); @@ -113,6 +114,8 @@ m_thickness[i] = obj.m_thickness[i]; } + /* m_enabledStatus NOT copied */ + m_rotationMatrix = obj.m_rotationMatrix; m_xAxisSelectionStatus = obj.m_xAxisSelectionStatus; @@ -129,6 +132,26 @@ } /** + * @return Enabled status + */ +bool +ClippingPlaneGroup::isEnabled() const +{ + return m_enabledStatus; +} + +/** + * Set clipping planes enabled + * @param status + * New enabled status + */ +void +ClippingPlaneGroup::setEnabled(const bool status) +{ + m_enabledStatus = status; +} + +/** * Reset the transformation. */ void @@ -155,8 +178,6 @@ { resetTransformation(); - m_displayClippingBoxStatus = false; - m_xAxisSelectionStatus = false; m_yAxisSelectionStatus = false; m_zAxisSelectionStatus = false; @@ -550,11 +571,13 @@ bool ClippingPlaneGroup::isFeaturesAndAnyAxisSelected() const { - if (m_featuresSelectionStatus) { - if (m_xAxisSelectionStatus - || m_yAxisSelectionStatus - || m_zAxisSelectionStatus) { - return true; + if (m_enabledStatus) { + if (m_featuresSelectionStatus) { + if (m_xAxisSelectionStatus + || m_yAxisSelectionStatus + || m_zAxisSelectionStatus) { + return true; + } } } @@ -814,7 +837,7 @@ { SceneClass* sceneClass = new SceneClass(instanceName, "ClippingPlaneGroup", - 1); + 2); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -844,6 +867,7 @@ ClippingPlaneGroup::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { + m_enabledStatus = false; resetToDefaultValues(); if (sceneClass == NULL) { @@ -863,6 +887,15 @@ else { m_rotationMatrix.identity(); } + + if (sceneClass->getVersionNumber() < 2) { + /* + * Enabled status was added in version 2 + */ + m_enabledStatus = (m_xAxisSelectionStatus + || m_yAxisSelectionStatus + || m_zAxisSelectionStatus); + } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, diff -Nru connectome-workbench-1.4.2/src/Brain/ClippingPlaneGroup.h connectome-workbench-1.5.0/src/Brain/ClippingPlaneGroup.h --- connectome-workbench-1.4.2/src/Brain/ClippingPlaneGroup.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ClippingPlaneGroup.h 2021-02-16 19:46:47.000000000 +0000 @@ -47,6 +47,10 @@ void resetToDefaultValues(); + bool isEnabled() const; + + void setEnabled(const bool status); + std::vector getActiveClippingPlanesForStructure(const StructureEnum::Enum structure) const; void getTranslation(float translation[3]) const; @@ -169,6 +173,8 @@ float m_thickness[3]; + bool m_enabledStatus = false; + bool m_xAxisSelectionStatus; bool m_yAxisSelectionStatus; diff -Nru connectome-workbench-1.4.2/src/Brain/CMakeLists.txt connectome-workbench-1.5.0/src/Brain/CMakeLists.txt --- connectome-workbench-1.4.2/src/Brain/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -37,6 +37,7 @@ BrainOpenGLChartTwoDrawingFixedPipeline.h BrainOpenGLChartTwoDrawingInterface.h BrainOpenGLFixedPipeline.h +BrainOpenGLMediaDrawing.h BrainOpenGLPrimitiveDrawing.h BrainOpenGLShape.h BrainOpenGLShapeCone.h @@ -55,6 +56,7 @@ BrowserTabContent.h BrowserWindowContent.h ChartTwoOverlay.h +ChartTwoOverlayActiveModeEnum.h ChartTwoOverlaySet.h ChartTwoOverlaySetArray.h ChartingDataManager.h @@ -76,19 +78,22 @@ DisplayPropertyDataEnum.h DisplayPropertyDataFloat.h DummyFontTextRenderer.h -EventAnnotationColorBarGet.h +EventAnnotationBarsGet.h EventBrainReset.h EventBrainStructureGetAll.h EventBrowserTabGet.h EventBrowserTabGetAll.h EventBrowserTabGetAllViewed.h +EventBrowserTabReopenAvailable.h EventBrowserWindowContent.h +EventBrowserWindowGetTabs.h EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h -EventChartOverlayValidate.h +EventChartTwoOverlayValidate.h EventDataFileAdd.h EventDataFileDelete.h EventDataFileRead.h EventDataFileReload.h +EventDataFileReloadAll.h EventGetBrainOpenGLTextRenderer.h EventIdentificationHighlightLocation.h EventModelAdd.h @@ -103,24 +108,36 @@ EventSpacerTabGet.h EventSpecFileReadDataFiles.h EventSurfacesGet.h +EventUserInputModeGet.h FeatureColoringTypeEnum.h FiberOrientationSamplesLoader.h FiberOrientationSamplesVector.h FiberOrientationSymbolTypeEnum.h +FociDrawingProjectionTypeEnum.h FociDrawingTypeEnum.h FtglFontTextRenderer.h GapsAndMargins.h +IdentificationFilter.h +IdentificationFilterTabSelectionEnum.h +IdentificationFormattedTextGenerator.h +IdentificationHistoryManager.h +IdentificationHistoryRecord.h IdentificationManager.h +IdentificationSimpleTextGenerator.h IdentificationStringBuilder.h -IdentificationTextGenerator.h +IdentificationSymbolSizeTypeEnum.h IdentificationWithColor.h IdentifiedItem.h IdentifiedItemNode.h IdentifiedItemVoxel.h ImageDepthPositionEnum.h +MediaOverlay.h +MediaOverlaySet.h +MediaOverlaySetArray.h Model.h ModelChart.h ModelChartTwo.h +ModelMedia.h ModelSurface.h ModelSurfaceMontage.h ModelSurfaceSelector.h @@ -146,6 +163,8 @@ SelectionItemChartTimeSeries.h SelectionItemChartTwoHistogram.h SelectionItemChartTwoLabel.h +SelectionItemChartTwoLineLayer.h +SelectionItemChartTwoLineLayerVerticalNearest.h SelectionItemChartTwoLineSeries.h SelectionItemChartTwoMatrix.h SelectionItemCiftiConnectivityMatrixRowColumn.h @@ -177,6 +196,7 @@ UserInputModeEnum.h ViewingTransformations.h ViewingTransformationsCerebellum.h +ViewingTransformationsMedia.h ViewingTransformationsVolume.h VolumeSliceDrawingTypeEnum.h VolumeSliceInterpolationEdgeEffectsMaskingEnum.h @@ -199,6 +219,7 @@ BrainOpenGLChartDrawingFixedPipeline.cxx BrainOpenGLChartTwoDrawingFixedPipeline.cxx BrainOpenGLFixedPipeline.cxx +BrainOpenGLMediaDrawing.cxx BrainOpenGLPrimitiveDrawing.cxx BrainOpenGLShape.cxx BrainOpenGLShapeCone.cxx @@ -217,6 +238,7 @@ BrowserTabContent.cxx BrowserWindowContent.cxx ChartTwoOverlay.cxx +ChartTwoOverlayActiveModeEnum.cxx ChartTwoOverlaySet.cxx ChartTwoOverlaySetArray.cxx ChartingDataManager.cxx @@ -237,19 +259,22 @@ DisplayPropertyDataBoolean.cxx DisplayPropertyDataFloat.cxx DummyFontTextRenderer.cxx -EventAnnotationColorBarGet.cxx +EventAnnotationBarsGet.cxx EventBrainReset.cxx EventBrainStructureGetAll.cxx EventBrowserTabGet.cxx EventBrowserTabGetAll.cxx EventBrowserTabGetAllViewed.cxx +EventBrowserTabReopenAvailable.cxx EventBrowserWindowContent.cxx +EventBrowserWindowGetTabs.cxx EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx -EventChartOverlayValidate.cxx +EventChartTwoOverlayValidate.cxx EventDataFileAdd.cxx EventDataFileDelete.cxx EventDataFileRead.cxx EventDataFileReload.cxx +EventDataFileReloadAll.cxx EventGetBrainOpenGLTextRenderer.cxx EventIdentificationHighlightLocation.cxx EventModelAdd.cxx @@ -264,23 +289,35 @@ EventSpacerTabGet.cxx EventSpecFileReadDataFiles.cxx EventSurfacesGet.cxx +EventUserInputModeGet.cxx FeatureColoringTypeEnum.cxx FiberOrientationSamplesLoader.cxx FiberOrientationSymbolTypeEnum.cxx +FociDrawingProjectionTypeEnum.cxx FociDrawingTypeEnum.cxx FtglFontTextRenderer.cxx GapsAndMargins.cxx +IdentificationFilter.cxx +IdentificationFilterTabSelectionEnum.cxx +IdentificationFormattedTextGenerator.cxx +IdentificationHistoryManager.cxx +IdentificationHistoryRecord.cxx IdentificationManager.cxx +IdentificationSimpleTextGenerator.cxx IdentificationStringBuilder.cxx -IdentificationTextGenerator.cxx +IdentificationSymbolSizeTypeEnum.cxx IdentificationWithColor.cxx IdentifiedItem.cxx IdentifiedItemNode.cxx IdentifiedItemVoxel.cxx ImageDepthPositionEnum.cxx +MediaOverlay.cxx +MediaOverlaySet.cxx +MediaOverlaySetArray.cxx Model.cxx ModelChart.cxx ModelChartTwo.cxx +ModelMedia.cxx ModelSurface.cxx ModelSurfaceMontage.cxx ModelSurfaceSelector.cxx @@ -306,6 +343,8 @@ SelectionItemChartTimeSeries.cxx SelectionItemChartTwoHistogram.cxx SelectionItemChartTwoLabel.cxx +SelectionItemChartTwoLineLayer.cxx +SelectionItemChartTwoLineLayerVerticalNearest.cxx SelectionItemChartTwoLineSeries.cxx SelectionItemChartTwoMatrix.cxx SelectionItemCiftiConnectivityMatrixRowColumn.cxx @@ -337,6 +376,7 @@ UserInputModeEnum.cxx ViewingTransformations.cxx ViewingTransformationsCerebellum.cxx +ViewingTransformationsMedia.cxx ViewingTransformationsVolume.cxx VolumeSliceDrawingTypeEnum.cxx VolumeSliceInterpolationEdgeEffectsMaskingEnum.cxx diff -Nru connectome-workbench-1.4.2/src/Brain/DataToolTipsManager.cxx connectome-workbench-1.5.0/src/Brain/DataToolTipsManager.cxx --- connectome-workbench-1.4.2/src/Brain/DataToolTipsManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DataToolTipsManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -26,14 +26,17 @@ #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" +#include "CaretPreferences.h" #include "EventManager.h" +#include "IdentificationFormattedTextGenerator.h" #include "IdentificationStringBuilder.h" -#include "IdentificationTextGenerator.h" +#include "IdentificationSimpleTextGenerator.h" #include "Overlay.h" #include "OverlaySet.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SelectionManager.h" +#include "SessionManager.h" using namespace caret; @@ -85,11 +88,47 @@ CaretAssert(browserTab); CaretAssert(selectionManager); - IdentificationTextGenerator itg; - const AString text = itg.createToolTipText(brain, - browserTab, - selectionManager, - this); + bool showOldToolTip(false); + bool showNewToolTip(false); + switch (SessionManager::get()->getCaretPreferences()->getIdentificationDisplayMode()) { + case IdentificationDisplayModeEnum::DEBUG_MODE: + showOldToolTip = true; + showNewToolTip = true; + break; + case IdentificationDisplayModeEnum::DIALOG: + showNewToolTip = true; + break; + case IdentificationDisplayModeEnum::LEGACY_DIALOG: + showOldToolTip = true; + break; + case IdentificationDisplayModeEnum::OVERLAY_TOOLBOX: + showNewToolTip = true; + break; + } + + AString text; + + if (showNewToolTip) { + IdentificationFormattedTextGenerator ftg; + text = ftg.createToolTipText(brain, + browserTab, + selectionManager, + this); + } + + if (showOldToolTip) { + IdentificationSimpleTextGenerator itg; + const AString oldText = itg.createToolTipText(brain, + browserTab, + selectionManager, + this); + if ( ! oldText.isEmpty()) { + if ( ! text.isEmpty()) { + text.append("\n\n"); + } + text.append(oldText); + } + } return text; } diff -Nru connectome-workbench-1.4.2/src/Brain/DisplayPropertiesBorders.cxx connectome-workbench-1.5.0/src/Brain/DisplayPropertiesBorders.cxx --- connectome-workbench-1.4.2/src/Brain/DisplayPropertiesBorders.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DisplayPropertiesBorders.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -208,6 +208,7 @@ this->setDisplayGroupForTab(targetTabIndex, displayGroup); this->m_contralateralDisplayStatusInTab[targetTabIndex] = this->m_contralateralDisplayStatusInTab[sourceTabIndex]; + this->m_coloringTypeInTab[targetTabIndex] = this->m_coloringTypeInTab[sourceTabIndex]; this->m_displayStatusInTab[targetTabIndex] = this->m_displayStatusInTab[sourceTabIndex]; this->m_drawingTypeInTab[targetTabIndex] = this->m_drawingTypeInTab[sourceTabIndex]; this->m_lineWidthInTab[targetTabIndex] = this->m_lineWidthInTab[sourceTabIndex]; diff -Nru connectome-workbench-1.4.2/src/Brain/DisplayPropertiesFiberOrientation.cxx connectome-workbench-1.5.0/src/Brain/DisplayPropertiesFiberOrientation.cxx --- connectome-workbench-1.4.2/src/Brain/DisplayPropertiesFiberOrientation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DisplayPropertiesFiberOrientation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,7 +23,10 @@ #include "DisplayPropertiesFiberOrientation.h" #undef __DISPLAY_PROPERTIES_FIBER_ORIENTATION_DECLARE__ +#include "Brain.h" #include "CaretAssert.h" +#include "CiftiFiberOrientationFile.h" +#include "DisplayPropertyDataFloat.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -47,10 +50,15 @@ /** * Constructor. + * @param brain + * The brain. */ -DisplayPropertiesFiberOrientation::DisplayPropertiesFiberOrientation() -: DisplayProperties() +DisplayPropertiesFiberOrientation::DisplayPropertiesFiberOrientation(const Brain* brain) +: DisplayProperties(), +m_brain(brain) { + CaretAssert(m_brain); + const float aboveLimit = 0.63; const float belowLimit = -0.63; const float minimumMagnitude = 0.05; @@ -88,6 +96,11 @@ m_displaySphereOrientationsInDisplayGroup[i] = displaySphereOrientions; } + m_maximumUncertainty.reset(new DisplayPropertyDataFloat(s_defaultMaximumUncertainty)); + m_sceneAssistant->add("m_maximumUncertainty", + "DisplayPropertyDataFloat", + m_maximumUncertainty.get()); + m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", m_displayGroup); m_sceneAssistant->addTabIndexedBooleanArray("m_displayStatusInTab", @@ -170,6 +183,7 @@ void DisplayPropertiesFiberOrientation::reset() { + m_maximumUncertainty->setAllValues(s_defaultMaximumUncertainty); } /** @@ -203,6 +217,7 @@ m_lengthMultiplierInTab[targetTabIndex] = m_lengthMultiplierInTab[sourceTabIndex]; m_fiberColoringTypeInTab[targetTabIndex] = m_fiberColoringTypeInTab[sourceTabIndex]; m_fanMultiplierInTab[targetTabIndex] = m_fanMultiplierInTab[sourceTabIndex]; + m_maximumUncertainty->copyValues(sourceTabIndex, targetTabIndex); } /** @@ -565,6 +580,40 @@ } /** + * @return The maximum uncertainty + * @param displayGroup + * The display group. + * @param tabIndex + * Index of browser tab. + */ +float +DisplayPropertiesFiberOrientation::getMaximumUncertainty(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const +{ + return m_maximumUncertainty->getValue(displayGroup, + tabIndex); +} + +/** + * Set the maximum uncertainty to the given value. + * @param displayGroup + * The display group. + * @param tabIndex + * Index of browser tab. + * @param maximumUncertainty + * New value for maximum uncertainty. + */ +void +DisplayPropertiesFiberOrientation::setMaximumUncertainty(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const float maximumUncertainty) +{ + m_maximumUncertainty->setValue(displayGroup, + tabIndex, + maximumUncertainty); +} + +/** * @return The length multiplier. * @param displayGroup * The display group. @@ -773,7 +822,9 @@ break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; - } + } + + setMaximumUncertaintyFromFiles(); } /** @@ -827,3 +878,27 @@ } } +/** + * Set the fiber's maximum uncertainty to maximum found in all fiber orientation files + */ +void +DisplayPropertiesFiberOrientation::setMaximumUncertaintyFromFiles() +{ + float maxUncert(s_defaultMaximumUncertainty); + + std::vector fiberFiles; + m_brain->getConnectivityFiberOrientationFiles(fiberFiles); + + if ( ! fiberFiles.empty()) { + maxUncert = 0.0; + for (const auto& ff : fiberFiles) { + const float u = ff->getMaximumVariance(); + if (u > maxUncert) { + maxUncert = u; + } + } + } + + m_maximumUncertainty->setAllValues(maxUncert); +} + diff -Nru connectome-workbench-1.4.2/src/Brain/DisplayPropertiesFiberOrientation.h connectome-workbench-1.5.0/src/Brain/DisplayPropertiesFiberOrientation.h --- connectome-workbench-1.4.2/src/Brain/DisplayPropertiesFiberOrientation.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DisplayPropertiesFiberOrientation.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "BrainConstants.h" #include "DisplayGroupEnum.h" #include "DisplayProperties.h" @@ -29,10 +31,14 @@ namespace caret { + class Brain; + + class DisplayPropertyDataFloat; + class DisplayPropertiesFiberOrientation : public DisplayProperties { public: - DisplayPropertiesFiberOrientation(); + DisplayPropertiesFiberOrientation(const Brain* brain); virtual ~DisplayPropertiesFiberOrientation(); @@ -40,6 +46,8 @@ virtual void update(); + void setMaximumUncertaintyFromFiles(); + virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); @@ -86,6 +94,13 @@ const int32_t tabIndex, const float minimumMagnitude); + float getMaximumUncertainty(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const; + + void setMaximumUncertainty(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const float maximumUncertainty); + float getLengthMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; @@ -131,6 +146,8 @@ DisplayPropertiesFiberOrientation& operator=(const DisplayPropertiesFiberOrientation&); + const Brain* m_brain; + DisplayGroupEnum::Enum m_displayGroup[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_displayStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; @@ -173,7 +190,9 @@ bool m_displaySphereOrientationsInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; -// friend class BrainOpenGLFixedPipeline; + std::unique_ptr m_maximumUncertainty; + + static constexpr float s_defaultMaximumUncertainty = 10.0f; }; #ifdef __DISPLAY_PROPERTIES_FIBER_ORIENTATION_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/Brain/DisplayPropertiesFoci.cxx connectome-workbench-1.5.0/src/Brain/DisplayPropertiesFoci.cxx --- connectome-workbench-1.4.2/src/Brain/DisplayPropertiesFoci.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DisplayPropertiesFoci.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -24,6 +24,7 @@ #undef __DISPLAY_PROPERTIES_FOCI_DECLARE__ #include "CaretAssert.h" +#include "DisplayPropertyDataFloat.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -44,29 +45,15 @@ : DisplayProperties() { const CaretColorEnum::Enum defaultColor = CaretColorEnum::BLACK; - - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); - m_pasteOntoSurfaceInTab[i] = false; - m_displayStatusInTab[i] = false; - m_contralateralDisplayStatusInTab[i] = false; - m_fociSizeInTab[i] = 4.0; - m_coloringTypeInTab[i] = FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME; - m_drawingTypeInTab[i] = FociDrawingTypeEnum::DRAW_AS_SQUARES; - m_standardColorTypeInTab[i] = defaultColor; - } - for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { - m_pasteOntoSurfaceInDisplayGroup[i] = false; - m_displayStatusInDisplayGroup[i] = false; - m_contralateralDisplayStatusInDisplayGroup[i] = false; - m_fociSizeInDisplayGroup[i] = 4.0; - m_coloringTypeInDisplayGroup[i] = FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME; - m_drawingTypeInDisplayGroup[i] = FociDrawingTypeEnum::DRAW_AS_SQUARES; - m_standardColorTypeInDisplayGroup[i] = defaultColor; - } + m_fociSizePercentage.reset(new DisplayPropertyDataFloat(4.0)); + m_fociSymbolSizeType.initialize(IdentificationSymbolSizeTypeEnum::MILLIMETERS); + resetPrivate(); - m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", + m_sceneAssistant->add("m_fociSizePercentage", "DisplayPropertyDataFloat", m_fociSizePercentage.get()); + m_sceneAssistant->add("m_fociSymbolSizeType", "DisplayPropertyDataEnum", &m_fociSymbolSizeType); + + m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", m_displayGroup); m_sceneAssistant->addTabIndexedBooleanArray("m_pasteOntoSurfaceInTab", m_pasteOntoSurfaceInTab); @@ -80,6 +67,8 @@ m_coloringTypeInTab); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_drawingTypeInTab", m_drawingTypeInTab); + m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_drawingProjectionTypeInTab", + m_drawingProjectionTypeInTab); m_sceneAssistant->addArray("m_pasteOntoSurfaceInDisplayGroup", m_pasteOntoSurfaceInDisplayGroup, @@ -106,6 +95,10 @@ m_drawingTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, FociDrawingTypeEnum::DRAW_AS_SQUARES); + m_sceneAssistant->addArray("m_drawingProjectionTypeInDisplayGroup", + m_drawingProjectionTypeInDisplayGroup, + DisplayGroupEnum::NUMBER_OF_GROUPS, + FociDrawingProjectionTypeEnum::PROJECTED); m_sceneAssistant->addArray("m_standardColorTypeInDisplayGroup", m_standardColorTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, @@ -141,6 +134,7 @@ m_contralateralDisplayStatusInTab[targetTabIndex] = m_contralateralDisplayStatusInTab[sourceTabIndex]; m_displayStatusInTab[targetTabIndex] = m_displayStatusInTab[sourceTabIndex]; m_drawingTypeInTab[targetTabIndex] = m_drawingTypeInTab[sourceTabIndex]; + m_drawingProjectionTypeInTab[targetTabIndex] = m_drawingProjectionTypeInTab[sourceTabIndex]; m_fociSizeInTab[targetTabIndex] = m_fociSizeInTab[sourceTabIndex]; m_pasteOntoSurfaceInTab[targetTabIndex] = m_pasteOntoSurfaceInTab[sourceTabIndex]; m_standardColorTypeInTab[targetTabIndex] = m_standardColorTypeInTab[sourceTabIndex]; @@ -153,12 +147,39 @@ void DisplayPropertiesFoci::reset() { -// for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { -// m_displayStatus[i] = true; -// m_contralateralDisplayStatus[i] = false; -// m_displayGroup[i] = DisplayGroupEnum::DISPLAY_ALL_WINDOWS; -// m_pasteOntoSurface[i] = false; -// } + resetPrivate(); +} + +void +DisplayPropertiesFoci::resetPrivate() +{ + const CaretColorEnum::Enum defaultColor = CaretColorEnum::BLACK; + + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); + m_pasteOntoSurfaceInTab[i] = false; + m_displayStatusInTab[i] = false; + m_contralateralDisplayStatusInTab[i] = false; + m_fociSizeInTab[i] = 4.0; + m_coloringTypeInTab[i] = FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME; + m_drawingTypeInTab[i] = FociDrawingTypeEnum::DRAW_AS_SQUARES; + m_drawingProjectionTypeInTab[i] = FociDrawingProjectionTypeEnum::PROJECTED; + m_standardColorTypeInTab[i] = defaultColor; + } + + for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { + m_pasteOntoSurfaceInDisplayGroup[i] = false; + m_displayStatusInDisplayGroup[i] = false; + m_contralateralDisplayStatusInDisplayGroup[i] = false; + m_fociSizeInDisplayGroup[i] = 4.0; + m_coloringTypeInDisplayGroup[i] = FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME; + m_drawingTypeInDisplayGroup[i] = FociDrawingTypeEnum::DRAW_AS_SQUARES; + m_drawingProjectionTypeInDisplayGroup[i] = FociDrawingProjectionTypeEnum::PROJECTED; + m_standardColorTypeInDisplayGroup[i] = defaultColor; + } + + m_fociSizePercentage->setAllValues(4.0); + m_fociSymbolSizeType.setAllValues(IdentificationSymbolSizeTypeEnum::MILLIMETERS); } /** @@ -298,12 +319,48 @@ } /** + * @return Size type for foci symbol + * @param displayGroup + * Display group. + * @param tabIndex + * Index of browser tab. + */ +IdentificationSymbolSizeTypeEnum::Enum +DisplayPropertiesFoci::getFociSymbolSizeType(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const +{ + return m_fociSymbolSizeType.getValue(displayGroup, + tabIndex); +} + +/** + * Set the foci size to the given value. + * @param displayGroup + * Display group. + * @param tabIndex + * Index of browser tab. + * @param sizeType + * Size type for foci symbol. + */ +void +DisplayPropertiesFoci::setFociSymbolSizeType(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const IdentificationSymbolSizeTypeEnum::Enum sizeType) +{ + m_fociSymbolSizeType.setValue(displayGroup, + tabIndex, + sizeType); +} + +/** * @return The foci size. * @param displayGroup * Display group. + * @param tabIndex + * Index of the tab */ float -DisplayPropertiesFoci::getFociSize(const DisplayGroupEnum::Enum displayGroup, +DisplayPropertiesFoci::getFociSizeMillimeters(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_fociSizeInDisplayGroup, @@ -322,11 +379,13 @@ * Set the foci size to the given value. * @param displayGroup * Display group. + * @param tabIndex + * Index of the tab * @param fociSize * New value for foci size. */ void -DisplayPropertiesFoci::setFociSize(const DisplayGroupEnum::Enum displayGroup, +DisplayPropertiesFoci::setFociSizeMillimeters(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float fociSize) { @@ -345,6 +404,40 @@ } /** + * @return The foci size. + * @param displayGroup + * Display group. + * @param tabIndex + * Index of the tab + */ +float +DisplayPropertiesFoci::getFociSizePercentage(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const +{ + return m_fociSizePercentage->getValue(displayGroup, + tabIndex); +} + +/** + * Set the foci size to the given value. + * @param displayGroup + * Display group. + * @param tabIndex + * Index of the tab + * @param fociSize + * New value for foci size. + */ +void +DisplayPropertiesFoci::setFociSizePercentage(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const float pointSize) +{ + m_fociSizePercentage->setValue(displayGroup, + tabIndex, + pointSize); +} + +/** * @return The coloring type. * @param displayGroup * Display group. @@ -486,6 +579,53 @@ } /** + * @param displayGroup + * Display group. + * @return The drawing type. + */ +FociDrawingProjectionTypeEnum::Enum +DisplayPropertiesFoci::getDrawingProjectionType(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const +{ + CaretAssertArrayIndex(m_drawingProjectionTypeInDisplayGroup, + DisplayGroupEnum::NUMBER_OF_GROUPS, + static_cast(displayGroup)); + if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { + CaretAssertArrayIndex(m_drawingProjectionTypeInTab, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + return m_drawingProjectionTypeInTab[tabIndex]; + } + return m_drawingProjectionTypeInDisplayGroup[displayGroup]; +} + +/** + * Set the drawing type to the given value. + * @param displayGroup + * Display group. + * @param drawingType + * New value for drawing type. + */ +void +DisplayPropertiesFoci::setDrawingProjectionType(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const FociDrawingProjectionTypeEnum::Enum drawingType) +{ + CaretAssertArrayIndex(m_drawingProjectionTypeInDisplayGroup, + DisplayGroupEnum::NUMBER_OF_GROUPS, + static_cast(displayGroup)); + if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { + CaretAssertArrayIndex(m_drawingProjectionTypeInTab, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + m_drawingProjectionTypeInTab[tabIndex] = drawingType; + } + else { + m_drawingProjectionTypeInDisplayGroup[displayGroup] = drawingType; + } +} + +/** * Set paste onto surface so the foci are placed directly on the surface. * @param displayGroup * Display group. @@ -586,6 +726,7 @@ if (sceneClass == NULL) { return; } + resetPrivate(); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); @@ -596,6 +737,5 @@ case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } - } diff -Nru connectome-workbench-1.4.2/src/Brain/DisplayPropertiesFoci.h connectome-workbench-1.5.0/src/Brain/DisplayPropertiesFoci.h --- connectome-workbench-1.4.2/src/Brain/DisplayPropertiesFoci.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DisplayPropertiesFoci.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,15 +21,21 @@ */ /*LICENSE_END*/ +#include + #include "BrainConstants.h" #include "CaretColorEnum.h" #include "DisplayGroupEnum.h" #include "DisplayProperties.h" +#include "DisplayPropertyDataEnum.h" #include "FeatureColoringTypeEnum.h" +#include "FociDrawingProjectionTypeEnum.h" #include "FociDrawingTypeEnum.h" +#include "IdentificationSymbolSizeTypeEnum.h" namespace caret { - + class DisplayPropertyDataFloat; + class DisplayPropertiesFoci : public DisplayProperties { public: @@ -63,12 +69,26 @@ void setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup); - float getFociSize(const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex) const; + IdentificationSymbolSizeTypeEnum::Enum getFociSymbolSizeType(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const; + + void setFociSymbolSizeType(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const IdentificationSymbolSizeTypeEnum::Enum sizeType); + + float getFociSizeMillimeters(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const; - void setFociSize(const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex, - const float pointSize); + void setFociSizeMillimeters(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const float pointSize); + + float getFociSizePercentage(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const; + + void setFociSizePercentage(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const float pointSize); FeatureColoringTypeEnum::Enum getColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; @@ -84,6 +104,13 @@ const int32_t tabIndex, const FociDrawingTypeEnum::Enum drawingType); + FociDrawingProjectionTypeEnum::Enum getDrawingProjectionType(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const; + + void setDrawingProjectionType(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const FociDrawingProjectionTypeEnum::Enum drawingProjectionType); + CaretColorEnum::Enum getStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; @@ -105,6 +132,8 @@ const SceneClass* sceneClass); private: + void resetPrivate(); + DisplayPropertiesFoci(const DisplayPropertiesFoci&); DisplayPropertiesFoci& operator=(const DisplayPropertiesFoci&); @@ -123,6 +152,10 @@ bool m_pasteOntoSurfaceInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + DisplayPropertyDataEnum m_fociSymbolSizeType; + + std::unique_ptr m_fociSizePercentage; + float m_fociSizeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_fociSizeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; @@ -135,6 +168,10 @@ FociDrawingTypeEnum::Enum m_drawingTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + FociDrawingProjectionTypeEnum::Enum m_drawingProjectionTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; + + FociDrawingProjectionTypeEnum::Enum m_drawingProjectionTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + CaretColorEnum::Enum m_standardColorTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; CaretColorEnum::Enum m_standardColorTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; diff -Nru connectome-workbench-1.4.2/src/Brain/DisplayPropertiesSurface.cxx connectome-workbench-1.5.0/src/Brain/DisplayPropertiesSurface.cxx --- connectome-workbench-1.4.2/src/Brain/DisplayPropertiesSurface.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DisplayPropertiesSurface.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -48,6 +48,7 @@ m_nodeSize = 2.0; m_surfaceDrawingType = SurfaceDrawingTypeEnum::DRAW_AS_TRIANGLES; m_opacity = 1.0; + resetDefaultColorRGB(); m_sceneAssistant->add("m_displayNormalVectors", &m_displayNormalVectors); @@ -59,6 +60,10 @@ &m_opacity); m_sceneAssistant->add("m_surfaceDrawingType", &m_surfaceDrawingType); + m_sceneAssistant->addArray("m_defaultColorRGB", + &m_defaultColorRGB[0], + m_defaultColorRGB.size(), + 178); } @@ -149,6 +154,8 @@ DisplayPropertiesSurface::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { + resetDefaultColorRGB(); + if (sceneClass == NULL) { return; } @@ -270,4 +277,53 @@ m_opacity = opacity; } +/** + * Set the default color used when no overlays color vertices + * @param defaultColorRGB + * The new default color + */ +void +DisplayPropertiesSurface::setDefaultColorRGB(const std::array& defaultColorRGB) +{ + m_defaultColorRGB = defaultColorRGB; +} + +/** + * @return The default color used when no overlays color vertices + */ +std::array +DisplayPropertiesSurface::getDefaultColorRGB() const +{ + return m_defaultColorRGB; +} + +/** + * Reset the default surface color + */ +void +DisplayPropertiesSurface::resetDefaultColorRGB() +{ + m_defaultColorRGB.fill(178); +} + +/** + * @return True if backface culling is enabled + */ +bool +DisplayPropertiesSurface::isBackfaceCullingEnabled() const +{ + return m_backfaceCullingEnabled; +} + +/** + * Set backface culling enabled + * @param enabled + * New status + */ +void +DisplayPropertiesSurface::setBackfaceCullingEnabled(const bool enabled) +{ + m_backfaceCullingEnabled = enabled; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/DisplayPropertiesSurface.h connectome-workbench-1.5.0/src/Brain/DisplayPropertiesSurface.h --- connectome-workbench-1.4.2/src/Brain/DisplayPropertiesSurface.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DisplayPropertiesSurface.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "DisplayProperties.h" #include "SurfaceDrawingTypeEnum.h" @@ -68,6 +70,16 @@ void setOpacity(const float opacity); + void setDefaultColorRGB(const std::array& defaultColorRGB); + + std::array getDefaultColorRGB() const; + + void resetDefaultColorRGB(); + + bool isBackfaceCullingEnabled() const; + + void setBackfaceCullingEnabled(const bool enabled); + private: DisplayPropertiesSurface(const DisplayPropertiesSurface&); @@ -82,6 +94,10 @@ SurfaceDrawingTypeEnum::Enum m_surfaceDrawingType; float m_opacity; + + std::array m_defaultColorRGB; + + bool m_backfaceCullingEnabled = false; }; #ifdef __DISPLAY_PROPERTIES_SURFACE_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/Brain/DisplayPropertyDataEnum.h connectome-workbench-1.5.0/src/Brain/DisplayPropertyDataEnum.h --- connectome-workbench-1.4.2/src/Brain/DisplayPropertyDataEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/DisplayPropertyDataEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -24,6 +24,7 @@ #include "BrainConstants.h" #include "CaretAssert.h" #include "CaretObject.h" +#include "CaretPointer.h" #include "DisplayGroupEnum.h" #include "SceneAttributes.h" #include "SceneClass.h" diff -Nru connectome-workbench-1.4.2/src/Brain/EventAnnotationBarsGet.cxx connectome-workbench-1.5.0/src/Brain/EventAnnotationBarsGet.cxx --- connectome-workbench-1.4.2/src/Brain/EventAnnotationBarsGet.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventAnnotationBarsGet.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,148 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_ANNOTATION_BARS_GET_DECLARE__ +#include "EventAnnotationBarsGet.h" +#undef __EVENT_ANNOTATION_BARS_GET_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventAnnotationBarsGet + * \brief Event to get annontation color bars for tab(s). + * \ingroup Brain + */ + +/** + * Constructor for getting annotation bars for ALL tabs. + */ +EventAnnotationBarsGet::EventAnnotationBarsGet() +: Event(EventTypeEnum::EVENT_ANNOTATION_BARS_GET), +m_allTabsFlag(true) +{ + +} + +/** + * Constructor for getting annotation bars for the given tab index. + * + * @param tabIndex + * Index of tab for which annotation bars are requested. + */ +EventAnnotationBarsGet::EventAnnotationBarsGet(const int32_t tabIndex) +: Event(EventTypeEnum::EVENT_ANNOTATION_BARS_GET), +m_allTabsFlag(false) +{ + m_tabIndices.insert(tabIndex); +} + +/** + * Constructor for getting annotation bars for the given tab indices. + * + * @param tabIndices + * Indices of tabs for which annotation bars are requested. If + * the indices are empty no bars will be gotten. + */ +EventAnnotationBarsGet::EventAnnotationBarsGet(const std::vector& tabIndices) +: Event(EventTypeEnum::EVENT_ANNOTATION_BARS_GET), +m_allTabsFlag(false) +{ + m_tabIndices.insert(tabIndices.begin(), + tabIndices.end()); +} + +/** + * Destructor. + */ +EventAnnotationBarsGet::~EventAnnotationBarsGet() +{ +} + +/** + * Add annotation color bars. + * + * @param annotationColorBars + * Annotation color bars that are added. + */ +void +EventAnnotationBarsGet::addAnnotationColorBars(const std::vector& annotationColorBars) +{ + m_annotationColorBars.insert(m_annotationColorBars.end(), + annotationColorBars.begin(), + annotationColorBars.end()); +} + +/** + * Add annotation scale bars. + * + * @param annotationScaleBar + * Annotation scale bar that is added. + */ +void +EventAnnotationBarsGet::addAnnotationScaleBar(AnnotationScaleBar* annotationScaleBar) +{ + m_annotationScaleBars.push_back(annotationScaleBar); +} + + +/** + * Are annotation bars requested for the given tab index. + * + * @param tabIndex + * Index of tab. + * @return + * True if annotation bars are requested for the given tab index, else false. + */ +bool +EventAnnotationBarsGet::isGetAnnotationColorBarsForTabIndex(const int32_t tabIndex) +{ + if (m_allTabsFlag) { + return true; + } + else if (m_tabIndices.find(tabIndex) != m_tabIndices.end()) { + return true; + } + + return false; +} + +/** + * @return The annotation color bars after event completes. + */ +std::vector +EventAnnotationBarsGet::getAnnotationColorBars() const +{ + return m_annotationColorBars; +} + +/** + * @return The annotation scale bars after event completes. + */ +std::vector +EventAnnotationBarsGet::getAnnotationScaleBars() const +{ + return m_annotationScaleBars; +} diff -Nru connectome-workbench-1.4.2/src/Brain/EventAnnotationBarsGet.h connectome-workbench-1.5.0/src/Brain/EventAnnotationBarsGet.h --- connectome-workbench-1.4.2/src/Brain/EventAnnotationBarsGet.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventAnnotationBarsGet.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,79 @@ +#ifndef __EVENT_ANNOTATION_BARS_GET_H__ +#define __EVENT_ANNOTATION_BARS_GET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "Event.h" + + + +namespace caret { + class AnnotationColorBar; + class AnnotationScaleBar; + + class EventAnnotationBarsGet : public Event { + + public: + EventAnnotationBarsGet(); + + EventAnnotationBarsGet(const int32_t tabIndex); + + EventAnnotationBarsGet(const std::vector& tabIndices); + + virtual ~EventAnnotationBarsGet(); + + void addAnnotationColorBars(const std::vector& colorBars); + + void addAnnotationScaleBar(AnnotationScaleBar* scaleBar); + + bool isGetAnnotationColorBarsForTabIndex(const int32_t tabIndex); + + std::vector getAnnotationColorBars() const; + + std::vector getAnnotationScaleBars() const; + + // ADD_NEW_METHODS_HERE + + private: + EventAnnotationBarsGet(const EventAnnotationBarsGet&); + + EventAnnotationBarsGet& operator=(const EventAnnotationBarsGet&); + + bool m_allTabsFlag; + + std::set m_tabIndices; + + std::vector m_annotationColorBars; + + std::vector m_annotationScaleBars; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_ANNOTATION_BARS_GET_DECLARE__ + // +#endif // __EVENT_ANNOTATION_BARS_GET_DECLARE__ + +} // namespace +#endif //__EVENT_ANNOTATION_BARS_GET_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/EventAnnotationColorBarGet.cxx connectome-workbench-1.5.0/src/Brain/EventAnnotationColorBarGet.cxx --- connectome-workbench-1.4.2/src/Brain/EventAnnotationColorBarGet.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventAnnotationColorBarGet.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,127 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ -#include "EventAnnotationColorBarGet.h" -#undef __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ - -#include "CaretAssert.h" -#include "EventTypeEnum.h" - -using namespace caret; - - - -/** - * \class caret::EventAnnotationColorBarGet - * \brief Event to get annontation color bars for tab(s). - * \ingroup Brain - */ - -/** - * Constructor for getting annotations for ALL tabs. - */ -EventAnnotationColorBarGet::EventAnnotationColorBarGet() -: Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), -m_allTabsFlag(true) -{ - -} - -/** - * Constructor for getting annotations for the given tab index. - * - * @param tabIndex - * Index of tab for which color bars are requested. - */ -EventAnnotationColorBarGet::EventAnnotationColorBarGet(const int32_t tabIndex) -: Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), -m_allTabsFlag(false) -{ - m_tabIndices.insert(tabIndex); -} - -/** - * Constructor for getting annotations for the given tab indices. - * - * @param tabIndices - * Indices of tabs for which color bars are requested. If - * the indices are empty no colorbars will be gotten. - */ -EventAnnotationColorBarGet::EventAnnotationColorBarGet(const std::vector& tabIndices) -: Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), -m_allTabsFlag(false) -{ - m_tabIndices.insert(tabIndices.begin(), - tabIndices.end()); -} - -/** - * Destructor. - */ -EventAnnotationColorBarGet::~EventAnnotationColorBarGet() -{ -} - -/** - * Add annotation color bars. - * - * @param annotationColorBars - * Annotation color bars that are added. - */ -void -EventAnnotationColorBarGet::addAnnotationColorBars(const std::vector& annotationColorBars) -{ - m_annotationColorBars.insert(m_annotationColorBars.end(), - annotationColorBars.begin(), - annotationColorBars.end()); -} - -/** - * Are annotation color bars requested for the given tab index. - * - * @param tabIndex - * Index of tab. - * @return - * True if annotations are requested for the given tab index, else false. - */ -bool -EventAnnotationColorBarGet::isGetAnnotationColorBarsForTabIndex(const int32_t tabIndex) -{ - if (m_allTabsFlag) { - return true; - } - else if (m_tabIndices.find(tabIndex) != m_tabIndices.end()) { - return true; - } - - return false; -} - -/** - * @return The annotation color bars after event completes. - */ -std::vector -EventAnnotationColorBarGet::getAnnotationColorBars() const -{ - return m_annotationColorBars; -} - diff -Nru connectome-workbench-1.4.2/src/Brain/EventAnnotationColorBarGet.h connectome-workbench-1.5.0/src/Brain/EventAnnotationColorBarGet.h --- connectome-workbench-1.4.2/src/Brain/EventAnnotationColorBarGet.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventAnnotationColorBarGet.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -#ifndef __EVENT_ANNOTATION_COLOR_BAR_GET_H__ -#define __EVENT_ANNOTATION_COLOR_BAR_GET_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -#include "Event.h" - - - -namespace caret { - class AnnotationColorBar; - - class EventAnnotationColorBarGet : public Event { - - public: - EventAnnotationColorBarGet(); - - EventAnnotationColorBarGet(const int32_t tabIndex); - - EventAnnotationColorBarGet(const std::vector& tabIndices); - - virtual ~EventAnnotationColorBarGet(); - - void addAnnotationColorBars(const std::vector& colorBars); - - bool isGetAnnotationColorBarsForTabIndex(const int32_t tabIndex); - - std::vector getAnnotationColorBars() const; - - // ADD_NEW_METHODS_HERE - - private: - EventAnnotationColorBarGet(const EventAnnotationColorBarGet&); - - EventAnnotationColorBarGet& operator=(const EventAnnotationColorBarGet&); - - bool m_allTabsFlag; - - std::set m_tabIndices; - - std::vector m_annotationColorBars; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ - // -#endif // __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ - -} // namespace -#endif //__EVENT_ANNOTATION_COLOR_BAR_GET_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/EventBrowserTabReopenAvailable.cxx connectome-workbench-1.5.0/src/Brain/EventBrowserTabReopenAvailable.cxx --- connectome-workbench-1.4.2/src/Brain/EventBrowserTabReopenAvailable.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventBrowserTabReopenAvailable.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,99 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_BROWSER_TAB_REOPEN_AVAILABLE_DECLARE__ +#include "EventBrowserTabReopenAvailable.h" +#undef __EVENT_BROWSER_TAB_REOPEN_AVAILABLE_DECLARE__ + +#include "BrainConstants.h" +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventBrowserTabReopenAvailable + * \brief Event to get browser tabs that can be reopened + * \ingroup Brain + */ + +/** + * Constructor. + */ +EventBrowserTabReopenAvailable::EventBrowserTabReopenAvailable() +: Event(EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_AVAILBLE) +{ +} + +/** + * Destructor. + */ +EventBrowserTabReopenAvailable::~EventBrowserTabReopenAvailable() +{ +} + +/** + * @return True if there is a tab available for reopening + */ +bool +EventBrowserTabReopenAvailable::isReopenValid() const +{ + return (m_tabIndex >= 0); +} + +/** + * @return True if there is a tab available for reopening + */ +int32_t +EventBrowserTabReopenAvailable::getTabIndex() const +{ + return m_tabIndex; +} + +/** + * @return True if there is a tab available for reopening + */ +AString +EventBrowserTabReopenAvailable::getTabName() const +{ + return m_tabName; +} + +/** + * Set the index and name of a tab that can be reopened + * This method is used by the receiver of the event. + * + * @param tabIndex + * Index of the tab + * @param tabName + * Name of the tab. + */ +void +EventBrowserTabReopenAvailable::setTabIndexAndName(const int32_t tabIndex, + const AString& tabName) +{ + m_tabIndex = tabIndex; + m_tabName = tabName; +} + + diff -Nru connectome-workbench-1.4.2/src/Brain/EventBrowserTabReopenAvailable.h connectome-workbench-1.5.0/src/Brain/EventBrowserTabReopenAvailable.h --- connectome-workbench-1.4.2/src/Brain/EventBrowserTabReopenAvailable.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventBrowserTabReopenAvailable.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,69 @@ +#ifndef __EVENT_BROWSER_TAB_REOPEN_AVAILABLE_H__ +#define __EVENT_BROWSER_TAB_REOPEN_AVAILABLE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class EventBrowserTabReopenAvailable : public Event { + + public: + EventBrowserTabReopenAvailable(); + + virtual ~EventBrowserTabReopenAvailable(); + + EventBrowserTabReopenAvailable(const EventBrowserTabReopenAvailable&) = delete; + + EventBrowserTabReopenAvailable& operator=(const EventBrowserTabReopenAvailable&) = delete; + + bool isReopenValid() const; + + int32_t getTabIndex() const; + + AString getTabName() const; + + void setTabIndexAndName(const int32_t tabIndex, + const AString& tabName); + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + int32_t m_tabIndex = -1; + + AString m_tabName; + }; + +#ifdef __EVENT_BROWSER_TAB_REOPEN_AVAILABLE_DECLARE__ + // +#endif // __EVENT_BROWSER_TAB_REOPEN_AVAILABLE_DECLARE__ + +} // namespace +#endif //__EVENT_BROWSER_TAB_REOPEN_AVAILABLE_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/EventBrowserWindowGetTabs.cxx connectome-workbench-1.5.0/src/Brain/EventBrowserWindowGetTabs.cxx --- connectome-workbench-1.4.2/src/Brain/EventBrowserWindowGetTabs.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventBrowserWindowGetTabs.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,104 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_BROWSER_WINDOW_GET_TABS_DECLARE__ +#include "EventBrowserWindowGetTabs.h" +#undef __EVENT_BROWSER_WINDOW_GET_TABS_DECLARE__ + +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventBrowserWindowGetTabs + * \brief Event to get browser tabs in a window + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param windowIndex + * Index of the window + */ +EventBrowserWindowGetTabs::EventBrowserWindowGetTabs(const int32_t windowIndex) +: Event(EventTypeEnum::EVENT_BROWSER_WINDOW_GET_TABS), +m_windowIndex(windowIndex) +{ + +} + +/** + * Destructor. + */ +EventBrowserWindowGetTabs::~EventBrowserWindowGetTabs() +{ +} + +/** + * @return Index of browser window + */ +int32_t +EventBrowserWindowGetTabs::getBrowserWindowIndex() const +{ + return m_windowIndex; +} + +/** + * @return Tab content in the window + */ +std::vector +EventBrowserWindowGetTabs::getBrowserTabs() const +{ + return m_browserTabs; +} + +/** + * @return Indices of the browser tabs in the window + */ +std::vector +EventBrowserWindowGetTabs::getBrowserTabIndices() const +{ + std::vector indices; + for (auto bt : m_browserTabs) { + indices.push_back(bt->getTabNumber()); + } + return indices; +} + +/** + * Add a browser tab + * + * @param browserTabContent + * Tab to add + */ +void +EventBrowserWindowGetTabs::addBrowserTab(BrowserTabContent* browserTabContent) +{ + CaretAssert(browserTabContent); + m_browserTabs.push_back(browserTabContent); +} + + diff -Nru connectome-workbench-1.4.2/src/Brain/EventBrowserWindowGetTabs.h connectome-workbench-1.5.0/src/Brain/EventBrowserWindowGetTabs.h --- connectome-workbench-1.4.2/src/Brain/EventBrowserWindowGetTabs.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventBrowserWindowGetTabs.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,71 @@ +#ifndef __EVENT_BROWSER_WINDOW_GET_TABS_H__ +#define __EVENT_BROWSER_WINDOW_GET_TABS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class BrowserTabContent; + + class EventBrowserWindowGetTabs : public Event { + + public: + EventBrowserWindowGetTabs(const int32_t windowIndex); + + virtual ~EventBrowserWindowGetTabs(); + + EventBrowserWindowGetTabs(const EventBrowserWindowGetTabs&) = delete; + + EventBrowserWindowGetTabs& operator=(const EventBrowserWindowGetTabs&) = delete; + + int32_t getBrowserWindowIndex() const; + + std::vector getBrowserTabs() const; + + std::vector getBrowserTabIndices() const; + + void addBrowserTab(BrowserTabContent* browserTabContent); + + // ADD_NEW_METHODS_HERE + + private: + const int32_t m_windowIndex; + + std::vector m_browserTabs; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_BROWSER_WINDOW_GET_TABS_DECLARE__ + // +#endif // __EVENT_BROWSER_WINDOW_GET_TABS_DECLARE__ + +} // namespace +#endif //__EVENT_BROWSER_WINDOW_GET_TABS_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx connectome-workbench-1.5.0/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx --- connectome-workbench-1.4.2/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -54,39 +54,64 @@ } /** - * Add file and map displayed in an chart overlay. + * Add file and map displayed in an chart one overlay. * * @param mapFile * File to add. * @param mapIndex * Index of selected map. + * @param tabIndex + * Index of the tab */ void -EventCaretMappableDataFilesAndMapsInDisplayedOverlays::addChartFileAndMap(CaretMappableDataFile* mapFile, - const int32_t mapIndex) +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::addChartOneFileAndMap(CaretMappableDataFile* mapFile, + const int32_t mapIndex, + const int32_t tabIndex) { - { - auto iter = m_mapFilesAndIndices.find(mapFile); - if (iter != m_mapFilesAndIndices.end()) { + if ( ! satisfiesConstraints(tabIndex)) { + return; + } + + auto iter = m_chartOneMapFilesAndIndices.find(mapFile); + if (iter != m_chartOneMapFilesAndIndices.end()) { iter->second.insert(mapIndex); } else { std::set indicesSet; indicesSet.insert(mapIndex); - m_mapFilesAndIndices.insert(std::make_pair(mapFile, - indicesSet)); + m_chartOneMapFilesAndIndices.insert(std::make_pair(mapFile, + indicesSet)); } +} + +/** + * Add file and map displayed in an chart two overlay. + * + * @param mapFile + * File to add. + * @param mapIndex + * Index of selected map. + * @param tabIndex + * Index of the tab + */ +void +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::addChartTwoFileAndMap(CaretMappableDataFile* mapFile, + const int32_t mapIndex, + const int32_t tabIndex) +{ + if ( ! satisfiesConstraints(tabIndex)) { + return; } - auto iter = m_chartMapFilesAndIndices.find(mapFile); - if (iter != m_chartMapFilesAndIndices.end()) { + auto iter = m_chartTwoMapFilesAndIndices.find(mapFile); + if (iter != m_chartTwoMapFilesAndIndices.end()) { iter->second.insert(mapIndex); } else { std::set indicesSet; indicesSet.insert(mapIndex); - m_chartMapFilesAndIndices.insert(std::make_pair(mapFile, - indicesSet)); + m_chartTwoMapFilesAndIndices.insert(std::make_pair(mapFile, + indicesSet)); } } @@ -97,33 +122,58 @@ * File to add. * @param mapIndex * Index of selected map. + * @param tabIndex + * Index of the tab */ void EventCaretMappableDataFilesAndMapsInDisplayedOverlays::addBrainordinateFileAndMap(CaretMappableDataFile* mapFile, - const int32_t mapIndex) + const int32_t mapIndex, + const int32_t tabIndex) { - { - auto iter = m_mapFilesAndIndices.find(mapFile); - if (iter != m_mapFilesAndIndices.end()) { + if ( ! satisfiesConstraints(tabIndex)) { + return; + } + + auto iter = m_surfaceVolumeMapFilesAndIndices.find(mapFile); + if (iter != m_surfaceVolumeMapFilesAndIndices.end()) { iter->second.insert(mapIndex); } else { std::set indicesSet; indicesSet.insert(mapIndex); - m_mapFilesAndIndices.insert(std::make_pair(mapFile, + m_surfaceVolumeMapFilesAndIndices.insert(std::make_pair(mapFile, indicesSet)); } +} + +/** + * Add media file and frame displayed in a media overlay. + * + * @param mediaFile + * File to add. + * @param frameIndex + * Index of selected frame. + * @param tabIndex + * Index of the tab + */ +void +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::addMediaFileAndFrame(MediaFile* mediaFile, + const int32_t frameIndex, + const int32_t tabIndex) +{ + if ( ! satisfiesConstraints(tabIndex)) { + return; } - auto iter = m_surfaceVolumeMapFilesAndIndices.find(mapFile); - if (iter != m_surfaceVolumeMapFilesAndIndices.end()) { - iter->second.insert(mapIndex); + auto iter = m_mediaFilesAndFrameIndices.find(mediaFile); + if (iter != m_mediaFilesAndFrameIndices.end()) { + iter->second.insert(frameIndex); } else { std::set indicesSet; - indicesSet.insert(mapIndex); - m_surfaceVolumeMapFilesAndIndices.insert(std::make_pair(mapFile, - indicesSet)); + indicesSet.insert(frameIndex); + m_mediaFilesAndFrameIndices.insert(std::make_pair(mediaFile, + indicesSet)); } } @@ -131,33 +181,89 @@ * @return Files and maps selected in overlays for both brainordinates * (surfaces and volumes) and charts. */ -std::vector +std::vector EventCaretMappableDataFilesAndMapsInDisplayedOverlays::getFilesAndMaps() const { - std::vector infoOut; + std::vector infoOut; for (auto iter : m_surfaceVolumeMapFilesAndIndices) { - infoOut.push_back(FileInfo(OverlayType::BRAINORDINATE, - iter.first, - iter.second)); - } - for (auto iter : m_chartMapFilesAndIndices) { - infoOut.push_back(FileInfo(OverlayType::CHART, - iter.first, - iter.second)); + infoOut.push_back(MapFileInfo(MapOverlayType::BRAINORDINATE, + iter.first, + iter.second)); + } + for (auto iter : m_chartOneMapFilesAndIndices) { + infoOut.push_back(MapFileInfo(MapOverlayType::CHART_ONE, + iter.first, + iter.second)); + } + for (auto iter : m_chartTwoMapFilesAndIndices) { + infoOut.push_back(MapFileInfo(MapOverlayType::CHART_TWO, + iter.first, + iter.second)); } + + return infoOut; +} + +/** + * @return Media files in media layers + */ +std::vector +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::getMediaFilesAndMaps() const +{ + std::vector infoOut; + for (auto iter : m_mediaFilesAndFrameIndices) { + infoOut.push_back(MediaFileInfo(iter.first, + iter.second)); + } return infoOut; } /** - * @return Map containing with each pair a map file and map indices selected for the file. + * Test to see if the given window index and tab index satisfy optional constraints + * @param tabIndex + * Index of the tab */ -std::map> -EventCaretMappableDataFilesAndMapsInDisplayedOverlays::getMapFilesAndIndices() const +bool +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::satisfiesConstraints(const int32_t tabIndex) { - return m_mapFilesAndIndices; + if (m_windowIndex >= 0) { +// if (windowIndex != m_windowIndex) { +// return false; +// } + } + + if ( ! m_tabIndices.empty()) { + if (m_tabIndices.find(tabIndex) == m_tabIndices.end()) { + return false; + } + } + + return true; +} + +/** + * Set a constraint to only get overlay for the given window + * @param windowIndex + * Index of the window + */ +void +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::setWindowIndexConstraint(const int32_t windowIndex) +{ + m_windowIndex = windowIndex; +} + +/** + * Set a constraint to only get overlay for the given tabs + * @param tabIndices + * Indices of the tabs + */ +void +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::setTabIndicesConstraint(const std::set& tabIndices) +{ + m_tabIndices = tabIndices; } /** @@ -170,12 +276,27 @@ * @param mapIndices * Indices of maps selected in overlays */ -EventCaretMappableDataFilesAndMapsInDisplayedOverlays::FileInfo::FileInfo(OverlayType overlayType, - CaretMappableDataFile* mapFile, - const std::set& mapIndices) +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::MapFileInfo::MapFileInfo(const MapOverlayType overlayType, + CaretMappableDataFile* mapFile, + const std::set& mapIndices) : m_overlayType(overlayType), m_mapFile(mapFile), m_mapIndices(mapIndices) { } +/** + * Constructor. + * + * @param mediaFile + * Media file in the overlay(s) + * @param framesIndices + * Indices of frames selected in overlays + */ +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::MediaFileInfo::MediaFileInfo(MediaFile* mediaFile, + const std::set frameIndices) +: m_mediaFile(mediaFile), +m_frameIndices(frameIndices) +{ +} + diff -Nru connectome-workbench-1.4.2/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h connectome-workbench-1.5.0/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h --- connectome-workbench-1.4.2/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h 2021-02-16 19:46:47.000000000 +0000 @@ -33,28 +33,40 @@ namespace caret { class CaretMappableDataFile; + class MediaFile; class EventCaretMappableDataFilesAndMapsInDisplayedOverlays : public Event { public: - enum class OverlayType { + enum class MapOverlayType { BRAINORDINATE, - CHART + CHART_ONE, + CHART_TWO }; - class FileInfo { + class MapFileInfo { public: - FileInfo(OverlayType overlayType, - CaretMappableDataFile* mapFile, - const std::set& mapIndices); + MapFileInfo(const MapOverlayType overlayType, + CaretMappableDataFile* mapFile, + const std::set& mapIndices); - const OverlayType m_overlayType; + const MapOverlayType m_overlayType; CaretMappableDataFile* m_mapFile; const std::set m_mapIndices; }; + class MediaFileInfo { + public: + MediaFileInfo(MediaFile* mediaFile, + const std::set frameIndices); + + MediaFile* m_mediaFile; + + const std::set m_frameIndices; + }; + EventCaretMappableDataFilesAndMapsInDisplayedOverlays(); virtual ~EventCaretMappableDataFilesAndMapsInDisplayedOverlays(); @@ -63,24 +75,46 @@ EventCaretMappableDataFilesAndMapsInDisplayedOverlays& operator=(const EventCaretMappableDataFilesAndMapsInDisplayedOverlays&) = delete; + void setWindowIndexConstraint(const int32_t windowIndex); + + void setTabIndicesConstraint(const std::set& tabIndices); + void addBrainordinateFileAndMap(CaretMappableDataFile* mapFile, - const int32_t mapIndex); + const int32_t mapIndex, + const int32_t tabIndex); - void addChartFileAndMap(CaretMappableDataFile* mapFile, - const int32_t mapIndex); - - std::map> getMapFilesAndIndices() const; + void addChartOneFileAndMap(CaretMappableDataFile* mapFile, + const int32_t mapIndex, + const int32_t tabIndex); + + void addChartTwoFileAndMap(CaretMappableDataFile* mapFile, + const int32_t mapIndex, + const int32_t tabIndex); + + void addMediaFileAndFrame(MediaFile* mediaFile, + const int32_t frameIndex, + const int32_t tabIndex); + + std::vector getFilesAndMaps() const; - std::vector getFilesAndMaps() const; + std::vector getMediaFilesAndMaps() const; // ADD_NEW_METHODS_HERE private: - std::map> m_mapFilesAndIndices; + bool satisfiesConstraints(const int32_t tabIndex); std::map> m_surfaceVolumeMapFilesAndIndices; - std::map> m_chartMapFilesAndIndices; + std::map> m_chartOneMapFilesAndIndices; + + std::map> m_chartTwoMapFilesAndIndices; + + std::map> m_mediaFilesAndFrameIndices; + + int32_t m_windowIndex = -1; + + std::set m_tabIndices; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/Brain/EventChartOverlayValidate.cxx connectome-workbench-1.5.0/src/Brain/EventChartOverlayValidate.cxx --- connectome-workbench-1.4.2/src/Brain/EventChartOverlayValidate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventChartOverlayValidate.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ -#include "EventChartOverlayValidate.h" -#undef __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ - -#include "EventTypeEnum.h" - -using namespace caret; - - - -/** - * \class caret::EventChartOverlayValidate - * \brief Test a chart overlay for validity (it exists). - * \ingroup Brain - */ - -/** - * Constructor. - * - * @param chartOverlay - * Chart overlay for verification. - */ -EventChartOverlayValidate::EventChartOverlayValidate(const ChartTwoOverlay* chartOverlay) -: Event(EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE), - m_chartOverlay(chartOverlay) -{ - m_valid = false; -} - -/** - * Destructor. - */ -EventChartOverlayValidate::~EventChartOverlayValidate() -{ - -} - -/** - * @return true if the chart overlay was found to be valid. - */ -bool -EventChartOverlayValidate::isValidChartOverlay() const -{ - return m_valid; -} - -/** - * Set the validity if the given overlay is the overlay - * that was passed to the constructor. - * - * @param chartOverlay - * Chart overlay tested for match. - */ -void -EventChartOverlayValidate::testValidChartOverlay(const ChartTwoOverlay* chartOverlay) -{ - if (m_chartOverlay == chartOverlay) { - m_valid = true; - } -} - diff -Nru connectome-workbench-1.4.2/src/Brain/EventChartOverlayValidate.h connectome-workbench-1.5.0/src/Brain/EventChartOverlayValidate.h --- connectome-workbench-1.4.2/src/Brain/EventChartOverlayValidate.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventChartOverlayValidate.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -#ifndef __EVENT_CHART_OVERLAY_VALIDATE_H__ -#define __EVENT_CHART_OVERLAY_VALIDATE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "Event.h" - -namespace caret { - - class ChartTwoOverlay; - - class EventChartOverlayValidate : public Event { - - public: - EventChartOverlayValidate(const ChartTwoOverlay* chartOverlay); - - virtual ~EventChartOverlayValidate(); - - bool isValidChartOverlay() const; - - void testValidChartOverlay(const ChartTwoOverlay* chartOverlay); - - private: - EventChartOverlayValidate(const EventChartOverlayValidate&); - - EventChartOverlayValidate& operator=(const EventChartOverlayValidate&); - - public: - - // ADD_NEW_METHODS_HERE - - private: - - // ADD_NEW_MEMBERS_HERE - - const ChartTwoOverlay* m_chartOverlay; - - bool m_valid; - - }; - -#ifdef __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ - // -#endif // __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ - -} // namespace -#endif //__EVENT_CHART_OVERLAY_VALIDATE_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/EventChartTwoOverlayValidate.cxx connectome-workbench-1.5.0/src/Brain/EventChartTwoOverlayValidate.cxx --- connectome-workbench-1.4.2/src/Brain/EventChartTwoOverlayValidate.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventChartTwoOverlayValidate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,82 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CHART_TWO_OVERLAY_VALIDATE_DECLARE__ +#include "EventChartTwoOverlayValidate.h" +#undef __EVENT_CHART_TWO_OVERLAY_VALIDATE_DECLARE__ + +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventChartTwoOverlayValidate + * \brief Test a chart overlay for validity (it exists). + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param chartOverlay + * Chart overlay for verification. + */ +EventChartTwoOverlayValidate::EventChartTwoOverlayValidate(const ChartTwoOverlay* chartOverlay) +: Event(EventTypeEnum::EVENT_CHART_TWO_OVERLAY_VALIDATE), + m_chartOverlay(chartOverlay) +{ + m_valid = false; +} + +/** + * Destructor. + */ +EventChartTwoOverlayValidate::~EventChartTwoOverlayValidate() +{ + +} + +/** + * @return true if the chart overlay was found to be valid. + */ +bool +EventChartTwoOverlayValidate::isValidChartOverlay() const +{ + return m_valid; +} + +/** + * Set the validity if the given overlay is the overlay + * that was passed to the constructor. + * + * @param chartOverlay + * Chart overlay tested for match. + */ +void +EventChartTwoOverlayValidate::testValidChartOverlay(const ChartTwoOverlay* chartOverlay) +{ + if (m_chartOverlay == chartOverlay) { + m_valid = true; + } +} + diff -Nru connectome-workbench-1.4.2/src/Brain/EventChartTwoOverlayValidate.h connectome-workbench-1.5.0/src/Brain/EventChartTwoOverlayValidate.h --- connectome-workbench-1.4.2/src/Brain/EventChartTwoOverlayValidate.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventChartTwoOverlayValidate.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,66 @@ +#ifndef __EVENT_CHART_TWO_OVERLAY_VALIDATE_H__ +#define __EVENT_CHART_TWO_OVERLAY_VALIDATE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "Event.h" + +namespace caret { + + class ChartTwoOverlay; + + class EventChartTwoOverlayValidate : public Event { + + public: + EventChartTwoOverlayValidate(const ChartTwoOverlay* chartOverlay); + + virtual ~EventChartTwoOverlayValidate(); + + bool isValidChartOverlay() const; + + void testValidChartOverlay(const ChartTwoOverlay* chartOverlay); + + private: + EventChartTwoOverlayValidate(const EventChartTwoOverlayValidate&); + + EventChartTwoOverlayValidate& operator=(const EventChartTwoOverlayValidate&); + + public: + + // ADD_NEW_METHODS_HERE + + private: + + // ADD_NEW_MEMBERS_HERE + + const ChartTwoOverlay* m_chartOverlay; + + bool m_valid; + + }; + +#ifdef __EVENT_CHART_TWO_OVERLAY_VALIDATE_DECLARE__ + // +#endif // __EVENT_CHART_TWO_OVERLAY_VALIDATE_DECLARE__ + +} // namespace +#endif //__EVENT_CHART_TWO_OVERLAY_VALIDATE_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/EventDataFileReloadAll.cxx connectome-workbench-1.5.0/src/Brain/EventDataFileReloadAll.cxx --- connectome-workbench-1.4.2/src/Brain/EventDataFileReloadAll.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventDataFileReloadAll.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,166 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_DATA_FILE_RELOAD_ALL_DECLARE__ +#include "EventDataFileReloadAll.h" +#undef __EVENT_DATA_FILE_RELOAD_ALL_DECLARE__ + +#include "CaretAssert.h" +#include "CaretDataFile.h" +#include "CaretResult.h" +#include "EventManager.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventDataFileReloadAll + * \brief Event for reloading all data files + * \ingroup Brain + */ + +/** + * Get all reloadable data files + * @param brain + * Brain for getting files + * @param reloadableFilesOut + * Output containing all reloadable data files + * @return + * CaretResult with success/error + */ +std::unique_ptr +EventDataFileReloadAll::getReloadableFiles(Brain* brain, + std::vector& reloadableFilesOut) +{ + EventDataFileReloadAll event(EventDataFileReloadAll::Mode::GET_FILES, + brain); + EventManager::get()->sendEvent(event.getPointer()); + if (event.isError()) { + return CaretResult::newInstanceError(event.getErrorMessage()); + } + + reloadableFilesOut = event.getReloadableFiles(); + return CaretResult::newInstanceSuccess(); +} + +/** + * Reload data files + * @param brain + * Brain that has its files reloaded + * @return + * CaretResult with success/error + */ +std::unique_ptr +EventDataFileReloadAll::reloadAllFiles(Brain* brain) +{ + EventDataFileReloadAll event(EventDataFileReloadAll::Mode::RELOAD_FILES, + brain); + EventManager::get()->sendEvent(event.getPointer()); + if (event.isError()) { + return CaretResult::newInstanceError(event.getErrorMessage()); + } + + return CaretResult::newInstanceSuccess(); +} + + +/** + * Constructor. + * @param mode + * The mode + * @param brain + * Brain that contains files for reloading + */ +EventDataFileReloadAll::EventDataFileReloadAll(const Mode mode, + Brain* brain) +: Event(EventTypeEnum::EVENT_DATA_FILE_RELOAD_ALL), +m_mode(mode), +m_brain(brain) +{ + CaretAssert(brain); +} + +/** + * Destructor. + */ +EventDataFileReloadAll::~EventDataFileReloadAll() +{ +} + +/** + * @return The mode + */ +EventDataFileReloadAll::Mode +EventDataFileReloadAll::getMode() +{ + return m_mode; +} + +/** + * @return Brain that is to reload all files + */ +Brain* +EventDataFileReloadAll::getBrain() +{ + return m_brain; +} + +/** + * @return All loaded files that are reloadable. Surfaces and volume will be in vector + * before any other data files. + */ +std::vector +EventDataFileReloadAll::getReloadableFiles() const +{ + std::vector surfadeAndVolumeFiles; + std::vector otherFiles; + + for (auto& df : m_reloadableFiles) { + switch (df->getDataFileType()) { + case DataFileTypeEnum::SURFACE: + case DataFileTypeEnum::VOLUME: + surfadeAndVolumeFiles.push_back(df); + break; + default: + otherFiles.push_back(df); + break; + } + } + + surfadeAndVolumeFiles.insert(surfadeAndVolumeFiles.end(), + otherFiles.begin(), + otherFiles.end()); + return surfadeAndVolumeFiles; +} + +/** + * Add a file to reloadable data files + * @param caretDataFile + * Data file for reloading + */ +void +EventDataFileReloadAll::addReloadableFile(CaretDataFile* caretDataFile) +{ + CaretAssert(caretDataFile); + m_reloadableFiles.push_back(caretDataFile); +} diff -Nru connectome-workbench-1.4.2/src/Brain/EventDataFileReloadAll.h connectome-workbench-1.5.0/src/Brain/EventDataFileReloadAll.h --- connectome-workbench-1.4.2/src/Brain/EventDataFileReloadAll.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventDataFileReloadAll.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,84 @@ +#ifndef __EVENT_DATA_FILE_RELOAD_ALL_H__ +#define __EVENT_DATA_FILE_RELOAD_ALL_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + +namespace caret { + + class Brain; + class CaretDataFile; + class CaretResult; + + class EventDataFileReloadAll : public Event { + + public: + enum class Mode { + GET_FILES, + RELOAD_FILES + }; + + static std::unique_ptr getReloadableFiles(Brain* brain, + std::vector& reloadableFilesOut); + + static std::unique_ptr reloadAllFiles(Brain* brain); + + virtual ~EventDataFileReloadAll(); + + Mode getMode(); + + Brain* getBrain(); + + EventDataFileReloadAll(const EventDataFileReloadAll&) = delete; + + EventDataFileReloadAll& operator=(const EventDataFileReloadAll&) = delete; + + std::vector getReloadableFiles() const; + + void addReloadableFile(CaretDataFile* caretDataFile); + + // ADD_NEW_METHODS_HERE + + private: + EventDataFileReloadAll(const Mode mode, + Brain* brain); + + const Mode m_mode; + + Brain* m_brain; + + std::vector m_reloadableFiles; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_DATA_FILE_RELOAD_ALL_DECLARE__ + // +#endif // __EVENT_DATA_FILE_RELOAD_ALL_DECLARE__ + +} // namespace +#endif //__EVENT_DATA_FILE_RELOAD_ALL_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/EventUserInputModeGet.cxx connectome-workbench-1.5.0/src/Brain/EventUserInputModeGet.cxx --- connectome-workbench-1.4.2/src/Brain/EventUserInputModeGet.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventUserInputModeGet.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,89 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_USER_INPUT_MODE_GET_DECLARE__ +#include "EventUserInputModeGet.h" +#undef __EVENT_USER_INPUT_MODE_GET_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventUserInputModeGet + * \brief Event to get the user input mode for a window + * \ingroup Brain + * + * + */ + +/** + * Constructor. + * @param windowIndex + * Index of window for obtaining mode. + */ +EventUserInputModeGet::EventUserInputModeGet(const int32_t windowIndex) +: Event(EventTypeEnum::EVENT_GET_USER_INPUT_MODE), +m_windowIndex(windowIndex) +{ + +} + +/** + * Destructor. + */ +EventUserInputModeGet::~EventUserInputModeGet() +{ +} + +/** + * @return Index of the window + */ +int32_t +EventUserInputModeGet::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * @return The user input modfe + */ +UserInputModeEnum::Enum +EventUserInputModeGet::getUserInputMode() const +{ + return m_userInputMode; +} + +/** + * Set the usert input mofe + * @param userInputMode + * The user input mode + */ +void +EventUserInputModeGet::setUserInputMode(const UserInputModeEnum::Enum userInputMode) +{ + m_userInputMode = userInputMode; +} + + diff -Nru connectome-workbench-1.4.2/src/Brain/EventUserInputModeGet.h connectome-workbench-1.5.0/src/Brain/EventUserInputModeGet.h --- connectome-workbench-1.4.2/src/Brain/EventUserInputModeGet.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/EventUserInputModeGet.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,67 @@ +#ifndef __EVENT_USER_INPUT_MODE_GET_H__ +#define __EVENT_USER_INPUT_MODE_GET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" +#include "UserInputModeEnum.h" + + +namespace caret { + + class EventUserInputModeGet : public Event { + + public: + EventUserInputModeGet(const int32_t windowIndex); + + virtual ~EventUserInputModeGet(); + + EventUserInputModeGet(const EventUserInputModeGet&) = delete; + + EventUserInputModeGet& operator=(const EventUserInputModeGet&) = delete; + + int32_t getWindowIndex() const; + + UserInputModeEnum::Enum getUserInputMode() const; + + void setUserInputMode(const UserInputModeEnum::Enum userInputMode); + + // ADD_NEW_METHODS_HERE + + private: + const int32_t m_windowIndex; + + UserInputModeEnum::Enum m_userInputMode = UserInputModeEnum::Enum::VIEW; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_USER_INPUT_MODE_GET_DECLARE__ + // +#endif // __EVENT_USER_INPUT_MODE_GET_DECLARE__ + +} // namespace +#endif //__EVENT_USER_INPUT_MODE_GET_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/FociDrawingProjectionTypeEnum.cxx connectome-workbench-1.5.0/src/Brain/FociDrawingProjectionTypeEnum.cxx --- connectome-workbench-1.4.2/src/Brain/FociDrawingProjectionTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/FociDrawingProjectionTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,430 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __FOCI_DRAWING_PROJECTION_TYPE_ENUM_DECLARE__ +#include "FociDrawingProjectionTypeEnum.h" +#undef __FOCI_DRAWING_PROJECTION_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::FociDrawingProjectionTypeEnum + * \brief Type for selection of how foci are drawn using their projectiojs + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_fociDrawingProjectionTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void fociDrawingProjectionTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "FociDrawingProjectionTypeEnum.h" + * + * Instatiate: + * m_fociDrawingProjectionTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_fociDrawingProjectionTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_fociDrawingProjectionTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(fociDrawingProjectionTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_fociDrawingProjectionTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const FociDrawingProjectionTypeEnum::Enum VARIABLE = m_fociDrawingProjectionTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +FociDrawingProjectionTypeEnum::FociDrawingProjectionTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +FociDrawingProjectionTypeEnum::~FociDrawingProjectionTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +FociDrawingProjectionTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(FociDrawingProjectionTypeEnum(PROJECTED, + "PROJECTED", + "Projected")); + + enumData.push_back(FociDrawingProjectionTypeEnum(STEREOTAXIC, + "STEREOTAXIC", + "Stereotaxic")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const FociDrawingProjectionTypeEnum* +FociDrawingProjectionTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const FociDrawingProjectionTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +FociDrawingProjectionTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const FociDrawingProjectionTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +FociDrawingProjectionTypeEnum::Enum +FociDrawingProjectionTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FociDrawingProjectionTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FociDrawingProjectionTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type FociDrawingProjectionTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +FociDrawingProjectionTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const FociDrawingProjectionTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +FociDrawingProjectionTypeEnum::Enum +FociDrawingProjectionTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FociDrawingProjectionTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FociDrawingProjectionTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type FociDrawingProjectionTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +FociDrawingProjectionTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const FociDrawingProjectionTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +FociDrawingProjectionTypeEnum::Enum +FociDrawingProjectionTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FociDrawingProjectionTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FociDrawingProjectionTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type FociDrawingProjectionTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +FociDrawingProjectionTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +FociDrawingProjectionTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(FociDrawingProjectionTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +FociDrawingProjectionTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(FociDrawingProjectionTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + +/** + * @return Tooltip for display in the GUI + */ +AString +FociDrawingProjectionTypeEnum::getToolTip() +{ + AString projectedName; + AString stereotaxicName; + std::vector allEnums; + getAllEnums(allEnums); + for (auto e : allEnums) { + switch (e) { + case PROJECTED: + projectedName = FociDrawingProjectionTypeEnum::toGuiName(e); + break; + case STEREOTAXIC: + stereotaxicName = FociDrawingProjectionTypeEnum::toGuiName(e); + break; + } + } + + AString s(""); + + s.appendWithNewLine("Display and Compatible Coordinate Type(s): "); + s.append("
    "); + s.appendWithNewLine("
  • All - " + projectedName + " or " + stereotaxicName); + s.appendWithNewLine("
  • Montage - " + projectedName); + s.appendWithNewLine("
  • Surface - " + projectedName); + s.appendWithNewLine("
  • Volume - " + stereotaxicName); + s.appendWithNewLine("
"); + + if (initializedFlag == false) initialize(); + + s.appendWithNewLine("Coordinate Types:"); + s.appendWithNewLine("
    "); + for (auto e : allEnums) { + s.appendWithNewLine("
  • " + + FociDrawingProjectionTypeEnum::toGuiName(e) + + " - "); + switch (e) { + case PROJECTED: + s.append("Show foci that are projected to their displayed surface (surface must be displayed). " + "Projected foci are shown on all surface types (anatomical, inflated, etc.)."); + break; + case STEREOTAXIC: + s.append("Show all foci at stereotaxic coordinates. This mode should be used only " + "with no surfaces, anatomical surfaces, or volume slices."); + break; + } + } + + s.appendWithNewLine("
"); + s.appendWithNewLine(""); + + return s; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/FociDrawingProjectionTypeEnum.h connectome-workbench-1.5.0/src/Brain/FociDrawingProjectionTypeEnum.h --- connectome-workbench-1.4.2/src/Brain/FociDrawingProjectionTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/FociDrawingProjectionTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __FOCI_DRAWING_PROJECTION_TYPE_ENUM_H__ +#define __FOCI_DRAWING_PROJECTION_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class FociDrawingProjectionTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + PROJECTED, + /** */ + STEREOTAXIC + }; + + + ~FociDrawingProjectionTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + + static AString getToolTip(); + +private: + FociDrawingProjectionTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const FociDrawingProjectionTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __FOCI_DRAWING_PROJECTION_TYPE_ENUM_DECLARE__ +std::vector FociDrawingProjectionTypeEnum::enumData; +bool FociDrawingProjectionTypeEnum::initializedFlag = false; +int32_t FociDrawingProjectionTypeEnum::integerCodeCounter = 0; +#endif // __FOCI_DRAWING_PROJECTION_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__FOCI_DRAWING_PROJECTION_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/GapsAndMargins.cxx connectome-workbench-1.5.0/src/Brain/GapsAndMargins.cxx --- connectome-workbench-1.4.2/src/Brain/GapsAndMargins.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/GapsAndMargins.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -118,8 +118,10 @@ EventBrowserTabNew* newTabEvent = dynamic_cast(event); CaretAssert(newTabEvent); + /* + * New tab event may fail when tab limit is reached + */ const BrowserTabContent* tab = newTabEvent->getBrowserTab(); - CaretAssert(tab); if (tab != NULL) { const int32_t tabIndex = tab->getTabNumber(); CaretAssertArrayIndex(m_tabMarginsLeft, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationFilter.cxx connectome-workbench-1.5.0/src/Brain/IdentificationFilter.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationFilter.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationFilter.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,209 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_FILTER_DECLARE__ +#include "IdentificationFilter.h" +#undef __IDENTIFICATION_FILTER_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::IdentificationFilter + * \brief Filtering seledtions for identification operations + * \ingroup Brain + */ + +/** + * Constructor. + */ +IdentificationFilter::IdentificationFilter() +: CaretObject() +{ + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_tabFiltering", &m_tabFiltering); + m_sceneAssistant->add("m_showCiftiLoadingEnabled", &m_showCiftiLoadingEnabled); + m_sceneAssistant->add("m_showBorderEnabled", &m_showBorderEnabled); + m_sceneAssistant->add("m_showFociEnabled", &m_showFociEnabled); +} + +/** + * Destructor. + */ +IdentificationFilter::~IdentificationFilter() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +IdentificationFilter::toString() const +{ + return "IdentificationFilter"; +} + +/** + * @return The tab filtering + */ +IdentificationFilterTabSelectionEnum::Enum +IdentificationFilter::getTabFiltering() const +{ + return m_tabFiltering; +} + +/** + * Set the tab filtering + * + * @param tabFiltering + * New tab filtering selection + */ +void +IdentificationFilter::setTabFiltering(const IdentificationFilterTabSelectionEnum::Enum tabFiltering) +{ + m_tabFiltering = tabFiltering; +} + +/** + * @return show CIFTI loading information + */ +bool +IdentificationFilter::isShowCiftiLoadingEnabled() const +{ + return m_showCiftiLoadingEnabled; +} + +/** + * Set show CIFTI loading information + * + * @param staus + * New loading status + */ +void +IdentificationFilter::setShowCiftiLoadingEnabled(const bool status) +{ + m_showCiftiLoadingEnabled = status; +} + +/** + * @return show border information + */ +bool +IdentificationFilter::isShowBorderEnabled() const +{ + return m_showBorderEnabled; +} + +/** + * Set show border information + * + * @param staus + * New loading status + */ +void +IdentificationFilter::setShowBorderEnabled(const bool status) +{ + m_showBorderEnabled = status; +} + +/** + * @return show foci information + */ +bool +IdentificationFilter::isShowFociEnabled() const +{ + return m_showFociEnabled; +} + +/** + * Set show foci information + * + * @param staus + * New loading status + */ +void +IdentificationFilter::setShowFociEnabled(const bool status) +{ + m_showFociEnabled = status; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +IdentificationFilter::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "IdentificationFilter", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +IdentificationFilter::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationFilter.h connectome-workbench-1.5.0/src/Brain/IdentificationFilter.h --- connectome-workbench-1.4.2/src/Brain/IdentificationFilter.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationFilter.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,107 @@ +#ifndef __IDENTIFICATION_FILTER_H__ +#define __IDENTIFICATION_FILTER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "IdentificationFilterTabSelectionEnum.h" +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + + class IdentificationFilter : public CaretObject, public SceneableInterface { + + public: + IdentificationFilter(); + + virtual ~IdentificationFilter(); + + IdentificationFilter(const IdentificationFilter&) = delete; + + IdentificationFilter& operator=(const IdentificationFilter&) = delete; + + IdentificationFilterTabSelectionEnum::Enum getTabFiltering() const; + + void setTabFiltering(const IdentificationFilterTabSelectionEnum::Enum tabFiltering); + + bool isShowCiftiLoadingEnabled() const; + + void setShowCiftiLoadingEnabled(const bool status); + + bool isShowBorderEnabled() const; + + void setShowBorderEnabled(const bool borderEnabled); + + bool isShowFociEnabled() const; + + void setShowFociEnabled(const bool fociEnabled); + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + std::unique_ptr m_sceneAssistant; + + IdentificationFilterTabSelectionEnum::Enum m_tabFiltering = IdentificationFilterTabSelectionEnum::ALL_DISPLAYED_TABS; + + bool m_showCiftiLoadingEnabled = true; + + bool m_showBorderEnabled = true; + + bool m_showFociEnabled = true; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __IDENTIFICATION_FILTER_DECLARE__ + // +#endif // __IDENTIFICATION_FILTER_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_FILTER_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationFilterTabSelectionEnum.cxx connectome-workbench-1.5.0/src/Brain/IdentificationFilterTabSelectionEnum.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationFilterTabSelectionEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationFilterTabSelectionEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,393 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __IDENTIFICATION_FILTER_TAB_SELECTION_ENUM_DECLARE__ +#include "IdentificationFilterTabSelectionEnum.h" +#undef __IDENTIFICATION_FILTER_TAB_SELECTION_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::IdentificationFilterTabSelectionEnum + * \brief Enumerated type for tab filtering of files for identification + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_identificationFilterTabSelectionEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void identificationFilterTabSelectionEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "IdentificationFilterTabSelectionEnum.h" + * + * Instatiate: + * m_identificationFilterTabSelectionEnumComboBox = new EnumComboBoxTemplate(this); + * m_identificationFilterTabSelectionEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_identificationFilterTabSelectionEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(identificationFilterTabSelectionEnumComboBoxItemActivated())); + * + * Update the selection: + * m_identificationFilterTabSelectionEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const IdentificationFilterTabSelectionEnum::Enum VARIABLE = m_identificationFilterTabSelectionEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +IdentificationFilterTabSelectionEnum::IdentificationFilterTabSelectionEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +IdentificationFilterTabSelectionEnum::~IdentificationFilterTabSelectionEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +IdentificationFilterTabSelectionEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(IdentificationFilterTabSelectionEnum(ALL_DISPLAYED_TABS, + "ALL_DISPLAYED_TABS", + "All Tabs")); + + enumData.push_back(IdentificationFilterTabSelectionEnum(MOUSE_CLICKED_TAB, + "MOUSE_CLICKED_TAB", + "Tab Containing Mouse")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const IdentificationFilterTabSelectionEnum* +IdentificationFilterTabSelectionEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const IdentificationFilterTabSelectionEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +IdentificationFilterTabSelectionEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const IdentificationFilterTabSelectionEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +IdentificationFilterTabSelectionEnum::Enum +IdentificationFilterTabSelectionEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationFilterTabSelectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationFilterTabSelectionEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type IdentificationFilterTabSelectionEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +IdentificationFilterTabSelectionEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const IdentificationFilterTabSelectionEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +IdentificationFilterTabSelectionEnum::Enum +IdentificationFilterTabSelectionEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationFilterTabSelectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationFilterTabSelectionEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type IdentificationFilterTabSelectionEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +IdentificationFilterTabSelectionEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const IdentificationFilterTabSelectionEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +IdentificationFilterTabSelectionEnum::Enum +IdentificationFilterTabSelectionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationFilterTabSelectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationFilterTabSelectionEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type IdentificationFilterTabSelectionEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +IdentificationFilterTabSelectionEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +IdentificationFilterTabSelectionEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(IdentificationFilterTabSelectionEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +IdentificationFilterTabSelectionEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(IdentificationFilterTabSelectionEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + +AString +IdentificationFilterTabSelectionEnum::toToolTip(Enum enumValue) +{ + AString toolTip; + + switch (enumValue) { + case ALL_DISPLAYED_TABS: + toolTip = ("Identify files in enabled overlays\n" + "in all displayed tabs"); + break; + case MOUSE_CLICKED_TAB: + toolTip = ("Identify files in enabled overlays\n" + "but only in the tab in which\n" + "the mouse was clicked"); + break; + } + + return toolTip; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationFilterTabSelectionEnum.h connectome-workbench-1.5.0/src/Brain/IdentificationFilterTabSelectionEnum.h --- connectome-workbench-1.4.2/src/Brain/IdentificationFilterTabSelectionEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationFilterTabSelectionEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __IDENTIFICATION_FILTER_TAB_SELECTION_ENUM_H__ +#define __IDENTIFICATION_FILTER_TAB_SELECTION_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class IdentificationFilterTabSelectionEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** All displayed tabs */ + ALL_DISPLAYED_TABS, + /** Tab containing mouse click */ + MOUSE_CLICKED_TAB + }; + + + ~IdentificationFilterTabSelectionEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + + static AString toToolTip(Enum enumValue); + +private: + IdentificationFilterTabSelectionEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const IdentificationFilterTabSelectionEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __IDENTIFICATION_FILTER_TAB_SELECTION_ENUM_DECLARE__ +std::vector IdentificationFilterTabSelectionEnum::enumData; +bool IdentificationFilterTabSelectionEnum::initializedFlag = false; +int32_t IdentificationFilterTabSelectionEnum::integerCodeCounter = 0; +#endif // __IDENTIFICATION_FILTER_TAB_SELECTION_ENUM_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_FILTER_TAB_SELECTION_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationFormattedTextGenerator.cxx connectome-workbench-1.5.0/src/Brain/IdentificationFormattedTextGenerator.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationFormattedTextGenerator.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationFormattedTextGenerator.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,2393 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_FORMATTED_TEXT_GENERATOR_DECLARE__ +#include "IdentificationFormattedTextGenerator.h" +#undef __IDENTIFICATION_FORMATTED_TEXT_GENERATOR_DECLARE__ + +#include "Border.h" +#include "Brain.h" +#include "BrainStructure.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "ChartDataCartesian.h" +#include "ChartDataSource.h" +#include "ChartModelDataSeries.h" +#include "ChartTwoDataCartesian.h" +#include "ChartableMatrixInterface.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineLayerChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartableTwoFileMatrixChart.h" +#include "CiftiMappableConnectivityMatrixDataFile.h" +#include "CiftiMappableDataFile.h" +#include "CaretVolumeExtension.h" +#include "DataToolTipsManager.h" +#include "EventBrowserTabGetAll.h" +#include "EventBrowserTabIndicesGetAllViewed.h" +#include "EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h" +#include "EventCaretMappableDataFilesGet.h" +#include "EventManager.h" +#include "FileIdentificationAttributes.h" +#include "FileInformation.h" +#include "FociFile.h" +#include "Focus.h" +#include "GiftiLabel.h" +#include "GraphicsPrimitive.h" +#include "GraphicsPrimitiveV3f.h" +#include "Histogram.h" +#include "HtmlTableBuilder.h" +#include "IdentificationFilter.h" +#include "IdentificationManager.h" +#include "ImageFile.h" +#include "MapFileDataSelector.h" +#include "MetricDynamicConnectivityFile.h" +#include "OverlaySet.h" +#include "SelectionItemBorderSurface.h" +#include "SelectionItemChartDataSeries.h" +#include "SelectionItemChartFrequencySeries.h" +#include "SelectionItemChartMatrix.h" +#include "SelectionItemCiftiConnectivityMatrixRowColumn.h" +#include "SelectionItemChartTimeSeries.h" +#include "SelectionItemChartTwoHistogram.h" +#include "SelectionItemChartTwoLineLayer.h" +#include "SelectionItemChartTwoLineLayerVerticalNearest.h" +#include "SelectionItemChartTwoLineSeries.h" +#include "SelectionItemChartTwoMatrix.h" +#include "SelectionItemFocusSurface.h" +#include "SelectionItemFocusVolume.h" +#include "SelectionItemImage.h" +#include "SelectionItemSurfaceNode.h" +#include "SelectionItemVoxel.h" +#include "SelectionManager.h" +#include "IdentificationStringBuilder.h" +#include "LabelFile.h" +#include "MetricFile.h" +#include "Surface.h" +#include "VolumeDynamicConnectivityFile.h" +#include "SurfaceProjectedItem.h" +#include "SurfaceProjectionBarycentric.h" +#include "SurfaceProjectionVanEssen.h" +#include "VolumeFile.h" + +using namespace caret; + + + +/** + * \class IdentificationFormattedTextGenerator + * \brief Creates text describing selected data. + * + * Examine the selected data and generate descriptive text. + */ + +/** + * Constructor. + */ +IdentificationFormattedTextGenerator::IdentificationFormattedTextGenerator() +: CaretObject(), +m_noDataText("no-data") +{ + +} + +/** + * Destructor. + */ +IdentificationFormattedTextGenerator::~IdentificationFormattedTextGenerator() +{ + +} + +/** + * Create identification text from selection in the identification manager. + * @param idManselectionManagerager + * Selection manager containing selection. + * @param brain + * The brain. + * @param tabIndex + * Index of tab where identification took place + */ +AString +IdentificationFormattedTextGenerator::createIdentificationText(const SelectionManager* selectionManager, + const Brain* brain, + const int32_t tabIndex) const +{ + CaretAssert(selectionManager); + CaretAssert(brain); + + const IdentificationManager* idManager = brain->getIdentificationManager(); + const IdentificationFilter* filter = idManager->getIdentificationFilter(); + + IdentificationStringBuilder idText; + std::unique_ptr chartHtmlTableBuilder = createHtmlTableBuilder(3); + chartHtmlTableBuilder->setTitleBold("Charts"); + std::unique_ptr geometryHtmlTableBuilder = createHtmlTableBuilder(3); + geometryHtmlTableBuilder->setTitleBold("Geometry"); + std::unique_ptr imageHtmlTableBuilder = createHtmlTableBuilder(2); + imageHtmlTableBuilder->setTitlePlain("Image"); + std::unique_ptr labelHtmlTableBuilder = createHtmlTableBuilder(2); + labelHtmlTableBuilder->setTitlePlain("Labels"); + std::unique_ptr layersHtmlTableBuilder = createHtmlTableBuilder(3); + layersHtmlTableBuilder->setTitlePlain("Layers"); + std::unique_ptr scalarHtmlTableBuilder = createHtmlTableBuilder(3); + scalarHtmlTableBuilder->setTitlePlain("Scalars"); + + const SelectionItemSurfaceNode* surfaceID = selectionManager->getSurfaceNodeIdentification(); + + this->generateSurfaceVertexIdentificationText(*geometryHtmlTableBuilder, + brain, + surfaceID); + this->generateVolumeVoxelIdentificationText(*geometryHtmlTableBuilder, + brain, + selectionManager->getVoxelIdentification()); + + const std::vector displayedFiles = getFilesForIdentification(filter, + tabIndex); + + for (auto fileInfo : displayedFiles) { + switch (fileInfo.m_overlayType) { + case EventCaretMappableDataFilesAndMapsInDisplayedOverlays::MapOverlayType::BRAINORDINATE: + if (surfaceID->isValid()) { + if (fileInfo.m_mapFile->isSurfaceMappable()) { + this->generateSurfaceDataIdentificationText(*labelHtmlTableBuilder, + *scalarHtmlTableBuilder, + fileInfo.m_mapFile, + fileInfo.m_mapIndices, + brain, + surfaceID); + } + } + if (selectionManager->getVoxelIdentification()->isValid()) { + if (fileInfo.m_mapFile->isVolumeMappable()) { + this->generateVolumeDataIdentificationText(*labelHtmlTableBuilder, + *scalarHtmlTableBuilder, + fileInfo.m_mapFile, + fileInfo.m_mapIndices, + brain, + selectionManager->getVoxelIdentification()); + } + } + break; + case EventCaretMappableDataFilesAndMapsInDisplayedOverlays::MapOverlayType::CHART_ONE: + break; + case EventCaretMappableDataFilesAndMapsInDisplayedOverlays::MapOverlayType::CHART_TWO: + this->generateChartTwoHistogramIdentificationText(*chartHtmlTableBuilder, + idText, + selectionManager->getChartTwoHistogramIdentification(), + fileInfo.m_mapFile, + fileInfo.m_mapIndices, + false); + + this->generateChartTwoLineLayerNearestIdentificationText(*chartHtmlTableBuilder, + idText, + selectionManager->getChartTwoLineLayerVerticalNearestIdentification(), + fileInfo.m_mapFile, + fileInfo.m_mapIndices, + false); + + this->generateChartTwoLineLayerIdentificationText(*chartHtmlTableBuilder, + idText, + selectionManager->getChartTwoLineLayerIdentification(), + fileInfo.m_mapFile, + fileInfo.m_mapIndices, + false); + + this->generateChartTwoLineSeriesIdentificationText(*chartHtmlTableBuilder, + idText, + selectionManager->getChartTwoLineSeriesIdentification(), + fileInfo.m_mapFile, + fileInfo.m_mapIndices, + false); + + this->generateChartTwoMatrixIdentificationText(*chartHtmlTableBuilder, + idText, + selectionManager->getChartTwoMatrixIdentification(), + fileInfo.m_mapFile, + fileInfo.m_mapIndices, + false); + + break; + } + } + + + if (filter->isShowFociEnabled()) { + this->generateSurfaceFocusIdentifcationText(*layersHtmlTableBuilder, + selectionManager->getSurfaceFocusIdentification(), + false); + this->generateVolumeFocusIdentifcationText(*layersHtmlTableBuilder, + selectionManager->getVolumeFocusIdentification()); + } + + if (filter->isShowBorderEnabled()) { + this->generateSurfaceBorderIdentifcationText(*layersHtmlTableBuilder, + idText, + selectionManager->getSurfaceBorderIdentification(), + false); + } + + + this->generateChartDataSeriesIdentificationText(*chartHtmlTableBuilder, + selectionManager->getChartDataSeriesIdentification()); + + this->generateChartFrequencySeriesIdentificationText(*chartHtmlTableBuilder, + selectionManager->getChartFrequencySeriesIdentification()); + + this->generateChartTimeSeriesIdentificationText(*chartHtmlTableBuilder, + selectionManager->getChartTimeSeriesIdentification()); + + this->generateChartMatrixIdentificationText(*chartHtmlTableBuilder, + selectionManager->getChartMatrixIdentification()); + + this->generateCiftiConnectivityMatrixIdentificationText(*chartHtmlTableBuilder, + selectionManager->getCiftiConnectivityMatrixRowColumnIdentification()); + + this->generateImageIdentificationText(*imageHtmlTableBuilder, + selectionManager->getImageIdentification()); + + AString textOut; + textOut.append(geometryHtmlTableBuilder->getAsHtmlTable()); + textOut.append(labelHtmlTableBuilder->getAsHtmlTable()); + textOut.append(scalarHtmlTableBuilder->getAsHtmlTable()); + textOut.append(layersHtmlTableBuilder->getAsHtmlTable()); + textOut.append(chartHtmlTableBuilder->getAsHtmlTable()); + textOut.append(imageHtmlTableBuilder->getAsHtmlTable()); + return textOut; +} + +/** + * @return Files for identification + * @param filter + * Identification filter + * @param tabIndex + * Index of tab where ID took place + */ +std::vector +IdentificationFormattedTextGenerator::getFilesForIdentification(const IdentificationFilter* filter, + const int32_t tabIndex) const +{ + /** + * Event gets files from enabled overlays in the viewed tab(s) + */ + EventCaretMappableDataFilesAndMapsInDisplayedOverlays overlayFilesEvent; + switch (filter->getTabFiltering()) { + case IdentificationFilterTabSelectionEnum::ALL_DISPLAYED_TABS: + { + EventBrowserTabIndicesGetAllViewed viewedTabsEvent; + EventManager::get()->sendEvent(viewedTabsEvent.getPointer()); + const std::vector tabs = viewedTabsEvent.getAllBrowserTabIndices(); + std::set tabsSet(tabs.begin(), + tabs.end()); + overlayFilesEvent.setTabIndicesConstraint(tabsSet); + } + break; + case IdentificationFilterTabSelectionEnum::MOUSE_CLICKED_TAB: + if (tabIndex >= 0) { + std::set tabIndices { tabIndex }; + overlayFilesEvent.setTabIndicesConstraint(tabIndices); + } + break; + } + EventManager::get()->sendEvent(overlayFilesEvent.getPointer()); + + /* + * Event gets all files + */ + EventCaretMappableDataFilesGet mapFilesEvent; + EventManager::get()->sendEvent(mapFilesEvent.getPointer()); + std::vector allMapFiles; + mapFilesEvent.getAllFiles(allMapFiles); + + /* + * Test all files for any that the users has enabled for identification + * on the Identification Dialog's Filtering tab and add them to the + * overlay files event + */ + for (auto mapFile : allMapFiles) { + CaretAssert(mapFile); + const FileIdentificationAttributes* fileAtts = mapFile->getFileIdentificationAttributes(); + CaretAssert(fileAtts); + if (fileAtts->isEnabled()) { + switch (fileAtts->getMapSelectionMode()) { + case FileIdentificationMapSelectionEnum::ALL: + { + const int32_t numMaps = mapFile->getNumberOfMaps(); + for (int32_t iMap = 0; iMap < numMaps; iMap++) { + overlayFilesEvent.addBrainordinateFileAndMap(mapFile, + iMap, + tabIndex); + } + } + break; + case FileIdentificationMapSelectionEnum::SELECTED: + overlayFilesEvent.addBrainordinateFileAndMap(mapFile, + fileAtts->getMapIndex(), + tabIndex); + break; + } + } + } + + /* + * Get the displayed and user selected file and return them + */ + std::vector displayedFiles = overlayFilesEvent.getFilesAndMaps(); + return displayedFiles; +} + +/** + * Get text for the tooltip for a selected node. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +AString +IdentificationFormattedTextGenerator::createToolTipText(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager) const +{ + CaretAssert(brain); + CaretAssert(browserTab); + CaretAssert(selectionManager); + CaretAssert(dataToolTipsManager); + + const SelectionItemSurfaceNode* selectedNode = selectionManager->getSurfaceNodeIdentification(); + const SelectionItemVoxel* selectedVoxel = selectionManager->getVoxelIdentification(); + + IdentificationStringBuilder idText; + + if (selectedNode->isValid()) { + generateSurfaceToolTip(brain, + browserTab, + selectionManager, + dataToolTipsManager, + idText); + } + else if (selectedVoxel->isValid()) { + generateVolumeToolTip(browserTab, + selectionManager, + dataToolTipsManager, + idText); + } + else { + generateChartToolTip(selectionManager, + dataToolTipsManager, + idText); + } + + AString text; + if (idText.length() > 0) { + text = idText.toStringWithHtmlBodyForToolTip(); + } + + return text; +} + + +/** + * Generate identification text for volume voxel identification. + * + * @param htmlTableBuilder + * Html table builder for identification text. + * @param brain + * The brain. + * @param idVolumeVoxel + * Information for volume voxel ID. + */ +void +IdentificationFormattedTextGenerator::generateVolumeVoxelIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const Brain* /*brain*/, + const SelectionItemVoxel* idVolumeVoxel) const +{ + if (idVolumeVoxel->isValid() == false) { + return; + } + + int64_t ijk[3]; + const VolumeMappableInterface* idVolumeFile = idVolumeVoxel->getVolumeFile(); + idVolumeVoxel->getVoxelIJK(ijk); + float x, y, z; + idVolumeFile->indexToSpace(ijk[0], ijk[1], ijk[2], x, y, z); + + const QString xyzText(AString::number(x, 'f', 2) + + ", " + + AString::number(y, 'f', 2) + + ", " + + AString::number(z, 'f', 2)); + + const QString ijkText("Voxel IJK (" + + AString::fromNumbers(ijk, 3, ", ") + + ")"); + + QString filename; + const CaretDataFile* caretDataFile = dynamic_cast(idVolumeFile); + if (caretDataFile != NULL) { + filename = caretDataFile->getFileNameNoPath(); + } + + htmlTableBuilder.addHeaderRow(ijkText, + xyzText, + filename); +} + +/** + * Generate identification text for volume data identification. + * + * @param labelHtmlTableBuilder + * HTML table builder for label identification text. + * @param scalarHtmlTableBuilder + * HTML table builder for scalar identification text. + * @param mapFile + * FIle for generating identification + * @param mapIndices + * Indices of map + * @param brain + * The brain. + * @param idVolumeVoxel + * Information for volume voxel ID. + */ +void +IdentificationFormattedTextGenerator::generateVolumeDataIdentificationText(HtmlTableBuilder& labelHtmlTableBuilder, + HtmlTableBuilder& scalarHtmlTableBuilder, + CaretMappableDataFile* mapFile, + const std::set& mapIndicesSet, + const Brain* brain, + const SelectionItemVoxel* idVolumeVoxel) const +{ + if (idVolumeVoxel->isValid() == false) { + return; + } + + int64_t ijk[3]; + const VolumeMappableInterface* idVolumeFile = idVolumeVoxel->getVolumeFile(); + idVolumeVoxel->getVoxelIJK(ijk); + float x, y, z; + idVolumeFile->indexToSpace(ijk[0], ijk[1], ijk[2], x, y, z); + const float xyz[3] = { x, y, z }; + + /* + * Get all volume files + */ + std::vector volumeInterfaces; + const int32_t numVolumeFiles = brain->getNumberOfVolumeFiles(); + for (int32_t i = 0; i < numVolumeFiles; i++) { + const VolumeFile* vf = brain->getVolumeFile(i); + volumeInterfaces.push_back(vf); + + const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); + if (volDynConnFile != NULL) { + if (volDynConnFile->isDataValid()) { + volumeInterfaces.push_back(volDynConnFile); + } + } + } + + /* + * Get the CIFTI files that are volume mappable + */ + std::vector allCiftiMappableDataFiles; + brain->getAllCiftiMappableDataFiles(allCiftiMappableDataFiles); + for (std::vector::iterator ciftiMapIter = allCiftiMappableDataFiles.begin(); + ciftiMapIter != allCiftiMappableDataFiles.end(); + ciftiMapIter++) { + const CiftiMappableDataFile* cmdf = *ciftiMapIter; + if (cmdf->isEmpty() == false) { + if (cmdf->isVolumeMappable()) { + volumeInterfaces.push_back(cmdf); + } + } + } + + const std::vector mapIndices(mapIndicesSet.begin(), + mapIndicesSet.end()); + const int32_t numMapIndices = static_cast(mapIndices.size()); + + /* + * In first loop, show values for 'idVolumeFile' (the underlay volume) + * In second loop, show values for all other volume files + */ + const VolumeMappableInterface* volumeInterfaceFile = dynamic_cast(mapFile); + if (volumeInterfaceFile != NULL) { + const VolumeFile* volumeFile = dynamic_cast(volumeInterfaceFile); + const CiftiMappableDataFile* ciftiFile = dynamic_cast(volumeInterfaceFile); + CaretAssert((volumeFile != NULL) + || (ciftiFile != NULL)); + + int64_t vfI, vfJ, vfK; + volumeInterfaceFile->enclosingVoxel(x, y, z, + vfI, vfJ, vfK); + + if (volumeInterfaceFile->indexValid(vfI, vfJ, vfK)) { + if (volumeFile != NULL) { + AString ijkText("IJK (" + + AString::number(vfI) + + ", " + + AString::number(vfJ) + + ", " + + AString::number(vfK) + + ") "); + + AString text; + + for (int32_t k = 0; k < numMapIndices; k++) { + CaretAssertVectorIndex(mapIndices, k); + const int32_t mapIndex = mapIndices[k]; + if (k > 0) { + text += " "; + } + if (volumeFile != NULL) { + if (volumeFile->getType() == SubvolumeAttributes::LABEL) { + const int32_t labelIndex = static_cast(volumeFile->getValue(vfI, vfJ, vfK, mapIndex)); + const GiftiLabelTable* glt = volumeFile->getMapLabelTable(mapIndex); + const GiftiLabel* gl = glt->getLabel(labelIndex); + if (gl != NULL) { + text += gl->getName(); + text += (" (" + + volumeFile->getMapName(mapIndex) + + ")"); + } + else { + text += ("LABLE_MISSING_FOR_INDEX=" + + AString::number(labelIndex)); + } + } + else if (volumeFile->getType() == SubvolumeAttributes::RGB) { + if (volumeFile->getNumberOfComponents() == 4) { + text += ("RGBA(" + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, mapIndex, 0)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, mapIndex, 1)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, mapIndex, 2)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, mapIndex, 3)) + + ")"); + } + else if (volumeFile->getNumberOfComponents() == 3) { + text += ("RGB(" + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, mapIndex, 0)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, mapIndex, 1)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, mapIndex, 2)) + + ")"); + } + } + else { + text += AString::number(volumeFile->getValue(vfI, vfJ, vfK, mapIndex)); + } + } + else if (ciftiFile != NULL) { + + } + } + + AString filename; + if (dynamic_cast(volumeFile) != NULL) { + filename.append(DataFileTypeEnum::toOverlayTypeName(DataFileTypeEnum::VOLUME_DYNAMIC) + " "); + } + filename.append(volumeFile->getFileNameNoPath()); + if (volumeFile->isMappedWithLabelTable()) { + labelHtmlTableBuilder.addRow(text, + filename); + } + else if (volumeFile->isMappedWithPalette()) { + scalarHtmlTableBuilder.addRow(text, + filename, + ""); + } + } + else if (ciftiFile != NULL) { + if (ciftiFile->isEmpty() == false) { + /* + * Does file have both label and scalar data + */ + const bool parcelDataFlag(isParcelAndScalarTypeFile(ciftiFile->getDataFileType())); + + AString textValue; + int64_t voxelIJK[3]; + const QString separator("
"); + if (ciftiFile->getVolumeVoxelIdentificationForMaps(mapIndices, + xyz, + separator, + voxelIJK, + textValue)) { + AString typeIJKText = (DataFileTypeEnum::toOverlayTypeName(ciftiFile->getDataFileType()) + + " " + + "IJK (" + + AString::number(voxelIJK[0]) + + ", " + + AString::number(voxelIJK[1]) + + ", " + + AString::number(voxelIJK[2]) + + ") "); + + + AString labelText; + if (ciftiFile->isMappedWithLabelTable()) { + labelText = textValue; + } + AString scalarText; + if (ciftiFile->isMappedWithPalette()) { + scalarText = textValue; + } + if (parcelDataFlag) { + /* + * Parcel data has parcel name, separator, scalar value 1, separator, scalar value 2, etc. + * Display parcel name in label table, and scalar + * value in the scalar table + */ + QStringList parcelAndValue = textValue.split(separator); + if (parcelAndValue.size() == static_cast(mapIndices.size() + 1)) { + labelText = parcelAndValue.at(0); + parcelAndValue.removeAt(0); + scalarText = parcelAndValue.join(separator); + } + } + + if ( ! labelText.isEmpty()) { + labelHtmlTableBuilder.addRow(labelText, + ciftiFile->getFileNameNoPath()); + } + if ( ! scalarText.isEmpty()) { + AString rowColumnIndex; + int64_t rowIndex(-1), columnIndex(-1); + if (ciftiFile->getRowColumnIndexFromVolumeXYZ(xyz, + rowIndex, + columnIndex)) { + if (rowIndex >= 0) { + rowColumnIndex = ("Row " + + AString::number(rowIndex + + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + } + if (columnIndex >= 0) { + if ( ! rowColumnIndex.isEmpty()) { + rowColumnIndex.append("
"); + } + rowColumnIndex.append("Column " + + AString::number(columnIndex + + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + } + } + scalarHtmlTableBuilder.addRow(scalarText, + ciftiFile->getFileNameNoPath(), + rowColumnIndex); + } + } + else { + /* no data */ + AString labelText; + if (ciftiFile->isMappedWithLabelTable()) { + labelHtmlTableBuilder.addRow(m_noDataText, + ciftiFile->getFileNameNoPath()); + } + if (ciftiFile->isMappedWithPalette()) { + scalarHtmlTableBuilder.addRow(m_noDataText, + ciftiFile->getFileNameNoPath()); + } + } + } + } + } + else { + if (mapFile->isMappedWithLabelTable()) { + labelHtmlTableBuilder.addRow(m_noDataText, + mapFile->getFileNameNoPath()); + } + if (mapFile->isMappedWithPalette()) { + scalarHtmlTableBuilder.addRow(m_noDataText, + mapFile->getFileNameNoPath()); + } + } + } +} + +/** + * Generate identification text for a surface vertex. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param brain + * The brain. + * @param browserTabContent + * Content of the browser tab. + * @param idSurfaceNode + * Information for surface node ID. + */ +void +IdentificationFormattedTextGenerator::generateSurfaceVertexIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const Brain* /*brain*/, + const SelectionItemSurfaceNode* idSurfaceNode) const +{ + const Surface* surface = idSurfaceNode->getSurface(); + const int32_t nodeNumber = idSurfaceNode->getNodeNumber(); + + if ((surface != NULL) + && (nodeNumber >= 0)) { + const float* xyz = surface->getCoordinate(nodeNumber); + const QString xyzText(AString::number(xyz[0]) + + ", " + + AString::number(xyz[1]) + + ", " + + AString::number(xyz[2])); + htmlTableBuilder.addHeaderRow(("VERTEX " + QString::number(nodeNumber)), + xyzText, + StructureEnum::toGuiName(surface->getStructure())); + } +} + +/** + * @return Is the given data file type a parcel and scalar type file? + * @param dataFileType + * Type of data file + */ +bool +IdentificationFormattedTextGenerator::isParcelAndScalarTypeFile(const DataFileTypeEnum::Enum dataFileType) const +{ + bool parcelDataFlag = false; + + switch (dataFileType) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + parcelDataFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + parcelDataFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + parcelDataFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + parcelDataFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + CaretAssert(0); + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; + } + + return parcelDataFlag; +} + +/** + * Generate identification text for a surface vertex. + * @param labelHtmlTableBuilder + * HTML table builder for label identification text. + * @param scalarHtmlTableBuilder + * HTML table builder for scalar identification text. + * @param mapFile + * FIle for generating identification + * @param mapIndices + * Indices of map + * @param brain + * The brain. + * @param browserTabContent + * Content of the browser tab. + * @param idSurfaceNode + * Information for surface node ID. + */ +void +IdentificationFormattedTextGenerator::generateSurfaceDataIdentificationText(HtmlTableBuilder& labelHtmlTableBuilder, + HtmlTableBuilder& scalarHtmlTableBuilder, + CaretMappableDataFile* mapFile, + const std::set& mapIndicesSet, + const Brain* /*brain*/, + const SelectionItemSurfaceNode* idSurfaceNode) const +{ + const Surface* surface = idSurfaceNode->getSurface(); + const int32_t nodeNumber = idSurfaceNode->getNodeNumber(); + + if ((surface != NULL) + && (nodeNumber >= 0)) { + LabelFile* labelFile(NULL); + MetricFile* metricFile(NULL); + MetricDynamicConnectivityFile* metricDynConnFile(NULL); + CiftiMappableDataFile* cmdf = dynamic_cast(mapFile); + if (cmdf == NULL) { + labelFile = dynamic_cast(mapFile); + if (labelFile == NULL) { + metricDynConnFile = dynamic_cast(mapFile); + if (metricDynConnFile == NULL) { + metricFile = dynamic_cast(mapFile); + } + } + } + + std::vector mapIndices(mapIndicesSet.begin(), + mapIndicesSet.end()); + const int numMapIndices = static_cast(mapIndices.size()); + + if (cmdf != NULL) { + AString boldText = (DataFileTypeEnum::toOverlayTypeName(cmdf->getDataFileType()) + + " " + + cmdf->getFileNameNoPath()); + + /* + * Does file have both label and scalar data + */ + const bool parcelDataFlag(isParcelAndScalarTypeFile(cmdf->getDataFileType())); + AString textValue; + + const AString separator("
"); + const bool valid = cmdf->getSurfaceNodeIdentificationForMaps(mapIndices, + surface->getStructure(), + nodeNumber, + surface->getNumberOfNodes(), + separator, + textValue); + if (valid) { + AString labelText; + if (cmdf->isMappedWithLabelTable()) { + labelText = textValue; + } + AString scalarText; + if (cmdf->isMappedWithPalette()) { + scalarText = textValue; + } + if (parcelDataFlag) { + /* + * Parcel data has parcel name, separator, scalar value 1, separator, scalar value 2, etc. + * Display parcel name in label table, and scalar + * value in the scalar table + */ + QStringList parcelAndValue = textValue.split(separator); + if (parcelAndValue.size() == static_cast(mapIndices.size() + 1)) { + labelText = parcelAndValue.at(0); + parcelAndValue.removeAt(0); + scalarText = parcelAndValue.join(separator); + } + } + + AString rowColumnIndex; + int64_t rowIndex(-1), columnIndex(-1); + if (cmdf->getRowColumnIndexFromSurfaceVertex(surface->getStructure(), + surface->getNumberOfNodes(), + nodeNumber, + rowIndex, + columnIndex)) { + if (rowIndex >= 0) { + rowColumnIndex = ("Row " + + AString::number(rowIndex + + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + } + if (columnIndex >= 0) { + if ( ! rowColumnIndex.isEmpty()) { + rowColumnIndex.append("
"); + } + rowColumnIndex.append("Column " + + AString::number(columnIndex + + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + } + } + if ( ! labelText.isEmpty()) { + labelHtmlTableBuilder.addRow(labelText, + cmdf->getFileNameNoPath()); + } + if ( ! scalarText.isEmpty()) { + scalarHtmlTableBuilder.addRow(scalarText, + cmdf->getFileNameNoPath(), + rowColumnIndex); + } + } + else { + if (cmdf->isMappedWithLabelTable()) { + labelHtmlTableBuilder.addRow(m_noDataText, + cmdf->getFileNameNoPath()); + } + if (cmdf->isMappedWithPalette()) { + scalarHtmlTableBuilder.addRow(m_noDataText, + cmdf->getFileNameNoPath()); + } + } + } + + if (labelFile != NULL) { + AString text; + for (int32_t k = 0; k < numMapIndices; k++) { + CaretAssertVectorIndex(mapIndices, k); + const int32_t mapIndex = mapIndices[k]; + if (k >= 1) { + text.append("
"); + } + AString labelName = labelFile->getLabelName(nodeNumber, mapIndex); + if (labelName.isEmpty()) { + labelName = ("Map-" + AString::number(mapIndex + 1)); + } + text.append(labelName); + text.append(" (" + + labelFile->getMapName(mapIndex) + + ")"); + } + labelHtmlTableBuilder.addRow(text, + labelFile->getFileNameNoPath()); + } + + if (metricFile != NULL) { + AString text; + for (int32_t k = 0; k < numMapIndices; k++) { + CaretAssertVectorIndex(mapIndices, k); + const int32_t mapIndex = mapIndices[k]; + if (k >= 1) { + text.append("
"); + } + text.append(AString::number(metricFile->getValue(nodeNumber, mapIndex))); + } + scalarHtmlTableBuilder.addRow(text, + metricFile->getFileNameNoPath(), + ""); + } + if (metricDynConnFile != NULL) { + if (metricDynConnFile->isDataValid()) { + if (metricDynConnFile->isEnabledAsLayer()) { + AString boldText = "METRIC DYNAMIC " + metricDynConnFile->getFileNameNoPath(); + AString text; + for (int32_t k = 0; k < numMapIndices; k++) { + CaretAssertVectorIndex(mapIndices, k); + const int32_t mapIndex = mapIndices[k]; + if (k >= 1) { + text.append("
"); + } + text += (" " + AString::number(metricDynConnFile->getValue(nodeNumber, mapIndex))); + } + scalarHtmlTableBuilder.addRow(text, + metricDynConnFile->getFileNameNoPath(), + ""); + } + } + } + } +} + +/** + * Find the usage of the file's maps in all overlays. + * + * @param caretMappableDataFile + * The file whose usage is desired. + * @param mapIndicesOut + * Indices of maps of the file that are used in overlays. + */ +void +IdentificationFormattedTextGenerator::getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, + std::vector& mapIndicesOut) const +{ + mapIndicesOut.clear(); + + EventBrowserTabGetAll allTabsEvent; + EventManager::get()->sendEvent(allTabsEvent.getPointer()); + const std::vector allTabs = allTabsEvent.getAllBrowserTabs(); + for (std::vector::const_iterator tabIter = allTabs.begin(); + tabIter != allTabs.end(); + tabIter++) { + BrowserTabContent* tabContent = *tabIter; + OverlaySet* overlaySet = tabContent->getOverlaySet(); + if (overlaySet != NULL) { + std::vector mapIndices; + overlaySet->getSelectedMapIndicesForFile(caretMappableDataFile, + false, // true => enabled overlays + mapIndices); + mapIndicesOut.insert(mapIndicesOut.end(), + mapIndices.begin(), + mapIndices.end()); + } + } + + /* + * Sort and remove all duplicates + */ + if (mapIndicesOut.empty() == false) { + std::sort(mapIndicesOut.begin(), + mapIndicesOut.end()); + std::vector::iterator uniqueIter = std::unique(mapIndicesOut.begin(), + mapIndicesOut.end()); + mapIndicesOut.resize(std::distance(mapIndicesOut.begin(), + uniqueIter)); + } +} + +/** + * Generate identification text for a data series chart. + * @param htmlTableBuilder + * String builder for identification text. + * @param idChartDataSeries + * Information for chart id. + */ +void +IdentificationFormattedTextGenerator::generateChartDataSeriesIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemChartDataSeries* idChartDataSeries) const +{ + if (idChartDataSeries->isValid()) { + const ChartDataCartesian* chartDataCartesian = idChartDataSeries->getChartDataCartesian(); + + const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); + generateChartDataSourceText(htmlTableBuilder, + "DATA SERIES CHART", + chartDataSource); + } +} + +/** + * Generate identification text for a data series chart. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idChartDataSeries + * Information for chart id. + */ +void +IdentificationFormattedTextGenerator::generateChartFrequencySeriesIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemChartFrequencySeries* idChartFrequencySeries) const +{ + if (idChartFrequencySeries->isValid()) { + const ChartDataCartesian* chartDataCartesian = idChartFrequencySeries->getChartDataCartesian(); + + const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); + generateChartDataSourceText(htmlTableBuilder, + "FREQUENCY SERIES CHART", + chartDataSource); + } +} + +/** + * Generate identification text for a matrix chart. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idChartMatrix + * Information for matrix chart id. + */ +void +IdentificationFormattedTextGenerator::generateChartMatrixIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemChartMatrix* idChartMatrix) const +{ + if (idChartMatrix->isValid()) { + const ChartableMatrixInterface* chartMatrixInterface = idChartMatrix->getChartableMatrixInterface(); + const CaretMappableDataFile* caretMappableDataFile = chartMatrixInterface->getMatrixChartCaretMappableDataFile(); + + const int32_t rowIndex = idChartMatrix->getMatrixRowIndex(); + const int32_t columnIndex = idChartMatrix->getMatrixColumnIndex(); + AString rowName; + AString columnName; + AString cellValue; + const bool validData = chartMatrixInterface->getMatrixCellAttributes(rowIndex, + columnIndex, + cellValue, + rowName, + columnName); + + AString boldText("MATRIX CHART"); + + if (validData) { + htmlTableBuilder.addRow(cellValue, + (("Row: " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())) + + "
" + + ("Column: " + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI()))), + caretMappableDataFile->getFileNameNoPath()); + } + else { + htmlTableBuilder.addRow(boldText, + "", + caretMappableDataFile->getFileNameNoPath()); + } + } +} + +/** + * Generate identification text for a chart two histogram. + * + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idText + * Identification string builder + * @param idChartTwoHistogram + * Information for selected chart two histogram. + * @param mapFile + * FIle for generating identification + * @param mapIndices + * Indices of map + * @param toolTipFlag + * If true, create tooltip text + */ +void +IdentificationFormattedTextGenerator::generateChartTwoHistogramIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoHistogram* idChartTwoHistogram, + CaretMappableDataFile* mapFile, + const std::set& mapIndices, + const bool toolTipFlag) const +{ + const int32_t mapIndex = idChartTwoHistogram->getMapIndex(); + const int32_t bucketIndex = idChartTwoHistogram->getBucketIndex(); + const bool allMapsFlag = idChartTwoHistogram->isAllMapsSelected(); + + if (idChartTwoHistogram->isValid()) { + ChartableTwoFileHistogramChart* fileHistogramChart = idChartTwoHistogram->getFileHistogramChart(); + CaretAssert(fileHistogramChart); + CaretMappableDataFile* chartMapFile = fileHistogramChart->getCaretMappableDataFile(); + CaretAssert(chartMapFile); + + { + ChartableTwoFileHistogramChart* chartingDelegate = chartMapFile->getChartingDelegate()->getHistogramCharting(); + CaretAssert(chartingDelegate); + const Histogram* histogram = chartingDelegate->getHistogramForChartDrawing(mapIndex, + allMapsFlag); + CaretAssert(histogram); + + float bucketValue = 0.0; + float bucketHeight = 0.0; + QString columnOne; + if (histogram->getHistogramDisplayBucketDataValueAndHeight(bucketIndex, bucketValue, bucketHeight)) { + if (toolTipFlag) { + AString boldText("Histogram"); + idText.addLine(false, + boldText, + chartMapFile->getFileNameNoPath()); + + idText.addLine(true, + "Bucket Index", + (AString::number(bucketIndex))); + + idText.addLine(true, + "Data Value at Bucket", + (AString::number(bucketValue))); + + const int64_t bucketHeightInteger = static_cast(bucketHeight); + idText.addLine(true, + "Bucket Count", + (AString::number(bucketHeightInteger))); + } + else { + if (mapFile == chartMapFile) { + if ((mapIndices.find(mapIndex) != mapIndices.end()) + || allMapsFlag) { + columnOne.append("Bucket: " + (AString::number(bucketIndex))); + columnOne.append("
Data Value: " + (AString::number(bucketValue))); + + const int64_t bucketHeightInteger = static_cast(bucketHeight); + columnOne.append("
Bucket Count: " + (AString::number(bucketHeightInteger))); + + htmlTableBuilder.addRow(columnOne, + "Histogram", + chartMapFile->getFileNameNoPath()); + } + } + } + } + } + } +} + +/** + * Generate identification text for a chart two line-layer. + * + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idText + * Identification string builder + * @param idChartTwoLineLayer + * Information for selected chart two line-layer. + * @param mapFile + * FIle for generating identification + * @param mapIndices + * Indices of map + * @param toolTipFlag + * If true, generate tooltip + */ +void +IdentificationFormattedTextGenerator::generateChartTwoLineLayerNearestIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineLayerVerticalNearest* idChartTwoLineLayer, + CaretMappableDataFile* mapFile, + const std::set& /*mapIndices*/, + const bool toolTipFlag) const +{ + if (idChartTwoLineLayer->isValid()) { + const ChartableTwoFileLineLayerChart* fileLineSeriesChart = idChartTwoLineLayer->getFileLineLayerChart(); + CaretAssert(fileLineSeriesChart); + const CaretMappableDataFile* chartMapFile = fileLineSeriesChart->getCaretMappableDataFile(); + CaretAssert(chartMapFile); + const ChartTwoDataCartesian* cartesianData = idChartTwoLineLayer->getChartTwoCartesianData(); + CaretAssert(cartesianData); + const MapFileDataSelector* mapFileDataSelector = cartesianData->getMapFileDataSelector(); + CaretAssert(mapFileDataSelector); + + if ( ! toolTipFlag) { + if (chartMapFile != mapFile) { + return; + } + } + int32_t primitiveIndex = idChartTwoLineLayer->getLineSegmentIndex(); + + AString boldText("Line Layer Chart"); + + cartesianData->getGraphicsPrimitive(); + const GraphicsPrimitive* primitive = cartesianData->getGraphicsPrimitive(); + CaretAssert(primitive); + + if (primitiveIndex >= 0) { + float xyz1[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz1); + + const int32_t nextIndex(((primitiveIndex + 1) < primitive->getNumberOfVertices()) + ? (primitiveIndex + 1) + : -1); + float xyz2[3]; + if (nextIndex >= 0) { + primitive->getVertexFloatXYZ(nextIndex, + xyz2); + } + + if (toolTipFlag) { + idText.addLine(true, + "XY Start", + AString::fromNumbers(xyz1, 2, ", ")); + if (nextIndex >= 0) { + idText.addLine(true, + "XY End ", + AString::fromNumbers(xyz2, 2, ", ")); + } + } + else { + AString text("XY Start:" + AString::fromNumbers(xyz1, 2, ", ")); + if (nextIndex >= 0) { + text.append(" XY End:" + AString::fromNumbers(xyz2, 2, ", ")); + } + htmlTableBuilder.addRow(text, + boldText, + chartMapFile->getFileNameNoPath()); + } + } + + generateMapFileSelectorText(htmlTableBuilder, + mapFileDataSelector); + } +} + +/** + * Generate identification text for a chart two line-layer. + * + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idText + * Identification string builder + * @param idChartTwoLineLayer + * Information for selected chart two line-layer. + * @param mapFile + * FIle for generating identification + * @param mapIndices + * Indices of map + * @param toolTipFlag + * If true, generate tooltip + */ +void +IdentificationFormattedTextGenerator::generateChartTwoLineLayerIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineLayer* idChartTwoLineLayer, + CaretMappableDataFile* mapFile, + const std::set& /*mapIndices*/, + const bool toolTipFlag) const +{ + if (idChartTwoLineLayer->isValid()) { + const ChartableTwoFileLineLayerChart* fileLineSeriesChart = idChartTwoLineLayer->getFileLineLayerChart(); + CaretAssert(fileLineSeriesChart); + const CaretMappableDataFile* chartMapFile = fileLineSeriesChart->getCaretMappableDataFile(); + CaretAssert(chartMapFile); + const ChartTwoDataCartesian* cartesianData = idChartTwoLineLayer->getChartTwoCartesianData(); + CaretAssert(cartesianData); + const MapFileDataSelector* mapFileDataSelector = cartesianData->getMapFileDataSelector(); + CaretAssert(mapFileDataSelector); + + if ( ! toolTipFlag) { + if (chartMapFile != mapFile) { + return; + } + } + int32_t primitiveIndex = idChartTwoLineLayer->getLineSegmentIndex(); + + AString boldText("Line Layer Chart"); + + cartesianData->getGraphicsPrimitive(); + const GraphicsPrimitive* primitive = cartesianData->getGraphicsPrimitive(); + CaretAssert(primitive); + + if (primitiveIndex >= 0) { + float xyz1[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz1); + + const int32_t nextIndex(((primitiveIndex + 1) < primitive->getNumberOfVertices()) + ? (primitiveIndex + 1) + : -1); + float xyz2[3]; + if (nextIndex >= 0) { + primitive->getVertexFloatXYZ(nextIndex, + xyz2); + } + + if (toolTipFlag) { + idText.addLine(true, + "XY Start", + AString::fromNumbers(xyz1, 2, ", ")); + if (nextIndex >= 0) { + idText.addLine(true, + "XY End ", + AString::fromNumbers(xyz2, 2, ", ")); + } + } + else { + AString text("XY Start:" + AString::fromNumbers(xyz1, 2, ", ")); + if (nextIndex >= 0) { + text.append(" XY End:" + AString::fromNumbers(xyz2, 2, ", ")); + } + htmlTableBuilder.addRow(text, + boldText, + chartMapFile->getFileNameNoPath()); + } + } + + generateMapFileSelectorText(htmlTableBuilder, + mapFileDataSelector); + } +} + +/** + * Generate identification text for a chart two line-series. + * + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idText + * Identification string builder + * @param idChartTwoLineSeries + * Information for selected chart two line-series. + * @param mapFile + * FIle for generating identification + * @param mapIndices + * Indices of map + * @param toolTipFlag + * If true, generate tooltip + */ +void +IdentificationFormattedTextGenerator::generateChartTwoLineSeriesIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineSeries* idChartTwoLineSeries, + CaretMappableDataFile* mapFile, + const std::set& /*mapIndices*/, + const bool toolTipFlag) const +{ + if (idChartTwoLineSeries->isValid()) { + const ChartableTwoFileLineSeriesChart* fileLineSeriesChart = idChartTwoLineSeries->getFileLineSeriesChart(); + CaretAssert(fileLineSeriesChart); + const CaretMappableDataFile* chartMapFile = fileLineSeriesChart->getCaretMappableDataFile(); + CaretAssert(chartMapFile); + const ChartTwoDataCartesian* cartesianData = idChartTwoLineSeries->getChartTwoCartesianData(); + CaretAssert(cartesianData); + const MapFileDataSelector* mapFileDataSelector = cartesianData->getMapFileDataSelector(); + CaretAssert(mapFileDataSelector); + + if ( ! toolTipFlag) { + if (chartMapFile != mapFile) { + return; + } + } + int32_t primitiveIndex = idChartTwoLineSeries->getLineSegmentIndex(); + + AString boldText("Line Series Chart"); + + cartesianData->getGraphicsPrimitive(); + const GraphicsPrimitive* primitive = cartesianData->getGraphicsPrimitive(); + CaretAssert(primitive); + + if (primitiveIndex >= 1) { + float xyz1[3]; + primitive->getVertexFloatXYZ(primitiveIndex - 1, + xyz1); + float xyz2[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz2); + if (toolTipFlag) { + idText.addLine(true, + "XY Start", + AString::fromNumbers(xyz1, 2, ", ")); + idText.addLine(true, + "XY End ", + AString::fromNumbers(xyz2, 2, ", ")); + } + else { + htmlTableBuilder.addRow(("XY Start:" + AString::fromNumbers(xyz1, 2, ", ")) + + ("XY End:" + AString::fromNumbers(xyz2, 2, ", ")), + boldText, + chartMapFile->getFileNameNoPath()); + } + } + else { + float xyz[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz); + if (toolTipFlag) { + idText.addLine(true, + "XY", + AString::fromNumbers(xyz, 2, ", ")); + } + else { + htmlTableBuilder.addRow(("XY:" + AString::fromNumbers(xyz, 2, ", ")), + boldText, + chartMapFile->getFileNameNoPath()); + } + } + + generateMapFileSelectorText(htmlTableBuilder, + mapFileDataSelector); + } +} + +/** + * Generate identification text for a chart two matrix. + * + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idText + * Identification string builder + * @param idChartTwoMatrix + * Information for selected chart two matrix. + * @param mapFile + * FIle for generating identification + * @param mapIndices + * Indices of map + * @param toolTipFlag + * If true, generate tool tip + */ +void +IdentificationFormattedTextGenerator::generateChartTwoMatrixIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoMatrix* idChartTwoMatrix, + CaretMappableDataFile* mapFile, + const std::set& /*mapIndices*/, + const bool toolTipFlag) const +{ + if (idChartTwoMatrix->isValid()) { + const ChartableTwoFileMatrixChart* matrixChart = idChartTwoMatrix->getFileMatrixChart(); + CaretAssert(matrixChart); + + const int32_t rowIndex = idChartTwoMatrix->getRowIndex(); + const int32_t colIndex = idChartTwoMatrix->getColumnIndex(); + + const CaretMappableDataFile* chartMapFile = matrixChart->getCaretMappableDataFile(); + CaretAssert(chartMapFile); + + if ( ! toolTipFlag) { + if (chartMapFile != mapFile) { + return; + } + } + + AString boldText("MATRIX "); + QString rowText; + QString colText; + if (rowIndex >= 0) { + if (matrixChart->hasRowSelection()) { + rowText = matrixChart->getRowName(rowIndex); + } + if (rowText.isEmpty()) { + rowText = ("Row: " + + AString::number(rowIndex + + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + } + } + if (colIndex >= 0) { + if (matrixChart->hasColumnSelection()) { + colText = matrixChart->getColumnName(colIndex); + } + if (colText.isEmpty()) { + colText = ("Col: " + + AString::number(colIndex + + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + } + } + + AString dataValueText; + if ((rowIndex >= 0) + && (colIndex >= 0)) { + const CiftiMappableDataFile* ciftiFile = matrixChart->getCiftiMappableDataFile(); + if (ciftiFile != NULL) { + MapFileDataSelector mapSelector; + mapSelector.setRowIndex(const_cast(chartMapFile), + "", + rowIndex); + std::vector rowData; + ciftiFile->getDataForSelector(mapSelector, rowData); + if ( ! rowData.empty()) { + if (colIndex < static_cast(rowData.size())) { + CaretAssertVectorIndex(rowData, colIndex); + dataValueText = AString::number(rowData[colIndex], 'f', 3); + } + } + } + } + + if (( ! colText.isEmpty()) + || ( ! rowText.isEmpty())) { + if (toolTipFlag) { + if ( ! colText.isEmpty()) { + colText.append(" "); + } + if ( ! dataValueText.isEmpty()) { + dataValueText.append(" "); + } + idText.addLine(true, + (dataValueText + rowText + "; " + colText), + chartMapFile->getFileNameNoPath()); + } + else { + htmlTableBuilder.addRow(dataValueText, + (rowText + "; " + colText), + chartMapFile->getFileNameNoPath()); + } + } + } +} + +/** + * Generate identification text for a CIFTI Connectivity Matrix Row/Column + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idCiftiConnMatrix + * Information for CIFTI Connectivity Matrix Row/Column. + */ +void +IdentificationFormattedTextGenerator::generateCiftiConnectivityMatrixIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const +{ + if (idCiftiConnMatrix->isValid()) { + const CiftiMappableConnectivityMatrixDataFile* connMatrixFile = idCiftiConnMatrix->getCiftiConnectivityMatrixFile(); + const int32_t rowIndex = idCiftiConnMatrix->getMatrixRowIndex(); + const int32_t colIndex = idCiftiConnMatrix->getMatrixColumnIndex(); + + AString colTwoText("MATRIX ROW/COLUMN"); + AString colThreeText(connMatrixFile->getFileNameNoPath()); + + AString colOneText; + AString rowName = " "; + AString colName = " "; + bool validData = true; + if (validData) { + if (rowIndex >= 0) { + colOneText = ("Row " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI()) + + "
" + + rowName); + } + if (colIndex >= 0) { + colOneText = ("Column " + AString::number(colIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI()) + + "
" + + colName); + } + } + + htmlTableBuilder.addRow(colOneText, + colTwoText, + colThreeText); + } +} + +/** + * Generate identification text for chart data source. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param typeOfChartText + * Text describing the type of chart. + * @param chartDataSource + * Source of chart data. + */ +void +IdentificationFormattedTextGenerator::generateChartDataSourceText(HtmlTableBuilder& htmlTableBuilder, + const AString& typeOfChartText, + const ChartDataSource* chartDataSource) const +{ + AString chartFileName = chartDataSource->getChartableFileName(); + if (! chartFileName.isEmpty()) { + chartFileName = FileInformation(chartFileName).getFileName(); + } + + AString columnOne; + switch (chartDataSource->getDataSourceMode()) { + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: + { + AString fileName; + int32_t rowIndex; + chartDataSource->getFileRow(fileName, + rowIndex); + chartFileName = fileName; + columnOne = ("Row " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: + { + AString structureName; + int32_t numberOfNodes; + int32_t nodeIndex; + chartDataSource->getSurfaceNode(structureName, + numberOfNodes, + nodeIndex); + columnOne = ("Vertex Index " + AString::number(nodeIndex)); + columnOne.append("
Structure " + structureName); + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: + { + AString structureName; + int32_t numberOfNodes; + std::vector nodeIndices; + chartDataSource->getSurfaceNodeAverage(structureName, numberOfNodes, nodeIndices); + columnOne = ("Vertex Avg Count " + AString::number(nodeIndices.size())); + columnOne.append("
Structure " + structureName); + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: + { + float voxelXYZ[3]; + chartDataSource->getVolumeVoxel(voxelXYZ); + columnOne = ("Voxel XYZ " + AString::fromNumbers(voxelXYZ, 3, ",")); + } + break; + } + + if (! columnOne.isEmpty()) { + htmlTableBuilder.addRow(columnOne, + typeOfChartText, + chartFileName); + } +} + +/** + * Generate text for a map file data selector. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param mapFileDataSelector + * The map file data selector. + */ +void +IdentificationFormattedTextGenerator::generateMapFileSelectorText(HtmlTableBuilder& htmlTableBuilder, + const MapFileDataSelector* mapFileDataSelector) const +{ + + switch (mapFileDataSelector->getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t columnIndex = -1; + mapFileDataSelector->getColumnIndex(mapFile, + mapFileName, + columnIndex); + htmlTableBuilder.addRow("Column: " + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI()), + "", + mapFileName); + } + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t rowIndex = -1; + mapFileDataSelector->getRowIndex(mapFile, + mapFileName, + rowIndex); + htmlTableBuilder.addRow("Row: " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI()), + "", + mapFileName); + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t numberOfVertices = 0; + int32_t vertexIndex = -1; + mapFileDataSelector->getSurfaceVertex(structure, + numberOfVertices, + vertexIndex); + + if ((structure != StructureEnum::INVALID) + && (vertexIndex >= 0)) { + htmlTableBuilder.addRow("Vertex: " + AString::number(vertexIndex), + "Structure: " + StructureEnum::toGuiName(structure), + ""); + } + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t numberOfVertices = 0; + std::vector vertexIndices; + mapFileDataSelector->getSurfaceVertexAverage(structure, + numberOfVertices, + vertexIndices); + + const int32_t averageCount = static_cast(vertexIndices.size()); + if ((structure != StructureEnum::INVALID) + && (averageCount > 0)) { + htmlTableBuilder.addRow("Vertex Avg Count: " + AString::number(averageCount), + "Structure: " + StructureEnum::toGuiName(structure), + ""); + } + } + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + { + float voxelXYZ[3]; + mapFileDataSelector->getVolumeVoxelXYZ(voxelXYZ); + htmlTableBuilder.addRow("Voxel XYZ: " + AString::fromNumbers(voxelXYZ, 3, ",")); + } + break; + } +} + + +/** + * Generate identification text for a time series chart. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idChartTimeSeries + * Information for chart id. + */ +void +IdentificationFormattedTextGenerator::generateChartTimeSeriesIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemChartTimeSeries* idChartTimeSeries) const +{ + if (idChartTimeSeries->isValid()) { + const ChartDataCartesian* chartDataCartesian = idChartTimeSeries->getChartDataCartesian(); + + const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); + generateChartDataSourceText(htmlTableBuilder, + "TIME SERIES CHART", + chartDataSource); + } +} + +/** + * Generate identification text for a surface border identification. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idText + * Identification string builder + * @param idSurfaceBorder + * Information for surface border ID. + * @param toolTipFlag + * True if this is for tooltip. + */ +void +IdentificationFormattedTextGenerator::generateSurfaceBorderIdentifcationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemBorderSurface* idSurfaceBorder, + const bool toolTipFlag) const +{ + if (idSurfaceBorder->isValid()) { + const Border* border = idSurfaceBorder->getBorder(); + const SurfaceProjectedItem* spi = border->getPoint(idSurfaceBorder->getBorderPointIndex()); + float xyz[3]; + spi->getProjectedPosition(*idSurfaceBorder->getSurface(), xyz, false); + + if (toolTipFlag) { + bool indentFlag = false; + idText.addLine(indentFlag, + "Border", + border->getName()); + indentFlag = true; + idText.addLine(indentFlag, + "XYZ", + AString::fromNumbers(xyz, 3, ",")); + } + else { + const AString numberIndexText = ("(" + + AString::number(idSurfaceBorder->getBorderIndex()) + + "," + + AString::number(idSurfaceBorder->getBorderPointIndex())); + + htmlTableBuilder.addRow(AString::fromNumbers(xyz, 3, ","), + ("BORDER " + numberIndexText), + ("Name: " + border->getName() + + "
Class: " + border->getClassName())); + + } + } +} + +/** + * Generate identification text for a surface focus identification. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idSurfaceFocus + * Information for surface focus ID. + * @param toolTipFlag + * True if this is for tooltip. + */ +void +IdentificationFormattedTextGenerator::generateSurfaceFocusIdentifcationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemFocusSurface* idSurfaceFocus, + const bool toolTipFlag) const +{ + if (idSurfaceFocus->isValid()) { + const Focus* focus = idSurfaceFocus->getFocus(); + const int32_t projectionIndex = idSurfaceFocus->getFocusProjectionIndex(); + + IdentificationStringBuilder idText; + generateFocusIdentifcationText(htmlTableBuilder, + idText, + focus, + idSurfaceFocus->getFocusIndex(), + projectionIndex, + idSurfaceFocus->getSurface(), + toolTipFlag); + } +} + +/** + * Generate identification text for a surface focus identification. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idText + * String builder for identification text. + * @param focus + * The focus. + * @param focusIndex + * Index of focus + * @param projectionIndex + * Index of projection + * @param surface + * Surface for focus (may be NULL) + * @param toolTipFlag + * True if this is for tooltip. + */ +void +IdentificationFormattedTextGenerator::generateFocusIdentifcationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const Focus* focus, + const int32_t focusIndex, + const int32_t projectionIndex, + const Surface* surface, + const bool toolTipFlag) const +{ + const SurfaceProjectedItem* spi = focus->getProjection(projectionIndex); + float xyzStereo[3]; + spi->getStereotaxicXYZ(xyzStereo); + const AString stereoXYZText((spi->isStereotaxicXYZValid() + ? AString::fromNumbers(xyzStereo, 3, ",") + : "Invalid")); + if (toolTipFlag) { + bool indentFlag = false; + idText.addLine(indentFlag, + "Focus", + focus->getName()); + indentFlag = true; + idText.addLine(indentFlag, + "XYZ", + stereoXYZText); + } + else { + htmlTableBuilder.addRow(stereoXYZText, + ("FOCUS " + AString::number(focusIndex)), + ("Name: " + focus->getName() + "
Class: " + focus->getClassName())); + if (surface != NULL) { + float xyzProj[3]; + if (spi->getProjectedPosition(*surface, xyzProj, false)) { + bool projValid = false; + AString xyzProjName = "XYZ (Projected)"; + if (spi->getBarycentricProjection()->isValid()) { + xyzProjName = "(Projected to Triangle)"; + projValid = true; + } + else if (spi->getVanEssenProjection()->isValid()) { + xyzProjName = "(Projected to Edge)"; + projValid = true; + } + if (projValid) { + idText.addLine(true, + xyzProjName, + xyzProj, + 3, + true); + htmlTableBuilder.addRow((AString::fromNumbers(xyzProj, 3, ", ") + xyzProjName), + StructureEnum::toGuiName(spi->getStructure()), + ""); + } + } + } + } +} + +/** + * Generate identification text for a volume focus identification. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idVolumeFocus + * Information for surface focus ID. + */ +void +IdentificationFormattedTextGenerator::generateVolumeFocusIdentifcationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemFocusVolume* idVolumeFocus) const +{ + if (idVolumeFocus->isValid()) { + const Focus* focus = idVolumeFocus->getFocus(); + const SurfaceProjectedItem* spi = focus->getProjection(idVolumeFocus->getFocusProjectionIndex()); + float xyzVolume[3]; + spi->getVolumeXYZ(xyzVolume); + float xyzStereo[3]; + spi->getStereotaxicXYZ(xyzStereo); + + IdentificationStringBuilder idText; + generateFocusIdentifcationText(htmlTableBuilder, + idText, + focus, + idVolumeFocus->getFocusIndex(), + idVolumeFocus->getFocusProjectionIndex(), + NULL, + false); + } +} + +/** + * Generate identification text for image identification. + * @param htmlTableBuilder + * HTML table builder for identification text. + * @param idImage + * Information for image ID. + */ +void +IdentificationFormattedTextGenerator::generateImageIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemImage* idImage) const +{ + if (idImage->isValid()) { + uint8_t pixelRGBA[4] = { 0, 0, 0, 0 }; + idImage->getPixelRGBA(pixelRGBA); + const ImageFile* imageFile = idImage->getImageFile(); + if (imageFile != NULL) { + htmlTableBuilder.addRow("Filename", + imageFile->getFileNameNoPath()); + } + htmlTableBuilder.addRow(("RGBA (" + AString::fromNumbers(pixelRGBA, 4, ",") + ")"), + ("Pixel IJ (" + + AString::number(idImage->getPixelI()) + + "," + + AString::number(idImage->getPixelJ()) + + ")"), + idImage->getImageFile()->getFileNameNoPath()); + + } +} + +/** + * Get text for the tooltip for a selected node. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationFormattedTextGenerator::generateSurfaceToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + std::unique_ptr htmlTableBuilder = createHtmlTableBuilder(3); + + const SelectionItemSurfaceNode* nodeSelection = selectionManager->getSurfaceNodeIdentification(); + CaretAssert(nodeSelection); + if (nodeSelection->isValid()) { + const Surface* surface = nodeSelection->getSurface(); + CaretAssert(surface); + int32_t surfaceNumberOfNodes = surface->getNumberOfNodes(); + int32_t surfaceNodeIndex = nodeSelection->getNodeNumber(); + StructureEnum::Enum surfaceStructure = surface->getStructure(); + + bool indentFlag = false; + if ((surfaceStructure != StructureEnum::INVALID) + && (surfaceNumberOfNodes > 0) + && (surfaceNodeIndex >= 0)) { + + bool addVertexFlag(false); + bool showSurfaceFlag = dataToolTipsManager->isShowSurfaceViewed(); + if (dataToolTipsManager->isShowSurfacePrimaryAnatomical()) { + const Surface* anatSurface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); + if (anatSurface != NULL) { + if (anatSurface->getNumberOfNodes() == surfaceNumberOfNodes) { + float xyz[3]; + anatSurface->getCoordinate(surfaceNodeIndex, + xyz); + idText.addLine(indentFlag, + "Vertex", + AString::number(surfaceNodeIndex)); + indentFlag = true; + addVertexFlag = false; + + idText.addLine(indentFlag, + "Anatomy Surface", + AString::fromNumbers(xyz, 3, ", ", 'f', 2)); + if (surface == anatSurface) { + showSurfaceFlag = false; + } + } + } + } + + if (showSurfaceFlag) { + float xyz[3]; + surface->getCoordinate(surfaceNodeIndex, + xyz); + if (addVertexFlag) { + idText.addLine(indentFlag, + "Vertex", + AString::number(surfaceNodeIndex)); + indentFlag = true; + } + + idText.addLine(indentFlag, + (SurfaceTypeEnum::toGuiName(surface->getSurfaceType()) + + " Surface"), + AString::fromNumbers(xyz, 3, ", ")); + } + + if (dataToolTipsManager->isShowTopEnabledLayer()) { + OverlaySet* overlaySet = const_cast(browserTab->getOverlaySet()); + CaretAssert(overlaySet); + Overlay* overlay = getTopEnabledOverlay(overlaySet); + if (overlay != NULL) { + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + mapFile->getSurfaceNodeIdentificationForMaps(mapIndices, + surfaceStructure, + surfaceNodeIndex, + surfaceNumberOfNodes, + " ", + textValue); + if ( ! textValue.isEmpty()) { + idText.addLine(indentFlag, + "Top Enabled Layer", + textValue); + } + } + } + } + } + } + + if (dataToolTipsManager->isShowBorder()) { + const SelectionItemBorderSurface* borderSelection = selectionManager->getSurfaceBorderIdentification(); + CaretAssert(borderSelection); + if (borderSelection->isValid()) { + generateSurfaceBorderIdentifcationText(*htmlTableBuilder, + idText, + borderSelection, + true); + } + } + + if (dataToolTipsManager->isShowFocus()) { + const SelectionItemFocusSurface* focusSelection = selectionManager->getSurfaceFocusIdentification(); + CaretAssert(focusSelection); + if (focusSelection->isValid()) { + const FociFile* fociFile = focusSelection->getFociFile(); + const int32_t focusIndex = focusSelection->getFocusIndex(); + if ((fociFile != NULL) + && (focusIndex >= 0)) { + const Focus* focus = fociFile->getFocus(focusIndex); + if (focus != NULL) { + generateFocusIdentifcationText(*htmlTableBuilder, + idText, + focus, + focusIndex, + focusSelection->getFocusProjectionIndex(), + focusSelection->getSurface(), + true); + } + } + } + } +} + +/** + * Get text for the tooltip for a selected node. + * + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationFormattedTextGenerator::generateVolumeToolTip(const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + const SelectionItemVoxel* voxelSelection = selectionManager->getVoxelIdentification(); + + OverlaySet* overlaySet = const_cast(browserTab->getOverlaySet()); + CaretAssert(overlaySet); + + double selectionXYZ[3]; + voxelSelection->getModelXYZ(selectionXYZ); + float xyz[3] { + static_cast(selectionXYZ[0]), + static_cast(selectionXYZ[1]), + static_cast(selectionXYZ[2]) + }; + + bool indentFlag = false; + if (dataToolTipsManager->isShowVolumeUnderlay()) { + Overlay* volumeUnderlay = overlaySet->getUnderlayContainingVolume(); + if (volumeUnderlay != NULL) { + CaretMappableDataFile* mapFile = NULL; + int32_t mapIndex(-1); + volumeUnderlay->getSelectionData(mapFile, + mapIndex); + + VolumeMappableInterface* underlayVolumeInterface = NULL; + if (mapFile != NULL) { + underlayVolumeInterface = dynamic_cast(mapFile); + CaretAssert(underlayVolumeInterface == overlaySet->getUnderlayVolume()); + } + + if (underlayVolumeInterface != NULL) { + /* + * Update IJK and XYZ since selection XYZ may be + * a different volume file. + */ + int64_t selectionIJK[3]; + voxelSelection->getVoxelIJK(selectionIJK); + int64_t ijk[3] { selectionIJK[0], selectionIJK[1], selectionIJK[2] }; + + + bool validFlag(false); + const float value = underlayVolumeInterface->getVoxelValue(xyz[0], xyz[1], xyz[2], + &validFlag, + mapIndex); + if (validFlag) { + underlayVolumeInterface->enclosingVoxel(xyz[0], xyz[1], xyz[2], + ijk[0], ijk[1], ijk[2]); + underlayVolumeInterface->indexToSpace(ijk, xyz); + idText.addLine(indentFlag, + "Underlay Value", + AString::number(value, 'f')); + indentFlag = true; + idText.addLine(indentFlag, + "IJK: ", + AString::fromNumbers(ijk, 3, ", ")); + idText.addLine(indentFlag, + "XYZ", + AString::fromNumbers(xyz, 3, ", ", 'f', 1)); + } + } + } + } + + if (dataToolTipsManager->isShowTopEnabledLayer()) { + Overlay* overlay = getTopEnabledOverlay(overlaySet); + if (overlay != NULL) { + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + int64_t ijk[3]; + mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, + xyz, + " ", + ijk, + textValue); + if ( ! textValue.isEmpty()) { + idText.addLine(indentFlag, + ("Top Enabled Layer: " + + textValue)); + } + } + } + } +} + +/** + * @return Get the top-most enabled overlay. NULL if no overlays enabled + * + * @param overlaySet + * Overlay set for overlay. + */ +Overlay* +IdentificationFormattedTextGenerator::getTopEnabledOverlay(OverlaySet* overlaySet) const +{ + CaretAssert(overlaySet); + const int32_t numberOfOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numberOfOverlays; i++) { + Overlay* overlay = overlaySet->getOverlay(i); + CaretAssert(overlay); + if (overlay->isEnabled()) { + return overlay; + } + } + return NULL; +} + +/** + * Get text for the tooltip for a selected node. + * + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationFormattedTextGenerator::generateChartToolTip(const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + if (dataToolTipsManager->isShowChart()) { + auto htmlTableBuilder = createHtmlTableBuilder(3); + + CaretMappableDataFile* dummyMapFile(NULL); + std::set dummyMapIndices; + this->generateChartTwoHistogramIdentificationText(*htmlTableBuilder, + idText, + selectionManager->getChartTwoHistogramIdentification(), + dummyMapFile, + dummyMapIndices, + true); + + this->generateChartTwoLineSeriesIdentificationText(*htmlTableBuilder, + idText, + selectionManager->getChartTwoLineSeriesIdentification(), + dummyMapFile, + dummyMapIndices, + true); + + this->generateChartTwoMatrixIdentificationText(*htmlTableBuilder, + idText, + selectionManager->getChartTwoMatrixIdentification(), + dummyMapFile, + dummyMapIndices, + true); + } +} + + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +IdentificationFormattedTextGenerator::toString() const +{ + return "IdentificationFormattedTextGenerator"; +} + +/** + * @return New instance of an HTML Table builder + * @param numberOfColumns + * Number of columns in the table + */ +std::unique_ptr +IdentificationFormattedTextGenerator::createHtmlTableBuilder(const int32_t numberOfColumns) const +{ + std::unique_ptr htb(new HtmlTableBuilder(HtmlTableBuilder::HtmlVersion::V4_01, + numberOfColumns)); + return htb; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationFormattedTextGenerator.h connectome-workbench-1.5.0/src/Brain/IdentificationFormattedTextGenerator.h --- connectome-workbench-1.4.2/src/Brain/IdentificationFormattedTextGenerator.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationFormattedTextGenerator.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,228 @@ +#ifndef __IDENTIFICATION_FORMATTED_TEXT_GENERATOR__H_ +#define __IDENTIFICATION_FORMATTED_TEXT_GENERATOR__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#include "CaretObject.h" +#include "DataFileTypeEnum.h" +#include "EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h" + +namespace caret { + + class Brain; + class BrowserTabContent; + class CaretMappableDataFile; + class ChartDataSource; + class DataToolTipsManager; + class Focus; + class HtmlTableBuilder; + class IdentificationFilter; + class MapFileDataSelector; + class Overlay; + class OverlaySet; + class SelectionItemBorderSurface; + class SelectionItemChartDataSeries; + class SelectionItemChartFrequencySeries; + class SelectionItemChartMatrix; + class SelectionItemChartTwoHistogram; + class SelectionItemChartTwoLineLayer; + class SelectionItemChartTwoLineLayerVerticalNearest; + class SelectionItemChartTwoLineSeries; + class SelectionItemChartTwoMatrix; + class SelectionItemCiftiConnectivityMatrixRowColumn; + class SelectionItemChartTimeSeries; + class SelectionItemFocusSurface; + class SelectionItemFocusVolume; + class SelectionItemImage; + class SelectionItemSurfaceNode; + class SelectionItemVoxel; + class SelectionManager; + class Surface; + class IdentificationStringBuilder; + + class IdentificationFormattedTextGenerator : public CaretObject { + + public: + IdentificationFormattedTextGenerator(); + + virtual ~IdentificationFormattedTextGenerator(); + + AString createIdentificationText(const SelectionManager* idManager, + const Brain* brain, + const int32_t tabIndex) const; + + AString createToolTipText(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager) const; + + private: + IdentificationFormattedTextGenerator(const IdentificationFormattedTextGenerator&); + + IdentificationFormattedTextGenerator& operator=(const IdentificationFormattedTextGenerator&); + + public: + virtual AString toString() const; + + private: + std::vector getFilesForIdentification(const IdentificationFilter* filter, + const int32_t tabIndex) const; + + void generateSurfaceToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + + void generateVolumeToolTip(const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + + void generateChartToolTip(const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + + void generateSurfaceBorderIdentifcationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemBorderSurface* idSurfaceBorder, + const bool toolTipFlag) const; + + void generateSurfaceFocusIdentifcationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemFocusSurface* idSurfaceFocus, + const bool toolTipFlag) const; + + void generateVolumeFocusIdentifcationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemFocusVolume* idVolumeFocus) const; + + void generateFocusIdentifcationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const Focus* focus, + const int32_t focusIndex, + const int32_t projectionIndex, + const Surface* surface, + const bool toolTipFlag) const; + + void generateSurfaceVertexIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const Brain* brain, + const SelectionItemSurfaceNode* idSurfaceNode) const; + + void generateSurfaceDataIdentificationText(HtmlTableBuilder& labelHtmlTableBuilder, + HtmlTableBuilder& scalarHtmlTableBuilder, + CaretMappableDataFile* mapFile, + const std::set& mapIndices, + const Brain* brain, + const SelectionItemSurfaceNode* idSurfaceNode) const; + + void generateImageIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemImage* idImage) const; + + void generateVolumeVoxelIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const Brain* brain, + const SelectionItemVoxel* idVolumeVoxel) const; + + void generateVolumeDataIdentificationText(HtmlTableBuilder& labelHtmlTableBuilder, + HtmlTableBuilder& scalarHtmlTableBuilder, + CaretMappableDataFile* mapFile, + const std::set& mapIndices, + const Brain* brain, + const SelectionItemVoxel* idVolumeVoxel) const; + + void generateChartDataSeriesIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemChartDataSeries* idChartDataSeries) const; + + void generateChartFrequencySeriesIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemChartFrequencySeries* idChartFrequencySeries) const; + + void generateChartMatrixIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemChartMatrix* idChartMatrix) const; + + void generateChartTwoHistogramIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoHistogram* idChartTwoHistogram, + CaretMappableDataFile* mapFile, + const std::set& mapIndices, + const bool toolTipFlag) const; + + void generateChartTwoLineLayerNearestIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineLayerVerticalNearest* idChartTwoLineLayer, + CaretMappableDataFile* mapFile, + const std::set& mapIndices, + const bool toolTipFlag) const; + + void generateChartTwoLineLayerIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineLayer* idChartTwoLineLayer, + CaretMappableDataFile* mapFile, + const std::set& mapIndices, + const bool toolTipFlag) const; + + void generateChartTwoLineSeriesIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineSeries* idChartTwoLineSeries, + CaretMappableDataFile* mapFile, + const std::set& mapIndices, + const bool toolTipFlag) const; + + void generateChartTwoMatrixIdentificationText(HtmlTableBuilder& htmlTableBuilder, + IdentificationStringBuilder& idText, + const SelectionItemChartTwoMatrix* idChartTwoMatrix, + CaretMappableDataFile* mapFile, + const std::set& mapIndices, + const bool toolTipFlag) const; + + void generateCiftiConnectivityMatrixIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const; + + void generateChartTimeSeriesIdentificationText(HtmlTableBuilder& htmlTableBuilder, + const SelectionItemChartTimeSeries* idChartTimeSeries) const; + + void getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, + std::vector& mapIndicesOut) const; + + void generateChartDataSourceText(HtmlTableBuilder& htmlTableBuilder, + const AString& typeOfChartText, + const ChartDataSource* chartDataSource) const; + + void generateMapFileSelectorText(HtmlTableBuilder& htmlTableBuilder, + const MapFileDataSelector* mapFileDataSelector) const; + + Overlay* getTopEnabledOverlay(OverlaySet* overlaySet) const; + + std::unique_ptr createHtmlTableBuilder(const int32_t numberOfColumns) const; + + bool isParcelAndScalarTypeFile(const DataFileTypeEnum::Enum dataFileType) const; + + const AString m_noDataText; + + friend class DataToolTipsManager; + }; + +#ifdef __IDENTIFICATION_FORMATTED_TEXT_GENERATOR_DECLARE__ + // +#endif // __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_FORMATTED_TEXT_GENERATOR__H_ diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationHistoryManager.cxx connectome-workbench-1.5.0/src/Brain/IdentificationHistoryManager.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationHistoryManager.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationHistoryManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,253 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_HISTORY_MANAGER_DECLARE__ +#include "IdentificationHistoryManager.h" +#undef __IDENTIFICATION_HISTORY_MANAGER_DECLARE__ + +#include "CaretAssert.h" +#include "IdentificationHistoryRecord.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::IdentificationHistoryManager + * \brief Manages history of identification operations + * \ingroup Brain + */ + +/** + * Constructor. + */ +IdentificationHistoryManager::IdentificationHistoryManager() +: CaretObject() +{ + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_showLastHistoryCount", + &m_showLastHistoryCount); + +} + +/** + * Destructor. + */ +IdentificationHistoryManager::~IdentificationHistoryManager() +{ +} + +/** + * @return The identification text contaiing the last "history count" records + */ +AString +IdentificationHistoryManager::getHtml() const +{ + AString text("\n" + "\n" + "\n" + "\n" + "\n"); + + /* + * Show last history ZERO means show all records + */ + const bool showAllFlag = (getShowLastHistoryCount() == 0); + + const int32_t numRecords = (showAllFlag + ? getNumberOfHistoryRecords() + : std::min(getShowLastHistoryCount(), + getNumberOfHistoryRecords())); + if (numRecords > 0) { + for (int32_t i = (numRecords - 1); i >= 0; i--) { + if ( ! text.isEmpty()) { + text.append("\n"); + } + + text.append(getHistoryRecord(i)->getText()); + } + } + + text.append("\n"); + +// std::cout << std::endl << std::endl << text << std::endl << std::endl; + + return text; +} + + +/** + * @return History count that is displayed + */ +int32_t +IdentificationHistoryManager::getShowLastHistoryCount() const +{ + return m_showLastHistoryCount; +} + +/** + * Set the history count + * @param historyCount + * New count + */ +void +IdentificationHistoryManager::setShowLastHistoryCount(const int32_t historyCount) +{ + m_showLastHistoryCount = historyCount; +} + +/** + * Clear the history + */ +void +IdentificationHistoryManager::clearHistory() +{ + m_historyRecords.clear(); +} + +/** + * Add a history record + * @param historyRecord + * The history record + */ +void +IdentificationHistoryManager::addHistoryRecord(IdentificationHistoryRecord* historyRecord) +{ + CaretAssert(historyRecord); + std::unique_ptr ptr(historyRecord); + m_historyRecords.push_front(std::move(ptr)); +} + +/** + * @return Number of history records + */ +int32_t +IdentificationHistoryManager::getNumberOfHistoryRecords() const +{ + return m_historyRecords.size(); +} + +/** + * @return History record at the given index + * @param historyIndex + * Index of the history record + */ +const IdentificationHistoryRecord* +IdentificationHistoryManager::getHistoryRecord(const int32_t historyIndex) const +{ + CaretAssert((historyIndex >= 0) + && (historyIndex < static_cast(m_historyRecords.size()))); + return m_historyRecords[historyIndex].get(); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +IdentificationHistoryManager::toString() const +{ + return "IdentificationHistoryManager"; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +IdentificationHistoryManager::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "IdentificationHistoryManager", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + for (auto& record : m_historyRecords) { + sceneClass->addClass(record->saveToScene(sceneAttributes, + "historyRecord")); + } + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +IdentificationHistoryManager::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_historyRecords.clear(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + const int32_t numChildren = sceneClass->getNumberOfObjects(); + for (int32_t i = 0; i < numChildren; i++) { + const SceneObject* so = sceneClass->getObjectAtIndex(i); + if (so->getName() == "historyRecord") { + const SceneClass* sc = dynamic_cast(so); + if (sc != NULL) { + IdentificationHistoryRecord* record = new IdentificationHistoryRecord(); + record->restoreFromScene(sceneAttributes, sc); + addHistoryRecord(record); + } + } + } + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationHistoryManager.h connectome-workbench-1.5.0/src/Brain/IdentificationHistoryManager.h --- connectome-workbench-1.4.2/src/Brain/IdentificationHistoryManager.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationHistoryManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,103 @@ +#ifndef __IDENTIFICATION_HISTORY_MANAGER_H__ +#define __IDENTIFICATION_HISTORY_MANAGER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "CaretObject.h" + +#include "SceneableInterface.h" + + +namespace caret { + class IdentificationHistoryRecord; + class SceneClassAssistant; + + class IdentificationHistoryManager : public CaretObject, public SceneableInterface { + + public: + IdentificationHistoryManager(); + + virtual ~IdentificationHistoryManager(); + + IdentificationHistoryManager(const IdentificationHistoryManager&) = delete; + + IdentificationHistoryManager& operator=(const IdentificationHistoryManager&) = delete; + + AString getHtml() const; + + int32_t getShowLastHistoryCount() const; + + void setShowLastHistoryCount(const int32_t historyCount); + + void clearHistory(); + + void addHistoryRecord(IdentificationHistoryRecord* historyRecord); + + int32_t getNumberOfHistoryRecords() const; + + const IdentificationHistoryRecord* getHistoryRecord(const int32_t historyIndex) const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + std::unique_ptr m_sceneAssistant; + + std::deque> m_historyRecords; + + int32_t m_showLastHistoryCount = 0; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __IDENTIFICATION_HISTORY_MANAGER_DECLARE__ + // +#endif // __IDENTIFICATION_HISTORY_MANAGER_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_HISTORY_MANAGER_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationHistoryRecord.cxx connectome-workbench-1.5.0/src/Brain/IdentificationHistoryRecord.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationHistoryRecord.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationHistoryRecord.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,144 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_HISTORY_RECORD_DECLARE__ +#include "IdentificationHistoryRecord.h" +#undef __IDENTIFICATION_HISTORY_RECORD_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::IdentificationHistoryRecord + * \brief Contains data from an identification operation + * \ingroup Brain + */ + +/** + * Constructor. + */ +IdentificationHistoryRecord::IdentificationHistoryRecord() +: CaretObject() +{ + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_text", + &m_text); +} + +/** + * Destructor. + */ +IdentificationHistoryRecord::~IdentificationHistoryRecord() +{ +} + +/** + * @return Text containing identification information + */ +AString +IdentificationHistoryRecord::getText() const +{ + return m_text; +} + +/** + * Set the identification information text + * @param text + * The textb + */ +void +IdentificationHistoryRecord::setText(const AString& text) +{ + m_text = text; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +IdentificationHistoryRecord::toString() const +{ + return "IdentificationHistoryRecord"; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +IdentificationHistoryRecord::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "IdentificationHistoryRecord", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +IdentificationHistoryRecord::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationHistoryRecord.h connectome-workbench-1.5.0/src/Brain/IdentificationHistoryRecord.h --- connectome-workbench-1.4.2/src/Brain/IdentificationHistoryRecord.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationHistoryRecord.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,91 @@ +#ifndef __IDENTIFICATION_HISTORY_RECORD_H__ +#define __IDENTIFICATION_HISTORY_RECORD_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "SceneableInterface.h" + + +namespace caret { + class Brain; + class SceneClassAssistant; + + class IdentificationHistoryRecord : public CaretObject, public SceneableInterface { + + public: + IdentificationHistoryRecord(); + + virtual ~IdentificationHistoryRecord(); + + IdentificationHistoryRecord(const IdentificationHistoryRecord&) = delete; + + IdentificationHistoryRecord& operator=(const IdentificationHistoryRecord&) = delete; + + AString getText() const; + + void setText(const AString& text); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + std::unique_ptr m_sceneAssistant; + + AString m_text; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __IDENTIFICATION_HISTORY_RECORD_DECLARE__ + // +#endif // __IDENTIFICATION_HISTORY_RECORD_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_HISTORY_RECORD_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationManager.cxx connectome-workbench-1.5.0/src/Brain/IdentificationManager.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -30,6 +30,9 @@ #include "CaretPreferences.h" #include "EventBrowserTabGetAll.h" #include "EventManager.h" +#include "IdentificationFilter.h" +#include "IdentificationHistoryManager.h" +#include "IdentificationHistoryRecord.h" #include "IdentifiedItemNode.h" #include "IdentifiedItemVoxel.h" #include "MathFunctions.h" @@ -61,20 +64,36 @@ m_contralateralIdentificationEnabled = false; m_identificationSymbolColor = CaretColorEnum::WHITE; m_identificationContralateralSymbolColor = CaretColorEnum::LIME; + m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; m_identifcationSymbolSize = 3.0; m_identifcationMostRecentSymbolSize = 5.0; + m_identifcationSymbolPercentageSize = 3.0; + m_identifcationMostRecentSymbolPercentageSize = 5.0; m_showSurfaceIdentificationSymbols = caretPreferences->isShowSurfaceIdentificationSymbols(); m_showVolumeIdentificationSymbols = caretPreferences->isShowVolumeIdentificationSymbols(); + m_identificationFilter.reset(new IdentificationFilter()); + m_identificationHistoryManager.reset(new IdentificationHistoryManager()); + + m_chartLineLayerSymbolSize = 4.0; + m_chartLineLayerToolTipTextSize = 4.0; m_sceneAssistant->add("m_contralateralIdentificationEnabled", &m_contralateralIdentificationEnabled); + m_sceneAssistant->add("m_identificationSymbolSizeType", + &m_identificationSymbolSizeType); m_sceneAssistant->add("m_identifcationSymbolSize", &m_identifcationSymbolSize); m_sceneAssistant->add("m_identifcationMostRecentSymbolSize", &m_identifcationMostRecentSymbolSize); + m_sceneAssistant->add("m_identifcationSymbolPercentageSize", + &m_identifcationSymbolPercentageSize); + + m_sceneAssistant->add("m_identifcationMostRecentSymbolPercentageSize", + &m_identifcationMostRecentSymbolPercentageSize); + m_sceneAssistant->add("m_identificationSymbolColor", &m_identificationSymbolColor); @@ -85,6 +104,17 @@ &m_showSurfaceIdentificationSymbols); m_sceneAssistant->add("m_showVolumeIdentificationSymbols", &m_showVolumeIdentificationSymbols); + m_sceneAssistant->add("m_identificationFilter", + "IdentificationFilter", + m_identificationFilter.get()); + m_sceneAssistant->add("m_identificationHistoryManager", + "m_identificationHistoryManager", + m_identificationHistoryManager.get()); + + m_sceneAssistant->add("m_chartLineLayerSymbolSize", + &m_chartLineLayerSymbolSize); + m_sceneAssistant->add("m_chartLineLayerToolTipTextSize", + &m_chartLineLayerToolTipTextSize); removeAllIdentifiedItems(); } @@ -120,7 +150,9 @@ } } - addIdentifiedItemPrivate(item); + const bool restoringSceneFlag(false); + addIdentifiedItemPrivate(item, + restoringSceneFlag); } /** @@ -128,14 +160,23 @@ * @param item * Identified item that is added. * NOTE: Takes ownership of this item and will delete, at the appropriate time. + * @param restoringSceneFlag + * If true, item is from a scene */ void -IdentificationManager::addIdentifiedItemPrivate(IdentifiedItem* item) +IdentificationManager::addIdentifiedItemPrivate(IdentifiedItem* item, + const bool restoringSceneFlag) { CaretAssert(item); m_mostRecentIdentifiedItem = item; m_identifiedItems.push_back(item); + + if ( ! restoringSceneFlag) { + IdentificationHistoryRecord* historyRecord = new IdentificationHistoryRecord(); + historyRecord->setText(item->getFormattedText()); + m_identificationHistoryManager->addHistoryRecord(historyRecord); + } } /** @@ -153,7 +194,7 @@ if (text.isEmpty() == false) { text += "

"; } - text += item->getText(); + text += item->getSimpleText(); } return text; @@ -227,11 +268,26 @@ nodeID.setSymbolRGB(symbolRGB); const float* contralateralSymbolRGB = CaretColorEnum::toRGB(m_identificationContralateralSymbolColor); nodeID.setContralateralSymbolRGB(contralateralSymbolRGB); + nodeID.setIdentificationSymbolSizeType(m_identificationSymbolSizeType); if (item == m_mostRecentIdentifiedItem) { - nodeID.setSymbolSize(m_identifcationMostRecentSymbolSize); + switch (m_identificationSymbolSizeType) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + nodeID.setSymbolSize(m_identifcationMostRecentSymbolSize); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + nodeID.setSymbolSize(m_identifcationMostRecentSymbolPercentageSize); + break; + } } else { - nodeID.setSymbolSize(m_identifcationSymbolSize); + switch (m_identificationSymbolSizeType) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + nodeID.setSymbolSize(m_identifcationSymbolSize); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + nodeID.setSymbolSize(m_identifcationSymbolPercentageSize); + break; + } } nodeItemsOut.push_back(nodeID); } @@ -261,7 +317,15 @@ IdentifiedItemVoxel voxelID(*voxelItem); const float* symbolRGB = CaretColorEnum::toRGB(m_identificationSymbolColor); voxelID.setSymbolRGB(symbolRGB); - voxelID.setSymbolSize(m_identifcationSymbolSize); + voxelID.setIdentificationSymbolSizeType(m_identificationSymbolSizeType); + switch (m_identificationSymbolSizeType) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + voxelID.setSymbolSize(m_identifcationSymbolSize); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + voxelID.setSymbolSize(m_identifcationSymbolPercentageSize); + break; + } itemsOut.push_back(voxelID); } } @@ -352,6 +416,8 @@ delete item; } + m_identificationHistoryManager->clearHistory(); + m_identifiedItems.clear(); m_mostRecentIdentifiedItem = NULL; @@ -382,7 +448,8 @@ m_mostRecentIdentifiedItem = NULL; } - itemToKeep = new IdentifiedItem(item->getText()); + itemToKeep = new IdentifiedItem(item->getSimpleText(), + item->getFormattedText()); delete item; } else { @@ -390,7 +457,8 @@ } if (itemToKeep != NULL) { - if (itemToKeep->getText().isEmpty()) { + if (itemToKeep->getSimpleText().isEmpty() + && itemToKeep->getFormattedText().isEmpty()) { delete itemToKeep; itemToKeep = NULL; } @@ -424,6 +492,26 @@ } /** + * @param The identification symbol size type + */ +IdentificationSymbolSizeTypeEnum::Enum +IdentificationManager::getIdentificationSymbolSizeType() const +{ + return m_identificationSymbolSizeType; +} + +/** + * Set the identification size type + * @param sizeType + * The new size type + */ +void +IdentificationManager::setIdentificationSymbolSizeType(const IdentificationSymbolSizeTypeEnum::Enum sizeType) +{ + m_identificationSymbolSizeType = sizeType; +} + +/** * @return The size of the identification symbol */ float @@ -464,6 +552,46 @@ } /** + * @return The percentage size of the identification symbol + */ +float +IdentificationManager::getIdentificationSymbolPercentageSize() const +{ + return m_identifcationSymbolPercentageSize; +} + +/** + * Set the percentage size of the identification symbol + * @param symbolSize + * New size of symbol. + */ +void +IdentificationManager::setIdentificationSymbolPercentageSize(const float symbolSize) +{ + m_identifcationSymbolPercentageSize = symbolSize; +} + +/** + * @return The percentage size of the most recent identification symbol + */ +float +IdentificationManager::getMostRecentIdentificationSymbolPercentageSize() const +{ + return m_identifcationMostRecentSymbolPercentageSize; +} + +/** + * Set the percentage size of the most recent identification symbol + * @param symbolSize + * New size of symbol. + */ +void +IdentificationManager::setMostRecentIdentificationSymbolPercentageSize(const float symbolSize) +{ + m_identifcationMostRecentSymbolPercentageSize = symbolSize; +} + +/** * @return The color of the identification symbol. */ CaretColorEnum::Enum @@ -546,6 +674,46 @@ } /** + * @return Size for chart line layer symbols, except selected layer (percentage of viewport height) + */ +float +IdentificationManager::getChartLineLayerSymbolSize() const +{ + return m_chartLineLayerSymbolSize; +} + +/** + * Set size for chart line layer symbols, except selected layer (percentage of viewport height) + * @param symbolSize + * New size for symbol + */ +void +IdentificationManager::setChartLineLayerSymbolSize(const float symbolSize) +{ + m_chartLineLayerSymbolSize = symbolSize; +} + +/** + * @return Size for chart line layer tooltip text (percentage of viewport height) + */ +float +IdentificationManager::getChartLineLayerToolTipTextSize() const +{ + return m_chartLineLayerToolTipTextSize; +} + +/** + * Set size for chart line layer tooltip text (percentage of viewport height) + * @param textSize + * New size for text + */ +void +IdentificationManager::setChartLineLayerToolTipTextSize(const float textSize) +{ + m_chartLineLayerToolTipTextSize = textSize; +} + +/** * Create a scene for an instance of a class. * * @param sceneAttributes @@ -606,11 +774,13 @@ return; } + m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; removeAllIdentifiedItems(); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); + const bool restoringSceneFlag(true); const int32_t numChildren = sceneClass->getNumberOfObjects(); for (int32_t i = 0; i < numChildren; i++) { const SceneObject* so = sceneClass->getObjectAtIndex(i); @@ -622,7 +792,8 @@ IdentifiedItem* item = new IdentifiedItem(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { - addIdentifiedItemPrivate(item); + addIdentifiedItemPrivate(item, + restoringSceneFlag); } else { delete item; @@ -632,7 +803,8 @@ IdentifiedItemNode* item = new IdentifiedItemNode(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { - addIdentifiedItemPrivate(item); + addIdentifiedItemPrivate(item, + restoringSceneFlag); } else { delete item; @@ -642,7 +814,8 @@ IdentifiedItemVoxel* item = new IdentifiedItemVoxel(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { - addIdentifiedItemPrivate(item); + addIdentifiedItemPrivate(item, + restoringSceneFlag); } else { delete item; @@ -681,3 +854,41 @@ } } } + +/** + * @return Pointer to the identification filter (const method) + */ +const IdentificationFilter* +IdentificationManager::getIdentificationFilter() const +{ + return m_identificationFilter.get(); +} + +/** + * @return Pointer to the identification filter + */ +IdentificationFilter* +IdentificationManager::getIdentificationFilter() +{ + return m_identificationFilter.get(); +} + +/** + * @return Pointer to the identification history manager + */ +const IdentificationHistoryManager* +IdentificationManager::getIdentificationHistoryManager() const +{ + return m_identificationHistoryManager.get(); +} + +/** + * @return Pointer to the identification history manager + */ +IdentificationHistoryManager* +IdentificationManager::getIdentificationHistoryManager() +{ + return m_identificationHistoryManager.get(); +} + + diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationManager.h connectome-workbench-1.5.0/src/Brain/IdentificationManager.h --- connectome-workbench-1.4.2/src/Brain/IdentificationManager.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,14 +22,18 @@ /*LICENSE_END*/ #include +#include #include "CaretColorEnum.h" +#include "IdentificationSymbolSizeTypeEnum.h" #include "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class CaretPreferences; + class IdentificationFilter; + class IdentificationHistoryManager; class IdentifiedItem; class IdentifiedItemNode; class IdentifiedItemVoxel; @@ -46,6 +50,14 @@ AString getIdentificationText() const; + const IdentificationFilter* getIdentificationFilter() const; + + IdentificationFilter* getIdentificationFilter(); + + const IdentificationHistoryManager* getIdentificationHistoryManager() const; + + IdentificationHistoryManager* getIdentificationHistoryManager(); + std::vector getNodeIdentifiedItemsForSurface(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes) const; @@ -67,6 +79,10 @@ void setContralateralIdentificationEnabled(const bool enabled); + IdentificationSymbolSizeTypeEnum::Enum getIdentificationSymbolSizeType() const; + + void setIdentificationSymbolSizeType(const IdentificationSymbolSizeTypeEnum::Enum sizeType); + float getIdentificationSymbolSize() const; void setIdentificationSymbolSize(const float symbolSize); @@ -75,6 +91,14 @@ void setMostRecentIdentificationSymbolSize(const float symbolSize); + float getIdentificationSymbolPercentageSize() const; + + void setIdentificationSymbolPercentageSize(const float symbolSize); + + float getMostRecentIdentificationSymbolPercentageSize() const; + + void setMostRecentIdentificationSymbolPercentageSize(const float symbolSize); + CaretColorEnum::Enum getIdentificationSymbolColor() const; void setIdentificationSymbolColor(const CaretColorEnum::Enum color); @@ -91,6 +115,14 @@ void setShowVolumeIdentificationSymbols(const bool showVolumeIdentificationSymbols); + float getChartLineLayerSymbolSize() const; + + void setChartLineLayerSymbolSize(const float symbolSize); + + float getChartLineLayerToolTipTextSize() const; + + void setChartLineLayerToolTipTextSize(const float textSize); + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -108,7 +140,8 @@ private: - void addIdentifiedItemPrivate(IdentifiedItem* item); + void addIdentifiedItemPrivate(IdentifiedItem* item, + const bool restoringSceneFlag); // ADD_NEW_MEMBERS_HERE @@ -122,14 +155,28 @@ bool m_contralateralIdentificationEnabled; + IdentificationSymbolSizeTypeEnum::Enum m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + float m_identifcationSymbolSize; float m_identifcationMostRecentSymbolSize; + float m_identifcationSymbolPercentageSize; + + float m_identifcationMostRecentSymbolPercentageSize; + CaretColorEnum::Enum m_identificationSymbolColor; CaretColorEnum::Enum m_identificationContralateralSymbolColor; + float m_chartLineLayerSymbolSize = 2.0; + + float m_chartLineLayerToolTipTextSize = 2.0; + + std::unique_ptr m_identificationFilter; + + std::unique_ptr m_identificationHistoryManager; + /** show surface identification symbols*/ bool m_showSurfaceIdentificationSymbols; diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationSimpleTextGenerator.cxx connectome-workbench-1.5.0/src/Brain/IdentificationSimpleTextGenerator.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationSimpleTextGenerator.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationSimpleTextGenerator.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,1930 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ +#include "IdentificationSimpleTextGenerator.h" +#undef __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ + +#include "Border.h" +#include "Brain.h" +#include "BrainStructure.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "ChartDataCartesian.h" +#include "ChartDataSource.h" +#include "ChartModelDataSeries.h" +#include "ChartTwoDataCartesian.h" +#include "ChartableMatrixInterface.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineLayerChart.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartableTwoFileMatrixChart.h" +#include "CiftiMappableConnectivityMatrixDataFile.h" +#include "CiftiMappableDataFile.h" +#include "CaretVolumeExtension.h" +#include "DataToolTipsManager.h" +#include "EventBrowserTabGetAll.h" +#include "EventManager.h" +#include "FileInformation.h" +#include "FociFile.h" +#include "Focus.h" +#include "GiftiLabel.h" +#include "GraphicsPrimitive.h" +#include "GraphicsPrimitiveV3f.h" +#include "Histogram.h" +#include "ImageFile.h" +#include "MapFileDataSelector.h" +#include "MetricDynamicConnectivityFile.h" +#include "OverlaySet.h" +#include "SelectionItemBorderSurface.h" +#include "SelectionItemChartDataSeries.h" +#include "SelectionItemChartFrequencySeries.h" +#include "SelectionItemChartMatrix.h" +#include "SelectionItemCiftiConnectivityMatrixRowColumn.h" +#include "SelectionItemChartTimeSeries.h" +#include "SelectionItemChartTwoHistogram.h" +#include "SelectionItemChartTwoLineLayerVerticalNearest.h" +#include "SelectionItemChartTwoLineSeries.h" +#include "SelectionItemChartTwoMatrix.h" +#include "SelectionItemFocusSurface.h" +#include "SelectionItemFocusVolume.h" +#include "SelectionItemImage.h" +#include "SelectionItemSurfaceNode.h" +#include "SelectionItemVoxel.h" +#include "SelectionManager.h" +#include "IdentificationStringBuilder.h" +#include "LabelFile.h" +#include "MetricFile.h" +#include "Surface.h" +#include "VolumeDynamicConnectivityFile.h" +#include "SurfaceProjectedItem.h" +#include "SurfaceProjectionBarycentric.h" +#include "SurfaceProjectionVanEssen.h" +#include "VolumeFile.h" + +using namespace caret; + + + +/** + * \class IdentificationSimpleTextGenerator + * \brief Creates text describing selected data. + * + * Examine the selected data and generate descriptive text. + */ + +/** + * Constructor. + */ +IdentificationSimpleTextGenerator::IdentificationSimpleTextGenerator() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +IdentificationSimpleTextGenerator::~IdentificationSimpleTextGenerator() +{ + +} + +/** + * Create identification text from selection in the identification manager. + * @param idManager + * Identification manager containing selection. + * @param brain + * The brain. + */ +AString +IdentificationSimpleTextGenerator::createIdentificationText(const SelectionManager* idManager, + const Brain* brain) const +{ + CaretAssert(idManager); + CaretAssert(brain); + + IdentificationStringBuilder idText; + + const SelectionItemSurfaceNode* surfaceID = idManager->getSurfaceNodeIdentification(); + + this->generateSurfaceIdentificationText(idText, + brain, + surfaceID); + + this->generateSurfaceBorderIdentifcationText(idText, + idManager->getSurfaceBorderIdentification(), + false); + + this->generateSurfaceFociIdentifcationText(idText, + idManager->getSurfaceFocusIdentification(), + false); + + this->generateVolumeFociIdentifcationText(idText, + idManager->getVolumeFocusIdentification()); + + this->generateVolumeIdentificationText(idText, + brain, + idManager->getVoxelIdentification()); + + this->generateChartDataSeriesIdentificationText(idText, + idManager->getChartDataSeriesIdentification()); + + this->generateChartFrequencySeriesIdentificationText(idText, + idManager->getChartFrequencySeriesIdentification()); + + this->generateChartTimeSeriesIdentificationText(idText, + idManager->getChartTimeSeriesIdentification()); + + this->generateChartMatrixIdentificationText(idText, + idManager->getChartMatrixIdentification()); + + this->generateCiftiConnectivityMatrixIdentificationText(idText, + idManager->getCiftiConnectivityMatrixRowColumnIdentification()); + + this->generateChartTwoHistogramIdentificationText(idText, + idManager->getChartTwoHistogramIdentification()); + + this->generateChartTwoLineLayerIdentificationText(idText, + idManager->getChartTwoLineLayerVerticalNearestIdentification()); + + this->generateChartTwoLineSeriesIdentificationText(idText, + idManager->getChartTwoLineSeriesIdentification()); + + this->generateChartTwoMatrixIdentificationText(idText, + idManager->getChartTwoMatrixIdentification()); + + this->generateImageIdentificationText(idText, + idManager->getImageIdentification()); + + return idText.toString(); +} + +/** + * Get text for the tooltip for a selected node. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +AString +IdentificationSimpleTextGenerator::createToolTipText(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager) const +{ + CaretAssert(brain); + CaretAssert(browserTab); + CaretAssert(selectionManager); + CaretAssert(dataToolTipsManager); + + const SelectionItemSurfaceNode* selectedNode = selectionManager->getSurfaceNodeIdentification(); + const SelectionItemVoxel* selectedVoxel = selectionManager->getVoxelIdentification(); + + IdentificationStringBuilder idText; + + if (selectedNode->isValid()) { + generateSurfaceToolTip(brain, + browserTab, + selectionManager, + dataToolTipsManager, + idText); + } + else if (selectedVoxel->isValid()) { + generateVolumeToolTip(browserTab, + selectionManager, + dataToolTipsManager, + idText); + } + else { + generateChartToolTip(selectionManager, + dataToolTipsManager, + idText); + } + + AString text; + if (idText.length() > 0) { + text = idText.toStringWithHtmlBodyForToolTip(); + } + + return text; +} + + +/** + * Generate identification text for volume voxel identification. + * + * @param idText + * String builder for identification text. + * @param brain + * The brain. + * @param idVolumeVoxel + * Information for volume voxel ID. + */ +void +IdentificationSimpleTextGenerator::generateVolumeIdentificationText(IdentificationStringBuilder& idText, + const Brain* brain, + const SelectionItemVoxel* idVolumeVoxel) const +{ + if (idVolumeVoxel->isValid() == false) { + return; + } + + int64_t ijk[3]; + const VolumeMappableInterface* idVolumeFile = idVolumeVoxel->getVolumeFile(); + idVolumeVoxel->getVoxelIJK(ijk); + float x, y, z; + idVolumeFile->indexToSpace(ijk[0], ijk[1], ijk[2], x, y, z); + + idText.addLine(false, + "Voxel XYZ (" + + AString::number(x) + + ", " + + AString::number(y) + + ", " + + AString::number(z) + + ")"); + + const float xyz[3] = { x, y, z }; + + + /* + * Get all volume files + */ + std::vector volumeInterfaces; + const int32_t numVolumeFiles = brain->getNumberOfVolumeFiles(); + for (int32_t i = 0; i < numVolumeFiles; i++) { + const VolumeFile* vf = brain->getVolumeFile(i); + volumeInterfaces.push_back(vf); + + const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); + if (volDynConnFile != NULL) { + if (volDynConnFile->isDataValid()) { + volumeInterfaces.push_back(volDynConnFile); + } + } + } + + /* + * Get the CIFTI files that are volume mappable + */ + std::vector allCiftiMappableDataFiles; + brain->getAllCiftiMappableDataFiles(allCiftiMappableDataFiles); + for (std::vector::iterator ciftiMapIter = allCiftiMappableDataFiles.begin(); + ciftiMapIter != allCiftiMappableDataFiles.end(); + ciftiMapIter++) { + const CiftiMappableDataFile* cmdf = *ciftiMapIter; + if (cmdf->isEmpty() == false) { + if (cmdf->isVolumeMappable()) { + volumeInterfaces.push_back(cmdf); + } + } + } + + /* + * In first loop, show values for 'idVolumeFile' (the underlay volume) + * In second loop, show values for all other volume files + */ + const int32_t numberOfVolumeMappableFiles = static_cast(volumeInterfaces.size()); + for (int32_t iLoop = 0; iLoop < 2; iLoop++) { + for (int32_t i = 0; i < numberOfVolumeMappableFiles; i++) { + const VolumeMappableInterface* volumeInterfaceFile = volumeInterfaces[i]; + const VolumeFile* volumeFile = dynamic_cast(volumeInterfaceFile); + const CiftiMappableDataFile* ciftiFile = dynamic_cast(volumeInterfaceFile); + CaretAssert((volumeFile != NULL) + || (ciftiFile != NULL)); + const CaretMappableDataFile* caretMappableDataFile = dynamic_cast(volumeInterfaceFile); + CaretAssert(caretMappableDataFile != NULL); + + if (volumeInterfaceFile == idVolumeFile) { + if (iLoop != 0) { + continue; + } + } + else if (iLoop == 0) { + continue; + } + + int64_t vfI, vfJ, vfK; + volumeInterfaceFile->enclosingVoxel(x, y, z, + vfI, vfJ, vfK); + + if (volumeInterfaceFile->indexValid(vfI, vfJ, vfK)) { + if (volumeFile != NULL) { + AString boldText = caretMappableDataFile->getFileNameNoPath(); + boldText += (" IJK (" + + AString::number(vfI) + + ", " + + AString::number(vfJ) + + ", " + + AString::number(vfK) + + ") "); + + AString text; + const int32_t numMaps = caretMappableDataFile->getNumberOfMaps(); + for (int jMap = 0; jMap < numMaps; jMap++) { + if (jMap > 0) { + text += " "; + } + if (volumeFile != NULL) { + if (volumeFile->getType() == SubvolumeAttributes::LABEL) { + const int32_t labelIndex = static_cast(volumeFile->getValue(vfI, vfJ, vfK, jMap)); + const GiftiLabelTable* glt = volumeFile->getMapLabelTable(jMap); + const GiftiLabel* gl = glt->getLabel(labelIndex); + if (gl != NULL) { + text += gl->getName(); + } + else { + text += ("LABLE_MISSING_FOR_INDEX=" + + AString::number(labelIndex)); + } + } + else if (volumeFile->getType() == SubvolumeAttributes::RGB) { + if (volumeFile->getNumberOfComponents() == 4) { + text += ("RGBA(" + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 0)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 1)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 2)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 3)) + + ")"); + } + else if (volumeFile->getNumberOfComponents() == 3) { + text += ("RGB(" + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 0)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 1)) + + "," + + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 2)) + + ")"); + } + } + else { + text += AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap)); + } + } + else if (ciftiFile != NULL) { + + } + } + + if (dynamic_cast(volumeFile) != NULL) { + boldText.insert(0, + (DataFileTypeEnum::toOverlayTypeName(DataFileTypeEnum::VOLUME_DYNAMIC) + " ")); + } + idText.addLine(true, + boldText, + text); + } + else if (ciftiFile != NULL) { + if (ciftiFile->isEmpty() == false) { + const int numMaps = ciftiFile->getNumberOfMaps(); + std::vector mapIndices; + for (int32_t i = 0; i < numMaps; i++) { + mapIndices.push_back(i); + } + + /* + * Limit dense scalar and data series to maps selected in the overlays + * from all tabs. + */ + bool limitMapIndicesFlag = false; + switch (ciftiFile->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + CaretAssert(0); + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; + } + if (limitMapIndicesFlag) { + getMapIndicesOfFileUsedInOverlays(ciftiFile, + mapIndices); + } + + AString textValue; + int64_t voxelIJK[3]; + if (ciftiFile->getVolumeVoxelIdentificationForMaps(mapIndices, + xyz, + " ", + voxelIJK, + textValue)) { + AString boldText = (DataFileTypeEnum::toOverlayTypeName(ciftiFile->getDataFileType()) + + " " + + ciftiFile->getFileNameNoPath() + + " IJK (" + + AString::number(voxelIJK[0]) + + ", " + + AString::number(voxelIJK[1]) + + ", " + + AString::number(voxelIJK[2]) + + ") "); + idText.addLine(true, boldText, textValue); + } + } + } + } + } + } +} + +/** + * Generate identification text for a surface node identification. + * @param idText + * String builder for identification text. + * @param brain + * The brain. + * @param browserTabContent + * Content of the browser tab. + * @param idSurfaceNode + * Information for surface node ID. + */ +void +IdentificationSimpleTextGenerator::generateSurfaceIdentificationText(IdentificationStringBuilder& idText, + const Brain* brain, + const SelectionItemSurfaceNode* idSurfaceNode) const +{ + const Surface* surface = idSurfaceNode->getSurface(); + const int32_t nodeNumber = idSurfaceNode->getNodeNumber(); + + if ((surface != NULL) + && (nodeNumber >= 0)) { + AString surfaceID; + surfaceID += ("VERTEX " + StructureEnum::toGuiName(surface->getStructure())); + idText.addLine(false, surfaceID, nodeNumber, false); + + const float* xyz = surface->getCoordinate(nodeNumber); + + idText.addLine(true, SurfaceTypeEnum::toGuiName(surface->getSurfaceType()).toUpper() + + " XYZ: " + + AString::number(xyz[0]) + + ", " + + AString::number(xyz[1]) + + ", " + + AString::number(xyz[2])); + + const BrainStructure* brainStructure = surface->getBrainStructure(); + CaretAssert(brainStructure); + + std::vector allCiftiMappableDataFiles; + brain->getAllCiftiMappableDataFiles(allCiftiMappableDataFiles); + for (std::vector::iterator ciftiMapIter = allCiftiMappableDataFiles.begin(); + ciftiMapIter != allCiftiMappableDataFiles.end(); + ciftiMapIter++) { + const CiftiMappableDataFile* cmdf = *ciftiMapIter; + AString boldText = (DataFileTypeEnum::toOverlayTypeName(cmdf->getDataFileType()) + + " " + + cmdf->getFileNameNoPath()); + + std::vector mapIndices; + for (int32_t i = 0; i < cmdf->getNumberOfMaps(); i++) { + mapIndices.push_back(i); + } + + /* + * Limit dense scalar and data series to maps selected in the overlays + * from all tabs. + */ + bool limitMapIndicesFlag = false; + switch (cmdf->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + limitMapIndicesFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + CaretAssert(0); + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; + } + if (limitMapIndicesFlag) { + getMapIndicesOfFileUsedInOverlays(cmdf, + mapIndices); + } + AString textValue; + + const bool valid = cmdf->getSurfaceNodeIdentificationForMaps(mapIndices, + surface->getStructure(), + nodeNumber, + surface->getNumberOfNodes(), + " ", + textValue); + if (valid) { + idText.addLine(true, + boldText, + textValue); + } + } + + + const int32_t numLabelFiles = brainStructure->getNumberOfLabelFiles(); + for (int32_t i = 0; i < numLabelFiles; i++) { + const LabelFile* lf = brainStructure->getLabelFile(i); + AString boldText = "LABEL " + lf->getFileNameNoPath(); + AString text; + const int numMaps = lf->getNumberOfMaps(); + for (int32_t j = 0; j < numMaps; j++) { + AString labelName = lf->getLabelName(nodeNumber, j); + if (labelName.isEmpty()) { + labelName = ("Map-" + AString::number(j + 1)); + } + text += (" " + labelName); + } + idText.addLine(true, boldText, text); + } + + std::vector metricDynConFiles; + const int32_t numMetricFiles = brainStructure->getNumberOfMetricFiles(); + for (int32_t i = 0; i < numMetricFiles; i++) { + const MetricFile* mf = brainStructure->getMetricFile(i); + AString boldText = "METRIC " + mf->getFileNameNoPath(); + AString text; + const int numMaps = mf->getNumberOfMaps(); + for (int32_t j = 0; j < numMaps; j++) { + text += (" " + AString::number(mf->getValue(nodeNumber, j))); + } + idText.addLine(true, boldText, text); + + const MetricDynamicConnectivityFile* mdcf = mf->getMetricDynamicConnectivityFile(); + if (mdcf != NULL) { + if (mdcf->isDataValid()) { + if (mdcf->isEnabledAsLayer()) { + AString boldText = "METRIC DYNAMIC " + mdcf->getFileNameNoPath(); + AString text; + const int numMaps = mdcf->getNumberOfMaps(); + for (int32_t j = 0; j < numMaps; j++) { + text += (" " + AString::number(mdcf->getValue(nodeNumber, j))); + } + idText.addLine(true, boldText, text); + } + } + } + } + } +} + +/** + * Find the usage of the file's maps in all overlays. + * + * @param caretMappableDataFile + * The file whose usage is desired. + * @param mapIndicesOut + * Indices of maps of the file that are used in overlays. + */ +void +IdentificationSimpleTextGenerator::getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, + std::vector& mapIndicesOut) const +{ + mapIndicesOut.clear(); + + EventBrowserTabGetAll allTabsEvent; + EventManager::get()->sendEvent(allTabsEvent.getPointer()); + const std::vector allTabs = allTabsEvent.getAllBrowserTabs(); + for (std::vector::const_iterator tabIter = allTabs.begin(); + tabIter != allTabs.end(); + tabIter++) { + BrowserTabContent* tabContent = *tabIter; + OverlaySet* overlaySet = tabContent->getOverlaySet(); + if (overlaySet != NULL) { + std::vector mapIndices; + overlaySet->getSelectedMapIndicesForFile(caretMappableDataFile, + false, // true => enabled overlays + mapIndices); + mapIndicesOut.insert(mapIndicesOut.end(), + mapIndices.begin(), + mapIndices.end()); + } + } + + /* + * Sort and remove all duplicates + */ + if (mapIndicesOut.empty() == false) { + std::sort(mapIndicesOut.begin(), + mapIndicesOut.end()); + std::vector::iterator uniqueIter = std::unique(mapIndicesOut.begin(), + mapIndicesOut.end()); + mapIndicesOut.resize(std::distance(mapIndicesOut.begin(), + uniqueIter)); + } +} + +/** + * Generate identification text for a data series chart. + * @param idText + * String builder for identification text. + * @param idChartDataSeries + * Information for chart id. + */ +void +IdentificationSimpleTextGenerator::generateChartDataSeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartDataSeries* idChartDataSeries) const +{ + if (idChartDataSeries->isValid()) { + const ChartDataCartesian* chartDataCartesian = idChartDataSeries->getChartDataCartesian(); + + const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); + generateChartDataSourceText(idText, + "DATA SERIES CHART", + chartDataSource); + } +} + +/** + * Generate identification text for a data series chart. + * @param idText + * String builder for identification text. + * @param idChartDataSeries + * Information for chart id. + */ +void +IdentificationSimpleTextGenerator::generateChartFrequencySeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartFrequencySeries* idChartFrequencySeries) const +{ + if (idChartFrequencySeries->isValid()) { + const ChartDataCartesian* chartDataCartesian = idChartFrequencySeries->getChartDataCartesian(); + + const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); + generateChartDataSourceText(idText, + "FREQUENCY SERIES CHART", + chartDataSource); + } +} + +/** + * Generate identification text for a matrix chart. + * @param idText + * String builder for identification text. + * @param idChartMatrix + * Information for matrix chart id. + */ +void +IdentificationSimpleTextGenerator::generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartMatrix* idChartMatrix) const +{ + if (idChartMatrix->isValid()) { + const ChartableMatrixInterface* chartMatrixInterface = idChartMatrix->getChartableMatrixInterface(); + const CaretMappableDataFile* caretMappableDataFile = chartMatrixInterface->getMatrixChartCaretMappableDataFile(); + + const int32_t rowIndex = idChartMatrix->getMatrixRowIndex(); + const int32_t columnIndex = idChartMatrix->getMatrixColumnIndex(); + AString rowName; + AString columnName; + AString cellValue; + const bool validData = chartMatrixInterface->getMatrixCellAttributes(rowIndex, + columnIndex, + cellValue, + rowName, + columnName); + + AString boldText("MATRIX CHART"); + idText.addLine(false, + boldText, + caretMappableDataFile->getFileNameNoPath()); + + if (validData) { + idText.addLine(true, + ("Row " + AString::number(rowIndex + 1)), + rowName); + idText.addLine(true, + ("Column " + AString::number(columnIndex + 1)), + columnName); + idText.addLine(true, "Value", + cellValue); + } + } +} + +/** + * Generate identification text for a chart two histogram. + * + * @param idText + * String builder for identification text. + * @param idChartTwoHistogram + * Information for selected chart two histogram. + */ +void +IdentificationSimpleTextGenerator::generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoHistogram* idChartTwoHistogram) const +{ + const int32_t mapIndex = idChartTwoHistogram->getMapIndex(); + const int32_t bucketIndex = idChartTwoHistogram->getBucketIndex(); + const bool allMapsFlag = idChartTwoHistogram->isAllMapsSelected(); + + if (idChartTwoHistogram->isValid()) { + ChartableTwoFileHistogramChart* fileHistogramChart = idChartTwoHistogram->getFileHistogramChart(); + CaretAssert(fileHistogramChart); + CaretMappableDataFile* mapFile = fileHistogramChart->getCaretMappableDataFile(); + CaretAssert(mapFile); + + { + ChartableTwoFileHistogramChart* chartingDelegate = mapFile->getChartingDelegate()->getHistogramCharting(); + CaretAssert(chartingDelegate); + const Histogram* histogram = chartingDelegate->getHistogramForChartDrawing(mapIndex, + allMapsFlag); + CaretAssert(histogram); + + float bucketValue = 0.0; + float bucketHeight = 0.0; + if (histogram->getHistogramDisplayBucketDataValueAndHeight(bucketIndex, bucketValue, bucketHeight)) { + AString boldText("Histogram"); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + + idText.addLine(true, + "Bucket Index", + (AString::number(bucketIndex))); + + idText.addLine(true, + "Data Value at Bucket", + (AString::number(bucketValue))); + + const int64_t bucketHeightInteger = static_cast(bucketHeight); + idText.addLine(true, + "Bucket Count", + (AString::number(bucketHeightInteger))); + } + } + } +} + +/** + * Generate identification text for a chart two line-layer. + * + * @param idText + * String builder for identification text. + * @param idChartTwoLineLayer + * Information for selected chart two line-layer. + */ +void +IdentificationSimpleTextGenerator::generateChartTwoLineLayerIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineLayerVerticalNearest* idChartTwoLineLayer) const +{ + if (idChartTwoLineLayer->isValid()) { + const ChartableTwoFileLineLayerChart* fileLineLayerChart = idChartTwoLineLayer->getFileLineLayerChart(); + CaretAssert(fileLineLayerChart); + const CaretMappableDataFile* mapFile = fileLineLayerChart->getCaretMappableDataFile(); + CaretAssert(mapFile); + const ChartTwoDataCartesian* cartesianData = idChartTwoLineLayer->getChartTwoCartesianData(); + CaretAssert(cartesianData); + const MapFileDataSelector* mapFileDataSelector = cartesianData->getMapFileDataSelector(); + CaretAssert(mapFileDataSelector); + + int32_t primitiveIndex = idChartTwoLineLayer->getLineSegmentIndex(); + + AString boldText("Line Layer Chart"); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + + const GraphicsPrimitive* primitive = cartesianData->getGraphicsPrimitive(); + CaretAssert(primitive); + + if (primitiveIndex >= 1) { + float xyz1[3]; + primitive->getVertexFloatXYZ(primitiveIndex - 1, + xyz1); + float xyz2[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz2); + idText.addLine(true, + "XY Start", + AString::fromNumbers(xyz1, 2, ", ")); + idText.addLine(true, + "XY End ", + AString::fromNumbers(xyz2, 2, ", ")); + } + else { + float xyz[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz); + idText.addLine(true, + "XY", + AString::fromNumbers(xyz, 2, ", ")); + } + + generateMapFileSelectorText(idText, + mapFileDataSelector); + } +} +/** + * Generate identification text for a chart two line-series. + * + * @param idText + * String builder for identification text. + * @param idChartTwoLineSeries + * Information for selected chart two line-series. + */ +void +IdentificationSimpleTextGenerator::generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineSeries* idChartTwoLineSeries) const +{ + if (idChartTwoLineSeries->isValid()) { + const ChartableTwoFileLineSeriesChart* fileLineSeriesChart = idChartTwoLineSeries->getFileLineSeriesChart(); + CaretAssert(fileLineSeriesChart); + const CaretMappableDataFile* mapFile = fileLineSeriesChart->getCaretMappableDataFile(); + CaretAssert(mapFile); + const ChartTwoDataCartesian* cartesianData = idChartTwoLineSeries->getChartTwoCartesianData(); + CaretAssert(cartesianData); + const MapFileDataSelector* mapFileDataSelector = cartesianData->getMapFileDataSelector(); + CaretAssert(mapFileDataSelector); + + int32_t primitiveIndex = idChartTwoLineSeries->getLineSegmentIndex(); + + AString boldText("Line Series Chart"); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + + cartesianData->getGraphicsPrimitive(); + const GraphicsPrimitive* primitive = cartesianData->getGraphicsPrimitive(); + CaretAssert(primitive); + + if (primitiveIndex >= 1) { + float xyz1[3]; + primitive->getVertexFloatXYZ(primitiveIndex - 1, + xyz1); + float xyz2[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz2); + idText.addLine(true, + "XY Start", + AString::fromNumbers(xyz1, 2, ", ")); + idText.addLine(true, + "XY End ", + AString::fromNumbers(xyz2, 2, ", ")); + } + else { + float xyz[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz); + idText.addLine(true, + "XY", + AString::fromNumbers(xyz, 2, ", ")); + } + + generateMapFileSelectorText(idText, + mapFileDataSelector); + } +} + +/** + * Generate identification text for a chart two matrix. + * + * @param idText + * String builder for identification text. + * @param idChartTwoMatrix + * Information for selected chart two matrix. + */ +void +IdentificationSimpleTextGenerator::generateChartTwoMatrixIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoMatrix* idChartTwoMatrix) const +{ + if (idChartTwoMatrix->isValid()) { + const ChartableTwoFileMatrixChart* matrixChart = idChartTwoMatrix->getFileMatrixChart(); + CaretAssert(matrixChart); + + const int32_t rowIndex = idChartTwoMatrix->getRowIndex(); + const int32_t colIndex = idChartTwoMatrix->getColumnIndex(); + + const CaretMappableDataFile* mapFile = matrixChart->getCaretMappableDataFile(); + CaretAssert(mapFile); + + const bool newIdFlag = true; + if (newIdFlag) { + AString boldText("MATRIX "); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + if ((rowIndex >= 0) + && (matrixChart->hasRowSelection())) { + const AString rowName = matrixChart->getRowName(rowIndex); + if ( ! rowName.isEmpty()) { + idText.addLine(true, + "", + rowName); + } + } + if ((colIndex >= 0) + && (matrixChart->hasColumnSelection())) { + const AString colName = matrixChart->getColumnName(colIndex); + if ( ! colName.isEmpty()) { + idText.addLine(true, + "", + colName); + } + } + } + else { + AString boldText("MATRIX ROW/COLUMN"); + idText.addLine(false, + boldText, + mapFile->getFileNameNoPath()); + + const CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(mapFile); + if (rowIndex >= 0) { + const AString rowName = (matrixFile != NULL) ? (" " + matrixFile->getRowName(rowIndex + 1)) : ""; + idText.addLine(true, + "Row", + (AString::number(rowIndex + 1) + rowName)); + } + if (colIndex >= 0) { + const AString colName = (matrixFile != NULL) ? (" " + matrixFile->getColumnName(colIndex + 1)) : ""; + idText.addLine(true, + "Column", + (AString::number(colIndex + 1) + colName)); + } + } + } +} + +/** + * Generate identification text for a CIFTI Connectivity Matrix Row/Column + * @param idText + * String builder for identification text. + * @param idCiftiConnMatrix + * Information for CIFTI Connectivity Matrix Row/Column. + */ +void +IdentificationSimpleTextGenerator::generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const +{ + if (idCiftiConnMatrix->isValid()) { + const CiftiMappableConnectivityMatrixDataFile* connMatrixFile = idCiftiConnMatrix->getCiftiConnectivityMatrixFile(); + const int32_t rowIndex = idCiftiConnMatrix->getMatrixRowIndex(); + const int32_t colIndex = idCiftiConnMatrix->getMatrixColumnIndex(); + + AString boldText("MATRIX ROW/COLUMN"); + idText.addLine(false, + boldText, + connMatrixFile->getFileNameNoPath()); + + AString rowName = " "; + AString colName = " "; + bool validData = true; + if (validData) { + if (rowIndex >= 0) { + idText.addLine(true, + ("Row " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + rowName); + } + if (colIndex >= 0) { + idText.addLine(true, + ("Column " + AString::number(colIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + colName); + } + } + } +} + +/** + * Generate identification text for chart data source. + * @param idText + * String builder for identification text. + * @param typeOfChartText + * Text describing the type of chart. + * @param chartDataSource + * Source of chart data. + */ +void +IdentificationSimpleTextGenerator::generateChartDataSourceText(IdentificationStringBuilder& idText, + const AString& typeOfChartText, + const ChartDataSource* chartDataSource) const +{ + AString chartFileName = chartDataSource->getChartableFileName(); + if (! chartFileName.isEmpty()) { + chartFileName = FileInformation(chartFileName).getFileName(); + } + + idText.addLine(false, + typeOfChartText, + chartDataSource->getChartableFileName()); + switch (chartDataSource->getDataSourceMode()) { + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: + { + AString fileName; + int32_t rowIndex; + chartDataSource->getFileRow(fileName, + rowIndex); + idText.addLine(true, + "File", + fileName); + idText.addLine(true, + "Row", + AString::number(rowIndex + 1)); + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: + { + AString structureName; + int32_t numberOfNodes; + int32_t nodeIndex; + chartDataSource->getSurfaceNode(structureName, + numberOfNodes, + nodeIndex); + idText.addLine(true, + "Structure", + structureName); + idText.addLine(true, + "Vertex Index", + AString::number(nodeIndex)); + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: + { + AString structureName; + int32_t numberOfNodes; + std::vector nodeIndices; + chartDataSource->getSurfaceNodeAverage(structureName, numberOfNodes, nodeIndices); + idText.addLine(true, + "Structure", + structureName); + idText.addLine(true, + "Vertex Average Count", + AString::number(nodeIndices.size())); + } + break; + case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: + { + float voxelXYZ[3]; + chartDataSource->getVolumeVoxel(voxelXYZ); + idText.addLine(true, + "Voxel XYZ", + AString::fromNumbers(voxelXYZ, 3, ",")); + } + break; + } +} + +/** + * Generate text for a map file data selector. + * @param idText + * String builder for identification text. + * @param mapFileDataSelector + * The map file data selector. + */ +void +IdentificationSimpleTextGenerator::generateMapFileSelectorText(IdentificationStringBuilder& idText, + const MapFileDataSelector* mapFileDataSelector) const +{ + + switch (mapFileDataSelector->getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t columnIndex = -1; + mapFileDataSelector->getColumnIndex(mapFile, + mapFileName, + columnIndex); + + idText.addLine(true, + "Column File", + mapFileName); + idText.addLine(true, + "Column Index", + AString::number(columnIndex + 1)); + } + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t rowIndex = -1; + mapFileDataSelector->getRowIndex(mapFile, + mapFileName, + rowIndex); + + idText.addLine(true, + "Row File", + mapFileName); + idText.addLine(true, + "Row Index", + AString::number(rowIndex + 1)); + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t numberOfVertices = 0; + int32_t vertexIndex = -1; + mapFileDataSelector->getSurfaceVertex(structure, + numberOfVertices, + vertexIndex); + + if ((structure != StructureEnum::INVALID) + && (vertexIndex >= 0)) { + idText.addLine(true, + "Structure", + StructureEnum::toGuiName(structure)); + idText.addLine(true, + "Vertex Index", + AString::number(vertexIndex)); + } + } + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t numberOfVertices = 0; + std::vector vertexIndices; + mapFileDataSelector->getSurfaceVertexAverage(structure, + numberOfVertices, + vertexIndices); + + const int32_t averageCount = static_cast(vertexIndices.size()); + if ((structure != StructureEnum::INVALID) + && (averageCount > 0)) { + idText.addLine(true, + "Structure", + StructureEnum::toGuiName(structure)); + idText.addLine(true, + "Vertex Average Count", + AString::number(averageCount)); + } + } + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + { + float voxelXYZ[3]; + mapFileDataSelector->getVolumeVoxelXYZ(voxelXYZ); + idText.addLine(true, + "Voxel XYZ", + AString::fromNumbers(voxelXYZ, 3, ",")); + } + break; + } +} + +/** + * Generate identification text for a time series chart. + * @param idText + * String builder for identification text. + * @param idChartTimeSeries + * Information for chart id. + */ +void +IdentificationSimpleTextGenerator::generateChartTimeSeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTimeSeries* idChartTimeSeries) const +{ + if (idChartTimeSeries->isValid()) { + const ChartDataCartesian* chartDataCartesian = idChartTimeSeries->getChartDataCartesian(); + + const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); + generateChartDataSourceText(idText, + "TIME SERIES CHART", + chartDataSource); + } +} + +/** + * Generate identification text for a surface border identification. + * @param idText + * String builder for identification text. + * @param idSurfaceBorder + * Information for surface border ID. + * @param toolTipFlag + * True if this is for tooltip. + */ +void +IdentificationSimpleTextGenerator::generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, + const SelectionItemBorderSurface* idSurfaceBorder, + const bool toolTipFlag) const +{ + if (idSurfaceBorder->isValid()) { + const Border* border = idSurfaceBorder->getBorder(); + const SurfaceProjectedItem* spi = border->getPoint(idSurfaceBorder->getBorderPointIndex()); + float xyz[3]; + spi->getProjectedPosition(*idSurfaceBorder->getSurface(), xyz, false); + + if (toolTipFlag) { + bool indentFlag = false; + idText.addLine(indentFlag, + "Border", + border->getName()); + indentFlag = true; + idText.addLine(indentFlag, + "XYZ", + AString::fromNumbers(xyz, 3, ",")); + } + else { + AString boldText = ("BORDER " + + StructureEnum::toGuiName(spi->getStructure()) + + " Name: " + + border->getName()); + if (border->getClassName().isEmpty() == false) { + boldText += (" ClassName: " + + border->getClassName() + + ": "); + } + + const AString text = ("(" + + AString::number(idSurfaceBorder->getBorderIndex()) + + "," + + AString::number(idSurfaceBorder->getBorderPointIndex()) + + ") (" + + AString::fromNumbers(xyz, 3, ",") + + ")"); + idText.addLine(false, boldText, text); + } + } +} + +/** + * Generate identification text for a surface focus identification. + * @param idText + * String builder for identification text. + * @param idSurfaceFocus + * Information for surface focus ID. + * @param toolTipFlag + * True if this is for tooltip. + */ +void +IdentificationSimpleTextGenerator::generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, + const SelectionItemFocusSurface* idSurfaceFocus, + const bool toolTipFlag) const +{ + if (idSurfaceFocus->isValid()) { + const Focus* focus = idSurfaceFocus->getFocus(); + const int32_t projectionIndex = idSurfaceFocus->getFocusProjectionIndex(); + const SurfaceProjectedItem* spi = focus->getProjection(projectionIndex); + float xyzStereo[3]; + spi->getStereotaxicXYZ(xyzStereo); + if (toolTipFlag) { + bool indentFlag = false; + idText.addLine(indentFlag, + "Focus", + focus->getName()); + indentFlag = true; + idText.addLine(indentFlag, + "XYZ", + (spi->isStereotaxicXYZValid() + ? AString::fromNumbers(xyzStereo, 3, ",") + : "Invalid")); + } + else { + idText.addLine(false, + "FOCUS", + focus->getName()); + + idText.addLine(true, + "Index", + AString::number(idSurfaceFocus->getFocusIndex())); + + float xyzProj[3]; + spi->getProjectedPosition(*idSurfaceFocus->getSurface(), xyzProj, false); + + idText.addLine(true, + "Structure", + StructureEnum::toGuiName(spi->getStructure())); + + if (spi->isStereotaxicXYZValid()) { + idText.addLine(true, + "XYZ (Stereotaxic)", + xyzStereo, + 3, + true); + } + else { + idText.addLine(true, + "XYZ (Stereotaxic)", + "Invalid"); + } + + bool projValid = false; + AString xyzProjName = "XYZ (Projected)"; + if (spi->getBarycentricProjection()->isValid()) { + xyzProjName = "XYZ (Projected to Triangle)"; + projValid = true; + } + else if (spi->getVanEssenProjection()->isValid()) { + xyzProjName = "XYZ (Projected to Edge)"; + projValid = true; + } + if (projValid) { + idText.addLine(true, + xyzProjName, + xyzProj, + 3, + true); + } + else { + idText.addLine(true, + xyzProjName, + "Invalid"); + } + + const int32_t numberOfProjections = focus->getNumberOfProjections(); + for (int32_t i = 0; i < numberOfProjections; i++) { + if (i != projectionIndex) { + const SurfaceProjectedItem* proj = focus->getProjection(i); + AString projTypeName = ""; + if (proj->getBarycentricProjection()->isValid()) { + projTypeName = "Triangle"; + + } + else if (proj->getVanEssenProjection()->isValid()) { + projTypeName = "Edge"; + } + if (projTypeName.isEmpty() == false) { + const AString txt = (StructureEnum::toGuiName(proj->getStructure()) + + " (" + + projTypeName + + ")"); + + idText.addLine(true, + "Ambiguous Projection", + txt); + } + } + } + + idText.addLine(true, + "Area", + focus->getArea()); + + idText.addLine(true, + "Class Name", + focus->getClassName()); + + idText.addLine(true, + "Comment", + focus->getComment()); + + idText.addLine(true, + "Extent", + focus->getExtent(), + true); + + idText.addLine(true, + "Geography", + focus->getGeography()); + + idText.addLine(true, + "Region of Interest", + focus->getRegionOfInterest()); + + idText.addLine(true, + "Statistic", + focus->getStatistic()); + + } + } +} + +/** + * Generate identification text for a volume focus identification. + * @param idText + * String builder for identification text. + * @param idVolumeFocus + * Information for surface focus ID. + */ +void +IdentificationSimpleTextGenerator::generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, + const SelectionItemFocusVolume* idVolumeFocus) const +{ + if (idVolumeFocus->isValid()) { + const Focus* focus = idVolumeFocus->getFocus(); + const SurfaceProjectedItem* spi = focus->getProjection(idVolumeFocus->getFocusProjectionIndex()); + float xyzVolume[3]; + spi->getVolumeXYZ(xyzVolume); + float xyzStereo[3]; + spi->getStereotaxicXYZ(xyzStereo); + + idText.addLine(false, + "FOCUS", + focus->getName()); + + idText.addLine(true, + "Index", + AString::number(idVolumeFocus->getFocusIndex())); + + idText.addLine(true, + "Structure", + StructureEnum::toGuiName(spi->getStructure())); + + if (spi->isStereotaxicXYZValid()) { + idText.addLine(true, + "XYZ (Stereotaxic)", + xyzStereo, + 3, + true); + } + else { + idText.addLine(true, + "XYZ (Stereotaxic)", + "Invalid"); + } + + AString xyzVolumeName = "XYZ (Volume)"; + idText.addLine(true, + xyzVolumeName, + xyzVolume, + 3, + true); + + idText.addLine(true, + "Area", + focus->getArea()); + + idText.addLine(true, + "Class Name", + focus->getClassName()); + + idText.addLine(true, + "Comment", + focus->getComment()); + + idText.addLine(true, + "Extent", + focus->getExtent(), + true); + + idText.addLine(true, + "Geography", + focus->getGeography()); + + idText.addLine(true, + "Region of Interest", + focus->getRegionOfInterest()); + + idText.addLine(true, + "Statistic", + focus->getStatistic()); + + } +} + +/** + * Generate identification text for image identification. + * @param idText + * String builder for identification text. + * @param idImage + * Information for image ID. + */ +void +IdentificationSimpleTextGenerator::generateImageIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemImage* idImage) const +{ + if (idImage->isValid()) { + AString text = ("Image " + + idImage->getImageFile()->getFileNameNoPath() + + " Pixel IJ (" + + AString::number(idImage->getPixelI()) + + "," + + AString::number(idImage->getPixelJ()) + + ")"); + + uint8_t pixelRGBA[4] = { 0, 0, 0, 0 }; + idImage->getPixelRGBA(pixelRGBA); + text.append(" RGBA (" + AString::fromNumbers(pixelRGBA, 4, ",") + ")"); + + idText.addLine(false, + text); + } +} + +/** + * Get text for the tooltip for a selected node. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationSimpleTextGenerator::generateSurfaceToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + const SelectionItemSurfaceNode* nodeSelection = selectionManager->getSurfaceNodeIdentification(); + CaretAssert(nodeSelection); + if (nodeSelection->isValid()) { + const Surface* surface = nodeSelection->getSurface(); + CaretAssert(surface); + int32_t surfaceNumberOfNodes = surface->getNumberOfNodes(); + int32_t surfaceNodeIndex = nodeSelection->getNodeNumber(); + StructureEnum::Enum surfaceStructure = surface->getStructure(); + + bool indentFlag = false; + if ((surfaceStructure != StructureEnum::INVALID) + && (surfaceNumberOfNodes > 0) + && (surfaceNodeIndex >= 0)) { + + bool addVertexFlag(false); + bool showSurfaceFlag = dataToolTipsManager->isShowSurfaceViewed(); + if (dataToolTipsManager->isShowSurfacePrimaryAnatomical()) { + const Surface* anatSurface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); + if (anatSurface != NULL) { + if (anatSurface->getNumberOfNodes() == surfaceNumberOfNodes) { + float xyz[3]; + anatSurface->getCoordinate(surfaceNodeIndex, + xyz); + idText.addLine(indentFlag, + "Vertex", + AString::number(surfaceNodeIndex)); + indentFlag = true; + addVertexFlag = false; + + idText.addLine(indentFlag, + "Anatomy Surface", + AString::fromNumbers(xyz, 3, ", ", 'f', 2)); + if (surface == anatSurface) { + showSurfaceFlag = false; + } + } + } + } + + if (showSurfaceFlag) { + float xyz[3]; + surface->getCoordinate(surfaceNodeIndex, + xyz); + if (addVertexFlag) { + idText.addLine(indentFlag, + "Vertex", + AString::number(surfaceNodeIndex)); + indentFlag = true; + } + + idText.addLine(indentFlag, + (SurfaceTypeEnum::toGuiName(surface->getSurfaceType()) + + " Surface"), + AString::fromNumbers(xyz, 3, ", ")); + } + + if (dataToolTipsManager->isShowTopEnabledLayer()) { + OverlaySet* overlaySet = const_cast(browserTab->getOverlaySet()); + CaretAssert(overlaySet); + Overlay* overlay = getTopEnabledOverlay(overlaySet); + if (overlay != NULL) { + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + mapFile->getSurfaceNodeIdentificationForMaps(mapIndices, + surfaceStructure, + surfaceNodeIndex, + surfaceNumberOfNodes, + " ", + textValue); + if ( ! textValue.isEmpty()) { + idText.addLine(indentFlag, + "Top Enabled Layer", + textValue); + } + } + } + } + } + } + + if (dataToolTipsManager->isShowBorder()) { + const SelectionItemBorderSurface* borderSelection = selectionManager->getSurfaceBorderIdentification(); + CaretAssert(borderSelection); + if (borderSelection->isValid()) { + generateSurfaceBorderIdentifcationText(idText, + borderSelection, + true); + } + } + + if (dataToolTipsManager->isShowFocus()) { + const SelectionItemFocusSurface* focusSelection = selectionManager->getSurfaceFocusIdentification(); + CaretAssert(focusSelection); + if (focusSelection->isValid()) { + generateSurfaceFociIdentifcationText(idText, + focusSelection, + true); + } + } +} + +/** + * Get text for the tooltip for a selected node. + * + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationSimpleTextGenerator::generateVolumeToolTip(const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + const SelectionItemVoxel* voxelSelection = selectionManager->getVoxelIdentification(); + + OverlaySet* overlaySet = const_cast(browserTab->getOverlaySet()); + CaretAssert(overlaySet); + + double selectionXYZ[3]; + voxelSelection->getModelXYZ(selectionXYZ); + float xyz[3] { + static_cast(selectionXYZ[0]), + static_cast(selectionXYZ[1]), + static_cast(selectionXYZ[2]) + }; + + bool indentFlag = false; + if (dataToolTipsManager->isShowVolumeUnderlay()) { + Overlay* volumeUnderlay = overlaySet->getUnderlayContainingVolume(); + if (volumeUnderlay != NULL) { + CaretMappableDataFile* mapFile = NULL; + int32_t mapIndex(-1); + volumeUnderlay->getSelectionData(mapFile, + mapIndex); + + VolumeMappableInterface* underlayVolumeInterface = NULL; + if (mapFile != NULL) { + underlayVolumeInterface = dynamic_cast(mapFile); + CaretAssert(underlayVolumeInterface == overlaySet->getUnderlayVolume()); + } + + if (underlayVolumeInterface != NULL) { + /* + * Update IJK and XYZ since selection XYZ may be + * a different volume file. + */ + int64_t selectionIJK[3]; + voxelSelection->getVoxelIJK(selectionIJK); + int64_t ijk[3] { selectionIJK[0], selectionIJK[1], selectionIJK[2] }; + + + bool validFlag(false); + const float value = underlayVolumeInterface->getVoxelValue(xyz[0], xyz[1], xyz[2], + &validFlag, + mapIndex); + if (validFlag) { + underlayVolumeInterface->enclosingVoxel(xyz[0], xyz[1], xyz[2], + ijk[0], ijk[1], ijk[2]); + underlayVolumeInterface->indexToSpace(ijk, xyz); + idText.addLine(indentFlag, + "Underlay Value", + AString::number(value, 'f')); + indentFlag = true; + idText.addLine(indentFlag, + "IJK: ", + AString::fromNumbers(ijk, 3, ", ")); + idText.addLine(indentFlag, + "XYZ", + AString::fromNumbers(xyz, 3, ", ", 'f', 1)); + } + } + } + } + + if (dataToolTipsManager->isShowTopEnabledLayer()) { + Overlay* overlay = getTopEnabledOverlay(overlaySet); + if (overlay != NULL) { + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + int64_t ijk[3]; + mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, + xyz, + " ", + ijk, + textValue); + if ( ! textValue.isEmpty()) { + idText.addLine(indentFlag, + ("Top Enabled Layer: " + + textValue)); + } + } + } + } +} + +/** + * @return Get the top-most enabled overlay. NULL if no overlays enabled + * + * @param overlaySet + * Overlay set for overlay. + */ +Overlay* +IdentificationSimpleTextGenerator::getTopEnabledOverlay(OverlaySet* overlaySet) const +{ + CaretAssert(overlaySet); + const int32_t numberOfOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numberOfOverlays; i++) { + Overlay* overlay = overlaySet->getOverlay(i); + CaretAssert(overlay); + if (overlay->isEnabled()) { + return overlay; + } + } + return NULL; +} + +/** + * Get text for the tooltip for a selected node. + * + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationSimpleTextGenerator::generateChartToolTip(const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + if (dataToolTipsManager->isShowChart()) { + this->generateChartTwoHistogramIdentificationText(idText, + selectionManager->getChartTwoHistogramIdentification()); + + this->generateChartTwoLineSeriesIdentificationText(idText, + selectionManager->getChartTwoLineSeriesIdentification()); + + this->generateChartTwoMatrixIdentificationText(idText, + selectionManager->getChartTwoMatrixIdentification()); + } +} + + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +IdentificationSimpleTextGenerator::toString() const +{ + return "IdentificationSimpleTextGenerator"; +} diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationSimpleTextGenerator.h connectome-workbench-1.5.0/src/Brain/IdentificationSimpleTextGenerator.h --- connectome-workbench-1.4.2/src/Brain/IdentificationSimpleTextGenerator.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationSimpleTextGenerator.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,163 @@ +#ifndef __IDENTIFICATION_SIMPLE_TEXT_GENERATOR__H_ +#define __IDENTIFICATION_SIMPLE_TEXT_GENERATOR__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "CaretObject.h" + +namespace caret { + + class Brain; + class BrowserTabContent; + class CaretMappableDataFile; + class ChartDataSource; + class DataToolTipsManager; + class MapFileDataSelector; + class Overlay; + class OverlaySet; + class SelectionItemBorderSurface; + class SelectionItemChartDataSeries; + class SelectionItemChartFrequencySeries; + class SelectionItemChartMatrix; + class SelectionItemChartTwoHistogram; + class SelectionItemChartTwoLineLayerVerticalNearest; + class SelectionItemChartTwoLineSeries; + class SelectionItemChartTwoMatrix; + class SelectionItemCiftiConnectivityMatrixRowColumn; + class SelectionItemChartTimeSeries; + class SelectionItemFocusSurface; + class SelectionItemFocusVolume; + class SelectionItemImage; + class SelectionItemSurfaceNode; + class SelectionItemVoxel; + class SelectionManager; + class IdentificationStringBuilder; + + class IdentificationSimpleTextGenerator : public CaretObject { + + public: + IdentificationSimpleTextGenerator(); + + virtual ~IdentificationSimpleTextGenerator(); + + AString createIdentificationText(const SelectionManager* idManager, + const Brain* brain) const; + + AString createToolTipText(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager) const; + + private: + IdentificationSimpleTextGenerator(const IdentificationSimpleTextGenerator&); + + IdentificationSimpleTextGenerator& operator=(const IdentificationSimpleTextGenerator&); + + public: + virtual AString toString() const; + + private: + void generateSurfaceToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + + void generateVolumeToolTip(const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + + void generateChartToolTip(const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + + void generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, + const SelectionItemBorderSurface* idSurfaceBorder, + const bool toolTipFlag) const; + + void generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, + const SelectionItemFocusSurface* idSurfaceFocus, + const bool toolTipFlag) const; + + void generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, + const SelectionItemFocusVolume* idVolumeFocus) const; + + void generateSurfaceIdentificationText(IdentificationStringBuilder& idText, + const Brain* brain, + const SelectionItemSurfaceNode* idSurfaceNode) const; + + void generateImageIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemImage* idImage) const; + + void generateVolumeIdentificationText(IdentificationStringBuilder& idText, + const Brain* brain, + const SelectionItemVoxel* idVolumeVoxel) const; + + void generateChartDataSeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartDataSeries* idChartDataSeries) const; + + void generateChartFrequencySeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartFrequencySeries* idChartFrequencySeries) const; + + void generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartMatrix* idChartMatrix) const; + + void generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoHistogram* idChartTwoHistogram) const; + + void generateChartTwoLineLayerIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineLayerVerticalNearest* idChartTwoLineLayer) const; + + void generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineSeries* idChartTwoLineSeries) const; + + void generateChartTwoMatrixIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoMatrix* idChartTwoMatrix) const; + + void generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const; + + void generateChartTimeSeriesIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTimeSeries* idChartTimeSeries) const; + + void getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, + std::vector& mapIndicesOut) const; + + void generateChartDataSourceText(IdentificationStringBuilder& idText, + const AString& typeOfChartText, + const ChartDataSource* chartDataSource) const; + + void generateMapFileSelectorText(IdentificationStringBuilder& idText, + const MapFileDataSelector* mapFileDataSelector) const; + + Overlay* getTopEnabledOverlay(OverlaySet* overlaySet) const; + + friend class DataToolTipsManager; + }; + +#ifdef __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ + // +#endif // __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_SIMPLE_TEXT_GENERATOR__H_ diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationSymbolSizeTypeEnum.cxx connectome-workbench-1.5.0/src/Brain/IdentificationSymbolSizeTypeEnum.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationSymbolSizeTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationSymbolSizeTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,417 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __IDENTIFICATION_SYMBOL_SIZE_TYPE_ENUM_DECLARE__ +#include "IdentificationSymbolSizeTypeEnum.h" +#undef __IDENTIFICATION_SYMBOL_SIZE_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::IdentificationSymbolSizeTypeEnum + * \brief Enumerated type for sizing of identification symbol + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_identificationSymbolSizeTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void identificationSymbolSizeTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "IdentificationSymbolSizeTypeEnum.h" + * + * Instatiate: + * m_identificationSymbolSizeTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_identificationSymbolSizeTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_identificationSymbolSizeTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(identificationSymbolSizeTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_identificationSymbolSizeTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const IdentificationSymbolSizeTypeEnum::Enum VARIABLE = m_identificationSymbolSizeTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +IdentificationSymbolSizeTypeEnum::IdentificationSymbolSizeTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +IdentificationSymbolSizeTypeEnum::~IdentificationSymbolSizeTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +IdentificationSymbolSizeTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(IdentificationSymbolSizeTypeEnum(MILLIMETERS, + "MILLIMETERS", + "Millimeters")); + + enumData.push_back(IdentificationSymbolSizeTypeEnum(PERCENTAGE, + "PERCENTAGE", + "Percentage")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const IdentificationSymbolSizeTypeEnum* +IdentificationSymbolSizeTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const IdentificationSymbolSizeTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +IdentificationSymbolSizeTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const IdentificationSymbolSizeTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +IdentificationSymbolSizeTypeEnum::Enum +IdentificationSymbolSizeTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationSymbolSizeTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationSymbolSizeTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type IdentificationSymbolSizeTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +IdentificationSymbolSizeTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const IdentificationSymbolSizeTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +IdentificationSymbolSizeTypeEnum::Enum +IdentificationSymbolSizeTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationSymbolSizeTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationSymbolSizeTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type IdentificationSymbolSizeTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +IdentificationSymbolSizeTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const IdentificationSymbolSizeTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +IdentificationSymbolSizeTypeEnum::Enum +IdentificationSymbolSizeTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationSymbolSizeTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationSymbolSizeTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type IdentificationSymbolSizeTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +IdentificationSymbolSizeTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +IdentificationSymbolSizeTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(IdentificationSymbolSizeTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +IdentificationSymbolSizeTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(IdentificationSymbolSizeTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + +/** + * Get tooltip for use in GUI. + */ +AString +IdentificationSymbolSizeTypeEnum::getToolTip(const AString& symbolTypeName) +{ + AString tt("" + "Select method for sizing YYYY symbols." + "
    "); + + std::vector allEnums; + getAllEnums(allEnums); + + for (auto e : allEnums) { + tt.append("
  • " + toGuiName(e) + " - "); + switch (e) { + case MILLIMETERS: + tt.append("XXXX symbols are sized in millimeters. If viewing brains of different sizes " + "(different species; child vs adult), the YYYY symbols appear larger " + "on smaller brains (monkey, child) and smaller on larger brains (human, adult)."); + break; + case PERCENTAGE: + tt.append("XXXX symbols are sized as a percentage of the maximum brain dimension (typically " + "the Anterior-Posterior axis). " + "Thus, the YYYY symbols are drawn in a proportional size on all brains."); + break; + } + } + tt.append("
"); + tt.append("Zooming a surface or volume always affects the drawn size of the YYYY symbols."); + tt.append(""); + + if ( ! symbolTypeName.isEmpty()) { + AString upperCase(symbolTypeName); + upperCase[0] = upperCase[0].toUpper(); + AString lowerCase(symbolTypeName); + lowerCase[0] = lowerCase[0].toLower(); + tt = tt.replace("XXXX", upperCase); + tt = tt.replace("YYYY", lowerCase); + } + + return tt; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationSymbolSizeTypeEnum.h connectome-workbench-1.5.0/src/Brain/IdentificationSymbolSizeTypeEnum.h --- connectome-workbench-1.4.2/src/Brain/IdentificationSymbolSizeTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationSymbolSizeTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __IDENTIFICATION_SYMBOL_SIZE_TYPE_ENUM_H__ +#define __IDENTIFICATION_SYMBOL_SIZE_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class IdentificationSymbolSizeTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + MILLIMETERS, + /** */ + PERCENTAGE + }; + + + ~IdentificationSymbolSizeTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + + static AString getToolTip(const AString& symbolTypeName); + +private: + IdentificationSymbolSizeTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const IdentificationSymbolSizeTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __IDENTIFICATION_SYMBOL_SIZE_TYPE_ENUM_DECLARE__ +std::vector IdentificationSymbolSizeTypeEnum::enumData; +bool IdentificationSymbolSizeTypeEnum::initializedFlag = false; +int32_t IdentificationSymbolSizeTypeEnum::integerCodeCounter = 0; +#endif // __IDENTIFICATION_SYMBOL_SIZE_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_SYMBOL_SIZE_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationTextGenerator.cxx connectome-workbench-1.5.0/src/Brain/IdentificationTextGenerator.cxx --- connectome-workbench-1.4.2/src/Brain/IdentificationTextGenerator.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationTextGenerator.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,1885 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ -#include "IdentificationTextGenerator.h" -#undef __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ - -#include "Border.h" -#include "Brain.h" -#include "BrainStructure.h" -#include "BrowserTabContent.h" -#include "CaretAssert.h" -#include "CaretMappableDataFile.h" -#include "ChartDataCartesian.h" -#include "ChartDataSource.h" -#include "ChartModelDataSeries.h" -#include "ChartTwoDataCartesian.h" -#include "ChartableMatrixInterface.h" -#include "ChartableTwoFileDelegate.h" -#include "ChartableTwoFileHistogramChart.h" -#include "ChartableTwoFileLineSeriesChart.h" -#include "ChartableTwoFileMatrixChart.h" -#include "CiftiMappableConnectivityMatrixDataFile.h" -#include "CiftiMappableDataFile.h" -#include "CaretVolumeExtension.h" -#include "DataToolTipsManager.h" -#include "EventBrowserTabGetAll.h" -#include "EventManager.h" -#include "FileInformation.h" -#include "FociFile.h" -#include "Focus.h" -#include "GiftiLabel.h" -#include "GraphicsPrimitive.h" -#include "GraphicsPrimitiveV3f.h" -#include "Histogram.h" -#include "ImageFile.h" -#include "MapFileDataSelector.h" -#include "MetricDynamicConnectivityFile.h" -#include "OverlaySet.h" -#include "SelectionItemBorderSurface.h" -#include "SelectionItemChartDataSeries.h" -#include "SelectionItemChartFrequencySeries.h" -#include "SelectionItemChartMatrix.h" -#include "SelectionItemCiftiConnectivityMatrixRowColumn.h" -#include "SelectionItemChartTimeSeries.h" -#include "SelectionItemChartTwoHistogram.h" -#include "SelectionItemChartTwoLineSeries.h" -#include "SelectionItemChartTwoMatrix.h" -#include "SelectionItemFocusSurface.h" -#include "SelectionItemFocusVolume.h" -#include "SelectionItemImage.h" -#include "SelectionItemSurfaceNode.h" -#include "SelectionItemVoxel.h" -#include "SelectionManager.h" -#include "IdentificationStringBuilder.h" -#include "LabelFile.h" -#include "MetricFile.h" -#include "Surface.h" -#include "VolumeDynamicConnectivityFile.h" -#include "SurfaceProjectedItem.h" -#include "SurfaceProjectionBarycentric.h" -#include "SurfaceProjectionVanEssen.h" -#include "VolumeFile.h" - -using namespace caret; - - - -/** - * \class IdentificationTextGenerator - * \brief Creates text describing selected data. - * - * Examine the selected data and generate descriptive text. - */ - -/** - * Constructor. - */ -IdentificationTextGenerator::IdentificationTextGenerator() -: CaretObject() -{ - -} - -/** - * Destructor. - */ -IdentificationTextGenerator::~IdentificationTextGenerator() -{ - -} - -/** - * Create identification text from selection in the identification manager. - * @param idManager - * Identification manager containing selection. - * @param brain - * The brain. - */ -AString -IdentificationTextGenerator::createIdentificationText(const SelectionManager* idManager, - const Brain* brain) const -{ - CaretAssert(idManager); - CaretAssert(brain); - - IdentificationStringBuilder idText; - - const SelectionItemSurfaceNode* surfaceID = idManager->getSurfaceNodeIdentification(); - - this->generateSurfaceIdentificationText(idText, - brain, - surfaceID); - - this->generateSurfaceBorderIdentifcationText(idText, - idManager->getSurfaceBorderIdentification(), - false); - - this->generateSurfaceFociIdentifcationText(idText, - idManager->getSurfaceFocusIdentification(), - false); - - this->generateVolumeFociIdentifcationText(idText, - idManager->getVolumeFocusIdentification()); - - this->generateVolumeIdentificationText(idText, - brain, - idManager->getVoxelIdentification()); - - this->generateChartDataSeriesIdentificationText(idText, - idManager->getChartDataSeriesIdentification()); - - this->generateChartFrequencySeriesIdentificationText(idText, - idManager->getChartFrequencySeriesIdentification()); - - this->generateChartTimeSeriesIdentificationText(idText, - idManager->getChartTimeSeriesIdentification()); - - this->generateChartMatrixIdentificationText(idText, - idManager->getChartMatrixIdentification()); - - this->generateCiftiConnectivityMatrixIdentificationText(idText, - idManager->getCiftiConnectivityMatrixRowColumnIdentification()); - - this->generateChartTwoHistogramIdentificationText(idText, - idManager->getChartTwoHistogramIdentification()); - - this->generateChartTwoLineSeriesIdentificationText(idText, - idManager->getChartTwoLineSeriesIdentification()); - - this->generateChartTwoMatrixIdentificationText(idText, - idManager->getChartTwoMatrixIdentification()); - - this->generateImageIdentificationText(idText, - idManager->getImageIdentification()); - - return idText.toString(); -} - -/** - * Get text for the tooltip for a selected node. - * - * @param brain - * The Brain. - * @param browserTab - * Browser tab in which tooltip is displayed - * @param selectionManager - * The selection manager. - * @param dataToolTipsManager - * The data tooltips manager - * @param idText - * String builder for identification text. - */ -AString -IdentificationTextGenerator::createToolTipText(const Brain* brain, - const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const DataToolTipsManager* dataToolTipsManager) const -{ - CaretAssert(brain); - CaretAssert(browserTab); - CaretAssert(selectionManager); - CaretAssert(dataToolTipsManager); - - const SelectionItemSurfaceNode* selectedNode = selectionManager->getSurfaceNodeIdentification(); - const SelectionItemVoxel* selectedVoxel = selectionManager->getVoxelIdentification(); - - IdentificationStringBuilder idText; - - if (selectedNode->isValid()) { - generateSurfaceToolTip(brain, - browserTab, - selectionManager, - dataToolTipsManager, - idText); - } - else if (selectedVoxel->isValid()) { - generateVolumeToolTip(browserTab, - selectionManager, - dataToolTipsManager, - idText); - } - else { - generateChartToolTip(selectionManager, - dataToolTipsManager, - idText); - } - - AString text; - if (idText.length() > 0) { - text = idText.toStringWithHtmlBodyForToolTip(); - } - - return text; -} - - -/** - * Generate identification text for volume voxel identification. - * - * @param idText - * String builder for identification text. - * @param brain - * The brain. - * @param idVolumeVoxel - * Information for volume voxel ID. - */ -void -IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStringBuilder& idText, - const Brain* brain, - const SelectionItemVoxel* idVolumeVoxel) const -{ - if (idVolumeVoxel->isValid() == false) { - return; - } - - int64_t ijk[3]; - const VolumeMappableInterface* idVolumeFile = idVolumeVoxel->getVolumeFile(); - idVolumeVoxel->getVoxelIJK(ijk); - float x, y, z; - idVolumeFile->indexToSpace(ijk[0], ijk[1], ijk[2], x, y, z); - - idText.addLine(false, - "Voxel XYZ (" - + AString::number(x) - + ", " - + AString::number(y) - + ", " - + AString::number(z) - + ")"); - - const float xyz[3] = { x, y, z }; - - - /* - * Get all volume files - */ - std::vector volumeInterfaces; - const int32_t numVolumeFiles = brain->getNumberOfVolumeFiles(); - for (int32_t i = 0; i < numVolumeFiles; i++) { - const VolumeFile* vf = brain->getVolumeFile(i); - volumeInterfaces.push_back(vf); - - const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); - if (volDynConnFile != NULL) { - if (volDynConnFile->isDataValid()) { - volumeInterfaces.push_back(volDynConnFile); - } - } - } - - /* - * Get the CIFTI files that are volume mappable - */ - std::vector allCiftiMappableDataFiles; - brain->getAllCiftiMappableDataFiles(allCiftiMappableDataFiles); - for (std::vector::iterator ciftiMapIter = allCiftiMappableDataFiles.begin(); - ciftiMapIter != allCiftiMappableDataFiles.end(); - ciftiMapIter++) { - const CiftiMappableDataFile* cmdf = *ciftiMapIter; - if (cmdf->isEmpty() == false) { - if (cmdf->isVolumeMappable()) { - volumeInterfaces.push_back(cmdf); - } - } - } - - /* - * In first loop, show values for 'idVolumeFile' (the underlay volume) - * In second loop, show values for all other volume files - */ - const int32_t numberOfVolumeMappableFiles = static_cast(volumeInterfaces.size()); - for (int32_t iLoop = 0; iLoop < 2; iLoop++) { - for (int32_t i = 0; i < numberOfVolumeMappableFiles; i++) { - const VolumeMappableInterface* volumeInterfaceFile = volumeInterfaces[i]; - const VolumeFile* volumeFile = dynamic_cast(volumeInterfaceFile); - const CiftiMappableDataFile* ciftiFile = dynamic_cast(volumeInterfaceFile); - CaretAssert((volumeFile != NULL) - || (ciftiFile != NULL)); - const CaretMappableDataFile* caretMappableDataFile = dynamic_cast(volumeInterfaceFile); - CaretAssert(caretMappableDataFile != NULL); - - if (volumeInterfaceFile == idVolumeFile) { - if (iLoop != 0) { - continue; - } - } - else if (iLoop == 0) { - continue; - } - - int64_t vfI, vfJ, vfK; - volumeInterfaceFile->enclosingVoxel(x, y, z, - vfI, vfJ, vfK); - - if (volumeInterfaceFile->indexValid(vfI, vfJ, vfK)) { - if (volumeFile != NULL) { - AString boldText = caretMappableDataFile->getFileNameNoPath(); - boldText += (" IJK (" - + AString::number(vfI) - + ", " - + AString::number(vfJ) - + ", " - + AString::number(vfK) - + ") "); - - AString text; - const int32_t numMaps = caretMappableDataFile->getNumberOfMaps(); - for (int jMap = 0; jMap < numMaps; jMap++) { - if (jMap > 0) { - text += " "; - } - if (volumeFile != NULL) { - if (volumeFile->getType() == SubvolumeAttributes::LABEL) { - const int32_t labelIndex = static_cast(volumeFile->getValue(vfI, vfJ, vfK, jMap)); - const GiftiLabelTable* glt = volumeFile->getMapLabelTable(jMap); - const GiftiLabel* gl = glt->getLabel(labelIndex); - if (gl != NULL) { - text += gl->getName(); - } - else { - text += ("LABLE_MISSING_FOR_INDEX=" - + AString::number(labelIndex)); - } - } - else if (volumeFile->getType() == SubvolumeAttributes::RGB) { - if (volumeFile->getNumberOfComponents() == 4) { - text += ("RGBA(" - + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 0)) - + "," - + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 1)) - + "," - + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 2)) - + "," - + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 3)) - + ")"); - } - else if (volumeFile->getNumberOfComponents() == 3) { - text += ("RGB(" - + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 0)) - + "," - + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 1)) - + "," - + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 2)) - + ")"); - } - } - else { - text += AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap)); - } - } - else if (ciftiFile != NULL) { - - } - } - - if (dynamic_cast(volumeFile) != NULL) { - boldText.insert(0, - (DataFileTypeEnum::toOverlayTypeName(DataFileTypeEnum::VOLUME_DYNAMIC) + " ")); - } - idText.addLine(true, - boldText, - text); - } - else if (ciftiFile != NULL) { - if (ciftiFile->isEmpty() == false) { - const int numMaps = ciftiFile->getNumberOfMaps(); - std::vector mapIndices; - for (int32_t i = 0; i < numMaps; i++) { - mapIndices.push_back(i); - } - - /* - * Limit dense scalar and data series to maps selected in the overlays - * from all tabs. - */ - bool limitMapIndicesFlag = false; - switch (ciftiFile->getDataFileType()) { - case DataFileTypeEnum::ANNOTATION: - break; - case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: - break; - case DataFileTypeEnum::BORDER: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: - break; - case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL: - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: - break; - case DataFileTypeEnum::FOCI: - break; - case DataFileTypeEnum::IMAGE: - break; - case DataFileTypeEnum::LABEL: - break; - case DataFileTypeEnum::METRIC: - break; - case DataFileTypeEnum::METRIC_DYNAMIC: - break; - case DataFileTypeEnum::PALETTE: - break; - case DataFileTypeEnum::RGBA: - break; - case DataFileTypeEnum::SCENE: - break; - case DataFileTypeEnum::SPECIFICATION: - break; - case DataFileTypeEnum::SURFACE: - break; - case DataFileTypeEnum::UNKNOWN: - CaretAssert(0); - break; - case DataFileTypeEnum::VOLUME: - break; - case DataFileTypeEnum::VOLUME_DYNAMIC: - break; - } - if (limitMapIndicesFlag) { - getMapIndicesOfFileUsedInOverlays(ciftiFile, - mapIndices); - } - - AString textValue; - int64_t voxelIJK[3]; - if (ciftiFile->getVolumeVoxelIdentificationForMaps(mapIndices, - xyz, - voxelIJK, - textValue)) { - AString boldText = (DataFileTypeEnum::toOverlayTypeName(ciftiFile->getDataFileType()) - + " " - + ciftiFile->getFileNameNoPath() - + " IJK (" - + AString::number(voxelIJK[0]) - + ", " - + AString::number(voxelIJK[1]) - + ", " - + AString::number(voxelIJK[2]) - + ") "); - idText.addLine(true, boldText, textValue); - } - } - } - } - } - } -} - -/** - * Generate identification text for a surface node identification. - * @param idText - * String builder for identification text. - * @param brain - * The brain. - * @param browserTabContent - * Content of the browser tab. - * @param idSurfaceNode - * Information for surface node ID. - */ -void -IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStringBuilder& idText, - const Brain* brain, - const SelectionItemSurfaceNode* idSurfaceNode) const -{ - const Surface* surface = idSurfaceNode->getSurface(); - const int32_t nodeNumber = idSurfaceNode->getNodeNumber(); - - if ((surface != NULL) - && (nodeNumber >= 0)) { - AString surfaceID; - surfaceID += ("VERTEX " + StructureEnum::toGuiName(surface->getStructure())); - idText.addLine(false, surfaceID, nodeNumber, false); - - const float* xyz = surface->getCoordinate(nodeNumber); - - idText.addLine(true, SurfaceTypeEnum::toGuiName(surface->getSurfaceType()).toUpper() - + " XYZ: " - + AString::number(xyz[0]) - + ", " - + AString::number(xyz[1]) - + ", " - + AString::number(xyz[2])); - - const BrainStructure* brainStructure = surface->getBrainStructure(); - CaretAssert(brainStructure); - - std::vector allCiftiMappableDataFiles; - brain->getAllCiftiMappableDataFiles(allCiftiMappableDataFiles); - for (std::vector::iterator ciftiMapIter = allCiftiMappableDataFiles.begin(); - ciftiMapIter != allCiftiMappableDataFiles.end(); - ciftiMapIter++) { - const CiftiMappableDataFile* cmdf = *ciftiMapIter; - AString boldText = (DataFileTypeEnum::toOverlayTypeName(cmdf->getDataFileType()) - + " " - + cmdf->getFileNameNoPath()); - - std::vector mapIndices; - for (int32_t i = 0; i < cmdf->getNumberOfMaps(); i++) { - mapIndices.push_back(i); - } - - /* - * Limit dense scalar and data series to maps selected in the overlays - * from all tabs. - */ - bool limitMapIndicesFlag = false; - switch (cmdf->getDataFileType()) { - case DataFileTypeEnum::ANNOTATION: - break; - case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: - break; - case DataFileTypeEnum::BORDER: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: - break; - case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL: - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: - limitMapIndicesFlag = true; - break; - case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: - break; - case DataFileTypeEnum::FOCI: - break; - case DataFileTypeEnum::IMAGE: - break; - case DataFileTypeEnum::LABEL: - break; - case DataFileTypeEnum::METRIC: - break; - case DataFileTypeEnum::METRIC_DYNAMIC: - break; - case DataFileTypeEnum::PALETTE: - break; - case DataFileTypeEnum::RGBA: - break; - case DataFileTypeEnum::SCENE: - break; - case DataFileTypeEnum::SPECIFICATION: - break; - case DataFileTypeEnum::SURFACE: - break; - case DataFileTypeEnum::UNKNOWN: - CaretAssert(0); - break; - case DataFileTypeEnum::VOLUME: - break; - case DataFileTypeEnum::VOLUME_DYNAMIC: - break; - } - if (limitMapIndicesFlag) { - getMapIndicesOfFileUsedInOverlays(cmdf, - mapIndices); - } - AString textValue; - - const bool valid = cmdf->getSurfaceNodeIdentificationForMaps(mapIndices, - surface->getStructure(), - nodeNumber, - surface->getNumberOfNodes(), - textValue); - if (valid) { - idText.addLine(true, - boldText, - textValue); - } - } - - - const int32_t numLabelFiles = brainStructure->getNumberOfLabelFiles(); - for (int32_t i = 0; i < numLabelFiles; i++) { - const LabelFile* lf = brainStructure->getLabelFile(i); - AString boldText = "LABEL " + lf->getFileNameNoPath(); - AString text; - const int numMaps = lf->getNumberOfMaps(); - for (int32_t j = 0; j < numMaps; j++) { - AString labelName = lf->getLabelName(nodeNumber, j); - if (labelName.isEmpty()) { - labelName = ("Map-" + AString::number(j + 1)); - } - text += (" " + labelName); - } - idText.addLine(true, boldText, text); - } - - std::vector metricDynConFiles; - const int32_t numMetricFiles = brainStructure->getNumberOfMetricFiles(); - for (int32_t i = 0; i < numMetricFiles; i++) { - const MetricFile* mf = brainStructure->getMetricFile(i); - AString boldText = "METRIC " + mf->getFileNameNoPath(); - AString text; - const int numMaps = mf->getNumberOfMaps(); - for (int32_t j = 0; j < numMaps; j++) { - text += (" " + AString::number(mf->getValue(nodeNumber, j))); - } - idText.addLine(true, boldText, text); - - const MetricDynamicConnectivityFile* mdcf = mf->getMetricDynamicConnectivityFile(); - if (mdcf != NULL) { - if (mdcf->isDataValid()) { - if (mdcf->isEnabledAsLayer()) { - AString boldText = "METRIC DYNAMIC " + mdcf->getFileNameNoPath(); - AString text; - const int numMaps = mdcf->getNumberOfMaps(); - for (int32_t j = 0; j < numMaps; j++) { - text += (" " + AString::number(mdcf->getValue(nodeNumber, j))); - } - idText.addLine(true, boldText, text); - } - } - } - } - } -} - -/** - * Find the usage of the file's maps in all overlays. - * - * @param caretMappableDataFile - * The file whose usage is desired. - * @param mapIndicesOut - * Indices of maps of the file that are used in overlays. - */ -void -IdentificationTextGenerator::getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, - std::vector& mapIndicesOut) const -{ - mapIndicesOut.clear(); - - EventBrowserTabGetAll allTabsEvent; - EventManager::get()->sendEvent(allTabsEvent.getPointer()); - const std::vector allTabs = allTabsEvent.getAllBrowserTabs(); - for (std::vector::const_iterator tabIter = allTabs.begin(); - tabIter != allTabs.end(); - tabIter++) { - BrowserTabContent* tabContent = *tabIter; - OverlaySet* overlaySet = tabContent->getOverlaySet(); - if (overlaySet != NULL) { - std::vector mapIndices; - overlaySet->getSelectedMapIndicesForFile(caretMappableDataFile, - false, // true => enabled overlays - mapIndices); - mapIndicesOut.insert(mapIndicesOut.end(), - mapIndices.begin(), - mapIndices.end()); - } - } - - /* - * Sort and remove all duplicates - */ - if (mapIndicesOut.empty() == false) { - std::sort(mapIndicesOut.begin(), - mapIndicesOut.end()); - std::vector::iterator uniqueIter = std::unique(mapIndicesOut.begin(), - mapIndicesOut.end()); - mapIndicesOut.resize(std::distance(mapIndicesOut.begin(), - uniqueIter)); - } -} - -/** - * Generate identification text for a data series chart. - * @param idText - * String builder for identification text. - * @param idChartDataSeries - * Information for chart id. - */ -void -IdentificationTextGenerator::generateChartDataSeriesIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartDataSeries* idChartDataSeries) const -{ - if (idChartDataSeries->isValid()) { - const ChartDataCartesian* chartDataCartesian = idChartDataSeries->getChartDataCartesian(); - - const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); - generateChartDataSourceText(idText, - "DATA SERIES CHART", - chartDataSource); - } -} - -/** - * Generate identification text for a data series chart. - * @param idText - * String builder for identification text. - * @param idChartDataSeries - * Information for chart id. - */ -void -IdentificationTextGenerator::generateChartFrequencySeriesIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartFrequencySeries* idChartFrequencySeries) const -{ - if (idChartFrequencySeries->isValid()) { - const ChartDataCartesian* chartDataCartesian = idChartFrequencySeries->getChartDataCartesian(); - - const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); - generateChartDataSourceText(idText, - "FREQUENCY SERIES CHART", - chartDataSource); - } -} - -/** - * Generate identification text for a matrix chart. - * @param idText - * String builder for identification text. - * @param idChartMatrix - * Information for matrix chart id. - */ -void -IdentificationTextGenerator::generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartMatrix* idChartMatrix) const -{ - if (idChartMatrix->isValid()) { - const ChartableMatrixInterface* chartMatrixInterface = idChartMatrix->getChartableMatrixInterface(); - const CaretMappableDataFile* caretMappableDataFile = chartMatrixInterface->getMatrixChartCaretMappableDataFile(); - - const int32_t rowIndex = idChartMatrix->getMatrixRowIndex(); - const int32_t columnIndex = idChartMatrix->getMatrixColumnIndex(); - AString rowName; - AString columnName; - AString cellValue; - const bool validData = chartMatrixInterface->getMatrixCellAttributes(rowIndex, - columnIndex, - cellValue, - rowName, - columnName); - - AString boldText("MATRIX CHART"); - idText.addLine(false, - boldText, - caretMappableDataFile->getFileNameNoPath()); - - if (validData) { - idText.addLine(true, - ("Row " + AString::number(rowIndex + 1)), - rowName); - idText.addLine(true, - ("Column " + AString::number(columnIndex + 1)), - columnName); - idText.addLine(true, "Value", - cellValue); - } - } -} - -/** - * Generate identification text for a chart two histogram. - * - * @param idText - * String builder for identification text. - * @param idChartTwoHistogram - * Information for selected chart two histogram. - */ -void -IdentificationTextGenerator::generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartTwoHistogram* idChartTwoHistogram) const -{ - const int32_t mapIndex = idChartTwoHistogram->getMapIndex(); - const int32_t bucketIndex = idChartTwoHistogram->getBucketIndex(); - const bool allMapsFlag = idChartTwoHistogram->isAllMapsSelected(); - - if (idChartTwoHistogram->isValid()) { - ChartableTwoFileHistogramChart* fileHistogramChart = idChartTwoHistogram->getFileHistogramChart(); - CaretAssert(fileHistogramChart); - CaretMappableDataFile* mapFile = fileHistogramChart->getCaretMappableDataFile(); - CaretAssert(mapFile); - - { - ChartableTwoFileHistogramChart* chartingDelegate = mapFile->getChartingDelegate()->getHistogramCharting(); - CaretAssert(chartingDelegate); - const Histogram* histogram = chartingDelegate->getHistogramForChartDrawing(mapIndex, - allMapsFlag); - CaretAssert(histogram); - - float bucketValue = 0.0; - float bucketHeight = 0.0; - if (histogram->getHistogramDisplayBucketDataValueAndHeight(bucketIndex, bucketValue, bucketHeight)) { - AString boldText("Histogram"); - idText.addLine(false, - boldText, - mapFile->getFileNameNoPath()); - - idText.addLine(true, - "Bucket Index", - (AString::number(bucketIndex))); - - idText.addLine(true, - "Data Value at Bucket", - (AString::number(bucketValue))); - - const int64_t bucketHeightInteger = static_cast(bucketHeight); - idText.addLine(true, - "Bucket Count", - (AString::number(bucketHeightInteger))); - } - } - } -} - -/** - * Generate identification text for a chart two line-series. - * - * @param idText - * String builder for identification text. - * @param idChartTwoLineSeries - * Information for selected chart two line-series. - */ -void -IdentificationTextGenerator::generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartTwoLineSeries* idChartTwoLineSeries) const -{ - if (idChartTwoLineSeries->isValid()) { - const ChartableTwoFileLineSeriesChart* fileLineSeriesChart = idChartTwoLineSeries->getFileLineSeriesChart(); - CaretAssert(fileLineSeriesChart); - const CaretMappableDataFile* mapFile = fileLineSeriesChart->getCaretMappableDataFile(); - CaretAssert(mapFile); - const ChartTwoDataCartesian* cartesianData = idChartTwoLineSeries->getChartTwoCartesianData(); - CaretAssert(cartesianData); - const MapFileDataSelector* mapFileDataSelector = cartesianData->getMapFileDataSelector(); - CaretAssert(mapFileDataSelector); - - int32_t primitiveIndex = idChartTwoLineSeries->getLineSegmentIndex(); - - AString boldText("Line Chart"); - idText.addLine(false, - boldText, - mapFile->getFileNameNoPath()); - - cartesianData->getGraphicsPrimitive(); - const GraphicsPrimitive* primitive = cartesianData->getGraphicsPrimitive(); - CaretAssert(primitive); - - if (primitiveIndex >= 1) { - float xyz1[3]; - primitive->getVertexFloatXYZ(primitiveIndex - 1, - xyz1); - float xyz2[3]; - primitive->getVertexFloatXYZ(primitiveIndex, - xyz2); - idText.addLine(true, - "XY Start", - AString::fromNumbers(xyz1, 2, ", ")); - idText.addLine(true, - "XY End ", - AString::fromNumbers(xyz2, 2, ", ")); - } - else { - float xyz[3]; - primitive->getVertexFloatXYZ(primitiveIndex, - xyz); - idText.addLine(true, - "XY", - AString::fromNumbers(xyz, 2, ", ")); - } - - generateMapFileSelectorText(idText, - mapFileDataSelector); - } -} - -/** - * Generate identification text for a chart two matrix. - * - * @param idText - * String builder for identification text. - * @param idChartTwoMatrix - * Information for selected chart two matrix. - */ -void -IdentificationTextGenerator::generateChartTwoMatrixIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartTwoMatrix* idChartTwoMatrix) const -{ - if (idChartTwoMatrix->isValid()) { - const ChartableTwoFileMatrixChart* matrixChart = idChartTwoMatrix->getFileMatrixChart(); - CaretAssert(matrixChart); - - const int32_t rowIndex = idChartTwoMatrix->getRowIndex(); - const int32_t colIndex = idChartTwoMatrix->getColumnIndex(); - - const CaretMappableDataFile* mapFile = matrixChart->getCaretMappableDataFile(); - CaretAssert(mapFile); - - const bool newIdFlag = true; - if (newIdFlag) { - AString boldText("MATRIX "); - idText.addLine(false, - boldText, - mapFile->getFileNameNoPath()); - if ((rowIndex >= 0) - && (matrixChart->hasRowSelection())) { - const AString rowName = matrixChart->getRowName(rowIndex); - if ( ! rowName.isEmpty()) { - idText.addLine(true, - ("Row " + AString::number(rowIndex + 1)), - rowName); - } - } - if ((colIndex >= 0) - && (matrixChart->hasColumnSelection())) { - const AString colName = matrixChart->getColumnName(colIndex); - if ( ! colName.isEmpty()) { - idText.addLine(true, - ("Column " + AString::number(colIndex + 1)), - colName); - } - } - } - else { - AString boldText("MATRIX ROW/COLUMN"); - idText.addLine(false, - boldText, - mapFile->getFileNameNoPath()); - - const CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(mapFile); - if (rowIndex >= 0) { - const AString rowName = (matrixFile != NULL) ? (" " + matrixFile->getRowName(rowIndex + 1)) : ""; - idText.addLine(true, - "Row", - (AString::number(rowIndex + 1) + rowName)); - } - if (colIndex >= 0) { - const AString colName = (matrixFile != NULL) ? (" " + matrixFile->getColumnName(colIndex + 1)) : ""; - idText.addLine(true, - "Column", - (AString::number(colIndex + 1) + colName)); - } - } - } -} - -/** - * Generate identification text for a CIFTI Connectivity Matrix Row/Column - * @param idText - * String builder for identification text. - * @param idCiftiConnMatrix - * Information for CIFTI Connectivity Matrix Row/Column. - */ -void -IdentificationTextGenerator::generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const -{ - if (idCiftiConnMatrix->isValid()) { - const CiftiMappableConnectivityMatrixDataFile* connMatrixFile = idCiftiConnMatrix->getCiftiConnectivityMatrixFile(); - const int32_t rowIndex = idCiftiConnMatrix->getMatrixRowIndex(); - const int32_t colIndex = idCiftiConnMatrix->getMatrixColumnIndex(); - - AString boldText("MATRIX ROW/COLUMN"); - idText.addLine(false, - boldText, - connMatrixFile->getFileNameNoPath()); - - AString rowName = " "; - AString colName = " "; - bool validData = true; - if (validData) { - if (rowIndex >= 0) { - idText.addLine(true, - ("Row " + AString::number(rowIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), - rowName); - } - if (colIndex >= 0) { - idText.addLine(true, - ("Column " + AString::number(colIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), - colName); - } - } - } -} - -/** - * Generate identification text for chart data source. - * @param idText - * String builder for identification text. - * @param typeOfChartText - * Text describing the type of chart. - * @param chartDataSource - * Source of chart data. - */ -void -IdentificationTextGenerator::generateChartDataSourceText(IdentificationStringBuilder& idText, - const AString& typeOfChartText, - const ChartDataSource* chartDataSource) const -{ - AString chartFileName = chartDataSource->getChartableFileName(); - if (! chartFileName.isEmpty()) { - chartFileName = FileInformation(chartFileName).getFileName(); - } - - idText.addLine(false, - typeOfChartText, - chartDataSource->getChartableFileName()); - switch (chartDataSource->getDataSourceMode()) { - case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: - break; - case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: - { - AString fileName; - int32_t rowIndex; - chartDataSource->getFileRow(fileName, - rowIndex); - idText.addLine(true, - "File", - fileName); - idText.addLine(true, - "Row", - AString::number(rowIndex + 1)); - } - break; - case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: - { - AString structureName; - int32_t numberOfNodes; - int32_t nodeIndex; - chartDataSource->getSurfaceNode(structureName, - numberOfNodes, - nodeIndex); - idText.addLine(true, - "Structure", - structureName); - idText.addLine(true, - "Vertex Index", - AString::number(nodeIndex)); - } - break; - case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: - { - AString structureName; - int32_t numberOfNodes; - std::vector nodeIndices; - chartDataSource->getSurfaceNodeAverage(structureName, numberOfNodes, nodeIndices); - idText.addLine(true, - "Structure", - structureName); - idText.addLine(true, - "Vertex Average Count", - AString::number(nodeIndices.size())); - } - break; - case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: - { - float voxelXYZ[3]; - chartDataSource->getVolumeVoxel(voxelXYZ); - idText.addLine(true, - "Voxel XYZ", - AString::fromNumbers(voxelXYZ, 3, ",")); - } - break; - } -} - -/** - * Generate text for a map file data selector. - * @param idText - * String builder for identification text. - * @param mapFileDataSelector - * The map file data selector. - */ -void -IdentificationTextGenerator::generateMapFileSelectorText(IdentificationStringBuilder& idText, - const MapFileDataSelector* mapFileDataSelector) const -{ - - switch (mapFileDataSelector->getDataSelectionType()) { - case MapFileDataSelector::DataSelectionType::INVALID: - break; - case MapFileDataSelector::DataSelectionType::COLUMN_DATA: - { - CaretMappableDataFile* mapFile = NULL; - AString mapFileName; - int32_t columnIndex = -1; - mapFileDataSelector->getColumnIndex(mapFile, - mapFileName, - columnIndex); - - idText.addLine(true, - "Column File", - mapFileName); - idText.addLine(true, - "Column Index", - AString::number(columnIndex + 1)); - } - break; - case MapFileDataSelector::DataSelectionType::ROW_DATA: - { - CaretMappableDataFile* mapFile = NULL; - AString mapFileName; - int32_t rowIndex = -1; - mapFileDataSelector->getRowIndex(mapFile, - mapFileName, - rowIndex); - - idText.addLine(true, - "Row File", - mapFileName); - idText.addLine(true, - "Row Index", - AString::number(rowIndex + 1)); - } - break; - case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: - { - StructureEnum::Enum structure = StructureEnum::INVALID; - int32_t numberOfVertices = 0; - int32_t vertexIndex = -1; - mapFileDataSelector->getSurfaceVertex(structure, - numberOfVertices, - vertexIndex); - - if ((structure != StructureEnum::INVALID) - && (vertexIndex >= 0)) { - idText.addLine(true, - "Structure", - StructureEnum::toGuiName(structure)); - idText.addLine(true, - "Vertex Index", - AString::number(vertexIndex)); - } - } - break; - case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: - { - StructureEnum::Enum structure = StructureEnum::INVALID; - int32_t numberOfVertices = 0; - std::vector vertexIndices; - mapFileDataSelector->getSurfaceVertexAverage(structure, - numberOfVertices, - vertexIndices); - - const int32_t averageCount = static_cast(vertexIndices.size()); - if ((structure != StructureEnum::INVALID) - && (averageCount > 0)) { - idText.addLine(true, - "Structure", - StructureEnum::toGuiName(structure)); - idText.addLine(true, - "Vertex Average Count", - AString::number(averageCount)); - } - } - break; - case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: - { - float voxelXYZ[3]; - mapFileDataSelector->getVolumeVoxelXYZ(voxelXYZ); - idText.addLine(true, - "Voxel XYZ", - AString::fromNumbers(voxelXYZ, 3, ",")); - } - break; - } -} - -/** - * Generate identification text for a time series chart. - * @param idText - * String builder for identification text. - * @param idChartTimeSeries - * Information for chart id. - */ -void -IdentificationTextGenerator::generateChartTimeSeriesIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartTimeSeries* idChartTimeSeries) const -{ - if (idChartTimeSeries->isValid()) { - const ChartDataCartesian* chartDataCartesian = idChartTimeSeries->getChartDataCartesian(); - - const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); - generateChartDataSourceText(idText, - "TIME SERIES CHART", - chartDataSource); - } -} - -/** - * Generate identification text for a surface border identification. - * @param idText - * String builder for identification text. - * @param idSurfaceBorder - * Information for surface border ID. - * @param toolTipFlag - * True if this is for tooltip. - */ -void -IdentificationTextGenerator::generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemBorderSurface* idSurfaceBorder, - const bool toolTipFlag) const -{ - if (idSurfaceBorder->isValid()) { - const Border* border = idSurfaceBorder->getBorder(); - const SurfaceProjectedItem* spi = border->getPoint(idSurfaceBorder->getBorderPointIndex()); - float xyz[3]; - spi->getProjectedPosition(*idSurfaceBorder->getSurface(), xyz, false); - - if (toolTipFlag) { - bool indentFlag = false; - idText.addLine(indentFlag, - "Border", - border->getName()); - indentFlag = true; - idText.addLine(indentFlag, - "XYZ", - AString::fromNumbers(xyz, 3, ",")); - } - else { - AString boldText = ("BORDER " - + StructureEnum::toGuiName(spi->getStructure()) - + " Name: " - + border->getName()); - if (border->getClassName().isEmpty() == false) { - boldText += (" ClassName: " - + border->getClassName() - + ": "); - } - - const AString text = ("(" - + AString::number(idSurfaceBorder->getBorderIndex()) - + "," - + AString::number(idSurfaceBorder->getBorderPointIndex()) - + ") (" - + AString::fromNumbers(xyz, 3, ",") - + ")"); - idText.addLine(false, boldText, text); - } - } -} - -/** - * Generate identification text for a surface focus identification. - * @param idText - * String builder for identification text. - * @param idSurfaceFocus - * Information for surface focus ID. - * @param toolTipFlag - * True if this is for tooltip. - */ -void -IdentificationTextGenerator::generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemFocusSurface* idSurfaceFocus, - const bool toolTipFlag) const -{ - if (idSurfaceFocus->isValid()) { - const Focus* focus = idSurfaceFocus->getFocus(); - const int32_t projectionIndex = idSurfaceFocus->getFocusProjectionIndex(); - const SurfaceProjectedItem* spi = focus->getProjection(projectionIndex); - float xyzStereo[3]; - spi->getStereotaxicXYZ(xyzStereo); - if (toolTipFlag) { - bool indentFlag = false; - idText.addLine(indentFlag, - "Focus", - focus->getName()); - indentFlag = true; - idText.addLine(indentFlag, - "XYZ", - (spi->isStereotaxicXYZValid() - ? AString::fromNumbers(xyzStereo, 3, ",") - : "Invalid")); - } - else { - idText.addLine(false, - "FOCUS", - focus->getName()); - - idText.addLine(true, - "Index", - AString::number(idSurfaceFocus->getFocusIndex())); - - float xyzProj[3]; - spi->getProjectedPosition(*idSurfaceFocus->getSurface(), xyzProj, false); - - idText.addLine(true, - "Structure", - StructureEnum::toGuiName(spi->getStructure())); - - if (spi->isStereotaxicXYZValid()) { - idText.addLine(true, - "XYZ (Stereotaxic)", - xyzStereo, - 3, - true); - } - else { - idText.addLine(true, - "XYZ (Stereotaxic)", - "Invalid"); - } - - bool projValid = false; - AString xyzProjName = "XYZ (Projected)"; - if (spi->getBarycentricProjection()->isValid()) { - xyzProjName = "XYZ (Projected to Triangle)"; - projValid = true; - } - else if (spi->getVanEssenProjection()->isValid()) { - xyzProjName = "XYZ (Projected to Edge)"; - projValid = true; - } - if (projValid) { - idText.addLine(true, - xyzProjName, - xyzProj, - 3, - true); - } - else { - idText.addLine(true, - xyzProjName, - "Invalid"); - } - - const int32_t numberOfProjections = focus->getNumberOfProjections(); - for (int32_t i = 0; i < numberOfProjections; i++) { - if (i != projectionIndex) { - const SurfaceProjectedItem* proj = focus->getProjection(i); - AString projTypeName = ""; - if (proj->getBarycentricProjection()->isValid()) { - projTypeName = "Triangle"; - - } - else if (proj->getVanEssenProjection()->isValid()) { - projTypeName = "Edge"; - } - if (projTypeName.isEmpty() == false) { - const AString txt = (StructureEnum::toGuiName(proj->getStructure()) - + " (" - + projTypeName - + ")"); - - idText.addLine(true, - "Ambiguous Projection", - txt); - } - } - } - - idText.addLine(true, - "Area", - focus->getArea()); - - idText.addLine(true, - "Class Name", - focus->getClassName()); - - idText.addLine(true, - "Comment", - focus->getComment()); - - idText.addLine(true, - "Extent", - focus->getExtent(), - true); - - idText.addLine(true, - "Geography", - focus->getGeography()); - - idText.addLine(true, - "Region of Interest", - focus->getRegionOfInterest()); - - idText.addLine(true, - "Statistic", - focus->getStatistic()); - - } - } -} - -/** - * Generate identification text for a volume focus identification. - * @param idText - * String builder for identification text. - * @param idVolumeFocus - * Information for surface focus ID. - */ -void -IdentificationTextGenerator::generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemFocusVolume* idVolumeFocus) const -{ - if (idVolumeFocus->isValid()) { - const Focus* focus = idVolumeFocus->getFocus(); - const SurfaceProjectedItem* spi = focus->getProjection(idVolumeFocus->getFocusProjectionIndex()); - float xyzVolume[3]; - spi->getVolumeXYZ(xyzVolume); - float xyzStereo[3]; - spi->getStereotaxicXYZ(xyzStereo); - - idText.addLine(false, - "FOCUS", - focus->getName()); - - idText.addLine(true, - "Index", - AString::number(idVolumeFocus->getFocusIndex())); - - idText.addLine(true, - "Structure", - StructureEnum::toGuiName(spi->getStructure())); - - if (spi->isStereotaxicXYZValid()) { - idText.addLine(true, - "XYZ (Stereotaxic)", - xyzStereo, - 3, - true); - } - else { - idText.addLine(true, - "XYZ (Stereotaxic)", - "Invalid"); - } - - AString xyzVolumeName = "XYZ (Volume)"; - idText.addLine(true, - xyzVolumeName, - xyzVolume, - 3, - true); - - idText.addLine(true, - "Area", - focus->getArea()); - - idText.addLine(true, - "Class Name", - focus->getClassName()); - - idText.addLine(true, - "Comment", - focus->getComment()); - - idText.addLine(true, - "Extent", - focus->getExtent(), - true); - - idText.addLine(true, - "Geography", - focus->getGeography()); - - idText.addLine(true, - "Region of Interest", - focus->getRegionOfInterest()); - - idText.addLine(true, - "Statistic", - focus->getStatistic()); - - } -} - -/** - * Generate identification text for image identification. - * @param idText - * String builder for identification text. - * @param idImage - * Information for image ID. - */ -void -IdentificationTextGenerator::generateImageIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemImage* idImage) const -{ - if (idImage->isValid()) { - AString text = ("Image " - + idImage->getImageFile()->getFileNameNoPath() - + " Pixel IJ (" - + AString::number(idImage->getPixelI()) - + "," - + AString::number(idImage->getPixelJ()) - + ")"); - - uint8_t pixelRGBA[4] = { 0, 0, 0, 0 }; - idImage->getPixelRGBA(pixelRGBA); - text.append(" RGBA (" + AString::fromNumbers(pixelRGBA, 4, ",") + ")"); - - idText.addLine(false, - text); - } -} - -/** - * Get text for the tooltip for a selected node. - * - * @param brain - * The Brain. - * @param browserTab - * Browser tab in which tooltip is displayed - * @param selectionManager - * The selection manager. - * @param dataToolTipsManager - * The data tooltips manager - * @param idText - * String builder for identification text. - */ -void -IdentificationTextGenerator::generateSurfaceToolTip(const Brain* brain, - const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const DataToolTipsManager* dataToolTipsManager, - IdentificationStringBuilder& idText) const -{ - const SelectionItemSurfaceNode* nodeSelection = selectionManager->getSurfaceNodeIdentification(); - CaretAssert(nodeSelection); - if (nodeSelection->isValid()) { - const Surface* surface = nodeSelection->getSurface(); - CaretAssert(surface); - int32_t surfaceNumberOfNodes = surface->getNumberOfNodes(); - int32_t surfaceNodeIndex = nodeSelection->getNodeNumber(); - StructureEnum::Enum surfaceStructure = surface->getStructure(); - - bool indentFlag = false; - if ((surfaceStructure != StructureEnum::INVALID) - && (surfaceNumberOfNodes > 0) - && (surfaceNodeIndex >= 0)) { - - bool addVertexFlag(false); - bool showSurfaceFlag = dataToolTipsManager->isShowSurfaceViewed(); - if (dataToolTipsManager->isShowSurfacePrimaryAnatomical()) { - const Surface* anatSurface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); - if (anatSurface != NULL) { - if (anatSurface->getNumberOfNodes() == surfaceNumberOfNodes) { - float xyz[3]; - anatSurface->getCoordinate(surfaceNodeIndex, - xyz); - idText.addLine(indentFlag, - "Vertex", - AString::number(surfaceNodeIndex)); - indentFlag = true; - addVertexFlag = false; - - idText.addLine(indentFlag, - "Anatomy Surface", - AString::fromNumbers(xyz, 3, ", ", 'f', 2)); - if (surface == anatSurface) { - showSurfaceFlag = false; - } - } - } - } - - if (showSurfaceFlag) { - float xyz[3]; - surface->getCoordinate(surfaceNodeIndex, - xyz); - if (addVertexFlag) { - idText.addLine(indentFlag, - "Vertex", - AString::number(surfaceNodeIndex)); - indentFlag = true; - } - - idText.addLine(indentFlag, - (SurfaceTypeEnum::toGuiName(surface->getSurfaceType()) - + " Surface"), - AString::fromNumbers(xyz, 3, ", ")); - } - - if (dataToolTipsManager->isShowTopEnabledLayer()) { - OverlaySet* overlaySet = const_cast(browserTab->getOverlaySet()); - CaretAssert(overlaySet); - Overlay* overlay = getTopEnabledOverlay(overlaySet); - if (overlay != NULL) { - CaretMappableDataFile* mapFile(NULL); - int32_t mapIndex(-1); - overlay->getSelectionData(mapFile, - mapIndex); - if ((mapFile != NULL) - && (mapIndex >= 0)) { - std::vector mapIndices { mapIndex }; - AString textValue; - mapFile->getSurfaceNodeIdentificationForMaps(mapIndices, - surfaceStructure, - surfaceNodeIndex, - surfaceNumberOfNodes, - textValue); - if ( ! textValue.isEmpty()) { - idText.addLine(indentFlag, - "Top Enabled Layer", - textValue); - } - } - } - } - } - } - - if (dataToolTipsManager->isShowBorder()) { - const SelectionItemBorderSurface* borderSelection = selectionManager->getSurfaceBorderIdentification(); - CaretAssert(borderSelection); - if (borderSelection->isValid()) { - generateSurfaceBorderIdentifcationText(idText, - borderSelection, - true); - -// const BorderFile* borderFile = borderSelection->getBorderFile(); -// const int32_t borderIndex = borderSelection->getBorderIndex(); -// if ((borderFile != NULL) -// && (borderIndex >= 0)) { -// const Border* border = borderFile->getBorder(borderIndex); -// if (border != NULL) { -// text.appendWithNewLine("Border: " -// + border->getName()); -// } -// } - } - } - - if (dataToolTipsManager->isShowFocus()) { - const SelectionItemFocusSurface* focusSelection = selectionManager->getSurfaceFocusIdentification(); - CaretAssert(focusSelection); - if (focusSelection->isValid()) { - generateSurfaceFociIdentifcationText(idText, - focusSelection, - true); - - -// const FociFile* fociFile = focusSelection->getFociFile(); -// const int32_t focusIndex = focusSelection->getFocusIndex(); -// if ((fociFile != NULL) -// && (focusIndex >= 0)) { -// const Focus* focus = fociFile->getFocus(focusIndex); -// if (focus != NULL) { -// text.appendWithNewLine("Focus: " -// + focus->getName()); -// } -// } - } - } -} - -/** - * Get text for the tooltip for a selected node. - * - * @param browserTab - * Browser tab in which tooltip is displayed - * @param selectionManager - * The selection manager. - * @param dataToolTipsManager - * The data tooltips manager - * @param idText - * String builder for identification text. - */ -void -IdentificationTextGenerator::generateVolumeToolTip(const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const DataToolTipsManager* dataToolTipsManager, - IdentificationStringBuilder& idText) const -{ - const SelectionItemVoxel* voxelSelection = selectionManager->getVoxelIdentification(); - - OverlaySet* overlaySet = const_cast(browserTab->getOverlaySet()); - CaretAssert(overlaySet); - - double selectionXYZ[3]; - voxelSelection->getModelXYZ(selectionXYZ); - float xyz[3] { - static_cast(selectionXYZ[0]), - static_cast(selectionXYZ[1]), - static_cast(selectionXYZ[2]) - }; - - bool indentFlag = false; - if (dataToolTipsManager->isShowVolumeUnderlay()) { - Overlay* volumeUnderlay = overlaySet->getUnderlayContainingVolume(); - if (volumeUnderlay != NULL) { - CaretMappableDataFile* mapFile = NULL; - int32_t mapIndex(-1); - volumeUnderlay->getSelectionData(mapFile, - mapIndex); - - VolumeMappableInterface* underlayVolumeInterface = NULL; - if (mapFile != NULL) { - underlayVolumeInterface = dynamic_cast(mapFile); - CaretAssert(underlayVolumeInterface == overlaySet->getUnderlayVolume()); - } - - if (underlayVolumeInterface != NULL) { - /* - * Update IJK and XYZ since selection XYZ may be - * a different volume file. - */ - int64_t selectionIJK[3]; - voxelSelection->getVoxelIJK(selectionIJK); - int64_t ijk[3] { selectionIJK[0], selectionIJK[1], selectionIJK[2] }; - - - bool validFlag(false); - const float value = underlayVolumeInterface->getVoxelValue(xyz[0], xyz[1], xyz[2], - &validFlag, - mapIndex); - if (validFlag) { - underlayVolumeInterface->enclosingVoxel(xyz[0], xyz[1], xyz[2], - ijk[0], ijk[1], ijk[2]); - underlayVolumeInterface->indexToSpace(ijk, xyz); - idText.addLine(indentFlag, - "Underlay Value", - AString::number(value, 'f')); - indentFlag = true; - idText.addLine(indentFlag, - "IJK: ", - AString::fromNumbers(ijk, 3, ", ")); - idText.addLine(indentFlag, - "XYZ", - AString::fromNumbers(xyz, 3, ", ", 'f', 1)); - } - } - } - } - - if (dataToolTipsManager->isShowTopEnabledLayer()) { - Overlay* overlay = getTopEnabledOverlay(overlaySet); - if (overlay != NULL) { - CaretMappableDataFile* mapFile(NULL); - int32_t mapIndex(-1); - overlay->getSelectionData(mapFile, - mapIndex); - if ((mapFile != NULL) - && (mapIndex >= 0)) { - std::vector mapIndices { mapIndex }; - AString textValue; - int64_t ijk[3]; - mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, - xyz, - ijk, - textValue); - if ( ! textValue.isEmpty()) { - idText.addLine(indentFlag, - ("Top Enabled Layer: " - + textValue)); - } - } - } - } -} - -/** - * @return Get the top-most enabled overlay. NULL if no overlays enabled - * - * @param overlaySet - * Overlay set for overlay. - */ -Overlay* -IdentificationTextGenerator::getTopEnabledOverlay(OverlaySet* overlaySet) const -{ - CaretAssert(overlaySet); - const int32_t numberOfOverlays = overlaySet->getNumberOfDisplayedOverlays(); - for (int32_t i = 0; i < numberOfOverlays; i++) { - Overlay* overlay = overlaySet->getOverlay(i); - CaretAssert(overlay); - if (overlay->isEnabled()) { - return overlay; - } - } - return NULL; -} - -/** - * Get text for the tooltip for a selected node. - * - * @param selectionManager - * The selection manager. - * @param dataToolTipsManager - * The data tooltips manager - * @param idText - * String builder for identification text. - */ -void -IdentificationTextGenerator::generateChartToolTip(const SelectionManager* selectionManager, - const DataToolTipsManager* dataToolTipsManager, - IdentificationStringBuilder& idText) const -{ - if (dataToolTipsManager->isShowChart()) { - this->generateChartTwoHistogramIdentificationText(idText, - selectionManager->getChartTwoHistogramIdentification()); - - this->generateChartTwoLineSeriesIdentificationText(idText, - selectionManager->getChartTwoLineSeriesIdentification()); - - this->generateChartTwoMatrixIdentificationText(idText, - selectionManager->getChartTwoMatrixIdentification()); - } -} - - - -/** - * Get a description of this object's content. - * @return String describing this object's content. - */ -AString -IdentificationTextGenerator::toString() const -{ - return "IdentificationTextGenerator"; -} diff -Nru connectome-workbench-1.4.2/src/Brain/IdentificationTextGenerator.h connectome-workbench-1.5.0/src/Brain/IdentificationTextGenerator.h --- connectome-workbench-1.4.2/src/Brain/IdentificationTextGenerator.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentificationTextGenerator.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -#ifndef __IDENTIFICATION_TEXT_GENERATOR__H_ -#define __IDENTIFICATION_TEXT_GENERATOR__H_ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "CaretObject.h" - -namespace caret { - - class Brain; - class BrowserTabContent; - class CaretMappableDataFile; - class ChartDataSource; - class DataToolTipsManager; - class MapFileDataSelector; - class Overlay; - class OverlaySet; - class SelectionItemBorderSurface; - class SelectionItemChartDataSeries; - class SelectionItemChartFrequencySeries; - class SelectionItemChartMatrix; - class SelectionItemChartTwoHistogram; - class SelectionItemChartTwoLineSeries; - class SelectionItemChartTwoMatrix; - class SelectionItemCiftiConnectivityMatrixRowColumn; - class SelectionItemChartTimeSeries; - class SelectionItemFocusSurface; - class SelectionItemFocusVolume; - class SelectionItemImage; - class SelectionItemSurfaceNode; - class SelectionItemVoxel; - class SelectionManager; - class IdentificationStringBuilder; - - class IdentificationTextGenerator : public CaretObject { - - public: - IdentificationTextGenerator(); - - virtual ~IdentificationTextGenerator(); - - AString createIdentificationText(const SelectionManager* idManager, - const Brain* brain) const; - - AString createToolTipText(const Brain* brain, - const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const DataToolTipsManager* dataToolTipsManager) const; - - private: - IdentificationTextGenerator(const IdentificationTextGenerator&); - - IdentificationTextGenerator& operator=(const IdentificationTextGenerator&); - - public: - virtual AString toString() const; - - private: - void generateSurfaceToolTip(const Brain* brain, - const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const DataToolTipsManager* dataToolTipsManager, - IdentificationStringBuilder& idText) const; - - void generateVolumeToolTip(const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const DataToolTipsManager* dataToolTipsManager, - IdentificationStringBuilder& idText) const; - - void generateChartToolTip(const SelectionManager* selectionManager, - const DataToolTipsManager* dataToolTipsManager, - IdentificationStringBuilder& idText) const; - - void generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemBorderSurface* idSurfaceBorder, - const bool toolTipFlag) const; - - void generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemFocusSurface* idSurfaceFocus, - const bool toolTipFlag) const; - - void generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemFocusVolume* idVolumeFocus) const; - - void generateSurfaceIdentificationText(IdentificationStringBuilder& idText, - const Brain* brain, - const SelectionItemSurfaceNode* idSurfaceNode) const; - - void generateImageIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemImage* idImage) const; - - void generateVolumeIdentificationText(IdentificationStringBuilder& idText, - const Brain* brain, - const SelectionItemVoxel* idVolumeVoxel) const; - - void generateChartDataSeriesIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartDataSeries* idChartDataSeries) const; - - void generateChartFrequencySeriesIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartFrequencySeries* idChartFrequencySeries) const; - - void generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartMatrix* idChartMatrix) const; - - void generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartTwoHistogram* idChartTwoHistogram) const; - - void generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartTwoLineSeries* idChartTwoLineSeries) const; - - void generateChartTwoMatrixIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartTwoMatrix* idChartTwoMatrix) const; - - void generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const; - - void generateChartTimeSeriesIdentificationText(IdentificationStringBuilder& idText, - const SelectionItemChartTimeSeries* idChartTimeSeries) const; - - void getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, - std::vector& mapIndicesOut) const; - - void generateChartDataSourceText(IdentificationStringBuilder& idText, - const AString& typeOfChartText, - const ChartDataSource* chartDataSource) const; - - void generateMapFileSelectorText(IdentificationStringBuilder& idText, - const MapFileDataSelector* mapFileDataSelector) const; - - Overlay* getTopEnabledOverlay(OverlaySet* overlaySet) const; - - friend class DataToolTipsManager; - }; - -#ifdef __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ - // -#endif // __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ - -} // namespace -#endif //__IDENTIFICATION_TEXT_GENERATOR__H_ diff -Nru connectome-workbench-1.4.2/src/Brain/IdentifiedItem.cxx connectome-workbench-1.5.0/src/Brain/IdentifiedItem.cxx --- connectome-workbench-1.4.2/src/Brain/IdentifiedItem.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentifiedItem.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -48,14 +48,18 @@ /** * Constructor. * - * @param text + * @param simpleText * Text describing the identified item. + * @param formattedText + * Formatted text describing the identified item. */ -IdentifiedItem::IdentifiedItem(const AString& text) +IdentifiedItem::IdentifiedItem(const AString& simpleText, + const AString& formattedText) : CaretObject() { initializeMembers(); - m_text = text; + m_text = simpleText; + m_formattedText = formattedText; } /** @@ -106,6 +110,7 @@ m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_text", &m_text); + m_sceneAssistant->add("m_formattedText", &m_formattedText); } @@ -135,11 +140,20 @@ /** * Append text to this item's text. + * @param simpleText + * Text describing the identified item. + * @param formattedText + * Formatted text describing the identified item. */ void -IdentifiedItem::appendText(const AString& text) +IdentifiedItem::appendText(const AString& simpleText, + const AString& formattedText) { - m_text += text; + m_text += simpleText; + if (m_formattedText.isEmpty()) { + m_formattedText.append("\n"); + } + m_formattedText += formattedText; } /** @@ -152,22 +166,32 @@ } /** - * @return The text describing the identified item. + * @return The simple text describing the identified item. */ AString -IdentifiedItem::getText() const +IdentifiedItem::getSimpleText() const { return m_text; } /** + * @return The formatted text describing the identified item. + */ +AString +IdentifiedItem::getFormattedText() const +{ + return m_formattedText; +} + +/** * Get a description of this object's content. * @return String describing this object's content. */ AString IdentifiedItem::toString() const { - return ("m_text=" + m_text); + return ("m_text=" + m_text + + "m_formattedText=" + m_formattedText); } /** diff -Nru connectome-workbench-1.4.2/src/Brain/IdentifiedItem.h connectome-workbench-1.5.0/src/Brain/IdentifiedItem.h --- connectome-workbench-1.4.2/src/Brain/IdentifiedItem.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentifiedItem.h 2021-02-16 19:46:47.000000000 +0000 @@ -34,7 +34,8 @@ public: IdentifiedItem(); - IdentifiedItem(const AString& text); + IdentifiedItem(const AString& simpleText, + const AString& formattedText); virtual ~IdentifiedItem(); @@ -47,12 +48,15 @@ virtual bool isValid() const; - void appendText(const AString& text); + void appendText(const AString& simpleText, + const AString& formattedText); void clearText(); - AString getText() const; + AString getSimpleText() const; + AString getFormattedText() const; + virtual AString toString() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, @@ -77,6 +81,8 @@ AString m_text; + AString m_formattedText; + SceneClassAssistant* m_sceneAssistant; friend class IdentificationManager; diff -Nru connectome-workbench-1.4.2/src/Brain/IdentifiedItemNode.cxx connectome-workbench-1.5.0/src/Brain/IdentifiedItemNode.cxx --- connectome-workbench-1.4.2/src/Brain/IdentifiedItemNode.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentifiedItemNode.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -61,8 +61,10 @@ /** * Constructor. * - * @param text + * @param simpleText * Text describing the identified item. + * @param formattedText + * Formatted text describing the identified item. * @param structure * Structure on which identification took place. * @param contralateralStructure @@ -73,11 +75,13 @@ * Index of node that was identified. * */ -IdentifiedItemNode::IdentifiedItemNode(const AString& text, +IdentifiedItemNode::IdentifiedItemNode(const AString& simpleText, + const AString& formattedText, const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex) -: IdentifiedItem(text) +: IdentifiedItem(simpleText, + formattedText) { initializeMembers(); @@ -135,6 +139,7 @@ m_contralateralStructure = obj.m_contralateralStructure; m_surfaceNumberOfNodes = obj.m_surfaceNumberOfNodes; m_nodeIndex = obj.m_nodeIndex; + m_identificationSymbolSizeType = obj.m_identificationSymbolSizeType; m_symbolRGB[0] = obj.m_symbolRGB[0]; m_symbolRGB[1] = obj.m_symbolRGB[1]; @@ -157,6 +162,8 @@ m_contralateralStructure = StructureEnum::INVALID; m_surfaceNumberOfNodes = -1, m_nodeIndex = -1; + m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_structure", &m_structure); @@ -166,6 +173,8 @@ m_sceneAssistant->addArray("m_symbolRGB", m_symbolRGB, 3, 0); m_sceneAssistant->addArray("m_contralateralSymbolRGB", m_contralateralSymbolRGB, 3, 0); m_sceneAssistant->add("m_symbolSize", &m_symbolSize); + m_sceneAssistant->add("m_identificationSymbolSizeType", + &m_identificationSymbolSizeType); } /** @@ -333,6 +342,26 @@ m_symbolSize = symbolSize; } +/** + * @param The identification symbol size type + */ +IdentificationSymbolSizeTypeEnum::Enum +IdentifiedItemNode::getIdentificationSymbolSizeType() const +{ + return m_identificationSymbolSizeType; +} + +/** + * Set the identification size type + * @param sizeType + * The new size type + */ +void +IdentifiedItemNode::setIdentificationSymbolSizeType(const IdentificationSymbolSizeTypeEnum::Enum sizeType) +{ + m_identificationSymbolSizeType = sizeType; +} + /** * Get a description of this object's content. @@ -408,6 +437,8 @@ return; } + m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff -Nru connectome-workbench-1.4.2/src/Brain/IdentifiedItemNode.h connectome-workbench-1.5.0/src/Brain/IdentifiedItemNode.h --- connectome-workbench-1.4.2/src/Brain/IdentifiedItemNode.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentifiedItemNode.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,6 +22,7 @@ /*LICENSE_END*/ +#include "IdentificationSymbolSizeTypeEnum.h" #include "IdentifiedItem.h" #include "StructureEnum.h" @@ -32,7 +33,8 @@ public: IdentifiedItemNode(); - IdentifiedItemNode(const AString& text, + IdentifiedItemNode(const AString& simpleText, + const AString& formattedText, const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex); @@ -75,6 +77,10 @@ void setSymbolSize(const float symbolSize); + IdentificationSymbolSizeTypeEnum::Enum getIdentificationSymbolSizeType() const; + + void setIdentificationSymbolSizeType(const IdentificationSymbolSizeTypeEnum::Enum sizeType); + virtual AString toString() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, @@ -104,6 +110,8 @@ float m_symbolSize; + IdentificationSymbolSizeTypeEnum::Enum m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + SceneClassAssistant* m_sceneAssistant; }; diff -Nru connectome-workbench-1.4.2/src/Brain/IdentifiedItemVoxel.cxx connectome-workbench-1.5.0/src/Brain/IdentifiedItemVoxel.cxx --- connectome-workbench-1.4.2/src/Brain/IdentifiedItemVoxel.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentifiedItemVoxel.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -49,10 +49,18 @@ /** * Constructor. + * @param simpleText + * Text describing the identified item. + * @param formattedText + * Formatted text describing the identified item. + * @param xyz + * XYZ of voxel */ -IdentifiedItemVoxel::IdentifiedItemVoxel(const AString& text, +IdentifiedItemVoxel::IdentifiedItemVoxel(const AString& simpleText, + const AString& formattedText, const float xyz[3]) -: IdentifiedItem(text) +: IdentifiedItem(simpleText, + formattedText) { initializeMembers(); @@ -111,11 +119,14 @@ m_symbolRGB[1] = 0; m_symbolRGB[1] = 0; m_symbolSize = 0.0; - + m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->addArray("m_xyz", m_xyz, 3, 0.0); m_sceneAssistant->addArray("m_symbolRGB", m_symbolRGB, 3, 0); m_sceneAssistant->add("m_symbolSize", &m_symbolSize); + m_sceneAssistant->add("m_identificationSymbolSizeType", + &m_identificationSymbolSizeType); } /** @@ -135,6 +146,7 @@ m_symbolRGB[2] = obj.m_symbolRGB[2]; m_symbolSize = obj.m_symbolSize; + m_identificationSymbolSizeType = obj.m_identificationSymbolSizeType; } /** @@ -227,6 +239,26 @@ } /** + * @param The identification symbol size type + */ +IdentificationSymbolSizeTypeEnum::Enum +IdentifiedItemVoxel::getIdentificationSymbolSizeType() const +{ + return m_identificationSymbolSizeType; +} + +/** + * Set the identification size type + * @param sizeType + * The new size type + */ +void +IdentifiedItemVoxel::setIdentificationSymbolSizeType(const IdentificationSymbolSizeTypeEnum::Enum sizeType) +{ + m_identificationSymbolSizeType = sizeType; +} + +/** * Get a description of this object's content. * @return String describing this object's content. */ @@ -291,6 +323,8 @@ return; } + m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff -Nru connectome-workbench-1.4.2/src/Brain/IdentifiedItemVoxel.h connectome-workbench-1.5.0/src/Brain/IdentifiedItemVoxel.h --- connectome-workbench-1.4.2/src/Brain/IdentifiedItemVoxel.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/IdentifiedItemVoxel.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,6 +22,7 @@ /*LICENSE_END*/ +#include "IdentificationSymbolSizeTypeEnum.h" #include "IdentifiedItem.h" @@ -33,7 +34,8 @@ public: IdentifiedItemVoxel(); - IdentifiedItemVoxel(const AString& text, + IdentifiedItemVoxel(const AString& simpleText, + const AString& formattedText, const float xyz[3]); virtual ~IdentifiedItemVoxel(); @@ -56,6 +58,10 @@ void setSymbolSize(const float symbolSize); + IdentificationSymbolSizeTypeEnum::Enum getIdentificationSymbolSizeType() const; + + void setIdentificationSymbolSizeType(const IdentificationSymbolSizeTypeEnum::Enum sizeType); + virtual AString toString() const; @@ -93,6 +99,8 @@ float m_symbolSize; + IdentificationSymbolSizeTypeEnum::Enum m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/Brain/MediaOverlay.cxx connectome-workbench-1.5.0/src/Brain/MediaOverlay.cxx --- connectome-workbench-1.4.2/src/Brain/MediaOverlay.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/MediaOverlay.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,507 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __MEDIA_OVERLAY_DECLARE__ +#include "MediaOverlay.h" +#undef __MEDIA_OVERLAY_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "MediaFile.h" +#include "EventManager.h" +#include "EventMediaFilesGet.h" +#include "EventOverlayValidate.h" +#include "ImageFile.h" +#include "PlainTextStringBuilder.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + +/** + * \class Overlay + * \brief An overlay for selection of media data. + */ + +/** + * Constructor + */ +MediaOverlay::MediaOverlay() +{ + m_opacity = 1.0; + m_selectedFile = NULL; + m_selectedFrameIndex = -1; + m_name = "Overlay "; + m_enabled = false; + m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + + m_sceneAssistant = new SceneClassAssistant(); + m_sceneAssistant->add("m_opacity", &m_opacity); + m_sceneAssistant->add("m_enabled", &m_enabled); + + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_OVERLAY_VALIDATE); +} + +/** + * Destructor. + */ +MediaOverlay::~MediaOverlay() +{ + EventManager::get()->removeAllEventsFromListener(this); + + delete m_sceneAssistant; +} + +/** + * Receives events that this object is listening for. + * + * @param event + * An event. + */ +void +MediaOverlay::receiveEvent(Event* /*event*/) +{ +} + +/** + * Set the number of this overlay. + * + * @param overlayIndex + * Index for this overlay. + */ +void +MediaOverlay::setOverlayNumber(const int32_t overlayIndex) +{ + m_name = "Overlay " + AString::number(overlayIndex + 1); +} + +/** + * Get the opacity. + * + * @return The opacity. + */ +float +MediaOverlay::getOpacity() const +{ + return m_opacity; +} + +/** + * Set the opacity. + * + * @param opacity + * New value for opacity. + */ +void +MediaOverlay::setOpacity(const float opacity) +{ + m_opacity = opacity; +} + +AString +MediaOverlay::getName() const +{ + return m_name; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +MediaOverlay::toString() const +{ + PlainTextStringBuilder tb; + getDescriptionOfContent(tb); + return tb.getText(); +} + +/** + * Get a text description of the window's content. + * + * @param descriptionOut + * Description of the window's content. + */ +void +MediaOverlay::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const +{ + MediaOverlay* me = const_cast(this); + if (me != NULL) { + if (me->isEnabled()) { + MediaFile* file = NULL; + int32_t index = 0; + me->getSelectionData(file, + index); + if (file != NULL) { + descriptionOut.addLine("File: "+ + file->getFileNameNoPath()); + descriptionOut.addLine("Frame Index: " + + AString::number(index + 1)); + } + } + } +} + +/** + * @return Enabled status for this surface overlay. + */ +bool +MediaOverlay::isEnabled() const +{ + return m_enabled; +} + +/** + * Set the enabled status for this surface overlay. + * @param enabled + * New status. + */ +void +MediaOverlay::setEnabled(const bool enabled) +{ + m_enabled = enabled; +} + +/** + * Copy the data from the given overlay to this overlay. + * @param overlay + * Overlay from which data is transferred. + */ +void +MediaOverlay::copyData(const MediaOverlay* overlay) +{ + CaretAssert(overlay); + + /* + * These members are not copied since they + * identify the overlay: + * name + * overlayIndex + * + */ + m_opacity = overlay->m_opacity; + m_enabled = overlay->m_enabled; + + m_selectedFile = overlay->m_selectedFile; + m_selectedFrameIndex = overlay->m_selectedFrameIndex; + m_mapYokingGroup = overlay->m_mapYokingGroup; +} + +/** + * Swap the data from the given overlay to this overlay. + * @param overlay + * Overlay from which data is transferred. + */ +void +MediaOverlay::swapData(MediaOverlay* overlay) +{ + MediaOverlay* swapOverlay = new MediaOverlay(); + + swapOverlay->copyData(overlay); + + overlay->copyData(this); + copyData(swapOverlay); + + delete swapOverlay; +} + +/** + * Return the selection information. This method is typically + * called to update the user-interface. + * + * @param selectedFileOut + * The selected file. May be NULL. + * @param selectedFrameIndexOut + * Index in the selected file. + */ +void +MediaOverlay::getSelectionData(MediaFile* &selectedFileOut, + int32_t& selectedFrameIndexOut) +{ + std::vector files; + + getSelectionData(files, + selectedFileOut, + selectedFrameIndexOut); +} + +/** + * Return the selection information. This method is typically + * called to update the user-interface. + * + * @param filesOut + * Contains all files that can be selected. + * @param selectedFileOut + * The selected file. May be NULL. + * @param selectedFrameIndexOut + * Index in the selected file. + */ +void +MediaOverlay::getSelectionData(std::vector& filesOut, + MediaFile* &selectedFileOut, + int32_t& selectedFrameIndexOut) +{ + filesOut.clear(); + selectedFileOut = NULL; + selectedFrameIndexOut = -1; + +// /** +// * Get the data files. +// */ + EventMediaFilesGet mediaFilesEvent; + EventManager::get()->sendEvent(mediaFilesEvent.getPointer()); + filesOut = mediaFilesEvent.getMediaFiles(); + + + /* + * Does selected data file still no longer exist? + */ + if (std::find(filesOut.begin(), + filesOut.end(), + m_selectedFile) == filesOut.end()) { + /* + * Invalidate seleted file and disable yoking since + * the selected file will change. + */ + m_selectedFile = NULL; + m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + } + + /* + * If selected data file is valid, see if selected + * indewx is still valid. If not, use first map. + */ + if (m_selectedFile != NULL) { + if (m_selectedFrameIndex >= m_selectedFile->getNumberOfFrames()) { + m_selectedFrameIndex = m_selectedFile->getNumberOfFrames() - 1; + } + if (m_selectedFrameIndex < 0) { + m_selectedFrameIndex = 0; + } + } + else { + /* + * Use in first file + */ + if (m_selectedFile == NULL) { + if ( ! filesOut.empty()) { + for (auto& file : filesOut) { + m_selectedFile = file; + m_selectedFrameIndex = 0; + break; + } + } + } + } + + selectedFileOut = m_selectedFile; + if (selectedFileOut != NULL) { + selectedFrameIndexOut = m_selectedFrameIndex; + } +} + +/** + * Set the selected file and index name. + * @param selectedFile + * File that is selected. + * @param selectedName + * Index name that is selected. + */ +void +MediaOverlay::setSelectionData(MediaFile* selectedFile, + const int32_t selectedFrameIndex) +{ + m_selectedFile = selectedFile; + m_selectedFrameIndex = selectedFrameIndex; + + if (m_mapYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + if (m_selectedFile == NULL) { + m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; + } + } +} + +/** + * @return Selected map yoking group. + */ +MapYokingGroupEnum::Enum +MediaOverlay::getMapYokingGroup() const +{ + return m_mapYokingGroup; +} + +/** + * Set the map yoking group. + * + * @param mapYokingGroup + * New value for map yoking group. + */ +void +MediaOverlay::setMapYokingGroup(const MapYokingGroupEnum::Enum mapYokingGroup) +{ + m_mapYokingGroup = mapYokingGroup; +} + +/** + * Create a scene for an instance of a class. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of the class' instance. + * + * @return Pointer to SceneClass object representing the state of + * this object. Under some circumstances a NULL pointer may be + * returned. Caller will take ownership of returned object. + */ +SceneClass* +MediaOverlay::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "MediaOverlay", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + std::vector files; + MediaFile* selectedFile = NULL; + int32_t selectedFrameIndex; + getSelectionData(files, + selectedFile, + selectedFrameIndex); + + if ((selectedFile != NULL) + && (selectedFrameIndex >= 0)) { + sceneClass->addPathName("selectedFileNameWithPath", + selectedFile->getFileName()); + sceneClass->addString("selectedFile", + selectedFile->getFileNameNoPath()); + sceneClass->addString("selectedName", + AString::number(selectedFrameIndex)); + sceneClass->addInteger("selectedFrameIndex", + selectedFrameIndex); + } + else { + sceneClass->addPathName("selectedFileNameWithPath", + ""); + sceneClass->addString("selectedFile", + ""); + sceneClass->addString("selectedName", + ""); + sceneClass->addInteger("selectedFrameIndex", + -1); + } + + return sceneClass; +} + +/** + * Restore the state of an instance of a class. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. May be NULL for some types of scenes. + */ +void +MediaOverlay::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + /* + * Making a call to getSelectionData() to get the availble + * files + */ + std::vector allFiles; + MediaFile* unusedSelectedFile = NULL; + int32_t unusedselectedFrameIndex; + getSelectionData(allFiles, + unusedSelectedFile, + unusedselectedFrameIndex); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + + const AString selectedFileNameWithPath = sceneClass->getPathNameValue("selectedFileNameWithPath"); + + const AString selectedFileName = sceneClass->getStringValue("selectedFile", + ""); + const AString selectedName = sceneClass->getStringValue("selectedName", + ""); + const int32_t selectedFrameIndex = sceneClass->getIntegerValue("selectedFrameIndex", + -1); + + + MediaFile* matchedFile = NULL; + + /* + * Try to match with full path + */ + for (auto& dataFile : allFiles) { + if (selectedFileNameWithPath == dataFile->getFileName()) { + matchedFile = dataFile; + break; + } + } + + /* + * Match by name only + */ + if (matchedFile == NULL) { + for (auto& dataFile : allFiles) { + if (selectedFileName == dataFile->getFileName()) { + matchedFile = dataFile; + break; + } + } + } + + if (matchedFile == NULL) { + CaretLogWarning("Unable to restore image overlay file: " + + selectedFileName); + if ( ! allFiles.empty()) { + CaretAssertVectorIndex(allFiles, 0); + matchedFile = allFiles[0]; + } + } + + setSelectionData(matchedFile, + selectedFrameIndex); +} + + + diff -Nru connectome-workbench-1.4.2/src/Brain/MediaOverlay.h connectome-workbench-1.5.0/src/Brain/MediaOverlay.h --- connectome-workbench-1.4.2/src/Brain/MediaOverlay.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/MediaOverlay.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,119 @@ +#ifndef __MEDIA_OVERLAY__H_ +#define __MEDIA_OVERLAY__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "CaretObject.h" +#include "DataFileTypeEnum.h" +#include "EventListenerInterface.h" +#include "MapYokingGroupEnum.h" +#include "PlainTextStringBuilder.h" +#include "SceneableInterface.h" + +namespace caret { + class MediaFile; + class SceneClassAssistant; + + class MediaOverlay : public CaretObject, public EventListenerInterface, public SceneableInterface { + + public: + MediaOverlay(); + + virtual ~MediaOverlay(); + + virtual void receiveEvent(Event* event); + + float getOpacity() const; + + void setOpacity(const float opacity); + + AString getName() const; + + void setOverlayNumber(const int32_t overlayIndex); + + virtual AString toString() const; + + virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; + + bool isEnabled() const; + + void setEnabled(const bool enabled); + + void copyData(const MediaOverlay* overlay); + + void swapData(MediaOverlay* overlay); + + void getSelectionData(std::vector& filesOut, + MediaFile* &selectedFileOut, + int32_t& selectedFrameIndexOut); + + void getSelectionData(MediaFile* &selectedFileOut, + int32_t& selectedFrameIndexOut); + + void setSelectionData(MediaFile* selectedFile, + const int32_t selectedFrameIndex); + + MapYokingGroupEnum::Enum getMapYokingGroup() const; + + void setMapYokingGroup(const MapYokingGroupEnum::Enum mapYokingGroup); + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + private: + MediaOverlay(const MediaOverlay&); + + MediaOverlay& operator=(const MediaOverlay&); + + /** Name of overlay (DO NOT COPY)*/ + AString m_name; + + /** Index of this overlay (DO NOT COPY)*/ + int32_t m_overlayIndex; + + /** opacity for overlay */ + float m_opacity; + + /** enabled status */ + mutable bool m_enabled; + + /** map yoking group */ + MapYokingGroupEnum::Enum m_mapYokingGroup; + + /** selected file */ + MediaFile* m_selectedFile; + + /** selected frame index */ + int32_t m_selectedFrameIndex; + + /** helps with scene save/restore */ + SceneClassAssistant* m_sceneAssistant; + }; + +#ifdef __MEDIA_OVERLAY_DECLARE__ +#endif // __MEDIA_OVERLAY_DECLARE__ + +} // namespace +#endif //__MEDIA_OVERLAY__H_ diff -Nru connectome-workbench-1.4.2/src/Brain/MediaOverlaySetArray.cxx connectome-workbench-1.5.0/src/Brain/MediaOverlaySetArray.cxx --- connectome-workbench-1.4.2/src/Brain/MediaOverlaySetArray.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/MediaOverlaySetArray.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,239 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __MEDIA_OVERLAY_SET_ARRAY_DECLARE__ +#include "MediaOverlaySetArray.h" +#undef __MEDIA_OVERLAY_SET_ARRAY_DECLARE__ + +#include "BrainConstants.h" +#include "CaretAssert.h" +#include "EventBrowserTabDelete.h" +#include "EventManager.h" +#include "MediaOverlaySet.h" +#include "SceneClass.h" + +using namespace caret; + + + +/** + * \class caret::OverlaySetArray + * \brief Maintains an array of overlay sets for use with a model + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param name + * Name of model using this overlay set. This name is displayed + * if there is an attempt to yoke models with incompatible overlays. + */ +MediaOverlaySetArray::MediaOverlaySetArray(const AString& name) +: CaretObject(), +m_name(name) +{ + m_overlaySets.resize(BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + m_overlaySets[i] = new MediaOverlaySet(name, + i); + } + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); +} + +/** + * Destructor. + */ +MediaOverlaySetArray::~MediaOverlaySetArray() +{ + EventManager::get()->removeAllEventsFromListener(this); + + for (std::vector::iterator iter = m_overlaySets.begin(); + iter != m_overlaySets.end(); + iter++) { + delete *iter; + } + m_overlaySets.clear(); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +MediaOverlaySetArray::toString() const +{ + return "OverlaySetArray"; +} + +/** + * @return The number of overlay sets. + */ +int32_t +MediaOverlaySetArray::getNumberOfOverlaySets() +{ + return m_overlaySets.size(); +} + +/** + * Get the overlay set at the given index. + * + * @param indx + * Index of overlay set. + * @return + * Overlay set at given index. + */ +MediaOverlaySet* +MediaOverlaySetArray::getOverlaySet(const int32_t indx) +{ + CaretAssertVectorIndex(m_overlaySets, indx); + + return m_overlaySets[indx]; +} + +/** + * Initialize the overlay selections. + */ +void +MediaOverlaySetArray::initializeOverlaySelections() +{ + for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { + CaretAssertVectorIndex(m_overlaySets, iTab); + m_overlaySets[iTab]->initializeOverlays(); + } +} + + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +MediaOverlaySetArray::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE) { + EventBrowserTabDelete* deleteTabEvent = dynamic_cast(event); + CaretAssert(deleteTabEvent); + + /* + * Since tab is being deleted, reset any overlay yoking for the tab. + */ + const int32_t tabIndex = deleteTabEvent->getBrowserTabIndex(); + if ((tabIndex > 0) + && (tabIndex < getNumberOfOverlaySets())) { + m_overlaySets[tabIndex]->resetOverlayYokingToOff(); + } + + deleteTabEvent->setEventProcessed(); + } +} + +/** + * Copy the overlay set from the source tab index to the + * destination tab index. + * + * @param sourceTabIndex + * Source from which tab content is copied. + * @param destinationTabIndex + * Destination to which tab content is copied. + */ +void +MediaOverlaySetArray::copyOverlaySet(const int32_t sourceTabIndex, + const int32_t destinationTabIndex) +{ + CaretAssertVectorIndex(m_overlaySets, sourceTabIndex); + CaretAssertVectorIndex(m_overlaySets, destinationTabIndex); + + const MediaOverlaySet* sourceOverlaySet = m_overlaySets[sourceTabIndex]; + MediaOverlaySet* destinationOverlaySet = m_overlaySets[destinationTabIndex]; + destinationOverlaySet->copyMediaOverlaySet(sourceOverlaySet); +} + +/** + * Save information specific to this type of model to the scene. + * + * @param tabIndices + * Tab indices that are valid. + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +MediaOverlaySetArray::saveTabIndicesToScene(const std::vector& tabIndices, + const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "MediaOverlaySetArray", + 1); + + /* + * Save overlay sets for tabs + */ + SceneObjectMapIntegerKey* overlaySetMap = new SceneObjectMapIntegerKey("m_mediaOverlaySetMAP", + SceneObjectDataTypeEnum::SCENE_CLASS); + + for (const auto tabIndex : tabIndices) { + overlaySetMap->addClass(tabIndex, m_overlaySets[tabIndex]->saveToScene(sceneAttributes, + "m_mediaOverlaySet")); + } + sceneClass->addChild(overlaySetMap); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +MediaOverlaySetArray::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + const SceneObjectMapIntegerKey* overlaySetMap = sceneClass->getMapIntegerKey("m_mediaOverlaySetMAP"); + if (overlaySetMap != NULL) { + const std::vector tabIndices = overlaySetMap->getKeys(); + for (const auto tabIndex : tabIndices) { + const SceneClass* sceneClass = overlaySetMap->classValue(tabIndex); + CaretAssertVectorIndex(m_overlaySets, tabIndex); + m_overlaySets[tabIndex]->restoreFromScene(sceneAttributes, + sceneClass); + } + } +} diff -Nru connectome-workbench-1.4.2/src/Brain/MediaOverlaySetArray.h connectome-workbench-1.5.0/src/Brain/MediaOverlaySetArray.h --- connectome-workbench-1.4.2/src/Brain/MediaOverlaySetArray.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/MediaOverlaySetArray.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,85 @@ +#ifndef __MEDIA_OVERLAY_SET_ARRAY_H__ +#define __MEDIA_OVERLAY_SET_ARRAY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "CaretObject.h" + +#include "EventListenerInterface.h" +#include "Overlay.h" + +namespace caret { + class MediaOverlaySet; + + class MediaOverlaySetArray : public CaretObject, public EventListenerInterface { + + public: + MediaOverlaySetArray(const AString& name); + + virtual ~MediaOverlaySetArray(); + + int32_t getNumberOfOverlaySets(); + + MediaOverlaySet* getOverlaySet(const int32_t indx); + + void initializeOverlaySelections(); + + void copyOverlaySet(const int32_t sourceTabIndex, + const int32_t destinationTabIndex); + + virtual SceneClass* saveTabIndicesToScene(const std::vector& tabIndices, + const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + private: + MediaOverlaySetArray(const MediaOverlaySetArray&); + + MediaOverlaySetArray& operator=(const MediaOverlaySetArray&); + + public: + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual void receiveEvent(Event* event); + + private: + // ADD_NEW_MEMBERS_HERE + + void initialize(); + + /** Name for this overlay set array */ + AString m_name; + + /** The overlay sets */ + std::vector m_overlaySets; + }; + +#ifdef __MEDIA_OVERLAY_SET_ARRAY_DECLARE__ + // +#endif // __MEDIA_OVERLAY_SET_ARRAY_DECLARE__ + +} // namespace +#endif //__MEDIA_OVERLAY_SET_ARRAY_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/MediaOverlaySet.cxx connectome-workbench-1.5.0/src/Brain/MediaOverlaySet.cxx --- connectome-workbench-1.4.2/src/Brain/MediaOverlaySet.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/MediaOverlaySet.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,517 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#include + +#define __MEDIA_OVERLAY_SET_DECLARE__ +#include "MediaOverlaySet.h" +#undef __MEDIA_OVERLAY_SET_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "MediaFile.h" +#include "EventManager.h" +#include "MediaOverlay.h" +#include "ModelMedia.h" +#include "PlainTextStringBuilder.h" +#include "Scene.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneClassAssistant.h" + +using namespace caret; + +/** + * \class MediaOverlaySet + * \brief Contains a set of overlay assignments + * + * The maximum number of overlays is fixed. The number + * of overlays presented to the user varies and is + * controlled using the ToolBox in a Browser Window. + * + * The primary overlay is always the overlay at index zero. + * The underlay is the overlay at (numberOfDisplayedOverlays - 1). + * When models are colored, the overlays are assigned + * starting with the underlay and concluding with the primary + * overlay. + */ + +/** + * Constructor for the given surface structures, surface types, and volumes. + * + * @param name + * Name for this overlay set + * @param tabIndex + * Index of tab for this overlay set. + */ +MediaOverlaySet::MediaOverlaySet(const AString& name, + const int32_t tabIndex) +: CaretObject(), +m_name(name), +m_tabIndex(tabIndex) +{ + m_numberOfDisplayedOverlays = BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS; + + m_sceneAssistant = new SceneClassAssistant(); + m_sceneAssistant->add("m_numberOfDisplayedOverlays", + &m_numberOfDisplayedOverlays); + + for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + m_overlays[i] = new MediaOverlay(); + } + + initializeOverlays(); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_VALIDATION); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP); +} + +/** + * Destructor. + */ +MediaOverlaySet::~MediaOverlaySet() +{ + EventManager::get()->removeAllEventsFromListener(this); + for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + delete m_overlays[i]; + } + delete m_sceneAssistant; +} + +/** + * Copy the given overlay set to this overlay set. + * @param overlaySet + * Overlay set that is copied. + */ +void +MediaOverlaySet::copyMediaOverlaySet(const MediaOverlaySet* overlaySet) +{ + for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + m_overlays[i]->copyData(overlaySet->getOverlay(i)); + } + m_numberOfDisplayedOverlays = overlaySet->m_numberOfDisplayedOverlays; +} + +/** + * @return Returns the top-most overlay regardless of its enabled status. + */ +MediaOverlay* +MediaOverlaySet::getPrimaryOverlay() +{ + return m_overlays[0]; +} + +/** + * @return Returns the underlay which is the lowest + * displayed overlay. + */ +MediaOverlay* +MediaOverlaySet::getUnderlay() +{ + return m_overlays[getNumberOfDisplayedOverlays() - 1]; +} + +/** + * Get the overlay at the specified index. + * @param overlayNumber + * Index of the overlay. + * @return Overlay at the given index. + */ +const MediaOverlay* +MediaOverlaySet::getOverlay(const int32_t overlayNumber) const +{ + CaretAssertArrayIndex(m_overlays, + BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, + overlayNumber); + return m_overlays[overlayNumber]; +} + +/** + * Get the overlay at the specified index. + * @param overlayNumber + * Index of the overlay. + * @return Overlay at the given index. + */ +MediaOverlay* +MediaOverlaySet::getOverlay(const int32_t overlayNumber) +{ + CaretAssertArrayIndex(m_overlays, + BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, + overlayNumber); + return m_overlays[overlayNumber]; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +MediaOverlaySet::toString() const +{ + PlainTextStringBuilder tb; + getDescriptionOfContent(tb); + return tb.getText(); +} + +/** + * Get a text description of the window's content. + * + * @param descriptionOut + * Description of the window's content. + */ +void +MediaOverlaySet::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const +{ + descriptionOut.addLine("Media Overlay Set"); + + const int numOverlays = getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numOverlays; i++) { + if (getOverlay(i)->isEnabled()) { + descriptionOut.pushIndentation(); + + descriptionOut.addLine("Media Overlay " + + AString::number(i + 1) + + ": "); + + descriptionOut.pushIndentation(); + getOverlay(i)->getDescriptionOfContent(descriptionOut); + descriptionOut.popIndentation(); + + descriptionOut.popIndentation(); + } + } +} + + +/** + * Add a displayed overlay. If the maximum + * number of surface overlays is reached, + * this method has no effect. + */ +void +MediaOverlaySet::addDisplayedOverlay() +{ + m_numberOfDisplayedOverlays++; + if (m_numberOfDisplayedOverlays > BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays = BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; + } +} + +/** + * @return Returns the number of displayed overlays. + */ +int32_t +MediaOverlaySet::getNumberOfDisplayedOverlays() const +{ + return m_numberOfDisplayedOverlays; +} + +/** + * Sets the number of displayed overlays. + * @param numberOfDisplayedOverlays + * Number of overlays for display. + */ +void +MediaOverlaySet::setNumberOfDisplayedOverlays(const int32_t numberOfDisplayedOverlays) +{ + const int32_t oldNumberOfDisplayedOverlays = m_numberOfDisplayedOverlays; + m_numberOfDisplayedOverlays = numberOfDisplayedOverlays; + if (m_numberOfDisplayedOverlays < BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays = BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS; + } + if (m_numberOfDisplayedOverlays > BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays = BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; + } + + /* + * If one overlay added (probably through GUI), + * shift all overlays down one position so that + * new overlay appears at the top + */ + const int32_t numberOfOverlaysAdded = m_numberOfDisplayedOverlays - oldNumberOfDisplayedOverlays; + if (numberOfOverlaysAdded == 1) { + for (int32_t i = (m_numberOfDisplayedOverlays - 1); i >= 0; i--) { + moveDisplayedOverlayDown(i); + } + } +} + +/** + * Insert an overlay below this overlay + * @param overlayIndex + * Index of overlay for which an overlay is added below + */ +void +MediaOverlaySet::insertOverlayAbove(const int32_t overlayIndex) +{ + if (m_numberOfDisplayedOverlays < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays++; + + for (int32_t i = (m_numberOfDisplayedOverlays - 2); i >= overlayIndex; i--) { + moveDisplayedOverlayDown(i); + } + } +} + +/** + * Insert an overlay above this overlay + * @param overlayIndex + * Index of overlay for which an overlay is added above + */ +void +MediaOverlaySet::insertOverlayBelow(const int32_t overlayIndex) +{ + if (m_numberOfDisplayedOverlays < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays++; + + for (int32_t i = (m_numberOfDisplayedOverlays - 2); i > overlayIndex; i--) { + moveDisplayedOverlayDown(i); + } + } +} + + +/** + * Remove a displayed overlay. This method will have + * no effect if the minimum number of overlays are + * displayed + * + * @param overlayIndex + * Index of overlay for removal from display. + */ +void +MediaOverlaySet::removeDisplayedOverlay(const int32_t overlayIndex) +{ + CaretAssertArrayIndex(m_overlays, + BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, + overlayIndex); + m_overlays[overlayIndex]->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); + + if (m_numberOfDisplayedOverlays > BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS) { + m_numberOfDisplayedOverlays--; + for (int32_t i = overlayIndex; i < m_numberOfDisplayedOverlays; i++) { + m_overlays[i]->copyData(m_overlays[i+1]); + } + } +} + +/** + * Move the overlay at the given index up one level + * (swap it with overlayIndex - 1). This method will + * have no effect if the overlay is the top-most overlay. + * + * @param overlayIndex + * Index of overlay that is to be moved up. + */ +void +MediaOverlaySet::moveDisplayedOverlayUp(const int32_t overlayIndex) +{ + if (overlayIndex > 0) { + m_overlays[overlayIndex]->swapData(m_overlays[overlayIndex - 1]); + } +} + +/** + * Move the overlay at the given index down one level + * (swap it with overlayIndex + 1). This method will + * have no effect if the overlay is the bottom-most overlay. + * + * @param overlayIndex + * Index of overlay that is to be moved down. + */ +void +MediaOverlaySet::moveDisplayedOverlayDown(const int32_t overlayIndex) +{ + const int32_t nextOverlayIndex = overlayIndex + 1; + if (nextOverlayIndex < m_numberOfDisplayedOverlays) { + m_overlays[overlayIndex]->swapData(m_overlays[nextOverlayIndex]); + } +} + +/** + * Initialize the overlays. + */ +void +MediaOverlaySet::initializeOverlays() +{ + /* + * Enable top overlay + */ + const int32_t numberOfDisplayedOverlays(getNumberOfDisplayedOverlays()); + for (int32_t i = 0; i < numberOfDisplayedOverlays; i++) { + getOverlay(i)->setEnabled(i == 0); + } +} + + +/** + * For the given caret data file, find overlays in which the + * file is selected and return the indices of the selected maps. + * + * @param mediaFile + * The caret data file. + * @param isLimitToEnabledOverlays + * If true, only include indices for overlay that are enabled. + * Otherwise, include indices for all overlays. + * @param selectedIndicesOut + * Output containing map indices for the given caret data files + * that are selected as overlays in this overlay set. + */ +void +MediaOverlaySet::getSelectedIndicesForFile(const MediaFile* mediaFile, + const bool isLimitToEnabledOverlays, + std::vector& selectedIndicesOut) const +{ + selectedIndicesOut.clear(); + + /* + * Put indices in a set to avoid duplicates and keep them sorted. + */ + std::set indicesSet; + + const int32_t numberOfOverlays = getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numberOfOverlays; i++) { + MediaOverlay* overlay = const_cast(getOverlay(i)); + bool checkIt = true; + if (isLimitToEnabledOverlays) { + if (overlay->isEnabled() == false) { + checkIt = false; + } + } + + if (checkIt) { + MediaFile* file; + int32_t mapIndex; + overlay->getSelectionData(file, mapIndex); + if (file == mediaFile) { + indicesSet.insert(mapIndex); + } + } + } + + selectedIndicesOut.insert(selectedIndicesOut.end(), + indicesSet.begin(), + indicesSet.end()); +} + +/** + * Reset the yoking status of all overlays to off. + */ +void +MediaOverlaySet::resetOverlayYokingToOff() +{ + for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + m_overlays[i]->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); + } +} + + +/** + * Create a scene for an instance of a class. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of the class' instance. + * + * @return Pointer to SceneClass object representing the state of + * this object. Under some circumstances a NULL pointer may be + * returned. Caller will take ownership of returned object. + */ +SceneClass* +MediaOverlaySet::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "OverlaySet", + 1); + + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + +// const int32_t numOverlaysToSave = BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; + const int32_t numOverlaysToSave = getNumberOfDisplayedOverlays(); + + std::vector overlayClassVector; + for (int i = 0; i < numOverlaysToSave; i++) { + overlayClassVector.push_back(m_overlays[i]->saveToScene(sceneAttributes, "m_overlays")); + } + + SceneClassArray* overlayClassArray = new SceneClassArray("m_overlays", + overlayClassVector); + sceneClass->addChild(overlayClassArray); + + return sceneClass; +} + +/** + * Restore the state of an instance of a class. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. May be NULL for some types of scenes. + */ +void +MediaOverlaySet::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + initializeOverlays(); + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + const SceneClassArray* overlayClassArray = sceneClass->getClassArray("m_overlays"); + if (overlayClassArray != NULL) { + const int32_t numOverlays = std::min(overlayClassArray->getNumberOfArrayElements(), + (int32_t)BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS); + for (int32_t i = 0; i < numOverlays; i++) { + m_overlays[i]->restoreFromScene(sceneAttributes, + overlayClassArray->getClassAtIndex(i)); + } + } +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +MediaOverlaySet::receiveEvent(Event* /*event*/) +{ + +} + diff -Nru connectome-workbench-1.4.2/src/Brain/MediaOverlaySet.h connectome-workbench-1.5.0/src/Brain/MediaOverlaySet.h --- connectome-workbench-1.4.2/src/Brain/MediaOverlaySet.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/MediaOverlaySet.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,123 @@ +#ifndef __MEDIA_OVERLAY_SET__H_ +#define __MEDIA_OVERLAY_SET__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "BrainConstants.h" +#include "CaretObject.h" +#include "EventListenerInterface.h" +#include "SceneableInterface.h" + +namespace caret { + + class MediaFile; + class BrowserTabContent; + class MediaOverlay; + class SceneClassAssistant; + class PlainTextStringBuilder; + + class MediaOverlaySet : public CaretObject, public EventListenerInterface, public SceneableInterface { + + public: + MediaOverlaySet(const AString& name, + const int32_t tabIndex); + + virtual ~MediaOverlaySet(); + + virtual void receiveEvent(Event* event); + + void copyMediaOverlaySet(const MediaOverlaySet* overlaySet); + + MediaOverlay* getPrimaryOverlay(); + + MediaOverlay* getUnderlay(); + + MediaOverlay* getOverlay(const int32_t overlayNumber); + + const MediaOverlay* getOverlay(const int32_t overlayNumber) const; + + void addDisplayedOverlay(); + + void setNumberOfDisplayedOverlays(const int32_t numberOfDisplayedOverlays); + + int32_t getNumberOfDisplayedOverlays() const; + + void insertOverlayAbove(const int32_t overlayIndex); + + void insertOverlayBelow(const int32_t overlayIndex); + + void removeDisplayedOverlay(const int32_t overlayIndex); + + void moveDisplayedOverlayUp(const int32_t overlayIndex); + + void moveDisplayedOverlayDown(const int32_t overlayIndex); + + void initializeOverlays(); + + void getSelectedIndicesForFile(const MediaFile* MediaFile, + const bool isLimitToEnabledOverlays, + std::vector& selectedIndicesOut) const; + + void resetOverlayYokingToOff(); + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + public: + virtual AString toString() const; + + virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; + + private: + + MediaOverlaySet(const MediaOverlaySet&); + + MediaOverlaySet& operator=(const MediaOverlaySet&); + + MediaOverlay* m_overlays[BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS]; + +// bool findFilesWithMapNamed(std::vector& matchedFilesOut, +// std::vector& matchedFileIndicesOut, +// const std::vector& matchToStructures, +// const DataFileTypeEnum::Enum dataFileType, +// const bool matchToVolumeData, +// const AString& matchToNamesRegularExpressionText, +// const bool matchToNamesRegularExpressionResult, +// const bool matchOneFilePerStructure); + + AString m_name; + + int32_t m_tabIndex; + + /** Surface structures of data files displayed in this overlay */ + int32_t m_numberOfDisplayedOverlays; + + SceneClassAssistant* m_sceneAssistant; + + }; + +#ifdef __MEDIA_OVERLAY_SET_DECLARE__ +#endif // __MEDIA_OVERLAY_SET_DECLARE__ + +} // namespace +#endif //__MEDIA_OVERLAY_SET__H_ diff -Nru connectome-workbench-1.4.2/src/Brain/ModelChartTwo.cxx connectome-workbench-1.5.0/src/Brain/ModelChartTwo.cxx --- connectome-workbench-1.4.2/src/Brain/ModelChartTwo.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ModelChartTwo.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -42,6 +42,7 @@ #include "ChartModelTimeSeries.h" #include "ChartableTwoFileDelegate.h" #include "ChartableTwoFileMatrixChart.h" +#include "ChartableTwoFileLineLayerChart.h" #include "ChartableTwoFileLineSeriesChart.h" #include "ChartTwoLineSeriesHistory.h" #include "ChartTwoOverlay.h" @@ -90,9 +91,10 @@ "Histogram Chart Overlays")); m_matrixChartOverlaySetArray = std::unique_ptr(new ChartTwoOverlaySetArray(ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX, "Matrix Chart Overlays")); + m_lineLayerChartOverlaySetArray = std::unique_ptr(new ChartTwoOverlaySetArray(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER, + "Line Layer Chart Overlays")); m_lineSeriesChartOverlaySetArray = std::unique_ptr(new ChartTwoOverlaySetArray(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES, - "Line Series Chart Overlays")); - + "Line Series Chart Overlays")); initializeCharts(); m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); @@ -236,7 +238,6 @@ { CaretAssert(ciftiMapFile); - //std::vector tabIndices; EventBrowserTabIndicesGetAll tabIndicesEvent; EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); @@ -381,62 +382,6 @@ std::vector cartesianChartData; -// for (std::list >::iterator dsIter = m_dataSeriesChartData.begin(); -// dsIter != m_dataSeriesChartData.end(); -// dsIter++) { -// QSharedPointer spCart = dsIter->toStrongRef(); -// if ( ! spCart.isNull()) { -// cartesianChartData.push_back(spCart.data()); -// } -// } -// for (std::list >::iterator tsIter = m_frequencySeriesChartData.begin(); -// tsIter != m_frequencySeriesChartData.end(); -// tsIter++) { -// QSharedPointer spCart = tsIter->toStrongRef(); -// if ( ! spCart.isNull()) { -// cartesianChartData.push_back(spCart.data()); -// } -// } -// for (std::list >::iterator tsIter = m_timeSeriesChartData.begin(); -// tsIter != m_timeSeriesChartData.end(); -// tsIter++) { -// QSharedPointer spCart = tsIter->toStrongRef(); -// if ( ! spCart.isNull()) { -// cartesianChartData.push_back(spCart.data()); -// } -// } -// -// -// /* -// * Iterate over node indices for which colors are desired. -// */ -// const std::vector nodeIndices = nodeChartID->getNodeIndices(); -// for (std::vector::const_iterator nodeIter = nodeIndices.begin(); -// nodeIter != nodeIndices.end(); -// nodeIter++) { -// const int32_t nodeIndex = *nodeIter; -// -// /* -// * Iterate over the data in the cartesian chart -// */ -// for (std::vector::iterator cdIter = cartesianChartData.begin(); -// cdIter != cartesianChartData.end(); -// cdIter++) { -// const ChartDataCartesian* cdc = *cdIter; -// const ChartDataSource* cds = cdc->getChartDataSource(); -// if (cds->isSurfaceNodeSourceOfData(structureName, nodeIndex)) { -// /* -// * Found node index so add its color to the event -// */ -// const CaretColorEnum::Enum color = cdc->getColor(); -// const float* rgb = CaretColorEnum::toRGB(color); -// nodeChartID->addNode(nodeIndex, -// rgb); -// break; -// } -// } -// } - nodeChartID->setEventProcessed(); } } @@ -526,6 +471,7 @@ const std::vector validTabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); std::vector histogramChartTabIndices; + std::vector lineLayerChartTabIndices; std::vector lineSeriesChartTabIndices; std::vector matrixChartTabIndices; for (auto tabIndex : validTabIndices) { @@ -535,6 +481,9 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: histogramChartTabIndices.push_back(tabIndex); break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + lineLayerChartTabIndices.push_back(tabIndex); + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: lineSeriesChartTabIndices.push_back(tabIndex); break; @@ -562,6 +511,15 @@ } } + if ( ! lineLayerChartTabIndices.empty()) { + SceneClass* lineLayerClass = m_lineLayerChartOverlaySetArray->saveTabIndicesToScene(lineLayerChartTabIndices, + sceneAttributes, + "m_lineLayerChartOverlaySetArray"); + if (lineLayerClass != NULL) { + sceneClass->addClass(lineLayerClass); + } + } + if ( ! matrixChartTabIndices.empty()) { SceneClass* matrixClass = m_matrixChartOverlaySetArray->saveTabIndicesToScene(matrixChartTabIndices, sceneAttributes, @@ -597,8 +555,10 @@ m_histogramChartOverlaySetArray->restoreFromScene(sceneAttributes, sceneClass->getClass("m_histogramChartOverlaySetArray")); + m_lineLayerChartOverlaySetArray->restoreFromScene(sceneAttributes, + sceneClass->getClass("m_lineLayerChartOverlaySetArray")); m_lineSeriesChartOverlaySetArray->restoreFromScene(sceneAttributes, - sceneClass->getClass("m_lineSeriesChartOverlaySetArray")); + sceneClass->getClass("m_lineSeriesChartOverlaySetArray")); m_matrixChartOverlaySetArray->restoreFromScene(sceneAttributes, sceneClass->getClass("m_matrixChartOverlaySetArray")); } @@ -736,7 +696,7 @@ * Find the data files and setup for loading of * lines series data. */ - for (const auto filenameAndColor : dataFileNamesAndColors) { + for (const auto& filenameAndColor : dataFileNamesAndColors) { const AString filename = filenameAndColor.first; CaretColorEnum::Enum color = filenameAndColor.second; CaretMappableDataFile* mapFileFullPath = NULL; @@ -963,7 +923,6 @@ selectedRowColumnIndex); chartTwoOverlay->setMatrixTriangularViewingMode(ChartTwoMatrixTriangularViewingModeEnum::MATRIX_VIEW_FULL); chartTwoOverlay->setAllMapsSelected(false); - chartTwoOverlay->setCartesianVerticalAxisLocation(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); chartTwoOverlay->setMapYokingGroup(chartOneYoking); AnnotationColorBar* chartTwoColorBar = chartTwoOverlay->getColorBar(); if ((chartOneColorBar != NULL) @@ -1002,8 +961,10 @@ m_histogramChartOverlaySetArray->copyChartOverlaySet(sourceTabIndex, destinationTabIndex); - m_lineSeriesChartOverlaySetArray->copyChartOverlaySet(sourceTabIndex, + m_lineLayerChartOverlaySetArray->copyChartOverlaySet(sourceTabIndex, destinationTabIndex); + m_lineSeriesChartOverlaySetArray->copyChartOverlaySet(sourceTabIndex, + destinationTabIndex); m_matrixChartOverlaySetArray->copyChartOverlaySet(sourceTabIndex, destinationTabIndex); } @@ -1025,6 +986,8 @@ m_histogramChartOverlaySetArray->copyChartOverlaySetCartesianAxes(sourceTabIndex, destinationTabIndex); + m_lineLayerChartOverlaySetArray->copyChartOverlaySetCartesianAxes(sourceTabIndex, + destinationTabIndex); m_lineSeriesChartOverlaySetArray->copyChartOverlaySetCartesianAxes(sourceTabIndex, destinationTabIndex); } @@ -1139,6 +1102,8 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -1165,6 +1130,9 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: chartOverlaySet = m_histogramChartOverlaySetArray->getChartTwoOverlaySet(tabIndex); break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + chartOverlaySet = m_lineLayerChartOverlaySetArray->getChartTwoOverlaySet(tabIndex); + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: chartOverlaySet = m_lineSeriesChartOverlaySetArray->getChartTwoOverlaySet(tabIndex); break; @@ -1194,4 +1162,23 @@ return chartOverlaySet; } +/** + * @return All chart two overlay sets in this model for the given tab index + * @param tabIndex + * Index of the tab + * @return vector containing the overlay sets + * + */ +std::vector +ModelChartTwo::getAllChartTwoOverlaySets(const int32_t tabIndex) const +{ + std::vector overlaySets; + + overlaySets.push_back(m_histogramChartOverlaySetArray->getChartTwoOverlaySet(tabIndex)); + overlaySets.push_back(m_lineLayerChartOverlaySetArray->getChartTwoOverlaySet(tabIndex)); + overlaySets.push_back(m_lineSeriesChartOverlaySetArray->getChartTwoOverlaySet(tabIndex)); + overlaySets.push_back(m_matrixChartOverlaySetArray->getChartTwoOverlaySet(tabIndex)); + + return overlaySets; +} diff -Nru connectome-workbench-1.4.2/src/Brain/ModelChartTwo.h connectome-workbench-1.5.0/src/Brain/ModelChartTwo.h --- connectome-workbench-1.4.2/src/Brain/ModelChartTwo.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ModelChartTwo.h 2021-02-16 19:46:47.000000000 +0000 @@ -88,6 +88,8 @@ virtual const ChartTwoOverlaySet* getChartTwoOverlaySet(const int tabIndex) const override; + std::vector getAllChartTwoOverlaySets(const int32_t tabIndex) const; + virtual void receiveEvent(Event* event) override; void getValidChartTwoDataTypes(std::vector& validChartDataTypesOut) const; @@ -135,13 +137,16 @@ mutable ChartTwoDataTypeEnum::Enum m_selectedChartTwoDataType[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; - /** Chart Overlay sets for XX data type */ + /** Chart Overlay sets for histogram data type */ std::unique_ptr m_histogramChartOverlaySetArray; - /** Chart Overlay sets for XX data type */ + /** Chart Overlay sets for line series data type */ std::unique_ptr m_lineSeriesChartOverlaySetArray; - /** Chart Overlay sets for XX data type */ + /** Chart Overlay sets for layer data type */ + std::unique_ptr m_lineLayerChartOverlaySetArray; + + /** Chart Overlay sets for matrix data type */ std::unique_ptr m_matrixChartOverlaySetArray; std::unique_ptr m_sceneAssistant; diff -Nru connectome-workbench-1.4.2/src/Brain/Model.cxx connectome-workbench-1.5.0/src/Brain/Model.cxx --- connectome-workbench-1.4.2/src/Brain/Model.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/Model.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -145,6 +145,34 @@ } /** + * Get the medai overlay set for this model. + * + * @param tabIndex + * Index for the chart overlay set. + * @return + * media overlay set or NULL if not valid for this model. + */ +MediaOverlaySet* +Model::getMediaOverlaySet(const int /*tabIndex*/) +{ + return NULL; +} + +/** + * Get the media overlay set for this model. + * + * @param tabIndex + * Index for the chart overlay set. + * @return + * media overlay set or NULL if not valid for this model. + */ +const MediaOverlaySet* +Model::getMediaOverlaySet(const int /*tabIndex*/) const +{ + return NULL; +} + +/** * Create a scene for an instance of a class. * * @param sceneAttributes diff -Nru connectome-workbench-1.4.2/src/Brain/Model.h connectome-workbench-1.5.0/src/Brain/Model.h --- connectome-workbench-1.4.2/src/Brain/Model.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/Model.h 2021-02-16 19:46:47.000000000 +0000 @@ -29,6 +29,7 @@ namespace caret { class Brain; class ChartTwoOverlaySet; + class MediaOverlaySet; class OverlaySet; class PlainTextStringBuilder; @@ -71,6 +72,10 @@ virtual const ChartTwoOverlaySet* getChartTwoOverlaySet(const int tabIndex) const; + virtual MediaOverlaySet* getMediaOverlaySet(const int tabIndex); + + virtual const MediaOverlaySet* getMediaOverlaySet(const int tabIndex) const; + virtual void initializeSelectedSurfaces(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, diff -Nru connectome-workbench-1.4.2/src/Brain/ModelMedia.cxx connectome-workbench-1.5.0/src/Brain/ModelMedia.cxx --- connectome-workbench-1.4.2/src/Brain/ModelMedia.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ModelMedia.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,339 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "Brain.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "CaretMappableDataFile.h" +#include "CaretDataFileSelectionModel.h" +#include "CaretLogger.h" +#include "EventManager.h" +#include "MapFileDataSelector.h" +#include "ModelMedia.h" +#include "MediaOverlaySet.h" +#include "MediaOverlaySetArray.h" +#include "OverlaySetArray.h" +#include "PlainTextStringBuilder.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneClassAssistant.h" + +using namespace caret; + +/** + * Constructor. + * + */ +ModelMedia::ModelMedia(Brain* brain) +: Model(ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA, + brain) +{ + std::vector overlaySurfaceStructures; + m_layerOverlaySetArray = new OverlaySetArray(overlaySurfaceStructures, + Overlay::INCLUDE_VOLUME_FILES_YES, + "Chart View"); + + m_mediaOverlaySetArray = new MediaOverlaySetArray("Media View"); + + std::vector supportedDataFileTypes { + DataFileTypeEnum::IMAGE + }; + + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + CaretAssertArrayIndex(m_fileSelectionModel, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, i); + m_fileSelectionModels[i].reset(CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(supportedDataFileTypes)); + } + + initializeMedia(); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); +} + +/** + * Destructor + */ +ModelMedia::~ModelMedia() +{ + delete m_layerOverlaySetArray; + delete m_mediaOverlaySetArray; + EventManager::get()->removeAllEventsFromListener(this); +} + +void +ModelMedia::initializeMedia() +{ + m_layerOverlaySetArray->initializeOverlaySelections(); +} + +/** + * Update the model. + */ +void +ModelMedia::updateModel() +{ +} + +/** + * Reset this model. + */ +void +ModelMedia::reset() +{ + initializeMedia(); +} + +/** + * Receive an event. + * + * @param event + * The event that the receive can respond to. + */ +void +ModelMedia::receiveEvent(Event* /*event*/) +{ +} + +/** + * @return File selection model for the given tab index + * @param tabIndex + * Index of the tab + */ +CaretDataFileSelectionModel* +ModelMedia::getFileSelectionModel(const int32_t tabIndex) +{ + CaretAssertArrayIndex(m_fileSelectionModels, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + return m_fileSelectionModels[tabIndex].get(); +} + +/** + * @return File selection model for the given tab index + * @param tabIndex + * Index of the tab + */ +const CaretDataFileSelectionModel* +ModelMedia::getFileSelectionModel(const int32_t tabIndex) const +{ + CaretAssertArrayIndex(m_fileSelectionModels, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); + return m_fileSelectionModels[tabIndex].get(); +} + +/** + * Get the name for use in a GUI. + * + * @param includeStructureFlag - Prefix label with structure to which + * this structure model belongs. + * @return Name for use in a GUI. + * + */ +AString +ModelMedia::getNameForGUI(const bool /*includeStructureFlag*/) const +{ + AString name = "Media"; + return name; +} + +/** + * @return The name that should be displayed in the tab + * displaying this model. + */ +AString +ModelMedia::getNameForBrowserTab() const +{ + AString name = "Media"; + return name; +} + +/** + * Get the media overlay set for the given tab. + * @param tabIndex + * Index of tab. + * @return + * Media overlay set at the given tab index. + */ +MediaOverlaySet* +ModelMedia::getMediaOverlaySet(const int tabIndex) +{ + CaretAssertArrayIndex(m_mediaOverlaySetArray, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + return m_mediaOverlaySetArray->getOverlaySet(tabIndex); +} + +/** + * Get the media overlay set for the given tab. + * @param tabIndex + * Index of tab. + * @return + * Media Overlay set at the given tab index. + */ +const MediaOverlaySet* +ModelMedia::getMediaOverlaySet(const int tabIndex) const +{ + CaretAssertArrayIndex(m_mediaOverlaySetArray, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + return m_mediaOverlaySetArray->getOverlaySet(tabIndex); +} + + +/** + * Initilize the overlays for this model. + */ +void +ModelMedia::initializeOverlays() +{ + m_layerOverlaySetArray->initializeOverlaySelections(); +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param sceneClass + * SceneClass to which model specific information is added. + */ +void +ModelMedia::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); + if ( ! tabIndices.empty()) { + SceneClass* mediaSetClass = m_mediaOverlaySetArray->saveTabIndicesToScene(tabIndices, + sceneAttributes, + "m_mediaOverlaySetArray"); + if (mediaSetClass != NULL) { + sceneClass->addClass(mediaSetClass); + } + } + + std::vector fileSelectionClasses; + for (auto& fs : m_fileSelectionModels) { + AString name("m_fileSelectionModels_" + + AString::number(fileSelectionClasses.size())); + fileSelectionClasses.push_back(fs->saveToScene(sceneAttributes, + name)); + } + + SceneClassArray* fileModels = new SceneClassArray("fileSelectionClasses", + fileSelectionClasses); + sceneClass->addChild(fileModels); +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ModelMedia::restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + reset(); + + if (sceneClass == NULL) { + return; + } + + const SceneClassArray* fileModels = sceneClass->getClassArray("fileSelectionClasses"); + if (fileModels != NULL) { + const int32_t numItems = fileModels->getNumberOfArrayElements(); + for (int32_t i = 0; i < numItems; i++) { + CaretAssertArrayIndex(m_fileSelectionModels, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, i); + m_fileSelectionModels[i]->restoreFromScene(sceneAttributes, + fileModels->getClassAtIndex(i)); + } + } + + m_mediaOverlaySetArray->restoreFromScene(sceneAttributes, + sceneClass->getClass("m_mediaOverlaySetArray")); + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + +/** + * Copy the tab content from the source tab index to the + * destination tab index. + * + * @param sourceTabIndex + * Source from which tab content is copied. + * @param destinationTabIndex + * Destination to which tab content is copied. + */ +void +ModelMedia::copyTabContent(const int32_t sourceTabIndex, + const int32_t destinationTabIndex) +{ + Model::copyTabContent(sourceTabIndex, + destinationTabIndex); + + m_layerOverlaySetArray->copyOverlaySet(sourceTabIndex, + destinationTabIndex); + m_mediaOverlaySetArray->copyOverlaySet(sourceTabIndex, + destinationTabIndex); + CaretAssertArrayIndex(m_fileSelectionModels, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, sourceTabIndex); + CaretAssertArrayIndex(m_fileSelectionModels, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, destinationTabIndex); + m_fileSelectionModels[destinationTabIndex]->setSelectedFile(m_fileSelectionModels[sourceTabIndex]->getSelectedFile()); +} + +/** + * Get the overlay set for the given tab. + * @param tabIndex + * Index of tab. + * @return + * Overlay set at the given tab index. + */ +OverlaySet* +ModelMedia::getOverlaySet(const int tabIndex) +{ + CaretAssertArrayIndex(m_overlaySetArray, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + return m_layerOverlaySetArray->getOverlaySet(tabIndex); +} + +/** + * Get the overlay set for the given tab. + * @param tabIndex + * Index of tab. + * @return + * Overlay set at the given tab index. + */ +const OverlaySet* +ModelMedia::getOverlaySet(const int tabIndex) const +{ + CaretAssertArrayIndex(m_overlaySetArray, + BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, + tabIndex); + return m_layerOverlaySetArray->getOverlaySet(tabIndex); +} diff -Nru connectome-workbench-1.4.2/src/Brain/ModelMedia.h connectome-workbench-1.5.0/src/Brain/ModelMedia.h --- connectome-workbench-1.4.2/src/Brain/ModelMedia.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ModelMedia.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,101 @@ +#ifndef __MODEL_MEDIA_H__ +#define __MODEL_MEDIA_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "BrainConstants.h" +#include "CaretDataFileSelectionModel.h" +#include "EventListenerInterface.h" +#include "MapYokingGroupEnum.h" +#include "Model.h" + +namespace caret { + + class CaretDataFileSelectionModel; + class MediaOverlaySet; + class MediaOverlaySetArray; + class OverlaySetArray; + + /// Controls the display of a chart. + class ModelMedia : public Model, public EventListenerInterface { + + public: + ModelMedia(Brain* brain); + + virtual ~ModelMedia(); + + virtual void initializeOverlays() override; + + void updateModel(); + + virtual AString getNameForGUI(const bool includeStructureFlag) const override; + + virtual AString getNameForBrowserTab() const override; + + virtual OverlaySet* getOverlaySet(const int tabIndex) override; + + virtual const OverlaySet* getOverlaySet(const int tabIndex) const override; + + virtual MediaOverlaySet* getMediaOverlaySet(const int tabIndex) override; + + virtual const MediaOverlaySet* getMediaOverlaySet(const int tabIndex) const override; + + CaretDataFileSelectionModel* getFileSelectionModel(const int32_t tabIndex); + + const CaretDataFileSelectionModel* getFileSelectionModel(const int32_t tabIndex) const; + + virtual void receiveEvent(Event* event) override; + + virtual void copyTabContent(const int32_t sourceTabIndex, + const int32_t destinationTabIndex) override; + + void reset(); + + protected: + virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) override; + + virtual void restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + private: + ModelMedia(const ModelMedia&); + + ModelMedia& operator=(const ModelMedia&); + + void initializeMedia(); + + /** Overlays sets for this model and for each tab */ + OverlaySetArray* m_layerOverlaySetArray; + + /** Overlays sets for this model and for each tab */ + MediaOverlaySetArray* m_mediaOverlaySetArray; + + std::unique_ptr m_fileSelectionModels[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + + std::unique_ptr m_sceneAssistant; + }; + +} // namespace + +#endif // __MODEL_MEDIA_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/ModelSurface.cxx connectome-workbench-1.5.0/src/Brain/ModelSurface.cxx --- connectome-workbench-1.4.2/src/Brain/ModelSurface.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ModelSurface.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -293,6 +293,11 @@ { Model::copyTabContent(sourceTabIndex, destinationTabIndex); + + /* + * Fix WB-879 Overlay not copied when duplicating tab containing surface + */ + getOverlaySet(destinationTabIndex)->copyOverlaySet(getOverlaySet(sourceTabIndex)); } diff -Nru connectome-workbench-1.4.2/src/Brain/ModelTypeEnum.cxx connectome-workbench-1.5.0/src/Brain/ModelTypeEnum.cxx --- connectome-workbench-1.4.2/src/Brain/ModelTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ModelTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -104,6 +104,12 @@ 6, "MODEL_TYPE_WHOLE_BRAIN", "Whole Brain")); + + enumData.push_back(ModelTypeEnum(MODEL_TYPE_MULTI_MEDIA, + 7, + "MODEL_TYPE_MULTI_MEDIA", + "Media")); + } /** diff -Nru connectome-workbench-1.4.2/src/Brain/ModelTypeEnum.h connectome-workbench-1.5.0/src/Brain/ModelTypeEnum.h --- connectome-workbench-1.4.2/src/Brain/ModelTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ModelTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -49,7 +49,9 @@ /** Volume Slices */ MODEL_TYPE_VOLUME_SLICES, /** Whole Brain */ - MODEL_TYPE_WHOLE_BRAIN + MODEL_TYPE_WHOLE_BRAIN, + /** Multi-Media (image/video) */ + MODEL_TYPE_MULTI_MEDIA }; diff -Nru connectome-workbench-1.4.2/src/Brain/MovieRecorder.cxx connectome-workbench-1.5.0/src/Brain/MovieRecorder.cxx --- connectome-workbench-1.4.2/src/Brain/MovieRecorder.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/MovieRecorder.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -620,7 +620,7 @@ TextFile textFile; try { - for (const auto name : m_imageFileNames) { + for (const auto& name : m_imageFileNames) { textFile.addLine("file " + name); } diff -Nru connectome-workbench-1.4.2/src/Brain/Overlay.cxx connectome-workbench-1.5.0/src/Brain/Overlay.cxx --- connectome-workbench-1.4.2/src/Brain/Overlay.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/Overlay.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -270,6 +270,8 @@ m_selectedMapIndex = overlay->m_selectedMapIndex; m_mapYokingGroup = overlay->m_mapYokingGroup; + m_wholeBrainVoxelDrawingMode = overlay->m_wholeBrainVoxelDrawingMode; + *m_colorBar = *overlay->m_colorBar; } diff -Nru connectome-workbench-1.4.2/src/Brain/OverlaySet.cxx connectome-workbench-1.5.0/src/Brain/OverlaySet.cxx --- connectome-workbench-1.4.2/src/Brain/OverlaySet.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/OverlaySet.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -1409,9 +1409,36 @@ selectMapEvent->setEventProcessed(); } - - -// const MapYokingGroupEnum::Enum mapYokingGroup = selectMapEvent->get } } +/** + * @return True if an enabled overlay contains a volume file that is an oblique only volume + */ +bool +OverlaySet::hasObliqueOnlyVolumeSelected() const +{ + const int32_t numberOfOverlays = getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numberOfOverlays; i++) { + Overlay* overlay = const_cast(getOverlay(i)); + if (overlay->isEnabled()) { + CaretMappableDataFile* mapFile; + int32_t mapIndex; + overlay->getSelectionData(mapFile, mapIndex); + if (mapFile != NULL) { + if (mapFile->getDataFileType() == DataFileTypeEnum::VOLUME) { + const VolumeFile* vf = dynamic_cast(mapFile); + CaretAssert(vf); + if ( ! vf->isPlumb()) { + return true; + } + } + } + } + } + + return false; +} + + + diff -Nru connectome-workbench-1.4.2/src/Brain/OverlaySet.h connectome-workbench-1.5.0/src/Brain/OverlaySet.h --- connectome-workbench-1.4.2/src/Brain/OverlaySet.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/OverlaySet.h 2021-02-16 19:46:47.000000000 +0000 @@ -92,6 +92,8 @@ std::vector& labelFilesOut, std::vector& labelMapIndicesOut); + bool hasObliqueOnlyVolumeSelected() const; + void resetOverlayYokingToOff(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItemAnnotation.cxx connectome-workbench-1.5.0/src/Brain/SelectionItemAnnotation.cxx --- connectome-workbench-1.4.2/src/Brain/SelectionItemAnnotation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItemAnnotation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -65,6 +65,7 @@ m_annotationFile = NULL; m_annotation = NULL; m_sizingHandle = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; + m_polyLineCoordinateIndex = -1; } /** @@ -104,6 +105,15 @@ } /** + * @return Index of poly line coordinate + */ +int32_t +SelectionItemAnnotation::getPolyLineCoordinateIndex() const +{ + return m_polyLineCoordinateIndex; +} + +/** * Add a annotation to the selected annotations. * * @param annotationFile @@ -112,17 +122,21 @@ * Annotation that is added. * @param annotationSizingHandle * Sizing handle that is selected. + * @param polyLineCoordinateIndex + * Index of poly line coordinate */ void SelectionItemAnnotation::setAnnotation(AnnotationFile* annotationFile, Annotation* annotation, - const AnnotationSizingHandleTypeEnum::Enum annotationSizingHandle) + const AnnotationSizingHandleTypeEnum::Enum annotationSizingHandle, + const int32_t polyLineCoordinateIndex) { CaretAssert(annotationFile); CaretAssert(annotation); m_annotationFile = annotationFile; m_annotation = annotation; m_sizingHandle = annotationSizingHandle; + m_polyLineCoordinateIndex = polyLineCoordinateIndex; } /** * Get a description of m_ object's content. @@ -133,7 +147,8 @@ { AString text = SelectionItem::toString(); text += ("Annotation type=" + AnnotationTypeEnum::toGuiName(m_annotation->getType()) - + " sizeHandleType=" + AnnotationSizingHandleTypeEnum::toGuiName(m_sizingHandle)); + + " sizeHandleType=" + AnnotationSizingHandleTypeEnum::toGuiName(m_sizingHandle) + + " m_polyLineCoordinateIndex=" + AString::number(m_polyLineCoordinateIndex)); AnnotationText* textAnn = dynamic_cast(m_annotation); if (textAnn != NULL) { diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItemAnnotation.h connectome-workbench-1.5.0/src/Brain/SelectionItemAnnotation.h --- connectome-workbench-1.4.2/src/Brain/SelectionItemAnnotation.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItemAnnotation.h 2021-02-16 19:46:47.000000000 +0000 @@ -49,9 +49,12 @@ AnnotationSizingHandleTypeEnum::Enum getSizingHandle() const; + int32_t getPolyLineCoordinateIndex() const; + void setAnnotation(AnnotationFile* annotationFile, Annotation* annotation, - const AnnotationSizingHandleTypeEnum::Enum annotationSizingHandle); + const AnnotationSizingHandleTypeEnum::Enum annotationSizingHandle, + const int32_t polyLineCoordinateIndex); // ADD_NEW_METHODS_HERE @@ -60,12 +63,14 @@ SelectionItemAnnotation& operator=(const SelectionItemAnnotation&); - AnnotationFile* m_annotationFile; + AnnotationFile* m_annotationFile = NULL; - Annotation* m_annotation; + Annotation* m_annotation = NULL; AnnotationSizingHandleTypeEnum::Enum m_sizingHandle; + int32_t m_polyLineCoordinateIndex = -1; + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItemChartTwoLineLayer.cxx connectome-workbench-1.5.0/src/Brain/SelectionItemChartTwoLineLayer.cxx --- connectome-workbench-1.4.2/src/Brain/SelectionItemChartTwoLineLayer.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItemChartTwoLineLayer.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,178 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SELECTION_ITEM_CHART_TWO_LINE_LAYER_DECLARE__ +#include "SelectionItemChartTwoLineLayer.h" +#undef __SELECTION_ITEM_CHART_TWO_LINE_LAYER_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SelectionItemChartTwoLineLayer + * \brief Selection of chart version two line layer + * \ingroup Brain + */ + +/** + * Constructor. + */ +SelectionItemChartTwoLineLayer::SelectionItemChartTwoLineLayer() +: SelectionItem(SelectionItemDataTypeEnum::CHART_TWO_LINE_LAYER) +{ + m_fileLineLayerChart = NULL; + m_chartTwoCartesianData = NULL; + m_chartOverlay = NULL; + m_lineSegmentIndex = -1; +} + +/** + * Destructor. + */ +SelectionItemChartTwoLineLayer::~SelectionItemChartTwoLineLayer() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SelectionItemChartTwoLineLayer::SelectionItemChartTwoLineLayer(const SelectionItemChartTwoLineLayer& obj) +: SelectionItem(obj) +{ + this->copyHelperSelectionItemChartTwoLineLayer(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SelectionItemChartTwoLineLayer& +SelectionItemChartTwoLineLayer::operator=(const SelectionItemChartTwoLineLayer& obj) +{ + if (this != &obj) { + SelectionItem::operator=(obj); + this->copyHelperSelectionItemChartTwoLineLayer(obj); + } + return *this; +} + +/** + * @return The File line layer chart. + */ +const ChartableTwoFileLineLayerChart* +SelectionItemChartTwoLineLayer::getFileLineLayerChart() const +{ + return m_fileLineLayerChart; +} + +/** + * @return The cartesian data. + */ +const ChartTwoDataCartesian* +SelectionItemChartTwoLineLayer::getChartTwoCartesianData() const +{ + return m_chartTwoCartesianData; +} + +ChartTwoOverlay* +SelectionItemChartTwoLineLayer::getChartTwoOverlay() +{ + return m_chartOverlay; +} + +/** + * @return The line segment index. + */ +int32_t +SelectionItemChartTwoLineLayer::getLineSegmentIndex() const +{ + return m_lineSegmentIndex; +} + +/** + * Set selection. + * + * @param fileLineLayerChart + * The line layer chart. + * @param chartTwoCartesianData + * The cartesian data. + * @param chartOverlay + * The chart overlay + * @param lineSegmentIndex + * Index of the line segment. + */ +void +SelectionItemChartTwoLineLayer::setLineLayerChart(ChartableTwoFileLineLayerChart* fileLineLayerChart, + ChartTwoDataCartesian* chartTwoCartesianData, + ChartTwoOverlay* chartOverlay, + const int32_t lineSegmentIndex) +{ + m_fileLineLayerChart = fileLineLayerChart; + m_chartTwoCartesianData = chartTwoCartesianData; + m_chartOverlay = chartOverlay; + m_lineSegmentIndex = lineSegmentIndex; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SelectionItemChartTwoLineLayer::copyHelperSelectionItemChartTwoLineLayer(const SelectionItemChartTwoLineLayer& obj) +{ + m_fileLineLayerChart = obj.m_fileLineLayerChart; + m_chartTwoCartesianData = obj.m_chartTwoCartesianData; + m_chartOverlay = obj.m_chartOverlay; + m_lineSegmentIndex = obj.m_lineSegmentIndex; +} + +/** + * @return True if the selected chart is valid, else false. + */ +bool +SelectionItemChartTwoLineLayer::isValid() const +{ + return ((m_fileLineLayerChart != NULL) + && (m_chartTwoCartesianData != NULL) + && (m_chartOverlay != NULL) + && (m_lineSegmentIndex >= 0)); +} + +/** + * Reset the selections. + */ +void +SelectionItemChartTwoLineLayer::reset() +{ + m_fileLineLayerChart = NULL; + m_chartTwoCartesianData = NULL; + m_chartOverlay = NULL; + m_lineSegmentIndex = -1; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItemChartTwoLineLayer.h connectome-workbench-1.5.0/src/Brain/SelectionItemChartTwoLineLayer.h --- connectome-workbench-1.4.2/src/Brain/SelectionItemChartTwoLineLayer.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItemChartTwoLineLayer.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,85 @@ +#ifndef __SELECTION_ITEM_CHART_TWO_LINE_LAYER_H__ +#define __SELECTION_ITEM_CHART_TWO_LINE_LAYER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "SelectionItem.h" + + + +namespace caret { + + class ChartTwoDataCartesian; + class ChartTwoOverlay; + class ChartableTwoFileLineLayerChart; + + class SelectionItemChartTwoLineLayer : public SelectionItem { + + public: + SelectionItemChartTwoLineLayer(); + + virtual ~SelectionItemChartTwoLineLayer(); + + SelectionItemChartTwoLineLayer(const SelectionItemChartTwoLineLayer& obj); + + SelectionItemChartTwoLineLayer& operator=(const SelectionItemChartTwoLineLayer& obj); + + virtual bool isValid() const override; + + virtual void reset() override; + + const ChartableTwoFileLineLayerChart* getFileLineLayerChart() const; + + const ChartTwoDataCartesian* getChartTwoCartesianData() const; + + ChartTwoOverlay* getChartTwoOverlay(); + + int32_t getLineSegmentIndex() const; + + void setLineLayerChart(ChartableTwoFileLineLayerChart* fileLineLayerChart, + ChartTwoDataCartesian* chartTwoCartesianData, + ChartTwoOverlay* chartOverlay, + const int32_t lineSegmentIndex); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperSelectionItemChartTwoLineLayer(const SelectionItemChartTwoLineLayer& obj); + + ChartableTwoFileLineLayerChart* m_fileLineLayerChart = NULL; + + ChartTwoDataCartesian* m_chartTwoCartesianData = NULL; + + ChartTwoOverlay* m_chartOverlay = NULL; + + int32_t m_lineSegmentIndex= 0; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SELECTION_ITEM_CHART_TWO_LINE_LAYER_DECLARE__ + // +#endif // __SELECTION_ITEM_CHART_TWO_LINE_LAYER_DECLARE__ + +} // namespace +#endif //__SELECTION_ITEM_CHART_TWO_LINE_LAYER_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.cxx connectome-workbench-1.5.0/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.cxx --- connectome-workbench-1.4.2/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,220 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SELECTION_ITEM_CHART_TWO_LINE_LAYER_VERTICAL_NEAREST_DECLARE__ +#include "SelectionItemChartTwoLineLayerVerticalNearest.h" +#undef __SELECTION_ITEM_CHART_TWO_LINE_LAYER_VERTICAL_NEAREST_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SelectionItemChartTwoLineLayerVerticalNearest + * \brief Selection of chart version two line layer for nearest vertical distance + * \ingroup Brain + */ + +/** + * Constructor. + */ +SelectionItemChartTwoLineLayerVerticalNearest::SelectionItemChartTwoLineLayerVerticalNearest() +: SelectionItem(SelectionItemDataTypeEnum::CHART_TWO_LINE_LAYER_VERTICAL_NEAREST) +{ + m_fileLineLayerChart = NULL; + m_chartTwoCartesianData = NULL; + m_chartOverlay = NULL; + m_distanceToLine = std::numeric_limits::max(); + m_lineSegmentIndex = -1; + m_outsideChartBoundsFlag = false; +} + +/** + * Destructor. + */ +SelectionItemChartTwoLineLayerVerticalNearest::~SelectionItemChartTwoLineLayerVerticalNearest() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SelectionItemChartTwoLineLayerVerticalNearest::SelectionItemChartTwoLineLayerVerticalNearest(const SelectionItemChartTwoLineLayerVerticalNearest& obj) +: SelectionItem(obj) +{ + this->copyHelperSelectionItemChartTwoLineLayerVerticalNearest(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SelectionItemChartTwoLineLayerVerticalNearest& +SelectionItemChartTwoLineLayerVerticalNearest::operator=(const SelectionItemChartTwoLineLayerVerticalNearest& obj) +{ + if (this != &obj) { + SelectionItem::operator=(obj); + this->copyHelperSelectionItemChartTwoLineLayerVerticalNearest(obj); + } + return *this; +} + +/** + * @return The File line layer chart. + */ +const ChartableTwoFileLineLayerChart* +SelectionItemChartTwoLineLayerVerticalNearest::getFileLineLayerChart() const +{ + return m_fileLineLayerChart; +} + +/** + * @return The cartesian data. + */ +const ChartTwoDataCartesian* +SelectionItemChartTwoLineLayerVerticalNearest::getChartTwoCartesianData() const +{ + return m_chartTwoCartesianData; +} + +ChartTwoOverlay* +SelectionItemChartTwoLineLayerVerticalNearest::getChartTwoOverlay() +{ + return m_chartOverlay; +} + +/** + * @return Distance to line segment + */ +float +SelectionItemChartTwoLineLayerVerticalNearest::getDistanceToLine() const +{ + return m_distanceToLine; +} + +/** + * @return The line segment index. + */ +int32_t +SelectionItemChartTwoLineLayerVerticalNearest::getLineSegmentIndex() const +{ + return m_lineSegmentIndex; +} + +/** + * Set selection. Replaces current if distance is less than current distance. + * + * @param fileLineLayerChart + * The line layer chart. + * @param chartTwoCartesianData + * The cartesian data. + * @param chartOverlay + * The chart overlay + * @param distanceToLine + * Distance to the line + * @param lineSegmentIndex + * Index of the line segment. + */ +void +SelectionItemChartTwoLineLayerVerticalNearest::setLineLayerChart(ChartableTwoFileLineLayerChart* fileLineLayerChart, + ChartTwoDataCartesian* chartTwoCartesianData, + ChartTwoOverlay* chartOverlay, + const float distanceToLine, + const int32_t lineSegmentIndex) +{ + if (distanceToLine < m_distanceToLine) { + m_fileLineLayerChart = fileLineLayerChart; + m_chartTwoCartesianData = chartTwoCartesianData; + m_chartOverlay = chartOverlay; + m_distanceToLine = distanceToLine; + m_lineSegmentIndex = lineSegmentIndex; + } +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SelectionItemChartTwoLineLayerVerticalNearest::copyHelperSelectionItemChartTwoLineLayerVerticalNearest(const SelectionItemChartTwoLineLayerVerticalNearest& obj) +{ + m_fileLineLayerChart = obj.m_fileLineLayerChart; + m_chartTwoCartesianData = obj.m_chartTwoCartesianData; + m_chartOverlay = obj.m_chartOverlay; + m_distanceToLine = obj.m_distanceToLine; + m_lineSegmentIndex = obj.m_lineSegmentIndex; + m_outsideChartBoundsFlag = obj.m_outsideChartBoundsFlag; +} + +/** + * @return True if the selected chart is valid, else false. + */ +bool +SelectionItemChartTwoLineLayerVerticalNearest::isValid() const +{ + return ((m_fileLineLayerChart != NULL) + && (m_chartTwoCartesianData != NULL) + && (m_chartOverlay != NULL) + && (m_lineSegmentIndex >= 0)); +} + +/** + * Reset the selections. + */ +void +SelectionItemChartTwoLineLayerVerticalNearest::reset() +{ + m_fileLineLayerChart = NULL; + m_chartTwoCartesianData = NULL; + m_chartOverlay = NULL; + m_distanceToLine = std::numeric_limits::max(); + m_lineSegmentIndex = -1; + m_outsideChartBoundsFlag = false; +} + +/** + * @return True if mouse was in the chart viewport but outside the bounds of the chart coordinates. + * Note isValid() will return false since mouse was not in chart bounds. + */ +bool +SelectionItemChartTwoLineLayerVerticalNearest::isOutsideChartBounds() const +{ + return m_outsideChartBoundsFlag; +} + +/** + * Set mouse was in the chart viewport but outside the bounds of the chart coordinates. + * @param status + * New status + */ +void +SelectionItemChartTwoLineLayerVerticalNearest::setOutsideChartBound(const bool status) +{ + m_outsideChartBoundsFlag = status; +} + diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.h connectome-workbench-1.5.0/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.h --- connectome-workbench-1.4.2/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,96 @@ +#ifndef __SELECTION_ITEM_CHART_TWO_LINE_LAYER_VERTICAL_NEAREST_H__ +#define __SELECTION_ITEM_CHART_TWO_LINE_LAYER_VERTICAL_NEAREST_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "SelectionItem.h" + + + +namespace caret { + + class ChartTwoDataCartesian; + class ChartTwoOverlay; + class ChartableTwoFileLineLayerChart; + + class SelectionItemChartTwoLineLayerVerticalNearest : public SelectionItem { + + public: + SelectionItemChartTwoLineLayerVerticalNearest(); + + virtual ~SelectionItemChartTwoLineLayerVerticalNearest(); + + SelectionItemChartTwoLineLayerVerticalNearest(const SelectionItemChartTwoLineLayerVerticalNearest& obj); + + SelectionItemChartTwoLineLayerVerticalNearest& operator=(const SelectionItemChartTwoLineLayerVerticalNearest& obj); + + virtual bool isValid() const override; + + virtual void reset() override; + + const ChartableTwoFileLineLayerChart* getFileLineLayerChart() const; + + const ChartTwoDataCartesian* getChartTwoCartesianData() const; + + ChartTwoOverlay* getChartTwoOverlay(); + + int32_t getLineSegmentIndex() const; + + float getDistanceToLine() const; + + void setLineLayerChart(ChartableTwoFileLineLayerChart* fileLineLayerChart, + ChartTwoDataCartesian* chartTwoCartesianData, + ChartTwoOverlay* chartOverlay, + const float distanceToLine, + const int32_t lineSegmentIndex); + + bool isOutsideChartBounds() const; + + void setOutsideChartBound(const bool status); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperSelectionItemChartTwoLineLayerVerticalNearest(const SelectionItemChartTwoLineLayerVerticalNearest& obj); + + ChartableTwoFileLineLayerChart* m_fileLineLayerChart = NULL; + + ChartTwoDataCartesian* m_chartTwoCartesianData = NULL; + + ChartTwoOverlay* m_chartOverlay = NULL; + + float m_distanceToLine = 0; + + int32_t m_lineSegmentIndex = 0; + + bool m_outsideChartBoundsFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SELECTION_ITEM_CHART_TWO_LINE_LAYER_VERTICAL_NEAREST_DECLARE__ + // +#endif // __SELECTION_ITEM_CHART_TWO_LINE_LAYER_VERTICAL_NEAREST_DECLARE__ + +} // namespace +#endif //__SELECTION_ITEM_CHART_TWO_LINE_LAYER_VERTICAL_NEAREST_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItem.cxx connectome-workbench-1.5.0/src/Brain/SelectionItem.cxx --- connectome-workbench-1.4.2/src/Brain/SelectionItem.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItem.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -289,7 +289,8 @@ * Multiple annotations that are on top of each other may be drawn at the same * depth so using "<=" should select the one that appears to be "on top" to the user. */ - if (m_itemDataType == SelectionItemDataTypeEnum::ANNOTATION) { + if ((m_itemDataType == SelectionItemDataTypeEnum::ANNOTATION) + || (m_itemDataType == SelectionItemDataTypeEnum::CHART_TWO_LINE_LAYER)) { if (otherScreenDepth <= m_screenDepth) { return true; } diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItemDataTypeEnum.cxx connectome-workbench-1.5.0/src/Brain/SelectionItemDataTypeEnum.cxx --- connectome-workbench-1.4.2/src/Brain/SelectionItemDataTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItemDataTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -103,6 +103,14 @@ "CHART_TWO_HISTOGRAM", "Histogram Chart Two")); + enumData.push_back(SelectionItemDataTypeEnum(CHART_TWO_LINE_LAYER, + "CHART_TWO_LINE_LAYER", + "Line-Layer Chart Two")); + + enumData.push_back(SelectionItemDataTypeEnum(CHART_TWO_LINE_LAYER_VERTICAL_NEAREST, + "CHART_TWO_LINE_LAYER_VERTICAL_NEAREST", + "Line-Layer Vertical Nearest Chart Two")); + enumData.push_back(SelectionItemDataTypeEnum(CHART_TWO_LINE_SERIES, "CHART_TWO_LINE_SERIES", "Line-Series Chart Two")); diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionItemDataTypeEnum.h connectome-workbench-1.5.0/src/Brain/SelectionItemDataTypeEnum.h --- connectome-workbench-1.4.2/src/Brain/SelectionItemDataTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionItemDataTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -61,6 +61,10 @@ CHART_TWO_HISTOGRAM, /** Version Two Chart Label */ CHART_TWO_LABEL, + /** Version Two Chart Line-Layer */ + CHART_TWO_LINE_LAYER, + /** Version Two Chart Line-Layer Nearest in Y */ + CHART_TWO_LINE_LAYER_VERTICAL_NEAREST, /** Version Two Chart Line-Series */ CHART_TWO_LINE_SERIES, /** Version Two Chart Matrix */ diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionManager.cxx connectome-workbench-1.5.0/src/Brain/SelectionManager.cxx --- connectome-workbench-1.4.2/src/Brain/SelectionManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -31,6 +31,8 @@ #include "SelectionManager.h" #undef __SELECTION_MANAGER_DECLARE__ +#include "IdentificationFormattedTextGenerator.h" +#include "IdentificationSimpleTextGenerator.h" #include "SelectionItemAnnotation.h" #include "SelectionItemBorderSurface.h" #include "SelectionItemChartDataSeries.h" @@ -39,6 +41,8 @@ #include "SelectionItemChartTimeSeries.h" #include "SelectionItemChartTwoHistogram.h" #include "SelectionItemChartTwoLabel.h" +#include "SelectionItemChartTwoLineLayer.h" +#include "SelectionItemChartTwoLineLayerVerticalNearest.h" #include "SelectionItemChartTwoLineSeries.h" #include "SelectionItemChartTwoMatrix.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" @@ -52,7 +56,6 @@ #include "SelectionItemVoxel.h" #include "SelectionItemVoxelEditing.h" #include "SelectionItemVoxelIdentificationSymbol.h" -#include "IdentificationTextGenerator.h" #include "Surface.h" using namespace caret; @@ -78,6 +81,8 @@ m_chartMatrixIdentification = new SelectionItemChartMatrix(); m_chartTwoHistogramIdentification = std::unique_ptr(new SelectionItemChartTwoHistogram()); m_chartTwoLabelIdentification = std::unique_ptr(new SelectionItemChartTwoLabel()); + m_chartTwoLineLayerIdentification = std::unique_ptr(new SelectionItemChartTwoLineLayer()); + m_chartTwoLineLayerVerticalNearestIdentification = std::unique_ptr(new SelectionItemChartTwoLineLayerVerticalNearest()); m_chartTwoLineSeriesIdentification = std::unique_ptr(new SelectionItemChartTwoLineSeries()); m_chartTwoMatrixIdentification = std::unique_ptr(new SelectionItemChartTwoMatrix()); @@ -102,6 +107,8 @@ m_allSelectionItems.push_back(m_chartTimeSeriesIdentification); m_allSelectionItems.push_back(m_chartTwoHistogramIdentification.get()); m_allSelectionItems.push_back(m_chartTwoLabelIdentification.get()); + m_allSelectionItems.push_back(m_chartTwoLineLayerIdentification.get()); + m_allSelectionItems.push_back(m_chartTwoLineLayerVerticalNearestIdentification.get()); m_allSelectionItems.push_back(m_chartTwoLineSeriesIdentification.get()); m_allSelectionItems.push_back(m_chartTwoMatrixIdentification.get()); m_allSelectionItems.push_back(m_ciftiConnectivityMatrixRowColumnIdentfication); @@ -126,7 +133,8 @@ m_volumeSelectedItems.push_back(m_voxelEditingIdentification); m_volumeSelectedItems.push_back(m_volumeFocusIdentification); - m_idTextGenerator = new IdentificationTextGenerator(); + m_idTextGenerator = new IdentificationSimpleTextGenerator(); + m_idFormattedTextGenerator.reset(new IdentificationFormattedTextGenerator()); m_lastSelectedItem = NULL; @@ -212,17 +220,20 @@ void SelectionManager::filterSelections(const bool applySelectionBackgroundFiltering) { - AString logText; - for (std::vector::iterator iter = m_allSelectionItems.begin(); - iter != m_allSelectionItems.end(); - iter++) { - SelectionItem* item = *iter; - if (item->isValid()) { - logText += ("\n" + item->toString() + "\n"); + const bool debugFlag(false); + if (debugFlag) { + AString logText; + for (std::vector::iterator iter = m_allSelectionItems.begin(); + iter != m_allSelectionItems.end(); + iter++) { + SelectionItem* item = *iter; + if (item->isValid()) { + logText += ("\n" + item->toString() + "\n"); + } } + std::cout << "Selected Items BEFORE filtering: " << logText << std::endl; } - CaretLogFine("Selected Items BEFORE filtering: " + logText); - + SelectionItemSurfaceTriangle* triangleID = m_surfaceTriangleIdentification; SelectionItemSurfaceNode* nodeID = m_surfaceNodeIdentification; @@ -302,16 +313,18 @@ clearDistantSelections(); } - logText = ""; - for (std::vector::iterator iter = m_allSelectionItems.begin(); - iter != m_allSelectionItems.end(); - iter++) { - SelectionItem* item = *iter; - if (item->isValid()) { - logText += ("\n" + item->toString() + "\n"); + if (debugFlag) { + AString logText; + for (std::vector::iterator iter = m_allSelectionItems.begin(); + iter != m_allSelectionItems.end(); + iter++) { + SelectionItem* item = *iter; + if (item->isValid()) { + logText += ("\n" + item->toString() + "\n"); + } } + std::cout << "Selected Items BEFORE filtering: " << logText << std::endl; } - CaretLogFine("Selected Items AFTER filtering: " + logText); } /** @@ -438,12 +451,12 @@ } /** - * Get text describing the current identification data. + * Get simple text describing the current identification data. * @param brain * Brain containing the data. */ AString -SelectionManager::getIdentificationText(const Brain* brain) const +SelectionManager::getSimpleIdentificationText(const Brain* brain) const { CaretAssert(brain); const AString text = m_idTextGenerator->createIdentificationText(this, @@ -452,6 +465,24 @@ } /** + * Get formatted text describing the current identification data. + * @param brain + * Brain containing the data. + * @param tabIndex + * Index of tab where identication took place + */ +AString +SelectionManager::getFormattedIdentificationText(const Brain* brain, + const int32_t tabIndex) const +{ + CaretAssert(brain); + const AString text = m_idFormattedTextGenerator->createIdentificationText(this, + brain, + tabIndex); + return text; +} + +/** * Reset all identification. */ void @@ -754,6 +785,42 @@ } /** + * @return Identification for chart two line layer identification + */ +SelectionItemChartTwoLineLayer* +SelectionManager::getChartTwoLineLayerIdentification() +{ + return m_chartTwoLineLayerIdentification.get(); +} + +/** + * @return Identification for chart two line layer identification + */ +const SelectionItemChartTwoLineLayer* +SelectionManager::getChartTwoLineLayerIdentification() const +{ + return m_chartTwoLineLayerIdentification.get(); +} + +/** + * @return Identification for chart two line layer nearest vertical identification + */ +SelectionItemChartTwoLineLayerVerticalNearest* +SelectionManager::getChartTwoLineLayerVerticalNearestIdentification() +{ + return m_chartTwoLineLayerVerticalNearestIdentification.get(); +} + +/** + * @return Identification for chart two line layer nearest vertical identification + */ +const SelectionItemChartTwoLineLayerVerticalNearest* +SelectionManager::getChartTwoLineLayerVerticalNearestIdentification() const +{ + return m_chartTwoLineLayerVerticalNearestIdentification.get(); +} + +/** * @return Identification for chart two line series identification */ SelectionItemChartTwoLineSeries* diff -Nru connectome-workbench-1.4.2/src/Brain/SelectionManager.h connectome-workbench-1.5.0/src/Brain/SelectionManager.h --- connectome-workbench-1.4.2/src/Brain/SelectionManager.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SelectionManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -39,6 +39,8 @@ class SelectionItemChartTimeSeries; class SelectionItemChartTwoHistogram; class SelectionItemChartTwoLabel; + class SelectionItemChartTwoLineLayer; + class SelectionItemChartTwoLineLayerVerticalNearest; class SelectionItemChartTwoLineSeries; class SelectionItemChartTwoMatrix; class SelectionItemCiftiConnectivityMatrixRowColumn; @@ -52,7 +54,8 @@ class SelectionItemVoxel; class SelectionItemVoxelEditing; class SelectionItemVoxelIdentificationSymbol; - class IdentificationTextGenerator; + class IdentificationFormattedTextGenerator; + class IdentificationSimpleTextGenerator; class Surface; class SelectionManager : public CaretObject, public EventListenerInterface { @@ -138,6 +141,14 @@ const SelectionItemChartTwoHistogram* getChartTwoHistogramIdentification() const; + SelectionItemChartTwoLineLayer* getChartTwoLineLayerIdentification(); + + const SelectionItemChartTwoLineLayer* getChartTwoLineLayerIdentification() const; + + SelectionItemChartTwoLineLayerVerticalNearest* getChartTwoLineLayerVerticalNearestIdentification(); + + const SelectionItemChartTwoLineLayerVerticalNearest* getChartTwoLineLayerVerticalNearestIdentification() const; + SelectionItemChartTwoLineSeries* getChartTwoLineSeriesIdentification(); const SelectionItemChartTwoLineSeries* getChartTwoLineSeriesIdentification() const; @@ -150,7 +161,10 @@ const SelectionItemChartTwoMatrix* getChartTwoMatrixIdentification() const; - AString getIdentificationText(const Brain* brain) const; + AString getSimpleIdentificationText(const Brain* brain) const; + + AString getFormattedIdentificationText(const Brain* brain, + const int32_t tabIndex) const; void filterSelections(const bool applySelectionBackgroundFiltering); @@ -203,6 +217,10 @@ std::unique_ptr m_chartTwoLineSeriesIdentification; + std::unique_ptr m_chartTwoLineLayerIdentification; + + std::unique_ptr m_chartTwoLineLayerVerticalNearestIdentification; + std::unique_ptr m_chartTwoLabelIdentification; std::unique_ptr m_chartTwoMatrixIdentification; @@ -223,7 +241,9 @@ SelectionItemSurfaceTriangle* m_surfaceTriangleIdentification; - IdentificationTextGenerator* m_idTextGenerator; + IdentificationSimpleTextGenerator* m_idTextGenerator; + + std::unique_ptr m_idFormattedTextGenerator; SelectionItemVoxel* m_voxelIdentification; diff -Nru connectome-workbench-1.4.2/src/Brain/SessionManager.cxx connectome-workbench-1.5.0/src/Brain/SessionManager.cxx --- connectome-workbench-1.4.2/src/Brain/SessionManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SessionManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,8 @@ #include "SessionManager.h" #undef __SESSION_MANAGER_DECLARE__ +#include "AnnotationBrowserTab.h" +#include "AnnotationManager.h" #include "ApplicationInformation.h" #include "BackgroundAndForegroundColorsSceneHelper.h" #include "Brain.h" @@ -34,24 +36,32 @@ #include "CaretLogger.h" #include "CaretPreferenceDataValue.h" #include "CaretPreferences.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlaySet.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryManager.h" #include "DataToolTipsManager.h" #include "ElapsedTimer.h" #include "EventManager.h" +#include "EventBrowserTabClose.h" #include "EventBrowserTabDelete.h" #include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" #include "EventBrowserTabIndicesGetAll.h" #include "EventBrowserTabNew.h" #include "EventBrowserTabNewClone.h" +#include "EventBrowserTabReopenAvailable.h" +#include "EventBrowserTabReopenClosed.h" #include "EventBrowserWindowContent.h" #include "EventCaretPreferencesGet.h" +#include "EventChartTwoCartesianAxisDisplayGroup.h" +#include "EventChartTwoCartesianOrientedAxesYoking.h" #include "EventModelAdd.h" #include "EventModelDelete.h" #include "EventModelGetAll.h" #include "EventModelGetAllDisplayed.h" #include "EventProgressUpdate.h" +#include "EventRecentFilesSystemAccessMode.h" #include "EventSpacerTabGet.h" #include "ImageCaptureSettings.h" #include "LogManager.h" @@ -84,27 +94,41 @@ m_ciftiFiberTrajectoryManager = new CiftiFiberTrajectoryManager(); m_dataToolTipsManager.reset(new DataToolTipsManager(m_caretPreferences->isShowDataToolTipsEnabled())); - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - m_browserTabs[i] = NULL; - } + m_browserTabs.fill(NULL); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { CaretAssertStdArrayIndex(m_browserWindowContent, i); m_browserWindowContent[i] = new BrowserWindowContent(i); } + /* + * Axes for display group yoking to chart axes + */ + for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { + ChartTwoOverlaySet* nullChartOverlaySet(NULL); + ChartTwoCartesianAxis* axis = new ChartTwoCartesianAxis(nullChartOverlaySet, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM); + m_chartingAxisDisplayGroups.push_back(std::unique_ptr(axis)); + } + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_CLOSE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_AVAILBLE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_CLOSED); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_PREFERENCES_GET); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_ADD); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_DELETE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_GET_ALL_DISPLAYED); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SPACER_TAB_GET); Brain* brain = new Brain(m_caretPreferences); @@ -122,12 +146,7 @@ * as browser tabs are closed. However, command line does not issue * commands to delete tabs so this code will do so. */ - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - if (m_browserTabs[i] != NULL) { - delete m_browserTabs[i]; - m_browserTabs[i] = NULL; - } - } + deleteAllBrowserTabs(); clearSpacerTabs(); @@ -295,6 +314,25 @@ } /** + * Get the maximum stack order from all tabs + */ +int32_t +SessionManager::getMaximumManualTabStackOrder() const +{ + int32_t maxStackOrder(0); + + for (const auto bt : m_browserTabs) { + if (bt != NULL) { + maxStackOrder = std::max(bt->getManualLayoutBrowserTabAnnotation()->getStackingOrder(), + maxStackOrder); + } + } + + return maxStackOrder; +} + + +/** * Receive events. * * @param event @@ -310,18 +348,11 @@ tabEvent->setEventProcessed(); - bool createdTab = false; - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - if (m_browserTabs[i] == NULL) { - BrowserTabContent* tab = new BrowserTabContent(i); - tab->update(m_models); - m_browserTabs[i] = tab; - tabEvent->setBrowserTab(tab); - createdTab = true; - break; - } + BrowserTabContent* newTab = createNewBrowserTab(); + if (newTab != NULL) { + tabEvent->setBrowserTab(newTab); } - if ( ! createdTab) { + else { tabEvent->setErrorMessage("Workbench is unable to create tabs, all tabs are in use."); } } @@ -338,42 +369,40 @@ return; } - int32_t newTabIndex(-1); - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - if (m_browserTabs[i] == NULL) { - newTabIndex = i; - break; - } - } - if (newTabIndex >= 0) { - CaretAssertArrayIndex(m_browserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, cloneTabIndex); - CaretAssertArrayIndex(m_browserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, newTabIndex); - m_browserTabs[newTabIndex] = new BrowserTabContent(newTabIndex); - m_browserTabs[newTabIndex]->update(m_models); - m_browserTabs[newTabIndex]->cloneBrowserTabContent(m_browserTabs[cloneTabIndex]); - cloneTabEvent->setNewBrowserTab(m_browserTabs[newTabIndex], - newTabIndex); + BrowserTabContent* newTab = createNewBrowserTab(); + if (newTab != NULL) { + CaretAssertStdArrayIndex(m_browserTabs, cloneTabIndex); + newTab->cloneBrowserTabContent(m_browserTabs[cloneTabIndex]); + cloneTabEvent->setNewBrowserTab(newTab, + newTab->getTabNumber()); } else { cloneTabEvent->setErrorMessage("Workbench is unable to create tabs, all tabs are in use."); } } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_CLOSE) { + EventBrowserTabClose* closeTabEvent = + dynamic_cast(event); + CaretAssert(closeTabEvent); + + AString errorMessage; + if ( ! closeBrowserTab(closeTabEvent, + errorMessage)) { + closeTabEvent->setErrorMessage(errorMessage); + } + closeTabEvent->setEventProcessed(); + } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE) { EventBrowserTabDelete* tabEvent = dynamic_cast(event); CaretAssert(tabEvent); - - BrowserTabContent* tab = tabEvent->getBrowserTab(); - - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - if (m_browserTabs[i] == tab) { - delete m_browserTabs[i]; - m_browserTabs[i] = NULL; - tabEvent->setEventProcessed(); - break; - } + AString errorMessage; + if ( ! deleteBrowserTab(tabEvent, + errorMessage)) { + tabEvent->setErrorMessage(errorMessage); } + tabEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_GET) { EventBrowserTabGet* tabEvent = @@ -383,8 +412,7 @@ tabEvent->setEventProcessed(); const int32_t tabNumber = tabEvent->getTabNumber(); - CaretAssertArrayIndex(m_browserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabNumber); - + CaretAssertStdArrayIndex(m_browserTabs, tabNumber); tabEvent->setBrowserTab(m_browserTabs[tabNumber]); } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL) { @@ -394,10 +422,9 @@ tabEvent->setEventProcessed(); - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - if (m_browserTabs[i] != NULL) { - tabEvent->addBrowserTab(m_browserTabs[i]); - } + std::vector activeTabs = getActiveBrowserTabs(); + for (auto bt : activeTabs) { + tabEvent->addBrowserTab(bt); } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL) { @@ -407,11 +434,36 @@ tabEvent->setEventProcessed(); - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - if (m_browserTabs[i] != NULL) { - tabEvent->addBrowserTabIndex(m_browserTabs[i]->getTabNumber()); - } + std::vector activeTabs = getActiveBrowserTabs(); + for (auto bt : activeTabs) { + tabEvent->addBrowserTabIndex(bt->getTabNumber()); + } + } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_AVAILBLE) { + EventBrowserTabReopenAvailable* tabEvent = dynamic_cast(event); + CaretAssert(tabEvent); + if ( ! m_closedBrowserTabs.empty()) { + BrowserTabContent* tab = m_closedBrowserTabs.front(); + CaretAssert(tab); + tabEvent->setTabIndexAndName(tab->getTabNumber(), + tab->getTabName()); + } + tabEvent->setEventProcessed(); + } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_CLOSED) { + EventBrowserTabReopenClosed* lastTabEvent = dynamic_cast(event); + CaretAssert(lastTabEvent); + + AString errorMessage; + BrowserTabContent* tab = reopenLastClosedTab(errorMessage); + if (tab != NULL) { + lastTabEvent->setBrowserTabContent(tab, + tab->getTabNumber()); + } + else { + lastTabEvent->setErrorMessage(errorMessage); } + lastTabEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT) { EventBrowserWindowContent* windowEvent = @@ -446,6 +498,37 @@ preferencesEvent->setCaretPreferences(m_caretPreferences); preferencesEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING) { + EventChartTwoCartesianOrientedAxesYoking* axesEvent = dynamic_cast(event); + CaretAssert(axesEvent); + + switch (axesEvent->getMode()) { + case EventChartTwoCartesianOrientedAxesYoking::Mode::GET_MINIMUM_AND_MAXIMUM_VALUES: + break; + case EventChartTwoCartesianOrientedAxesYoking::Mode::GET_YOKED_AXES: + { + const ChartTwoAxisScaleRangeModeEnum::Enum rangeMode = axesEvent->getYokingRangeMode(); + if (ChartTwoAxisScaleRangeModeEnum::isYokingRangeMode(rangeMode)) { + std::vector activeTabs = getActiveBrowserTabs(); + for (auto bt : activeTabs) { + std::vector tabAxes = bt->getYokedAxes(axesEvent->getAxisOrientation(), + axesEvent->getYokingRangeMode()); + for (auto axes : tabAxes) { + axesEvent->addYokedAxes(axes); + } + } + + axesEvent->setEventProcessed(); + } + } + break; + case EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MINIMUM_VALUE: + case EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MAXIMUM_VALUE: + case EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MINIMUM_AND_MAXIMUM_VALUES: + /* Nothing */ + break; + } + } else if (event->getEventType() == EventTypeEnum::EVENT_MODEL_ADD) { EventModelAdd* addModelsEvent = dynamic_cast(event); @@ -492,14 +575,19 @@ dynamic_cast(event); CaretAssert(getDisplayedModelsEvent); - for (const auto tab : m_browserTabs) { - if (tab != NULL) { - getDisplayedModelsEvent->addModel(tab->getModelForDisplay()); - } + std::vector activeTabs = getActiveBrowserTabs(); + for (auto bt : activeTabs) { + getDisplayedModelsEvent->addModel(bt->getModelForDisplay()); } getDisplayedModelsEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE) { + EventRecentFilesSystemAccessMode* modeEvent = dynamic_cast(event); + CaretAssert(modeEvent); + modeEvent->setMode(m_caretPreferences->getRecentFilesSystemAccessMode()); + modeEvent->setEventProcessed(); + } else if (event->getEventType() == EventTypeEnum::EVENT_SPACER_TAB_GET) { EventSpacerTabGet* spacerTabEvent = dynamic_cast(event); CaretAssert(spacerTabEvent); @@ -529,6 +617,24 @@ spacerTabEvent->setSpacerTabContent(spacerTabContent); spacerTabEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP) { + EventChartTwoCartesianAxisDisplayGroup* dgEvent = dynamic_cast(event); + CaretAssert(dgEvent); + + switch (dgEvent->getMode()) { + case EventChartTwoCartesianAxisDisplayGroup::Mode::GET_ALL_YOKED_AXES: + break; + case EventChartTwoCartesianAxisDisplayGroup::Mode::GET_DISPLAY_GROUP_AXIS: + { + const DisplayGroupEnum::Enum displayGroup = dgEvent->getDisplayGroup(); + const int32_t displayGroupIndex = DisplayGroupEnum::toIntegerCode(displayGroup); + CaretAssertVectorIndex(m_chartingAxisDisplayGroups, displayGroupIndex); + dgEvent->setAxis(m_chartingAxisDisplayGroups[displayGroupIndex].get()); + dgEvent->setEventProcessed(); + } + break; + } + } } /** @@ -537,10 +643,17 @@ void SessionManager::updateBrowserTabContents() { - for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - if (m_browserTabs[i] != NULL) { - m_browserTabs[i]->update(m_models); - } + std::vector activeTabs = getActiveBrowserTabs(); + for (auto bt : activeTabs) { + bt->update(m_models); + } + + /* + * Update the closed tabs so that they do not + * reference and data that is no longer valid + */ + for (auto bt : m_closedBrowserTabs) { + bt->update(m_models); } } @@ -635,15 +748,13 @@ * Save browser tabs */ std::vector browserTabSceneClasses; - for (int32_t tabIndex = 0; tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; tabIndex++) { + std::vector validTabs = getActiveBrowserTabs(); + for (auto tab : validTabs) { if (std::find(tabIndicesForScene.begin(), tabIndicesForScene.end(), - tabIndex) != tabIndicesForScene.end()) { - BrowserTabContent* btc = m_browserTabs[tabIndex]; - if (btc != NULL) { - browserTabSceneClasses.push_back(btc->saveToScene(sceneAttributes, - "m_browserTabs")); - } + tab->getTabNumber()) != tabIndicesForScene.end()) { + browserTabSceneClasses.push_back(tab->saveToScene(sceneAttributes, + "m_browserTabs")); } } sceneClass->addChild(new SceneClassArray("m_browserTabs", @@ -679,6 +790,21 @@ sceneClass->addClass(savePreferencesToScene(sceneAttributes, "ScenePreferenceDataValues")); + /* + * Save axis display groups + */ + std::vector axisSceneClasses; + const int32_t numAxis = static_cast(m_chartingAxisDisplayGroups.size()); + for (int32_t i = 0; i < numAxis; i++) { + CaretAssertVectorIndex(m_chartingAxisDisplayGroups, i); + SceneClass* sc = m_chartingAxisDisplayGroups[i]->saveToScene(sceneAttributes, + "m_chartingAxisDisplayGroups" + AString::number(i)); + axisSceneClasses.push_back(sc); + } + SceneClassArray* axisArray = new SceneClassArray("m_chartingAxisDisplayGroups", + axisSceneClasses); + sceneClass->addChild(axisArray); + return sceneClass; } @@ -704,6 +830,8 @@ m_caretPreferences->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); m_caretPreferences->invalidateSceneDataValues(); + m_sceneRestoredWithChartOldFlag = false; + if (sceneClass == NULL) { return; } @@ -736,6 +864,23 @@ } + /* + * Restore axis display groups + */ + for (auto& a : m_chartingAxisDisplayGroups) { + a->reset(); + } + + const SceneClassArray* axisArray = sceneClass->getClassArray("m_chartingAxisDisplayGroups"); + if (axisArray != NULL) { + const int32_t numElem = std::min(static_cast(m_chartingAxisDisplayGroups.size()), + axisArray->getNumberOfArrayElements()); + for (int32_t i = 0; i < numElem; i++) { + CaretAssertVectorIndex(m_chartingAxisDisplayGroups, i); + m_chartingAxisDisplayGroups[i]->restoreFromScene(sceneAttributes, axisArray->getClassAtIndex(i)); + } + } + { /* * Restore map yoking groups @@ -883,12 +1028,7 @@ /* * Remove all tabs */ - for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { - if (m_browserTabs[iTab] != NULL) { - delete m_browserTabs[iTab]; - m_browserTabs[iTab] = NULL; - } - } + deleteAllBrowserTabs(); /* * Remove all spacer tabs @@ -911,7 +1051,28 @@ browserTabClass); const int32_t tabIndex = tab->getTabNumber(); CaretAssert(tabIndex >= 0); + CaretAssertStdArrayIndex(m_browserTabs, tabIndex); m_browserTabs[tabIndex] = tab; + + switch (tab->getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + m_sceneRestoredWithChartOldFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + break; + } } /* @@ -1199,3 +1360,265 @@ return m_movieRecorder.get(); } +/** + * @return Active browser tabs in a vector + */ +std::vector +SessionManager::getActiveBrowserTabs() +{ + std::vector tabs; + + for (auto bt: m_browserTabs) { + if (bt != NULL) { + tabs.push_back(bt); + } + } + + return tabs; +} + +/** + * @return a new browser tab or NULL if a tab cannot be created + */ +BrowserTabContent* +SessionManager::createNewBrowserTab() +{ + BrowserTabContent* newTab(NULL); + + /* + * Try to find an unused tab index + */ + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + CaretAssertStdArrayIndex(m_browserTabs, i); + if (m_browserTabs[i] == NULL) { + if ( ! isTabInClosedBrowserTabs(i)) { + newTab = new BrowserTabContent(i); + break; + } + } + } + + /* + * If no available tabs, remove the oldest tab in the closed tabs, delete the + * closed tab, and use its index for a new tab + */ + if (newTab == NULL) { + if ( ! m_closedBrowserTabs.empty()) { + BrowserTabContent* btc = m_closedBrowserTabs.back(); + CaretAssert(btc); + const int32_t tabIndex = btc->getTabNumber(); + if (m_browserTabs[tabIndex] == NULL) { + m_closedBrowserTabs.pop_back(); + delete btc; + btc = NULL; + + newTab = new BrowserTabContent(tabIndex); + } + else { + CaretLogSevere("Program Error: Trying to delete closed tab with index=" + + AString::number(tabIndex) + + " but there is a currently open tab at that index"); + } + } + } + + if (newTab != NULL) { + const int32_t tabIndex = newTab->getTabNumber(); + m_browserTabs[tabIndex] = newTab; + newTab->update(m_models); + newTab->getManualLayoutBrowserTabAnnotation()->setStackingOrder(getMaximumManualTabStackOrder() + 1); + + std::vector tabs = getActiveBrowserTabs(); + std::vector anns; + for (auto t : tabs) { + AnnotationBrowserTab* abt = t->getManualLayoutBrowserTabAnnotation(); + CaretAssert(abt); + anns.push_back(abt); + } + + CaretAssert(m_brains[0]); + AnnotationManager* annMan = m_brains[0]->getAnnotationManager(); + AString errorMessage; + const bool resultFlag = annMan->moveTabOrWindowAnnotationToFront(newTab->getManualLayoutBrowserTabAnnotation(), + errorMessage); + if ( ! resultFlag) { + CaretLogWarning("Moving to front after creation of new tab error: " + + errorMessage); + } + } + + return newTab; +} + +/** + * Close the given tab in the given window that may be reopened later + * + * @param closeTabEvent + * The delete tab event + * @param errorMessageOut + * Contains error information + * @return True if tab was closed, else false + */ +bool +SessionManager::closeBrowserTab(EventBrowserTabClose* closeTabEvent, + AString& errorMessageOut) +{ + BrowserTabContent* tab = closeTabEvent->getBrowserTab(); + const int32_t windowIndex = closeTabEvent->getWindowIndex(); + CaretAssert(tab); + CaretAssertStdArrayIndex(m_browserWindowContent, windowIndex); + + const int32_t tabIndex = tab->getTabNumber(); + if ((tabIndex < 0) + || (tabIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { + errorMessageOut = ("Tab for closing has invalid tabIndex=" + + AString::number(tabIndex)); + return false; + } + + CaretAssertStdArrayIndex(m_browserTabs, tabIndex); + if (tab != m_browserTabs[tabIndex]) { + errorMessageOut = ("Tab for closing with tabIndex=" + + AString::number(tabIndex) + + " is not at that index in array of tabs."); + return false; + } + + tab->setClosedStatusFromSessionManager(true); + tab->setClosedTabWindowTabBarPositionIndex(closeTabEvent->getWindowTabBarPositionIndex()); + tab->setClosedTabWindowIndex(windowIndex); + m_closedBrowserTabs.push_front(tab); + m_browserTabs[tabIndex] = NULL; + + return true; +} + +/** + * Delete the given tab in the given window + * + * @param deleteTabEvent + * The delete tab event + * @param errorMessageOut + * Contains error information + * @return True if tab was closed, else false + */ +bool +SessionManager::deleteBrowserTab(EventBrowserTabDelete* deleteTabEvent, + AString& errorMessageOut) +{ + BrowserTabContent* tab = deleteTabEvent->getBrowserTab(); + CaretAssert(tab); + + const int32_t tabIndex = tab->getTabNumber(); + if ((tabIndex < 0) + || (tabIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { + errorMessageOut = ("Tab for deleting has invalid tabIndex=" + + AString::number(tabIndex)); + return false; + } + + CaretAssertStdArrayIndex(m_browserTabs, tabIndex); + if (tab != m_browserTabs[tabIndex]) { + errorMessageOut = ("Tab for deleting with tabIndex=" + + AString::number(tabIndex) + + " is not at that index in array of tabs."); + CaretAssertMessage(0, errorMessageOut); + return false; + } + + delete m_browserTabs[tabIndex]; + m_browserTabs[tabIndex] = NULL; + + return true; +} + +/** + * Reopen that last tab closed in the given window + * + * @param windowIndex + * Index of window containing the tab + * @param errorMessageOut + * Contains error information + * @return Tab that was reopened or NULL if no tab was reopened + */ +BrowserTabContent* +SessionManager::reopenLastClosedTab(AString& errorMessageOut) +{ + if ( ! m_closedBrowserTabs.empty()) { + BrowserTabContent* btc = m_closedBrowserTabs.front(); + CaretAssert(btc); + const int32_t tabIndex = btc->getTabNumber(); + if (m_browserTabs[tabIndex] == NULL) { + m_browserTabs[tabIndex] = btc; + m_closedBrowserTabs.pop_front(); + btc->update(m_models); + btc->setClosedStatusFromSessionManager(false); + return btc; + } + else { + errorMessageOut = ("Program Error: Trying to reopen tab with index=" + + AString::number(tabIndex) + + " but there is a currently open tab at that index"); + } + } + + errorMessageOut = "No tabs are available for reopening"; + + return NULL; +} + +/** + * Delete all browser tabs both active and recently closed + */ +void +SessionManager::deleteAllBrowserTabs() +{ + for (auto bt : m_browserTabs) { + if (bt != NULL) { + delete bt; + } + } + m_browserTabs.fill(NULL); + + for (auto bt : m_closedBrowserTabs) { + delete bt; + } + m_closedBrowserTabs.clear(); +} + +/** + * @return True if a tab with the given index in the closed browser tabs + * @param tabIndex + * Index of the browser tab + */ +bool +SessionManager::isTabInClosedBrowserTabs(const int32_t tabIndex) +{ + for (auto bt : m_closedBrowserTabs) { + CaretAssert(bt); + if (tabIndex == bt->getTabNumber()) { + return true; + } + } + return false; +} + +/** + * @return True if a scene was loaded that contains a chart old + */ +bool +SessionManager::hasSceneWithChartOld() const +{ + return m_sceneRestoredWithChartOldFlag; +} + +/** + * Reset the scene contains an old chart model + */ +void +SessionManager::resetSceneWithChartOld() +{ + m_sceneRestoredWithChartOldFlag = false; +} + + diff -Nru connectome-workbench-1.4.2/src/Brain/SessionManager.h connectome-workbench-1.5.0/src/Brain/SessionManager.h --- connectome-workbench-1.4.2/src/Brain/SessionManager.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SessionManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,6 +22,7 @@ /*LICENSE_END*/ #include +#include #include #include @@ -38,9 +39,13 @@ class BrowserTabContent; class BrowserWindowContent; class CaretPreferences; + class ChartTwoCartesianAxis; + class ChartTwoOverlaySet; class CiftiConnectivityMatrixDataFileManager; class CiftiFiberTrajectoryManager; class DataToolTipsManager; + class EventBrowserTabClose; + class EventBrowserTabDelete; class ImageCaptureSettings; class Model; class MovieRecorder; @@ -91,6 +96,11 @@ virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); + + bool hasSceneWithChartOld() const; + + void resetSceneWithChartOld(); + private: SessionManager(); @@ -116,11 +126,36 @@ void restorePreferencesFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); + int32_t getMaximumManualTabStackOrder() const; + + std::vector getActiveBrowserTabs(); + + BrowserTabContent* createNewBrowserTab(); + + bool closeBrowserTab(EventBrowserTabClose* closeTabEvent, + AString& errorMessageOut); + + bool deleteBrowserTab(EventBrowserTabDelete* deleteTabEvent, + AString& errorMessageOut); + + BrowserTabContent* reopenLastClosedTab(AString& errorMessageOut); + + void deleteAllBrowserTabs(); + + bool isTabInClosedBrowserTabs(const int32_t tabIndex); + /** The session manager */ static SessionManager* s_singletonSessionManager; - - /** The browser tabs */ - BrowserTabContent* m_browserTabs[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + + /** Active browser tabs */ + std::array m_browserTabs; + + /** + * Closed browser tabs + * Tab at front is reopened. + * If there is a request to create a new tab and none are available, tab at back is deleted so a new tab can be created + */ + std::deque m_closedBrowserTabs; /** The browser window content */ std::array m_browserWindowContent; @@ -150,6 +185,10 @@ std::map m_spacerTabsMap; std::unique_ptr m_movieRecorder; + + std::vector> m_chartingAxisDisplayGroups; + + bool m_sceneRestoredWithChartOldFlag = false; }; #ifdef __SESSION_MANAGER_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/Brain/SurfaceDrawingTypeEnum.cxx connectome-workbench-1.5.0/src/Brain/SurfaceDrawingTypeEnum.cxx --- connectome-workbench-1.4.2/src/Brain/SurfaceDrawingTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SurfaceDrawingTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -82,6 +82,10 @@ "DRAW_AS_LINKS", "Links (Edges)")); + enumData.push_back(SurfaceDrawingTypeEnum(DRAW_AS_LINKS_TRANSPARENT, + "DRAW_AS_LINKS_TRANSPARENT", + "Links (Transparent)")); + enumData.push_back(SurfaceDrawingTypeEnum(DRAW_AS_NODES, "DRAW_AS_NODES", "Vertices")); diff -Nru connectome-workbench-1.4.2/src/Brain/SurfaceDrawingTypeEnum.h connectome-workbench-1.5.0/src/Brain/SurfaceDrawingTypeEnum.h --- connectome-workbench-1.4.2/src/Brain/SurfaceDrawingTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SurfaceDrawingTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -39,6 +39,8 @@ DRAW_HIDE, /** Draw as links (edges) */ DRAW_AS_LINKS, + /** Draw as links (edges) */ + DRAW_AS_LINKS_TRANSPARENT, /** Draw as nodes */ DRAW_AS_NODES, /** Draw as triangles */ diff -Nru connectome-workbench-1.4.2/src/Brain/SurfaceNodeColoring.cxx connectome-workbench-1.5.0/src/Brain/SurfaceNodeColoring.cxx --- connectome-workbench-1.4.2/src/Brain/SurfaceNodeColoring.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SurfaceNodeColoring.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -42,6 +42,7 @@ #include "DisplayPropertiesSurface.h" #include "GroupAndNameHierarchyModel.h" #include "DisplayPropertiesLabels.h" +#include "DisplayPropertiesSurface.h" #include "EventManager.h" #include "EventModelSurfaceGet.h" #include "GiftiLabel.h" @@ -197,6 +198,12 @@ displayPropertiesLabels = brain->getDisplayPropertiesLabels(); } + std::array defaultColor { 178, 178, 178 }; + if (brain != NULL) { + const DisplayPropertiesSurface* dsp = brain->getDisplayPropertiesSurface(); + defaultColor = dsp->getDefaultColorRGB(); + } + const int numNodes = surface->getNumberOfNodes(); const int numColorComponents = numNodes * 4; float *rgbaColor = new float[numColorComponents]; @@ -207,6 +214,7 @@ this->colorSurfaceNodes(displayPropertiesLabels, browserTabIndex, surface, + defaultColor, overlaySet, rgbaColor); @@ -285,8 +293,14 @@ /** * Assign color components to surface nodes. * + * @param displayPropertiesLabels + * Label display properties + * @param browserTabIndex + * Index of browser tab * @param surface * Surface that has its nodes colored. + * @param defaultSurfaceColor + * Default coloring for surface * @param overlaySet * Surface overlay assignments for surface. * @param rgbaNodeColors @@ -296,20 +310,27 @@ SurfaceNodeColoring::colorSurfaceNodes(const DisplayPropertiesLabels* displayPropertiesLabels, const int32_t browserTabIndex, const Surface* surface, + const std::array& defaultSurfaceColor, OverlaySet* overlaySet, float* rgbaNodeColors) { const int32_t numNodes = surface->getNumberOfNodes(); const int32_t numberOfDisplayedOverlays = overlaySet->getNumberOfDisplayedOverlays(); + const float defaultColorFloat[3] = { + defaultSurfaceColor[0] / 255.0f, + defaultSurfaceColor[1] / 255.0f, + defaultSurfaceColor[2] / 255.0f + }; + /* * Default color. */ for (int32_t i = 0; i < numNodes; i++) { const int32_t i4 = i * 4; - rgbaNodeColors[i4] = 0.70; - rgbaNodeColors[i4+1] = 0.70; - rgbaNodeColors[i4+2] = 0.70; + rgbaNodeColors[i4] = defaultColorFloat[0]; + rgbaNodeColors[i4+1] = defaultColorFloat[1]; + rgbaNodeColors[i4+2] = defaultColorFloat[2]; rgbaNodeColors[i4+3] = 1.0; } diff -Nru connectome-workbench-1.4.2/src/Brain/SurfaceNodeColoring.h connectome-workbench-1.5.0/src/Brain/SurfaceNodeColoring.h --- connectome-workbench-1.4.2/src/Brain/SurfaceNodeColoring.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/SurfaceNodeColoring.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "CaretColorEnum.h" #include "CaretObject.h" #include "CaretPointer.h" @@ -82,6 +84,7 @@ void colorSurfaceNodes(const DisplayPropertiesLabels* dpl, const int32_t browserTabIndex, const Surface* surface, + const std::array& defaultSurfaceColor, OverlaySet* overlaySet, float* rgbaNodeColors); diff -Nru connectome-workbench-1.4.2/src/Brain/UserInputModeEnum.cxx connectome-workbench-1.5.0/src/Brain/UserInputModeEnum.cxx --- connectome-workbench-1.4.2/src/Brain/UserInputModeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/UserInputModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -106,31 +106,35 @@ } initializedFlag = true; - enumData.push_back(UserInputModeEnum(INVALID, + enumData.push_back(UserInputModeEnum(Enum::INVALID, "INVALID", "Invalid")); - enumData.push_back(UserInputModeEnum(ANNOTATIONS, + enumData.push_back(UserInputModeEnum(Enum::ANNOTATIONS, "ANNOTATIONS", "Annotations")); - enumData.push_back(UserInputModeEnum(BORDERS, + enumData.push_back(UserInputModeEnum(Enum::BORDERS, "BORDERS", "Borders")); - enumData.push_back(UserInputModeEnum(FOCI, + enumData.push_back(UserInputModeEnum(Enum::FOCI, "FOCI", "Foci")); - enumData.push_back(UserInputModeEnum(IMAGE, + enumData.push_back(UserInputModeEnum(Enum::IMAGE, "IMAGE", "Image")); - enumData.push_back(UserInputModeEnum(VIEW, + enumData.push_back(UserInputModeEnum(Enum::TILE_TABS_MANUAL_LAYOUT_EDITING, + "TILE_TABS_MANUAL_LAYOUT_EDITING", + "Tile Tabs Manual Layout Editing")); + + enumData.push_back(UserInputModeEnum(Enum::VIEW, "VIEW", "View")); - enumData.push_back(UserInputModeEnum(VOLUME_EDIT, + enumData.push_back(UserInputModeEnum(Enum::VOLUME_EDIT, "VOLUME_EDIT", "Volume Edit")); diff -Nru connectome-workbench-1.4.2/src/Brain/UserInputModeEnum.h connectome-workbench-1.5.0/src/Brain/UserInputModeEnum.h --- connectome-workbench-1.4.2/src/Brain/UserInputModeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/UserInputModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -34,7 +34,7 @@ /** * Enumerated values. */ - enum Enum { + enum class Enum { /** Invalid */ INVALID, /** Annotations */ @@ -45,6 +45,8 @@ FOCI, /** Image */ IMAGE, + /** Tile tabs manual layouot editing */ + TILE_TABS_MANUAL_LAYOUT_EDITING, /** View */ VIEW, /** Volume Edit */ diff -Nru connectome-workbench-1.4.2/src/Brain/ViewingTransformations.cxx connectome-workbench-1.5.0/src/Brain/ViewingTransformations.cxx --- connectome-workbench-1.4.2/src/Brain/ViewingTransformations.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ViewingTransformations.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -49,6 +49,8 @@ m_translation[1] = 0.0; m_translation[2] = 0.0; m_scaling = 1.0; + m_flatRotationMatrix = new Matrix4x4(); + m_rightCortexFlatMapOffset[0] = 0.0; m_rightCortexFlatMapOffset[1] = 0.0; m_rightCortexFlatMapZoomFactor = 1.0; @@ -59,6 +61,7 @@ 0.0); m_sceneAssistant->add("m_scaling", &m_scaling); + m_sceneAssistant->addArray("m_rightCortexFlatMapOffset", m_rightCortexFlatMapOffset, 2, @@ -73,6 +76,7 @@ ViewingTransformations::~ViewingTransformations() { delete m_rotationMatrix; + delete m_flatRotationMatrix; delete m_sceneAssistant; } @@ -117,6 +121,7 @@ m_translation[1] = obj.m_translation[1]; m_translation[2] = obj.m_translation[2]; m_scaling = obj.m_scaling; + *m_flatRotationMatrix = *obj.m_flatRotationMatrix; m_rightCortexFlatMapOffset[0] = obj.m_rightCortexFlatMapOffset[0]; m_rightCortexFlatMapOffset[1] = obj.m_rightCortexFlatMapOffset[1]; m_rightCortexFlatMapZoomFactor = obj.m_rightCortexFlatMapZoomFactor; @@ -221,6 +226,26 @@ } /** + * @return The flat rotation matrix + */ +Matrix4x4 +ViewingTransformations::getFlatRotationMatrix() const +{ + return *m_flatRotationMatrix; +} + +/** + * Set the flat rotation matrix + * @param flatRotationMatrix + * New flat rotation matrix + */ +void +ViewingTransformations::setFlatRotationMatrix(const Matrix4x4& flatRotationMatrix) +{ + *m_flatRotationMatrix = flatRotationMatrix; +} + +/** * Get the offset for the right cortex flat map. * * @param rightCortexFlatMapOffsetX @@ -283,6 +308,7 @@ setTranslation(0.0, 0.0, 0.0); m_rotationMatrix->identity(); setScaling(1.0); + m_flatRotationMatrix->identity(); setRightCortexFlatMapOffset(0.0, 0.0); setRightCortexFlatMapZoomFactor(1.0); leftView(); @@ -318,8 +344,6 @@ { m_rotationMatrix->identity(); m_rotationMatrix->setRotation(90.0, 0.0, -180.0); -// m_rotationMatrix->rotateX(-90.0); -// m_rotationMatrix->rotateY(180.0); } /** @@ -330,7 +354,6 @@ { m_rotationMatrix->identity(); m_rotationMatrix->setRotation(-90.0, 0.0, 0.0); -// m_rotationMatrix->rotateX(-90.0); } /** @@ -339,7 +362,6 @@ void ViewingTransformations::dorsalView() { -// m_rotationMatrix->identity(); m_rotationMatrix->setRotation(0.0, 0.0, 90.0); } @@ -349,8 +371,6 @@ void ViewingTransformations::ventralView() { -// m_rotationMatrix->identity(); -// m_rotationMatrix->rotateY(-180.0); m_rotationMatrix->setRotation(0.0, 180.0, 90.0); } @@ -392,6 +412,11 @@ m_rotationMatrix->getMatrix(matrix); sceneClass->addFloatArray("m_rotationMatrix", (float*)matrix, 16); + /* + * Save flat rotation matrices. + */ + m_flatRotationMatrix->getMatrix(matrix); + sceneClass->addFloatArray("m_flatRotationMatrix", (float*)matrix, 16); return sceneClass; } @@ -427,6 +452,17 @@ else { m_rotationMatrix->identity(); } + + /* + * Restore flat rotation matrices. + */ + if (sceneClass->getFloatArrayValue("m_flatRotationMatrix", (float*)matrix, 16) == 16) { + m_flatRotationMatrix->setMatrix(matrix); + } + else { + m_flatRotationMatrix->identity(); + } + } diff -Nru connectome-workbench-1.4.2/src/Brain/ViewingTransformations.h connectome-workbench-1.5.0/src/Brain/ViewingTransformations.h --- connectome-workbench-1.4.2/src/Brain/ViewingTransformations.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ViewingTransformations.h 2021-02-16 19:46:47.000000000 +0000 @@ -64,6 +64,10 @@ void setRightCortexFlatMapOffset(const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY); + Matrix4x4 getFlatRotationMatrix() const; + + void setFlatRotationMatrix(const Matrix4x4& flatRotationMatrix); + float getRightCortexFlatMapZoomFactor() const; void setRightCortexFlatMapZoomFactor(const float rightCortexFlatMapZoomFactor); @@ -102,6 +106,9 @@ /** Scaling. */ float m_scaling; + /** Rotation matrix for flat surface */ + Matrix4x4* m_flatRotationMatrix; + /** Offset for right cortex flat map */ float m_rightCortexFlatMapOffset[2]; diff -Nru connectome-workbench-1.4.2/src/Brain/ViewingTransformationsMedia.cxx connectome-workbench-1.5.0/src/Brain/ViewingTransformationsMedia.cxx --- connectome-workbench-1.4.2/src/Brain/ViewingTransformationsMedia.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ViewingTransformationsMedia.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,101 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __VIEWING_TRANSFORMATIONS_MEDIA_DECLARE__ +#include "ViewingTransformationsMedia.h" +#undef __VIEWING_TRANSFORMATIONS_MEDIA_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + +/** + * \class caret::ViewingTransformationsMedia + * \brief Viewing transformations (pan/rotate/zoom) for media. + * \ingroup Brain + * + * Extends ViewingTransformations with differences for media viewing. + */ + +/** + * Constructor. + */ +ViewingTransformationsMedia::ViewingTransformationsMedia() +: ViewingTransformations() +{ + +} + +/** + * Destructor. + */ +ViewingTransformationsMedia::~ViewingTransformationsMedia() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ViewingTransformationsMedia::ViewingTransformationsMedia(const ViewingTransformationsMedia& obj) +: ViewingTransformations(obj) +{ + this->copyHelperViewingTransformationsMedia(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ViewingTransformationsMedia& +ViewingTransformationsMedia::operator=(const ViewingTransformationsMedia& obj) +{ + if (this != &obj) { + ViewingTransformations::operator=(obj); + this->copyHelperViewingTransformationsMedia(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ViewingTransformationsMedia::copyHelperViewingTransformationsMedia(const ViewingTransformationsMedia& /*obj*/) +{ + +} + +/** + * Reset the view to the default view for media. + */ +void +ViewingTransformationsMedia::resetView() +{ + ViewingTransformations::resetView(); + m_rotationMatrix->identity(); +} + diff -Nru connectome-workbench-1.4.2/src/Brain/ViewingTransformationsMedia.h connectome-workbench-1.5.0/src/Brain/ViewingTransformationsMedia.h --- connectome-workbench-1.4.2/src/Brain/ViewingTransformationsMedia.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/ViewingTransformationsMedia.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,59 @@ +#ifndef __VIEWING_TRANSFORMATIONS_MEDIA_H__ +#define __VIEWING_TRANSFORMATIONS_MEDIA_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include "ViewingTransformations.h" + + + +namespace caret { + + class ViewingTransformationsMedia : public ViewingTransformations { + + public: + ViewingTransformationsMedia(); + + virtual ~ViewingTransformationsMedia(); + + ViewingTransformationsMedia(const ViewingTransformationsMedia& obj); + + ViewingTransformationsMedia& operator=(const ViewingTransformationsMedia& obj); + + + virtual void resetView(); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperViewingTransformationsMedia(const ViewingTransformationsMedia& obj); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __VIEWING_TRANSFORMATIONS_MEDIA_DECLARE__ + // +#endif // __VIEWING_TRANSFORMATIONS_MEDIA_DECLARE__ + +} // namespace +#endif //__VIEWING_TRANSFORMATIONS_MEDIA_H__ diff -Nru connectome-workbench-1.4.2/src/Brain/VolumeSliceSettings.cxx connectome-workbench-1.5.0/src/Brain/VolumeSliceSettings.cxx --- connectome-workbench-1.4.2/src/Brain/VolumeSliceSettings.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/VolumeSliceSettings.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -192,6 +192,8 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: diff -Nru connectome-workbench-1.4.2/src/Brain/VolumeSurfaceOutlineModel.cxx connectome-workbench-1.5.0/src/Brain/VolumeSurfaceOutlineModel.cxx --- connectome-workbench-1.4.2/src/Brain/VolumeSurfaceOutlineModel.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Brain/VolumeSurfaceOutlineModel.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -131,7 +131,16 @@ CaretAssert(colorEvent); colorEvent->setEventProcessed(); - clearOutlineCache(); + switch (m_colorOrTabModel->getSelectedItem()->getItemType()) { + case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_BROWSER_TAB: + /* + * Color of surface may have changed + */ + clearOutlineCache(); + break; + case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_COLOR: + break; + } } } diff -Nru connectome-workbench-1.4.2/src/Charting/2 connectome-workbench-1.5.0/src/Charting/2 --- connectome-workbench-1.4.2/src/Charting/2 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/2 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,129 @@ + +# +# Name of Project +# +PROJECT(Charting) + +# +# Qt include files +# +if(Qt5_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) +endif() + +# +# Create a library +# +ADD_LIBRARY(Charting +ChartAxis.h +ChartAxisCartesian.h +ChartAxisLocationEnum.h +ChartAxisTypeEnum.h +ChartAxisUnitsEnum.h +ChartData.h +ChartDataCartesian.h +ChartDataSource.h +ChartDataSourceModeEnum.h +ChartMatrixDisplayProperties.h +ChartMatrixLoadingDimensionEnum.h +ChartMatrixScaleModeEnum.h +ChartModel.h +ChartModelCartesian.h +ChartModelDataSeries.h +ChartModelFrequencySeries.h +ChartModelTimeSeries.h +ChartPoint.h +ChartScaleAutoRanging.h +ChartSelectionModeEnum.h +ChartOneDataTypeEnum.h +ChartingVersionEnum.h + +ChartTwoAxisOrientationTypeEnum.h +ChartTwoAxisScaleRangeModeEnum.h +ChartTwoCartesianAxis.h +ChartTwoCartesianCustomSubdivisions.h +ChartTwoCartesianCustomAxisLabel.h +ChartTwoCartesianCustomAxisLabelModeEnum.h +ChartTwoCartesianOrientedAxes.h +ChartTwoCartesianOrientedAxesYokingManager.h +ChartTwoCompoundDataType.h +ChartTwoDataCartesian.h +ChartTwoDataTypeEnum.h +ChartTwoHistogramContentTypeEnum.h +ChartTwoLineLayerContentTypeEnum.h +ChartTwoLineSeriesContentTypeEnum.h +ChartTwoMatrixContentTypeEnum.h +ChartTwoMatrixDisplayProperties.h +ChartTwoMatrixLoadingDimensionEnum.h +ChartTwoMatrixTriangularViewingModeEnum.h +ChartTwoNumericSubdivisionsModeEnum.h +ChartTwoLineSeriesHistory.h +ChartTwoOverlaySetInterface.h +ChartTwoTitle.h +EventChartTwoCartesianOrientedAxesYoking.h +EventChartTwoLoadLineSeriesData.h +MapFileDataSelector.h + +ChartAxis.cxx +ChartAxisCartesian.cxx +ChartAxisLocationEnum.cxx +ChartAxisTypeEnum.cxx +ChartAxisUnitsEnum.cxx +ChartData.cxx +ChartDataCartesian.cxx +ChartDataSource.cxx +ChartDataSourceModeEnum.cxx +ChartMatrixDisplayProperties.cxx +ChartMatrixLoadingDimensionEnum.cxx +ChartMatrixScaleModeEnum.cxx +ChartModel.cxx +ChartModelCartesian.cxx +ChartModelDataSeries.cxx +ChartModelFrequencySeries.cxx +ChartModelTimeSeries.cxx +ChartPoint.cxx +ChartScaleAutoRanging.cxx +ChartSelectionModeEnum.cxx +ChartOneDataTypeEnum.cxx +ChartingVersionEnum.cxx + +ChartTwoAxisOrientationTypeEnum.cxx +ChartTwoAxisScaleRangeModeEnum.cxx +ChartTwoCartesianAxis.cxx +ChartTwoCartesianCustomSubdivisions.cxx +ChartTwoCartesianCustomAxisLabel.cxx +ChartTwoCartesianCustomAxisLabelModeEnum.cxx +ChartTwoCartesianOrientedAxes.cxx +ChartTwoCartesianOrientedAxesYokingManager.cxx +ChartTwoCompoundDataType.cxx +ChartTwoDataCartesian.cxx +ChartTwoDataTypeEnum.cxx +ChartTwoHistogramContentTypeEnum.cxx +ChartTwoLineLayerContentTypeEnum.cxx +ChartTwoLineSeriesContentTypeEnum.cxx +ChartTwoMatrixContentTypeEnum.cxx +ChartTwoMatrixDisplayProperties.cxx +ChartTwoMatrixLoadingDimensionEnum.cxx +ChartTwoMatrixTriangularViewingModeEnum.cxx +ChartTwoNumericSubdivisionsModeEnum.cxx +ChartTwoLineSeriesHistory.cxx +ChartTwoOverlaySetInterface.cxx +ChartTwoTitle.cxx +EventChartTwoCartesianOrientedAxesYoking.cxx +EventChartTwoLoadLineSeriesData.cxx +MapFileDataSelector.cxx +) + +TARGET_LINK_LIBRARIES(Charting ${CARET_QT5_LINK}) + +# +# Include directories +# +INCLUDE_DIRECTORIES( +${CMAKE_SOURCE_DIR}/Annotations +${CMAKE_SOURCE_DIR}/Charting +${CMAKE_SOURCE_DIR}/Common +${CMAKE_SOURCE_DIR}/Graphics +${CMAKE_SOURCE_DIR}/Scenes +) + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoAxisOrientationTypeEnum.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoAxisOrientationTypeEnum.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoAxisOrientationTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoAxisOrientationTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,374 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_AXIS_ORIENTATION_TYPE_ENUM_DECLARE__ +#include "ChartTwoAxisOrientationTypeEnum.h" +#undef __CHART_TWO_AXIS_ORIENTATION_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoAxisOrientationTypeEnum + * \brief Orientation for axes + * + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartTwoAxisOrientationEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartTwoAxisOrientationEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoAxisOrientationTypeEnum.h" + * + * Instatiate: + * m_chartTwoAxisOrientationEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartTwoAxisOrientationEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartTwoAxisOrientationEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartTwoAxisOrientationEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartTwoAxisOrientationEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoAxisOrientationTypeEnum::Enum VARIABLE = m_chartTwoAxisOrientationEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoAxisOrientationTypeEnum::ChartTwoAxisOrientationTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoAxisOrientationTypeEnum::~ChartTwoAxisOrientationTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoAxisOrientationTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoAxisOrientationTypeEnum(HORIZONTAL, + "HORIZONTAL", + "Horizontal")); + + enumData.push_back(ChartTwoAxisOrientationTypeEnum(VERTICAL, + "VERTICAL", + "Vertical")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoAxisOrientationTypeEnum* +ChartTwoAxisOrientationTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoAxisOrientationTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoAxisOrientationTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoAxisOrientationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoAxisOrientationTypeEnum::Enum +ChartTwoAxisOrientationTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoAxisOrientationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoAxisOrientationTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoAxisOrientationTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoAxisOrientationTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoAxisOrientationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoAxisOrientationTypeEnum::Enum +ChartTwoAxisOrientationTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoAxisOrientationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoAxisOrientationTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoAxisOrientationTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoAxisOrientationTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoAxisOrientationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoAxisOrientationTypeEnum::Enum +ChartTwoAxisOrientationTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoAxisOrientationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoAxisOrientationTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoAxisOrientationTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoAxisOrientationTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoAxisOrientationTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoAxisOrientationTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoAxisOrientationTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoAxisOrientationTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoAxisOrientationTypeEnum.h connectome-workbench-1.5.0/src/Charting/ChartTwoAxisOrientationTypeEnum.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoAxisOrientationTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoAxisOrientationTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,104 @@ +#ifndef __CHART_TWO_AXIS_ORIENTATION_TYPE_ENUM_H__ +#define __CHART_TWO_AXIS_ORIENTATION_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoAxisOrientationTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + HORIZONTAL, + /** */ + VERTICAL + }; + + + ~ChartTwoAxisOrientationTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoAxisOrientationTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoAxisOrientationTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_AXIS_ORIENTATION_TYPE_ENUM_DECLARE__ +std::vector ChartTwoAxisOrientationTypeEnum::enumData; +bool ChartTwoAxisOrientationTypeEnum::initializedFlag = false; +int32_t ChartTwoAxisOrientationTypeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_AXIS_ORIENTATION_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_AXIS_ORIENTATION_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -110,6 +110,7 @@ } initializedFlag = true; + const AString invalidOldName(""); enumData.push_back(ChartTwoAxisScaleRangeModeEnum(AUTO, "AUTO", "Auto", @@ -125,6 +126,26 @@ "User", "AXIS_DATA_RANGE_USER")); + enumData.push_back(ChartTwoAxisScaleRangeModeEnum(YOKE_A, + "YOKE_A", + "Yoke A", + invalidOldName)); + + enumData.push_back(ChartTwoAxisScaleRangeModeEnum(YOKE_B, + "YOKE_B", + "Yoke B", + invalidOldName)); + + enumData.push_back(ChartTwoAxisScaleRangeModeEnum(YOKE_C, + "YOKE_C", + "Yoke C", + invalidOldName)); + + enumData.push_back(ChartTwoAxisScaleRangeModeEnum(YOKE_D, + "YOKE_D", + "Yoke D", + invalidOldName)); + } /** @@ -383,3 +404,27 @@ } } +/** + * @return True if the given mode is a yoking range mode + * @param enumValue + * The enumerated type value + */ +bool +ChartTwoAxisScaleRangeModeEnum::isYokingRangeMode(const Enum enumValue) +{ + bool yokeFlag(false); + switch (enumValue) { + case ChartTwoAxisScaleRangeModeEnum::AUTO: + case ChartTwoAxisScaleRangeModeEnum::DATA: + case ChartTwoAxisScaleRangeModeEnum::USER: + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_A: + case ChartTwoAxisScaleRangeModeEnum::YOKE_B: + case ChartTwoAxisScaleRangeModeEnum::YOKE_C: + case ChartTwoAxisScaleRangeModeEnum::YOKE_D: + yokeFlag = true; + break; + } + + return yokeFlag; +} diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoAxisScaleRangeModeEnum.h connectome-workbench-1.5.0/src/Charting/ChartTwoAxisScaleRangeModeEnum.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoAxisScaleRangeModeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoAxisScaleRangeModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -40,8 +40,15 @@ /** Scales to minimum and maximum of data */ DATA, /** Scales to user-specified minimum and maximum */ - /** */ - USER + USER, + /** Yoke A */ + YOKE_A, + /** Yoke B */ + YOKE_B, + /** Yoke C */ + YOKE_C, + /** Yoke D */ + YOKE_D }; @@ -65,6 +72,8 @@ static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + static bool isYokingRangeMode(const Enum enumValue); + private: ChartTwoAxisScaleRangeModeEnum(const Enum enumValue, const AString& name, diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianAxis.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianAxis.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianAxis.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianAxis.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -26,7 +26,9 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "ChartScaleAutoRanging.h" -#include "EventChartTwoAxisGetDataRange.h" +#include "ChartTwoCartesianCustomSubdivisions.h" +#include "ChartTwoOverlaySetInterface.h" +#include "EventChartTwoCartesianAxisDisplayGroup.h" #include "EventManager.h" #include "MathFunctions.h" #include "NumericTextFormatting.h" @@ -46,29 +48,25 @@ /** * Constructor. * + * @param parentChartOverlaySet + * Parent of this axis. (may be NULL) * @param axisLocation * Location of the axis (left, right, bottom, top) - * @param parentChartOverlaySet - * Parent of this axis. */ -ChartTwoCartesianAxis::ChartTwoCartesianAxis(const ChartTwoOverlaySet* parentChartOverlaySet, +ChartTwoCartesianAxis::ChartTwoCartesianAxis(const ChartTwoOverlaySetInterface* parentChartOverlaySetInterface, const ChartAxisLocationEnum::Enum axisLocation) : CaretObject(), SceneableInterface(), -m_parentChartOverlaySet(parentChartOverlaySet), +m_parentChartOverlaySetInterface(parentChartOverlaySetInterface), m_axisLocation(axisLocation) { - /* - * Note: Parent chart overlay set is used for sending an event - * to the parent to get the range of data. Note that parent - * chart overlay is in 'Brain' module which we have no access. - */ - CaretAssert(m_parentChartOverlaySet); + m_customSubdivisions.reset(new ChartTwoCartesianCustomSubdivisions()); - m_showTickmarks = true; - m_showLabel = true; + reset(); m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_displayGroup", + &m_displayGroup); m_sceneAssistant->add("m_displayedByUser", &m_displayedByUser); m_sceneAssistant->add("m_userScaleMinimumValue", @@ -90,8 +88,6 @@ &m_numericSubdivsionsMode); m_sceneAssistant->add("m_userNumberOfSubdivisions", &m_userNumberOfSubdivisions); - m_sceneAssistant->add("m_enabledByChart", - &m_enabledByChart); m_sceneAssistant->add("m_showTickmarks", &m_showTickmarks); m_sceneAssistant->add("m_showLabel", @@ -108,6 +104,11 @@ &m_numericsTextRotated); m_sceneAssistant->add("m_paddingSize", &m_paddingSize); + m_sceneAssistant->add("m_customSubdivisions", + "ChartTwoCartesianCustomSubdivisions", + m_customSubdivisions.get()); + m_sceneAssistant->add("m_subdivisionsMode", + &m_subdivisionsMode); } /** @@ -155,28 +156,72 @@ void ChartTwoCartesianAxis::copyHelperChartTwoCartesianAxis(const ChartTwoCartesianAxis& obj) { - CaretAssert(m_axisLocation == obj.m_axisLocation); + copyAxisParameters(&obj); +} + +/** + * Copy the parameters from the given axis to this axis. + * @param axis + * Axis whose parameters are copied to this axis + */ +void +ChartTwoCartesianAxis::copyAxisParameters(const ChartTwoCartesianAxis* axis) +{ + CaretAssert(axis); - m_titleOverlayIndex = obj.m_titleOverlayIndex; - m_displayedByUser = obj.m_displayedByUser; - m_userScaleMinimumValue = obj.m_userScaleMinimumValue; - m_userScaleMaximumValue = obj.m_userScaleMaximumValue; - m_axisLabelsStepValue = obj.m_axisLabelsStepValue; - m_userDigitsRightOfDecimal = obj.m_userDigitsRightOfDecimal; - m_scaleRangeMode = obj.m_scaleRangeMode; - m_units = obj.m_units; - m_userNumericFormat = obj.m_userNumericFormat; - m_numericSubdivsionsMode = obj.m_numericSubdivsionsMode; - m_userNumberOfSubdivisions = obj.m_userNumberOfSubdivisions; - m_enabledByChart = obj.m_enabledByChart; - m_showTickmarks = obj.m_showTickmarks; - m_showLabel = obj.m_showLabel; - m_labelTextSize = obj.m_labelTextSize; - m_numericsTextSize = obj.m_numericsTextSize; - m_numericsTextDisplayed = obj.m_numericsTextDisplayed; - m_numericsTextRotated = obj.m_numericsTextRotated; - m_paddingSize = obj.m_paddingSize; - limitUserScaleMinMaxToValidRange(); + /* + * Do not copy: + * m_displayGroup + */ + m_userScaleMinimumValue = axis->m_userScaleMinimumValue; + m_userScaleMaximumValue = axis->m_userScaleMaximumValue; + m_axisLabelsStepValue = axis->m_axisLabelsStepValue; + m_userDigitsRightOfDecimal = axis->m_userDigitsRightOfDecimal; + m_titleOverlayIndex = axis->m_titleOverlayIndex; + m_scaleRangeMode = axis->m_scaleRangeMode; + m_units = axis->m_units; + m_userNumericFormat = axis->m_userNumericFormat; + m_numericSubdivsionsMode = axis->m_numericSubdivsionsMode; + m_userNumberOfSubdivisions = axis->m_userNumberOfSubdivisions; + m_labelTextSize = axis->m_labelTextSize; + m_numericsTextSize = axis->m_numericsTextSize; + m_numericsTextDisplayed = axis->m_numericsTextDisplayed; + m_numericsTextRotated = axis->m_numericsTextRotated; + m_paddingSize = axis->m_paddingSize; + m_showTickmarks = axis->m_showTickmarks; + m_showLabel = axis->m_showLabel; + m_displayedByUser = axis->m_displayedByUser; + *m_customSubdivisions = *axis->m_customSubdivisions; + m_subdivisionsMode = axis->m_subdivisionsMode; +} + +/* + * Reset axis to its defaults + */ +void +ChartTwoCartesianAxis::reset() +{ + m_displayGroup = DisplayGroupEnum::DISPLAY_GROUP_TAB; + m_userScaleMinimumValue = -100.0f; + m_userScaleMaximumValue = 100.0f; + m_axisLabelsStepValue = 1.0f; + m_userDigitsRightOfDecimal = 1; + m_titleOverlayIndex = 0; + m_scaleRangeMode = ChartTwoAxisScaleRangeModeEnum::AUTO; + m_units = CaretUnitsTypeEnum::NONE; + m_userNumericFormat = NumericFormatModeEnum::AUTO; + m_numericSubdivsionsMode = ChartTwoNumericSubdivisionsModeEnum::AUTO; + m_userNumberOfSubdivisions = 2; + m_labelTextSize = 2.5f; + m_numericsTextSize = 2.5f; + m_numericsTextDisplayed = true; + m_numericsTextRotated = false; + m_paddingSize = 0.0f; + m_showTickmarks = true; + m_showLabel = true; + m_displayedByUser = true; + m_customSubdivisions->reset(); + m_subdivisionsMode = ChartTwoCartesianSubdivisionsModeEnum::STANDARD; } /** @@ -207,6 +252,28 @@ { m_displayedByUser = displayedByUser; } + +/** + * @return The display group + */ +DisplayGroupEnum::Enum +ChartTwoCartesianAxis::getDisplayGroup() const +{ + return m_displayGroup; +} + +/** + * Set the display group + * @param displayGroup + * New display group + */ +void +ChartTwoCartesianAxis::setDisplayGroup(const DisplayGroupEnum::Enum displayGroup) +{ + m_displayGroup = displayGroup; +} + + /** * Get the range of data for this axis. * @@ -219,43 +286,24 @@ ChartTwoCartesianAxis::getDataRange(float& rangeMinimumOut, float& rangeMaximumOut) const { - EventChartTwoAxisGetDataRange rangeEvent(m_parentChartOverlaySet, - m_axisLocation); - EventManager::get()->sendEvent(rangeEvent.getPointer()); - - if ( ! rangeEvent.getMinimumAndMaximumValues(rangeMinimumOut, - rangeMaximumOut)) { - rangeMinimumOut = 0.0f; - rangeMaximumOut = 0.0f; + CaretAssert(m_parentChartOverlaySetInterface); + if (m_parentChartOverlaySetInterface != NULL) { + if ( ! m_parentChartOverlaySetInterface->getDataRangeForAxis(m_axisLocation, + rangeMinimumOut, + rangeMaximumOut)) { + rangeMinimumOut = 0.0; + rangeMaximumOut = 0.0; + } } } /** - * Limit the user scale min/max values to be within the valid range. - */ -void -ChartTwoCartesianAxis::limitUserScaleMinMaxToValidRange() -{ -// float minimumValue = 0.0f; -// float maximumValue = 0.0f; -// getDataRange(minimumValue, -// maximumValue); -// -// m_userScaleMinimumValue = MathFunctions::limitRange(m_userScaleMinimumValue, -// minimumValue, -// maximumValue); -// m_userScaleMaximumValue = MathFunctions::limitRange(m_userScaleMaximumValue, -// minimumValue, -// maximumValue); -} - -/** * @return User's digits right of decimal point. */ int32_t ChartTwoCartesianAxis::getUserDigitsRightOfDecimal() const { - return m_userDigitsRightOfDecimal; + return getDisplayGroupOrThisAxisForGet()->m_userDigitsRightOfDecimal; } /** @@ -267,7 +315,7 @@ void ChartTwoCartesianAxis::setUserDigitsRightOfDecimal(const int32_t digitsRightOfDecimal) { - m_userDigitsRightOfDecimal = digitsRightOfDecimal; + getDisplayGroupOrThisAxisForSet()->m_userDigitsRightOfDecimal = digitsRightOfDecimal; } @@ -277,7 +325,7 @@ int32_t ChartTwoCartesianAxis::getUserNumberOfSubdivisions() const { - return m_userNumberOfSubdivisions; + return getDisplayGroupOrThisAxisForGet()->m_userNumberOfSubdivisions; } /** @@ -288,7 +336,7 @@ void ChartTwoCartesianAxis::setUserNumberOfSubdivisions(const int32_t numberOfSubdivisions) { - m_userNumberOfSubdivisions = numberOfSubdivisions; + getDisplayGroupOrThisAxisForSet()->m_userNumberOfSubdivisions = numberOfSubdivisions; } /** @@ -297,7 +345,7 @@ ChartTwoNumericSubdivisionsModeEnum::Enum ChartTwoCartesianAxis::getNumericSubdivsionsMode() const { - return m_numericSubdivsionsMode; + return getDisplayGroupOrThisAxisForGet()->m_numericSubdivsionsMode; } /** @@ -309,70 +357,63 @@ void ChartTwoCartesianAxis::setNumericSubdivsionsMode(const ChartTwoNumericSubdivisionsModeEnum::Enum numericSubdivsionsMode) { - m_numericSubdivsionsMode = numericSubdivsionsMode; + getDisplayGroupOrThisAxisForSet()->m_numericSubdivsionsMode = numericSubdivsionsMode; } /** - * @return User scale's minimum value + * @return The custom subdivisions */ -float -ChartTwoCartesianAxis::getUserScaleMinimumValue() const +ChartTwoCartesianCustomSubdivisions* +ChartTwoCartesianAxis::getCustomSubdivisions() { - return m_userScaleMinimumValue; + return getDisplayGroupOrThisAxisForSet()->m_customSubdivisions.get(); } /** - * @return User scale's maximum value + * @return The custom subdivisions (const method) */ -float -ChartTwoCartesianAxis::getUserScaleMaximumValue() const +const ChartTwoCartesianCustomSubdivisions* +ChartTwoCartesianAxis::getCustomSubdivisions() const { - return m_userScaleMaximumValue; + return getDisplayGroupOrThisAxisForGet()->m_customSubdivisions.get(); } /** - * Set User scale's maximum value - * @param userScaleMaximumValue - * New value for User scale's maximum value + * @return The subdivisions mode */ -void -ChartTwoCartesianAxis::setUserScaleMaximumValue(const float userScaleMaximumValue) +ChartTwoCartesianSubdivisionsModeEnum::Enum +ChartTwoCartesianAxis::getSubdivisionsMode() const { - m_userScaleMaximumValue = userScaleMaximumValue; - limitUserScaleMinMaxToValidRange(); + return getDisplayGroupOrThisAxisForGet()->m_subdivisionsMode; } /** - * Set User scale's minimum value - * @param userScaleMinimumValue - * New value for User scale's minimum value + * Set the subdivisions mode + * @param subdivisionsMode + * New subdivisions mode */ void -ChartTwoCartesianAxis::setUserScaleMinimumValue(const float userScaleMinimumValue) +ChartTwoCartesianAxis::setSubdivisionsMode(const ChartTwoCartesianSubdivisionsModeEnum::Enum subdivisionsMode) { - m_userScaleMinimumValue = userScaleMinimumValue; - limitUserScaleMinMaxToValidRange(); + getDisplayGroupOrThisAxisForSet()->m_subdivisionsMode = subdivisionsMode; } /** - * @return Is the axis enabled because a chart is using it + * @return User scale's minimum value form scene */ -bool -ChartTwoCartesianAxis::isEnabledByChart() const +float +ChartTwoCartesianAxis::getSceneUserScaleMinimumValue() const { - return m_enabledByChart; + return m_userScaleMinimumValue; } /** - * Set the axis enabled because a chart is using it - * - * @param enabled - * New enabled status + * @return User scale's maximum value from scene */ -void -ChartTwoCartesianAxis::setEnabledByChart(const bool enabled) +float +ChartTwoCartesianAxis::getSceneUserScaleMaximumValue() const { - m_enabledByChart = enabled; + return m_userScaleMaximumValue; } /** @@ -381,7 +422,7 @@ bool ChartTwoCartesianAxis::isShowTickmarks() const { - return m_showTickmarks; + return getDisplayGroupOrThisAxisForGet()->m_showTickmarks; } /** @@ -392,7 +433,7 @@ void ChartTwoCartesianAxis::setShowTickmarks(const bool showTickmarks) { - m_showTickmarks = showTickmarks; + getDisplayGroupOrThisAxisForSet()->m_showTickmarks = showTickmarks; } /** @@ -401,7 +442,7 @@ bool ChartTwoCartesianAxis::isShowLabel() const { - return m_showLabel; + return getDisplayGroupOrThisAxisForGet()->m_showLabel; } /** @@ -412,31 +453,19 @@ void ChartTwoCartesianAxis::setShowLabel(const bool showLabel) { - m_showLabel = showLabel; + getDisplayGroupOrThisAxisForSet()->m_showLabel = showLabel; } /** - * @return Scale Range Mode + * @return Scale Range Mode from Scene */ ChartTwoAxisScaleRangeModeEnum::Enum -ChartTwoCartesianAxis::getScaleRangeMode() const +ChartTwoCartesianAxis::getSceneScaleRangeMode() const { return m_scaleRangeMode; } /** - * Set Scale Range Mode - * - * @param scaleRangeMode - * New value for Scale Range Mode - */ -void -ChartTwoCartesianAxis::setScaleRangeMode(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode) -{ - m_scaleRangeMode = scaleRangeMode; -} - -/** * @return Axis units */ CaretUnitsTypeEnum::Enum @@ -463,7 +492,7 @@ NumericFormatModeEnum::Enum ChartTwoCartesianAxis::getUserNumericFormat() const { - return m_userNumericFormat; + return getDisplayGroupOrThisAxisForGet()->m_userNumericFormat; } /** @@ -475,7 +504,7 @@ void ChartTwoCartesianAxis::setUserNumericFormat(const NumericFormatModeEnum::Enum numericFormat) { - m_userNumericFormat = numericFormat; + getDisplayGroupOrThisAxisForSet()->m_userNumericFormat = numericFormat; } /** @@ -525,7 +554,7 @@ float ChartTwoCartesianAxis::getLabelTextSize() const { - return m_labelTextSize; + return getDisplayGroupOrThisAxisForGet()->m_labelTextSize; } /** @@ -537,7 +566,7 @@ void ChartTwoCartesianAxis::setLabelTextSize(const float labelTextSize) { - m_labelTextSize = labelTextSize; + getDisplayGroupOrThisAxisForSet()->m_labelTextSize = labelTextSize; } /** @@ -546,7 +575,7 @@ float ChartTwoCartesianAxis::getNumericsTextSize() const { - return m_numericsTextSize; + return getDisplayGroupOrThisAxisForGet()->m_numericsTextSize; } /** @@ -558,7 +587,7 @@ void ChartTwoCartesianAxis::setNumericsTextSize(const float numericsTextSize) { - m_numericsTextSize = numericsTextSize; + getDisplayGroupOrThisAxisForSet()->m_numericsTextSize = numericsTextSize; } /** @@ -567,7 +596,7 @@ bool ChartTwoCartesianAxis::isNumericsTextDisplayed() const { - return m_numericsTextDisplayed; + return getDisplayGroupOrThisAxisForGet()->m_numericsTextDisplayed; } /** @@ -579,7 +608,7 @@ void ChartTwoCartesianAxis::setNumericsTextDisplayed(const bool numericsTextDisplayed) { - m_numericsTextDisplayed = numericsTextDisplayed; + getDisplayGroupOrThisAxisForSet()->m_numericsTextDisplayed = numericsTextDisplayed; } /** @@ -588,7 +617,7 @@ bool ChartTwoCartesianAxis::isNumericsTextRotated() const { - return m_numericsTextRotated; + return getDisplayGroupOrThisAxisForGet()->m_numericsTextRotated; } /** @@ -600,7 +629,7 @@ void ChartTwoCartesianAxis::setNumericsTextRotated(const bool numericsTextRotated) { - m_numericsTextRotated = numericsTextRotated; + getDisplayGroupOrThisAxisForSet()->m_numericsTextRotated = numericsTextRotated; } /** @@ -609,7 +638,7 @@ float ChartTwoCartesianAxis::getPaddingSize() const { - return m_paddingSize; + return getDisplayGroupOrThisAxisForGet()->m_paddingSize; } /** @@ -621,7 +650,7 @@ void ChartTwoCartesianAxis::setPaddingSize(const float paddingSize) { - m_paddingSize = paddingSize; + getDisplayGroupOrThisAxisForSet()->m_paddingSize = paddingSize; } /** @@ -671,347 +700,91 @@ return; } - // may not be in older scenes + /* may not be in older scenes */ + m_displayGroup = DisplayGroupEnum::DISPLAY_GROUP_TAB; m_displayedByUser = true; m_numericsTextDisplayed = true; m_numericsTextRotated = false; m_sceneAssistant->restoreMembers(sceneAttributes, - sceneClass); + sceneClass); //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); - } /** - * Given the bounds of the data, determine the auto range minimum and maximum values. - * - * @param minimumValue - * Minimum data value - * @param maximumValue - * Maximum data value - * @param minimumOut - * Output minimum value for autoranging. - * @param maximumOut - * Output maximum value for autoranging. - * @param stepValueOut - * Output step value for scale. - * @param digitsRightOfDecimalOut - * Output with digits right of decimal. - * @return - * True if output values are valid, else false. + * @return Axis from current display group selection. When the + * active display group is 'TAB', 'this' axis is returned. When a + * group is selected, the axis for that group's attributes is returned. */ -bool -ChartTwoCartesianAxis::getAutoRangeMinimumAndMaximum(const float minimumValue, - const float maximumValue, - float& minimumOut, - float& maximumOut, - float& stepValueOut, - int32_t& digitsRightOfDecimalOut) const -{ - float minValue = minimumValue; - float maxValue = maximumValue; - - if (maxValue > minValue) { - double scaleStep = 0.0; - double scaleMin = 0.0; - double scaleMax = 0.0; - int32_t digitsRightOfDecimal = 0; - - ChartScaleAutoRanging::createAutoScale(minValue, - maxValue, - scaleMin, - scaleMax, - scaleStep, - digitsRightOfDecimal); - minimumOut = scaleMin; - maximumOut = scaleMax; - stepValueOut = scaleStep; - digitsRightOfDecimalOut = digitsRightOfDecimal; - - //m_rangeMinimumValue = minimumOut; - //m_rangeMaximumValue = maximumOut; - - return true; +ChartTwoCartesianAxis* +ChartTwoCartesianAxis::getDisplayGroupOrThisAxisForSet() +{ + ChartTwoCartesianAxis* axis(this); + + /* + * if parent overlay set interface is NULL, then + * 'this' is a display group axes + */ + if (m_parentChartOverlaySetInterface != NULL) { + switch (m_displayGroup) { + case DisplayGroupEnum::DISPLAY_GROUP_A: + case DisplayGroupEnum::DISPLAY_GROUP_B: + case DisplayGroupEnum::DISPLAY_GROUP_C: + case DisplayGroupEnum::DISPLAY_GROUP_D: + /* + * + */ + axis = EventChartTwoCartesianAxisDisplayGroup::getAxisForDisplayGroup(m_displayGroup); + break; + case DisplayGroupEnum::DISPLAY_GROUP_TAB: + axis = this; + break; + } } + CaretAssert(axis); - return false; + return axis; } /** - * Get the axis scale text values and their positions for drawing the scale. - * - * @param minimumDataValue - * Minimum data value - * @param maximumDataValue - * Maximum data value - * @param axisLength - * Length of axis (no specific unit type is assumed) - * @param minimumOut - * Output minimum value for autoranging. - * @param maximumOut - * Output maximum value for autoranging. - * @param scaleValuesOffsetInPixelsOut - * Output containing offset in pixels for the scale values. - * @param scaleValuesOut - * Output containing text for scale values. - * @return - * True if output data is valid, else false. + * @return Axis from current display group selection. When the + * active display group is 'TAB', 'this' axis is returned. When a + * group is selected, the axis for that group's attributes is returned. */ -bool -ChartTwoCartesianAxis::getScaleValuesAndOffsets(const float minimumDataValue, - const float maximumDataValue, - const float axisLength, - float& minimumOut, - float& maximumOut, - std::vector& scaleValuesOffsetInPixelsOut, - std::vector& scaleValuesOut) const -{ - float minimumValue = minimumDataValue; - float maximumValue = maximumDataValue; - - minimumOut = 0.0; - maximumOut = 0.0; - scaleValuesOffsetInPixelsOut.clear(); - scaleValuesOut.clear(); - - if (axisLength <= 0.0) { - CaretAssert(0); - return false; - } - - float labelsStart = 0.0; - float labelsEnd = 0.0; - float labelsStep = 1.0; - int32_t labelsDigitsRightOfDecimal = 0; - - switch (m_scaleRangeMode) { - case ChartTwoAxisScaleRangeModeEnum::AUTO: - break; - case ChartTwoAxisScaleRangeModeEnum::DATA: - break; - case ChartTwoAxisScaleRangeModeEnum::USER: - switch (m_axisLocation) { - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - minimumValue = m_userScaleMinimumValue; - maximumValue = m_userScaleMaximumValue; - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - minimumValue = m_userScaleMinimumValue; - maximumValue = m_userScaleMaximumValue; - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - minimumValue = m_userScaleMinimumValue; - maximumValue = m_userScaleMaximumValue; - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - minimumValue = m_userScaleMinimumValue; - maximumValue = m_userScaleMaximumValue; - break; - } - break; - } - - if ( ! getAutoRangeMinimumAndMaximum(minimumValue, - maximumValue, - labelsStart, - labelsEnd, - labelsStep, - labelsDigitsRightOfDecimal)) { - return false; - } - - switch (m_scaleRangeMode) { - case ChartTwoAxisScaleRangeModeEnum::AUTO: - m_userScaleMinimumValue = labelsStart; - m_userScaleMaximumValue = labelsEnd; - break; - case ChartTwoAxisScaleRangeModeEnum::DATA: - { - const double range = maximumDataValue - minimumDataValue; - if (range > 0.0) { - const int32_t numSteps = MathFunctions::round((labelsEnd - labelsStart) / labelsStep); - if (numSteps > 0) { - labelsStart = minimumDataValue; - labelsEnd = maximumDataValue; - labelsStep = range / numSteps; - m_userScaleMinimumValue = labelsStart; - m_userScaleMaximumValue = labelsEnd; - } - } - } - break; - case ChartTwoAxisScaleRangeModeEnum::USER: -// labelsStart = m_userScaleMinimumValue; -// labelsEnd = m_userScaleMaximumValue; - { - const double range = m_userScaleMaximumValue - m_userScaleMinimumValue; - if (range > 0.0) { - const int32_t numSteps = MathFunctions::round((labelsEnd - labelsStart) / labelsStep); - if (numSteps > 0) { - labelsStart = m_userScaleMinimumValue; - labelsEnd = m_userScaleMaximumValue; - labelsStep = range / numSteps; - m_userScaleMinimumValue = labelsStart; - m_userScaleMaximumValue = labelsEnd; - } - } - } - break; - } - - switch (m_numericSubdivsionsMode) { - case ChartTwoNumericSubdivisionsModeEnum::AUTO: - break; - case ChartTwoNumericSubdivisionsModeEnum::USER: - { - const float labelsRange = labelsEnd - labelsStart; - if (labelsRange <= 0.0) { - return false; - } - const float dividend = (1.0 + m_userNumberOfSubdivisions); - labelsStep = labelsRange / dividend; - } - break; - } - - minimumOut = labelsStart; - maximumOut = labelsEnd; +const ChartTwoCartesianAxis* +ChartTwoCartesianAxis::getDisplayGroupOrThisAxisForGet() const +{ + const ChartTwoCartesianAxis* axis(this); /* - * If the "labels end" or "labels start" value is not valid (infinity or not-a-number) there - * are invalid values in the data and will cause the labels processing later - * in this method to fail. So, alert the user that there is a problem in - * the data. - * - * A set is used to track those models for which the user has - * already been alerted. Otherwise, the alert message will be - * displayed every time this method is called (which is many) and - * the user will receive endless pop-ups. + * if parent overlay set interface is NULL, then + * 'this' is a display group axes */ - if ( (! MathFunctions::isNumeric(labelsStart)) - || (! MathFunctions::isNumeric(labelsEnd))) { - const AString msg("Invalid numbers (infinity or not-a-number) found when trying to create chart. " - "Run \"wb_command -file-information\" on files being charted to find the file " - "that contains invalid data so that the file can be fixed."); - CaretLogWarning(msg); - return false; - } - - float labelsRange = (labelsEnd - labelsStart); - if (labelsRange <= 0.0) { - return false; - } - - const float tickLabelsStep = labelsStep; - if (tickLabelsStep <= 0.0) { - return false; - } - - const float onePercentRange = labelsRange * 0.01f; - - std::vector labelNumericValues; - - float labelValue = labelsStart; - while (labelValue <= labelsEnd) { - float labelParametricValue = (labelValue - labelsStart) / labelsRange; - - float labelValueForText = labelValue; - - if (labelsRange >= 10.0) { - /* - * Is this the first label? - */ - if (labelValue <= labelsStart) { + if (m_parentChartOverlaySetInterface != NULL) { + switch (m_displayGroup) { + case DisplayGroupEnum::DISPLAY_GROUP_A: + case DisplayGroupEnum::DISPLAY_GROUP_B: + case DisplayGroupEnum::DISPLAY_GROUP_C: + case DisplayGroupEnum::DISPLAY_GROUP_D: + { /* - * Handles case when the minimum DATA value is just a little - * bit greater than the minimum value for axis labels such - * as in Data-Series data when the minimum data value is "1" - * and the minimum axis label value is "0". Without this - * code no value is displayed at the left edge of the axis. + * Copy the yoked axis to this axis so that if the user switches back + * to TAB, the axes will remain the same */ - if (labelParametricValue < 0.0) { - const float nextParametricValue = ((labelValue + tickLabelsStep) - labelsStart) / labelsRange; - if (nextParametricValue > 0.05) { - labelParametricValue = 0.0; - labelValueForText = labelsStart; - } - } - } - - if (labelParametricValue < 0.0) { - if (labelParametricValue >= -0.01) { - labelParametricValue = 0.0; - } - } - - /* - * Is this the last label? - */ - if (labelValue >= labelsEnd) { - /* - * Like above, ensures a value is displayed at the right - * edge of the axis. - */ - if (labelParametricValue > 1.0) { - const float prevParametricValue = ((labelValue - tickLabelsStep) - labelsStart) / labelsRange; - if (prevParametricValue < 0.95) { - labelParametricValue = 1.0; - labelValueForText = labelsEnd; - } - } - } - - if (labelParametricValue > 1.0) { - if (labelParametricValue < 1.01) { - labelParametricValue = 1.0; - } - } - } - - if ((labelParametricValue >= 0.0) - && (labelParametricValue <= 1.0)) { - const float labelPixelsPosition = axisLength * labelParametricValue; - labelNumericValues.push_back(labelValueForText); - scaleValuesOffsetInPixelsOut.push_back(labelPixelsPosition); - } - else { - // std::cout << "Label value=" << labelValue << " parametric=" << labelParametricValue << " failed." << std::endl; - } - - labelValue += tickLabelsStep; - - /* - * It is possible that 'labelValue' may be slightly greater than 'labelsEnd' - * for the last label which results in the last label not displayed. - * So, if the 'labelValue' is slightly greater than 'labelsEnd', - * limit 'labelValue' so that the label at the end of the data range - * is displayed. - * - * Example: labelValue = 73.9500046 - * labelsEnd = 73.9499969 - */ - if (labelValue > labelsEnd) { - const float diff = labelValue - labelsEnd; - if (diff < onePercentRange) { - labelValue = labelsEnd; + ChartTwoCartesianAxis* yokedAxis = EventChartTwoCartesianAxisDisplayGroup::getAxisForDisplayGroup(m_displayGroup); + CaretAssert(yokedAxis); + const_cast(this)->copyAxisParameters(yokedAxis); } + break; + case DisplayGroupEnum::DISPLAY_GROUP_TAB: + axis = this; + break; } } + CaretAssert(axis); - const int32_t numValues = static_cast(labelNumericValues.size()); - if (numValues > 0) { - scaleValuesOut.resize(numValues); - NumericTextFormatting::formatValueRange(m_userNumericFormat, - m_userDigitsRightOfDecimal, - &labelNumericValues[0], - &scaleValuesOut[0], - labelNumericValues.size()); - } - - CaretAssert(scaleValuesOffsetInPixelsOut.size() == scaleValuesOut.size()); - return ( ! scaleValuesOut.empty()); + return axis; } - diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianAxis.h connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianAxis.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianAxis.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianAxis.h 2021-02-16 19:46:47.000000000 +0000 @@ -25,21 +25,24 @@ #include "CaretObject.h" #include "ChartAxisLocationEnum.h" -#include "CaretUnitsTypeEnum.h" #include "ChartTwoAxisScaleRangeModeEnum.h" +#include "ChartTwoCartesianSubdivisionsModeEnum.h" #include "ChartTwoNumericSubdivisionsModeEnum.h" +#include "CaretUnitsTypeEnum.h" +#include "DisplayGroupEnum.h" #include "NumericFormatModeEnum.h" #include "SceneableInterface.h" namespace caret { - class ChartTwoOverlaySet; + class ChartTwoCartesianCustomSubdivisions; + class ChartTwoOverlaySetInterface; class SceneClassAssistant; class ChartTwoCartesianAxis : public CaretObject, public SceneableInterface { public: - ChartTwoCartesianAxis(const ChartTwoOverlaySet* parentChartOverlaySet, + ChartTwoCartesianAxis(const ChartTwoOverlaySetInterface* parentChartOverlaySetInterface, const ChartAxisLocationEnum::Enum axisLocation); virtual ~ChartTwoCartesianAxis(); @@ -48,30 +51,32 @@ ChartTwoCartesianAxis& operator=(const ChartTwoCartesianAxis& obj); + void reset(); + + void copyAxisParameters(const ChartTwoCartesianAxis* axis); + ChartAxisLocationEnum::Enum getAxisLocation() const; bool isDisplayedByUser() const; void setDisplayedByUser(const bool displayed); + DisplayGroupEnum::Enum getDisplayGroup() const; + + void setDisplayGroup(const DisplayGroupEnum::Enum displayGroup); + void getDataRange(float& rangeMinimumOut, float& rangeMaximumOut) const; - float getUserScaleMinimumValue() const; - - void setUserScaleMinimumValue(const float value); + float getSceneUserScaleMinimumValue() const; - float getUserScaleMaximumValue() const; + float getSceneUserScaleMaximumValue() const; - void setUserScaleMaximumValue(const float value); - int32_t getUserDigitsRightOfDecimal() const; void setUserDigitsRightOfDecimal(const int32_t digitsRightOfDecimal); - ChartTwoAxisScaleRangeModeEnum::Enum getScaleRangeMode() const; - - void setScaleRangeMode(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode); + ChartTwoAxisScaleRangeModeEnum::Enum getSceneScaleRangeMode() const; CaretUnitsTypeEnum::Enum getUnits() const; @@ -89,10 +94,6 @@ void setUserNumberOfSubdivisions(const int32_t numberOfSubdivisions); - bool isEnabledByChart() const; - - void setEnabledByChart(const bool enabled); - bool isShowTickmarks() const; void setShowTickmarks(const bool showTickmarks); @@ -121,13 +122,13 @@ void setPaddingSize(const float paddingSize); - bool getScaleValuesAndOffsets(const float minimumDataValue, - const float maximumDataValue, - const float axisLength, - float& minimumOut, - float& maximumOut, - std::vector& scaleValuesOffsetInPixelsOut, - std::vector& scaleValuesOut) const; + ChartTwoCartesianCustomSubdivisions* getCustomSubdivisions(); + + const ChartTwoCartesianCustomSubdivisions* getCustomSubdivisions() const; + + ChartTwoCartesianSubdivisionsModeEnum::Enum getSubdivisionsMode() const; + + void setSubdivisionsMode(const ChartTwoCartesianSubdivisionsModeEnum::Enum subdivisionsMode); int32_t getLabelOverlayIndex(const int32_t maximumNumberOfOverlays) const; @@ -143,11 +144,6 @@ virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); - - - - - // If there will be sub-classes of this class that need to save // and restore data from scenes, these pure virtual methods can // be uncommented to force their implementation by sub-classes. @@ -161,21 +157,18 @@ private: void copyHelperChartTwoCartesianAxis(const ChartTwoCartesianAxis& obj); - bool getAutoRangeMinimumAndMaximum(const float minimumValue, - const float maximumValue, - float& minimumOut, - float& maximumOut, - float& stepValueOut, - int32_t& digitsRightOfDecimalOut) const; - - void limitUserScaleMinMaxToValidRange(); + const ChartTwoCartesianAxis* getDisplayGroupOrThisAxisForGet() const; - const ChartTwoOverlaySet* m_parentChartOverlaySet; + ChartTwoCartesianAxis* getDisplayGroupOrThisAxisForSet(); + + const ChartTwoOverlaySetInterface* m_parentChartOverlaySetInterface; const ChartAxisLocationEnum::Enum m_axisLocation; std::unique_ptr m_sceneAssistant; + DisplayGroupEnum::Enum m_displayGroup = DisplayGroupEnum::DISPLAY_GROUP_TAB; + mutable float m_userScaleMinimumValue = -100.0f; mutable float m_userScaleMaximumValue = 100.0f; @@ -197,6 +190,10 @@ int32_t m_userNumberOfSubdivisions = 2; + std::unique_ptr m_customSubdivisions; + + ChartTwoCartesianSubdivisionsModeEnum::Enum m_subdivisionsMode = ChartTwoCartesianSubdivisionsModeEnum::STANDARD; + /** size of label text*/ float m_labelTextSize = 2.5f; @@ -212,12 +209,10 @@ /** size of padding*/ float m_paddingSize = 0.0f; - bool m_enabledByChart = false; - bool m_showTickmarks = true; /** show axis label*/ - bool m_showLabel; + bool m_showLabel = true; /** user display the axis*/ bool m_displayedByUser = true; diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianCustomSubdivisions.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianCustomSubdivisions.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianCustomSubdivisions.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianCustomSubdivisions.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,532 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_DECLARE__ +#include "ChartTwoCartesianCustomSubdivisions.h" +#undef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_DECLARE__ + +#include "CaretAssert.h" +#include "CaretResult.h" +#include "ChartTwoCartesianCustomSubdivisionsLabel.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoCartesianCustomSubdivisions + * \brief Custom axis for cartesian charts + * \ingroup Charting + */ + +/** + * Constructor. + */ +ChartTwoCartesianCustomSubdivisions::ChartTwoCartesianCustomSubdivisions() +: CaretObject() +{ + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + reset(); +} + +/** + * Destructor. + */ +ChartTwoCartesianCustomSubdivisions::~ChartTwoCartesianCustomSubdivisions() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoCartesianCustomSubdivisions::ChartTwoCartesianCustomSubdivisions(const ChartTwoCartesianCustomSubdivisions& obj) +: CaretObject(obj), +SceneableInterface(obj) +{ + this->copyHelperChartTwoCartesianCustomSubdivisions(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoCartesianCustomSubdivisions& +ChartTwoCartesianCustomSubdivisions::operator=(const ChartTwoCartesianCustomSubdivisions& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperChartTwoCartesianCustomSubdivisions(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoCartesianCustomSubdivisions::copyHelperChartTwoCartesianCustomSubdivisions(const ChartTwoCartesianCustomSubdivisions& obj) +{ + m_labels.clear(); + + for (const auto& label : obj.m_labels) { + addLabel(new ChartTwoCartesianCustomSubdivisionsLabel(*label)); + } +} + +/** + * @return Number of labels + */ +int32_t +ChartTwoCartesianCustomSubdivisions::getNumberOfLabels() const +{ + return m_labels.size(); +} + +/** + * @return Numeric value for label at the given index + * @param index + * Index of label + */ +float +ChartTwoCartesianCustomSubdivisions::getLabelNumericValue(const int32_t index) const +{ + return getLabel(index)->getNumericValue(); +} + +/** + * Set the numeric value for the label at the given index + * @param index + * Index of label + * @param value + * New value for label + */ +void +ChartTwoCartesianCustomSubdivisions::setLabelNumericValue(const int32_t index, + const float value) +{ + getLabel(index)->setNumericValue(value); +} + +/** + * @return Text for label at the given index + * @param index + * Index of label + */ +AString +ChartTwoCartesianCustomSubdivisions::getLabelText(const int32_t index) const +{ + return getLabel(index)->getCustomText(); +} + +/** + * Set the text value for the label at the given index + * @param index + * Index of label + * @param text + * New text for label + */ +void +ChartTwoCartesianCustomSubdivisions::setLabelText(const int32_t index, + const AString& text) +{ + getLabel(index)->setCustomText(text); +} + +/** + * Sort the label's in descending order using the numeric values. + * @return True if the order of the labels is changed. + */ +bool +ChartTwoCartesianCustomSubdivisions::sortLabelsByNumericValue() +{ + /* + * Avoid sorting if labels are in descending order + * using the label's value + */ + bool needSorting = false; + const int32_t numLabels = static_cast(m_labels.size()); + if (numLabels > 1) { + for (int32_t i = 0; i < (numLabels - 1); i++) { + CaretAssertVectorIndex(m_labels, (i + 1)); + if (m_labels[i + 1]->getNumericValue() > m_labels[i]->getNumericValue()) { + needSorting = true; + break; + } + } + } + + if ( ! needSorting) { + return false; + } + + std::sort(m_labels.begin(), + m_labels.end(), + [](const std::unique_ptr& lhs, + const std::unique_ptr& rhs) -> bool + { return ( ! (lhs->getNumericValue() > rhs->getNumericValue())); } ); + + return true; +} + + +/** + * Add a label. This instance will take ownership of the given label. + * Label will be inserted at the bottom (end). It may be necessary + * to sort the labels after calling this method. + * @param label + * Label that is added. + */ +void +ChartTwoCartesianCustomSubdivisions::addLabel(ChartTwoCartesianCustomSubdivisionsLabel* label) +{ + CaretAssert(label); + std::unique_ptr ptr(label); + m_labels.push_back(std::move(ptr)); +} + +/** + * Reset to default of two labels + */ +void +ChartTwoCartesianCustomSubdivisions::reset() +{ + m_labels.clear(); + + ChartTwoCartesianCustomSubdivisionsLabel* labelOne = new ChartTwoCartesianCustomSubdivisionsLabel(); + labelOne->setCustomText("1.0"); + labelOne->setNumericValue(1.0); + addLabel(labelOne); + + ChartTwoCartesianCustomSubdivisionsLabel* labelTwo = new ChartTwoCartesianCustomSubdivisionsLabel(); + labelTwo->setCustomText("0.0"); + labelTwo->setNumericValue(0.0); + addLabel(labelTwo); +} + +/** + * @return Label at the given index + * @param index + * Index of label + */ +ChartTwoCartesianCustomSubdivisionsLabel* +ChartTwoCartesianCustomSubdivisions::getLabel(const int32_t index) +{ + CaretAssertVectorIndex(m_labels, index); + return m_labels[index].get(); +} + +/** + * @return Label at the given index (const method) + * @param index + * Index of label + */ +const ChartTwoCartesianCustomSubdivisionsLabel* +ChartTwoCartesianCustomSubdivisions::getLabel(const int32_t index) const +{ + CaretAssertVectorIndex(m_labels, index); + return m_labels[index].get(); +} + +/** + * @return Label at the given index of -1 if not found + * @param label + * Label for which index is sought + */ +int32_t +ChartTwoCartesianCustomSubdivisions::getIndexOfLabel(const ChartTwoCartesianCustomSubdivisionsLabel* label) +{ + CaretAssert(label); + const int32_t numLabels = static_cast(m_labels.size()); + for (int32_t i = 0; i < numLabels; i++) { + CaretAssertVectorIndex(m_labels, i); + if (m_labels[i].get() == label) { + return i; + } + } + return -1; +} + +/** + * Insert a lable above (lower index) the given index. Index 0 is at the top. + * @param index + * Index of label that has label inserted above it (lower index) + * @return a CaretResult indicating success or error + */ +std::unique_ptr +ChartTwoCartesianCustomSubdivisions::insertLabelAbove(const int32_t index) +{ + if ( ! isValidLabelIndex(index)) { + return CaretResult::newInstanceError("Invalid index for inserting label above."); + } + + float value(0.0); + if (index == 0) { + CaretAssertVectorIndex(m_labels, index); + value = m_labels[index]->getNumericValue() + 1.0; + } + else { + CaretAssertVectorIndex(m_labels, index); + CaretAssertVectorIndex(m_labels, index - 1); + value = ((m_labels[index]->getNumericValue() + + m_labels[index - 1]->getNumericValue()) + / 2.0); + } + + std::unique_ptr ptr(new ChartTwoCartesianCustomSubdivisionsLabel()); + ptr->setNumericValue(value); + ptr->setCustomText(QString::number(value, 'f')); + m_labels.insert(m_labels.begin() + index, + std::move(ptr)); + + return CaretResult::newInstanceSuccess(); +} + +/** + * Insert a lable below (higher index) the given index. Index 0 is at the top. + * @param index + * Index of label that has label inserted below it (higher index) + * @return a CaretResult indicating success or error + */ +std::unique_ptr +ChartTwoCartesianCustomSubdivisions::insertLabelBelow(const int32_t index) +{ + if ( ! isValidLabelIndex(index)) { + return CaretResult::newInstanceError("Invalid index for inserting label below."); + } + + const int32_t lastIndex(getNumberOfLabels() - 1); + float value(0.0); + if (index == lastIndex) { + CaretAssertVectorIndex(m_labels, index); + value = m_labels[index]->getNumericValue() - 1.0; + } + else { + CaretAssertVectorIndex(m_labels, index); + CaretAssertVectorIndex(m_labels, index + 1); + value = ((m_labels[index]->getNumericValue() + + m_labels[index + 1]->getNumericValue()) + / 2.0); + } + + std::unique_ptr ptr(new ChartTwoCartesianCustomSubdivisionsLabel()); + ptr->setNumericValue(value); + ptr->setCustomText(QString::number(value, 'f')); + m_labels.insert(m_labels.begin() + index + 1, + std::move(ptr)); + + return CaretResult::newInstanceSuccess(); +} + + +/** + * Remove the given label + * @param label + * Label to remove + * @return a CaretResult indicating success or error + */ +std::unique_ptr +ChartTwoCartesianCustomSubdivisions::removeLabel(ChartTwoCartesianCustomSubdivisionsLabel* label) +{ + CaretAssert(label); + const int32_t index = getIndexOfLabel(label); + if (index >= 0) { + return removeLabelAtIndex(index); + } + return CaretResult::newInstanceError("Label not found, unable to remove"); +} + +/** + * Remove the label at the given index + * @param index + * Index of label to remove + * @return a CaretResult indicating success or error + */ +std::unique_ptr +ChartTwoCartesianCustomSubdivisions::removeLabelAtIndex(const int32_t index) +{ + if (m_labels.size() <= 2) { + return CaretResult::newInstanceError("Cannot remove label (minimum number of labels has been reached)"); + } + + if (isValidLabelIndex(index)) { + m_labels.erase(m_labels.begin() + index); + return CaretResult::newInstanceSuccess(); + } + + return CaretResult::newInstanceError("Invalid index for removing label"); +} + +/** + * Get the valid range of values for a spin box containing the label value at the given index + * @param index + * Index of the label + * @param rangeMinimumOut + * Minimum value allowed in spin box + * @param rangeMaximumOut + * Maximum value allowed in spin box + */ +void +ChartTwoCartesianCustomSubdivisions::getRangeForLabelAtIndex(const int32_t index, + float& rangeMinimumOut, + float& rangeMaximumout) const +{ + rangeMaximumout = std::numeric_limits::max(); + rangeMinimumOut = -rangeMaximumout; + + const int32_t numLabels = getNumberOfLabels(); + if (numLabels <= 1) { + return; + } + + /* + * Values are descending starting at index zero + */ + if (index < (numLabels - 1)) { + const ChartTwoCartesianCustomSubdivisionsLabel* labelBelow = getLabel(index + 1); + rangeMinimumOut = labelBelow->getNumericValue(); + } + if (index > 0) { + const ChartTwoCartesianCustomSubdivisionsLabel* labelAbove = getLabel(index - 1); + rangeMaximumout = labelAbove->getNumericValue(); + } +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoCartesianCustomSubdivisions::toString() const +{ + return "ChartTwoCartesianCustomSubdivisions"; +} + +/** + * @return True if the given label index is valid, else false. + * @param index + * Index of the label. + */ +bool +ChartTwoCartesianCustomSubdivisions::isValidLabelIndex(const int32_t index) const +{ + if ((index >= 0) + && (index < static_cast(m_labels.size()))) { + return true; + } + return false; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoCartesianCustomSubdivisions::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoCartesianCustomSubdivisions", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + if ( ! m_labels.empty()) { + std::vector labelsVector; + for (const auto& label : m_labels) { + labelsVector.push_back(label->saveToScene(sceneAttributes, + "LabelInstance")); + } + SceneClassArray* sceneClassArray = new SceneClassArray("m_labels", + labelsVector); + sceneClass->addChild(sceneClassArray); + } + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoCartesianCustomSubdivisions::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + m_labels.clear(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + const SceneClassArray* labelsArray = sceneClass->getClassArray("m_labels"); + if (labelsArray != NULL) { + const int32_t numLabels = labelsArray->getNumberOfArrayElements(); + for (int32_t i = 0; i < numLabels; i++) { + const SceneClass* labelClass = labelsArray->getClassAtIndex(i); + ChartTwoCartesianCustomSubdivisionsLabel* label = new ChartTwoCartesianCustomSubdivisionsLabel(); + label->restoreFromScene(sceneAttributes, labelClass); + addLabel(label); + } + } + + if (m_labels.size() < 2) { + reset(); + } + + sortLabelsByNumericValue(); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianCustomSubdivisions.h connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianCustomSubdivisions.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianCustomSubdivisions.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianCustomSubdivisions.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,128 @@ +#ifndef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_H__ +#define __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "SceneableInterface.h" + + +namespace caret { + class CaretResult; + class ChartTwoCartesianCustomSubdivisionsLabel; + class SceneClassAssistant; + + class ChartTwoCartesianCustomSubdivisions : public CaretObject, public SceneableInterface { + + public: + ChartTwoCartesianCustomSubdivisions(); + + virtual ~ChartTwoCartesianCustomSubdivisions(); + + ChartTwoCartesianCustomSubdivisions(const ChartTwoCartesianCustomSubdivisions& obj); + + ChartTwoCartesianCustomSubdivisions& operator=(const ChartTwoCartesianCustomSubdivisions& obj); + + void reset(); + + int32_t getNumberOfLabels() const; + + float getLabelNumericValue(const int32_t index) const; + + void setLabelNumericValue(const int32_t index, + const float value); + + AString getLabelText(const int32_t index) const; + + void setLabelText(const int32_t index, + const AString& text); + + std::unique_ptr insertLabelAbove(const int32_t index); + + std::unique_ptr insertLabelBelow(const int32_t index); + + std::unique_ptr removeLabelAtIndex(const int32_t index); + + void getRangeForLabelAtIndex(const int32_t index, + float& rangeMinimumOut, + float& rangeMaximumout) const; + + bool sortLabelsByNumericValue(); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperChartTwoCartesianCustomSubdivisions(const ChartTwoCartesianCustomSubdivisions& obj); + + void addLabel(ChartTwoCartesianCustomSubdivisionsLabel* label); + + bool isValidLabelIndex(const int32_t index) const; + + int32_t getIndexOfLabel(const ChartTwoCartesianCustomSubdivisionsLabel* label); + + ChartTwoCartesianCustomSubdivisionsLabel* getLabel(const int32_t index); + + const ChartTwoCartesianCustomSubdivisionsLabel* getLabel(const int32_t index) const; + + std::unique_ptr removeLabel(ChartTwoCartesianCustomSubdivisionsLabel* label); + + std::unique_ptr m_sceneAssistant; + + std::vector> m_labels; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_DECLARE__ + // +#endif // __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_DECLARE__ + +} // namespace +#endif //__CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,227 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_LABEL_DECLARE__ +#include "ChartTwoCartesianCustomSubdivisionsLabel.h" +#undef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_LABEL_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoCartesianCustomSubdivisionsLabel + * \brief Axis label for custom cartesian axis + * \ingroup Charting + */ + +/** + * Constructor. + */ +ChartTwoCartesianCustomSubdivisionsLabel::ChartTwoCartesianCustomSubdivisionsLabel() +: CaretObject() +{ + initializeInstance(); +} + +/** + * Destructor. + */ +ChartTwoCartesianCustomSubdivisionsLabel::~ChartTwoCartesianCustomSubdivisionsLabel() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoCartesianCustomSubdivisionsLabel::ChartTwoCartesianCustomSubdivisionsLabel(const ChartTwoCartesianCustomSubdivisionsLabel& obj) +: CaretObject(obj), +SceneableInterface(obj) +{ + initializeInstance(); + this->copyHelperChartTwoCartesianCustomSubdivisionsLabel(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoCartesianCustomSubdivisionsLabel& +ChartTwoCartesianCustomSubdivisionsLabel::operator=(const ChartTwoCartesianCustomSubdivisionsLabel& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperChartTwoCartesianCustomSubdivisionsLabel(obj); + } + return *this; +} + +/** + * Less than operator for sorting by numeric value + * @param customAxisLabel + * Label for comparison + * @return True if 'this' is less than given label using numeric value + */ +bool +ChartTwoCartesianCustomSubdivisionsLabel::operator<(const ChartTwoCartesianCustomSubdivisionsLabel& customAxisLabel) const +{ + return (m_numericValue < customAxisLabel.m_numericValue); +} + +/** + * Initialize an instance of this class + */ +void +ChartTwoCartesianCustomSubdivisionsLabel::initializeInstance() +{ + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_numericValue", + &m_numericValue); + m_sceneAssistant->add("m_customLabelText", + &m_customLabelText); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoCartesianCustomSubdivisionsLabel::copyHelperChartTwoCartesianCustomSubdivisionsLabel(const ChartTwoCartesianCustomSubdivisionsLabel& obj) +{ + m_numericValue = obj.m_numericValue; + m_customLabelText = obj.m_customLabelText; +} + +/** + * @return The numeric value + */ +float +ChartTwoCartesianCustomSubdivisionsLabel::getNumericValue() const +{ + return m_numericValue; +} + +/** + * Set the numeric value + * @param numericValue + * New numeric value + */ +void +ChartTwoCartesianCustomSubdivisionsLabel::setNumericValue(const float numericValue) +{ + m_numericValue = numericValue; +} + +/** + * @return The custom text + */ +AString +ChartTwoCartesianCustomSubdivisionsLabel::getCustomText() const +{ + return m_customLabelText; +} + +/** + * Set the custom text + * @param customText + * New custom text for label + */ +void +ChartTwoCartesianCustomSubdivisionsLabel::setCustomText(const AString& customText) +{ + m_customLabelText = customText; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoCartesianCustomSubdivisionsLabel::toString() const +{ + return "ChartTwoCartesianCustomSubdivisionsLabel"; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoCartesianCustomSubdivisionsLabel::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoCartesianCustomSubdivisionsLabel", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoCartesianCustomSubdivisionsLabel::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); +} + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.h connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,101 @@ +#ifndef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_LABEL_H__ +#define __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_LABEL_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + + class ChartTwoCartesianCustomSubdivisionsLabel : public CaretObject, public SceneableInterface { + + public: + ChartTwoCartesianCustomSubdivisionsLabel(); + + virtual ~ChartTwoCartesianCustomSubdivisionsLabel(); + + ChartTwoCartesianCustomSubdivisionsLabel(const ChartTwoCartesianCustomSubdivisionsLabel& obj); + + ChartTwoCartesianCustomSubdivisionsLabel& operator=(const ChartTwoCartesianCustomSubdivisionsLabel& obj); + + bool operator<(const ChartTwoCartesianCustomSubdivisionsLabel& customAxisLabel) const; + + float getNumericValue() const; + + void setNumericValue(const float numericValue); + + AString getCustomText() const; + + void setCustomText(const AString& customText); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperChartTwoCartesianCustomSubdivisionsLabel(const ChartTwoCartesianCustomSubdivisionsLabel& obj); + + void initializeInstance(); + + std::unique_ptr m_sceneAssistant; + + float m_numericValue = 0.0; + + AString m_customLabelText; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_LABEL_DECLARE__ + // +#endif // __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_LABEL_DECLARE__ + +} // namespace +#endif //__CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_LABEL_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianOrientedAxes.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianOrientedAxes.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianOrientedAxes.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianOrientedAxes.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,1274 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_CARTESIAN_ORIENTATED_AXES_DECLARE__ +#include "ChartTwoCartesianOrientedAxes.h" +#undef __CHART_TWO_CARTESIAN_ORIENTATED_AXES_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "ChartScaleAutoRanging.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoCartesianCustomSubdivisions.h" +#include "ChartTwoCartesianCustomSubdivisionsLabel.h" +#include "ChartTwoOverlaySetInterface.h" +#include "EventChartTwoCartesianOrientedAxesYoking.h" +#include "EventManager.h" +#include "MathFunctions.h" +#include "NumericTextFormatting.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoCartesianOrientedAxes + * \brief Axis orientation that contains two axes + * \ingroup Charting + */ + +/** + * Constructor. + * @param orientationType + * The orientation type + */ +ChartTwoCartesianOrientedAxes::ChartTwoCartesianOrientedAxes(const ChartTwoOverlaySetInterface* parentChartOverlaySetInterface, + const ChartTwoAxisOrientationTypeEnum::Enum orientationType) +: CaretObject(), +m_parentChartOverlaySetInterface(parentChartOverlaySetInterface), +m_orientationType(orientationType) +{ + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + + switch (m_orientationType) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + m_leftOrBottomAxis.reset(new ChartTwoCartesianAxis(parentChartOverlaySetInterface, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM)); + m_rightOrTopAxis.reset(new ChartTwoCartesianAxis(parentChartOverlaySetInterface, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP)); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + m_leftOrBottomAxis.reset(new ChartTwoCartesianAxis(parentChartOverlaySetInterface, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT)); + m_rightOrTopAxis.reset(new ChartTwoCartesianAxis(parentChartOverlaySetInterface, + ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT)); + break; + } + + reset(); + + m_sceneAssistant->add("m_scaleRangeMode", + &m_scaleRangeMode); + m_sceneAssistant->add("m_userScaleMinimumValue", + &m_userScaleMinimumValue); + m_sceneAssistant->add("m_userScaleMaximumValue", + &m_userScaleMaximumValue); + m_sceneAssistant->add("m_leftOrBottomAxis", + "ChartTwoCartesianAxis", + m_leftOrBottomAxis.get()); + m_sceneAssistant->add("m_rightOrTopAxis", + "ChartTwoCartesianAxis", + m_rightOrTopAxis.get()); + m_sceneAssistant->add("m_transformationEnabled", + &m_transformationEnabled); +} + +/** + * Destructor. + */ +ChartTwoCartesianOrientedAxes::~ChartTwoCartesianOrientedAxes() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoCartesianOrientedAxes::ChartTwoCartesianOrientedAxes(const ChartTwoCartesianOrientedAxes& obj) +: CaretObject(obj), +SceneableInterface(obj), +m_orientationType(obj.m_orientationType) +{ + this->copyHelperChartTwoCartesianOrientedAxes(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoCartesianOrientedAxes& +ChartTwoCartesianOrientedAxes::operator=(const ChartTwoCartesianOrientedAxes& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperChartTwoCartesianOrientedAxes(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoCartesianOrientedAxes::copyHelperChartTwoCartesianOrientedAxes(const ChartTwoCartesianOrientedAxes& obj) +{ + m_scaleRangeMode = obj.m_scaleRangeMode; + m_userScaleMinimumValue = obj.m_userScaleMinimumValue; + m_userScaleMaximumValue = obj.m_userScaleMaximumValue; + *m_leftOrBottomAxis = *obj.m_leftOrBottomAxis; + *m_rightOrTopAxis = *obj.m_rightOrTopAxis; + m_transformationEnabled = obj.m_transformationEnabled; +} + +/* + * Reset axis to its defaults + */ +void +ChartTwoCartesianOrientedAxes::reset() +{ + m_scaleRangeMode = ChartTwoAxisScaleRangeModeEnum::AUTO; + m_userScaleMinimumValue = -100.0f; + m_userScaleMaximumValue = 100.0f; + m_transformationEnabled = false; + + m_leftOrBottomAxis->reset(); + m_rightOrTopAxis->reset(); +} + + +/** + * @return The orientation type + */ +ChartTwoAxisOrientationTypeEnum::Enum +ChartTwoCartesianOrientedAxes::getOrientationType() +{ + return m_orientationType; +} + +/** + * @return Scale Range Mode + */ +ChartTwoAxisScaleRangeModeEnum::Enum +ChartTwoCartesianOrientedAxes::getScaleRangeMode() const +{ + return m_scaleRangeMode; +} + +/** + * Copy the left/bottom and right/top axes + * @param axes + * Axes to copy to 'this' + */ +void +ChartTwoCartesianOrientedAxes::copyAxes(const ChartTwoCartesianOrientedAxes* axes) +{ + *m_leftOrBottomAxis = *axes->m_leftOrBottomAxis; + *m_rightOrTopAxis = *axes->m_rightOrTopAxis; +} + + +/** + * @return The left axis (vertical orientation) or bottom axis (horizontal orientation) + */ +ChartTwoCartesianAxis* +ChartTwoCartesianOrientedAxes::getLeftOrBottomAxis() +{ + return m_leftOrBottomAxis.get(); +} + +/** + * @return The left axis (vertical orientation) or bottom axis (horizontal orientation) const method + */ +const ChartTwoCartesianAxis* +ChartTwoCartesianOrientedAxes::getLeftOrBottomAxis() const +{ + return m_leftOrBottomAxis.get(); +} + +/** + * @return The right axis (vertical orientation) or top axis (horizontal orientation) + */ +ChartTwoCartesianAxis* +ChartTwoCartesianOrientedAxes::getRightOrTopAxis() +{ + return m_rightOrTopAxis.get(); +} + +/** + * @return The right axis (vertical orientation) or top axis (horizontal orientation), const method + */ +const ChartTwoCartesianAxis* +ChartTwoCartesianOrientedAxes::getRightOrTopAxis() const +{ + return m_rightOrTopAxis.get(); +} + +void +ChartTwoCartesianOrientedAxes::initializeScaleRangeMode(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode) +{ + m_scaleRangeMode = scaleRangeMode; +} + +/** + * Set Scale Range Mode when called from GUI + * + * @param scaleRangeMode + * New value for Scale Range Mode + */ +void +ChartTwoCartesianOrientedAxes::setScaleRangeModeFromGUI(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode) +{ + /* + * If yoking is changing to yoking mode and was non-yoking mode + */ + if (ChartTwoAxisScaleRangeModeEnum::isYokingRangeMode(scaleRangeMode)) { + if ( ! ChartTwoAxisScaleRangeModeEnum::isYokingRangeMode(m_scaleRangeMode)) { + /* + * See if any axes are yoked to the range (yoking) mode + */ + std::vector yokedAxes = EventChartTwoCartesianOrientedAxesYoking::getYokedAxes(m_orientationType, + scaleRangeMode); + + m_scaleRangeMode = scaleRangeMode; + + /* + * No other axes yoked, then set yoking min/max in the yoking manager + */ + if (yokedAxes.empty()) { + /* + * Since no axes yoked, initialize yoked range with "this" range + */ + EventChartTwoCartesianOrientedAxesYoking::setMinMaxValues(m_orientationType, + m_scaleRangeMode, + m_userScaleMinimumValue, + m_userScaleMaximumValue); + } + } + } + m_scaleRangeMode = scaleRangeMode; + + updateMinMaxValuesForYoking(); +} + +/* + * Get the minimum and maximum data values for this axis + * @param minimumValueOut + * Minimum value out + * @param maximumValueOut + * Maximum value out + */ +void +ChartTwoCartesianOrientedAxes::getDataRange(float& minimumValueOut, + float& maximumValueOut) const +{ + if ( ! m_parentChartOverlaySetInterface->getDataRangeForAxisOrientation(m_orientationType, minimumValueOut, maximumValueOut)) { + minimumValueOut = 0.0; + maximumValueOut = 0.0; + } +} + +/** + * Get user scale minimum and maximum value + * This is more efficient than calling both getUserScaleMinimumValue() and getUserScaleMaximumValue() + * @param minimumOut + * Output with minimum value + * @param maximumOut + * Output with maximum value + */ +void +ChartTwoCartesianOrientedAxes::getUserScaleMinimumMaximumValues(float& minimumOut, + float& maximumOut) const +{ + updateMinMaxValuesForYoking(); + + minimumOut = m_userScaleMinimumValue; + maximumOut = m_userScaleMaximumValue; +} + +/** + * @return User scale's minimum value + */ +float +ChartTwoCartesianOrientedAxes::getUserScaleMinimumValue() const +{ + updateMinMaxValuesForYoking(); + + return m_userScaleMinimumValue; +} + +/** + * @return User scale's maximum value + */ +float +ChartTwoCartesianOrientedAxes::getUserScaleMaximumValue() const +{ + updateMinMaxValuesForYoking(); + + return m_userScaleMaximumValue; +} + +/** + * Update the minimum and maximum values if yoking is on + */ +void +ChartTwoCartesianOrientedAxes::updateMinMaxValuesForYoking() const +{ + switch (m_scaleRangeMode) { + case ChartTwoAxisScaleRangeModeEnum::AUTO: + { + float minValue(0.0), maxValue(0.0); + getDataRange(minValue, + maxValue); + float dummyStepValue(0.0); + int32_t dummyDigitsRightOfDecimal(0); + getAutoRangeMinimumAndMaximum(minValue, maxValue, + m_userScaleMinimumValue, m_userScaleMaximumValue, + dummyStepValue, dummyDigitsRightOfDecimal); + } + break; + case ChartTwoAxisScaleRangeModeEnum::DATA: + { + getDataRange(m_userScaleMinimumValue, + m_userScaleMaximumValue); + } + break; + case ChartTwoAxisScaleRangeModeEnum::USER: + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_A: + case ChartTwoAxisScaleRangeModeEnum::YOKE_B: + case ChartTwoAxisScaleRangeModeEnum::YOKE_C: + case ChartTwoAxisScaleRangeModeEnum::YOKE_D: + { + /* + * When yoked, use min/max that is stored in the yoking manager + */ + float minValue(0.0), maxValue(0.0); + EventChartTwoCartesianOrientedAxesYoking::getMinMaxValues(m_orientationType, + m_scaleRangeMode, + minValue, + maxValue); + m_userScaleMinimumValue = minValue; + m_userScaleMaximumValue = maxValue; + } + break; + } +} + +/** + * Set User scale's maximum value + * @param userScaleMaximumValue + * New value for User scale's maximum value + */ +void +ChartTwoCartesianOrientedAxes::setUserScaleMaximumValueFromGUI(const float userScaleMaximumValue) +{ + if (ChartTwoAxisScaleRangeModeEnum::isYokingRangeMode(m_scaleRangeMode)) { + /* + * When yoked, min/max are stored in yoking manager + */ + EventChartTwoCartesianOrientedAxesYoking::setMaximumValue(m_orientationType, + m_scaleRangeMode, + userScaleMaximumValue); + return; + } + + m_userScaleMaximumValue = userScaleMaximumValue; + if (m_userScaleMaximumValue < m_userScaleMinimumValue) { + m_userScaleMinimumValue = m_userScaleMaximumValue; + } + updateRangeModeAfterMinimumOrMaximumChanged(); +} + +/** + * Set User scale's minimum value + * @param userScaleMinimumValue + * New value for User scale's minimum value + */ +void +ChartTwoCartesianOrientedAxes::setUserScaleMinimumValueFromGUI(const float userScaleMinimumValue) +{ + if (ChartTwoAxisScaleRangeModeEnum::isYokingRangeMode(m_scaleRangeMode)) { + /* + * When yoked, min/max are stored in yoking manager + */ + EventChartTwoCartesianOrientedAxesYoking::setMinimumValue(m_orientationType, + m_scaleRangeMode, + userScaleMinimumValue); + return; + } + + m_userScaleMinimumValue = userScaleMinimumValue; + if (m_userScaleMinimumValue > m_userScaleMaximumValue) { + m_userScaleMaximumValue = m_userScaleMinimumValue; + } + updateRangeModeAfterMinimumOrMaximumChanged(); +} + +/** + * Called to possibly change the range scale + */ +void +ChartTwoCartesianOrientedAxes::updateRangeModeAfterMinimumOrMaximumChanged() +{ + switch (m_scaleRangeMode) { + case ChartTwoAxisScaleRangeModeEnum::AUTO: + m_scaleRangeMode = ChartTwoAxisScaleRangeModeEnum::USER; + break; + case ChartTwoAxisScaleRangeModeEnum::DATA: + m_scaleRangeMode = ChartTwoAxisScaleRangeModeEnum::USER; + break; + case ChartTwoAxisScaleRangeModeEnum::USER: + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_A: + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_B: + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_C: + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_D: + break; + } +} + +/* + * Reset the user scale range. + * For AUTO and DATA, no action is taken since they already use the data range. + * For USER, the range is reset to the data range. + * For YOKING, the range is set to the minimum and maximum from all axes yoked to the group. + */ +void +ChartTwoCartesianOrientedAxes::resetUserScaleRange() +{ + switch (m_scaleRangeMode) { + case ChartTwoAxisScaleRangeModeEnum::AUTO: + break; + case ChartTwoAxisScaleRangeModeEnum::DATA: + break; + case ChartTwoAxisScaleRangeModeEnum::USER: + { + /* + * Reset to range of data + */ + float minValue(0.0), maxValue(0.0); + getDataRange(minValue, maxValue); + m_userScaleMinimumValue = minValue; + m_userScaleMaximumValue = maxValue; + } + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_A: + case ChartTwoAxisScaleRangeModeEnum::YOKE_B: + case ChartTwoAxisScaleRangeModeEnum::YOKE_C: + case ChartTwoAxisScaleRangeModeEnum::YOKE_D: + { + /* + * Get range of all yoked charts + */ + float minValue(0.0), maxValue(0.0); + if (EventChartTwoCartesianOrientedAxesYoking::getDataRangeMinMaxValues(m_orientationType, + m_scaleRangeMode, + minValue, + maxValue)) { + /* + * Set yoked range + */ + EventChartTwoCartesianOrientedAxesYoking::setMinMaxValues(m_orientationType, + m_scaleRangeMode, + minValue, + maxValue); + } + } + break; + } +} + + +/** + * Set the range mode and user scale from an old scene + * @param scaleRangeMode + * Range mode + * @param minimumValue + * Minimum user scale + * @param maximumValue + * Maximum user scale + */ +void +ChartTwoCartesianOrientedAxes::setRangeModeAndUserScaleFromVersionOneScene(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode, + const float userScaleMinimumValue, + const float userScaleMaximumValue) +{ + /* + * Note: version one did not have yoking + */ + m_scaleRangeMode = scaleRangeMode; + m_userScaleMinimumValue = userScaleMinimumValue; + m_userScaleMaximumValue = userScaleMaximumValue; +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoCartesianOrientedAxes::toString() const +{ + return "ChartTwoCartesianOrientedAxes"; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoCartesianOrientedAxes::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoCartesianOrientedAxes", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoCartesianOrientedAxes::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + reset(); + + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + +/** + * Given the bounds of the data, determine the auto range minimum and maximum values. + * + * @param minimumValue + * Minimum data value + * @param maximumValue + * Maximum data value + * @param minimumOut + * Output minimum value for autoranging. + * @param maximumOut + * Output maximum value for autoranging. + * @param stepValueOut + * Output step value for scale. + * @param digitsRightOfDecimalOut + * Output with digits right of decimal. + * @return + * True if output values are valid, else false. + */ +bool +ChartTwoCartesianOrientedAxes::getAutoRangeMinimumAndMaximum(const float minimumValue, + const float maximumValue, + float& minimumOut, + float& maximumOut, + float& stepValueOut, + int32_t& digitsRightOfDecimalOut) const +{ + float minValue = minimumValue; + float maxValue = maximumValue; + + if (maxValue > minValue) { + double scaleStep = 0.0; + double scaleMin = 0.0; + double scaleMax = 0.0; + int32_t digitsRightOfDecimal = 0; + + ChartScaleAutoRanging::createAutoScale(minValue, + maxValue, + scaleMin, + scaleMax, + scaleStep, + digitsRightOfDecimal); + minimumOut = scaleMin; + maximumOut = scaleMax; + stepValueOut = scaleStep; + digitsRightOfDecimalOut = digitsRightOfDecimal; + + return true; + } + + return false; +} + +/** + * Get the axis scale text values and their positions for drawing the scale. + * + * @param minimumDataValue + * Minimum data value + * @param maximumDataValue + * Maximum data value + * @param axisLength + * Length of axis (no specific unit type is assumed) + * @param minimumOut + * Output minimum value for autoranging. + * @param maximumOut + * Output maximum value for autoranging. + * @param scaleValuesOffsetInPixelsOut + * Output containing offset in pixels for the scale values. + * @param scaleValuesOut + * Output containing text for scale values. + * @return + * True if output data is valid, else false. + */ +bool +ChartTwoCartesianOrientedAxes::getScaleValuesAndOffsets(const ChartTwoCartesianAxis* cartesianAxis, + const float minimumDataValue, + const float maximumDataValue, + const float axisLength, + float& minimumOut, + float& maximumOut, + std::vector& scaleValuesOffsetInPixelsOut, + std::vector& scaleValuesOut) const +{ + CaretAssert(cartesianAxis); + + float minimumValue = minimumDataValue; + float maximumValue = maximumDataValue; + + minimumOut = 0.0; + maximumOut = 0.0; + scaleValuesOffsetInPixelsOut.clear(); + scaleValuesOut.clear(); + + if (axisLength <= 0.0) { + CaretAssert(0); + return false; + } + + float labelsStart = 0.0; + float labelsEnd = 0.0; + float labelsStep = 1.0; + int32_t labelsDigitsRightOfDecimal = 0; + + switch (m_scaleRangeMode) { + case ChartTwoAxisScaleRangeModeEnum::AUTO: + getDataRange(minimumValue, + maximumValue); + break; + case ChartTwoAxisScaleRangeModeEnum::DATA: + getDataRange(minimumValue, + maximumValue); + break; + case ChartTwoAxisScaleRangeModeEnum::USER: + minimumValue = m_userScaleMinimumValue; + maximumValue = m_userScaleMaximumValue; + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_A: + case ChartTwoAxisScaleRangeModeEnum::YOKE_B: + case ChartTwoAxisScaleRangeModeEnum::YOKE_C: + case ChartTwoAxisScaleRangeModeEnum::YOKE_D: + updateMinMaxValuesForYoking(); + minimumValue = m_userScaleMinimumValue; + maximumValue = m_userScaleMaximumValue; + break; + } + + if ( ! getAutoRangeMinimumAndMaximum(minimumValue, + maximumValue, + labelsStart, + labelsEnd, + labelsStep, + labelsDigitsRightOfDecimal)) { + return false; + } + + switch (m_scaleRangeMode) { + case ChartTwoAxisScaleRangeModeEnum::AUTO: + m_userScaleMinimumValue = labelsStart; + m_userScaleMaximumValue = labelsEnd; + break; + case ChartTwoAxisScaleRangeModeEnum::DATA: + { + const double range = maximumDataValue - minimumDataValue; + if (range > 0.0) { + const int32_t numSteps = MathFunctions::round((labelsEnd - labelsStart) / labelsStep); + if (numSteps > 0) { + labelsStart = minimumDataValue; + labelsEnd = maximumDataValue; + labelsStep = range / numSteps; + m_userScaleMinimumValue = labelsStart; + m_userScaleMaximumValue = labelsEnd; + } + } + } + break; + case ChartTwoAxisScaleRangeModeEnum::USER: + { + const double range = m_userScaleMaximumValue - m_userScaleMinimumValue; + if (range > 0.0) { + const int32_t numSteps = MathFunctions::round((labelsEnd - labelsStart) / labelsStep); + if (numSteps > 0) { + labelsStart = m_userScaleMinimumValue; + labelsEnd = m_userScaleMaximumValue; + labelsStep = range / numSteps; + m_userScaleMinimumValue = labelsStart; + m_userScaleMaximumValue = labelsEnd; + } + } + } + break; + case ChartTwoAxisScaleRangeModeEnum::YOKE_A: + case ChartTwoAxisScaleRangeModeEnum::YOKE_B: + case ChartTwoAxisScaleRangeModeEnum::YOKE_C: + case ChartTwoAxisScaleRangeModeEnum::YOKE_D: + { + const double range = m_userScaleMaximumValue - m_userScaleMinimumValue; + if (range > 0.0) { + const int32_t numSteps = MathFunctions::round((labelsEnd - labelsStart) / labelsStep); + if (numSteps > 0) { + labelsStart = m_userScaleMinimumValue; + labelsEnd = m_userScaleMaximumValue; + labelsStep = range / numSteps; + m_userScaleMinimumValue = labelsStart; + m_userScaleMaximumValue = labelsEnd; + } + } + } + break; + } + + minimumOut = labelsStart; + maximumOut = labelsEnd; + + /* + * If the "labels end" or "labels start" value is not valid (infinity or not-a-number) there + * are invalid values in the data and will cause the labels processing later + * in this method to fail. So, alert the user that there is a problem in + * the data. + * + * A set is used to track those models for which the user has + * already been alerted. Otherwise, the alert message will be + * displayed every time this method is called (which is many) and + * the user will receive endless pop-ups. + */ + if ( (! MathFunctions::isNumeric(labelsStart)) + || (! MathFunctions::isNumeric(labelsEnd))) { + const AString msg("Invalid numbers (infinity or not-a-number) found when trying to create chart. " + "Run \"wb_command -file-information\" on files being charted to find the file " + "that contains invalid data so that the file can be fixed."); + CaretLogWarning(msg); + return false; + } + + bool result(false); + switch (cartesianAxis->getSubdivisionsMode()) { + case ChartTwoCartesianSubdivisionsModeEnum::CUSTOM: + result = getCustomSubdivisionsScalesOffsets(cartesianAxis, + axisLength, + labelsStart, + labelsEnd, + scaleValuesOffsetInPixelsOut, + scaleValuesOut); + break; + case ChartTwoCartesianSubdivisionsModeEnum::STANDARD: + result = getStandardSubdivisionsScalesOffsets(cartesianAxis, + axisLength, + labelsStart, + labelsEnd, + labelsStep, + scaleValuesOffsetInPixelsOut, + scaleValuesOut); + break; + } + + return result; +} + +/** + * Compute placement of numerics for standard subdivisions in an axis. + * + * @param cartesianAxis + * The axis + * @param axisLength + * Length of the axis in pixels + * @param labelsStart + * Starting position of labels in axis coordinate + * @param labelsEnd + * Ending position of labels in axis coordinate + * @param labelStepIn + * The step between consecutive labels in axis coordinate + * @param scaleValuesOffsetInPixelsOut + * Output containing offset in pixels for the scale values. + * @param scaleValuesOut + * Output containing text for scale values. + * @return + * True if output data is valid, else false. + */ +bool +ChartTwoCartesianOrientedAxes::getStandardSubdivisionsScalesOffsets(const ChartTwoCartesianAxis* cartesianAxis, + const float axisLength, + const float labelsStart, + const float labelsEnd, + const float labelsStepIn, + std::vector& scaleValuesOffsetInPixelsOut, + std::vector& scaleValuesOut) const +{ + float labelsStep(labelsStepIn); + + switch (cartesianAxis->getNumericSubdivsionsMode()) { + case ChartTwoNumericSubdivisionsModeEnum::AUTO: + break; + case ChartTwoNumericSubdivisionsModeEnum::USER: + { + const float labelsRange = labelsEnd - labelsStart; + if (labelsRange <= 0.0) { + return false; + } + const float dividend = (1.0 + cartesianAxis->getUserNumberOfSubdivisions()); + labelsStep = labelsRange / dividend; + } + break; + } + + /* + * If the "labels end" or "labels start" value is not valid (infinity or not-a-number) there + * are invalid values in the data and will cause the labels processing later + * in this method to fail. So, alert the user that there is a problem in + * the data. + * + * A set is used to track those models for which the user has + * already been alerted. Otherwise, the alert message will be + * displayed every time this method is called (which is many) and + * the user will receive endless pop-ups. + */ + if ( (! MathFunctions::isNumeric(labelsStart)) + || (! MathFunctions::isNumeric(labelsEnd))) { + const AString msg("Invalid numbers (infinity or not-a-number) found when trying to create chart. " + "Run \"wb_command -file-information\" on files being charted to find the file " + "that contains invalid data so that the file can be fixed."); + CaretLogWarning(msg); + return false; + } + + float labelsRange = (labelsEnd - labelsStart); + if (labelsRange <= 0.0) { + return false; + } + + const float tickLabelsStep = labelsStep; + if (tickLabelsStep <= 0.0) { + return false; + } + + const float onePercentRange = labelsRange * 0.01f; + + std::vector labelNumericValues; + + float labelValue = labelsStart; + while (labelValue <= labelsEnd) { + float labelParametricValue = (labelValue - labelsStart) / labelsRange; + + float labelValueForText = labelValue; + + if (labelsRange >= 10.0) { + /* + * Is this the first label? + */ + if (labelValue <= labelsStart) { + /* + * Handles case when the minimum DATA value is just a little + * bit greater than the minimum value for axis labels such + * as in Data-Series data when the minimum data value is "1" + * and the minimum axis label value is "0". Without this + * code no value is displayed at the left edge of the axis. + */ + if (labelParametricValue < 0.0) { + const float nextParametricValue = ((labelValue + tickLabelsStep) - labelsStart) / labelsRange; + if (nextParametricValue > 0.05) { + labelParametricValue = 0.0; + labelValueForText = labelsStart; + } + } + } + + if (labelParametricValue < 0.0) { + if (labelParametricValue >= -0.01) { + labelParametricValue = 0.0; + } + } + + /* + * Is this the last label? + */ + if (labelValue >= labelsEnd) { + /* + * Like above, ensures a value is displayed at the right + * edge of the axis. + */ + if (labelParametricValue > 1.0) { + const float prevParametricValue = ((labelValue - tickLabelsStep) - labelsStart) / labelsRange; + if (prevParametricValue < 0.95) { + labelParametricValue = 1.0; + labelValueForText = labelsEnd; + } + } + } + + if (labelParametricValue > 1.0) { + if (labelParametricValue < 1.01) { + labelParametricValue = 1.0; + } + } + } + + if ((labelParametricValue >= 0.0) + && (labelParametricValue <= 1.0)) { + const float labelPixelsPosition = axisLength * labelParametricValue; + labelNumericValues.push_back(labelValueForText); + scaleValuesOffsetInPixelsOut.push_back(labelPixelsPosition); + } + + labelValue += tickLabelsStep; + + /* + * It is possible that 'labelValue' may be slightly greater than 'labelsEnd' + * for the last label which results in the last label not displayed. + * So, if the 'labelValue' is slightly greater than 'labelsEnd', + * limit 'labelValue' so that the label at the end of the data range + * is displayed. + * + * Example: labelValue = 73.9500046 + * labelsEnd = 73.9499969 + */ + if (labelValue > labelsEnd) { + const float diff = labelValue - labelsEnd; + if (diff < onePercentRange) { + labelValue = labelsEnd; + } + } + } + + const int32_t numValues = static_cast(labelNumericValues.size()); + if (numValues > 0) { + scaleValuesOut.resize(numValues); + NumericTextFormatting::formatValueRange(cartesianAxis->getUserNumericFormat(), + cartesianAxis->getUserDigitsRightOfDecimal(), + &labelNumericValues[0], + &scaleValuesOut[0], + labelNumericValues.size()); + } + + CaretAssert(scaleValuesOffsetInPixelsOut.size() == scaleValuesOut.size()); + return ( ! scaleValuesOut.empty()); +} + +/** + * Compute placement of numerics for custom subdivisions in an axis. + * + * @param cartesianAxis + * The axis + * @param axisLength + * Length of the axis in pixels + * @param labelsStart + * Starting position of labels in axis coordinate + * @param labelsEnd + * Ending position of labels in axis coordinate + * @param scaleValuesOffsetInPixelsOut + * Output containing offset in pixels for the scale values. + * @param scaleValuesOut + * Output containing text for scale values. + * @return + * True if output data is valid, else false. + */ +bool +ChartTwoCartesianOrientedAxes::getCustomSubdivisionsScalesOffsets(const ChartTwoCartesianAxis* cartesianAxis, + const float axisLength, + const float labelsStart, + const float labelsEnd, + std::vector& scaleValuesOffsetInPixelsOut, + std::vector& scaleValuesOut) const +{ + const float labelRange = labelsEnd - labelsStart; + if (labelRange <= 0.0) { + return false; + } + + const ChartTwoCartesianCustomSubdivisions* subdivisions = cartesianAxis->getCustomSubdivisions(); + CaretAssert(subdivisions); + + + const int32_t numLabels = subdivisions->getNumberOfLabels(); + for (int32_t i = 0; i < numLabels; i++) { + const float labelValue = subdivisions->getLabelNumericValue(i); + const float normalizedValue = (labelValue - labelsStart) / labelRange; + if ((normalizedValue >= 0.0) + && (normalizedValue <= 1.0)) { + const float pixelValue = (normalizedValue * axisLength); + scaleValuesOffsetInPixelsOut.push_back(pixelValue); + scaleValuesOut.push_back(subdivisions->getLabelText(i)); + } + } + + CaretAssert(scaleValuesOut.size() == scaleValuesOffsetInPixelsOut.size()); + if ( ! scaleValuesOut.empty()) { + return true; + } + return false; +} + +/** + * @return True if transformations are enabled + */ +bool +ChartTwoCartesianOrientedAxes::isTransformationEnabled() const +{ + return m_transformationEnabled; +} + +/** + * Set transformations enabled + * @param enabled + * New enabled status for transformations + */ +void +ChartTwoCartesianOrientedAxes::setTransformationEnabled(const bool enabled) +{ + m_transformationEnabled = enabled; +} + +/** + * Convert the given viewport value to a percentage of the viewport, multiply the + * percentage of the viewport by the range of the data, and return the data value. + * @param viewportWidth + * Width of viewport + * @param viewportHeight + * Height of viewport + * @param viewportValue + * Value in viewport + * @return Data value multiplied by percentage of viewport + */ +float +ChartTwoCartesianOrientedAxes::getDataPercentageFromPercentageOfViewport(const int32_t viewportWidth, + const int32_t viewportHeight, + const float viewportValue) const +{ + float dataValueOut(0.0); + + /* + * Range of displayed data + */ + float minValue(0.0), maxValue(0.0); + getUserScaleMinimumMaximumValues(minValue, maxValue); + const float dataRange(maxValue - minValue); + if (dataRange <= 0.0) { + return dataValueOut; + } + + /* + * Size of viewport + */ + float viewportSize(0.0); + switch (m_orientationType) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + viewportSize = viewportWidth; + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + viewportSize = viewportHeight; + break; + } + /* + * Percentage of viewport + */ + const float viewportPercentage((viewportSize > 0) + ? (viewportValue / viewportSize) + : 0.0); + + dataValueOut = (dataRange * viewportPercentage); + + return dataValueOut; +} + +/** + * Convert the given viewport coodinate to an axes coordinate + * @param viewportWidth + * Width of viewport + * @param viewportHeight + * Height of viewport + *@param viewportCoordinate + * Coordinate in viewport + * @return Data value multiplied by percentage of viewport + */ +float +ChartTwoCartesianOrientedAxes::getAxesCoordinateFromViewportCoordinate(const int32_t viewportWidth, + const int32_t viewportHeight, + const float viewportCoordinate) const +{ + float value = getDataPercentageFromPercentageOfViewport(viewportWidth, + viewportHeight, + viewportCoordinate); + value += getUserScaleMinimumValue(); + return value; +} + + +/** + * Apply mouse translation to the current chart's axes + * @param viewport + * Viewport containing chart + * @param mouseDX + * The change in mouse X + * @param mouseDY + * The change in mouse Y + */ +void +ChartTwoCartesianOrientedAxes::applyMouseTranslation(const int32_t viewport[4], + const float mouseDX, + const float mouseDY) +{ + if ( ! m_transformationEnabled) { + return; + } + + float mouseDeltaXY(0.0); + switch (m_orientationType) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + mouseDeltaXY = mouseDX; + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + mouseDeltaXY = mouseDY; + break; + } + if (mouseDeltaXY == 0.0) { + return; + } + + float deltaData = getDataPercentageFromPercentageOfViewport(viewport[2], + viewport[3], + mouseDeltaXY); + if (deltaData != 0.0) { + float minValue(0.0), maxValue(0.0); + getUserScaleMinimumMaximumValues(minValue, maxValue); + setUserScaleMinimumValueFromGUI(minValue - deltaData); + setUserScaleMaximumValueFromGUI(maxValue - deltaData); + } +} + +/** + * Apply mouse scaling to the current chart's axes + * @param viewport + * Viewport containing chart + * @param mouseXY + * Position of the mouse along axis + * @param mouseDY + * The change in mouse Y + */ +void +ChartTwoCartesianOrientedAxes::applyMouseScaling(const int32_t viewport[4], + const float mouseXY, + const float mouseDY) +{ + if ( ! m_transformationEnabled) { + return; + } + + if (mouseDY == 0.0) { + return; + } + + /* + * Want to scale about the position of the mouse + */ + float percentMin(1.0); + switch (m_orientationType) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + percentMin = (mouseXY / viewport[2]); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + percentMin = (mouseXY / viewport[3]); + break; + } + const float percentMax(1.0 - percentMin); + + const float accelerate(15.0); + const float deltaData = (accelerate * getDataPercentageFromPercentageOfViewport(viewport[2], + viewport[3], + mouseDY)); + float deltaMin(deltaData * percentMin); + float deltaMax(deltaData * percentMax); + float minValue(0.0), maxValue(0.0); + getUserScaleMinimumMaximumValues(minValue, maxValue); + const float newMin(minValue + deltaMin); + const float newMax(maxValue - deltaMax); + if (newMax > newMin) { + setUserScaleMinimumValueFromGUI(newMin); + setUserScaleMaximumValueFromGUI(newMax); + } +} + +/** + * @return A percentage of the data's range + * @param percentage + * The percentage ranging [0.0, 100.0] + */ +float +ChartTwoCartesianOrientedAxes::getPercentageOfDataRange(const float percentage) const +{ + float dataMin(0.0), dataMax(0.0); + getDataRange(dataMin, dataMax); + const float percent((dataMax - dataMin) * (percentage / 100.0)); + return percent; +} + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianOrientedAxes.h connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianOrientedAxes.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianOrientedAxes.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianOrientedAxes.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,203 @@ +#ifndef __CHART_TWO_CARTESIAN_ORIENTATED_AXES_H__ +#define __CHART_TWO_CARTESIAN_ORIENTATED_AXES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "ChartTwoAxisOrientationTypeEnum.h" +#include "ChartTwoAxisScaleRangeModeEnum.h" +#include "SceneableInterface.h" + + +namespace caret { + class ChartTwoCartesianAxis; + class ChartTwoOverlaySetInterface; + class SceneClassAssistant; + + class ChartTwoCartesianOrientedAxes : public CaretObject, public SceneableInterface { + + public: + ChartTwoCartesianOrientedAxes(const ChartTwoOverlaySetInterface* parentChartOverlaySetInterface, + const ChartTwoAxisOrientationTypeEnum::Enum orientationType); + + virtual ~ChartTwoCartesianOrientedAxes(); + + ChartTwoCartesianOrientedAxes(const ChartTwoCartesianOrientedAxes& obj); + + ChartTwoCartesianOrientedAxes& operator=(const ChartTwoCartesianOrientedAxes& obj); + + ChartTwoAxisOrientationTypeEnum::Enum getOrientationType(); + + ChartTwoAxisScaleRangeModeEnum::Enum getScaleRangeMode() const; + + void initializeScaleRangeMode(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode); + + void setScaleRangeModeFromGUI(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode); + + void getDataRange(float& minimumValueOut, + float& maximumValueOut) const; + + void getUserScaleMinimumMaximumValues(float& minimumOut, + float& maximumOut) const; + + float getUserScaleMinimumValue() const; + + void setUserScaleMinimumValueFromGUI(const float value); + + float getUserScaleMaximumValue() const; + + void setUserScaleMaximumValueFromGUI(const float value); + + void resetUserScaleRange(); + + void setRangeModeAndUserScaleFromVersionOneScene(const ChartTwoAxisScaleRangeModeEnum::Enum scaleRangeMode, + const float userScaleMinimumValue, + const float userScaleMaximumValue); + + void copyAxes(const ChartTwoCartesianOrientedAxes* axes); + + ChartTwoCartesianAxis* getLeftOrBottomAxis(); + + const ChartTwoCartesianAxis* getLeftOrBottomAxis() const; + + ChartTwoCartesianAxis* getRightOrTopAxis(); + + const ChartTwoCartesianAxis* getRightOrTopAxis() const; + + bool getCustomSubdivisionsScalesOffsets(const ChartTwoCartesianAxis* cartesianAxis, + const float axisLength, + const float labelsStart, + const float labelsEnd, + std::vector& scaleValuesOffsetInPixelsOut, + std::vector& scaleValuesOut) const; + + bool getStandardSubdivisionsScalesOffsets(const ChartTwoCartesianAxis* cartesianAxis, + const float axisLength, + const float labelsStart, + const float labelsEnd, + const float labelsStepIn, + std::vector& scaleValuesOffsetInPixelsOut, + std::vector& scaleValuesOut) const; + + bool getScaleValuesAndOffsets(const ChartTwoCartesianAxis* cartesianAxis, + const float minimumDataValue, + const float maximumDataValue, + const float axisLength, + float& minimumOut, + float& maximumOut, + std::vector& scaleValuesOffsetInPixelsOut, + std::vector& scaleValuesOut) const; + + bool isTransformationEnabled() const; + + void setTransformationEnabled(const bool enabled); + + void applyMouseTranslation(const int32_t viewport[4], + const float mouseDX, + const float mouseDY); + + void applyMouseScaling(const int32_t viewport[4], + const float mouseXY, + const float mouseDY); + + float getPercentageOfDataRange(const float percentage) const; + + void reset(); + + float getAxesCoordinateFromViewportCoordinate(const int32_t viewportWidth, + const int32_t viewportHeight, + const float viewportValue) const; + + float getDataPercentageFromPercentageOfViewport(const int32_t viewportWidth, + const int32_t viewportHeight, + const float viewportValue) const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperChartTwoCartesianOrientedAxes(const ChartTwoCartesianOrientedAxes& obj); + + bool getAutoRangeMinimumAndMaximum(const float minimumValue, + const float maximumValue, + float& minimumOut, + float& maximumOut, + float& stepValueOut, + int32_t& digitsRightOfDecimalOut) const; + + void updateRangeModeAfterMinimumOrMaximumChanged(); + + void updateMinMaxValuesForYoking() const; + + const ChartTwoOverlaySetInterface* m_parentChartOverlaySetInterface; + + const ChartTwoAxisOrientationTypeEnum::Enum m_orientationType; + + std::unique_ptr m_sceneAssistant; + + ChartTwoAxisScaleRangeModeEnum::Enum m_scaleRangeMode = ChartTwoAxisScaleRangeModeEnum::AUTO; + + mutable float m_userScaleMinimumValue = -100.0f; + + mutable float m_userScaleMaximumValue = 100.0f; + + std::unique_ptr m_leftOrBottomAxis; + + std::unique_ptr m_rightOrTopAxis; + + bool m_transformationEnabled = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_CARTESIAN_ORIENTATED_AXES_DECLARE__ + // +#endif // __CHART_TWO_CARTESIAN_ORIENTATED_AXES_DECLARE__ + +} // namespace +#endif //__CHART_TWO_CARTESIAN_ORIENTATED_AXES_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,262 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_AXES_YOKING_MANAGER_DECLARE__ +#include "ChartTwoCartesianOrientedAxesYokingManager.h" +#undef __CHART_TWO_AXES_YOKING_MANAGER_DECLARE__ + +#include "CaretAssert.h" +#include "ChartTwoAxisScaleRangeModeEnum.h" +#include "EventChartTwoCartesianOrientedAxesYoking.h" +#include "EventManager.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoCartesianOrientedAxesYokingManager + * \brief Yoking manager for chart axis, maintains yoked values + * \ingroup Charting + */ + +/** + * Constructor. + */ +ChartTwoCartesianOrientedAxesYokingManager::ChartTwoCartesianOrientedAxesYokingManager() +: CaretObject() +{ + std::vector allRangeModes; + ChartTwoAxisScaleRangeModeEnum::getAllEnums(allRangeModes); + const int32_t numRangeModes = static_cast(allRangeModes.size()); + CaretAssert(numRangeModes > 0); + + m_horizontalMinimum.resize(numRangeModes, 0.0); + m_horizontalMaximum.resize(numRangeModes, 1.0); + m_verticalMinimum.resize(numRangeModes, 0.0); + m_verticalMaximum.resize(numRangeModes, 1.0); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->addArray("m_horizontalMinimum", &m_horizontalMinimum[0], m_horizontalMinimum.size(), 0.0); + m_sceneAssistant->addArray("m_horizontalMaximum", &m_horizontalMaximum[0], m_horizontalMaximum.size(), 1.0); + m_sceneAssistant->addArray("m_verticalMinimum", &m_verticalMinimum[0], m_verticalMinimum.size(), 0.0); + m_sceneAssistant->addArray("m_verticalMaximum", &m_verticalMaximum[0], m_verticalMaximum.size(), 1.0); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING); +} + +/** + * Destructor. + */ +ChartTwoCartesianOrientedAxesYokingManager::~ChartTwoCartesianOrientedAxesYokingManager() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +ChartTwoCartesianOrientedAxesYokingManager::toString() const +{ + return "ChartTwoCartesianOrientedAxesYokingManager"; +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartTwoCartesianOrientedAxesYokingManager::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING) { + auto chartEvent = dynamic_cast(event); + CaretAssert(chartEvent); + + switch (chartEvent->getMode()) { + case EventChartTwoCartesianOrientedAxesYoking::Mode::GET_MINIMUM_AND_MAXIMUM_VALUES: + { + const int32_t yokeIndex(static_cast(chartEvent->getYokingRangeMode())); + switch (chartEvent->getAxisOrientation()) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + CaretAssertVectorIndex(m_horizontalMinimum, yokeIndex); + CaretAssertVectorIndex(m_horizontalMaximum, yokeIndex); + chartEvent->setMinimumAndMaximumValues(m_horizontalMinimum[yokeIndex], + m_horizontalMaximum[yokeIndex]); + chartEvent->setEventProcessed(); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + CaretAssertVectorIndex(m_verticalMinimum, yokeIndex); + CaretAssertVectorIndex(m_verticalMaximum, yokeIndex); + chartEvent->setMinimumAndMaximumValues(m_verticalMinimum[yokeIndex], + m_verticalMaximum[yokeIndex]); + chartEvent->setEventProcessed(); + break; + } + } + break; + case EventChartTwoCartesianOrientedAxesYoking::Mode::GET_YOKED_AXES: + break; + case EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MINIMUM_AND_MAXIMUM_VALUES: + { + const int32_t yokeIndex(static_cast(chartEvent->getYokingRangeMode())); + switch (chartEvent->getAxisOrientation()) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + { + CaretAssertVectorIndex(m_horizontalMinimum, yokeIndex); + CaretAssertVectorIndex(m_horizontalMaximum, yokeIndex); + m_horizontalMinimum[yokeIndex] = chartEvent->getMinimumValue(); + m_horizontalMaximum[yokeIndex] = chartEvent->getMaximumValue(); + chartEvent->setEventProcessed(); + } + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + { + CaretAssertVectorIndex(m_verticalMinimum, yokeIndex); + CaretAssertVectorIndex(m_verticalMaximum, yokeIndex); + m_verticalMinimum[yokeIndex] = chartEvent->getMinimumValue(); + m_verticalMaximum[yokeIndex] = chartEvent->getMaximumValue(); + chartEvent->setEventProcessed(); + } + break; + } + } + break; + case EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MAXIMUM_VALUE: + { + const int32_t yokeIndex(static_cast(chartEvent->getYokingRangeMode())); + switch (chartEvent->getAxisOrientation()) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + CaretAssertVectorIndex(m_horizontalMinimum, yokeIndex); + CaretAssertVectorIndex(m_horizontalMaximum, yokeIndex); + m_horizontalMaximum[yokeIndex] = chartEvent->getMaximumValue(); + if (m_horizontalMinimum[yokeIndex] > m_horizontalMaximum[yokeIndex]) { + m_horizontalMinimum[yokeIndex] = m_horizontalMaximum[yokeIndex]; + } + chartEvent->setEventProcessed(); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + CaretAssertVectorIndex(m_verticalMinimum, yokeIndex); + CaretAssertVectorIndex(m_verticalMaximum, yokeIndex); + m_verticalMaximum[yokeIndex] = chartEvent->getMaximumValue(); + if (m_verticalMinimum[yokeIndex] > m_verticalMaximum[yokeIndex]) { + m_verticalMinimum[yokeIndex] = m_verticalMaximum[yokeIndex]; + } + chartEvent->setEventProcessed(); + break; + } + } + break; + case EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MINIMUM_VALUE: + { + /* + * Note: "get" from event is loading into 'this' min/max + */ + const int32_t yokeIndex(static_cast(chartEvent->getYokingRangeMode())); + switch (chartEvent->getAxisOrientation()) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + CaretAssertVectorIndex(m_horizontalMinimum, yokeIndex); + CaretAssertVectorIndex(m_horizontalMaximum, yokeIndex); + m_horizontalMinimum[yokeIndex] = chartEvent->getMinimumValue(); + if (m_horizontalMaximum[yokeIndex] < m_horizontalMinimum[yokeIndex]) { + m_horizontalMaximum[yokeIndex] = m_horizontalMinimum[yokeIndex]; + } + chartEvent->setEventProcessed(); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + CaretAssertVectorIndex(m_verticalMinimum, yokeIndex); + CaretAssertVectorIndex(m_verticalMaximum, yokeIndex); + m_verticalMinimum[yokeIndex] = chartEvent->getMinimumValue(); + if (m_verticalMaximum[yokeIndex] < m_verticalMinimum[yokeIndex]) { + m_verticalMaximum[yokeIndex] = m_verticalMinimum[yokeIndex]; + } + chartEvent->setEventProcessed(); + break; + } + } + break; + } + + event->setEventProcessed(); + } +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +ChartTwoCartesianOrientedAxesYokingManager::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ChartTwoCartesianOrientedAxesYokingManager", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +ChartTwoCartesianOrientedAxesYokingManager::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.h connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,96 @@ +#ifndef __CHART_TWO_AXES_YOKING_MANAGER_H__ +#define __CHART_TWO_AXES_YOKING_MANAGER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "EventListenerInterface.h" +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + + class ChartTwoCartesianOrientedAxesYokingManager : public CaretObject, public EventListenerInterface, public SceneableInterface { + + public: + ChartTwoCartesianOrientedAxesYokingManager(); + + virtual ~ChartTwoCartesianOrientedAxesYokingManager(); + + ChartTwoCartesianOrientedAxesYokingManager(const ChartTwoCartesianOrientedAxesYokingManager&) = delete; + + ChartTwoCartesianOrientedAxesYokingManager& operator=(const ChartTwoCartesianOrientedAxesYokingManager&) = delete; + + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual void receiveEvent(Event* event); + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + std::unique_ptr m_sceneAssistant; + + std::vector m_horizontalMinimum; + + std::vector m_horizontalMaximum; + + std::vector m_verticalMinimum; + + std::vector m_verticalMaximum; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_AXES_YOKING_MANAGER_DECLARE__ + // +#endif // __CHART_TWO_AXES_YOKING_MANAGER_DECLARE__ + +} // namespace +#endif //__CHART_TWO_AXES_YOKING_MANAGER_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_CARTESIAN_SUBDIVISIONS_MODE_ENUM_DECLARE__ +#include "ChartTwoCartesianSubdivisionsModeEnum.h" +#undef __CHART_TWO_CARTESIAN_SUBDIVISIONS_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoCartesianSubdivisionsModeEnum + * \brief Mode for chart subdivisions + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_chartTwoCartesianSubdivisionsModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void chartTwoCartesianSubdivisionsModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoCartesianSubdivisionsModeEnum.h" + * + * Instatiate: + * m_chartTwoCartesianSubdivisionsModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_chartTwoCartesianSubdivisionsModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_chartTwoCartesianSubdivisionsModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(chartTwoCartesianSubdivisionsModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_chartTwoCartesianSubdivisionsModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoCartesianSubdivisionsModeEnum::Enum VARIABLE = m_chartTwoCartesianSubdivisionsModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoCartesianSubdivisionsModeEnum::ChartTwoCartesianSubdivisionsModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoCartesianSubdivisionsModeEnum::~ChartTwoCartesianSubdivisionsModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoCartesianSubdivisionsModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoCartesianSubdivisionsModeEnum(STANDARD, + "STANDARD", + "Standard")); + + enumData.push_back(ChartTwoCartesianSubdivisionsModeEnum(CUSTOM, + "CUSTOM", + "Custom")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoCartesianSubdivisionsModeEnum* +ChartTwoCartesianSubdivisionsModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoCartesianSubdivisionsModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoCartesianSubdivisionsModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoCartesianSubdivisionsModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoCartesianSubdivisionsModeEnum::Enum +ChartTwoCartesianSubdivisionsModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoCartesianSubdivisionsModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoCartesianSubdivisionsModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoCartesianSubdivisionsModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoCartesianSubdivisionsModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoCartesianSubdivisionsModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoCartesianSubdivisionsModeEnum::Enum +ChartTwoCartesianSubdivisionsModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoCartesianSubdivisionsModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoCartesianSubdivisionsModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoCartesianSubdivisionsModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoCartesianSubdivisionsModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoCartesianSubdivisionsModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoCartesianSubdivisionsModeEnum::Enum +ChartTwoCartesianSubdivisionsModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoCartesianSubdivisionsModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoCartesianSubdivisionsModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoCartesianSubdivisionsModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoCartesianSubdivisionsModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoCartesianSubdivisionsModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoCartesianSubdivisionsModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoCartesianSubdivisionsModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoCartesianSubdivisionsModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.h connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,104 @@ +#ifndef __CHART_TWO_CARTESIAN_SUBDIVISIONS_MODE_ENUM_H__ +#define __CHART_TWO_CARTESIAN_SUBDIVISIONS_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoCartesianSubdivisionsModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Standard subdivisions */ + STANDARD, + /** Custom subdivisions*/ + CUSTOM + }; + + + ~ChartTwoCartesianSubdivisionsModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoCartesianSubdivisionsModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoCartesianSubdivisionsModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_CARTESIAN_SUBDIVISIONS_MODE_ENUM_DECLARE__ +std::vector ChartTwoCartesianSubdivisionsModeEnum::enumData; +bool ChartTwoCartesianSubdivisionsModeEnum::initializedFlag = false; +int32_t ChartTwoCartesianSubdivisionsModeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_CARTESIAN_SUBDIVISIONS_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_CARTESIAN_SUBDIVISIONS_MODE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCompoundDataType.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoCompoundDataType.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoCompoundDataType.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCompoundDataType.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -49,6 +49,8 @@ * Number of bins in the histogram. * @param lineChartUnitsAxisX * Line chart x-axis units. + * @param lineChartUnitsAxisY + * Line chart y-axis units. * @param lineChartNumberOfElementsAxisX * Line chart x-axis number of elements. * @param matrixNumberOfRows @@ -59,6 +61,7 @@ ChartTwoCompoundDataType::ChartTwoCompoundDataType(const ChartTwoDataTypeEnum::Enum chartDataType, const int32_t histogramNumberOfBuckets, const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisY, const int32_t lineChartNumberOfElementsAxisX, const int32_t matrixNumberOfRows, const int32_t matrixNumberOfColumns) @@ -69,6 +72,7 @@ m_chartDataType = chartDataType; m_histogramNumberOfBuckets = histogramNumberOfBuckets; m_lineChartUnitsAxisX = lineChartUnitsAxisX; + m_lineChartUnitsAxisY = lineChartUnitsAxisY; m_lineChartNumberOfElementsAxisX = lineChartNumberOfElementsAxisX; m_matrixNumberOfRows = matrixNumberOfRows; m_matrixNumberOfColumns = matrixNumberOfColumns; @@ -102,26 +106,55 @@ return ChartTwoCompoundDataType(ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM, histogramNumberOfBins, CaretUnitsTypeEnum::NONE, + CaretUnitsTypeEnum::NONE, 0, 0, 0); } /** + * @return A new instance for a line-layer chart. + * + * @param lineChartUnitsAxisX + * Line chart x-axis units. + * @param lineChartUnitsAxisY + * Units for the Y-axis + * @param lineChartNumberOfElementsAxisX + * Line chart x-axis number of elements. + */ +ChartTwoCompoundDataType +ChartTwoCompoundDataType::newInstanceForLineLayer(const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisY, + const int32_t lineChartNumberOfElementsAxisX) +{ + return ChartTwoCompoundDataType(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER, + 0, + lineChartUnitsAxisX, + lineChartUnitsAxisY, + lineChartNumberOfElementsAxisX, + 0, + 0); +} + +/** * @return A new instance for a line-series chart. * * @param lineChartUnitsAxisX * Line chart x-axis units. + * @param lineChartUnitsAxisY + * Units for the Y-axis * @param lineChartNumberOfElementsAxisX * Line chart x-axis number of elements. */ ChartTwoCompoundDataType ChartTwoCompoundDataType::newInstanceForLineSeries(const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, - const int32_t lineChartNumberOfElementsAxisX) + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisY, + const int32_t lineChartNumberOfElementsAxisX) { return ChartTwoCompoundDataType(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES, 0, lineChartUnitsAxisX, + lineChartUnitsAxisY, lineChartNumberOfElementsAxisX, 0, 0); @@ -130,18 +163,25 @@ /** * @return A new instance for a matrix chart. * + * @param lineChartUnitsAxisX + * Units for the X-axis + * @param lineChartUnitsAxisY + * Units for the Y-axis * @param matrixNumberOfRows * Matrix number of rows. * @param matrixNumberOfColumns * Matrix number of columns. */ ChartTwoCompoundDataType -ChartTwoCompoundDataType::newInstanceForMatrix(const int32_t matrixNumberOfRows, - const int32_t matrixNumberOfColumns) +ChartTwoCompoundDataType::newInstanceForMatrix(const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisY, + const int32_t matrixNumberOfRows, + const int32_t matrixNumberOfColumns) { return ChartTwoCompoundDataType(ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX, 0, - CaretUnitsTypeEnum::NONE, + lineChartUnitsAxisX, + lineChartUnitsAxisY, 0, matrixNumberOfRows, matrixNumberOfColumns); @@ -201,6 +241,7 @@ { m_chartDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; m_lineChartUnitsAxisX = CaretUnitsTypeEnum::NONE; + m_lineChartUnitsAxisY = CaretUnitsTypeEnum::NONE; m_lineChartNumberOfElementsAxisX = 0; m_matrixNumberOfRows = 0; m_matrixNumberOfColumns = 0; @@ -210,6 +251,8 @@ &m_chartDataType); m_sceneAssistant->add("m_lineChartUnitsAxisX", &m_lineChartUnitsAxisX); + m_sceneAssistant->add("m_lineChartUnitsAxisY", + &m_lineChartUnitsAxisY); m_sceneAssistant->add("m_lineChartNumberOfElementsAxisX", &m_lineChartNumberOfElementsAxisX); m_sceneAssistant->add("m_matrixNumberOfRows", @@ -223,7 +266,7 @@ * Equality operator. * * Charts must be same type. - * Line charts must be same units OR same number of elements in X-axis. + * Line charts must be same units OR same number of elements in X-axis (Y-axis ignored) * Matrix charts must have same number of rows and columns. * * @param obj @@ -246,6 +289,16 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: return true; break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + if (m_lineChartUnitsAxisX == obj.m_lineChartUnitsAxisX) { + return true; + } + if (m_lineChartNumberOfElementsAxisX == obj.m_lineChartNumberOfElementsAxisX) { + return true; + } + } + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: { if (m_lineChartUnitsAxisX == obj.m_lineChartUnitsAxisX) { @@ -296,6 +349,15 @@ } /** + * @return Line chart Y-axis units. + */ +CaretUnitsTypeEnum::Enum +ChartTwoCompoundDataType::getLineChartUnitsAxisY() const +{ + return m_lineChartUnitsAxisY; +} + +/** * @return Line chart number of element in the X-axis. */ int32_t @@ -337,20 +399,37 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: - //text.appendWithNewLine(indent - // + "buckets=" - // + QString::number(m_histogramNumberOfBuckets)); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + text.appendWithNewLine(indent + + "x-units=" + + CaretUnitsTypeEnum::toName(m_lineChartUnitsAxisX)); + text.appendWithNewLine(indent + + "y-units=" + + CaretUnitsTypeEnum::toName(m_lineChartUnitsAxisY)); + text.appendWithNewLine(indent + + "x-elements=" + + AString::number(m_lineChartNumberOfElementsAxisX)); break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: text.appendWithNewLine(indent - + "x=units=" + + "x-units=" + CaretUnitsTypeEnum::toName(m_lineChartUnitsAxisX)); text.appendWithNewLine(indent + + "y-units=" + + CaretUnitsTypeEnum::toName(m_lineChartUnitsAxisY)); + text.appendWithNewLine(indent + "x-elements=" + AString::number(m_lineChartNumberOfElementsAxisX)); break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: text.appendWithNewLine(indent + + "x-units=" + + CaretUnitsTypeEnum::toName(m_lineChartUnitsAxisX)); + text.appendWithNewLine(indent + + "y-units=" + + CaretUnitsTypeEnum::toName(m_lineChartUnitsAxisY)); + text.appendWithNewLine(indent + "rows=" + AString::number(m_matrixNumberOfRows)); text.appendWithNewLine(indent diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoCompoundDataType.h connectome-workbench-1.5.0/src/Charting/ChartTwoCompoundDataType.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoCompoundDataType.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoCompoundDataType.h 2021-02-16 19:46:47.000000000 +0000 @@ -39,17 +39,25 @@ ChartTwoCompoundDataType(const ChartTwoDataTypeEnum::Enum chartDataType, const int32_t histogramNumberOfBuckets, const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisY, const int32_t lineChartNumberOfElementsAxisX, const int32_t matrixNumberOfRows, const int32_t matrixNumberOfColumns); static ChartTwoCompoundDataType newInstanceForHistogram(const int32_t histogramNumberOfBuckets); + static ChartTwoCompoundDataType newInstanceForLineLayer(const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisY, + const int32_t lineChartNumberOfElementsAxisX); + static ChartTwoCompoundDataType newInstanceForLineSeries(const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, - const int32_t lineChartNumberOfElementsAxisX); + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisY, + const int32_t lineChartNumberOfElementsAxisX); - static ChartTwoCompoundDataType newInstanceForMatrix(const int32_t matrixNumberOfRows, - const int32_t matrixNumberOfColumns); + static ChartTwoCompoundDataType newInstanceForMatrix(const CaretUnitsTypeEnum::Enum lineChartUnitsAxisX, + const CaretUnitsTypeEnum::Enum lineChartUnitsAxisY, + const int32_t matrixNumberOfRows, + const int32_t matrixNumberOfColumns); virtual ~ChartTwoCompoundDataType(); @@ -66,6 +74,8 @@ CaretUnitsTypeEnum::Enum getLineChartUnitsAxisX() const; + CaretUnitsTypeEnum::Enum getLineChartUnitsAxisY() const; + int32_t getLineChartNumberOfElementsAxisX() const; int32_t getMatrixNumberOfRows() const; @@ -110,6 +120,8 @@ CaretUnitsTypeEnum::Enum m_lineChartUnitsAxisX; + CaretUnitsTypeEnum::Enum m_lineChartUnitsAxisY; + int32_t m_lineChartNumberOfElementsAxisX; int32_t m_matrixNumberOfRows; diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoDataCartesian.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoDataCartesian.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoDataCartesian.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoDataCartesian.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,6 +23,7 @@ #include "ChartTwoDataCartesian.h" #undef __CHART_TWO_DATA_CARTESIAN_DECLARE__ +#include #include #include @@ -32,6 +33,8 @@ #include "ChartPoint.h" #include "GraphicsPrimitiveV3f.h" #include "MapFileDataSelector.h" +#include "MathFunctions.h" +#include "Matrix4x4Interface.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "ScenePrimitiveArray.h" @@ -76,6 +79,8 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: CaretAssert(0); break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -101,7 +106,6 @@ m_mapFileDataSelector = std::unique_ptr(new MapFileDataSelector()); m_selectionStatus = true; - m_color = CaretColorEnum::RED; m_lineWidth = getDefaultLineWidth(); m_timeStartInSecondsAxisX = 0.0; m_timeStepInSecondsAxisX = 1.0; @@ -112,6 +116,7 @@ CaretColorEnum::getColorEnumsNoBlackOrWhite(colorEnums); const int32_t numCaretColors = static_cast(colorEnums.size()); + CaretColorEnum::Enum defaultColor = CaretColorEnum::RED; bool colorFound = false; while ( ! colorFound) { ChartTwoDataCartesian::caretColorIndex++; @@ -126,11 +131,11 @@ /* do not use white */ } else { - m_color = colorEnums[ChartTwoDataCartesian::caretColorIndex]; + defaultColor = colorEnums[ChartTwoDataCartesian::caretColorIndex]; colorFound = true; } } - setColor(m_color); + setColorEnum(defaultColor); m_sceneAssistant = new SceneClassAssistant(); @@ -140,8 +145,6 @@ &m_dataAxisUnitsX); m_sceneAssistant->add("m_dataAxisUnitsY", &m_dataAxisUnitsY); - m_sceneAssistant->add("m_color", - &m_color); m_sceneAssistant->add("m_lineWidth", &m_lineWidth); m_sceneAssistant->add("m_timeStartInSecondsAxisX", @@ -151,6 +154,10 @@ m_sceneAssistant->add("m_mapFileDataSelector", "MapFileDataSelector", m_mapFileDataSelector.get()); + m_sceneAssistant->add("m_selectedPointIndex", + &m_selectedPointIndex); + m_sceneAssistant->add("m_selectedPointDisplayed", + &m_selectedPointDisplayed); } /** @@ -159,15 +166,14 @@ std::unique_ptr ChartTwoDataCartesian::createGraphicsPrimitive() { - float rgba[4]; - CaretColorEnum::toRGBAFloat(m_color, rgba); + std::array rgba = m_caretColor.getRGBA(); /* * Note: LINES (line segments) are used so that individual segments can be identified. * Cannot use line strip since it is identified as a single item. */ return std::unique_ptr(GraphicsPrimitive::newPrimitiveV3f(m_graphicsPrimitiveType, - rgba)); + rgba.data())); } /** @@ -205,6 +211,26 @@ } /** + * At times a copy of chart data will be needed BUT it must be + * the proper subclass so copy constructor and assignment operator + * will no function when this abstract, base class is used. Each + * subclass will override this method so that the returned class + * is of the proper type. + * + * @param matrix + * Data points are transformed using this matrix + * @return Copy of this instance that is the actual subclass + */ +ChartTwoDataCartesian* +ChartTwoDataCartesian::clone(const Matrix4x4Interface& matrix) const +{ + ChartTwoDataCartesian* cloneCopy = new ChartTwoDataCartesian(*this); + GraphicsPrimitiveV3f* primitive = cloneCopy->getGraphicsPrimitive(); + primitive->transformVerticesFloatXYZ(matrix); + return cloneCopy; +} + +/** * Copy constructor. * @param obj * Object that is copied. @@ -252,10 +278,13 @@ m_graphicsPrimitive.reset(dynamic_cast(obj.m_graphicsPrimitive->clone())); - m_color = obj.m_color; - m_lineWidth = obj.m_lineWidth; + m_caretColor = obj.m_caretColor; + m_lineWidth = obj.m_lineWidth; m_timeStartInSecondsAxisX = obj.m_timeStartInSecondsAxisX; m_timeStepInSecondsAxisX = obj.m_timeStepInSecondsAxisX; + + m_selectedPointIndex = obj.m_selectedPointIndex; + m_selectedPointDisplayed = obj.m_selectedPointDisplayed; } /** @@ -288,16 +317,6 @@ ChartTwoDataCartesian::setSelected(const bool selectionStatus) { m_selectionStatus = selectionStatus; - - /* - * When selection status is true, - * notify parent. - */ - // if (m_selectionStatus[tabIndex]) { - // if (m_parentChartModel != NULL) { - // m_parentChartModel->childChartTwoDataSelectionChanged(this); - // } - // } } @@ -311,12 +330,36 @@ */ void ChartTwoDataCartesian::addPoint(const float x, - const float y) + const float y) { m_graphicsPrimitive->addVertex(x, y); } /** + * Get the coordinates of the point at the given index + * @param pointIndex + * Index of the point + * @param xyzOut + * Output containing XYZ of point + */ +void +ChartTwoDataCartesian::getPointXYZ(const int32_t pointIndex, + float xyzOut[3]) const +{ + if ((pointIndex >= 0) + && (pointIndex < m_graphicsPrimitive->getNumberOfVertices())) { + m_graphicsPrimitive->getVertexFloatXYZ(pointIndex, + xyzOut); + } + else { + xyzOut[0] = 0.0; + xyzOut[1] = 0.0; + xyzOut[2] = 0.0; + } +} + + +/** * Get a bounds box for the cartesian data. * * @param boundingBoxOut @@ -395,12 +438,21 @@ } /** - * @return Color for chart + * @return Color enum for chart */ CaretColorEnum::Enum +ChartTwoDataCartesian::getColorEnum() const +{ + return m_caretColor.getCaretColorEnum(); +} + +/** + * @return Color for chart + */ +CaretColor ChartTwoDataCartesian::getColor() const { - return m_color; + return m_caretColor; } /** @@ -410,18 +462,30 @@ * New color for chart. */ void -ChartTwoDataCartesian::setColor(const CaretColorEnum::Enum color) +ChartTwoDataCartesian::setColor(const CaretColor& color) { - m_color = color; + m_caretColor = color; if (m_graphicsPrimitive != NULL) { - float rgba[4]; - CaretColorEnum::toRGBAFloat(m_color, rgba); - m_graphicsPrimitive->replaceAllVertexSolidFloatRGBA(rgba); + m_graphicsPrimitive->replaceAllVertexSolidByteRGBA(m_caretColor.getRGBA().data()); } } /** + * Set the color for the chart with a color enum + * + * @param colorEnum + * New color for chart. + */ +void +ChartTwoDataCartesian::setColorEnum(const CaretColorEnum::Enum colorEnum) +{ + CaretColor cc; + cc.setCaretColorEnum(colorEnum); + setColor(cc); +} + +/** * @return The line width. */ float @@ -445,6 +509,157 @@ } /** + * @return Index of line segment containing X. Index is of point at start of line segment. + * Negative if X is not within range of line. + * + * WARNING: Points MUST be ordered in ascending order by the X-coordinate. If not, + * behavior is undefined. + * + * @param x + * The X-coordinate + */ +int32_t +ChartTwoDataCartesian::getLineSegmentIndexContainingX(const float x) const +{ + int32_t segmentIndex(-1); + + const float numLineSegments = m_graphicsPrimitive->getNumberOfVertices() - 1; + if (numLineSegments >= 1) { + /* + * Verify X is within range of the endpoints + */ + float leftXYZ[3]; + m_graphicsPrimitive->getVertexFloatXYZ(0, leftXYZ); + float rightXYZ[3]; + m_graphicsPrimitive->getVertexFloatXYZ(numLineSegments, rightXYZ); + if ((x < leftXYZ[0]) + || (x > rightXYZ[0])) { + return segmentIndex; + } + + int32_t left(0); + int32_t right(numLineSegments); + while (left <= right) { + const int32_t middle = left + (right - left) / 2; + + float p1[3], p2[3]; + m_graphicsPrimitive->getVertexFloatXYZ(middle, p1); + m_graphicsPrimitive->getVertexFloatXYZ(middle + 1, p2); + + if ((x >= p1[0]) + && (x <= p2[0])) { + segmentIndex = middle; + break; + } + else { + if (x > p2[0]) { + left = middle + 1; + } + else { + right = middle; + } + } + } + +#ifdef VERIFY_FLAG + /* + * This is a slow linear search, used only in debug + * mode to verify binary search is correct + */ + int32_t linearIndex(-1); + for (int32_t i = 0; i < numLineSegments; i++) { + float p1[3], p2[3]; + m_graphicsPrimitive->getVertexFloatXYZ(i, p1); + m_graphicsPrimitive->getVertexFloatXYZ(i + 1, p2); + if ((x >= p1[0]) + && (x <= p2[0])) { + linearIndex = i; + } + } + CaretAssert(segmentIndex == linearIndex); +#endif // VERIFY_FLAG + } + + return segmentIndex; +} + +/** + * @return The vertical distance from the line to the given X, Y coordinates. A negative + * value is returned if the X-coordinate is not within the line's range of X-coordinates. + * @param x + * The X-coordinate + * @param y + * The Y-coordinate + */ +bool +ChartTwoDataCartesian::getVerticalDistanceToXY(const float x, + const float y, + float& distanceOut, + int32_t& pointIndexOut, + float chartXYZOut[3]) const +{ + distanceOut = std::numeric_limits::max(); + pointIndexOut = -1; + const int32_t leftIndex = getLineSegmentIndexContainingX(x); + + if (leftIndex >= 0) { + const int32_t rightIndex(leftIndex + 1); + const float xyz[3] { x, y, 0.0f }; + + float p1[3], p2[3]; + m_graphicsPrimitive->getVertexFloatXYZ(leftIndex, p1); + m_graphicsPrimitive->getVertexFloatXYZ(rightIndex, p2); + p1[2] = 0.0; + p2[2] = 0.0; + const float dist1(MathFunctions::distance3D(xyz, p1)); + const float dist2(MathFunctions::distance3D(xyz, p2)); + + const float dx = p2[0] - p1[0]; + if (dx != 0.0) { + /* + * Vertical distance from point to the line segment + */ + const float dy = p2[1] - p1[1]; + const float m = dy / dx; + const float b = p1[1] - (m * p1[0]); + const float yOnSegment = m * x + b; + distanceOut = std::fabs(yOnSegment - y); + if (dist1 < dist2) { + pointIndexOut = leftIndex; + } + else { + pointIndexOut = rightIndex; + } + + chartXYZOut[0] = x; + chartXYZOut[1] = yOnSegment; + chartXYZOut[2] = p1[2]; + } + else { + /* + * Vertical line so use nearest point in line + */ + if (dist1 < dist2) { + pointIndexOut = leftIndex; + distanceOut = dist1; + chartXYZOut[0] = p1[0]; + chartXYZOut[1] = p1[1]; + chartXYZOut[2] = p1[2]; + } + else { + pointIndexOut = rightIndex; + distanceOut = dist2; + chartXYZOut[0] = p2[0]; + chartXYZOut[1] = p2[1]; + chartXYZOut[2] = p2[2]; + } + } + } + + return (pointIndexOut >= 0); +} + +/** * Save information specific to this type of model to the scene. * * @param sceneAttributes @@ -464,9 +679,12 @@ 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); - -// chartDataCartesian->addClass(m_mapFileDataSelector->saveToScene(sceneAttributes, -// "m_mapFileDataSelector")); + + /* + * Note: For line layer charts, color is from the chart overlay not this color + */ + sceneClass->addString("m_caretColor", + m_caretColor.encodeInXML()); const std::vector& xyz = m_graphicsPrimitive->getFloatXYZ(); const int32_t numXYZ = static_cast(xyz.size()); @@ -501,7 +719,25 @@ m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); -// m_mapFileDataSelector->restoreFromScene(sceneAttributes, chartDataCartesian->getClass("m_mapFileDataSelector")); + /* + * m_caretColor (instance of CaretColor) replaced + * m_color (instance CaretColorEnum) + * Note: For line layer charts, color is from the chart overlay not this color + */ + const QString caretColorText = sceneClass->getStringValue("m_caretColor", ""); + if ( ! caretColorText.isEmpty()) { + AString errorMessage; + if ( ! m_caretColor.decodeFromXML(caretColorText, + errorMessage)) { + sceneAttributes->addToErrorMessage(errorMessage); + } + } + else { + const CaretColorEnum::Enum color = sceneClass->getEnumeratedTypeValue("m_color", + CaretColorEnum::BLUE); + m_caretColor.setCaretColorEnum(color); + } + const ScenePrimitiveArray* xyzArray = sceneClass->getPrimitiveArray("xyz"); if (xyzArray != NULL) { diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoDataCartesian.h connectome-workbench-1.5.0/src/Charting/ChartTwoDataCartesian.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoDataCartesian.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoDataCartesian.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,7 +21,7 @@ */ /*LICENSE_END*/ -#include "CaretColorEnum.h" +#include "CaretColor.h" #include "CaretObjectTracksModification.h" #include "CaretUnitsTypeEnum.h" #include "ChartTwoDataTypeEnum.h" @@ -34,6 +34,7 @@ class ChartPoint; class GraphicsPrimitiveV3f; class MapFileDataSelector; + class Matrix4x4Interface; class SceneClassAssistant; class ChartTwoDataCartesian : public CaretObjectTracksModification, public SceneableInterface { @@ -48,6 +49,8 @@ virtual ChartTwoDataCartesian* clone() const; + virtual ChartTwoDataCartesian* clone(const Matrix4x4Interface& matrix) const; + bool isSelected() const; void setSelected(const bool selectionStatus); @@ -55,6 +58,9 @@ void addPoint(const float x, const float y); + void getPointXYZ(const int32_t pointIndex, + float xyzOut[3]) const; + bool getBounds(BoundingBox& boundingBoxOut) const; GraphicsPrimitiveV3f* getGraphicsPrimitive() const; @@ -67,9 +73,13 @@ CaretUnitsTypeEnum::Enum getDataAxisUnitsY(); - CaretColorEnum::Enum getColor() const; + CaretColor getColor() const; + + CaretColorEnum::Enum getColorEnum() const; - void setColor(const CaretColorEnum::Enum color); + void setColorEnum(const CaretColorEnum::Enum colorEnum); + + void setColor(const CaretColor& color); float getLineWidth() const; @@ -96,13 +106,13 @@ */ static float getDefaultLineWidth() { return 0.5f; } - protected: -// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, -// SceneClass* sceneClass); -// -// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, -// const SceneClass* sceneClass); + bool getVerticalDistanceToXY(const float x, + const float y, + float& distanceOut, + int32_t& pointIndexOut, + float chartXYZOut[3]) const; + protected: private: ChartTwoDataCartesian(const ChartTwoDataCartesian& obj); @@ -115,6 +125,8 @@ std::unique_ptr createGraphicsPrimitive(); + int32_t getLineSegmentIndexContainingX(const float x) const; + std::unique_ptr m_mapFileDataSelector; std::unique_ptr m_graphicsPrimitive; @@ -127,7 +139,7 @@ bool m_selectionStatus; - CaretColorEnum::Enum m_color; + CaretColor m_caretColor; float m_lineWidth; @@ -135,6 +147,10 @@ float m_timeStepInSecondsAxisX; + mutable int32_t m_selectedPointIndex = 0; + + bool m_selectedPointDisplayed = false; + static int32_t caretColorIndex; SceneClassAssistant* m_sceneAssistant; diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoDataTypeEnum.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoDataTypeEnum.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoDataTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoDataTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -77,15 +77,19 @@ * * @param guiName * User-friendly name for use in user-interface. + * @param toolTipText + * Text for tooltip */ ChartTwoDataTypeEnum::ChartTwoDataTypeEnum(const Enum enumValue, - const AString& name, - const AString& guiName) + const AString& name, + const AString& guiName, + const AString& toolTipText) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; + this->toolTipText = toolTipText; } /** @@ -107,21 +111,32 @@ initializedFlag = true; enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_INVALID, - "CHART_DATA_TYPE_INVALID", - "Invalid")); + "CHART_DATA_TYPE_INVALID", + "Invalid", + "invalid")); enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_HISTOGRAM, - "CHART_DATA_TYPE_HISTOGRAM", - "Histogram")); + "CHART_DATA_TYPE_HISTOGRAM", + "Histogram", + "Histogram containing a file's map or row data")); + + enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_LINE_LAYER, + "CHART_DATA_TYPE_LINE_LAYER", + "Lines", + "Line chart containing a file's brainordinate, map, or row data")); enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_LINE_SERIES, - "CHART_DATA_TYPE_LINE_SERIES", - "Line Series")); + "CHART_DATA_TYPE_LINE_SERIES", + "Dyn Lines", + "Lines showing dynamic connectivity generated by an identification operation. " + "Enables files for dynamic connectivity in the Overlay Toolbox's Connectivity tab" + "")); enumData.push_back(ChartTwoDataTypeEnum(CHART_DATA_TYPE_MATRIX, - "CHART_DATA_TYPE_MATRIX", - "Matrix")); - + "CHART_DATA_TYPE_MATRIX", + "Matrix", + "Matrix containing a file's rows and columns")); + /* If this fails (chart types change), update value for NUMBER_OF_CHART_DATA_TYPES */ CaretAssertMessage(enumData.size() == NUMBER_OF_CHART_DATA_TYPES, "Have chart types changed?"); @@ -257,6 +272,21 @@ } /** + * Get aToolTip text representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing tool tip text + */ +AString +ChartTwoDataTypeEnum::toToolTipText(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoDataTypeEnum* enumInstance = findData(enumValue); + return enumInstance->toolTipText; +} + +/** * Get the integer code for a data type. * * @return diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoDataTypeEnum.h connectome-workbench-1.5.0/src/Charting/ChartTwoDataTypeEnum.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoDataTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoDataTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -39,6 +39,8 @@ CHART_DATA_TYPE_INVALID, /** Histogram */ CHART_DATA_TYPE_HISTOGRAM, + /** Line Layer */ + CHART_DATA_TYPE_LINE_LAYER, /** Line Series */ CHART_DATA_TYPE_LINE_SERIES, /** Matrix */ @@ -56,6 +58,8 @@ static Enum fromGuiName(const AString& guiName, bool* isValidOut); + static AString toToolTipText(const Enum enumValue); + static int32_t toIntegerCode(Enum enumValue); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); @@ -66,12 +70,13 @@ static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); - static const int32_t NUMBER_OF_CHART_DATA_TYPES = 4; + static const int32_t NUMBER_OF_CHART_DATA_TYPES = 5; private: ChartTwoDataTypeEnum(const Enum enumValue, - const AString& name, - const AString& guiName); + const AString& name, + const AString& guiName, + const AString& toolTipText); static const ChartTwoDataTypeEnum* findData(const Enum enumValue); @@ -98,6 +103,8 @@ /** A user-friendly name that is displayed in the GUI */ AString guiName; + + AString toolTipText; }; #ifdef __CHART_TWO_DATA_TYPE_ENUM_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoLineLayerContentTypeEnum.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoLineLayerContentTypeEnum.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoLineLayerContentTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoLineLayerContentTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,372 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CHART_TWO_LINE_LAYER_CONTENT_TYPE_ENUM_DECLARE__ +#include "ChartTwoLineLayerContentTypeEnum.h" +#undef __CHART_TWO_LINE_LAYER_CONTENT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ChartTwoLineLayerContentTypeEnum + * \brief Type of content for line series charting. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_ChartTwoLineLayerContentTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void ChartTwoLineLayerContentTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ChartTwoLineLayerContentTypeEnum.h" + * + * Instatiate: + * m_ChartTwoLineLayerContentTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_ChartTwoLineLayerContentTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_ChartTwoLineLayerContentTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(ChartTwoLineLayerContentTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_ChartTwoLineLayerContentTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ChartTwoLineLayerContentTypeEnum::Enum VARIABLE = m_ChartTwoLineLayerContentTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ChartTwoLineLayerContentTypeEnum::ChartTwoLineLayerContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ChartTwoLineLayerContentTypeEnum::~ChartTwoLineLayerContentTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ChartTwoLineLayerContentTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ChartTwoLineLayerContentTypeEnum(LINE_LAYER_CONTENT_UNSUPPORTED, + "LINE_LAYER_CONTENT_UNSUPPORTED", + "Unsupported")); + + enumData.push_back(ChartTwoLineLayerContentTypeEnum(LINE_LAYER_CONTENT_ROW_DATA, + "LINE_LAYER_CONTENT_ROW_DATA", + "Row Data")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ChartTwoLineLayerContentTypeEnum* +ChartTwoLineLayerContentTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ChartTwoLineLayerContentTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoLineLayerContentTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoLineLayerContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoLineLayerContentTypeEnum::Enum +ChartTwoLineLayerContentTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoLineLayerContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoLineLayerContentTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ChartTwoLineLayerContentTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ChartTwoLineLayerContentTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ChartTwoLineLayerContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ChartTwoLineLayerContentTypeEnum::Enum +ChartTwoLineLayerContentTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoLineLayerContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoLineLayerContentTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ChartTwoLineLayerContentTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ChartTwoLineLayerContentTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ChartTwoLineLayerContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ChartTwoLineLayerContentTypeEnum::Enum +ChartTwoLineLayerContentTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ChartTwoLineLayerContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ChartTwoLineLayerContentTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ChartTwoLineLayerContentTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ChartTwoLineLayerContentTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoLineLayerContentTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ChartTwoLineLayerContentTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ChartTwoLineLayerContentTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ChartTwoLineLayerContentTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoLineLayerContentTypeEnum.h connectome-workbench-1.5.0/src/Charting/ChartTwoLineLayerContentTypeEnum.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoLineLayerContentTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoLineLayerContentTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __CHART_TWO_LINE_LAYER_CONTENT_TYPE_ENUM_H__ +#define __CHART_TWO_LINE_LAYER_CONTENT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ChartTwoLineLayerContentTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Unsupported */ + LINE_LAYER_CONTENT_UNSUPPORTED, + /** Load charts from brainordinates */ + LINE_LAYER_CONTENT_BRAINORDINATE_DATA, + /** Load charts from a files rows */ + LINE_LAYER_CONTENT_ROW_DATA, + }; + + + ~ChartTwoLineLayerContentTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ChartTwoLineLayerContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ChartTwoLineLayerContentTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __CHART_TWO_LINE_LAYER_CONTENT_TYPE_ENUM_DECLARE__ +std::vector ChartTwoLineLayerContentTypeEnum::enumData; +bool ChartTwoLineLayerContentTypeEnum::initializedFlag = false; +int32_t ChartTwoLineLayerContentTypeEnum::integerCodeCounter = 0; +#endif // __CHART_TWO_LINE_LAYER_CONTENT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__CHART_TWO_LINE_LAYER_CONTENT_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoLineSeriesHistory.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoLineSeriesHistory.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoLineSeriesHistory.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoLineSeriesHistory.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -386,7 +386,7 @@ } } - historyItem->setColor(m_defaultColor); + historyItem->setColorEnum(m_defaultColor); historyItem->setLineWidth(m_defaultLineWidth); addHistoryItemNoDefaults(historyItem); updateDisplayedHistoryItems(); diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoMatrixDisplayProperties.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoMatrixDisplayProperties.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoMatrixDisplayProperties.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoMatrixDisplayProperties.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ #include "CaretAssert.h" #include "EventManager.h" +#include "MathFunctions.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -66,10 +67,6 @@ { resetPropertiesToDefault(); m_sceneAssistant = new SceneClassAssistant(); - m_sceneAssistant->add("m_cellPercentageZoomWidth", - &m_cellPercentageZoomWidth); - m_sceneAssistant->add("m_cellPercentageZoomHeight", - &m_cellPercentageZoomHeight); m_sceneAssistant->add("m_displayGridLinesFlag", &m_displayGridLinesFlag); m_sceneAssistant->add("m_highlightSelectedRowColumnFlag", @@ -119,8 +116,6 @@ void ChartTwoMatrixDisplayProperties::copyHelperChartTwoMatrixDisplayProperties(const ChartTwoMatrixDisplayProperties& obj) { - m_cellPercentageZoomWidth = obj.m_cellPercentageZoomWidth; - m_cellPercentageZoomHeight = obj.m_cellPercentageZoomHeight; m_displayGridLinesFlag = obj.m_displayGridLinesFlag; m_highlightSelectedRowColumnFlag = obj.m_highlightSelectedRowColumnFlag; } @@ -152,8 +147,6 @@ void ChartTwoMatrixDisplayProperties::resetPropertiesToDefault() { - m_cellPercentageZoomWidth = 100.0; - m_cellPercentageZoomHeight = 100.0; m_highlightSelectedRowColumnFlag = true; m_displayGridLinesFlag = false; } @@ -201,47 +194,6 @@ } /** - * @return The cell percentage zoom width. - */ -float -ChartTwoMatrixDisplayProperties::getCellPercentageZoomWidth() const -{ - return m_cellPercentageZoomWidth; -} - -/** - * Set the cell percentage zoom width. - * - * @param cellPercentageZoomWidth - * New value for cell percentage zoom width. - */ -void -ChartTwoMatrixDisplayProperties::setCellPercentageZoomWidth(const float cellPercentageZoomWidth) -{ - m_cellPercentageZoomWidth = cellPercentageZoomWidth; -} - -/** - * @return The cell percentage zoom height. - */ -float -ChartTwoMatrixDisplayProperties::getCellPercentageZoomHeight() const -{ - return m_cellPercentageZoomHeight; -} - -/** - * Set the cell percentage zoom height. - * - * @param cellPercentageZoomHeight - * New value for cell percentage zoom height. - */ -void ChartTwoMatrixDisplayProperties::setCellPercentageZoomHeight(const float cellPercentageZoomHeight) -{ - m_cellPercentageZoomHeight = cellPercentageZoomHeight; -} - -/** * Save information specific to this type of model to the scene. * * @param sceneAttributes @@ -258,7 +210,7 @@ { SceneClass* sceneClass = new SceneClass(instanceName, "ChartTwoMatrixDisplayProperties", - 1); + 2); /* Version 2 removes cell zoom width/height */ m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -291,6 +243,17 @@ m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); + if (sceneClass->getVersionNumber() < 2) { + const float zoomHeight = sceneClass->getFloatValue("m_cellPercentageZoomHeight", 100.0); + const float zoomWidth = sceneClass->getFloatValue("m_cellPercentageZoomWidth", 100.0); + if (MathFunctions::compareValuesEqual(&zoomHeight, 1, 100.0, 0.01) + && MathFunctions::compareValuesEqual(&zoomWidth, 1, 100.0, 0.01)) { + /* OK */ + } + else { + sceneAttributes->setSceneRestoreWarningCode(SceneRestoreWarningCodesEnum::CHART_TWO_MATRIX_CELL_WIDTH_HEIGHT); + } + } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoMatrixDisplayProperties.h connectome-workbench-1.5.0/src/Charting/ChartTwoMatrixDisplayProperties.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoMatrixDisplayProperties.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoMatrixDisplayProperties.h 2021-02-16 19:46:47.000000000 +0000 @@ -52,14 +52,6 @@ void setSelectedRowColumnHighlighted(const bool highlightStatus); - float getCellPercentageZoomWidth() const; - - void setCellPercentageZoomWidth(const float cellPercentageZoomWidth); - - float getCellPercentageZoomHeight() const; - - void setCellPercentageZoomHeight(const float cellPercentageZoomHeight); - // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -94,10 +86,6 @@ SceneClassAssistant* m_sceneAssistant; - float m_cellPercentageZoomHeight; - - float m_cellPercentageZoomWidth; - bool m_highlightSelectedRowColumnFlag; bool m_displayGridLinesFlag; diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -106,13 +106,17 @@ } initializedFlag = true; + /* + * Note space at right side of 'gui name'. When placed in combo box + * the right-most digit is missing when combo box's pop-up menu is displayed. + */ enumData.push_back(ChartTwoNumericSubdivisionsModeEnum(AUTO, "AUTO", - "Auto")); + "Auto ")); enumData.push_back(ChartTwoNumericSubdivisionsModeEnum(USER, "USER", - "User")); + "User ")); } diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoOverlaySetInterface.cxx connectome-workbench-1.5.0/src/Charting/ChartTwoOverlaySetInterface.cxx --- connectome-workbench-1.4.2/src/Charting/ChartTwoOverlaySetInterface.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoOverlaySetInterface.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,88 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_OVERLAY_SET_INTERFACE_DECLARE__ +#include "ChartTwoOverlaySetInterface.h" +#undef __CHART_TWO_OVERLAY_SET_INTERFACE_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::ChartTwoOverlaySetInterface + * \brief Interface for some functions that are needed in Chart module + * \ingroup Charting + */ + +/** + * Constructor. + */ +ChartTwoOverlaySetInterface::ChartTwoOverlaySetInterface() +{ + +} + +/** + * Destructor. + */ +ChartTwoOverlaySetInterface::~ChartTwoOverlaySetInterface() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +ChartTwoOverlaySetInterface::ChartTwoOverlaySetInterface(const ChartTwoOverlaySetInterface& obj) +{ + this->copyHelperChartTwoOverlaySetInterface(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +ChartTwoOverlaySetInterface& +ChartTwoOverlaySetInterface::operator=(const ChartTwoOverlaySetInterface& obj) +{ + if (this != &obj) { + this->copyHelperChartTwoOverlaySetInterface(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +ChartTwoOverlaySetInterface::copyHelperChartTwoOverlaySetInterface(const ChartTwoOverlaySetInterface& /* obj */) +{ + +} + diff -Nru connectome-workbench-1.4.2/src/Charting/ChartTwoOverlaySetInterface.h connectome-workbench-1.5.0/src/Charting/ChartTwoOverlaySetInterface.h --- connectome-workbench-1.4.2/src/Charting/ChartTwoOverlaySetInterface.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/ChartTwoOverlaySetInterface.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,65 @@ +#ifndef __CHART_TWO_OVERLAY_SET_INTERFACE_H__ +#define __CHART_TWO_OVERLAY_SET_INTERFACE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include "ChartAxisLocationEnum.h" +#include "ChartTwoAxisOrientationTypeEnum.h" + +namespace caret { + + class ChartTwoOverlaySetInterface { + + public: + ChartTwoOverlaySetInterface(); + + virtual ~ChartTwoOverlaySetInterface(); + + virtual bool getDataRangeForAxis(const ChartAxisLocationEnum::Enum axisLocation, + float& dataMinimum, + float& dataMaximum) const = 0; + + virtual bool getDataRangeForAxisOrientation(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + float& dataMinimum, + float& dataMaximum) const = 0; + + // ADD_NEW_METHODS_HERE + + private: + ChartTwoOverlaySetInterface(const ChartTwoOverlaySetInterface& obj); + + ChartTwoOverlaySetInterface& operator=(const ChartTwoOverlaySetInterface& obj); + + void copyHelperChartTwoOverlaySetInterface(const ChartTwoOverlaySetInterface& obj); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_OVERLAY_SET_INTERFACE_DECLARE__ + // +#endif // __CHART_TWO_OVERLAY_SET_INTERFACE_DECLARE__ + +} // namespace +#endif //__CHART_TWO_OVERLAY_SET_INTERFACE_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/CMakeLists.txt connectome-workbench-1.5.0/src/Charting/CMakeLists.txt --- connectome-workbench-1.4.2/src/Charting/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -38,12 +38,19 @@ ChartOneDataTypeEnum.h ChartingVersionEnum.h +ChartTwoAxisOrientationTypeEnum.h ChartTwoAxisScaleRangeModeEnum.h ChartTwoCartesianAxis.h +ChartTwoCartesianCustomSubdivisions.h +ChartTwoCartesianCustomSubdivisionsLabel.h +ChartTwoCartesianOrientedAxes.h +ChartTwoCartesianOrientedAxesYokingManager.h +ChartTwoCartesianSubdivisionsModeEnum.h ChartTwoCompoundDataType.h ChartTwoDataCartesian.h ChartTwoDataTypeEnum.h ChartTwoHistogramContentTypeEnum.h +ChartTwoLineLayerContentTypeEnum.h ChartTwoLineSeriesContentTypeEnum.h ChartTwoMatrixContentTypeEnum.h ChartTwoMatrixDisplayProperties.h @@ -51,13 +58,13 @@ ChartTwoMatrixTriangularViewingModeEnum.h ChartTwoNumericSubdivisionsModeEnum.h ChartTwoLineSeriesHistory.h +ChartTwoOverlaySetInterface.h ChartTwoTitle.h -EventChartTwoAttributesChanged.h -EventChartTwoAxisGetDataRange.h +EventChartTwoCartesianAxisDisplayGroup.h +EventChartTwoCartesianOrientedAxesYoking.h EventChartTwoLoadLineSeriesData.h MapFileDataSelector.h -ChartTwoAxisScaleRangeModeEnum.cxx ChartAxis.cxx ChartAxisCartesian.cxx ChartAxisLocationEnum.cxx @@ -81,11 +88,19 @@ ChartOneDataTypeEnum.cxx ChartingVersionEnum.cxx +ChartTwoAxisOrientationTypeEnum.cxx +ChartTwoAxisScaleRangeModeEnum.cxx ChartTwoCartesianAxis.cxx +ChartTwoCartesianCustomSubdivisions.cxx +ChartTwoCartesianCustomSubdivisionsLabel.cxx +ChartTwoCartesianOrientedAxes.cxx +ChartTwoCartesianOrientedAxesYokingManager.cxx +ChartTwoCartesianSubdivisionsModeEnum.cxx ChartTwoCompoundDataType.cxx ChartTwoDataCartesian.cxx ChartTwoDataTypeEnum.cxx ChartTwoHistogramContentTypeEnum.cxx +ChartTwoLineLayerContentTypeEnum.cxx ChartTwoLineSeriesContentTypeEnum.cxx ChartTwoMatrixContentTypeEnum.cxx ChartTwoMatrixDisplayProperties.cxx @@ -93,9 +108,10 @@ ChartTwoMatrixTriangularViewingModeEnum.cxx ChartTwoNumericSubdivisionsModeEnum.cxx ChartTwoLineSeriesHistory.cxx +ChartTwoOverlaySetInterface.cxx ChartTwoTitle.cxx -EventChartTwoAttributesChanged.cxx -EventChartTwoAxisGetDataRange.cxx +EventChartTwoCartesianAxisDisplayGroup.cxx +EventChartTwoCartesianOrientedAxesYoking.cxx EventChartTwoLoadLineSeriesData.cxx MapFileDataSelector.cxx ) diff -Nru connectome-workbench-1.4.2/src/Charting/EventChartTwoAttributesChanged.cxx connectome-workbench-1.5.0/src/Charting/EventChartTwoAttributesChanged.cxx --- connectome-workbench-1.4.2/src/Charting/EventChartTwoAttributesChanged.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/EventChartTwoAttributesChanged.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,191 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2017 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_DECLARE__ -#include "EventChartTwoAttributesChanged.h" -#undef __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_DECLARE__ - -#include "CaretAssert.h" -#include "EventTypeEnum.h" - -using namespace caret; - - - -/** - * \class caret::EventChartTwoAttributesChanged - * \brief Event for change in chart two attributes - * \ingroup Charting - */ - -/** - * Constructor. - */ -EventChartTwoAttributesChanged::EventChartTwoAttributesChanged() -: Event(EventTypeEnum::EVENT_CHART_TWO_ATTRIBUTES_CHANGED) -{ - -} - -/** - * Destructor. - */ -EventChartTwoAttributesChanged::~EventChartTwoAttributesChanged() -{ -} - -/** - * @return The mode. - */ -EventChartTwoAttributesChanged::Mode -EventChartTwoAttributesChanged::getMode() const -{ - return m_mode; -} - -/** - * Set for a change in a cartesian axis. - * - * @param yokingGroup - * Selected yoking group. - * @param chartTwoDataType - * Type of chart. - * @param cartesianAxis - * Cartesian axis that is changed. - */ -void -EventChartTwoAttributesChanged::setCartesianAxisChanged(const YokingGroupEnum::Enum yokingGroup, - const ChartTwoDataTypeEnum::Enum chartTwoDataType, - const ChartTwoCartesianAxis* cartesianAxis) -{ - CaretAssert(cartesianAxis); - m_mode = Mode::CARTESIAN_AXIS; - m_yokingGroup = yokingGroup; - m_chartTwoDataType = chartTwoDataType; - m_cartesianAxis = const_cast(cartesianAxis); -} - -/** - * Get for a change in a cartesian axis. - * - * @param yokingGroupOut - * Selected yoking group. - * @param chartTwoDataTypeOut - * Type of chart. - * @param cartesianAxisOut - * Cartesian axis that is changed. - */ -void -EventChartTwoAttributesChanged::getCartesianAxisChanged(YokingGroupEnum::Enum &yokingGroupOut, - ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, - ChartTwoCartesianAxis* &cartesianAxisOut) const -{ - CaretAssert(m_mode == Mode::CARTESIAN_AXIS); - yokingGroupOut = m_yokingGroup; - chartTwoDataTypeOut = m_chartTwoDataType; - cartesianAxisOut = m_cartesianAxis; -} - -/** - * Set for a change in line thickness. - * - * @param yokingGroup - * Selected yoking group. - * @param chartTwoDataType - * Type of chart. - * @param lineThickness - * New line thickness - */ -void -EventChartTwoAttributesChanged::setLineThicknessChanged(const YokingGroupEnum::Enum yokingGroup, - const ChartTwoDataTypeEnum::Enum chartTwoDataType, - const float lineThickness) -{ - m_mode = Mode::LINE_THICKESS; - m_yokingGroup = yokingGroup; - m_chartTwoDataType = chartTwoDataType; - m_lineThickness = lineThickness; -} - -/** - * Get for a change in line thickness - * - * @param yokingGroupOut - * Selected yoking group. - * @param chartTwoDataTypeOut - * Type of chart. - * @param lineThicknessOut - * Output line thickness - */ -void -EventChartTwoAttributesChanged::getLineThicknessChanged(YokingGroupEnum::Enum &yokingGroupOut, - ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, - float& lineThicknessOut) const -{ - CaretAssert(m_mode == Mode::LINE_THICKESS); - yokingGroupOut = m_yokingGroup; - chartTwoDataTypeOut = m_chartTwoDataType; - lineThicknessOut = m_lineThickness; -} - -/** - * Set for a change in a title. - * - * @param yokingGroup - * Selected yoking group. - * @param chartTwoDataType - * Type of chart. - * @parm chartTitle - * Chart title. - */ -void -EventChartTwoAttributesChanged::setTitleChanged(const YokingGroupEnum::Enum yokingGroup, - const ChartTwoDataTypeEnum::Enum chartTwoDataType, - const ChartTwoTitle* chartTitle) -{ - CaretAssert(chartTitle); - m_mode = Mode::TITLE; - m_yokingGroup = yokingGroup; - m_chartTwoDataType = chartTwoDataType; - m_chartTitle = const_cast(chartTitle); -} - -/** - * Get for a change in title. - * - * @param yokingGroupOut - * Selected yoking group. - * @param chartTwoDataTypeOut - * Type of chart. - * @parm chartTitleOut - * Chart title. - */ -void -EventChartTwoAttributesChanged::getTitleChanged(YokingGroupEnum::Enum &yokingGroupOut, - ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, - ChartTwoTitle* &chartTitleOut) const -{ - CaretAssert(m_mode == Mode::TITLE); - yokingGroupOut = m_yokingGroup; - chartTwoDataTypeOut = m_chartTwoDataType; - chartTitleOut = m_chartTitle; -} - diff -Nru connectome-workbench-1.4.2/src/Charting/EventChartTwoAttributesChanged.h connectome-workbench-1.5.0/src/Charting/EventChartTwoAttributesChanged.h --- connectome-workbench-1.4.2/src/Charting/EventChartTwoAttributesChanged.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/EventChartTwoAttributesChanged.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -#ifndef __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_H__ -#define __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2017 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - -#include "ChartTwoDataTypeEnum.h" -#include "Event.h" -#include "YokingGroupEnum.h" - - -namespace caret { - - class ChartTwoCartesianAxis; - class ChartTwoTitle; - class EventChartTwoAttributesChanged : public Event { - - public: - enum class Mode { - INVALID, - CARTESIAN_AXIS, - LINE_THICKESS, - TITLE - }; - - EventChartTwoAttributesChanged(); - - ~EventChartTwoAttributesChanged(); - - Mode getMode() const; - - void setCartesianAxisChanged(const YokingGroupEnum::Enum yokingGroup, - const ChartTwoDataTypeEnum::Enum chartTwoDataType, - const ChartTwoCartesianAxis* cartesianAxis); - - void getCartesianAxisChanged(YokingGroupEnum::Enum &yokingGroupOut, - ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, - ChartTwoCartesianAxis* &cartesianAxisOut) const; - - void setLineThicknessChanged(const YokingGroupEnum::Enum yokingGroup, - const ChartTwoDataTypeEnum::Enum chartTwoDataType, - const float lineThickness); - - void getLineThicknessChanged(YokingGroupEnum::Enum &yokingGroupOut, - ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, - float& lineThicknessOut) const; - - void setTitleChanged(const YokingGroupEnum::Enum yokingGroup, - const ChartTwoDataTypeEnum::Enum chartTwoDataType, - const ChartTwoTitle* chartTitle); - - void getTitleChanged(YokingGroupEnum::Enum &yokingGroupOut, - ChartTwoDataTypeEnum::Enum &chartTwoDataTypeOut, - ChartTwoTitle* &chartTitleOut) const; - - // ADD_NEW_METHODS_HERE - - private: - EventChartTwoAttributesChanged(const EventChartTwoAttributesChanged&); - - EventChartTwoAttributesChanged& operator=(const EventChartTwoAttributesChanged&); - - Mode m_mode = Mode::INVALID; - - YokingGroupEnum::Enum m_yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; - - ChartTwoDataTypeEnum::Enum m_chartTwoDataType = ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID; - - ChartTwoCartesianAxis* m_cartesianAxis = NULL; - - ChartTwoTitle* m_chartTitle = NULL; - - float m_lineThickness = 1.0f; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_DECLARE__ - // -#endif // __EVENT_CHART_TWO_ATTRIBUTES_CHANGED_DECLARE__ - -} // namespace -#endif //__EVENT_CHART_TWO_ATTRIBUTES_CHANGED_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/EventChartTwoAxisGetDataRange.cxx connectome-workbench-1.5.0/src/Charting/EventChartTwoAxisGetDataRange.cxx --- connectome-workbench-1.4.2/src/Charting/EventChartTwoAxisGetDataRange.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/EventChartTwoAxisGetDataRange.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2017 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_DECLARE__ -#include "EventChartTwoAxisGetDataRange.h" -#undef __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_DECLARE__ - -#include "CaretAssert.h" -#include "EventTypeEnum.h" - -using namespace caret; - - - -/** - * \class caret::EventChartTwoAxisGetDataRange - * \brief Event to get the range of data for a chart two axis - * \ingroup Charting - */ - -/** - * Constructor. - */ -EventChartTwoAxisGetDataRange::EventChartTwoAxisGetDataRange(const ChartTwoOverlaySet* chartOverlaySet, - const ChartAxisLocationEnum::Enum chartAxisLocation) -: Event(EventTypeEnum::EVENT_CHART_TWO_AXIS_GET_DATA_RANGE), -m_chartOverlaySet(chartOverlaySet), -m_chartAxisLocation(chartAxisLocation) -{ - -} - -/** - * Destructor. - */ -EventChartTwoAxisGetDataRange::~EventChartTwoAxisGetDataRange() -{ -} - -/** - * @return The chart overlay set that contains the axis. - */ -const ChartTwoOverlaySet* -EventChartTwoAxisGetDataRange::getChartOverlaySet() const -{ - return m_chartOverlaySet; -} - -/** - * @return The location of the axis. - */ -ChartAxisLocationEnum::Enum -EventChartTwoAxisGetDataRange::getChartAxisLocation() const -{ - return m_chartAxisLocation; -} - -/** - * Get the minimum and maximum values. - * - * @param minimumValue - * The minimum value. - * @param maximumValue - * The maximum value. - * @return - * True if the minimum and maximum are valid, else false. - */ -bool -EventChartTwoAxisGetDataRange::getMinimumAndMaximumValues(float& minimumValue, - float& maximumValue) const -{ - minimumValue = m_minimumValue; - maximumValue = m_maximumValue; - - return m_valuesValidFlag; -} - - -/** - * Set the minimum and maximum values. - * - * @param minimumValue - * The minimum value. - * @param maximumValue - * The maximum value. - */ -void EventChartTwoAxisGetDataRange::setMinimumAndMaximumValues(const float minimumValue, - const float maximumValue) -{ - m_minimumValue = minimumValue; - m_maximumValue = maximumValue; - m_valuesValidFlag = true; -} diff -Nru connectome-workbench-1.4.2/src/Charting/EventChartTwoAxisGetDataRange.h connectome-workbench-1.5.0/src/Charting/EventChartTwoAxisGetDataRange.h --- connectome-workbench-1.4.2/src/Charting/EventChartTwoAxisGetDataRange.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/EventChartTwoAxisGetDataRange.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -#ifndef __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_H__ -#define __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2017 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - -#include "ChartAxisLocationEnum.h" -#include "Event.h" - -namespace caret { - - class ChartTwoOverlaySet; - - class EventChartTwoAxisGetDataRange : public Event { - - public: - EventChartTwoAxisGetDataRange(const ChartTwoOverlaySet* chartOverlaySet, - const ChartAxisLocationEnum::Enum chartAxisLocation); - - virtual ~EventChartTwoAxisGetDataRange(); - - const ChartTwoOverlaySet* getChartOverlaySet() const; - - ChartAxisLocationEnum::Enum getChartAxisLocation() const; - - bool getMinimumAndMaximumValues(float& minimumValue, - float& maximumValue) const; - - void setMinimumAndMaximumValues(const float minimumValue, - const float maximumValue); - - // ADD_NEW_METHODS_HERE - - private: - EventChartTwoAxisGetDataRange(const EventChartTwoAxisGetDataRange&); - - EventChartTwoAxisGetDataRange& operator=(const EventChartTwoAxisGetDataRange&); - - const ChartTwoOverlaySet* m_chartOverlaySet; - - const ChartAxisLocationEnum::Enum m_chartAxisLocation; - - float m_minimumValue = 0.0f; - - float m_maximumValue = 0.0f; - - bool m_valuesValidFlag = false; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_DECLARE__ - // -#endif // __EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_DECLARE__ - -} // namespace -#endif //__EVENT_CHART_TWO_AXIS_GET_DATA_RANGE_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/EventChartTwoCartesianAxisDisplayGroup.cxx connectome-workbench-1.5.0/src/Charting/EventChartTwoCartesianAxisDisplayGroup.cxx --- connectome-workbench-1.4.2/src/Charting/EventChartTwoCartesianAxisDisplayGroup.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/EventChartTwoCartesianAxisDisplayGroup.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,241 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CHART_TWO_CARTESIAN_AXIS_DISPLAY_GROUP_DECLARE__ +#include "EventChartTwoCartesianAxisDisplayGroup.h" +#undef __EVENT_CHART_TWO_CARTESIAN_AXIS_DISPLAY_GROUP_DECLARE__ + +#include "CaretAssert.h" +#include "CaretResult.h" +#include "ChartTwoCartesianAxis.h" +#include "EventManager.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventChartTwoCartesianAxisDisplayGroup + * \brief Event to get an axis associated with a display group + * \ingroup Charting + */ + +/** + * Constructor. + * @param displayGroup + * Display group for desired axis + */ +EventChartTwoCartesianAxisDisplayGroup::EventChartTwoCartesianAxisDisplayGroup(const Mode mode, + const DisplayGroupEnum::Enum displayGroup, + ChartTwoCartesianAxis* axis) +: Event(EventTypeEnum::EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP), +m_mode(mode), +m_displayGroup(displayGroup), +m_axis(axis) +{ + +} + +/** + * Destructor. + */ +EventChartTwoCartesianAxisDisplayGroup::~EventChartTwoCartesianAxisDisplayGroup() +{ +} + +/** + * @return Axis associated with the given display group. This axis exists + * in SessionManager but since the Charting module cannot access + * SessionManager, this axis is obtained throught this event. + * + * @param displayGroup (NEVER use TAB) + * The display group + */ +ChartTwoCartesianAxis* +EventChartTwoCartesianAxisDisplayGroup::getAxisForDisplayGroup(const DisplayGroupEnum::Enum displayGroup) +{ + switch (displayGroup) { + case DisplayGroupEnum::DISPLAY_GROUP_A: + case DisplayGroupEnum::DISPLAY_GROUP_B: + case DisplayGroupEnum::DISPLAY_GROUP_C: + case DisplayGroupEnum::DISPLAY_GROUP_D: + break; + case DisplayGroupEnum::DISPLAY_GROUP_TAB: + CaretAssert(0); + /** Note event below will function for TAB */ + break; + } + + ChartTwoCartesianAxis* nullAxis(NULL); + EventChartTwoCartesianAxisDisplayGroup event(Mode::GET_DISPLAY_GROUP_AXIS, + displayGroup, + nullAxis); + EventManager::get()->sendEvent(event.getPointer()); + CaretAssert(event.getEventProcessCount()); + return event.getAxis(); +} + +/** + * @return All chart axes yoked to the given display group. Only those axes that + * are in displayed charts are included. + * @param displayGroup (NEVER use TAB) + * The display group + */ +std::vector +EventChartTwoCartesianAxisDisplayGroup::getAxesYokedToDisplayGroup(const DisplayGroupEnum::Enum displayGroup) +{ + std::vector axes; + switch (displayGroup) { + case DisplayGroupEnum::DISPLAY_GROUP_A: + case DisplayGroupEnum::DISPLAY_GROUP_B: + case DisplayGroupEnum::DISPLAY_GROUP_C: + case DisplayGroupEnum::DISPLAY_GROUP_D: + { + + ChartTwoCartesianAxis* nullAxis(NULL); + EventChartTwoCartesianAxisDisplayGroup event(Mode::GET_ALL_YOKED_AXES, + displayGroup, + nullAxis); + EventManager::get()->sendEvent(event.getPointer()); + axes = event.getYokedAxes(); + } + break; + case DisplayGroupEnum::DISPLAY_GROUP_TAB: + CaretAssert(0); + return axes; + break; + } + + return axes; +} + +/** + * Initialize the display group axis if it is not yoked to any axes, otherwise, do nothing. + * @param displayGroup (NEVER use TAB) + * The display group + * @param axis + * Axis that is copied to the display group axis. + */ +void +EventChartTwoCartesianAxisDisplayGroup::initializeUnyokedDisplayGroupAxis(const DisplayGroupEnum::Enum displayGroup, + const ChartTwoCartesianAxis* axis) +{ + CaretAssert(axis); + + switch (displayGroup) { + case DisplayGroupEnum::DISPLAY_GROUP_A: + case DisplayGroupEnum::DISPLAY_GROUP_B: + case DisplayGroupEnum::DISPLAY_GROUP_C: + case DisplayGroupEnum::DISPLAY_GROUP_D: + { + /* + * Test for NO axes yoked to display group + */ + std::vector axes = EventChartTwoCartesianAxisDisplayGroup::getAxesYokedToDisplayGroup(displayGroup); + if (axes.empty()) { + ChartTwoCartesianAxis* displayGroupAxis = EventChartTwoCartesianAxisDisplayGroup::getAxisForDisplayGroup(displayGroup); + if (displayGroupAxis != NULL) { + /* + * Since no axes yet yoked to display group, copy the given + * axis parameters to the display group's axis + */ + displayGroupAxis->copyAxisParameters(axis); + } + } + } + break; + case DisplayGroupEnum::DISPLAY_GROUP_TAB: + CaretAssert(0); + return; + break; + } +} + + +/** + * @return The mode + */ +EventChartTwoCartesianAxisDisplayGroup::Mode +EventChartTwoCartesianAxisDisplayGroup::getMode() const +{ + return m_mode; +} + + +/** + * @return The display group + */ +DisplayGroupEnum::Enum +EventChartTwoCartesianAxisDisplayGroup::getDisplayGroup() const +{ + return m_displayGroup; +} + +/** + * @return the axis + */ +ChartTwoCartesianAxis* +EventChartTwoCartesianAxisDisplayGroup::getAxis() +{ + return m_axis; +} + +/** + * @return the axis (const method) + */ +const ChartTwoCartesianAxis* +EventChartTwoCartesianAxisDisplayGroup::getAxis() const +{ + return m_axis; +} + +/* + * Set the axis + * @param axis + * New value for axis + */ +void +EventChartTwoCartesianAxisDisplayGroup::setAxis(ChartTwoCartesianAxis* axis) +{ + m_axis = axis; +} + +/** + * @return All yoked axes + */ +std::vector +EventChartTwoCartesianAxisDisplayGroup::getYokedAxes() const +{ + return m_yokedAxes; +} + +/** + * Add to the yoked axes + * @param axes + * Axes added to the yoked axes + */ +void +EventChartTwoCartesianAxisDisplayGroup::addToYokedAxes(ChartTwoCartesianAxis* axis) +{ + m_yokedAxes.push_back(axis); +} + diff -Nru connectome-workbench-1.4.2/src/Charting/EventChartTwoCartesianAxisDisplayGroup.h connectome-workbench-1.5.0/src/Charting/EventChartTwoCartesianAxisDisplayGroup.h --- connectome-workbench-1.4.2/src/Charting/EventChartTwoCartesianAxisDisplayGroup.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/EventChartTwoCartesianAxisDisplayGroup.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,101 @@ +#ifndef __EVENT_CHART_TWO_CARTESIAN_AXIS_DISPLAY_GROUP_H__ +#define __EVENT_CHART_TWO_CARTESIAN_AXIS_DISPLAY_GROUP_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include "DisplayGroupEnum.h" +#include "Event.h" + + + +namespace caret { + + class CaretResult; + class ChartTwoCartesianAxis; + + class EventChartTwoCartesianAxisDisplayGroup : public Event { + public: + enum class Mode { + /** Get a list of all active yoked axes */ + GET_ALL_YOKED_AXES, + /* Get the axis associated with a display group*/ + GET_DISPLAY_GROUP_AXIS + }; + + static ChartTwoCartesianAxis* getAxisForDisplayGroup(const DisplayGroupEnum::Enum displayGroup); + + static std::vector getAxesYokedToDisplayGroup(const DisplayGroupEnum::Enum displayGroup); + + static void initializeUnyokedDisplayGroupAxis(const DisplayGroupEnum::Enum displayGroup, + const ChartTwoCartesianAxis* axis); + + // add static method to initialize display group with an axis when no axes are yokeed + + virtual ~EventChartTwoCartesianAxisDisplayGroup(); + + Mode getMode() const; + + DisplayGroupEnum::Enum getDisplayGroup() const; + + ChartTwoCartesianAxis* getAxis(); + + const ChartTwoCartesianAxis* getAxis() const; + + void setAxis(ChartTwoCartesianAxis* axis); + + std::vector getYokedAxes() const; + + void addToYokedAxes(ChartTwoCartesianAxis* axis); + + EventChartTwoCartesianAxisDisplayGroup(const EventChartTwoCartesianAxisDisplayGroup&) = delete; + + EventChartTwoCartesianAxisDisplayGroup& operator=(const EventChartTwoCartesianAxisDisplayGroup&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + EventChartTwoCartesianAxisDisplayGroup(const Mode mode, + const DisplayGroupEnum::Enum displayGroup, + ChartTwoCartesianAxis* axis); + + const Mode m_mode; + + const DisplayGroupEnum::Enum m_displayGroup; + + ChartTwoCartesianAxis* m_axis = NULL; + + std::vector m_yokedAxes; + + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_CHART_TWO_CARTESIAN_AXIS_DISPLAY_GROUP_DECLARE__ + // +#endif // __EVENT_CHART_TWO_CARTESIAN_AXIS_DISPLAY_GROUP_DECLARE__ + +} // namespace +#endif //__EVENT_CHART_TWO_CARTESIAN_AXIS_DISPLAY_GROUP_H__ diff -Nru connectome-workbench-1.4.2/src/Charting/EventChartTwoCartesianOrientedAxesYoking.cxx connectome-workbench-1.5.0/src/Charting/EventChartTwoCartesianOrientedAxesYoking.cxx --- connectome-workbench-1.4.2/src/Charting/EventChartTwoCartesianOrientedAxesYoking.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/EventChartTwoCartesianOrientedAxesYoking.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,342 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING_DECLARE__ +#include "EventChartTwoCartesianOrientedAxesYoking.h" +#undef __EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING_DECLARE__ + +#include "CaretAssert.h" +#include "ChartTwoCartesianOrientedAxes.h" +#include "EventManager.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventChartTwoCartesianOrientedAxesYoking + * \brief Event for getting/setting min/max values for chart axes yoking + * \ingroup Charting + */ + +/** + * Constructor. + * @param mode + * The mode for the event + * @param axisOrientation + * The axis orientation + * @param yokingRangeMode + * The yoking range mode + */ +EventChartTwoCartesianOrientedAxesYoking::EventChartTwoCartesianOrientedAxesYoking(const Mode mode, + const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode) +: Event(EventTypeEnum::EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING), +m_mode(mode), +m_axisOrientation(axisOrientation), +m_yokingRangeMode(yokingRangeMode) +{ + +} + +/** + * Destructor. + */ +EventChartTwoCartesianOrientedAxesYoking::~EventChartTwoCartesianOrientedAxesYoking() +{ +} + +/** + * @return The mode + */ +EventChartTwoCartesianOrientedAxesYoking::Mode +EventChartTwoCartesianOrientedAxesYoking::getMode() const +{ + return m_mode; +} + +/** + * @return The axes orientation + */ +ChartTwoAxisOrientationTypeEnum::Enum +EventChartTwoCartesianOrientedAxesYoking::getAxisOrientation() const +{ + return m_axisOrientation; +} + +/** + * @return The yoking range mode + */ +ChartTwoAxisScaleRangeModeEnum::Enum +EventChartTwoCartesianOrientedAxesYoking::getYokingRangeMode() const +{ + return m_yokingRangeMode; +} + +/** + * Get the minimum and maximum values for an orientation and yoking group + * @param minimumValueOut + * Output with minimum value + * @param maximumValueOut + * Output with maximum value + * @return True if the output values are valid; false if no axes are yoked to the yokingRangeMode + */ +bool +EventChartTwoCartesianOrientedAxesYoking::getMinimumAndMaximumValues(float& minimumValueOut, + float& maximumValueOut) const +{ + minimumValueOut = m_minimumValue; + maximumValueOut = m_maximumValue; + return m_valuesValid; +} + +/** + * @return The minimum value + */ +float +EventChartTwoCartesianOrientedAxesYoking::getMinimumValue() const +{ + return m_minimumValue; +} + +/** + * @return The maximum value + */ +float +EventChartTwoCartesianOrientedAxesYoking::getMaximumValue() const +{ + return m_maximumValue; +} + +/** + * Set the minimum and maximum values for an orientation and yoking group + * and sets the validty for the minimum and maximum values + * @param minimumValue + * Minimum value + * @param maximumValue + * Maximum value + */ +void +EventChartTwoCartesianOrientedAxesYoking::setMinimumAndMaximumValues(const float minimumValue, + const float maximumValue) +{ + m_minimumValue = minimumValue; + m_maximumValue = maximumValue; + m_valuesValid = true; +} + +/** + * Add to the yoked axes + * @param axes + * Axes to add + */ +void +EventChartTwoCartesianOrientedAxesYoking::addYokedAxes(ChartTwoCartesianOrientedAxes* axes) +{ + m_yokedAxes.push_back(axes); +} + +/** + * @return Vector containing the yoked axes + */ +std::vector +EventChartTwoCartesianOrientedAxesYoking::getYokedAxes() const +{ + return m_yokedAxes; +} + + +/** + * Static method to get the yoked axes + * @param axisOrientation + * The axis orientation + * @param yokingRangeMode + * The yoking range mode + * @return Vector containing the yoked axes + * + */ +std::vector +EventChartTwoCartesianOrientedAxesYoking::getYokedAxes(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode) +{ + std::vector axesOut; + + EventChartTwoCartesianOrientedAxesYoking axesEvent(EventChartTwoCartesianOrientedAxesYoking::Mode::GET_YOKED_AXES, + axisOrientation, + yokingRangeMode); + EventManager::get()->sendEvent(axesEvent.getPointer()); + + return axesEvent.getYokedAxes(); +} + +/** + * Static method to get the minimum and maximum from the data in all yoked charts for an orientation and yoking group + * @param axisOrientation + * The axis orientation + * @param yokingRangeMode + * The yoking range mode + * @param minimumValueOut + * Output with minimum value + * @param maximumValueOut + * Output with maximum value + * @return True if the output values are valid; false if no axes are yoked to the yokingRangeMode + */ +bool +EventChartTwoCartesianOrientedAxesYoking::getDataRangeMinMaxValues(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + float& dataMinimumValueOut, + float& dataMaximumValueOut) +{ + dataMinimumValueOut = 0.0; + dataMaximumValueOut = 0.0; + + EventChartTwoCartesianOrientedAxesYoking axesEvent(EventChartTwoCartesianOrientedAxesYoking::Mode::GET_YOKED_AXES, + axisOrientation, + yokingRangeMode); + EventManager::get()->sendEvent(axesEvent.getPointer()); + if (axesEvent.getEventProcessCount() > 0) { + float minValue(std::numeric_limits::max()); + float maxValue(-minValue); + + std::vector axes = axesEvent.getYokedAxes(); + for (auto a : axes) { + float minData(0.0), maxData(0.0); + a->getDataRange(minData, maxData); + + if (minData < minValue) minValue = minData; + if (maxData > maxValue) maxValue = maxData; + } + + if (maxValue > minValue) { + dataMinimumValueOut = minValue; + dataMaximumValueOut = maxValue; + return true; + } + } + + return false; +} + +/** + * Static method to get the minimum and maximum values for an orientation and yoking group + * @param axisOrientation + * The axis orientation + * @param yokingRangeMode + * The yoking range mode + * @param minimumValueOut + * Output with minimum value + * @param maximumValueOut + * Output with maximum value + * @return True if the output values are valid; false if no axes are yoked to the yokingRangeMode + */ +bool +EventChartTwoCartesianOrientedAxesYoking::getMinMaxValues(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + float& minimumValueOut, + float& maximumValueOut) +{ + minimumValueOut = 0.0; + maximumValueOut = 0.0; + + EventChartTwoCartesianOrientedAxesYoking axesEvent(EventChartTwoCartesianOrientedAxesYoking::Mode::GET_MINIMUM_AND_MAXIMUM_VALUES, + axisOrientation, + yokingRangeMode); + EventManager::get()->sendEvent(axesEvent.getPointer()); + if (axesEvent.getEventProcessCount() > 0) { + minimumValueOut = axesEvent.getMinimumValue(); + maximumValueOut = axesEvent.getMaximumValue(); + return true; + } + + return false; +} + +/** + * Static method to set the minimum and maximum values for an orientation and yoking group + * @param axisOrientation + * The axis orientation + * @param yokingRangeMode + * The yoking range mode + * @param minimumValue + * New minimum value + * @param maximumValue + * New maximum value + */ +void +EventChartTwoCartesianOrientedAxesYoking::setMinMaxValues(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + const float minimumValue, + const float maximumValue) +{ + EventChartTwoCartesianOrientedAxesYoking axesEvent(EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MINIMUM_AND_MAXIMUM_VALUES, + axisOrientation, + yokingRangeMode); + axesEvent.setMinimumAndMaximumValues(minimumValue, + maximumValue); + EventManager::get()->sendEvent(axesEvent.getPointer()); +} + +/** + * Static method to set the minimum value for an orientation and yoking group + * @param axisOrientation + * The axis orientation + * @param yokingRangeMode + * The yoking range mode + * @param minimumValue + * New minimum value + */ +void +EventChartTwoCartesianOrientedAxesYoking::setMinimumValue(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + const float minimumValue) +{ + EventChartTwoCartesianOrientedAxesYoking axesEvent(EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MINIMUM_VALUE, + axisOrientation, + yokingRangeMode); + float dummy(0.0); + axesEvent.setMinimumAndMaximumValues(minimumValue, + dummy); + EventManager::get()->sendEvent(axesEvent.getPointer()); +} + +/** + * Static method to set the maximum value for an orientation and yoking group + * @param axisOrientation + * The axis orientation + * @param yokingRangeMode + * The yoking range mode + * @param maximumValue + * New maximum value + */ +void +EventChartTwoCartesianOrientedAxesYoking::setMaximumValue(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + const float maximumValue) +{ + EventChartTwoCartesianOrientedAxesYoking axesEvent(EventChartTwoCartesianOrientedAxesYoking::Mode::SET_MAXIMUM_VALUE, + axisOrientation, + yokingRangeMode); + float dummy(0.0); + axesEvent.setMinimumAndMaximumValues(dummy, + maximumValue); + EventManager::get()->sendEvent(axesEvent.getPointer()); +} diff -Nru connectome-workbench-1.4.2/src/Charting/EventChartTwoCartesianOrientedAxesYoking.h connectome-workbench-1.5.0/src/Charting/EventChartTwoCartesianOrientedAxesYoking.h --- connectome-workbench-1.4.2/src/Charting/EventChartTwoCartesianOrientedAxesYoking.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Charting/EventChartTwoCartesianOrientedAxesYoking.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,131 @@ +#ifndef __EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING_H__ +#define __EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "ChartTwoAxisOrientationTypeEnum.h" +#include "ChartTwoAxisScaleRangeModeEnum.h" +#include "Event.h" + + + +namespace caret { + + class ChartTwoCartesianOrientedAxes; + + class EventChartTwoCartesianOrientedAxesYoking : public Event { + + public: + enum class Mode { + GET_YOKED_AXES, + GET_MINIMUM_AND_MAXIMUM_VALUES, + SET_MINIMUM_AND_MAXIMUM_VALUES, + SET_MINIMUM_VALUE, + SET_MAXIMUM_VALUE + }; + + static std::vector getYokedAxes(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode); + + static bool getDataRangeMinMaxValues(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + float& dataMinimumValueOut, + float& dataMaximumValueOut); + + static bool getMinMaxValues(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + float& minimumValueOut, + float& maximumValueOut); + + static void setMinMaxValues(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + const float minimumValue, + const float maximumValue); + + static void setMinimumValue(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + const float minimumValue); + + static void setMaximumValue(const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode, + const float maximumValue); + + virtual ~EventChartTwoCartesianOrientedAxesYoking(); + + EventChartTwoCartesianOrientedAxesYoking(const EventChartTwoCartesianOrientedAxesYoking&) = delete; + + EventChartTwoCartesianOrientedAxesYoking& operator=(const EventChartTwoCartesianOrientedAxesYoking&) = delete; + + Mode getMode() const; + + ChartTwoAxisOrientationTypeEnum::Enum getAxisOrientation() const; + + ChartTwoAxisScaleRangeModeEnum::Enum getYokingRangeMode() const; + + float getMinimumValue() const; + + float getMaximumValue() const; + + bool getMinimumAndMaximumValues(float& minimumValueOut, + float& maximumValueOut) const; + + void setMinimumAndMaximumValues(const float minimumValue, + const float maximumValue); + + void addYokedAxes(ChartTwoCartesianOrientedAxes* axes); + + std::vector getYokedAxes() const; + + // ADD_NEW_METHODS_HERE + + private: + EventChartTwoCartesianOrientedAxesYoking(const Mode mode, + const ChartTwoAxisOrientationTypeEnum::Enum axisOrientation, + const ChartTwoAxisScaleRangeModeEnum::Enum yokingRangeMode); + + const Mode m_mode; + + const ChartTwoAxisOrientationTypeEnum::Enum m_axisOrientation; + + const ChartTwoAxisScaleRangeModeEnum::Enum m_yokingRangeMode; + + std::vector m_yokedAxes; + + float m_minimumValue = 0.0; + + float m_maximumValue = 0.0; + + bool m_valuesValid = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING_DECLARE__ + // +#endif // __EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING_DECLARE__ + +} // namespace +#endif //__EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING_H__ diff -Nru connectome-workbench-1.4.2/src/Cifti/CiftiFile.cxx connectome-workbench-1.5.0/src/Cifti/CiftiFile.cxx --- connectome-workbench-1.4.2/src/Cifti/CiftiFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Cifti/CiftiFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -551,6 +551,7 @@ default: CaretLogWarning("unhandled cifti type in extension warning check, tell the developers what you just tried to do"); CaretAssert(0);//yes, let it fall through to "unknown" in release so that it at least looks for .nii + //-fallthrough case 3000://unknown if (!filename.contains(QRegExp("\\.[^.]*\\.nii$"))) { @@ -560,7 +561,7 @@ if (filename.contains(QRegExp("\\.(dconn|dtseries|pconn|ptseries|dscalar|dfan|fiberTEMP|dlabel|pscalar|pdconn|dpconn|pconnseries|pconnscalar)\\.nii$"))) { CaretLogWarning("cifti file of nonstandard mapping combination '" + filename + "' should NOT be saved using an already-used cifti extension, " - + "please choose a different, reasonable cifti extension ending in ..nii"); + + "please choose a different, reasonable cifti extension of the form ..nii"); } break; case 3001: diff -Nru connectome-workbench-1.4.2/src/CMakeLists.txt connectome-workbench-1.5.0/src/CMakeLists.txt --- connectome-workbench-1.4.2/src/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -3,7 +3,7 @@ # CMAKE_MINIMUM_REQUIRED (VERSION 2.8) -SET(WB_VERSION "1.4.2") +SET(WB_VERSION "1.5.0") # # Set to true for verbose output when debugging this file # diff -Nru connectome-workbench-1.4.2/src/CommandLine/CMakeLists.txt connectome-workbench-1.5.0/src/CommandLine/CMakeLists.txt --- connectome-workbench-1.4.2/src/CommandLine/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/CommandLine/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -147,3 +147,6 @@ ${QUAZIP_INCLUDE_DIRS} ) +ENABLE_TESTING() + +ADD_TEST(command_help ${EXE_NAME} -all-commands-help) diff -Nru connectome-workbench-1.4.2/src/Commands/CommandClassCreateEnum.cxx connectome-workbench-1.5.0/src/Commands/CommandClassCreateEnum.cxx --- connectome-workbench-1.4.2/src/Commands/CommandClassCreateEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Commands/CommandClassCreateEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -524,7 +524,7 @@ t += (" *isValidOut = validFlag;\n"); t += (" }\n"); t += (" else if (validFlag == false) {\n"); - t += (" CaretAssertMessage(0, AString(\"Name \" + name + \"failed to match enumerated value for type " + enumClassName + "\"));\n"); + t += (" CaretAssertMessage(0, AString(\"Name \" + name + \" failed to match enumerated value for type " + enumClassName + "\"));\n"); t += (" }\n"); t += (" return enumValue;\n"); t += ("}\n"); @@ -581,7 +581,7 @@ t += (" *isValidOut = validFlag;\n"); t += (" }\n"); t += (" else if (validFlag == false) {\n"); - t += (" CaretAssertMessage(0, AString(\"guiName \" + guiName + \"failed to match enumerated value for type " + enumClassName + "\"));\n"); + t += (" CaretAssertMessage(0, AString(\"guiName \" + guiName + \" failed to match enumerated value for type " + enumClassName + "\"));\n"); t += (" }\n"); t += (" return enumValue;\n"); t += ("}\n"); @@ -638,7 +638,7 @@ t += (" *isValidOut = validFlag;\n"); t += (" }\n"); t += (" else if (validFlag == false) {\n"); - t += (" CaretAssertMessage(0, AString(\"Integer code \" + AString::number(integerCode) + \"failed to match enumerated value for type " + enumClassName + "\"));\n"); + t += (" CaretAssertMessage(0, AString(\"Integer code \" + AString::number(integerCode) + \" failed to match enumerated value for type " + enumClassName + "\"));\n"); t += (" }\n"); t += (" return enumValue;\n"); t += ("}\n"); diff -Nru connectome-workbench-1.4.2/src/Commands/CommandOperation.cxx connectome-workbench-1.5.0/src/Commands/CommandOperation.cxx --- connectome-workbench-1.4.2/src/Commands/CommandOperation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Commands/CommandOperation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -82,6 +82,14 @@ { } +void CommandOperation::setVolumeOutputDTypeAndScale(const int16_t&, const double&, const double&) +{ +} + +void CommandOperation::setVolumeOutputDTypeNoScale(const int16_t&) +{ +} + AString CommandOperation::doCompletion(ProgramParameters&, const bool&) { return ""; diff -Nru connectome-workbench-1.4.2/src/Commands/CommandOperation.h connectome-workbench-1.5.0/src/Commands/CommandOperation.h --- connectome-workbench-1.4.2/src/Commands/CommandOperation.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Commands/CommandOperation.h 2021-02-16 19:46:47.000000000 +0000 @@ -42,11 +42,15 @@ void execute(ProgramParameters& parameters, const bool& preventProvenance); virtual void setCiftiOutputDTypeAndScale(const int16_t& dtype, const double& minVal, const double& maxVal); - + virtual void setCiftiOutputDTypeNoScale(const int16_t& dtype); - + + virtual void setVolumeOutputDTypeAndScale(const int16_t& dtype, const double& minVal, const double& maxVal); + + virtual void setVolumeOutputDTypeNoScale(const int16_t& dtype); + virtual AString doCompletion(ProgramParameters& parameters, const bool& useExtGlob); - + protected: /** * Execute the operation. diff -Nru connectome-workbench-1.4.2/src/Commands/CommandOperationManager.cxx connectome-workbench-1.5.0/src/Commands/CommandOperationManager.cxx --- connectome-workbench-1.4.2/src/Commands/CommandOperationManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Commands/CommandOperationManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -57,6 +57,7 @@ #include "AlgorithmCiftiReorder.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmCiftiResample.h" +#include "AlgorithmCiftiRestrictDenseMap.h" #include "AlgorithmCiftiROIsFromExtrema.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiSmoothing.h" @@ -133,6 +134,7 @@ #include "AlgorithmVolumeParcelSmoothing.h" #include "AlgorithmVolumeReduce.h" #include "AlgorithmVolumeRemoveIslands.h" +#include "AlgorithmVolumeResample.h" #include "AlgorithmVolumeROIsFromExtrema.h" #include "AlgorithmVolumeSmoothing.h" #include "AlgorithmVolumeTFCE.h" @@ -164,7 +166,6 @@ #include "OperationCiftiMerge.h" #include "OperationCiftiPalette.h" #include "OperationCiftiResampleDconnMemory.h" -#include "AlgorithmCiftiRestrictDenseMap.h" #include "OperationCiftiROIAverage.h" #include "OperationCiftiSeparateAll.h" #include "OperationCiftiStats.h" @@ -248,7 +249,7 @@ #include "CaretLogger.h" #include "dot_wrapper.h" -#include "StructureEnum.h" +#include "CaretCommandGlobalOptions.h" #include #include @@ -380,7 +381,6 @@ this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceSphereProjectUnproject())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceToSurface3dDistance())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceWedgeVolume())); - this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeAffineResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeAllLabelsToROIs())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeDilate())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeDistortion())); @@ -399,13 +399,13 @@ this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeParcelSmoothing())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeReduce())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeRemoveIslands())); + this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeROIsFromExtrema())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeSmoothing())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeTFCE())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeToSurfaceMapping())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeVectorOperation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeWarpfieldAffineRegression())); - this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeWarpfieldResample())); this->commandOperations.push_back(new CommandParser(new AutoOperationAddToSpecFile())); this->commandOperations.push_back(new CommandParser(new AutoOperationBackendAverageDenseROI())); @@ -509,6 +509,8 @@ this->deprecatedOperations.push_back(new CommandParser(new AutoOperationCiftiSeparateAll())); this->deprecatedOperations.push_back(new CommandParser(new AutoOperationMetricVertexSum())); this->deprecatedOperations.push_back(new CommandParser(new AutoOperationSetMapName())); + this->deprecatedOperations.push_back(new CommandParser(new AutoAlgorithmVolumeAffineResample())); + this->deprecatedOperations.push_back(new CommandParser(new AutoAlgorithmVolumeWarpfieldResample())); } /** @@ -550,7 +552,7 @@ map::iterator iter = nameToCode.find(input); if (iter == nameToCode.end()) { - throw CommandException("Unrecognized cifti datatype: '" + input + "'"); + throw CommandException("Unrecognized nifti datatype: '" + input + "'"); } return iter->second; } @@ -587,9 +589,9 @@ CaretLogWarning("SIMD type '" + DotSIMDEnum::toName(impl) + "' not supported (could be cpu, compiler, or build options), using '" + DotSIMDEnum::toName(retval) + "'"); } } - int16_t ciftiDType = NIFTI_TYPE_FLOAT32; - bool ciftiScale = false; - double ciftiMin = -1.0, ciftiMax = -1.0; + int16_t ciftiDType = NIFTI_TYPE_FLOAT32, niftiDType = NIFTI_TYPE_FLOAT32; + bool ciftiScale = false, niftiScale = false; + double ciftiMin = -1.0, ciftiMax = -1.0, niftiMin = -1.0, niftiMax = -1.0; if (getGlobalOption(parameters, "-cifti-output-datatype", 1, globalOptionArgs)) { ciftiDType = stringToCiftiType(globalOptionArgs[0]); @@ -603,6 +605,23 @@ ciftiMax = globalOptionArgs[1].toDouble(&valid); if (!valid) throw CommandException("non-numeric option to -cifti-output-range: '" + globalOptionArgs[1] + "'"); } + if (getGlobalOption(parameters, "-nifti-output-datatype", 1, globalOptionArgs)) + { + niftiDType = stringToCiftiType(globalOptionArgs[0]); + } + if (getGlobalOption(parameters, "-nifti-output-range", 2, globalOptionArgs)) + { + niftiScale = true; + bool valid = false; + niftiMin = globalOptionArgs[0].toDouble(&valid); + if (!valid) throw CommandException("non-numeric option to -nifti-output-range: '" + globalOptionArgs[0] + "'"); + niftiMax = globalOptionArgs[1].toDouble(&valid); + if (!valid) throw CommandException("non-numeric option to -nifti-output-range: '" + globalOptionArgs[1] + "'"); + } + if (getGlobalOption(parameters, "-cifti-read-memory", 0, globalOptionArgs)) + { + caret_global_command_options.m_ciftiReadMemory = true; + } const uint64_t numberOfCommands = this->commandOperations.size(); const uint64_t numberOfDeprecated = this->deprecatedOperations.size(); @@ -676,6 +695,14 @@ { cout << operation->getHelpInformation(myProgramName) << endl; } else { + if (niftiScale) + { + operation->setCiftiOutputDTypeAndScale(niftiDType, niftiMin, niftiMax); + operation->setVolumeOutputDTypeAndScale(niftiDType, niftiMin, niftiMax); + } else { + operation->setCiftiOutputDTypeNoScale(niftiDType); + operation->setVolumeOutputDTypeNoScale(niftiDType); + } if (ciftiScale) { operation->setCiftiOutputDTypeAndScale(ciftiDType, ciftiMin, ciftiMax); @@ -721,14 +748,25 @@ OptionInfo ciftiDTypeInfo = parseGlobalOption(parameters, "-cifti-output-datatype", 1, globalOptionArgs, true); if (ciftiDTypeInfo.specified && !ciftiDTypeInfo.complete) { - return "wordlist INT8 UINT8 INT16 UINT16 INT32 UINT32 INT64 UINT64 FLOAT32 FLOAT64 FLOAT128"; + return "wordlist INT8\\ UINT8\\ INT16\\ UINT16\\ INT32\\ UINT32\\ INT64\\ UINT64\\ FLOAT32\\ FLOAT64\\ FLOAT128"; } OptionInfo ciftiRangeInfo = parseGlobalOption(parameters, "-cifti-output-range", 2, globalOptionArgs, true); if (ciftiRangeInfo.specified && !ciftiRangeInfo.complete) {//can't tab complete a literal number return ""; } - ret = "wordlist -disable-provenance\\ -logging\\ -simd\\ -cifti-output-datatype\\ -cifti-output-range";//we could prevent suggesting an already-provided global option, but that would be a bit surprising + OptionInfo niftiDTypeInfo = parseGlobalOption(parameters, "-nifti-output-datatype", 1, globalOptionArgs, true); + if (niftiDTypeInfo.specified && !niftiDTypeInfo.complete) + { + return "wordlist INT8\\ UINT8\\ INT16\\ UINT16\\ INT32\\ UINT32\\ INT64\\ UINT64\\ FLOAT32\\ FLOAT64\\ FLOAT128"; + } + OptionInfo niftiRangeInfo = parseGlobalOption(parameters, "-nifti-output-range", 2, globalOptionArgs, true); + if (niftiRangeInfo.specified && !niftiRangeInfo.complete) + { + return ""; + } + /*OptionInfo ciftiReadMemInfo = */parseGlobalOption(parameters, "-cifti-read-memory", 0, globalOptionArgs, true); + ret = "wordlist -disable-provenance\\ -logging\\ -simd\\ -cifti-output-datatype\\ -cifti-output-range\\ -nifti-output-datatype\\ -nifti-output-range\\ -cifti-read-memory";//we could prevent suggesting an already-provided global option, but that would be a bit surprising const uint64_t numberOfCommands = this->commandOperations.size(); const uint64_t numberOfDeprecated = this->deprecatedOperations.size(); if (!parameters.hasNext()) @@ -1084,10 +1122,10 @@ cout << " -disable-provenance don't generate provenance info in output" << endl; cout << " files" << endl; cout << endl; - cout << " -cifti-output-datatype write cifti output with the given" << endl; - cout << " datatype (default FLOAT32), note that" << endl; - cout << " calculation precision is only float32," << endl; - cout << " valid values are:" << endl; + cout << " -nifti-output-datatype write cifti and volume output with the" << endl; + cout << " given datatype (default FLOAT32), note" << endl; + cout << " that calculation precision is only" << endl; + cout << " float32, valid values are:" << endl; cout << " INT8" << endl; cout << " UINT8" << endl; cout << " INT16" << endl; @@ -1101,11 +1139,12 @@ cout << " FLOAT128" << endl; cout << endl; //guide for wrap, assuming 80 columns: | - cout << " -cifti-output-range write cifti output with scaling and offset" << endl; - cout << " header fields such that and " << endl; - cout << " are the most extreme values that can be" << endl; - cout << " represented, mostly useful with integer" << endl; - cout << " output datatypes (see above)" << endl; + cout << " -nifti-output-range write cifti and volume output with scaling" << endl; + cout << " and offset header fields such that" << endl; + cout << " and are the most extreme" << endl; + cout << " values that can be represented, not" << endl; + cout << " recommended with floating point types" << endl; + cout << " (see above)" << endl; cout << endl; cout << " -logging set the logging level, valid values are:" << endl; vector logLevels; @@ -1128,6 +1167,13 @@ cout << " " << DotSIMDEnum::toName(*iter) << endl; } cout << endl; + cout << " -cifti-read-memory read cifti input files into memory, to" << endl; + cout << " avoid hitting limits on number of open" << endl; + cout << " files" << endl; + cout << endl; + cout << " -cifti-output-datatype deprecated, only affects cifti outputs" << endl; + cout << " -cifti-output-range deprecated, only affects cifti outputs" << endl; + cout << endl; } void CommandOperationManager::printCiftiHelp() diff -Nru connectome-workbench-1.4.2/src/Commands/CommandParser.cxx connectome-workbench-1.5.0/src/Commands/CommandParser.cxx --- connectome-workbench-1.4.2/src/Commands/CommandParser.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Commands/CommandParser.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -26,6 +26,7 @@ #include "BorderFile.h" #include "CaretAssert.h" #include "CaretCommandLine.h" +#include "CaretCommandGlobalOptions.h" #include "CaretDataFileHelper.h" #include "CaretLogger.h" #include "CiftiFile.h" @@ -56,10 +57,10 @@ OperationParserInterface(myAutoOper) { m_doProvenance = true; - m_ciftiScale = false; - m_ciftiDType = NIFTI_TYPE_FLOAT32; - m_ciftiMax = -1.0;//these values won't get used, but don't leave them uninitialized - m_ciftiMin = -1.0; + m_volumeScale = m_ciftiScale = false; + m_volumeDType = m_ciftiDType = NIFTI_TYPE_FLOAT32; + m_volumeMax = m_ciftiMax = -1.0;//these values won't get used, but don't leave them uninitialized + m_volumeMin = m_ciftiMin = -1.0; } void CommandParser::disableProvenance() @@ -83,6 +84,22 @@ m_ciftiScale = false; } +void CommandParser::setVolumeOutputDTypeAndScale(const int16_t& dtype, const double& minVal, const double& maxVal) +{ + m_volumeDType = dtype; + m_volumeMin = minVal; + m_volumeMax = maxVal; + m_volumeScale = true; +} + +void CommandParser::setVolumeOutputDTypeNoScale(const int16_t& dtype) +{ + m_volumeDType = dtype; + m_volumeMax = -1.0;//for sanity, don't keep any previous value around + m_volumeMin = -1.0; + m_volumeScale = false; +} + void CommandParser::executeOperation(ProgramParameters& parameters) { CaretPointer myAlgParams(m_autoOper->getParameters());//could be an autopointer, but this is safer @@ -219,7 +236,12 @@ FileInformation myInfo(nextArg); CaretPointer myFile(new CiftiFile()); myFile->openFile(nextArg); - m_inputCiftiNames[myInfo.getCanonicalFilePath()] = myFile;//track input cifti, so we can check their size + if (caret_global_command_options.m_ciftiReadMemory) + { + myFile->convertToInMemory(); + } else { + m_inputCiftiOnDiskMap[myInfo.getCanonicalFilePath()] = myFile;//track name and file, to additionally check file size to avoid warning for small files + } if (m_doProvenance)//just an optimization, if we aren't going to write provenance, don't generate it, either { const GiftiMetaData* md = myFile->getCiftiXML().getFileMetaData(); @@ -546,6 +568,7 @@ cout << "Now parsing repeatable option " << myComponent->m_repeatableOptions[i]->m_optionSwitch << endl; } myComponent->m_repeatableOptions[i]->m_instances.push_back(new ParameterComponent(myComponent->m_repeatableOptions[i]->m_template)); + myComponent->m_repeatableOptions[i]->m_positions.push_back(parameters.getParameterIndex() - 1);//parameters is on the next argument already parseComponent(myComponent->m_repeatableOptions[i]->m_instances.back(), parameters, outAssociation, debug); if (debug) { @@ -989,8 +1012,8 @@ { CiftiParameter* myCiftiParam = (CiftiParameter*)myParam; FileInformation myInfo(outAssociation[i].m_fileName); - map::iterator iter = m_inputCiftiNames.find(myInfo.getCanonicalFilePath()); - if (iter != m_inputCiftiNames.end()) + map::iterator iter = m_inputCiftiOnDiskMap.find(myInfo.getCanonicalFilePath()); + if (iter != m_inputCiftiOnDiskMap.end()) { vector dims = iter->second->getDimensions(); int64_t totalSize = sizeof(float); @@ -1080,6 +1103,12 @@ case OperationParametersEnum::VOLUME: { VolumeFile* myFile = ((VolumeParameter*)myParam)->m_parameter; + if (m_volumeScale) + { + myFile->setWritingDataTypeAndScaling(m_volumeDType, m_volumeMin, m_volumeMax); + } else { + myFile->setWritingDataTypeNoScaling(m_volumeDType); + } myFile->writeFile(outAssociation[i].m_fileName); break; } diff -Nru connectome-workbench-1.4.2/src/Commands/CommandParser.h connectome-workbench-1.5.0/src/Commands/CommandParser.h --- connectome-workbench-1.4.2/src/Commands/CommandParser.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Commands/CommandParser.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,11 +37,11 @@ { int m_minIndent, m_maxIndent, m_indentIncrement, m_maxWidth; AString m_provenance, m_parentProvenance, m_workingDir; - bool m_doProvenance, m_ciftiScale; - double m_ciftiMin, m_ciftiMax; - int16_t m_ciftiDType; + bool m_doProvenance, m_ciftiScale, m_volumeScale; + double m_ciftiMin, m_ciftiMax, m_volumeMin, m_volumeMax; + int16_t m_ciftiDType, m_volumeDType; const static AString PROVENANCE_NAME, PARENT_PROVENANCE_NAME, PROGRAM_PROVENANCE_NAME, CWD_PROVENANCE_NAME;//TODO: put this elsewhere? - std::map m_inputCiftiNames; + std::map m_inputCiftiOnDiskMap; struct OutputAssoc {//how the output is stored is up to the parser, in the GUI it should load into memory without writing to disk AString m_fileName; @@ -76,6 +76,8 @@ void disableProvenance(); void setCiftiOutputDTypeAndScale(const int16_t& dtype, const double& minVal, const double& maxVal); void setCiftiOutputDTypeNoScale(const int16_t& dtype); + void setVolumeOutputDTypeAndScale(const int16_t& dtype, const double& minVal, const double& maxVal); + void setVolumeOutputDTypeNoScale(const int16_t& dtype); void executeOperation(ProgramParameters& parameters); void showParsedOperation(ProgramParameters& parameters); AString doCompletion(ProgramParameters& parameters, const bool& useExtGlob); diff -Nru connectome-workbench-1.4.2/src/Common/AString.cxx connectome-workbench-1.5.0/src/Common/AString.cxx --- connectome-workbench-1.4.2/src/Common/AString.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/AString.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -977,7 +977,7 @@ */ CaretAssertVectorIndex(stringVector, 0); AString shortestString = stringVector[0]; - for (const auto name : stringVector) { + for (const auto& name : stringVector) { if (name.length() < shortestString.length()) { shortestString = name; } @@ -992,7 +992,7 @@ const QChar character = shortestString.at(ich); bool allMatchFlag = true; - for (const auto name : stringVector) { + for (const auto& name : stringVector) { CaretAssert(ich < name.length()); if (name.at(ich) != character) { allMatchFlag = false; @@ -1020,7 +1020,7 @@ const bool debugFlag = false; if (debugFlag) { std::cout << "Longest prefix: " << longestPrefix << std::endl; - for (const auto name : stringVector) { + for (const auto& name : stringVector) { std::cout << " " << name << std::endl; } } diff -Nru connectome-workbench-1.4.2/src/Common/AString.h connectome-workbench-1.5.0/src/Common/AString.h --- connectome-workbench-1.4.2/src/Common/AString.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/AString.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,6 +22,7 @@ #define ASTRING_H #include +#include #include #include @@ -40,6 +41,7 @@ AString(const char *ch) : QString(ch){} AString(const QByteArray &a) : QString(a) {} AString(const Null &t) : QString(t) {} + AString(const std::string& std_string) : QString(std_string.c_str()) {} AString &operator=(const Null &t) { QString::operator=(t); return *this; } //AString(int size, Qt::Initialization) : QString(size,Qt::Initialization) {} @@ -54,36 +56,6 @@ //std::string compatibility operator std::string () {return this->toStdString(); } - //char * compatibility - operator const char* () {return this->toLatin1(); } - - //double compatiblity - operator double () {return this->toDouble(); } - - //float compatiblity - operator float () { return this->toFloat(); } - - //int compatiblity - operator int () { return this->toInt(); } - - //long compatiblity - operator long () { return this->toLong(); } - - //long long compatiblity - operator long long () { return this->toLongLong(); } - - //unsigned int compatiblity - operator unsigned int () { return this->toUInt(); } - - //unsigned long compatiblity - operator unsigned long () { return this->toULong(); } - - //unsigned long long compatiblity - operator unsigned long long () { return this->toULongLong(); } - - /// convert to a const char* (the operator() does not work in C++ library I/O functions) - //const char* c_str() const { return qPrintable(*this); } - char* toCharArray() const; AString convertURLsToHyperlinks() const; diff -Nru connectome-workbench-1.4.2/src/Common/BackgroundAndForegroundColors.cxx connectome-workbench-1.5.0/src/Common/BackgroundAndForegroundColors.cxx --- connectome-workbench-1.4.2/src/Common/BackgroundAndForegroundColors.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/BackgroundAndForegroundColors.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -140,6 +140,14 @@ equalFlag = false; break; } + if (m_colorBackgroundMedia[i] != obj.m_colorBackgroundMedia[i]) { + equalFlag = false; + break; + } + if (m_colorForegroundMedia[i] != obj.m_colorForegroundMedia[i]) { + equalFlag = false; + break; + } } return equalFlag; @@ -166,6 +174,8 @@ m_colorBackgroundVolume[i] = obj.m_colorBackgroundVolume[i]; m_colorChartMatrixGridLines[i] = obj.m_colorChartMatrixGridLines[i]; m_colorChartHistogramThreshold[i] = obj.m_colorChartHistogramThreshold[i]; + m_colorForegroundMedia[i] = obj.m_colorForegroundMedia[i]; + m_colorBackgroundMedia[i] = obj.m_colorBackgroundMedia[i]; } } @@ -212,6 +222,10 @@ const uint8_t threshGreen = 100; const uint8_t threshBlue = 255; setColor(m_colorChartHistogramThreshold, threshRed, threshGreen, threshBlue); + + setColor(m_colorForegroundMedia, foreRed, foreGreen, foreBlue); + + setColor(m_colorBackgroundMedia, backRed, backGreen, backBlue); } /** @@ -554,6 +568,61 @@ } } +/** + * Get the foreground color for viewing the Mult-Media model. + * + * @param colorForeground + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::getColorForegroundMediaView(uint8_t colorForeground[3]) const +{ + for (int32_t i = 0; i < 3; i++) { + colorForeground[i] = m_colorForegroundMedia[i]; + } +} + +/** + * Set the foreground color for viewing the Mult-Media model. + * + * @param colorForeground + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::setColorForegroundMediaView(const uint8_t colorForeground[3]) +{ + for (int32_t i = 0; i < 3; i++) { + m_colorForegroundMedia[i] = colorForeground[i]; + } +} + +/** + * Get the background color for viewing the Mult-Media model. + * + * @param colorBackground + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::getColorBackgroundMediaView(uint8_t colorBackground[3]) const +{ + for (int32_t i = 0; i < 3; i++) { + colorBackground[i] = m_colorBackgroundMedia[i]; + } +} + +/** + * Set the background color for viewing the Mult-Media model. + * + * @param colorBackground + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::setColorBackgroundMediaView(const uint8_t colorBackground[3]) +{ + for (int32_t i = 0; i < 3; i++) { + m_colorBackgroundMedia[i] = colorBackground[i]; + } +} /** * Set a color with the given color components that range 0 to 255. diff -Nru connectome-workbench-1.4.2/src/Common/BackgroundAndForegroundColors.h connectome-workbench-1.5.0/src/Common/BackgroundAndForegroundColors.h --- connectome-workbench-1.4.2/src/Common/BackgroundAndForegroundColors.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/BackgroundAndForegroundColors.h 2021-02-16 19:46:47.000000000 +0000 @@ -96,6 +96,14 @@ void setColorChartHistogramThreshold(const uint8_t colorChartMatrixGridLines[3]); + void getColorForegroundMediaView(uint8_t colorForeground[3]) const; + + void setColorForegroundMediaView(const uint8_t colorForeground[3]); + + void getColorBackgroundMediaView(uint8_t colorForeground[3]) const; + + void setColorBackgroundMediaView(const uint8_t colorForeground[3]); + private: void copyHelperBackgroundAndForegroundColors(const BackgroundAndForegroundColors& obj); @@ -128,6 +136,10 @@ uint8_t m_colorChartHistogramThreshold[3]; + uint8_t m_colorForegroundMedia[3]; + + uint8_t m_colorBackgroundMedia[3]; + // ADD_NEW_MEMBERS_HERE friend class BackgroundAndForegroundColorsSceneHelper; diff -Nru connectome-workbench-1.4.2/src/Common/BoundingBox.cxx connectome-workbench-1.5.0/src/Common/BoundingBox.cxx --- connectome-workbench-1.4.2/src/Common/BoundingBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/BoundingBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -22,6 +22,7 @@ #include #include #include "BoundingBox.h" +#include "MathFunctions.h" using namespace caret; @@ -109,6 +110,19 @@ } /** + * @return True if the bounding box is valid for 2D (minX < maxX and minY < maxY) + */ +bool +BoundingBox::isValid2D() const +{ + if ((getMinX() < getMaxX()) + && (getMinY() < getMaxY())) { + return true; + } + return false; +} + +/** * Reset a new bounding box with the minimum and maximum values * all set to zero. */ @@ -204,6 +218,30 @@ } /** + * Set the bound box to the extent found in the 4 given triplets + * @param a + * First triplet + * @param b + * Second triplet + * @param c + * Third triplet + * @param d + * Fourth triplet + */ +void +BoundingBox::set(const float a[3], + const float b[3], + const float c[3], + const float d[4]) +{ + resetForUpdate(); + update(a); + update(b); + update(c); + update(d); +} + +/** * Update the bounding box with the XYZ value passed in. The bound box * must have been created with newInstanceForUpdate() or properly * initialized by the user. @@ -250,6 +288,63 @@ } /** + * Update the bounding box with the XYZ value passed in. The bound box + * must have been created with newInstanceForUpdate() or properly + * initialized by the user. + * + * @param xyz - Three dimensional array containing XYZ. + * + */ +void +BoundingBox::updateExcludeNanInf(const float xyz[3]) +{ + if (MathFunctions::isNumeric(xyz[0])) { + if (xyz[0] < this->boundingBox[0]) this->boundingBox[0] = xyz[0]; + if (xyz[0] > this->boundingBox[1]) this->boundingBox[1] = xyz[0]; + } + if (MathFunctions::isNumeric(xyz[1])) { + if (xyz[1] < this->boundingBox[2]) this->boundingBox[2] = xyz[1]; + if (xyz[1] > this->boundingBox[3]) this->boundingBox[3] = xyz[1]; + } + if (MathFunctions::isNumeric(xyz[2])) { + if (xyz[2] < this->boundingBox[4]) this->boundingBox[4] = xyz[2]; + if (xyz[2] > this->boundingBox[5]) this->boundingBox[5] = xyz[2]; + } +} + +/** + * Update the bounding box with the XYZ value passed in. The bound box + * must have been created with newInstanceForUpdate() or properly + * initialized by the user. + * + * @param x + * X-coordinate. + * @param y + * Y-coordinate. + * @param Z + * Z-coordinate. + * + */ +void +BoundingBox::updateExcludeNanInf(const float x, + const float y, + const float z) +{ + if (MathFunctions::isNumeric(x)) { + if (x < this->boundingBox[0]) this->boundingBox[0] = x; + if (x > this->boundingBox[1]) this->boundingBox[1] = x; + } + if (MathFunctions::isNumeric(y)) { + if (y < this->boundingBox[2]) this->boundingBox[2] = y; + if (y > this->boundingBox[3]) this->boundingBox[3] = y; + } + if (MathFunctions::isNumeric(z)) { + if (z < this->boundingBox[4]) this->boundingBox[4] = z; + if (z > this->boundingBox[5]) this->boundingBox[5] = z; + } +} + +/** * Get the bounds in an array. * @return Array of six containing minX, maxX, minY, maxY, minZ, maxZ. * @@ -310,6 +405,18 @@ } /** + * @return The maximum difference of the X, Y, Z differences. + */ +float +BoundingBox::getMaximumDifferenceOfXYZ() const +{ + const float maxDiff(std::max(getDifferenceX(), + std::max(getDifferenceY(), + getDifferenceZ()))); + return maxDiff; +} + +/** * Get the X minimum value. * @return Its value. * @@ -376,14 +483,12 @@ } /** - * Get the minimum XYZ of the bounding box. * @return Minimum XYZ of the bounding box. - * in an array that the caller MUST delete[]; */ -float* +std::array BoundingBox::getMinXYZ() const { - float* f = new float[3]; + std::array f; f[0] = this->boundingBox[0]; f[1] = this->boundingBox[2]; f[2] = this->boundingBox[4]; @@ -391,14 +496,12 @@ } /** - * Get the maximum XYZ of the bounding box. * @return Maximum XYZ of the bounding box - * in an array that the caller MUST delete[]; */ -float* +std::array BoundingBox::getMaxXYZ() const { - float* f = new float[3]; + std::array f; f[0] = this->boundingBox[1]; f[1] = this->boundingBox[3]; f[2] = this->boundingBox[5]; @@ -594,3 +697,48 @@ return s; } +/** + * @return True if this bounding box intersects the given bounding box + * using only the X and Y coordinates. + */ +bool +BoundingBox::intersectsXY(const BoundingBox& bb) const +{ + /* + * Does self overlap + */ + if (this == &bb) { + return true; + } + + /* + * Note: Since the geometry is aligned with the X- and Y-axes, + * we only need to test for one to be above or the the right of the other + * + * https://www.geeksforgeeks.org/find-two-rectangles-overlap/ + * https://leetcode.com/articles/rectangle-overlap/ + */ + /* 'this' is on right side of 'other' */ + if (getMinX() >= bb.getMaxX()) { + return false; + } + + /* 'other' is on right side of 'this' */ + if (bb.getMinX() >= getMaxX()) { + return false; + } + + /* 'this' is above 'other */ + if (getMinY() >= bb.getMaxY()) { + return false; + } + + /* 'other' is above 'this' */ + if (bb.getMinY() >= getMaxY()) { + return false; + } + + return true; +} + + diff -Nru connectome-workbench-1.4.2/src/Common/BoundingBox.h connectome-workbench-1.5.0/src/Common/BoundingBox.h --- connectome-workbench-1.4.2/src/Common/BoundingBox.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/BoundingBox.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,7 +21,7 @@ */ /*LICENSE_END*/ - +#include #include #include @@ -47,6 +47,8 @@ virtual ~BoundingBox(); public: + bool isValid2D() const; + void resetZeros(); void resetWithMaximumExtent(); @@ -64,12 +66,23 @@ void set(const float minMaxXYZ[6]); + void set(const float a[3], + const float b[3], + const float c[3], + const float d[4]); + void update(const float xyz[]); void update(const float x, const float y, const float z); + void updateExcludeNanInf(const float xyz[]); + + void updateExcludeNanInf(const float x, + const float y, + const float z); + const float* getBounds() const; void getBounds(float bounds[6]) const; @@ -80,6 +93,8 @@ float getDifferenceZ() const; + float getMaximumDifferenceOfXYZ() const; + float getMinX() const; float getMaxX() const; @@ -92,9 +107,9 @@ float getMaxZ() const; - float* getMinXYZ() const; + std::array getMinXYZ() const; - float* getMaxXYZ() const; + std::array getMaxXYZ() const; void setMinX(const float value); @@ -122,6 +137,8 @@ void limitCoordinateToBoundingBox(double xyz[3]) const; + bool intersectsXY(const BoundingBox& bb) const; + AString toString() const; private: diff -Nru connectome-workbench-1.4.2/src/Common/CardinalDirectionEnum.cxx connectome-workbench-1.5.0/src/Common/CardinalDirectionEnum.cxx --- connectome-workbench-1.4.2/src/Common/CardinalDirectionEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CardinalDirectionEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,466 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __CARDINAL_DIRECTION_ENUM_DECLARE__ +#include "CardinalDirectionEnum.h" +#undef __CARDINAL_DIRECTION_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::CardinalDirectionEnum + * \brief Enumerated type for cardinal direction + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_cardinalDirectionEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void cardinalDirectionEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "CardinalDirectionEnum.h" + * + * Instatiate: + * m_cardinalDirectionEnumComboBox = new EnumComboBoxTemplate(this); + * m_cardinalDirectionEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_cardinalDirectionEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(cardinalDirectionEnumComboBoxItemActivated())); + * + * Update the selection: + * m_cardinalDirectionEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const CardinalDirectionEnum::Enum VARIABLE = m_cardinalDirectionEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * @param guiName + * User-friendly name for use in user-interface. + * @param guiShortName + * Short User-friendly name for use in user-interface + */ +CardinalDirectionEnum::CardinalDirectionEnum(const Enum enumValue, + const AString& name, + const AString& guiName, + const AString& guiShortName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; + this->guiShortName = guiShortName; +} + +/** + * Destructor. + */ +CardinalDirectionEnum::~CardinalDirectionEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +CardinalDirectionEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(CardinalDirectionEnum(AUTO, + "AUTO", + "Auto", + "A")); + + enumData.push_back(CardinalDirectionEnum(NORTH, + "NORTH", + "North", + "N")); + + enumData.push_back(CardinalDirectionEnum(NORTHEAST, + "NORTHEAST", + "Northeast", + "NE")); + + enumData.push_back(CardinalDirectionEnum(EAST, + "EAST", + "East", + "E")); + + enumData.push_back(CardinalDirectionEnum(SOUTHEAST, + "SOUTHEAST", + "Southeast", + "SE")); + + enumData.push_back(CardinalDirectionEnum(SOUTH, + "SOUTH", + "South", + "S")); + + enumData.push_back(CardinalDirectionEnum(SOUTHWEST, + "SOUTHWEST", + "Southwest", + "SW")); + + enumData.push_back(CardinalDirectionEnum(WEST, + "WEST", + "West", + "W")); + + enumData.push_back(CardinalDirectionEnum(NORTHWEST, + "NORTHWEST", + "Northwest", + "NW")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const CardinalDirectionEnum* +CardinalDirectionEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const CardinalDirectionEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +CardinalDirectionEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const CardinalDirectionEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +CardinalDirectionEnum::Enum +CardinalDirectionEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = CardinalDirectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const CardinalDirectionEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type CardinalDirectionEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +CardinalDirectionEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const CardinalDirectionEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get a short GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +CardinalDirectionEnum::toGuiShortName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const CardinalDirectionEnum* enumInstance = findData(enumValue); + return enumInstance->guiShortName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +CardinalDirectionEnum::Enum +CardinalDirectionEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = CardinalDirectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const CardinalDirectionEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type CardinalDirectionEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +CardinalDirectionEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const CardinalDirectionEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +CardinalDirectionEnum::Enum +CardinalDirectionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = CardinalDirectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const CardinalDirectionEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type CardinalDirectionEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * AUTO is excluded unless it is in the options + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + * @param options + * Options that control if AUTO is included. + */ +void +CardinalDirectionEnum::getAllEnums(std::vector& allEnums, + const std::set& options) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + switch (iter->enumValue) { + case AUTO: + if (options.find(Options::INCLUDE_AUTO) == options.end()) { + continue; + } + break; + default: + break; + } + + allEnums.push_back(iter->enumValue); + } +} + + +/** + * Get all of the enumerated type values EXCEPT for AUTO. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +CardinalDirectionEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + if (iter->enumValue == CardinalDirectionEnum::AUTO) { + continue; + } + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +CardinalDirectionEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(CardinalDirectionEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +CardinalDirectionEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(CardinalDirectionEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/CardinalDirectionEnum.h connectome-workbench-1.5.0/src/Common/CardinalDirectionEnum.h --- connectome-workbench-1.4.2/src/Common/CardinalDirectionEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CardinalDirectionEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,131 @@ +#ifndef __CARDINAL_DIRECTION_ENUM_H__ +#define __CARDINAL_DIRECTION_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include +#include "AString.h" + +namespace caret { + +class CardinalDirectionEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Automatic */ + AUTO, + /** North */ + NORTH, + /** Northeast */ + NORTHEAST, + /** East */ + EAST, + /** Southeast */ + SOUTHEAST, + /** South */ + SOUTH, + /** Southwest */ + SOUTHWEST, + /** West */ + WEST, + /** Northwest */ + NORTHWEST + }; + + enum class Options { + INCLUDE_AUTO + }; + + + ~CardinalDirectionEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static AString toGuiShortName(Enum enumValue); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllEnums(std::vector& allEnums, + const std::set& options); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + CardinalDirectionEnum(const Enum enumValue, + const AString& name, + const AString& guiName, + const AString& guiShortName); + + static const CardinalDirectionEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; + + /** A short user-friendly name that is displayed in the GUI */ + AString guiShortName; +}; + +#ifdef __CARDINAL_DIRECTION_ENUM_DECLARE__ +std::vector CardinalDirectionEnum::enumData; +bool CardinalDirectionEnum::initializedFlag = false; +int32_t CardinalDirectionEnum::integerCodeCounter = 0; +#endif // __CARDINAL_DIRECTION_ENUM_DECLARE__ + +} // namespace +#endif //__CARDINAL_DIRECTION_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/CaretAssert.h connectome-workbench-1.5.0/src/Common/CaretAssert.h --- connectome-workbench-1.4.2/src/Common/CaretAssert.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretAssert.h 2021-02-16 19:46:47.000000000 +0000 @@ -138,7 +138,7 @@ */ #define CaretAssertMessage(e, m) \ (((e) == 0) \ - ? caret::CaretAssertion::assertFailed(#e, AString(m), __FILE__, __LINE__) \ + ? caret::CaretAssertion::assertFailed(#e, m, __FILE__, __LINE__) \ : (void)0) /** diff -Nru connectome-workbench-1.4.2/src/Common/CaretAssertion.cxx connectome-workbench-1.5.0/src/Common/CaretAssertion.cxx --- connectome-workbench-1.4.2/src/Common/CaretAssertion.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretAssertion.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -151,6 +151,36 @@ * file, and the line number in the file. If * a callstack (backtrace) is available, it will * also be printed. Lastly, abort() is called. + * + * + * @param expression + * Expression that failed assertion testing. + * @param message + * Message that is printed. + * @param filename + * Name of file in which assertion failed. + * @param lineNumber + * Line number where assertion failed. + */ +void +CaretAssertion::assertFailed(const char* expression, + const AString& message, + const char* filename, + const int64_t lineNumber) +{ + assertFailed(expression, + message.toLatin1().constData(), + filename, + lineNumber); +} + +/** + * Called when an assertion has failed. + * The following events will occur: + * Prints the expression that failed, the name of + * file, and the line number in the file. If + * a callstack (backtrace) is available, it will + * also be printed. Lastly, abort() is called. * * * @param expression diff -Nru connectome-workbench-1.4.2/src/Common/CaretAssertion.h connectome-workbench-1.5.0/src/Common/CaretAssertion.h --- connectome-workbench-1.4.2/src/Common/CaretAssertion.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretAssertion.h 2021-02-16 19:46:47.000000000 +0000 @@ -27,6 +27,8 @@ namespace caret { + class AString; + /** * Contains static methods for assertion processing. * @@ -54,6 +56,11 @@ const char* filename, const int64_t lineNumber); + static void assertFailed(const char* expression, + const AString& message, + const char* filename, + const int64_t lineNumber); + static void assertToDoWarning(const char* filename, const int64_t lineNumber); diff -Nru connectome-workbench-1.4.2/src/Common/CaretBinaryFile.cxx connectome-workbench-1.5.0/src/Common/CaretBinaryFile.cxx --- connectome-workbench-1.4.2/src/Common/CaretBinaryFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretBinaryFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -348,6 +348,7 @@ void QFileImpl::seek(const int64_t& position) { + if (m_file.pos() == position) return; //QFile::seek always does a flush in qt5, so try to avoid calling it if (!m_file.seek(position)) throw DataFileException("seek failed in file '" + m_fileName + "'"); } diff -Nru connectome-workbench-1.4.2/src/Common/CaretColor.cxx connectome-workbench-1.5.0/src/Common/CaretColor.cxx --- connectome-workbench-1.4.2/src/Common/CaretColor.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretColor.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,433 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CARET_COLOR_DECLARE__ +#include "CaretColor.h" +#undef __CARET_COLOR_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" + +using namespace caret; + + + +/** + * \class caret::CaretColor + * \brief Caret color that is a CaretColorEnum but also supports custom color in one object + * \ingroup Common + */ + +/** + * Constructor. + */ +CaretColor::CaretColor() +: CaretObject() +{ + setDefaultColor(); +} + +/** + * Destructor. + */ +CaretColor::~CaretColor() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +CaretColor::CaretColor(const CaretColor& obj) +: CaretObject(obj) +{ + this->copyHelperCaretColor(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +CaretColor& +CaretColor::operator=(const CaretColor& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperCaretColor(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +CaretColor::copyHelperCaretColor(const CaretColor& obj) +{ + m_color = obj.m_color; + m_customRGBA = obj.m_customRGBA; +} + +/** + * Equality operator. + * @param obj + * Instance compared to this for equality. + * @return + * True if this instance and 'obj' instance are considered equal. + */ +bool +CaretColor::operator==(const CaretColor& obj) const +{ + if (this == &obj) { + return true; + } + + if (m_color == obj.m_color) { + if (m_color == CaretColorEnum::CUSTOM) { + if (m_customRGBA == obj.m_customRGBA) { + return true; + } + } + else { + return true; + } + } + + return false; +} + +/** + * Inequality operator. + * @param obj + * Instance compared to this for inequality. + * @return + * True if this instance and 'obj' instance are considered not equal. + */ +bool +CaretColor::operator!=(const CaretColor& obj) const +{ + return ( ! (*this == obj)); +} + +/** + * Set the to the default color + */ +void +CaretColor::setDefaultColor() +{ + /* + * Do not use black or white for default color + * as they are commonly used for background color. + */ + m_color = CaretColorEnum::RED; + CaretColorEnum::toRGBAByte(m_color, m_customRGBA.data()); +} + +/** + * @return The current color + */ +CaretColorEnum::Enum +CaretColor::getCaretColorEnum() const +{ + return m_color; +} + +/** + * Set the current color + * @param color + * New value for color + */ +void +CaretColor::setCaretColorEnum(const CaretColorEnum::Enum color) +{ + m_color = color; +} + +/** + * @return RGBA components for custom color + */ +std::array +CaretColor::getCustomColorRGBA() const +{ + return m_customRGBA; +} + +/** + * @return Float RGBA components for custom color + */ +std::array +CaretColor::getCustomColorFloatRGBA() const +{ + std::array rgba = getCustomColorRGBA(); + std::array rgbaF { + rgba[0] / 255.0f, + rgba[1] / 255.0f, + rgba[2] / 255.0f, + rgba[3] / 255.0f + }; + + return rgbaF; +} + + +/** + * Set the color components for the custom color + * @param customRGBA + * RGBA components + */ +void CaretColor::setCustomColorRGBA(const std::array& customRGBA) +{ + m_customRGBA = customRGBA; +} + +/** + * @return RGBA components for current color + */ +std::array +CaretColor::getRGBA() const +{ + if (m_color == CaretColorEnum::CUSTOM) { + return m_customRGBA; + } + std::array rgba; + CaretColorEnum::toRGBAByte(m_color, rgba.data()); + return rgba; +} + +/** + * @return Float RGBA components for current color + */ +std::array +CaretColor::getFloatRGBA() const +{ + std::array rgba = getRGBA(); + std::array rgbaF { + rgba[0] / 255.0f, + rgba[1] / 255.0f, + rgba[2] / 255.0f, + rgba[3] / 255.0f + }; + + return rgbaF; +} + + +/** + * @return String containing CaretColor in XML for use in scenes + */ +AString +CaretColor::encodeInXML() const +{ + AString xml; + + QXmlStreamWriter writer(&xml); + writer.writeStartElement(TAG_CARET_COLOR); + writer.writeAttribute(ATTRIBUTE_VERSION, VALUE_VERSION_ONE); + writer.writeAttribute(ATTRIBUTE_COLOR_NAME, CaretColorEnum::toName(m_color)); + writer.writeAttribute(ATTRIBUTE_CUSTOM_COLOR_RED, QString::number(m_customRGBA[0])); + writer.writeAttribute(ATTRIBUTE_CUSTOM_COLOR_GREEN, QString::number(m_customRGBA[1])); + writer.writeAttribute(ATTRIBUTE_CUSTOM_COLOR_BLUE, QString::number(m_customRGBA[2])); + writer.writeAttribute(ATTRIBUTE_CUSTOM_COLOR_ALPHA, QString::number(m_customRGBA[3])); + writer.writeEndElement(); + + return xml; +} + +/** + * Decode CaretColor from XML used in Scenes + * @param xml + * The XML text + * @param errorMessageOut + * Output containing error message + * @return + * True if decoding is successful, else false + */ +bool +CaretColor::decodeFromXML(const AString& xml, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + const QString errorPrefix("CaretColor xml decoding "); + + QXmlStreamReader reader(xml); + if (reader.readNextStartElement()) { + const QString tagName(reader.name().toString()); + if (tagName == TAG_CARET_COLOR) { + const QXmlStreamAttributes atts = reader.attributes(); + if (atts.hasAttribute(ATTRIBUTE_VERSION)) { + const QString versionText = atts.value(ATTRIBUTE_VERSION).toString(); + if (versionText == VALUE_VERSION_ONE) { + const QString nameText = atts.value(ATTRIBUTE_COLOR_NAME).toString(); + bool validFlag(false); + m_color = CaretColorEnum::fromName(nameText, &validFlag); + if (validFlag) { + m_customRGBA[0] = atts.value(ATTRIBUTE_CUSTOM_COLOR_RED).toInt(); + m_customRGBA[1] = atts.value(ATTRIBUTE_CUSTOM_COLOR_GREEN).toInt(); + m_customRGBA[2] = atts.value(ATTRIBUTE_CUSTOM_COLOR_BLUE).toInt(); + m_customRGBA[3] = atts.value(ATTRIBUTE_CUSTOM_COLOR_ALPHA).toInt(); + return true; + } + else { + errorMessageOut = (errorPrefix + + "invalid color name=\"" + + nameText + + "\""); + } + } + else { + errorMessageOut = (errorPrefix + + "version unsupported=\"" + + versionText + + "\""); + } + } + else { + errorMessageOut = (errorPrefix + + "version is missing"); + } + } + else { + errorMessageOut = (errorPrefix + + "invalid element name: " + + tagName); + } + } + else { + errorMessageOut = (errorPrefix + + "start element not found"); + } + + setDefaultColor(); + + return false; +} + +/** + * @return String containing CaretColor in JSON for use in scenes + */ +AString +CaretColor::encodeInJson() const +{ + QJsonObject jso; + jso.insert(ATTRIBUTE_VERSION, VALUE_VERSION_ONE); + jso.insert(ATTRIBUTE_COLOR_NAME, CaretColorEnum::toName(m_color)); + jso.insert(ATTRIBUTE_CUSTOM_COLOR_RED, QString::number(m_customRGBA[0])); + jso.insert(ATTRIBUTE_CUSTOM_COLOR_GREEN, QString::number(m_customRGBA[1])); + jso.insert(ATTRIBUTE_CUSTOM_COLOR_BLUE, QString::number(m_customRGBA[2])); + jso.insert(ATTRIBUTE_CUSTOM_COLOR_ALPHA, QString::number(m_customRGBA[3])); + + return QJsonDocument(jso).toJson(QJsonDocument::Compact); +} + +/** + * Decode CaretColor from JSON used in Scenes + * @param text + * The json text + * @param errorMessageOut + * Output containing error message + * @return + * True if decoding is successful, else false + */ +bool +CaretColor::decodeFromJson(const AString& text, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + QJsonParseError parseError; + QJsonDocument jdoc = QJsonDocument::fromJson(text.toUtf8(), + &parseError); + if (jdoc.isNull()) { + errorMessageOut = parseError.errorString(); + return false; + } + + const QJsonObject jso = jdoc.object(); + if (jso.contains(ATTRIBUTE_VERSION)) { + const QString version = jso[ATTRIBUTE_VERSION].toString(); + if (version == VALUE_VERSION_ONE) { + if (jso.contains(ATTRIBUTE_COLOR_NAME) + && jso.contains(ATTRIBUTE_CUSTOM_COLOR_RED) + && jso.contains(ATTRIBUTE_CUSTOM_COLOR_GREEN) + && jso.contains(ATTRIBUTE_CUSTOM_COLOR_BLUE) + && jso.contains(ATTRIBUTE_CUSTOM_COLOR_ALPHA)) { + bool validFlag(false); + m_color = CaretColorEnum::fromName(jso[ATTRIBUTE_COLOR_NAME].toString(), + &validFlag); + if (validFlag) { + m_customRGBA[0] = jso.value(ATTRIBUTE_CUSTOM_COLOR_RED).toString().toInt(); + m_customRGBA[1] = jso.value(ATTRIBUTE_CUSTOM_COLOR_GREEN).toString().toInt(); + m_customRGBA[2] = jso.value(ATTRIBUTE_CUSTOM_COLOR_BLUE).toString().toInt(); + m_customRGBA[3] = jso.value(ATTRIBUTE_CUSTOM_COLOR_ALPHA).toString().toInt(); + + return true; + } + + errorMessageOut = ("Invalid color name: " + + jso[ATTRIBUTE_COLOR_NAME].toString()); + } + else { + errorMessageOut = ("Missing color name or color component in: " + + text); + } + } + else { + errorMessageOut = ("Version invalid in: " + + text); + } + } + else { + errorMessageOut = ("Version attribute is missing from: " + + text); + } + + setDefaultColor(); + + return false; +} + + +/** + * Get a description of this object's content for debugging or printing + * @return String describing this object's content. + */ +AString +CaretColor::toString() const +{ + AString s("CaretColor=" + + CaretColorEnum::toName(m_color)); + if (m_color == CaretColorEnum::CUSTOM) { + s.append(", RGBA=" + + AString::fromNumbers(m_customRGBA.data(), 4, ",")); + } + return s; +} + diff -Nru connectome-workbench-1.4.2/src/Common/CaretColor.h connectome-workbench-1.5.0/src/Common/CaretColor.h --- connectome-workbench-1.4.2/src/Common/CaretColor.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretColor.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,116 @@ +#ifndef __CARET_COLOR_H__ +#define __CARET_COLOR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "CaretColorEnum.h" +#include "CaretObject.h" + +namespace caret { + class CaretColor : public CaretObject { + + public: + CaretColor(); + + virtual ~CaretColor(); + + CaretColor(const CaretColor& obj); + + CaretColor& operator=(const CaretColor& obj); + + bool operator==(const CaretColor& obj) const; + + bool operator!=(const CaretColor& obj) const; + + CaretColorEnum::Enum getCaretColorEnum() const; + + void setCaretColorEnum(const CaretColorEnum::Enum color); + + std::array getCustomColorRGBA() const; + + std::array getCustomColorFloatRGBA() const; + + void setCustomColorRGBA(const std::array& customRGBA); + + std::array getRGBA() const; + + std::array getFloatRGBA() const; + + AString encodeInJson() const; + + bool decodeFromJson(const AString& xml, + AString& errorMessageOut); + + AString encodeInXML() const; + + bool decodeFromXML(const AString& xml, + AString& errorMessageOut); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperCaretColor(const CaretColor& obj); + + void setDefaultColor(); + + CaretColorEnum::Enum m_color; + + std::array m_customRGBA; + + static const QString VALUE_VERSION_ONE; + + static const QString TAG_CARET_COLOR; + + static const QString ATTRIBUTE_VERSION; + + static const QString ATTRIBUTE_COLOR_NAME; + + static const QString ATTRIBUTE_CUSTOM_COLOR_RED; + + static const QString ATTRIBUTE_CUSTOM_COLOR_GREEN; + + static const QString ATTRIBUTE_CUSTOM_COLOR_BLUE; + + static const QString ATTRIBUTE_CUSTOM_COLOR_ALPHA; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CARET_COLOR_DECLARE__ + const QString CaretColor::VALUE_VERSION_ONE = "1"; + const QString CaretColor::TAG_CARET_COLOR = "CaretColor"; + const QString CaretColor::ATTRIBUTE_VERSION = "Version"; + const QString CaretColor::ATTRIBUTE_COLOR_NAME = "Name"; + const QString CaretColor::ATTRIBUTE_CUSTOM_COLOR_RED = "R"; + const QString CaretColor::ATTRIBUTE_CUSTOM_COLOR_GREEN = "G"; + const QString CaretColor::ATTRIBUTE_CUSTOM_COLOR_BLUE = "B"; + const QString CaretColor::ATTRIBUTE_CUSTOM_COLOR_ALPHA = "A"; + +#endif // __CARET_COLOR_DECLARE__ + +} // namespace +#endif //__CARET_COLOR_H__ diff -Nru connectome-workbench-1.4.2/src/Common/CaretCommandGlobalOptions.cxx connectome-workbench-1.5.0/src/Common/CaretCommandGlobalOptions.cxx --- connectome-workbench-1.4.2/src/Common/CaretCommandGlobalOptions.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretCommandGlobalOptions.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,25 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretCommandGlobalOptions.h" + +using namespace caret; + +CommandGlobalOptions caret::caret_global_command_options; diff -Nru connectome-workbench-1.4.2/src/Common/CaretCommandGlobalOptions.h connectome-workbench-1.5.0/src/Common/CaretCommandGlobalOptions.h --- connectome-workbench-1.4.2/src/Common/CaretCommandGlobalOptions.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretCommandGlobalOptions.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,35 @@ +#ifndef __CARET_COMMAND_GLOBAL_OPTIONS_H__ +#define __CARET_COMMAND_GLOBAL_OPTIONS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +namespace caret { + + struct CommandGlobalOptions + { + bool m_ciftiReadMemory = false; + }; + + extern CommandGlobalOptions caret_global_command_options; + +} + +#endif //__CARET_COMMAND_GLOBAL_OPTIONS_H__ diff -Nru connectome-workbench-1.4.2/src/Common/CaretLogger.h connectome-workbench-1.5.0/src/Common/CaretLogger.h --- connectome-workbench-1.4.2/src/Common/CaretLogger.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretLogger.h 2021-02-16 19:46:47.000000000 +0000 @@ -53,6 +53,11 @@ */ inline static Logger* getLogger() { return CaretLogger::logger; } + /** + * @return True if the CaretLogger is valid and can be used, else false. + */ + static bool isValid() { return (CaretLogger::logger != NULL); } + private: CaretLogger() { } ~CaretLogger() { } diff -Nru connectome-workbench-1.4.2/src/Common/CaretMathExpression.cxx connectome-workbench-1.5.0/src/Common/CaretMathExpression.cxx --- connectome-workbench-1.4.2/src/Common/CaretMathExpression.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretMathExpression.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -36,7 +36,7 @@ m_root = orExpr(); if (skipWhitespace())//if we DON'T hit the end of input, there are extra characters - like if the input is "x + 1 $blah" { - throw CaretException("extra characters on end of expression input: '" + m_input.mid(m_position) + "'"); + throw CaretException("extra characters on end of expression: '" + m_input.mid(m_position) + "'"); } CaretLogFiner("parsed '" + expression + "' as '" + toString() + "'"); } @@ -396,10 +396,9 @@ return ret; } -AString CaretMathExpression::MathNode::toString(const std::vector& varNames) const +AString CaretMathExpression::MathNode::toString(const std::vector& varNames, bool addParens) const { AString ret = ""; - bool addParens = true; switch (m_type) { case OR: @@ -409,7 +408,7 @@ ret = m_arguments[0]->toString(varNames); for (int i = 1; i < end; ++i) { - ret += "||" + m_arguments[i]->toString(varNames); + ret += " || " + m_arguments[i]->toString(varNames); } break; } @@ -420,7 +419,7 @@ ret = m_arguments[0]->toString(varNames); for (int i = 1; i < end; ++i) { - ret += "&&" + m_arguments[i]->toString(varNames); + ret += " && " + m_arguments[i]->toString(varNames); } break; } @@ -434,9 +433,9 @@ { if (m_invert[i]) { - ret += "!=" + m_arguments[i]->toString(varNames); + ret += " != " + m_arguments[i]->toString(varNames); } else { - ret += "==" + m_arguments[i]->toString(varNames); + ret += " == " + m_arguments[i]->toString(varNames); } } break; @@ -454,16 +453,16 @@ { if (m_invert[i]) { - ret += "<=" + m_arguments[i]->toString(varNames); + ret += " <= " + m_arguments[i]->toString(varNames); } else { - ret += ">=" + m_arguments[i]->toString(varNames); + ret += " >= " + m_arguments[i]->toString(varNames); } } else { if (m_invert[i]) { - ret += "<" + m_arguments[i]->toString(varNames); + ret += " < " + m_arguments[i]->toString(varNames); } else { - ret += ">" + m_arguments[i]->toString(varNames); + ret += " > " + m_arguments[i]->toString(varNames); } } } @@ -479,9 +478,9 @@ { if (m_invert[i]) { - ret += "-" + m_arguments[i]->toString(varNames); + ret += " - " + m_arguments[i]->toString(varNames); } else { - ret += "+" + m_arguments[i]->toString(varNames); + ret += " + " + m_arguments[i]->toString(varNames); } } break; @@ -496,9 +495,9 @@ { if (m_invert[i]) { - ret += "/" + m_arguments[i]->toString(varNames); + ret += " / " + m_arguments[i]->toString(varNames); } else { - ret += "*" + m_arguments[i]->toString(varNames); + ret += " * " + m_arguments[i]->toString(varNames); } } break; @@ -520,10 +519,10 @@ addParens = false; int end = (int)m_arguments.size(); ret = MathFunctionEnum::toName(m_function) + "("; - if (end > 0) ret += m_arguments[0]->toString(varNames);//allow for functions that take 0 arguments + if (end > 0) ret += m_arguments[0]->toString(varNames, false);//allow for functions that take 0 arguments, don't add outer parens to function arguments for (int i = 1; i < end; ++i) { - ret += "," + m_arguments[i]->toString(varNames); + ret += ", " + m_arguments[i]->toString(varNames); } ret += ")"; break; @@ -546,13 +545,13 @@ CaretAssertMessage(0, "toString called on invalid MathNode"); throw CaretException("parsing problem in CaretMathExpression"); } - if (addParens) ret = "(" + ret + ")";//parenthesize almost everything + if (addParens) ret = "(" + ret + ")";//parenthesize unless we are a terminal or function, or the node above told us not to return ret; } AString CaretMathExpression::toString() const { - return m_root->toString(getVarNames()); + return m_root->toString(getVarNames(), false); } bool CaretMathExpression::skipWhitespace()//return false if end of input @@ -573,8 +572,8 @@ void CaretMathExpression::expect(const char& c, const int& exprStart) { CaretAssert(exprStart < m_end); - if (!skipWhitespace()) throw CaretException("unexpected end of input while parsing '" + m_input.mid(exprStart) + "', expected '" + AString(c) + "'"); - if (m_input[m_position] != c) throw CaretException("expected '" + AString(c) + "', got '" + m_input[m_position] + "' while parsing '" + m_input.mid(exprStart, m_position - exprStart + 1) + "'"); + if (!skipWhitespace()) throw CaretException("unexpected end of expression while parsing '" + m_input.mid(exprStart) + "', expected '" + AString(c) + "'"); + if (m_input[m_position] != c) throw CaretException("expected '" + AString(c) + "', got '" + m_input[m_position] + "' while parsing expression '" + m_input.mid(exprStart, m_position - exprStart + 1) + "'"); ++m_position; } @@ -805,6 +804,21 @@ return temp; } +bool CaretMathExpression::isFuncNamePattern(const AString& testStr) +{ + bool onlydigits = true; + for (int i = 0; i < testStr.length(); ++i) + { + QChar thisChar = testStr[i];//allow letters, numbers, and underscore, basically same as variables + if (!thisChar.isLetterOrNumber() && thisChar != '_') + { + return false; + } + if (!thisChar.isDigit()) onlydigits = false;//if there are only digits, this is not a variable, tryLiteral failed on something that was intended to be a literal + } + return !onlydigits; +} + CaretPointer CaretMathExpression::funcExpr() { int start = m_position; @@ -817,10 +831,15 @@ int funcnameEnd = m_input.indexOf('(', m_position); if (funcnameEnd != -1) { - bool ok = false; - MathFunctionEnum::Enum myfunc = MathFunctionEnum::fromName(m_input.mid(m_position, funcnameEnd - m_position).trimmed(), &ok); - if (ok) - { + AString funcName = m_input.mid(m_position, funcnameEnd - m_position).trimmed(); + if (isFuncNamePattern(funcName)) + {//if it looks like a function name, error if it is unknown, rather than a generic "extra characters" message + bool ok = false; + MathFunctionEnum::Enum myfunc = MathFunctionEnum::fromName(funcName, &ok); + if (!ok) + { + throw CaretException("expression contains unknown function name '" + funcName + "'"); + } int numArgs = -1; switch(myfunc) { @@ -884,7 +903,7 @@ { CaretPointer ret = tryLiteral();//try literal first, our relaxed rules for variable names overlap with integers if (ret != NULL) return ret; - if (!skipWhitespace()) throw CaretException("unexpected end of input, expected operand");//now, try named constant/variable + if (!skipWhitespace()) throw CaretException("unexpected end of expression, expected operand");//now, try named constant/variable int varEnd = m_position; bool onlydigits = true; while (varEnd < m_end) @@ -901,7 +920,7 @@ AString identifier = m_input.mid(m_position, varEnd - m_position); if (identifier.size() == 0)//hit an invalid character or end of input before getting any valid characters {//figure out why we stopped, give appropriate error - if (varEnd >= m_end) throw CaretException("unexpected end of input, expected operand"); + if (varEnd >= m_end) throw CaretException("unexpected end of expression, expected operand"); throw CaretException("unexpected character '" + AString(m_input[varEnd]) + "' at beginning of operand");//if it fails for all prefix unary, parens, function, literal, variable, then this character can never start an operand } if (onlydigits) throw CaretException("error parsing literal value beginning with '" + identifier + "'"); @@ -928,7 +947,7 @@ CaretPointer CaretMathExpression::tryLiteral() { - if (!skipWhitespace()) throw CaretException("unexpected end of input, expected operand");//now, try literal + if (!skipWhitespace()) throw CaretException("unexpected end of expression, expected operand");//now, try literal int litstart = m_position, litend = m_position; if (m_input[litend] == '-' || m_input[litend] == '+') ++litend;//allow literals to start with - or + : however, - will not happen, due to unary - (which does that so that -2^-2 works as -(2^(-2)) bool havedot = false, haveexp = false; diff -Nru connectome-workbench-1.4.2/src/Common/CaretMathExpression.h connectome-workbench-1.5.0/src/Common/CaretMathExpression.h --- connectome-workbench-1.4.2/src/Common/CaretMathExpression.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretMathExpression.h 2021-02-16 19:46:47.000000000 +0000 @@ -61,7 +61,7 @@ MathNode() { m_type = INVALID; m_function = MathFunctionEnum::INVALID; } MathNode(const ExprType& type) { m_type = type; m_function = MathFunctionEnum::INVALID; } double eval(const std::vector& values) const; - AString toString(const std::vector& varNames) const; + AString toString(const std::vector& varNames, bool addParens = true) const; }; std::map m_varNames; AString m_input; @@ -70,6 +70,7 @@ bool skipWhitespace(); bool accept(const char& c); void expect(const char& c, const int& exprStart); + static bool isFuncNamePattern(const AString& testStr); CaretPointer orExpr();//hopefully we have enough stack space that we won't overflow without a hundred or so levels of parenthesis/functions/exponents CaretPointer andExpr(); CaretPointer equalExpr(); diff -Nru connectome-workbench-1.4.2/src/Common/CaretPreferenceDataValue.h connectome-workbench-1.5.0/src/Common/CaretPreferenceDataValue.h --- connectome-workbench-1.4.2/src/Common/CaretPreferenceDataValue.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretPreferenceDataValue.h 2021-02-16 19:46:47.000000000 +0000 @@ -39,6 +39,7 @@ public: enum class DataType { + BOOLEAN, FLOAT, INTEGER, STRING diff -Nru connectome-workbench-1.4.2/src/Common/CaretPreferences.cxx connectome-workbench-1.5.0/src/Common/CaretPreferences.cxx --- connectome-workbench-1.4.2/src/Common/CaretPreferences.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretPreferences.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -29,11 +29,18 @@ #include #include +#include "ApplicationInformation.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferenceDataValue.h" +#include "DataFileTypeEnum.h" +#include "FileInformation.h" #include "ModelTransform.h" -#include "TileTabsConfiguration.h" +#include "RecentFileItem.h" +#include "RecentFileItemsContainer.h" +#include "RecentFileItemsFilter.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "TileTabsLayoutManualConfiguration.h" #include "WuQMacroGroup.h" using namespace caret; @@ -64,7 +71,7 @@ CaretPreferenceDataValue::DataType::FLOAT, CaretPreferenceDataValue::SavedInScene::SAVE_YES, 0.0)); - m_preferenceDataValues.push_back(m_volumeCrossHairGapPreference.get()); + m_preferenceStoredInSceneDataValues.push_back(m_volumeCrossHairGapPreference.get()); const QString defAllSliceLayout = VolumeSliceViewAllPlanesLayoutEnum::toName(VolumeSliceViewAllPlanesLayoutEnum::ROW_LAYOUT); m_volumeAllSlicePlanesLayout.reset(new CaretPreferenceDataValue(this->qSettings, @@ -72,7 +79,52 @@ CaretPreferenceDataValue::DataType::STRING, CaretPreferenceDataValue::SavedInScene::SAVE_NO, defAllSliceLayout)); - m_preferenceDataValues.push_back(m_volumeAllSlicePlanesLayout.get()); + m_preferenceStoredInSceneDataValues.push_back(m_volumeAllSlicePlanesLayout.get()); + + m_guiGesturesEnabled.reset(new CaretPreferenceDataValue(this->qSettings, + "guiGesturesEnabled", + CaretPreferenceDataValue::DataType::BOOLEAN, + CaretPreferenceDataValue::SavedInScene::SAVE_NO, + false)); + + m_toolBarWidthModePreference.reset(new CaretPreferenceDataValue(this->qSettings, + "toolBarWidthMode", + CaretPreferenceDataValue::DataType::STRING, + CaretPreferenceDataValue::SavedInScene::SAVE_NO, + ToolBarWidthModeEnum::toName(ToolBarWidthModeEnum::STANDARD))); + + m_fileOpenFromOperatingSystemTypePreference.reset(new CaretPreferenceDataValue(this->qSettings, + "openFileFromOperatingSystemType", + CaretPreferenceDataValue::DataType::STRING, + CaretPreferenceDataValue::SavedInScene::SAVE_NO, + FileOpenFromOpSysTypeEnum::toName(FileOpenFromOpSysTypeEnum::ASK_USER))); + + m_identificationDisplayModePreference.reset(new CaretPreferenceDataValue(this->qSettings, + "identificationDisplayMode", + CaretPreferenceDataValue::DataType::STRING, + CaretPreferenceDataValue::SavedInScene::SAVE_NO, + IdentificationDisplayModeEnum::toName(IdentificationDisplayModeEnum::OVERLAY_TOOLBOX))); + m_preferenceStoredInSceneDataValues.push_back(m_identificationDisplayModePreference.get()); + + + const int32_t maximumFilesDirectories(25); + m_recentMaximumNumberOfSceneAndSpecFilesPreference.reset(new CaretPreferenceDataValue(this->qSettings, + "recentMaximumNumberOfFiles", + CaretPreferenceDataValue::DataType::INTEGER, + CaretPreferenceDataValue::SavedInScene::SAVE_NO, + maximumFilesDirectories)); + + m_recentMaximumNumberOfDirectoriesPreferences.reset(new CaretPreferenceDataValue(this->qSettings, + "recentMaximumNumberOfDirectories", + CaretPreferenceDataValue::DataType::INTEGER, + CaretPreferenceDataValue::SavedInScene::SAVE_NO, + maximumFilesDirectories)); + + m_recentFilesSystemAccessMode.reset(new CaretPreferenceDataValue(this->qSettings, + "recentFilesSystemAccessMode", + CaretPreferenceDataValue::DataType::STRING, + CaretPreferenceDataValue::SavedInScene::SAVE_NO, + RecentFilesSystemAccessModeEnum::toName(RecentFilesSystemAccessModeEnum::ON))); m_colorsMode = BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES; } @@ -86,7 +138,7 @@ * Note DO NOT delete items in this vector as they are pointers to items * in unique_ptr's */ - m_preferenceDataValues.clear(); + m_preferenceStoredInSceneDataValues.clear(); this->removeAllCustomViews(); @@ -102,7 +154,7 @@ void CaretPreferences::invalidateSceneDataValues() { - for (auto pdv : m_preferenceDataValues) { + for (auto pdv : m_preferenceStoredInSceneDataValues) { pdv->setSceneValueValid(false); } } @@ -114,7 +166,7 @@ std::vector CaretPreferences::getPreferenceSceneDataValues() { - return m_preferenceDataValues; + return m_preferenceStoredInSceneDataValues; } /** @@ -446,10 +498,8 @@ void CaretPreferences::removeAllTileTabsConfigurations() { - for (std::vector::iterator iter = this->tileTabsConfigurations.begin(); - iter != this->tileTabsConfigurations.end(); - iter++) { - delete *iter; + for (auto ttc : this->tileTabsConfigurations) { + delete ttc; } this->tileTabsConfigurations.clear(); } @@ -458,16 +508,63 @@ * Write the tile tabs configurations. */ void -CaretPreferences::writeTileTabsConfigurations() +CaretPreferences::writeTileTabsUserConfigurations() { + /* + * NOTE: The GRID and MANUAL configurations are written separately. + * Older versions of wb_view will correctly read the GRID configurations + * and ignore the MANUAL configurations (not log error messages). + * Newer versions of wb_view will read both. + */ + + /* + * Write only GRID configurations to "NAME_TILE_TABS_CONFIGURATIONS", + * This allows previous versions of the software to read the configurations + */ this->qSettings->beginWriteArray(NAME_TILE_TABS_CONFIGURATIONS); const int32_t numViews = static_cast(this->tileTabsConfigurations.size()); + int32_t gridConfigCounter(0); for (int32_t i = 0; i < numViews; i++) { - this->qSettings->setArrayIndex(i); - this->qSettings->setValue(AString::number(i), - this->tileTabsConfigurations[i]->encodeInXML()); + CaretAssertVectorIndex(this->tileTabsConfigurations, i); + const TileTabsLayoutBaseConfiguration* config = this->tileTabsConfigurations[i]; + switch (config->getLayoutType()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + this->qSettings->setArrayIndex(gridConfigCounter); + this->qSettings->setValue(AString::number(gridConfigCounter), + config->encodeInXML()); + gridConfigCounter++; + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + break; + } } this->qSettings->endArray(); + + /* + * Write only MANUAL configurations to "NAME_TILE_TABS_CONFIGURATIONS_TWO", + * Older versions of the software will not try to read these configurations + */ + this->qSettings->beginWriteArray(NAME_TILE_TABS_CONFIGURATIONS_TWO); + int32_t manualConfigCounter(0); + for (int32_t i = 0; i < numViews; i++) { + CaretAssertVectorIndex(this->tileTabsConfigurations, i); + const TileTabsLayoutBaseConfiguration* config = this->tileTabsConfigurations[i]; + switch (config->getLayoutType()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + this->qSettings->setArrayIndex(manualConfigCounter); + this->qSettings->setValue(AString::number(manualConfigCounter), + config->encodeInXML()); + manualConfigCounter++; + break; + } + } + this->qSettings->endArray(); + this->qSettings->sync(); } @@ -481,7 +578,7 @@ * by a concurrently running workbench. */ void -CaretPreferences::readTileTabsConfigurations(const bool performSync) +CaretPreferences::readTileTabsUserConfigurations(const bool performSync) { if (performSync) { this->qSettings->sync(); @@ -490,92 +587,178 @@ this->removeAllTileTabsConfigurations(); /* - * Read Configurations + * NOTE: The GRID and MANUAL configurations are written separately. + * Older versions of wb_view will correctly read the GRID configurations + * and ignore the MANUAL configurations (not log error messages). + * Newer versions of wb_view will read both. */ - const int numConfigurations = this->qSettings->beginReadArray(NAME_TILE_TABS_CONFIGURATIONS); - for (int i = 0; i < numConfigurations; i++) { + std::vector configurationStrings; + + /* + * Read GRID Configurations (Array Name: NAME_TILE_TABS_CONFIGURATIONS) + */ + const int numGridConfigs = this->qSettings->beginReadArray(NAME_TILE_TABS_CONFIGURATIONS); + for (int32_t i = 0; i < numGridConfigs; i++) { + this->qSettings->setArrayIndex(i); + const AString str = this->qSettings->value(AString::number(i)).toString(); + configurationStrings.push_back(str); + } + this->qSettings->endArray(); + + /* + * Read MANUAL Configurations (Array Name: NAME_TILE_TABS_CONFIGURATIONS_TWO) + */ + const int numManualConfigs = this->qSettings->beginReadArray(NAME_TILE_TABS_CONFIGURATIONS_TWO); + for (int32_t i = 0; i < numManualConfigs; i++) { this->qSettings->setArrayIndex(i); - const AString configString = this->qSettings->value(AString::number(i)).toString(); - TileTabsConfiguration* ttc = new TileTabsConfiguration(); + const AString str = this->qSettings->value(AString::number(i)).toString(); + configurationStrings.push_back(str); + } + this->qSettings->endArray(); + + /* + * Read from the configuration strings to create layouts + */ + const int numConfigurations = static_cast(configurationStrings.size()); + for (int i = 0; i < numConfigurations; i++) { + CaretAssertVectorIndex(configurationStrings, i); + const AString configString = configurationStrings[i]; AString errorMessage; - if (ttc->decodeFromXML(configString, - errorMessage)) { + TileTabsLayoutBaseConfiguration* ttc = TileTabsLayoutBaseConfiguration::decodeFromXML(configString, + errorMessage); + if (ttc != NULL) { this->tileTabsConfigurations.push_back(ttc); } else { CaretLogWarning(errorMessage); - delete ttc; + } + + const bool testFlag(false); + if (testFlag) { + AString errorMessage; + TileTabsLayoutBaseConfiguration* config = TileTabsLayoutBaseConfiguration::decodeFromXML(configString, + errorMessage); + if (config != NULL) { + std::cout << "Read config: " << config->toString() << std::endl; + + TileTabsLayoutGridConfiguration* gridConfig = dynamic_cast(config); + if (gridConfig != NULL) { + const int32_t rowCount = gridConfig->getNumberOfRows(); + const int32_t colCount = gridConfig->getNumberOfColumns(); + const int32_t numTabs = rowCount * colCount; + std::vector tabIndices; + for (int32_t i = 0; i < numTabs; i++) { + tabIndices.push_back(i); + } + TileTabsLayoutManualConfiguration* manualLayout + = TileTabsLayoutManualConfiguration::newInstanceFromGridLayout(gridConfig, + TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID, + tabIndices); + if (manualLayout != NULL) { + std::cout << "MANUAL VERSION OF GRID LAYOUT: " << std::endl; + std::cout << manualLayout->toString() << std::endl; + + AString xmlText = manualLayout->encodeInXML(); + std::cout << "XML: " << std::endl; + std::cout << xmlText << std::endl << std::endl; + + delete manualLayout; + + TileTabsLayoutBaseConfiguration* configMan = + TileTabsLayoutBaseConfiguration::decodeFromXML(xmlText, + errorMessage); + if (configMan != NULL) { + std::cout << "After reading manual configuration and writing it: " << std::endl; + std::cout << configMan->toString() << std::endl << std::endl; + } + else { + std::cout << "ERROR decoding from manual layout: " + << errorMessage << std::endl; + } + } + } + } + else { + std::cout << "Error reading config: " << errorMessage << std::endl; + std::cout << "FROM: " << configString << std::endl; + } + std::cout << std::endl; } } - this->qSettings->endArray(); -} - -/** - * @return The Tile tabs configurations sorted by name. - */ -std::vector -CaretPreferences::getTileTabsConfigurationsSortedByName() const -{ - /* - * Copy the configurations and sort them by name. - */ - std::vector configurations; - configurations.insert(configurations.end(), - this->tileTabsConfigurations.begin(), - this->tileTabsConfigurations.end()); - std::sort(configurations.begin(), - configurations.end(), - TileTabsConfiguration::lessThanComparisonByName); - - return configurations; } /** - * Get the tile tabs configuration with the given unique identifier. - * - * @param uniqueIdentifier - * Unique identifier of the requested tile tabs configuration. - * @return - * Pointer to tile tabs configuration with the given unique identifier - * or NULL is it does not exist. - */ -TileTabsConfiguration* -CaretPreferences::getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) -{ - for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); - iter != this->tileTabsConfigurations.end(); - iter++) { - TileTabsConfiguration* ttc = *iter; - if (ttc->getUniqueIdentifier() == uniqueIdentifier) { - return ttc; + * @return A vector containing pairs of Name and Unique Identifier for each + * Tile Tabs configuration. The items are sorted by name with 'first' being + * the name, and 'second' being the unique identifier. + * + * @param includeManualConfigurationsFlag + * If true, include manual configurations; if false exclude manual configurations + */ +std::vector> +CaretPreferences::getTileTabsUserConfigurationsNamesAndUniqueIdentifiers(const bool includeManualConfigurationsFlag) const +{ + std::vector> nameIDs; + + for (const auto ttc : this->tileTabsConfigurations) { + if (ttc->getLayoutType() == TileTabsLayoutConfigurationTypeEnum::MANUAL) { + if ( ! includeManualConfigurationsFlag) { + continue; + } + } + + QString typeString; + switch (ttc->getLayoutType()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + typeString = " (AG)"; + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + typeString = (" (G," + + AString::number(ttc->getNumberOfTabs()) + + ")"); + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + typeString = (" (M," + + AString::number(ttc->getNumberOfTabs()) + + ")"); + break; } + nameIDs.push_back(std::make_pair(ttc->getName() + typeString, + ttc->getUniqueIdentifier())); } - return NULL; + std::sort(nameIDs.begin(), + nameIDs.end(), + [](const std::pair& a, const std::pair& b) { return a.first < b.first; }); + + return nameIDs; } /** - * Get the tile tabs configuration with the given unique identifier. - * + * @return A copy of the tile tabs configuration with the given unique identifier. A copy is returned + * since preferenes may get updated which causes reloading of the user configurations and if the caller + * had a pointer to a user configuration, that pointer would be invalid (point to destroyed configuration) + * when preferences are updated (synced). Pointer will be NULL if there is no user configuration with + * the given unique identifier. + * * @param uniqueIdentifier * Unique identifier of the requested tile tabs configuration. - * @return - * Pointer to tile tabs configuration with the given unique identifier - * or NULL is it does not exist. */ -const TileTabsConfiguration* -CaretPreferences::getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) const +std::unique_ptr +CaretPreferences::getCopyOfTileTabsUserConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) const { - for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); + std::unique_ptr pointer; + + for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); iter != this->tileTabsConfigurations.end(); iter++) { - const TileTabsConfiguration* ttc = *iter; + const TileTabsLayoutBaseConfiguration* ttc = *iter; if (ttc->getUniqueIdentifier() == uniqueIdentifier) { - return ttc; + pointer.reset(ttc->newCopyWithNewUniqueIdentifier()); } } - - return NULL; + + return pointer; } /** @@ -587,13 +770,13 @@ * Pointer to tile tabs configuration with the given name * or NULL is it does not exist. */ -TileTabsConfiguration* -CaretPreferences::getTileTabsConfigurationByName(const AString& name) const +TileTabsLayoutBaseConfiguration* +CaretPreferences::getTileTabsUserConfigurationByName(const AString& name) const { - for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); + for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); iter != this->tileTabsConfigurations.end(); iter++) { - TileTabsConfiguration* ttc = *iter; + TileTabsLayoutBaseConfiguration* ttc = *iter; if (name == ttc->getName()) { return ttc; @@ -604,39 +787,157 @@ } /** - * Add a new tile tabs configuration. + * Add a new tile tabs user configuration. * * @param tileTabsConfiguration - * New tile tabs configuration that is added. + * New tile tabs configuration that is copied and added. + * @param configurationName + * New name for copied configuration (ignored if empty) */ void -CaretPreferences::addTileTabsConfiguration(TileTabsConfiguration* tileTabsConfiguration) +CaretPreferences::addTileTabsUserConfiguration(const TileTabsLayoutBaseConfiguration* tileTabsConfiguration, + const AString& configurationName) +{ + TileTabsLayoutBaseConfiguration* configCopy = tileTabsConfiguration->newCopyWithNewUniqueIdentifier(); + if ( ! configurationName.isEmpty()) { + configCopy->setName(configurationName); + } + this->tileTabsConfigurations.push_back(configCopy); + this->writeTileTabsUserConfigurations(); +} + +/** + * Replace a tile tabs configuration with another tile tabs configuration + * + * @param replaceUserTileTabsUniqueIdentifier + * Unique identifier of configuration that is to be replaced + * This user configuration will be replaced and this pointer will be INVALID after + * this method is called. + * @param replaceWithConfiguration + * This configuration is copied and replaces the other configuration. + * @param errorMessageOut + * Contains error information if fails to replace configuration + * @return + * True if successful, else false + */ +bool +CaretPreferences::replaceTileTabsUserConfiguration(const AString& replaceUserTileTabsUniqueIdentifier, + const TileTabsLayoutBaseConfiguration* replaceWithConfiguration, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + CaretAssert(replaceWithConfiguration); + + int32_t replaceIndex = -1; + const int32_t numConfigs = static_cast(this->tileTabsConfigurations.size()); + for (int32_t i = 0; i < numConfigs; i++) { + CaretAssertVectorIndex(this->tileTabsConfigurations, i); + if (this->tileTabsConfigurations[i]->getUniqueIdentifier() == replaceUserTileTabsUniqueIdentifier) { + replaceIndex = i; + break; + } + } + + if (replaceIndex < 0) { + errorMessageOut = "Failed to find configuration for replacement."; + return false; + } + + /* + * Delete configuration since it may be a different subclass than other configuration + */ + CaretAssertVectorIndex(this->tileTabsConfigurations, replaceIndex); + const AString name = this->tileTabsConfigurations[replaceIndex]->getName(); + const AString uuid = this->tileTabsConfigurations[replaceIndex]->getUniqueIdentifier(); + delete this->tileTabsConfigurations[replaceIndex]; + + TileTabsLayoutBaseConfiguration* newConfig = replaceWithConfiguration->newCopyWithUniqueIdentifier(uuid); + CaretAssert(newConfig); + newConfig->setName(name); + this->tileTabsConfigurations[replaceIndex] = newConfig; + + this->writeTileTabsUserConfigurations(); + + return true; +} + +/** + * Rename the tile tabs configuration with the given name. + * + * @param tileTabsUniqueIdentifier + * Unique identifier of configuration that will be renamed. + * @param name + * New name for configuration + * @param errorMessageOut + * Will contain error information if command fails + * @return + * True if configuration was removed, else false. + */ +bool +CaretPreferences::renameTileTabsUserConfiguration(const QString& tileTabsUniqueIdentifier, + const AString& name, + AString& errorMessageOut) { - this->tileTabsConfigurations.push_back(tileTabsConfiguration); - this->writeTileTabsConfigurations(); + errorMessageOut.clear(); + + if (tileTabsUniqueIdentifier.isEmpty()) { + errorMessageOut = "Tile Tabs Unique Identifier is empty."; + return false; + } + + for (std::vector::iterator iter = this->tileTabsConfigurations.begin(); + iter != this->tileTabsConfigurations.end(); + iter++) { + TileTabsLayoutBaseConfiguration* ttc = *iter; + if (ttc->getUniqueIdentifier() == tileTabsUniqueIdentifier) { + ttc->setName(name); + this->writeTileTabsUserConfigurations(); + return true; + } + } + + errorMessageOut = ("Did not find a Tile Tabs Configuration with UniqueID=" + + tileTabsUniqueIdentifier); + return false; } /** - * Remove the tile tabs configuration with the given name. + * Remove the tile tabs configuration with the given unique identifier. * * @param tileTabsUniqueIdentifier * Unique identifier of configuration that will be removed. + * @param errorMessageOut + * Will contain error information if command fails + * @return + * True if configuration was removed, else false. */ -void -CaretPreferences::removeTileTabsConfigurationByUniqueIdentifier(const AString& tileTabsUniqueIdentifier) +bool +CaretPreferences::removeTileTabsUserConfigurationByUniqueIdentifier(const AString& tileTabsUniqueIdentifier, + AString& errorMessageOut) { - for (std::vector::iterator iter = this->tileTabsConfigurations.begin(); + errorMessageOut.clear(); + + if (tileTabsUniqueIdentifier.isEmpty()) { + errorMessageOut = "Tile Tabs Unique Identifier is empty."; + return false; + } + + for (std::vector::iterator iter = this->tileTabsConfigurations.begin(); iter != this->tileTabsConfigurations.end(); iter++) { - TileTabsConfiguration* ttc = *iter; + TileTabsLayoutBaseConfiguration* ttc = *iter; if (ttc->getUniqueIdentifier() == tileTabsUniqueIdentifier) { this->tileTabsConfigurations.erase(iter); delete ttc; - break; + this->writeTileTabsUserConfigurations(); + return true; } } - this->writeTileTabsConfigurations(); + errorMessageOut = ("Did not find a Tile Tabs Configuration with UniqueID=" + + tileTabsUniqueIdentifier); + return false; } @@ -711,6 +1012,13 @@ this->userColors.m_colorBackgroundChart, 3); + writeUnsignedByteArray(NAME_COLOR_FOREGROUND_MEDIA, + this->userColors.m_colorForegroundMedia, + 3); + writeUnsignedByteArray(NAME_COLOR_BACKGROUND_MEDIA, + this->userColors.m_colorBackgroundMedia, + 3); + writeUnsignedByteArray(NAME_COLOR_FOREGROUND_SURFACE, this->userColors.m_colorForegroundSurface, 3); @@ -766,186 +1074,6 @@ m_colorsMode = colorsMode; } - -/** - * Get the previous spec files. - * - * @param previousSpecFiles - * Will contain previous spec files. - */ -void -CaretPreferences::getPreviousSpecFiles(std::vector& previousSpecFiles) const -{ - previousSpecFiles = this->previousSpecFiles; -} - -/** - * Add to the previous spec files. - * - * @param specFileName - * Spec file added to the previous spec files. - */ -void -CaretPreferences::addToPreviousSpecFiles(const AString& specFileName) -{ - if (specFileName.isEmpty() == false) { - this->addToPrevious(this->previousSpecFiles, - specFileName); - } - - const int32_t num = static_cast(this->previousSpecFiles.size()); - this->qSettings->beginWriteArray(NAME_PREVIOUS_SPEC_FILES); - for (int i = 0; i < num; i++) { - this->qSettings->setArrayIndex(i); - this->qSettings->setValue(AString::number(i), - this->previousSpecFiles[i]); - } - this->qSettings->endArray(); - this->qSettings->sync(); -} - -/** - * Clear the previous spec files. - */ -void -CaretPreferences::clearPreviousSpecFiles() -{ - this->previousSpecFiles.clear(); - this->addToPreviousSpecFiles(""); -} - -/** - * Get the previous scene files. - * - * @param previousSceneFiles - * Will contain previous scene files. - */ -void -CaretPreferences::getPreviousSceneFiles(std::vector& previousSceneFiles) const -{ - previousSceneFiles = this->previousSceneFiles; -} - -/** - * Add to the previous scene files. - * - * @param sceneFileName - * Scene file added to the previous scene files. - */ -void -CaretPreferences::addToPreviousSceneFiles(const AString& sceneFileName) -{ - if (sceneFileName.isEmpty() == false) { - this->addToPrevious(this->previousSceneFiles, - sceneFileName); - } - - const int32_t num = static_cast(this->previousSceneFiles.size()); - this->qSettings->beginWriteArray(NAME_PREVIOUS_SCENE_FILES); - for (int i = 0; i < num; i++) { - this->qSettings->setArrayIndex(i); - this->qSettings->setValue(AString::number(i), - this->previousSceneFiles[i]); - } - this->qSettings->endArray(); - this->qSettings->sync(); -} - -/** - * Clear the previous scene files. - */ -void -CaretPreferences::clearPreviousSceneFiles() -{ - this->previousSceneFiles.clear(); - this->addToPreviousSceneFiles(""); -} - -/** - * Get the directories that were used in the Open File Dialog. - * - * @param previousOpenFileDirectories - * Will contain previous directories. - */ -void -CaretPreferences::getPreviousOpenFileDirectories(std::vector& previousOpenFileDirectories) const -{ - previousOpenFileDirectories = this->previousOpenFileDirectories; -} - -/** - * Get the directories that were used in the Open File Dialog. - * - * @param previousOpenFileDirectories - * Will contain previous directories. - */ -void -CaretPreferences::getPreviousOpenFileDirectories(QStringList& previousOpenFileDirectoriesList) const -{ - previousOpenFileDirectoriesList.clear(); - const int32_t numDirectories = static_cast(this->previousOpenFileDirectories.size()); - for (int32_t i = 0; i < numDirectories; i++) { - previousOpenFileDirectoriesList.append(this->previousOpenFileDirectories[i]); - } -} - -/** - * Add to the previous directories that were used in the Open File Dialog. - * - * @param directoryName - * Directory added to the previous directories from Open File Dialog. - */ -void -CaretPreferences::addToPreviousOpenFileDirectories(const AString& directoryName) -{ - this->addToPrevious(this->previousOpenFileDirectories, - directoryName); - - const int32_t num = static_cast(this->previousOpenFileDirectories.size()); - this->qSettings->beginWriteArray(NAME_PREVIOUS_OPEN_FILE_DIRECTORIES); - for (int i = 0; i < num; i++) { - this->qSettings->setArrayIndex(i); - this->qSettings->setValue(AString::number(i), - this->previousOpenFileDirectories[i]); - } - this->qSettings->endArray(); - this->qSettings->sync(); -} - -/** - * Add to a list of previous, removing any matching entries - * and limiting the size of the list. - * - * @param previousDeque - * Deque containing the previous elements. - * @param newName - * Name that is added at the front. - */ -void -CaretPreferences::addToPrevious(std::vector& previousVector, - const AString& newName) -{ - /* - * Note: remove moves duplicate elements to after 'pos' but - * does not change the size of the container so use erase - * to remove the duplicate elements from the container. - */ - std::vector::iterator pos = std::remove(previousVector.begin(), - previousVector.end(), - newName); - previousVector.erase(pos, - previousVector.end()); - - const uint64_t MAX_ELEMENTS = 10; - if (previousVector.size() > MAX_ELEMENTS) { - previousVector.erase(previousVector.begin() + MAX_ELEMENTS, - previousVector.end()); - } - - previousVector.insert(previousVector.begin(), - newName); -} - /** * @return The logging level. */ @@ -1247,60 +1375,156 @@ } /** - * @return Are axes crosshairs displayed? + * @return The toolbar's width mode */ -bool -CaretPreferences::isVolumeAxesCrosshairsDisplayed() const +ToolBarWidthModeEnum::Enum +CaretPreferences::getToolBarWidthMode() const { - return this->displayVolumeAxesCrosshairs; + QString stringValue(m_toolBarWidthModePreference->getValue().toString()); + bool validFlag(false); + const ToolBarWidthModeEnum::Enum enumValue = + ToolBarWidthModeEnum::fromName(stringValue, &validFlag); + return enumValue; } /** - * Set axes crosshairs displayed - * @param displayed - * New status. + * Set the toolbar's width mode + * @param widthMode + * The new width mode */ -void -CaretPreferences::setVolumeAxesCrosshairsDisplayed(const bool displayed) +void +CaretPreferences::setToolBarWidthMode(const ToolBarWidthModeEnum::Enum widthMode) { - if (this->displayVolumeAxesCrosshairs == displayed) { - return; - } - - this->displayVolumeAxesCrosshairs = displayed; - this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_CROSSHAIRS, - this->displayVolumeAxesCrosshairs); - this->qSettings->sync(); + const QString stringValue = ToolBarWidthModeEnum::toName(widthMode); + m_toolBarWidthModePreference->setValue(stringValue); } /** - * @return The volume all slice planes layout + * @return File open from operating system type */ -VolumeSliceViewAllPlanesLayoutEnum::Enum -CaretPreferences::getVolumeAllSlicePlanesLayout() const +FileOpenFromOpSysTypeEnum::Enum +CaretPreferences::getFileOpenFromOpSysType() const { - QString stringValue(m_volumeAllSlicePlanesLayout->getValue().toString()); + QString stringValue(m_fileOpenFromOperatingSystemTypePreference->getValue().toString()); bool validFlag(false); - VolumeSliceViewAllPlanesLayoutEnum::Enum enumValue = - VolumeSliceViewAllPlanesLayoutEnum::fromName(stringValue, &validFlag); + const FileOpenFromOpSysTypeEnum::Enum enumValue = FileOpenFromOpSysTypeEnum::fromName(stringValue, + &validFlag); return enumValue; } /** - * Set volume all slice planes layout - * - * @param allViewLayout - * The all slice planes layout + * Set the open file from operating system type + * @param openType + * The new open type */ void -CaretPreferences::setVolumeAllSlicePlanesLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum allViewLayout) +CaretPreferences::setFileOpenFromOpSysType(const FileOpenFromOpSysTypeEnum::Enum openType) { - const QString stringValue = VolumeSliceViewAllPlanesLayoutEnum::toName(allViewLayout); - m_volumeAllSlicePlanesLayout->setValue(stringValue); + const QString stringValue = FileOpenFromOpSysTypeEnum::toName(openType); + m_fileOpenFromOperatingSystemTypePreference->setValue(stringValue); } /** - * @return The crosshair gap + * @return The identification display mode + */ +IdentificationDisplayModeEnum::Enum +CaretPreferences::getIdentificationDisplayMode() const +{ + QString stringValue(m_identificationDisplayModePreference->getValue().toString()); + bool validFlag(false); + const IdentificationDisplayModeEnum::Enum enumValue = + IdentificationDisplayModeEnum::fromName(stringValue, &validFlag); + return enumValue; +} + +/** + * Set the identification display mode + * @param identificationDisplayMode + * New identification display mode + */ +void +CaretPreferences::setIdentificationDisplayMode(const IdentificationDisplayModeEnum::Enum identificationDisplayMode) +{ + const QString stringValue = IdentificationDisplayModeEnum::toName(identificationDisplayMode); + m_identificationDisplayModePreference->setValue(stringValue); +} + +/** + * @return Are axes crosshairs displayed? + */ +bool +CaretPreferences::isVolumeAxesCrosshairsDisplayed() const +{ + return this->displayVolumeAxesCrosshairs; +} + +/** + * Set axes crosshairs displayed + * @param displayed + * New status. + */ +void +CaretPreferences::setVolumeAxesCrosshairsDisplayed(const bool displayed) +{ + if (this->displayVolumeAxesCrosshairs == displayed) { + return; + } + + this->displayVolumeAxesCrosshairs = displayed; + this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_CROSSHAIRS, + this->displayVolumeAxesCrosshairs); + this->qSettings->sync(); +} + +/** + * @return The volume all slice planes layout + */ +VolumeSliceViewAllPlanesLayoutEnum::Enum +CaretPreferences::getVolumeAllSlicePlanesLayout() const +{ + QString stringValue(m_volumeAllSlicePlanesLayout->getValue().toString()); + bool validFlag(false); + VolumeSliceViewAllPlanesLayoutEnum::Enum enumValue = + VolumeSliceViewAllPlanesLayoutEnum::fromName(stringValue, &validFlag); + return enumValue; +} + +/** + * Set volume all slice planes layout + * + * @param allViewLayout + * The all slice planes layout + */ +void +CaretPreferences::setVolumeAllSlicePlanesLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum allViewLayout) +{ + const QString stringValue = VolumeSliceViewAllPlanesLayoutEnum::toName(allViewLayout); + m_volumeAllSlicePlanesLayout->setValue(stringValue); +} + +/** + * @retrurn Gestures enabled in GUI + */ +bool +CaretPreferences::isGuiGesturesEnabled() const +{ + return m_guiGesturesEnabled->getValue().toBool(); +} + +/** + * Set Gestures enabled in GUI + * + * @param status + * New enabled status + */ +void +CaretPreferences::setGuiGesturesEnabled(const bool status) +{ + m_guiGesturesEnabled->setValue(status); +} + +/** + * @return The crosshair gap */ float CaretPreferences::getVolumeCrosshairGap() const @@ -1691,6 +1915,18 @@ 3); userColors.setColorBackgroundSurfaceView(colorRGB); + userColors.getColorForegroundMediaView(colorRGB); + readUnsignedByteArray(NAME_COLOR_FOREGROUND_MEDIA, + colorRGB, + 3); + userColors.setColorForegroundMediaView(colorRGB); + + userColors.getColorBackgroundMediaView(colorRGB); + readUnsignedByteArray(NAME_COLOR_BACKGROUND_MEDIA, + colorRGB, + 3); + userColors.setColorBackgroundMediaView(colorRGB); + userColors.getColorForegroundVolumeView(colorRGB); readUnsignedByteArray(NAME_COLOR_FOREGROUND_VOLUME, colorRGB, @@ -1715,33 +1951,33 @@ 3); userColors.setColorChartHistogramThreshold(colorRGB); - this->previousSpecFiles.clear(); - const int numPrevSpec = this->qSettings->beginReadArray(NAME_PREVIOUS_SPEC_FILES); - for (int i = 0; i < numPrevSpec; i++) { - this->qSettings->setArrayIndex(i); - previousSpecFiles.push_back(this->qSettings->value(AString::number(i)).toString()); - } - this->qSettings->endArray(); - - this->previousSceneFiles.clear(); - const int numPrevScene = this->qSettings->beginReadArray(NAME_PREVIOUS_SCENE_FILES); - for (int i = 0; i < numPrevScene; i++) { - this->qSettings->setArrayIndex(i); - previousSceneFiles.push_back(this->qSettings->value(AString::number(i)).toString()); - } - this->qSettings->endArray(); - - this->previousOpenFileDirectories.clear(); - const int numPrevDir = this->qSettings->beginReadArray(NAME_PREVIOUS_OPEN_FILE_DIRECTORIES); - for (int i = 0; i < numPrevDir; i++) { - this->qSettings->setArrayIndex(i); - previousOpenFileDirectories.push_back(this->qSettings->value(AString::number(i)).toString()); + /* + * Old storage for previous spec and scene no longer used + * but might want to use them to initialize the new + * scene and spce files in RecentFileItem's. + */ + const bool readObsoleteFlag(false); + if (readObsoleteFlag) { + this->previousSpecFiles.clear(); + const int numPrevSpec = this->qSettings->beginReadArray(NAME_PREVIOUS_SPEC_FILES); + for (int i = 0; i < numPrevSpec; i++) { + this->qSettings->setArrayIndex(i); + previousSpecFiles.push_back(this->qSettings->value(AString::number(i)).toString()); + } + this->qSettings->endArray(); + + this->previousSceneFiles.clear(); + const int numPrevScene = this->qSettings->beginReadArray(NAME_PREVIOUS_SCENE_FILES); + for (int i = 0; i < numPrevScene; i++) { + this->qSettings->setArrayIndex(i); + previousSceneFiles.push_back(this->qSettings->value(AString::number(i)).toString()); + } + this->qSettings->endArray(); } - this->qSettings->endArray(); this->readCustomViews(false); - this->readTileTabsConfigurations(false); + this->readTileTabsUserConfigurations(false); this->readMacros(false); @@ -1918,4 +2154,927 @@ } } +/** + * Read the recent scene and spec files from preferences and add them to the given container. + * + * @param container + * Recent scene and spec files are added to this container + * @param errorMessageOut + * Container error information if false returned + * @return True if successful, false if error. + */ +bool +CaretPreferences::readRecentSceneAndSpecFiles(RecentFileItemsContainer* container, + AString& errorMessageOut) +{ + const bool successFlag = readRecentFileItemsContainer(NAME_RECENT_SCENE_AND_SPEC_FILES, + container, + errorMessageOut); + if (successFlag) { + container->removeItemsExceedingMaximumNumber(getRecentMaximumNumberOfSceneAndSpecFiles()); + } + + return successFlag; +} + +/** + * Write the recent scene and spec files to preferences from the given container. + * + * @param container + * Recent scene and spec files written to preferences + * @param errorMessageOut + * Container error information if false returned + * @return True if successful, false if error. + */ +bool +CaretPreferences::writeRecentSceneAndSpecFiles(RecentFileItemsContainer* container, + AString& errorMessageOut) +{ + container->removeItemsExceedingMaximumNumber(getRecentMaximumNumberOfSceneAndSpecFiles()); + return writeRecentFileItemsContainer(NAME_RECENT_SCENE_AND_SPEC_FILES, + container, + errorMessageOut); +} + +/** + * If the file is a scene or spec file (detected by extension) add it to recent scene/spec files. + * Always add the directory containing the file to the recent directories. + * + * @param filename + * Name of file. + */ +void +CaretPreferences::addToRecentFilesAndOrDirectories(const AString& directoryOrFileName) +{ + /* + * Only update recents directories if GUI application (wb_view) + */ + if (ApplicationInformation().getApplicationType() != ApplicationTypeEnum::APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE) { + return; + } + + if ((getRecentMaximumNumberOfSceneAndSpecFiles() <= 0) + && (getRecentMaximumNumberOfDirectories() <= 0)) { + return; + } + + /* + * User could have a directory ending with a scene or spec file extension. + * While highly unlikely, make sure it is a file before adding to + * recent scene/spec files. + */ + FileInformation fileInfo(directoryOrFileName); + if (fileInfo.isFile()) { + bool validFlag; + const DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromFileExtension(directoryOrFileName, + &validFlag); + if (validFlag) { + switch (dataFileType) { + case DataFileTypeEnum::SCENE: + case DataFileTypeEnum::SPECIFICATION: + addToRecentSceneAndSpecFiles(directoryOrFileName); + break; + default: + break; + } + } + } + + /* + * Always update the recent directories + * unless a remote file (http, ftp, etc) + */ + if ( ! fileInfo.isRemoteFile()) { + addToRecentDirectories(directoryOrFileName); + } +} + +/** + * Add the given filename to the recent scene and spec files + * @param filename + * Name of file. + */ +void +CaretPreferences::addToRecentSceneAndSpecFiles(const AString& filename) +{ + /* + * Only update recents files if GUI application (wb_view) + */ + if (ApplicationInformation().getApplicationType() != ApplicationTypeEnum::APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE) { + return; + } + + if (getRecentMaximumNumberOfSceneAndSpecFiles() <= 0) { + return; + } + + if (filename.isEmpty()) { + return; + } + + if (isInRecentFilesExclusionPaths(filename)) { + return; + } + + const AString errorPrefix("Error updating recent scene spec files "); + + RecentFileItemTypeEnum::Enum itemType = RecentFileItemTypeEnum::SCENE_FILE; + bool validFlag(false); + const DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromFileExtension(filename, + &validFlag); + if (validFlag) { + switch (dataFileType) { + case DataFileTypeEnum::SCENE: + itemType = RecentFileItemTypeEnum::SCENE_FILE; + break; + case DataFileTypeEnum::SPECIFICATION: + itemType = RecentFileItemTypeEnum::SPEC_FILE; + break; + default: + CaretLogSevere(errorPrefix + + "file extension is neither scene nor spec " + + filename); + return; + break; + } + } + else { + CaretLogSevere(errorPrefix + + "invalid file extension on " + + filename); + return; + } + + std::unique_ptr container(RecentFileItemsContainer::newInstance()); + CaretAssert(container); + + AString errorMessage; + bool successFlag(readRecentSceneAndSpecFiles(container.get(), + errorMessage)); + if ( ! successFlag) { + CaretLogSevere(errorPrefix + + " during reading: " + + errorMessage); + return; + } + + RecentFileItem* newItem = new RecentFileItem(itemType, + filename); + newItem->setLastAccessByWorkbenchDateTimeToCurrentDateTime(); + container->addItem(newItem); + + successFlag = writeRecentSceneAndSpecFiles(container.get(), + errorMessage); + if ( ! successFlag) { + CaretLogSevere(errorPrefix + + " during writing: " + + errorMessage); + return; + } +} + +/** + * Read the recent file items container using the given preference name. + * + * @param preferenceName + * Name of container in preferences + * @param container + * The recent file items container + * @param errorMessageOut + * Eerror information if false returned + * @return True if successful, false if error. + */ +bool +CaretPreferences::readRecentFileItemsContainer(const AString& preferenceName, + RecentFileItemsContainer* container, + AString& errorMessageOut) +{ + CaretAssert(container); + bool resultFlag(false); + errorMessageOut.clear(); + + const AString xmlText = getString(preferenceName); + if ( ! xmlText.isEmpty()) { + resultFlag = container->readFromXML(xmlText, + errorMessageOut); + } + else { + /* Empty string (no previous value in preferences) is OK */ + resultFlag = true; + } + + return resultFlag; +} + +/** + * Write the recent file items container using the given preference name. + * + * @param preferenceName + * Name of container in preferences + * @param container + * The recent file items container + * @param errorMessageOut + * Eerror information if false returned + * @return True if successful, false if error. + */ +bool +CaretPreferences::writeRecentFileItemsContainer(const AString& preferenceName, + const RecentFileItemsContainer* container, + AString& errorMessageOut) +{ + CaretAssert(container); + bool resultFlag(false); + errorMessageOut.clear(); + + AString xmlText; + resultFlag = container->writeToXML(xmlText, + errorMessageOut); + if (resultFlag) { + setString(preferenceName, + xmlText); + } + + return resultFlag; +} + +/** + * Read the recent directories from preferences and add them to the given container. + * + * @param container + * Recent directories are added to this container + * @param errorMessageOut + * Container error information if false returned + * @return True if successful, false if error. + */ +bool +CaretPreferences::readRecentDirectories(RecentFileItemsContainer* container, + AString& errorMessageOut) +{ + const bool successFlag = readRecentFileItemsContainer(NAME_RECENT_DIRECTORIES, + container, + errorMessageOut); + if (successFlag) { + container->removeItemsExceedingMaximumNumber(getRecentMaximumNumberOfDirectories()); + } + + return successFlag; +} + +/** + * Write the recent directories to preferences from the given container. + * + * @param container + * Recent directories written to preferences + * @param errorMessageOut + * Container error information if false returned + * @return True if successful, false if error. + */ +bool +CaretPreferences::writeRecentDirectories(RecentFileItemsContainer* container, + AString& errorMessageOut) +{ + container->removeItemsExceedingMaximumNumber(getRecentMaximumNumberOfDirectories()); + + return writeRecentFileItemsContainer(NAME_RECENT_DIRECTORIES, + container, + errorMessageOut); +} + +/** + * Add a directory (or parent directory of file) to the recent directories. If the given directory/file is a file, the files parent + * directory is added to the recent directories. If the parameter is neither a valid file nor valid directory, + * no action is taken. + * + * @param directoryOrFileName + * Name of directory or file. + */ +void +CaretPreferences::addToRecentDirectories(const AString& directoryOrFileName) +{ + /* + * Only update recents directories if GUI application (wb_view) + */ + if (ApplicationInformation().getApplicationType() != ApplicationTypeEnum::APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE) { + return; + } + + if (getRecentMaximumNumberOfDirectories() <= 0) { + return; + } + + if (isInRecentFilesExclusionPaths(directoryOrFileName)) { + return; + } + + AString directoryName(directoryOrFileName); + + if (directoryName.isEmpty()) { + return; + } + + const AString errorPrefix("Error updating recent directories "); + + FileInformation fileInfo(directoryName); + if ( ! fileInfo.isDirectory()) { + if (fileInfo.isFile()) { + directoryName = fileInfo.getAbsolutePath(); + + if (directoryName.isEmpty()) { + CaretLogSevere(errorPrefix + + " unable to determine path for file: " + + directoryName); + return; + } + } + else { + CaretLogSevere(errorPrefix + + " not a valid directory nor file: " + + directoryName); + return; + } + } + + std::unique_ptr container(RecentFileItemsContainer::newInstance()); + CaretAssert(container); + + AString errorMessage; + bool successFlag(readRecentDirectories(container.get(), + errorMessage)); + if ( ! successFlag) { + CaretLogSevere(errorPrefix + + " during reading: " + + errorMessage); + return; + } + + RecentFileItem* newItem = new RecentFileItem(RecentFileItemTypeEnum::DIRECTORY, + directoryName); + newItem->setLastAccessByWorkbenchDateTimeToCurrentDateTime(); + container->addItem(newItem); + + successFlag = writeRecentDirectories(container.get(), + errorMessage); + if ( ! successFlag) { + CaretLogSevere(errorPrefix + + " during writing: " + + errorMessage); + return; + } +} + +/** + * Get the recent directories for use in the Open Data File Dialog's "history". + * @param favoritesFirstFlag + * If true, any directories that are in user's favorites are listed first. + * @param directoriesOut + * Output containing the recent directories + */ +void +CaretPreferences::getRecentDirectoriesForOpenFileDialogHistory(const bool favoritesFirstFlag, + std::vector& directoriesOut) +{ + directoriesOut.clear(); + + std::unique_ptr dirsContainer(RecentFileItemsContainer::newInstanceRecentDirectories(this, + RecentFileItemsContainer::WriteIfModifiedType::WRITE_NO)); + RecentFileItemsFilter filter; + filter.setListDirectories(true); + std::vector items = dirsContainer->getItems(filter); + RecentFileItemsContainer::sort(RecentFileItemSortingKeyEnum::DATE_NEWEST, + items); + + std::vector notFavs; + for (auto rfi : items) { + if (favoritesFirstFlag + && rfi->isFavorite()) { + directoriesOut.push_back(rfi->getPathAndFileName()); + } + else { + notFavs.push_back(rfi->getPathAndFileName()); + } + } + directoriesOut.insert(directoriesOut.end(), + notFavs.begin(), + notFavs.end()); +} + +/** + * @return Maximum number of recent files for open recent files dialog + */ +int32_t +CaretPreferences::getRecentMaximumNumberOfSceneAndSpecFiles() const +{ + return m_recentMaximumNumberOfSceneAndSpecFilesPreference->getValue().toInt(); +} + +/** + * Set maximum number of recent files for open recent files dialog + * @param maximumNumberOfFiles + * New maximum number of files + */ +void +CaretPreferences::setRecentMaximumNumberOfSceneAndSpecFiles(const int32_t maximumNumberOfFiles) +{ + m_recentMaximumNumberOfSceneAndSpecFilesPreference->setValue(maximumNumberOfFiles); +} + +/** + * Clear the recent files + * @param removeFavoritesFlag + * If true remove any favorites, else keep them. + */ +void +CaretPreferences::clearRecentSceneAndSpecFiles(const bool removeFavoritesFlag) +{ + const AString errorPrefix("Error updating recent directories "); + + std::unique_ptr container(RecentFileItemsContainer::newInstance()); + AString errorMessage; + bool successFlag(readRecentSceneAndSpecFiles(container.get(), + errorMessage)); + if ( ! successFlag) { + CaretLogSevere(errorPrefix + + " during reading: " + + errorMessage); + return; + } + + if (removeFavoritesFlag) { + container->removeAllItemsIncludingFavorites(); + } + else { + container->removeAllItemsExcludingFavorites(); + } + + successFlag = writeRecentSceneAndSpecFiles(container.get(), + errorMessage); + if ( ! successFlag) { + CaretLogSevere(errorPrefix + + " during writing: " + + errorMessage); + return; + } +} + +/** + * @return Maximum number of recent directories for open recent files dialog + */ +int32_t +CaretPreferences::getRecentMaximumNumberOfDirectories() const +{ + return m_recentMaximumNumberOfDirectoriesPreferences->getValue().toInt(); +} + +/** + * Set maximum number of recent directories for open recent files dialog + * @param maximumNumberOfDirectories + * New maximum number of directories + */ +void CaretPreferences::setRecentMaximumNumberOfDirectories(const int32_t maximumNumberOfDirectories) +{ + m_recentMaximumNumberOfDirectoriesPreferences->setValue(maximumNumberOfDirectories); +} + +/** + * Clear the recent directories + * @param removeFavoritesFlag + * If true remove any favorites, else keep them. + */ +void +CaretPreferences::clearRecentDirectories(const bool removeFavoritesFlag) +{ + const AString errorPrefix("Error updating recent directories "); + + std::unique_ptr container(RecentFileItemsContainer::newInstance()); + AString errorMessage; + bool successFlag(readRecentDirectories(container.get(), + errorMessage)); + if ( ! successFlag) { + CaretLogSevere(errorPrefix + + " during reading: " + + errorMessage); + return; + } + + if (removeFavoritesFlag) { + container->removeAllItemsIncludingFavorites(); + } + else { + container->removeAllItemsExcludingFavorites(); + } + + successFlag = writeRecentDirectories(container.get(), + errorMessage); + if ( ! successFlag) { + CaretLogSevere(errorPrefix + + " during writing: " + + errorMessage); + return; + } +} + +/** + * @return Mode for accessing the file system for recent files/directories file info (last modified) + */ +RecentFilesSystemAccessModeEnum::Enum +CaretPreferences::getRecentFilesSystemAccessMode() const +{ + bool validFlag(false); + RecentFilesSystemAccessModeEnum::Enum mode = RecentFilesSystemAccessModeEnum::fromName(m_recentFilesSystemAccessMode->getValue().toString(), + &validFlag); + return mode; +} + +/** + * Set mode for accessing the file system for recent files/directories file info (last modified) + * @param filesSystemAccessMode + * New mode + */ +void +CaretPreferences::setRecentFilesSystemAccessMode(const RecentFilesSystemAccessModeEnum::Enum filesSystemAccessMode) +{ + m_recentFilesSystemAccessMode->setValue(RecentFilesSystemAccessModeEnum::toName(filesSystemAccessMode)); +} + +/** + * Get the recent files exclusion paths + * @param exclusionPathsOut + * Output containing the exclusion paths + */ +void +CaretPreferences::readRecentFilesExclusionPaths(std::set& exclusionPathsOut) +{ + exclusionPathsOut.clear(); + + std::unique_ptr container(RecentFileItemsContainer::newInstance()); + AString errorMessage; + const bool resultFlag = readRecentFileItemsContainer(NAME_RECENT_EXCLUSION_PATHS, + container.get(), + errorMessage); + if (resultFlag) { + RecentFileItemsFilter filter; + filter.setListDirectories(true); + std::vector items = container->getItems(filter); + for (auto p : items) { + exclusionPathsOut.insert(p->getPathAndFileName()); + } + } + else { + CaretLogWarning("Reading recent files exclusion paths: " + + errorMessage); + } +} + +/** + * Set the recent files exclusion paths + * @param exclusionPathsOut + * Exclusion paths saved into preferences + */ +void +CaretPreferences::writeRecentFilesExclusionPaths(const std::set& exclusionPaths) +{ + std::unique_ptr container(RecentFileItemsContainer::newInstance()); + for (auto p : exclusionPaths) { + RecentFileItem* item = new RecentFileItem(RecentFileItemTypeEnum::DIRECTORY, + p); + container->addItem(item); + } + AString errorMessage; + const bool resultFlag = writeRecentFileItemsContainer(NAME_RECENT_EXCLUSION_PATHS, + container.get(), + errorMessage); + if ( ! resultFlag) { + CaretLogWarning("Writing recent files exclusion paths: " + + errorMessage); + } +} + +/** + * Add a path to the recent files exclusion paths + * @param exclusionPath + * Path to add + */ +void +CaretPreferences::addToRecentFilesExclusionPaths(const AString& exclusionPath) +{ + std::set paths; + readRecentFilesExclusionPaths(paths); + paths.insert(exclusionPath); + writeRecentFilesExclusionPaths(paths); +} + +/** + * Remove a path to the recent files exclusion paths + * @param exclusionPath + * Path to remove + */ +void +CaretPreferences::removeFromRecentFilesExclusionPaths(const AString& exclusionPath) +{ + std::set paths; + readRecentFilesExclusionPaths(paths); + paths.erase(exclusionPath); + writeRecentFilesExclusionPaths(paths); +} + +/** + * @return True if the given file/directory is in a recent files exclusion path + * @param fileOrDirectoryName + * Name of file or directory + */ +bool +CaretPreferences::isInRecentFilesExclusionPaths(const AString& fileOrDirectoryName) +{ + if (fileOrDirectoryName.isEmpty()) { + return false; + } + + std::set exclusionPaths; + readRecentFilesExclusionPaths(exclusionPaths); + if (exclusionPaths.empty()) { + return false; + } + + const AString directoryName = FileInformation(fileOrDirectoryName).getAbsolutePath(); + + for (auto ep : exclusionPaths) { + if (directoryName.startsWith(ep)) { + return true; + } + } + + return false; +} + +/** + * Get the key in QSettings for a palette with the given name + * @param paletteName + * Name of palette + * @return + * Key in the form value-of-NAME_PALETTE_GROUP_KEY/value-of-paletteName + * eg: palette/PSYCH + */ +AString +CaretPreferences::getPaletteKey(const AString& paletteName) const +{ + const QString name(paletteName.trimmed()); + if (name.isEmpty()) { + CaretLogSevere("Empty palette name for generation of preferences key"); + return ""; + } + + return (NAME_PALETTE_GROUP_KEY + + "/" + + name); +} + + +/** + * Test to verify a palette with the given name exists + * @param paletteName + * Name of palette + * @return True if palette exists, else false. + */ +bool +CaretPreferences::paletteUserCustomExists(const AString& paletteName) +{ + return this->qSettings->contains(getPaletteKey(paletteName)); +} + + +/** + * Get the XML representation of all user custom palettes + * @param palettesXmlOut + * XML text for all palettes + */ +void +CaretPreferences::paletteUserCustomGetAll(std::vector& palettesXmlOut) +{ + palettesXmlOut.clear(); + + /* + * Get keys in palette group. These keys will be just + * the name of the palette since the child keys are requested + * while the palette group is active + */ + this->qSettings->beginGroup(NAME_PALETTE_GROUP_KEY); + QStringList paletteKeyNames = this->qSettings->childKeys(); + this->qSettings->endGroup(); + + QStringListIterator iter(paletteKeyNames); + while (iter.hasNext()) { + AString paletteXML; + if (paletteUserCustomGetByName(iter.next(), + paletteXML)) { + palettesXmlOut.push_back(paletteXML); + } + } +} + +/** + * Get a user custom palette XML representation by name of palette + * @param paletteName + * Name of palette + * @param paletteXmlOut + * XML representation of palette + * @return True if palette exists and output xml is valid, else false. + */ +bool +CaretPreferences::paletteUserCustomGetByName(const AString& paletteName, + AString& paletteXmlOut) +{ + paletteXmlOut.clear(); + + const QString keyName = getPaletteKey(paletteName); + if ( ! keyName.isEmpty()) { + QString paletteXML = this->qSettings->value(keyName, "").toString(); + if (paletteXML.isEmpty()) { + CaretLogSevere("Palette XML is empty in Preferences for " + + keyName); + } + else { + paletteXmlOut = paletteXML; + return true; + } + } + + return false; +} + +/** + * Add a user custom palette XML representation by name of palette. + * Palette names must be unique and attempting to add a palette whose name + * matches the name of an existing palette will fail. + * @param paletteName + * Name of palette + * @param paletteXml + * XML representation of palette + * @param errorMessageOut + * Output with error message if adding palette fails. + * @return True if palette was added, else false (palette with name exists) + */ +bool +CaretPreferences::paletteUserCustomAdd(const AString& paletteName, + const AString& paletteXML, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + if (paletteName.trimmed().isEmpty()) { + errorMessageOut = ("Attempting to add palette with empty name."); + return false; + } + + if (paletteUserCustomExists(paletteName)) { + errorMessageOut = ("Unable to add palette named \"" + + paletteName + + "\". Palette with that names exists and palette " + "names must be unique."); + return false; + } + + if (paletteXML.trimmed().isEmpty()) { + errorMessageOut = ("Unable to add palette with name \"" + + paletteName + + "\". Palette XML is empty."); + return false; + } + + this->qSettings->setValue(getPaletteKey(paletteName), + paletteXML); + return true; +} + +/** + * Replace a user custom palette. + * A palette with the given name must exist or an error will occur. + * @param paletteName + * Name of palette + * @param paletteXml + * XML representation of palette + * @param errorMessageOut + * Output with error message if replacing palette fails. + * @return True if palette was replaced, else false (palette with name does not exist) + */ +bool +CaretPreferences::paletteUserCustomReplace(const AString& paletteName, + const AString& paletteXML, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + if (paletteName.trimmed().isEmpty()) { + errorMessageOut = ("Attempting to replace palette with empty name."); + return false; + } + + if ( ! paletteUserCustomExists(paletteName)) { + errorMessageOut = ("Unable to replace palette named \"" + + paletteName + + "\". Palette with that names does not exist."); + return false; + } + + if (paletteXML.trimmed().isEmpty()) { + errorMessageOut = ("Unable to replace palette with name \"" + + paletteName + + "\". Palette XML is empty."); + return false; + } + + this->qSettings->setValue(getPaletteKey(paletteName), + paletteXML); + return true; +} + +/** + * Remove a user custom palette. + * A palette with the given name must exist or an error will occur. + * @param paletteName + * Name of palette + * @param errorMessageOut + * Output with error message if removing palette fails. + * @return True if palette was removed, else false (palette with name does not exist) + */ +bool +CaretPreferences::paletteUserCustomRemove(const AString& paletteName, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + if (paletteName.trimmed().isEmpty()) { + errorMessageOut = ("Attempting to remove palette with empty name."); + return false; + } + + if ( ! paletteUserCustomExists(paletteName)) { + errorMessageOut = ("Unable to remove palette named \"" + + paletteName + + "\". Palette with that names does not exist."); + return false; + } + + this->qSettings->remove(getPaletteKey(paletteName)); + + return true; +} + +/** + * Rename a user custom palette. + * A palette with the given name must exist or an error will occur. + * @param paletteName + * Name of palette + * @param newPaletteName + * New name of palette + * @param paletteXML + * XML for palette with new name + * @param errorMessageOut + * Output with error message if renaming palette fails. + * @return True if palette was renamed, else false (palette with name does not exist) + */ +bool +CaretPreferences::paletteUserCustomRename(const AString& paletteName, + const AString& newPaletteName, + const AString& paletteXML, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + errorMessageOut.clear(); + + if (paletteName.trimmed().isEmpty()) { + errorMessageOut = ("Attempting to replace palette but old name is empty"); + return false; + } + if (newPaletteName.trimmed().isEmpty()) { + errorMessageOut = ("Attempting to replace palette but new name is empty."); + return false; + } + + if (paletteXML.trimmed().isEmpty()) { + errorMessageOut = ("Unable to rename palette from \"" + + paletteName + + "\" to \"" + + newPaletteName + + ". Palette XML is empty."); + return false; + } + + if ( ! paletteUserCustomExists(newPaletteName)) { + errorMessageOut = ("Unable to rename palette from \"" + + paletteName + + "\" to \"" + + newPaletteName + + ". Palette with new name exists and palette names " + "must be unique."); + return false; + } + + this->qSettings->remove(getPaletteKey(paletteXML)); + + this->qSettings->setValue(getPaletteKey(newPaletteName), + paletteXML); + + return true; +} diff -Nru connectome-workbench-1.4.2/src/Common/CaretPreferences.h connectome-workbench-1.5.0/src/Common/CaretPreferences.h --- connectome-workbench-1.4.2/src/Common/CaretPreferences.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretPreferences.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,15 +22,20 @@ /*LICENSE_END*/ #include +#include #include #include "BackgroundAndForegroundColors.h" #include "BackgroundAndForegroundColorsModeEnum.h" #include "CaretObject.h" -#include "LogLevelEnum.h" +#include "FileOpenFromOpSysTypeEnum.h" +#include "IdentificationDisplayModeEnum.h" #include "ImageCaptureMethodEnum.h" +#include "LogLevelEnum.h" #include "OpenGLDrawingMethodEnum.h" +#include "RecentFilesSystemAccessModeEnum.h" #include "SpecFileDialogViewFilesTypeEnum.h" +#include "ToolBarWidthModeEnum.h" #include "VolumeSliceViewAllPlanesLayoutEnum.h" class QSettings; @@ -40,7 +45,8 @@ class CaretPreferenceDataValue; class ModelTransform; - class TileTabsConfiguration; + class RecentFileItemsContainer; + class TileTabsLayoutBaseConfiguration; class WuQMacroGroup; class CaretPreferences : public CaretObject { @@ -61,24 +67,47 @@ void setSceneBackgroundAndForegroundColors(const BackgroundAndForegroundColors& colors); void setBackgroundAndForegroundColorsMode(const BackgroundAndForegroundColorsModeEnum::Enum colorsMode); + + void addToRecentFilesAndOrDirectories(const AString& directoryOrFileName); + + bool readRecentSceneAndSpecFiles(RecentFileItemsContainer* container, + AString& errorMessageOut); + + bool writeRecentSceneAndSpecFiles(RecentFileItemsContainer* container, + AString& errorMessageOut); + + bool readRecentDirectories(RecentFileItemsContainer* container, + AString& errorMessageOut); + + bool writeRecentDirectories(RecentFileItemsContainer* container, + AString& errorMessageOut); - void getPreviousSpecFiles(std::vector& previousSpecFiles) const; + void getRecentDirectoriesForOpenFileDialogHistory(const bool favoritesFirstFlag, + std::vector& directoriesOut); - void addToPreviousSpecFiles(const AString& specFileName); + int32_t getRecentMaximumNumberOfSceneAndSpecFiles() const; - void clearPreviousSpecFiles(); + void setRecentMaximumNumberOfSceneAndSpecFiles(const int32_t maximumNumberOfFiles); - void getPreviousSceneFiles(std::vector& previousSceneFiles) const; + void clearRecentSceneAndSpecFiles(const bool removeFavoritesFlag); - void addToPreviousSceneFiles(const AString& specFileName); + int32_t getRecentMaximumNumberOfDirectories() const; - void clearPreviousSceneFiles(); + void setRecentMaximumNumberOfDirectories(const int32_t maximumNumberOfDirectories); - void getPreviousOpenFileDirectories(std::vector& previousOpenFileDirectories) const; + void clearRecentDirectories(const bool removeFavoritesFlag); + + RecentFilesSystemAccessModeEnum::Enum getRecentFilesSystemAccessMode() const; + + void setRecentFilesSystemAccessMode(const RecentFilesSystemAccessModeEnum::Enum filesSystemAccessMode); + + void readRecentFilesExclusionPaths(std::set& exclusionPathsOut); - void getPreviousOpenFileDirectories(QStringList& previousOpenFileDirectories) const; + void writeRecentFilesExclusionPaths(const std::set& exclusionPaths); - void addToPreviousOpenFileDirectories(const AString& directoryName); + void addToRecentFilesExclusionPaths(const AString& exclusionPath); + + void removeFromRecentFilesExclusionPaths(const AString& exclusionPath); LogLevelEnum::Enum getLoggingLevel() const; @@ -92,6 +121,18 @@ void setOpenGLDrawingMethod(const OpenGLDrawingMethodEnum::Enum openGLDrawingMethod); + ToolBarWidthModeEnum::Enum getToolBarWidthMode() const; + + void setToolBarWidthMode(const ToolBarWidthModeEnum::Enum widthMode); + + FileOpenFromOpSysTypeEnum::Enum getFileOpenFromOpSysType() const; + + void setFileOpenFromOpSysType(const FileOpenFromOpSysTypeEnum::Enum openType); + + IdentificationDisplayModeEnum::Enum getIdentificationDisplayMode() const; + + void setIdentificationDisplayMode(const IdentificationDisplayModeEnum::Enum identificationDisplayMode); + VolumeSliceViewAllPlanesLayoutEnum::Enum getVolumeAllSlicePlanesLayout() const; void setVolumeAllSlicePlanesLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum allViewLayout); @@ -136,21 +177,29 @@ void setShowDataToolTipsEnabled(const bool enabled); - void readTileTabsConfigurations(const bool performSync = true); + void readTileTabsUserConfigurations(const bool performSync = true); + + std::vector> getTileTabsUserConfigurationsNamesAndUniqueIdentifiers(const bool includeManualConfigurationsFlag) const; - std::vector getTileTabsConfigurationsSortedByName() const; + std::unique_ptr getCopyOfTileTabsUserConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) const; - TileTabsConfiguration* getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier); + TileTabsLayoutBaseConfiguration* getTileTabsUserConfigurationByName(const AString& name) const; - const TileTabsConfiguration* getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) const; + void addTileTabsUserConfiguration(const TileTabsLayoutBaseConfiguration* tileTabsConfiguration, + const AString& configurationName); - TileTabsConfiguration* getTileTabsConfigurationByName(const AString& name) const; + bool replaceTileTabsUserConfiguration(const AString& replaceUserTileTabsUniqueIdentifier, + const TileTabsLayoutBaseConfiguration* replaceWithConfiguration, + AString& errorMessageOut); - void addTileTabsConfiguration(TileTabsConfiguration* tileTabsConfiguration); + bool renameTileTabsUserConfiguration(const QString& tileTabsUniqueIdentifier, + const AString& name, + AString& errorMessageOut); - void removeTileTabsConfigurationByUniqueIdentifier(const AString& tileTabsUniqueIdentifier); + bool removeTileTabsUserConfigurationByUniqueIdentifier(const AString& tileTabsUniqueIdentifier, + AString& errorMessageOut); - void writeTileTabsConfigurations(); + void writeTileTabsUserConfigurations(); void readCustomViews(const bool performSync = true); @@ -206,6 +255,10 @@ void setDynamicConnectivityDefaultedOn(const bool defaultedOn); + bool isGuiGesturesEnabled() const; + + void setGuiGesturesEnabled(const bool status); + WuQMacroGroup* getMacros(); const WuQMacroGroup* getMacros() const; @@ -218,6 +271,29 @@ std::vector getPreferenceSceneDataValues(); + bool paletteUserCustomExists(const AString& paletteName); + + void paletteUserCustomGetAll(std::vector& palettesXmlOut); + + bool paletteUserCustomGetByName(const AString& paletteName, + AString& paletteXmlOut); + + bool paletteUserCustomAdd(const AString& paletteName, + const AString& paletteXML, + AString& errorMessageOut); + + bool paletteUserCustomReplace(const AString& paletteName, + const AString& paletteXML, + AString& errorMessageOut); + + bool paletteUserCustomRemove(const AString& paletteName, + AString& errorMessageOut); + + bool paletteUserCustomRename(const AString& paletteName, + const AString& newPaletteName, + const AString& paletteXML, + AString& errorMessageOut); + private: CaretPreferences(const CaretPreferences&); @@ -227,6 +303,10 @@ virtual AString toString() const; private: + void addToRecentSceneAndSpecFiles(const AString& filename); + + void addToRecentDirectories(const AString& directoryOrFileName); + bool getBoolean(const AString& name, const bool defaultValue = false); @@ -245,9 +325,6 @@ void setString(const AString& name, const AString& value); - void addToPrevious(std::vector& previousVector, - const AString& newName); - void readUnsignedByteArray(const AString& name, uint8_t array[], const int32_t numberOfElements); @@ -264,6 +341,18 @@ void writeCustomViews(); + AString getPaletteKey(const AString& paletteName) const; + + bool readRecentFileItemsContainer(const AString& preferenceName, + RecentFileItemsContainer* container, + AString& errorMessageOut); + + bool writeRecentFileItemsContainer(const AString& preferenceName, + const RecentFileItemsContainer* container, + AString& errorMessageOut); + + bool isInRecentFilesExclusionPaths(const AString& fileOrDirectoryName); + mutable QSettings* qSettings; BackgroundAndForegroundColors userColors; @@ -277,8 +366,6 @@ std::vector previousSceneFiles; - std::vector previousOpenFileDirectories; - LogLevelEnum::Enum loggingLevel; ImageCaptureMethodEnum::Enum imageCaptureMethod; @@ -287,7 +374,7 @@ std::vector customViews; - std::vector tileTabsConfigurations; + std::vector tileTabsConfigurations; bool displayVolumeAxesCrosshairs; @@ -303,7 +390,21 @@ std::unique_ptr m_volumeCrossHairGapPreference; - std::vector m_preferenceDataValues; + std::unique_ptr m_guiGesturesEnabled; + + std::unique_ptr m_toolBarWidthModePreference; + + std::unique_ptr m_identificationDisplayModePreference; + + std::unique_ptr m_fileOpenFromOperatingSystemTypePreference; + + std::vector m_preferenceStoredInSceneDataValues; + + std::unique_ptr m_recentMaximumNumberOfSceneAndSpecFilesPreference; + + std::unique_ptr m_recentMaximumNumberOfDirectoriesPreferences; + + std::unique_ptr m_recentFilesSystemAccessMode; bool splashScreenEnabled; @@ -348,12 +449,15 @@ static const AString NAME_COLOR_FOREGROUND_ALL; static const AString NAME_COLOR_BACKGROUND_CHART; static const AString NAME_COLOR_FOREGROUND_CHART; + static const AString NAME_COLOR_BACKGROUND_MEDIA; + static const AString NAME_COLOR_FOREGROUND_MEDIA; static const AString NAME_COLOR_BACKGROUND_SURFACE; static const AString NAME_COLOR_FOREGROUND_SURFACE; static const AString NAME_COLOR_BACKGROUND_VOLUME; static const AString NAME_COLOR_FOREGROUND_VOLUME; static const AString NAME_COLOR_CHART_MATRIX_GRID_LINES; static const AString NAME_COLOR_CHART_HISTOGRAM_THRESHOLD; + static const AString NAME_CUSTOM_VIEWS; static const AString NAME_DEVELOP_MENU; static const AString NAME_DATA_TOOL_TIPS; static const AString NAME_DYNAMIC_CONNECTIVITY_ON; @@ -362,11 +466,14 @@ static const AString NAME_MACROS; static const AString NAME_MANAGE_FILES_VIEW_FILE_TYPE; static const AString NAME_OPENGL_DRAWING_METHOD; + static const AString NAME_PALETTE_GROUP_KEY; static const AString NAME_PREVIOUS_SCENE_FILES; static const AString NAME_PREVIOUS_SPEC_FILES; static const AString NAME_PREVIOUS_OPEN_FILE_DIRECTORIES; static const AString NAME_SPLASH_SCREEN; - static const AString NAME_CUSTOM_VIEWS; + static const AString NAME_RECENT_DIRECTORIES; + static const AString NAME_RECENT_EXCLUSION_PATHS; + static const AString NAME_RECENT_SCENE_AND_SPEC_FILES; static const AString NAME_REMOTE_FILE_USER_NAME; static const AString NAME_REMOTE_FILE_PASSWORD; static const AString NAME_REMOTE_FILE_LOGIN_SAVED; @@ -395,12 +502,15 @@ const AString CaretPreferences::NAME_COLOR_FOREGROUND_ALL = "colorForegroundAll"; const AString CaretPreferences::NAME_COLOR_BACKGROUND_CHART = "colorBackgroundChart"; const AString CaretPreferences::NAME_COLOR_FOREGROUND_CHART = "colorForegroundChart"; + const AString CaretPreferences::NAME_COLOR_BACKGROUND_MEDIA = "colorBackgroundMedia"; + const AString CaretPreferences::NAME_COLOR_FOREGROUND_MEDIA = "colorForegroundMedia"; const AString CaretPreferences::NAME_COLOR_BACKGROUND_SURFACE = "colorBackgroundSurface"; const AString CaretPreferences::NAME_COLOR_FOREGROUND_SURFACE = "colorForegroundSurface"; const AString CaretPreferences::NAME_COLOR_BACKGROUND_VOLUME = "colorBackgroundVolume"; const AString CaretPreferences::NAME_COLOR_FOREGROUND_VOLUME = "colorForegroundVolume"; const AString CaretPreferences::NAME_COLOR_CHART_MATRIX_GRID_LINES = "colorChartMatrixGridLines"; const AString CaretPreferences::NAME_COLOR_CHART_HISTOGRAM_THRESHOLD = "colorChartHistogramThreshold"; + const AString CaretPreferences::NAME_CUSTOM_VIEWS = "customViews"; const AString CaretPreferences::NAME_DEVELOP_MENU = "developMenu"; const AString CaretPreferences::NAME_DATA_TOOL_TIPS = "dataToolTips"; const AString CaretPreferences::NAME_DYNAMIC_CONNECTIVITY_ON = "dynamicConnectivityDefaultedOn"; @@ -409,11 +519,14 @@ const AString CaretPreferences::NAME_MACROS = "macros"; const AString CaretPreferences::NAME_MANAGE_FILES_VIEW_FILE_TYPE = "manageFilesViewFileType"; const AString CaretPreferences::NAME_OPENGL_DRAWING_METHOD = "openGLDrawingMethod"; + const AString CaretPreferences::NAME_PALETTE_GROUP_KEY = "palette"; const AString CaretPreferences::NAME_PREVIOUS_SCENE_FILES = "previousSceneFiles"; const AString CaretPreferences::NAME_PREVIOUS_SPEC_FILES = "previousSpecFiles"; const AString CaretPreferences::NAME_PREVIOUS_OPEN_FILE_DIRECTORIES = "previousOpenFileDirectories"; const AString CaretPreferences::NAME_SPLASH_SCREEN = "splashScreen"; - const AString CaretPreferences::NAME_CUSTOM_VIEWS = "customViews"; + const AString CaretPreferences::NAME_RECENT_DIRECTORIES = "recentDirectories"; + const AString CaretPreferences::NAME_RECENT_EXCLUSION_PATHS = "recentExclusionPaths"; + const AString CaretPreferences::NAME_RECENT_SCENE_AND_SPEC_FILES = "recentSceneAndSpecFiles"; const AString CaretPreferences::NAME_REMOTE_FILE_USER_NAME = "remoteFileUserName"; const AString CaretPreferences::NAME_REMOTE_FILE_PASSWORD = "remoteFilePassword"; const AString CaretPreferences::NAME_REMOTE_FILE_LOGIN_SAVED = "removeFileLoginSaved"; diff -Nru connectome-workbench-1.4.2/src/Common/CaretResult.cxx connectome-workbench-1.5.0/src/Common/CaretResult.cxx --- connectome-workbench-1.4.2/src/Common/CaretResult.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretResult.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,233 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CARET_RESULT_DECLARE__ +#include "CaretResult.h" +#undef __CARET_RESULT_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::CaretResult + * \brief Contains an error status and an error description that are returned from functions/methods. + * \ingroup Common + * + * This class helps with returning an error status and description from functions/methods. + * For new instances, it is best to use the static method to create the instance. These static + * methods create a unique pointer for the instance that can prevent copy operations and + * will destroy the instance. + * + * The design is loosely based upon Qt's Q*Error classes. + */ + +/** + * @return A unique pointer that points to a new instance of CaretResult + * that indicates success (NO_ERROR) + */ +std::unique_ptr +CaretResult::newInstanceSuccess() +{ + std::unique_ptr ptr(new CaretResult()); + return ptr; +} + +/** + * @return New instance containing error status code GENERIC_ERROR and description + * @param errorDescription + * The description of the error + */ + +std::unique_ptr +CaretResult::newInstanceError(const AString& errorDescription) +{ + std::unique_ptr ptr(new CaretResult(ErrorStatusCode::GENERIC_ERROR, + errorDescription)); + return ptr; +} + +/** + * @return New instance containing the given error status code and description + * @param errorStatusCode + * The error status + * @param errorDescription + * The description of the error + */ + +std::unique_ptr +CaretResult::newInstanceError(const ErrorStatusCode errorStatusCode, + const AString& errorDescription) +{ + std::unique_ptr ptr(new CaretResult(errorStatusCode, + errorDescription)); + return ptr; +} + +/** + * Constructor that creates instance with a NO_ERROR status code (indicates success). + */ +CaretResult::CaretResult() +: CaretObject(), +m_errorStatusCode(NO_ERROR) +{ +} + +/** + * Constructor that creates instance with the given error status code and description + * @param errorStatusCode + * The error status + * @param errorDescription + * The description of the error + */ +CaretResult::CaretResult(const ErrorStatusCode errorStatusCode, + const AString& errorDescription) +: CaretObject(), +m_errorStatusCode(errorStatusCode), +m_errorDescription(errorDescription) +{ +} + +/** + * Destructor. + */ +CaretResult::~CaretResult() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +CaretResult::CaretResult(const CaretResult& obj) +: CaretObject(obj) +{ + this->copyHelperCaretResult(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +CaretResult& +CaretResult::operator=(const CaretResult& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperCaretResult(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +CaretResult::copyHelperCaretResult(const CaretResult& obj) +{ + m_errorStatusCode = obj.m_errorStatusCode; + m_errorDescription = obj.m_errorDescription; +} + +/** + * Convert an error status code to its name + * @param errorStatusCode + * The error status code + * @return Name of an error status code + */ +AString +CaretResult::errorStatusCodeToName(const ErrorStatusCode errorStatusCode) +{ + AString name; + switch (errorStatusCode) { + case GENERIC_ERROR: + name = "Generic_Error"; + break; + case NO_ERROR: + name = "No_Error"; + break; + } + return name; +} + +/** + * @param The name of the error status code + */ +AString +CaretResult::getErrorStatusCodeName() const +{ + return errorStatusCodeToName(m_errorStatusCode); +} + +/** + * @return The error status code + */ +CaretResult::ErrorStatusCode +CaretResult::getErrorStatusCode() const +{ + return m_errorStatusCode; +} + +/** + * @return The error description + */ +AString +CaretResult::getErrorDescription() const +{ + return m_errorDescription; +} + +/** + * @return True if there is an error (the error status code is anything but NO_ERROR). + */ +bool +CaretResult::isError() const{ + return (m_errorStatusCode != ErrorStatusCode::NO_ERROR); +} + +/** + * @return True if successful (error status code is NO_ERROR) + */ +bool +CaretResult::isSuccess() const{ + return (m_errorStatusCode == ErrorStatusCode::NO_ERROR); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +CaretResult::toString() const +{ + return ("Error status code: " + + getErrorStatusCodeName() + + " Description: " + + getErrorDescription()); +} + diff -Nru connectome-workbench-1.4.2/src/Common/CaretResult.h connectome-workbench-1.5.0/src/Common/CaretResult.h --- connectome-workbench-1.4.2/src/Common/CaretResult.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretResult.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,98 @@ +#ifndef __CARET_RESULT_H__ +#define __CARET_RESULT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class CaretResult : public CaretObject { + + public: + /** + * Enumerated type for error status codes + * Additional error codes can be added but they must designate an error. + */ + enum ErrorStatusCode { + /* No Error, the only error code that indicates no error */ + NO_ERROR, + /* Generic error */ + GENERIC_ERROR + }; + + static std::unique_ptr newInstanceSuccess(); + + static std::unique_ptr newInstanceError(const AString& errorDescription); + + static std::unique_ptr newInstanceError(const ErrorStatusCode errorStatusCode, + const AString& errorDescription); + + CaretResult(); + + CaretResult(const ErrorStatusCode errorStatusCode, + const AString& errorDescription); + + virtual ~CaretResult(); + + CaretResult(const CaretResult& obj); + + CaretResult& operator=(const CaretResult& obj); + + static AString errorStatusCodeToName(const ErrorStatusCode errorStatusCode); + + AString getErrorStatusCodeName() const; + + ErrorStatusCode getErrorStatusCode() const; + + AString getErrorDescription() const; + + bool isError() const; + + bool isSuccess() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperCaretResult(const CaretResult& obj); + + ErrorStatusCode m_errorStatusCode; + + AString m_errorDescription; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CARET_RESULT_DECLARE__ + // +#endif // __CARET_RESULT_DECLARE__ + +} // namespace +#endif //__CARET_RESULT_H__ diff -Nru connectome-workbench-1.4.2/src/Common/CaretRgb.cxx connectome-workbench-1.5.0/src/Common/CaretRgb.cxx --- connectome-workbench-1.4.2/src/Common/CaretRgb.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretRgb.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,128 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CARET_RGB_DECLARE__ +#include "CaretRgb.h" +#undef __CARET_RGB_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::CaretRgb + * \brief An RGB triplet using std::array + * \ingroup Common + */ + +/** + * Constructor that defaults to black + */ +CaretRgb::CaretRgb() +: CaretRgb(0, 0, 0) +{ +} + +/** + * Constructor with RGB triplet + * @param red + * Red component (0 to 255) + * @param green + * Green component (0 to 255) + * @param blue + * Blue component (0 to 255) + */ +CaretRgb::CaretRgb(const int32_t red, + const int32_t green, + const int32_t blue) +: std::array() +{ + (*this)[0] = red; + (*this)[1] = green; + (*this)[2] = blue; +} + +/** + * Destructor. + */ +CaretRgb::~CaretRgb() +{ +} + +///** +// * Copy constructor. +// * @param obj +// * Object that is copied. +// */ +//CaretRgb::CaretRgb(const CaretRgb& obj) +//: std::array(obj) +//{ +// this->copyHelperCaretRgb(obj); +//} +// +///** +// * Assignment operator. +// * @param obj +// * Data copied from obj to this. +// * @return +// * Reference to this object. +// */ +//CaretRgb& +//CaretRgb::operator=(const CaretRgb& obj) +//{ +// if (this != &obj) { +// std::array::operator=(obj); +// this->copyHelperCaretRgb(obj); +// } +// return *this; +//} + +///** +// * Helps with copying an object of this type. +// * @param obj +// * Object that is copied. +// */ +//void +//CaretRgb::copyHelperCaretRgb(const CaretRgb& obj) +//{ +// +//} + +///** +// * Equality operator. +// * @param obj +// * Instance compared to this for equality. +// * @return +// * True if this instance and 'obj' instance are considered equal. +// */ +//bool +//CaretRgb::operator==(const CaretRgb& obj) const +//{ +// if (this == &obj) { +// return true; +// } +// +// /* perform equality testing HERE and return true if equal ! */ +// +// return false; +//} + diff -Nru connectome-workbench-1.4.2/src/Common/CaretRgb.h connectome-workbench-1.5.0/src/Common/CaretRgb.h --- connectome-workbench-1.4.2/src/Common/CaretRgb.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CaretRgb.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,71 @@ +#ifndef __CARET_RGB_H__ +#define __CARET_RGB_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +namespace caret { + + class CaretRgb : public std::array { + + public: + CaretRgb(); + + CaretRgb(const int32_t red, + const int32_t green, + const int32_t blue); + + virtual ~CaretRgb(); + + /** @return The red component */ + int32_t red() const { return (*this)[0]; } /* could use "at(0)". at() does bounds testing and throws exception */ + + /** @return The green component */ + int32_t green() const { return (*this)[1]; } + + /** @return The blue component */ + int32_t blue() const { return (*this)[2]; } + +// CaretRgb(const CaretRgb& obj); + +// CaretRgb& operator=(const CaretRgb& obj); +// +// bool operator==(const CaretRgb& obj) const; + + + // ADD_NEW_METHODS_HERE + + private: +// void copyHelperCaretRgb(const CaretRgb& obj); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CARET_RGB_DECLARE__ + // +#endif // __CARET_RGB_DECLARE__ + +} // namespace +#endif //__CARET_RGB_H__ diff -Nru connectome-workbench-1.4.2/src/Common/CMakeLists.txt connectome-workbench-1.5.0/src/Common/CMakeLists.txt --- connectome-workbench-1.4.2/src/Common/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -58,11 +58,14 @@ BrainConstants.h ByteOrderEnum.h ByteSwapping.h +CardinalDirectionEnum.h CaretAssert.h CaretAssertion.h CaretBinaryFile.h +CaretColor.h CaretColorEnum.h CaretCommandLine.h +CaretCommandGlobalOptions.h CaretCompact3DLookup.h CaretCompactLookup.h CaretException.h @@ -79,10 +82,13 @@ CaretPointLocator.h CaretPreferenceDataValue.h CaretPreferences.h +CaretResult.h +CaretRgb.h CaretTemporaryFile.h CaretUndoCommand.h CaretUndoStack.h CaretUnitsTypeEnum.h +ColorFunctions.h ConnectivityCorrelation.h CubicSpline.h DataCompressZLib.h @@ -100,24 +106,32 @@ ElapsedTimer.h Event.h EventAlertUser.h +EventBrowserTabClose.h EventBrowserTabDelete.h EventBrowserTabIndicesGetAll.h +EventBrowserTabIndicesGetAllViewed.h EventBrowserTabNew.h EventBrowserTabNewClone.h +EventBrowserTabReopenClosed.h +EventBrowserTabSelectInWindow.h EventCaretPreferencesGet.h EventGetViewportSize.h EventListenerInterface.h EventManager.h EventPaletteGetByName.h EventProgressUpdate.h -EventTileTabsConfigurationModification.h +EventRecentFilesSystemAccessMode.h +EventTileTabsGridConfigurationModification.h EventTypeEnum.h FastStatistics.h FileAdapter.h FileInformation.h +FileOpenFromOpSysTypeEnum.h FloatMatrix.h Histogram.h HtmlStringBuilder.h +HtmlTableBuilder.h +IdentificationDisplayModeEnum.h ImageCaptureMethodEnum.h JsonHelper.h Logger.h @@ -128,6 +142,7 @@ LogRecord.h MathFunctionEnum.h MathFunctions.h +Matrix4x4Interface.h MatrixFunctions.h ModelTransform.h MultiDimArray.h @@ -143,6 +158,13 @@ ProgramParametersException.h ProgressObject.h ProgressReportingInterface.h +RecentFileItem.h +RecentFileItemSortingKeyEnum.h +RecentFileItemTypeEnum.h +RecentFileItemsContainer.h +RecentFileItemsContainerModeEnum.h +RecentFileItemsFilter.h +RecentFilesSystemAccessModeEnum.h ReductionEnum.h ReductionOperation.h SpacerTabIndex.h @@ -152,20 +174,23 @@ StringTableModel.h StructureEnum.h SystemUtilities.h -TileTabsConfiguration.h -TileTabsConfigurationLayoutTypeEnum.h -TileTabsBaseConfiguration.h -TileTabsGridLayoutConfiguration.h -TileTabsGridModeEnum.h +TileTabsBrowserTabGeometry.h +TileTabsLayoutBackgroundTypeEnum.h +TileTabsLayoutBaseConfiguration.h +TileTabsLayoutConfigurationTypeEnum.h +TileTabsLayoutGridConfiguration.h +TileTabsLayoutManualConfiguration.h TileTabsGridRowColumnContentTypeEnum.h TileTabsGridRowColumnElement.h TileTabsGridRowColumnStretchTypeEnum.h +ToolBarWidthModeEnum.h TracksModificationInterface.h TriStateSelectionStatusEnum.h Vector3D.h VectorOperation.h VolumeSliceViewAllPlanesLayoutEnum.h VoxelIJK.h +WorkbenchQtMessageHandler.h WorkbenchSpecialVersionEnum.h WuQMacro.h WuQMacroCommand.h @@ -198,10 +223,13 @@ BrainConstants.cxx ByteOrderEnum.cxx ByteSwapping.cxx +CardinalDirectionEnum.cxx CaretAssertion.cxx CaretBinaryFile.cxx +CaretColor.cxx CaretColorEnum.cxx CaretCommandLine.cxx +CaretCommandGlobalOptions.cxx CaretException.cxx CaretHttpManager.cxx CaretLogger.cxx @@ -211,10 +239,13 @@ CaretPointLocator.cxx CaretPreferenceDataValue.cxx CaretPreferences.cxx +CaretResult.cxx +CaretRgb.cxx CaretTemporaryFile.cxx CaretUndoCommand.cxx CaretUndoStack.cxx CaretUnitsTypeEnum.cxx +ColorFunctions.cxx ConnectivityCorrelation.cxx CubicSpline.cxx DataCompressZLib.cxx @@ -230,24 +261,32 @@ ElapsedTimer.cxx Event.cxx EventAlertUser.cxx +EventBrowserTabClose.cxx EventBrowserTabDelete.cxx EventBrowserTabIndicesGetAll.cxx +EventBrowserTabIndicesGetAllViewed.cxx EventBrowserTabNew.cxx EventBrowserTabNewClone.cxx +EventBrowserTabReopenClosed.cxx +EventBrowserTabSelectInWindow.cxx EventCaretPreferencesGet.cxx EventGetViewportSize.cxx EventListenerInterface.cxx EventManager.cxx EventPaletteGetByName.cxx EventProgressUpdate.cxx -EventTileTabsConfigurationModification.cxx +EventRecentFilesSystemAccessMode.cxx +EventTileTabsGridConfigurationModification.cxx EventTypeEnum.cxx FastStatistics.cxx FileAdapter.cxx FileInformation.cxx +FileOpenFromOpSysTypeEnum.cxx FloatMatrix.cxx Histogram.cxx HtmlStringBuilder.cxx +HtmlTableBuilder.cxx +IdentificationDisplayModeEnum.cxx ImageCaptureMethodEnum.cxx JsonHelper.cxx Logger.cxx @@ -268,6 +307,13 @@ ProgramParameters.cxx ProgramParametersException.cxx ProgressObject.cxx +RecentFileItem.cxx +RecentFileItemSortingKeyEnum.cxx +RecentFileItemTypeEnum.cxx +RecentFileItemsContainer.cxx +RecentFileItemsContainerModeEnum.cxx +RecentFileItemsFilter.cxx +RecentFilesSystemAccessModeEnum.cxx ReductionEnum.cxx ReductionOperation.cxx SpacerTabIndex.cxx @@ -277,18 +323,21 @@ StringTableModel.cxx StructureEnum.cxx SystemUtilities.cxx -TileTabsConfiguration.cxx -TileTabsConfigurationLayoutTypeEnum.cxx -TileTabsBaseConfiguration.cxx -TileTabsGridLayoutConfiguration.cxx -TileTabsGridModeEnum.cxx +TileTabsBrowserTabGeometry.cxx +TileTabsLayoutBackgroundTypeEnum.cxx +TileTabsLayoutBaseConfiguration.cxx +TileTabsLayoutConfigurationTypeEnum.cxx +TileTabsLayoutGridConfiguration.cxx +TileTabsLayoutManualConfiguration.cxx TileTabsGridRowColumnContentTypeEnum.cxx TileTabsGridRowColumnElement.cxx TileTabsGridRowColumnStretchTypeEnum.cxx +ToolBarWidthModeEnum.cxx TriStateSelectionStatusEnum.cxx Vector3D.cxx VectorOperation.cxx VolumeSliceViewAllPlanesLayoutEnum.cxx +WorkbenchQtMessageHandler.cxx WorkbenchSpecialVersionEnum.cxx WuQMacro.cxx WuQMacroCommand.cxx diff -Nru connectome-workbench-1.4.2/src/Common/ColorFunctions.cxx connectome-workbench-1.5.0/src/Common/ColorFunctions.cxx --- connectome-workbench-1.4.2/src/Common/ColorFunctions.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/ColorFunctions.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,200 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "ColorFunctions.h" + +#include + +using namespace std; +using namespace caret; + +namespace +{ + float sRGBinvGamma(const float nonlin) + {//https://en.wikipedia.org/wiki/SRGB + if (nonlin <= 0.04045f) + { + return 25 * nonlin / 323; + } else { + return pow((nonlin + 0.055) / 1.055f, 2.4); + } + } + + float Labf(const float t) + {//https://en.wikipedia.org/wiki/CIELAB_color_space + if (t > (6.0f / 29.0f) * (6.0f / 29.0f) * (6.0f / 29.0f))//assume this is more likely to be optimized out than pow() + { + return pow(t, 1.0f / 3.0f); + } else { + return t / (3.0f * (6.0f / 29.0f) * (6.0f / 29.0f)) + 4.0f / 29.0f; + } + } + + struct Vector2D + { + float m_vec[2]; + Vector2D() { m_vec[0] = 0.0f; m_vec[1] = 0.0f; } + Vector2D(const float* rhs) + { + m_vec[0] = rhs[0]; + m_vec[1] = rhs[1]; + } + Vector2D(const float x, const float y) + { + m_vec[0] = x; + m_vec[1] = y; + } + float length() const + { + return sqrt(m_vec[0] * m_vec[0] + m_vec[1] * m_vec[1]); + } + float dot(const Vector2D& rhs) const + { + return m_vec[0] * rhs.m_vec[0] + m_vec[1] * rhs.m_vec[1]; + } + Vector2D operator*(const float& rhs) const + { + Vector2D ret = *this; + ret.m_vec[0] *= rhs; + ret.m_vec[1] *= rhs; + return ret; + } + Vector2D operator/(const float& rhs) const + { + Vector2D ret = *this; + ret.m_vec[0] /= rhs; + ret.m_vec[1] /= rhs; + return ret; + } + Vector2D operator+(const Vector2D& rhs) const + { + Vector2D ret = *this; + ret.m_vec[0] += rhs.m_vec[0]; + ret.m_vec[1] += rhs.m_vec[1]; + return ret; + } + Vector2D operator-(const Vector2D& rhs) const + { + Vector2D ret = *this; + ret.m_vec[0] -= rhs.m_vec[0]; + ret.m_vec[1] -= rhs.m_vec[1]; + return ret; + } + Vector2D normal() const + { + return (*this) / length(); + } + Vector2D perpRot() const + { + return Vector2D(m_vec[1], -m_vec[0]); + } + }; + + //w3c uses CIE Lu'v' (NOT u*v*) to approximate atypical color vision ambiguity lines, so use that plus geometry + //and assume Lab space of the modified colors is still near-linear for atypical + //https://www.w3.org/Graphics/atypical-color-response + //protanope lines look wrong, and it doesn't cover change in lightness, find a different approach + + //does NOT do anomalous red darkness in protanopia + void confusionTransform(const float tritanLuv[3], float deficientLuv[3], const float confPoint[2], const float resolvStart[2], const float resolvEnd[2]) + { + deficientLuv[0] = tritanLuv[0]; + Vector2D tritanuv(tritanLuv[1], tritanLuv[2]); + Vector2D toconf(confPoint[0] - tritanLuv[1], confPoint[1] - tritanLuv[2]); + Vector2D tostart(resolvStart[0] - tritanLuv[1], resolvStart[1] - tritanLuv[2]); + Vector2D resolvdir(resolvEnd[0] - resolvStart[0], resolvEnd[1] - resolvStart[1]); + Vector2D resolvPerpHat = resolvdir.perpRot().normal(); + float distClosest = tostart.dot(resolvPerpHat); + float perpDistConf = toconf.dot(resolvPerpHat); + Vector2D result = tritanuv + toconf * distClosest / perpDistConf; + deficientLuv[1] = result.m_vec[0]; + deficientLuv[2] = result.m_vec[1]; + } + + //for now, leave color deficient transforms private, in case we want to change approaches + void deuteranopeXYZtransform(const float tritanXYZ[3], float deuterXYZOut[3]) + {//https://www.w3.org/Graphics/atypical-color-response + float tritanLuv[3], deuterLuv[3];//u'v', not u*v* + ColorFunctions::CIEXYZtoCIELuvPrime(tritanXYZ, tritanLuv); + float confusionuv[2] = {-4.75f, 1.31f}; + float ambiguvstart[2] = {0.26f, 0.02f};//uv line to move all ambiguous colors to, eyeballed + float ambiguvend[2] = {0.25f, 0.56f}; + confusionTransform(tritanLuv, deuterLuv, confusionuv, ambiguvstart, ambiguvend); + ColorFunctions::CIELuvPrimetoCIEXYZ(deuterLuv, deuterXYZOut); + } +} + +void ColorFunctions::sRGBtoCIEXYZ(const float sRGB[3], float xyzOut[3]) +{//https://en.wikipedia.org/wiki/SRGB + float rgblin[3]; + for (int i = 0; i < 3; ++i) rgblin[i] = sRGBinvGamma(sRGB[i]); + xyzOut[0] = 0.4124f * rgblin[0] + 0.3576f * rgblin[1] + 0.1805f * rgblin[2]; + xyzOut[1] = 0.2126f * rgblin[0] + 0.7152f * rgblin[1] + 0.0722f * rgblin[2]; + xyzOut[2] = 0.0193f * rgblin[0] + 0.1192f * rgblin[1] + 0.9505f * rgblin[2]; +} + +void ColorFunctions::CIEXYZtoCIELab(const float xyz[3], float LabOut[3]) +{//https://en.wikipedia.org/wiki/CIELAB_color_space + float fy = Labf(xyz[1] / 1.0f);//our Yn = 1, not 100 + LabOut[0] = 116.0f * fy - 16.0f; + LabOut[1] = 500.0f * (Labf(xyz[0] / 0.950489f) - fy);//let's use D65, i guess? + LabOut[2] = 200.0f * (fy - Labf(xyz[2] / 1.088840f));//and output Lab in the range of hundreds to make it harder to confuse with sRGB and XYZ? +} + +void ColorFunctions::CIEXYZtoCIELuvPrime(const float xyz[3], float LuvOut[3]) +{//https://en.wikipedia.org/wiki/CIELUV + if (xyz[1] <= (6.0f / 29.0f) * (6.0f / 29.0f) * (6.0f / 29.0f)) + { + LuvOut[0] = (29.0f / 3.0f) * (29.0f / 3.0f) * (29.0f / 3.0f) * xyz[1];//Yn = 1 + } else { + LuvOut[0] = 116.0f * pow(xyz[1], 1.0f / 3.0f) - 16; + } + float uvdenom = 1.0f / (xyz[0] + 15 * xyz[1] + 3 * xyz[2]); + LuvOut[1] = 4 * xyz[0] * uvdenom; + LuvOut[2] = 9 * xyz[1] * uvdenom;//yes, Z is only in the denominator +} + +void ColorFunctions::CIELuvPrimetoCIEXYZ (const float Luv[3], float xyzOut[3]) +{ + if (Luv[0] <= 8) + { + xyzOut[1] = Luv[0] * (3.0f / 29.0f) * (3.0f / 29.0f) * (3.0f / 29.0f);//Yn = 1 + } else { + xyzOut[1] = pow((Luv[0] + 16) / 116, 3); + } + xyzOut[0] = xyzOut[1] * 9 * Luv[1] / (4 * Luv[2]); + xyzOut[2] = xyzOut[1] * (12 - 3 * Luv[1] - 20 * Luv[2]) / (4 * Luv[2]); +} + +float ColorFunctions::perceptualDistanceSRGB(const float srgb1[3], const float srgb2[3]) +{ + float xyz1[3], xyz2[3], lab1[3], lab2[3]; + sRGBtoCIEXYZ(srgb1, xyz1); + sRGBtoCIEXYZ(srgb2, xyz2); + CIEXYZtoCIELab(xyz1, lab1); + CIEXYZtoCIELab(xyz2, lab2); + float accum = 0.0f;//TODO: CIE DE 2000? https://en.wikipedia.org/wiki/CIEDE2000 + for (int i = 0; i < 3; ++i) + { + float diff = lab1[i] - lab2[i]; + accum += diff * diff; + } + return sqrt(accum); +} diff -Nru connectome-workbench-1.4.2/src/Common/ColorFunctions.h connectome-workbench-1.5.0/src/Common/ColorFunctions.h --- connectome-workbench-1.4.2/src/Common/ColorFunctions.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/ColorFunctions.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,44 @@ +#ifndef __COLOR_FUNCTIONS_H__ +#define __COLOR_FUNCTIONS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +namespace caret { + + class ColorFunctions + { + public: + ///sRGB is 0 to 1 + static void sRGBtoCIEXYZ(const float sRGB[3], float xyzOut[3]); + + static void CIEXYZtoCIELab(const float xyz[3], float LabOut[3]); + //maybe add the inverses for completeness? + + static void CIEXYZtoCIELuvPrime (const float xyz[3], float LuvOut[3]); + static void CIELuvPrimetoCIEXYZ (const float Luv[3], float xyzOut[3]); + + //TODO: color deficiency simulation + static float perceptualDistanceSRGB(const float srgb1[3], const float srgb2[3]); + }; + +}//namespace + +#endif //__COLOR_FUNCTIONS_H__ diff -Nru connectome-workbench-1.4.2/src/Common/CubicSpline.cxx connectome-workbench-1.5.0/src/Common/CubicSpline.cxx --- connectome-workbench-1.4.2/src/Common/CubicSpline.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/CubicSpline.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -29,7 +29,7 @@ CubicSpline CubicSpline::hermite(float frac, bool lowEdge, bool highEdge) {//these equations are derived from hermite basis functions, plug the commented m0, m1 into the hermite representation to rederive - CaretAssert(frac > -0.01f && frac < 1.01f);//give some leeway for rounding errors + CaretAssert(frac > -0.05f && frac < 1.05f);//give some leeway for rounding errors, but don't allow large extrapolation CubicSpline ret; float frac2 = frac * frac; float frac3 = frac2 * frac; @@ -66,7 +66,7 @@ CubicSpline CubicSpline::bspline(float frac, bool lowEdge, bool highEdge) { - CaretAssert(frac > -0.01f && frac < 1.01f);//give some leeway for rounding errors + CaretAssert(frac > -0.05f && frac < 1.05f);//give some leeway for rounding errors, but don't allow large extrapolation CubicSpline ret; float frac2 = frac * frac; float frac3 = frac2 * frac; diff -Nru connectome-workbench-1.4.2/src/Common/DataFileTypeEnum.cxx connectome-workbench-1.5.0/src/Common/DataFileTypeEnum.cxx --- connectome-workbench-1.4.2/src/Common/DataFileTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/DataFileTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,6 +23,13 @@ #include "DataFileTypeEnum.h" #undef __DATA_FILE_TYPE_ENUM_DECLARE__ +#include + +#include +#include +#include +#include + #include "CaretAssert.h" #include "CaretLogger.h" @@ -72,32 +79,49 @@ this->oneStructureFlag = fileIsUsedWithOneStructure; if (fileExtensionOne.isEmpty() == false) { - this->fileExtensions.push_back(fileExtensionOne); + this->readAndWriteFileExtensions.push_back(fileExtensionOne); } if (fileExtensionTwo.isEmpty() == false) { - this->fileExtensions.push_back(fileExtensionTwo); + this->readAndWriteFileExtensions.push_back(fileExtensionTwo); } if (fileExtensionThree.isEmpty() == false) { - this->fileExtensions.push_back(fileExtensionThree); + this->readAndWriteFileExtensions.push_back(fileExtensionThree); } if (fileExtensionFour.isEmpty() == false) { - this->fileExtensions.push_back(fileExtensionFour); + this->readAndWriteFileExtensions.push_back(fileExtensionFour); } - AString filterText = this->guiName + " Files ("; - - - for (std::vector::const_iterator iter = this->fileExtensions.begin(); - iter != this->fileExtensions.end(); - iter++) { - if (iter != fileExtensions.begin()) { - filterText += " "; - } - filterText += ("*." + *iter); - } - filterText += ")"; - - this->qFileDialogNameFilter = filterText; + if (this->enumValue == DataFileTypeEnum::IMAGE) { + /* + * Images support different extensions for reading and writing + */ + AString defaultWriteImageExtension; + getWorkbenchSupportedImageFileExtensions(this->readFileExtensions, + this->writeFileExtensions, + defaultWriteImageExtension); + + std::set uniqueExtensions(this->readFileExtensions.begin(), + this->readFileExtensions.end()); + uniqueExtensions.insert(this->writeFileExtensions.begin(), + this->writeFileExtensions.end()); + + this->readAndWriteFileExtensions.clear(); + this->readAndWriteFileExtensions.insert(this->readAndWriteFileExtensions.end(), + uniqueExtensions.begin(), + uniqueExtensions.end()); + } + else { + /* + * Most files use same extension for reading or writing + */ + this->readFileExtensions = this->readAndWriteFileExtensions; + this->writeFileExtensions = this->readAndWriteFileExtensions; + } + + this->qReadFileDialogNameFilter = createQFileDialogNameFilter(this->guiName, + this->readFileExtensions); + this->qWriteFileDialogNameFilter = createQFileDialogNameFilter(this->guiName, + this->writeFileExtensions); } /** @@ -251,10 +275,7 @@ "Image", "IMAGE", false, - "jpg", - "jpeg", - "png", - "ppm")); + "XXX")); /* Extensions for IMAGE set in constructor */ enumData.push_back(DataFileTypeEnum(LABEL, "LABEL", @@ -564,7 +585,8 @@ iter != enumData.end(); iter++) { const DataFileTypeEnum& d = *iter; - if (d.qFileDialogNameFilter == qFileDialogNameFilter) { + if ((d.qReadFileDialogNameFilter == qFileDialogNameFilter) + && (d.qWriteFileDialogNameFilter == qFileDialogNameFilter)) { enumValue = d.enumValue; validFlag = true; break; @@ -581,7 +603,7 @@ } /** - * Get the file filter text for use in a QFileDialog. + * Get the file filter text for use in a QFileDialog for READING files * * @param enumValue * Enumerated type for file filter. @@ -589,11 +611,27 @@ * Text containing file filter. */ AString -DataFileTypeEnum::toQFileDialogFilter(const Enum enumValue) +DataFileTypeEnum::toQFileDialogFilterForReading(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const DataFileTypeEnum* enumInstance = findData(enumValue); + return enumInstance->qReadFileDialogNameFilter; +} + +/** + * Get the file filter text for use in a QFileDialog for WRITING files + * + * @param enumValue + * Enumerated type for file filter. + * @return + * Text containing file filter. + */ +AString +DataFileTypeEnum::toQFileDialogFilterForWriting(const Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); - return enumInstance->qFileDialogNameFilter; + return enumInstance->qWriteFileDialogNameFilter; } /** @@ -611,6 +649,60 @@ } /** + * @return All wild card matching (*.ext) for the given enum value. + * @param enumValue + * Enumerated type for file extensions. + */ +std::vector +DataFileTypeEnum::getWildCardMatching(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + const DataFileTypeEnum* enumInstance = findData(enumValue); + std::vector wildcards; + for (auto ext : enumInstance->readAndWriteFileExtensions) { + wildcards.push_back("*." + ext); + } + + return wildcards; +} + +/** + * Get files for the given enumerated type in the given directory + * @param enumValue + * Enumerated type for file extensions. + * @param directoryPath + * Name of directory + * @return Files in the directory for the type + */ +std::vector +DataFileTypeEnum::getFilesInDirectory(const Enum enumValue, + const AString& directoryPath) +{ + std::vector wildCards = DataFileTypeEnum::getWildCardMatching(enumValue); + + QStringList fileNameFilters; + for (auto w : wildCards) { + fileNameFilters.append(w); + } + + QDir::Filters typeFilter(QDir::Files); + + QDir dir(directoryPath); + std::vector filenamesOut; + + QFileInfoList fileInfoList = dir.entryInfoList(fileNameFilters, + typeFilter); + QListIterator iter(fileInfoList); + while (iter.hasNext()) { + filenamesOut.push_back(iter.next().absoluteFilePath()); + } + + return filenamesOut; +} + + +/** * @return All valid file extensions for the given enum value. * @param enumValue * Enumerated type for file extensions. @@ -620,7 +712,7 @@ { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); - return enumInstance->fileExtensions; + return enumInstance->readAndWriteFileExtensions; } /** @@ -709,8 +801,8 @@ if (validFlag) { allExtensions.insert(allExtensions.end(), - enumIter->fileExtensions.begin(), - enumIter->fileExtensions.end()); + enumIter->readAndWriteFileExtensions.begin(), + enumIter->readAndWriteFileExtensions.end()); } } @@ -742,8 +834,8 @@ * See if filename ends with any of the available extensions for the * given data file type. */ - for (std::vector::const_iterator iter = enumInstance->fileExtensions.begin(); - iter != enumInstance->fileExtensions.end(); + for (std::vector::const_iterator iter = enumInstance->writeFileExtensions.begin(); + iter != enumInstance->writeFileExtensions.end(); iter++) { const AString ext = ("." + *iter); if (filename.endsWith(ext)) { @@ -774,9 +866,22 @@ const DataFileTypeEnum* enumInstance = findData(enumValue); AString ext = "file"; - if (enumInstance->fileExtensions.empty() == false) { - ext = enumInstance->fileExtensions[0]; + + if ( ! enumInstance->readAndWriteFileExtensions.empty()) { + ext = enumInstance->readAndWriteFileExtensions[0]; + } + + if (enumValue == IMAGE) { + /* + * Image file's preferred extension + */ + std::vector readFileExtensions; + std::vector writeFileExtensions; + getWorkbenchSupportedImageFileExtensions(readFileExtensions, + writeFileExtensions, + ext); } + return ext; } @@ -801,8 +906,8 @@ * See if filename ends with any of the available extensions for the * given data file type. */ - for (std::vector::const_iterator iter = enumInstance->fileExtensions.begin(); - iter != enumInstance->fileExtensions.end(); + for (std::vector::const_iterator iter = enumInstance->readAndWriteFileExtensions.begin(); + iter != enumInstance->readAndWriteFileExtensions.end(); iter++) { const AString ext = ("." + *iter); if (filename.endsWith(ext)) { @@ -835,7 +940,7 @@ iter != enumData.end(); iter++) { const DataFileTypeEnum& d = *iter; - const std::vector extensions = iter->fileExtensions; + const std::vector extensions = iter->readAndWriteFileExtensions; for (std::vector::const_iterator extIter = extensions.begin(); extIter != extensions.end(); extIter++) { @@ -925,7 +1030,7 @@ const bool includeVolumeDynamicFlag = (options & OPTIONS_INCLUDE_VOLUME_DENSE_DYNAMIC); const bool includeUnknownFlag = (options & OPTIONS_INCLUDE_UNKNOWN); - for (const auto dataType : enumData) { + for (const auto& dataType : enumData) { bool addEnumFlag(true); switch (dataType.enumValue) { @@ -1048,4 +1153,246 @@ } +/** + * Create a QDialog file filter using the given file type name and extensions + * @param fileTypeName + * Description of file type name + * @param fileExtensions + * Valid file extensions + */ +AString +DataFileTypeEnum::createQFileDialogNameFilter(const AString& fileTypeName, + const std::vector& fileExtensions) +{ + AString filterText(fileTypeName + " Files ("); + + bool firstTime(true); + for (auto& ext : fileExtensions) { + if ( ! firstTime) { + filterText += " "; + } + filterText += ("*." + ext); + + firstTime = false; + } + filterText += ")"; + + return filterText; +} + +/** + * Get all image file extensions supported by Qt for reading and writing image files. + * @param readableExtensionsOut + * Output contains all readable image file extensions + * @param writableExtensionsOut + * Output contains all writable image file extensions + */ +void +DataFileTypeEnum::getQtSupportedImageFileExtensions(std::vector& readableExtensionsOut, + std::vector& writableExtensionsOut) +{ + readableExtensionsOut.clear(); + writableExtensionsOut.clear(); + + /* + * Extensions Qt can read (use set so extensions sorted) + */ + std::set qtReadExtensions; + QList readTypes(QImageReader::supportedImageFormats()); + QListIterator readTypesIterator(readTypes); + while (readTypesIterator.hasNext()) { + QString fmt(readTypesIterator.next()); + qtReadExtensions.insert(fmt); + } + + /* + * Extensions Qt can write + */ + std::set qtWriteExtensions; + QList writeTypes(QImageWriter::supportedImageFormats()); + QListIterator writeTypesIterator(writeTypes); + while (writeTypesIterator.hasNext()) { + QString fmt(writeTypesIterator.next()); + qtWriteExtensions.insert(fmt); + } + + readableExtensionsOut.insert(readableExtensionsOut.end(), + qtReadExtensions.begin(), + qtReadExtensions.end()); + + writableExtensionsOut.insert(writableExtensionsOut.end(), + qtWriteExtensions.begin(), + qtWriteExtensions.end()); +} + +/** + * Get all image file extensions supported by Workbench for reading and writing image files. + * These are a subset of the extensions supported by Qt. + * @param readableExtensionsOut + * Output contains all readable image file extensions + * @param writableExtensionsOut + * Output contains all writable image file extensions + * @param defaultWritableExtension + * Default extension for writing images + */ +void +DataFileTypeEnum::getWorkbenchSupportedImageFileExtensions(std::vector& readableExtensionsOut, + std::vector& writableExtensionsOut, + AString& defaultWritableExtension) +{ + readableExtensionsOut.clear(); + writableExtensionsOut.clear(); + defaultWritableExtension.clear(); + + /* + * Extensions that we want to support + * There are some such as SVG that we don't want to support + */ + std::set supportedExtensions; + supportedExtensions.insert("bmp"); + supportedExtensions.insert("gif"); + supportedExtensions.insert("jpg"); + supportedExtensions.insert("jpeg"); + supportedExtensions.insert("jp2"); + supportedExtensions.insert("png"); + supportedExtensions.insert("ppm"); + supportedExtensions.insert("tiff"); + supportedExtensions.insert("tif"); + + std::vector qtReadExtensions; + std::vector qtWriteExtensions; + getQtSupportedImageFileExtensions(qtReadExtensions, + qtWriteExtensions); + + /* + * Use only those extensions preferred for reading and writing + */ + for (auto& ext : supportedExtensions) { + if (std::find(qtReadExtensions.begin(), + qtReadExtensions.end(), + ext) != qtReadExtensions.end()) { + readableExtensionsOut.push_back(ext); + } + if (std::find(qtWriteExtensions.begin(), + qtWriteExtensions.end(), + ext) != qtWriteExtensions.end()) { + writableExtensionsOut.push_back(ext); + } + } + + AString pngExtension; + AString jpegExtension; + AString jpgExtension; + AString tifExtension; + AString tiffExtension; + + for (auto& extension : writableExtensionsOut) { + if (extension == "png") { + pngExtension = extension; + } + else if (extension == "jpg") { + jpgExtension = extension; + } + else if (extension == "jpeg") { + jpegExtension = extension; + } + else if (extension == "tif") { + tifExtension = extension; + } + else if (extension == "tiff") { + tiffExtension = extension; + } + } + + if ( ! writableExtensionsOut.empty()) { + defaultWritableExtension = writableExtensionsOut[0]; + + if ( ! pngExtension.isEmpty()) { + defaultWritableExtension = pngExtension; + } + else if ( ! jpgExtension.isEmpty()) { + defaultWritableExtension = jpgExtension; + } + else if ( ! jpegExtension.isEmpty()) { + defaultWritableExtension = jpegExtension; + } + else if ( ! tiffExtension.isEmpty()) { + defaultWritableExtension = tiffExtension; + } + else if ( ! tifExtension.isEmpty()) { + defaultWritableExtension = tifExtension; + } + } +} + +/** + * Get filters for saving images with each image type in its own filter and the default filter + * @param imageFiltersOut + * All image filters to saving images + * @param defaultImageFilterOut + * Default image filter + */ +void +DataFileTypeEnum::getSaveQFileDialogImageFilters(std::vector& imageFiltersOut, + AString& defaultImageFilterOut) +{ + imageFiltersOut.clear(); + defaultImageFilterOut.clear(); + + std::vector readableExtensions; + std::vector writableExtensions; + AString defaultWritableExtension; + getWorkbenchSupportedImageFileExtensions(readableExtensions, + writableExtensions, + defaultWritableExtension); + + const DataFileTypeEnum* imageType = findData(DataFileTypeEnum::IMAGE); + CaretAssert(imageType); + + for (const auto& extension: writableExtensions) { + const AString name(extension.toUpper() + + " " + + imageType->guiName); + const std::vector extVector { extension }; + + const AString filter(createQFileDialogNameFilter(name, + extVector)); + imageFiltersOut.push_back(filter); + + if (extension == defaultWritableExtension) { + defaultImageFilterOut = filter; + } + } + + if (defaultImageFilterOut.isEmpty()) { + if ( ! imageFiltersOut.empty()) { + defaultImageFilterOut = imageFiltersOut[0]; + } + } +} + +/** + * Get files extensions supports by QMovie + * @param readableExtensionsOut + * Output containing extensions of files that can be read by QMovie + */ +void +DataFileTypeEnum::getQtSupportedMovieFileExtensions(std::vector& readableExtensionsOut) +{ + /* + * Extensions Qt can read (use set so extensions sorted) + */ + std::set qtReadExtensions; + QList readTypes(QMovie::supportedFormats()); + QListIterator readTypesIterator(readTypes); + while (readTypesIterator.hasNext()) { + QString fmt(readTypesIterator.next()); + qtReadExtensions.insert(fmt); + } + + readableExtensionsOut.clear(); + readableExtensionsOut.insert(readableExtensionsOut.end(), + qtReadExtensions.begin(), + qtReadExtensions.end()); +} diff -Nru connectome-workbench-1.4.2/src/Common/DataFileTypeEnum.h connectome-workbench-1.5.0/src/Common/DataFileTypeEnum.h --- connectome-workbench-1.4.2/src/Common/DataFileTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/DataFileTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -137,7 +137,9 @@ static Enum fromQFileDialogFilter(const AString& qFileDialogNameFilter, bool* isValidOut); - static AString toQFileDialogFilter(const Enum enumValue); + static AString toQFileDialogFilterForReading(const Enum enumValue); + + static AString toQFileDialogFilterForWriting(const Enum enumValue); static Enum fromFileExtension(const AString& filename, bool* isValidOut); @@ -150,6 +152,11 @@ static std::vector getFilesExtensionsForEveryFile(const bool includeNonWritableFileTypesFlag = false); + static std::vector getWildCardMatching(const Enum enumValue); + + static std::vector getFilesInDirectory(const Enum enumValue, + const AString& directoryPath); + static bool isFileUsedWithOneStructure(const Enum enumValue); static bool isConnectivityDataType(const Enum enumValue); @@ -159,6 +166,18 @@ static AString addFileExtensionIfMissing(const AString& filename, const Enum enumValue); + static void getSaveQFileDialogImageFilters(std::vector& imageFiltersOut, + AString& defaultImageFilterOut); + + static void getQtSupportedImageFileExtensions(std::vector& readableExtensionsOut, + std::vector& writableExtensionsOut); + + static void getWorkbenchSupportedImageFileExtensions(std::vector& readableExtensionsOut, + std::vector& writableExtensionsOut, + AString& defaultWritableExtension); + + static void getQtSupportedMovieFileExtensions(std::vector& readableExtensionsOut); + private: DataFileTypeEnum(const Enum enumValue, const AString& name, @@ -172,6 +191,9 @@ static const DataFileTypeEnum* findData(const Enum enumValue); + static AString createQFileDialogNameFilter(const AString& fileTypeName, + const std::vector& fileExtensions); + /** Holds all instance of enum values and associated metadata */ static std::vector enumData; @@ -199,11 +221,20 @@ /** Name for use in overlay selection */ AString overlayTypeName; - /** Extension(s) for the file */ - std::vector fileExtensions; + /** All Extension(s) for the file */ + std::vector readAndWriteFileExtensions; + + /** Extension(s) for reading the file */ + std::vector readFileExtensions; + + /** Extension(s) for writing the file */ + std::vector writeFileExtensions; + + /** Name filter for reading files in a QFileDialog */ + AString qReadFileDialogNameFilter; - /** Name filter for use in a QFileDialog */ - AString qFileDialogNameFilter; + /** Name filter for writing files in a QFileDialog */ + AString qWriteFileDialogNameFilter; /** Is file for use with one structure */ bool oneStructureFlag; diff -Nru connectome-workbench-1.4.2/src/Common/DeveloperFlagsEnum.cxx connectome-workbench-1.5.0/src/Common/DeveloperFlagsEnum.cxx --- connectome-workbench-1.4.2/src/Common/DeveloperFlagsEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/DeveloperFlagsEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -135,11 +135,30 @@ CheckableEnum::YES, false)); + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_VOXEL_CUBES_TEST, + "DEVELOPER_FLAG_VOXEL_CUBES_TEST", + "Voxel Cubes Drawing Test", + CheckableEnum::YES, + true)); + + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_CHART_OPENGL_LINES, + "DEVELOPER_FLAG_CHART_OPENGL_LINES", + "Draw Chart Lines with OpenGL Lines", + CheckableEnum::YES, + true)); + + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_BLENDING, + "DEVELOPER_FLAG_BLENDING", + "Separate RGB / Alpha Opacity", + CheckableEnum::YES, + true)); +#ifdef HAVE_WEBKIT checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_BALSA, "DEVELOPER_FLAG_BALSA", "Visit BALSA...", CheckableEnum::NO, false)); +#endif std::vector notCheckableItems; diff -Nru connectome-workbench-1.4.2/src/Common/DeveloperFlagsEnum.h connectome-workbench-1.5.0/src/Common/DeveloperFlagsEnum.h --- connectome-workbench-1.4.2/src/Common/DeveloperFlagsEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/DeveloperFlagsEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -39,7 +39,10 @@ DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, DEVELOPER_FLAG_TEXTURE_VOLUME, DELELOPER_FLAG_VOXEL_SMOOTH, - DEVELOPER_FLAG_BALSA + DEVELOPER_FLAG_BALSA, + DEVELOPER_FLAG_VOXEL_CUBES_TEST, + DEVELOPER_FLAG_CHART_OPENGL_LINES, + DEVELOPER_FLAG_BLENDING }; ~DeveloperFlagsEnum(); diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabClose.cxx connectome-workbench-1.5.0/src/Common/EventBrowserTabClose.cxx --- connectome-workbench-1.4.2/src/Common/EventBrowserTabClose.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabClose.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,100 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretAssert.h" +#include "EventBrowserTabClose.h" + +using namespace caret; + +/** + * Constructor. + * + * @param browserTab + * The tab to close + * @param browserTabIndex + * Index of the tab to close + * @param windowIndex + * Index of window containing tab to close + * @param windowTabBarPositionIndex + * Position of the tab in the windows tab bar + */ +EventBrowserTabClose::EventBrowserTabClose(BrowserTabContent* browserTab, + const int32_t browserTabIndex, + const int32_t windowIndex, + const int32_t windowTabBarPositionIndex) +: Event(EventTypeEnum::EVENT_BROWSER_TAB_CLOSE), +m_browserTab(browserTab), +m_browserTabIndex(browserTabIndex), +m_windowIndex(windowIndex), +m_windowTabBarPositionIndex(windowTabBarPositionIndex) +{ + CaretAssert(browserTab); +} + +/** + * Destructor. + */ +EventBrowserTabClose::~EventBrowserTabClose() +{ + +} + +/** + * Get the browser tab that is to be deleted. + * Note that this may point to a browser tab that + * has been deleted and using the pointer in this + * case could be a disaster. + * + * @return + * Pointer to browser tab that is to be deleted. + */ +BrowserTabContent* +EventBrowserTabClose::getBrowserTab() +{ + return m_browserTab; +} + +/** + * @return Index of browser tab being deleted. + */ +int32_t +EventBrowserTabClose::getBrowserTabIndex() const +{ + return m_browserTabIndex; +} + +/** + * @return Index of browser window that is deleting tab + */ +int32_t +EventBrowserTabClose::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * @return Position of the tab in the window's tab bar + */ +int32_t +EventBrowserTabClose::getWindowTabBarPositionIndex() const +{ + return m_windowTabBarPositionIndex; +} + diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabClose.h connectome-workbench-1.5.0/src/Common/EventBrowserTabClose.h --- connectome-workbench-1.4.2/src/Common/EventBrowserTabClose.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabClose.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,67 @@ +#ifndef __EVENT_BROWSER_TAB_CLOSE_H__ +#define __EVENT_BROWSER_TAB_CLOSE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "Event.h" + +namespace caret { + + class BrowserTabContent; + + /// Event for deleting a browser tab + class EventBrowserTabClose : public Event { + + public: + EventBrowserTabClose(BrowserTabContent* browserTab, + const int32_t browserTabIndex, + const int32_t windowIndex, + const int32_t windowTabBarPositionIndex); + + virtual ~EventBrowserTabClose(); + + BrowserTabContent* getBrowserTab(); + + int32_t getBrowserTabIndex() const; + + int32_t getWindowIndex() const; + + int32_t getWindowTabBarPositionIndex() const; + + private: + EventBrowserTabClose(const EventBrowserTabClose&); + + EventBrowserTabClose& operator=(const EventBrowserTabClose&); + + BrowserTabContent* m_browserTab; + + const int32_t m_browserTabIndex; + + const int32_t m_windowIndex; + + const int32_t m_windowTabBarPositionIndex; + }; + +} // namespace + +#endif // __EVENT_BROWSER_TAB_CLOSE_H__ diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabDelete.cxx connectome-workbench-1.5.0/src/Common/EventBrowserTabDelete.cxx --- connectome-workbench-1.4.2/src/Common/EventBrowserTabDelete.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabDelete.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,12 +25,21 @@ /** * Constructor. + * + * @param browserTab + * The tab to delete + * @param browserTabIndex + * Index of the tab to delete + * @param windowIndex + * Index of window containing tab to close */ EventBrowserTabDelete::EventBrowserTabDelete(BrowserTabContent* browserTab, - const int32_t browserTabIndex) + const int32_t browserTabIndex, + const int32_t windowIndex) : Event(EventTypeEnum::EVENT_BROWSER_TAB_DELETE), m_browserTab(browserTab), -m_browserTabIndex(browserTabIndex) +m_browserTabIndex(browserTabIndex), +m_windowIndex(windowIndex) { CaretAssert(browserTab); } @@ -67,3 +76,12 @@ return m_browserTabIndex; } +/** + * @return Index of browser window that is deleting tab + */ +int32_t +EventBrowserTabDelete::getWindowIndex() const +{ + return m_windowIndex; +} + diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabDelete.h connectome-workbench-1.5.0/src/Common/EventBrowserTabDelete.h --- connectome-workbench-1.4.2/src/Common/EventBrowserTabDelete.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabDelete.h 2021-02-16 19:46:47.000000000 +0000 @@ -34,7 +34,8 @@ public: EventBrowserTabDelete(BrowserTabContent* browserTab, - const int32_t browserTabIndex); + const int32_t browserTabIndex, + const int32_t windowIndex); virtual ~EventBrowserTabDelete(); @@ -42,6 +43,8 @@ int32_t getBrowserTabIndex() const; + int32_t getWindowIndex() const; + private: EventBrowserTabDelete(const EventBrowserTabDelete&); @@ -50,6 +53,8 @@ BrowserTabContent* m_browserTab; const int32_t m_browserTabIndex; + + const int32_t m_windowIndex; }; } // namespace diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabIndicesGetAllViewed.cxx connectome-workbench-1.5.0/src/Common/EventBrowserTabIndicesGetAllViewed.cxx --- connectome-workbench-1.4.2/src/Common/EventBrowserTabIndicesGetAllViewed.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabIndicesGetAllViewed.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,95 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED_DECLARE__ +#include "EventBrowserTabIndicesGetAllViewed.h" +#undef __EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventBrowserTabIndicesGetAllViewed + * \brief Event that gets all viewed browser tabs (single tabs view and tile tabs views) + * \ingroup Common + */ + +/** + * Constructor. + */ +EventBrowserTabIndicesGetAllViewed::EventBrowserTabIndicesGetAllViewed() +: Event(EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED) +{ + +} + +/** + * Destructor. + */ +EventBrowserTabIndicesGetAllViewed::~EventBrowserTabIndicesGetAllViewed() +{ +} + +/** + * Add the tab index of a valid browser tab. + * + * @parm browserTabIndex + * Index of the browser tab. + */ +void +EventBrowserTabIndicesGetAllViewed::addBrowserTabIndex(const int32_t browserTabIndex) +{ + m_browserTabIndices.push_back(browserTabIndex); +} + +/** + * @return Indices of all valid browser tabs. + */ +std::vector +EventBrowserTabIndicesGetAllViewed::getAllBrowserTabIndices() const +{ + return m_browserTabIndices; +} + +/** + * Get the validity of a browser tab. + * + * @parm browserTabIndex + * Index of the browser tab. + * @return + * True if browser tab index is valid, else false. + */ +bool +EventBrowserTabIndicesGetAllViewed::isValidBrowserTabIndex(const int32_t browserTabIndex) +{ + if (std::find(m_browserTabIndices.begin(), + m_browserTabIndices.end(), + browserTabIndex) != m_browserTabIndices.end()) { + return true; + } + + return false; +} + diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabIndicesGetAllViewed.h connectome-workbench-1.5.0/src/Common/EventBrowserTabIndicesGetAllViewed.h --- connectome-workbench-1.4.2/src/Common/EventBrowserTabIndicesGetAllViewed.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabIndicesGetAllViewed.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,66 @@ +#ifndef __EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED_H__ +#define __EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include + +#include "Event.h" + + + +namespace caret { + + class EventBrowserTabIndicesGetAllViewed : public Event { + + public: + EventBrowserTabIndicesGetAllViewed(); + + virtual ~EventBrowserTabIndicesGetAllViewed(); + + EventBrowserTabIndicesGetAllViewed(const EventBrowserTabIndicesGetAllViewed&) = delete; + + EventBrowserTabIndicesGetAllViewed& operator=(const EventBrowserTabIndicesGetAllViewed&) = delete; + + void addBrowserTabIndex(const int32_t browserTabIndex); + + std::vector getAllBrowserTabIndices() const; + + bool isValidBrowserTabIndex(const int32_t browserTabIndex); + + // ADD_NEW_METHODS_HERE + + private: + std::vector m_browserTabIndices; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED_DECLARE__ + // +#endif // __EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED_DECLARE__ + +} // namespace +#endif //__EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED_H__ diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabReopenClosed.cxx connectome-workbench-1.5.0/src/Common/EventBrowserTabReopenClosed.cxx --- connectome-workbench-1.4.2/src/Common/EventBrowserTabReopenClosed.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabReopenClosed.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,88 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_BROWSER_TAB_REOPEN_CLOSED_DECLARE__ +#include "EventBrowserTabReopenClosed.h" +#undef __EVENT_BROWSER_TAB_REOPEN_CLOSED_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventBrowserTabReopenClosed + * \brief Event to open a recently closed tab in a window + * \ingroup Brain + */ + +/** + * Constructor. + */ +EventBrowserTabReopenClosed::EventBrowserTabReopenClosed() +: Event(EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_CLOSED) +{ + +} + +/** + * Destructor. + */ +EventBrowserTabReopenClosed::~EventBrowserTabReopenClosed() +{ +} + +/** + * @return Content of reopened tab or NULL if tab could not be reopened + */ +BrowserTabContent* +EventBrowserTabReopenClosed::getBrowserTabContent() const +{ + return m_browserTabContent; +} + +/** + * Set the browser tab content for the reopened browser tab. + * + * @param browserTabContent + * Content of the reopened browser tab + * @param tabIndex + * Index of the reopened browser tab + */ +void +EventBrowserTabReopenClosed::setBrowserTabContent(BrowserTabContent* browserTabContent, + const int32_t tabIndex) +{ + m_browserTabContent = browserTabContent; + m_tabIndex = tabIndex; +} + +/** + * @return Index of browser window tab that was reopened + */ +int32_t +EventBrowserTabReopenClosed::getTabIndex() const +{ + CaretAssert(m_tabIndex >= 0); + return m_tabIndex; +} diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabReopenClosed.h connectome-workbench-1.5.0/src/Common/EventBrowserTabReopenClosed.h --- connectome-workbench-1.4.2/src/Common/EventBrowserTabReopenClosed.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabReopenClosed.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,70 @@ +#ifndef __EVENT_BROWSER_TAB_REOPEN_CLOSED_H__ +#define __EVENT_BROWSER_TAB_REOPEN_CLOSED_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class BrowserTabContent; + + class EventBrowserTabReopenClosed : public Event { + + public: + EventBrowserTabReopenClosed(); + + virtual ~EventBrowserTabReopenClosed(); + + EventBrowserTabReopenClosed(const EventBrowserTabReopenClosed&) = delete; + + EventBrowserTabReopenClosed& operator=(const EventBrowserTabReopenClosed&) = delete; + + BrowserTabContent* getBrowserTabContent() const; + + void setBrowserTabContent(BrowserTabContent* browserTabContent, + const int32_t tabIndex); + + int32_t getTabIndex() const; + + // ADD_NEW_METHODS_HERE + + private: + BrowserTabContent* m_browserTabContent = NULL; + + int32_t m_tabIndex; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_BROWSER_TAB_REOPEN_CLOSED_DECLARE__ + // +#endif // __EVENT_BROWSER_TAB_REOPEN_CLOSED_DECLARE__ + +} // namespace +#endif //__EVENT_BROWSER_TAB_REOPEN_CLOSED_H__ diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabSelectInWindow.cxx connectome-workbench-1.5.0/src/Common/EventBrowserTabSelectInWindow.cxx --- connectome-workbench-1.4.2/src/Common/EventBrowserTabSelectInWindow.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabSelectInWindow.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,67 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_BROWSER_TAB_SELECT_IN_WINDOW_DECLARE__ +#include "EventBrowserTabSelectInWindow.h" +#undef __EVENT_BROWSER_TAB_SELECT_IN_WINDOW_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventBrowserTabSelectInWindow + * \brief Request selection of a browser tab in the window that contains the browser tab + * \ingroup Common + */ + +/** + * Constructor. + * @param browserTabIndex + * Index of the browser tab + */ +EventBrowserTabSelectInWindow::EventBrowserTabSelectInWindow(const int32_t browserTabIndex) +: Event(EventTypeEnum::EVENT_BROWSER_TAB_SELECT_IN_WINDOW), +m_browserTabIndex(browserTabIndex) +{ + +} + +/** + * Destructor. + */ +EventBrowserTabSelectInWindow::~EventBrowserTabSelectInWindow() +{ +} + +/** + * @return Index of the browser tab + */ +int32_t +EventBrowserTabSelectInWindow::getBrowserTabIndex() const +{ + return m_browserTabIndex; +} + + diff -Nru connectome-workbench-1.4.2/src/Common/EventBrowserTabSelectInWindow.h connectome-workbench-1.5.0/src/Common/EventBrowserTabSelectInWindow.h --- connectome-workbench-1.4.2/src/Common/EventBrowserTabSelectInWindow.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventBrowserTabSelectInWindow.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,61 @@ +#ifndef __EVENT_BROWSER_TAB_SELECT_IN_WINDOW_H__ +#define __EVENT_BROWSER_TAB_SELECT_IN_WINDOW_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class EventBrowserTabSelectInWindow : public Event { + + public: + EventBrowserTabSelectInWindow(const int32_t browserTabIndex); + + virtual ~EventBrowserTabSelectInWindow(); + + EventBrowserTabSelectInWindow(const EventBrowserTabSelectInWindow&) = delete; + + EventBrowserTabSelectInWindow& operator=(const EventBrowserTabSelectInWindow&) = delete; + + int32_t getBrowserTabIndex() const; + + // ADD_NEW_METHODS_HERE + + private: + const int32_t m_browserTabIndex; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_BROWSER_TAB_SELECT_IN_WINDOW_DECLARE__ + // +#endif // __EVENT_BROWSER_TAB_SELECT_IN_WINDOW_DECLARE__ + +} // namespace +#endif //__EVENT_BROWSER_TAB_SELECT_IN_WINDOW_H__ diff -Nru connectome-workbench-1.4.2/src/Common/EventGetViewportSize.h connectome-workbench-1.5.0/src/Common/EventGetViewportSize.h --- connectome-workbench-1.4.2/src/Common/EventGetViewportSize.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventGetViewportSize.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,6 +37,8 @@ MODE_TAB_AFTER_MARGINS_INDEX, MODE_VOLUME_MONTAGE, MODE_WINDOW_INDEX, + /** Gets window viewport for window containing the tab with the index*/ + MODE_WINDOW_FROM_TAB_INDEX }; EventGetViewportSize(const Mode mode, diff -Nru connectome-workbench-1.4.2/src/Common/EventManager.cxx connectome-workbench-1.5.0/src/Common/EventManager.cxx --- connectome-workbench-1.4.2/src/Common/EventManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -367,9 +367,13 @@ { switch (eventType) { + /* + * Simple Events (no Event subclass) + */ case EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE: case EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE: case EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE: + case EventTypeEnum::EVENT_TOOLBAR_CHART_ORIENTED_AXES_UPDATE: case EventTypeEnum::EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR: { sendEvent(Event(eventType).getPointer()); @@ -384,27 +388,41 @@ CaretLogSevere(msg); } break; + /* + * Events that have a subclass of Event + */ case EventTypeEnum::EVENT_ALERT_USER: case EventTypeEnum::EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE: + case EventTypeEnum::EVENT_ANNOTATION_BARS_GET: case EventTypeEnum::EVENT_ANNOTATION_CHART_LABEL_GET: - case EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET: case EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE: case EventTypeEnum::EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW: case EventTypeEnum::EVENT_ANNOTATION_GROUP_GET_WITH_KEY: case EventTypeEnum::EVENT_ANNOTATION_GROUPING: + case EventTypeEnum::EVENT_ANNOTATION_TEXT_GET_BOUNDS: case EventTypeEnum::EVENT_ANNOTATION_TEXT_SUBSTITUTION_GET: case EventTypeEnum::EVENT_ANNOTATION_TEXT_SUBSTITUTION_INVALIDATE: + case EventTypeEnum::EVENT_ANNOTATION_VALIDATE: case EventTypeEnum::EVENT_BRAIN_RESET: case EventTypeEnum::EVENT_BRAIN_STRUCTURE_GET_ALL: + case EventTypeEnum::EVENT_BROWSER_TAB_CLOSE: + case EventTypeEnum::EVENT_BROWSER_TAB_CLOSE_IN_TOOL_BAR: case EventTypeEnum::EVENT_BROWSER_TAB_DELETE: + case EventTypeEnum::EVENT_BROWSER_TAB_DELETE_IN_TOOL_BAR: case EventTypeEnum::EVENT_BROWSER_TAB_GET: case EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL: case EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL_VIEWED: case EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL: + case EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED: case EventTypeEnum::EVENT_BROWSER_TAB_NEW: + case EventTypeEnum::EVENT_BROWSER_TAB_NEW_IN_GUI: case EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE: + case EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_AVAILBLE: + case EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_CLOSED: + case EventTypeEnum::EVENT_BROWSER_TAB_SELECT_IN_WINDOW: case EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT: case EventTypeEnum::EVENT_BROWSER_WINDOW_CREATE_TABS: + case EventTypeEnum::EVENT_BROWSER_WINDOW_GET_TABS: case EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET: case EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN: case EventTypeEnum::EVENT_BROWSER_WINDOW_NEW: @@ -415,18 +433,20 @@ case EventTypeEnum::EVENT_CARET_PREFERENCES_GET: case EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_AND_MAPS_IN_DISPLAYED_OVERLAYS: case EventTypeEnum::EVENT_CHART_MATRIX_YOKING_VALIDATION: - case EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE: - case EventTypeEnum::EVENT_CHART_TWO_ATTRIBUTES_CHANGED: - case EventTypeEnum::EVENT_CHART_TWO_AXIS_GET_DATA_RANGE: + case EventTypeEnum::EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP: + case EventTypeEnum::EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING: case EventTypeEnum::EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA: + case EventTypeEnum::EVENT_CHART_TWO_OVERLAY_VALIDATE: case EventTypeEnum::EVENT_DATA_FILE_ADD: case EventTypeEnum::EVENT_DATA_FILE_DELETE: case EventTypeEnum::EVENT_DATA_FILE_READ: case EventTypeEnum::EVENT_DATA_FILE_RELOAD: + case EventTypeEnum::EVENT_DATA_FILE_RELOAD_ALL: case EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES: case EventTypeEnum::EVENT_GET_NODE_DATA_FILES: case EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE: case EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW: + case EventTypeEnum::EVENT_GET_USER_INPUT_MODE: case EventTypeEnum::EVENT_GET_VIEWPORT_SIZE: case EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_BUFFER_OBJECT: case EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME: @@ -435,14 +455,15 @@ case EventTypeEnum::EVENT_GRAPHICS_TIMING_ONE_WINDOW: case EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS: case EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW: + case EventTypeEnum::EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP: case EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY: case EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION: case EventTypeEnum::EVENT_IDENTIFICATION_SYMBOL_REMOVAL: case EventTypeEnum::EVENT_IDENTIFICATION_REQUEST: case EventTypeEnum::EVENT_IMAGE_CAPTURE: - case EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE: case EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP: case EventTypeEnum::EVENT_MAP_YOKING_VALIDATION: + case EventTypeEnum::EVENT_MEDIA_FILES_GET: case EventTypeEnum::EVENT_MODEL_ADD: case EventTypeEnum::EVENT_MODEL_DELETE: case EventTypeEnum::EVENT_MODEL_GET_ALL: @@ -456,6 +477,8 @@ case EventTypeEnum::EVENT_OVERLAY_VALIDATE: case EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW: case EventTypeEnum::EVENT_PALETTE_GET_BY_NAME: + case EventTypeEnum::EVENT_PALETTE_GROUPS_GET: + case EventTypeEnum::EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE: case EventTypeEnum::EVENT_SCENE_ACTIVE: case EventTypeEnum::EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG: case EventTypeEnum::EVENT_SPACER_TAB_GET: diff -Nru connectome-workbench-1.4.2/src/Common/EventRecentFilesSystemAccessMode.cxx connectome-workbench-1.5.0/src/Common/EventRecentFilesSystemAccessMode.cxx --- connectome-workbench-1.4.2/src/Common/EventRecentFilesSystemAccessMode.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventRecentFilesSystemAccessMode.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,74 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE_DECLARE__ +#include "EventRecentFilesSystemAccessMode.h" +#undef __EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventRecentFilesSystemAccessMode + * \brief Event to get the recent files system access mode + * \ingroup Common + */ + +/** + * Constructor. + */ +EventRecentFilesSystemAccessMode::EventRecentFilesSystemAccessMode() +: Event(EventTypeEnum::EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE) +{ + +} + +/** + * Destructor. + */ +EventRecentFilesSystemAccessMode::~EventRecentFilesSystemAccessMode() +{ +} + +/** + * @return Mode for accessing the file system for recent files + */ +RecentFilesSystemAccessModeEnum::Enum +EventRecentFilesSystemAccessMode::getMode() const +{ + return m_mode; +} + +/** + * Set the mode for accessing the file system for recent files + * @param mode + * New value for mode + */ +void +EventRecentFilesSystemAccessMode::setMode(const RecentFilesSystemAccessModeEnum::Enum mode) +{ + m_mode = mode; +} + diff -Nru connectome-workbench-1.4.2/src/Common/EventRecentFilesSystemAccessMode.h connectome-workbench-1.5.0/src/Common/EventRecentFilesSystemAccessMode.h --- connectome-workbench-1.4.2/src/Common/EventRecentFilesSystemAccessMode.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventRecentFilesSystemAccessMode.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,63 @@ +#ifndef __EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE_H__ +#define __EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" +#include "RecentFilesSystemAccessModeEnum.h" + + +namespace caret { + + class EventRecentFilesSystemAccessMode : public Event { + + public: + EventRecentFilesSystemAccessMode(); + + virtual ~EventRecentFilesSystemAccessMode(); + + EventRecentFilesSystemAccessMode(const EventRecentFilesSystemAccessMode&) = delete; + + EventRecentFilesSystemAccessMode& operator=(const EventRecentFilesSystemAccessMode&) = delete; + + RecentFilesSystemAccessModeEnum::Enum getMode() const; + + void setMode(const RecentFilesSystemAccessModeEnum::Enum mode); + + // ADD_NEW_METHODS_HERE + + private: + RecentFilesSystemAccessModeEnum::Enum m_mode = RecentFilesSystemAccessModeEnum::OFF; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE_DECLARE__ + // +#endif // __EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE_DECLARE__ + +} // namespace +#endif //__EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE_H__ diff -Nru connectome-workbench-1.4.2/src/Common/EventTileTabsConfigurationModification.cxx connectome-workbench-1.5.0/src/Common/EventTileTabsConfigurationModification.cxx --- connectome-workbench-1.4.2/src/Common/EventTileTabsConfigurationModification.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventTileTabsConfigurationModification.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ -#include "EventTileTabsConfigurationModification.h" -#undef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ - -#include "CaretAssert.h" -#include "EventTypeEnum.h" - -using namespace caret; - - - -/** - * \class caret::EventTileTabsConfigurationModification - * \brief Event for modifying a tile tabs configuration. - * \ingroup Common - */ - -/** - * Constructor. - * - * @param tileTabsConfiguration - * Tile tabs configuration that will be modified - * @param rowColumnIndex - * Index of the row or column used for modification - * @param rowColumnType - * 'Row' or "Column' type - * @param operation - * Enumerated type with type of modification. - */ -EventTileTabsConfigurationModification::EventTileTabsConfigurationModification(TileTabsConfiguration* tileTabsConfiguration, - const int32_t rowColumnIndex, - const RowColumnType rowColumnType, - const Operation operation) -: Event(EventTypeEnum::EVENT_TILE_TABS_MODIFICATION), -m_tileTabsConfiguration(tileTabsConfiguration), -m_rowColumnIndex(rowColumnIndex), -m_rowColumnType(rowColumnType), -m_operation(operation), -m_windowIndex(-1) -{ - -} - -/** - * Destructor. - */ -EventTileTabsConfigurationModification::~EventTileTabsConfigurationModification() -{ -} - -/** - * @return Tile tabs configuration that is modified. - */ -TileTabsConfiguration* -EventTileTabsConfigurationModification::getTileTabsConfiguration() -{ - return m_tileTabsConfiguration; -} - - -/** - * @return Index of the window. - */ -int32_t -EventTileTabsConfigurationModification::getWindowIndex() const -{ - return m_windowIndex; -} - -/** - * Set the window index. - * - * @param windowIndex - * Index of the window. - */ -void -EventTileTabsConfigurationModification::setWindowIndex(const int32_t windowIndex) -{ - m_windowIndex = windowIndex; -} - -/** - * @return The index of the row or column. - */ -int32_t -EventTileTabsConfigurationModification::getRowColumnIndex() const -{ - return m_rowColumnIndex; -} - -/** - * @return The type ROW or COLUMN - */ -EventTileTabsConfigurationModification::RowColumnType -EventTileTabsConfigurationModification::getRowColumnType() const -{ - return m_rowColumnType; -} - -/** - * @return The operation. - */ -EventTileTabsConfigurationModification::Operation -EventTileTabsConfigurationModification::getOperation() const -{ - return m_operation; -} - diff -Nru connectome-workbench-1.4.2/src/Common/EventTileTabsConfigurationModification.h connectome-workbench-1.5.0/src/Common/EventTileTabsConfigurationModification.h --- connectome-workbench-1.4.2/src/Common/EventTileTabsConfigurationModification.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventTileTabsConfigurationModification.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -#ifndef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ -#define __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - -#include "Event.h" - - - -namespace caret { - - class TileTabsConfiguration; - - class EventTileTabsConfigurationModification : public Event { - - public: - /** - * Row or Column - */ - enum class RowColumnType { - COLUMN, - ROW - }; - - /** - * Operation - */ - enum class Operation { - DELETE_IT, /* DELETE is reserved word on Windows */ - DUPLICATE_AFTER, - DUPLICATE_BEFORE, - INSERT_SPACER_BEFORE, - INSERT_SPACER_AFTER, - MOVE_AFTER, - MOVE_BEFORE - }; - - EventTileTabsConfigurationModification(TileTabsConfiguration* tileTabsConfiguration, - const int32_t rowColumnIndex, - const RowColumnType rowColumnType, - const Operation operation); - - virtual ~EventTileTabsConfigurationModification(); - - EventTileTabsConfigurationModification(const EventTileTabsConfigurationModification&) = delete; - - EventTileTabsConfigurationModification& operator=(const EventTileTabsConfigurationModification&) = delete; - - TileTabsConfiguration* getTileTabsConfiguration(); - - int32_t getRowColumnIndex() const; - - RowColumnType getRowColumnType() const; - - Operation getOperation() const; - - int32_t getWindowIndex() const; - - void setWindowIndex(const int32_t windowIndex); - - // ADD_NEW_METHODS_HERE - - private: - TileTabsConfiguration* m_tileTabsConfiguration; - - const int32_t m_rowColumnIndex; - - const RowColumnType m_rowColumnType; - - const Operation m_operation; - - int32_t m_windowIndex = -1; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ - // -#endif // __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ - -} // namespace -#endif //__EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ diff -Nru connectome-workbench-1.4.2/src/Common/EventTileTabsGridConfigurationModification.cxx connectome-workbench-1.5.0/src/Common/EventTileTabsGridConfigurationModification.cxx --- connectome-workbench-1.4.2/src/Common/EventTileTabsGridConfigurationModification.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventTileTabsGridConfigurationModification.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,129 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_DECLARE__ +#include "EventTileTabsGridConfigurationModification.h" +#undef __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventTileTabsGridConfigurationModification + * \brief Event for modifying a grid tile tabs configuration. + * \ingroup Common + */ + +/** + * Constructor. + * + * @param tileTabsConfiguration + * Tile tabs configuration that will be modified + * @param rowColumnIndex + * Index of the row or column used for modification + * @param rowColumnType + * 'Row' or "Column' type + * @param operation + * Enumerated type with type of modification. + */ +EventTileTabsGridConfigurationModification::EventTileTabsGridConfigurationModification(TileTabsLayoutGridConfiguration* tileTabsConfiguration, + const int32_t rowColumnIndex, + const RowColumnType rowColumnType, + const Operation operation) +: Event(EventTypeEnum::EVENT_TILE_TABS_MODIFICATION), +m_tileTabsConfiguration(tileTabsConfiguration), +m_rowColumnIndex(rowColumnIndex), +m_rowColumnType(rowColumnType), +m_operation(operation), +m_windowIndex(-1) +{ + CaretAssert(m_tileTabsConfiguration); +} + +/** + * Destructor. + */ +EventTileTabsGridConfigurationModification::~EventTileTabsGridConfigurationModification() +{ +} + +/** + * @return Tile tabs configuration that is modified. + */ +TileTabsLayoutGridConfiguration* +EventTileTabsGridConfigurationModification::getTileTabsConfiguration() +{ + return m_tileTabsConfiguration; +} + + +/** + * @return Index of the window. + */ +int32_t +EventTileTabsGridConfigurationModification::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * Set the window index. + * + * @param windowIndex + * Index of the window. + */ +void +EventTileTabsGridConfigurationModification::setWindowIndex(const int32_t windowIndex) +{ + m_windowIndex = windowIndex; +} + +/** + * @return The index of the row or column. + */ +int32_t +EventTileTabsGridConfigurationModification::getRowColumnIndex() const +{ + return m_rowColumnIndex; +} + +/** + * @return The type ROW or COLUMN + */ +EventTileTabsGridConfigurationModification::RowColumnType +EventTileTabsGridConfigurationModification::getRowColumnType() const +{ + return m_rowColumnType; +} + +/** + * @return The operation. + */ +EventTileTabsGridConfigurationModification::Operation +EventTileTabsGridConfigurationModification::getOperation() const +{ + return m_operation; +} + diff -Nru connectome-workbench-1.4.2/src/Common/EventTileTabsGridConfigurationModification.h connectome-workbench-1.5.0/src/Common/EventTileTabsGridConfigurationModification.h --- connectome-workbench-1.4.2/src/Common/EventTileTabsGridConfigurationModification.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventTileTabsGridConfigurationModification.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,105 @@ +#ifndef __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_H__ +#define __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class TileTabsLayoutGridConfiguration; + + class EventTileTabsGridConfigurationModification : public Event { + + public: + /** + * Row or Column + */ + enum class RowColumnType { + COLUMN, + ROW + }; + + /** + * Operation + */ + enum class Operation { + DELETE_IT, /* DELETE is reserved word on Windows */ + DUPLICATE_AFTER, + DUPLICATE_BEFORE, + INSERT_SPACER_BEFORE, + INSERT_SPACER_AFTER, + MOVE_AFTER, + MOVE_BEFORE + }; + + EventTileTabsGridConfigurationModification(TileTabsLayoutGridConfiguration* tileTabsConfiguration, + const int32_t rowColumnIndex, + const RowColumnType rowColumnType, + const Operation operation); + + virtual ~EventTileTabsGridConfigurationModification(); + + EventTileTabsGridConfigurationModification(const EventTileTabsGridConfigurationModification&) = delete; + + EventTileTabsGridConfigurationModification& operator=(const EventTileTabsGridConfigurationModification&) = delete; + + TileTabsLayoutGridConfiguration* getTileTabsConfiguration(); + + int32_t getRowColumnIndex() const; + + RowColumnType getRowColumnType() const; + + Operation getOperation() const; + + int32_t getWindowIndex() const; + + void setWindowIndex(const int32_t windowIndex); + + // ADD_NEW_METHODS_HERE + + private: + TileTabsLayoutGridConfiguration* m_tileTabsConfiguration; + + const int32_t m_rowColumnIndex; + + const RowColumnType m_rowColumnType; + + const Operation m_operation; + + int32_t m_windowIndex = -1; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_DECLARE__ + // +#endif // __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_DECLARE__ + +} // namespace +#endif //__EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_H__ diff -Nru connectome-workbench-1.4.2/src/Common/EventTypeEnum.cxx connectome-workbench-1.5.0/src/Common/EventTypeEnum.cxx --- connectome-workbench-1.4.2/src/Common/EventTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -78,9 +78,9 @@ "EVENT_ANNOTATION_CHART_LABEL_GET", "Event to get annotation chart labels")); - enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_COLOR_BAR_GET, - "EVENT_ANNOTATION_COLOR_BAR_GET", - "Event to get annotation color bars from tab(s)")); + enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_BARS_GET, + "EVENT_ANNOTATION_BARS_GET", + "Event to get annotation color and scale bars from tab(s)")); enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_CREATE_NEW_TYPE, "EVENT_ANNOTATION_CREATE_NEW_TYPE", @@ -98,6 +98,10 @@ "EVENT_ANNOTATION_GROUPING", "Event for annotation grouping")); + enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_TEXT_GET_BOUNDS, + "EVENT_ANNOTATION_TEXT_GET_BOUNDS", + "Get bounds for annotation text")); + enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_TEXT_SUBSTITUTION_GET, "EVENT_ANNOTATION_TEXT_SUBSTITUTION_GET", "Get an annotation text substitution")); @@ -110,6 +114,10 @@ "EVENT_ANNOTATION_TOOLBAR_UPDATE", "Event to update annotation toolbar")); + enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_VALIDATE, + "EVENT_ANNOTATION_VALIDATE", + "Verify that an annotation is valid (valid pointer to annotation")); + enumData.push_back(EventTypeEnum(EVENT_BRAIN_RESET, "EVENT_BRAIN_RESET", "Brain has been reset")); @@ -118,11 +126,23 @@ "EVENT_BRAIN_STRUCTURE_GET_ALL", "Get all brain structures")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_CLOSE, + "EVENT_BROWSER_TAB_CLOSE", + "Close a browser tab, may reopen later")); + + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_CLOSE_IN_TOOL_BAR, + "EVENT_BROWSER_TAB_CLOSE_IN_TOOL_BAR", + "Close (for possible reopening) a browser tab in the tool bar")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_DELETE, "EVENT_BROWSER_TAB_DELETE", "Delete a browser tab")); - enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_GET, + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_DELETE_IN_TOOL_BAR, + "EVENT_BROWSER_TAB_DELETE_IN_TOOL_BAR", + "Delete a browser tab in the tool bar")); + + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_GET, "EVENT_BROWSER_TAB_GET", "Get a browser tab by number")); @@ -138,14 +158,34 @@ "EVENT_BROWSER_TAB_INDICES_GET_ALL", "Browser Tab Indices Get All")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED, + "EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED", + "Browser Tab Indices Get All Viewed")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_NEW, "EVENT_BROWSER_TAB_NEW", "Create a browser tab")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_NEW_IN_GUI, + "EVENT_BROWSER_TAB_NEW_IN_GUI", + "Create a browser tab from/in the GUI so toolbar is updated")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_NEW_CLONE, "EVENT_BROWSER_TAB_NEW_CLONE", "Create a browser tab by cloning an existing browser tab")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_REOPEN_AVAILBLE, + "EVENT_BROWSER_TAB_REOPEN_AVAILBLE", + "Event for getting available closed browser tab(s)")); + + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_REOPEN_CLOSED, + "EVENT_BROWSER_TAB_REOPEN_CLOSED", + "Event for reopening a closed browser tab")); + + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_SELECT_IN_WINDOW, + "EVENT_BROWSER_TAB_SELECT_IN_WINDOW", + "Event for selecting a browser tab in a window")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_CONTENT, "EVENT_BROWSER_WINDOW_CONTENT", "Event for browser window content")); @@ -158,6 +198,10 @@ "EVENT_BROWSER_WINDOW_CREATE_TABS", "Create tabs (if needed) after loading data files")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_GET_TABS, + "EVENT_BROWSER_WINDOW_GET_TABS", + "Get the tabs in a browser window")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN, "EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN", "A Browser Window's graphics have been redrawn")); @@ -210,22 +254,23 @@ "EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME", "Delete an OpenGL Texture Name for an OpenGL Context")); - enumData.push_back(EventTypeEnum(EVENT_CHART_OVERLAY_VALIDATE, - "EVENT_CHART_OVERLAY_VALIDATE", - "Validate a chart overlay for validity (it exists)")); - - enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_ATTRIBUTES_CHANGED, - "EVENT_CHART_TWO_ATTRIBUTES_CHANGED", - "GUI notification of the change in chart two atttributes")); - - enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_AXIS_GET_DATA_RANGE, - "EVENT_CHART_TWO_AXIS_GET_DATA_RANGE", - "Get the range of data for a chart two axis")); + enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP, + "EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP", + "Get a cartesian axis for a display group")); + + enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING, + "EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING", + "Get/set/query chart two cartesian axes yoking")); + enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA, "EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA", "Load line series data for chart two implementation")); + enumData.push_back(EventTypeEnum(EVENT_CHART_TWO_OVERLAY_VALIDATE, + "EVENT_CHART_TWO_OVERLAY_VALIDATE", + "Validate a chart two overlay for validity (it exists)")); + enumData.push_back(EventTypeEnum(EVENT_DATA_FILE_ADD, "EVENT_DATA_FILE_ADD", "Add a data file to the Brain")); @@ -242,6 +287,10 @@ "EVENT_DATA_FILE_RELOAD", "Reopen a data file (replace it with saved version) in the Brain")); + enumData.push_back(EventTypeEnum(EVENT_DATA_FILE_RELOAD_ALL, + "EVENT_DATA_FILE_RELOAD_ALL", + "Reopen all data files (replace it with saved version) in the Brain")); + enumData.push_back(EventTypeEnum(EVENT_GET_DISPLAYED_DATA_FILES, "EVENT_GET_DISPLAYED_DATA_FILES", "Get data files displayed in windows/tabs")); @@ -258,6 +307,10 @@ "EVENT_GET_TEXT_RENDERER_FOR_WINDOW", "Get the text renderer for a window")); + enumData.push_back(EventTypeEnum(EVENT_GET_USER_INPUT_MODE, + "EVENT_GET_USER_INPUT_MODE", + "Get the user input mode for a window")); + enumData.push_back(EventTypeEnum(EVENT_GET_VIEWPORT_SIZE, "EVENT_GET_VIEWPORT_SIZE", "Get the viewport size")); @@ -274,6 +327,10 @@ "EVENT_GRAPHICS_UPDATE_ONE_WINDOW", "Update graphics in one window")); + enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP, + "EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP", + "Show tooltip in graphics window")); + enumData.push_back(EventTypeEnum(EVENT_HELP_VIEWER_DISPLAY, "EVENT_HELP_VIEWER_DISPLAY", "Display the help viewer")); @@ -294,10 +351,6 @@ "EVENT_IMAGE_CAPTURE", "Capture an Image of Browser Window Graphics Region")); - enumData.push_back(EventTypeEnum(EVENT_MAC_DOCK_MENU_UPDATE, - "EVENT_MAC_DOCK_MENU_UPDATE", - "Update the Mac Dock Menu")); - enumData.push_back(EventTypeEnum(EVENT_MAP_YOKING_SELECT_MAP, "EVENT_MAP_YOKING_SELECT_MAP", "Map Yoking Select Map")); @@ -306,6 +359,10 @@ "EVENT_MAP_YOKING_VALIDATION", "Map Yoking Validation")); + enumData.push_back(EventTypeEnum(EVENT_MEDIA_FILES_GET, + "EVENT_MEDIA_FILES_GET", + "Get media files")); + enumData.push_back(EventTypeEnum(EVENT_MODEL_ADD, "EVENT_MODEL_ADD", "Add a model")); @@ -362,6 +419,14 @@ "EVENT_PALETTE_GET_BY_NAME", "Read the selected files in a spec file")); + enumData.push_back(EventTypeEnum(EVENT_PALETTE_GROUPS_GET, + "EVENT_PALETTE_GROUPS_GET", + "Get all palette groups")); + + enumData.push_back(EventTypeEnum(EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE, + "EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE", + "Get file system access mode for recent files")); + enumData.push_back(EventTypeEnum(EVENT_SCENE_ACTIVE, "EVENT_SCENE_ACTIVE", "Get/Set the active scene")); @@ -394,11 +459,15 @@ "EVENT_TILE_TABS_MODIFICATION", "Tile tabs modification")); + enumData.push_back(EventTypeEnum(EVENT_TOOLBAR_CHART_ORIENTED_AXES_UPDATE, + "EVENT_TOOLBAR_CHART_ORIENTED_AXES_UPDATE", + "Update the toolbar's chart two oriented axes controls")); + enumData.push_back(EventTypeEnum(EVENT_TOOLBOX_SELECTION_DISPLAY, "EVENT_TOOLBOX_SELECTION_DISPLAY", "Display or hide the selection toolbox")); - - enumData.push_back(EventTypeEnum(EVENT_USER_INTERFACE_UPDATE, + + enumData.push_back(EventTypeEnum(EVENT_USER_INTERFACE_UPDATE, "EVENT_USER_INTERFACE_UPDATE", "Update the user-interface")); diff -Nru connectome-workbench-1.4.2/src/Common/EventTypeEnum.h connectome-workbench-1.5.0/src/Common/EventTypeEnum.h --- connectome-workbench-1.4.2/src/Common/EventTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/EventTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -44,8 +44,8 @@ EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE, /** Get annnotation chart labels */ EVENT_ANNOTATION_CHART_LABEL_GET, - /** Get color bars from tab(s) */ - EVENT_ANNOTATION_COLOR_BAR_GET, + /** Get color and scale bars from tab(s) */ + EVENT_ANNOTATION_BARS_GET, /** Annotation create new of a particular type */ EVENT_ANNOTATION_CREATE_NEW_TYPE, /** Get the annotations drawn in a window */ @@ -54,36 +54,58 @@ EVENT_ANNOTATION_GROUP_GET_WITH_KEY, /** Annotation grouping (group, regroup, ungroup) operation */ EVENT_ANNOTATION_GROUPING, + /** Get the bounds of annotation text */ + EVENT_ANNOTATION_TEXT_GET_BOUNDS, /** Get annotation text substitutions */ EVENT_ANNOTATION_TEXT_SUBSTITUTION_GET, /** Invalid annotation text substitutions */ EVENT_ANNOTATION_TEXT_SUBSTITUTION_INVALIDATE, /** Annotation toolbar update */ EVENT_ANNOTATION_TOOLBAR_UPDATE, + /** Test a pointer to an annotation to verify that it is still valid (exists) */ + EVENT_ANNOTATION_VALIDATE, /** Inform that Brain has been reset (new spec or scene loaded) */ EVENT_BRAIN_RESET, /** Get all brain structures */ EVENT_BRAIN_STRUCTURE_GET_ALL, + /** Close a browser tab but may reopen later. */ + EVENT_BROWSER_TAB_CLOSE, + /** Close (for possible later reopening) a browser tab from within the toolbar */ + EVENT_BROWSER_TAB_CLOSE_IN_TOOL_BAR, /** Delete a browser tab. */ EVENT_BROWSER_TAB_DELETE, + /** Delete a browser tab from within the toolbar */ + EVENT_BROWSER_TAB_DELETE_IN_TOOL_BAR, /** Get a browser tab by tab number */ EVENT_BROWSER_TAB_GET, /** Get indices of all valid browser tabs */ EVENT_BROWSER_TAB_INDICES_GET_ALL, + /** Get indices of all viewed browser tabs */ + EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED, /** Get ALL (both viewed and not viewed) browser tabs */ EVENT_BROWSER_TAB_GET_ALL, /** Get ALL VIEWED browser tabs (tabs that are viewed in windows) */ EVENT_BROWSER_TAB_GET_ALL_VIEWED, /** Create a new browser tab */ EVENT_BROWSER_TAB_NEW, + /** Create a new browser tab from code within the GUI so toolbar is updated */ + EVENT_BROWSER_TAB_NEW_IN_GUI, /** Create a new browser tab by cloning an existing browser tab */ EVENT_BROWSER_TAB_NEW_CLONE, + /** Reopen a closed tab */ + EVENT_BROWSER_TAB_REOPEN_AVAILBLE, + /** Reopen a closed tab */ + EVENT_BROWSER_TAB_REOPEN_CLOSED, + /** Select a tab in a window */ + EVENT_BROWSER_TAB_SELECT_IN_WINDOW, /** Event for browser window content */ EVENT_BROWSER_WINDOW_CONTENT, /** Get the content of a browser window */ EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET, /** Create tabs after loading a file */ EVENT_BROWSER_WINDOW_CREATE_TABS, + /** Get tabs in a browser window */ + EVENT_BROWSER_WINDOW_GET_TABS, /** Issued after a browser window's graphicshave been redrawn */ EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN, /** Issued when displayed browser window menu's may change */ @@ -104,14 +126,14 @@ EVENT_CARET_PREFERENCES_GET, /** Event for yoking the loading of matrix chart rows/columns */ EVENT_CHART_MATRIX_YOKING_VALIDATION, - /** Validate that chart overlay is valid (it exists). */ - EVENT_CHART_OVERLAY_VALIDATE, - /** GUI notification of the change in chart two atttributes */ - EVENT_CHART_TWO_ATTRIBUTES_CHANGED, - /** Get the range of data for a chart two axis */ - EVENT_CHART_TWO_AXIS_GET_DATA_RANGE, + /** Get a chart cartesian axis for a display group */ + EVENT_CHART_TWO_CARTEISAN_AXIS_DISPLAY_GROUP, + /** Event for yoking of chart two cartesian oriented axes */ + EVENT_CHART_TWO_CARTESIAN_ORIENTED_AXES_YOKING, /** Load chart two line series data */ EVENT_CHART_TWO_LOAD_LINE_SERIES_DATA, + /** Validate that chart two overlay is valid (it exists). */ + EVENT_CHART_TWO_OVERLAY_VALIDATE, /** Add a data file into the Brain*/ EVENT_DATA_FILE_ADD, /** Delete a data file from the brain */ @@ -120,6 +142,8 @@ EVENT_DATA_FILE_READ, /** Reload (replace) a data file with its saved version in the brain*/ EVENT_DATA_FILE_RELOAD, + /** Reload (replace) a data file with its saved version in the brain*/ + EVENT_DATA_FILE_RELOAD_ALL, /** Get data files that are display in windows/tabs */ EVENT_GET_DISPLAYED_DATA_FILES, /** Get node data files */ @@ -128,6 +152,8 @@ EVENT_GET_OR_SET_USER_INPUT_MODE, /** Get the text renderer for a window */ EVENT_GET_TEXT_RENDERER_FOR_WINDOW, + /** Get the user input mode for a window */ + EVENT_GET_USER_INPUT_MODE, /** Get the viewport size for model, tab, window */ EVENT_GET_VIEWPORT_SIZE, /** Create a buffer object for an OpenGL context */ @@ -144,6 +170,8 @@ EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, /** Update graphics in a window */ EVENT_GRAPHICS_UPDATE_ONE_WINDOW, + /** Show tooltip in graphics window */ + EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP, /** Display the help viewer */ EVENT_HELP_VIEWER_DISPLAY, /** Highlight location when an identification occurs */ @@ -154,12 +182,12 @@ EVENT_IDENTIFICATION_SYMBOL_REMOVAL, /** Browser window image capture */ EVENT_IMAGE_CAPTURE, - /** Update the Mac Dock Menu */ - EVENT_MAC_DOCK_MENU_UPDATE, /** Validate when adding a mapped file to mapped yoking */ EVENT_MAP_YOKING_SELECT_MAP, /** Select a map for mapped yoked files */ EVENT_MAP_YOKING_VALIDATION, + /** Get media files */ + EVENT_MEDIA_FILES_GET, /** model - ADD */ EVENT_MODEL_ADD, /** model - DELETE */ @@ -188,6 +216,10 @@ EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW, /** Get a palette by name from a palette file */ EVENT_PALETTE_GET_BY_NAME, + /** Get palette groups */ + EVENT_PALETTE_GROUPS_GET, + /** Get the file system access mode for recent files */ + EVENT_RECENT_FILES_SYSTEM_ACCESS_MODE, /** Get the active scene */ EVENT_SCENE_ACTIVE, /** Show a dialog containing warnings encountered when reading data files */ @@ -204,6 +236,8 @@ EVENT_SURFACE_STRUCTURES_VALID_GET, /** Tile tabs modification */ EVENT_TILE_TABS_MODIFICATION, + /** Update the toolbar's chart two oriented axes controls */ + EVENT_TOOLBAR_CHART_ORIENTED_AXES_UPDATE, /** Display/Hide the selection toolbox */ EVENT_TOOLBOX_SELECTION_DISPLAY, /** Update the User-Interface */ diff -Nru connectome-workbench-1.4.2/src/Common/FileInformation.cxx connectome-workbench-1.5.0/src/Common/FileInformation.cxx --- connectome-workbench-1.4.2/src/Common/FileInformation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/FileInformation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -751,6 +751,15 @@ } /** + * @return Date/Time last modified + */ +QDateTime +FileInformation::getLastModified() const +{ + return m_fileInfo.lastModified(); +} + +/** * Get a description of this object's content. * @return String describing this object's content. */ diff -Nru connectome-workbench-1.4.2/src/Common/FileInformation.h connectome-workbench-1.5.0/src/Common/FileInformation.h --- connectome-workbench-1.4.2/src/Common/FileInformation.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/FileInformation.h 2021-02-16 19:46:47.000000000 +0000 @@ -88,6 +88,8 @@ AString getLastDirectory() const; + QDateTime getLastModified() const; + void getFileComponents(AString& absolutePathOut, AString& fileNameWithoutExtensionOut, AString& extensionWithoutDotOut) const; diff -Nru connectome-workbench-1.4.2/src/Common/FileOpenFromOpSysTypeEnum.cxx connectome-workbench-1.5.0/src/Common/FileOpenFromOpSysTypeEnum.cxx --- connectome-workbench-1.4.2/src/Common/FileOpenFromOpSysTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/FileOpenFromOpSysTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,380 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __FILE_OPEN_FROM_OP_SYS_TYPE_ENUM_DECLARE__ +#include "FileOpenFromOpSysTypeEnum.h" +#undef __FILE_OPEN_FROM_OP_SYS_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::FileOpenFromOpSysTypeEnum + * \brief Type for how to process a file open request received from the operating sytems + * + * A file open request from the operating system normally occurs when + * the user double-clicks a file in the OS such as MacOS Finder + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_fileOpenFromOpSysTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void fileOpenFromOpSysTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "FileOpenFromOpSysTypeEnum.h" + * + * Instatiate: + * m_fileOpenFromOpSysTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_fileOpenFromOpSysTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_fileOpenFromOpSysTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(fileOpenFromOpSysTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_fileOpenFromOpSysTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const FileOpenFromOpSysTypeEnum::Enum VARIABLE = m_fileOpenFromOpSysTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +FileOpenFromOpSysTypeEnum::FileOpenFromOpSysTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +FileOpenFromOpSysTypeEnum::~FileOpenFromOpSysTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +FileOpenFromOpSysTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(FileOpenFromOpSysTypeEnum(ASK_USER, + "ASK_USER", + "Ask User")); + + enumData.push_back(FileOpenFromOpSysTypeEnum(IN_CURRENT_WB_VIEW, + "IN_CURRENT_WB_VIEW", + "In Current wb_view")); + + enumData.push_back(FileOpenFromOpSysTypeEnum(IN_NEW_WB_VIEW, + "IN_NEW_WB_VIEW", + "In New wb_view")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const FileOpenFromOpSysTypeEnum* +FileOpenFromOpSysTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const FileOpenFromOpSysTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +FileOpenFromOpSysTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const FileOpenFromOpSysTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +FileOpenFromOpSysTypeEnum::Enum +FileOpenFromOpSysTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FileOpenFromOpSysTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FileOpenFromOpSysTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type FileOpenFromOpSysTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +FileOpenFromOpSysTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const FileOpenFromOpSysTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +FileOpenFromOpSysTypeEnum::Enum +FileOpenFromOpSysTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FileOpenFromOpSysTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FileOpenFromOpSysTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type FileOpenFromOpSysTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +FileOpenFromOpSysTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const FileOpenFromOpSysTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +FileOpenFromOpSysTypeEnum::Enum +FileOpenFromOpSysTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FileOpenFromOpSysTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FileOpenFromOpSysTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type FileOpenFromOpSysTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +FileOpenFromOpSysTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +FileOpenFromOpSysTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(FileOpenFromOpSysTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +FileOpenFromOpSysTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(FileOpenFromOpSysTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/FileOpenFromOpSysTypeEnum.h connectome-workbench-1.5.0/src/Common/FileOpenFromOpSysTypeEnum.h --- connectome-workbench-1.4.2/src/Common/FileOpenFromOpSysTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/FileOpenFromOpSysTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __FILE_OPEN_FROM_OP_SYS_TYPE_ENUM_H__ +#define __FILE_OPEN_FROM_OP_SYS_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class FileOpenFromOpSysTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Ask the user */ + ASK_USER, + /** Open file in a currently running instance of wb_view */ + IN_CURRENT_WB_VIEW, + /** Open file in a new instance of wb_view */ + IN_NEW_WB_VIEW + }; + + + ~FileOpenFromOpSysTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + FileOpenFromOpSysTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const FileOpenFromOpSysTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __FILE_OPEN_FROM_OP_SYS_TYPE_ENUM_DECLARE__ +std::vector FileOpenFromOpSysTypeEnum::enumData; +bool FileOpenFromOpSysTypeEnum::initializedFlag = false; +int32_t FileOpenFromOpSysTypeEnum::integerCodeCounter = 0; +#endif // __FILE_OPEN_FROM_OP_SYS_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__FILE_OPEN_FROM_OP_SYS_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/FloatMatrix.cxx connectome-workbench-1.5.0/src/Common/FloatMatrix.cxx --- connectome-workbench-1.4.2/src/Common/FloatMatrix.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/FloatMatrix.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -291,6 +291,19 @@ offset[0] = m_matrix[0][3]; offset[1] = m_matrix[1][3]; offset[2] = m_matrix[2][3]; } +Vector3D FloatMatrix::transformPoint(const Vector3D& coordIn) const +{ + Vector3D ret; + if (m_matrix.size() < 3 || m_matrix.size() > 4 || m_matrix[0].size() != 4) + { + throw CaretException("transformPoint called on incorrectly sized matrix"); + } + ret[0] = coordIn[0] * m_matrix[0][0] + coordIn[1] * m_matrix[0][1] + coordIn[2] * m_matrix[0][2] + m_matrix[0][3]; + ret[1] = coordIn[0] * m_matrix[1][0] + coordIn[1] * m_matrix[1][1] + coordIn[2] * m_matrix[1][2] + m_matrix[1][3]; + ret[2] = coordIn[0] * m_matrix[2][0] + coordIn[1] * m_matrix[2][1] + coordIn[2] * m_matrix[2][2] + m_matrix[2][3]; + return ret; +} + FloatMatrix FloatMatrix::operator-() const { int64_t rows, cols; diff -Nru connectome-workbench-1.4.2/src/Common/FloatMatrix.h connectome-workbench-1.5.0/src/Common/FloatMatrix.h --- connectome-workbench-1.4.2/src/Common/FloatMatrix.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/FloatMatrix.h 2021-02-16 19:46:47.000000000 +0000 @@ -101,6 +101,8 @@ const std::vector >& getMatrix() const; ///separate 3x4 or 4x4 into Vector3Ds, throw on wrong dimensions void getAffineVectors(Vector3D& xvec, Vector3D& yvec, Vector3D& zvec, Vector3D& offset) const; + ///convenience for use as affine + Vector3D transformPoint(const Vector3D& coordIn) const; ///get number of rows int64_t getNumberOfRows() const { return (int64_t)m_matrix.size(); } ///get number of columns diff -Nru connectome-workbench-1.4.2/src/Common/HtmlTableBuilder.cxx connectome-workbench-1.5.0/src/Common/HtmlTableBuilder.cxx --- connectome-workbench-1.4.2/src/Common/HtmlTableBuilder.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/HtmlTableBuilder.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,293 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __HTML_TABLE_BUILDER_DECLARE__ +#include "HtmlTableBuilder.h" +#undef __HTML_TABLE_BUILDER_DECLARE__ + +#include + +#include "CaretAssert.h" + +using namespace caret; + + + +/** + * \class caret::HtmlTableBuilder + * \brief Helps create an HTML table + * \ingroup Common + */ + +/** + * Constructor. + * @param htmlVersion + * Version of HTML + * @param numberOfColumns + * Number of columns in the table + */ +HtmlTableBuilder::HtmlTableBuilder(const HtmlVersion htmlVersion, + const int32_t numberOfColumns) +: CaretObject(), +m_htmlVersion(htmlVersion), +m_numberOfColumns(numberOfColumns) +{ + +} + +/** + * Destructor. + */ +HtmlTableBuilder::~HtmlTableBuilder() +{ +} + + +/** + * Set the title of the table using bold text + * @param title + * Title displayed spanning all rows at top of table + */ +void +HtmlTableBuilder::setTitleBold(const QString& title) +{ + m_title = title; + m_titleStyle = TitleStyle::BOLD; +} + +/** + * Set the title of the table using plain text + * @param title + * Title displayed spanning all rows at top of table + */ +void +HtmlTableBuilder::setTitlePlain(const QString& title) +{ + m_title = title; + m_titleStyle = TitleStyle::PLAIN; +} + +/** + * Add data as header columns into a table row + * + * @param textColOne + * Data for first column + * @param textColTwo + * Data for second column + * @param textColThree + * Data for third column + * @param textColFour + * Data for fourth column + * @param textColFive + * Data for fifth column + */ +void +HtmlTableBuilder::addHeaderRow(const QString& textColOne, + const QString& textColTwo, + const QString& textColThree, + const QString& textColFour, + const QString& textColFive) +{ + addRowPrivate("th", + textColOne, + textColTwo, + textColThree, + textColFour, + textColFive); +} + +/** + * Add data as columns into a table row + * + * @param textColOne + * Data for first column + * @param textColTwo + * Data for second column + * @param textColThree + * Data for third column + * @param textColFour + * Data for fourth column + * @param textColFive + * Data for fifth column + */ +void +HtmlTableBuilder::addRow(const QString& textColOne, + const QString& textColTwo, + const QString& textColThree, + const QString& textColFour, + const QString& textColFive) +{ + addRowPrivate("td", + textColOne, + textColTwo, + textColThree, + textColFour, + textColFive); +} + +/** + * Add data as columns into a table row + * + * @param rowElementTag + * The tag for the row element + * @param textColOne + * Data for first column + * @param textColTwo + * Data for second column + * @param textColThree + * Data for third column + * @param textColFour + * Data for fourth column + * @param textColFive + * Data for fifth column + */ +void +HtmlTableBuilder::addRowPrivate(const QString& rowElementTag, + const QString& textColOne, + const QString& textColTwo, + const QString& textColThree, + const QString& textColFour, + const QString& textColFive) +{ + QString tr(""); + + addTableDataElementToRow(tr, + rowElementTag, + textColOne); + + if (m_numberOfColumns >= 2) { + addTableDataElementToRow(tr, + rowElementTag, + textColTwo); + } + + if (m_numberOfColumns >= 3) { + addTableDataElementToRow(tr, + rowElementTag, + textColThree); + } + + if (m_numberOfColumns >= 4) { + addTableDataElementToRow(tr, + rowElementTag, + textColFour); + } + + if (m_numberOfColumns >= 5) { + addTableDataElementToRow(tr, + rowElementTag, + textColFive); + } + + tr.append(""); + + m_tableRows.push_back(tr); +} + + +/** + * Add table data element to a row + * @param tableRow + * The table row + * @param rowElementTag + * The tag for the row element + * @param columnData + * Data to add to row + */ +void +HtmlTableBuilder::addTableDataElementToRow(QString& tableRow, + const QString& rowElementTag, + const QString& columnData) +{ + tableRow.append(" <" + rowElementTag + " align=\"left\">" + + columnData + + "\n"); + + m_modifiedSinceLastUpdatedFlag = true; +} + +/** + * @return The table in HTML table format + */ +QString +HtmlTableBuilder::getAsHtmlTable() const +{ + QString html; + + switch (m_htmlVersion) { + case V4_01: + html = getAsHtmlTableV401(); + break; + } + + return html; +} + +/** + * @return The table in HTML 4.01 table format + */ +QString +HtmlTableBuilder::getAsHtmlTableV401() const +{ + if (m_tableRows.empty()) { + return ""; + } + + /* + * Cache the HTML and only update when a data has been added + */ + if (m_modifiedSinceLastUpdatedFlag) { + m_modifiedSinceLastUpdatedFlag = false; + m_tableText.clear(); + + m_tableText.append("\n"); + + if (m_title != NULL) { + QString tdTH; + switch (m_titleStyle) { + case BOLD: + tdTH = "th"; + break; + case PLAIN: + tdTH = "td"; + break; + } + const QString s("<" + + tdTH + + " colspan=\"" + AString::number(m_numberOfColumns) + "\" align=\"left\">" + + m_title + + "\n"); + m_tableText.append(s); + } + + for (const auto& tr : m_tableRows) { + m_tableText.append(tr); + } + + m_tableText.append("
\n"); + m_tableText.append("

\n"); + } + + return m_tableText; +} + diff -Nru connectome-workbench-1.4.2/src/Common/HtmlTableBuilder.h connectome-workbench-1.5.0/src/Common/HtmlTableBuilder.h --- connectome-workbench-1.4.2/src/Common/HtmlTableBuilder.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/HtmlTableBuilder.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,115 @@ +#ifndef __HTML_TABLE_BUILDER_H__ +#define __HTML_TABLE_BUILDER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include + +#include "CaretObject.h" + +namespace caret { + class HtmlTableBuilder : public CaretObject { + + public: + /* + * Version of HTML + */ + enum HtmlVersion { + /** Version 4.01 */ + V4_01 + }; + + HtmlTableBuilder(const HtmlVersion htmlVersion, + const int32_t numberOfColumns); + + virtual ~HtmlTableBuilder(); + + HtmlTableBuilder(const HtmlTableBuilder&) = delete; + + HtmlTableBuilder& operator=(const HtmlTableBuilder&) = delete; + + void setTitleBold(const QString& title); + + void setTitlePlain(const QString& title); + + void addHeaderRow(const QString& textColOne, + const QString& textColTwo = "", + const QString& textColThree = "", + const QString& textColFour = "", + const QString& textColFive = ""); + + void addRow(const QString& textColOne, + const QString& textColTwo = "", + const QString& textColThree = "", + const QString& textColFour = "", + const QString& textColFive = ""); + + QString getAsHtmlTable() const; + + // ADD_NEW_METHODS_HERE + + private: + enum TitleStyle { + BOLD, + PLAIN + }; + + void addTableDataElementToRow(QString& tableRow, + const QString& rowElementTag, + const QString& columnData); + + QString getAsHtmlTableV401() const; + + void addRowPrivate(const QString& rowElementTag, + const QString& textColOne, + const QString& textColTwo = "", + const QString& textColThree = "", + const QString& textColFour = "", + const QString& textColFive = ""); + + const HtmlVersion m_htmlVersion; + + const int32_t m_numberOfColumns; + + QString m_title; + + TitleStyle m_titleStyle = TitleStyle::PLAIN; + + std::vector m_tableRows; + + mutable QString m_tableText; + + mutable bool m_modifiedSinceLastUpdatedFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __HTML_TABLE_BUILDER_DECLARE__ + // +#endif // __HTML_TABLE_BUILDER_DECLARE__ + +} // namespace +#endif //__HTML_TABLE_BUILDER_H__ + diff -Nru connectome-workbench-1.4.2/src/Common/IdentificationDisplayModeEnum.cxx connectome-workbench-1.5.0/src/Common/IdentificationDisplayModeEnum.cxx --- connectome-workbench-1.4.2/src/Common/IdentificationDisplayModeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/IdentificationDisplayModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,380 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __IDENTIFICATION_DISPLAY_MODE_ENUM_DECLARE__ +#include "IdentificationDisplayModeEnum.h" +#undef __IDENTIFICATION_DISPLAY_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::IdentificationDisplayModeEnum + * \brief Enumerated type for identification display opertions + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_identificationDisplayModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void identificationDisplayModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "IdentificationDisplayModeEnum.h" + * + * Instatiate: + * m_identificationDisplayModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_identificationDisplayModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_identificationDisplayModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(identificationDisplayModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_identificationDisplayModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const IdentificationDisplayModeEnum::Enum VARIABLE = m_identificationDisplayModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +IdentificationDisplayModeEnum::IdentificationDisplayModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +IdentificationDisplayModeEnum::~IdentificationDisplayModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +IdentificationDisplayModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(IdentificationDisplayModeEnum(DIALOG, + "DIALOG", + "Dialog")); + + enumData.push_back(IdentificationDisplayModeEnum(OVERLAY_TOOLBOX, + "OVERLAY_TOOLBOX", + "Overlay Toolbox")); + + enumData.push_back(IdentificationDisplayModeEnum(LEGACY_DIALOG, + "LEGACY_DIALOG", + "Legacy Dialog")); + + enumData.push_back(IdentificationDisplayModeEnum(DEBUG_MODE, + "DEBUG_MODE", + "Debug (Old and New Dialogs)")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const IdentificationDisplayModeEnum* +IdentificationDisplayModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const IdentificationDisplayModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +IdentificationDisplayModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const IdentificationDisplayModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +IdentificationDisplayModeEnum::Enum +IdentificationDisplayModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationDisplayModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationDisplayModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type IdentificationDisplayModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +IdentificationDisplayModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const IdentificationDisplayModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +IdentificationDisplayModeEnum::Enum +IdentificationDisplayModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationDisplayModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationDisplayModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type IdentificationDisplayModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +IdentificationDisplayModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const IdentificationDisplayModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +IdentificationDisplayModeEnum::Enum +IdentificationDisplayModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = IdentificationDisplayModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const IdentificationDisplayModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type IdentificationDisplayModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +IdentificationDisplayModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +IdentificationDisplayModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(IdentificationDisplayModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +IdentificationDisplayModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(IdentificationDisplayModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/IdentificationDisplayModeEnum.h connectome-workbench-1.5.0/src/Common/IdentificationDisplayModeEnum.h --- connectome-workbench-1.4.2/src/Common/IdentificationDisplayModeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/IdentificationDisplayModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,108 @@ +#ifndef __IDENTIFICATION_DISPLAY_MODE_ENUM_H__ +#define __IDENTIFICATION_DISPLAY_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class IdentificationDisplayModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** In Dialog */ + DIALOG, + /** In Overlay ToolBox */ + OVERLAY_TOOLBOX, + /** In Legacy (old) Dialog */ + LEGACY_DIALOG, + /** Show both new and old dialogs*/ + DEBUG_MODE + }; + + + ~IdentificationDisplayModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + IdentificationDisplayModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const IdentificationDisplayModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __IDENTIFICATION_DISPLAY_MODE_ENUM_DECLARE__ +std::vector IdentificationDisplayModeEnum::enumData; +bool IdentificationDisplayModeEnum::initializedFlag = false; +int32_t IdentificationDisplayModeEnum::integerCodeCounter = 0; +#endif // __IDENTIFICATION_DISPLAY_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_DISPLAY_MODE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/MathFunctionEnum.cxx connectome-workbench-1.5.0/src/Common/MathFunctionEnum.cxx --- connectome-workbench-1.4.2/src/Common/MathFunctionEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/MathFunctionEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -76,10 +76,10 @@ enumData.push_back(MathFunctionEnum(ATAN2, "atan2", "2 arguments, atan2(y, x) returns the inverse of tangent of (y/x), in radians, determining quadrant by the sign of both arguments")); enumData.push_back(MathFunctionEnum(SINH, "sinh", "1 argument, the hyperbolic sine of the argument")); enumData.push_back(MathFunctionEnum(COSH, "cosh", "1 argument, the hyperbolic cosine of the argument")); - enumData.push_back(MathFunctionEnum(TANH, "tanh", "1 argument, the hyperboloc tangent of the argument")); + enumData.push_back(MathFunctionEnum(TANH, "tanh", "1 argument, the hyperbolic tangent of the argument")); enumData.push_back(MathFunctionEnum(ASINH, "asinh", "1 argument, the inverse hyperbolic sine of the argument")); enumData.push_back(MathFunctionEnum(ACOSH, "acosh", "1 argument, the inverse hyperbolic cosine of the argument")); - enumData.push_back(MathFunctionEnum(ATANH, "atanh", "1 argument, the inverse hyperboloc tangent of the argument")); + enumData.push_back(MathFunctionEnum(ATANH, "atanh", "1 argument, the inverse hyperbolic tangent of the argument")); enumData.push_back(MathFunctionEnum(LN, "ln", "1 argument, the natural logarithm of the argument")); enumData.push_back(MathFunctionEnum(EXP, "exp", "1 argument, the constant e raised to the power of the argument")); enumData.push_back(MathFunctionEnum(LOG, "log", "1 argument, the base 10 logarithm of the argument")); diff -Nru connectome-workbench-1.4.2/src/Common/MathFunctionEnum.h connectome-workbench-1.5.0/src/Common/MathFunctionEnum.h --- connectome-workbench-1.4.2/src/Common/MathFunctionEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/MathFunctionEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -29,7 +29,7 @@ namespace caret { /** - * \brief Enumerated type for a structure in a brain. + * \brief Enumerated type for math functions. * * Enumerated types for math functions. */ diff -Nru connectome-workbench-1.4.2/src/Common/MathFunctions.cxx connectome-workbench-1.5.0/src/Common/MathFunctions.cxx --- connectome-workbench-1.4.2/src/Common/MathFunctions.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/MathFunctions.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -2705,3 +2705,33 @@ expandLinePercentage3D(bottomRight, topRight, extraSpacePercentage); } +/* + * Test if a given array of data are almost equal to a values. + * @param data + * The array of data + * @param numberOfElements + * Number of elements in array + * @param value + * Value for testing equal to + * @param tolerance + * Tolerance from value + * @return True if all values in 'data' are in range [value - tolerance, value + tolerance] + */ +bool +MathFunctions::compareValuesEqual(const float* data, + const int32_t numberOfElements, + const float value, + const float tolerance) { + const float minValue(value - tolerance); + const float maxValue(value + tolerance); + + for (int32_t i = 0; i < numberOfElements; i++) { + if ((data[i] < minValue) + || (data[i] > maxValue)) { + return false; + } + } + + return true; +} + diff -Nru connectome-workbench-1.4.2/src/Common/MathFunctions.h connectome-workbench-1.5.0/src/Common/MathFunctions.h --- connectome-workbench-1.4.2/src/Common/MathFunctions.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/MathFunctions.h 2021-02-16 19:46:47.000000000 +0000 @@ -357,6 +357,11 @@ const int32_t numElemets, const float tolerance); + static bool compareValuesEqual(const float* data, + const int32_t numberOfElements, + const float value, + const float tolerance); + static int32_t clamp( const int32_t value, const int32_t minimum, diff -Nru connectome-workbench-1.4.2/src/Common/Matrix4x4Interface.h connectome-workbench-1.5.0/src/Common/Matrix4x4Interface.h --- connectome-workbench-1.4.2/src/Common/Matrix4x4Interface.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/Matrix4x4Interface.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,65 @@ +#ifndef __MATRIX4X4_INTERFACE_H__ +#define __MATRIX4X4_INTERFACE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +/** + * \class caret::Matrix4x4Interface + * \brief Interface so that Matrix4x4 operations can be performed by modules that cannot access Matrix4x4 + * \ingroup Common + */ + + +#include + + + + + +namespace caret { + + class Matrix4x4Interface { + + public: + Matrix4x4Interface() { } + + virtual ~Matrix4x4Interface() { } + + virtual void multiplyPoint3(float p[3]) const = 0; + + Matrix4x4Interface(const Matrix4x4Interface&) { } + + Matrix4x4Interface& operator=(const Matrix4x4Interface&) { return *this; } + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __MATRIX4X4_INTERFACE_DECLARE__ + // +#endif // __MATRIX4X4_INTERFACE_DECLARE__ + +} // namespace +#endif //__MATRIX4X4_INTERFACE_H__ diff -Nru connectome-workbench-1.4.2/src/Common/ModelTransform.cxx connectome-workbench-1.5.0/src/Common/ModelTransform.cxx --- connectome-workbench-1.4.2/src/Common/ModelTransform.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/ModelTransform.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -70,12 +70,14 @@ for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { if (i == j) { - this->rotation[i][j] = 1.0; + this->rotation[i][j] = 1.0; this->obliqueRotation[i][j] = 1.0; + this->flatRotation[i][j] = 1.0; } else { - this->rotation[i][j] = 0.0; + this->rotation[i][j] = 0.0; this->obliqueRotation[i][j] = 0.0; + this->flatRotation[i][j] = 0.0; } } } @@ -212,6 +214,21 @@ } /** + * Get the flat rotation matrix. + * @param flatRotation + * Flat rotation matrix. + */ +void +ModelTransform::getFlatRotation(float flatRotation[4][4]) const +{ + for (int32_t i = 0; i < 4; i++) { + for (int32_t j = 0; j < 4; j++) { + flatRotation[i][j] = this->flatRotation[i][j]; + } + } +} + +/** * @return The scaling. */ float @@ -324,6 +341,22 @@ } /** + * Set the flat rotation + * @param flatRotation + * New value for flat rotation + */ +void +ModelTransform::setFlatRotation(const float flatRotation[4][4]) +{ + for (int32_t i = 0; i < 4; i++) { + for (int32_t j = 0; j < 4; j++) { + this->flatRotation[i][j] = flatRotation[i][j]; + } + } +} + + +/** * Set the offset for drawing the right cortex flat map. * * @param rightCortexFlatMapOffsetX @@ -364,9 +397,10 @@ /** * Returns the user view in a string that contains, - * separated by commas: View Name, translation[3], + * separated by commas: View Name, comment, translation[3], * rotation[4][4], scaling, obliqueRotation[4][4], - * and rightCortexFlatMapOffset[2]; + * flatRotation, + * and rightCortexFlatMapOffset[2], rightCortextFlatMapZoom */ AString ModelTransform::getAsString() const @@ -391,6 +425,12 @@ } } + for (int32_t i = 0; i < 4; i++) { + for (int32_t j = 0; j < 4; j++) { + s += (s_separatorInPreferences + AString::number(this->flatRotation[i][j])); + } + } + s += (s_separatorInPreferences + AString::number(this->rightCortexFlatMapOffsetXY[0])); s += (s_separatorInPreferences + AString::number(this->rightCortexFlatMapOffsetXY[1])); s += (s_separatorInPreferences + AString::number(this->rightCortexFlatMapZoomFactor)); @@ -400,15 +440,17 @@ /** * Set the user view from a string that contains, - * separated by commas: View Name, translation[3], + * separated by commas: View Name, comment, translation[3], * rotation[4][4], scaling, obliqueRotation[4][4], - * and rightCortexFlatMapOffset[2]; + * flatRotation, + * and rightCortexFlatMapOffset[2], rightCortextFlatMapZoom */ bool ModelTransform::setFromString(const AString& s) { bool hasComment = false; bool hasObliqueRotation = false; + bool hasFlatRotation = false; bool hasRightFlatMapOffset = false; bool hasRightFlatMapZoomFactor = false; @@ -417,7 +459,15 @@ sl = s.split(s_separatorInPreferences, QString::KeepEmptyParts); const int numElements = sl.count(); - if (numElements == 41) { + + if (numElements == 57) { + hasComment = true; + hasObliqueRotation = true; + hasFlatRotation = true; + hasRightFlatMapOffset = true; + hasRightFlatMapZoomFactor = true; + } + else if (numElements == 41) { hasComment = true; hasObliqueRotation = true; hasRightFlatMapOffset = true; @@ -436,7 +486,7 @@ hasComment = true; } else { - CaretLogSevere("User view string does not contain 22, 38, or 40 elements"); + CaretLogSevere("User view string does not contain 22, 38, 40, 41, or 42 elements"); return false; } } @@ -489,6 +539,26 @@ } } + if (hasFlatRotation) { + for (int32_t i = 0; i < 4; i++) { + for (int32_t j = 0; j < 4; j++) { + this->flatRotation[i][j] = sl.at(ctr++).toFloat(); + } + } + } + else { + for (int32_t i = 0; i < 4; i++) { + for (int32_t j = 0; j < 4; j++) { + if (i == j) { + this->flatRotation[i][j] = 1.0; + } + else { + this->flatRotation[i][j] = 0.0; + } + } + } + } + if (hasRightFlatMapOffset) { this->rightCortexFlatMapOffsetXY[0] = sl.at(ctr++).toFloat(); this->rightCortexFlatMapOffsetXY[1] = sl.at(ctr++).toFloat(); @@ -524,8 +594,9 @@ for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { - this->rotation[i][j] = modelTransform.rotation[i][j]; + this->rotation[i][j] = modelTransform.rotation[i][j]; this->obliqueRotation[i][j] = modelTransform.obliqueRotation[i][j]; + this->flatRotation[i][j] = modelTransform.flatRotation[i][j]; } } @@ -550,6 +621,8 @@ * 4x4 rotation matrix. * @param obliqueRotationMatrix * 4x4 oblique rotation matrix. + * @param floatRotationMatrixArray + * The flat rotation matrix * @param zoom * Zooming. * @param rightCortexFlatMapOffsetX @@ -565,6 +638,7 @@ const float panZ, const float rotationMatrix[4][4], const float obliqueRotationMatrix[4][4], + const float floatRotationMatrixArray[4][4], const float zoom, const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY, @@ -576,6 +650,8 @@ setObliqueRotation(obliqueRotationMatrix); + this->setFlatRotation(floatRotationMatrixArray); + this->setScaling(zoom); this->setRightCortexFlatMapOffset(rightCortexFlatMapOffsetX, @@ -595,6 +671,8 @@ * 4x4 rotation matrix. * @param obliqueRotationMatrix * 4x4 oblique rotation matrix. + * @param floatRotationMatrixArray + * The flat rotation matrix * @param zoom * Zooming. * @param rightCortexFlatMapOffsetX @@ -610,6 +688,7 @@ float& panZ, float rotationMatrix[4][4], float obliqueRotationMatrix[4][4], + float floatRotationMatrixArray[4][4], float& zoom, float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY, @@ -623,6 +702,8 @@ getObliqueRotation(obliqueRotationMatrix); + getFlatRotation(floatRotationMatrixArray); + zoom = getScaling(); getRightCortexFlatMapOffset(rightCortexFlatMapOffsetX, diff -Nru connectome-workbench-1.4.2/src/Common/ModelTransform.h connectome-workbench-1.5.0/src/Common/ModelTransform.h --- connectome-workbench-1.4.2/src/Common/ModelTransform.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/ModelTransform.h 2021-02-16 19:46:47.000000000 +0000 @@ -45,6 +45,8 @@ void getObliqueRotation(float obliqueRotation[4][4]) const; + void getFlatRotation(float flatRotation[4][4]) const; + void getRightCortexFlatMapOffset(float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY) const; @@ -68,6 +70,8 @@ void setObliqueRotation(const float obliqueRotation[4][4]); + void setFlatRotation(const float flatRotation[4][4]); + void setRightCortexFlatMapOffset(const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY); @@ -80,6 +84,7 @@ const float panZ, const float rotationMatrix[4][4], const float obliqueRotationMatrix[4][4], + const float flatRotationMatrix[4][4], const float zoom, const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY, @@ -90,6 +95,7 @@ float& panZ, float rotationMatrix[4][4], float obliqueRotationMatrix[4][4], + float flatRotationMatrix[4][4], float& zoom, float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY, @@ -121,6 +127,8 @@ float obliqueRotation[4][4]; + float flatRotation[4][4]; + float scaling; float rightCortexFlatMapOffsetXY[2]; diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItem.cxx connectome-workbench-1.5.0/src/Common/RecentFileItem.cxx --- connectome-workbench-1.4.2/src/Common/RecentFileItem.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItem.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,401 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __RECENT_FILE_ITEM_DECLARE__ +#include "RecentFileItem.h" +#undef __RECENT_FILE_ITEM_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventManager.h" +#include "EventRecentFilesSystemAccessMode.h" +#include "FileInformation.h" + +using namespace caret; + + + +/** + * \class caret::RecentFileItem + * \brief Contains data for one recent file + * \ingroup Common + */ + +/** + * Constructor. + * @param fileItemType + * Type of file + * @param pathAndFileName + * Path and name of a file + */ +RecentFileItem::RecentFileItem(const RecentFileItemTypeEnum::Enum fileItemType, + const AString& pathAndFileName) +: CaretObjectTracksModification(), +m_fileItemType(fileItemType), +m_pathAndFileName(pathAndFileName) +{ + bool useFileSystemFlag(false); + EventRecentFilesSystemAccessMode modeEvent; + EventManager::get()->sendEvent(modeEvent.getPointer()); + switch (modeEvent.getMode()) { + case RecentFilesSystemAccessModeEnum::OFF: + break; + case RecentFilesSystemAccessModeEnum::ON: + useFileSystemFlag = true; + break; + } + + if (useFileSystemFlag) { + FileInformation fileInfo(pathAndFileName); + m_pathAndFileName = fileInfo.getAbsoluteFilePath(); + switch (m_fileItemType) { + case RecentFileItemTypeEnum::DIRECTORY: + m_pathName = m_pathAndFileName; /* directory !!! */ + break; + case RecentFileItemTypeEnum::SCENE_FILE: + case RecentFileItemTypeEnum::SPEC_FILE: + m_pathName = fileInfo.getAbsolutePath(); + break; + } + + m_fileName = fileInfo.getFileName(); + m_notFoundFlag = ( ! fileInfo.exists()); + m_lastModifiedDateTime = fileInfo.getLastModified(); + } + else { + m_pathAndFileName = pathAndFileName; + switch (m_fileItemType) { + case RecentFileItemTypeEnum::DIRECTORY: + m_pathName = m_pathAndFileName; /* directory !!! */ + break; + case RecentFileItemTypeEnum::SCENE_FILE: + case RecentFileItemTypeEnum::SPEC_FILE: + { + const int lastSlash = m_pathAndFileName.lastIndexOf('/'); + m_pathName = ""; + if (lastSlash >= 0) { + m_pathName = m_pathAndFileName.left(lastSlash); + } + } + break; + } + + const int lastSlash = m_pathAndFileName.lastIndexOf('/'); + if (lastSlash >= 0) { + m_fileName = m_pathAndFileName.mid(lastSlash); + } + m_notFoundFlag = false; + m_lastModifiedDateTime = QDateTime(); + } + + m_forgetFlag = false; + m_favoriteFlag = false; + m_lastAccessDateTime = QDateTime(); /* invalid date/time */ +} + +/** + * Destructor. + */ +RecentFileItem::~RecentFileItem() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +RecentFileItem::RecentFileItem(const RecentFileItem& obj) +: CaretObjectTracksModification(obj) +{ + this->copyHelperRecentFileItem(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +RecentFileItem& +RecentFileItem::operator=(const RecentFileItem& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperRecentFileItem(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +RecentFileItem::copyHelperRecentFileItem(const RecentFileItem& obj) +{ + m_fileItemType = obj.m_fileItemType; + m_lastAccessDateTime = obj.m_lastAccessDateTime; + m_pathAndFileName = obj.m_pathAndFileName; + m_pathName = obj.m_pathName; + m_fileName = obj.m_fileName; + m_comment = obj.m_comment; + m_forgetFlag = obj.m_forgetFlag; + m_favoriteFlag = obj.m_favoriteFlag; + m_notFoundFlag = obj.m_notFoundFlag; + m_lastModifiedDateTime = obj.m_lastModifiedDateTime; +} + +/** + * Less than operator that compares by path and name + * @param obj + * Instance compared to this for less than comparison. + * @return + * True if this instance is less than 'obj' instance. + */ +bool +RecentFileItem::operator<(const RecentFileItem& obj) const +{ + if (this == &obj) { + return false; + } + + return (m_pathAndFileName < obj.m_pathAndFileName); +} + +/** + * Equality operator that compares by path and name + * @param obj + * Instance compared to this for equality. + * @return + * True if this instance is less than 'obj' instance. + */ +bool +RecentFileItem::operator==(const RecentFileItem& obj) const +{ + if (this == &obj) { + return true; + } + + return (m_pathAndFileName == obj.m_pathAndFileName); +} + +/** + * @return The type of item + */ +RecentFileItemTypeEnum::Enum +RecentFileItem::getFileItemType() const +{ + return m_fileItemType; +} + +/** + * @return Time last accessed by wb_view + */ +QDateTime +RecentFileItem::getLastAccessByWorkbenchDateTime() const +{ + return m_lastAccessDateTime; +} + +/** + * @return Time last accessed by wb_view + */ +AString +RecentFileItem::getLastAccessByWorkbenchDateTimeAsString() const +{ + AString s(m_lastAccessDateTime.toString(s_qtStringDateFormat)); + return s; +} + +/** + * Set last accessed by wb_view time from a string + * @param dateTimeString + * Date/Time that must be in a valid date/time format + */ +void +RecentFileItem::setLastAccessByWorkbenchDateTimeFromString(const AString& dateTimeString) +{ + if (dateTimeString.isEmpty()) { + m_lastAccessDateTime = QDateTime(); + return; + } + + m_lastAccessDateTime = QDateTime::fromString(dateTimeString, + s_qtStringDateFormat); + if (m_lastAccessDateTime.isNull()) { + CaretLogWarning("Invalid date/time string \"" + + dateTimeString + + "\""); + } +} + +/** + * Set last accessed time by wb_view + * @param dateTime + * Last time accessed + */ +void +RecentFileItem::setLastAccessByWorkbenchDateTime(const QDateTime& dateTime) +{ + m_lastAccessDateTime = dateTime; + setModified(); +} + +/** + * Set last accessed by wb_view time to the current date/time + */ +void +RecentFileItem::setLastAccessByWorkbenchDateTimeToCurrentDateTime() +{ + setLastAccessByWorkbenchDateTime(QDateTime::currentDateTime()); +} + +/** + * @return Last time item was modiifed as reported by the operating system + */ +QDateTime +RecentFileItem::getLastModifiedDateTime() const +{ + return m_lastModifiedDateTime; +} + +/** + * @return Last time item was modiifed as a string as reported by the operating system + */ +AString +RecentFileItem::getLastModifiedDateTimeAsString() const +{ + AString s(m_lastModifiedDateTime.toString(s_qtStringDateFormat)); + return s; +} + +/** + * @return The path and file name + */ +AString +RecentFileItem::getPathAndFileName() const +{ + return m_pathAndFileName; +} + +/** + * @return The path name + */ +AString +RecentFileItem::getPathName() const +{ + return m_pathName; +} + +/** + * @return The file name + */ +AString +RecentFileItem::getFileName() const +{ + return m_fileName; +} + +/** + * @return User comment + */ +AString +RecentFileItem::getComment() const +{ + return m_comment; +} + +/** + * Set the user's comment + * @param text + * New text for user's comment + */ +void +RecentFileItem::setComment(const AString& text) +{ + m_comment = text; + setModified(); +} + +/** + * @return True if this is a favorite + */ +bool +RecentFileItem::isFavorite() const +{ + return m_favoriteFlag; +} + +/** + * Set the favorite status + * @param status + * New status + */ +void +RecentFileItem::setFavorite(const bool status) +{ + m_favoriteFlag = status; + setModified(); +} + +/** + * @return True if this item is not found + */ +bool +RecentFileItem::isNotFound() const +{ + return m_notFoundFlag; +} + +/** + * @return True if this item is to forgtten + */ +bool +RecentFileItem::isForget() const +{ + return m_forgetFlag; +} + +/** + * Set the forget status + * @param status + * New status + */ +void +RecentFileItem::setForget(const bool status) +{ + m_forgetFlag = status; + setModified(); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +RecentFileItem::toString() const +{ + return "RecentFileItem"; +} + diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItem.h connectome-workbench-1.5.0/src/Common/RecentFileItem.h --- connectome-workbench-1.4.2/src/Common/RecentFileItem.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItem.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,125 @@ +#ifndef __RECENT_FILE_ITEM_H__ +#define __RECENT_FILE_ITEM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include + +#include "CaretObjectTracksModification.h" +#include "RecentFileItemTypeEnum.h" + +class QXmlStreamWriter; + +namespace caret { + + class RecentFileItem : public CaretObjectTracksModification { + + public: + RecentFileItem(const RecentFileItemTypeEnum::Enum fileItemType, + const AString& pathAndFileName); + + virtual ~RecentFileItem(); + + RecentFileItem(const RecentFileItem& obj); + + RecentFileItem& operator=(const RecentFileItem& obj); + + bool operator<(const RecentFileItem& obj) const; + + bool operator==(const RecentFileItem& obj) const; + + RecentFileItemTypeEnum::Enum getFileItemType() const; + + AString getLastAccessByWorkbenchDateTimeAsString() const; + + void setLastAccessByWorkbenchDateTimeFromString(const AString& dateTimeString); + + QDateTime getLastAccessByWorkbenchDateTime() const; + + void setLastAccessByWorkbenchDateTime(const QDateTime& dateTime); + + void setLastAccessByWorkbenchDateTimeToCurrentDateTime(); + + QDateTime getLastModifiedDateTime() const; + + AString getLastModifiedDateTimeAsString() const; + + AString getPathAndFileName() const; + + AString getPathName() const; + + AString getFileName() const; + + AString getComment() const; + + void setComment(const AString& text); + + bool isFavorite() const; + + void setFavorite(const bool status); + + bool isNotFound() const; + + bool isForget() const; + + void setForget(const bool status); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperRecentFileItem(const RecentFileItem& obj); + + RecentFileItemTypeEnum::Enum m_fileItemType; + + QDateTime m_lastAccessDateTime; + + QDateTime m_lastModifiedDateTime; + + AString m_pathAndFileName; + + AString m_fileName; + + AString m_pathName; + + AString m_comment; + + bool m_favoriteFlag = false; + + bool m_notFoundFlag = false; + + bool m_forgetFlag = false; + + static const Qt::DateFormat s_qtStringDateFormat = Qt::TextDate; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __RECENT_FILE_ITEM_DECLARE__ + // +#endif // __RECENT_FILE_ITEM_DECLARE__ + +} // namespace +#endif //__RECENT_FILE_ITEM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemsContainer.cxx connectome-workbench-1.5.0/src/Common/RecentFileItemsContainer.cxx --- connectome-workbench-1.4.2/src/Common/RecentFileItemsContainer.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemsContainer.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,836 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __RECENT_FILE_ITEMS_CONTAINER_DECLARE__ +#include "RecentFileItemsContainer.h" +#undef __RECENT_FILE_ITEMS_CONTAINER_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "CaretPreferences.h" +#include "DataFileTypeEnum.h" +#include "RecentFileItem.h" +#include "RecentFileItemsFilter.h" + +using namespace caret; + + + +/** + * \class caret::RecentFileItemsContainer + * \brief Container for recent file items + * \ingroup Common + */ + +/** + * Constructor. + * @param mode + * Mode for the content type in the container + * @param writeIfModifiedType + * Mode for writing if modified when instance is destroyed + */ +RecentFileItemsContainer::RecentFileItemsContainer(const RecentFileItemsContainerModeEnum::Enum mode, + const WriteIfModifiedType writeIfModifiedType) +: CaretObjectTracksModification(), +m_mode(mode), +m_writeIfModifiedType(writeIfModifiedType) +{ +} + +/** + * Destructor. + */ +RecentFileItemsContainer::~RecentFileItemsContainer() +{ + if (isModified()) { + bool writeFlag(false); + switch (m_writeIfModifiedType) { + case WRITE_NO: + writeFlag = false; + break; + case WRITE_YES: + writeFlag = true; + break; + } + + if (writeFlag) { + switch (m_mode) { + case RecentFileItemsContainerModeEnum::DIRECTORY_SCENE_AND_SPEC_FILES: + /* Nothing to write */ + break; + case RecentFileItemsContainerModeEnum::FAVORITES: + /* Nothing to write */ + break; + case RecentFileItemsContainerModeEnum::OTHER: + /* Nothing to write */ + break; + case RecentFileItemsContainerModeEnum::RECENT_DIRECTORIES: + { + CaretAssert(m_caretPreferences); + AString errorMessage; + if ( ! m_caretPreferences->writeRecentDirectories(this, errorMessage)) { + CaretLogSevere("Failed to write recent directories to preferences: " + + errorMessage); + } + } + break; + case RecentFileItemsContainerModeEnum::RECENT_FILES: + { + CaretAssert(m_caretPreferences); + AString errorMessage; + if ( ! m_caretPreferences->writeRecentSceneAndSpecFiles(this, errorMessage)) { + CaretLogSevere("Failed to write recent scene and spec files to preferences: " + + errorMessage); + } + } + break; + } + } + } + + removeAllItemsIncludingFavorites(); +} + +/** + * @return New instance for other usage. + */ +RecentFileItemsContainer* +RecentFileItemsContainer::newInstance() +{ + RecentFileItemsContainer* container = new RecentFileItemsContainer(RecentFileItemsContainerModeEnum::OTHER, + WriteIfModifiedType::WRITE_NO); + return container; +} + +/** + * @return A new instance containing favorites using items in other containers + * @param otherContainers + * Other containers with items that are shared with this container + */ +RecentFileItemsContainer* +RecentFileItemsContainer::newInstanceFavorites(std::vector& otherContainers) +{ + RecentFileItemsContainer* containerOut = new RecentFileItemsContainer(RecentFileItemsContainerModeEnum::FAVORITES, + WriteIfModifiedType::WRITE_NO); + containerOut->updateFavorites(otherContainers); + + return containerOut; +} + +/** + * @return A new instance containing spec and scene files in a directory + * @param directoryPath + * Directory from which files are read + */ +RecentFileItemsContainer* +RecentFileItemsContainer::newInstanceSceneAndSpecFilesInDirectory(const AString& directoryPath) +{ + RecentFileItemsContainer* container = new RecentFileItemsContainer(RecentFileItemsContainerModeEnum::DIRECTORY_SCENE_AND_SPEC_FILES, + WriteIfModifiedType::WRITE_NO); + CaretAssert(container); + container->addFilesInDirectoryToRecentItems(RecentFileItemTypeEnum::SCENE_FILE, + directoryPath); + container->addFilesInDirectoryToRecentItems(RecentFileItemTypeEnum::SPEC_FILE, + directoryPath); + return container; +} + +/** + * @return A new instance containing recent scene and spec files from Preferences + * @param preferences + * The caret preferences + * @param writeIfModifiedType + * Mode for writing if modified when instance is destroyed + */ +RecentFileItemsContainer* +RecentFileItemsContainer::newInstanceRecentSceneAndSpecFiles(CaretPreferences* preferences, + const WriteIfModifiedType writeIfModifiedType) +{ + RecentFileItemsContainer* container = new RecentFileItemsContainer(RecentFileItemsContainerModeEnum::RECENT_FILES, + writeIfModifiedType); + CaretAssert(container); + container->m_caretPreferences = preferences; + + AString errorMessage; + const bool flag = preferences->readRecentSceneAndSpecFiles(container, + errorMessage); + if ( ! flag) { + CaretLogSevere("Reading recent scene/spec files from preferences: " + + errorMessage); + } + return container; +} + +/** + * @return A new instance containing recent directories from Preferences + * @param preferences + * The caret preferences + * @param writeIfModifiedType + * Mode for writing if modified when instance is destroyed + */ +RecentFileItemsContainer* +RecentFileItemsContainer::newInstanceRecentDirectories(CaretPreferences* preferences, + const WriteIfModifiedType writeIfModifiedType) +{ + RecentFileItemsContainer* container = new RecentFileItemsContainer(RecentFileItemsContainerModeEnum::RECENT_DIRECTORIES, + writeIfModifiedType); + CaretAssert(container); + container->m_caretPreferences = preferences; + + AString errorMessage; + const bool flag = preferences->readRecentDirectories(container, + errorMessage); + if ( ! flag) { + CaretLogSevere("Reading recent directories from preferences: " + + errorMessage); + } + return container; +} + +/** + * Update a favorites with items from other containers + * @param otherContainers + * Items that are favorites from other containers are shared with this container + */ +void +RecentFileItemsContainer::updateFavorites(std::vector& otherContainers) +{ + removeAllItemsIncludingFavorites(); + + for (auto& container : otherContainers) { + for (auto& rfi : container->m_recentFiles) { + if (rfi->isFavorite()) { + std::shared_ptr rfiCopy(rfi); + addItemPointer(rfiCopy); + } + } + } +} + +/** + *@return True if this instance has been modified, else false. + */ +bool +RecentFileItemsContainer::isModified() const +{ + if (CaretObjectTracksModification::isModified()) { + return true; + } + + for (const auto& rfi : m_recentFiles) { + if (rfi->isModified()) { + return true; + } + } + + return false; +} + +/** + * Clear the modified status + */ +void +RecentFileItemsContainer::clearModified() +{ + CaretObjectTracksModification::clearModified(); + + for (auto& rfi : m_recentFiles) { + rfi->clearModified(); + } +} + +/** + * @return The mode + */ +RecentFileItemsContainerModeEnum::Enum +RecentFileItemsContainer::getMode() const +{ + return m_mode; +} + +/** + * Add an item to this container. If an item with the same name is already in the container, it is removed + * and the given item is added. + * @param recentFile + * Recent file item that is added will be managed (destroyed at appropriate time) by this container + */ +void +RecentFileItemsContainer::addItem(RecentFileItem* recentFile) +{ + CaretAssert(recentFile); + + /* + * Item might not get added but if this happens + * shared pointer will delete the item. + */ + std::shared_ptr sp(recentFile); + addItemPointer(sp); +} + +/** + * Add an item in a shared pointer to this container. If an item with the same name is already in the container, it is removed + * and the given item is added. + * @param recentFilePointer + * Shared pointer that will be added to this container. + */ +void +RecentFileItemsContainer::addItemPointer(std::shared_ptr& recentFilePointer) +{ + /* + * Returned pair contains iterator (first) and a boolean (second). + * If the boolean is FALSE, that means the new items was NOT inserted + * since it duplicates an item already in the set. + */ + auto iter = m_recentFiles.insert(recentFilePointer); + if (iter.second) { + /* Item was inserted */ + } + else { + /* + * Item was not inserted, just need to update the date + */ + (*iter.first)->setLastAccessByWorkbenchDateTime(recentFilePointer->getLastAccessByWorkbenchDateTime()); + } +} + +/** + * Remove all items in this container including favorites + */ +void +RecentFileItemsContainer::removeAllItemsIncludingFavorites() +{ + m_recentFiles.clear(); +} + +/** + * Remove all items in this container excluding favorites + */ +void +RecentFileItemsContainer::removeAllItemsExcludingFavorites() +{ + for (auto rf : m_recentFiles) { + if ( ! rf->isFavorite()) { + rf->setForget(true); + } + } +} + +/** + * Add files of the given type and in the given directory to the recent items + * @param recentFileItemType + * The recent file item type + * @param directoryPaht + * Directory from which to get files + */ +void +RecentFileItemsContainer::addFilesInDirectoryToRecentItems(const RecentFileItemTypeEnum::Enum recentFileItemType, + const AString& directoryPath) +{ + DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::UNKNOWN; + switch (recentFileItemType) { + case RecentFileItemTypeEnum::DIRECTORY: + CaretAssert(0); + return; + break; + case RecentFileItemTypeEnum::SCENE_FILE: + dataFileType = DataFileTypeEnum::SCENE; + break; + case RecentFileItemTypeEnum::SPEC_FILE: + dataFileType = DataFileTypeEnum::SPECIFICATION; + break; + } + + std::vector fileNames = DataFileTypeEnum::getFilesInDirectory(dataFileType, + directoryPath); + + for (auto name : fileNames) { + RecentFileItem* fileItem = new RecentFileItem(recentFileItemType, + name); + addItem(fileItem); + } +} + +/** + * Get the recent file item with the given path and file name. + * @param pathAndFileName + * Path and file name to match + * @return Pointer to matching item or NULL if not found. + */ +RecentFileItem* +RecentFileItemsContainer::getItemWithPathAndFileName(const AString& pathAndFileName) +{ + for (auto& item : m_recentFiles) { + if (pathAndFileName == item->getPathAndFileName()) { + return item.get(); + } + } + + return NULL; +} + +/** + * @return True if this container is empty (no recent file items) + */ +bool +RecentFileItemsContainer::isEmpty() const +{ + return m_recentFiles.empty(); +} + +/** + * Get items in this container using the given item filter + * @param itemsFilter + * Contains information for filtering items + * @return + * Items in container that match filter + */ +std::vector +RecentFileItemsContainer::getItems(const RecentFileItemsFilter& itemsFilter) const +{ + std::vector itemsOut; + + for (const auto& item : m_recentFiles) { + CaretAssert(item); + if (itemsFilter.testItemPassesFilter(item.get())) { + itemsOut.push_back(item.get()); + } + } + + return itemsOut; +} + +/** + * @return All items in this container + */ +std::vector +RecentFileItemsContainer::getAllItems() const +{ + std::vector itemsOut; + + for (const auto& item : m_recentFiles) { + CaretAssert(item); + itemsOut.push_back(item.get()); + } + + return itemsOut; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +RecentFileItemsContainer::toString() const +{ + return "RecentFileItemsContainer"; +} + +/** + * Sort recent file items ordered by the given sorting key + * @param sortingKey + * Key on which to sort + */ +void +RecentFileItemsContainer::sort(const RecentFileItemSortingKeyEnum::Enum sortingKey, + std::vector& items) +{ + switch (sortingKey) { + case RecentFileItemSortingKeyEnum::DATE_NEWEST: + std::sort(items.begin(), + items.end(), /* > is newer in Qt Documentation */ + [](const RecentFileItem* a, RecentFileItem* b) { return a->getLastAccessByWorkbenchDateTime() > b->getLastAccessByWorkbenchDateTime(); }); + break; + case RecentFileItemSortingKeyEnum::DATE_OLDEST: + std::sort(items.begin(), + items.end(), /* < is earlier in Qt Documentation */ + [](const RecentFileItem* a, RecentFileItem* b) { return a->getLastAccessByWorkbenchDateTime() < b->getLastAccessByWorkbenchDateTime(); }); + break; + case RecentFileItemSortingKeyEnum::MODIFIED_NEWEST: + std::sort(items.begin(), + items.end(), /* > is newer in Qt Documentation */ + [](const RecentFileItem* a, RecentFileItem* b) { return a->getLastModifiedDateTime() > b->getLastModifiedDateTime(); }); + break; + case RecentFileItemSortingKeyEnum::MODIFIED_OLDEST: + std::sort(items.begin(), + items.end(), /* < is earlier in Qt Documentation */ + [](const RecentFileItem* a, RecentFileItem* b) { return a->getLastModifiedDateTime() < b->getLastModifiedDateTime(); }); + break; + case RecentFileItemSortingKeyEnum::NAME_ASCENDING: + std::sort(items.begin(), + items.end(), + [](const RecentFileItem* a, RecentFileItem* b) { return a->getFileName() < b->getFileName(); }); + break; + case RecentFileItemSortingKeyEnum::NAME_DESCENDING: + std::sort(items.begin(), + items.end(), + [](const RecentFileItem* a, RecentFileItem* b) { return a->getFileName() > b->getFileName(); }); + break; + } +} + +/** + * Reduce the number of items in the container so that it does not exceed the given maximum number of items. + * Note: Favorites are NOT removed + * @param maximumNumberOfItems + * Maximum number of items in the container after this method completes. However, since favorites are + * never removed, this maximum may be exceeded if it is less than the number of favorites in the container. + * @return Number of items that were removed from the container + */ +int32_t +RecentFileItemsContainer::removeItemsExceedingMaximumNumber(const int32_t maximumNumberOfItems) +{ + std::vector allItems = getAllItems(); + const int32_t numItems = static_cast(allItems.size()); + if (numItems <= maximumNumberOfItems) { + return 0; + } + + /* + * Keep newest items so sort by date last accessed by wb_view + */ + sort(RecentFileItemSortingKeyEnum::DATE_NEWEST, + allItems); + + /* + * Keep all favorites + */ + std::vector itemsToKeep; + int32_t numItemsToKeep(maximumNumberOfItems); + for (int32_t i = 0; i < numItems; i++) { + CaretAssertVectorIndex(allItems, i); + if (allItems[i]->isFavorite()) { + --numItemsToKeep; + itemsToKeep.push_back(new RecentFileItem(*allItems[i])); + } + } + + /* + * Keep a limited number of non-favorites + */ + if (numItemsToKeep > 0) { + for (int32_t i = 0; i < numItems; i++) { + CaretAssertVectorIndex(allItems, i); + if ( ! allItems[i]->isFavorite()) { + itemsToKeep.push_back(new RecentFileItem(*allItems[i])); + --numItemsToKeep; + } + if (numItemsToKeep <= 0) { + break; + } + } + } + + /* + * Remove all existing items and add back items + */ + removeAllItemsIncludingFavorites(); + for (auto ptr : itemsToKeep) { + addItem(ptr); + } + + const int32_t numItemsRemoved = (numItems - m_recentFiles.size()); + return numItemsRemoved; +} + +/** + * Read from the given xml string + * @param xml + * String containing XML. + * @param errorMessageOut + * Contains error information + * @return + * True if successful, else false + */ +bool +RecentFileItemsContainer::readFromXML(const AString& xml, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + removeAllItemsIncludingFavorites(); + + QXmlStreamReader reader(xml); + if (reader.atEnd()) { + /* empty string is OK */ + clearModified(); + return true; + } + + clearModified(); + + reader.readNextStartElement(); + if (reader.name() == XML_TAG_RECENT_FILE_ITEMS_CONTAINER) { + readFromXmlVersionOne(reader); + } + else { + reader.raiseError("First element should be \"" + + XML_TAG_RECENT_FILE_ITEMS_CONTAINER + + "\" but is \"" + + reader.name().toString() + + "\""); + } + + if (reader.hasError()) { + errorMessageOut = reader.errorString(); + return false; + } + + return true; +} + +/** + * Read the XML version one + * @param reader + * The XML reader + */ +void +RecentFileItemsContainer::readFromXmlVersionOne(QXmlStreamReader& reader) +{ + bool endElementFoundFlag(false); + + while ( ( ! reader.atEnd()) + && ( ! endElementFoundFlag)) { + reader.readNext(); + if (reader.isStartElement()) { + if (reader.name() == XML_TAG_RECENT_FILE_ITEM) { + readFromXMLVersionOneRecentFileItem(reader); + } + else { + reader.raiseError("Unrecognized element \"" + + reader.name().toString() + + "\""); + reader.skipCurrentElement(); + } + } + else if (reader.isEndElement()) { + if (reader.name() == XML_TAG_RECENT_FILE_ITEMS_CONTAINER) { + endElementFoundFlag = true; + } + } + } +} + +/** + * Read the XML version one for a recent file item + * @param reader + * The XML reader + */ +void +RecentFileItemsContainer::readFromXMLVersionOneRecentFileItem(QXmlStreamReader& reader) +{ + bool endElementFoundFlag(false); + + AString pathAndFileName; + AString comment; + AString fileTypeString; + AString dateAndTimeString; + AString favoriteString; + + while ( ( ! reader.atEnd()) + && ( ! endElementFoundFlag)) { + reader.readNext(); + if (reader.isStartElement()) { + if (reader.name() == XML_TAG_RECENT_FILE_ITEM_COMMENT) { + comment = reader.readElementText().trimmed(); + } + else if (reader.name() == XML_TAG_RECENT_FILE_ITEM_DATE_AND_TIME) { + dateAndTimeString = reader.readElementText().trimmed(); + } + else if (reader.name() == XML_TAG_RECENT_FILE_ITEM_FILE_ITEM_TYPE) { + fileTypeString = reader.readElementText().trimmed(); + } + else if (reader.name() == XML_TAG_RECENT_FILE_ITEM_FAVORITE) { + favoriteString = reader.readElementText().trimmed(); + } + else if (reader.name() == XML_TAG_RECENT_FILE_ITEM_PATH_AND_FILE_NAME) { + pathAndFileName = reader.readElementText().trimmed(); + } + else { + reader.raiseError("Unrecognized child element of " + + XML_TAG_RECENT_FILE_ITEM + + " \"" + + reader.name().toString() + + "\""); + reader.skipCurrentElement(); + return; + } + } + else if (reader.isEndElement()) { + if (reader.name() == XML_TAG_RECENT_FILE_ITEM) { + endElementFoundFlag = true; + } + } + } + + bool validFileTypeFlag(false); + const RecentFileItemTypeEnum::Enum fileType = RecentFileItemTypeEnum::fromName(fileTypeString, + &validFileTypeFlag); + + if ( ! validFileTypeFlag) { + reader.raiseError(XML_TAG_RECENT_FILE_ITEM + + " has invalid file type \"" + + fileTypeString + + "\""); + return; + } + + if (pathAndFileName.isEmpty()) { + reader.raiseError(XML_TAG_RECENT_FILE_ITEM + + " has empty path and file name"); + return; + } + + RecentFileItem* item = new RecentFileItem(fileType, + pathAndFileName); + item->setComment(comment); + item->setFavorite(favoriteString.toBool()); + item->setLastAccessByWorkbenchDateTimeFromString(dateAndTimeString); + + addItem(item); +} + +/** + * Write to the given xml string + * @param xml + * XML written to this string. + * @param errorMessageOut + * Contains error information + * @return + * True if successful, else false + */ +bool +RecentFileItemsContainer::writeToXML(AString& xml, + AString& errorMessageOut) const +{ + xml.clear(); + errorMessageOut.clear(); + + QXmlStreamWriter writer(&xml); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement(XML_TAG_RECENT_FILE_ITEMS_CONTAINER); + + for (const auto& rfi : m_recentFiles) { + if ( ! rfi->isForget()) { + writer.writeStartElement(XML_TAG_RECENT_FILE_ITEM); + writer.writeTextElement(XML_TAG_RECENT_FILE_ITEM_COMMENT, + rfi->getComment()); + writer.writeTextElement(XML_TAG_RECENT_FILE_ITEM_DATE_AND_TIME, + rfi->getLastAccessByWorkbenchDateTimeAsString()); + writer.writeTextElement(XML_TAG_RECENT_FILE_ITEM_FAVORITE, + AString::fromBool(rfi->isFavorite())); + writer.writeTextElement(XML_TAG_RECENT_FILE_ITEM_FILE_ITEM_TYPE, + RecentFileItemTypeEnum::toName(rfi->getFileItemType())); + writer.writeTextElement(XML_TAG_RECENT_FILE_ITEM_PATH_AND_FILE_NAME, + rfi->getPathAndFileName()); + writer.writeEndElement(); + } + } + + writer.writeEndElement(); + writer.writeEndDocument(); + + return true; +} + +/** + * Test XML reading/writing by writing and reading it + */ +void +RecentFileItemsContainer::testXmlReadingAndWriting() +{ + const int32_t validCount = static_cast(m_recentFiles.size()); + AString xmlOne; + AString errorMessage; + bool successFlag = writeToXML(xmlOne, errorMessage); + if ( ! successFlag) { + std::cout << "First write to XML failed " << errorMessage << std::endl; + return; + } + std::cout << "XML First write: " << std::endl << xmlOne << std::endl << std::endl; + + + successFlag = readFromXML(xmlOne, errorMessage); + if ( ! successFlag) { + std::cout << "Read from XML failed " << errorMessage << std::endl; + return; + } + + AString xmlTwo; + successFlag = writeToXML(xmlTwo, errorMessage); + if ( ! successFlag) { + std::cout << "Second write to XML failed " << errorMessage << std::endl; + return; + } + + std::cout << "XML second write: " << std::endl << xmlTwo << std::endl << std::endl; + + if (xmlOne != xmlTwo) { + std::cout << "XML strings to not match: " << std::endl; + std::cout << "FIRST: " << std::endl << xmlOne << std::endl; + std::cout << "Two: " << std::endl << xmlTwo << std::endl; + return; + } + + if (validCount != static_cast(m_recentFiles.size())) { + std::cout << "Number of elements changed from " << validCount << " to " << m_recentFiles.size() + << " during testing" << std::endl; + return; + } + + std::cout << "XML Read/Write Testing Successful" << std::endl; +} + +/** + * Comparison operation used by the SET containing the RecentFileItems. + * @param lhs + * First item for comparison. + * @param lhs + * Second item for comparison. + * @return + * True if "lhs" is less than "rhs" + */ +bool +RecentFileItemsContainer::ItemCompare::operator() (const RecentFileItem* lhs, const RecentFileItem* rhs) const +{ + CaretAssert(lhs); + CaretAssert(rhs); + return (lhs->getPathAndFileName() < rhs->getPathAndFileName()); +} +/** + * Comparison operation used by the SET containing the RecentFileItems. + * @param lhs + * First item for comparison. + * @param lhs + * Second item for comparison. + * @return + * True if "lhs" is less than "rhs" + */ +bool +RecentFileItemsContainer::ItemCompareSharedPtr::operator() (const std::shared_ptr& lhs, + const std::shared_ptr& rhs) const +{ + return (lhs->getPathAndFileName() < rhs->getPathAndFileName()); +} diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemsContainer.h connectome-workbench-1.5.0/src/Common/RecentFileItemsContainer.h --- connectome-workbench-1.4.2/src/Common/RecentFileItemsContainer.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemsContainer.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,178 @@ +#ifndef __RECENT_FILE_ITEMS_CONTAINER_H__ +#define __RECENT_FILE_ITEMS_CONTAINER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include + +#include "CaretObjectTracksModification.h" +#include "RecentFileItemSortingKeyEnum.h" +#include "RecentFileItemTypeEnum.h" +#include "RecentFileItemsContainerModeEnum.h" + +class QXmlStreamReader; + +namespace caret { + + class CaretPreferences; + class RecentFileItem; + class RecentFileItemsFilter; + + class RecentFileItemsContainer : public CaretObjectTracksModification { + + public: + /* + * Write if modified when this container is destroyed + */ + enum WriteIfModifiedType { + /* No, do not update preferences */ + WRITE_NO, + /* Yes, update preferences */ + WRITE_YES + }; + + static RecentFileItemsContainer* newInstance(); + + static RecentFileItemsContainer* newInstanceFavorites(std::vector& otherContainers); + + static RecentFileItemsContainer* newInstanceSceneAndSpecFilesInDirectory(const AString& directoryPath); + + static RecentFileItemsContainer* newInstanceRecentSceneAndSpecFiles(CaretPreferences* preferences, + const WriteIfModifiedType writeIfModifiedType); + + static RecentFileItemsContainer* newInstanceRecentDirectories(CaretPreferences* preferences, + const WriteIfModifiedType writeIfModifiedType); + + virtual ~RecentFileItemsContainer(); + + RecentFileItemsContainer(const RecentFileItemsContainer&) = delete; + + RecentFileItemsContainer& operator=(const RecentFileItemsContainer&) = delete; + + void updateFavorites(std::vector& otherContainers); + + bool isEmpty() const; + + std::vector getItems(const RecentFileItemsFilter& itemsFilter) const; + + void addItem(RecentFileItem* recentFile); + + void removeAllItemsIncludingFavorites(); + + void removeAllItemsExcludingFavorites(); + + RecentFileItem* getItemWithPathAndFileName(const AString& pathAndFileName); + + RecentFileItemsContainerModeEnum::Enum getMode() const; + + virtual bool isModified() const; + + virtual void clearModified(); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + int32_t removeItemsExceedingMaximumNumber(const int32_t maximumNumberOfItems); + + static void sort(const RecentFileItemSortingKeyEnum::Enum sortingKey, + std::vector& items); + + bool readFromXML(const AString& xml, + AString& errorMessageOut); + + bool writeToXML(AString& xml, + AString& errorMessageOut) const; + + void testXmlReadingAndWriting(); + + private: + RecentFileItemsContainer(const RecentFileItemsContainerModeEnum::Enum mode, + const WriteIfModifiedType writeIfModifiedType); + + void addItemPointer(std::shared_ptr& recentFilePointer); + + void addFilesInDirectoryToRecentItems(const RecentFileItemTypeEnum::Enum recentFileItemType, + const AString& directoryPath); + + void readFromXmlVersionOne(QXmlStreamReader& reader); + + void readFromXMLVersionOneRecentFileItem(QXmlStreamReader& reader); + + std::vector getAllItems() const; + + RecentFileItemsContainerModeEnum::Enum m_mode; + + CaretPreferences* m_caretPreferences = NULL; + + const WriteIfModifiedType m_writeIfModifiedType; + + /** + * Used with the SET containing RecentFileItems to compare using the path and filename. + * Without this, just pointers are inserted and that would allow duplicates with same path and filename. + */ + struct ItemCompare { + bool operator() (const RecentFileItem* lhs, const RecentFileItem* rhs) const; + }; + + struct ItemCompareSharedPtr { + bool operator() (const std::shared_ptr& lhs, const std::shared_ptr& rhs) const; + }; + + /** + * Set containing RecentFileItem's. Note use of comparison operator so that + * comparison is performed on content of the RecentFileItem and NOT the pointers. + * + * A Favorites container uses items from other containers so shared pointers are used + * since an item may in a favorites container and another container. + * + * A set is used to avoid duplicate entries. + */ + std::set, ItemCompareSharedPtr> m_recentFiles; + + // ADD_NEW_MEMBERS_HERE + + + static const AString XML_TAG_RECENT_FILE_ITEMS_CONTAINER; + static const AString XML_TAG_RECENT_FILE_ITEM; + static const AString XML_TAG_RECENT_FILE_ITEM_COMMENT; + static const AString XML_TAG_RECENT_FILE_ITEM_DATE_AND_TIME; + static const AString XML_TAG_RECENT_FILE_ITEM_FILE_ITEM_TYPE; + static const AString XML_TAG_RECENT_FILE_ITEM_FAVORITE; + static const AString XML_TAG_RECENT_FILE_ITEM_PATH_AND_FILE_NAME; + + }; + +#ifdef __RECENT_FILE_ITEMS_CONTAINER_DECLARE__ + const AString RecentFileItemsContainer::XML_TAG_RECENT_FILE_ITEMS_CONTAINER = "RecentFileItemsContainer"; + const AString RecentFileItemsContainer::XML_TAG_RECENT_FILE_ITEM = "RecentFileItem"; + const AString RecentFileItemsContainer::XML_TAG_RECENT_FILE_ITEM_COMMENT = "Comment"; + const AString RecentFileItemsContainer::XML_TAG_RECENT_FILE_ITEM_DATE_AND_TIME = "DateTime"; + const AString RecentFileItemsContainer::XML_TAG_RECENT_FILE_ITEM_FILE_ITEM_TYPE = "FileItemType"; + const AString RecentFileItemsContainer::XML_TAG_RECENT_FILE_ITEM_FAVORITE = "Favorite"; + const AString RecentFileItemsContainer::XML_TAG_RECENT_FILE_ITEM_PATH_AND_FILE_NAME = "PathAndFileName"; +#endif // __RECENT_FILE_ITEMS_CONTAINER_DECLARE__ + +} // namespace +#endif //__RECENT_FILE_ITEMS_CONTAINER_H__ diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemsContainerModeEnum.cxx connectome-workbench-1.5.0/src/Common/RecentFileItemsContainerModeEnum.cxx --- connectome-workbench-1.4.2/src/Common/RecentFileItemsContainerModeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemsContainerModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,428 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __RECENT_FILE_ITEMS_CONTAINER_MODE_ENUM_DECLARE__ +#include "RecentFileItemsContainerModeEnum.h" +#undef __RECENT_FILE_ITEMS_CONTAINER_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::RecentFileItemsContainerModeEnum + * \brief Enumerated type for types of recently opened files + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_recentFilesModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void recentFilesModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "RecentFileItemsContainerModeEnum.h" + * + * Instatiate: + * m_recentFilesModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_recentFilesModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_recentFilesModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(recentFilesModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_recentFilesModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const RecentFileItemsContainerModeEnum::Enum VARIABLE = m_recentFilesModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +RecentFileItemsContainerModeEnum::RecentFileItemsContainerModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName, + const AString& guiButtonName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; + this->guiButtonName = guiButtonName; +} + +/** + * Destructor. + */ +RecentFileItemsContainerModeEnum::~RecentFileItemsContainerModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +RecentFileItemsContainerModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + /* + * Changing the order of these will affect the order in the recent files dialog buttons + */ + + enumData.push_back(RecentFileItemsContainerModeEnum(FAVORITES, + "FAVORITES", + "Favorites", + "Favorites")); + + enumData.push_back(RecentFileItemsContainerModeEnum(RECENT_FILES, + "RECENT_FILES", + "Recent Files", + "Recent\nFiles")); + + const QString noButtonName(""); + enumData.push_back(RecentFileItemsContainerModeEnum(OTHER, + "OTHER", + "Other", + noButtonName)); + + enumData.push_back(RecentFileItemsContainerModeEnum(DIRECTORY_SCENE_AND_SPEC_FILES, + "DIRECTORY_SCENE_AND_SPEC_FILES", + "Directory Scene and Spec Files", + "Current\nDirectory\nFiles")); + + enumData.push_back(RecentFileItemsContainerModeEnum(RECENT_DIRECTORIES, + "RECENT_DIRECTORIES", + "Recent Directories", + "Recent\nDirectories")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const RecentFileItemsContainerModeEnum* +RecentFileItemsContainerModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const RecentFileItemsContainerModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +RecentFileItemsContainerModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFileItemsContainerModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +RecentFileItemsContainerModeEnum::Enum +RecentFileItemsContainerModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = getDefaultValue(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemsContainerModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type RecentFileItemsContainerModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +RecentFileItemsContainerModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFileItemsContainerModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get a GUI string representation of the enumerated type for Recent Files Dialog buttons + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. If empty string, then do not use as a button + */ +AString +RecentFileItemsContainerModeEnum::toGuiButtonName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFileItemsContainerModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiButtonName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +RecentFileItemsContainerModeEnum::Enum +RecentFileItemsContainerModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = getDefaultValue(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemsContainerModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type RecentFileItemsContainerModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +RecentFileItemsContainerModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const RecentFileItemsContainerModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +RecentFileItemsContainerModeEnum::Enum +RecentFileItemsContainerModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = getDefaultValue(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemsContainerModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type RecentFileItemsContainerModeEnum")); + } + return enumValue; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Default enum value. + */ +RecentFileItemsContainerModeEnum::Enum +RecentFileItemsContainerModeEnum::getDefaultValue() +{ + return RECENT_FILES; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +RecentFileItemsContainerModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +RecentFileItemsContainerModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(RecentFileItemsContainerModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +RecentFileItemsContainerModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(RecentFileItemsContainerModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemsContainerModeEnum.h connectome-workbench-1.5.0/src/Common/RecentFileItemsContainerModeEnum.h --- connectome-workbench-1.4.2/src/Common/RecentFileItemsContainerModeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemsContainerModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,118 @@ +#ifndef __RECENT_FILE_ITEMS_CONTAINER_MODE_ENUM_H__ +#define __RECENT_FILE_ITEMS_CONTAINER_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class RecentFileItemsContainerModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Scene and Spec files in current directory */ + DIRECTORY_SCENE_AND_SPEC_FILES, + /** Favorites */ + FAVORITES, + /** Other usage not specific to any type of content */ + OTHER, + /** Recent directories containing recently opened or saved files */ + RECENT_DIRECTORIES, + /** Recently opened Scene or Spec fiels*/ + RECENT_FILES + }; + + + ~RecentFileItemsContainerModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiButtonName(Enum enumValue); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + RecentFileItemsContainerModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName, + const AString& guiButtonName); + + static const RecentFileItemsContainerModeEnum* findData(const Enum enumValue); + + static Enum getDefaultValue(); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; + + /** A user-friendly name that is displayed in the Recent Files Dialog buttons */ + AString guiButtonName; +}; + +#ifdef __RECENT_FILE_ITEMS_CONTAINER_MODE_ENUM_DECLARE__ +std::vector RecentFileItemsContainerModeEnum::enumData; +bool RecentFileItemsContainerModeEnum::initializedFlag = false; +int32_t RecentFileItemsContainerModeEnum::integerCodeCounter = 0; +#endif // __RECENT_FILE_ITEMS_CONTAINER_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__RECENT_FILE_ITEMS_CONTAINER_MODE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemsFilter.cxx connectome-workbench-1.5.0/src/Common/RecentFileItemsFilter.cxx --- connectome-workbench-1.4.2/src/Common/RecentFileItemsFilter.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemsFilter.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,369 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __RECENT_FILE_ITEMS_FILTER_DECLARE__ +#include "RecentFileItemsFilter.h" +#undef __RECENT_FILE_ITEMS_FILTER_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "RecentFileItem.h" + +using namespace caret; + +#define _MATCH_WITH_Q_REG_EXP_ + +/** + * \class caret::RecentFileItemsFilter + * \brief Filters recent file items + * \ingroup Common + */ + +/** + * Constructor with all items off (filter will reject all items). + */ +RecentFileItemsFilter::RecentFileItemsFilter() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +RecentFileItemsFilter::~RecentFileItemsFilter() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +RecentFileItemsFilter::RecentFileItemsFilter(const RecentFileItemsFilter& obj) +: CaretObject(obj) +{ + this->copyHelperRecentFileItemsFilter(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +RecentFileItemsFilter& +RecentFileItemsFilter::operator=(const RecentFileItemsFilter& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperRecentFileItemsFilter(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +RecentFileItemsFilter::copyHelperRecentFileItemsFilter(const RecentFileItemsFilter& obj) +{ + m_favoritesOnly = obj.m_favoritesOnly; + m_nameMatching = obj.m_nameMatching; + m_listDirectories = obj.m_listDirectories; + m_listSceneFiles = obj.m_listSceneFiles; + m_listSpecFiles = obj.m_listSpecFiles; + m_showFilePaths = obj.m_showFilePaths; +} + +/** + * @return True if the items passes the filters selections, else false. + * @param recentFileItem + * Item that is tested to pass this filter's selections + */ +bool +RecentFileItemsFilter::testItemPassesFilter(const RecentFileItem* recentFileItem) const +{ + if (recentFileItem == NULL) { + return false; + } + + if (m_favoritesOnly) { + if ( ! recentFileItem->isFavorite()) { + return false; + } + } + + switch (recentFileItem->getFileItemType()) { + case RecentFileItemTypeEnum::DIRECTORY: + if ( ! m_listDirectories) { + return false; + } + break; + case RecentFileItemTypeEnum::SCENE_FILE: + if ( ! m_listSceneFiles) { + return false; + } + break; + case RecentFileItemTypeEnum::SPEC_FILE: + if ( ! m_listSpecFiles) { + return false; + } + break; + } + + if ( ! m_nameMatching.isEmpty()) { +#ifdef _MATCH_WITH_Q_REG_EXP_ + if ( ! m_regExp) { + m_regExp.reset(new QRegExp(m_nameMatching)); + m_regExp->setPatternSyntax(QRegExp::Wildcard); + m_regExp->setCaseSensitivity(Qt::CaseInsensitive); + if ( ! m_regExp->isValid()) { + CaretLogFine("Regular expression failure for RecentFileItem: " + "Name matching \"" + + m_nameMatching + + "\" error message \"" + + m_regExp->errorString() + + "\""); + } + } + if (m_regExp) { + if (m_regExp->isValid()) { + if ( ! m_regExp->exactMatch(recentFileItem->getPathAndFileName())) { + return false; + } + } + } +#else /* _MATCH_WITH_Q_REG_EXP_ */ + /* + * NOTE: QRegularExpression::wildcardToRegularExpression() added in Qt 5.12 + */ + if ( ! m_regularExpression) { + const QString reText(QRegularExpression::wildcardToRegularExpression(m_nameMatching), + QRegularExpression::CaseInsensitiveOption; + m_regularExpression.reset(new QRegularExpression(reText)); + if ( ! m_regularExpression->isValid()) { + CaretLogFine("Regular expression failure for RecentFileItem: " + "Name matching \"" + + m_nameMatching + + "\" converted to regular expression \"" + + reText + + "\" error message \"" + + m_regularExpression->errorString() + + "\""); + } + } + + if (m_regularExpression != NULL) { + if (m_regularExpression->isValid()) { + if ( ! m_regularExpression->match(recentFileItem->getPathAndFileName()).hasMatch()) { + return false; + } + } + } +#endif /* _MATCH_WITH_Q_REG_EXP_ */ + } + + /* + * Passed all filtering + */ + return true; +} + +/** + * @return Tooltip for the matching line edit in the GUI + */ +AString +RecentFileItemsFilter::getMatchingLineEditToolTip() +{ + AString text; + +#ifdef _MATCH_WITH_Q_REG_EXP_ + text = ("" + "Enter text for case-insensitive wildcard (GLOB) matching:" + "

    " + "
  • c Any character represenents iteslf (c matches c)" + "
  • ? Matches any single character" + "
  • * Matches zero or more of any character" + "
  • [abc] Matches one character in the brackets" + "
  • [a-c] Matches one character from the range in the brackets" + "
" + ""); +#else /* _MATCH_WITH_Q_REG_EXP_ */ + text = ("" + "Enter text for case-insensitive wildcard (GLOB) matching:" + "
    " + "
  • c Any character represenents iteslf (c matches c)" + "
  • ? Matches any single character" + "
  • * Matches zero or more of any character" + "
  • [abc] Matches one character in the brackets" + "
  • [a-c] Matches one character from the range in the brackets" + "
  • [!abc] Matches one character NOT in the brackets" + "
  • [!a-c] Matches one character NOT from the range in the brackets" + "
" + ""); +#endif /* _MATCH_WITH_Q_REG_EXP_ */ + return text; +} + +/** + * @return name matching (glob) + */ +AString +RecentFileItemsFilter::getNameMatching() const +{ + return m_nameMatching; +} + +/** + * Set name matching (glob) + * + * @param nameMatching + * New value for name matching (glob) + */ +void +RecentFileItemsFilter::setNameMatching(const AString& nameMatching) +{ + m_nameMatching = nameMatching; + m_regularExpression.reset(); + m_regExp.reset(); +} + +/** + * @return List spec files + */ +bool +RecentFileItemsFilter::isListSpecFiles() const +{ + return m_listSpecFiles; +} + +/** + * Set List spec files + * + * @param listSpecFiles + * New value for List spec files + */ +void +RecentFileItemsFilter::setListSpecFiles(const bool listSpecFiles) +{ + m_listSpecFiles = listSpecFiles; +} + +/** + * @return List scene files + */ +bool +RecentFileItemsFilter::isListSceneFiles() const +{ + return m_listSceneFiles; +} + +/** + * Set List scene files + * + * @param listSceneFiles + * New value for List scene files + */ +void +RecentFileItemsFilter::setListSceneFiles(const bool listSceneFiles) +{ + m_listSceneFiles = listSceneFiles; +} + +/** + * @return list directories + */ +bool +RecentFileItemsFilter::isListDirectories() const +{ + return m_listDirectories; +} +/** +* Set list directories +* +* @param listDirectories +* New value for list directories +*/ +void +RecentFileItemsFilter::setListDirectories(const bool listDirectories) +{ + m_listDirectories = listDirectories; +} + +/** +* @return Show file paths +*/ +bool +RecentFileItemsFilter::isShowFilePaths() const +{ + return m_showFilePaths; +} + +/** +* Set show file paths +* +* @param showFilePaths +* New value for show paths +*/ +void +RecentFileItemsFilter::setShowFilePaths(const bool showFilePaths) +{ + m_showFilePaths = showFilePaths; +} + +/** + * @return show only favorites + */ +bool +RecentFileItemsFilter::isFavoritesOnly() const +{ + return m_favoritesOnly; +} + +/** + * Set show only favorites + * + * @param favoritesOnly + * New value for show only favorites + */ +void +RecentFileItemsFilter::setFavoritesOnly(const bool favoritesOnly) +{ + m_favoritesOnly = favoritesOnly; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +RecentFileItemsFilter::toString() const +{ + return "RecentFileItemsFilter"; +} + diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemsFilter.h connectome-workbench-1.5.0/src/Common/RecentFileItemsFilter.h --- connectome-workbench-1.4.2/src/Common/RecentFileItemsFilter.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemsFilter.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,114 @@ +#ifndef __RECENT_FILE_ITEMS_FILTER_H__ +#define __RECENT_FILE_ITEMS_FILTER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +class QRegExp; +class QRegularExpression; + +namespace caret { + + class RecentFileItem; + + class RecentFileItemsFilter : public CaretObject { + + public: + RecentFileItemsFilter(); + + virtual ~RecentFileItemsFilter(); + + RecentFileItemsFilter(const RecentFileItemsFilter& obj); + + RecentFileItemsFilter& operator=(const RecentFileItemsFilter& obj); + + bool testItemPassesFilter(const RecentFileItem* recentFileItem) const; + + AString getNameMatching() const; + + void setNameMatching(const AString& nameMatching); + + bool isListSpecFiles() const; + + void setListSpecFiles(const bool listSpecFiles); + + bool isListSceneFiles() const; + + void setListSceneFiles(const bool listSceneFiles); + + bool isListDirectories() const; + + void setListDirectories(const bool listDirectories); + + bool isShowFilePaths() const; + + void setShowFilePaths(const bool showFilePaths); + + bool isFavoritesOnly() const; + + void setFavoritesOnly(const bool favoritesOnly); + + static AString getMatchingLineEditToolTip(); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperRecentFileItemsFilter(const RecentFileItemsFilter& obj); + + /** name matching (glob)*/ + AString m_nameMatching; + + /** List spec files*/ + bool m_listSpecFiles = false; + + /** List scene files*/ + bool m_listSceneFiles = false; + + /** List directories*/ + bool m_listDirectories = false; + + /** Show file paths */ + bool m_showFilePaths = true; + + /** show only favorites*/ + bool m_favoritesOnly = false; + + mutable std::unique_ptr m_regularExpression; + + mutable std::unique_ptr m_regExp; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __RECENT_FILE_ITEMS_FILTER_DECLARE__ + // +#endif // __RECENT_FILE_ITEMS_FILTER_DECLARE__ + +} // namespace +#endif //__RECENT_FILE_ITEMS_FILTER_H__ diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemSortingKeyEnum.cxx connectome-workbench-1.5.0/src/Common/RecentFileItemSortingKeyEnum.cxx --- connectome-workbench-1.4.2/src/Common/RecentFileItemSortingKeyEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemSortingKeyEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,388 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __RECENT_FILE_ITEM_SORTING_KEY_ENUM_DECLARE__ +#include "RecentFileItemSortingKeyEnum.h" +#undef __RECENT_FILE_ITEM_SORTING_KEY_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::RecentFileItemSortingKeyEnum + * \brief Enumerated type for RecentFilleItem sorting keys + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_recentFileItemSortingKeyEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void recentFileItemSortingKeyEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "RecentFileItemSortingKeyEnum.h" + * + * Instatiate: + * m_recentFileItemSortingKeyEnumComboBox = new EnumComboBoxTemplate(this); + * m_recentFileItemSortingKeyEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_recentFileItemSortingKeyEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(recentFileItemSortingKeyEnumComboBoxItemActivated())); + * + * Update the selection: + * m_recentFileItemSortingKeyEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const RecentFileItemSortingKeyEnum::Enum VARIABLE = m_recentFileItemSortingKeyEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +RecentFileItemSortingKeyEnum::RecentFileItemSortingKeyEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +RecentFileItemSortingKeyEnum::~RecentFileItemSortingKeyEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +RecentFileItemSortingKeyEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(RecentFileItemSortingKeyEnum(DATE_NEWEST, + "DATE_NEWEST", + "Date Newest")); + + enumData.push_back(RecentFileItemSortingKeyEnum(DATE_OLDEST, + "DATE_OLDEST", + "Date Oldest")); + + enumData.push_back(RecentFileItemSortingKeyEnum(MODIFIED_NEWEST, + "MODIFIED_NEWEST", + "Modified Newest")); + + enumData.push_back(RecentFileItemSortingKeyEnum(MODIFIED_OLDEST, + "MODIFIED_OLDEST", + "Modified Oldest")); + + enumData.push_back(RecentFileItemSortingKeyEnum(NAME_ASCENDING, + "NAME_ASCENDING", + "Name Ascending")); + + enumData.push_back(RecentFileItemSortingKeyEnum(NAME_DESCENDING, + "NAME_DESCENDING", + "Name Descending")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const RecentFileItemSortingKeyEnum* +RecentFileItemSortingKeyEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const RecentFileItemSortingKeyEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +RecentFileItemSortingKeyEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFileItemSortingKeyEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +RecentFileItemSortingKeyEnum::Enum +RecentFileItemSortingKeyEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFileItemSortingKeyEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemSortingKeyEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type RecentFileItemSortingKeyEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +RecentFileItemSortingKeyEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFileItemSortingKeyEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +RecentFileItemSortingKeyEnum::Enum +RecentFileItemSortingKeyEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFileItemSortingKeyEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemSortingKeyEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type RecentFileItemSortingKeyEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +RecentFileItemSortingKeyEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const RecentFileItemSortingKeyEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +RecentFileItemSortingKeyEnum::Enum +RecentFileItemSortingKeyEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFileItemSortingKeyEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemSortingKeyEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type RecentFileItemSortingKeyEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +RecentFileItemSortingKeyEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +RecentFileItemSortingKeyEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(RecentFileItemSortingKeyEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +RecentFileItemSortingKeyEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(RecentFileItemSortingKeyEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemSortingKeyEnum.h connectome-workbench-1.5.0/src/Common/RecentFileItemSortingKeyEnum.h --- connectome-workbench-1.4.2/src/Common/RecentFileItemSortingKeyEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemSortingKeyEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,112 @@ +#ifndef __RECENT_FILE_ITEM_SORTING_KEY_ENUM_H__ +#define __RECENT_FILE_ITEM_SORTING_KEY_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class RecentFileItemSortingKeyEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Newest by date */ + DATE_NEWEST, + /** Oldest by date */ + DATE_OLDEST, + /** Newest by modified date */ + MODIFIED_NEWEST, + /** Oldest by modified date */ + MODIFIED_OLDEST, + /** Ascending by name */ + NAME_ASCENDING, + /** Dscending by name */ + NAME_DESCENDING + }; + + + ~RecentFileItemSortingKeyEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + RecentFileItemSortingKeyEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const RecentFileItemSortingKeyEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __RECENT_FILE_ITEM_SORTING_KEY_ENUM_DECLARE__ +std::vector RecentFileItemSortingKeyEnum::enumData; +bool RecentFileItemSortingKeyEnum::initializedFlag = false; +int32_t RecentFileItemSortingKeyEnum::integerCodeCounter = 0; +#endif // __RECENT_FILE_ITEM_SORTING_KEY_ENUM_DECLARE__ + +} // namespace +#endif //__RECENT_FILE_ITEM_SORTING_KEY_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemTypeEnum.cxx connectome-workbench-1.5.0/src/Common/RecentFileItemTypeEnum.cxx --- connectome-workbench-1.4.2/src/Common/RecentFileItemTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __RECENT_FILE_ITEM_TYPE_ENUM_DECLARE__ +#include "RecentFileItemTypeEnum.h" +#undef __RECENT_FILE_ITEM_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::RecentFileItemTypeEnum + * \brief Types for recent file items + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_recentFileTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void recentFileTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "RecentFileItemTypeEnum.h" + * + * Instatiate: + * m_recentFileTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_recentFileTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_recentFileTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(recentFileTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_recentFileTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const RecentFileItemTypeEnum::Enum VARIABLE = m_recentFileTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +RecentFileItemTypeEnum::RecentFileItemTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +RecentFileItemTypeEnum::~RecentFileItemTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +RecentFileItemTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(RecentFileItemTypeEnum(DIRECTORY, + "DIRECTORY", + "Directory")); + + enumData.push_back(RecentFileItemTypeEnum(SCENE_FILE, + "SCENE_FILE", + "Scene File")); + + enumData.push_back(RecentFileItemTypeEnum(SPEC_FILE, + "SPEC_FILE", + "Spec File")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const RecentFileItemTypeEnum* +RecentFileItemTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const RecentFileItemTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +RecentFileItemTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFileItemTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +RecentFileItemTypeEnum::Enum +RecentFileItemTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFileItemTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type RecentFileItemTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +RecentFileItemTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFileItemTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +RecentFileItemTypeEnum::Enum +RecentFileItemTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFileItemTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type RecentFileItemTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +RecentFileItemTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const RecentFileItemTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +RecentFileItemTypeEnum::Enum +RecentFileItemTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFileItemTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFileItemTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type RecentFileItemTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +RecentFileItemTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +RecentFileItemTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(RecentFileItemTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +RecentFileItemTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(RecentFileItemTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/RecentFileItemTypeEnum.h connectome-workbench-1.5.0/src/Common/RecentFileItemTypeEnum.h --- connectome-workbench-1.4.2/src/Common/RecentFileItemTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFileItemTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __RECENT_FILE_ITEM_TYPE_ENUM_H__ +#define __RECENT_FILE_ITEM_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class RecentFileItemTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Directory */ + DIRECTORY, + /** Scene File */ + SCENE_FILE, + /** Spec File */ + SPEC_FILE + }; + + + ~RecentFileItemTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + RecentFileItemTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const RecentFileItemTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __RECENT_FILE_ITEM_TYPE_ENUM_DECLARE__ +std::vector RecentFileItemTypeEnum::enumData; +bool RecentFileItemTypeEnum::initializedFlag = false; +int32_t RecentFileItemTypeEnum::integerCodeCounter = 0; +#endif // __RECENT_FILE_ITEM_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__RECENT_FILE_ITEM_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/RecentFilesSystemAccessModeEnum.cxx connectome-workbench-1.5.0/src/Common/RecentFilesSystemAccessModeEnum.cxx --- connectome-workbench-1.4.2/src/Common/RecentFilesSystemAccessModeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFilesSystemAccessModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __RECENT_FILES_SYSTEM_ACCESS_MODE_ENUM_DECLARE__ +#include "RecentFilesSystemAccessModeEnum.h" +#undef __RECENT_FILES_SYSTEM_ACCESS_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::RecentFilesSystemAccessModeEnum + * \brief Enumerated type for accessing files system in recent files processing + * + * Enumerated type for accessing files system in recent files processing. + * In some instances (bad NFS mount), accessing the file system to get file information + * may be very slow. This enumerated type is a mode that controls accessing the + * file system. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_recentFilesSystemAccessModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void recentFilesSystemAccessModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "RecentFilesSystemAccessModeEnum.h" + * + * Instatiate: + * m_recentFilesSystemAccessModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_recentFilesSystemAccessModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_recentFilesSystemAccessModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(recentFilesSystemAccessModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_recentFilesSystemAccessModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const RecentFilesSystemAccessModeEnum::Enum VARIABLE = m_recentFilesSystemAccessModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +RecentFilesSystemAccessModeEnum::RecentFilesSystemAccessModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +RecentFilesSystemAccessModeEnum::~RecentFilesSystemAccessModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +RecentFilesSystemAccessModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(RecentFilesSystemAccessModeEnum(ON, + "ON", + "On")); + + enumData.push_back(RecentFilesSystemAccessModeEnum(OFF, + "OFF", + "Off")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const RecentFilesSystemAccessModeEnum* +RecentFilesSystemAccessModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const RecentFilesSystemAccessModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +RecentFilesSystemAccessModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFilesSystemAccessModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +RecentFilesSystemAccessModeEnum::Enum +RecentFilesSystemAccessModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFilesSystemAccessModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFilesSystemAccessModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type RecentFilesSystemAccessModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +RecentFilesSystemAccessModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const RecentFilesSystemAccessModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +RecentFilesSystemAccessModeEnum::Enum +RecentFilesSystemAccessModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFilesSystemAccessModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFilesSystemAccessModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type RecentFilesSystemAccessModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +RecentFilesSystemAccessModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const RecentFilesSystemAccessModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +RecentFilesSystemAccessModeEnum::Enum +RecentFilesSystemAccessModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = RecentFilesSystemAccessModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const RecentFilesSystemAccessModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type RecentFilesSystemAccessModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +RecentFilesSystemAccessModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +RecentFilesSystemAccessModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(RecentFilesSystemAccessModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +RecentFilesSystemAccessModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(RecentFilesSystemAccessModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/RecentFilesSystemAccessModeEnum.h connectome-workbench-1.5.0/src/Common/RecentFilesSystemAccessModeEnum.h --- connectome-workbench-1.4.2/src/Common/RecentFilesSystemAccessModeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/RecentFilesSystemAccessModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,104 @@ +#ifndef __RECENT_FILES_SYSTEM_ACCESS_MODE_ENUM_H__ +#define __RECENT_FILES_SYSTEM_ACCESS_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class RecentFilesSystemAccessModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** On */ + ON, + /** Off */ + OFF + }; + + + ~RecentFilesSystemAccessModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + RecentFilesSystemAccessModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const RecentFilesSystemAccessModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __RECENT_FILES_SYSTEM_ACCESS_MODE_ENUM_DECLARE__ +std::vector RecentFilesSystemAccessModeEnum::enumData; +bool RecentFilesSystemAccessModeEnum::initializedFlag = false; +int32_t RecentFilesSystemAccessModeEnum::integerCodeCounter = 0; +#endif // __RECENT_FILES_SYSTEM_ACCESS_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__RECENT_FILES_SYSTEM_ACCESS_MODE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/StructureEnum.cxx connectome-workbench-1.5.0/src/Common/StructureEnum.cxx --- connectome-workbench-1.4.2/src/Common/StructureEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/StructureEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -287,7 +287,7 @@ *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type StructureEnum")); + CaretAssertMessage(0, AString("Name " + name + " failed to match enumerated value for type StructureEnum")); } return enumValue; } @@ -340,7 +340,7 @@ *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type StructureEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + " failed to match enumerated value for type StructureEnum")); } return enumValue; } @@ -396,7 +396,7 @@ *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + ciftiName + "failed to match enumerated value for type StructureEnum")); + CaretAssertMessage(0, AString("ciftiName " + ciftiName + " failed to match enumerated value for type StructureEnum")); } return enumValue; } @@ -449,7 +449,7 @@ *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type StructureEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + " failed to match enumerated value for type StructureEnum")); } return enumValue; } diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsBaseConfiguration.cxx connectome-workbench-1.5.0/src/Common/TileTabsBaseConfiguration.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsBaseConfiguration.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsBaseConfiguration.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,328 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#include - -#define __TILE_TABS_BASE_CONFIGURATION_DECLARE__ -#include "TileTabsBaseConfiguration.h" -#undef __TILE_TABS_BASE_CONFIGURATION_DECLARE__ - -#include -#include - -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "SystemUtilities.h" -#include "TileTabsGridLayoutConfiguration.h" - -using namespace caret; - - - -/** - * \class caret::TileTabsBaseConfiguration - * \brief Defines a tile tabs configuration - * \ingroup Common - */ - -/** - * Constructor that creates a 2 by 2 configuration. - */ -TileTabsBaseConfiguration::TileTabsBaseConfiguration(const TileTabsConfigurationLayoutTypeEnum::Enum layoutType) -: CaretObject(), -m_layoutType(layoutType) -{ - initializeTileTabsBaseConfiguration(); -} - -/** - * Destructor. - */ -TileTabsBaseConfiguration::~TileTabsBaseConfiguration() -{ -} - -/** - * Copy constructor. - * - * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() - * - * @param obj - * Object that is copied. - */ -TileTabsBaseConfiguration::TileTabsBaseConfiguration(const TileTabsBaseConfiguration& obj) -: CaretObject(obj), -m_layoutType(obj.m_layoutType) -{ - const AString savedUniqueID = m_uniqueIdentifier; - initializeTileTabsBaseConfiguration(); - m_uniqueIdentifier = savedUniqueID; - this->copyHelperTileTabsBaseConfiguration(obj); -} - -/** - * Assignment operator. - * - * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() - * - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -TileTabsBaseConfiguration& -TileTabsBaseConfiguration::operator=(const TileTabsBaseConfiguration& obj) -{ - if (this != &obj) { - CaretObject::operator=(obj); - this->copyHelperTileTabsBaseConfiguration(obj); - } - return *this; -} - -/** - * Initialize an instance of a tile tabs configuration. - */ -void -TileTabsBaseConfiguration::initializeTileTabsBaseConfiguration() -{ - m_name.clear(); - m_uniqueIdentifier = SystemUtilities::createUniqueID(); -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -TileTabsBaseConfiguration::copyHelperTileTabsBaseConfiguration(const TileTabsBaseConfiguration& obj) -{ - if (this == &obj) { - return; - } - - m_name = obj.m_name; - //DO NOT CHANGE THE UNIQUE IDENTIFIER: m_uniqueIdentifier -} - -/** - * Copies the tile tabs configuration rows, columns, and - * stretch factors. Name is NOT copied. - */ -void -TileTabsBaseConfiguration::copy(const TileTabsBaseConfiguration& rhs) -{ - CaretAssertToDoFatal(); // need to do this a different way - AString savedName = m_name; - copyHelperTileTabsBaseConfiguration(rhs); - m_name = savedName; -} - -/** - * @return Type of layout for the configuration - */ -TileTabsConfigurationLayoutTypeEnum::Enum -TileTabsBaseConfiguration::getLayoutType() const -{ - return m_layoutType; -} - -/** - * @return the name of the tile tabs configuration. - */ -AString -TileTabsBaseConfiguration::getName() const -{ - return m_name; -} - -/** - * @return Get the unique identifier that uniquely identifies each configuration. - */ -AString -TileTabsBaseConfiguration::getUniqueIdentifier() const -{ - return m_uniqueIdentifier; -} - -/** - * Set the name of the tile tabs configuration. - * - * @param name - * New name for configuration. - */ -void -TileTabsBaseConfiguration::setName(const AString& name) -{ - m_name = name; -} - -/** - * Set the unique identifier of the tile tabs configuration. - * - * @param uniqueID - * New unique identifier for configuration. - */ -void -TileTabsBaseConfiguration::setUniqueIdentifierProtected(const AString& uniqueID) -{ - m_uniqueIdentifier = uniqueID; -} - - -/** - * @return Encoded tile tabs configuration in XML - */ -AString -TileTabsBaseConfiguration::encodeInXML() const -{ - AString s; - encodeInXML(s); - return s; -} - -/** - * Decode the tile tabs configuration from XML - * - * @param xmlString - * String containing XML. - * @param errorMessageOut - * Contains error information if decoding fails. - * @return - * Pointer to the configuration or NULL if there was an error. - */ -TileTabsBaseConfiguration* -TileTabsBaseConfiguration::decodeFromXML(const AString& xmlString, - AString& errorMessageOut) -{ - errorMessageOut.clear(); - TileTabsBaseConfiguration* configurationOut(NULL); - - QXmlStreamReader xml(xmlString); - - if (xml.readNextStartElement()) { - const QStringRef tagName(xml.name()); - if (tagName == TileTabsGridLayoutConfiguration::s_v1_rootTagName) { - TileTabsGridLayoutConfiguration* v1 = new TileTabsGridLayoutConfiguration(); - v1->decodeFromXML(xml, - tagName.toString()); - configurationOut = v1; - } - else if (tagName == TileTabsGridLayoutConfiguration::s_v2_rootTagName) { - TileTabsGridLayoutConfiguration* v2 = new TileTabsGridLayoutConfiguration(); - v2->decodeFromXML(xml, - tagName.toString()); - configurationOut = v2; - } - else { - xml.raiseError("TileTabsBaseConfiguration first element is " - + xml.name().toString() - + " but should be " - + TileTabsGridLayoutConfiguration::s_v1_rootTagName - + " or " - + TileTabsGridLayoutConfiguration::s_v2_rootTagName); - } - } - else { - xml.raiseError("TileTabsBaseConfiguration failed to find start elemnent."); - } - - if (xml.hasError()) { - errorMessageOut = ("Tile Tabs Configuration Read Error at line number=" - + AString::number(xml.lineNumber()) - + " column number=" - + AString::number(xml.columnNumber()) - + " description=" - + xml.errorString()); - if (configurationOut != NULL) { - delete configurationOut; - configurationOut = NULL; - } - return configurationOut; - } - - const bool debugFlag(false); - if (debugFlag) { -// AString xmlText = encodeInXMLWithStreamWriterVersionTwo(); -// std::cout << std::endl << "NEW: " << xmlText << std::endl << std::endl; -// AString em; -// TileTabsBaseConfiguration temp; -// QXmlStreamReader tempReader(xmlText); -// tempReader.readNextStartElement(); -// temp.decodeFromXMLWithStreamReaderVersionTwo(tempReader); -// if (tempReader.hasError()) { -// std::cout << "Decode error: " << tempReader.errorString() << std::endl; -// } -// else { -// std::cout << "Decoded: " << temp.toString() << std::endl; -// } -// -// std::cout << std::endl; - } - return configurationOut; -} - - -/** - * @return String version of an instance. - */ -AString -TileTabsBaseConfiguration::toString() const -{ - AString s("Name: %1, Unique ID: %2\n"); - s = s.arg(m_name).arg(m_uniqueIdentifier); -// -// int32_t indx(0); -// for (const auto item : m_columns) { -// s.append(" Column " + AString::number(indx) + ": " + item.toString() + "\n"); -// indx++; -// } -// indx = 0; -// for (const auto item : m_rows) { -// s.append(" Row " + AString::number(indx) + ": " + item.toString() + "\n"); -// indx++; -// } - - return s; -} - -/** - * Compare two tile tabs configurations by name. - * - * @param ttc1 - * First tile tab configuration. - * @param ttc2 - * Second tile tab configuration. - * @return - * True if ttc1 is "less than" when compared by name, else false. - */ -bool -TileTabsBaseConfiguration::lessThanComparisonByName(const TileTabsBaseConfiguration* ttc1, - const TileTabsBaseConfiguration* ttc2) -{ - if (ttc1->getName() < ttc2->getName()) { - return true; - } - return false; -} - diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsBaseConfiguration.h connectome-workbench-1.5.0/src/Common/TileTabsBaseConfiguration.h --- connectome-workbench-1.4.2/src/Common/TileTabsBaseConfiguration.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsBaseConfiguration.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -#ifndef __TILE_TABS_BASE_CONFIGURATION_H__ -#define __TILE_TABS_BASE_CONFIGURATION_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include "CaretException.h" -#include "CaretObject.h" -#include "TileTabsConfigurationLayoutTypeEnum.h" -#include "TileTabsGridModeEnum.h" -#include "TileTabsGridRowColumnElement.h" - -class QXmlStreamReader; -class QXmlStreamWriter; - -namespace caret { - - class TileTabsBaseConfiguration : public CaretObject { - - protected: - TileTabsBaseConfiguration(const TileTabsConfigurationLayoutTypeEnum::Enum layoutType); - - public: - virtual ~TileTabsBaseConfiguration(); - - TileTabsBaseConfiguration(const TileTabsBaseConfiguration& obj); - - TileTabsBaseConfiguration& operator=(const TileTabsBaseConfiguration& obj); - - void copy(const TileTabsBaseConfiguration& rhs); - - virtual TileTabsBaseConfiguration* newCopyWithNewUniqueIdentifier() const = 0; - - TileTabsConfigurationLayoutTypeEnum::Enum getLayoutType() const; - - AString getName() const; - - void setName(const AString& name); - - AString getUniqueIdentifier() const; - - - AString encodeInXML() const; - - static TileTabsBaseConfiguration* decodeFromXML(const AString& xmlString, - AString& errorMessageOut); - - AString toString() const override; - - static bool lessThanComparisonByName(const TileTabsBaseConfiguration* ttc1, - const TileTabsBaseConfiguration* ttc2); - - // ADD_NEW_METHODS_HERE - - - protected: - /** - * Decode the configuration using the given XML stream reader and root element - * If there is an error, xml.raiseError() should be used to specify the error - * and caller of this method can test for the error using xml.isError(). - * - * @param xml - * The XML stream reader. - * @param rootElement - * The root element. - */ - virtual void decodeFromXML(QXmlStreamReader& xml, - const QString& rootElementText) = 0; - - /** - * Encode the configuration in XML. - * - * @param xmlTextOut - * Contains XML representation of configuration. - */ - virtual void encodeInXML(AString& xmlTextOut) const = 0; - - void setUniqueIdentifierProtected(const AString& uniqueID); - - void copyHelperTileTabsBaseConfiguration(const TileTabsBaseConfiguration& obj); - - private: - - void initializeTileTabsBaseConfiguration(); - - // ADD_NEW_MEMBERS_HERE - - const TileTabsConfigurationLayoutTypeEnum::Enum m_layoutType; - - AString m_name; - - /** Unique identifier does not get copied */ - AString m_uniqueIdentifier; - - - }; - -#ifdef __TILE_TABS_BASE_CONFIGURATION_DECLARE__ - -#endif // __TILE_TABS_BASE_CONFIGURATION_DECLARE__ - -} // namespace -#endif //__TILE_TABS_BASE_CONFIGURATION_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsBrowserTabGeometry.cxx connectome-workbench-1.5.0/src/Common/TileTabsBrowserTabGeometry.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsBrowserTabGeometry.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsBrowserTabGeometry.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,526 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __TILE_TABS_BROWSER_TAB_GEOMETRY_DECLARE__ +#include "TileTabsBrowserTabGeometry.h" +#undef __TILE_TABS_BROWSER_TAB_GEOMETRY_DECLARE__ + +#include + +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "MathFunctions.h" + +using namespace caret; + +/** + * \class caret::TileTabsBrowserTabGeometry + * \brief Information about a tab's position in the window using percentage coordinates + * \ingroup Common + * + * + * + */ + +/** + * Constructor. + */ +TileTabsBrowserTabGeometry::TileTabsBrowserTabGeometry(const int32_t tabIndex) +: CaretObject(), +m_tabIndex(tabIndex) +{ + float xy(10.0f + (5.0 * tabIndex)); + const float widthHeight(20.0); + xy = MathFunctions::limitRange(xy, 5.0f, 95.0f - widthHeight); + m_minX = xy; + m_maxX = xy + widthHeight; + m_minY = xy; + m_maxY = xy + widthHeight; + m_stackingOrder = tabIndex; +} + +/** + * Destructor. + */ +TileTabsBrowserTabGeometry::~TileTabsBrowserTabGeometry() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +TileTabsBrowserTabGeometry::TileTabsBrowserTabGeometry(const TileTabsBrowserTabGeometry& obj) +: CaretObject(obj) +{ + this->copyHelperTileTabsBrowserTabGeometry(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +TileTabsBrowserTabGeometry& +TileTabsBrowserTabGeometry::operator=(const TileTabsBrowserTabGeometry& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperTileTabsBrowserTabGeometry(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +TileTabsBrowserTabGeometry::copyHelperTileTabsBrowserTabGeometry(const TileTabsBrowserTabGeometry& obj) +{ + m_displayFlag = obj.m_displayFlag; + m_tabIndex = obj.m_tabIndex; + m_minX = obj.m_minX; + m_maxX = obj.m_maxX; + m_minY = obj.m_minY; + m_maxY = obj.m_maxY; + m_stackingOrder = obj.m_stackingOrder; + m_backgroundType = obj.m_backgroundType; +} + +/** + * Copy the given geometry to 'this' + * + * @param geometry + * Geometry that is copied + */ +void +TileTabsBrowserTabGeometry::copyGeometry(const TileTabsBrowserTabGeometry& geometry) +{ + copyHelperTileTabsBrowserTabGeometry(geometry); +} + +/** + * @return Index of the tab + */ +int32_t +TileTabsBrowserTabGeometry::getTabIndex() const +{ + return m_tabIndex; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +TileTabsBrowserTabGeometry::toString() const +{ + AString str; + QTextStream ts(&str); + + ts << "TileTabsBrowserTabGeometry: " + << "m_displayFlag=" << AString::fromBool(m_displayFlag) + << " m_tabIndex=" << m_tabIndex + << " m_minX=" << m_minX + << " m_maxX=" << m_maxX + << " m_minY=" << m_minY + << " m_maxY=" << m_maxY + << " m_stackingOrder=" << m_stackingOrder + << " m_backgroundType=" << TileTabsLayoutBackgroundTypeEnum::toName(m_backgroundType); + + return str; +} + +/** + * @return The display status + */ +bool +TileTabsBrowserTabGeometry::isDisplayed() const +{ + return m_displayFlag; +} + +/** + * Set the display status + * + * @param status + * New display status + */ +void +TileTabsBrowserTabGeometry::setDisplayed(const bool status) +{ + m_displayFlag = status; +} + +/** + * Set the bounds + * + * @param minX + * The minimum X value + * @param maxX + * The maximum X value + * @param minY + * The minimum Y value + * @param maxY + * The maximum Y value + */ +void +TileTabsBrowserTabGeometry::setBounds(const float minX, + const float maxX, + const float minY, + const float maxY) +{ + m_minX = minX; + m_maxX = maxX; + m_minY = minY; + m_maxY = maxY; +} + +/** + * Get the bounds + * + * @param minX + * The minimum X value + * @param maxX + * The maximum X value + * @param minY + * The minimum Y value + * @param maxY + * The maximum Y value + */ +void +TileTabsBrowserTabGeometry::getBounds(float& minX, + float& maxX, + float& minY, + float& maxY) const +{ + minX = m_minX; + maxX = m_maxX; + minY = m_minY; + maxY = m_maxY; +} + +/** + * @return center x-coordinate as percentage 0% to 100% + */ +float +TileTabsBrowserTabGeometry::getCenterX() const +{ + return ((m_minX + m_maxX) / 2.0); +} + +/** + * Set center x-coordinate as percentage 0% to 100% + * + * @param x + * New value for center x-coordinate as percentage 0% to 100% + */ +void +TileTabsBrowserTabGeometry::setCenterX(const float x) +{ + const float dx = x - getCenterX(); + m_minX += dx; + m_maxX += dx; +} + +/** + * @return center y-coordinate as percentage 0% to 100% + */ +float +TileTabsBrowserTabGeometry::getCenterY() const +{ + return ((m_minY + m_maxY) / 2.0); +} + +/** + * Set center y-coordinate as percentage 0% to 100% + * + * @param y + * New value for center y-coordinate as percentage 0% to 100% + */ +void +TileTabsBrowserTabGeometry::setCenterY(const float y) +{ + const float dy = y - getCenterY(); + m_minY += dy; + m_maxY += dy; +} + +/** + * @return width as percentage 0% to 100% + */ +float +TileTabsBrowserTabGeometry::getWidth() const +{ + return (m_maxX - m_minX); +} + +/** + * Set width as percentage 0% to 100% + * + * @param width + * New value for width as percentage 0% to 100% + */ +void +TileTabsBrowserTabGeometry::setWidth(const float width) +{ + const float cx = getCenterX(); + const float half = width / 2.0; + m_minX = cx - half; + m_maxX = cx + half; +} + +/** + * @return height as percentage 0% to 100% + */ +float +TileTabsBrowserTabGeometry::getHeight() const +{ + return (m_maxY - m_minY); +} + +/** + * Set height as percentage 0% to 100% + * + * @param height + * New value for height as percentage 0% to 100% + */ +void +TileTabsBrowserTabGeometry::setHeight(const float height) +{ + const float cy = getCenterY(); + const float half = height / 2.0; + m_minY = cy - half; + m_maxY = cy + half; +} + +/** + * @return minimum x percentage 0% to 100% at left + */ +float +TileTabsBrowserTabGeometry::getMinX() const +{ + return m_minX; +} + +/** + * Set minimum x percentage 0% to 100% at left + * + * @param minX + * New value for minimum x percentage 0% to 100% at left + */ +void +TileTabsBrowserTabGeometry::setMinX(const float minX) +{ + m_minX = minX; + m_maxX = std::max(m_maxX, + m_minX + 1.0f); +} + +/** + * @return maximum x percentage 0% to 100% at rigth + */ +float +TileTabsBrowserTabGeometry::getMaxX() const +{ + return m_maxX; +} + +/** + * Set maximum x percentage 0% to 100% at right + * + * @param maxX + * New value for maximum x percentage 0% to 100% at right + */ +void +TileTabsBrowserTabGeometry::setMaxX(const float maxX) +{ + m_maxX = maxX; + m_minX = std::min(m_minX, + m_maxX - 1.0f); +} + +/** + * @return minimum y percentage 0% to 100% at bottom + */ +float +TileTabsBrowserTabGeometry::getMinY() const +{ + return m_minY; +} + +/** + * Set minimum y percentage 0% to 100% at bottom + * + * @param minY + * New value for minimum y percentage 0% to 100% at bottom + */ +void +TileTabsBrowserTabGeometry::setMinY(const float minY) +{ + m_minY = minY; + m_maxY = std::max(m_maxY, + m_minY + 1.0f); +} + +/** + * @return maximum y percentage 0% to 100% at top + */ +float +TileTabsBrowserTabGeometry::getMaxY() const +{ + return m_maxY; +} + +/** + * Set maximum y percentage 0% to 100% at top + * + * @param maxY + * New value for maximum y percentage 0% to 100% at top + */ +void +TileTabsBrowserTabGeometry::setMaxY(const float maxY) +{ + m_maxY = maxY; + m_minY = std::min(m_minY, + m_maxY - 1.0f); +} + +/** + * Get the window viewport + * + * @param windowWidth + * Width of window in pixels + * @param windowHeight + * Height of window in pixels + * @param viewportOut + * Output containing lower-left X, Y, Width, and Height in window coordinates + */ +void +TileTabsBrowserTabGeometry::getWindowViewport(const int32_t windowWidth, + const int32_t windowHeight, + int32_t viewportOut[4]) const +{ + viewportOut[0] = static_cast((getMinX() / 100.0) * windowWidth); + viewportOut[1] = static_cast((getMinY() / 100.0) * windowHeight); + viewportOut[2] = static_cast((getWidth() / 100.0) * windowWidth); + viewportOut[3] = static_cast((getHeight() / 100.0) * windowHeight); +} + +/** + * @return Stacking order (depth in screen) of tab, greater value is 'in front' + */ +int32_t +TileTabsBrowserTabGeometry::getStackingOrder() const +{ + return m_stackingOrder; +} + +/** + * Set Stacking order (depth in screen) of tab, greater value is 'in front' + * + * @param stackingOrder + * New value for Stacking order (depth in screen) of tab, greater value is 'in front' + */ +void +TileTabsBrowserTabGeometry::setStackingOrder(const int32_t stackingOrder) +{ + m_stackingOrder = stackingOrder; +} + +/** + * @return Type of background (opaque / transparent) for tab + */ +TileTabsLayoutBackgroundTypeEnum::Enum +TileTabsBrowserTabGeometry::getBackgroundType() const +{ + return m_backgroundType; +} + +/** + * Set Type of background (opaque / transparent) for tab + * + * @param backgroundType + * New value for Type of background (opaque / transparent) for tab + */ +void +TileTabsBrowserTabGeometry::setBackgroundType(const TileTabsLayoutBackgroundTypeEnum::Enum backgroundType) +{ + m_backgroundType = backgroundType; +} + +/** + * @return true if this and the given geometry intersect. + * NOTE: if 'other' is 'this' true is returned (overlaps self) but this + * could change so it is best to avoid testing overlap of self. + * + * @param other + * Other geometry for intersection test + */ +bool +TileTabsBrowserTabGeometry::intersectionTest(const TileTabsBrowserTabGeometry* other) const +{ + CaretAssert(other); + + /* + * Does self overlap + */ + if (this == other) { + return true; + } + + /* + * Note: Since the geometry is aligned with the X- and Y-axes, + * we only need to test for one to be above or the the right of the other + * + * https://www.geeksforgeeks.org/find-two-rectangles-overlap/ + * https://leetcode.com/articles/rectangle-overlap/ + */ + /* 'this' is on right side of 'other' */ + if (m_minX >= other->m_maxX) { + return false; + } + + /* 'other' is on right side of 'this' */ + if (other->m_minX >= m_maxX) { + return false; + } + + /* 'this' is above 'other */ + if (m_minY >= other->m_maxY) { + return false; + } + + /* 'other' is above 'this' */ + if (other->m_minY >= m_maxY) { + return false; + } + + return true; +} + diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsBrowserTabGeometry.h connectome-workbench-1.5.0/src/Common/TileTabsBrowserTabGeometry.h --- connectome-workbench-1.4.2/src/Common/TileTabsBrowserTabGeometry.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsBrowserTabGeometry.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,151 @@ +#ifndef __TILE_TABS_BROWSER_TAB_GEOMETRY_H__ +#define __TILE_TABS_BROWSER_TAB_GEOMETRY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "TileTabsLayoutBackgroundTypeEnum.h" + + +namespace caret { + + class TileTabsBrowserTabGeometry : public CaretObject { + + public: + TileTabsBrowserTabGeometry(const int32_t tabIndex); + + virtual ~TileTabsBrowserTabGeometry(); + + TileTabsBrowserTabGeometry(const TileTabsBrowserTabGeometry& obj); + + TileTabsBrowserTabGeometry& operator=(const TileTabsBrowserTabGeometry& obj); + + void copyGeometry(const TileTabsBrowserTabGeometry& geometry); + + int32_t getTabIndex() const; + + bool isDisplayed() const; + + void setDisplayed(const bool status); + + void setBounds(const float minX, + const float maxX, + const float minY, + const float maxY); + + void getBounds(float& minX, + float& maxX, + float& minY, + float& maxY) const; + + float getCenterX() const; + + void setCenterX(const float x); + + float getCenterY() const; + + void setCenterY(const float y); + + float getWidth() const; + + void setWidth(const float width); + + float getHeight() const; + + void setHeight(const float height); + + float getMinX() const; + + void setMinX(const float minX); + + float getMaxX() const; + + void setMaxX(const float maxX); + + float getMinY() const; + + void setMinY(const float minY); + + float getMaxY() const; + + void setMaxY(const float maxY); + + void getWindowViewport(const int32_t windowWidth, + const int32_t windowHeight, + int32_t viewportOut[4]) const; + + int32_t getStackingOrder() const; + + void setStackingOrder(const int32_t stackingOrder); + + TileTabsLayoutBackgroundTypeEnum::Enum getBackgroundType() const; + + void setBackgroundType(const TileTabsLayoutBackgroundTypeEnum::Enum backgroundType); + + bool intersectionTest(const TileTabsBrowserTabGeometry* other) const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperTileTabsBrowserTabGeometry(const TileTabsBrowserTabGeometry& obj); + + /** display status of tab */ + bool m_displayFlag = true; + + /** index of tab */ + int32_t m_tabIndex = -1; + + /** left as percentage*/ + float m_minX = 10.0; + + /** right as percentage*/ + float m_maxX = 20.0; + + /** bottom as percentage*/ + float m_minY = 10.0; + + /** topas percentage*/ + float m_maxY = 20.0; + + /** Stacking order (depth in screen) of tab, greater value is 'in front'*/ + int32_t m_stackingOrder = 1; + + /** Type of background (opaque / transparent) for tab*/ + TileTabsLayoutBackgroundTypeEnum::Enum m_backgroundType = TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG; + + // ADD_NEW_MEMBERS_HERE + + /** IF NEW MEMBERS ADDED, MUST UPDATE THIS CLASS */ + friend class TileTabsBrowserTabGeometrySceneHelper; + }; + +#ifdef __TILE_TABS_BROWSER_TAB_GEOMETRY_DECLARE__ + // +#endif // __TILE_TABS_BROWSER_TAB_GEOMETRY_DECLARE__ + +} // namespace +#endif //__TILE_TABS_BROWSER_TAB_GEOMETRY_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsConfiguration.cxx connectome-workbench-1.5.0/src/Common/TileTabsConfiguration.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsConfiguration.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsConfiguration.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,1182 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#include - -#define __TILE_TABS_CONFIGURATION_DECLARE__ -#include "TileTabsConfiguration.h" -#undef __TILE_TABS_CONFIGURATION_DECLARE__ - -#include -#include - -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "SystemUtilities.h" - -using namespace caret; - - - -/** - * \class caret::TileTabsConfiguration - * \brief Defines a tile tabs configuration - * \ingroup Common - */ - -/** - * Constructor that creates a 2 by 2 configuration. - */ -TileTabsConfiguration::TileTabsConfiguration() -: CaretObject() -{ - initialize(); -} - -/** - * Destructor. - */ -TileTabsConfiguration::~TileTabsConfiguration() -{ -} - -/** - * Copy constructor. - * - * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() - * - * @param obj - * Object that is copied. - */ -TileTabsConfiguration::TileTabsConfiguration(const TileTabsConfiguration& obj) -: CaretObject(obj) -{ - const AString savedUniqueID = m_uniqueIdentifier; - initialize(); - m_uniqueIdentifier = savedUniqueID; - this->copyHelperTileTabsConfiguration(obj); -} - -/** - * Assignment operator. - * - * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() - * - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -TileTabsConfiguration& -TileTabsConfiguration::operator=(const TileTabsConfiguration& obj) -{ - if (this != &obj) { - CaretObject::operator=(obj); - this->copyHelperTileTabsConfiguration(obj); - } - return *this; -} - -/** - * Copy this instance and give it a new unique identifier. - * Note that copy constructor does not create a new unique identifier. - * - * @return The new Copy. - */ -TileTabsConfiguration* -TileTabsConfiguration::newCopyWithNewUniqueIdentifier() const -{ - TileTabsConfiguration* newCopy = new TileTabsConfiguration(*this); - CaretAssert(newCopy); - newCopy->m_uniqueIdentifier = SystemUtilities::createUniqueID(); - return newCopy; -} - - -/** - * Initialize an instance of a tile tabs configuration. - */ -void -TileTabsConfiguration::initialize() -{ - setNumberOfRows(2); - setNumberOfColumns(2); - - m_name.clear(); - m_uniqueIdentifier = SystemUtilities::createUniqueID(); - m_centeringCorrectionEnabled = false; -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -TileTabsConfiguration::copyHelperTileTabsConfiguration(const TileTabsConfiguration& obj) -{ - if (this == &obj) { - return; - } - - m_name = obj.m_name; - m_columns = obj.m_columns; - m_rows = obj.m_rows; - m_centeringCorrectionEnabled = obj.m_centeringCorrectionEnabled; - //DO NOT CHANGE THE UNIQUE IDENTIFIER: m_uniqueIdentifier -} - -/** - * Copies the tile tabs configuration rows, columns, and - * stretch factors. Name is NOT copied. - */ -void -TileTabsConfiguration::copy(const TileTabsConfiguration& rhs) -{ - AString savedName = m_name; - copyHelperTileTabsConfiguration(rhs); - m_name = savedName; -} - -/** - * Get infoformation for the given elemnent in the columns. - * - * @param Information for element. - */ -TileTabsGridRowColumnElement* -TileTabsConfiguration::getColumn(const int32_t columnIndex) -{ - CaretAssertVectorIndex(m_columns, columnIndex); - return &m_columns[columnIndex]; -} - -/** - * Get infoformation for the given elemnent in the columns. - * - * @param Information for element. - */ -const TileTabsGridRowColumnElement* -TileTabsConfiguration::getColumn(const int32_t columnIndex) const -{ - CaretAssertVectorIndex(m_columns, columnIndex); - return &m_columns[columnIndex]; -} - -/** - * Get infoformation for the given elemnent in the rows. - * - * @param Information for element. - */ -TileTabsGridRowColumnElement* -TileTabsConfiguration::getRow(const int32_t rowIndex) -{ - CaretAssertVectorIndex(m_rows, rowIndex); - return &m_rows[rowIndex]; -} - -/** - * Get infoformation for the given elemnent in the rows. - * - * @param Information for element. - */ -const TileTabsGridRowColumnElement* -TileTabsConfiguration::getRow(const int32_t rowIndex) const -{ - CaretAssertVectorIndex(m_rows, rowIndex); - return &m_rows[rowIndex]; -} - -/** - * Get the row heights and column widths for this tile tabs configuration using the - * given window width and height. - * - * @param windowWidth - * Width of window. - * @param windowHeight - * Height of window. - * @param numberOfModelsToDraw - * Number of models to draw. - * @param configurationMode - * The tile tabs configuration mode - * @param rowHeightsOut - * Output containing height of each row. - * @param columnWidthsOut - * Output containing width of each column. - * @return - * True if the ouput is valid, else false. - */ -bool -TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, - const int32_t windowHeight, - const int32_t numberOfModelsToDraw, - const TileTabsGridModeEnum::Enum configurationMode, - std::vector& rowHeightsOut, - std::vector& columnWidthsOut) -{ - /* - * NOTE: When computing widths and heights, do not round. - * Rounding may cause the bottom most row or column to extend - * outside the graphics region. Shrinking the last row or - * column is not desired since it might cause the last model - * to be drawn slightly smaller than the others. - */ - - int32_t numRows = 0; - int32_t numCols = 0; - - rowHeightsOut.clear(); - columnWidthsOut.clear(); - - switch (configurationMode) { - case TileTabsGridModeEnum::AUTOMATIC: - { - /* - * Update number of rows/columns in the default configuration - * so that if a scene is saved, the correct number of rows - * and columns are saved to the scene. - */ - updateAutomaticConfigurationRowsAndColumns(numberOfModelsToDraw); - numRows = getNumberOfRows(); - numCols = getNumberOfColumns(); - - for (int32_t i = 0; i < numRows; i++) { - rowHeightsOut.push_back(windowHeight / numRows); - } - for (int32_t i = 0; i < numCols; i++) { - columnWidthsOut.push_back(windowWidth / numCols); - } - } break; - case TileTabsGridModeEnum::CUSTOM: - { - /* - * Rows/columns from user configuration - */ - numRows = getNumberOfRows(); - numCols = getNumberOfColumns(); - - /* - * Determine height of each row - */ - float rowPercentTotal = 0.0; - float rowStretchTotal = 0.0; - for (int32_t i = 0; i < numRows; i++) { - const TileTabsGridRowColumnElement* e = getRow(i); - switch (e->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - rowPercentTotal += (e->getPercentStretch() / 100.0); - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - rowStretchTotal += e->getWeightStretch(); - break; - } - } - - float windowWeightHeight = windowHeight; - if (rowPercentTotal > 0.0) { - if (rowPercentTotal >= 1.0) { - windowWeightHeight = 0.0; - } - else { - windowWeightHeight = (1.0 - rowPercentTotal) * windowHeight; - } - } - for (int32_t i = 0; i < numRows; i++) { - int32_t h = 0; - const TileTabsGridRowColumnElement* e = getRow(i); - switch (e->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - h = (e->getPercentStretch() / 100.0) * windowHeight; - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - if (rowStretchTotal > 0.0) { - h = static_cast((e->getWeightStretch() / rowStretchTotal) - * windowWeightHeight); - } - break; - } - - rowHeightsOut.push_back(h); - } - - /* - * Determine width of each column - */ - float columnPercentTotal = 0.0; - float columnStretchTotal = 0.0; - for (int32_t i = 0; i < numCols; i++) { - const TileTabsGridRowColumnElement* e = getColumn(i); - switch (e->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - columnPercentTotal += (e->getPercentStretch() / 100.0); - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - columnStretchTotal += e->getWeightStretch(); - break; - } - } - - float windowWeightWidth = windowWidth; - if (columnPercentTotal > 0.0) { - if (columnPercentTotal >= 1.0) { - windowWeightWidth = 0.0; - } - else { - windowWeightWidth = (1.0 - columnPercentTotal) * windowWidth; - } - } - - for (int32_t i = 0; i < numCols; i++) { - int32_t w = 0; - const TileTabsGridRowColumnElement* e = getColumn(i); - switch (e->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - w = (e->getPercentStretch() / 100.0) * windowWidth; - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - if (columnStretchTotal > 0.0) { - w = static_cast((e->getWeightStretch() / columnStretchTotal) - * windowWeightWidth); - } - break; - } - columnWidthsOut.push_back(w); - } - } break; - } - - if ((numRows == static_cast(rowHeightsOut.size())) - && (numCols == static_cast(columnWidthsOut.size()))) { -// /* -// * Verify all rows fit within the window -// */ -// int32_t rowHeightsSum = 0; -// for (int32_t i = 0; i < numRows; i++) { -// rowHeightsSum += rowHeightsOut[i]; -// } -// if (rowHeightsSum > windowHeight) { -// CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); -//// rowHeightsOut[numRows - 1] -= (rowHeightsSum - windowHeight); -// } -// -// /* -// * Adjust width of last column so that it does not extend beyond viewport -// */ -// int32_t columnWidthsSum = 0; -// for (int32_t i = 0; i < numCols; i++) { -// columnWidthsSum += columnWidthsOut[i]; -// } -// if (columnWidthsSum > windowWidth) { -// CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); -//// columnWidthsOut[numCols - 1] = columnWidthsSum - windowWidth; -// } -// -// CaretLogFiner("Tile Tabs Row Heights: " -// + AString::fromNumbers(rowHeightsOut, ", ")); -// CaretLogFiner("Tile Tabs Column Widths: " -// + AString::fromNumbers(columnWidthsOut, ", ")); - return true; - } - - const QString msg("Row and heights failed rows=" - + AString::number(numRows) - + " rowHeights=" - + AString::number(rowHeightsOut.size()) - + " cols=" - + AString::number(numCols) - + " rowHeights=" - + AString::number(columnWidthsOut.size())); - CaretAssertMessage(0, msg); - CaretLogSevere(msg); - return false; -} - - -/** - * @return the name of the tile tabs configuration. - */ -AString -TileTabsConfiguration::getName() const -{ - return m_name; -} - -/** - * @return Get the unique identifier that uniquely identifies each configuration. - */ -AString -TileTabsConfiguration::getUniqueIdentifier() const -{ - return m_uniqueIdentifier; -} - -/** - * Set the name of the tile tabs configuration. - * - * @param name - * New name for configuration. - */ -void -TileTabsConfiguration::setName(const AString& name) -{ - m_name = name; -} - -/** - * @return Number of rows. - */ -int32_t -TileTabsConfiguration::getNumberOfRows() const -{ - return m_rows.size(); -} - -/** - * Set number of rows. - * - * @param numberOfRows - * New number of rows. - */ -void -TileTabsConfiguration::setNumberOfRows(const int32_t numberOfRows) -{ - m_rows.resize(numberOfRows); -} - -/** - * @return Number of columns. - */ -int32_t -TileTabsConfiguration::getNumberOfColumns() const -{ - return m_columns.size(); -} - -/** - * Set number of rows. - * - * @param numberOfColumns - * New number of rows. - */ -void -TileTabsConfiguration::setNumberOfColumns(const int32_t numberOfColumns) -{ - m_columns.resize(numberOfColumns); -} - -/** - * Get the number of rows and columns for an automatic layout with the - * given number of tabs. - * @param numberOfTabs - * Number of tabs. - * @param numberOfRowsOut - * Output with number of rows. - * @param numberOfColumnsOut - * Output with number of columns. - */ -void -TileTabsConfiguration::getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, - int32_t& numberOfRowsOut, - int32_t& numberOfColumnsOut) -{ - CaretAssert(numberOfTabs >= 0); - - numberOfRowsOut = (int)std::sqrt((double)numberOfTabs); - numberOfColumnsOut = numberOfRowsOut; - int32_t row2 = numberOfRowsOut * numberOfRowsOut; - if (row2 < numberOfTabs) { - numberOfColumnsOut++; - } - if ((numberOfRowsOut * numberOfColumnsOut) < numberOfTabs) { - numberOfRowsOut++; - } -} - -/** - * @return True if the centering correction is enabled. - */ -bool -TileTabsConfiguration::isCenteringCorrectionEnabled() const -{ - return m_centeringCorrectionEnabled; -} - -/** - * Set the enabled status of the centering correction - * - * @param status - * New status for enabling the centering correction - */ -void -TileTabsConfiguration::setCenteringCorrectionEnabled(const bool status) -{ - m_centeringCorrectionEnabled = status; -} - - -/** - * Updates the number of rows and columns for the automatic configuration - * based upon the number of tabs. - * - * Since screen width typically exceeds height, ensure the number of - * columns is always greater than the number of rows. - */ -void -TileTabsConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs) -{ - int32_t numRows(0), numCols(0); - getRowsAndColumnsForNumberOfTabs(numberOfTabs, - numRows, - numCols); - - setNumberOfRows(numRows); - setNumberOfColumns(numCols); -} - -/** - * @return Encoded tile tabs configuration in XML - */ -AString -TileTabsConfiguration::encodeInXML() const -{ - return encodeVersionInXML(2); -} - -/** - * @return Encoded tile tabs configuration in XML - * using the give XML version of TileTabsConfiguration. - */ -AString -TileTabsConfiguration::encodeVersionInXML(const int32_t versionNumber) const -{ - AString s; - - switch (versionNumber) { - case 1: - s = encodeInXMLWithStreamWriterVersionOne(); - break; - case 2: - s = encodeInXMLWithStreamWriterVersionTwo(); - break; - default: - CaretAssertMessage(0, "Requested invalid version=" + AString::number(versionNumber)); - break; - } - - return s; -} - - -/** - * @return Encoded tile tabs configuration in XML with Stream Writer - */ -AString -TileTabsConfiguration::encodeInXMLWithStreamWriterVersionOne() const -{ - AString xmlString; - QXmlStreamWriter writer(&xmlString); - writer.setAutoFormatting(true); - - writer.writeStartElement(s_v1_rootTagName); - - writer.writeStartElement(s_v1_versionTagName); - writer.writeAttribute(s_v1_versionNumberAttributeName, "1"); - writer.writeEndElement(); - - writer.writeTextElement(s_nameTagName, m_name); - writer.writeTextElement(s_uniqueIdentifierTagName, m_uniqueIdentifier); - - const int32_t numberOfRows = getNumberOfRows(); - writer.writeStartElement(s_v1_rowStretchFactorsTagName); - writer.writeAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName, AString::number(numberOfRows)); - writer.writeAttribute(s_v1_rowStretchFactorsTotalCountAttributeName, AString::number(numberOfRows)); - std::vector rowStretchFactors; - for (const auto e : m_rows) { - rowStretchFactors.push_back(e.getWeightStretch()); - } - writer.writeCharacters(AString::fromNumbers(rowStretchFactors, " ")); - writer.writeEndElement(); - - const int32_t numberOfColumns = getNumberOfColumns(); - writer.writeStartElement(s_v1_columnStretchFactorsTagName); - writer.writeAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName, AString::number(numberOfColumns)); - writer.writeAttribute(s_v1_columnStretchFactorsTotalCountAttributeName, AString::number(numberOfColumns)); - std::vector columnStretchFactors; - for (const auto e : m_columns) { - columnStretchFactors.push_back(e.getWeightStretch()); - } - writer.writeCharacters(AString::fromNumbers(columnStretchFactors, " ")); - writer.writeEndElement(); - - writer.writeEndElement(); - - return xmlString; -} - -/** - * @return Encoded tile tabs configuration in XML with Stream Writer - */ -AString -TileTabsConfiguration::encodeInXMLWithStreamWriterVersionTwo() const -{ - AString xmlString; - QXmlStreamWriter writer(&xmlString); - writer.setAutoFormatting(true); - - writer.writeStartElement(s_v2_rootTagName); - writer.writeAttribute(s_v2_versionAttributeName, "2"); - - writer.writeTextElement(s_nameTagName, m_name); - writer.writeTextElement(s_uniqueIdentifierTagName, m_uniqueIdentifier); - writer.writeTextElement(s_v2_centeringCorrectionName, AString::fromBool(m_centeringCorrectionEnabled)); - - encodeRowColumnElement(writer, s_v2_columnsTagName, m_columns); - encodeRowColumnElement(writer, s_v2_rowsTagName, m_rows); - - writer.writeEndElement(); - - return xmlString; -} - -/** - * Encode a vector of elements into xml. - * - * @param writer - * The XML stream writer. - * @param tagName - * Tag name for enclosing the elements. - * @param elements - * Vector of elements added to XML. - */ -void -TileTabsConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer, - const AString tagName, - const std::vector& elements) const -{ - writer.writeStartElement(tagName); - - for (const auto e : elements) { - writer.writeStartElement(s_v2_elementTagName); - writer.writeAttribute(s_v2_contentTypeAttributeName, TileTabsGridRowColumnContentTypeEnum::toName(e.getContentType())); - writer.writeAttribute(s_v2_stretchTypeAttributeName, TileTabsGridRowColumnStretchTypeEnum::toName(e.getStretchType())); - writer.writeAttribute(s_v2_percentStretchAttributeName, AString::number(e.getPercentStretch(), 'f', 2)); - writer.writeAttribute(s_v2_weightStretchAttributeName, AString::number(e.getWeightStretch(), 'f', 2)); - writer.writeEndElement(); - } - - writer.writeEndElement(); -} - -/** - * Decode the tile tabs configuration from XML with stream reader. - * - * @param xmlString - * String containing XML. - * @param errorMessageOut - * Will contain error information. - * @return - * True if decoding is successful, else false. - */ -bool -TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString, - AString& errorMessageOut) -{ - m_centeringCorrectionEnabled = false; - - QXmlStreamReader xml(xmlString); - - if (xml.readNextStartElement()) { - const QStringRef tagName(xml.name()); - if (tagName == s_v1_rootTagName) { - decodeFromXMLWithStreamReaderVersionOne(xml); - } - else if (tagName == s_v2_rootTagName) { - /* - * Version 2 uses a different root tag than version 1. The reason is that - * the older code for decoding from XML will throw an exception if it - * encounters invalid elements or the version number is invalid. The problem - * is that the exception is not caught and wb_view will terminate. - */ - QString versionNumberText("Unknown"); - const QXmlStreamAttributes atts = xml.attributes(); - if (atts.hasAttribute(s_v2_versionAttributeName)) { - versionNumberText = atts.value(s_v2_versionAttributeName).toString(); - } - - if (versionNumberText == "2") { - decodeFromXMLWithStreamReaderVersionTwo(xml); - } - else { - xml.raiseError("TileTabsConfiguration invalid version=" - + versionNumberText); - } - } - else { - xml.raiseError("TileTabsConfiguration first element is " - + xml.name().toString() - + " but should be " - + s_v1_rootTagName - + " or " - + s_v2_rootTagName); - } - } - else { - xml.raiseError("TileTabsConfiguration failed to find start elemnent."); - } - - if (xml.hasError()) { - errorMessageOut = ("Tile Tabs Configuration Read Error at line number=" - + AString::number(xml.lineNumber()) - + " column number=" - + AString::number(xml.columnNumber()) - + " description=" - + xml.errorString()); - return false; - } - - const bool debugFlag(false); - if (debugFlag) { - AString xmlText = encodeInXMLWithStreamWriterVersionTwo(); - std::cout << std::endl << "NEW: " << xmlText << std::endl << std::endl; - AString em; - TileTabsConfiguration temp; - QXmlStreamReader tempReader(xmlText); - tempReader.readNextStartElement(); - temp.decodeFromXMLWithStreamReaderVersionTwo(tempReader); - if (tempReader.hasError()) { - std::cout << "Decode error: " << tempReader.errorString() << std::endl; - } - else { - std::cout << "Decoded: " << temp.toString() << std::endl; - } - - std::cout << std::endl; - } - return true; -} - -/** - * Decode Version One of the tile tabs configuration from XML with stream reader. - * - * @param xml - * Stream XML parser. - */ -void -TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml) -{ - std::set invalidElements; - - AString name; - AString uniqueID; - std::vector rowStretchFactors; - std::vector columnStretchFactors; - int32_t numberOfRows(0); - int32_t numberOfColumns(0); - - QString message; - - while ( ! xml.atEnd()) { - xml.readNext(); - - if (xml.isStartElement()) { - const QStringRef tagName(xml.name()); - - if (tagName == s_v1_versionTagName) { - /* ignore */ - } - else if (tagName == s_nameTagName) { - name = xml.readElementText(); - } - else if (tagName == s_uniqueIdentifierTagName) { - uniqueID = xml.readElementText(); - } - else if (tagName == s_v1_rowStretchFactorsTagName) { - QXmlStreamAttributes atts = xml.attributes(); - if (atts.hasAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName)) { - numberOfRows = atts.value(s_v1_rowStretchFactorsSelectedCountAttributeName).toInt(); - } - - AString::toNumbers(xml.readElementText(), rowStretchFactors); - } - else if (tagName == s_v1_columnStretchFactorsTagName) { - QXmlStreamAttributes atts = xml.attributes(); - if (atts.hasAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName)) { - numberOfColumns = atts.value(s_v1_columnStretchFactorsSelectedCountAttributeName).toInt(); - } - AString::toNumbers(xml.readElementText(), columnStretchFactors); - } - else { - invalidElements.insert(tagName.toString()); - xml.skipCurrentElement(); - } - } - } - - static int32_t missingNameCounter = 1; - if (name.isEmpty()) { - name = ("Config_V1_" - + AString::number(missingNameCounter)); - missingNameCounter++; - } - if (uniqueID.isEmpty()) { - uniqueID = SystemUtilities::createUniqueID(); - } - if (rowStretchFactors.empty()) { - message.append(s_v1_rowStretchFactorsTagName - + " not found or invalid. "); - } - if (columnStretchFactors.empty()) { - message.append(s_v1_columnStretchFactorsTagName - + " not found or invalid. "); - } - if (numberOfRows <= 0) { - message.append(s_v1_rowStretchFactorsTagName - + " attribute " - + s_v1_rowStretchFactorsSelectedCountAttributeName - + " is missing or invalid." ); - } - if (numberOfRows <= 0) { - message.append(s_v1_columnStretchFactorsTagName - + " attribute " - + s_v1_columnStretchFactorsSelectedCountAttributeName - + " is missing or invalid." ); - } - - if ( ! invalidElements.empty()) { - /* - * If invalid elements were encountered, don't throw - */ - AString msg("Invalid element(s) ignored: "); - for (const auto s : invalidElements) { - msg.append(s + " "); - } - CaretLogWarning(msg); - } - - if (message.isEmpty()) { - m_name = name; - m_uniqueIdentifier = uniqueID; - - m_rows.clear(); - m_columns.clear(); - - for (int32_t i = 0; i < numberOfRows; i++) { - TileTabsGridRowColumnElement element; - CaretAssertVectorIndex(rowStretchFactors, i); - element.setWeightStretch(rowStretchFactors[i]); - element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); - element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); - m_rows.push_back(element); - } - CaretAssert(numberOfRows == static_cast(m_rows.size())); - - for (int32_t i = 0; i < numberOfColumns; i++) { - TileTabsGridRowColumnElement element; - CaretAssertVectorIndex(columnStretchFactors, i); - element.setWeightStretch(columnStretchFactors[i]); - element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); - element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); - m_columns.push_back(element); - } - CaretAssert(numberOfColumns == static_cast(m_columns.size())); - } - else { - xml.raiseError(message); - } -} - -/** - * Decode Version Two of the tile tabs configuration from XML with stream reader. - * - * @param xml - * Stream XML parser. - */ -void -TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml) -{ - m_rows.clear(); - m_columns.clear(); - m_uniqueIdentifier.clear(); - - std::set invalidElements; - - AString name; - AString uniqueID; - - QString message; - - enum class ReadMode { - OTHER, - COLUMNS, - ROWS - }; - ReadMode readMode = ReadMode::OTHER; - - AString centeringCorrectionTextString; - - while ( ! xml.atEnd()) { - xml.readNext(); - - if (xml.isStartElement()) { - const QStringRef tagName(xml.name()); - - if (tagName == s_nameTagName) { - name = xml.readElementText(); - } - else if (tagName == s_uniqueIdentifierTagName) { - uniqueID = xml.readElementText(); - } - else if (tagName == s_v2_columnsTagName) { - readMode = ReadMode::COLUMNS; - } - else if (tagName == s_v2_rowsTagName) { - readMode = ReadMode::ROWS; - } - else if (tagName == s_v2_centeringCorrectionName) { - centeringCorrectionTextString = xml.readElementText(); - } - else if (tagName == s_v2_elementTagName) { - switch (readMode) { - case ReadMode::OTHER: - CaretAssert(0); - break; - case ReadMode::COLUMNS: - { - AString errorMessage; - TileTabsGridRowColumnElement e; - if (decodeRowColumnElement(xml, e, errorMessage)) { - m_columns.push_back(e); - } - else { - message.append(errorMessage); - } - } - break; - case ReadMode::ROWS: - { - AString errorMessage; - TileTabsGridRowColumnElement e; - if (decodeRowColumnElement(xml, e, errorMessage)) { - m_rows.push_back(e); - } - else { - message.append(errorMessage); - } - } - break; - } - } - else { - invalidElements.insert(tagName.toString()); - xml.skipCurrentElement(); - } - } - else if (xml.isEndElement()) { - const QStringRef tagName(xml.name()); - - if (tagName == s_v2_columnsTagName) { - readMode = ReadMode::OTHER; - } - else if (tagName == s_v2_rowsTagName) { - readMode = ReadMode::OTHER; - } - } - } - - static int32_t missingNameCounter = 1; - if (name.isEmpty()) { - name = ("Config_" - + AString::number(missingNameCounter)); - missingNameCounter++; - } - if (uniqueID.isEmpty()) { - uniqueID = SystemUtilities::createUniqueID(); - } - - if ( ! invalidElements.empty()) { - /* - * If invalid elements were encountered, don't throw - */ - AString msg("Invalid element(s) ignored: "); - for (const auto s : invalidElements) { - msg.append(s + " "); - } - CaretLogWarning(msg); - } - - if (message.isEmpty()) { - m_name = name; - m_uniqueIdentifier = uniqueID; - - /* - * Only set centering correction if it is found. This allows usage - * of the default value in the event this is not found in the XML. - */ - if ( ! centeringCorrectionTextString.isEmpty()) { - m_centeringCorrectionEnabled = centeringCorrectionTextString.toBool(); - } - - } - else { - xml.raiseError(message); - } -} - -/** - * Decode elements in a row or column. - * - * @param reader - * The XML stream reader. - * @param element - * row/column element that is read - * @param errorMessageOut - * Contains error information. - * @return - * True if read successfully, else false. - */ -bool -TileTabsConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, - TileTabsGridRowColumnElement& element, - AString& errorMessageOut) -{ - const QXmlStreamAttributes atts = reader.attributes(); - - errorMessageOut.clear(); - - if (atts.hasAttribute(s_v2_contentTypeAttributeName)) { - bool validFlag(false); - const AString s = atts.value(s_v2_contentTypeAttributeName).toString(); - element.setContentType(TileTabsGridRowColumnContentTypeEnum::fromName(s, &validFlag)); - if ( ! validFlag) { - errorMessageOut.append("Content type \"" + s + "\" is not valid. "); - } - } - else { - errorMessageOut.append("Content type is missing. "); - } - - if (atts.hasAttribute(s_v2_stretchTypeAttributeName)) { - bool validFlag(false); - const AString s = atts.value(s_v2_stretchTypeAttributeName).toString(); - element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::fromName(s, &validFlag)); - if ( ! validFlag) { - errorMessageOut.append("Stretch type \"" + s + "\" is not valid. "); - } - } - else { - errorMessageOut.append("Stretch type is missing. "); - } - - if (atts.hasAttribute(s_v2_percentStretchAttributeName)) { - const float f = atts.value(s_v2_percentStretchAttributeName).toFloat(); - if ((f >= 0.0) && (f < 100.0)) { - element.setPercentStretch(f); - } - else { - errorMessageOut.append("Stretch percentage=" + AString::number(f) + " is invalid."); - } - } - else { - errorMessageOut.append("Stretch percentage is missing. "); - } - - if (atts.hasAttribute(s_v2_weightStretchAttributeName)) { - const float f = atts.value(s_v2_weightStretchAttributeName).toFloat(); - if ((f >= 0.0) && (f < 100.0)) { - element.setWeightStretch(f); - } - else { - errorMessageOut.append("Stretch weight=" + AString::number(f) + " is invalid."); - } - } - else { - errorMessageOut.append("Stretch weight is missing. "); - } - - if (errorMessageOut.isEmpty()) { - return true; - } - return false; -} - -/** - * Decode the tile tabs configuration from XML using DOM - * - * @param xmlString - * String containing XML. - * @param errorMessageOut - * Contains error information if decoding fails. - * @return - * True if configuration was successfully read from the XML, else false. - */ -bool -TileTabsConfiguration::decodeFromXML(const AString& xmlString, - AString& errorMessageOut) -{ - errorMessageOut.clear(); - - return decodeFromXMLWithStreamReader(xmlString, - errorMessageOut); -} - -/** - * @return String version of an instance. - */ -AString -TileTabsConfiguration::toString() const -{ - AString s("Name: %1, Unique ID: %2\n"); - s = s.arg(m_name).arg(m_uniqueIdentifier); - - int32_t indx(0); - for (const auto item : m_columns) { - s.append(" Column " + AString::number(indx) + ": " + item.toString() + "\n"); - indx++; - } - indx = 0; - for (const auto item : m_rows) { - s.append(" Row " + AString::number(indx) + ": " + item.toString() + "\n"); - indx++; - } - - return s; -} - -/** - * Compare two tile tabs configurations by name. - * - * @param ttc1 - * First tile tab configuration. - * @param ttc2 - * Second tile tab configuration. - * @return - * True if ttc1 is "less than" when compared by name, else false. - */ -bool -TileTabsConfiguration::lessThanComparisonByName(const TileTabsConfiguration* ttc1, - const TileTabsConfiguration* ttc2) -{ - if (ttc1->getName() < ttc2->getName()) { - return true; - } - return false; -} - diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsConfiguration.h connectome-workbench-1.5.0/src/Common/TileTabsConfiguration.h --- connectome-workbench-1.4.2/src/Common/TileTabsConfiguration.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsConfiguration.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,192 +0,0 @@ -#ifndef __TILE_TABS_CONFIGURATION_H__ -#define __TILE_TABS_CONFIGURATION_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include "CaretException.h" -#include "CaretObject.h" -#include "TileTabsGridModeEnum.h" -#include "TileTabsGridRowColumnElement.h" - -class QXmlStreamReader; -class QXmlStreamWriter; - -namespace caret { - - class TileTabsConfiguration : public CaretObject { - - public: - TileTabsConfiguration(); - - virtual ~TileTabsConfiguration(); - - TileTabsConfiguration(const TileTabsConfiguration& obj); - - TileTabsConfiguration& operator=(const TileTabsConfiguration& obj); - - void copy(const TileTabsConfiguration& rhs); - - TileTabsConfiguration* newCopyWithNewUniqueIdentifier() const; - - bool getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, - const int32_t windowHeight, - const int32_t numberOfModelsToDraw, - const TileTabsGridModeEnum::Enum configurationMode, - std::vector& rowHeightsOut, - std::vector& columnWidthsOut); - - AString getName() const; - - void setName(const AString& name); - - AString getUniqueIdentifier() const; - - int32_t getNumberOfRows() const; - - void setNumberOfRows(const int32_t numberOfRows); - - int32_t getNumberOfColumns() const; - - void setNumberOfColumns(const int32_t numberOfColumns); - - TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex); - - const TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex) const; - - TileTabsGridRowColumnElement* getRow(const int32_t rowIndex); - - const TileTabsGridRowColumnElement* getRow(const int32_t rowIndex) const; - - AString encodeInXML() const; - - AString encodeVersionInXML(const int32_t versionNumber) const; - - bool decodeFromXML(const AString& xmlString, - AString& errorMessageOut); - - void updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs); - - bool isCenteringCorrectionEnabled() const; - - void setCenteringCorrectionEnabled(const bool status); - - AString toString() const override; - - static bool lessThanComparisonByName(const TileTabsConfiguration* ttc1, - const TileTabsConfiguration* ttc2); - - static void getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, - int32_t& numberOfRowsOut, - int32_t& numberOfColumnsOut); - // ADD_NEW_METHODS_HERE - - - private: - void copyHelperTileTabsConfiguration(const TileTabsConfiguration& obj); - - bool decodeFromXMLWithStreamReader(const AString& xmlString, - AString& errorMessageOut); - - void decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml); - - void decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml); - - AString encodeInXMLWithStreamWriterVersionOne() const; - - AString encodeInXMLWithStreamWriterVersionTwo() const; - - void encodeRowColumnElement(QXmlStreamWriter& writer, - const AString tagName, - const std::vector& elements) const; - - bool decodeRowColumnElement(QXmlStreamReader& reader, - TileTabsGridRowColumnElement& element, - AString& errorMessageOut); - - void initialize(); - - // ADD_NEW_MEMBERS_HERE - - AString m_name; - - /** Unique identifier does not get copied */ - AString m_uniqueIdentifier; - - std::vector m_columns; - - std::vector m_rows; - - bool m_centeringCorrectionEnabled = false; - - static const AString s_nameTagName; - static const AString s_uniqueIdentifierTagName; - - static const AString s_v1_rootTagName; - static const AString s_v1_versionTagName; - static const AString s_v1_versionNumberAttributeName; - static const AString s_v1_columnStretchFactorsTagName; - static const AString s_v1_columnStretchFactorsSelectedCountAttributeName; - static const AString s_v1_columnStretchFactorsTotalCountAttributeName; - static const AString s_v1_rowStretchFactorsTagName; - static const AString s_v1_rowStretchFactorsSelectedCountAttributeName; - static const AString s_v1_rowStretchFactorsTotalCountAttributeName; - - static const AString s_v2_rootTagName; - static const AString s_v2_versionAttributeName; - static const AString s_v2_columnsTagName; - static const AString s_v2_contentTypeAttributeName; - static const AString s_v2_elementTagName; - static const AString s_v2_percentStretchAttributeName; - static const AString s_v2_rowsTagName; - static const AString s_v2_stretchTypeAttributeName; - static const AString s_v2_weightStretchAttributeName; - static const AString s_v2_centeringCorrectionName; - - }; - -#ifdef __TILE_TABS_CONFIGURATION_DECLARE__ - const AString TileTabsConfiguration::s_nameTagName = "Name"; - const AString TileTabsConfiguration::s_uniqueIdentifierTagName = "UniqueIdentifier"; - - const AString TileTabsConfiguration::s_v1_rootTagName = "TileTabsConfiguration"; - const AString TileTabsConfiguration::s_v1_versionTagName = "Version"; - const AString TileTabsConfiguration::s_v1_versionNumberAttributeName = "Number"; - const AString TileTabsConfiguration::s_v1_columnStretchFactorsTagName = "ColumnStretchFactors"; - const AString TileTabsConfiguration::s_v1_columnStretchFactorsSelectedCountAttributeName = "SelectedRowCount"; - const AString TileTabsConfiguration::s_v1_columnStretchFactorsTotalCountAttributeName = "TotalRowCount"; - const AString TileTabsConfiguration::s_v1_rowStretchFactorsTagName = "RowStretchFactors"; - const AString TileTabsConfiguration::s_v1_rowStretchFactorsSelectedCountAttributeName = "SelectedColumnCount"; - const AString TileTabsConfiguration::s_v1_rowStretchFactorsTotalCountAttributeName = "TotalColumnCount"; - - const AString TileTabsConfiguration::s_v2_rootTagName = "TileTabsConfigurationTwo"; - const AString TileTabsConfiguration::s_v2_versionAttributeName = "Version"; - const AString TileTabsConfiguration::s_v2_columnsTagName = "Columns"; - const AString TileTabsConfiguration::s_v2_contentTypeAttributeName = "ContentType"; - const AString TileTabsConfiguration::s_v2_elementTagName = "Element"; - const AString TileTabsConfiguration::s_v2_percentStretchAttributeName = "PercentStretch"; - const AString TileTabsConfiguration::s_v2_rowsTagName = "Rows"; - const AString TileTabsConfiguration::s_v2_stretchTypeAttributeName = "StretchType"; - const AString TileTabsConfiguration::s_v2_weightStretchAttributeName = "WeightStretch"; - const AString TileTabsConfiguration::s_v2_centeringCorrectionName = "CenteringCorrection"; -#endif // __TILE_TABS_CONFIGURATION_DECLARE__ - -} // namespace -#endif //__TILE_TABS_CONFIGURATION_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx connectome-workbench-1.5.0/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,373 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2019 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#define __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ -#include "TileTabsConfigurationLayoutTypeEnum.h" -#undef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ - -#include "CaretAssert.h" - -using namespace caret; - - -/** - * \class caret::TileTabsConfigurationLayoutTypeEnum - * \brief Types of tile tabs layouts - * - * Using this enumerated type in the GUI with an EnumComboBoxTemplate - * - * Header File (.h) - * Forward declare the data type: - * class EnumComboBoxTemplate; - * - * Declare the member: - * EnumComboBoxTemplate* m_tileTabsConfigurationLayoutTypeEnumComboBox; - * - * Declare a slot that is called when user changes selection - * private slots: - * void tileTabsConfigurationLayoutTypeEnumComboBoxItemActivated(); - * - * Implementation File (.cxx) - * Include the header files - * #include "EnumComboBoxTemplate.h" - * #include "TileTabsConfigurationLayoutTypeEnum.h" - * - * Instatiate: - * m_tileTabsConfigurationLayoutTypeEnumComboBox = new EnumComboBoxTemplate(this); - * m_tileTabsConfigurationLayoutTypeEnumComboBox->setup(); - * - * Get notified when the user changes the selection: - * QObject::connect(m_tileTabsConfigurationLayoutTypeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(tileTabsConfigurationLayoutTypeEnumComboBoxItemActivated())); - * - * Update the selection: - * m_tileTabsConfigurationLayoutTypeEnumComboBox->setSelectedItem(NEW_VALUE); - * - * Read the selection: - * const TileTabsConfigurationLayoutTypeEnum::Enum VARIABLE = m_tileTabsConfigurationLayoutTypeEnumComboBox->getSelectedItem(); - * - */ - -/** - * Constructor. - * - * @param enumValue - * An enumerated value. - * @param name - * Name of enumerated value. - * - * @param guiName - * User-friendly name for use in user-interface. - */ -TileTabsConfigurationLayoutTypeEnum::TileTabsConfigurationLayoutTypeEnum(const Enum enumValue, - const AString& name, - const AString& guiName) -{ - this->enumValue = enumValue; - this->integerCode = integerCodeCounter++; - this->name = name; - this->guiName = guiName; -} - -/** - * Destructor. - */ -TileTabsConfigurationLayoutTypeEnum::~TileTabsConfigurationLayoutTypeEnum() -{ -} - -/** - * Initialize the enumerated metadata. - */ -void -TileTabsConfigurationLayoutTypeEnum::initialize() -{ - if (initializedFlag) { - return; - } - initializedFlag = true; - - enumData.push_back(TileTabsConfigurationLayoutTypeEnum(GRID, - "GRID", - "Grid")); - - enumData.push_back(TileTabsConfigurationLayoutTypeEnum(MANUAL, - "MANUAL", - "Manual")); - -} - -/** - * Find the data for and enumerated value. - * @param enumValue - * The enumerated value. - * @return Pointer to data for this enumerated type - * or NULL if no data for type or if type is invalid. - */ -const TileTabsConfigurationLayoutTypeEnum* -TileTabsConfigurationLayoutTypeEnum::findData(const Enum enumValue) -{ - if (initializedFlag == false) initialize(); - - size_t num = enumData.size(); - for (size_t i = 0; i < num; i++) { - const TileTabsConfigurationLayoutTypeEnum* d = &enumData[i]; - if (d->enumValue == enumValue) { - return d; - } - } - - return NULL; -} - -/** - * Get a string representation of the enumerated type. - * @param enumValue - * Enumerated value. - * @return - * String representing enumerated value. - */ -AString -TileTabsConfigurationLayoutTypeEnum::toName(Enum enumValue) { - if (initializedFlag == false) initialize(); - - const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); - return enumInstance->name; -} - -/** - * Get an enumerated value corresponding to its name. - * @param name - * Name of enumerated value. - * @param isValidOut - * If not NULL, it is set indicating that a - * enum value exists for the input name. - * @return - * Enumerated value. - */ -TileTabsConfigurationLayoutTypeEnum::Enum -TileTabsConfigurationLayoutTypeEnum::fromName(const AString& name, bool* isValidOut) -{ - if (initializedFlag == false) initialize(); - - bool validFlag = false; - Enum enumValue = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - const TileTabsConfigurationLayoutTypeEnum& d = *iter; - if (d.name == name) { - enumValue = d.enumValue; - validFlag = true; - break; - } - } - - if (isValidOut != 0) { - *isValidOut = validFlag; - } - else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); - } - return enumValue; -} - -/** - * Get a GUI string representation of the enumerated type. - * @param enumValue - * Enumerated value. - * @return - * String representing enumerated value. - */ -AString -TileTabsConfigurationLayoutTypeEnum::toGuiName(Enum enumValue) { - if (initializedFlag == false) initialize(); - - const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); - return enumInstance->guiName; -} - -/** - * Get an enumerated value corresponding to its GUI name. - * @param s - * Name of enumerated value. - * @param isValidOut - * If not NULL, it is set indicating that a - * enum value exists for the input name. - * @return - * Enumerated value. - */ -TileTabsConfigurationLayoutTypeEnum::Enum -TileTabsConfigurationLayoutTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) -{ - if (initializedFlag == false) initialize(); - - bool validFlag = false; - Enum enumValue = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - const TileTabsConfigurationLayoutTypeEnum& d = *iter; - if (d.guiName == guiName) { - enumValue = d.enumValue; - validFlag = true; - break; - } - } - - if (isValidOut != 0) { - *isValidOut = validFlag; - } - else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); - } - return enumValue; -} - -/** - * Get the integer code for a data type. - * - * @return - * Integer code for data type. - */ -int32_t -TileTabsConfigurationLayoutTypeEnum::toIntegerCode(Enum enumValue) -{ - if (initializedFlag == false) initialize(); - const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); - return enumInstance->integerCode; -} - -/** - * Find the data type corresponding to an integer code. - * - * @param integerCode - * Integer code for enum. - * @param isValidOut - * If not NULL, on exit isValidOut will indicate if - * integer code is valid. - * @return - * Enum for integer code. - */ -TileTabsConfigurationLayoutTypeEnum::Enum -TileTabsConfigurationLayoutTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) -{ - if (initializedFlag == false) initialize(); - - bool validFlag = false; - Enum enumValue = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - const TileTabsConfigurationLayoutTypeEnum& enumInstance = *iter; - if (enumInstance.integerCode == integerCode) { - enumValue = enumInstance.enumValue; - validFlag = true; - break; - } - } - - if (isValidOut != 0) { - *isValidOut = validFlag; - } - else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); - } - return enumValue; -} - -/** - * Get all of the enumerated type values. The values can be used - * as parameters to toXXX() methods to get associated metadata. - * - * @param allEnums - * A vector that is OUTPUT containing all of the enumerated values. - */ -void -TileTabsConfigurationLayoutTypeEnum::getAllEnums(std::vector& allEnums) -{ - if (initializedFlag == false) initialize(); - - allEnums.clear(); - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - allEnums.push_back(iter->enumValue); - } -} - -/** - * Get all of the names of the enumerated type values. - * - * @param allNames - * A vector that is OUTPUT containing all of the names of the enumerated values. - * @param isSorted - * If true, the names are sorted in alphabetical order. - */ -void -TileTabsConfigurationLayoutTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) -{ - if (initializedFlag == false) initialize(); - - allNames.clear(); - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - allNames.push_back(TileTabsConfigurationLayoutTypeEnum::toName(iter->enumValue)); - } - - if (isSorted) { - std::sort(allNames.begin(), allNames.end()); - } -} - -/** - * Get all of the GUI names of the enumerated type values. - * - * @param allNames - * A vector that is OUTPUT containing all of the GUI names of the enumerated values. - * @param isSorted - * If true, the names are sorted in alphabetical order. - */ -void -TileTabsConfigurationLayoutTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) -{ - if (initializedFlag == false) initialize(); - - allGuiNames.clear(); - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - allGuiNames.push_back(TileTabsConfigurationLayoutTypeEnum::toGuiName(iter->enumValue)); - } - - if (isSorted) { - std::sort(allGuiNames.begin(), allGuiNames.end()); - } -} - diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsConfigurationLayoutTypeEnum.h connectome-workbench-1.5.0/src/Common/TileTabsConfigurationLayoutTypeEnum.h --- connectome-workbench-1.4.2/src/Common/TileTabsConfigurationLayoutTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsConfigurationLayoutTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -#ifndef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ -#define __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2019 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include -#include -#include "AString.h" - -namespace caret { - -class TileTabsConfigurationLayoutTypeEnum { - -public: - /** - * Enumerated values. - */ - enum Enum { - /** */ - GRID, - /** */ - MANUAL - }; - - - ~TileTabsConfigurationLayoutTypeEnum(); - - static AString toName(Enum enumValue); - - static Enum fromName(const AString& name, bool* isValidOut); - - static AString toGuiName(Enum enumValue); - - static Enum fromGuiName(const AString& guiName, bool* isValidOut); - - static int32_t toIntegerCode(Enum enumValue); - - static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); - - static void getAllEnums(std::vector& allEnums); - - static void getAllNames(std::vector& allNames, const bool isSorted); - - static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); - -private: - TileTabsConfigurationLayoutTypeEnum(const Enum enumValue, - const AString& name, - const AString& guiName); - - static const TileTabsConfigurationLayoutTypeEnum* findData(const Enum enumValue); - - /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; - - /** Initialize instances that contain the enum values and metadata */ - static void initialize(); - - /** Indicates instance of enum values and metadata have been initialized */ - static bool initializedFlag; - - /** Auto generated integer codes */ - static int32_t integerCodeCounter; - - /** The enumerated type value for an instance */ - Enum enumValue; - - /** The integer code associated with an enumerated value */ - int32_t integerCode; - - /** The name, a text string that is identical to the enumerated value */ - AString name; - - /** A user-friendly name that is displayed in the GUI */ - AString guiName; -}; - -#ifdef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ -std::vector TileTabsConfigurationLayoutTypeEnum::enumData; -bool TileTabsConfigurationLayoutTypeEnum::initializedFlag = false; -int32_t TileTabsConfigurationLayoutTypeEnum::integerCodeCounter = 0; -#endif // __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ - -} // namespace -#endif //__TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsGridLayoutConfiguration.cxx connectome-workbench-1.5.0/src/Common/TileTabsGridLayoutConfiguration.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsGridLayoutConfiguration.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsGridLayoutConfiguration.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,1092 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#include - -#define __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ -#include "TileTabsGridLayoutConfiguration.h" -#undef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ - -#include -#include - -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "SystemUtilities.h" - -using namespace caret; - - - -/** - * \class caret::TileTabsGridLayoutConfiguration - * \brief Defines a tile tabs configuration - * \ingroup Common - */ - -/** - * Constructor that creates a 2 by 2 configuration. - */ -TileTabsGridLayoutConfiguration::TileTabsGridLayoutConfiguration() -: TileTabsBaseConfiguration(TileTabsConfigurationLayoutTypeEnum::GRID) -{ - initialize(); -} - -/** - * Destructor. - */ -TileTabsGridLayoutConfiguration::~TileTabsGridLayoutConfiguration() -{ -} - -/** - * Copy constructor. - * - * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() - * - * @param obj - * Object that is copied. - */ -TileTabsGridLayoutConfiguration::TileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj) -: TileTabsBaseConfiguration(obj) -{ - initialize(); - this->copyHelperTileTabsGridLayoutConfiguration(obj); -} - -/** - * Assignment operator. - * - * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() - * - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -TileTabsGridLayoutConfiguration& -TileTabsGridLayoutConfiguration::operator=(const TileTabsGridLayoutConfiguration& obj) -{ - if (this != &obj) { - TileTabsBaseConfiguration::operator=(obj); - this->copyHelperTileTabsGridLayoutConfiguration(obj); - } - return *this; -} - -/** - * Copy this instance and give it a new unique identifier. - * Note that copy constructor does not create a new unique identifier. - * - * @return The new Copy. - */ -TileTabsGridLayoutConfiguration* -TileTabsGridLayoutConfiguration::newCopyWithNewUniqueIdentifier() const -{ - TileTabsGridLayoutConfiguration* newCopy = new TileTabsGridLayoutConfiguration(*this); - CaretAssert(newCopy); - return newCopy; -} - - -/** - * Initialize an instance of a tile tabs configuration. - */ -void -TileTabsGridLayoutConfiguration::initialize() -{ - setNumberOfRows(2); - setNumberOfColumns(2); - - m_centeringCorrectionEnabled = false; -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -TileTabsGridLayoutConfiguration::copyHelperTileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj) -{ - if (this == &obj) { - return; - } - - copyHelperTileTabsBaseConfiguration(obj); - m_columns = obj.m_columns; - m_rows = obj.m_rows; - m_centeringCorrectionEnabled = obj.m_centeringCorrectionEnabled; -} - -/** - * Copies the tile tabs configuration rows, columns, and - * stretch factors. Name is NOT copied. - */ -void -TileTabsGridLayoutConfiguration::copy(const TileTabsGridLayoutConfiguration& rhs) -{ - AString savedName = getName(); - copyHelperTileTabsGridLayoutConfiguration(rhs); - setName(savedName); -} - -/** - * Get infoformation for the given elemnent in the columns. - * - * @param Information for element. - */ -TileTabsGridRowColumnElement* -TileTabsGridLayoutConfiguration::getColumn(const int32_t columnIndex) -{ - CaretAssertVectorIndex(m_columns, columnIndex); - return &m_columns[columnIndex]; -} - -/** - * Get infoformation for the given elemnent in the columns. - * - * @param Information for element. - */ -const TileTabsGridRowColumnElement* -TileTabsGridLayoutConfiguration::getColumn(const int32_t columnIndex) const -{ - CaretAssertVectorIndex(m_columns, columnIndex); - return &m_columns[columnIndex]; -} - -/** - * Get infoformation for the given elemnent in the rows. - * - * @param Information for element. - */ -TileTabsGridRowColumnElement* -TileTabsGridLayoutConfiguration::getRow(const int32_t rowIndex) -{ - CaretAssertVectorIndex(m_rows, rowIndex); - return &m_rows[rowIndex]; -} - -/** - * Get infoformation for the given elemnent in the rows. - * - * @param Information for element. - */ -const TileTabsGridRowColumnElement* -TileTabsGridLayoutConfiguration::getRow(const int32_t rowIndex) const -{ - CaretAssertVectorIndex(m_rows, rowIndex); - return &m_rows[rowIndex]; -} - -/** - * Get the row heights and column widths for this tile tabs configuration using the - * given window width and height. - * - * @param windowWidth - * Width of window. - * @param windowHeight - * Height of window. - * @param numberOfModelsToDraw - * Number of models to draw. - * @param configurationMode - * The tile tabs configuration mode - * @param rowHeightsOut - * Output containing height of each row. - * @param columnWidthsOut - * Output containing width of each column. - * @return - * True if the ouput is valid, else false. - */ -bool -TileTabsGridLayoutConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, - const int32_t windowHeight, - const int32_t numberOfModelsToDraw, - const TileTabsGridModeEnum::Enum configurationMode, - std::vector& rowHeightsOut, - std::vector& columnWidthsOut) -{ - /* - * NOTE: When computing widths and heights, do not round. - * Rounding may cause the bottom most row or column to extend - * outside the graphics region. Shrinking the last row or - * column is not desired since it might cause the last model - * to be drawn slightly smaller than the others. - */ - - int32_t numRows = 0; - int32_t numCols = 0; - - rowHeightsOut.clear(); - columnWidthsOut.clear(); - - switch (configurationMode) { - case TileTabsGridModeEnum::AUTOMATIC: - { - /* - * Update number of rows/columns in the default configuration - * so that if a scene is saved, the correct number of rows - * and columns are saved to the scene. - */ - updateAutomaticConfigurationRowsAndColumns(numberOfModelsToDraw); - numRows = getNumberOfRows(); - numCols = getNumberOfColumns(); - - for (int32_t i = 0; i < numRows; i++) { - rowHeightsOut.push_back(windowHeight / numRows); - } - for (int32_t i = 0; i < numCols; i++) { - columnWidthsOut.push_back(windowWidth / numCols); - } - } break; - case TileTabsGridModeEnum::CUSTOM: - { - /* - * Rows/columns from user configuration - */ - numRows = getNumberOfRows(); - numCols = getNumberOfColumns(); - - /* - * Determine height of each row - */ - float rowPercentTotal = 0.0; - float rowStretchTotal = 0.0; - for (int32_t i = 0; i < numRows; i++) { - const TileTabsGridRowColumnElement* e = getRow(i); - switch (e->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - rowPercentTotal += (e->getPercentStretch() / 100.0); - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - rowStretchTotal += e->getWeightStretch(); - break; - } - } - - float windowWeightHeight = windowHeight; - if (rowPercentTotal > 0.0) { - if (rowPercentTotal >= 1.0) { - windowWeightHeight = 0.0; - } - else { - windowWeightHeight = (1.0 - rowPercentTotal) * windowHeight; - } - } - for (int32_t i = 0; i < numRows; i++) { - int32_t h = 0; - const TileTabsGridRowColumnElement* e = getRow(i); - switch (e->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - h = (e->getPercentStretch() / 100.0) * windowHeight; - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - if (rowStretchTotal > 0.0) { - h = static_cast((e->getWeightStretch() / rowStretchTotal) - * windowWeightHeight); - } - break; - } - - rowHeightsOut.push_back(h); - } - - /* - * Determine width of each column - */ - float columnPercentTotal = 0.0; - float columnStretchTotal = 0.0; - for (int32_t i = 0; i < numCols; i++) { - const TileTabsGridRowColumnElement* e = getColumn(i); - switch (e->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - columnPercentTotal += (e->getPercentStretch() / 100.0); - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - columnStretchTotal += e->getWeightStretch(); - break; - } - } - - float windowWeightWidth = windowWidth; - if (columnPercentTotal > 0.0) { - if (columnPercentTotal >= 1.0) { - windowWeightWidth = 0.0; - } - else { - windowWeightWidth = (1.0 - columnPercentTotal) * windowWidth; - } - } - - for (int32_t i = 0; i < numCols; i++) { - int32_t w = 0; - const TileTabsGridRowColumnElement* e = getColumn(i); - switch (e->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - w = (e->getPercentStretch() / 100.0) * windowWidth; - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - if (columnStretchTotal > 0.0) { - w = static_cast((e->getWeightStretch() / columnStretchTotal) - * windowWeightWidth); - } - break; - } - columnWidthsOut.push_back(w); - } - } break; - } - - if ((numRows == static_cast(rowHeightsOut.size())) - && (numCols == static_cast(columnWidthsOut.size()))) { -// /* -// * Verify all rows fit within the window -// */ -// int32_t rowHeightsSum = 0; -// for (int32_t i = 0; i < numRows; i++) { -// rowHeightsSum += rowHeightsOut[i]; -// } -// if (rowHeightsSum > windowHeight) { -// CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); -//// rowHeightsOut[numRows - 1] -= (rowHeightsSum - windowHeight); -// } -// -// /* -// * Adjust width of last column so that it does not extend beyond viewport -// */ -// int32_t columnWidthsSum = 0; -// for (int32_t i = 0; i < numCols; i++) { -// columnWidthsSum += columnWidthsOut[i]; -// } -// if (columnWidthsSum > windowWidth) { -// CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); -//// columnWidthsOut[numCols - 1] = columnWidthsSum - windowWidth; -// } -// -// CaretLogFiner("Tile Tabs Row Heights: " -// + AString::fromNumbers(rowHeightsOut, ", ")); -// CaretLogFiner("Tile Tabs Column Widths: " -// + AString::fromNumbers(columnWidthsOut, ", ")); - return true; - } - - const QString msg("Row and heights failed rows=" - + AString::number(numRows) - + " rowHeights=" - + AString::number(rowHeightsOut.size()) - + " cols=" - + AString::number(numCols) - + " rowHeights=" - + AString::number(columnWidthsOut.size())); - CaretAssertMessage(0, msg); - CaretLogSevere(msg); - return false; -} - -/** - * @return Number of rows. - */ -int32_t -TileTabsGridLayoutConfiguration::getNumberOfRows() const -{ - return m_rows.size(); -} - -/** - * Set number of rows. - * - * @param numberOfRows - * New number of rows. - */ -void -TileTabsGridLayoutConfiguration::setNumberOfRows(const int32_t numberOfRows) -{ - m_rows.resize(numberOfRows); -} - -/** - * @return Number of columns. - */ -int32_t -TileTabsGridLayoutConfiguration::getNumberOfColumns() const -{ - return m_columns.size(); -} - -/** - * Set number of rows. - * - * @param numberOfColumns - * New number of rows. - */ -void -TileTabsGridLayoutConfiguration::setNumberOfColumns(const int32_t numberOfColumns) -{ - m_columns.resize(numberOfColumns); -} - -/** - * Get the number of rows and columns for an automatic layout with the - * given number of tabs. - * @param numberOfTabs - * Number of tabs. - * @param numberOfRowsOut - * Output with number of rows. - * @param numberOfColumnsOut - * Output with number of columns. - */ -void -TileTabsGridLayoutConfiguration::getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, - int32_t& numberOfRowsOut, - int32_t& numberOfColumnsOut) -{ - CaretAssert(numberOfTabs >= 0); - - numberOfRowsOut = (int)std::sqrt((double)numberOfTabs); - numberOfColumnsOut = numberOfRowsOut; - int32_t row2 = numberOfRowsOut * numberOfRowsOut; - if (row2 < numberOfTabs) { - numberOfColumnsOut++; - } - if ((numberOfRowsOut * numberOfColumnsOut) < numberOfTabs) { - numberOfRowsOut++; - } -} - -/** - * @return True if the centering correction is enabled. - */ -bool -TileTabsGridLayoutConfiguration::isCenteringCorrectionEnabled() const -{ - return m_centeringCorrectionEnabled; -} - -/** - * Set the enabled status of the centering correction - * - * @param status - * New status for enabling the centering correction - */ -void -TileTabsGridLayoutConfiguration::setCenteringCorrectionEnabled(const bool status) -{ - m_centeringCorrectionEnabled = status; -} - - -/** - * Updates the number of rows and columns for the automatic configuration - * based upon the number of tabs. - * - * Since screen width typically exceeds height, ensure the number of - * columns is always greater than the number of rows. - */ -void -TileTabsGridLayoutConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs) -{ - int32_t numRows(0), numCols(0); - getRowsAndColumnsForNumberOfTabs(numberOfTabs, - numRows, - numCols); - - setNumberOfRows(numRows); - setNumberOfColumns(numCols); -} - -/** - * Encode the configuration in XML. - * - * @param xmlTextOut - * Contains XML representation of configuration. - */ -void -TileTabsGridLayoutConfiguration::encodeInXML(AString& xmlTextOut) const -{ - xmlTextOut = encodeVersionInXML(2); -} - -/** - * @return Encoded tile tabs configuration in XML - * using the give XML version of TileTabsGridLayoutConfiguration. - */ -AString -TileTabsGridLayoutConfiguration::encodeVersionInXML(const int32_t versionNumber) const -{ - AString s; - - switch (versionNumber) { - case 1: - s = encodeInXMLWithStreamWriterVersionOne(); - break; - case 2: - s = encodeInXMLWithStreamWriterVersionTwo(); - break; - default: - CaretAssertMessage(0, "Requested invalid version=" + AString::number(versionNumber)); - break; - } - - return s; -} - - -/** - * @return Encoded tile tabs configuration in XML with Stream Writer - */ -AString -TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionOne() const -{ - AString xmlString; - QXmlStreamWriter writer(&xmlString); - writer.setAutoFormatting(true); - - writer.writeStartElement(s_v1_rootTagName); - - writer.writeStartElement(s_v1_versionTagName); - writer.writeAttribute(s_v1_versionNumberAttributeName, "1"); - writer.writeEndElement(); - - writer.writeTextElement(s_nameTagName, getName()); - writer.writeTextElement(s_uniqueIdentifierTagName, getUniqueIdentifier()); - - const int32_t numberOfRows = getNumberOfRows(); - writer.writeStartElement(s_v1_rowStretchFactorsTagName); - writer.writeAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName, AString::number(numberOfRows)); - writer.writeAttribute(s_v1_rowStretchFactorsTotalCountAttributeName, AString::number(numberOfRows)); - std::vector rowStretchFactors; - for (const auto e : m_rows) { - rowStretchFactors.push_back(e.getWeightStretch()); - } - writer.writeCharacters(AString::fromNumbers(rowStretchFactors, " ")); - writer.writeEndElement(); - - const int32_t numberOfColumns = getNumberOfColumns(); - writer.writeStartElement(s_v1_columnStretchFactorsTagName); - writer.writeAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName, AString::number(numberOfColumns)); - writer.writeAttribute(s_v1_columnStretchFactorsTotalCountAttributeName, AString::number(numberOfColumns)); - std::vector columnStretchFactors; - for (const auto e : m_columns) { - columnStretchFactors.push_back(e.getWeightStretch()); - } - writer.writeCharacters(AString::fromNumbers(columnStretchFactors, " ")); - writer.writeEndElement(); - - writer.writeEndElement(); - - return xmlString; -} - -/** - * @return Encoded tile tabs configuration in XML with Stream Writer - */ -AString -TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionTwo() const -{ - AString xmlString; - QXmlStreamWriter writer(&xmlString); - writer.setAutoFormatting(true); - - writer.writeStartElement(s_v2_rootTagName); - writer.writeAttribute(s_v2_versionAttributeName, "2"); - - writer.writeTextElement(s_nameTagName, getName()); - writer.writeTextElement(s_uniqueIdentifierTagName, getUniqueIdentifier()); - writer.writeTextElement(s_v2_centeringCorrectionName, AString::fromBool(m_centeringCorrectionEnabled)); - - encodeRowColumnElement(writer, s_v2_columnsTagName, m_columns); - encodeRowColumnElement(writer, s_v2_rowsTagName, m_rows); - - writer.writeEndElement(); - - return xmlString; -} - -/** - * Encode a vector of elements into xml. - * - * @param writer - * The XML stream writer. - * @param tagName - * Tag name for enclosing the elements. - * @param elements - * Vector of elements added to XML. - */ -void -TileTabsGridLayoutConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer, - const AString tagName, - const std::vector& elements) const -{ - writer.writeStartElement(tagName); - - for (const auto e : elements) { - writer.writeStartElement(s_v2_elementTagName); - writer.writeAttribute(s_v2_contentTypeAttributeName, TileTabsGridRowColumnContentTypeEnum::toName(e.getContentType())); - writer.writeAttribute(s_v2_stretchTypeAttributeName, TileTabsGridRowColumnStretchTypeEnum::toName(e.getStretchType())); - writer.writeAttribute(s_v2_percentStretchAttributeName, AString::number(e.getPercentStretch(), 'f', 2)); - writer.writeAttribute(s_v2_weightStretchAttributeName, AString::number(e.getWeightStretch(), 'f', 2)); - writer.writeEndElement(); - } - - writer.writeEndElement(); -} - -/** - * Decode the configuration using the given XML stream reader and root element - * If there is an error, xml.raiseError() should be used to specify the error - * and caller of this method can test for the error using xml.isError(). - * - * @param xml - * The XML stream reader. - * @param rootElement - * The root element. - */ -void -TileTabsGridLayoutConfiguration::decodeFromXML(QXmlStreamReader& xml, - const QString& rootElementText) -{ - CaretAssert( ! rootElementText.isEmpty()); - - m_centeringCorrectionEnabled = false; - - if (rootElementText == s_v1_rootTagName) { - decodeFromXMLWithStreamReaderVersionOne(xml); - } - else if (rootElementText == s_v2_rootTagName) { - /* - * Version 2 uses a different root tag than version 1. The reason is that - * the older code for decoding from XML will throw an exception if it - * encounters invalid elements or the version number is invalid. The problem - * is that the exception is not caught and wb_view will terminate. - */ - QString versionNumberText("Unknown"); - const QXmlStreamAttributes atts = xml.attributes(); - if (atts.hasAttribute(s_v2_versionAttributeName)) { - versionNumberText = atts.value(s_v2_versionAttributeName).toString(); - } - - if (versionNumberText == "2") { - decodeFromXMLWithStreamReaderVersionTwo(xml); - } - else { - xml.raiseError("TileTabsGridLayoutConfiguration invalid version=" - + versionNumberText); - } - } - else { - xml.raiseError("TileTabsGridLayoutConfiguration first element is " - + xml.name().toString() - + " but should be " - + s_v1_rootTagName - + " or " - + s_v2_rootTagName); - } - -// const bool debugFlag(false); -// if (debugFlag) { -// AString xmlText = encodeInXMLWithStreamWriterVersionTwo(); -// std::cout << std::endl << "NEW: " << xmlText << std::endl << std::endl; -// AString em; -// TileTabsGridLayoutConfiguration temp; -// QXmlStreamReader tempReader(xmlText); -// tempReader.readNextStartElement(); -// temp.decodeFromXMLWithStreamReaderVersionTwo(tempReader); -// if (tempReader.hasError()) { -// std::cout << "Decode error: " << tempReader.errorString() << std::endl; -// } -// else { -// std::cout << "Decoded: " << temp.toString() << std::endl; -// } -// -// std::cout << std::endl; -// } -// return true; -} - -/** - * Decode Version One of the tile tabs configuration from XML with stream reader. - * - * @param xml - * Stream XML parser. - */ -void -TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml) -{ - std::set invalidElements; - - AString name; - AString uniqueID; - std::vector rowStretchFactors; - std::vector columnStretchFactors; - int32_t numberOfRows(0); - int32_t numberOfColumns(0); - - QString message; - - while ( ! xml.atEnd()) { - xml.readNext(); - - if (xml.isStartElement()) { - const QStringRef tagName(xml.name()); - - if (tagName == s_v1_versionTagName) { - /* ignore */ - } - else if (tagName == s_nameTagName) { - name = xml.readElementText(); - } - else if (tagName == s_uniqueIdentifierTagName) { - uniqueID = xml.readElementText(); - } - else if (tagName == s_v1_rowStretchFactorsTagName) { - QXmlStreamAttributes atts = xml.attributes(); - if (atts.hasAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName)) { - numberOfRows = atts.value(s_v1_rowStretchFactorsSelectedCountAttributeName).toInt(); - } - - AString::toNumbers(xml.readElementText(), rowStretchFactors); - } - else if (tagName == s_v1_columnStretchFactorsTagName) { - QXmlStreamAttributes atts = xml.attributes(); - if (atts.hasAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName)) { - numberOfColumns = atts.value(s_v1_columnStretchFactorsSelectedCountAttributeName).toInt(); - } - AString::toNumbers(xml.readElementText(), columnStretchFactors); - } - else { - invalidElements.insert(tagName.toString()); - xml.skipCurrentElement(); - } - } - } - - static int32_t missingNameCounter = 1; - if (name.isEmpty()) { - name = ("Config_V1_" - + AString::number(missingNameCounter)); - missingNameCounter++; - } - if (uniqueID.isEmpty()) { - uniqueID = SystemUtilities::createUniqueID(); - } - if (rowStretchFactors.empty()) { - message.append(s_v1_rowStretchFactorsTagName - + " not found or invalid. "); - } - if (columnStretchFactors.empty()) { - message.append(s_v1_columnStretchFactorsTagName - + " not found or invalid. "); - } - if (numberOfRows <= 0) { - message.append(s_v1_rowStretchFactorsTagName - + " attribute " - + s_v1_rowStretchFactorsSelectedCountAttributeName - + " is missing or invalid." ); - } - if (numberOfRows <= 0) { - message.append(s_v1_columnStretchFactorsTagName - + " attribute " - + s_v1_columnStretchFactorsSelectedCountAttributeName - + " is missing or invalid." ); - } - - if ( ! invalidElements.empty()) { - /* - * If invalid elements were encountered, don't throw - */ - AString msg("Invalid element(s) ignored: "); - for (const auto s : invalidElements) { - msg.append(s + " "); - } - CaretLogWarning(msg); - } - - if (message.isEmpty()) { - setName(name); - setUniqueIdentifierProtected(uniqueID); - - m_rows.clear(); - m_columns.clear(); - - for (int32_t i = 0; i < numberOfRows; i++) { - TileTabsGridRowColumnElement element; - CaretAssertVectorIndex(rowStretchFactors, i); - element.setWeightStretch(rowStretchFactors[i]); - element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); - element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); - m_rows.push_back(element); - } - CaretAssert(numberOfRows == static_cast(m_rows.size())); - - for (int32_t i = 0; i < numberOfColumns; i++) { - TileTabsGridRowColumnElement element; - CaretAssertVectorIndex(columnStretchFactors, i); - element.setWeightStretch(columnStretchFactors[i]); - element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); - element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); - m_columns.push_back(element); - } - CaretAssert(numberOfColumns == static_cast(m_columns.size())); - } - else { - xml.raiseError(message); - } -} - -/** - * Decode Version Two of the tile tabs configuration from XML with stream reader. - * - * @param xml - * Stream XML parser. - */ -void -TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml) -{ - m_rows.clear(); - m_columns.clear(); - setName(""); - setUniqueIdentifierProtected(""); - - std::set invalidElements; - - AString name; - AString uniqueID; - - QString message; - - enum class ReadMode { - OTHER, - COLUMNS, - ROWS - }; - ReadMode readMode = ReadMode::OTHER; - - AString centeringCorrectionTextString; - - while ( ! xml.atEnd()) { - xml.readNext(); - - if (xml.isStartElement()) { - const QStringRef tagName(xml.name()); - - if (tagName == s_nameTagName) { - name = xml.readElementText(); - } - else if (tagName == s_uniqueIdentifierTagName) { - uniqueID = xml.readElementText(); - } - else if (tagName == s_v2_columnsTagName) { - readMode = ReadMode::COLUMNS; - } - else if (tagName == s_v2_rowsTagName) { - readMode = ReadMode::ROWS; - } - else if (tagName == s_v2_centeringCorrectionName) { - centeringCorrectionTextString = xml.readElementText(); - } - else if (tagName == s_v2_elementTagName) { - switch (readMode) { - case ReadMode::OTHER: - CaretAssert(0); - break; - case ReadMode::COLUMNS: - { - AString errorMessage; - TileTabsGridRowColumnElement e; - if (decodeRowColumnElement(xml, e, errorMessage)) { - m_columns.push_back(e); - } - else { - message.append(errorMessage); - } - } - break; - case ReadMode::ROWS: - { - AString errorMessage; - TileTabsGridRowColumnElement e; - if (decodeRowColumnElement(xml, e, errorMessage)) { - m_rows.push_back(e); - } - else { - message.append(errorMessage); - } - } - break; - } - } - else { - invalidElements.insert(tagName.toString()); - xml.skipCurrentElement(); - } - } - else if (xml.isEndElement()) { - const QStringRef tagName(xml.name()); - - if (tagName == s_v2_columnsTagName) { - readMode = ReadMode::OTHER; - } - else if (tagName == s_v2_rowsTagName) { - readMode = ReadMode::OTHER; - } - } - } - - static int32_t missingNameCounter = 1; - if (name.isEmpty()) { - name = ("Config_" - + AString::number(missingNameCounter)); - missingNameCounter++; - } - if (uniqueID.isEmpty()) { - uniqueID = SystemUtilities::createUniqueID(); - } - - if ( ! invalidElements.empty()) { - /* - * If invalid elements were encountered, don't throw - */ - AString msg("Invalid element(s) ignored: "); - for (const auto s : invalidElements) { - msg.append(s + " "); - } - CaretLogWarning(msg); - } - - if (message.isEmpty()) { - setName(name); - setUniqueIdentifierProtected(uniqueID); - - /* - * Only set centering correction if it is found. This allows usage - * of the default value in the event this is not found in the XML. - */ - if ( ! centeringCorrectionTextString.isEmpty()) { - m_centeringCorrectionEnabled = centeringCorrectionTextString.toBool(); - } - - } - else { - xml.raiseError(message); - } -} - -/** - * Decode elements in a row or column. - * - * @param reader - * The XML stream reader. - * @param element - * row/column element that is read - * @param errorMessageOut - * Contains error information. - * @return - * True if read successfully, else false. - */ -bool -TileTabsGridLayoutConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, - TileTabsGridRowColumnElement& element, - AString& errorMessageOut) -{ - const QXmlStreamAttributes atts = reader.attributes(); - - errorMessageOut.clear(); - - if (atts.hasAttribute(s_v2_contentTypeAttributeName)) { - bool validFlag(false); - const AString s = atts.value(s_v2_contentTypeAttributeName).toString(); - element.setContentType(TileTabsGridRowColumnContentTypeEnum::fromName(s, &validFlag)); - if ( ! validFlag) { - errorMessageOut.append("Content type \"" + s + "\" is not valid. "); - } - } - else { - errorMessageOut.append("Content type is missing. "); - } - - if (atts.hasAttribute(s_v2_stretchTypeAttributeName)) { - bool validFlag(false); - const AString s = atts.value(s_v2_stretchTypeAttributeName).toString(); - element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::fromName(s, &validFlag)); - if ( ! validFlag) { - errorMessageOut.append("Stretch type \"" + s + "\" is not valid. "); - } - } - else { - errorMessageOut.append("Stretch type is missing. "); - } - - if (atts.hasAttribute(s_v2_percentStretchAttributeName)) { - const float f = atts.value(s_v2_percentStretchAttributeName).toFloat(); - if ((f >= 0.0) && (f < 100.0)) { - element.setPercentStretch(f); - } - else { - errorMessageOut.append("Stretch percentage=" + AString::number(f) + " is invalid."); - } - } - else { - errorMessageOut.append("Stretch percentage is missing. "); - } - - if (atts.hasAttribute(s_v2_weightStretchAttributeName)) { - const float f = atts.value(s_v2_weightStretchAttributeName).toFloat(); - if ((f >= 0.0) && (f < 100.0)) { - element.setWeightStretch(f); - } - else { - errorMessageOut.append("Stretch weight=" + AString::number(f) + " is invalid."); - } - } - else { - errorMessageOut.append("Stretch weight is missing. "); - } - - if (errorMessageOut.isEmpty()) { - return true; - } - return false; -} - -/** - * @return String version of an instance. - */ -AString -TileTabsGridLayoutConfiguration::toString() const -{ - AString s = TileTabsBaseConfiguration::toString(); - - int32_t indx(0); - for (const auto item : m_columns) { - s.append(" Column " + AString::number(indx) + ": " + item.toString() + "\n"); - indx++; - } - indx = 0; - for (const auto item : m_rows) { - s.append(" Row " + AString::number(indx) + ": " + item.toString() + "\n"); - indx++; - } - - return s; -} - diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsGridLayoutConfiguration.h connectome-workbench-1.5.0/src/Common/TileTabsGridLayoutConfiguration.h --- connectome-workbench-1.4.2/src/Common/TileTabsGridLayoutConfiguration.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsGridLayoutConfiguration.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,178 +0,0 @@ -#ifndef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_H__ -#define __TILE_TABS_GRID_LAYOUT_CONFIGURATION_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include "CaretException.h" -#include "TileTabsBaseConfiguration.h" -#include "TileTabsGridModeEnum.h" -#include "TileTabsGridRowColumnElement.h" - -class QXmlStreamReader; -class QXmlStreamWriter; - -namespace caret { - - class TileTabsGridLayoutConfiguration : public TileTabsBaseConfiguration { - - public: - TileTabsGridLayoutConfiguration(); - - virtual ~TileTabsGridLayoutConfiguration(); - - TileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj); - - TileTabsGridLayoutConfiguration& operator=(const TileTabsGridLayoutConfiguration& obj); - - void copy(const TileTabsGridLayoutConfiguration& rhs); - - virtual TileTabsGridLayoutConfiguration* newCopyWithNewUniqueIdentifier() const override; - - bool getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, - const int32_t windowHeight, - const int32_t numberOfModelsToDraw, - const TileTabsGridModeEnum::Enum configurationMode, - std::vector& rowHeightsOut, - std::vector& columnWidthsOut); - - int32_t getNumberOfRows() const; - - void setNumberOfRows(const int32_t numberOfRows); - - int32_t getNumberOfColumns() const; - - void setNumberOfColumns(const int32_t numberOfColumns); - - TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex); - - const TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex) const; - - TileTabsGridRowColumnElement* getRow(const int32_t rowIndex); - - const TileTabsGridRowColumnElement* getRow(const int32_t rowIndex) const; - - void updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs); - - bool isCenteringCorrectionEnabled() const; - - void setCenteringCorrectionEnabled(const bool status); - - virtual AString toString() const override; - - static void getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, - int32_t& numberOfRowsOut, - int32_t& numberOfColumnsOut); - // ADD_NEW_METHODS_HERE - - protected: - public: - virtual void decodeFromXML(QXmlStreamReader& xml, - const QString& rootElement) override; - - virtual void encodeInXML(AString& xmlTextOut) const override; - - private: - void copyHelperTileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj); - - void decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml); - - void decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml); - - AString encodeVersionInXML(const int32_t versionNumber) const; - - AString encodeInXMLWithStreamWriterVersionOne() const; - - AString encodeInXMLWithStreamWriterVersionTwo() const; - - void encodeRowColumnElement(QXmlStreamWriter& writer, - const AString tagName, - const std::vector& elements) const; - - bool decodeRowColumnElement(QXmlStreamReader& reader, - TileTabsGridRowColumnElement& element, - AString& errorMessageOut); - - void initialize(); - - // ADD_NEW_MEMBERS_HERE - - std::vector m_columns; - - std::vector m_rows; - - bool m_centeringCorrectionEnabled = false; - - static const AString s_nameTagName; - static const AString s_uniqueIdentifierTagName; - - static const AString s_v1_rootTagName; - static const AString s_v1_versionTagName; - static const AString s_v1_versionNumberAttributeName; - static const AString s_v1_columnStretchFactorsTagName; - static const AString s_v1_columnStretchFactorsSelectedCountAttributeName; - static const AString s_v1_columnStretchFactorsTotalCountAttributeName; - static const AString s_v1_rowStretchFactorsTagName; - static const AString s_v1_rowStretchFactorsSelectedCountAttributeName; - static const AString s_v1_rowStretchFactorsTotalCountAttributeName; - - static const AString s_v2_rootTagName; - static const AString s_v2_versionAttributeName; - static const AString s_v2_columnsTagName; - static const AString s_v2_contentTypeAttributeName; - static const AString s_v2_elementTagName; - static const AString s_v2_percentStretchAttributeName; - static const AString s_v2_rowsTagName; - static const AString s_v2_stretchTypeAttributeName; - static const AString s_v2_weightStretchAttributeName; - static const AString s_v2_centeringCorrectionName; - - - friend class TileTabsBaseConfiguration; - }; - -#ifdef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ - const AString TileTabsGridLayoutConfiguration::s_nameTagName = "Name"; - const AString TileTabsGridLayoutConfiguration::s_uniqueIdentifierTagName = "UniqueIdentifier"; - - const AString TileTabsGridLayoutConfiguration::s_v1_rootTagName = "TileTabsGridLayoutConfiguration"; - const AString TileTabsGridLayoutConfiguration::s_v1_versionTagName = "Version"; - const AString TileTabsGridLayoutConfiguration::s_v1_versionNumberAttributeName = "Number"; - const AString TileTabsGridLayoutConfiguration::s_v1_columnStretchFactorsTagName = "ColumnStretchFactors"; - const AString TileTabsGridLayoutConfiguration::s_v1_columnStretchFactorsSelectedCountAttributeName = "SelectedRowCount"; - const AString TileTabsGridLayoutConfiguration::s_v1_columnStretchFactorsTotalCountAttributeName = "TotalRowCount"; - const AString TileTabsGridLayoutConfiguration::s_v1_rowStretchFactorsTagName = "RowStretchFactors"; - const AString TileTabsGridLayoutConfiguration::s_v1_rowStretchFactorsSelectedCountAttributeName = "SelectedColumnCount"; - const AString TileTabsGridLayoutConfiguration::s_v1_rowStretchFactorsTotalCountAttributeName = "TotalColumnCount"; - - const AString TileTabsGridLayoutConfiguration::s_v2_rootTagName = "TileTabsGridLayoutConfigurationTwo"; - const AString TileTabsGridLayoutConfiguration::s_v2_versionAttributeName = "Version"; - const AString TileTabsGridLayoutConfiguration::s_v2_columnsTagName = "Columns"; - const AString TileTabsGridLayoutConfiguration::s_v2_contentTypeAttributeName = "ContentType"; - const AString TileTabsGridLayoutConfiguration::s_v2_elementTagName = "Element"; - const AString TileTabsGridLayoutConfiguration::s_v2_percentStretchAttributeName = "PercentStretch"; - const AString TileTabsGridLayoutConfiguration::s_v2_rowsTagName = "Rows"; - const AString TileTabsGridLayoutConfiguration::s_v2_stretchTypeAttributeName = "StretchType"; - const AString TileTabsGridLayoutConfiguration::s_v2_weightStretchAttributeName = "WeightStretch"; - const AString TileTabsGridLayoutConfiguration::s_v2_centeringCorrectionName = "CenteringCorrection"; -#endif // __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ - -} // namespace -#endif //__TILE_TABS_GRID_LAYOUT_CONFIGURATION_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsGridModeEnum.cxx connectome-workbench-1.5.0/src/Common/TileTabsGridModeEnum.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsGridModeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsGridModeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,373 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#define __TILE_TABS_GRID_MODE_ENUM_DECLARE__ -#include "TileTabsGridModeEnum.h" -#undef __TILE_TABS_GRID_MODE_ENUM_DECLARE__ - -#include "CaretAssert.h" - -using namespace caret; - - -/** - * \class caret::TileTabsGridModeEnum - * \brief Enumerated type for Grid Tile Tab Configuration Mode (auto, custom) - * - * Using this enumerated type in the GUI with an EnumComboBoxTemplate - * - * Header File (.h) - * Forward declare the data type: - * class EnumComboBoxTemplate; - * - * Declare the member: - * EnumComboBoxTemplate* m_TileTabsGridModeEnumComboBox; - * - * Declare a slot that is called when user changes selection - * private slots: - * void TileTabsGridModeEnumComboBoxItemActivated(); - * - * Implementation File (.cxx) - * Include the header files - * #include "EnumComboBoxTemplate.h" - * #include "TileTabsGridModeEnum.h" - * - * Instatiate: - * m_TileTabsGridModeEnumComboBox = new EnumComboBoxTemplate(this); - * m_TileTabsGridModeEnumComboBox->setup(); - * - * Get notified when the user changes the selection: - * QObject::connect(m_TileTabsGridModeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(TileTabsGridModeEnumComboBoxItemActivated())); - * - * Update the selection: - * m_TileTabsGridModeEnumComboBox->setSelectedItem(NEW_VALUE); - * - * Read the selection: - * const TileTabsGridModeEnum::Enum VARIABLE = m_TileTabsGridModeEnumComboBox->getSelectedItem(); - * - */ - -/** - * Constructor. - * - * @param enumValue - * An enumerated value. - * @param name - * Name of enumerated value. - * - * @param guiName - * User-friendly name for use in user-interface. - */ -TileTabsGridModeEnum::TileTabsGridModeEnum(const Enum enumValue, - const AString& name, - const AString& guiName) -{ - this->enumValue = enumValue; - this->integerCode = integerCodeCounter++; - this->name = name; - this->guiName = guiName; -} - -/** - * Destructor. - */ -TileTabsGridModeEnum::~TileTabsGridModeEnum() -{ -} - -/** - * Initialize the enumerated metadata. - */ -void -TileTabsGridModeEnum::initialize() -{ - if (initializedFlag) { - return; - } - initializedFlag = true; - - enumData.push_back(TileTabsGridModeEnum(AUTOMATIC, - "AUTOMATIC", - "Automatic")); - - enumData.push_back(TileTabsGridModeEnum(CUSTOM, - "CUSTOM", - "Custom")); - -} - -/** - * Find the data for and enumerated value. - * @param enumValue - * The enumerated value. - * @return Pointer to data for this enumerated type - * or NULL if no data for type or if type is invalid. - */ -const TileTabsGridModeEnum* -TileTabsGridModeEnum::findData(const Enum enumValue) -{ - if (initializedFlag == false) initialize(); - - size_t num = enumData.size(); - for (size_t i = 0; i < num; i++) { - const TileTabsGridModeEnum* d = &enumData[i]; - if (d->enumValue == enumValue) { - return d; - } - } - - return NULL; -} - -/** - * Get a string representation of the enumerated type. - * @param enumValue - * Enumerated value. - * @return - * String representing enumerated value. - */ -AString -TileTabsGridModeEnum::toName(Enum enumValue) { - if (initializedFlag == false) initialize(); - - const TileTabsGridModeEnum* enumInstance = findData(enumValue); - return enumInstance->name; -} - -/** - * Get an enumerated value corresponding to its name. - * @param name - * Name of enumerated value. - * @param isValidOut - * If not NULL, it is set indicating that a - * enum value exists for the input name. - * @return - * Enumerated value. - */ -TileTabsGridModeEnum::Enum -TileTabsGridModeEnum::fromName(const AString& name, bool* isValidOut) -{ - if (initializedFlag == false) initialize(); - - bool validFlag = false; - Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - const TileTabsGridModeEnum& d = *iter; - if (d.name == name) { - enumValue = d.enumValue; - validFlag = true; - break; - } - } - - if (isValidOut != 0) { - *isValidOut = validFlag; - } - else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsGridModeEnum")); - } - return enumValue; -} - -/** - * Get a GUI string representation of the enumerated type. - * @param enumValue - * Enumerated value. - * @return - * String representing enumerated value. - */ -AString -TileTabsGridModeEnum::toGuiName(Enum enumValue) { - if (initializedFlag == false) initialize(); - - const TileTabsGridModeEnum* enumInstance = findData(enumValue); - return enumInstance->guiName; -} - -/** - * Get an enumerated value corresponding to its GUI name. - * @param s - * Name of enumerated value. - * @param isValidOut - * If not NULL, it is set indicating that a - * enum value exists for the input name. - * @return - * Enumerated value. - */ -TileTabsGridModeEnum::Enum -TileTabsGridModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) -{ - if (initializedFlag == false) initialize(); - - bool validFlag = false; - Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - const TileTabsGridModeEnum& d = *iter; - if (d.guiName == guiName) { - enumValue = d.enumValue; - validFlag = true; - break; - } - } - - if (isValidOut != 0) { - *isValidOut = validFlag; - } - else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsGridModeEnum")); - } - return enumValue; -} - -/** - * Get the integer code for a data type. - * - * @return - * Integer code for data type. - */ -int32_t -TileTabsGridModeEnum::toIntegerCode(Enum enumValue) -{ - if (initializedFlag == false) initialize(); - const TileTabsGridModeEnum* enumInstance = findData(enumValue); - return enumInstance->integerCode; -} - -/** - * Find the data type corresponding to an integer code. - * - * @param integerCode - * Integer code for enum. - * @param isValidOut - * If not NULL, on exit isValidOut will indicate if - * integer code is valid. - * @return - * Enum for integer code. - */ -TileTabsGridModeEnum::Enum -TileTabsGridModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) -{ - if (initializedFlag == false) initialize(); - - bool validFlag = false; - Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - const TileTabsGridModeEnum& enumInstance = *iter; - if (enumInstance.integerCode == integerCode) { - enumValue = enumInstance.enumValue; - validFlag = true; - break; - } - } - - if (isValidOut != 0) { - *isValidOut = validFlag; - } - else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsGridModeEnum")); - } - return enumValue; -} - -/** - * Get all of the enumerated type values. The values can be used - * as parameters to toXXX() methods to get associated metadata. - * - * @param allEnums - * A vector that is OUTPUT containing all of the enumerated values. - */ -void -TileTabsGridModeEnum::getAllEnums(std::vector& allEnums) -{ - if (initializedFlag == false) initialize(); - - allEnums.clear(); - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - allEnums.push_back(iter->enumValue); - } -} - -/** - * Get all of the names of the enumerated type values. - * - * @param allNames - * A vector that is OUTPUT containing all of the names of the enumerated values. - * @param isSorted - * If true, the names are sorted in alphabetical order. - */ -void -TileTabsGridModeEnum::getAllNames(std::vector& allNames, const bool isSorted) -{ - if (initializedFlag == false) initialize(); - - allNames.clear(); - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - allNames.push_back(TileTabsGridModeEnum::toName(iter->enumValue)); - } - - if (isSorted) { - std::sort(allNames.begin(), allNames.end()); - } -} - -/** - * Get all of the GUI names of the enumerated type values. - * - * @param allNames - * A vector that is OUTPUT containing all of the GUI names of the enumerated values. - * @param isSorted - * If true, the names are sorted in alphabetical order. - */ -void -TileTabsGridModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) -{ - if (initializedFlag == false) initialize(); - - allGuiNames.clear(); - - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - allGuiNames.push_back(TileTabsGridModeEnum::toGuiName(iter->enumValue)); - } - - if (isSorted) { - std::sort(allGuiNames.begin(), allGuiNames.end()); - } -} - diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsGridModeEnum.h connectome-workbench-1.5.0/src/Common/TileTabsGridModeEnum.h --- connectome-workbench-1.4.2/src/Common/TileTabsGridModeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsGridModeEnum.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -#ifndef __TILE_TABS_GRID_MODE_ENUM_H__ -#define __TILE_TABS_GRID_MODE_ENUM_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include -#include -#include "AString.h" - -namespace caret { - -class TileTabsGridModeEnum { - -public: - /** - * Enumerated values. - */ - enum Enum { - /** Workbench creates tab layout */ - AUTOMATIC, - /** User customizes tab layout */ - CUSTOM - }; - - - ~TileTabsGridModeEnum(); - - static AString toName(Enum enumValue); - - static Enum fromName(const AString& name, bool* isValidOut); - - static AString toGuiName(Enum enumValue); - - static Enum fromGuiName(const AString& guiName, bool* isValidOut); - - static int32_t toIntegerCode(Enum enumValue); - - static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); - - static void getAllEnums(std::vector& allEnums); - - static void getAllNames(std::vector& allNames, const bool isSorted); - - static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); - -private: - TileTabsGridModeEnum(const Enum enumValue, - const AString& name, - const AString& guiName); - - static const TileTabsGridModeEnum* findData(const Enum enumValue); - - /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; - - /** Initialize instances that contain the enum values and metadata */ - static void initialize(); - - /** Indicates instance of enum values and metadata have been initialized */ - static bool initializedFlag; - - /** Auto generated integer codes */ - static int32_t integerCodeCounter; - - /** The enumerated type value for an instance */ - Enum enumValue; - - /** The integer code associated with an enumerated value */ - int32_t integerCode; - - /** The name, a text string that is identical to the enumerated value */ - AString name; - - /** A user-friendly name that is displayed in the GUI */ - AString guiName; -}; - -#ifdef __TILE_TABS_GRID_MODE_ENUM_DECLARE__ -std::vector TileTabsGridModeEnum::enumData; -bool TileTabsGridModeEnum::initializedFlag = false; -int32_t TileTabsGridModeEnum::integerCodeCounter = 0; -#endif // __TILE_TABS_GRID_MODE_ENUM_DECLARE__ - -} // namespace -#endif //__TILE_TABS_GRID_MODE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutBackgroundTypeEnum.cxx connectome-workbench-1.5.0/src/Common/TileTabsLayoutBackgroundTypeEnum.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutBackgroundTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutBackgroundTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,380 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __TILE_TABS_LAYOUT_BACKGROUND_TYPE_ENUM_DECLARE__ +#include "TileTabsLayoutBackgroundTypeEnum.h" +#undef __TILE_TABS_LAYOUT_BACKGROUND_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::TileTabsLayoutBackgroundTypeEnum + * \brief Background type (opaque, transparent) for tabs in a tile tabs layout + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_TileTabsLayoutBackgroundTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void TileTabsLayoutBackgroundTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "TileTabsLayoutBackgroundTypeEnum.h" + * + * Instatiate: + * m_TileTabsLayoutBackgroundTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_TileTabsLayoutBackgroundTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_TileTabsLayoutBackgroundTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(TileTabsLayoutBackgroundTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_TileTabsLayoutBackgroundTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const TileTabsLayoutBackgroundTypeEnum::Enum VARIABLE = m_TileTabsLayoutBackgroundTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +TileTabsLayoutBackgroundTypeEnum::TileTabsLayoutBackgroundTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +TileTabsLayoutBackgroundTypeEnum::~TileTabsLayoutBackgroundTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +TileTabsLayoutBackgroundTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(TileTabsLayoutBackgroundTypeEnum(OPAQUE_BG, + "OPAQUE_BG", + "Opaque")); + + enumData.push_back(TileTabsLayoutBackgroundTypeEnum(TRANSPARENT_BG, + "TRANSPARENT_BG", + "Transparent")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const TileTabsLayoutBackgroundTypeEnum* +TileTabsLayoutBackgroundTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const TileTabsLayoutBackgroundTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsLayoutBackgroundTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsLayoutBackgroundTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsLayoutBackgroundTypeEnum::Enum +TileTabsLayoutBackgroundTypeEnum::fromName(const AString& nameIn, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + AString name(nameIn); + if (name == "OPAQUE") { + name = "OPAQUE_BG"; + } + else if (name == "TRANSPARENT") { + name = "TRANSPARENT_BG"; + } + bool validFlag = false; + Enum enumValue = TileTabsLayoutBackgroundTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsLayoutBackgroundTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if ( ! validFlag) { + CaretAssertMessage(0, AString("Name " + name + " failed to match enumerated value for type TileTabsLayoutBackgroundTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsLayoutBackgroundTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsLayoutBackgroundTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsLayoutBackgroundTypeEnum::Enum +TileTabsLayoutBackgroundTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsLayoutBackgroundTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsLayoutBackgroundTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsLayoutBackgroundTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +TileTabsLayoutBackgroundTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const TileTabsLayoutBackgroundTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +TileTabsLayoutBackgroundTypeEnum::Enum +TileTabsLayoutBackgroundTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsLayoutBackgroundTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsLayoutBackgroundTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsLayoutBackgroundTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +TileTabsLayoutBackgroundTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsLayoutBackgroundTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(TileTabsLayoutBackgroundTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsLayoutBackgroundTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(TileTabsLayoutBackgroundTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutBackgroundTypeEnum.h connectome-workbench-1.5.0/src/Common/TileTabsLayoutBackgroundTypeEnum.h --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutBackgroundTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutBackgroundTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,104 @@ +#ifndef __TILE_TABS_LAYOUT_BACKGROUND_TYPE_ENUM_H__ +#define __TILE_TABS_LAYOUT_BACKGROUND_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class TileTabsLayoutBackgroundTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Opaque Note: 'OPAQUE' clashes with symbol in Window OpenGL */ + OPAQUE_BG, + /** Transparent Note: 'TRANSPARENT' clashes with symbol in Window OpenGL*/ + TRANSPARENT_BG + }; + + + ~TileTabsLayoutBackgroundTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + TileTabsLayoutBackgroundTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const TileTabsLayoutBackgroundTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __TILE_TABS_LAYOUT_BACKGROUND_TYPE_ENUM_DECLARE__ +std::vector TileTabsLayoutBackgroundTypeEnum::enumData; +bool TileTabsLayoutBackgroundTypeEnum::initializedFlag = false; +int32_t TileTabsLayoutBackgroundTypeEnum::integerCodeCounter = 0; +#endif // __TILE_TABS_LAYOUT_BACKGROUND_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__TILE_TABS_LAYOUT_BACKGROUND_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutBaseConfiguration.cxx connectome-workbench-1.5.0/src/Common/TileTabsLayoutBaseConfiguration.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutBaseConfiguration.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutBaseConfiguration.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,359 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#define __TILE_TABS_LAYOUT_BASE_CONFIGURATION_DECLARE__ +#include "TileTabsLayoutBaseConfiguration.h" +#undef __TILE_TABS_LAYOUT_BASE_CONFIGURATION_DECLARE__ + +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "SystemUtilities.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "TileTabsLayoutManualConfiguration.h" + +using namespace caret; + + + +/** + * \class caret::TileTabsLayoutBaseConfiguration + * \brief Defines a tile tabs configuration + * \ingroup Common + */ + +/** + * Constructor that creates a 2 by 2 configuration. + */ +TileTabsLayoutBaseConfiguration::TileTabsLayoutBaseConfiguration(const TileTabsLayoutConfigurationTypeEnum::Enum layoutType) +: CaretObject(), +m_layoutType(layoutType) +{ + initializeTileTabsLayoutBaseConfiguration(); +} + +/** + * Destructor. + */ +TileTabsLayoutBaseConfiguration::~TileTabsLayoutBaseConfiguration() +{ +} + +/** + * Copy constructor. + * + * @param obj + * Object that is copied. + */ +TileTabsLayoutBaseConfiguration::TileTabsLayoutBaseConfiguration(const TileTabsLayoutBaseConfiguration& obj) +: CaretObject(obj), +m_layoutType(obj.m_layoutType) +{ + initializeTileTabsLayoutBaseConfiguration(); + this->copyHelperTileTabsLayoutBaseConfiguration(obj); +} + +/** + * Assignment operator. + * + * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() + * + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +TileTabsLayoutBaseConfiguration& +TileTabsLayoutBaseConfiguration::operator=(const TileTabsLayoutBaseConfiguration& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + const QString savedUniqueID = m_uniqueIdentifier; + this->copyHelperTileTabsLayoutBaseConfiguration(obj); + m_uniqueIdentifier = savedUniqueID; + } + return *this; +} + +/** + * Initialize an instance of a tile tabs configuration. + */ +void +TileTabsLayoutBaseConfiguration::initializeTileTabsLayoutBaseConfiguration() +{ + m_name.clear(); + m_uniqueIdentifier = SystemUtilities::createUniqueID(); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +TileTabsLayoutBaseConfiguration::copyHelperTileTabsLayoutBaseConfiguration(const TileTabsLayoutBaseConfiguration& obj) +{ + if (this == &obj) { + return; + } + + m_name = obj.m_name; + /* DO NOT CHANGE THE UNIQUE IDENTIFIER: m_uniqueIdentifier */ +} + +/** + * Create a copy of this configuration and set its unique identifier to + * the given unique identifier. + * + * @param uniqueIdentifier + * The unique identifier (if empty a valid ID will be in the new configuration) + * @return + * Pointer to the new configuration. + */ +TileTabsLayoutBaseConfiguration* +TileTabsLayoutBaseConfiguration::newCopyWithUniqueIdentifier(const AString& uniqueIdentifier) const +{ + TileTabsLayoutBaseConfiguration* config = newCopyWithNewUniqueIdentifier(); + if ( ! uniqueIdentifier.isEmpty()) { + config->setUniqueIdentifierProtected(uniqueIdentifier); + } + return config; +} + +/** + * @return Type of layout for the configuration + */ +TileTabsLayoutConfigurationTypeEnum::Enum +TileTabsLayoutBaseConfiguration::getLayoutType() const +{ + return m_layoutType; +} + +/** + * @return the name of the tile tabs configuration. + */ +AString +TileTabsLayoutBaseConfiguration::getName() const +{ + return m_name; +} + +/** + * @return Get the unique identifier that uniquely identifies each configuration. + */ +AString +TileTabsLayoutBaseConfiguration::getUniqueIdentifier() const +{ + return m_uniqueIdentifier; +} + +/** + * Set the name of the tile tabs configuration. + * + * @param name + * New name for configuration. + */ +void +TileTabsLayoutBaseConfiguration::setName(const AString& name) +{ + m_name = name; +} + +/** + * Set the unique identifier of the tile tabs configuration. + * + * @param uniqueID + * New unique identifier for configuration. + */ +void +TileTabsLayoutBaseConfiguration::setUniqueIdentifierProtected(const AString& uniqueID) +{ + m_uniqueIdentifier = uniqueID; +} + + +/** + * @return Encoded tile tabs configuration in XML + */ +AString +TileTabsLayoutBaseConfiguration::encodeInXML() const +{ + AString s; + encodeInXMLString(s); + return s; +} + +/** + * Decode the tile tabs configuration from XML + * + * @param xmlString + * String containing XML. + * @param errorMessageOut + * Contains error information if decoding fails. + * @return + * Pointer to the configuration or NULL if there was an error. + */ +TileTabsLayoutBaseConfiguration* +TileTabsLayoutBaseConfiguration::decodeFromXML(const AString& xmlString, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + TileTabsLayoutBaseConfiguration* configurationOut(NULL); + + QXmlStreamReader xml(xmlString); + + if (xml.readNextStartElement()) { + const QStringRef tagName(xml.name()); + if (tagName == TileTabsLayoutGridConfiguration::s_v1_rootTagName) { + TileTabsLayoutGridConfiguration* v1 = TileTabsLayoutGridConfiguration::newInstanceCustomGrid(); + v1->decodeFromXMLString(xml, + tagName.toString()); + configurationOut = v1; + } + else if (tagName == TileTabsLayoutGridConfiguration::s_v2_rootTagName) { + TileTabsLayoutGridConfiguration* v2 = TileTabsLayoutGridConfiguration::newInstanceCustomGrid(); + v2->decodeFromXMLString(xml, + tagName.toString()); + configurationOut = v2; + } + else if (tagName == TileTabsLayoutManualConfiguration::s_rootElementName) { + TileTabsLayoutManualConfiguration* manConfig = new TileTabsLayoutManualConfiguration(); + manConfig->decodeFromXMLString(xml, + tagName.toString()); + configurationOut = manConfig; + } + else { + xml.raiseError("TileTabsLayoutBaseConfiguration first element is " + + xml.name().toString() + + " but should be " + + TileTabsLayoutGridConfiguration::s_v1_rootTagName + + " or " + + TileTabsLayoutGridConfiguration::s_v2_rootTagName); + } + } + else { + xml.raiseError("TileTabsLayoutBaseConfiguration failed to find start elemnent."); + } + + if (xml.hasError()) { + errorMessageOut = ("Tile Tabs Configuration Read Error at line number=" + + AString::number(xml.lineNumber()) + + " column number=" + + AString::number(xml.columnNumber()) + + " description=" + + xml.errorString()); + if (configurationOut != NULL) { + delete configurationOut; + configurationOut = NULL; + } + return configurationOut; + } + + return configurationOut; +} + + +/** + * @return String version of an instance. + */ +AString +TileTabsLayoutBaseConfiguration::toString() const +{ + QString str; + QTextStream ts(&str); + ts << "TileTabsLayoutBaseConfiguration: " + << " m_name=" << m_name + << " m_uniqueIdentifier=" << m_uniqueIdentifier + << " m_layoutType=" << TileTabsLayoutConfigurationTypeEnum::toName(m_layoutType); + + return str; +} + +/** + * Cast to a grid configuration (avoids dynamic_cast that can be slow) + * + * @return Pointer to grid configuration or NULL if not a grid configuration. + */ +TileTabsLayoutGridConfiguration* +TileTabsLayoutBaseConfiguration::castToGridConfiguration() +{ + return NULL; +} + +/** + * Cast to a grid configuration (avoids dynamic_cast that can be slow) + * + * @return Pointer to grid configuration or NULL if not a grid configuration. + */ +const TileTabsLayoutGridConfiguration* +TileTabsLayoutBaseConfiguration::castToGridConfiguration() const +{ + return NULL; +} + +/** + * Cast to a manual configuration (avoids dynamic_cast that can be slow) + * + * @return Pointer to manual configuration or NULL if not a manual configuration. + */ +TileTabsLayoutManualConfiguration* +TileTabsLayoutBaseConfiguration::castToManualConfiguration() +{ + return NULL; +} + +/** + * Cast to a manual configuration (avoids dynamic_cast that can be slow) + * + * @return Pointer to manual configuration or NULL if not a manual configuration. + */ +const TileTabsLayoutManualConfiguration* +TileTabsLayoutBaseConfiguration::castToManualConfiguration() const +{ + return NULL; +} + +/** + * Compare two tile tabs configurations by name. + * + * @param ttc1 + * First tile tab configuration. + * @param ttc2 + * Second tile tab configuration. + * @return + * True if ttc1 is "less than" when compared by name, else false. + */ +bool +TileTabsLayoutBaseConfiguration::lessThanComparisonByName(const TileTabsLayoutBaseConfiguration* ttc1, + const TileTabsLayoutBaseConfiguration* ttc2) +{ + if (ttc1->getName() < ttc2->getName()) { + return true; + } + return false; +} + diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutBaseConfiguration.h connectome-workbench-1.5.0/src/Common/TileTabsLayoutBaseConfiguration.h --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutBaseConfiguration.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutBaseConfiguration.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,141 @@ +#ifndef __TILE_TABS_LAYOUT_BASE_CONFIGURATION_H__ +#define __TILE_TABS_LAYOUT_BASE_CONFIGURATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretException.h" +#include "CaretObject.h" +#include "TileTabsLayoutConfigurationTypeEnum.h" +#include "TileTabsGridRowColumnElement.h" + +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace caret { + + class TileTabsLayoutGridConfiguration; + class TileTabsLayoutManualConfiguration; + + class TileTabsLayoutBaseConfiguration : public CaretObject { + + protected: + TileTabsLayoutBaseConfiguration(const TileTabsLayoutConfigurationTypeEnum::Enum layoutType); + + public: + virtual ~TileTabsLayoutBaseConfiguration(); + + TileTabsLayoutBaseConfiguration(const TileTabsLayoutBaseConfiguration& obj); + + TileTabsLayoutBaseConfiguration& operator=(const TileTabsLayoutBaseConfiguration& obj); + + /** + * Copy the given configuration to "this" configuration. If given configuration + * does not cast to "this class type" log a warning and do not copy. + * Name property is not copied. + * + * @param rhs + * Configuration to copy. + */ + virtual void copy(const TileTabsLayoutBaseConfiguration& rhs) = 0; + + virtual TileTabsLayoutBaseConfiguration* newCopyWithNewUniqueIdentifier() const = 0; + + TileTabsLayoutBaseConfiguration* newCopyWithUniqueIdentifier(const AString& uniqueIdentifier) const; + + TileTabsLayoutConfigurationTypeEnum::Enum getLayoutType() const; + + AString getName() const; + + void setName(const AString& name); + + AString getUniqueIdentifier() const; + + virtual int32_t getNumberOfTabs() const = 0; + + AString encodeInXML() const; + + static TileTabsLayoutBaseConfiguration* decodeFromXML(const AString& xmlString, + AString& errorMessageOut); + + AString toString() const override; + + virtual TileTabsLayoutGridConfiguration* castToGridConfiguration(); + + virtual const TileTabsLayoutGridConfiguration* castToGridConfiguration() const; + + virtual TileTabsLayoutManualConfiguration* castToManualConfiguration(); + + virtual const TileTabsLayoutManualConfiguration* castToManualConfiguration() const; + + static bool lessThanComparisonByName(const TileTabsLayoutBaseConfiguration* ttc1, + const TileTabsLayoutBaseConfiguration* ttc2); + + // ADD_NEW_METHODS_HERE + + + protected: + /** + * Decode the configuration using the given XML stream reader and root element + * If there is an error, xml.raiseError() should be used to specify the error + * and caller of this method can test for the error using xml.isError(). + * + * @param xml + * The XML stream reader. + * @param rootElement + * The root element. + */ + virtual void decodeFromXMLString(QXmlStreamReader& xml, + const AString& rootElementText) = 0; + + /** + * Encode the configuration in XML. + * + * @param xmlTextOut + * Contains XML representation of configuration. + */ + virtual void encodeInXMLString(AString& xmlTextOut) const = 0; + + void setUniqueIdentifierProtected(const AString& uniqueID); + + void copyHelperTileTabsLayoutBaseConfiguration(const TileTabsLayoutBaseConfiguration& obj); + + private: + + void initializeTileTabsLayoutBaseConfiguration(); + + // ADD_NEW_MEMBERS_HERE + + const TileTabsLayoutConfigurationTypeEnum::Enum m_layoutType; + + AString m_name; + + /** Unique identifier does not get copied */ + AString m_uniqueIdentifier; + + + }; + +#ifdef __TILE_TABS_LAYOUT_BASE_CONFIGURATION_H__ + +#endif // __TILE_TABS_LAYOUT_BASE_CONFIGURATION_DECLARE__ + +} // namespace +#endif //__TILE_TABS_LAYOUT_BASE_CONFIGURATION_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutConfigurationTypeEnum.cxx connectome-workbench-1.5.0/src/Common/TileTabsLayoutConfigurationTypeEnum.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutConfigurationTypeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutConfigurationTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,389 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __TILE_TABS_LAYOUT_CONFIGURATION_TYPE_ENUM_DECLARE__ +#include "TileTabsLayoutConfigurationTypeEnum.h" +#undef __TILE_TABS_LAYOUT_CONFIGURATION_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::TileTabsLayoutConfigurationTypeEnum + * \brief Types of tile tabs layouts + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_TileTabsLayoutConfigurationTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void TileTabsLayoutConfigurationTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "TileTabsLayoutConfigurationTypeEnum.h" + * + * Instatiate: + * m_TileTabsLayoutConfigurationTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_TileTabsLayoutConfigurationTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_TileTabsLayoutConfigurationTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(TileTabsLayoutConfigurationTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_TileTabsLayoutConfigurationTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const TileTabsLayoutConfigurationTypeEnum::Enum VARIABLE = m_TileTabsLayoutConfigurationTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +TileTabsLayoutConfigurationTypeEnum::TileTabsLayoutConfigurationTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +TileTabsLayoutConfigurationTypeEnum::~TileTabsLayoutConfigurationTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +TileTabsLayoutConfigurationTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(TileTabsLayoutConfigurationTypeEnum(AUTOMATIC_GRID, + "AUTOMATIC_GRID", + "Automatic Grid")); + + enumData.push_back(TileTabsLayoutConfigurationTypeEnum(CUSTOM_GRID, + "CUSTOM_GRID", + "Custom Grid")); + + enumData.push_back(TileTabsLayoutConfigurationTypeEnum(MANUAL, + "MANUAL", + "Manual")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const TileTabsLayoutConfigurationTypeEnum* +TileTabsLayoutConfigurationTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const TileTabsLayoutConfigurationTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsLayoutConfigurationTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsLayoutConfigurationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsLayoutConfigurationTypeEnum::Enum +TileTabsLayoutConfigurationTypeEnum::fromName(const AString& nameIn, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + /* + * Convert names from older versions of tile tab layout's TileTabsGridModeEnum + */ + QString name(nameIn); + if (name == "AUTOMATIC") { + name = TileTabsLayoutConfigurationTypeEnum::toName(TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID); + } + else if (name == "CUSTOM") { + name = TileTabsLayoutConfigurationTypeEnum::toName(TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID); + } + + bool validFlag = false; + Enum enumValue = TileTabsLayoutConfigurationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsLayoutConfigurationTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsLayoutConfigurationTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsLayoutConfigurationTypeEnum::toGuiName(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + const TileTabsLayoutConfigurationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsLayoutConfigurationTypeEnum::Enum +TileTabsLayoutConfigurationTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsLayoutConfigurationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsLayoutConfigurationTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsLayoutConfigurationTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +TileTabsLayoutConfigurationTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const TileTabsLayoutConfigurationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +TileTabsLayoutConfigurationTypeEnum::Enum +TileTabsLayoutConfigurationTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsLayoutConfigurationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsLayoutConfigurationTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsLayoutConfigurationTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +TileTabsLayoutConfigurationTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsLayoutConfigurationTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(TileTabsLayoutConfigurationTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsLayoutConfigurationTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(TileTabsLayoutConfigurationTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutConfigurationTypeEnum.h connectome-workbench-1.5.0/src/Common/TileTabsLayoutConfigurationTypeEnum.h --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutConfigurationTypeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutConfigurationTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ +#define __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class TileTabsLayoutConfigurationTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Automatic grid with rows/columns sufficient to show all tabs */ + AUTOMATIC_GRID, + /** Grid with rows/columns customized by user */ + CUSTOM_GRID, + /** Use sets bounds of layout */ + MANUAL + }; + + + ~TileTabsLayoutConfigurationTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + TileTabsLayoutConfigurationTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const TileTabsLayoutConfigurationTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __TILE_TABS_LAYOUT_CONFIGURATION_TYPE_ENUM_DECLARE__ +std::vector TileTabsLayoutConfigurationTypeEnum::enumData; +bool TileTabsLayoutConfigurationTypeEnum::initializedFlag = false; +int32_t TileTabsLayoutConfigurationTypeEnum::integerCodeCounter = 0; +#endif // __TILE_TABS_LAYOUT_CONFIGURATION_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutGridConfiguration.cxx connectome-workbench-1.5.0/src/Common/TileTabsLayoutGridConfiguration.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutGridConfiguration.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutGridConfiguration.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,1240 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#define __TILE_TABS_LAYOUT_GRID_CONFIGURATION_DECLARE__ +#include "TileTabsLayoutGridConfiguration.h" +#undef __TILE_TABS_LAYOUT_GRID_CONFIGURATION_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "SystemUtilities.h" + +using namespace caret; + + + +/** + * \class caret::TileTabsLayoutGridConfiguration + * \brief Defines a tile tabs configuration + * \ingroup Common + */ + +/** + * @return A new instance containing a automatic grid configuration + */ +TileTabsLayoutGridConfiguration* +TileTabsLayoutGridConfiguration::newInstanceAutomaticGrid() +{ + TileTabsLayoutGridConfiguration* config = new TileTabsLayoutGridConfiguration(TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID); + return config; +} + +/** + * @return A new instance containing a custom grid configuration + */ +TileTabsLayoutGridConfiguration* +TileTabsLayoutGridConfiguration::newInstanceCustomGrid() +{ + TileTabsLayoutGridConfiguration* config = new TileTabsLayoutGridConfiguration(TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID); + return config; +} + +/** + * Constructor that creates a 2 by 2 configuration. + */ +TileTabsLayoutGridConfiguration::TileTabsLayoutGridConfiguration(const TileTabsLayoutConfigurationTypeEnum::Enum gridConfigType) +: TileTabsLayoutBaseConfiguration(gridConfigType) +{ + switch (gridConfigType) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + CaretAssert(0); + break; + } + + initialize(); +} + +/** + * Destructor. + */ +TileTabsLayoutGridConfiguration::~TileTabsLayoutGridConfiguration() +{ +} + +/** + * Copy constructor. + * + * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() + * + * @param obj + * Object that is copied. + */ +TileTabsLayoutGridConfiguration::TileTabsLayoutGridConfiguration(const TileTabsLayoutGridConfiguration& obj) +: TileTabsLayoutBaseConfiguration(obj) +{ + initialize(); + this->copyHelperTileTabsLayoutGridConfiguration(obj); +} + +/** + * Assignment operator. + * + * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() + * + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +TileTabsLayoutGridConfiguration& +TileTabsLayoutGridConfiguration::operator=(const TileTabsLayoutGridConfiguration& obj) +{ + if (this != &obj) { + TileTabsLayoutBaseConfiguration::operator=(obj); + this->copyHelperTileTabsLayoutGridConfiguration(obj); + } + return *this; +} + +/** + * Copy this instance and give it a new unique identifier. + * Note that copy constructor does not create a new unique identifier. + * + * @return The new Copy. + */ +TileTabsLayoutBaseConfiguration* +TileTabsLayoutGridConfiguration::newCopyWithNewUniqueIdentifier() const +{ + TileTabsLayoutGridConfiguration* newCopy = new TileTabsLayoutGridConfiguration(*this); + CaretAssert(newCopy); + return newCopy; +} + + +/** + * Initialize an instance of a tile tabs configuration. + */ +void +TileTabsLayoutGridConfiguration::initialize() +{ + setNumberOfRows(2); + setNumberOfColumns(2); + + m_centeringCorrectionEnabled = false; + m_customDefaultFlag = false; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +TileTabsLayoutGridConfiguration::copyHelperTileTabsLayoutGridConfiguration(const TileTabsLayoutGridConfiguration& obj) +{ +// if (this == &obj) { +// return; +// } + + copyHelperTileTabsLayoutBaseConfiguration(obj); + m_columns = obj.m_columns; + m_rows = obj.m_rows; + m_centeringCorrectionEnabled = obj.m_centeringCorrectionEnabled; + m_customDefaultFlag = false; +} + +/** + * Copy the given configuration to "this" configuration. If given configuration + * does not cast to "this class type" log a warning and do not copy. + * Name property is not copied. + * + * @param rhs + * Configuration to copy. + */ +void +TileTabsLayoutGridConfiguration::copy(const TileTabsLayoutBaseConfiguration& rhs) +{ + const TileTabsLayoutGridConfiguration* gridConfig = rhs.castToGridConfiguration(); + if (gridConfig != NULL) { + AString savedName = getName(); + copyHelperTileTabsLayoutGridConfiguration(*gridConfig); + setName(savedName); + } + else { + CaretLogSevere("Attempt to copy layout configuration " + + rhs.toString() + + " to " + + toString()); + } +} + +/** + * @return Number of tabs in this layout (spacers are excluded) + */ +int32_t +TileTabsLayoutGridConfiguration::getNumberOfTabs() const +{ + /* + * Need to exclude spacer rows/columns + */ + + int32_t rowCount(0); + for (const auto& row : m_rows) { + switch (row.getContentType()) { + case TileTabsGridRowColumnContentTypeEnum::SPACE: + break; + case TileTabsGridRowColumnContentTypeEnum::TAB: + rowCount++; + break; + } + } + + int32_t columnCount(0); + for (const auto& col : m_columns) { + switch (col.getContentType()) { + case TileTabsGridRowColumnContentTypeEnum::SPACE: + break; + case TileTabsGridRowColumnContentTypeEnum::TAB: + columnCount++; + break; + } + } + + const int32_t numTabs = rowCount * columnCount; + return numTabs; +} + +/** + * Get infoformation for the given elemnent in the columns. + * + * @param Information for element. + */ +TileTabsGridRowColumnElement* +TileTabsLayoutGridConfiguration::getColumn(const int32_t columnIndex) +{ + CaretAssertVectorIndex(m_columns, columnIndex); + return &m_columns[columnIndex]; +} + +/** + * Get infoformation for the given elemnent in the columns. + * + * @param Information for element. + */ +const TileTabsGridRowColumnElement* +TileTabsLayoutGridConfiguration::getColumn(const int32_t columnIndex) const +{ + CaretAssertVectorIndex(m_columns, columnIndex); + return &m_columns[columnIndex]; +} + +/** + * Get infoformation for the given elemnent in the rows. + * + * @param Information for element. + */ +TileTabsGridRowColumnElement* +TileTabsLayoutGridConfiguration::getRow(const int32_t rowIndex) +{ + CaretAssertVectorIndex(m_rows, rowIndex); + return &m_rows[rowIndex]; +} + +/** + * Get infoformation for the given elemnent in the rows. + * + * @param Information for element. + */ +const TileTabsGridRowColumnElement* +TileTabsLayoutGridConfiguration::getRow(const int32_t rowIndex) const +{ + CaretAssertVectorIndex(m_rows, rowIndex); + return &m_rows[rowIndex]; +} + +/** + * Get the row heights and column widths for this tile tabs configuration using the + * given window width and height. + * + * @param windowWidth + * Width of window. + * @param windowHeight + * Height of window. + * @param numberOfModelsToDraw + * Number of models to draw. + * @param configurationMode + * The tile tabs configuration mode + * @param rowHeightsOut + * Output containing height of each row. + * @param columnWidthsOut + * Output containing width of each column. + * @return + * True if the ouput is valid, else false. + */ +bool +TileTabsLayoutGridConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, + const int32_t windowHeight, + const int32_t numberOfModelsToDraw, + const TileTabsLayoutConfigurationTypeEnum::Enum configurationMode, + std::vector& rowHeightsOut, + std::vector& columnWidthsOut) +{ + /* + * NOTE: When computing widths and heights, do not round. + * Rounding may cause the bottom most row or column to extend + * outside the graphics region. Shrinking the last row or + * column is not desired since it might cause the last model + * to be drawn slightly smaller than the others. + */ + + int32_t numRows = 0; + int32_t numCols = 0; + + rowHeightsOut.clear(); + columnWidthsOut.clear(); + + switch (configurationMode) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + { + /* + * Update number of rows/columns in the default configuration + * so that if a scene is saved, the correct number of rows + * and columns are saved to the scene. + */ + updateAutomaticConfigurationRowsAndColumns(numberOfModelsToDraw); + numRows = getNumberOfRows(); + numCols = getNumberOfColumns(); + + for (int32_t i = 0; i < numRows; i++) { + rowHeightsOut.push_back(windowHeight / numRows); + } + for (int32_t i = 0; i < numCols; i++) { + columnWidthsOut.push_back(windowWidth / numCols); + } + } + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + { + /* + * Rows/columns from user configuration + */ + numRows = getNumberOfRows(); + numCols = getNumberOfColumns(); + + /* + * Determine height of each row + */ + float rowPercentTotal = 0.0; + float rowStretchTotal = 0.0; + for (int32_t i = 0; i < numRows; i++) { + const TileTabsGridRowColumnElement* e = getRow(i); + switch (e->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + rowPercentTotal += (e->getPercentStretch() / 100.0); + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + rowStretchTotal += e->getWeightStretch(); + break; + } + } + + float windowWeightHeight = windowHeight; + if (rowPercentTotal > 0.0) { + if (rowPercentTotal >= 1.0) { + windowWeightHeight = 0.0; + } + else { + windowWeightHeight = (1.0 - rowPercentTotal) * windowHeight; + } + } + for (int32_t i = 0; i < numRows; i++) { + int32_t h = 0; + const TileTabsGridRowColumnElement* e = getRow(i); + switch (e->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + h = (e->getPercentStretch() / 100.0) * windowHeight; + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + if (rowStretchTotal > 0.0) { + h = static_cast((e->getWeightStretch() / rowStretchTotal) + * windowWeightHeight); + } + break; + } + + rowHeightsOut.push_back(h); + } + + /* + * Determine width of each column + */ + float columnPercentTotal = 0.0; + float columnStretchTotal = 0.0; + for (int32_t i = 0; i < numCols; i++) { + const TileTabsGridRowColumnElement* e = getColumn(i); + switch (e->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + columnPercentTotal += (e->getPercentStretch() / 100.0); + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + columnStretchTotal += e->getWeightStretch(); + break; + } + } + + float windowWeightWidth = windowWidth; + if (columnPercentTotal > 0.0) { + if (columnPercentTotal >= 1.0) { + windowWeightWidth = 0.0; + } + else { + windowWeightWidth = (1.0 - columnPercentTotal) * windowWidth; + } + } + + for (int32_t i = 0; i < numCols; i++) { + int32_t w = 0; + const TileTabsGridRowColumnElement* e = getColumn(i); + switch (e->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + w = (e->getPercentStretch() / 100.0) * windowWidth; + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + if (columnStretchTotal > 0.0) { + w = static_cast((e->getWeightStretch() / columnStretchTotal) + * windowWeightWidth); + } + break; + } + columnWidthsOut.push_back(w); + } + } break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + CaretAssert(0); + break; + } + + if ((numRows == static_cast(rowHeightsOut.size())) + && (numCols == static_cast(columnWidthsOut.size()))) { + const bool debugFlag(false); + if (debugFlag) { + /* + * Verify all rows fit within the window + */ + int32_t rowHeightsSum = 0; + for (int32_t i = 0; i < numRows; i++) { + rowHeightsSum += rowHeightsOut[i]; + } + if (rowHeightsSum > windowHeight) { + CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); + } + + /* + * Adjust width of last column so that it does not extend beyond viewport + */ + int32_t columnWidthsSum = 0; + for (int32_t i = 0; i < numCols; i++) { + columnWidthsSum += columnWidthsOut[i]; + } + if (columnWidthsSum > windowWidth) { + CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); + } + + CaretLogFiner("Tile Tabs Row Heights: " + + AString::fromNumbers(rowHeightsOut, ", ")); + CaretLogFiner("Tile Tabs Column Widths: " + + AString::fromNumbers(columnWidthsOut, ", ")); + } + return true; + } + + const QString msg("Row and heights failed rows=" + + AString::number(numRows) + + " rowHeights=" + + AString::number(rowHeightsOut.size()) + + " cols=" + + AString::number(numCols) + + " rowHeights=" + + AString::number(columnWidthsOut.size())); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return false; +} + +/** + * @return Number of rows (all rows including tabs and spacers) + */ +int32_t +TileTabsLayoutGridConfiguration::getNumberOfRows() const +{ + return m_rows.size(); +} + +/** + * Set number of rows. + * + * @param numberOfRows + * New number of rows. + */ +void +TileTabsLayoutGridConfiguration::setNumberOfRows(const int32_t numberOfRows) +{ + m_rows.resize(numberOfRows); +} + +/** + * @return Number of columns (All columns including tabs and spacers) + */ +int32_t +TileTabsLayoutGridConfiguration::getNumberOfColumns() const +{ + return m_columns.size(); +} + +/** + * Set number of rows. + * + * @param numberOfColumns + * New number of rows. + */ +void +TileTabsLayoutGridConfiguration::setNumberOfColumns(const int32_t numberOfColumns) +{ + m_columns.resize(numberOfColumns); +} + +/** + * Get the number of rows and columns containing tab (excludes spacer rows/columns) + * + * @param numberOfRowsOut + * Output with number of rows + * @param numberOfColumnsOut + * Output with number of columns + */ +void +TileTabsLayoutGridConfiguration::getNumberOfRowsAndColumnsContainingTabs(int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut) const +{ + numberOfRowsOut = 0; + numberOfColumnsOut = 0; + + for (const auto& row : m_rows) { + switch (row.getContentType()) { + case TileTabsGridRowColumnContentTypeEnum::SPACE: + break; + case TileTabsGridRowColumnContentTypeEnum::TAB: + numberOfRowsOut++; + break; + } + } + + for (const auto& col : m_columns) { + switch (col.getContentType()) { + case TileTabsGridRowColumnContentTypeEnum::SPACE: + break; + case TileTabsGridRowColumnContentTypeEnum::TAB: + numberOfColumnsOut++; + break; + } + } +} + +/** + * Get the number of rows and columns for an automatic layout with the + * given number of tabs. + * @param numberOfTabs + * Number of tabs. + * @param numberOfRowsOut + * Output with number of rows. + * @param numberOfColumnsOut + * Output with number of columns. + */ +void +TileTabsLayoutGridConfiguration::getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, + int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut) +{ + CaretAssert(numberOfTabs >= 0); + + numberOfRowsOut = (int)std::sqrt((double)numberOfTabs); + numberOfColumnsOut = numberOfRowsOut; + int32_t row2 = numberOfRowsOut * numberOfRowsOut; + if (row2 < numberOfTabs) { + numberOfColumnsOut++; + } + if ((numberOfRowsOut * numberOfColumnsOut) < numberOfTabs) { + numberOfRowsOut++; + } +} + +/** + * @return True if the centering correction is enabled. + */ +bool +TileTabsLayoutGridConfiguration::isCenteringCorrectionEnabled() const +{ + return m_centeringCorrectionEnabled; +} + +/** + * Set the enabled status of the centering correction + * + * @param status + * New status for enabling the centering correction + */ +void +TileTabsLayoutGridConfiguration::setCenteringCorrectionEnabled(const bool status) +{ + m_centeringCorrectionEnabled = status; +} + + +/** + * Updates the number of rows and columns for the automatic configuration + * based upon the number of tabs. + * + * Since screen width typically exceeds height, ensure the number of + * columns is always greater than the number of rows. + */ +void +TileTabsLayoutGridConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs) +{ + int32_t numRows(0), numCols(0); + getRowsAndColumnsForNumberOfTabs(numberOfTabs, + numRows, + numCols); + setNumberOfRows(numRows); + setNumberOfColumns(numCols); +} + +/** + * Encode the configuration in XML. + * + * @param xmlTextOut + * Contains XML representation of configuration. + */ +void +TileTabsLayoutGridConfiguration::encodeInXMLString(AString& xmlTextOut) const +{ + xmlTextOut = encodeVersionInXML(2); +} + +/** + * @return Encoded tile tabs configuration in XML + * using the give XML version of TileTabsLayoutGridConfiguration. + */ +AString +TileTabsLayoutGridConfiguration::encodeVersionInXML(const int32_t versionNumber) const +{ + AString s; + + switch (versionNumber) { + case 1: + s = encodeInXMLWithStreamWriterVersionOne(); + break; + case 2: + s = encodeInXMLWithStreamWriterVersionTwo(); + break; + default: + CaretAssertMessage(0, "Requested invalid version=" + AString::number(versionNumber)); + break; + } + + return s; +} + + +/** + * @return Encoded tile tabs configuration in XML with Stream Writer + */ +AString +TileTabsLayoutGridConfiguration::encodeInXMLWithStreamWriterVersionOne() const +{ + AString xmlString; + QXmlStreamWriter writer(&xmlString); + writer.setAutoFormatting(true); + + writer.writeStartElement(s_v1_rootTagName); + + writer.writeStartElement(s_v1_versionTagName); + writer.writeAttribute(s_v1_versionNumberAttributeName, "1"); + writer.writeEndElement(); + + writer.writeTextElement(s_nameTagName, getName()); + writer.writeTextElement(s_uniqueIdentifierTagName, getUniqueIdentifier()); + + const int32_t numberOfRows = getNumberOfRows(); + writer.writeStartElement(s_v1_rowStretchFactorsTagName); + writer.writeAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName, AString::number(numberOfRows)); + writer.writeAttribute(s_v1_rowStretchFactorsTotalCountAttributeName, AString::number(numberOfRows)); + std::vector rowStretchFactors; + for (const auto& e : m_rows) { + rowStretchFactors.push_back(e.getWeightStretch()); + } + writer.writeCharacters(AString::fromNumbers(rowStretchFactors, " ")); + writer.writeEndElement(); + + const int32_t numberOfColumns = getNumberOfColumns(); + writer.writeStartElement(s_v1_columnStretchFactorsTagName); + writer.writeAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName, AString::number(numberOfColumns)); + writer.writeAttribute(s_v1_columnStretchFactorsTotalCountAttributeName, AString::number(numberOfColumns)); + std::vector columnStretchFactors; + for (const auto& e : m_columns) { + columnStretchFactors.push_back(e.getWeightStretch()); + } + writer.writeCharacters(AString::fromNumbers(columnStretchFactors, " ")); + writer.writeEndElement(); + + writer.writeEndElement(); + + return xmlString; +} + +/** + * @return Encoded tile tabs configuration in XML with Stream Writer + */ +AString +TileTabsLayoutGridConfiguration::encodeInXMLWithStreamWriterVersionTwo() const +{ + AString xmlString; + QXmlStreamWriter writer(&xmlString); + writer.setAutoFormatting(true); + + writer.writeStartElement(s_v2_rootTagName); + writer.writeAttribute(s_v2_versionAttributeName, "2"); + + writer.writeTextElement(s_nameTagName, getName()); + writer.writeTextElement(s_uniqueIdentifierTagName, getUniqueIdentifier()); + writer.writeTextElement(s_v2_centeringCorrectionName, AString::fromBool(m_centeringCorrectionEnabled)); + + encodeRowColumnElement(writer, s_v2_columnsTagName, m_columns); + encodeRowColumnElement(writer, s_v2_rowsTagName, m_rows); + + writer.writeEndElement(); + + return xmlString; +} + +/** + * Encode a vector of elements into xml. + * + * @param writer + * The XML stream writer. + * @param tagName + * Tag name for enclosing the elements. + * @param elements + * Vector of elements added to XML. + */ +void +TileTabsLayoutGridConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer, + const AString tagName, + const std::vector& elements) const +{ + writer.writeStartElement(tagName); + + for (const auto& e : elements) { + writer.writeStartElement(s_v2_elementTagName); + writer.writeAttribute(s_v2_contentTypeAttributeName, TileTabsGridRowColumnContentTypeEnum::toName(e.getContentType())); + writer.writeAttribute(s_v2_stretchTypeAttributeName, TileTabsGridRowColumnStretchTypeEnum::toName(e.getStretchType())); + writer.writeAttribute(s_v2_percentStretchAttributeName, AString::number(e.getPercentStretch(), 'f', 2)); + writer.writeAttribute(s_v2_weightStretchAttributeName, AString::number(e.getWeightStretch(), 'f', 2)); + writer.writeEndElement(); + } + + writer.writeEndElement(); +} + +/** + * Decode the configuration using the given XML stream reader and root element + * If there is an error, xml.raiseError() should be used to specify the error + * and caller of this method can test for the error using xml.isError(). + * + * @param xml + * The XML stream reader. + * @param rootElement + * The root element. + */ +void +TileTabsLayoutGridConfiguration::decodeFromXMLString(QXmlStreamReader& xml, + const AString& rootElementText) +{ + CaretAssert( ! rootElementText.isEmpty()); + + m_centeringCorrectionEnabled = false; + + if (rootElementText == s_v1_rootTagName) { + decodeFromXMLWithStreamReaderVersionOne(xml); + } + else if (rootElementText == s_v2_rootTagName) { + /* + * Version 2 uses a different root tag than version 1. The reason is that + * the older code for decoding from XML will throw an exception if it + * encounters invalid elements or the version number is invalid. The problem + * is that the exception is not caught and wb_view will terminate. + */ + QString versionNumberText("Unknown"); + const QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_v2_versionAttributeName)) { + versionNumberText = atts.value(s_v2_versionAttributeName).toString(); + } + + if (versionNumberText == "2") { + decodeFromXMLWithStreamReaderVersionTwo(xml); + } + else { + xml.raiseError("TileTabsLayoutGridConfiguration invalid version=" + + versionNumberText); + } + } + else { + xml.raiseError("TileTabsLayoutGridConfiguration first element is " + + xml.name().toString() + + " but should be " + + s_v1_rootTagName + + " or " + + s_v2_rootTagName); + } +} + +/** + * Decode Version One of the tile tabs configuration from XML with stream reader. + * + * @param xml + * Stream XML parser. + */ +void +TileTabsLayoutGridConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml) +{ + std::set invalidElements; + + AString name; + AString uniqueID; + std::vector rowStretchFactors; + std::vector columnStretchFactors; + int32_t numberOfRows(0); + int32_t numberOfColumns(0); + + QString message; + + while ( ! xml.atEnd()) { + xml.readNext(); + + if (xml.isStartElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_v1_versionTagName) { + /* ignore */ + } + else if (tagName == s_nameTagName) { + name = xml.readElementText(); + } + else if (tagName == s_uniqueIdentifierTagName) { + uniqueID = xml.readElementText(); + } + else if (tagName == s_v1_rowStretchFactorsTagName) { + QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName)) { + numberOfRows = atts.value(s_v1_rowStretchFactorsSelectedCountAttributeName).toInt(); + } + + AString::toNumbers(xml.readElementText(), rowStretchFactors); + } + else if (tagName == s_v1_columnStretchFactorsTagName) { + QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName)) { + numberOfColumns = atts.value(s_v1_columnStretchFactorsSelectedCountAttributeName).toInt(); + } + AString::toNumbers(xml.readElementText(), columnStretchFactors); + } + else { + invalidElements.insert(tagName.toString()); + xml.skipCurrentElement(); + } + } + } + + static int32_t missingNameCounter = 1; + if (name.isEmpty()) { + name = ("Config_V1_" + + AString::number(missingNameCounter)); + missingNameCounter++; + } + if (uniqueID.isEmpty()) { + uniqueID = SystemUtilities::createUniqueID(); + } + if (rowStretchFactors.empty()) { + message.append(s_v1_rowStretchFactorsTagName + + " not found or invalid. "); + } + if (columnStretchFactors.empty()) { + message.append(s_v1_columnStretchFactorsTagName + + " not found or invalid. "); + } + if (numberOfRows <= 0) { + message.append(s_v1_rowStretchFactorsTagName + + " attribute " + + s_v1_rowStretchFactorsSelectedCountAttributeName + + " is missing or invalid." ); + } + if (numberOfRows <= 0) { + message.append(s_v1_columnStretchFactorsTagName + + " attribute " + + s_v1_columnStretchFactorsSelectedCountAttributeName + + " is missing or invalid." ); + } + + if ( ! invalidElements.empty()) { + /* + * If invalid elements were encountered, don't throw + */ + AString msg("Invalid element(s) ignored: "); + for (const auto& s : invalidElements) { + msg.append(s + " "); + } + CaretLogWarning(msg); + } + + if (message.isEmpty()) { + setName(name); + setUniqueIdentifierProtected(uniqueID); + + m_rows.clear(); + m_columns.clear(); + + for (int32_t i = 0; i < numberOfRows; i++) { + TileTabsGridRowColumnElement element; + CaretAssertVectorIndex(rowStretchFactors, i); + element.setWeightStretch(rowStretchFactors[i]); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); + m_rows.push_back(element); + } + CaretAssert(numberOfRows == static_cast(m_rows.size())); + + for (int32_t i = 0; i < numberOfColumns; i++) { + TileTabsGridRowColumnElement element; + CaretAssertVectorIndex(columnStretchFactors, i); + element.setWeightStretch(columnStretchFactors[i]); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); + m_columns.push_back(element); + } + CaretAssert(numberOfColumns == static_cast(m_columns.size())); + } + else { + xml.raiseError(message); + } +} + +/** + * Decode Version Two of the tile tabs configuration from XML with stream reader. + * + * @param xml + * Stream XML parser. + */ +void +TileTabsLayoutGridConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml) +{ + m_rows.clear(); + m_columns.clear(); + setName(""); + setUniqueIdentifierProtected(""); + + std::set invalidElements; + + AString name; + AString uniqueID; + + QString message; + + enum class ReadMode { + OTHER, + COLUMNS, + ROWS + }; + ReadMode readMode = ReadMode::OTHER; + + AString centeringCorrectionTextString; + + while ( ! xml.atEnd()) { + xml.readNext(); + + if (xml.isStartElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_nameTagName) { + name = xml.readElementText(); + } + else if (tagName == s_uniqueIdentifierTagName) { + uniqueID = xml.readElementText(); + } + else if (tagName == s_v2_columnsTagName) { + readMode = ReadMode::COLUMNS; + } + else if (tagName == s_v2_rowsTagName) { + readMode = ReadMode::ROWS; + } + else if (tagName == s_v2_centeringCorrectionName) { + centeringCorrectionTextString = xml.readElementText(); + } + else if (tagName == s_v2_elementTagName) { + switch (readMode) { + case ReadMode::OTHER: + CaretAssert(0); + break; + case ReadMode::COLUMNS: + { + AString errorMessage; + TileTabsGridRowColumnElement e; + if (decodeRowColumnElement(xml, e, errorMessage)) { + m_columns.push_back(e); + } + else { + message.append(errorMessage); + } + } + break; + case ReadMode::ROWS: + { + AString errorMessage; + TileTabsGridRowColumnElement e; + if (decodeRowColumnElement(xml, e, errorMessage)) { + m_rows.push_back(e); + } + else { + message.append(errorMessage); + } + } + break; + } + } + else { + invalidElements.insert(tagName.toString()); + xml.skipCurrentElement(); + } + } + else if (xml.isEndElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_v2_columnsTagName) { + readMode = ReadMode::OTHER; + } + else if (tagName == s_v2_rowsTagName) { + readMode = ReadMode::OTHER; + } + } + } + + static int32_t missingNameCounter = 1; + if (name.isEmpty()) { + name = ("Config_" + + AString::number(missingNameCounter)); + missingNameCounter++; + } + if (uniqueID.isEmpty()) { + uniqueID = SystemUtilities::createUniqueID(); + } + + if ( ! invalidElements.empty()) { + /* + * If invalid elements were encountered, don't throw + */ + AString msg("Invalid element(s) ignored: "); + for (const auto& s : invalidElements) { + msg.append(s + " "); + } + CaretLogWarning(msg); + } + + if (message.isEmpty()) { + setName(name); + setUniqueIdentifierProtected(uniqueID); + + /* + * Only set centering correction if it is found. This allows usage + * of the default value in the event this is not found in the XML. + */ + if ( ! centeringCorrectionTextString.isEmpty()) { + m_centeringCorrectionEnabled = centeringCorrectionTextString.toBool(); + } + + } + else { + xml.raiseError(message); + } +} + +/** + * Decode elements in a row or column. + * + * @param reader + * The XML stream reader. + * @param element + * row/column element that is read + * @param errorMessageOut + * Contains error information. + * @return + * True if read successfully, else false. + */ +bool +TileTabsLayoutGridConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, + TileTabsGridRowColumnElement& element, + AString& errorMessageOut) +{ + const QXmlStreamAttributes atts = reader.attributes(); + + errorMessageOut.clear(); + + if (atts.hasAttribute(s_v2_contentTypeAttributeName)) { + bool validFlag(false); + const AString s = atts.value(s_v2_contentTypeAttributeName).toString(); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::fromName(s, &validFlag)); + if ( ! validFlag) { + errorMessageOut.append("Content type \"" + s + "\" is not valid. "); + } + } + else { + errorMessageOut.append("Content type is missing. "); + } + + if (atts.hasAttribute(s_v2_stretchTypeAttributeName)) { + bool validFlag(false); + const AString s = atts.value(s_v2_stretchTypeAttributeName).toString(); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::fromName(s, &validFlag)); + if ( ! validFlag) { + errorMessageOut.append("Stretch type \"" + s + "\" is not valid. "); + } + } + else { + errorMessageOut.append("Stretch type is missing. "); + } + + if (atts.hasAttribute(s_v2_percentStretchAttributeName)) { + const float f = atts.value(s_v2_percentStretchAttributeName).toFloat(); + if ((f >= 0.0) && (f < 100.0)) { + element.setPercentStretch(f); + } + else { + errorMessageOut.append("Stretch percentage=" + AString::number(f) + " is invalid."); + } + } + else { + errorMessageOut.append("Stretch percentage is missing. "); + } + + if (atts.hasAttribute(s_v2_weightStretchAttributeName)) { + const float f = atts.value(s_v2_weightStretchAttributeName).toFloat(); + if ((f >= 0.0) && (f < 100.0)) { + element.setWeightStretch(f); + } + else { + errorMessageOut.append("Stretch weight=" + AString::number(f) + " is invalid."); + } + } + else { + errorMessageOut.append("Stretch weight is missing. "); + } + + if (errorMessageOut.isEmpty()) { + return true; + } + return false; +} + +/** + * @return String version of an instance. + */ +AString +TileTabsLayoutGridConfiguration::toString() const +{ + AString s = TileTabsLayoutBaseConfiguration::toString(); + if ( ! s.isEmpty()) { + s.append("\n"); + } + s.append("TileTabsLayoutGridConfiguration:"); + s.append("\n"); + + int32_t indx(0); + for (const auto& item : m_columns) { + s.append(" Column " + AString::number(indx) + ": " + item.toString() + "\n"); + indx++; + } + indx = 0; + for (const auto& item : m_rows) { + s.append(" Row " + AString::number(indx) + ": " + item.toString() + "\n"); + indx++; + } + + return s; +} + +/** + * Cast to a grid configuration (avoids dynamic_cast that can be slow) + * + * @return Pointer to grid configuration or NULL if not a grid configuration. + */ +TileTabsLayoutGridConfiguration* +TileTabsLayoutGridConfiguration::castToGridConfiguration() +{ + return this; +} + +/** + * Cast to a grid configuration (avoids dynamic_cast that can be slow) + * + * @return Pointer to grid configuration or NULL if not a grid configuration. + */ +const TileTabsLayoutGridConfiguration* +TileTabsLayoutGridConfiguration::castToGridConfiguration() const +{ + return this; +} + +/** + * @return Status of custom default flag (used by BrowserWindowContent) + */ +bool +TileTabsLayoutGridConfiguration::isCustomDefaultFlag() const +{ + return m_customDefaultFlag; +} + +/** + * Set status of custom default flag (used by BrowserWindowContent) + * + * @param defaultFlag + * New status of custom default flag + */ +void +TileTabsLayoutGridConfiguration::setCustomDefaultFlag(const bool defaultFlag) +{ + m_customDefaultFlag = defaultFlag; +} diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutGridConfiguration.h connectome-workbench-1.5.0/src/Common/TileTabsLayoutGridConfiguration.h --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutGridConfiguration.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutGridConfiguration.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,197 @@ +#ifndef __TILE_TABS_LAYOUT_GRID_CONFIGURATION_H__ +#define __TILE_TABS_LAYOUT_GRID_CONFIGURATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretException.h" +#include "TileTabsLayoutBaseConfiguration.h" +#include "TileTabsLayoutConfigurationTypeEnum.h" +#include "TileTabsGridRowColumnElement.h" + +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace caret { + + class TileTabsLayoutGridConfiguration : public TileTabsLayoutBaseConfiguration { + + public: + static TileTabsLayoutGridConfiguration* newInstanceAutomaticGrid(); + + static TileTabsLayoutGridConfiguration* newInstanceCustomGrid(); + + TileTabsLayoutGridConfiguration(const TileTabsLayoutConfigurationTypeEnum::Enum gridConfigType); + + virtual ~TileTabsLayoutGridConfiguration(); + + TileTabsLayoutGridConfiguration(const TileTabsLayoutGridConfiguration& obj); + + TileTabsLayoutGridConfiguration& operator=(const TileTabsLayoutGridConfiguration& obj); + + virtual void copy(const TileTabsLayoutBaseConfiguration& rhs) override; + + virtual TileTabsLayoutBaseConfiguration* newCopyWithNewUniqueIdentifier() const override; + + virtual int32_t getNumberOfTabs() const override; + + void getNumberOfRowsAndColumnsContainingTabs(int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut) const; + + bool getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, + const int32_t windowHeight, + const int32_t numberOfModelsToDraw, + const TileTabsLayoutConfigurationTypeEnum::Enum configurationMode, + std::vector& rowHeightsOut, + std::vector& columnWidthsOut); + + int32_t getNumberOfRows() const; + + void setNumberOfRows(const int32_t numberOfRows); + + int32_t getNumberOfColumns() const; + + void setNumberOfColumns(const int32_t numberOfColumns); + + TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex); + + const TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex) const; + + TileTabsGridRowColumnElement* getRow(const int32_t rowIndex); + + const TileTabsGridRowColumnElement* getRow(const int32_t rowIndex) const; + + void updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs); + + bool isCenteringCorrectionEnabled() const; + + void setCenteringCorrectionEnabled(const bool status); + + virtual AString toString() const override; + + static void getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, + int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut); + AString encodeVersionInXML(const int32_t versionNumber) const; + + virtual TileTabsLayoutGridConfiguration* castToGridConfiguration() override; + + virtual const TileTabsLayoutGridConfiguration* castToGridConfiguration() const override; + + bool isCustomDefaultFlag() const; + + void setCustomDefaultFlag(const bool defaultFlag); + + // ADD_NEW_METHODS_HERE + + protected: + virtual void decodeFromXMLString(QXmlStreamReader& xml, + const AString& rootElement) override; + + virtual void encodeInXMLString(AString& xmlTextOut) const override; + + private: + void copyHelperTileTabsLayoutGridConfiguration(const TileTabsLayoutGridConfiguration& obj); + + void decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml); + + void decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml); + + AString encodeInXMLWithStreamWriterVersionOne() const; + + AString encodeInXMLWithStreamWriterVersionTwo() const; + + void encodeRowColumnElement(QXmlStreamWriter& writer, + const AString tagName, + const std::vector& elements) const; + + bool decodeRowColumnElement(QXmlStreamReader& reader, + TileTabsGridRowColumnElement& element, + AString& errorMessageOut); + + void initialize(); + + // ADD_NEW_MEMBERS_HERE + + std::vector m_columns; + + std::vector m_rows; + + /** Used to track default custom config. Neither read nor/written to scene or preferences */ + bool m_customDefaultFlag = false; + + bool m_centeringCorrectionEnabled = false; + + static const AString s_nameTagName; + static const AString s_uniqueIdentifierTagName; + + static const AString s_v1_rootTagName; + static const AString s_v1_versionTagName; + static const AString s_v1_versionNumberAttributeName; + static const AString s_v1_columnStretchFactorsTagName; + static const AString s_v1_columnStretchFactorsSelectedCountAttributeName; + static const AString s_v1_columnStretchFactorsTotalCountAttributeName; + static const AString s_v1_rowStretchFactorsTagName; + static const AString s_v1_rowStretchFactorsSelectedCountAttributeName; + static const AString s_v1_rowStretchFactorsTotalCountAttributeName; + + static const AString s_v2_rootTagName; + static const AString s_v2_versionAttributeName; + static const AString s_v2_columnsTagName; + static const AString s_v2_contentTypeAttributeName; + static const AString s_v2_elementTagName; + static const AString s_v2_percentStretchAttributeName; + static const AString s_v2_rowsTagName; + static const AString s_v2_stretchTypeAttributeName; + static const AString s_v2_weightStretchAttributeName; + static const AString s_v2_centeringCorrectionName; + + + friend class TileTabsLayoutBaseConfiguration; + }; + +#ifdef __TILE_TABS_LAYOUT_GRID_CONFIGURATION_DECLARE__ + const AString TileTabsLayoutGridConfiguration::s_nameTagName = "Name"; + const AString TileTabsLayoutGridConfiguration::s_uniqueIdentifierTagName = "UniqueIdentifier"; + + const AString TileTabsLayoutGridConfiguration::s_v1_rootTagName = "TileTabsConfiguration"; + const AString TileTabsLayoutGridConfiguration::s_v1_versionTagName = "Version"; + const AString TileTabsLayoutGridConfiguration::s_v1_versionNumberAttributeName = "Number"; + const AString TileTabsLayoutGridConfiguration::s_v1_columnStretchFactorsTagName = "ColumnStretchFactors"; + const AString TileTabsLayoutGridConfiguration::s_v1_columnStretchFactorsSelectedCountAttributeName = "SelectedRowCount"; + const AString TileTabsLayoutGridConfiguration::s_v1_columnStretchFactorsTotalCountAttributeName = "TotalRowCount"; + const AString TileTabsLayoutGridConfiguration::s_v1_rowStretchFactorsTagName = "RowStretchFactors"; + const AString TileTabsLayoutGridConfiguration::s_v1_rowStretchFactorsSelectedCountAttributeName = "SelectedColumnCount"; + const AString TileTabsLayoutGridConfiguration::s_v1_rowStretchFactorsTotalCountAttributeName = "TotalColumnCount"; + + const AString TileTabsLayoutGridConfiguration::s_v2_rootTagName = "TileTabsConfigurationTwo"; + const AString TileTabsLayoutGridConfiguration::s_v2_versionAttributeName = "Version"; + const AString TileTabsLayoutGridConfiguration::s_v2_columnsTagName = "Columns"; + const AString TileTabsLayoutGridConfiguration::s_v2_contentTypeAttributeName = "ContentType"; + const AString TileTabsLayoutGridConfiguration::s_v2_elementTagName = "Element"; + const AString TileTabsLayoutGridConfiguration::s_v2_percentStretchAttributeName = "PercentStretch"; + const AString TileTabsLayoutGridConfiguration::s_v2_rowsTagName = "Rows"; + const AString TileTabsLayoutGridConfiguration::s_v2_stretchTypeAttributeName = "StretchType"; + const AString TileTabsLayoutGridConfiguration::s_v2_weightStretchAttributeName = "WeightStretch"; + const AString TileTabsLayoutGridConfiguration::s_v2_centeringCorrectionName = "CenteringCorrection"; +#endif // __TILE_TABS_LAYOUT_GRID_CONFIGURATION_DECLARE__ + +} // namespace +#endif //__TILE_TABS_LAYOUT_GRID_CONFIGURATION_H__ diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutManualConfiguration.cxx connectome-workbench-1.5.0/src/Common/TileTabsLayoutManualConfiguration.cxx --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutManualConfiguration.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutManualConfiguration.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,581 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __TILE_TABS_LAYOUT_MANUAL_CONFIGURATION_DECLARE__ +#include "TileTabsLayoutManualConfiguration.h" +#undef __TILE_TABS_LAYOUT_MANUAL_CONFIGURATION_DECLARE__ + +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "SystemUtilities.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "TileTabsBrowserTabGeometry.h" + +using namespace caret; + +/** + * \class caret::TileTabsLayoutManualConfiguration + * \brief Contains a manual layout for tabs + * \ingroup Common + */ + +/** + * Constructor. + */ +TileTabsLayoutManualConfiguration::TileTabsLayoutManualConfiguration() +: TileTabsLayoutBaseConfiguration(TileTabsLayoutConfigurationTypeEnum::MANUAL) +{ + initializeManualConfiguration(); +} + +/** + * Destructor. + */ +TileTabsLayoutManualConfiguration::~TileTabsLayoutManualConfiguration() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +TileTabsLayoutManualConfiguration::TileTabsLayoutManualConfiguration(const TileTabsLayoutManualConfiguration& obj) +: TileTabsLayoutBaseConfiguration(obj) +{ + this->copyHelperTileTabsLayoutManualConfiguration(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +TileTabsLayoutManualConfiguration& +TileTabsLayoutManualConfiguration::operator=(const TileTabsLayoutManualConfiguration& obj) +{ + if (this != &obj) { + TileTabsLayoutBaseConfiguration::operator=(obj); + this->copyHelperTileTabsLayoutManualConfiguration(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +TileTabsLayoutManualConfiguration::copyHelperTileTabsLayoutManualConfiguration(const TileTabsLayoutManualConfiguration& obj) +{ + initializeManualConfiguration(); + copyHelperTileTabsLayoutBaseConfiguration(obj); + m_windowAnnotationsStackingOrder = obj.m_windowAnnotationsStackingOrder; + + for (const auto& ti : obj.m_tabInfo) { + TileTabsBrowserTabGeometry* tabInfo = new TileTabsBrowserTabGeometry(*ti); + addTabInfo(tabInfo); + } +} + +/** + * Copy the given configuration to "this" configuration. If given configuration + * does not cast to "this class type" log a warning and do not copy. + * Name property is not copied. + * + * @param rhs + * Configuration to copy. + */ +void +TileTabsLayoutManualConfiguration::copy(const TileTabsLayoutBaseConfiguration& rhs) +{ + const TileTabsLayoutManualConfiguration* manualConfig = rhs.castToManualConfiguration(); + if (manualConfig != NULL) { + AString savedName = getName(); + copyHelperTileTabsLayoutManualConfiguration(*manualConfig); + setName(savedName); + } + else { + CaretLogSevere("Attempt to copy layout configuration " + + rhs.toString() + + " to " + + toString()); + } +} + +/** + * Copy this instance and give it a new unique identifier. + * Note that copy constructor does not create a new unique identifier. + * + * @return The new Copy. + */ +TileTabsLayoutBaseConfiguration* +TileTabsLayoutManualConfiguration::newCopyWithNewUniqueIdentifier() const +{ + TileTabsLayoutBaseConfiguration* newCopy = new TileTabsLayoutManualConfiguration(*this); + CaretAssert(newCopy); + return newCopy; +} + +/** + * Create a manual layout from the given grid layout using the given tab indices + * + * @param gridLayout + * The grid layout + * @param gridMode + * The grid mode + * @param tabIndices + * Indices of tabs + */ +TileTabsLayoutManualConfiguration* +TileTabsLayoutManualConfiguration::newInstanceFromGridLayout(TileTabsLayoutGridConfiguration* gridLayout, + const TileTabsLayoutConfigurationTypeEnum::Enum gridMode, + const std::vector& tabIndices) +{ + CaretAssert(gridLayout); + + const int32_t numTabs = static_cast(tabIndices.size()); +// if (numTabs <= 0) { +// return NULL; +// } + + const int32_t windowWidth(10000); + const int32_t windowHeight(10000); + std::vector rowHeights; + std::vector columnWidths; + + if (gridLayout->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, + windowHeight, + numTabs, + gridMode, + rowHeights, + columnWidths)) { + const int32_t numRows = static_cast(rowHeights.size()); + const int32_t numCols = static_cast(columnWidths.size()); + + TileTabsLayoutManualConfiguration* manualLayout = new TileTabsLayoutManualConfiguration(); + manualLayout->setName("From row:" + + AString::number(rowHeights.size()) + + " col:" + + AString::number(columnWidths.size())); + + int32_t tabCounter(0); + float yBottom(windowHeight); + for (int32_t i = 0; i < numRows; i++) { + CaretAssertVectorIndex(rowHeights, i); + const float height = rowHeights[i]; + yBottom -= height; + + switch (gridLayout->getRow(i)->getContentType()) { + case TileTabsGridRowColumnContentTypeEnum::SPACE: + break; + case TileTabsGridRowColumnContentTypeEnum::TAB: + { + float xLeft(0.0); + for (int32_t j = 0; j < numCols; j++) { + CaretAssertVectorIndex(columnWidths, j); + const float width = columnWidths[j]; + + switch (gridLayout->getColumn(j)->getContentType()) { + case TileTabsGridRowColumnContentTypeEnum::SPACE: + break; + case TileTabsGridRowColumnContentTypeEnum::TAB: + { + int32_t tabIndex(-1); + if (tabCounter < numTabs) { + CaretAssertVectorIndex(tabIndices, tabCounter); + tabIndex = tabIndices[tabCounter]; + } + + const float centerX = xLeft + (width / 2.0); + const float centerY = yBottom + (height / 2.0); + TileTabsBrowserTabGeometry* tabInfo = new TileTabsBrowserTabGeometry(tabIndex); + tabInfo->setCenterX((centerX / windowWidth) * 100.0f); + tabInfo->setCenterY((centerY / windowHeight) * 100.0f); + tabInfo->setWidth((width / windowWidth) * 100.0f); + tabInfo->setHeight((height / windowHeight) * 100.0f); + tabInfo->setStackingOrder(tabCounter + 1); + tabInfo->setBackgroundType(TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG); + + manualLayout->addTabInfo(tabInfo); + + tabCounter++; + } + break; + } + + xLeft += width; + } + } + break; + } + } + + return manualLayout; + } + + return NULL; +} + + +/** + * Add the tab info to this configuration. This configuration will take + * ownership of the tab including destruction of it. + * + * @param tabInfo + * Tab info to add. + */ +void +TileTabsLayoutManualConfiguration::addTabInfo(TileTabsBrowserTabGeometry* tabInfo) +{ + std::unique_ptr ptr(tabInfo); + m_tabInfo.push_back(std::move(ptr)); +} + +/** + * Initialize a configuration + */ +void +TileTabsLayoutManualConfiguration::initializeManualConfiguration() +{ + m_windowAnnotationsStackingOrder = -1000; + m_tabInfo.clear(); +} + +/** + * @return Number of tab info in this configuration + */ +int32_t +TileTabsLayoutManualConfiguration::getNumberOfTabs() const +{ + return m_tabInfo.size(); +} + +/** + * @return The tab info at the given index + * + * @param index + * Index of the tab info + */ +TileTabsBrowserTabGeometry* +TileTabsLayoutManualConfiguration::getTabInfo(const int32_t index) +{ + CaretAssertVectorIndex(m_tabInfo, index); + return m_tabInfo[index].get(); +} + +/** + * @return The tab info at the given index + * + * @param index + * Index of the tab info + */ +const TileTabsBrowserTabGeometry* +TileTabsLayoutManualConfiguration::getTabInfo(const int32_t index) const +{ + CaretAssertVectorIndex(m_tabInfo, index); + return m_tabInfo[index].get(); +} + +/** + * @return The window annotations stacking order for a manual tile tabs layout + */ +int32_t +TileTabsLayoutManualConfiguration::getWindowAnnotationsStackingOrder() const +{ + return m_windowAnnotationsStackingOrder; +} + +/** + * Set the window annotations stacking order for a manual tile tabs layout + * @param stackingOrder + * The new stacking order + */ +void +TileTabsLayoutManualConfiguration::setWindowAnnotationsStackingOrder(const int32_t stackingOrder) +{ + m_windowAnnotationsStackingOrder = stackingOrder; +} + +/** + * @return String version of an instance. + */ +AString +TileTabsLayoutManualConfiguration::toString() const +{ + QString str(TileTabsLayoutBaseConfiguration::toString()); + + QTextStream ts(&str); + ts << "\n" << "m_windowAnnotationsStackingOrder=" << m_windowAnnotationsStackingOrder << "\n"; + + ts << "\n" << "TileTabsLayoutManualConfiguration: " << "\n"; + + for (const auto& ti : m_tabInfo) { + ts << ti->toString() << "\n"; + } + + return str; +} + + +/** + * Encode the configuration in XML. + * + * @param xmlTextOut + * Contains XML representation of configuration. + */ +void +TileTabsLayoutManualConfiguration::encodeInXMLString(AString& xmlTextOut) const +{ + xmlTextOut.clear(); + + QXmlStreamWriter writer(&xmlTextOut); + writer.setAutoFormatting(true); + + writer.writeStartElement(s_rootElementName); + writer.writeAttribute(s_rootElementAttributeName, + getName()); + writer.writeAttribute(s_rootElementAttributeVersion, + s_rootElementAttributeValueVersionOne); + writer.writeAttribute(s_rootElementAttributeUniqueID, + getUniqueIdentifier()); + + writer.writeStartElement(s_windowInfoElementName); + writer.writeAttribute(s_windowInfoAttributeStackingOrder, + AString::number(m_windowAnnotationsStackingOrder)); + writer.writeEndElement(); + + for (const auto& tabInfo : m_tabInfo) { + writer.writeStartElement(s_tabInfoElementName); + writer.writeAttribute(s_tabInfoAttributeDisplayStatus, + AString::fromBool(tabInfo->isDisplayed())); + writer.writeAttribute(s_tabInfoAttributeTabIndex, + AString::number(tabInfo->getTabIndex())); + writer.writeAttribute(s_tabInfoAttributeMinX, + AString::number(tabInfo->getMinX(), 'f', 2)); + writer.writeAttribute(s_tabInfoAttributeMaxX, + AString::number(tabInfo->getMaxX(), 'f', 2)); + writer.writeAttribute(s_tabInfoAttributeMinY, + AString::number(tabInfo->getMinY(), 'f', 2)); + writer.writeAttribute(s_tabInfoAttributeMaxY, + AString::number(tabInfo->getMaxY(), 'f', 2)); + writer.writeAttribute(s_tabInfoAttributeStackingOrder, + AString::number(tabInfo->getStackingOrder())); + writer.writeAttribute(s_tabInfoAttributeBackground, + TileTabsLayoutBackgroundTypeEnum::toName(tabInfo->getBackgroundType())); + writer.writeEndElement(); + } + + writer.writeEndElement(); +} + +/** + * Decode the configuration using the given XML stream reader and root element + * If there is an error, xml.raiseError() should be used to specify the error + * and caller of this method can test for the error using xml.isError(). + * + * @param xml + * The XML stream reader. + * @param rootElement + * The root element. + */ +void +TileTabsLayoutManualConfiguration::decodeFromXMLString(QXmlStreamReader& xml, + const AString& rootElementText) +{ + initializeManualConfiguration(); + + static int32_t invalidNameCounter(1); + + CaretAssert( ! rootElementText.isEmpty()); + + if (rootElementText != s_rootElementName) { + xml.raiseError("TileTabsLayoutManualConfiguration first element is " + + rootElementText + + " but should be " + + s_rootElementName); + return; + } + + AString errorString; + const QXmlStreamAttributes rootAttributes = xml.attributes(); + + const AString version = rootAttributes.value(s_rootElementAttributeVersion).toString(); + if (version != s_rootElementAttributeValueVersionOne) { + errorString.appendWithNewLine("Invalid version \"" + version + "\""); + } + + AString name = rootAttributes.value(s_rootElementAttributeName).toString(); + if (name.isEmpty()) { + name = ("Tabs_" + AString::number(invalidNameCounter)); + invalidNameCounter++; + CaretLogWarning("Tile Tabs Manual Configuration is missing name and has been assigned name \"" + + name + + "\""); + } + setName(name); + + AString uniqueID = rootAttributes.value(s_rootElementAttributeUniqueID).toString(); + if (uniqueID.isEmpty()) { + uniqueID = SystemUtilities::createUniqueID(); + CaretLogWarning("Tile Tabs Manual Configuration is missing uniqueID and has been assigned uniqueID \"" + + uniqueID + + "\""); + + } + setUniqueIdentifierProtected(uniqueID); + + if (! errorString.isEmpty()) { + xml.skipCurrentElement(); + xml.raiseError(errorString); + return; + } + + std::set invalidElementNames; + + while ( ! xml.atEnd()) { + xml.readNext(); + + if (xml.isStartElement()) { + const QString elementName(xml.name().toString()); + + if (elementName == s_windowInfoElementName) { + const QXmlStreamAttributes atts = xml.attributes(); + bool stackOrderValidFlag(false); + const int32_t stackOrder = atts.value(s_windowInfoAttributeStackingOrder).toInt(&stackOrderValidFlag); + if (stackOrderValidFlag) { + m_windowAnnotationsStackingOrder = stackOrder; + } + } + else if (elementName == s_tabInfoElementName) { + bool tabIndexValid(false); + bool minXValid(false); + bool maxXValid(false); + bool minYValid(false); + bool maxYValid(false); + bool stackingValid(false); + const QXmlStreamAttributes atts = xml.attributes(); + AString displayStatusText = atts.value(s_tabInfoAttributeDisplayStatus).toString(); + const int32_t tabIndex = atts.value(s_tabInfoAttributeTabIndex).toInt(&tabIndexValid); + const float minX = atts.value(s_tabInfoAttributeMinX).toFloat(&minXValid); + const float maxX = atts.value(s_tabInfoAttributeMaxX).toFloat(&maxXValid); + const float minY = atts.value(s_tabInfoAttributeMinY).toFloat(&minYValid); + const float maxY = atts.value(s_tabInfoAttributeMaxY).toFloat(&maxYValid); + const int32_t stacking = atts.value(s_tabInfoAttributeStackingOrder).toInt(&stackingValid); + const QString backStr = atts.value(s_tabInfoAttributeBackground).toString(); + const TileTabsLayoutBackgroundTypeEnum::Enum backType = TileTabsLayoutBackgroundTypeEnum::fromName(backStr, NULL); + + if (displayStatusText.isEmpty()) { + displayStatusText = "true"; + } + const bool displayStatus = displayStatusText.toBool(); + + if (tabIndexValid + && minXValid + && maxXValid + && minYValid + && maxYValid + && stackingValid) { + TileTabsBrowserTabGeometry* tabInfo = new TileTabsBrowserTabGeometry(tabIndex); + tabInfo->setMinX(minX); + tabInfo->setMaxX(maxX); + tabInfo->setMinY(minY); + tabInfo->setMaxY(maxY); + tabInfo->setDisplayed(displayStatus); + tabInfo->setStackingOrder(stacking); + tabInfo->setBackgroundType(backType); + + addTabInfo(tabInfo); + } + else { + CaretLogWarning("Failed to parse Manual Tile Tabs Configuration from Preferences: " + + xml.tokenString()); + } + } + else { + invalidElementNames.insert(elementName); + } + } + } + + if ( ! invalidElementNames.empty()) { + AString msg("Invalid element(s):"); + for (auto& n : invalidElementNames) { + msg.append(" " + n); + } + CaretLogWarning(msg); + } + + correctOldStackingOrder(); +} + +/** + * Correct old stacking order. Early on, stacking order started at + * zero but was changed to start at one. + */ +void +TileTabsLayoutManualConfiguration::correctOldStackingOrder() +{ + int32_t minimumStackingOrder(1); + for (auto& ti : m_tabInfo) { + if (ti->getStackingOrder() < minimumStackingOrder) { + minimumStackingOrder = ti->getStackingOrder(); + } + } + if (minimumStackingOrder < 1) { + const int32_t offset = 1 - minimumStackingOrder; + for (auto& ti : m_tabInfo) { + ti->setStackingOrder(offset + ti->getStackingOrder()); + } + } +} + +/** + * Cast to a manual configuration (avoids dynamic_cast that can be slow) + * + * @return Pointer to manual configuration or NULL if not a manual configuration. + */ +TileTabsLayoutManualConfiguration* +TileTabsLayoutManualConfiguration::castToManualConfiguration() +{ + return this; +} + +/** + * Cast to a manual configuration (avoids dynamic_cast that can be slow) + * + * @return Pointer to manual configuration or NULL if not a manual configuration. + */ +const TileTabsLayoutManualConfiguration* +TileTabsLayoutManualConfiguration::castToManualConfiguration() const +{ + return this; +} + diff -Nru connectome-workbench-1.4.2/src/Common/TileTabsLayoutManualConfiguration.h connectome-workbench-1.5.0/src/Common/TileTabsLayoutManualConfiguration.h --- connectome-workbench-1.4.2/src/Common/TileTabsLayoutManualConfiguration.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/TileTabsLayoutManualConfiguration.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,144 @@ +#ifndef __TILE_TABS_LAYOUT_MANUAL_CONFIGURATION_H__ +#define __TILE_TABS_LAYOUT_MANUAL_CONFIGURATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "TileTabsLayoutBaseConfiguration.h" +#include "TileTabsLayoutConfigurationTypeEnum.h" + +namespace caret { + + class BrowserTabContent; + class TileTabsLayoutGridConfiguration; + class TileTabsBrowserTabGeometry; + + class TileTabsLayoutManualConfiguration : public TileTabsLayoutBaseConfiguration { + + public: + static TileTabsLayoutManualConfiguration* newInstanceFromGridLayout(TileTabsLayoutGridConfiguration* gridLayout, + const TileTabsLayoutConfigurationTypeEnum::Enum gridMode, + const std::vector& tabIndices); + + TileTabsLayoutManualConfiguration(); + + virtual ~TileTabsLayoutManualConfiguration(); + + TileTabsLayoutManualConfiguration(const TileTabsLayoutManualConfiguration& obj); + + TileTabsLayoutManualConfiguration& operator=(const TileTabsLayoutManualConfiguration& obj); + + virtual void copy(const TileTabsLayoutBaseConfiguration& rhs) override; + + virtual TileTabsLayoutBaseConfiguration* newCopyWithNewUniqueIdentifier() const override; + + virtual AString toString() const override; + + void addTabInfo(TileTabsBrowserTabGeometry* tabInfo); + + virtual int32_t getNumberOfTabs() const override; + + TileTabsBrowserTabGeometry* getTabInfo(const int32_t index); + + const TileTabsBrowserTabGeometry* getTabInfo(const int32_t index) const; + + virtual TileTabsLayoutManualConfiguration* castToManualConfiguration() override; + + virtual const TileTabsLayoutManualConfiguration* castToManualConfiguration() const override; + + int32_t getWindowAnnotationsStackingOrder() const; + + void setWindowAnnotationsStackingOrder(const int32_t stackingOrder); + + void initializeManualConfiguration(); + + // ADD_NEW_METHODS_HERE + + protected: + virtual void decodeFromXMLString(QXmlStreamReader& xml, + const AString& rootElementText) override; + + virtual void encodeInXMLString(AString& xmlTextOut) const override; + + private: + void correctOldStackingOrder(); + + void copyHelperTileTabsLayoutManualConfiguration(const TileTabsLayoutManualConfiguration& obj); + + std::vector> m_tabInfo; + + int32_t m_windowAnnotationsStackingOrder = -1000; + + + // ADD_NEW_MEMBERS_HERE + + + static const AString s_rootElementName; + static const AString s_rootElementAttributeVersion; + static const AString s_rootElementAttributeName; + static const AString s_rootElementAttributeUniqueID; + + static const AString s_rootElementAttributeValueVersionOne; + + static const AString s_tabInfoElementName; + static const AString s_tabInfoAttributeDisplayStatus; + static const AString s_tabInfoAttributeTabIndex; + static const AString s_tabInfoAttributeMinX; + static const AString s_tabInfoAttributeMaxX; + static const AString s_tabInfoAttributeMinY; + static const AString s_tabInfoAttributeMaxY; + static const AString s_tabInfoAttributeStackingOrder; + static const AString s_tabInfoAttributeBackground; + + static const AString s_windowInfoElementName; + static const AString s_windowInfoAttributeStackingOrder; + + friend class TileTabsLayoutBaseConfiguration; + }; + +#ifdef __TILE_TABS_LAYOUT_MANUAL_CONFIGURATION_DECLARE__ + const AString TileTabsLayoutManualConfiguration::s_rootElementName = "TileTabsManualLayout"; + const AString TileTabsLayoutManualConfiguration::s_rootElementAttributeVersion = "Version"; + const AString TileTabsLayoutManualConfiguration::s_rootElementAttributeName = "Name"; + const AString TileTabsLayoutManualConfiguration::s_rootElementAttributeUniqueID = "UniqueID"; + + const AString TileTabsLayoutManualConfiguration::s_rootElementAttributeValueVersionOne = "1"; + + const AString TileTabsLayoutManualConfiguration::s_tabInfoElementName = "TabInfo"; + const AString TileTabsLayoutManualConfiguration::s_tabInfoAttributeDisplayStatus = "DisplayStatus"; + const AString TileTabsLayoutManualConfiguration::s_tabInfoAttributeTabIndex = "TabIndex"; + const AString TileTabsLayoutManualConfiguration::s_tabInfoAttributeMinX = "MinX"; + const AString TileTabsLayoutManualConfiguration::s_tabInfoAttributeMaxX = "MaxX"; + const AString TileTabsLayoutManualConfiguration::s_tabInfoAttributeMinY = "MinY"; + const AString TileTabsLayoutManualConfiguration::s_tabInfoAttributeMaxY = "MaxY"; + const AString TileTabsLayoutManualConfiguration::s_tabInfoAttributeStackingOrder = "StackingOrder"; + const AString TileTabsLayoutManualConfiguration::s_tabInfoAttributeBackground = "Background"; + + const AString TileTabsLayoutManualConfiguration::s_windowInfoElementName = "WindowInfo"; + const AString TileTabsLayoutManualConfiguration::s_windowInfoAttributeStackingOrder = "StackingOrder"; + +#endif // __TILE_TABS_LAYOUT_MANUAL_CONFIGURATION_DECLARE__ + +} // namespace +#endif //__TILE_TABS_LAYOUT_MANUAL_CONFIGURATION_H__ diff -Nru connectome-workbench-1.4.2/src/Common/ToolBarWidthModeEnum.cxx connectome-workbench-1.5.0/src/Common/ToolBarWidthModeEnum.cxx --- connectome-workbench-1.4.2/src/Common/ToolBarWidthModeEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/ToolBarWidthModeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,375 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __TOOL_BAR_WIDTH_MODE_ENUM_DECLARE__ +#include "ToolBarWidthModeEnum.h" +#undef __TOOL_BAR_WIDTH_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::ToolBarWidthModeEnum + * \brief Enumerated type for tool bar width mode + * + * + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_toolBarWidthModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void toolBarWidthModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "ToolBarWidthModeEnum.h" + * + * Instatiate: + * m_toolBarWidthModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_toolBarWidthModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_toolBarWidthModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(toolBarWidthModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_toolBarWidthModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const ToolBarWidthModeEnum::Enum VARIABLE = m_toolBarWidthModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +ToolBarWidthModeEnum::ToolBarWidthModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +ToolBarWidthModeEnum::~ToolBarWidthModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +ToolBarWidthModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(ToolBarWidthModeEnum(STANDARD, + "STANDARD", + "Standard")); + + enumData.push_back(ToolBarWidthModeEnum(WIDE, + "WIDE", + "Wide")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const ToolBarWidthModeEnum* +ToolBarWidthModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const ToolBarWidthModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ToolBarWidthModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ToolBarWidthModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ToolBarWidthModeEnum::Enum +ToolBarWidthModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ToolBarWidthModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ToolBarWidthModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ToolBarWidthModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +ToolBarWidthModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const ToolBarWidthModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +ToolBarWidthModeEnum::Enum +ToolBarWidthModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ToolBarWidthModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ToolBarWidthModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ToolBarWidthModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +ToolBarWidthModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const ToolBarWidthModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +ToolBarWidthModeEnum::Enum +ToolBarWidthModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = ToolBarWidthModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const ToolBarWidthModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ToolBarWidthModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +ToolBarWidthModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ToolBarWidthModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(ToolBarWidthModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +ToolBarWidthModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(ToolBarWidthModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Common/ToolBarWidthModeEnum.h connectome-workbench-1.5.0/src/Common/ToolBarWidthModeEnum.h --- connectome-workbench-1.4.2/src/Common/ToolBarWidthModeEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/ToolBarWidthModeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,104 @@ +#ifndef __TOOL_BAR_WIDTH_MODE_ENUM_H__ +#define __TOOL_BAR_WIDTH_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class ToolBarWidthModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Standard width */ + STANDARD, + /** Expanded for wide displays */ + WIDE + }; + + + ~ToolBarWidthModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + ToolBarWidthModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const ToolBarWidthModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __TOOL_BAR_WIDTH_MODE_ENUM_DECLARE__ +std::vector ToolBarWidthModeEnum::enumData; +bool ToolBarWidthModeEnum::initializedFlag = false; +int32_t ToolBarWidthModeEnum::integerCodeCounter = 0; +#endif // __TOOL_BAR_WIDTH_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__TOOL_BAR_WIDTH_MODE_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Common/Vector3D.h connectome-workbench-1.5.0/src/Common/Vector3D.h --- connectome-workbench-1.4.2/src/Common/Vector3D.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/Vector3D.h 2021-02-16 19:46:47.000000000 +0000 @@ -59,6 +59,7 @@ Vector3D operator*(const float& right) const; Vector3D operator/(const float& right) const;//NOTE: doesn't really make sense to have the other division, unlike multiplication inline operator float*() { return m_vec; } + inline operator const float*() const { return m_vec; } }; Vector3D operator*(const float& left, const Vector3D& right); diff -Nru connectome-workbench-1.4.2/src/Common/WorkbenchQtMessageHandler.cxx connectome-workbench-1.5.0/src/Common/WorkbenchQtMessageHandler.cxx --- connectome-workbench-1.4.2/src/Common/WorkbenchQtMessageHandler.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/WorkbenchQtMessageHandler.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,339 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2021 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WORKBENCH_QT_MESSAGE_HANDLER_DECLARE__ +#include "WorkbenchQtMessageHandler.h" +#undef __WORKBENCH_QT_MESSAGE_HANDLER_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" + +using namespace caret; +using namespace std; + + +/** + * \class caret::WorkbenchQtMessageHandler + * \brief Processes messages from Qt with filtering to suppress some messages + * \ingroup Common + */ + +/** + * Constructor. + */ +WorkbenchQtMessageHandler::WorkbenchQtMessageHandler() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +WorkbenchQtMessageHandler::~WorkbenchQtMessageHandler() +{ +} + +/** + * Setup message handle for Qt. + * @param beeperCallback + * Instance of 'Beeper' class with method for issuing 'beep' sound. + */ + +void +WorkbenchQtMessageHandler::setupHandler(Beeper* beeper) +{ + s_beeper = beeper; + +#if QT_VERSION >= 0x050000 + qInstallMessageHandler(WorkbenchQtMessageHandler::messageHandlerForQt5);//this handler uses CaretLogger and GuiManager, so we must install it after the logger is available and the application is created +#else // QT_VERSION + qInstallMsgHandler(WorkbenchQtMessageHandler::messageHandlerForQt4);//this handler uses CaretLogger and GuiManager, so we must install it after the logger is available and the application is created +#endif // QT_VERSION +} + + +#if QT_VERSION >= 0x050000 +/** + * Setup message handle for Qt 5 + * @param type + * Type of message (Debug, info, warning, etc) + * @param context + * Message log context (see Qt Doc for QMessageLogContext) + * @param msg + * Message that is displayed. + */ +void +WorkbenchQtMessageHandler::messageHandlerForQt5(QtMsgType type, + const QMessageLogContext& context, + const QString& msg) +{ + /* + * Some messages produced by Qt can be safely ignored + */ + if (WorkbenchQtMessageHandler::isMessageTextSuppressed(msg)) { + return; + } + + const AString backtrace = SystemUtilities::getBackTrace(); + + const AString contextInfo = (" Context Info File (" + + QString(context.file) + + ") Function (" + QString(context.function) + + ") Line (" + QString::number(context.line) + + ") Version (" + QString::number(context.version) + + ") Category (" + QString(context.category) + + ")"); + const AString message = (AString(msg) + "\n" + + contextInfo + "\n" + + backtrace); + + if (CaretLogger::isValid()) { + bool abortFlag = false; + bool displayedFlag = false; + switch (type) { + case QtDebugMsg: + CaretLogInfo(message); + displayedFlag = CaretLogger::getLogger()->isInfo(); + break; + case QtWarningMsg: + CaretLogWarning(message); + displayedFlag = CaretLogger::getLogger()->isWarning(); + break; + case QtCriticalMsg: + CaretLogSevere(message); + displayedFlag = CaretLogger::getLogger()->isSevere(); + break; + case QtFatalMsg: + cerr << "Qt Fatal: " << message << endl; + abortFlag = true;//fatal will cause an abort, so always display it, bypassing logger entirely + displayedFlag = true; + break; +#if QT_VERSION >= 0x050500 + case QtInfoMsg: + CaretLogInfo(message); + displayedFlag = CaretLogger::getLogger()->isInfo(); + break; +#endif + } + + /* + * Beep to alert user about an error!!! + */ + if (displayedFlag && (type != QtDebugMsg))//don't beep for debug + { + WorkbenchQtMessageHandler::makeBeepSound(); + } +#ifndef NDEBUG + if (!displayedFlag) + { + cerr << "DEBUG: Qt "; + switch (type) + { + case QtDebugMsg: + cerr << "Debug "; + break; + case QtWarningMsg: + cerr << "Warning "; + break; + case QtCriticalMsg: + cerr << "Critical "; + break; + case QtFatalMsg: + cerr << "FATAL (?!?) ";//should never happen + break; +#if QT_VERSION >= 0x050500 + case QtInfoMsg: + std::cerr << "Info "; + break; +#endif + } + cerr << "message hidden" << endl; + } +#endif + + if (abortFlag) { + std::abort(); + } + } + else { + switch (type) { + case QtDebugMsg: + std::cerr << "Qt Debug: " << message << std::endl; + break; + case QtWarningMsg: + std::cerr << "Qt Warning: " << message << std::endl; + break; + case QtCriticalMsg: + std::cerr << "Qt Critical: " << message << std::endl; + break; + case QtFatalMsg: + std::cerr << "Qt Fatal: " << message << std::endl; + std::abort(); + break; +#if QT_VERSION >= 0x050500 + case QtInfoMsg: + std::cerr << "Qt Info: " << message << std::endl; + break; +#endif + } + } +} + +#else //QT_VERSION >= 0x050000 + +/** + * Setup message handle for Qt 4 + * @param type + * Type of message (Debug, info, warning, etc) + * @param msg + * Message that is displayed. + */ +void +WorkbenchQtMessageHandler::messageHandlerForQt4(QtMsgType type, + const char* msg) +{ + const AString backtrace = SystemUtilities::getBackTrace(); + + const AString message = (AString(msg) + "\n" + backtrace); + + if (CaretLogger::isValid()) { + bool abortFlag = false; + bool displayedFlag = false; + switch (type) { + case QtDebugMsg: + CaretLogInfo(message); + displayedFlag = CaretLogger::getLogger()->isInfo(); + break; + case QtWarningMsg: + CaretLogWarning(message); + displayedFlag = CaretLogger::getLogger()->isWarning(); + break; + case QtCriticalMsg: + CaretLogSevere(message); + displayedFlag = CaretLogger::getLogger()->isSevere(); + break; + case QtFatalMsg: + cerr << "Qt Fatal: " << message << endl; + abortFlag = true;//fatal will cause an abort, so always display it, bypassing logger entirely + displayedFlag = true; + break; + } + + /* + * Beep to alert user about an error!!! + */ + if (displayedFlag && (type != QtDebugMsg))//don't beep for debug + { + makeBeepSound(); + } +#ifndef NDEBUG + if (!displayedFlag) + { + cerr << "DEBUG: Qt "; + switch (type) + { + case QtDebugMsg: + cerr << "Debug "; + break; + case QtWarningMsg: + cerr << "Warning "; + break; + case QtCriticalMsg: + cerr << "Critical "; + break; + case QtFatalMsg: + cerr << "FATAL (?!?) ";//should never happen + break; + } + cerr << "message hidden" << endl; + } +#endif + + if (abortFlag) { + std::abort(); + } + } + else { + switch (type) { + case QtDebugMsg: + std::cerr << "Qt Debug: " << message << std::endl; + break; + case QtWarningMsg: + std::cerr << "Qt Warning: " << message << std::endl; + break; + case QtCriticalMsg: + std::cerr << "Qt Critical: " << message << std::endl; + break; + case QtFatalMsg: + std::cerr << "Qt Fatal: " << message << std::endl; + std::abort(); + break; + } + } + +} + +#endif // QT_VERSION >= 0x050000 + +/** + * Make a beeping sound using the 'Beeper' instance, if available + */ +void +WorkbenchQtMessageHandler::makeBeepSound() +{ + if (s_beeper != NULL) { + s_beeper->makeBeep(); + } +} + +/** + * @return True if the given message should be suppressed else false. + * @param msg + * Message tested for suppression. + * + * Note: some messages produce by Qt are not a problem and can be safely ignored. + * An example is "ICC" profile issues for PNG images. + */ +bool +WorkbenchQtMessageHandler::isMessageTextSuppressed(const QString& msg) +{ + if ( ! s_suppressedMessagesTextValidFlag) { + s_suppressedMessagesTextValidFlag = true; + + /* + * Suppress invalid "ICC Profile" messages from PNG images + */ + s_supressedMessagesText.push_back("QPngHandler: Failed to parse ICC profile"); + s_supressedMessagesText.push_back("fromIccProfile: failed general sanity check"); + s_supressedMessagesText.push_back("Unsupported ICC profile class"); + } + + for (const auto& smt : s_supressedMessagesText) { + if (msg.contains(smt)) { + return true; + } + } + + return false; +} + + diff -Nru connectome-workbench-1.4.2/src/Common/WorkbenchQtMessageHandler.h connectome-workbench-1.5.0/src/Common/WorkbenchQtMessageHandler.h --- connectome-workbench-1.4.2/src/Common/WorkbenchQtMessageHandler.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Common/WorkbenchQtMessageHandler.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,99 @@ +#ifndef __WORKBENCH_QT_MESSAGE_HANDLER_H__ +#define __WORKBENCH_QT_MESSAGE_HANDLER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2021 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "CaretObject.h" + +namespace caret { + + class WorkbenchQtMessageHandler : public CaretObject { + + public: + /* + * Class for issuing a 'beep' sound + */ + class Beeper { + public: + /** + * Constructor + */ + Beeper() { } + + /** + * Destructor + */ + virtual ~Beeper() { } + + /** + * Issue the beep sound (must be overridden to produce a beep sound) + */ + virtual void makeBeep() { } + }; + + WorkbenchQtMessageHandler(const WorkbenchQtMessageHandler&) = delete; + + WorkbenchQtMessageHandler& operator=(const WorkbenchQtMessageHandler&) = delete; + + static void setupHandler(Beeper* beeper); + + static void makeBeepSound(); + + // ADD_NEW_METHODS_HERE + + private: + WorkbenchQtMessageHandler(); + + virtual ~WorkbenchQtMessageHandler(); + + static void messageHandlerForQt4(QtMsgType type, + const char* msg); + + static void messageHandlerForQt5(QtMsgType type, + const QMessageLogContext& context, + const QString& msg); + + static bool isMessageTextSuppressed(const QString& msg); + + static Beeper* s_beeper; + + static std::vector s_supressedMessagesText; + + static bool s_suppressedMessagesTextValidFlag; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WORKBENCH_QT_MESSAGE_HANDLER_DECLARE__ + WorkbenchQtMessageHandler::Beeper* WorkbenchQtMessageHandler::s_beeper = NULL; + std::vector WorkbenchQtMessageHandler::s_supressedMessagesText; + bool WorkbenchQtMessageHandler::s_suppressedMessagesTextValidFlag = false; +#endif // __WORKBENCH_QT_MESSAGE_HANDLER_DECLARE__ + +} // namespace +#endif //__WORKBENCH_QT_MESSAGE_HANDLER_H__ diff -Nru connectome-workbench-1.4.2/src/Desktop/desktop.cxx connectome-workbench-1.5.0/src/Desktop/desktop.cxx --- connectome-workbench-1.4.2/src/Desktop/desktop.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Desktop/desktop.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -55,218 +55,18 @@ #include "GuiManager.h" #include "MacApplication.h" #include "ProgramParameters.h" +#include "RecentFilesDialog.h" +#include "RecentFilesSystemAccessModeEnum.h" +#include "SceneDialog.h" #include "SessionManager.h" -#include "SplashScreen.h" #include "SystemUtilities.h" +#include "WorkbenchQtMessageHandler.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" -static bool caretLoggerIsValid = false; - using namespace caret; using namespace std; -#if QT_VERSION >= 0x050000 -/** - * Handles message produced by Qt 5 - */ -static void -messageHandlerForQt5(QtMsgType type, const QMessageLogContext& context, const QString& msg) -{ - const AString backtrace = SystemUtilities::getBackTrace(); - - const AString contextInfo = (" Context Info File (" - + QString(context.file) - + ") Function (" + QString(context.function) - + ") Line (" + QString::number(context.line) - + ") Version (" + QString::number(context.version) - + ") Category (" + QString(context.category) - + ")"); - const AString message = (AString(msg) + "\n" - + contextInfo + "\n" - + backtrace); - - if (caretLoggerIsValid) { - bool abortFlag = false; - bool displayedFlag = false; - switch (type) { - case QtDebugMsg: - CaretLogInfo(message); - displayedFlag = CaretLogger::getLogger()->isInfo(); - break; - case QtWarningMsg: - CaretLogWarning(message); - displayedFlag = CaretLogger::getLogger()->isWarning(); - break; - case QtCriticalMsg: - CaretLogSevere(message); - displayedFlag = CaretLogger::getLogger()->isSevere(); - break; - case QtFatalMsg: - cerr << "Qt Fatal: " << message << endl; - abortFlag = true;//fatal will cause an abort, so always display it, bypassing logger entirely - displayedFlag = true; - break; -#if QT_VERSION >= 0x050500 - case QtInfoMsg: - CaretLogInfo(message); - displayedFlag = CaretLogger::getLogger()->isInfo(); - break; -#endif - } - - /* - * Beep to alert user about an error!!! - */ - if (displayedFlag && (type != QtDebugMsg))//don't beep for debug - { - GuiManager::beep(); - } -#ifndef NDEBUG - if (!displayedFlag) - { - cerr << "DEBUG: Qt "; - switch (type) - { - case QtDebugMsg: - cerr << "Debug "; - break; - case QtWarningMsg: - cerr << "Warning "; - break; - case QtCriticalMsg: - cerr << "Critical "; - break; - case QtFatalMsg: - cerr << "FATAL (?!?) ";//should never happen - break; -#if QT_VERSION >= 0x050500 - case QtInfoMsg: - std::cerr << "Info "; - break; -#endif - } - cerr << "message hidden" << endl; - } -#endif - - if (abortFlag) { - std::abort(); - } - } - else { - switch (type) { - case QtDebugMsg: - std::cerr << "Qt Debug: " << message << std::endl; - break; - case QtWarningMsg: - std::cerr << "Qt Warning: " << message << std::endl; - break; - case QtCriticalMsg: - std::cerr << "Qt Critical: " << message << std::endl; - break; - case QtFatalMsg: - std::cerr << "Qt Fatal: " << message << std::endl; - std::abort(); - break; -#if QT_VERSION >= 0x050500 - case QtInfoMsg: - std::cerr << "Qt Info: " << message << std::endl; - break; -#endif - } - } -} - -#else // QT_VERSION - -/** - * Handles message produced by Qt 4. - */ -static void -messageHandlerForQt4(QtMsgType type, const char* msg) -{ - const AString backtrace = SystemUtilities::getBackTrace(); - - const AString message = (AString(msg) + "\n" + backtrace); - - if (caretLoggerIsValid) { - bool abortFlag = false; - bool displayedFlag = false; - switch (type) { - case QtDebugMsg: - CaretLogInfo(message); - displayedFlag = CaretLogger::getLogger()->isInfo(); - break; - case QtWarningMsg: - CaretLogWarning(message); - displayedFlag = CaretLogger::getLogger()->isWarning(); - break; - case QtCriticalMsg: - CaretLogSevere(message); - displayedFlag = CaretLogger::getLogger()->isSevere(); - break; - case QtFatalMsg: - cerr << "Qt Fatal: " << message << endl; - abortFlag = true;//fatal will cause an abort, so always display it, bypassing logger entirely - displayedFlag = true; - break; - } - - /* - * Beep to alert user about an error!!! - */ - if (displayedFlag && (type != QtDebugMsg))//don't beep for debug - { - GuiManager::beep(); - } -#ifndef NDEBUG - if (!displayedFlag) - { - cerr << "DEBUG: Qt "; - switch (type) - { - case QtDebugMsg: - cerr << "Debug "; - break; - case QtWarningMsg: - cerr << "Warning "; - break; - case QtCriticalMsg: - cerr << "Critical "; - break; - case QtFatalMsg: - cerr << "FATAL (?!?) ";//should never happen - break; - } - cerr << "message hidden" << endl; - } -#endif - - if (abortFlag) { - std::abort(); - } - } - else { - switch (type) { - case QtDebugMsg: - std::cerr << "Qt Debug: " << message << std::endl; - break; - case QtWarningMsg: - std::cerr << "Qt Warning: " << message << std::endl; - break; - case QtCriticalMsg: - std::cerr << "Qt Critical: " << message << std::endl; - break; - case QtFatalMsg: - std::cerr << "Qt Fatal: " << message << std::endl; - std::abort(); - break; - } - } -} - -#endif // QT_VERSION //struct for communicating stuff back to main from parseCommandLine struct ProgramState @@ -276,16 +76,33 @@ int windowPosXY[2]; int graphicsSizeXY[2]; bool showSplash; + bool macMenuFlag = false; + + AString directoryName; AString specFileNameLoadWithDialog; AString specFileNameLoadAll; AString sceneFileName; + AString sceneFileNameNoDialog; AString sceneNameOrNumber; ProgramState(); }; +class GuiBeeper : public WorkbenchQtMessageHandler::Beeper +{ +public: + GuiBeeper() { } + + ~GuiBeeper() { } + + /** + * Override to make the beep sound + */ + virtual void makeBeep() override { GuiManager::get()->beep(); } + +}; //declare the functions associated with command line void printHelp(const AString& progName); @@ -305,7 +122,6 @@ * Create the session manager. */ SessionManager::createSessionManager(ApplicationTypeEnum::APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE); - caretLoggerIsValid = true; /* * Parameters for the program. @@ -399,16 +215,25 @@ BrainOpenGLWidget::initializeDefaultGLFormat(); #endif -#if QT_VERSION >= 0x050000 - qInstallMessageHandler(messageHandlerForQt5);//this handler uses CaretLogger and GuiManager, so we must install it after the logger is available and the application is created -#else // QT_VERSION - qInstallMsgHandler(messageHandlerForQt4);//this handler uses CaretLogger and GuiManager, so we must install it after the logger is available and the application is created -#endif // QT_VERSION + /* + * Setup handlers (callbacks) for messages produced by Qt + */ + GuiBeeper beeper; + WorkbenchQtMessageHandler::setupHandler(&beeper); + /* * Log debug status */ + CaretLogConfig("Version " + applicationInformation.getVersion()); CaretLogConfig(applicationInformation.getCompiledWithDebugStatus()); + + /* + * Mac Option to put menus on window and not use the native tool bar + */ + if (myState.macMenuFlag) { + app.setAttribute(Qt::AA_DontUseNativeMenuBar); + } /* * Enabled the desired splash screen based upon user preferences @@ -468,24 +293,39 @@ * Show file selection splash screen if enabled via user's preferences */ if (showSelectionSplashScreen) { - /* - * Show selection splash screen. - * Need to process events since QApplication::exec() has not - * been called. - */ - SplashScreen splashScreen(NULL); - app.processEvents(); - if (splashScreen.exec()) { - const QString dataFileName = splashScreen.getSelectedDataFileName(); - if ( ! dataFileName.isEmpty()) { - myState.fileList.clear(); - if (dataFileName.endsWith(DataFileTypeEnum::toFileExtension(DataFileTypeEnum::SPECIFICATION))) { - myState.specFileNameLoadWithDialog = dataFileName; - } - else { - myState.fileList.push_back(dataFileName); + AString dataFileName; + int32_t sceneIndex(-1); + RecentFilesDialog::ResultModeEnum result = RecentFilesDialog::runDialog(RecentFilesDialog::RunMode::SPLASH_SCREEN, + dataFileName, + sceneIndex); + switch (result) { + case RecentFilesDialog::ResultModeEnum::CANCEL: + break; + case RecentFilesDialog::ResultModeEnum::LOAD_FILES_IN_SPEC_FILE: + myState.specFileNameLoadAll = dataFileName; + break; + case RecentFilesDialog::ResultModeEnum::LOAD_SCENE_FROM_SCENE_FILE: + myState.sceneFileName = dataFileName; + myState.sceneNameOrNumber = AString::number(sceneIndex); + break; + case RecentFilesDialog::ResultModeEnum::OPEN_DIRECTORY: + myState.directoryName = dataFileName; + break; + case RecentFilesDialog::ResultModeEnum::OPEN_FILE: + if ( ! dataFileName.isEmpty()) { + bool validFlag(false); + if (DataFileTypeEnum::fromFileExtension(dataFileName, + &validFlag) == DataFileTypeEnum::SPECIFICATION) { + myState.specFileNameLoadWithDialog = dataFileName; + } + else { + myState.fileList.push_back(dataFileName); + } } - } + break; + case RecentFilesDialog::ResultModeEnum::OPEN_OTHER: + myState.directoryName = dataFileName; + break; } } @@ -590,7 +430,18 @@ if ( ! myState.sceneFileName.isEmpty()) { myWindow->loadSceneFromCommandLine(myState.sceneFileName, - myState.sceneNameOrNumber); + myState.sceneNameOrNumber, + BrainBrowserWindow::LoadSceneFromCommandLineDialogMode::SHOW_YES); + } + + if ( ! myState.sceneFileNameNoDialog.isEmpty()) { + myWindow->loadSceneFromCommandLine(myState.sceneFileNameNoDialog, + myState.sceneNameOrNumber, + BrainBrowserWindow::LoadSceneFromCommandLineDialogMode::SHOW_NO); + } + + if ( ! myState.directoryName.isEmpty()) { + myWindow->loadDirectoryFromCommandLine(myState.directoryName); } #ifndef WORKBENCH_USE_QT5_QOPENGL_WIDGET @@ -741,23 +592,45 @@ cout << endl - << " -mac-menu-duplicate" << endl - << " MacOS Only - Adds menus to the top of the Browser Window " << endl - << " that duplicate the menu bar at the top of the window. " << endl - << " The menus are similar to that on Linux and Windows. " << endl - << " May be useful for creating tutorial images." << endl - << " This functionality is EXPERIMENTAL and subject to " << endl - << " removal in future versions of wb_view." << endl + << " -mac-menu-in-window" << endl + << " MacOS Only - Menus are at the top of each main window " + << " similar to Linux and Window Applications. " + << " May be useful for creating tutorial images and videos." << endl + << endl + << " -no-recent-files-dialog" << endl + << " Inhibits display of Open Recent Files Dialog at wb_view startup." << endl << endl << " -no-splash" << endl - << " disable all splash screens" << endl + << " (Obsolete) Replaced by \"-no-recent-files-dialog\"" << endl + << " Splash screen was replaced with Open Recent Fiels Dialog." << endl + << endl + << " -recent-files-mode " << endl + << " Set the recent file's file system access mode" << endl + << " (overrides and replaces value in preferences)." << endl + << " Using an off mode prevents file system access for " << endl + << " obtaining last modified time and file existance." << endl + << " This option may be useful if recent files are on a mounted" << endl + << " file system that is having problems that may cause wb_view" << endl + << " to hang at startup. Valid modes are:" << endl; + + std::vector recentFilesModes; + RecentFilesSystemAccessModeEnum::getAllEnums(recentFilesModes); + for (auto rfm : recentFilesModes) { + std::cout << " " < " << endl << " load the specified scene file and display the scene " << endl << " in the file that matches by name or number. Name" << endl << " takes precedence over number. The scene numbers " << endl - << " start at one." << endl - << " " << endl + << " start at one. The scene dialog remains visible " << endl + << " after loading of the scene." << endl + << endl + << " -scene-load-hd " << endl + << " Same as \"-scene-load\" except that the scene dialog " << endl + << " is hidden after the scene has loaded." << endl << endl << " -style " << endl << " change the window style to the specified style" << endl @@ -825,10 +698,32 @@ hasFatalError = true; } } - } else if (thisParam == "-mac-menu-duplicate") { - BrainBrowserWindow::setEnableMacDuplicateMenuBar(true); - } else if (thisParam == "-no-splash") { + } else if (thisParam == "-mac-menu-in-window") { + myState.macMenuFlag = true; + } else if ((thisParam == "-no-splash") + || (thisParam == "-no-recent-files-dialog")) { myState.showSplash = false; + } else if (thisParam == "-recent-files-mode") { + if (myParams->hasNext()) { + const AString recentFilesModeName = myParams->nextString("Recent Files Mode").toUpper(); + bool valid = false; + const RecentFilesSystemAccessModeEnum::Enum recentFilesMode = RecentFilesSystemAccessModeEnum::fromName(recentFilesModeName, &valid); + if (valid) + { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->setRecentFilesSystemAccessMode(recentFilesMode); + } + else { + cerr << "Invalid recent files mode \"" + << qPrintable(recentFilesModeName) + << "\" for \"-recent-files-mode\" option" << std::endl; + hasFatalError = true; + } + } + else { + cerr << "Missing recent files mode for \"-recent-files-mode\" option" << std::endl; + hasFatalError = true; + } } else if (thisParam == "-scene-load") { if (myParams->hasNext()) { myState.sceneFileName = myParams->nextString("Scene File Name"); @@ -836,12 +731,27 @@ myState.sceneNameOrNumber = myParams->nextString("Scene Name or Number"); } else { - cerr << "Missing scene name/number for \"-scene\" option" << std::endl; + cerr << "Missing scene name/number for \"-scene-load\" option" << std::endl; + hasFatalError = true; + } + } + else { + cerr << "Missing scene file name for \"-scene-load\" option" << std::endl; + hasFatalError = true; + } + } else if (thisParam == "-scene-load-hd") { + if (myParams->hasNext()) { + myState.sceneFileNameNoDialog = myParams->nextString("Scene File Name"); + if (myParams->hasNext()) { + myState.sceneNameOrNumber = myParams->nextString("Scene Name or Number"); + } + else { + cerr << "Missing scene name/number for " << thisParam << " option" << std::endl; hasFatalError = true; } } else { - cerr << "Missing scene file name for \"-scene\" option" << std::endl; + cerr << "Missing scene file name for " << thisParam << " option" << std::endl; hasFatalError = true; } } else if (thisParam == "-spec-load-all") { @@ -969,6 +879,9 @@ if ( ! myState.sceneFileName.isEmpty()) { myState.showSplash = false; } + if ( ! myState.sceneFileNameNoDialog.isEmpty()) { + myState.showSplash = false; + } if ( ! myState.specFileNameLoadWithDialog.isEmpty()) { myState.showSplash = false; } @@ -980,6 +893,7 @@ ProgramState::ProgramState() { sceneFileName = ""; + sceneFileNameNoDialog = ""; sceneNameOrNumber = ""; windowSizeXY[0] = -1; windowSizeXY[1] = -1; @@ -988,4 +902,5 @@ graphicsSizeXY[0] = -1; graphicsSizeXY[1] = -1; showSplash = true; + macMenuFlag = false; } diff -Nru connectome-workbench-1.4.2/src/Files/AffineFile.cxx connectome-workbench-1.5.0/src/Files/AffineFile.cxx --- connectome-workbench-1.4.2/src/Files/AffineFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AffineFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,8 @@ #include "FileInformation.h" #include "NiftiIO.h" +#include + #include #include #include @@ -52,7 +54,7 @@ { FileInformation affineInfo(filename); if (!affineInfo.exists()) throw DataFileException("affine file '" + filename + "' does not exist"); - fstream affineFile(filename.toLocal8Bit().constData(), fstream::in); + ifstream affineFile(filename.toStdString()); if (!affineFile.good()) throw DataFileException("error opening file '" + filename + "' for reading"); FloatMatrix ret = FloatMatrix::identity(4);//to ensure the right size and the fourth 0 0 0 1 row for (int i = 0; i < 3; ++i)//DO NOT read the fourth row from the file into the matrix @@ -68,7 +70,8 @@ void AffineFile::write44(const FloatMatrix& out, const AString& filename) { - fstream affineFile(filename.toLocal8Bit().constData(), fstream::out); + QFile::remove(filename); + ofstream affineFile(filename.toStdString()); if (!affineFile.good()) { throw DataFileException("error opening file '" + filename + "' for writing"); @@ -87,14 +90,14 @@ } } -void AffineFile::readWorld(const AString& filename, bool inverse) +void AffineFile::readWorld(const AString& filename, const bool inverse) { FloatMatrix temp = read34(filename); if (inverse) temp = temp.inverse();//support reading reverse affines m_matrix = temp; } -void AffineFile::writeWorld(const AString& filename, bool inverse) const +void AffineFile::writeWorld(const AString& filename, const bool inverse) const { FloatMatrix temp = m_matrix; if (inverse) temp = temp.inverse();//and writing diff -Nru connectome-workbench-1.4.2/src/Files/AffineFile.h connectome-workbench-1.5.0/src/Files/AffineFile.h --- connectome-workbench-1.4.2/src/Files/AffineFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AffineFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -34,8 +34,8 @@ static void getFSLQuirks(const AString& niftiName, FloatMatrix& outSform, FloatMatrix& outScale);//just a convenience wrapper around the vector > version in NiftiHeaderIO public: AffineFile(); - void readWorld(const AString& filename, bool inverse = false);//forward nifti coordinate transform, optionally reverse - void writeWorld(const AString& filename, bool inverse = false) const; + void readWorld(const AString& filename, const bool inverse = false);//forward nifti coordinate transform, optionally reverse + void writeWorld(const AString& filename, const bool inverse = false) const; void readFlirt(const AString& filename, const AString& sourceName, const AString& targetName);//flirt convention matrix, requires source/target volumes void writeFlirt(const AString& filename, const AString& sourceName, const AString& targetName) const; void readITK(const AString& filename);//reverse, LPS rather than RAS, with strange encoding for translation diff -Nru connectome-workbench-1.4.2/src/Files/AffineSeriesFile.cxx connectome-workbench-1.5.0/src/Files/AffineSeriesFile.cxx --- connectome-workbench-1.4.2/src/Files/AffineSeriesFile.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AffineSeriesFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,185 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AffineSeriesFile.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "DataFileException.h" +#include "FileInformation.h" +#include "NiftiIO.h" + +#include + +#include +#include +#include + +using namespace caret; +using namespace std; + +namespace +{ + void getFSLQuirks(const AString& niftiName, FloatMatrix& outSform, FloatMatrix& outScale) + { + NiftiIO myIO; + myIO.openRead(niftiName); + outSform = FloatMatrix(myIO.getHeader().getSForm());//NOTE: this is expected to return a 4x4 matrix with the 0 0 0 1 row intact + outScale = FloatMatrix(myIO.getHeader().getFSLSpace()); + } + + vector readFileRaw(const AString& filename, const bool inverse = false) + { + vector ret; + FileInformation fileInfo(filename); + if (!fileInfo.exists()) throw DataFileException("affine series file '" + filename + "' does not exist"); + ifstream affineSeriesFile(filename.toStdString()); + if (!affineSeriesFile.good()) throw DataFileException("error opening file '" + filename + "' for reading"); + string myline; + int whatline = 0;//will be 1-based, but this way we can increment at top of loop + bool emptyLine = false;//track if we have found an empty line, so it can be an error when in the middle of the file + FloatMatrix tempMatrix = FloatMatrix::identity(4); + while (getline(affineSeriesFile, myline))//this needs to be a function for reuse + { + ++whatline; + stringstream mystr(myline); + vector linevals; + float tempf; + while (linevals.size() < 17 && (mystr >> tempf)) + { + linevals.push_back(tempf); + } + switch (linevals.size()) + { + case 16: + if (linevals[12] != 0.0f || linevals[13] != 0.0f || linevals[14] != 0.0f || linevals[15] != 1.0f) + { + throw DataFileException("values 13 through 16 in file '" + filename + "', line " + AString::number(whatline) + " are not equal to '0 0 0 1'"); + }//intentional fallthrough + case 12: + if (emptyLine) + {//we have seen an empty line, consider it an error even if next content is valid + throw DataFileException("empty line in file '" + filename + "', line " + AString::number(whatline - 1)); + } + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 4; ++j) + { + tempMatrix[i][j] = linevals[j + i * 4]; + } + } + if (inverse) + { + ret.push_back(tempMatrix.inverse()); + } else { + ret.push_back(tempMatrix); + } + break; + case 0: + if (AString(myline).trimmed().size() == 0) + { + emptyLine = true;//accept empty lines on end of file, but not in middle + } + break; + default: + throw DataFileException("malformed line in file '" + filename + "', line " + AString::number(whatline)); + } + } + return ret; + } + + //write as 12-parameter, because there may be nobody else using this format + void writeFileRaw(const AString& filename, const vector matrixList, const bool inverse = false) + { + QFile::remove(filename); + ofstream outFile(filename.toStdString()); + for (auto matrix : matrixList) + { + if (inverse) matrix = matrix.inverse(); + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 4; ++j) + { + if (i != 0 && j != 0) outFile << "\t"; + outFile << matrix[i][j]; + } + } + outFile << endl; + } + } +} + +void AffineSeriesFile::readWorld(const AString& filename, const bool inverse) +{ + m_matrixList = readFileRaw(filename, inverse); +} + +void AffineSeriesFile::writeWorld(const AString& filename, const bool inverse) const +{ + writeFileRaw(filename, m_matrixList, inverse); +} + +void AffineSeriesFile::readFlirt(const AString& filename, const AString& sourceName, const AString& targetName) +{ + vector flirtMatList = readFileRaw(filename); + FloatMatrix sourceMat, sourceScale, targetMat, targetScale; + getFSLQuirks(sourceName, sourceMat, sourceScale); + getFSLQuirks(targetName, targetMat, targetScale); + m_matrixList.resize(flirtMatList.size()); + //via aff_conv : world = targmat * trgscale^-1 * input * srcscale * sourcemat^-1 + //m_matrix = targetMat * targetScale.inverse() * flirtMat * sourceScale * sourceMat.inverse(); + for (size_t i = 0; i < flirtMatList.size(); ++i) + { + m_matrixList[i] = targetMat * targetScale.inverse() * flirtMatList[i] * sourceScale * sourceMat.inverse(); + } +} + +void AffineSeriesFile::writeFlirt(const AString& filename, const AString& sourceName, const AString& targetName) const +{ + vector flirtListOut(m_matrixList.size()); + FloatMatrix sourceMat, sourceScale, targetMat, targetScale; + getFSLQuirks(sourceName, sourceMat, sourceScale); + getFSLQuirks(targetName, targetMat, targetScale); + //FloatMatrix flirtMat = targetScale * targetMat.inverse() * m_matrix * sourceMat * sourceScale.inverse(); + for (size_t i = 0; i < m_matrixList.size(); ++i) + { + flirtListOut[i] = targetScale * targetMat.inverse() * m_matrixList[i] * sourceMat * sourceScale.inverse(); + } + writeFileRaw(filename, flirtListOut); +} + +vector AffineSeriesFile::getInverseMatrixList() const +{ + vector ret(m_matrixList.size()); + for (size_t i = 0; i < m_matrixList.size(); ++i) + { + ret[i] = m_matrixList[i].inverse(); + } + return ret; +} + +void AffineSeriesFile::setMatrixList(const vector& matrixList) +{ + for (auto& matrix : matrixList) + { + if (matrix.getNumberOfColumns() != 4) throw DataFileException("all matrices must have 4 columns"); + if (matrix.getNumberOfRows() != 4) throw DataFileException("all matrices must have 4 rows"); + } + m_matrixList = matrixList; +} diff -Nru connectome-workbench-1.4.2/src/Files/AffineSeriesFile.h connectome-workbench-1.5.0/src/Files/AffineSeriesFile.h --- connectome-workbench-1.4.2/src/Files/AffineSeriesFile.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AffineSeriesFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,47 @@ +#ifndef __AFFINE_SERIES_FILE_H__ +#define __AFFINE_SERIES_FILE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AString.h" +#include "FloatMatrix.h" + +#include + +namespace caret { + + class AffineSeriesFile + { + std::vector m_matrixList; + public: + void readWorld(const AString& filename, const bool inverse = false);//forward nifti coordinate transform, optionally reverse + void writeWorld(const AString& filename, const bool inverse = false) const; + void readFlirt(const AString& filename, const AString& sourceName, const AString& targetName);//flirt convention matrix, requires source/target volumes + void writeFlirt(const AString& filename, const AString& sourceName, const AString& targetName) const; + //no ITK format, they have an extra 3 parameters in their affine + const std::vector& getMatrixList() const { return m_matrixList; } + std::vector getInverseMatrixList() const;//convenience function, because it is a little annoying to do when needed + void setMatrixList(const std::vector& matrixList);//needs to do sanity checking, so don't inline + }; + +} + +#endif //__AFFINE_SERIES_FILE_H__ diff -Nru connectome-workbench-1.4.2/src/Files/AnnotationFile.cxx connectome-workbench-1.5.0/src/Files/AnnotationFile.cxx --- connectome-workbench-1.4.2/src/Files/AnnotationFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AnnotationFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -43,10 +43,12 @@ #include "EventAnnotationGroupGetWithKey.h" #include "EventAnnotationGrouping.h" #include "EventAnnotationTextSubstitutionInvalidate.h" +#include "EventBrowserTabClose.h" #include "EventBrowserTabDelete.h" #include "EventBrowserTabNewClone.h" +#include "EventBrowserTabReopenClosed.h" #include "EventManager.h" -#include "EventTileTabsConfigurationModification.h" +#include "EventTileTabsGridConfigurationModification.h" #include "GiftiMetaData.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -132,6 +134,8 @@ } } break; + case ANNOTATION_FILE_DUMMY_FOR_DRAWING: + break; } } @@ -226,8 +230,10 @@ /* NEED THIS AFTER Tile Tabs have been modified */ EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_TILE_TABS_MODIFICATION); + EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_CLOSE); EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE); + EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_CLOSED); } /** @@ -352,6 +358,19 @@ void AnnotationFile::receiveEvent(Event* event) { + switch (m_fileSubType) { + case ANNOTATION_FILE_SAVE_TO_FILE: + break; + case ANNOTATION_FILE_SAVE_TO_SCENE: + break; + case ANNOTATION_FILE_DUMMY_FOR_DRAWING: + /* + * No event processing for dummy file + */ + return; + break; + } + if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE) { EventAnnotationAddToRemoveFromFile* annEvent = dynamic_cast(event); CaretAssert(annEvent); @@ -461,12 +480,23 @@ } } } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_CLOSE) { + EventBrowserTabClose* closeEvent = dynamic_cast(event); + CaretAssert(closeEvent); + + if (s_preserveRestoreDeletedTabFlag) { + const int32_t tabIndex = closeEvent->getBrowserTabIndex(); + preserveAnnotationsInClosedTab(tabIndex); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE) { EventBrowserTabDelete* deleteEvent = dynamic_cast(event); CaretAssert(deleteEvent); - const int32_t tabIndex = deleteEvent->getBrowserTabIndex(); - removeAnnotationsInTab(tabIndex); + if (s_preserveRestoreDeletedTabFlag) { + const int32_t tabIndex = deleteEvent->getBrowserTabIndex(); + removeAnnotationsInTab(tabIndex); + } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE) { EventBrowserTabNewClone* cloneTabEvent = dynamic_cast(event); @@ -477,6 +507,13 @@ cloneAnnotationsFromTabToTab(cloneFromTabIndex, cloneToTabIndex); } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_REOPEN_CLOSED) { + EventBrowserTabReopenClosed* reopenTabEvent = dynamic_cast(event); + CaretAssert(reopenTabEvent); + if (s_preserveRestoreDeletedTabFlag) { + restoreAnnotationsInReopendTab(reopenTabEvent->getTabIndex()); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_TEXT_SUBSTITUTION_INVALIDATE) { EventAnnotationTextSubstitutionInvalidate* textSubEvent = dynamic_cast(event); CaretAssert(textSubEvent); @@ -493,7 +530,7 @@ textSubEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_TILE_TABS_MODIFICATION) { - EventTileTabsConfigurationModification* modEvent = dynamic_cast(event); + EventTileTabsGridConfigurationModification* modEvent = dynamic_cast(event); CaretAssert(modEvent); updateSpacerAnnotationsAfterTileTabsModification(modEvent); } @@ -873,36 +910,6 @@ AnnotationFile::removeAnnotationWithUndoRedo(Annotation* annotation) { return removeAnnotationPrivate(annotation, true); - - -// for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); -// groupIter != m_annotationGroups.end(); -// groupIter++) { -// QSharedPointer group = *groupIter; -// QSharedPointer removedAnnotationPointer; -// if (group->removeAnnotation(annotation, -// removedAnnotationPointer)) { -// -// removedAnnotationPointer->invalidateAnnotationGroupKey(); -// -// m_removedAnnotations.insert(removedAnnotationPointer); -// -// /* -// * Remove group if it is empty. -// */ -// if (group->isEmpty()) { -// m_annotationGroups.erase(groupIter); -// } -// -// setModified(); -// return true; -// } -// } -// -// /* -// * Annotation not in this file -// */ -// return false; } /** @@ -971,24 +978,48 @@ * with the given tab index. */ bool annotationsWereClonedFlag(false); - for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); - groupIter != m_annotationGroups.end(); - groupIter++) { - QSharedPointer& group = *groupIter; - if (group->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { - if (group->getTabOrWindowIndex() == fromTabIndex) { - std::vector annotations; - group->getAllAnnotations(annotations); - - for (auto ann : annotations) { - CaretAssert(ann->getTabIndex() == fromTabIndex); - Annotation* newAnn = ann->clone(); - newAnn->setTabIndex(toTabIndex); - addAnnotationPrivate(newAnn, - generateUniqueKey()); - annotationsWereClonedFlag = true; + + /* + * Need to copy groups as annotation will be added when cloned + * and that will cause new groups to be added and we do not + * want to interate throught new groups or crash will occur. + */ + std::vector groupPointers; + for (auto& group : m_annotationGroups) { + groupPointers.push_back(group.data()); + } + + for (auto group : groupPointers) { + CaretAssert(group); + switch (group->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::CHART: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::SURFACE: + group->copySelections(fromTabIndex, + toTabIndex); + break; + case AnnotationCoordinateSpaceEnum::TAB: + if (group->getTabOrWindowIndex() == fromTabIndex) { + std::vector annotations; + group->getAllAnnotations(annotations); + + for (auto ann : annotations) { + CaretAssert(ann->getTabIndex() == fromTabIndex); + Annotation* newAnn = ann->clone(); + newAnn->setTabIndex(toTabIndex); + addAnnotationPrivate(newAnn, + generateUniqueKey()); + annotationsWereClonedFlag = true; + } } - } + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; } } @@ -1007,38 +1038,115 @@ bool AnnotationFile::removeAnnotationsInTab(const int32_t tabIndex) { + std::vector> removedGroups; + removeAnnotationsInTabGroupAux(tabIndex, + m_annotationGroups, + removedGroups); + const bool annotationsRemovedFlag( ! removedGroups.empty()); + + /* + * Not necessary since vector deletion at end of function + * would destroy the sharded pointers in the vector + */ + removedGroups.clear(); + + if (annotationsRemovedFlag) { + setModified(); + } + + return annotationsRemovedFlag; +} + +/** + * Remove all annotations in tab space in the given tab. Any removed annotation groups are returned + * and it is callers duty to allow deletion or retention of them. + * + * @param tabIndex + * Index of the tab. + * @param annotationsGroups + * Group from which annotation group(s) are removed + * @param removedGroupsOut + * Output containg any removed groups + */ +void +AnnotationFile::removeAnnotationsInTabGroupAux(const int32_t tabIndex, + std::vector>& annotationsGroups, + std::vector>& removedGroupsOut) +{ + removedGroupsOut.clear(); + + + CaretUsedInDebugCompileOnly(const uint32_t numAnn = static_cast(annotationsGroups.size())); + std::vector groupsToRemoveIterators; /* * Find annotation group(s) in tab space * with the given tab index. */ - for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); - groupIter != m_annotationGroups.end(); + for (AnnotationGroupIterator groupIter = annotationsGroups.begin(); + groupIter != annotationsGroups.end(); groupIter++) { - QSharedPointer& group = *groupIter; + QSharedPointer group = *groupIter; if (group->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { if (group->getTabOrWindowIndex() == tabIndex) { + removedGroupsOut.push_back(group); groupsToRemoveIterators.push_back(groupIter); } } } /* - * Remove the groups (shared pointers will do all deleting) - * NOTE: MUST delete iterators that are closest to the end first - * as any iterators that point to elements at the erased iterator - * or beyond are invalidated. + * Remove the groups from the vector (does not delete the groups) + * Note: Must start at the end and move backward as removing + * an item invalidate iterators pointing to items at larger + * indices (resulting in a crash) */ const int32_t numGroupsToRemove = static_cast(groupsToRemoveIterators.size()); for (int32_t i = (numGroupsToRemove - 1); i >= 0; i--) { CaretAssertVectorIndex(groupsToRemoveIterators, i); - m_annotationGroups.erase(groupsToRemoveIterators[i]); + annotationsGroups.erase(groupsToRemoveIterators[i]); } + + CaretAssert(numAnn + == (annotationsGroups.size() + removedGroupsOut.size())); +} + +/** + * Preserve annotations in a closed tab. The annotations are restored if the tab is reopened. + * @param tabIndex + * Index of tab + */ +void +AnnotationFile::preserveAnnotationsInClosedTab(const int32_t tabIndex) +{ + std::vector> removedGroups; + removeAnnotationsInTabGroupAux(tabIndex, + m_annotationGroups, + removedGroups); - return ( ! groupsToRemoveIterators.empty()); + m_closedTabAnnotationGroups.insert(m_closedTabAnnotationGroups.end(), + removedGroups.begin(), + removedGroups.end()); } +/** + * Restore annotations in reopened tab. + * @param tabIndex + * Index of tab + */ +void +AnnotationFile::restoreAnnotationsInReopendTab(const int32_t tabIndex) +{ + std::vector> removedGroups; + removeAnnotationsInTabGroupAux(tabIndex, + m_closedTabAnnotationGroups, + removedGroups); + + m_annotationGroups.insert(m_annotationGroups.end(), + removedGroups.begin(), + removedGroups.end()); +} /** * Get all annotations in this file. @@ -1091,7 +1199,7 @@ bool AnnotationFile::hasAnnotationsInCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordinateSpace) const { - for (const auto groupIter : m_annotationGroups) { + for (const auto& groupIter : m_annotationGroups) { if (groupIter->getCoordinateSpace() == coordinateSpace) { return true; } @@ -1142,30 +1250,6 @@ AnnotationGroup* spaceGroup = (*spaceGroupIter).data(); CaretAssert(spaceGroup); - - - - - -// AnnotationGroupKey spaceGroupKey = groupingEvent->getAnnotationGroupKey(); -// -// AnnotationGroup* spaceGroup = NULL; -// AnnotationGroupIterator spaceGroupIter = m_annotationGroups.end(); -// for (spaceGroupIter = m_annotationGroups.begin(); -// spaceGroupIter != m_annotationGroups.end(); -// spaceGroupIter++) { -// QSharedPointer groupPointer = *spaceGroupIter; -// if (spaceGroupKey == groupPointer->getAnnotationGroupKey()) { -// spaceGroup = groupPointer.data(); -// break; -// } -// } -// -// if (spaceGroup == NULL) { -// groupingEvent->setErrorMessage("PROGRAM ERROR: Did not find space group for source of grouping annotations"); -// return; -// } - groupingEvent->setEventProcessed(); @@ -1318,7 +1402,6 @@ * Find annotations in ONE space group that were * previously assigned to the previous user group. */ - //AnnotationGroupIterator spaceGroupIter = m_annotationGroups.end(); for (AnnotationGroupIterator spaceGroupIter = m_annotationGroups.begin(); spaceGroupIter != m_annotationGroups.end(); spaceGroupIter++) { @@ -1721,6 +1804,8 @@ } } break; + case ANNOTATION_FILE_DUMMY_FOR_DRAWING: + break; } /* @@ -1760,6 +1845,7 @@ case ANNOTATION_FILE_SAVE_TO_FILE: break; case ANNOTATION_FILE_SAVE_TO_SCENE: + { QString fileContentInString = sceneClass->getStringValue("AnnotationFileContent"); if ( ! fileContentInString.isEmpty()) { try { @@ -1773,6 +1859,9 @@ sceneAttributes->addToErrorMessage(dfe.whatString()); } } + } + break; + case ANNOTATION_FILE_DUMMY_FOR_DRAWING: break; } @@ -2130,9 +2219,6 @@ const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status) { -// m_displayGroupAndTabItemHelper->setSelected(displayGroup, -// tabIndex, -// status); /* * Note: An annotation file's selection status is based * of the the file's annotation groups so we do not need to set @@ -2173,10 +2259,10 @@ * The tile tabs modify event. */ void -AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTileTabsConfigurationModification* modEvent) +AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTileTabsGridConfigurationModification* modEvent) { const int32_t rowColumnIndex = modEvent->getRowColumnIndex(); - const bool rowFlag = (modEvent->getRowColumnType() == EventTileTabsConfigurationModification::RowColumnType::ROW); + const bool rowFlag = (modEvent->getRowColumnType() == EventTileTabsGridConfigurationModification::RowColumnType::ROW); int32_t deleteIndex(-1); int32_t shiftStartIndex(-1); @@ -2186,35 +2272,35 @@ int32_t moveTwoIndex(-1); switch (modEvent->getOperation()) { - case EventTileTabsConfigurationModification::Operation::DELETE_IT: + case EventTileTabsGridConfigurationModification::Operation::DELETE_IT: deleteIndex = rowColumnIndex; shiftStartIndex = rowColumnIndex + 1; break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_AFTER: { shiftStartIndex = rowColumnIndex + 1; duplicateFromIndex = rowColumnIndex; duplicateToIndex = rowColumnIndex + 1; } break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_BEFORE: { shiftStartIndex = rowColumnIndex; duplicateFromIndex = rowColumnIndex + 1; duplicateToIndex = rowColumnIndex; } break; - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_BEFORE: shiftStartIndex = rowColumnIndex; break; - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_AFTER: shiftStartIndex = rowColumnIndex + 1; break; - case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: + case EventTileTabsGridConfigurationModification::Operation::MOVE_AFTER: moveOneIndex = rowColumnIndex; moveTwoIndex = rowColumnIndex + 1; break; - case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::MOVE_BEFORE: moveOneIndex = rowColumnIndex; moveTwoIndex = rowColumnIndex - 1; break; @@ -2242,7 +2328,7 @@ int32_t columnIndex = spacerTabIndex.getColumnIndex(); switch (modEvent->getOperation()) { - case EventTileTabsConfigurationModification::Operation::DELETE_IT: + case EventTileTabsGridConfigurationModification::Operation::DELETE_IT: { int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); if (rcIndex == deleteIndex) { @@ -2264,8 +2350,8 @@ } } break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: - case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_AFTER: + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_BEFORE: { int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); /* @@ -2302,8 +2388,8 @@ } } break; - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_AFTER: { int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); if (rcIndex >= shiftStartIndex) { @@ -2317,8 +2403,8 @@ } } break; - case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: - case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::MOVE_AFTER: + case EventTileTabsGridConfigurationModification::Operation::MOVE_BEFORE: { int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); int32_t newIndex(-1); diff -Nru connectome-workbench-1.4.2/src/Files/AnnotationFile.h connectome-workbench-1.5.0/src/Files/AnnotationFile.h --- connectome-workbench-1.4.2/src/Files/AnnotationFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AnnotationFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -33,7 +33,7 @@ #include "DisplayGroupAndTabItemInterface.h" #include "EventAnnotationGrouping.h" #include "EventListenerInterface.h" -#include "EventTileTabsConfigurationModification.h" +#include "EventTileTabsGridConfigurationModification.h" namespace caret { @@ -66,7 +66,14 @@ * "Brain" for the scene annotation file that is only saved * to scenes and never saved to a file. */ - ANNOTATION_FILE_SAVE_TO_SCENE + ANNOTATION_FILE_SAVE_TO_SCENE, + + /** + * A dummy file is used when drawing annotations as + * some data types (color bars) are drawn as annotations + * but are not in an annotation file + */ + ANNOTATION_FILE_DUMMY_FOR_DRAWING }; AnnotationFile(); @@ -209,6 +216,14 @@ const int32_t toTabIndex); bool removeAnnotationsInTab(const int32_t tabIndex); + + static void removeAnnotationsInTabGroupAux(const int32_t tabIndex, + std::vector>& annotationsGroups, + std::vector>& removedGroupsOut); + + void preserveAnnotationsInClosedTab(const int32_t tabIndex); + + void restoreAnnotationsInReopendTab(const int32_t tabIndex); int32_t generateUniqueKey(); @@ -218,7 +233,7 @@ AnnotationGroup* getSpaceAnnotationGroup(const Annotation* annotation); - void updateSpacerAnnotationsAfterTileTabsModification(const EventTileTabsConfigurationModification* modEvent); + void updateSpacerAnnotationsAfterTileTabsModification(const EventTileTabsGridConfigurationModification* modEvent); const AnnotationFileSubType m_fileSubType; @@ -230,7 +245,9 @@ int32_t m_uniqueKeyGenerator; - std::vector > m_annotationGroups; + std::vector> m_annotationGroups; + + std::vector> m_closedTabAnnotationGroups; /** * Contains annotation that have been delete/removed so that @@ -242,13 +259,20 @@ typedef std::vector >::const_iterator AnnotationGroupConstIterator; + /** + * If true: When an browser tab is closed, any tab-space annotations are moved to 'm_removedAnnotations' and if the + * tab is reopend, the annotations are moved back to 'm_annotationGroups'. + * If false, When a browser tab is closed, no changes are made with annotations. + */ + static bool s_preserveRestoreDeletedTabFlag; + // ADD_NEW_MEMBERS_HERE friend class AnnotationFileXmlReader; }; #ifdef __ANNOTATION_FILE_DECLARE__ - // + bool AnnotationFile::s_preserveRestoreDeletedTabFlag = true; #endif // __ANNOTATION_FILE_DECLARE__ } // namespace diff -Nru connectome-workbench-1.4.2/src/Files/AnnotationFileXmlFormatBase.h connectome-workbench-1.5.0/src/Files/AnnotationFileXmlFormatBase.h --- connectome-workbench-1.4.2/src/Files/AnnotationFileXmlFormatBase.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AnnotationFileXmlFormatBase.h 2021-02-16 19:46:47.000000000 +0000 @@ -52,6 +52,8 @@ static const QString ATTRIBUTE_BACKGROUND_CUSTOM_RGBA; + static const QString ATTRIBUTE_COORDINATE_LIST_COUNT; + static const QString ATTRIBUTE_COORDINATE_SPACE; static const QString ATTRIBUTE_COORD_X; @@ -136,6 +138,10 @@ static const QString ELEMENT_BOX; + static const QString ELEMENT_COORDINATE; + + static const QString ELEMENT_COORDINATE_LIST; + static const QString ELEMENT_COORDINATE_ONE; static const QString ELEMENT_COORDINATE_TWO; @@ -156,6 +162,8 @@ static const QString ELEMENT_POINT_SIZE_TEXT; + static const QString ELEMENT_POLY_LINE; + static const QString ELEMENT_TEXT_OBSOLETE; static const QString ELEMENT_TEXT_DATA; @@ -178,6 +186,8 @@ const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORDINATE_SPACE = "coordinateSpace"; + const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORDINATE_LIST_COUNT = "count"; + const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_X = "x"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_Y = "y"; @@ -260,6 +270,10 @@ const QString AnnotationFileXmlFormatBase::ELEMENT_BOX = "box"; + const QString AnnotationFileXmlFormatBase::ELEMENT_COORDINATE = "coord"; + + const QString AnnotationFileXmlFormatBase::ELEMENT_COORDINATE_LIST = "coordList"; + const QString AnnotationFileXmlFormatBase::ELEMENT_COORDINATE_ONE = "coordOne"; const QString AnnotationFileXmlFormatBase::ELEMENT_COORDINATE_TWO = "coordTwo"; @@ -280,6 +294,8 @@ const QString AnnotationFileXmlFormatBase::ELEMENT_POINT_SIZE_TEXT = "pointSizeText"; + const QString AnnotationFileXmlFormatBase::ELEMENT_POLY_LINE = "polyLine"; + const QString AnnotationFileXmlFormatBase::ELEMENT_TEXT_OBSOLETE = "text"; const QString AnnotationFileXmlFormatBase::ELEMENT_TEXT_DATA = "textData"; diff -Nru connectome-workbench-1.4.2/src/Files/AnnotationFileXmlReader.cxx connectome-workbench-1.5.0/src/Files/AnnotationFileXmlReader.cxx --- connectome-workbench-1.4.2/src/Files/AnnotationFileXmlReader.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AnnotationFileXmlReader.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -34,6 +34,7 @@ #include "AnnotationImage.h" #include "AnnotationLine.h" #include "AnnotationOval.h" +#include "AnnotationPolyLine.h" #include "AnnotationPercentSizeText.h" #include "AnnotationPointSizeText.h" #include "CaretAssert.h" @@ -231,50 +232,50 @@ } else if (elementName == ELEMENT_BOX) { CaretPointer annotation(new AnnotationBox(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_BOX, + readOneCoordinateAnnotation(ELEMENT_BOX, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_IMAGE) { CaretPointer annotation(new AnnotationImage(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_IMAGE, + readOneCoordinateAnnotation(ELEMENT_IMAGE, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_LINE) { CaretPointer annotation(new AnnotationLine(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readOneDimensionalAnnotation(ELEMENT_LINE, + readTwoCoordinateAnnotation(ELEMENT_LINE, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_OVAL) { CaretPointer annotation(new AnnotationOval(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_OVAL, + readOneCoordinateAnnotation(ELEMENT_OVAL, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_PERCENT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_PERCENT_SIZE_TEXT, + readOneCoordinateAnnotation(ELEMENT_PERCENT_SIZE_TEXT, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_PERCENT_WIDTH_SIZE_TEXT) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL, AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_WIDTH)); - readTwoDimensionalAnnotation(ELEMENT_PERCENT_WIDTH_SIZE_TEXT, + readOneCoordinateAnnotation(ELEMENT_PERCENT_WIDTH_SIZE_TEXT, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_POINT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPointSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_POINT_SIZE_TEXT, + readOneCoordinateAnnotation(ELEMENT_POINT_SIZE_TEXT, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_TEXT_OBSOLETE) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_TEXT_OBSOLETE, + readOneCoordinateAnnotation(ELEMENT_TEXT_OBSOLETE, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } @@ -598,7 +599,7 @@ } /** - * Read a one dimensional annotation. + * Read a two coordinate annotation. * * @param annotationElementName * Name of one-dimensional attribute. @@ -606,8 +607,8 @@ * One-dimensional annotation that has its data read. */ void -AnnotationFileXmlReader::readOneDimensionalAnnotation(const QString& annotationElementName, - AnnotationOneDimensionalShape* annotation) +AnnotationFileXmlReader::readTwoCoordinateAnnotation(const QString& annotationElementName, + AnnotationTwoCoordinateShape* annotation) { CaretAssert(annotation); @@ -636,6 +637,54 @@ } /** + * Read a multi coordinate annotation. + * + * @param annotationElementName + * Name of one-dimensional attribute. + * @param annotation + * One-dimensional annotation that has its data read. + */ +void +AnnotationFileXmlReader::readMultiCoordinateAnnotation(const QString& annotationElementName, + AnnotationMultiCoordinateShape* annotation) +{ + CaretAssert(annotation); + + const QXmlStreamAttributes attributes = m_stream->attributes(); + + readAnnotationAttributes(annotation, + annotationElementName, + attributes); + + if (m_stream->readNextStartElement()) { + if (m_stream->name() == ELEMENT_COORDINATE_LIST) { + const QXmlStreamAttributes coordListAtts(m_stream->attributes()); + const int32_t numberOfCoordiantes = m_streamHelper->getRequiredAttributeIntValue(coordListAtts, + ELEMENT_COORDINATE_LIST, + ATTRIBUTE_COORDINATE_LIST_COUNT); + for (int32_t i = 0; i < numberOfCoordiantes; i++) { + AnnotationCoordinate* ac = new AnnotationCoordinate(annotation->m_attributeDefaultType); + readCoordinate(ELEMENT_COORDINATE, ac); + annotation->addCoordinate(ac); + } + + m_stream->skipCurrentElement(); + } + else { + m_streamHelper->throwDataFileException("Expected elment " + + ELEMENT_COORDINATE_LIST + + " but read element " + + m_stream->name().toString()); + + } + } + else { + m_streamHelper->throwDataFileException("Failed to multi-coordinate child element " + + ELEMENT_COORDINATE_LIST); + } +} + +/** * Read an annotation group. * * @param annotationFile @@ -713,55 +762,68 @@ if (elementName == ELEMENT_BOX) { CaretPointer annotation(new AnnotationBox(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_BOX, + readOneCoordinateAnnotation(ELEMENT_BOX, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_IMAGE) { CaretPointer annotation(new AnnotationImage(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_IMAGE, + readOneCoordinateAnnotation(ELEMENT_IMAGE, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_LINE) { CaretPointer annotation(new AnnotationLine(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readOneDimensionalAnnotation(ELEMENT_LINE, + readTwoCoordinateAnnotation(ELEMENT_LINE, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_OVAL) { CaretPointer annotation(new AnnotationOval(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_OVAL, + readOneCoordinateAnnotation(ELEMENT_OVAL, annotation); annotations.push_back(annotation.releasePointer()); } + else if (elementName == ELEMENT_POLY_LINE) { + CaretPointer annotation(new AnnotationPolyLine(AnnotationAttributesDefaultTypeEnum::NORMAL)); + readMultiCoordinateAnnotation(ELEMENT_POLY_LINE, + annotation); + annotations.push_back(annotation.releasePointer()); + } else if (elementName == ELEMENT_PERCENT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_PERCENT_SIZE_TEXT, + readOneCoordinateAnnotation(ELEMENT_PERCENT_SIZE_TEXT, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_PERCENT_WIDTH_SIZE_TEXT) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_PERCENT_WIDTH_SIZE_TEXT, + readOneCoordinateAnnotation(ELEMENT_PERCENT_WIDTH_SIZE_TEXT, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_POINT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPointSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_POINT_SIZE_TEXT, + readOneCoordinateAnnotation(ELEMENT_POINT_SIZE_TEXT, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_TEXT_OBSOLETE) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); - readTwoDimensionalAnnotation(ELEMENT_TEXT_OBSOLETE, + readOneCoordinateAnnotation(ELEMENT_TEXT_OBSOLETE, annotation); annotations.push_back(annotation.releasePointer()); } else { - m_streamHelper->throwDataFileException("Unexpected XML element " - + elementName); + /* + * Issue warning (instead of fatal error) if unrecognized element found. + * Will skip over remainder of element later in code. + */ + annotationFile->addFileReadWarning("Unrecognized element \"" + + elementName + + "\" and content ignored. Updating Workbench " + "may correct this problem."); + skipCurrentElementFlag = true; } /* @@ -791,8 +853,8 @@ * Two-dimensional annotation that has its data read. */ void -AnnotationFileXmlReader::readTwoDimensionalAnnotation(const QString& annotationElementName, - AnnotationTwoDimensionalShape* annotation) +AnnotationFileXmlReader::readOneCoordinateAnnotation(const QString& annotationElementName, + AnnotationOneCoordinateShape* annotation) { CaretAssert(annotation); diff -Nru connectome-workbench-1.4.2/src/Files/AnnotationFileXmlReader.h connectome-workbench-1.5.0/src/Files/AnnotationFileXmlReader.h --- connectome-workbench-1.4.2/src/Files/AnnotationFileXmlReader.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AnnotationFileXmlReader.h 2021-02-16 19:46:47.000000000 +0000 @@ -35,9 +35,10 @@ class AnnotationFile; class AnnotationGroup; class AnnotationImage; - class AnnotationOneDimensionalShape; + class AnnotationMultiCoordinateShape; + class AnnotationTwoCoordinateShape; class AnnotationText; - class AnnotationTwoDimensionalShape; + class AnnotationOneCoordinateShape; class XmlStreamReaderHelper; class AnnotationFileXmlReader : public AnnotationFileXmlFormatBase { @@ -69,11 +70,14 @@ void readGroup(AnnotationFile* annotationFile); - void readOneDimensionalAnnotation(const QString& annotationElementName, - AnnotationOneDimensionalShape* annotation); + void readTwoCoordinateAnnotation(const QString& annotationElementName, + AnnotationTwoCoordinateShape* annotation); - void readTwoDimensionalAnnotation(const QString& annotationElementName, - AnnotationTwoDimensionalShape* annotation); + void readOneCoordinateAnnotation(const QString& annotationElementName, + AnnotationOneCoordinateShape* annotation); + + void readMultiCoordinateAnnotation(const QString& annotationElementName, + AnnotationMultiCoordinateShape* annotation); void readCoordinate(const QString& coordinateElementName, AnnotationCoordinate* coordinate); diff -Nru connectome-workbench-1.4.2/src/Files/AnnotationFileXmlWriter.cxx connectome-workbench-1.5.0/src/Files/AnnotationFileXmlWriter.cxx --- connectome-workbench-1.4.2/src/Files/AnnotationFileXmlWriter.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AnnotationFileXmlWriter.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -34,6 +34,7 @@ #include "AnnotationImage.h" #include "AnnotationLine.h" #include "AnnotationOval.h" +#include "AnnotationPolyLine.h" #include "AnnotationText.h" #include "CaretAssert.h" #include "DataFileException.h" @@ -183,36 +184,6 @@ m_streamHelper->writeMetaData(annotationFile->getFileMetaData()); -// std::vector annotations; -// annotationFile->getAllAnnotations(annotations); -// const int32_t numberOfAnnotations = static_cast(annotations.size()); -// for (int32_t i = 0; i < numberOfAnnotations; i++) { -// CaretAssertVectorIndex(annotations, i); -// const Annotation* annotation = annotations[i]; -// CaretAssert(annotation); -// -// switch (annotation->getType()) { -// case AnnotationTypeEnum::BOX: -// writeBox(dynamic_cast(annotation)); -// break; -// case AnnotationTypeEnum::COLOR_BAR: -// CaretAssertMessage(0, "Color bar is NEVER written to an annotation file"); -// break; -// case AnnotationTypeEnum::IMAGE: -// writeImage(dynamic_cast(annotation)); -// break; -// case AnnotationTypeEnum::LINE: -// writeLine(dynamic_cast(annotation)); -// break; -// case AnnotationTypeEnum::OVAL: -// writeOval(dynamic_cast(annotation)); -// break; -// case AnnotationTypeEnum::TEXT: -// writeText(dynamic_cast(annotation)); -// break; -// } -// } - std::vector annotationGroups; annotationFile->getAllAnnotationGroups(annotationGroups); @@ -269,6 +240,9 @@ case AnnotationTypeEnum::BOX: writeBox(dynamic_cast(annotation)); break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssertMessage(0, "Browser Tab is NEVER written to an annotation file"); + break; case AnnotationTypeEnum::COLOR_BAR: CaretAssertMessage(0, "Color bar is NEVER written to an annotation file"); break; @@ -281,15 +255,18 @@ case AnnotationTypeEnum::OVAL: writeOval(dynamic_cast(annotation)); break; + case AnnotationTypeEnum::POLY_LINE: + writePolyLine(dynamic_cast(annotation)); + break; + case AnnotationTypeEnum::SCALE_BAR: + CaretAssertMessage(0, "Scale bar is NEVER written to an annotation file"); + break; case AnnotationTypeEnum::TEXT: writeText(dynamic_cast(annotation)); break; } } - // const AString indicesString = AString::fromNumbers(group->getAnnotationUniqueIdentifiers(), " "); - // m_stream->writeCharacters(indicesString); - m_stream->writeEndElement(); } @@ -304,7 +281,7 @@ { CaretAssert(box); - writeTwoDimensionalAnnotation(box, + writeOneCoordinateShapeAnnotation(box, ELEMENT_BOX); } @@ -371,11 +348,26 @@ { CaretAssert(line); - writeOneDimensionalAnnotation(line, + writeTwoCoordinateShapeAnnotation(line, ELEMENT_LINE); } /** + * Write the given annotation poly line in XML. + * + * @param polyLine + * The annotation line. + */ +void +AnnotationFileXmlWriter::writePolyLine(const AnnotationPolyLine* polyLine) +{ + CaretAssert(polyLine); + + writeMultiCoordinateShapeAnnotation(polyLine, + ELEMENT_POLY_LINE); +} + +/** * Write the given annotation oval in XML. * * @param oval @@ -386,7 +378,7 @@ { CaretAssert(oval); - writeTwoDimensionalAnnotation(oval, + writeOneCoordinateShapeAnnotation(oval, ELEMENT_OVAL); } @@ -530,7 +522,7 @@ * XML attributes to which properties are appended. */ void -AnnotationFileXmlWriter::getTwoDimAnnotationPropertiesAsAttributes(const AnnotationTwoDimensionalShape* shape, +AnnotationFileXmlWriter::getTwoDimAnnotationPropertiesAsAttributes(const AnnotationOneCoordinateShape* shape, QXmlStreamAttributes& attributes) { CaretAssert(shape); @@ -549,15 +541,15 @@ } /** - * Write the given two dimensional annotation in XML. + * Write the given one coordinate shape annotation in XML. * * @param shape - * The two-dimensional annotation. + * The one coordinate shape annotation. * @param annotationXmlElement * The XML element for the annotation. */ void -AnnotationFileXmlWriter::writeTwoDimensionalAnnotation(const AnnotationTwoDimensionalShape* shape, +AnnotationFileXmlWriter::writeOneCoordinateShapeAnnotation(const AnnotationOneCoordinateShape* shape, const QString& annotationXmlElement) { CaretAssert(shape); @@ -578,15 +570,15 @@ /** - * Write the given one dimensional annotation in XML. + * Write the given two coordinate shape annotation in XML. * * @param shape - * The one-dimensional annotation. + * The two coordinate shape annotation. * @param annotationXmlElement * The XML element for the annotation. */ void -AnnotationFileXmlWriter::writeOneDimensionalAnnotation(const AnnotationOneDimensionalShape* shape, +AnnotationFileXmlWriter::writeTwoCoordinateShapeAnnotation(const AnnotationTwoCoordinateShape* shape, const QString& annotationXmlElement) { CaretAssert(shape); @@ -615,6 +607,42 @@ m_stream->writeEndElement(); } + +/** + * Write the given mult-coordinate annotation in XML. + * + * @param shape + * The multi-coordinate annotation. + * @param annotationXmlElement + * The XML element for the annotation. + */ +void +AnnotationFileXmlWriter::writeMultiCoordinateShapeAnnotation(const AnnotationMultiCoordinateShape* shape, + const QString& annotationXmlElement) +{ + CaretAssert(shape); + + QXmlStreamAttributes attributes; + getAnnotationPropertiesAsAttributes(shape, + attributes); + + m_stream->writeStartElement(annotationXmlElement); + + m_stream->writeAttributes(attributes); + + const int32_t numCoords = shape->getNumberOfCoordinates(); + + m_stream->writeStartElement(ELEMENT_COORDINATE_LIST); + m_stream->writeAttribute(ATTRIBUTE_COORDINATE_LIST_COUNT, + AString::number(numCoords)); + for (int32_t i = 0; i < numCoords; i++) { + const AnnotationCoordinate* ac = shape->getCoordinate(i); + writeCoordinate(ac, ELEMENT_COORDINATE); + } + m_stream->writeEndElement(); + + m_stream->writeEndElement(); +} /** * Write the given annotation coordinate in XML. diff -Nru connectome-workbench-1.4.2/src/Files/AnnotationFileXmlWriter.h connectome-workbench-1.5.0/src/Files/AnnotationFileXmlWriter.h --- connectome-workbench-1.4.2/src/Files/AnnotationFileXmlWriter.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AnnotationFileXmlWriter.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,10 +37,12 @@ class AnnotationGroup; class AnnotationImage; class AnnotationLine; - class AnnotationOneDimensionalShape; + class AnnotationMultiCoordinateShape; + class AnnotationPolyLine; + class AnnotationTwoCoordinateShape; class AnnotationOval; class AnnotationText; - class AnnotationTwoDimensionalShape; + class AnnotationOneCoordinateShape; class XmlStreamWriterHelper; class AnnotationFileXmlWriter : public AnnotationFileXmlFormatBase { @@ -70,7 +72,7 @@ void getAnnotationPropertiesAsAttributes(const Annotation* annotation, QXmlStreamAttributes& attributes); - void getTwoDimAnnotationPropertiesAsAttributes(const AnnotationTwoDimensionalShape* shape, + void getTwoDimAnnotationPropertiesAsAttributes(const AnnotationOneCoordinateShape* shape, QXmlStreamAttributes& attributes); void writeFileContentToXmlStreamWriter(const AnnotationFile* annotationFile, @@ -87,10 +89,15 @@ void writeLine(const AnnotationLine* line); - void writeOneDimensionalAnnotation(const AnnotationOneDimensionalShape* shape, + void writePolyLine(const AnnotationPolyLine* polyLine); + + void writeMultiCoordinateShapeAnnotation(const AnnotationMultiCoordinateShape* shape, + const QString& annotationXmlElement); + + void writeTwoCoordinateShapeAnnotation(const AnnotationTwoCoordinateShape* shape, const QString& annotationXmlElement); - void writeTwoDimensionalAnnotation(const AnnotationTwoDimensionalShape* shape, + void writeOneCoordinateShapeAnnotation(const AnnotationOneCoordinateShape* shape, const QString& annotationXmlElement); void writeOval(const AnnotationOval* oval); @@ -100,7 +107,7 @@ CaretPointer m_stream; CaretPointer m_streamHelper; - + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/Files/AnnotationTextSubstitutionFile.cxx connectome-workbench-1.5.0/src/Files/AnnotationTextSubstitutionFile.cxx --- connectome-workbench-1.4.2/src/Files/AnnotationTextSubstitutionFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/AnnotationTextSubstitutionFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -330,7 +330,7 @@ AnnotationTextSubstitutionFile::getSubstitutionValues(EventAnnotationTextSubstitutionGet* substituteEvent) const { const std::vector& substitutionNames = substituteEvent->getSubstitutionNames(); - for (const auto name : substitutionNames) { + for (const auto& name : substitutionNames) { AString value = getTextSubstitution(name, getSelectedValueIndex()); substituteEvent->setSubstitutionValueForName(name, diff -Nru connectome-workbench-1.4.2/src/Files/CaretDataFile.cxx connectome-workbench-1.5.0/src/Files/CaretDataFile.cxx --- connectome-workbench-1.4.2/src/Files/CaretDataFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CaretDataFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -455,6 +455,45 @@ return name; } +/** + * @return File casted to an image file (avoids use of dynamic_cast that can be slow) + */ +ImageFile* +CaretDataFile::castToImageFile() +{ + return NULL; +} + +/** + * @return File casted to an image file (avoids use of dynamic_cast that can be slow) + * Overidden in ImageFile + */ +const ImageFile* +CaretDataFile::castToImageFile() const +{ + return NULL; +} + + +/** + * @return File casted to an media file (avoids use of dynamic_cast that can be slow) + * Overidden in MediaFile + */ +MediaFile* +CaretDataFile::castToMediaFile() +{ + return NULL; +} + +/** + * @return File casted to an media file (avoids use of dynamic_cast that can be slow) + * Overidden in ImageFile + */ +const MediaFile* +CaretDataFile::castToMediaFile() const +{ + return NULL; +} diff -Nru connectome-workbench-1.4.2/src/Files/CaretDataFile.h connectome-workbench-1.5.0/src/Files/CaretDataFile.h --- connectome-workbench-1.4.2/src/Files/CaretDataFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CaretDataFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -29,6 +29,8 @@ namespace caret { + class ImageFile; + class MediaFile; class GiftiMetaData; class CaretDataFile : public DataFile, public SceneableInterface { @@ -85,6 +87,12 @@ static AString getFileReadingPassword(); + virtual ImageFile* castToImageFile(); + virtual const ImageFile* castToImageFile() const; + + virtual MediaFile* castToMediaFile(); + virtual const MediaFile* castToMediaFile() const; + protected: CaretDataFile(const CaretDataFile& cdf); diff -Nru connectome-workbench-1.4.2/src/Files/CaretMappableDataFile.cxx connectome-workbench-1.5.0/src/Files/CaretMappableDataFile.cxx --- connectome-workbench-1.4.2/src/Files/CaretMappableDataFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CaretMappableDataFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -34,6 +34,7 @@ #include "DataFileContentInformation.h" #include "EventManager.h" #include "FastStatistics.h" +#include "FileIdentificationAttributes.h" #include "FileInformation.h" #include "GiftiLabelTable.h" #include "GiftiMetaDataXmlElements.h" @@ -109,6 +110,7 @@ CaretMappableDataFile::initializeCaretMappableDataFileInstance(const DataFileTypeEnum::Enum /*dataFileType*/) { m_labelDrawingProperties = std::unique_ptr(new LabelDrawingProperties()); + m_fileIdentificationAttributes.reset(new FileIdentificationAttributes()); m_applyToAllMapsSelected = false; } @@ -361,7 +363,9 @@ sceneClass->addClass(m_labelDrawingProperties->saveToScene(sceneAttributes, "m_labelDrawingProperties")); - + sceneClass->addClass(m_fileIdentificationAttributes->saveToScene(sceneAttributes, + "m_fileIdentificationAttributes")); + if (m_chartingDelegate != NULL) { SceneClass* chartDelegateScene = m_chartingDelegate->saveToScene(sceneAttributes, "m_chartingDelegate"); @@ -389,9 +393,26 @@ m_applyToAllMapsSelected); if (sceneAttributes->isModifiedPaletteSettingsSavedToScene()) { + /* + * WB-916 Scene files very large for series type files with + * modified palette color mapping + * + * When one palette coloring mapping is used for a file, + * we only need to save the palette color mapping for the + * first map. Only some CIFTI files, particularly + * series type files, use one palette color mapping for all + * maps. Until 12 Oct 2020, palette color mapping was + * always written for all maps which greatly bloated the + * scene file for scalar data-series files with many rows. + */ + int32_t numMaps = getNumberOfMaps(); + if (numMaps > 0) { + if (isOnePaletteUsedForAllMaps()) { + numMaps = 1; + } + } + std::vector pcmClassVector; - - const int32_t numMaps = getNumberOfMaps(); for (int32_t i = 0; i < numMaps; i++) { const PaletteColorMapping* pcmConst = getMapPaletteColorMapping(i); bool savePaletteFlag = false; @@ -491,6 +512,9 @@ m_labelDrawingProperties->restoreFromScene(sceneAttributes, sceneClass->getClass("m_labelDrawingProperties")); + m_fileIdentificationAttributes->restoreFromScene(sceneAttributes, + sceneClass->getClass("m_fileIdentificationAttributes")); + const SceneClass* chartingDelegateClass = sceneClass->getClass("m_chartingDelegate"); ChartableTwoFileDelegate* chartDelegate = getChartingDelegate(); chartDelegate->updateAfterFileChanged(); @@ -1484,6 +1508,8 @@ * Index of the node. * @param numberOfNodes * Number of nodes in the surface. + * @param dataValueSeparator + * Separator between multiple data values * @param textOut * Output containing identification information. */ @@ -1492,6 +1518,7 @@ const StructureEnum::Enum /*structure*/, const int /*nodeIndex*/, const int32_t /*numberOfNodes*/, + const AString& /*dataValueSeparator*/, AString& textOut) const { textOut.clear(); @@ -1507,12 +1534,15 @@ * Coordinate of voxel. * @param ijkOut * Voxel indices of value. + * @param dataValueSeparator + * Separator between multiple data values * @param textOut * Output containing identification information. */ bool CaretMappableDataFile::getVolumeVoxelIdentificationForMaps(const std::vector& /*mapIndices*/, const float* /*xyz[3]*/, + const AString& /*dataValueSeparator*/, int64_t* /*ijkOut[3]*/, AString& textOut) const { @@ -1520,4 +1550,22 @@ return false; } +/** + * @return The file identification attributes + */ +FileIdentificationAttributes* +CaretMappableDataFile::getFileIdentificationAttributes() +{ + return m_fileIdentificationAttributes.get(); +} + +/** + * @return The file identification attributes (const method) + */ +const FileIdentificationAttributes* +CaretMappableDataFile::getFileIdentificationAttributes() const +{ + return m_fileIdentificationAttributes.get(); +} + diff -Nru connectome-workbench-1.4.2/src/Files/CaretMappableDataFile.h connectome-workbench-1.5.0/src/Files/CaretMappableDataFile.h --- connectome-workbench-1.4.2/src/Files/CaretMappableDataFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CaretMappableDataFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,6 +37,7 @@ class ChartDataCartesian; class ChartableTwoFileDelegate; class FastStatistics; + class FileIdentificationAttributes; class GiftiMetaData; class GiftiLabelTable; class Histogram; @@ -508,16 +509,21 @@ const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, + const AString& dataValueSeparator, AString& textOut) const; virtual bool getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], + const AString& dataValueSeparator, int64_t ijkOut[3], AString& textOut) const; - void updateAfterFileDataChanges(); + FileIdentificationAttributes* getFileIdentificationAttributes(); + + const FileIdentificationAttributes* getFileIdentificationAttributes() const; + protected: CaretMappableDataFile(const CaretMappableDataFile&); @@ -551,6 +557,8 @@ std::vector> m_mapThresholdFileSelectionModels; + std::unique_ptr m_fileIdentificationAttributes; + /** * Added by WB-781 Apply to All Maps for ColorBar. * This value is saved to scenes but NOT to the data file. diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileBaseChart.cxx connectome-workbench-1.5.0/src/Files/ChartableTwoFileBaseChart.cxx --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileBaseChart.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileBaseChart.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -76,8 +76,8 @@ setDefaultAxisTitles(); m_sceneAssistant = new SceneClassAssistant(); - m_sceneAssistant->add("m_bottomAxisTitleText", - &m_bottomAxisTitleText); + m_sceneAssistant->add("m_bottomAxisTitleText", // Continue to use OLD name before top axis was added + &m_bottomTopAxisTitleText); m_sceneAssistant->add("m_leftRightAxisTitleText", &m_leftRightAxisTitleText); @@ -99,7 +99,7 @@ void ChartableTwoFileBaseChart::setDefaultAxisTitles() { - m_bottomAxisTitleText = setDefaultAxisTitle(ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM); + m_bottomTopAxisTitleText = setDefaultAxisTitle(ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM); m_leftRightAxisTitleText = setDefaultAxisTitle(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); } @@ -123,6 +123,24 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: title = "Data"; break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + title = CaretUnitsTypeEnum::toGuiName(m_compoundChartDataType.getLineChartUnitsAxisX()); + switch (m_compoundChartDataType.getLineChartUnitsAxisX()) { + case CaretUnitsTypeEnum::NONE: + title = "Data"; + break; + case CaretUnitsTypeEnum::HERTZ: + break; + case CaretUnitsTypeEnum::METERS: + break; + case CaretUnitsTypeEnum::PARTS_PER_MILLION: + break; + case CaretUnitsTypeEnum::RADIANS: + break; + case CaretUnitsTypeEnum::SECONDS: + break; + } + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: title = CaretUnitsTypeEnum::toGuiName(m_compoundChartDataType.getLineChartUnitsAxisX()); switch (m_compoundChartDataType.getLineChartUnitsAxisX()) { @@ -142,6 +160,22 @@ } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + title = CaretUnitsTypeEnum::toGuiName(m_compoundChartDataType.getLineChartUnitsAxisX()); + switch (m_compoundChartDataType.getLineChartUnitsAxisX()) { + case CaretUnitsTypeEnum::NONE: + title = "Data"; + break; + case CaretUnitsTypeEnum::HERTZ: + break; + case CaretUnitsTypeEnum::METERS: + break; + case CaretUnitsTypeEnum::PARTS_PER_MILLION: + break; + case CaretUnitsTypeEnum::RADIANS: + break; + case CaretUnitsTypeEnum::SECONDS: + break; + } break; } break; @@ -154,10 +188,14 @@ case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: title = "Counts"; break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + title = "Value"; + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: title = "Value"; break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + title = "Value"; break; } } @@ -239,13 +277,13 @@ /** * @return Chart compound data type supported by subclass. */ -ChartTwoCompoundDataType +const ChartTwoCompoundDataType* ChartableTwoFileBaseChart::getChartTwoCompoundDataType() const { CaretAssertMessage((m_compoundChartDataType.getChartTwoDataType() != ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID), "Data type is invalid, was updateChartCompoundDataTypeAfterFileChanges() called by " "implementing class."); - return m_compoundChartDataType; + return &m_compoundChartDataType; } /** @@ -267,25 +305,25 @@ } /** - * @return Annotation for the bottom axis title (const method) + * @return Annotation for the bottom top axis title (const method) */ AString -ChartableTwoFileBaseChart::getBottomAxisTitle() const +ChartableTwoFileBaseChart::getBottomTopAxisTitle() const { - return m_bottomAxisTitleText; + return m_bottomTopAxisTitleText; } /** - * Set the bottom axis title. + * Set the bottom top axis title. * * @param title * New bottom axis title. */ void -ChartableTwoFileBaseChart::setBottomAxisTitle(const AString& title) +ChartableTwoFileBaseChart::setBottomTopAxisTitle(const AString& title) { - if (title != m_bottomAxisTitleText) { - m_bottomAxisTitleText = title; + if (title != m_bottomTopAxisTitleText) { + m_bottomTopAxisTitleText = title; setModified(); } } @@ -370,12 +408,12 @@ sceneClass); if (sceneClass->getVersionNumber() <= 2) { - AnnotationPercentSizeText bottomTitle(AnnotationAttributesDefaultTypeEnum::NORMAL, - AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); - bottomTitle.restoreFromScene(sceneAttributes, - sceneClass->getClass("m_bottomAxisTitle")); - if ( ! bottomTitle.getText().isEmpty()) { - m_bottomAxisTitleText = bottomTitle.getText(); + AnnotationPercentSizeText bottomTopTitle(AnnotationAttributesDefaultTypeEnum::NORMAL, + AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT); + bottomTopTitle.restoreFromScene(sceneAttributes, + sceneClass->getClass("m_bottomAxisTitle")); // Use OLD name before top axis was added + if ( ! bottomTopTitle.getText().isEmpty()) { + m_bottomTopAxisTitleText = bottomTopTitle.getText(); } AnnotationPercentSizeText leftRightTitle(AnnotationAttributesDefaultTypeEnum::NORMAL, diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileBaseChart.h connectome-workbench-1.5.0/src/Files/ChartableTwoFileBaseChart.h --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileBaseChart.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileBaseChart.h 2021-02-16 19:46:47.000000000 +0000 @@ -64,11 +64,11 @@ ChartTwoDataTypeEnum::Enum getChartTwoDataType() const; - ChartTwoCompoundDataType getChartTwoCompoundDataType() const; + const ChartTwoCompoundDataType* getChartTwoCompoundDataType() const; - AString getBottomAxisTitle() const; + AString getBottomTopAxisTitle() const; - void setBottomAxisTitle(const AString& title); + void setBottomTopAxisTitle(const AString& title); AString getLeftRightAxisTitle() const; @@ -121,7 +121,7 @@ ChartTwoCompoundDataType m_compoundChartDataType; - AString m_bottomAxisTitleText; + AString m_bottomTopAxisTitleText; AString m_leftRightAxisTitleText; diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileDelegate.cxx connectome-workbench-1.5.0/src/Files/ChartableTwoFileDelegate.cxx --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileDelegate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileDelegate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -28,6 +28,7 @@ #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineLayerChart.h" #include "ChartableTwoFileLineSeriesChart.h" #include "ChartableTwoFileMatrixChart.h" #include "CiftiConnectivityMatrixParcelFile.h" @@ -82,6 +83,7 @@ ChartTwoHistogramContentTypeEnum::Enum histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_UNSUPPORTED; ChartTwoLineSeriesContentTypeEnum::Enum lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED; + ChartTwoLineLayerContentTypeEnum::Enum lineLayerType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED; ChartTwoMatrixContentTypeEnum::Enum matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED; std::vector validMatrixRowColumnSelectionDimensions; @@ -100,10 +102,12 @@ break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineLayerType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA; lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineLayerType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA; lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: @@ -121,17 +125,20 @@ break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineLayerType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA; lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_BRAINORDINATE_MAPPABLE; validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineLayerType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA; lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_BRAINORDINATE_MAPPABLE; validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN); break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + lineLayerType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA; lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_ROW_SCALAR_DATA; matrixType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_SCALARS; validMatrixRowColumnSelectionDimensions.push_back(ChartTwoMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW); @@ -154,6 +161,7 @@ break; case DataFileTypeEnum::METRIC: histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineLayerType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_BRAINORDINATE_DATA; lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; break; case DataFileTypeEnum::METRIC_DYNAMIC: @@ -174,6 +182,7 @@ case DataFileTypeEnum::VOLUME: if ( ! m_caretMappableDataFile->isMappedWithLabelTable()) { histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + lineLayerType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_BRAINORDINATE_DATA; lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; } break; @@ -192,6 +201,16 @@ m_caretMappableDataFile)); } + if (m_lineLayerCharting) { + if (lineLayerType != m_lineLayerCharting->getLineLayerContentType()) { + m_lineLayerCharting.reset(); + } + } + if ( ! m_lineLayerCharting) { + m_lineLayerCharting = std::unique_ptr(new ChartableTwoFileLineLayerChart(lineLayerType, + m_caretMappableDataFile)); + } + if (m_lineSeriesCharting) { if (lineSeriesType != m_lineSeriesCharting->getLineSeriesContentType()) { m_lineSeriesCharting.reset(); @@ -245,6 +264,9 @@ m_histogramCharting = std::unique_ptr(new ChartableTwoFileHistogramChart(ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_UNSUPPORTED, m_caretMappableDataFile)); + m_lineLayerCharting = std::unique_ptr(new ChartableTwoFileLineLayerChart(ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED, + m_caretMappableDataFile)); + m_lineSeriesCharting = std::unique_ptr(new ChartableTwoFileLineSeriesChart(ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED, m_caretMappableDataFile)); @@ -261,6 +283,7 @@ ChartableTwoFileDelegate::clearModified() { m_histogramCharting->clearModified(); + m_lineLayerCharting->clearModified(); m_lineSeriesCharting->clearModified(); m_matrixCharting->clearModified(); } @@ -277,6 +300,9 @@ if (m_matrixCharting->isModified()) { return true; } + if (m_lineLayerCharting->isModified()) { + return true; + } if (m_lineSeriesCharting->isModified()) { return true; } @@ -323,6 +349,24 @@ } /** + * @return Line layer charting. + */ +ChartableTwoFileLineLayerChart* +ChartableTwoFileDelegate::getLineLayerCharting() +{ + return m_lineLayerCharting.get(); +} + +/** + * @return Line layer charting. + */ +const ChartableTwoFileLineLayerChart* +ChartableTwoFileDelegate::getLineLayerCharting() const +{ + return m_lineLayerCharting.get(); +} + +/** * @return Line series charting. */ ChartableTwoFileLineSeriesChart* @@ -404,11 +448,11 @@ bool ChartableTwoFileDelegate::isChartingSupportedForChartTwoCompoundDataType(const ChartTwoCompoundDataType& chartCompoundDataType) const { - std::vector chartCompoundDataTypes; + std::vector chartCompoundDataTypes; getSupportedChartTwoCompoundDataTypes(chartCompoundDataTypes); for (auto& ccdt : chartCompoundDataTypes) { - if (ccdt == chartCompoundDataType) { + if (*ccdt == chartCompoundDataType) { return true; } } @@ -430,6 +474,9 @@ if (m_histogramCharting != NULL) { chartDataTypesOut.push_back(m_histogramCharting->getChartTwoDataType()); } + if (m_lineLayerCharting != NULL) { + chartDataTypesOut.push_back(m_lineLayerCharting->getChartTwoDataType()); + } if (m_lineSeriesCharting!= NULL) { chartDataTypesOut.push_back(m_lineSeriesCharting->getChartTwoDataType()); } @@ -445,13 +492,16 @@ * Output containing all chart data types supported by this data file. */ void -ChartableTwoFileDelegate::getSupportedChartTwoCompoundDataTypes(std::vector& chartCompoundDataTypesOut) const +ChartableTwoFileDelegate::getSupportedChartTwoCompoundDataTypes(std::vector& chartCompoundDataTypesOut) const { chartCompoundDataTypesOut.clear(); if (m_histogramCharting->getHistogramContentType() != ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_UNSUPPORTED) { chartCompoundDataTypesOut.push_back(m_histogramCharting->getChartTwoCompoundDataType()); } + if (m_lineLayerCharting->getLineLayerContentType() != ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED) { + chartCompoundDataTypesOut.push_back(m_lineLayerCharting->getChartTwoCompoundDataType()); + } if (m_lineSeriesCharting->getLineSeriesContentType() != ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED) { chartCompoundDataTypesOut.push_back(m_lineSeriesCharting->getChartTwoCompoundDataType()); } @@ -476,11 +526,11 @@ ChartableTwoFileDelegate::getChartTwoCompoundDataTypeForChartTwoDataType(const ChartTwoDataTypeEnum::Enum chartDataType, ChartTwoCompoundDataType& chartCompoundDataTypeOut) const { - std::vector chartCompoundDataTypes; + std::vector chartCompoundDataTypes; getSupportedChartTwoCompoundDataTypes(chartCompoundDataTypes); for (auto& cdt : chartCompoundDataTypes) { - if (cdt.getChartTwoDataType() == chartDataType) { - chartCompoundDataTypeOut = cdt; + if (cdt->getChartTwoDataType() == chartDataType) { + chartCompoundDataTypeOut = *cdt; return true; } } @@ -519,9 +569,13 @@ sceneClass->addClass(m_histogramCharting->saveToScene(sceneAttributes, "m_histogramCharting")); } + if (m_lineLayerCharting != NULL) { + sceneClass->addClass(m_lineLayerCharting->saveToScene(sceneAttributes, + "m_lineLayerCharting")); + } if (m_lineSeriesCharting != NULL) { sceneClass->addClass(m_lineSeriesCharting->saveToScene(sceneAttributes, - "m_lineSeriesCharting")); + "m_lineSeriesCharting")); } if (m_matrixCharting != NULL) { sceneClass->addClass(m_matrixCharting->saveToScene(sceneAttributes, @@ -560,11 +614,18 @@ histogramClass); } + const SceneClass* lineLayerClass = sceneClass->getClass("m_lineLayerCharting"); + if (lineLayerClass != NULL) { + CaretAssert(m_lineLayerCharting); + m_lineLayerCharting->restoreFromScene(sceneAttributes, + lineLayerClass); + } + const SceneClass* lineSeriesClass = sceneClass->getClass("m_lineSeriesCharting"); if (lineSeriesClass != NULL) { CaretAssert(m_lineSeriesCharting); m_lineSeriesCharting->restoreFromScene(sceneAttributes, - lineSeriesClass); + lineSeriesClass); } const SceneClass* matrixClass = sceneClass->getClass("m_matrixCharting"); diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileDelegate.h connectome-workbench-1.5.0/src/Files/ChartableTwoFileDelegate.h --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileDelegate.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileDelegate.h 2021-02-16 19:46:47.000000000 +0000 @@ -32,6 +32,7 @@ class CaretMappableDataFile; class ChartableTwoFileHistogramChart; + class ChartableTwoFileLineLayerChart; class ChartableTwoFileLineSeriesChart; class ChartableTwoFileMatrixChart; @@ -59,6 +60,10 @@ const ChartableTwoFileHistogramChart* getHistogramCharting() const; + ChartableTwoFileLineLayerChart* getLineLayerCharting(); + + const ChartableTwoFileLineLayerChart* getLineLayerCharting() const; + ChartableTwoFileLineSeriesChart* getLineSeriesCharting(); const ChartableTwoFileLineSeriesChart* getLineSeriesCharting() const; @@ -75,7 +80,7 @@ void getSupportedChartTwoDataTypes(std::vector& chartDataTypesOut) const; - void getSupportedChartTwoCompoundDataTypes(std::vector& chartCompoundDataTypesOut) const; + void getSupportedChartTwoCompoundDataTypes(std::vector& chartCompoundDataTypesOut) const; bool getChartTwoCompoundDataTypeForChartTwoDataType(const ChartTwoDataTypeEnum::Enum chartDataType, ChartTwoCompoundDataType& chartCompoundDataTypeOut) const; @@ -101,6 +106,8 @@ std::unique_ptr m_histogramCharting; + std::unique_ptr m_lineLayerCharting; + std::unique_ptr m_lineSeriesCharting; std::unique_ptr m_matrixCharting; diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileLineLayerChart.cxx connectome-workbench-1.5.0/src/Files/ChartableTwoFileLineLayerChart.cxx --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileLineLayerChart.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileLineLayerChart.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,781 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHARTABLE_TWO_FILE_LINE_LAYER_CHART_DECLARE__ +#include "ChartableTwoFileLineLayerChart.h" +#undef __CHARTABLE_TWO_FILE_LINE_LAYER_CHART_DECLARE__ + +#include "BoundingBox.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "ChartTwoDataCartesian.h" +#include "ChartTwoLineSeriesHistory.h" +#include "CiftiMappableDataFile.h" +#include "CiftiScalarDataSeriesFile.h" +#include "DeveloperFlagsEnum.h" +#include "EventChartTwoLoadLineSeriesData.h" +#include "EventManager.h" +#include "MetricFile.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" +#include "SceneObjectMapIntegerKey.h" +#include "ScenePrimitiveArray.h" +#include "VolumeFile.h" + +using namespace caret; + +/** + * \class caret::ChartableTwoFileLineLayerChart + * \brief Implementation of base chart delegate for line layer charts. + * \ingroup Files + */ + +/** + * Constructor. + * + * @param lineLayerContentType + * Content type of the line series data. + * @param parentCaretMappableDataFile + * Parent caret mappable data file that this delegate supports. + */ +ChartableTwoFileLineLayerChart::ChartableTwoFileLineLayerChart(const ChartTwoLineLayerContentTypeEnum::Enum lineLayerContentType, + CaretMappableDataFile* parentCaretMappableDataFile) +: ChartableTwoFileBaseChart(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER, + parentCaretMappableDataFile), +m_lineLayerContentType(lineLayerContentType) +{ + CaretUnitsTypeEnum::Enum xAxisUnits = CaretUnitsTypeEnum::NONE; + int32_t xAxisNumberOfElements = 0; + + const CaretMappableDataFile* cmdf = getCaretMappableDataFile(); + CaretAssert(cmdf); + + int64_t numberOfChartMaps(0); + + switch (lineLayerContentType) { + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED: + break; + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_BRAINORDINATE_DATA: + if (cmdf->getNumberOfMaps() > 1) { + const NiftiTimeUnitsEnum::Enum mapUnits = cmdf->getMapIntervalUnits(); + xAxisUnits = CaretUnitsTypeEnum::NONE; + switch (mapUnits) { + case NiftiTimeUnitsEnum::NIFTI_UNITS_HZ: + xAxisUnits = CaretUnitsTypeEnum::HERTZ; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_PPM: + xAxisUnits = CaretUnitsTypeEnum::PARTS_PER_MILLION; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_SEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: + break; + } + xAxisNumberOfElements = cmdf->getNumberOfMaps(); + + if (cmdf->getDataFileType() == DataFileTypeEnum::METRIC) { + const MetricFile* mf = dynamic_cast(cmdf); + numberOfChartMaps = mf->getNumberOfNodes(); + } + else if (cmdf->getDataFileType() == DataFileTypeEnum::VOLUME) { + const VolumeFile* vf = dynamic_cast(cmdf); + CaretAssert(vf); + std::vector dims; + vf->getDimensions(dims); + if (dims.size() > 3) { + numberOfChartMaps = dims[0] * dims[1] * dims[2]; + } + } + else { + CaretAssertMessage(0, "Unsupported file type for brainordinate data"); + } + } + break; + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA: + { + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + if (ciftiMapFile != NULL) { + CaretAssert(ciftiMapFile); + std::vector dims; + ciftiMapFile->getMapDimensions(dims); + CaretAssertVectorIndex(dims, 1); + const int32_t numCols = dims[0]; + const int32_t numRows = dims[1]; + + if ((numRows > 0) + && (numCols > 1)) { + numberOfChartMaps = numRows; + + const NiftiTimeUnitsEnum::Enum mapUnits = ciftiMapFile->getMapIntervalUnits(); + xAxisUnits = CaretUnitsTypeEnum::NONE; + switch (mapUnits) { + case NiftiTimeUnitsEnum::NIFTI_UNITS_HZ: + xAxisUnits = CaretUnitsTypeEnum::HERTZ; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_PPM: + xAxisUnits = CaretUnitsTypeEnum::PARTS_PER_MILLION; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_SEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: + break; + } + xAxisNumberOfElements = numCols; + } + } + } + break; + } + + /* + * For Cifti Files, use units from dimensions + */ + CaretUnitsTypeEnum::Enum yAxisUnits = CaretUnitsTypeEnum::NONE; + { + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + if (ciftiMapFile != NULL) { + float start(0.0), step(0.0); + ciftiMapFile->getDimensionUnits(CiftiXML::ALONG_ROW, + xAxisUnits, + start, + step); + ciftiMapFile->getDimensionUnits(CiftiXML::ALONG_COLUMN, + yAxisUnits, + start, + step); + } + } + + /* + * Must have two or more elements + */ + if (xAxisNumberOfElements <= 1) { + m_lineLayerContentType = ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED; + } + + updateChartTwoCompoundDataTypeAfterFileChanges(ChartTwoCompoundDataType::newInstanceForLineLayer(xAxisUnits, + yAxisUnits, + xAxisNumberOfElements)); + m_mapLineCharts.resize(numberOfChartMaps); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); +} + +/** + * Destructor. + */ +ChartableTwoFileLineLayerChart::~ChartableTwoFileLineLayerChart() +{ + /* + * If events added, remove only events add by this instance + * since parent also receives events + */ +} + +/** + * Clear the chart lines + */ +void +ChartableTwoFileLineLayerChart::clearChartLines() +{ + for (auto& ptr : m_mapLineCharts) { + ptr.reset(); + } +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +ChartableTwoFileLineLayerChart::receiveEvent(Event* event) +{ + ChartableTwoFileBaseChart::receiveEvent(event); +} + +/** + * THIS METHOD IS FOR USE ONLY BY ChartTwoOverlay instances since the chart two overlay may + * transform the data. + * + * Get a bounding box for map data displayed within this overlay. + * Bounds are provided for histogram and line-series charts only. + * @param mapIndex + * Index of map + * @param boundingBox + * Upon exit contains bounds for data within this overlay + * @return + * True if the bounds are valid, else false. + */bool +ChartableTwoFileLineLayerChart::getBoundsForChartTwoOverlay(const int32_t mapIndex, + BoundingBox& boundingBoxOut) const +{ + return getBoundsPrivate(mapIndex, + boundingBoxOut); +} + +/** + * Get a bounding box for map data displayed within this overlay. + * Bounds are provided for histogram and line-series charts only. + * @param mapIndex + * Index of map + * @param boundingBox + * Upon exit contains bounds for data within this overlay + * @return + * True if the bounds are valid, else false. + */bool +ChartableTwoFileLineLayerChart::getBoundsPrivate(const int32_t mapIndex, + BoundingBox& boundingBoxOut) const +{ + boundingBoxOut.resetForUpdate(); + ChartableTwoFileLineLayerChart* nonConst = const_cast(this); + CaretAssert(nonConst); + const ChartTwoDataCartesian* cd = nonConst->getChartMapLinePrivate(mapIndex); + if (cd != NULL) { + cd->getBounds(boundingBoxOut); + return true; + } + return false; +} + +/** + * @return Number of chart maps (usually different that number of brainmapped maps). + */ +int32_t +ChartableTwoFileLineLayerChart::getNumberOfChartMaps() const +{ + return m_mapLineCharts.size(); +} + +/** + * Get map names for a CIFTI Brain Models map + * @param brainModelsMap + * The brain models map + * @param mapNames + * Output with map names + */ +void +ChartableTwoFileLineLayerChart::getMapNamesFromCiftiBrainMap(const CiftiBrainModelsMap& brainModelsMap, + std::vector& mapNames) +{ + const int64_t numNames = brainModelsMap.getLength(); + if (numNames == static_cast(mapNames.size())) { + std::vector surfaceStructures = brainModelsMap.getSurfaceStructureList(); + for (auto s : surfaceStructures) { + const AString name(StructureEnum::toGuiName(s)); + std::vector surfaceMap = brainModelsMap.getSurfaceMap(s); + for (auto sm : surfaceMap) { + CaretAssertVectorIndex(mapNames, sm.m_ciftiIndex); + mapNames[sm.m_ciftiIndex] = (name + + " Vertex " + + AString::number(sm.m_surfaceNode + 1)); + } + + } + + std::vector volumeStructures = brainModelsMap.getVolumeStructureList(); + for (auto vs : volumeStructures) { + const AString name(StructureEnum::toGuiName(vs)); + std::vector volumeMap = brainModelsMap.getVolumeStructureMap(vs); + for (auto vm : volumeMap) { + CaretAssertVectorIndex(mapNames, vm.m_ciftiIndex); + mapNames[vm.m_ciftiIndex] = (name + + " Voxel IJK " + + AString::fromNumbers(vm.m_ijk, 3, ",")); + } + } + } +} + +/** + * Get the name of the maps for the line charts + */ +void +ChartableTwoFileLineLayerChart::getChartMapNames(std::vector& mapNamesOut) +{ + if (m_mapLineChartNames.size() != m_mapLineCharts.size()) { + const int32_t numMaps = static_cast(m_mapLineCharts.size()); + if (numMaps > 0) { + m_mapLineChartNames.resize(numMaps); + switch (m_lineLayerContentType) { + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED: + break; + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_BRAINORDINATE_DATA: + { + const CaretMappableDataFile* cmdf = getCaretMappableDataFile(); + CaretAssert(cmdf); + if (cmdf->getDataFileType() == DataFileTypeEnum::METRIC) { + const MetricFile* mf = dynamic_cast(cmdf); + const int32_t numVertices = mf->getNumberOfNodes(); + for (int32_t i = 0; i < numVertices; i++) { + m_mapLineChartNames[i] = ("Vertex " + + QString::number(i)); + } + } + else if (cmdf->getDataFileType() == DataFileTypeEnum::VOLUME) { + if ( ! m_volumeAttributesValid) { + setVolumeMapNamesAndVoxelXYZ(); + } + } + else { + CaretAssertMessage(0, "Unsupported file type for brainordinate data"); + } + } + break; + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA: + { + CiftiMappableDataFile* ciftiFile = getCiftiMappableDataFile(); + if (ciftiFile != NULL) { + const CiftiXML& ciftiXML = ciftiFile->getCiftiXML(); + const int mapDirection(CiftiXML::ALONG_COLUMN); + + if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) { + getMapNamesFromCiftiBrainMap(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN), + m_mapLineChartNames); + } + else { + const CiftiMappingType* cmt = ciftiXML.getMap(mapDirection); + if (cmt != NULL) { + const int64_t len = cmt->getLength(); + CaretAssert(numMaps == len); + if (numMaps == len) { + for (int64_t i = 0; i < len; i++) { + m_mapLineChartNames[i] = cmt->getIndexName(i); + } + } + } + } + } + } + break; + } + } + else { + m_mapLineChartNames.clear(); + } + } + + mapNamesOut = m_mapLineChartNames; +} + +/** + * Set the volume map names and VoxelXYZ + */ +void +ChartableTwoFileLineLayerChart::setVolumeMapNamesAndVoxelXYZ() +{ + const int64_t numMaps = static_cast(m_mapLineCharts.size()); + m_mapLineChartNames.resize(numMaps); + + m_voxelXYZ.resize(numMaps * 3); + + const CaretMappableDataFile* cmdf = getCaretMappableDataFile(); + CaretAssert(cmdf); + const VolumeFile* vf = dynamic_cast(cmdf); + CaretAssert(vf); + int64_t dimI(0), dimJ(0), dimK(0), dimComp(0), dimTime(0); + vf->getDimensions(dimI, dimJ, dimK, dimTime, dimComp); + const int64_t sliceSize(dimI * dimJ); + for (int64_t k = 0; k < dimK; k++) { + for (int64_t j = 0; j < dimJ; j++) { + for (int64_t i = 0; i < dimI; i++) { + const int64_t indx = ((k * sliceSize) + + (j * dimI) + + i); + float xyz[3]; + vf->indexToSpace(i, j, k, xyz); + CaretAssertVectorIndex(m_voxelXYZ, indx * 3 + 2); + m_voxelXYZ[indx * 3] = xyz[0]; + m_voxelXYZ[indx * 3 + 1] = xyz[1]; + m_voxelXYZ[indx * 3 + 2] = xyz[2]; + m_mapLineChartNames[indx] = ("Voxel IJK (" + + AString::number(i) + + ", " + + AString::number(j) + + ", " + + AString::number(k) + + ") XYZ (" + + AString::fromNumbers(xyz, 3, ", ") + + ")"); + } + } + } + + m_volumeAttributesValid = true; +} + +/** + * THIS METHOD IS FOR USE ONLY BY ChartTwoOverlay instances since the chart two overlay may + * transform the data. + * + * Get the chart lines for a map. + * @param chartMapIndex + * Index of the map + * @retrurn Line chart for map or NULL if not available. + */ +ChartTwoDataCartesian* +ChartableTwoFileLineLayerChart::getChartMapLineForChartTwoOverlay(const int32_t chartMapIndex) +{ + return getChartMapLinePrivate(chartMapIndex); +} + +/** + * Get the chart lines for a map + * @param chartMapIndex + * Index of the map + * @retrurn Line chart for map or NULL if not available. + */ +ChartTwoDataCartesian* +ChartableTwoFileLineLayerChart::getChartMapLinePrivate(const int32_t chartMapIndex) +{ + ChartTwoDataCartesian* chartDataOut(NULL); + + CaretMappableDataFile* mapFile = getCaretMappableDataFile(); + CaretAssert(mapFile); + const AString mapFileName(mapFile->getFileName()); + + if ((chartMapIndex >= 0) + && (chartMapIndex < static_cast(m_mapLineCharts.size()))) { + CaretAssertVectorIndex(m_mapLineCharts, chartMapIndex); + if ( ! m_mapLineCharts[chartMapIndex]) { + MapFileDataSelector mapFileSelector; + bool loadDataFlag(true); + switch (getLineLayerContentType()) { + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED: + break; + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_BRAINORDINATE_DATA: + { + if (mapFile->getDataFileType() == DataFileTypeEnum::METRIC) { + const MetricFile* mf = dynamic_cast(mapFile); + mapFileSelector.setSurfaceVertex(mf->getStructure(), + mf->getNumberOfNodes(), + chartMapIndex); + loadDataFlag = true; + } + else if (mapFile->getDataFileType() == DataFileTypeEnum::VOLUME) { + if ( ! m_volumeAttributesValid) { + setVolumeMapNamesAndVoxelXYZ(); + } + CaretAssert(dynamic_cast(mapFile)); + CaretAssertVectorIndex(m_voxelXYZ, chartMapIndex * 3 + 2); + mapFileSelector.setVolumeVoxelXYZ(&m_voxelXYZ[chartMapIndex * 3]); + } + else { + CaretAssertMessage(0, "Unsupported file type for brainordinate data"); + } + } + break; + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA: + mapFileSelector.setRowIndex(mapFile, + mapFileName, + chartMapIndex); + loadDataFlag = true; + break; + } + + if (loadDataFlag) { + ChartTwoDataCartesian* cd = loadChartForMapFileSelector(mapFileSelector); + if (cd != NULL) { + CaretAssertVectorIndex(m_mapLineCharts, chartMapIndex); + m_mapLineCharts[chartMapIndex].reset(cd); + } + } + } + + CaretAssertVectorIndex(m_mapLineCharts, chartMapIndex); + chartDataOut = m_mapLineCharts[chartMapIndex].get(); + } + + return chartDataOut; +} + +/** + * Get the chart lines for a map file selector + * @param mapFileDataSelector + * Map file selector + * @retrurn Line chart for map or NULL if not available. + */ +ChartTwoDataCartesian* +ChartableTwoFileLineLayerChart::loadChartForMapFileSelector(const MapFileDataSelector& mapFileDataSelector) +{ + ChartTwoDataCartesian* chartDataOut(NULL); + + bool loadDataFlag = false; + switch (m_lineLayerContentType) { + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED: + return NULL; + break; + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_BRAINORDINATE_DATA: + switch (mapFileDataSelector.getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + loadDataFlag = true; + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + loadDataFlag = true; + break; + } + break; + case ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_ROW_DATA: + switch (mapFileDataSelector.getDataSelectionType()) { + case MapFileDataSelector::DataSelectionType::INVALID: + break; + case MapFileDataSelector::DataSelectionType::COLUMN_DATA: + break; + case MapFileDataSelector::DataSelectionType::ROW_DATA: + loadDataFlag = true; + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: + break; + case MapFileDataSelector::DataSelectionType::SURFACE_VERTICES_AVERAGE: + break; + case MapFileDataSelector::DataSelectionType::VOLUME_XYZ: + break; + } + break; + } + + if (loadDataFlag) { + std::vector data; + getCaretMappableDataFile()->getDataForSelector(mapFileDataSelector, + data); + if ( ! data.empty()) { + CaretAssert(getChartTwoCompoundDataType()->getLineChartNumberOfElementsAxisX() == static_cast(data.size())); + chartDataOut = createChartData(); + chartDataOut->setMapFileDataSelector(mapFileDataSelector); + + float xStart = 0.0f; + float xStep = 0.0f; + getCaretMappableDataFile()->getMapIntervalStartAndStep(xStart, + xStep); + + /* + * Set to inject invalid numbers for testing + */ + const bool testNanFlag(false); + const int32_t numData = static_cast(data.size()); + for (int32_t i = 0; i < numData; i++) { + const float x(xStart + (i * xStep)); + CaretAssertVectorIndex(data, i); + + if (testNanFlag) { + float d = data[i]; + if ((i == 1) + || (i == 50) + || (i == 100)) { + d = qQNaN(); + } + chartDataOut->addPoint(x, d); + } + else { + chartDataOut->addPoint(x, data[i]); + } + } + } + } + + /* + * Default to an invalid color. Color for line layer charts + * comes from Chart Overlay and is set when drawing the chart + */ + chartDataOut->setColorEnum(CaretColorEnum::NONE); + + return chartDataOut; +} + +ChartTwoDataCartesian* +ChartableTwoFileLineLayerChart::createChartData() const +{ + GraphicsPrimitive::PrimitiveType primitiveType = GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN; + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_CHART_OPENGL_LINES)) { + primitiveType = GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP; + } + const CaretUnitsTypeEnum::Enum xUnits = getChartTwoCompoundDataType()->getLineChartUnitsAxisX(); + return new ChartTwoDataCartesian(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER, + xUnits, + CaretUnitsTypeEnum::NONE, + primitiveType); +} + +/** + * @return Content type of the line series data. + */ +ChartTwoLineLayerContentTypeEnum::Enum +ChartableTwoFileLineLayerChart::getLineLayerContentType() const +{ + return m_lineLayerContentType; +} + +/** + * @return Is this charting valid ? + */ +bool +ChartableTwoFileLineLayerChart::isValid() const +{ + return (m_lineLayerContentType != ChartTwoLineLayerContentTypeEnum::LINE_LAYER_CONTENT_UNSUPPORTED); +} + +/** + * @retrurn Is this charting empty (no data at this time) + */ +bool +ChartableTwoFileLineLayerChart::isEmpty() const +{ + if ( ! isValid()) { + return true; + } + + return getCaretMappableDataFile()->isEmpty(); +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +ChartableTwoFileLineLayerChart::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + /* + * If true, save only indices of map and not chart points. + * Indices is much more efficient. + */ + const bool saveIndicesFlag(true); + + SceneObjectMapIntegerKey* objMap(NULL); + std::vector mapIndices; + const int32_t numItems = static_cast(m_mapLineCharts.size()); + for (int32_t i = 0; i < numItems; i++) { + ChartTwoDataCartesian* cd = m_mapLineCharts[i].get(); + if (cd != NULL) { + if (saveIndicesFlag) { + mapIndices.push_back(i); + } + else { + if (objMap == NULL) { + objMap = new SceneObjectMapIntegerKey("m_mapLineCharts", + SceneObjectDataTypeEnum::SCENE_CLASS); + } + const QString name("m_mapLineCharts[" + + AString::number(i) + + "]"); + CaretAssert(objMap); + objMap->addClass(i, + cd->saveToScene(sceneAttributes, + name)); + } + } + } + + if (objMap != NULL) { + sceneClass->addChild(objMap); + } + else if ( ! mapIndices.empty()) { + sceneClass->addIntegerArray("mapIndices", + &mapIndices[0], + mapIndices.size()); + } +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +ChartableTwoFileLineLayerChart::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + clearChartLines(); + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + const ScenePrimitiveArray* mapIndicesArray = sceneClass->getPrimitiveArray("mapIndices"); + if (mapIndicesArray != NULL) { + std::vector mapIndices; + mapIndicesArray->integerVectorValues(mapIndices); + for (auto mi : mapIndices) { + getChartMapLinePrivate(mi); + } + } + else { + const SceneObjectMapIntegerKey* objMap = sceneClass->getMapIntegerKey("m_mapLineCharts"); + if (objMap != NULL) { + if (objMap->getDataType() == SceneObjectDataTypeEnum::SCENE_CLASS) { + const std::vector indexKeys = objMap->getKeys(); + for (const auto key : indexKeys) { + const SceneObject* so = objMap->getObject(key); + if (so != NULL) { + const SceneClass* sc = dynamic_cast(so); + if (sc != NULL) { + ChartTwoDataCartesian* cartData = createChartData(); + CaretAssert(cartData); + cartData->restoreFromScene(sceneAttributes, + sc); + m_mapLineCharts[key].reset(cartData); + } + } + } + } + } + } +} diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileLineLayerChart.h connectome-workbench-1.5.0/src/Files/ChartableTwoFileLineLayerChart.h --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileLineLayerChart.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileLineLayerChart.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,126 @@ +#ifndef __CHARTABLE_TWO_FILE_LINE_LAYER_CHART_H__ +#define __CHARTABLE_TWO_FILE_LINE_LAYER_CHART_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "BrainConstants.h" +#include "ChartableTwoFileBaseChart.h" +#include "ChartTwoLineLayerContentTypeEnum.h" + + +namespace caret { + class BoundingBox; + class ChartTwoDataCartesian; + class CiftiBrainModelsMap; + class MapFileDataSelector; + class SceneClassAssistant; + + class ChartableTwoFileLineLayerChart : public ChartableTwoFileBaseChart { + + public: + ChartableTwoFileLineLayerChart(const ChartTwoLineLayerContentTypeEnum::Enum lineLayerContentType, + CaretMappableDataFile* parentCaretMappableDataFile); + + ChartableTwoFileLineLayerChart(const ChartableTwoFileLineLayerChart&); + + ChartableTwoFileLineLayerChart& operator=(const ChartableTwoFileLineLayerChart&); + + virtual ~ChartableTwoFileLineLayerChart(); + + ChartTwoLineLayerContentTypeEnum::Enum getLineLayerContentType() const; + + virtual bool isValid() const override; + + virtual bool isEmpty() const override; + + virtual void receiveEvent(Event* event) override; + + int32_t getNumberOfChartMaps() const; + + void getChartMapNames(std::vector& mapNamesOut); + + void clearChartLines(); + + // ADD_NEW_METHODS_HERE + + + + + + + protected: + /* Since line may be normalized, must access throught ChartTwoOverlay */ + ChartTwoDataCartesian* getChartMapLineForChartTwoOverlay(const int32_t chartMapIndex); + + /* Since line may be normalized, must access throught ChartTwoOverlay */ + bool getBoundsForChartTwoOverlay(const int32_t mapIndex, + BoundingBox& boundingBoxOut) const; + + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) override; + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + private: + enum class VolumeOpMode { + MAP_NAMES, + VOXEL_XYZ + }; + + ChartTwoDataCartesian* loadChartForMapFileSelector(const MapFileDataSelector& mapFileSelector); + + ChartTwoDataCartesian* createChartData() const; + + void getMapNamesFromCiftiBrainMap(const CiftiBrainModelsMap& brainModelsMap, + std::vector& mapNames); + + void setVolumeMapNamesAndVoxelXYZ(); + + ChartTwoDataCartesian* getChartMapLinePrivate(const int32_t chartMapIndex); + + bool getBoundsPrivate(const int32_t mapIndex, + BoundingBox& boundingBoxOut) const; + + std::unique_ptr m_sceneAssistant; + + std::vector> m_mapLineCharts; + + std::vector m_mapLineChartNames; + + std::vector m_voxelXYZ; + + ChartTwoLineLayerContentTypeEnum::Enum m_lineLayerContentType; + + bool m_volumeAttributesValid = false; + + // ADD_NEW_MEMBERS_HERE + + friend class ChartTwoOverlay; + }; + +#ifdef __CHARTABLE_TWO_FILE_LINE_LAYER_CHART_DECLARE__ +#endif // __CHARTABLE_TWO_FILE_LINE_LAYER_CHART_DECLARE__ + +} // namespace +#endif //__CHARTABLE_TWO_FILE_LINE_LAYER_CHART_H__ diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileLineSeriesChart.cxx connectome-workbench-1.5.0/src/Files/ChartableTwoFileLineSeriesChart.cxx --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileLineSeriesChart.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileLineSeriesChart.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -142,8 +142,28 @@ m_lineSeriesContentType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED; } + /* + * For Cifti Files, use units from dimensions + */ + CaretUnitsTypeEnum::Enum yAxisUnits = CaretUnitsTypeEnum::NONE; + { + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + if (ciftiMapFile != NULL) { + float start(0.0), step(0.0); + ciftiMapFile->getDimensionUnits(CiftiXML::ALONG_ROW, + xAxisUnits, + start, + step); + ciftiMapFile->getDimensionUnits(CiftiXML::ALONG_COLUMN, + yAxisUnits, + start, + step); + } + } + updateChartTwoCompoundDataTypeAfterFileChanges(ChartTwoCompoundDataType::newInstanceForLineSeries(xAxisUnits, - xAxisNumberOfElements)); + yAxisUnits, + xAxisNumberOfElements)); if (m_lineSeriesContentType != ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED) { EventManager::get()->addEventListener(this, @@ -266,8 +286,8 @@ getCaretMappableDataFile()->getDataForSelector(mapFileDataSelector, data); if ( ! data.empty()) { - const CaretUnitsTypeEnum::Enum xUnits = getChartTwoCompoundDataType().getLineChartUnitsAxisX(); - CaretAssert(getChartTwoCompoundDataType().getLineChartNumberOfElementsAxisX() == static_cast(data.size())); + const CaretUnitsTypeEnum::Enum xUnits = getChartTwoCompoundDataType()->getLineChartUnitsAxisX(); + CaretAssert(getChartTwoCompoundDataType()->getLineChartNumberOfElementsAxisX() == static_cast(data.size())); ChartTwoDataCartesian* cartesianData = new ChartTwoDataCartesian(ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES, xUnits, CaretUnitsTypeEnum::NONE, @@ -278,16 +298,11 @@ float xStep = 0.0f; getCaretMappableDataFile()->getMapIntervalStartAndStep(x, xStep); - /* - * Note: Start at index one since line segments are used - */ const int32_t numData = static_cast(data.size()); - for (int32_t i = 1; i < numData; i++) { - CaretAssertVectorIndex(data, i - 1); - cartesianData->addPoint(x, data[i - 1]); + for (int32_t i = 0; i < numData; i++) { + CaretAssertVectorIndex(data, i); + cartesianData->addPoint(x, data[i]); x += xStep; -// CaretAssertVectorIndex(data, i); -// cartesianData->addPoint(x, data[i]); } m_lineChartHistory->addHistoryItem(cartesianData); diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileMatrixChart.cxx connectome-workbench-1.5.0/src/Files/ChartableTwoFileMatrixChart.cxx --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileMatrixChart.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileMatrixChart.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -34,6 +34,7 @@ #include "ConnectivityDataLoaded.h" #include "EventCaretMappableDataFileMapsViewedInOverlays.h" #include "EventManager.h" +#include "GraphicsPrimitiveV3fC4f.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -157,7 +158,8 @@ bool hasColumnParcelsFlag = false; bool hasColumnMapNamesFlag = false; bool hasRowParcelsFlag = false; - bool hasRowNumbersFlag = false; + bool hasRowNumbersFlag = false; + bool hasRowScalarsFlag = false; switch (m_matrixDataFileType) { case MatrixDataFileType::INVALID: CaretAssert(0); @@ -193,6 +195,7 @@ m_scalarDataSeriesFile = dynamic_cast(ciftiMapFile); CaretAssert(m_scalarDataSeriesFile); m_hasRowSelectionFlag = true; + hasRowScalarsFlag = true; break; } @@ -227,6 +230,31 @@ m_rowNumberAndNames.push_back("Row " + AString::number(i + ROW_COLUMN_INDEX_BASE_OFFSET) + ": " + AString::number(i + 1)); } } + else if (hasRowScalarsFlag) { + const CiftiScalarsMap* rowScalarsMap = ciftiMapFile->getCiftiScalarsMapForDirection(CiftiXML::ALONG_COLUMN); + if (rowScalarsMap != NULL) { + const int32_t numRowScalars = rowScalarsMap->getLength(); + CaretAssert(numRowScalars == m_numberOfRows); + for (int32_t i = 0; i < numRowScalars; i++) { + AString rowName(rowScalarsMap->getIndexName(i)); + const AString rowNum(AString::number(i + ROW_COLUMN_INDEX_BASE_OFFSET)); + AString rowNameAndNumber; + if (rowName.isEmpty()) { + rowName = ("Row: " + + rowNum); + rowNameAndNumber = rowName; + } + else { + rowNameAndNumber = ("Row " + + AString::number(i + ROW_COLUMN_INDEX_BASE_OFFSET) + + ": " + + rowName); + } + m_rowNames.push_back(rowName); + m_rowNumberAndNames.push_back(rowNameAndNumber); + } + } + } } if (m_hasColumnSelectionFlag) { @@ -262,23 +290,6 @@ m_columnNumberAndNames.push_back("Column " + AString::number(i + ROW_COLUMN_INDEX_BASE_OFFSET) + ": " + ciftiMapFile->getMapName(i)); } } - -// if (hasColumnMapSelectionFlag) { -// m_columnNames.clear(); -// for (int32_t i = 0; i < m_numberOfColumns; i++) { -// m_columnNames.push_back(ciftiMapFile->getMapName(i)); -// } -// if (m_parcelScalarFile != NULL) { -// for (int32_t i = 0; i < m_numberOfColumns; i++) { -// m_columnNames.push_back(m_parcelScalarFile->getMapName(i)); -// } -// } -// if (m_parcelSeriesFile != NULL) { -// for (int32_t i = 0; i < m_numberOfColumns; i++) { -// m_columnNames.push_back(m_parcelSeriesFile->getMapName(i)); -// } -// } -// } } } @@ -294,8 +305,50 @@ m_matrixContentType = ChartTwoMatrixContentTypeEnum::MATRIX_CONTENT_UNSUPPORTED; } + const NiftiTimeUnitsEnum::Enum mapUnits = parentCaretMappableDataFile->getMapIntervalUnits(); + CaretUnitsTypeEnum::Enum xAxisUnits = CaretUnitsTypeEnum::NONE; + switch (mapUnits) { + case NiftiTimeUnitsEnum::NIFTI_UNITS_HZ: + xAxisUnits = CaretUnitsTypeEnum::HERTZ; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_PPM: + xAxisUnits = CaretUnitsTypeEnum::PARTS_PER_MILLION; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_SEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: + xAxisUnits = CaretUnitsTypeEnum::SECONDS; + break; + case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: + break; + } - updateChartTwoCompoundDataTypeAfterFileChanges(ChartTwoCompoundDataType::newInstanceForMatrix(m_numberOfRows, + /* + * For Cifti Files, use units from dimensions + */ + CaretUnitsTypeEnum::Enum yAxisUnits = CaretUnitsTypeEnum::NONE; + { + const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); + if (ciftiMapFile != NULL) { + float start(0.0), step(0.0); + ciftiMapFile->getDimensionUnits(CiftiXML::ALONG_ROW, + xAxisUnits, + start, + step); + ciftiMapFile->getDimensionUnits(CiftiXML::ALONG_COLUMN, + yAxisUnits, + start, + step); + } + } + + updateChartTwoCompoundDataTypeAfterFileChanges(ChartTwoCompoundDataType::newInstanceForMatrix(xAxisUnits, + yAxisUnits, + m_numberOfRows, m_numberOfColumns)); } @@ -417,16 +470,20 @@ * The matrix visualization mode (upper/lower). * @param gridMode * The grid mode (filled or outline) + * @param opacity + * Opacity of the matrix */ -GraphicsPrimitiveV3fC4f* +GraphicsPrimitive* ChartableTwoFileMatrixChart::getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, - const CiftiMappableDataFile::MatrixGridMode gridMode) const + const CiftiMappableDataFile::MatrixGridMode gridMode, + const float opacity) const { const CiftiMappableDataFile* ciftiMapFile = getCiftiMappableDataFile(); CaretAssert(ciftiMapFile); return ciftiMapFile->getMatrixChartingGraphicsPrimitive(matrixViewMode, - gridMode); + gridMode, + opacity); } /** @@ -594,12 +651,6 @@ { CaretAssertArrayIndex(m_parcelLabelFileSelectedColumn, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); columnIndicesSet.insert(m_parcelLabelFileSelectedColumn[tabIndex]); -// CaretAssert(m_parcelLabelFile); -// EventCaretMappableDataFileMapsViewedInOverlays mapOverlayEvent(m_parcelLabelFile); -// EventManager::get()->sendEvent(mapOverlayEvent.getPointer()); -// for (auto indx : mapOverlayEvent.getSelectedMapIndices()) { -// columnIndicesSet.insert(indx); -// } } break; case MatrixDataFileType::PARCEL_SCALAR: @@ -682,11 +733,6 @@ CaretAssert(m_parcelLabelFile); CaretAssertArrayIndex(m_parcelLabelFileSelectedColumn, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_parcelLabelFileSelectedColumn[tabIndex] = rowColumnIndex; - // EventCaretMappableDataFileMapsViewedInOverlays mapOverlayEvent(m_parcelLabelFile); - // EventManager::get()->sendEvent(mapOverlayEvent.getPointer()); - // for (auto indx : mapOverlayEvent.getSelectedMapIndices()) { - // columnIndicesSet.insert(indx); - // } } break; case MatrixDataFileType::PARCEL_SCALAR: diff -Nru connectome-workbench-1.4.2/src/Files/ChartableTwoFileMatrixChart.h connectome-workbench-1.5.0/src/Files/ChartableTwoFileMatrixChart.h --- connectome-workbench-1.4.2/src/Files/ChartableTwoFileMatrixChart.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ChartableTwoFileMatrixChart.h 2021-02-16 19:46:47.000000000 +0000 @@ -35,7 +35,7 @@ class CiftiParcelScalarFile; class CiftiParcelSeriesFile; class CiftiScalarDataSeriesFile; - class GraphicsPrimitiveV3fC4f; + class GraphicsPrimitive; class ChartableTwoFileMatrixChart : public ChartableTwoFileBaseChart { @@ -59,8 +59,9 @@ int32_t& numberOfColumnsOut, std::vector& rgbaOut) const; - GraphicsPrimitiveV3fC4f* getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, - const CiftiMappableDataFile::MatrixGridMode gridMode) const; + GraphicsPrimitive* getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, + const CiftiMappableDataFile::MatrixGridMode gridMode, + const float opacity) const; int32_t getMatrixChartGraphicsPrimitiveGridColorIdentifier() const; diff -Nru connectome-workbench-1.4.2/src/Files/CiftiFiberOrientationFile.cxx connectome-workbench-1.5.0/src/Files/CiftiFiberOrientationFile.cxx --- connectome-workbench-1.4.2/src/Files/CiftiFiberOrientationFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CiftiFiberOrientationFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -19,6 +19,8 @@ */ /*LICENSE_END*/ +#include + #define __CIFTI_FIBER_ORIENTATION_FILE_DECLARE__ #include "CiftiFiberOrientationFile.h" #undef __CIFTI_FIBER_ORIENTATION_FILE_DECLARE__ @@ -542,3 +544,24 @@ return false; } +/** + * @return The maxmum variance of all fibers in this file + */ +float +CiftiFiberOrientationFile::getMaximumVariance() const +{ + /* + * Variance should never be negative + */ + float maxValue(0.0); + + for (const auto& fo : m_fiberOrientations) { + for (const auto f : fo->m_fibers) { + if (f->m_varF > maxValue) { + maxValue = f->m_varF; + } + } + } + + return maxValue; +} diff -Nru connectome-workbench-1.4.2/src/Files/CiftiFiberOrientationFile.h connectome-workbench-1.5.0/src/Files/CiftiFiberOrientationFile.h --- connectome-workbench-1.4.2/src/Files/CiftiFiberOrientationFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CiftiFiberOrientationFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -79,6 +79,8 @@ void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); + float getMaximumVariance() const; + // ADD_NEW_METHODS_HERE private: @@ -103,7 +105,7 @@ bool m_displayStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_volumeSpacing[3]; - + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/Files/CiftiMappableDataFile.cxx connectome-workbench-1.5.0/src/Files/CiftiMappableDataFile.cxx --- connectome-workbench-1.4.2/src/Files/CiftiMappableDataFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CiftiMappableDataFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -55,6 +55,8 @@ #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsPrimitiveV3fT3f.h" +#include "GraphicsUtilitiesOpenGL.h" #include "GroupAndNameHierarchyModel.h" #include "Histogram.h" #include "MapFileDataSelector.h" @@ -301,7 +303,12 @@ CaretAssert(colorInvalidateEvent); colorInvalidateEvent->setEventProcessed(); - invalidateColoringInAllMaps(); + if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + /* Do not update colors in this file */ + } + else { + invalidateColoringInAllMaps(); + } } } @@ -703,6 +710,12 @@ if (m_ciftiFile != NULL) { setFileName(ciftiMapFileName); // need by charting delegate + + if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + /* needed when reloading file */ + invalidateColoringInAllMaps(); + } + initializeAfterReading(ciftiMapFileName); } } @@ -1473,9 +1486,11 @@ * Force recreation of matrix so that it receives updates to coloring * and in particular, matrix grid outline coloring */ - m_matrixGraphicsPrimitive.reset(); + m_matrixGraphicsTrianglesPrimitive.reset(); + m_matrixGraphicsTexturePrimitive.reset(); m_matrixGraphicsOutlinePrimitive.reset(); invalidateHistogramChartColoring(); + m_previousMatrixOpacity = -1.0; } /** @@ -1555,32 +1570,118 @@ * * @param matrixViewMode * The matrix visualization mode (upper/lower). - * @param gridMode + * @param gridModeIn * The grid mode (filled or outline) + * @param opacity + * Opacity of the matrix */ -GraphicsPrimitiveV3fC4f* +GraphicsPrimitive* CiftiMappableDataFile::getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, - const MatrixGridMode gridMode) const + const MatrixGridMode gridModeIn, + const float opacityIn) const { + float opacity(opacityIn); + if (opacity < 0.0) { + opacity = 0.0; + } + if (opacity > 1.0) { + opacity = 1.0; + } + + MatrixGridMode gridMode = gridModeIn; + switch (gridMode) { + case MatrixGridMode::FILLED_TEXTURE: + { + /* + * Matrices are often non-power of 2. + * OpenGL 2.0 allows non-power of 2 textures + */ + if (GraphicsUtilitiesOpenGL::isVersionOrGreater(2, 0)) { + int32_t numMatrixRows(0); + int32_t numMatrixColumns(0); + + /* + * Verify texture dimensions are supported + */ + helpMapFileGetMatrixDimensions(numMatrixRows, + numMatrixColumns); + const int32_t maximumWidthHeight = GraphicsUtilitiesOpenGL::getTextureWidthHeightMaximumDimension(); + if ((numMatrixRows > maximumWidthHeight) + || (numMatrixColumns > maximumWidthHeight)) { + /* + * Dimensions too big, must use triangles + */ + gridMode = MatrixGridMode::FILLED_TRIANGLES; + + static bool firstTimeFlag(true); + if (firstTimeFlag) { + firstTimeFlag = true; + const QString msg("Matrix dimensions for file " + + getFileName() + + " are too big for OpenGL Texture.\n" + "Matrix dim(" + + AString::number(numMatrixRows) + + ", " + + AString::number(numMatrixColumns) + + ") OpenGL Maximum dim=" + + AString::number(maximumWidthHeight)); + CaretLogSevere(msg); + } + } + } + else { + /* + * Old OpenGL does not allow non-power of two texture images + */ + gridMode = MatrixGridMode::FILLED_TRIANGLES; + + static bool firstTimeFlag(true); + if (firstTimeFlag) { + firstTimeFlag = true; + const QString msg("OpenGL version is too old for texture drawing of Matrices. " + "Version=" + + GraphicsUtilitiesOpenGL::getVersion()); + CaretLogSevere(msg); + } + } + } + break; + case MatrixGridMode::FILLED_TRIANGLES: + break; + case MatrixGridMode::OUTLINE: + break; + } + + int32_t matrixNumRows(0), matrixNumCols(0); + helpMapFileGetMatrixDimensions(matrixNumRows, matrixNumCols); + EventCaretPreferencesGet preferencesEvent; EventManager::get()->sendEvent(preferencesEvent.getPointer()); CaretPreferences* caretPreferences = preferencesEvent.getCaretPreferences(); uint8_t gridByteRGBA[4] = { 0, 0, 0, 0 }; - GraphicsPrimitiveV3fC4f* matrixPrimitive = NULL; + GraphicsPrimitive* matrixPrimitive(NULL); + GraphicsPrimitiveV3fC4f* matrixTrianglePrimitive(NULL); + GraphicsPrimitiveV3fT3f* matrixTexturePrimitive(NULL); switch (gridMode) { - case MatrixGridMode::FILLED: - matrixPrimitive = m_matrixGraphicsPrimitive.get(); + case MatrixGridMode::FILLED_TRIANGLES: + matrixTrianglePrimitive = m_matrixGraphicsTrianglesPrimitive.get(); + matrixPrimitive = matrixTrianglePrimitive; + break; + case MatrixGridMode::FILLED_TEXTURE: + matrixTexturePrimitive = m_matrixGraphicsTexturePrimitive.get(); + matrixPrimitive = matrixTexturePrimitive; break; case MatrixGridMode::OUTLINE: caretPreferences->getBackgroundAndForegroundColors()->getColorChartMatrixGridLines(gridByteRGBA); - matrixPrimitive = m_matrixGraphicsOutlinePrimitive.get(); + matrixTrianglePrimitive = m_matrixGraphicsOutlinePrimitive.get(); + matrixPrimitive = matrixTrianglePrimitive; if ((gridByteRGBA[0] != m_previousMatrixGridRGBA[0]) || (gridByteRGBA[1] != m_previousMatrixGridRGBA[1]) || (gridByteRGBA[2] != m_previousMatrixGridRGBA[2]) || (gridByteRGBA[3] != m_previousMatrixGridRGBA[3])) { - matrixPrimitive = NULL; + matrixTrianglePrimitive = NULL; m_previousMatrixGridRGBA[0] = gridByteRGBA[0]; m_previousMatrixGridRGBA[1] = gridByteRGBA[1]; m_previousMatrixGridRGBA[2] = gridByteRGBA[2]; @@ -1589,25 +1690,36 @@ break; } + if (opacity != m_previousMatrixOpacity) { + const_cast(this)->invalidateColoringInAllMaps(); + matrixPrimitive = NULL; + } + if (matrixPrimitive == NULL) { int32_t numberOfRows = 0; int32_t numberOfColumns = 0; std::vector matrixRGBA; if (getMatrixForChartingRGBA(numberOfRows, numberOfColumns, matrixRGBA)) { + std::vector matrixTextureRGBA; const int32_t numberOfCells = numberOfRows * numberOfColumns; if (numberOfCells > 0) { switch (gridMode) { - case MatrixGridMode::FILLED: - matrixPrimitive = GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES); - matrixPrimitive->reserveForNumberOfVertices(numberOfCells * 6); // 2 triangles per cell, 3 vertices per triangle + case MatrixGridMode::FILLED_TRIANGLES: + matrixTrianglePrimitive = GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES); + matrixTrianglePrimitive->reserveForNumberOfVertices(numberOfCells * 6); // 2 triangles per cell, 3 vertices per triangle + matrixPrimitive = matrixTrianglePrimitive; + break; + case MatrixGridMode::FILLED_TEXTURE: + /* NOTE: Primitive created after texture RGBA is filled */ + matrixTextureRGBA.resize(matrixRGBA.size(), 0); break; case MatrixGridMode::OUTLINE: /* Lines are used around each cell to simplify upper/lower triangular options */ - matrixPrimitive = GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_LINES); - matrixPrimitive->reserveForNumberOfVertices(numberOfCells * 8); // 4 lines per cell, 2 vertices per line + matrixTrianglePrimitive = GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::OPENGL_LINES); + matrixTrianglePrimitive->reserveForNumberOfVertices(numberOfCells * 8); // 4 lines per cell, 2 vertices per line + matrixPrimitive = matrixTrianglePrimitive; break; } - matrixPrimitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES); /* * RGBA for grid outline @@ -1625,6 +1737,12 @@ */ const float cellNotDrawRGBA[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + CaretUnitsTypeEnum::Enum unusedUnits; + float xAxisStart(0.0), xAxisStep(0.0); + float yAxisStart(0.0), yAxisStep(0.0); + getDimensionUnits(CiftiXML::ALONG_ROW, unusedUnits, xAxisStart, xAxisStep); + getDimensionUnits(CiftiXML::ALONG_COLUMN, unusedUnits, yAxisStart, yAxisStep); + /* * NOTE: All matrix cells receive coloring, event those that are * not displayed due to the triangular view selection. @@ -1634,14 +1752,23 @@ * OpenGL buffers are used, drawing is very fast. */ int32_t rgbaOffset = 0; - const float cellHeight = 1.0; - const float cellWidth = 1.0; - float cellY = (numberOfRows - 1) * cellHeight; + const int32_t indexStepY = 1; + const int32_t indexStepX = 1; + int32_t indexY = (numberOfRows - 1) * indexStepY; for (int32_t rowIndex = 0; rowIndex < numberOfRows; rowIndex++) { - float cellX = 0; + switch (gridMode) { + case MatrixGridMode::FILLED_TRIANGLES: + break; + case MatrixGridMode::FILLED_TEXTURE: + break; + case MatrixGridMode::OUTLINE: + break; + } + int32_t indexX = 0; for (int32_t columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) { CaretAssertVectorIndex(matrixRGBA, rgbaOffset+3); - const float* rgba = &matrixRGBA[rgbaOffset]; + float* rgba = &matrixRGBA[rgbaOffset]; + rgba[3] = opacity; rgbaOffset += 4; bool drawCellFlag = true; @@ -1704,58 +1831,135 @@ } } + const float cellLeft(xAxisStart + (xAxisStep * indexX)); + const float cellRight(cellLeft + xAxisStep); + const float cellBottom(yAxisStart + (yAxisStep * indexY)); + const float cellTop(cellBottom + yAxisStep); switch (gridMode) { - case MatrixGridMode::FILLED: + case MatrixGridMode::FILLED_TRIANGLES: { const float* cellRGBA = (drawCellFlag ? rgba : cellNotDrawRGBA); - matrixPrimitive->addVertex(cellX, cellY + cellHeight, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX, cellY, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX + cellWidth, cellY, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellLeft, cellTop, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellLeft, cellBottom, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellRight, cellBottom, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX, cellY + cellHeight, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX + cellWidth, cellY, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX + cellWidth, cellY + cellHeight, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellLeft, cellTop, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellRight, cellBottom, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellRight, cellTop, 0.0, cellRGBA); + } + break; + case MatrixGridMode::FILLED_TEXTURE: + { + const float* cellRGBA = (drawCellFlag ? rgba : cellNotDrawRGBA); + int32_t cellOffset = ((indexY * numberOfColumns * 4) + + (indexX * 4)); + for (int32_t k = 0; k < 4; k++) { + CaretAssertVectorIndex(matrixTextureRGBA, cellOffset + 3); + matrixTextureRGBA[cellOffset + k] = static_cast(cellRGBA[k] * 255.0); + } } break; case MatrixGridMode::OUTLINE: { const float* cellRGBA = (drawCellFlag ? cellOutlineRGBA : cellNotDrawRGBA); - matrixPrimitive->addVertex(cellX, cellY, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX + cellWidth, cellY, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellLeft, cellBottom, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellRight, cellBottom, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX + cellWidth, cellY, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX + cellWidth, cellY + cellHeight, 0.0, cellRGBA); - - matrixPrimitive->addVertex(cellX + cellWidth, cellY + cellHeight, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX, cellY + cellHeight, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellRight, cellBottom, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellRight, cellTop, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX, cellY + cellHeight, 0.0, cellRGBA); - matrixPrimitive->addVertex(cellX, cellY, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellRight, cellTop, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellLeft, cellTop, 0.0, cellRGBA); + + matrixTrianglePrimitive->addVertex(cellLeft, cellTop, 0.0, cellRGBA); + matrixTrianglePrimitive->addVertex(cellLeft, cellBottom, 0.0, cellRGBA); } break; } - cellX += cellWidth; + indexX += indexStepX; } - cellY -= cellHeight; + indexY -= indexStepY; + } + + switch (gridMode) { + case MatrixGridMode::FILLED_TRIANGLES: + break; + case MatrixGridMode::FILLED_TEXTURE: + { +// /* +// * Ranges to (-halfstep, num-rows - halfstep) +// * For three rows: (-0.5, 2.5) +// */ +// const float matrixLeft(xAxisStart); +// const float matrixRight(matrixLeft + (xAxisStep * (numberOfColumns - 1))); +// const float matrixBottom(yAxisStart - (yAxisStep * 0.5)); +// const float matrixTop(matrixBottom + (yAxisStep * (numberOfRows))); +// /* +// * 0 to N does not match grid outline +// */ +// const float matrixLeft(xAxisStart); +// const float matrixRight(matrixLeft + (xAxisStep * (numberOfColumns - 1))); +// const float matrixBottom(yAxisStart); +// const float matrixTop(matrixBottom + (yAxisStep * (numberOfRows - 1))); + /* + * 0 to N+1 matches grid outline + */ + const float matrixLeft(xAxisStart); + const float matrixRight(matrixLeft + (xAxisStep * (numberOfColumns))); + const float matrixBottom(yAxisStart); + const float matrixTop(matrixBottom + (yAxisStep * (numberOfRows))); + matrixTexturePrimitive = GraphicsPrimitive::newPrimitiveV3fT3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + &matrixTextureRGBA[0], + numberOfColumns, + numberOfRows, + GraphicsPrimitive::TextureWrappingType::CLAMP, + GraphicsPrimitive::TextureFilteringType::NEAREST); + matrixTexturePrimitive->addVertex(matrixLeft, matrixTop, 0, 1); /* Top Left */ + matrixTexturePrimitive->addVertex(matrixLeft, matrixBottom, 0, 0); /* Bottom Left */ + matrixTexturePrimitive->addVertex(matrixRight, matrixTop, 1, 1); /* Top Right */ + matrixTexturePrimitive->addVertex(matrixRight, matrixBottom, 1, 0); /* Bottom Right */ + matrixPrimitive = matrixTexturePrimitive; + } + break; + case MatrixGridMode::OUTLINE: + break; } } } + + CaretAssert(matrixPrimitive); + + matrixPrimitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES); + + /* + * Allow release of instance data after OpenGL buffers are loaded to save memory + */ + matrixPrimitive->setReleaseInstanceDataMode(GraphicsPrimitive::ReleaseInstanceDataMode::ENABLED); } switch (gridMode) { - case MatrixGridMode::FILLED: - if (matrixPrimitive != m_matrixGraphicsPrimitive.get()) { - m_matrixGraphicsPrimitive.reset(matrixPrimitive); + case MatrixGridMode::FILLED_TRIANGLES: + CaretAssert(matrixTrianglePrimitive); + if (matrixTrianglePrimitive != m_matrixGraphicsTrianglesPrimitive.get()) { + m_matrixGraphicsTrianglesPrimitive.reset(matrixTrianglePrimitive); + } + break; + case MatrixGridMode::FILLED_TEXTURE: + CaretAssert(matrixTexturePrimitive); + if (matrixTexturePrimitive != m_matrixGraphicsTexturePrimitive.get()) { + m_matrixGraphicsTexturePrimitive.reset(matrixTexturePrimitive); } break; case MatrixGridMode::OUTLINE: - if (matrixPrimitive != m_matrixGraphicsOutlinePrimitive.get()) { - m_matrixGraphicsOutlinePrimitive.reset(matrixPrimitive); + CaretAssert(matrixTrianglePrimitive); + if (matrixTrianglePrimitive != m_matrixGraphicsOutlinePrimitive.get()) { + m_matrixGraphicsOutlinePrimitive.reset(matrixTrianglePrimitive); } break; } + m_previousMatrixOpacity = opacity; return matrixPrimitive; } @@ -2426,6 +2630,37 @@ } /** + * Get the CIFTI Scalars Map for the given direction. + * + * @param direction + * Direction of mapping. MUST BE one of CiftiXML::ALONG_ROW or + * CiftiXML::ALONG_COLUMN. + * @return + * Pointer to the map's Cifti Scalars Map or NULL if the file is not + * mapped using scalars or NULL if direction is invalid. + */ +const CiftiScalarsMap* +CiftiMappableDataFile::getCiftiScalarsMapForDirection(const int direction) const +{ + if (m_ciftiFile != NULL) { + if ((direction != CiftiXML::ALONG_ROW) + && (direction != CiftiXML::ALONG_COLUMN)) { + CaretAssert(0); + return NULL; + } + + const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); + const CiftiMappingType* mapping = ciftiXML.getMap(direction); + if (mapping->getType() == CiftiMappingType::SCALARS) { + const CiftiScalarsMap* csm = dynamic_cast(mapping); + CaretAssert(csm); + return csm; + } + } + return NULL; +} + +/** * @return Is the data in the file mapped to colors using * a label table. */ @@ -2582,8 +2817,10 @@ */ invalidateHistogramChartColoring(); - m_matrixGraphicsPrimitive.reset(); + m_matrixGraphicsTrianglesPrimitive.reset(); + m_matrixGraphicsTexturePrimitive.reset(); m_matrixGraphicsOutlinePrimitive.reset(); + m_previousMatrixOpacity = -1.0; } /** @@ -4030,6 +4267,157 @@ } /** + * Get the row and/or column for a surface vertex + * @param structure + * The structure + * @param surfaceNumberOfVertices, + * Number of vertices in the surface + * @param vertexIndex + * Index of the vertex + * @param rowIndexOut + * Index of the row + * @param columnIndexOut + * Index of the column + * @return True if either or both of row and/or column index is valid. + */ +bool +CiftiMappableDataFile::getRowColumnIndexFromSurfaceVertex(const StructureEnum::Enum structure, + const int64_t surfaceNumberOfVertices, + const int64_t vertexIndex, + int64_t& rowIndexOut, + int64_t& columnIndexOut) const +{ + rowIndexOut = -1; + columnIndexOut = -1; + + if (m_ciftiFile == NULL) { + return false; + } + + const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); + + switch (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN)) + { + case CiftiMappingType::BRAIN_MODELS: + { + const CiftiBrainModelsMap& bmm = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); + if (surfaceNumberOfVertices == bmm.getSurfaceNumberOfNodes(structure)) { + rowIndexOut = bmm.getIndexForNode(vertexIndex, structure); + } + } + break; + case CiftiMappingType::PARCELS: + break; + case CiftiMappingType::LABELS: + break; + case CiftiMappingType::SCALARS: + break; + case CiftiMappingType::SERIES: + break; + } + + switch (ciftiXML.getMappingType(CiftiXML::ALONG_ROW)) + { + case CiftiMappingType::BRAIN_MODELS: + { + const CiftiBrainModelsMap& bmm = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_ROW); + if (surfaceNumberOfVertices == bmm.getSurfaceNumberOfNodes(structure)) { + columnIndexOut = bmm.getIndexForNode(vertexIndex, structure); + } + } + break; + case CiftiMappingType::PARCELS: + break; + case CiftiMappingType::LABELS: + break; + case CiftiMappingType::SCALARS: + break; + case CiftiMappingType::SERIES: + break; + } + + return ((rowIndexOut >= 0) + || (columnIndexOut >= 0)); +} + +/** + * Get the row and/or column for a volume coordinate + * @param xyz + * The coordinate + * @param rowIndexOut + * Index of the row + * @param columnIndexOut + * Index of the column + * @return True if either or both of row and/or column index is valid. + */ +bool +CiftiMappableDataFile::getRowColumnIndexFromVolumeXYZ(const float xyz[3], + int64_t& rowIndexOut, + int64_t& columnIndexOut) const +{ + rowIndexOut = -1; + columnIndexOut = -1; + + if (m_ciftiFile == NULL) { + return false; + } + + int64_t voxelI, voxelJ, voxelK; + enclosingVoxel(xyz[0], + xyz[1], + xyz[2], + voxelI, + voxelJ, + voxelK); + if ( ! indexValid(voxelI, + voxelJ, + voxelK)) { + return false; + } + + const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); + + switch (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN)) + { + case CiftiMappingType::BRAIN_MODELS: + { + const CiftiBrainModelsMap& bmm = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); + rowIndexOut = bmm.getIndexForVoxel(voxelI, voxelJ, voxelK); + } + break; + case CiftiMappingType::PARCELS: + break; + case CiftiMappingType::LABELS: + break; + case CiftiMappingType::SCALARS: + break; + case CiftiMappingType::SERIES: + break; + } + + switch (ciftiXML.getMappingType(CiftiXML::ALONG_ROW)) + { + case CiftiMappingType::BRAIN_MODELS: + { + const CiftiBrainModelsMap& bmm = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_ROW); + columnIndexOut = bmm.getIndexForVoxel(voxelI, voxelJ, voxelK); + } + break; + case CiftiMappingType::PARCELS: + break; + case CiftiMappingType::LABELS: + break; + case CiftiMappingType::SCALARS: + break; + case CiftiMappingType::SERIES: + break; + } + + return ((rowIndexOut >= 0) + || (columnIndexOut >= 0)); +} + +/** * Get the brainordinate from the given row. * * @param rowIndex @@ -4457,6 +4845,8 @@ * Index of the node * @param numberOfNodes * Number of nodes in the surface. + * @param dataValueSeparator + * Separator between multiple data values * @param numericalValuesOut * Numerical values out for all map indices * @param numericalValuesOutValid @@ -4477,6 +4867,7 @@ const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, + const AString& dataValueSeparator, std::vector& numericalValuesOut, std::vector& numericalValuesOutValid, AString& textValueOut) const @@ -4513,20 +4904,26 @@ numericalValuesOut.push_back(mapData[dataIndex]); numericalValuesOutValid.push_back(true); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } if (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML) == CiftiMappingType::LABELS) { const GiftiLabelTable* glt = getMapLabelTable(mapIndex); const int32_t labelKey = static_cast(value); const GiftiLabel* gl = glt->getLabel(labelKey); if (gl != NULL) { - textValueOut += (" " + gl->getName()); + textValueOut += gl->getName(); + textValueOut.append(" (" + + getMapName(mapIndex) + + ")"); } else { - textValueOut += (" InvalidLabelKey=" + textValueOut += ("InvalidLabelKey=" + AString::number(labelKey)); } } else { - textValueOut += (" " + AString::number(value, 'f')); + textValueOut += (AString::number(value, 'f')); } } } @@ -4571,13 +4968,19 @@ structure); if ((mappingDataParcelIndex >= 0) && (mappingDataParcelIndex < static_cast(mappingParcels.size()))) { - textValueOut = mappingParcels[mappingDataParcelIndex].m_name; + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut += mappingParcels[mappingDataParcelIndex].m_name; std::vector dataLoaded; matrixFile->getMapData(0, dataLoaded); if ((mappingDataParcelIndex >= 0) && (mappingDataParcelIndex < static_cast(dataLoaded.size()))) { - textValueOut += (" " + AString::number(dataLoaded[mappingDataParcelIndex])); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut += (AString::number(dataLoaded[mappingDataParcelIndex], 'f', 5)); return true; } } @@ -4600,6 +5003,9 @@ structure); if ((mappingDataParcelIndex >= 0) && (mappingDataParcelIndex < static_cast(mappingParcels.size()))) { + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } textValueOut = mappingParcels[mappingDataParcelIndex].m_name; } } @@ -4613,6 +5019,9 @@ const int64_t numRows = m_ciftiFile->getNumberOfRows(); const int64_t numCols = m_ciftiFile->getNumberOfColumns(); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } switch (m_dataReadingDirectionForCiftiXML) { case CiftiXML::ALONG_COLUMN: { @@ -4621,7 +5030,7 @@ CaretAssert(readingDataParcelIndex < numRows); m_ciftiFile->getRow(&data[0], readingDataParcelIndex); CaretAssertVectorIndex(data, mappingDataParcelIndex); - textValueOut += (" " + AString::number(data[mappingDataParcelIndex])); + textValueOut += (AString::number(data[mappingDataParcelIndex])); } break; case CiftiXML::ALONG_ROW: @@ -4631,7 +5040,7 @@ CaretAssert(readingDataParcelIndex < numCols); m_ciftiFile->getColumn(&data[0], readingDataParcelIndex); CaretAssertVectorIndex(data, mappingDataParcelIndex); - textValueOut += (" " + AString::number(data[mappingDataParcelIndex])); + textValueOut += (AString::number(data[mappingDataParcelIndex])); } break; } @@ -4650,6 +5059,9 @@ structure); if ((parcelIndex >= 0) && (parcelIndex < static_cast(parcels.size()))) { + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } textValueOut = parcels[parcelIndex].m_name; } } @@ -4687,6 +5099,9 @@ const int64_t numRows = m_ciftiFile->getNumberOfRows(); const int64_t numCols = m_ciftiFile->getNumberOfColumns(); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } switch (m_dataReadingDirectionForCiftiXML) { case CiftiXML::ALONG_COLUMN: { @@ -4695,7 +5110,7 @@ CaretAssert(parcelIndex < numCols); m_ciftiFile->getColumn(&data[0], parcelIndex); CaretAssertVectorIndex(data, itemIndex); - textValueOut += (" " + AString::number(data[itemIndex])); + textValueOut += (AString::number(data[itemIndex])); } break; case CiftiXML::ALONG_ROW: @@ -4705,7 +5120,7 @@ CaretAssert(parcelIndex < numRows); m_ciftiFile->getRow(&data[0], parcelIndex); CaretAssertVectorIndex(data, itemIndex); - textValueOut += (" " + AString::number(data[itemIndex])); + textValueOut += (AString::number(data[itemIndex])); } break; } @@ -4819,6 +5234,8 @@ * Index of the node. * @param numberOfNodes * Number of nodes in the surface. + * @param dataValueSeparator + * Separator between multiple data values * @param textOut * Output containing identification information. */ @@ -4827,6 +5244,7 @@ const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, + const AString& dataValueSeparator, AString& textOut) const { CaretAssert(m_ciftiFile); @@ -4942,6 +5360,7 @@ structure, nodeIndex, numberOfNodes, + dataValueSeparator, numericalValues, numericalValuesValid, textValue)) { @@ -4967,6 +5386,9 @@ const GiftiLabelTable* glt = getMapLabelTable(mapIndex); const int32_t labelKey = static_cast(value); const GiftiLabel* gl = glt->getLabel(labelKey); + if ( ! textOut.isEmpty()) { + textOut.append(dataValueSeparator); + } if (gl != NULL) { textOut += gl->getName(); } @@ -4974,17 +5396,21 @@ textOut += ("InvalidLabelKey=" + AString::number(value)); } + textOut.append(" (" + + getMapName(mapIndex) + + ")"); validID = true; } else if (isMappedWithPalette()) { + if ( ! textOut.isEmpty()) { + textOut.append(dataValueSeparator); + } textOut += AString::number(value); validID = true; } else { CaretAssert(0); } - - textOut += " "; } } } @@ -5002,8 +5428,10 @@ numericalValue, numericalValueValid, textValue)) { + if (! textOut.isEmpty()) { + textOut.append(dataValueSeparator); + } textOut += textValue; - textOut += " "; validID = true; } } @@ -5511,6 +5939,8 @@ * Indices of the maps. * @param xyz * Coordinate of voxel. + * @param dataValueSeparator + * Separator between multiple data values * @param ijkOut * Voxel indices of value. * @param numericalValuesOut @@ -5531,6 +5961,7 @@ bool CiftiMappableDataFile::getMapVolumeVoxelValues(const std::vector mapIndices, const float xyz[3], + const AString& dataValueSeparator, int64_t ijkOut[3], std::vector& numericalValuesOut, std::vector& numericalValuesOutValid, @@ -5591,8 +6022,10 @@ for (std::vector::const_iterator mapIter = mapIndices.begin(); mapIter != mapIndices.end(); mapIter++) { - textValueOut += (getMapName(*mapIter) - + " "); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut += getMapName(*mapIter); } return true; } @@ -5617,27 +6050,32 @@ const float value = mapData[dataOffset]; if (isMappedWithLabelTable()) { - textValueOut = "Invalid Label Index"; - const GiftiLabelTable* glt = getMapLabelTable(mapIndex); const int32_t labelKey = static_cast(value); const GiftiLabel* gl = glt->getLabel(labelKey); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } if (gl != NULL) { - textValueOut = (gl->getName()); + textValueOut += gl->getName(); } else { textValueOut += ("InvalidLabelKey=" - + AString::number(labelKey) - + " "); + + AString::number(labelKey)); } + textValueOut.append(" (" + + getMapName(mapIndex) + + ")"); numericalValuesOut.push_back(value); numericalValuesOutValid.push_back(false); // NOT VALID ! } else if (isMappedWithPalette()) { numericalValuesOut.push_back(value); numericalValuesOutValid.push_back(true); - textValueOut = (AString::number(value) - + " "); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut += AString::number(value); } else { CaretAssert(0); @@ -5653,69 +6091,178 @@ break; case CiftiMappingType::PARCELS: { - const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); - const int64_t parcelMapIndex = map.getIndexForVoxel(ijk); - const std::vector& parcels = map.getParcels(); - if ((parcelMapIndex >= 0) - && (parcelMapIndex < static_cast(parcels.size()))) { - CaretAssertVectorIndex(parcels, - parcelMapIndex); - textValueOut = parcels[parcelMapIndex].m_name; - } - - for (std::vector::const_iterator mapIter = mapIndices.begin(); - mapIter != mapIndices.end(); - mapIter++) { - const int32_t mapIndex = *mapIter; - if (parcelMapIndex >= 0) { - int64_t itemIndex = -1; - switch (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML)) { - case CiftiMappingType::BRAIN_MODELS: + if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_PARCEL) { + const CiftiParcelsMap dataMap = ciftiXML.getParcelsMap(m_dataReadingDirectionForCiftiXML); + const int64_t readingDataParcelIndex = dataMap.getIndexForVoxel(ijk); + + /* + * Special case for matrix type files + */ + const CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(this); + if (matrixFile != NULL) { + const ConnectivityDataLoaded* dataLoaded = matrixFile->getConnectivityDataLoaded(); + switch (dataLoaded->getMode()) { + case ConnectivityDataLoaded::MODE_NONE: + return false; + break; + case ConnectivityDataLoaded::MODE_COLUMN: + case ConnectivityDataLoaded::MODE_ROW: + case ConnectivityDataLoaded::MODE_SURFACE_NODE: + case ConnectivityDataLoaded::MODE_SURFACE_NODE_AVERAGE: + case ConnectivityDataLoaded::MODE_VOXEL_IJK_AVERAGE: + case ConnectivityDataLoaded::MODE_VOXEL_XYZ: { - const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataReadingDirectionForCiftiXML); - itemIndex = map.getIndexForVoxel(ijk); + int mappingDataParcelIndex(-1); + const CiftiParcelsMap& mappingMap = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); + const std::vector& mappingParcels = mappingMap.getParcels(); + mappingDataParcelIndex = mappingMap.getIndexForVoxel(ijk); + if ((mappingDataParcelIndex >= 0) + && (mappingDataParcelIndex < static_cast(mappingParcels.size()))) { + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut += mappingParcels[mappingDataParcelIndex].m_name; + + std::vector dataLoaded; + matrixFile->getMapData(0, dataLoaded); + if ((mappingDataParcelIndex >= 0) + && (mappingDataParcelIndex < static_cast(dataLoaded.size()))) { + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut += (AString::number(dataLoaded[mappingDataParcelIndex], 'f', 5)); + return true; + } + } } break; - case CiftiMappingType::LABELS: - break; - case CiftiMappingType::PARCELS: - break; - case CiftiMappingType::SCALARS: - itemIndex = mapIndex; - break; - case CiftiMappingType::SERIES: - itemIndex = mapIndex; - break; } - if (itemIndex >= 0) { + } + + int64_t mappingDataParcelIndex = -1; + if (readingDataParcelIndex >= 0) { + /* + * Only get parcel name if there is a row for reading + */ + const CiftiParcelsMap& mappingMap = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); + const std::vector& mappingParcels = mappingMap.getParcels(); + mappingDataParcelIndex = mappingMap.getIndexForVoxel(ijk); + if ((mappingDataParcelIndex >= 0) + && (mappingDataParcelIndex < static_cast(mappingParcels.size()))) { + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut = mappingParcels[mappingDataParcelIndex].m_name; + } + } + + for (std::vector::const_iterator mapIter = mapIndices.begin(); + mapIter != mapIndices.end(); + mapIter++) { + if ((mappingDataParcelIndex >= 0) + && (readingDataParcelIndex >= 0)) { const int64_t numRows = m_ciftiFile->getNumberOfRows(); const int64_t numCols = m_ciftiFile->getNumberOfColumns(); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } switch (m_dataReadingDirectionForCiftiXML) { case CiftiXML::ALONG_COLUMN: { std::vector data; - data.resize(numRows); - CaretAssert(parcelMapIndex < numCols); - m_ciftiFile->getColumn(&data[0], parcelMapIndex); - CaretAssertVectorIndex(data, itemIndex); - textValueOut += (" " + AString::number(data[itemIndex])); + data.resize(numCols); + CaretAssert(readingDataParcelIndex < numRows); + m_ciftiFile->getRow(&data[0], readingDataParcelIndex); + CaretAssertVectorIndex(data, mappingDataParcelIndex); + textValueOut += (AString::number(data[mappingDataParcelIndex])); } break; case CiftiXML::ALONG_ROW: { std::vector data; - data.resize(numCols); - CaretAssert(parcelMapIndex < numRows); - m_ciftiFile->getRow(&data[0], parcelMapIndex); - CaretAssertVectorIndex(data, itemIndex); - textValueOut += (" " + AString::number(data[itemIndex])); + data.resize(numRows); + CaretAssert(readingDataParcelIndex < numCols); + m_ciftiFile->getColumn(&data[0], readingDataParcelIndex); + CaretAssertVectorIndex(data, mappingDataParcelIndex); + textValueOut += (AString::number(data[mappingDataParcelIndex])); } break; } } } } + { + const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); + const int64_t parcelMapIndex = map.getIndexForVoxel(ijk); + const std::vector& parcels = map.getParcels(); + if ((parcelMapIndex >= 0) + && (parcelMapIndex < static_cast(parcels.size()))) { + CaretAssertVectorIndex(parcels, + parcelMapIndex); + textValueOut = parcels[parcelMapIndex].m_name; + } + + for (std::vector::const_iterator mapIter = mapIndices.begin(); + mapIter != mapIndices.end(); + mapIter++) { + const int32_t mapIndex = *mapIter; + if (parcelMapIndex >= 0) { + int64_t itemIndex = -1; + switch (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML)) { + case CiftiMappingType::BRAIN_MODELS: + { + const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataReadingDirectionForCiftiXML); + itemIndex = map.getIndexForVoxel(ijk); + } + break; + case CiftiMappingType::LABELS: + break; + case CiftiMappingType::PARCELS: + break; + case CiftiMappingType::SCALARS: + itemIndex = mapIndex; + break; + case CiftiMappingType::SERIES: + itemIndex = mapIndex; + break; + } + if (itemIndex >= 0) { + const int64_t numRows = m_ciftiFile->getNumberOfRows(); + const int64_t numCols = m_ciftiFile->getNumberOfColumns(); + + switch (m_dataReadingDirectionForCiftiXML) { + case CiftiXML::ALONG_COLUMN: + { + std::vector data; + data.resize(numRows); + CaretAssert(parcelMapIndex < numCols); + m_ciftiFile->getColumn(&data[0], parcelMapIndex); + CaretAssertVectorIndex(data, itemIndex); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut += (AString::number(data[itemIndex])); + } + break; + case CiftiXML::ALONG_ROW: + { + std::vector data; + data.resize(numCols); + CaretAssert(parcelMapIndex < numRows); + m_ciftiFile->getRow(&data[0], parcelMapIndex); + CaretAssertVectorIndex(data, itemIndex); + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } + textValueOut += (AString::number(data[itemIndex])); + } + break; + } + } + } + } + } } return true; break; @@ -5883,6 +6430,8 @@ * Indices of maps for which identification information is requested. * @param xyz * Coordinate of voxel. + * @param dataValueSeparator + * Separator between multiple data values * @param ijkOut * Voxel indices of value. * @param textOut @@ -5891,6 +6440,7 @@ bool CiftiMappableDataFile::getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], + const AString& dataValueSeparator, int64_t ijkOut[3], AString& textOut) const { @@ -5908,6 +6458,7 @@ AString textValue; if (getMapVolumeVoxelValues(mapIndices, xyz, + dataValueSeparator, ijkOut, numericalValues, numericalValuesValid, @@ -5977,6 +6528,82 @@ } /** + * Get the units type, start value, and step value for a dimension. For dimensions that do not + * 'support' units the start value will be zero and the step value will be one. + * + * @param dimensionIndex + * Index of dimension (use CiftiXML::ALONG_ROW, ALONG_COLUMN, ALONG_STACK) + * @param unitsOut + * Output with units + * @param startValueOut + * The starting value along the dimension + * @param stepValueOut + * The step value along the dimension + */ +void +CiftiMappableDataFile::getDimensionUnits(const int32_t dimensionIndex, + CaretUnitsTypeEnum::Enum& unitsOut, + float& startValueOut, + float& stepValueOut) const +{ + unitsOut = CaretUnitsTypeEnum::NONE; + startValueOut = 0.0f; + stepValueOut = 1.0f; + + switch (dimensionIndex) { + case CiftiXML::ALONG_ROW: + break; + case CiftiXML::ALONG_COLUMN: + break; + case CiftiXML::ALONG_STACK: + break; + default: + CaretAssertMessage(0, ("Invalid dimension index=" + QString::number(dimensionIndex))); + return; + break; + } + + if (m_ciftiFile != NULL) { + const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); + if ((dimensionIndex >= 0) + && (dimensionIndex < ciftiXML.getNumberOfDimensions())) { + switch (ciftiXML.getMappingType(dimensionIndex)) { + case CiftiMappingType::BRAIN_MODELS: + break; + case CiftiMappingType::LABELS: + break; + case CiftiMappingType::PARCELS: + break; + case CiftiMappingType::SCALARS: + break; + case CiftiMappingType::SERIES: + { + const CiftiSeriesMap& seriesMap = ciftiXML.getSeriesMap(dimensionIndex); + switch (seriesMap.getUnit()) { + case CiftiSeriesMap::HERTZ: + unitsOut = CaretUnitsTypeEnum::HERTZ; + break; + case CiftiSeriesMap::METER: + unitsOut = CaretUnitsTypeEnum::METERS; + break; + case CiftiSeriesMap::RADIAN: + unitsOut = CaretUnitsTypeEnum::RADIANS; + break; + case CiftiSeriesMap::SECOND: + unitsOut = CaretUnitsTypeEnum::SECONDS; + break; + } + startValueOut = seriesMap.getStart(); + stepValueOut = seriesMap.getStep(); + } + break; + } + } + } +} + + +/** * Get the units value for the first map and the * quantity of units between consecutive maps. If the * units for the maps is unknown, value of one (1) are @@ -7018,35 +7645,29 @@ break; case MapFileDataSelector::DataSelectionType::COLUMN_DATA: { - const bool loadColumnDataFlag = false; - if (loadColumnDataFlag) { - const DataFileTypeEnum::Enum dataFileType = getDataFileType(); - if (dataFileType == DataFileTypeEnum::UNKNOWN) { - CaretMappableDataFile* mapFile = NULL; - AString mapFileName; - int32_t columnIndex = -1; - mapFileDataSelector.getColumnIndex(mapFile, - mapFileName, - columnIndex); - bool loadDataFlag = false; - if (mapFile != NULL) { - if (mapFile == dynamic_cast(this)) { - loadDataFlag = true; - } - } - else if (mapFileName.endsWith(getFileNameNoPath())) { - loadDataFlag = true; - } - if (loadDataFlag) { - if ((columnIndex >= 0) - && (columnIndex < m_ciftiFile->getNumberOfColumns())) { - const int32_t numberOfElementsInColumn = m_ciftiFile->getNumberOfRows(); - if (numberOfElementsInColumn > 0) { - dataOut.resize(numberOfElementsInColumn); - m_ciftiFile->getColumn(&dataOut[0], - columnIndex); - } - } + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t columnIndex = -1; + mapFileDataSelector.getColumnIndex(mapFile, + mapFileName, + columnIndex); + bool loadDataFlag = false; + if (mapFile != NULL) { + if (mapFile == dynamic_cast(this)) { + loadDataFlag = true; + } + } + else if (mapFileName.endsWith(getFileNameNoPath())) { + loadDataFlag = true; + } + if (loadDataFlag) { + if ((columnIndex >= 0) + && (columnIndex < m_ciftiFile->getNumberOfColumns())) { + const int32_t numberOfElementsInColumn = m_ciftiFile->getNumberOfRows(); + if (numberOfElementsInColumn > 0) { + dataOut.resize(numberOfElementsInColumn); + m_ciftiFile->getColumn(&dataOut[0], + columnIndex); } } } @@ -7054,38 +7675,35 @@ break; case MapFileDataSelector::DataSelectionType::ROW_DATA: { - const DataFileTypeEnum::Enum dataFileType = getDataFileType(); - if (dataFileType == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { - CaretMappableDataFile* mapFile = NULL; - AString mapFileName; - int32_t rowIndex = -1; - mapFileDataSelector.getRowIndex(mapFile, - mapFileName, - rowIndex); - - bool loadDataFlag = false; - if (mapFile != NULL) { - if (mapFile == dynamic_cast(this)) { - loadDataFlag = true; - } - } - else if (mapFileName.endsWith(getFileNameNoPath())) { + CaretMappableDataFile* mapFile = NULL; + AString mapFileName; + int32_t rowIndex = -1; + mapFileDataSelector.getRowIndex(mapFile, + mapFileName, + rowIndex); + + bool loadDataFlag = false; + if (mapFile != NULL) { + if (mapFile == dynamic_cast(this)) { loadDataFlag = true; } - if (loadDataFlag) { - if ((rowIndex >= 0) - && (rowIndex < m_ciftiFile->getNumberOfRows())) { - const int32_t numberOfElementsInRow = m_ciftiFile->getNumberOfColumns(); - if (numberOfElementsInRow > 0) { - dataOut.resize(numberOfElementsInRow); - m_ciftiFile->getRow(&dataOut[0], - rowIndex); - } + } + else if (mapFileName.endsWith(getFileNameNoPath())) { + loadDataFlag = true; + } + if (loadDataFlag) { + if ((rowIndex >= 0) + && (rowIndex < m_ciftiFile->getNumberOfRows())) { + const int32_t numberOfElementsInRow = m_ciftiFile->getNumberOfColumns(); + if (numberOfElementsInRow > 0) { + dataOut.resize(numberOfElementsInRow); + m_ciftiFile->getRow(&dataOut[0], + rowIndex); } } } } - break; + break; case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: try { StructureEnum::Enum structure = StructureEnum::INVALID; @@ -7357,9 +7975,17 @@ const CiftiScalarsMap& map = ciftiXML.getScalarsMap(m_readingDirectionForCiftiXML); m_metadata = map.getMapMetadata(mapIndex); CaretAssert(m_metadata); - m_paletteColorMapping = map.getMapPalette(mapIndex); - CaretAssert(m_paletteColorMapping); - + if (ciftiMappableDataFile->isOnePaletteUsedForAllMaps()) { + /* + * One palette is used for all maps so use file's palette + */ + m_paletteColorMapping = ciftiXML.getFilePalette(); + } + else { + m_paletteColorMapping = map.getMapPalette(mapIndex); + CaretAssert(m_paletteColorMapping); + } + m_mapName = map.getMapName(m_mapIndex); } break; diff -Nru connectome-workbench-1.4.2/src/Files/CiftiMappableDataFile.h connectome-workbench-1.5.0/src/Files/CiftiMappableDataFile.h --- connectome-workbench-1.4.2/src/Files/CiftiMappableDataFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CiftiMappableDataFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -24,6 +24,7 @@ #include "CaretMappableDataFile.h" #include "CaretPointer.h" #include "CaretObjectTracksModification.h" +#include "CaretUnitsTypeEnum.h" #include "ChartTwoMatrixTriangularViewingModeEnum.h" #include "CiftiMappingType.h" #include "CiftiXMLElements.h" @@ -40,9 +41,12 @@ class ChartDataCartesian; class CiftiFile; class CiftiParcelsMap; + class CiftiScalarsMap; class CiftiXML; class FastStatistics; + class GraphicsPrimitive; class GraphicsPrimitiveV3fC4f; + class GraphicsPrimitiveV3fT3f; class GroupAndNameHierarchyModel; class Histogram; class SparseVolumeIndexer; @@ -241,6 +245,8 @@ const CiftiParcelsMap* getCiftiParcelsMapForDirection(const int direction) const; + const CiftiScalarsMap* getCiftiScalarsMapForDirection(const int direction) const; + virtual bool isMappedWithLabelTable() const; virtual GiftiLabelTable* getMapLabelTable(const int32_t mapIndex); @@ -371,6 +377,7 @@ virtual bool getMapVolumeVoxelValues(const std::vector mapIndices, const float xyz[3], + const AString& dataValueSeparator, int64_t ijkOut[3], std::vector& numericalValuesOut, std::vector& numericalValuesOutValid, @@ -393,8 +400,9 @@ virtual bool getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], + const AString& dataValueSeparator, int64_t ijkOut[3], - AString& textOut) const; + AString& textOut) const override; std::vector getUniqueLabelKeysUsedInMap(const int32_t mapIndex) const; @@ -414,6 +422,7 @@ const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, + const AString& dataValueSeparator, std::vector& numericalValuesOut, std::vector& numericalValuesOutValid, AString& textValueOut) const; @@ -422,6 +431,7 @@ const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, + const AString& dataValueSeparator, AString& textOut) const override; int32_t getMappingSurfaceNumberOfNodes(const StructureEnum::Enum structure) const; @@ -445,6 +455,11 @@ virtual NiftiTimeUnitsEnum::Enum getMapIntervalUnits() const; + void getDimensionUnits(const int32_t dimensionIndex, + CaretUnitsTypeEnum::Enum& unitsOut, + float& startValueOut, + float& stepValueOut) const; + virtual void getMapIntervalStartAndStep(float& firstMapUnitsValueOut, float& mapIntervalStepValueOut) const; @@ -466,6 +481,16 @@ void invalidateColoringInAllMaps(); + bool getRowColumnIndexFromVolumeXYZ(const float xyz[3], + int64_t& rowIndexOut, + int64_t& columnIndexOut) const; + + bool getRowColumnIndexFromSurfaceVertex(const StructureEnum::Enum structure, + const int64_t surfaceNumberOfVertices, + const int64_t vertexIndex, + int64_t& rowIndexOut, + int64_t& columnIndexOut) const; + void getBrainordinateFromRowIndex(const int64_t rowIndex, StructureEnum::Enum& surfaceStructureOut, int32_t& surfaceNodeIndexOut, @@ -497,7 +522,8 @@ public: enum class MatrixGridMode { - FILLED, + FILLED_TRIANGLES, + FILLED_TEXTURE, OUTLINE }; @@ -513,8 +539,9 @@ int32_t& numberOfColumnsOut, std::vector& rgbaOut) const; - GraphicsPrimitiveV3fC4f* getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, - const MatrixGridMode gridMode) const; + GraphicsPrimitive* getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode, + const MatrixGridMode gridMode, + const float opacity) const; /** Identifier for the matrix primitives alternative color used for the grid coloring */ int32_t getMatrixChartGraphicsPrimitiveGridColorIdentifier() const { return 1; } @@ -777,14 +804,19 @@ /** Histogram used when statistics computed on all data in file */ CaretPointer m_fileHistogram; - /** Primitive for matrix cells */ - mutable std::unique_ptr m_matrixGraphicsPrimitive; + /** Primitive for matrix cells drawn with triangles*/ + mutable std::unique_ptr m_matrixGraphicsTrianglesPrimitive; + + /** Primitive for matrix cells drawn using a texture */ + mutable std::unique_ptr m_matrixGraphicsTexturePrimitive; /** Primitive for grid outline around matrix cells */ mutable std::unique_ptr m_matrixGraphicsOutlinePrimitive; mutable uint8_t m_previousMatrixGridRGBA[4] = { 0, 1, 2, 3 }; + mutable float m_previousMatrixOpacity = -1.0; + int32_t m_fileHistogramNumberOfBuckets = 100; /** Histogram with limited values used when statistics computed on all data in file */ diff -Nru connectome-workbench-1.4.2/src/Files/CMakeLists.txt connectome-workbench-1.5.0/src/Files/CMakeLists.txt --- connectome-workbench-1.4.2/src/Files/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -22,6 +22,7 @@ # ADD_LIBRARY(Files AffineFile.h +AffineSeriesFile.h AnnotationFile.h AnnotationFileXmlFormatBase.h AnnotationFileXmlReader.h @@ -50,6 +51,7 @@ ChartableTwoFileDelegate.h ChartableTwoFileBaseChart.h ChartableTwoFileHistogramChart.h +ChartableTwoFileLineLayerChart.h ChartableTwoFileLineSeriesChart.h ChartableTwoFileMatrixChart.h CiftiBrainordinateDataSeriesFile.h @@ -80,6 +82,7 @@ EventGetDisplayedDataFiles.h EventMapYokingSelectMap.h EventMapYokingValidation.h +EventMediaFilesGet.h EventSurfaceColoringInvalidate.h EventSurfaceStructuresValidGet.h Fiber.h @@ -89,6 +92,8 @@ FiberTrajectoryColorModel.h FiberTrajectoryMapProperties.h FiberTrajectoryDisplayModeEnum.h +FileIdentificationAttributes.h +FileIdentificationMapSelectionEnum.h FilePathNamePrefixCompactor.h FociFile.h FociFileSaxReader.h @@ -109,6 +114,7 @@ LabelDrawingTypeEnum.h LabelFile.h MapYokingGroupEnum.h +MediaFile.h MetricDynamicConnectivityFile.h MetricFile.h MetricSmoothingObject.h @@ -162,6 +168,7 @@ XmlStreamWriterHelper.h AffineFile.cxx +AffineSeriesFile.cxx AnnotationFile.cxx AnnotationFileXmlFormatBase.cxx AnnotationFileXmlReader.cxx @@ -185,6 +192,7 @@ ChartableTwoFileDelegate.cxx ChartableTwoFileBaseChart.cxx ChartableTwoFileHistogramChart.cxx +ChartableTwoFileLineLayerChart.cxx ChartableTwoFileLineSeriesChart.cxx ChartableTwoFileMatrixChart.cxx CiftiBrainordinateDataSeriesFile.cxx @@ -215,6 +223,7 @@ EventGetDisplayedDataFiles.cxx EventMapYokingSelectMap.cxx EventMapYokingValidation.cxx +EventMediaFilesGet.cxx EventSurfaceColoringInvalidate.cxx EventSurfaceStructuresValidGet.cxx Fiber.cxx @@ -224,6 +233,8 @@ FiberTrajectoryColorModel.cxx FiberTrajectoryDisplayModeEnum.cxx FiberTrajectoryMapProperties.cxx +FileIdentificationAttributes.cxx +FileIdentificationMapSelectionEnum.cxx FilePathNamePrefixCompactor.cxx FociFile.cxx FociFileSaxReader.cxx @@ -244,6 +255,7 @@ LabelDrawingTypeEnum.cxx LabelFile.cxx MapYokingGroupEnum.cxx +MediaFile.cxx MetricDynamicConnectivityFile.cxx MetricFile.cxx MetricSmoothingObject.cxx diff -Nru connectome-workbench-1.4.2/src/Files/EventCaretMappableDataFilesGet.cxx connectome-workbench-1.5.0/src/Files/EventCaretMappableDataFilesGet.cxx --- connectome-workbench-1.4.2/src/Files/EventCaretMappableDataFilesGet.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/EventCaretMappableDataFilesGet.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -18,6 +18,7 @@ */ /*LICENSE_END*/ +#include #include #include "CaretAssert.h" @@ -109,4 +110,24 @@ allFilesOut = m_allCaretMappableDataFiles; } +/** + * Get all map data files sorted by name of file + * + * @param allFilesOut + * All map data files output. + */ +void +EventCaretMappableDataFilesGet::getAllFilesSortedByName(std::vector& allFilesOut) const +{ + allFilesOut = m_allCaretMappableDataFiles; + + std::sort(allFilesOut.begin(), + allFilesOut.end(), + [] (CaretMappableDataFile* lhs, CaretMappableDataFile* rhs) { + const int result = lhs->getFileNameNoPath().compare(rhs->getFileNameNoPath(), Qt::CaseInsensitive); + return (result < 0); + } ); +} + + diff -Nru connectome-workbench-1.4.2/src/Files/EventCaretMappableDataFilesGet.h connectome-workbench-1.5.0/src/Files/EventCaretMappableDataFilesGet.h --- connectome-workbench-1.4.2/src/Files/EventCaretMappableDataFilesGet.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/EventCaretMappableDataFilesGet.h 2021-02-16 19:46:47.000000000 +0000 @@ -41,6 +41,8 @@ void getAllFiles(std::vector& allFilesOut) const; + void getAllFilesSortedByName(std::vector& allFilesOut) const; + private: enum Mode { MODE_ANY_DATA_FILE_TYPE, diff -Nru connectome-workbench-1.4.2/src/Files/EventGetDisplayedDataFiles.cxx connectome-workbench-1.5.0/src/Files/EventGetDisplayedDataFiles.cxx --- connectome-workbench-1.4.2/src/Files/EventGetDisplayedDataFiles.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/EventGetDisplayedDataFiles.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -26,8 +26,9 @@ #undef __EVENT_GET_DISPLAYED_DATA_FILES_DECLARE__ #include "CaretAssert.h" +#include "EventBrowserTabIndicesGetAllViewed.h" +#include "EventManager.h" #include "EventTypeEnum.h" -#include "SurfaceFile.h" using namespace caret; @@ -40,7 +41,33 @@ */ /** - * Constructor for finding data files displayed in the given tab indices. + * Constructor for finding data files displayed in all windows/tabs. + * @param mode + * Mode for getting tab + */ +EventGetDisplayedDataFiles::EventGetDisplayedDataFiles(const Mode mode) +: Event(EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES), +m_mode(mode) +{ + switch (m_mode) { + case Mode::FILES_IN_ALL_TABS: + break; + case Mode::FILES_IN_VIEWED_TABS: + { + EventBrowserTabIndicesGetAllViewed viewedTabs; + EventManager::get()->sendEvent(viewedTabs.getPointer()); + std::vector indices = viewedTabs.getAllBrowserTabIndices(); + m_tabIndices.insert(indices.begin(), + indices.end()); + } + break; + case Mode::FILES_IN_WINDOWS_TABS: + CaretAssertMessage(0, "Use constructor that accepts window/tab indiceds for this mode"); + break; + } +} +/** + * Constructor for finding data files displayed in the given window and tab indices. * * param windowIndices * Indices of windows for displayed data files. @@ -49,7 +76,8 @@ */ EventGetDisplayedDataFiles::EventGetDisplayedDataFiles(const std::vector& windowIndices, const std::vector& tabIndices) -: Event(EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES) +: Event(EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES), +m_mode(Mode::FILES_IN_WINDOWS_TABS) { m_tabIndices.insert(tabIndices.begin(), tabIndices.end()); @@ -65,6 +93,15 @@ } /** + * @return the mode + */ +EventGetDisplayedDataFiles::Mode +EventGetDisplayedDataFiles::getMode() const +{ + return m_mode; +} + +/** * Is the tab index one for determining displayed data files. * * @param tabIndex @@ -75,6 +112,16 @@ bool EventGetDisplayedDataFiles::isTestForDisplayedDataFileInTabIndex(const int32_t tabIndex) const { + switch (m_mode) { + case Mode::FILES_IN_ALL_TABS: + return true; + break; + case Mode::FILES_IN_VIEWED_TABS: + break; + case Mode::FILES_IN_WINDOWS_TABS: + break; + } + if (m_tabIndices.find(tabIndex) != m_tabIndices.end()) { return true; } @@ -93,29 +140,18 @@ bool EventGetDisplayedDataFiles::isTestForDisplayedDataFileInWindowIndex(const int32_t windowIndex) const { - if (m_windowIndices.find(windowIndex) != m_windowIndices.end()) { - return true; + switch (m_mode) { + case Mode::FILES_IN_ALL_TABS: + return true; + break; + case Mode::FILES_IN_VIEWED_TABS: + return true; + break; + case Mode::FILES_IN_WINDOWS_TABS: + break; } - return false; -} - -/** - * Is the given surface structure displayed? - * - * @param surfaceStructure - * The surface structure. - * @return - * True if the structure is displayed, else false. - */ -bool -EventGetDisplayedDataFiles::isTestForDisplayedSurfaceStructure(const StructureEnum::Enum surfaceStructure) const -{ - setupSurfaceStrucutures(); - - if (std::find(m_surfaceStructures.begin(), - m_surfaceStructures.end(), - surfaceStructure) != m_surfaceStructures.end()) { + if (m_windowIndices.find(windowIndex) != m_windowIndices.end()) { return true; } @@ -162,48 +198,6 @@ } /** - * @return The displayed surface structures. Must be called - * AFTER event completes. - */ -std::vector -EventGetDisplayedDataFiles::getDisplayedSurfaceStructures() const -{ - setupSurfaceStrucutures(); - - return m_surfaceStructures; -} - -/** - * Setup the surface structures. - */ -void -EventGetDisplayedDataFiles::setupSurfaceStrucutures() const -{ - if ( ! m_surfaceStructuresValid) { - std::set structureSet; - - for (std::set::const_iterator dataFileIter = m_displayedDataFiles.begin(); - dataFileIter != m_displayedDataFiles.end(); - dataFileIter++) { - const CaretDataFile* dataFile = *dataFileIter; - CaretAssert(dataFile); - - if (dataFile->getDataFileType() == DataFileTypeEnum::SURFACE) { - const SurfaceFile* surfaceFile = dynamic_cast(dataFile); - CaretAssert(surfaceFile); - structureSet.insert(surfaceFile->getStructure()); - } - } - - m_surfaceStructures.insert(m_surfaceStructures.end(), - structureSet.begin(), - structureSet.end()); - m_surfaceStructuresValid = true; - } -} - - -/** * @return The tab indices. */ std::vector diff -Nru connectome-workbench-1.4.2/src/Files/EventGetDisplayedDataFiles.h connectome-workbench-1.5.0/src/Files/EventGetDisplayedDataFiles.h --- connectome-workbench-1.4.2/src/Files/EventGetDisplayedDataFiles.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/EventGetDisplayedDataFiles.h 2021-02-16 19:46:47.000000000 +0000 @@ -24,8 +24,6 @@ #include #include "Event.h" -#include "StructureEnum.h" - namespace caret { @@ -34,17 +32,25 @@ class EventGetDisplayedDataFiles : public Event { public: + enum class Mode { + FILES_IN_ALL_TABS, + FILES_IN_VIEWED_TABS, + FILES_IN_WINDOWS_TABS + }; + + EventGetDisplayedDataFiles(const Mode mode); + EventGetDisplayedDataFiles(const std::vector& windowIndices, const std::vector& tabIndices); virtual ~EventGetDisplayedDataFiles(); + Mode getMode() const; + bool isTestForDisplayedDataFileInTabIndex(const int32_t tabIndex) const; bool isTestForDisplayedDataFileInWindowIndex(const int32_t windowIndex) const; - bool isTestForDisplayedSurfaceStructure(const StructureEnum::Enum surfaceStructure) const; - void addDisplayedDataFile(const CaretDataFile* caretDataFile); bool isDataFileDisplayed(const CaretDataFile* caretDataFile) const; @@ -55,8 +61,6 @@ std::vector getWindowIndices() const; - std::vector getDisplayedSurfaceStructures() const; - private: EventGetDisplayedDataFiles(const EventGetDisplayedDataFiles&); @@ -67,7 +71,7 @@ // ADD_NEW_METHODS_HERE private: - void setupSurfaceStrucutures() const; + const Mode m_mode; // ADD_NEW_MEMBERS_HERE @@ -76,10 +80,6 @@ std::set m_tabIndices; std::set m_displayedDataFiles; - - mutable std::vector m_surfaceStructures; - - mutable bool m_surfaceStructuresValid; }; #ifdef __EVENT_GET_DISPLAYED_DATA_FILES_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/Files/EventMediaFilesGet.cxx connectome-workbench-1.5.0/src/Files/EventMediaFilesGet.cxx --- connectome-workbench-1.4.2/src/Files/EventMediaFilesGet.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/EventMediaFilesGet.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,75 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_MEDIA_FILES_GET_DECLARE__ +#include "EventMediaFilesGet.h" +#undef __EVENT_MEDIA_FILES_GET_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventMediaFilesGet + * \brief Get media type files + * \ingroup Files + */ + +/** + * Constructor. + */ +EventMediaFilesGet::EventMediaFilesGet() +: Event(EventTypeEnum::EVENT_MEDIA_FILES_GET) +{ + +} + +/** + * Destructor. + */ +EventMediaFilesGet::~EventMediaFilesGet() +{ +} + +/** + * Add a media file + * @param dataFile + * Media file for adding + */ +void +EventMediaFilesGet::addMediaFile(MediaFile* dataFile) +{ + CaretAssert(dataFile); + m_mediaFiles.push_back(dataFile); +} + +/** + * @return the media files + */ +std::vector +EventMediaFilesGet::getMediaFiles() const +{ + return m_mediaFiles; +} + diff -Nru connectome-workbench-1.4.2/src/Files/EventMediaFilesGet.h connectome-workbench-1.5.0/src/Files/EventMediaFilesGet.h --- connectome-workbench-1.4.2/src/Files/EventMediaFilesGet.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/EventMediaFilesGet.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,65 @@ +#ifndef __EVENT_MEDIA_FILES_GET_H__ +#define __EVENT_MEDIA_FILES_GET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#include "Event.h" + + + +namespace caret { + + class MediaFile; + + class EventMediaFilesGet : public Event { + + public: + EventMediaFilesGet(); + + virtual ~EventMediaFilesGet(); + + EventMediaFilesGet(const EventMediaFilesGet&) = delete; + + EventMediaFilesGet& operator=(const EventMediaFilesGet&) = delete; + + void addMediaFile(MediaFile* mediaFile); + + std::vector getMediaFiles() const; + + // ADD_NEW_METHODS_HERE + + private: + + std::vector m_mediaFiles; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_MEDIA_FILES_GET_DECLARE__ + // +#endif // __EVENT_MEDIA_FILES_GET_DECLARE__ + +} // namespace +#endif //__EVENT_MEDIA_FILES_GET_H__ diff -Nru connectome-workbench-1.4.2/src/Files/FileIdentificationAttributes.cxx connectome-workbench-1.5.0/src/Files/FileIdentificationAttributes.cxx --- connectome-workbench-1.4.2/src/Files/FileIdentificationAttributes.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/FileIdentificationAttributes.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,244 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __FILE_IDENTIFICATION_ATTRIBUTES_DECLARE__ +#include "FileIdentificationAttributes.h" +#undef __FILE_IDENTIFICATION_ATTRIBUTES_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::FileIdentificationAttributes + * \brief File identification attributes used in identification file filtering + * \ingroup Files + */ + +/** + * Constructor. + */ +FileIdentificationAttributes::FileIdentificationAttributes() +: CaretObject() +{ + reset(); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_enabled", + &m_enabled); + m_sceneAssistant->add("m_mapSelectionMode", + &m_mapSelectionMode); + m_sceneAssistant->add("m_mapIndex", + &m_mapIndex); +} + +void +FileIdentificationAttributes::reset() +{ + m_enabled = false; + m_mapSelectionMode = FileIdentificationMapSelectionEnum::SELECTED; + m_mapIndex = 0; +} + +/** + * Destructor. + */ +FileIdentificationAttributes::~FileIdentificationAttributes() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +FileIdentificationAttributes::FileIdentificationAttributes(const FileIdentificationAttributes& obj) +: CaretObject(obj), +SceneableInterface(obj) +{ + this->copyHelperFileIdentificationAttributes(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +FileIdentificationAttributes& +FileIdentificationAttributes::operator=(const FileIdentificationAttributes& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperFileIdentificationAttributes(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +FileIdentificationAttributes::copyHelperFileIdentificationAttributes(const FileIdentificationAttributes& obj) +{ + m_enabled = obj.m_enabled; + m_mapSelectionMode = obj.m_mapSelectionMode; + m_mapIndex = obj.m_mapIndex; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +FileIdentificationAttributes::toString() const +{ + return "FileIdentificationAttributes"; +} + +/** + * @return enabled for identification + */ +bool +FileIdentificationAttributes::isEnabled() const +{ + return m_enabled; +} + +/** + * Set enabled for identification + * + * @param enabled + * New value for enabled for identification + */ +void +FileIdentificationAttributes::setEnabled(const bool enabled) +{ + m_enabled = enabled; +} + +/** + * @return Map selection mode + */ +FileIdentificationMapSelectionEnum::Enum +FileIdentificationAttributes::getMapSelectionMode() const +{ + return m_mapSelectionMode; +} + +/** + * Set the map selection mode + * + * @param mapSelectionMode + * New map selection mode + */ +void +FileIdentificationAttributes::setMapSelectionMode(const FileIdentificationMapSelectionEnum::Enum mapSelectionMode) +{ + m_mapSelectionMode = mapSelectionMode; +} + +/** + * @return map selected for identification + */ +int32_t +FileIdentificationAttributes::getMapIndex() const +{ + return m_mapIndex; +} + +/** + * Set map selected for identification + * + * @param mapIndex + * New value for map selected for identification + */ +void +FileIdentificationAttributes::setMapIndex(const int32_t mapIndex) +{ + m_mapIndex = mapIndex; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +FileIdentificationAttributes::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "FileIdentificationAttributes", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +FileIdentificationAttributes::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + reset(); + + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff -Nru connectome-workbench-1.4.2/src/Files/FileIdentificationAttributes.h connectome-workbench-1.5.0/src/Files/FileIdentificationAttributes.h --- connectome-workbench-1.4.2/src/Files/FileIdentificationAttributes.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/FileIdentificationAttributes.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,109 @@ +#ifndef __FILE_IDENTIFICATION_ATTRIBUTES_H__ +#define __FILE_IDENTIFICATION_ATTRIBUTES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "FileIdentificationMapSelectionEnum.h" +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + + class FileIdentificationAttributes : public CaretObject, public SceneableInterface { + + public: + FileIdentificationAttributes(); + + virtual ~FileIdentificationAttributes(); + + FileIdentificationAttributes(const FileIdentificationAttributes& obj); + + FileIdentificationAttributes& operator=(const FileIdentificationAttributes& obj); + + bool isEnabled() const; + + void setEnabled(const bool enabled); + + FileIdentificationMapSelectionEnum::Enum getMapSelectionMode() const; + + void setMapSelectionMode(const FileIdentificationMapSelectionEnum::Enum mapSelectionMode); + + int32_t getMapIndex() const; + + void setMapIndex(const int32_t mapIndex); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperFileIdentificationAttributes(const FileIdentificationAttributes& obj); + + void reset(); + + std::unique_ptr m_sceneAssistant; + + /** enabled for identification*/ + bool m_enabled = false; + + /** identifiy all maps in file*/ + FileIdentificationMapSelectionEnum::Enum m_mapSelectionMode = FileIdentificationMapSelectionEnum::SELECTED; + + /** map selected for identification*/ + int32_t m_mapIndex = 0; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __FILE_IDENTIFICATION_ATTRIBUTES_DECLARE__ + // +#endif // __FILE_IDENTIFICATION_ATTRIBUTES_DECLARE__ + +} // namespace +#endif //__FILE_IDENTIFICATION_ATTRIBUTES_H__ diff -Nru connectome-workbench-1.4.2/src/Files/FileIdentificationMapSelectionEnum.cxx connectome-workbench-1.5.0/src/Files/FileIdentificationMapSelectionEnum.cxx --- connectome-workbench-1.4.2/src/Files/FileIdentificationMapSelectionEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/FileIdentificationMapSelectionEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __FILE_IDENTIFICATION_MAP_SELECTION_ENUM_DECLARE__ +#include "FileIdentificationMapSelectionEnum.h" +#undef __FILE_IDENTIFICATION_MAP_SELECTION_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::FileIdentificationMapSelectionEnum + * \brief Enumerated type for identification file map selection + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_fileIdentificationMapSelectionEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void fileIdentificationMapSelectionEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "FileIdentificationMapSelectionEnum.h" + * + * Instatiate: + * m_fileIdentificationMapSelectionEnumComboBox = new EnumComboBoxTemplate(this); + * m_fileIdentificationMapSelectionEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_fileIdentificationMapSelectionEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(fileIdentificationMapSelectionEnumComboBoxItemActivated())); + * + * Update the selection: + * m_fileIdentificationMapSelectionEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const FileIdentificationMapSelectionEnum::Enum VARIABLE = m_fileIdentificationMapSelectionEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +FileIdentificationMapSelectionEnum::FileIdentificationMapSelectionEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +FileIdentificationMapSelectionEnum::~FileIdentificationMapSelectionEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +FileIdentificationMapSelectionEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(FileIdentificationMapSelectionEnum(ALL, + "ALL", + "All Maps")); + + enumData.push_back(FileIdentificationMapSelectionEnum(SELECTED, + "SELECTED", + "Selected Map")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const FileIdentificationMapSelectionEnum* +FileIdentificationMapSelectionEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const FileIdentificationMapSelectionEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +FileIdentificationMapSelectionEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const FileIdentificationMapSelectionEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +FileIdentificationMapSelectionEnum::Enum +FileIdentificationMapSelectionEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FileIdentificationMapSelectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FileIdentificationMapSelectionEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type FileIdentificationMapSelectionEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +FileIdentificationMapSelectionEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const FileIdentificationMapSelectionEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +FileIdentificationMapSelectionEnum::Enum +FileIdentificationMapSelectionEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FileIdentificationMapSelectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FileIdentificationMapSelectionEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type FileIdentificationMapSelectionEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +FileIdentificationMapSelectionEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const FileIdentificationMapSelectionEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +FileIdentificationMapSelectionEnum::Enum +FileIdentificationMapSelectionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = FileIdentificationMapSelectionEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const FileIdentificationMapSelectionEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type FileIdentificationMapSelectionEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +FileIdentificationMapSelectionEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +FileIdentificationMapSelectionEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(FileIdentificationMapSelectionEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +FileIdentificationMapSelectionEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(FileIdentificationMapSelectionEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Files/FileIdentificationMapSelectionEnum.h connectome-workbench-1.5.0/src/Files/FileIdentificationMapSelectionEnum.h --- connectome-workbench-1.4.2/src/Files/FileIdentificationMapSelectionEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/FileIdentificationMapSelectionEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,104 @@ +#ifndef __FILE_IDENTIFICATION_MAP_SELECTION_ENUM_H__ +#define __FILE_IDENTIFICATION_MAP_SELECTION_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class FileIdentificationMapSelectionEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** All Maps */ + ALL, + /** Selected Map */ + SELECTED + }; + + + ~FileIdentificationMapSelectionEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + FileIdentificationMapSelectionEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const FileIdentificationMapSelectionEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __FILE_IDENTIFICATION_MAP_SELECTION_ENUM_DECLARE__ +std::vector FileIdentificationMapSelectionEnum::enumData; +bool FileIdentificationMapSelectionEnum::initializedFlag = false; +int32_t FileIdentificationMapSelectionEnum::integerCodeCounter = 0; +#endif // __FILE_IDENTIFICATION_MAP_SELECTION_ENUM_DECLARE__ + +} // namespace +#endif //__FILE_IDENTIFICATION_MAP_SELECTION_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Files/GiftiTypeFile.cxx connectome-workbench-1.5.0/src/Files/GiftiTypeFile.cxx --- connectome-workbench-1.4.2/src/Files/GiftiTypeFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/GiftiTypeFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -1313,6 +1313,8 @@ * Index of the node. * @param numberOfNodes * Number of nodes in the surface. + * @param dataValueSeparator + * Separator between multiple data values * @param textOut * Output containing identification information. */ @@ -1321,6 +1323,7 @@ const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, + const AString& dataValueSeparator, AString& textOut) const { textOut.clear(); @@ -1377,9 +1380,12 @@ mapIndex); if (key >= 0) { if ( ! valuesText.isEmpty()) { - valuesText.append(", "); + valuesText.append(dataValueSeparator); } valuesText.append(labelTable->getLabel(key)->getName()); + valuesText.append(" (" + + getMapName(mapIndex) + + ")"); } } @@ -1398,7 +1404,7 @@ const float value = mf->getValue(nodeIndex, mapIndex); if ( ! valuesText.isEmpty()) { - valuesText.append(", "); + valuesText.append(dataValueSeparator); } valuesText.append(AString::number(value, 'f', 3)); } diff -Nru connectome-workbench-1.4.2/src/Files/GiftiTypeFile.h connectome-workbench-1.5.0/src/Files/GiftiTypeFile.h --- connectome-workbench-1.4.2/src/Files/GiftiTypeFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/GiftiTypeFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -174,7 +174,8 @@ const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, - AString& textOut) const; + const AString& dataValueSeparator, + AString& textOut) const override; private: void copyHelperGiftiTypeFile(const GiftiTypeFile& gtf); diff -Nru connectome-workbench-1.4.2/src/Files/GroupAndNameHierarchyItem.cxx connectome-workbench-1.5.0/src/Files/GroupAndNameHierarchyItem.cxx --- connectome-workbench-1.4.2/src/Files/GroupAndNameHierarchyItem.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/GroupAndNameHierarchyItem.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -722,6 +722,19 @@ m_selectedInTab[targetTabIndex] = m_selectedInTab[sourceTabIndex]; m_expandedStatusInTab[targetTabIndex] = m_expandedStatusInTab[sourceTabIndex]; + AString indent; + switch (m_itemType) { + case ITEM_TYPE_GROUP: + indent = " "; + break; + case ITEM_TYPE_MODEL: + std::cout << std::endl; + break; + case ITEM_TYPE_NAME: + indent = " "; + break; + } + for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { diff -Nru connectome-workbench-1.4.2/src/Files/ImageFile.cxx connectome-workbench-1.5.0/src/Files/ImageFile.cxx --- connectome-workbench-1.4.2/src/Files/ImageFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ImageFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -19,10 +19,12 @@ /*LICENSE_END*/ #include +#include #include #include #include +#include #include #include @@ -31,8 +33,10 @@ #include "ControlPointFile.h" #include "ControlPoint3D.h" #include "DataFileException.h" +#include "DataFileContentInformation.h" #include "FileInformation.h" #include "GiftiMetaData.h" +#include "GraphicsUtilitiesOpenGL.h" #include "ImageCaptureSettings.h" #include "ImageFile.h" #include "Matrix4x4.h" @@ -48,7 +52,7 @@ * Constructor. */ ImageFile::ImageFile() -: CaretDataFile(DataFileTypeEnum::IMAGE) +: MediaFile(DataFileTypeEnum::IMAGE) { m_controlPointFile.grabNew(new ControlPointFile()); @@ -63,7 +67,7 @@ * QImage that is copied to this image file. */ ImageFile::ImageFile(const QImage& qimage) -: CaretDataFile(DataFileTypeEnum::IMAGE) +: MediaFile(DataFileTypeEnum::IMAGE) { m_controlPointFile.grabNew(new ControlPointFile()); @@ -89,7 +93,7 @@ const int imageWidth, const int imageHeight, const IMAGE_DATA_ORIGIN_LOCATION imageOrigin) -: CaretDataFile(DataFileTypeEnum::IMAGE) +: MediaFile(DataFileTypeEnum::IMAGE) { m_controlPointFile.grabNew(new ControlPointFile()); @@ -162,23 +166,15 @@ } /** - * @return The structure for this file. + * @return Number of frames in the file */ -StructureEnum::Enum -ImageFile::getStructure() const -{ - return StructureEnum::INVALID; -} - -/** - * Set the structure for this file. - * @param structure - * New structure for this file. - */ -void -ImageFile::setStructure(const StructureEnum::Enum /*structure */) +int32_t +ImageFile::getNumberOfFrames() const { - /* File does not support structures */ + if (m_image != NULL) { + return 1; + } + return 0; } /** @@ -209,17 +205,6 @@ return (m_image->width() <= 0); } -///** -// * @return A pointer to the QImage in this file. -// * Note that manipulating the pointer's data will -// * alter the contents of this file. -// */ -//QImage* -//ImageFile::getAsQImage() -//{ -// return m_image; -//} - /** * @return A pointer to the QImage in this file. */ @@ -994,76 +979,7 @@ } /** - * Get the image file extensions for the supported image types. - * The extensions do not include the leading period. - * - * @param imageFileExtensions - * Output filled with extensions for supported image types. - * @param defaultExtension - * The default extension (preference is png, jpg, jpeg) - */ -void -ImageFile::getImageFileExtensions(std::vector& imageFileExtensions, - AString& defaultExtension) -{ - imageFileExtensions.clear(); - defaultExtension = ""; - - QString firstExtension; - QString pngExtension; - QString jpegExtension; - QString jpgExtension; - QString tifExtension; - QString tiffExtension; - - QList imageFormats = QImageWriter::supportedImageFormats(); - const int numFormats = imageFormats.count(); - for (int i = 0; i < numFormats; i++) { - AString extension = QString(imageFormats.at(i)).toLower(); - imageFileExtensions.push_back(extension); - - if (i == 0) { - firstExtension = extension; - } - if (extension == "png") { - pngExtension = extension; - } - else if (extension == "jpg") { - jpgExtension = extension; - } - else if (extension == "jpeg") { - jpegExtension = extension; - } - else if (extension == "tif") { - tifExtension = extension; - } - else if (extension == "tiff") { - tiffExtension = extension; - } - } - - if (pngExtension.isEmpty() == false) { - defaultExtension = pngExtension; - } - else if (jpgExtension.isEmpty() == false) { - defaultExtension = jpgExtension; - } - else if (jpegExtension.isEmpty() == false) { - defaultExtension = jpegExtension; - } - else if (tifExtension.isEmpty() == false) { - defaultExtension = tifExtension; - } - else if (tiffExtension.isEmpty() == false) { - defaultExtension = tiffExtension; - } - else { - defaultExtension = firstExtension; - } -} - -/** - * Get the image file filters for the supported image types. + * Get the image file filters for the supported image types for saving image files * * @param imageFileFilters * Output filled with the filters for supported image types. @@ -1071,36 +987,11 @@ * Filter for the preferred image type. */ void -ImageFile::getImageFileFilters(std::vector& imageFileFilters, - AString& defaultFilter) +ImageFile::getSaveQFileDialogImageFilters(std::vector& imageFileFilters, + AString& defaultFilter) { - imageFileFilters.clear(); - defaultFilter.clear(); - - std::vector imageFileExtensions; - AString defaultExtension; - ImageFile::getImageFileExtensions(imageFileExtensions, - defaultExtension); - - const int32_t numExtensions = static_cast(imageFileExtensions.size()); - for (int32_t i = 0; i < numExtensions; i++) { - const AString ext = imageFileExtensions[i]; - const AString filter = (ext.toUpper() - + " Image File (*." - + ext - + ")"); - imageFileFilters.push_back(filter); - - if (ext == defaultExtension) { - defaultFilter = filter; - } - } - - if (defaultFilter.isEmpty()) { - if (imageFileFilters.empty() == false) { - defaultFilter = imageFileFilters[0]; - } - } + DataFileTypeEnum::getSaveQFileDialogImageFilters(imageFileFilters, + defaultFilter); } /** @@ -1708,6 +1599,100 @@ } /** + * @return The graphics primitive for drawing the image as a texture in media drawing model. + */ +GraphicsPrimitiveV3fT3f* +ImageFile::getGraphicsPrimitiveForMediaDrawing() const +{ + if (m_image == NULL) { + return NULL; + } + + if (m_graphicsPrimitiveForMediaDrawing == NULL) { + std::vector bytesRGBA; + int32_t width(0); + int32_t height(0); + + /* + * If image is too big for OpenGL texture limits, scale image to acceptable size + */ + const int32_t maxTextureWidthHeight = GraphicsUtilitiesOpenGL::getTextureWidthHeightMaximumDimension(); + if (maxTextureWidthHeight > 0) { + const int32_t excessWidth(m_image->width() - maxTextureWidthHeight); + const int32_t excessHeight(m_image->height() - maxTextureWidthHeight); + if ((excessWidth > 0) + || (excessHeight > 0)) { + if (excessWidth > excessHeight) { + CaretLogWarning(getFileName() + + " is too big for texture. Maximum width/height is: " + + AString::number(maxTextureWidthHeight) + + " Image Width: " + + AString::number(m_image->width()) + + " Image Height: " + + AString::number(m_image->height())); + } + } + } + + /* + * Some images may use a color table so convert images + * if there are not in preferred format prior to + * getting colors of pixels + */ + bool validRGBA(false); + if (m_image->format() != QImage::Format_RGB32) { + QImage image = m_image->convertToFormat(QImage::Format_RGB32); + if (! image.isNull()) { + ImageFile convImageFile; + convImageFile.setFromQImage(image); + validRGBA = convImageFile.getImageBytesRGBA(IMAGE_DATA_ORIGIN_AT_BOTTOM, + bytesRGBA, + width, + height); + } + } + else { + validRGBA = getImageBytesRGBA(IMAGE_DATA_ORIGIN_AT_BOTTOM, + bytesRGBA, + width, + height); + } + + if (validRGBA) { + GraphicsPrimitiveV3fT3f* primitive = GraphicsPrimitive::newPrimitiveV3fT3f(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + &bytesRGBA[0], + width, + height, + GraphicsPrimitive::TextureWrappingType::CLAMP, + GraphicsPrimitive::TextureFilteringType::LINEAR); + /* + * A Triangle Strip (consisting of two triangles) is used + * for drawing the image. At this time, the XYZ coordinates + * do not matter and they will be updated when the annotation + * is drawn by a call to ::setVertexBounds(). + * The order of the vertices in the triangle strip is + * Top Left, Bottom Left, Top Right, Bottom Right. If this + * order changes, ::setVertexBounds must be updated. + * + * Zeros are used for the X- and Y-coordinates. + * The third and fourth parameters are the texture + * S and T coordinates. + */ + const float halfWidth(width * 0.5); + const float halfHeight(height * 0.5); + primitive->addVertex(-halfWidth, halfHeight, 0, 1); /* Top Left */ + primitive->addVertex(-halfWidth, -halfHeight, 0, 0); /* Bottom Left */ + primitive->addVertex(halfWidth, halfHeight, 1, 1); /* Top Right */ + primitive->addVertex(halfWidth, -halfHeight, 1, 0); /* Bottom Right */ + + m_graphicsPrimitiveForMediaDrawing.reset(primitive); + } + } + + return m_graphicsPrimitiveForMediaDrawing.get(); +} + +/** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. @@ -1724,6 +1709,9 @@ ImageFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { + MediaFile::saveFileDataToScene(sceneAttributes, + sceneClass); + if (m_controlPointFile != NULL) { sceneClass->addClass(m_controlPointFile->saveToScene(sceneAttributes, "m_controlPointFile")); @@ -1748,8 +1736,81 @@ ImageFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { + MediaFile::restoreFileDataFromScene(sceneAttributes, + sceneClass); + m_controlPointFile->restoreFromScene(sceneAttributes, sceneClass->getClass("m_controlPointFile")); } +/** + * @return File casted to an image file (avoids use of dynamic_cast that can be slow) + */ +ImageFile* +ImageFile::castToImageFile() +{ + return this; +} + +/** + * @return File casted to an image file (avoids use of dynamic_cast that can be slow) + * Overidden in ImageFile + */ +const ImageFile* +ImageFile::castToImageFile() const +{ + return this; +} + +/** + * @param dataFileInformation + * Item to which information is added. + */ +void +ImageFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) +{ + MediaFile::addToDataFileContentInformation(dataFileInformation); + + if (m_image != NULL) { + dataFileInformation.addNameAndValue("Width", m_image->width()); + dataFileInformation.addNameAndValue("Height", m_image->height()); + dataFileInformation.addNameAndValue("Color Table", (m_image->colorTable().empty() + ? "No" + : "Yes")); + } +} +/** + * Get all image file extensions supported by Qt for reading and writing image files. + * @param readableExtensionsOut + * Output contains all readable image file extensions + * @param writableExtensionsOut + * Output contains all writable image file extensions + */ +void +ImageFile::getQtSupportedImageFileExtensions(std::vector& readableExtensionsOut, + std::vector& writableExtensionsOut) +{ + DataFileTypeEnum::getQtSupportedImageFileExtensions(readableExtensionsOut, + writableExtensionsOut); +} + +/** + * Get all image file extensions supported by Workbench for reading and writing image files. + * These are a subset of the extensions supported by Qt. + * @param readableExtensionsOut + * Output contains all readable image file extensions + * @param writableExtensionsOut + * Output contains all writable image file extensions + * @param defaultWritableExtension + * The default file extension + */ +void +ImageFile::getWorkbenchSupportedImageFileExtensions(std::vector& readableExtensionsOut, + std::vector& writableExtensionsOut, + AString& defaultWritableExtension) +{ + DataFileTypeEnum::getWorkbenchSupportedImageFileExtensions(readableExtensionsOut, + writableExtensionsOut, + defaultWritableExtension); +} diff -Nru connectome-workbench-1.4.2/src/Files/ImageFile.h connectome-workbench-1.5.0/src/Files/ImageFile.h --- connectome-workbench-1.4.2/src/Files/ImageFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/ImageFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,8 +21,9 @@ #ifndef __IMAGE_FILE_H__ #define __IMAGE_FILE_H__ -#include "CaretDataFile.h" +#include "MediaFile.h" #include "CaretPointer.h" +#include "GraphicsPrimitiveV3fT3f.h" class QColor; class QImage; @@ -33,7 +34,7 @@ class VolumeFile; /// File for images -class ImageFile : public CaretDataFile { +class ImageFile : public MediaFile { public: // class ControlPoint { // public: @@ -99,34 +100,25 @@ void clear(); /** - * @return The structure for this file. + * @return Number of frames in the file */ - virtual StructureEnum::Enum getStructure() const; - - /** - * Set the structure for this file. - * @param structure - * New structure for this file. - */ - virtual void setStructure(const StructureEnum::Enum structure); - + virtual int32_t getNumberOfFrames() const override; + /** * @return Get access to the file's metadata. */ - virtual GiftiMetaData* getFileMetaData(); + virtual GiftiMetaData* getFileMetaData() override; /** * @return Get access to unmodifiable file's metadata. */ - virtual const GiftiMetaData* getFileMetaData() const; + virtual const GiftiMetaData* getFileMetaData() const override; virtual bool compareFileForUnitTesting(const DataFile* df, const float tolerance, AString& messageOut) const; - bool isEmpty() const; - - //QImage* getAsQImage(); + bool isEmpty() const override; const QImage* getAsQImage() const; @@ -151,9 +143,9 @@ int32_t getHeight() const; - virtual void readFile(const AString& filename); + virtual void readFile(const AString& filename) override; - virtual void writeFile(const AString& filename); + virtual void writeFile(const AString& filename) override; void cropImageRemoveBackground(const int marginSize, const uint8_t backgroundColor[3]); @@ -191,11 +183,8 @@ const int numImagesPerRow, const uint8_t backgroundColor[3]); - static void getImageFileExtensions(std::vector& imageFileExtensions, - AString& defaultExtension); - - static void getImageFileFilters(std::vector& imageFileFilters, - AString& defaultFilter); + static void getSaveQFileDialogImageFilters(std::vector& imageFileFilters, + AString& defaultFilter); static QImage scaleToSizeWithPadding(const QImage& image, const int width, @@ -205,15 +194,31 @@ VolumeFile* convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, AString& errorMessageOut) const; + GraphicsPrimitiveV3fT3f* getGraphicsPrimitiveForMediaDrawing() const; + ControlPointFile* getControlPointFile(); const ControlPointFile* getControlPointFile() const; virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, - SceneClass* sceneClass); + SceneClass* sceneClass) override; virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass); + const SceneClass* sceneClass) override; + + ImageFile* castToImageFile(); + + const ImageFile* castToImageFile() const; + + virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) override; + + static void getQtSupportedImageFileExtensions(std::vector& readableExtensionsOut, + std::vector& writableExtensionsOut); + + static void getWorkbenchSupportedImageFileExtensions(std::vector& readableExtensionsOut, + std::vector& writableExtensionsOut, + AString& defaultWritableExtension); + private: ImageFile(const ImageFile&); @@ -234,6 +239,8 @@ CaretPointer m_controlPointFile; + mutable std::unique_ptr m_graphicsPrimitiveForMediaDrawing; + static const float s_defaultWindowDepthPercentage; }; diff -Nru connectome-workbench-1.4.2/src/Files/MediaFile.cxx connectome-workbench-1.5.0/src/Files/MediaFile.cxx --- connectome-workbench-1.4.2/src/Files/MediaFile.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/MediaFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,192 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __MEDIA_FILE_DECLARE__ +#include "MediaFile.h" +#undef __MEDIA_FILE_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::MediaFile + * \brief Base class for media type files (image, movie) + * \ingroup Files + */ + +/** + * Constructor. + * @param dataFileType + * Type of data file + */ +MediaFile::MediaFile(const DataFileTypeEnum::Enum dataFileType) +: CaretDataFile(dataFileType) +{ + switch (dataFileType) { + case DataFileTypeEnum::IMAGE: + break; + default: + { + const AString msg("Invalid data file type=" + + DataFileTypeEnum::toName(dataFileType) + + ". Has new file type been added?"); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + } + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + +} + +/** + * Destructor. + */ +MediaFile::~MediaFile() +{ +} + +/** + * @return Name of frame at given index. + * @param frameIndex Index of the frame + */ +AString +MediaFile::getFrameName(const int32_t frameIndex) const +{ + CaretAssert((frameIndex >= 0) && (frameIndex < getNumberOfFrames())); + const AString defaultFrameName(AString::number(frameIndex+1)); + return defaultFrameName; +} + +/** + * @return The units for the 'interval' between two consecutive frames. + */ +NiftiTimeUnitsEnum::Enum +MediaFile::getFrameIntervalUnits() const +{ + return NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN; +} + +/** + * Get the units value for the first frame and the + * quantity of units between consecutive frames. If the + * units for the frame is unknown, value of one (1) are + * returned for both output values. + * + * @param firstFrameUnitsValueOut + * Output containing units value for first frame. + * @param frameIntervalStepValueOut + * Output containing number of units between consecutive frame. + */ +void +MediaFile::getFrameIntervalStartAndStep(float& firstFrameUnitsValueOut, + float& frameIntervalStepValueOut) const +{ + firstFrameUnitsValueOut = 1.0; + frameIntervalStepValueOut = 1.0; +} + +/** + * @return The structure for this file. + */ +StructureEnum::Enum +MediaFile::getStructure() const +{ + return StructureEnum::INVALID; +} + +/** + * Set the structure for this file. + * @param structure + * New structure for this file. + */ +void +MediaFile::setStructure(const StructureEnum::Enum /*structure */) +{ + /* File does not support structures */ +} + + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +MediaFile::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +MediaFile::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + +/** + * @return File casted to an media file (avoids use of dynamic_cast that can be slow) + * Overidden in MediaFile + */ +MediaFile* +MediaFile::castToMediaFile() +{ + return NULL; +} + +/** + * @return File casted to an media file (avoids use of dynamic_cast that can be slow) + * Overidden in ImageFile + */ +const MediaFile* +MediaFile::castToMediaFile() const +{ + return NULL; +} diff -Nru connectome-workbench-1.4.2/src/Files/MediaFile.h connectome-workbench-1.5.0/src/Files/MediaFile.h --- connectome-workbench-1.4.2/src/Files/MediaFile.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/MediaFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,117 @@ +#ifndef __MEDIA_FILE_H__ +#define __MEDIA_FILE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretDataFile.h" +#include "NiftiEnums.h" +#include "SceneClassAssistant.h" + +namespace caret { + + class MediaFile : public CaretDataFile { + + public: + virtual ~MediaFile(); + + MediaFile(const MediaFile&) = delete; + + MediaFile& operator=(const MediaFile&) = delete; + + /** + * @return The structure for this file. + */ + virtual StructureEnum::Enum getStructure() const override; + + /** + * Set the structure for this file. + * @param structure + * New structure for this file. + */ + virtual void setStructure(const StructureEnum::Enum structure) override; + + /** + * @return Number of frames in the file + */ + virtual int32_t getNumberOfFrames() const = 0; + + /** + * @return Name of frame at given index. + * @param frameIndex Index of the frame + */ + virtual AString getFrameName(const int32_t frameIndex) const; + + /** + * @return The units for the 'interval' between two consecutive frames. + */ + NiftiTimeUnitsEnum::Enum getFrameIntervalUnits() const; + + /** + * Get the units value for the first frame and the + * quantity of units between consecutive frames. If the + * units for the frame is unknown, value of one (1) are + * returned for both output values. + * + * @param firstFrameUnitsValueOut + * Output containing units value for first frame. + * @param frameIntervalStepValueOut + * Output containing number of units between consecutive frame. + */ + virtual void getFrameIntervalStartAndStep(float& firstFrameUnitsValueOut, + float& frameIntervalStepValueOut) const; + + + MediaFile* castToMediaFile(); + + const MediaFile* castToMediaFile() const; + + // ADD_NEW_METHODS_HERE + + + + + + protected: + MediaFile(const DataFileTypeEnum::Enum dataFileType); + + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private: + std::unique_ptr m_sceneAssistant; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __MEDIA_FILE_DECLARE__ + // +#endif // __MEDIA_FILE_DECLARE__ + +} // namespace +#endif //__MEDIA_FILE_H__ diff -Nru connectome-workbench-1.4.2/src/Files/PaletteFile.cxx connectome-workbench-1.5.0/src/Files/PaletteFile.cxx --- connectome-workbench-1.4.2/src/Files/PaletteFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/PaletteFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -95,6 +95,77 @@ } /** + * Add a scalar and RGB INTEGER color to a palette. Palette MUST have a name. + * @param palette + * Palette that receives scalar and color + * @param scalar + * The scalar value + * @param red + * Red component [0, 255] + * @param green + * Green component [0, 255] + * @param blue + * Blue component [0, 255] + */ +void +PaletteFile::addPaletteScalarAndColor(Palette& palette, + const float scalar, + const int32_t red, + const int32_t green, + const int32_t blue) +{ + AString name(palette.getName()); + if (name.isEmpty()) { + CaretAssertMessage(0, "Palette is missing name. Palette will be invalid."); + return; + } + + /* + * Name of color is name of palette followed by index of color + */ + name.append("_" + AString::number(palette.getNumberOfScalarsAndColors())); + +#ifndef NDEBUG + if (this->labelTable.getLabel(name) != NULL) { + CaretAssertMessage(0, ("Generation of color name failed. Color \"" + + name + + "\" already exists.")); + } +#endif + + this->addColor(name, red, green, blue); + + palette.addScalarAndColor(scalar, name); +} + +/** + * Add a scalar and RGB FLOAT color to a palette. Palette MUST have a name. + * @param palette + * Palette that receives scalar and color + * @param scalar + * The scalar value + * @param red + * Red component [0, 1] + * @param green + * Green component [0, 1] + * @param blue + * Blue component [0, 1] + */ +void +PaletteFile::addPaletteScalarAndColorFloat(Palette& palette, + const float scalar, + const float red, + const float green, + const float blue) +{ + addPaletteScalarAndColor(palette, + scalar, + red * 255.0, + green * 255.0, + blue * 255.0); +} + +/** * Add a palette color. * * @param pc - color to add. @@ -805,6 +876,94 @@ powerSurf.addScalarAndColor( 0.0, "_ps_0"); addPalette(powerSurf); } + + //black to color versions of three of the fsl palettes below + if (this->getPaletteByName("black-red") == NULL) { + Palette blackRed; + blackRed.setName("black-red"); + this->addColor("black-red_0", 0, 0, 0); + this->addColor("black-red_05", 127, 0, 0); //provide a zero color for neg to pos palette, for sanity + this->addColor("black-red_1", 255, 0, 0); + blackRed.addScalarAndColor(1.0f, "black-red_1"); + blackRed.addScalarAndColor(0.0f, "black-red_05"); + blackRed.addScalarAndColor(-1.0f, "black-red_0"); + addPalette(blackRed); + + Palette blackGreen; + blackGreen.setName("black-green"); + this->addColor("black-green_0", 0, 0, 0); + this->addColor("black-green_05", 0, 127, 0); + this->addColor("black-green_1", 0, 255, 0); + //this->addColor("black-green_05", 0, 90, 0); //experimental option to try to match brightness across color options + //this->addColor("black-green_1", 0, 180, 0); + blackGreen.addScalarAndColor(1.0f, "black-green_1"); + blackGreen.addScalarAndColor(0.0f, "black-green_05"); + blackGreen.addScalarAndColor(-1.0f, "black-green_0"); + addPalette(blackGreen); + + Palette blackBlue; + blackBlue.setName("black-blue"); + this->addColor("black-blue_0", 0, 0, 0); + this->addColor("black-blue_05", 0, 0, 127); + this->addColor("black-blue_1", 0, 0, 255); + //this->addColor("black-blue_05", 50, 50, 127); //experimental option to try to match brightness across color options + //this->addColor("black-blue_1", 100, 100, 255); + blackBlue.addScalarAndColor(1.0f, "black-blue_1"); + blackBlue.addScalarAndColor(0.0f, "black-blue_05"); + blackBlue.addScalarAndColor(-1.0f, "black-blue_0"); + addPalette(blackBlue); + + Palette blackRedPos; + blackRedPos.setName("black-red-positive"); + blackRedPos.addScalarAndColor(1.0f, "black-red_1"); + blackRedPos.addScalarAndColor(0.0f, "black-red_0"); + addPalette(blackRedPos); + + Palette blackGreenPositive; + blackGreenPositive.setName("black-green-positive"); + blackGreenPositive.addScalarAndColor(1.0f, "black-green_1"); + blackGreenPositive.addScalarAndColor(0.0f, "black-green_0"); + addPalette(blackGreenPositive); + + Palette blackBluePositive; + blackBluePositive.setName("black-blue-positive"); + blackBluePositive.addScalarAndColor(1.0f, "black-blue_1"); + blackBluePositive.addScalarAndColor(0.0f, "black-blue_0"); + addPalette(blackBluePositive); + } + + if (this->getPaletteByName("blue-black-green") == NULL) { + Palette bbg; + bbg.setName("blue-black-green"); + + addPaletteScalarAndColor(bbg, 1.0, 0, 255, 0); /* green */ + addPaletteScalarAndColor(bbg, 0.0, 0, 0, 0); /* black */ + addPaletteScalarAndColor(bbg, -1.0, 0, 0, 255); /* blue */ + + addPalette(bbg); + } + + if (this->getPaletteByName("blue-black-red") == NULL) { + Palette bbr; + bbr.setName("blue-black-red"); + + addPaletteScalarAndColor(bbr, 1.0, 255, 0, 0); /* red */ + addPaletteScalarAndColor(bbr, 0.0, 0, 0, 0); /* black */ + addPaletteScalarAndColor(bbr, -1.0, 0, 0, 255); /* blue */ + + addPalette(bbr); + } + + if (this->getPaletteByName("red-black-green") == NULL) { + Palette rbg; + rbg.setName("red-black-green"); + + addPaletteScalarAndColor(rbg, 1.0, 0, 255, 0); /* green */ + addPaletteScalarAndColor(rbg, 0.0, 0, 0, 0); /* black */ + addPaletteScalarAndColor(rbg, -1.0, 255, 0, 0); /* red */ + + addPalette(rbg); + } /* * FSL Red palette from WB-289 @@ -1122,12 +1281,12 @@ // Psych palette // if (this->getPaletteByName("PSYCH") == NULL) { - this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 ); + /*this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 );//don't add colors repeatedly, since GiftiLabelTable now warns about everything this->addColor("_poran-red", 0xff, 0x44, 0x00 ); this->addColor("_pblue", 0x00, 0x44, 0xff ); this->addColor("_pltblue1", 0x00, 0x69, 0xff ); this->addColor("_pltblue2", 0x00, 0x99, 0xff ); - this->addColor("_pbluecyan", 0x00, 0xcc, 0xff ); + this->addColor("_pbluecyan", 0x00, 0xcc, 0xff );//*/ Palette psych; psych.setName("PSYCH"); @@ -1149,12 +1308,12 @@ // Psych no-none palette // if (this->getPaletteByName("PSYCH-NO-NONE") == NULL) { - this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 ); + /*this->addColor("_pyell-oran", 0xff, 0xcc, 0x00 ); this->addColor("_poran-red", 0xff, 0x44, 0x00 ); this->addColor("_pblue", 0x00, 0x44, 0xff ); this->addColor("_pltblue1", 0x00, 0x69, 0xff ); this->addColor("_pltblue2", 0x00, 0x99, 0xff ); - this->addColor("_pbluecyan", 0x00, 0xcc, 0xff ); + this->addColor("_pbluecyan", 0x00, 0xcc, 0xff );//*/ Palette psychNoNone; psychNoNone.setName("PSYCH-NO-NONE"); @@ -1299,12 +1458,12 @@ // // Colors by Russ H. // - int _rbgyr20_10[3] = { 0x00, 0xff, 0x00 }; + /*int _rbgyr20_10[3] = { 0x00, 0xff, 0x00 }; this->addColor("_rbgyr20_10", _rbgyr20_10); int _rbgyr20_15[3] = { 0xff, 0xff, 0x00 }; this->addColor("_rbgyr20_15", _rbgyr20_15); int _rbgyr20_20[3] = { 0xff, 0x00, 0x00 }; - this->addColor("_rbgyr20_20", _rbgyr20_20); + this->addColor("_rbgyr20_20", _rbgyr20_20);//*/ int _rbgyr20_21[3] = { 0x9d, 0x22, 0xc1 }; this->addColor("_rbgyr20_21", _rbgyr20_21); @@ -1511,8 +1670,8 @@ // Positive/Negative palette // if (this->getPaletteByName("POS_NEG") == NULL) { - this->addColor("pos_neg_blue", 0x00, 0x00, 0xff ); - this->addColor("pos_neg_red", 0xff, 0x00, 0x00 ); + /*this->addColor("pos_neg_blue", 0x00, 0x00, 0xff ); + this->addColor("pos_neg_red", 0xff, 0x00, 0x00 );//*/ Palette posNeg; posNeg.setName("POS_NEG"); diff -Nru connectome-workbench-1.4.2/src/Files/PaletteFile.h connectome-workbench-1.5.0/src/Files/PaletteFile.h --- connectome-workbench-1.4.2/src/Files/PaletteFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/PaletteFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -75,6 +75,18 @@ const AString& name, const int32_t rgb[]); + void addPaletteScalarAndColor(Palette& palette, + const float scalar, + const int32_t red, + const int32_t green, + const int32_t blue); + + void addPaletteScalarAndColorFloat(Palette& palette, + const float scalar, + const float red, + const float green, + const float blue); + const GiftiLabel* getColor(const int32_t index) const; const GiftiLabel* getColorByName(const AString& colorName) const; diff -Nru connectome-workbench-1.4.2/src/Files/RibbonMappingHelper.cxx connectome-workbench-1.5.0/src/Files/RibbonMappingHelper.cxx --- connectome-workbench-1.4.2/src/Files/RibbonMappingHelper.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/RibbonMappingHelper.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -263,6 +263,120 @@ return ((float)inside) / (divisions * divisions * divisions * 2); } + void appendSubvoxelWeights(vector& outList, const VolumeSpace& myVolSpace, const int64_t* ijk, PolyInfo& myPoly, const int divisions, + const Vector3D& ivec, const Vector3D& jvec, const Vector3D& kvec) + { + Vector3D myLowCorner; + myVolSpace.indexToSpace(ijk[0] - 0.5f, ijk[1] - 0.5f, ijk[2] - 0.5f, myLowCorner); + Vector3D istep = ivec / divisions; + Vector3D jstep = jvec / divisions; + Vector3D kstep = kvec / divisions; + myLowCorner += istep * 0.5f + jstep * 0.5f + kstep * 0.5f; + for (int i = 0; i < divisions; ++i) + { + Vector3D tempVeci = myLowCorner + istep * i; + for (int j = 0; j < divisions; ++j) + { + Vector3D tempVecj = tempVeci + jstep * j; + for (int k = 0; k < divisions; ++k) + { + Vector3D thisPoint = tempVecj + kstep * k; + int inside = myPoly.isInside(thisPoint); + if (inside > 0) + { + outList.push_back(PointWeight(inside, thisPoint)); + } + } + } + } + } +} + +vector > RibbonMappingHelper::computePointsRibbon(const VolumeSpace& myVolSpace, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const float* roiFrame, const int& numDivisions, const bool& thinColumn) +{ + if (!innerSurf->hasNodeCorrespondence(*outerSurf)) + { + throw CaretException("input surfaces to ribbon mapping do not have vertex correspondence"); + } + if (numDivisions < 1) + { + throw CaretException("number of voxel subdivisions must be positive for ribbon mapping"); + } + int64_t numNodes = outerSurf->getNumberOfNodes(); + vector > myPointsOut(numNodes); + Vector3D origin, ivec, jvec, kvec;//these are the spatial projections of the ijk unit vectors (also, the offset that specifies the origin) + myVolSpace.getSpacingVectors(ivec, jvec, kvec, origin); + const float* outerCoords = outerSurf->getCoordinateData(); + const float* innerCoords = innerSurf->getCoordinateData(); + const int64_t* myDims = myVolSpace.getDims(); +#pragma omp CARET_PAR + { + int maxPointCount = numDivisions * numDivisions * numDivisions;//guess for preallocating vectors + CaretPointer myTopoHelp = innerSurf->getTopologyHelper(); +#pragma omp CARET_FOR schedule(dynamic) + for (int64_t node = 0; node < numNodes; ++node) + { + myPointsOut[node].clear(); + myPointsOut[node].reserve(maxPointCount); + int64_t node3 = node * 3; + PolyInfo myPoly(innerSurf, outerSurf, node, thinColumn);//build the polygon + Vector3D minIndex, maxIndex, tempvec; + myVolSpace.spaceToIndex(innerCoords + node3, minIndex);//find the bounding box in VOLUME INDEX SPACE, starting with the center nodes + maxIndex = minIndex;//this could be tightened to bounding box in divided voxel space, but as-is it is symmetric with the voxel weight code + myVolSpace.spaceToIndex(outerCoords + node3, tempvec); + for (int i = 0; i < 3; ++i) + { + if (tempvec[i] < minIndex[i]) minIndex[i] = tempvec[i]; + if (tempvec[i] > maxIndex[i]) maxIndex[i] = tempvec[i]; + } + int numNeigh; + const int* myNeighList = myTopoHelp->getNodeNeighbors(node, numNeigh);//and now the neighbors + for (int j = 0; j < numNeigh; ++j) + { + int neigh3 = myNeighList[j] * 3; + myVolSpace.spaceToIndex(outerCoords + neigh3, tempvec); + for (int i = 0; i < 3; ++i) + { + if (tempvec[i] < minIndex[i]) minIndex[i] = tempvec[i]; + if (tempvec[i] > maxIndex[i]) maxIndex[i] = tempvec[i]; + } + myVolSpace.spaceToIndex(innerCoords + neigh3, tempvec); + for (int i = 0; i < 3; ++i) + { + if (tempvec[i] < minIndex[i]) minIndex[i] = tempvec[i]; + if (tempvec[i] > maxIndex[i]) maxIndex[i] = tempvec[i]; + } + } + int startIndex[3], endIndex[3]; + for (int i = 0; i < 3; ++i) + { + startIndex[i] = (int)ceil(minIndex[i] - 0.5f);//give an extra half voxel in order to get anything which could have some polygon in it + endIndex[i] = (int)floor(maxIndex[i] + 0.5f) + 1;//ditto, plus the one-after end convention + if (startIndex[i] < 0) startIndex[i] = 0;//keep it inside the volume boundaries + if (endIndex[i] > myDims[i]) endIndex[i] = myDims[i]; + } + int64_t ijk[3]; + for (ijk[0] = startIndex[0]; ijk[0] < endIndex[0]; ++ijk[0]) + { + for (ijk[1] = startIndex[1]; ijk[1] < endIndex[1]; ++ijk[1]) + { + for (ijk[2] = startIndex[2]; ijk[2] < endIndex[2]; ++ijk[2]) + { + if (roiFrame == NULL || roiFrame[myVolSpace.getIndex(ijk)] > 0.0f) + { + appendSubvoxelWeights(myPointsOut[node], myVolSpace, ijk, myPoly, numDivisions, ivec, jvec, kvec); + } + } + } + } + if ((int)myPointsOut[node].size() > maxPointCount) + {//capacity() would use more memory + maxPointCount = myPointsOut[node].size(); + } + } + } + return myPointsOut; } void RibbonMappingHelper::computeWeightsRibbon(vector >& myWeightsOut, const VolumeSpace& myVolSpace, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, diff -Nru connectome-workbench-1.4.2/src/Files/RibbonMappingHelper.h connectome-workbench-1.5.0/src/Files/RibbonMappingHelper.h --- connectome-workbench-1.4.2/src/Files/RibbonMappingHelper.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/RibbonMappingHelper.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include "Vector3D.h" + #include "stdint.h" #include #include @@ -45,6 +47,14 @@ } }; + struct PointWeight + {//for precomputation for interpolation-type ribbon mapping + Vector3D coord; + int_fast8_t weight;//will be 1 or 2, needed because of quadrilateral ambiguity + PointWeight() { }; + PointWeight(const int weightIn, const Vector3D coordIn) { weight = weightIn; coord = coordIn; } + }; + class RibbonMappingHelper { public: @@ -52,6 +62,11 @@ static void computeWeightsRibbon(std::vector >& myWeightsOut, const VolumeSpace& myVolSpace, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const float* roiFrame = NULL, const int& numDivisions = 3, const bool& thinColumn = false); + + ///compute per-vertex ribbon mapping points - surfaces must have vertex correspondence, or an exception is thrown + static std::vector > computePointsRibbon(const VolumeSpace& myVolSpace, + const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, + const float* roiFrame = NULL, const int& numDivisions = 3, const bool& thinColumn = false); }; } diff -Nru connectome-workbench-1.4.2/src/Files/SceneDataFileInfo.cxx connectome-workbench-1.5.0/src/Files/SceneDataFileInfo.cxx --- connectome-workbench-1.4.2/src/Files/SceneDataFileInfo.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SceneDataFileInfo.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -307,7 +307,7 @@ void SceneDataFileInfo::print(const std::vector& sceneDataFileInfo) { - for (const auto sdfi : sceneDataFileInfo) { + for (const auto& sdfi : sceneDataFileInfo) { std::cout << sdfi.getAbsolutePathAndFileName() << std::endl; } } diff -Nru connectome-workbench-1.4.2/src/Files/SceneFile.cxx connectome-workbench-1.5.0/src/Files/SceneFile.cxx --- connectome-workbench-1.4.2/src/Files/SceneFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SceneFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -576,6 +576,53 @@ } /** + * Get the type of base directory and the name of the base directory + * @param basePathTypeOut + * Output with type of base path + * @param basePathNameOut + * Output with name of base path + * @param errorMessageOut + * Output with error information + * @return + * True if the outputs are valid, else false. + */ +bool +SceneFile::getSelectedBasePathTypeAndName(SceneFileBasePathTypeEnum::Enum& basePathTypeOut, + AString& basePathNameOut, + AString& errorMessageOut) const +{ + basePathNameOut.clear(); + bool validFlag(false); + + switch (getBasePathType()) { + case SceneFileBasePathTypeEnum::AUTOMATIC: + { + std::vector missingFileNames; + validFlag = findBaseDirectoryForDataFiles(basePathNameOut, + missingFileNames, + errorMessageOut); + } + break; + case SceneFileBasePathTypeEnum::CUSTOM: + { + basePathNameOut = getBalsaCustomBaseDirectory(); + if ( ! basePathNameOut.isEmpty()) { + validFlag = true; + } + else { + errorMessageOut = "Custom path is invalid (empty)"; + } + } + break; + } + + basePathTypeOut = getBasePathType(); + + return validFlag; +} + + +/** * Set the name of the file. * * @param filename @@ -1097,6 +1144,37 @@ std::vector& missingFileNamesOut, AString& errorMessageOut) const { + return SceneFile::findBaseDirectoryForDataFiles(getFileName(), + getAllDataFileNamesFromAllScenes(), + baseDirectoryOut, + missingFileNamesOut, + errorMessageOut); +} + +/** + * Find the base directory that is a directory that is parent to all loaded data files + * and also including the scene file. + * + * @param sceneFileName + * Name of scene file + * @param filesFromScenes + * Files in all scenes in the scene file + * @param baseDirectoryOut + * Output containing the base directory + * @param missingFileNamesOut + * Will contain data files that are in scenes but do not exist. + * @param errorMessageOut + * Error message if finding base directory fails + * @return + * True if the base directory is valid, else false. + */ +bool +SceneFile::findBaseDirectoryForDataFiles(const AString& sceneFileName, + const std::set& filesFromScenes, + AString& baseDirectoryOut, + std::vector& missingFileNamesOut, + AString& errorMessageOut) +{ baseDirectoryOut.clear(); missingFileNamesOut.clear(); errorMessageOut.clear(); @@ -1104,11 +1182,12 @@ const AString directorySeparator("/"); std::vector allFileNames; - std::set filesFromScenes = getAllDataFileNamesFromAllScenes(); for (const auto& nameInfo : filesFromScenes) { allFileNames.push_back(nameInfo.m_dataFileName); } - allFileNames.push_back(getFileName()); + if ( ! sceneFileName.isEmpty()) { + allFileNames.push_back(sceneFileName); + } /* * Find a unique set of directory names used by the data files and @@ -1142,8 +1221,11 @@ } } - const FileInformation fileInfo(getFileName()); - const AString sceneFilePath = fileInfo.getAbsolutePath().trimmed(); + AString sceneFilePath; + if ( ! sceneFileName.isEmpty()) { + const FileInformation fileInfo(sceneFileName); + sceneFilePath = fileInfo.getAbsolutePath().trimmed(); + } missingFileNamesOut.insert(missingFileNamesOut.end(), missingFileNames.begin(), @@ -1152,8 +1234,13 @@ const int32_t numDirs = static_cast(directoryNamesUniqueSet.size()); if (numDirs <= 0) { /* - * if no valid files in scene, user path of scene file. + * if no valid files in scene, use path of scene file. */ + if (sceneFilePath.isEmpty()) { + errorMessageOut = "No data files available for determining base path"; + return false; + } + baseDirectoryOut = sceneFilePath; return true; } @@ -1166,7 +1253,7 @@ */ bool firstFlag = true; std::vector longestPathMatch; - for (const auto dirName : directoryNamesUniqueSet) { + for (const auto& dirName : directoryNamesUniqueSet) { if (firstFlag) { firstFlag = false; longestPathMatch = AString::stringListToVector(dirName.split(directorySeparator)); @@ -1179,13 +1266,12 @@ } } - baseDirectoryOut = sceneFilePath; if ( ! longestPathMatch.empty()) { /* * Assemble the path components into a directory. */ baseDirectoryOut = AString::join(longestPathMatch, - directorySeparator); + directorySeparator); } /* @@ -1421,7 +1507,7 @@ std::vector fileInfoOut; - for (const auto nameAndIndices : allNamesAndIndices) { + for (const auto& nameAndIndices : allNamesAndIndices) { fileInfoOut.emplace_back(nameAndIndices.m_dataFileName, basePath, getFileName(), diff -Nru connectome-workbench-1.4.2/src/Files/SceneFile.h connectome-workbench-1.5.0/src/Files/SceneFile.h --- connectome-workbench-1.4.2/src/Files/SceneFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SceneFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -45,7 +45,31 @@ SceneFile& operator=(const SceneFile&); public: - + class FileAndSceneIndicesInfo { + public: + FileAndSceneIndicesInfo(const AString& dataFileName, + const int32_t sceneIndex) + : m_dataFileName(dataFileName) { + m_sceneIndices.push_back(sceneIndex + 1); + } + + bool operator<(const FileAndSceneIndicesInfo& rhs) const { + return m_dataFileName < rhs.m_dataFileName; + } + + void addSceneIndex(const int32_t sceneIndex) const { + m_sceneIndices.push_back(sceneIndex + 1); + } + + AString getSceneIndices() const { + return AString::fromNumbers(m_sceneIndices, ","); + } + + const AString m_dataFileName; + + mutable std::vector m_sceneIndices; + }; + virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); void clear(); @@ -117,32 +141,17 @@ std::vector& missingFileNamesOut, AString& errorMessageOut) const; + static bool findBaseDirectoryForDataFiles(const AString& sceneFileName, + const std::set& filesFromScenes, + AString& baseDirectoryOut, + std::vector& missingFileNamesOut, + AString& errorMessageOut); + std::vector getBaseDirectoryHierarchyForDataFiles(const int32_t maximumAncestorCount = 25); - class FileAndSceneIndicesInfo { - public: - FileAndSceneIndicesInfo(const AString& dataFileName, - const int32_t sceneIndex) - : m_dataFileName(dataFileName) { - m_sceneIndices.push_back(sceneIndex + 1); - } - - bool operator<(const FileAndSceneIndicesInfo& rhs) const { - return m_dataFileName < rhs.m_dataFileName; - } - - void addSceneIndex(const int32_t sceneIndex) const { - m_sceneIndices.push_back(sceneIndex + 1); - } - - AString getSceneIndices() const { - return AString::fromNumbers(m_sceneIndices, ","); - } - - const AString m_dataFileName; - - mutable std::vector m_sceneIndices; - }; + bool getSelectedBasePathTypeAndName(SceneFileBasePathTypeEnum::Enum& basePathTypeOut, + AString& basePathNameOut, + AString& errorMessageOut) const; std::set getAllDataFileNamesFromAllScenes() const; diff -Nru connectome-workbench-1.4.2/src/Files/SceneFileXmlStreamBase.h connectome-workbench-1.5.0/src/Files/SceneFileXmlStreamBase.h --- connectome-workbench-1.4.2/src/Files/SceneFileXmlStreamBase.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SceneFileXmlStreamBase.h 2021-02-16 19:46:47.000000000 +0000 @@ -54,6 +54,8 @@ static const QString ELEMENT_SCENE_FILE_BALSA_BASE_DIRECTORY; + static const QString ELEMENT_SCENE_FILE_OBSOLETE_BASE_DIRECTORY; + static const QString ELEMENT_SCENE_FILE_BALSA_EXTRACT_TO_DIRECTORY; static const QString ELEMENT_SCENE_FILE_BALSA_BASE_PATH_TYPE; @@ -80,6 +82,8 @@ const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_BALSA_BASE_DIRECTORY = "BalsaBaseDirectory"; + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_OBSOLETE_BASE_DIRECTORY = "BaseDirectory"; + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_BALSA_EXTRACT_TO_DIRECTORY = "BalsaExtractToDirectory"; const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_BALSA_BASE_PATH_TYPE = "BasePathType"; diff -Nru connectome-workbench-1.4.2/src/Files/SceneFileXmlStreamReader.cxx connectome-workbench-1.5.0/src/Files/SceneFileXmlStreamReader.cxx --- connectome-workbench-1.4.2/src/Files/SceneFileXmlStreamReader.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SceneFileXmlStreamReader.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -36,6 +36,7 @@ #include "SceneFile.h" #include "SceneInfo.h" #include "SceneInfoXmlStreamReader.h" +#include "ScenePathName.h" #include "SceneTypeEnum.h" #include "SceneXmlStreamReader.h" @@ -226,6 +227,13 @@ SceneFileXmlStreamReader::readSceneInfoDirectory(QXmlStreamReader& xmlReader, SceneFile* sceneFile) { + + /* + * Default to CUSTOM base path since some older scenes do not + * have AUTOMATIC/CUSTOM element (ELEMENT_SCENE_FILE_BALSA_BASE_PATH_TYPE( + */ + sceneFile->setBasePathType(SceneFileBasePathTypeEnum::CUSTOM); + /* * Gets set when ending scene info directory element is read */ @@ -236,20 +244,25 @@ xmlReader.readNext(); switch (xmlReader.tokenType()) { case QXmlStreamReader::StartElement: -// { -// std::cout << "Start Element " << xmlReader.name().toString() -// << " characters " << xmlReader.readElementText() -// << " CDATA" << (xmlReader.isCDATA() ? " Yes" : " No") << std::endl; -// -// } if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_STUDY_ID) { sceneFile->setBalsaStudyID(xmlReader.readElementText()); } else if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_STUDY_TITLE) { sceneFile->setBalsaStudyTitle(xmlReader.readElementText()); } - else if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_BASE_DIRECTORY) { - sceneFile->setBalsaCustomBaseDirectory(xmlReader.readElementText()); + else if ((xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_BASE_DIRECTORY) + || (xmlReader.name() == ELEMENT_SCENE_FILE_OBSOLETE_BASE_DIRECTORY)) { + /* + * Note: Base path is relative since the scene file may be used + * on different computers that contain different directory hierarchies. + */ + const AString sceneFileBasePath = xmlReader.readElementText(); + + ScenePathName basePathName("customBaseDir", + sceneFileBasePath); + basePathName.setValueToAbsolutePath(m_filename, + sceneFileBasePath); + sceneFile->setBalsaCustomBaseDirectory(basePathName.stringValue()); } else if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_EXTRACT_TO_DIRECTORY) { sceneFile->setBalsaExtractToDirectoryName(xmlReader.readElementText()); diff -Nru connectome-workbench-1.4.2/src/Files/SceneFileXmlStreamWriter.cxx connectome-workbench-1.5.0/src/Files/SceneFileXmlStreamWriter.cxx --- connectome-workbench-1.4.2/src/Files/SceneFileXmlStreamWriter.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SceneFileXmlStreamWriter.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -182,7 +182,9 @@ { /* * Write base path as a path RELATIVE to the scene file - * but only when base path type is CUSTOM + * but only when base path type is CUSTOM. This must be done + * since users may have different directory hierarchies and an + * absolute path may not be valid on another users's computer. * Note: we do not use FileInformation::getCanonicalFilePath() * because it returns an empty string if the file DOES NOT exist * and this may occur since the file may be new and has not diff -Nru connectome-workbench-1.4.2/src/Files/SignedDistanceHelper.cxx connectome-workbench-1.5.0/src/Files/SignedDistanceHelper.cxx --- connectome-workbench-1.4.2/src/Files/SignedDistanceHelper.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SignedDistanceHelper.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -113,6 +113,69 @@ return bestTriDist * computeSign(coord, bestInfo, myWinding); } +float SignedDistanceHelper::distLimited(const float coord[3], const float limit, bool& validOut, SignedDistanceHelper::WindingLogic myWinding) +{ + CaretMutexLocker locked(&m_mutex); + validOut = false; + CaretSimpleMinHeap*, float> myHeap; + myHeap.push(m_base->m_indexRoot, m_base->m_indexRoot->distToPoint(coord)); + ClosestPointInfo tempInfo, bestInfo; + float tempf = -1.0f, bestTriDist = limit; + int numChanged = 0; + while (!myHeap.isEmpty()) + { + Oct* curOct = myHeap.pop(&tempf); + if (tempf < bestTriDist) + { + if (curOct->m_leaf) + { + vector& myVecRef = *(curOct->m_data.m_triList); + int numTris = (int)myVecRef.size(); + for (int i = 0; i < numTris; ++i) + { + if (m_triMarked[myVecRef[i]] != 1) + { + m_triMarked[myVecRef[i]] = 1; + m_triMarkChanged[numChanged++] = myVecRef[i]; + tempf = unsignedDistToTri(coord, myVecRef[i], tempInfo); + if (tempf < bestTriDist) + { + bestInfo = tempInfo; + bestTriDist = tempf; + validOut = true; + } + } + } + } else { + for (int ci = 0; ci < 2; ++ci) + { + for (int cj = 0; cj < 2; ++cj) + { + for (int ck = 0; ck < 2; ++ck) + { + tempf = curOct->m_children[ci][cj][ck]->distToPoint(coord); + if (tempf < bestTriDist) + { + myHeap.push(curOct->m_children[ci][cj][ck], tempf); + } + } + } + } + } + } + } + while (numChanged) + { + m_triMarked[m_triMarkChanged[--numChanged]] = 0;//need to do this before computeSign + } + if (validOut) + { + return bestTriDist * computeSign(coord, bestInfo, myWinding); + } else { + return NAN;//should never be used in this case, so give a nan + } +} + void SignedDistanceHelper::barycentricWeights(const float coord[3], BarycentricInfo& baryInfoOut) { CaretMutexLocker locked(&m_mutex); diff -Nru connectome-workbench-1.4.2/src/Files/SignedDistanceHelper.h connectome-workbench-1.5.0/src/Files/SignedDistanceHelper.h --- connectome-workbench-1.4.2/src/Files/SignedDistanceHelper.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SignedDistanceHelper.h 2021-02-16 19:46:47.000000000 +0000 @@ -111,6 +111,7 @@ ///return the signed distance value at the point float dist(const float coord[3], WindingLogic myWinding); + float distLimited(const float coord[3], const float limit, bool& validOut, WindingLogic myWinding); ///find the closest point ON the surface, and return information about it ///will never have negative barycentric weights, or a point outside the triangle diff -Nru connectome-workbench-1.4.2/src/Files/SurfaceProjectedItem.cxx connectome-workbench-1.5.0/src/Files/SurfaceProjectedItem.cxx --- connectome-workbench-1.4.2/src/Files/SurfaceProjectedItem.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SurfaceProjectedItem.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -198,6 +198,30 @@ * Get the projected position of this item. * The first valid of this positions is used: (1) Barycentric, * (2) VanEssen, (3) Stereotaxic. + * + * @param surfaceFile + * Surface File for positioning. + * @param xyzOut + * Output containing the projected position. + * @param pasteOntoSurfaceFlag + * Place directly on the surface. + * @return true if the position is valid, else false. + * + */ +bool +SurfaceProjectedItem::getProjectedPosition(const SurfaceFile& surfaceFile, + float xyzOut[3], + const bool isUnprojectedOntoSurface) const +{ + return getProjectedPosition(&surfaceFile, + xyzOut, + isUnprojectedOntoSurface); +} + +/** + * Get the projected position of this item. + * The first valid of this positions is used: (1) Barycentric, + * (2) VanEssen, (3) Stereotaxic. * * @param surfaceFile * Surface File for positioning. @@ -209,31 +233,33 @@ * */ bool -SurfaceProjectedItem::getProjectedPosition(const SurfaceFile& surfaceFile, +SurfaceProjectedItem::getProjectedPosition(const SurfaceFile* surfaceFile, float xyzOut[3], const bool isUnprojectedOntoSurface) const { bool valid = false; - if (valid == false) { - if (this->barycentricProjection->isValid()) { - valid = this->barycentricProjection->unprojectToSurface(surfaceFile, - xyzOut, - 0.0, - isUnprojectedOntoSurface); + if (surfaceFile != NULL) { + if ( ! valid) { + if (this->barycentricProjection->isValid()) { + valid = this->barycentricProjection->unprojectToSurface(*surfaceFile, + xyzOut, + 0.0, + isUnprojectedOntoSurface); + } + } + + if ( ! valid) { + if (this->vanEssenProjection->isValid()) { + valid = this->vanEssenProjection->unprojectToSurface(*surfaceFile, + xyzOut, + 0.0, + isUnprojectedOntoSurface); + } } } - if (valid == false) { - if (this->vanEssenProjection->isValid()) { - valid = this->vanEssenProjection->unprojectToSurface(surfaceFile, - xyzOut, - 0.0, - isUnprojectedOntoSurface); - } - } - - if (valid == false) { + if ( ! valid) { if (this->stereotaxicXYZValid) { this->getStereotaxicXYZ(xyzOut); valid = true; @@ -372,9 +398,7 @@ /* * If not set, return stereotaxic coordinate */ - if ((volumeXYZ[0] == 0.0) - && (volumeXYZ[1] == 0.0) - && (volumeXYZ[2] == 0.0)) { + if ( ! isVolumeXYZValid()) { const float* stereoXYZ = getStereotaxicXYZ(); return stereoXYZ; } @@ -392,9 +416,7 @@ /* * If not set, return stereotaxic coordinate */ - if ((volumeXYZ[0] == 0.0) - && (volumeXYZ[1] == 0.0) - && (volumeXYZ[2] == 0.0)) { + if ( ! isVolumeXYZValid()) { getStereotaxicXYZ(xyzOut); return; } diff -Nru connectome-workbench-1.4.2/src/Files/SurfaceProjectedItem.h connectome-workbench-1.5.0/src/Files/SurfaceProjectedItem.h --- connectome-workbench-1.4.2/src/Files/SurfaceProjectedItem.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/SurfaceProjectedItem.h 2021-02-16 19:46:47.000000000 +0000 @@ -63,6 +63,10 @@ void unprojectToVolumeXYZ(const SurfaceFile& sf, const bool isUnprojectedOntoSurface); + bool getProjectedPosition(const SurfaceFile* sf, + float xyzOut[3], + const bool isUnprojectedOntoSurface) const; + bool getProjectedPosition(const SurfaceFile& sf, float xyzOut[3], const bool isUnprojectedOntoSurface) const; diff -Nru connectome-workbench-1.4.2/src/Files/VolumeDynamicConnectivityFile.cxx connectome-workbench-1.5.0/src/Files/VolumeDynamicConnectivityFile.cxx --- connectome-workbench-1.4.2/src/Files/VolumeDynamicConnectivityFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/VolumeDynamicConnectivityFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -489,7 +489,7 @@ const int64_t myTimePointOffset = getVoxelOffset(ijk[0], ijk[1], ijk[2], 0); connCorrelation->getCorrelationForBrainordinate(myTimePointOffset, voxelsOut); - CaretAssert(m_dimI * m_dimJ * m_dimK == static_cast(voxelsOut.size())); + CaretAssert((m_dimI * m_dimJ * m_dimK) == static_cast(voxelsOut.size())); validFlag = true; } } diff -Nru connectome-workbench-1.4.2/src/Files/VolumeFile.cxx connectome-workbench-1.5.0/src/Files/VolumeFile.cxx --- connectome-workbench-1.4.2/src/Files/VolumeFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/VolumeFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -18,6 +18,7 @@ */ /*LICENSE_END*/ +#include #include #include #include @@ -89,6 +90,10 @@ for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } + m_writingDoScale = false; + m_writingDType = NIFTI_TYPE_FLOAT32; + m_minScalingVal = -1.0;//unused, but make them consistent + m_maxScalingVal = 1.0; validateMembers(); } @@ -100,6 +105,10 @@ for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } + m_writingDoScale = false; + m_writingDType = NIFTI_TYPE_FLOAT32; + m_minScalingVal = -1.0;//unused, but make them consistent + m_maxScalingVal = 1.0; validateMembers(); } @@ -112,6 +121,10 @@ m_chartingEnabledForTab[i] = false; } if (templateHeader != NULL) m_header.grabNew(templateHeader->clone()); + m_writingDoScale = false; + m_writingDType = NIFTI_TYPE_FLOAT32; + m_minScalingVal = -1.0;//unused, but make them consistent + m_maxScalingVal = 1.0; validateMembers(); setType(whatType); } @@ -215,6 +228,11 @@ m_volumeFileEditorDelegate->clear(); m_lazyInitializedDynamicConnectivityFile.reset(); + + m_writingDoScale = false; + m_writingDType = NIFTI_TYPE_FLOAT32; + m_minScalingVal = -1.0;//unused, but make them consistent + m_maxScalingVal = 1.0; } void VolumeFile::readFile(const AString& filename) @@ -378,7 +396,30 @@ outHeader.setDescription(("Connectome Workbench, version " + ApplicationInformation().getVersion()).toLatin1().constData());//30 chars, plus however many chars the version is (generally 5) outHeader.setSForm(getVolumeSpace().getSform()); outHeader.setDimensions(getOriginalDimensions()); - outHeader.setDataType(NIFTI_TYPE_FLOAT32);//could set this to an integer when type is label + if (getType() == SubvolumeAttributes::LABEL) + { + switch (m_writingDType) + { + case NIFTI_TYPE_INT16://ensure integer when type is label + case NIFTI_TYPE_INT32://int8 could be problematic for users, so disallow it? + case NIFTI_TYPE_INT64: + case NIFTI_TYPE_UINT16: + case NIFTI_TYPE_UINT32: + case NIFTI_TYPE_UINT64: + outHeader.setDataType(m_writingDType); + break; + default: + outHeader.setDataType(NIFTI_TYPE_INT32); + break; + } + } else { + if (m_writingDoScale) + { + outHeader.setDataTypeAndScaleRange(m_writingDType, m_minScalingVal, m_maxScalingVal); + } else { + outHeader.setDataType(m_writingDType); + } + } NiftiIO myIO; int outVersion = 1; if (!outHeader.canWriteVersion(1)) outVersion = 2; @@ -401,6 +442,22 @@ clearModified(); } +void VolumeFile::setWritingDataTypeNoScaling(const int16_t& type) +{ + m_writingDType = type;//could do some validation here + m_writingDoScale = false; + m_minScalingVal = -1.0; + m_maxScalingVal = 1.0; +} + +void VolumeFile::setWritingDataTypeAndScaling(const int16_t& type, const double& minval, const double& maxval) +{ + m_writingDType = type;//could do some validation here + m_writingDoScale = true; + m_minScalingVal = minval; + m_maxScalingVal = maxval; +} + bool VolumeFile::hasGoodSpatialInformation() const { if (m_header != NULL) @@ -432,12 +489,12 @@ { float indexSpace[3]; spaceToIndex(coordIn1, coordIn2, coordIn3, indexSpace); - int64_t ind1low = floor(indexSpace[0]); - int64_t ind2low = floor(indexSpace[1]); - int64_t ind3low = floor(indexSpace[2]); - int64_t ind1high = ind1low + 1; - int64_t ind2high = ind2low + 1; - int64_t ind3high = ind3low + 1; + int64_t ind1low = floor(indexSpace[0] + 0.01f);//allow some rounding error + int64_t ind2low = floor(indexSpace[1] + 0.01f);//here these are ONLY used for checking validity + int64_t ind3low = floor(indexSpace[2] + 0.01f);//need to do something different in trilinear + int64_t ind1high = ceil(indexSpace[0] - 0.01f); + int64_t ind2high = ceil(indexSpace[1] - 0.01f); + int64_t ind3high = ceil(indexSpace[2] - 0.01f); if (!indexValid(ind1low, ind2low, ind3low, brickIndex, component) || !indexValid(ind1high, ind2high, ind3high, brickIndex, component)) { if (validOut != NULL) *validOut = false;//check for valid coord before deconvolving the frame @@ -457,22 +514,31 @@ { float index1, index2, index3; spaceToIndex(coordIn1, coordIn2, coordIn3, index1, index2, index3); - int64_t ind1low = floor(index1); - int64_t ind2low = floor(index2); - int64_t ind3low = floor(index3); - int64_t ind1high = ind1low + 1; - int64_t ind2high = ind2low + 1; - int64_t ind3high = ind3low + 1; - if (!indexValid(ind1low, ind2low, ind3low, brickIndex, component) || !indexValid(ind1high, ind2high, ind3high, brickIndex, component)) { - if (validOut != NULL) *validOut = false; - if (getType() == SubvolumeAttributes::LABEL) + int64_t ind1low = floor(index1 + 0.01f);//allow some rounding error for ONLY sanity checking + int64_t ind2low = floor(index2 + 0.01f);//need to do something different in the math + int64_t ind3low = floor(index3 + 0.01f); + int64_t ind1high = ceil(index1 - 0.01f); + int64_t ind2high = ceil(index2 - 0.01f); + int64_t ind3high = ceil(index3 - 0.01f); + if (!indexValid(ind1low, ind2low, ind3low, brickIndex, component) || !indexValid(ind1high, ind2high, ind3high, brickIndex, component)) { - return getMapLabelTable(brickIndex)->getUnassignedLabelKey(); - } else { - return INVALID_INTERP_VALUE; + if (validOut != NULL) *validOut = false; + if (getType() == SubvolumeAttributes::LABEL) + { + return getMapLabelTable(brickIndex)->getUnassignedLabelKey(); + } else { + return INVALID_INTERP_VALUE; + } } } + const int64_t* dims = getDimensionsPtr(); + int64_t ind1low = min(max(int64_t(floor(index1)), int64_t(0)), dims[0] - 2); + int64_t ind2low = min(max(int64_t(floor(index2)), int64_t(0)), dims[1] - 2); + int64_t ind3low = min(max(int64_t(floor(index3)), int64_t(0)), dims[2] - 2); + int64_t ind1high = ind1low + 1; + int64_t ind2high = ind2low + 1; + int64_t ind3high = ind3low + 1; float xhighWeight = index1 - ind1low; float xlowWeight = 1.0f - xhighWeight; float xinterp[2][2];//manually unrolled, because loops of 2 seem silly @@ -2396,6 +2462,8 @@ * Indices of maps for which identification information is requested. * @param xyz * Coordinate of voxel. + * @param dataValueSeparator + * Separator between multiple data values * @param ijkOut * Voxel indices of value. * @param textOut @@ -2404,6 +2472,7 @@ bool VolumeFile::getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], + const AString& dataValueSeparator, int64_t ijkOut[3], AString& textOut) const { @@ -2418,7 +2487,7 @@ AString valuesText; for (const auto mapIndex : mapIndices) { if ( ! valuesText.isEmpty()) { - valuesText.append(", "); + valuesText.append(dataValueSeparator); } bool validFlag(false); const float value = getVoxelValue(xyz, @@ -2437,6 +2506,9 @@ else { valuesText.append("?"); } + valuesText.append(" (" + + getMapName(mapIndex) + + ")"); } else { valuesText.append(AString::number(value, 'f', 3)); diff -Nru connectome-workbench-1.4.2/src/Files/VolumeFile.h connectome-workbench-1.5.0/src/Files/VolumeFile.h --- connectome-workbench-1.4.2/src/Files/VolumeFile.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/VolumeFile.h 2021-02-16 19:46:47.000000000 +0000 @@ -127,6 +127,12 @@ CaretPointer m_volumeFileEditorDelegate; static const AString s_paletteColorMappingNameInMetaData; + + int16_t m_writingDType; + + bool m_writingDoScale; + + double m_minScalingVal, m_maxScalingVal; protected: VolumeFile(const DataFileTypeEnum::Enum dataFileType); @@ -200,6 +206,10 @@ virtual void writeFile(const AString& filename); + ///data type and scaling options + void setWritingDataTypeNoScaling(const int16_t& type = NIFTI_TYPE_FLOAT32); + void setWritingDataTypeAndScaling(const int16_t& type, const double& minval, const double& maxval); + bool isEmpty() const { return VolumeBase::isEmpty(); } bool hasGoodSpatialInformation() const; @@ -379,6 +389,7 @@ virtual bool getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], + const AString& dataValueSeparator, int64_t ijkOut[3], AString& textOut) const; diff -Nru connectome-workbench-1.4.2/src/Files/VolumeSpline.cxx connectome-workbench-1.5.0/src/Files/VolumeSpline.cxx --- connectome-workbench-1.4.2/src/Files/VolumeSpline.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/VolumeSpline.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -67,7 +67,7 @@ ++index2; } } -#pragma omp CARET_PARFOR schedule(dynamic) +#pragma omp CARET_PARFOR schedule(guided, 10) for (int j = 0; j < m_dims[1]; ++j) { int64_t privIndex = j * m_dims[0]; @@ -100,7 +100,7 @@ ++index; } } -#pragma omp CARET_PARFOR schedule(dynamic) +#pragma omp CARET_PARFOR schedule(guided, 10) for (int i = 0; i < m_dims[0]; ++i) { int64_t privindex = i * m_dims[1]; @@ -134,7 +134,7 @@ ++index; } } -#pragma omp CARET_PARFOR schedule(dynamic) +#pragma omp CARET_PARFOR schedule(guided, 10) for (int i = 0; i < m_dims[0]; ++i) { int64_t privindex = i * m_dims[2]; @@ -154,27 +154,40 @@ } } +namespace +{ + int64_t sampleParams(const float indexf, const int64_t dim, float& fpartOut, bool& lowEdgeOut, bool& highEdgeOut) + { + int64_t ret; + float ipartf = 0.0f; + fpartOut = modf(indexf, &ipartf);//even -0.99f should give -0 for ipart, and sample() will only allow -0.01f, so don't need to max(..., 0) + ret = int64_t(ipartf);//extrapolate for beyond the voxel center without attempting invalid access + if (ret > dim - 2) + { + fpartOut += ret - (dim - 2); + ret = dim - 2; + } + lowEdgeOut = (ret < 1);//extrapolation is very limited, see the range test in sample() + highEdgeOut = (ret >= dim - 2);//to extrapolate by half a voxel, should decide what the second-outside voxel's value should be (repeat or reflect) + return ret;//otherwise, it will trend toward zero + } +} + float VolumeSpline::sample(const float& ifloat, const float& jfloat, const float& kfloat) { - if (m_dims[0] < 2 || ifloat < 0.0f || jfloat < 0.0f || kfloat < 0.0f || ifloat > m_dims[0] - 1 || jfloat > m_dims[1] - 1 || kfloat > m_dims[2] - 1) return 0.0f;//yeesh + if (m_dims[0] < 2 || m_dims[1] < 2 || m_dims[2] < 2 || + ifloat < -0.01f || jfloat < -0.01f || kfloat < -0.01f || + ifloat > m_dims[0] - 0.99f || jfloat > m_dims[1] - 0.99f || kfloat > m_dims[2] - 0.99f) return 0.0f;//allow tiny extrapolation beyond voxel center const int64_t zstep = m_dims[0] * m_dims[1]; - float iparti, ipartj, ipartk; - float fparti = modf(ifloat, &iparti); - float fpartj = modf(jfloat, &ipartj); - float fpartk = modf(kfloat, &ipartk); - int64_t lowi = (int64_t)iparti; - int64_t lowj = (int64_t)ipartj; - int64_t lowk = (int64_t)ipartk; - bool lowedgei = (lowi < 1); - bool lowedgej = (lowj < 1); - bool lowedgek = (lowk < 1); - bool highedgei = (lowi >= m_dims[0] - 2); - bool highedgej = (lowj >= m_dims[1] - 2); - bool highedgek = (lowk >= m_dims[2] - 2); + float fparti, fpartj, fpartk; + bool lowedgei, highedgei, lowedgej, highedgej, lowedgek, highedgek; + int64_t lowi = sampleParams(ifloat, m_dims[0], fparti, lowedgei, highedgei); + int64_t lowj = sampleParams(jfloat, m_dims[1], fpartj, lowedgej, highedgej); + int64_t lowk = sampleParams(kfloat, m_dims[2], fpartk, lowedgek, highedgek); CubicSpline ispline = CubicSpline::bspline(fparti, lowedgei, highedgei); CubicSpline jspline = CubicSpline::bspline(fpartj, lowedgej, highedgej); CubicSpline kspline = CubicSpline::bspline(fpartk, lowedgek, highedgek); - float jtemp[4], ktemp[4];//the weights of the splines are zero for off-the edge values, but zero the data anyway + float jtemp[4], ktemp[4];//the weights of the splines are zero and never touched for off-the edge values, but zero the data anyway jtemp[0] = 0.0f; jtemp[3] = 0.0f; ktemp[0] = 0.0f; @@ -233,7 +246,7 @@ void VolumeSpline::deconvolve(float* data, const float* backsubs, const int64_t& length) { if (length < 1) return; - const float A = 1.0f / 6.0f, B = 2.0f / 3.0f;//the coefficients of a bspline at center and +/-1 + const float A = 1.0f / 6.0f, B = 2.0f / 3.0f;//the values of a bspline at center and +/-1 //forward pass simulating gaussian elimination on matrix of bspline kernels and data data[0] /= B + A;//repeat final value for data outside the bounding box, to prevent bright edges for (int i = 1; i < length - 1; ++i)//the first and last rows are handled slightly differently diff -Nru connectome-workbench-1.4.2/src/Files/WarpfieldFile.cxx connectome-workbench-1.5.0/src/Files/WarpfieldFile.cxx --- connectome-workbench-1.4.2/src/Files/WarpfieldFile.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/WarpfieldFile.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -174,10 +174,14 @@ if (m_warpfield == NULL) throw DataFileException("writeWorld called on uninitialized warpfield"); vector dims; m_warpfield->getDimensions(dims); - dims.resize(4);//drop number of components + dims.resize(3);//keep only spatial dims + dims.push_back(1);//ITK doesn't use time dimension + dims.push_back(3);//it uses the fifth dimension VolumeFile outFile; - outFile.reinitialize(dims, m_warpfield->getSform()); - outFile.setMapName(0, "x displacement"); + NiftiHeader myHeader; + myHeader.setIntent(NIFTI_INTENT_VECTOR, "vector");//ITK sets intent to vector + outFile.reinitialize(dims, m_warpfield->getSform(), 1, SubvolumeAttributes::ANATOMY, &myHeader); + outFile.setMapName(0, "x displacement");//the rest should still work on fifth dimension, we internally flatten non-spatial dimensions outFile.setMapName(1, "y displacement"); outFile.setMapName(2, "z displacement"); for (int64_t k = 0; k < dims[2]; ++k) diff -Nru connectome-workbench-1.4.2/src/Files/XmlStreamReaderHelper.cxx connectome-workbench-1.5.0/src/Files/XmlStreamReaderHelper.cxx --- connectome-workbench-1.4.2/src/Files/XmlStreamReaderHelper.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Files/XmlStreamReaderHelper.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -165,9 +165,20 @@ } } else { - throwDataFileException(attributeName - + " is missing from element " - + elementName); + std::vector attNames; + for (int32_t i = 0; i < attributes.count(); i++) { + attNames.push_back(attributes.at(i).name().toString()); + } + + AString msg(attributeName + + " is missing from element " + + elementName); + if ( ! attributes.empty()) { + msg.appendWithNewLine("Available attributes: " + + AString::join(attNames, "; ")); + } + throwDataFileException(msg); + } return valueString; diff -Nru connectome-workbench-1.4.2/src/FilesBase/GiftiLabelTable.cxx connectome-workbench-1.5.0/src/FilesBase/GiftiLabelTable.cxx --- connectome-workbench-1.4.2/src/FilesBase/GiftiLabelTable.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/FilesBase/GiftiLabelTable.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -148,7 +148,7 @@ * */ std::map -GiftiLabelTable::append(const GiftiLabelTable& glt) +GiftiLabelTable::append(const GiftiLabelTable& glt, const bool errorOnLabelConflict) { std::map keyConverterMap; @@ -156,8 +156,8 @@ iter != glt.labelsMap.end(); iter++) { int32_t key = iter->first; - int32_t newKey = this->addLabel(iter->second); - + int32_t newKey = this->addLabel(iter->second, errorOnLabelConflict); + keyConverterMap.insert(std::make_pair(key, newKey)); } return keyConverterMap; @@ -183,7 +183,7 @@ const float blue, const float alpha) { - const GiftiLabel gl(GiftiLabel::getInvalidLabelKey(), labelName, red, green, blue, alpha); + const GiftiLabel gl(generateUnusedKey(), labelName, red, green, blue, alpha); return this->addLabel(&gl); } @@ -191,27 +191,6 @@ * Add a label. If a label with the name exists, its colors * are replaced with these color components. * @param labelName Name of label. - * @param red Red color component ranging 0.0 to 1.0. - * @param green Green color component ranging 0.0 to 1.0. - * @param blue Blue color component ranging 0.0 to 1.0. - * @return Index of the existing label, or, if no label - * exists with name, index of new label. - * - */ -int32_t -GiftiLabelTable::addLabel( - const AString& labelName, - const float red, - const float green, - const float blue) -{ - return this->addLabel(labelName, red, green, blue, 1.0f); -} - -/** - * Add a label. If a label with the name exists, its colors - * are replaced with these color components. - * @param labelName Name of label. * @param red Red color component ranging 0 to 255. * @param green Green color component ranging 0 to 255. * @param blue Blue color component ranging 0 to 255. @@ -228,32 +207,11 @@ const int32_t blue, const int32_t alpha) { - const GiftiLabel gl(GiftiLabel::getInvalidLabelKey(), labelName, red, green, blue, alpha); + const GiftiLabel gl(generateUnusedKey(), labelName, red, green, blue, alpha); return this->addLabel(&gl); } /** - * Add a label. If a label with the name exists, its colors - * are replaced with these color components. - * @param labelName Name of label. - * @param red Red color component ranging 0 to 255. - * @param green Green color component ranging 0 to 255. - * @param blue Blue color component ranging 0 to 255. - * @return Index of the existing label, or, if no label - * exists with name, index of new label. - * - */ -int32_t -GiftiLabelTable::addLabel( - const AString& labelName, - const int32_t red, - const int32_t green, - const int32_t blue) -{ - return this->addLabel(labelName, red, green, blue, 255); -} - -/** * Add a label to the label table. If the label's key is already in * the label table, a new key is created. If a label of the same * name already exists, the key of the existing label is returned @@ -263,12 +221,16 @@ * */ int32_t -GiftiLabelTable::addLabel(const GiftiLabel* glIn) +GiftiLabelTable::addLabel(const GiftiLabel* glIn, const bool errorOnLabelConflict) { /* * First see if a label with the same name already exists */ int32_t key = this->getLabelKeyFromName(glIn->getName()); + if (key != GiftiLabel::getInvalidLabelKey() && key != glIn->getKey()) + {//Matt says warn if this finds a key other than the one it already has + CaretLogWarning("merging keys for label '" + glIn->getName() + "' from key " + AString::number(glIn->getKey()) + " to key " + AString::number(key)); + } /* * If no label with the name exists, get the key @@ -287,9 +249,14 @@ /* * Still need a key, find an unused key */ - if (key == GiftiLabel::getInvalidLabelKey()) { + if (key == GiftiLabel::getInvalidLabelKey()) {//addLabel without a key has been fixed to use generateUnused, not getInvalid... key = this->generateUnusedKey(); - + if (errorOnLabelConflict) + { + throw CaretException("conflicting key value " + AString::number(glIn->getKey()) + " for label '" + glIn->getName() + "', please fix this and any additional conflicts with a label-modify-keys command, or an option in this command, if available"); + } else { + CaretLogWarning("conflicting key value for label '" + glIn->getName() + "' reassigned to key " + AString::number(key)); + } GiftiLabel* gl = new GiftiLabel(*glIn); gl->setKey(key); this->labelsMap.insert(std::make_pair(key, gl)); diff -Nru connectome-workbench-1.4.2/src/FilesBase/GiftiLabelTable.h connectome-workbench-1.5.0/src/FilesBase/GiftiLabelTable.h --- connectome-workbench-1.4.2/src/FilesBase/GiftiLabelTable.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/FilesBase/GiftiLabelTable.h 2021-02-16 19:46:47.000000000 +0000 @@ -70,35 +70,23 @@ public: void clear(); - std::map append(const GiftiLabelTable& glt); + std::map append(const GiftiLabelTable& glt, const bool errorOnLabelConflict = false); int32_t addLabel( const AString& labelName, const float red, const float green, const float blue, - const float alpha); - - int32_t addLabel( - const AString& labelName, - const float red, - const float green, - const float blue); + const float alpha = 1.0f); int32_t addLabel( const AString& labelName, const int32_t red, const int32_t green, const int32_t blue, - const int32_t alpha); - - int32_t addLabel( - const AString& labelName, - const int32_t red, - const int32_t green, - const int32_t blue); + const int32_t alpha = 255); - int32_t addLabel(const GiftiLabel* glt); + int32_t addLabel(const GiftiLabel* glt, const bool errorOnLabelConflict = false); void deleteLabel(const int32_t key); diff -Nru connectome-workbench-1.4.2/src/FilesBase/VolumeMappableInterface.cxx connectome-workbench-1.5.0/src/FilesBase/VolumeMappableInterface.cxx --- connectome-workbench-1.4.2/src/FilesBase/VolumeMappableInterface.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/FilesBase/VolumeMappableInterface.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -43,13 +43,12 @@ float& spacingOut2, float& spacingOut3) const { - float originX, originY, originZ; - float x1, y1, z1; - indexToSpace(0, 0, 0, originX, originY, originZ); - indexToSpace(1, 1, 1, x1, y1, z1); - spacingOut1 = x1 - originX; - spacingOut2 = y1 - originY; - spacingOut3 = z1 - originZ; + //TSC: below code will always return positive, so the calling code doesn't need to use abs() anymore + Vector3D istep, jstep, kstep, origin; + getVolumeSpace().getSpacingVectors(istep, jstep, kstep, origin); + spacingOut1 = istep.length(); + spacingOut2 = jstep.length(); + spacingOut3 = kstep.length(); } /** diff -Nru connectome-workbench-1.4.2/src/FilesBase/VolumeSpace.cxx connectome-workbench-1.5.0/src/FilesBase/VolumeSpace.cxx --- connectome-workbench-1.4.2/src/FilesBase/VolumeSpace.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/FilesBase/VolumeSpace.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -55,7 +55,7 @@ void VolumeSpace::setSpace(const int64_t dims[3], const vector >& sform) { - if (sform.size() < 2 || sform.size() > 4) + if (sform.size() < 3 || sform.size() > 4) { CaretAssert(false); throw CaretException("VolumeSpace initialized with wrong size sform"); diff -Nru connectome-workbench-1.4.2/src/FtglFont/FTGL/ftgl.h connectome-workbench-1.5.0/src/FtglFont/FTGL/ftgl.h --- connectome-workbench-1.4.2/src/FtglFont/FTGL/ftgl.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/FtglFont/FTGL/ftgl.h 2021-02-16 19:46:47.000000000 +0000 @@ -28,6 +28,16 @@ #ifndef __ftgl__ #define __ftgl__ +/* + * JWH 02 July 2020 + * OpenGL is deprecated on MacOS 10.15, Catalina + * This definition suppress the deprecation warning that will + * appear on every OpenGL function call + */ +#ifdef CARET_OS_MACOSX +#define GL_SILENCE_DEPRECATION 1 +#endif + /* We need the Freetype headers */ #include #include FT_FREETYPE_H diff -Nru connectome-workbench-1.4.2/src/Gifti/GiftiDataArray.cxx connectome-workbench-1.5.0/src/Gifti/GiftiDataArray.cxx --- connectome-workbench-1.4.2/src/Gifti/GiftiDataArray.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Gifti/GiftiDataArray.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -680,12 +680,12 @@ // // Decode the Base64 data using VTK's algorithm // - unsigned char* dataBuffer = new unsigned char[data.size()]; + std::vector dataBuffer(text.size() - text.size() / 4 + 10);//generous constant to make up for integer rounding //const char* textChars = text.toLatin1().constData(); const uint64_t numDecoded = Base64::decode((unsigned char*)text.toStdString().c_str(), - data.size(), - dataBuffer); + dataBuffer.size(), + dataBuffer.data()); if (numDecoded == 0) { std::ostringstream str; str << "Decoding of GZip Base64 Binary data failed." @@ -700,7 +700,7 @@ // DataCompressZLib compressor; const uint64_t uncompressedDataLength = - compressor.uncompressData(dataBuffer, + compressor.uncompressData(dataBuffer.data(), numDecoded, (unsigned char*)&data[0], data.size()); @@ -713,11 +713,6 @@ } // - // Free memory - // - delete[] dataBuffer; - - // // Is byte swapping needed ? // if (endian != getSystemEndian()) { @@ -1205,36 +1200,30 @@ // Compress the data with VTK's ZLIB algorithm // DataCompressZLib compressor; - unsigned long compressedDataBufferLength = + uint64_t compressedDataBufferLength = compressor.getMaximumCompressionSpace(data.size()); - unsigned char* compressedDataBuffer = new unsigned char[compressedDataBufferLength]; - unsigned long compressedDataLength = + std::vector compressedDataBuffer(compressedDataBufferLength); + uint64_t compressedDataLength = compressor.compressData(&data[0], data.size(), - compressedDataBuffer, + compressedDataBuffer.data(), compressedDataBufferLength); // // Encode the data with VTK's Base64 algorithm // - char* buffer = new char[static_cast(compressedDataLength * 1.5)]; + std::vector buffer(compressedDataLength + compressedDataLength / 3 + 10);//generous constant for partial bytes, integer rounding, and possible "=" formatting const uint64_t compressedLength = - Base64::encode(compressedDataBuffer, + Base64::encode(compressedDataBuffer.data(), compressedDataLength, - (unsigned char*)buffer); + buffer.data()); buffer[compressedLength] = '\0'; // // Write the data MUST BE NO space around data // - xmlWriter.writeElementNoSpace(GiftiXmlElements::TAG_DATA, buffer); + xmlWriter.writeElementNoSpace(GiftiXmlElements::TAG_DATA, (char*)buffer.data()); - - // - // Free memory - // - delete[] buffer; - delete[] compressedDataBuffer; } break; case GiftiEncodingEnum::EXTERNAL_FILE_BINARY: diff -Nru connectome-workbench-1.4.2/src/Graphics/CaretOpenGLInclude.h connectome-workbench-1.5.0/src/Graphics/CaretOpenGLInclude.h --- connectome-workbench-1.4.2/src/Graphics/CaretOpenGLInclude.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/CaretOpenGLInclude.h 2021-02-16 19:46:47.000000000 +0000 @@ -28,6 +28,16 @@ */ /* + * OpenGL is deprecated on MacOS 10.15, Catalina + * This definition suppress the deprecation warning that will + * appear on every OpenGL function call + */ +#ifdef CARET_OS_MACOSX +#define GL_SILENCE_DEPRECATION 1 +#endif + + +/* * When using GLEW, GL/glew.h MUST be included before Gl/gl.h. * Note: Windows.h includes Gl/gl.h. * diff -Nru connectome-workbench-1.4.2/src/Graphics/CMakeLists.txt connectome-workbench-1.5.0/src/Graphics/CMakeLists.txt --- connectome-workbench-1.4.2/src/Graphics/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ GraphicsPrimitiveV3fC4f.h GraphicsPrimitiveV3fC4ub.h GraphicsPrimitiveV3fN3f.h +GraphicsPrimitiveV3fN3fC4ub.h GraphicsPrimitiveV3fT3f.h GraphicsShape.h GraphicsUtilitiesOpenGL.h @@ -46,6 +47,7 @@ GraphicsPrimitiveV3fC4f.cxx GraphicsPrimitiveV3fC4ub.cxx GraphicsPrimitiveV3fN3f.cxx +GraphicsPrimitiveV3fN3fC4ub.cxx GraphicsPrimitiveV3fT3f.cxx GraphicsShape.cxx GraphicsUtilitiesOpenGL.cxx diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsEngineDataOpenGL.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsEngineDataOpenGL.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsEngineDataOpenGL.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsEngineDataOpenGL.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -31,11 +31,14 @@ #include "EventOpenGLObjectToWindowTransform.h" #include "EventManager.h" #include "GraphicsOpenGLBufferObject.h" +#include "GraphicsOpenGLError.h" #include "GraphicsOpenGLPolylineTriangles.h" #include "GraphicsOpenGLTextureName.h" #include "GraphicsPrimitive.h" #include "GraphicsPrimitiveSelectionHelper.h" +#include "GraphicsPrimitiveV3f.h" #include "GraphicsShape.h" +#include "GraphicsUtilitiesOpenGL.h" #include "Matrix4x4.h" using namespace caret; @@ -142,7 +145,7 @@ switch (primitive->m_vertexDataType) { case GraphicsPrimitive::VertexDataType::FLOAT_XYZ: m_coordinateDataType = GL_FLOAT; - m_coordinatesPerVertex = 3; // X, Y, Z + m_coordinatesPerVertex = 3; /* X, Y, Z */ coordinateCount = primitive->m_xyz.size(); const GLuint xyzSizeBytes = coordinateCount * sizeof(float); @@ -384,11 +387,37 @@ glBindTexture(GL_TEXTURE_2D, openGLTextureName); bool useMipMapFlag = true; + switch (primitive->getTextureFilteringType()) { + case GraphicsPrimitive::TextureFilteringType::LINEAR: + break; + case GraphicsPrimitive::TextureFilteringType::NEAREST: + useMipMapFlag = false; + break; + } + if (useMipMapFlag) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + switch (primitive->getTextureWrappingType()) { + case GraphicsPrimitive::TextureWrappingType::CLAMP: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + break; + case GraphicsPrimitive::TextureWrappingType::REPEAT: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + break; + } + + switch (primitive->getTextureFilteringType()) { + case GraphicsPrimitive::TextureFilteringType::LINEAR: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + break; + case GraphicsPrimitive::TextureFilteringType::NEAREST: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + useMipMapFlag = false; + break; + } /* * This code seems to work if OpenGL 3.0 or later and @@ -427,11 +456,27 @@ } if ( ! useMipMapFlag) { - CaretAssert(0); // image must be 2^N by 2^M - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + switch (primitive->getTextureWrappingType()) { + case GraphicsPrimitive::TextureWrappingType::CLAMP: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + break; + case GraphicsPrimitive::TextureWrappingType::REPEAT: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + break; + } + + switch (primitive->getTextureFilteringType()) { + case GraphicsPrimitive::TextureFilteringType::LINEAR: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + break; + case GraphicsPrimitive::TextureFilteringType::NEAREST: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + break; + } glTexImage2D(GL_TEXTURE_2D, // MUST BE GL_TEXTURE_2D 0, // level of detail 0=base, n is nth mipmap reduction @@ -442,6 +487,16 @@ GL_RGBA, // format of the pixel data GL_UNSIGNED_BYTE, // data type of pixel data imageBytesRGBA); // pointer to image data + + const auto errorGL = GraphicsUtilitiesOpenGL::getOpenGLError(); + if (errorGL) { + CaretLogSevere("OpenGL error after glTexImage2D(), width=" + + AString::number(imageWidth) + + ", height=" + + AString::number(imageHeight) + + ": " + + errorGL->getVerboseDescription()); + } } glBindTexture(GL_TEXTURE_2D, 0); @@ -464,11 +519,9 @@ { CaretAssert(primitive); - if (primitive->getNumberOfVertices() <= 0) { - return; - } if ( ! primitive->isValid()) { - CaretLogWarning("Attempting to draw invalid Graphics Primitive"); + CaretLogFine("Attempting to draw invalid Graphics Primitive"); + return; } GraphicsPrimitive* primitiveToDraw = primitive; @@ -523,6 +576,56 @@ break; } + if (modelSpaceLineFlag + || windowSpaceLineFlag) { + if (primitive->getNumberOfVertices() == 1) { + spheresFlag = true; + + uint8_t rgba[4]; + switch (primitive->getColorDataType()) { + case GraphicsPrimitive::ColorDataType::FLOAT_RGBA: + { + float rgbaFloat[4]; + primitive->getVertexFloatRGBA(0, rgbaFloat); + for (int32_t i = 0; i < 4; i++) { + rgba[i] = static_cast(rgbaFloat[i] / 255.0); + } + } + break; + case GraphicsPrimitive::ColorDataType::NONE: + break; + case GraphicsPrimitive::ColorDataType::UNSIGNED_BYTE_RGBA: + primitive->getVertexByteRGBA(0, rgba); + break; + } + float sphereXYZ[3]; + primitive->getVertexFloatXYZ(0, sphereXYZ); + GraphicsPrimitive::LineWidthType lineWidthType; + float lineWidth; + primitive->getLineWidth(lineWidthType, lineWidth); + std::unique_ptr pointPrimitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::OPENGL_POINTS, + rgba)); + pointPrimitive->addVertex(sphereXYZ); + + GraphicsPrimitive::PointSizeType pointSizeType(GraphicsPrimitive::PointSizeType::PIXELS); + switch (lineWidthType) { + case GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT: + pointSizeType = GraphicsPrimitive::PointSizeType::PERCENTAGE_VIEWPORT_HEIGHT; + break; + case GraphicsPrimitive::LineWidthType::PIXELS: + pointSizeType = GraphicsPrimitive::PointSizeType::PIXELS; + break; + } + pointPrimitive->setPointDiameter(pointSizeType, + lineWidth); + + drawPrivate(PrivateDrawMode::DRAW_NORMAL, + pointPrimitive.get(), + NULL); + return; + } + } + if (millimeterPointsFlag) { /* * Special case for points in millimeters @@ -848,6 +951,8 @@ { CaretAssert(primitive); + bool modelSpaceLineFlag = false; + bool windowSpaceLineFlag = false; switch (primitive->m_primitiveType) { case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: break; @@ -868,14 +973,14 @@ case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: + modelSpaceLineFlag = true; break; case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: - break; case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: - break; case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: + windowSpaceLineFlag = true; break; case GraphicsPrimitive::PrimitiveType::SPHERES: CaretAssertMessage(0, "Not yet implemented"); @@ -888,6 +993,13 @@ GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); + + /* + * Must clear color and depth buffers + */ + glClear(GL_COLOR_BUFFER_BIT + | GL_DEPTH_BUFFER_BIT); + if ((pixelX >= viewport[0]) && (pixelX < (viewport[0] + viewport[2])) && (pixelY >= viewport[1]) @@ -901,11 +1013,50 @@ glShadeModel(GL_FLAT); glDisable(GL_LIGHTING); - GraphicsPrimitiveSelectionHelper selectionHelper(primitive); - selectionHelper.setupSelectionBeforeDrawing(); - drawPrivate(PrivateDrawMode::DRAW_SELECTION, - primitive, - &selectionHelper); + std::vector triangleVertexIndicesToLineVertexIndices; + std::unique_ptr selectionHelper; + if (modelSpaceLineFlag + || windowSpaceLineFlag) { + AString errorMessage; + + std::unique_ptr primitiveToDraw(GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(primitive, + &triangleVertexIndicesToLineVertexIndices, + errorMessage)); + if (primitiveToDraw == NULL) { + const AString msg("For developer: " + + errorMessage); +#ifdef NDEBUG + CaretLogFine(msg); +#else + CaretLogSevere(msg); +#endif + return; + } + SpaceMode spaceMode = SpaceMode::WINDOW; + if (modelSpaceLineFlag) { + spaceMode = SpaceMode::MODEL; + } + else if (windowSpaceLineFlag) { + spaceMode = SpaceMode::WINDOW; + } + else { + CaretAssert(0); + } + + selectionHelper.reset(new GraphicsPrimitiveSelectionHelper(primitiveToDraw.get())); + selectionHelper->setupSelectionBeforeDrawing(); + drawModelOrWindowSpace(spaceMode, + PrivateDrawMode::DRAW_SELECTION, + primitiveToDraw.get(), + selectionHelper.get()); + } + else { + selectionHelper.reset(new GraphicsPrimitiveSelectionHelper(primitive)); + selectionHelper->setupSelectionBeforeDrawing(); + drawPrivate(PrivateDrawMode::DRAW_SELECTION, + primitive, + selectionHelper.get()); + } /* * QOpenGLWidget Note: The QOpenGLWidget always renders in a @@ -922,7 +1073,7 @@ glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); - glPixelStorei(GL_PACK_ALIGNMENT, 1); // bytes + glPixelStorei(GL_PACK_ALIGNMENT, 1); /* bytes */ glReadPixels(pixelX, pixelY, 1, @@ -934,7 +1085,7 @@ /* * Get depth from depth buffer */ - glPixelStorei(GL_PACK_ALIGNMENT, 4); // float + glPixelStorei(GL_PACK_ALIGNMENT, 4); /* float */ glReadPixels(pixelX, pixelY, 1, @@ -946,7 +1097,31 @@ glPopAttrib(); glPopClientAttrib(); - selectedPrimitiveIndexOut = selectionHelper.getPrimitiveIndexFromEncodedRGBA(pixelRGBA); + CaretAssert(selectionHelper.get()); + selectedPrimitiveIndexOut = selectionHelper->getPrimitiveIndexFromEncodedRGBA(pixelRGBA); + + if (modelSpaceLineFlag + || windowSpaceLineFlag) { + if ((selectedPrimitiveIndexOut >= 0) + && selectedPrimitiveIndexOut < static_cast(triangleVertexIndicesToLineVertexIndices.size())) { + CaretAssertVectorIndex(triangleVertexIndicesToLineVertexIndices, selectedPrimitiveIndexOut); + selectedPrimitiveIndexOut = triangleVertexIndicesToLineVertexIndices[selectedPrimitiveIndexOut]; + + if (selectedPrimitiveIndexOut >= primitive->getNumberOfVertices()) { + selectedPrimitiveIndexOut = -1; + } + } + else { + selectedPrimitiveIndexOut = -1; + } + } + else { + if (selectedPrimitiveIndexOut >= 0) { + if (selectedPrimitiveIndexOut >= primitive->getNumberOfVertices()) { + selectedPrimitiveIndexOut = -1; + } + } + } } } @@ -1002,6 +1177,11 @@ openglData->loadTextureImageDataBuffer(primitive); + /* + * Primitive may want to delete its instance data to save memory + */ + primitive->setOpenGLBuffersHaveBeenLoadedByGraphicsEngine(); + const GLuint coordBufferID = openglData->m_coordinateBufferObject->getBufferObjectName(); CaretAssert(coordBufferID); if ( ! glIsBuffer(coordBufferID)) { @@ -1078,18 +1258,18 @@ const GLenum colorDataType = GL_UNSIGNED_BYTE; const std::vector selectionRGBA = primitiveSelectionHelper->getSelectionEncodedRGBA(); - CaretAssert((selectionRGBA.size() / 4) == numberOfVertices); - - const GLuint colorSizeBytes = selectionRGBA.size() * sizeof(GLubyte); - const GLvoid* colorDataPointer = (const GLvoid*)&selectionRGBA[0]; - glBindBuffer(GL_ARRAY_BUFFER, - localColorBufferName); - glBufferData(GL_ARRAY_BUFFER, - colorSizeBytes, - colorDataPointer, - GL_STREAM_DRAW); - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(componentsPerColor, colorDataType, 0, (GLvoid*)0); + if (static_cast(selectionRGBA.size() / 4) == numberOfVertices) { + const GLuint colorSizeBytes = selectionRGBA.size() * sizeof(GLubyte); + const GLvoid* colorDataPointer = (const GLvoid*)&selectionRGBA[0]; + glBindBuffer(GL_ARRAY_BUFFER, + localColorBufferName); + glBufferData(GL_ARRAY_BUFFER, + colorSizeBytes, + colorDataPointer, + GL_STREAM_DRAW); + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(componentsPerColor, colorDataType, 0, (GLvoid*)0); + } } } break; diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -54,6 +54,8 @@ /** * Constructor. * + * @param mode + * The conversion mode * @param xyz * The XYZ vertices. * @param floatRGBA @@ -118,7 +120,35 @@ */ GraphicsPrimitive* GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(const GraphicsPrimitive* primitive, - AString& errorMessageOut) + AString& errorMessageOut) +{ + return convertWorkbenchLinePrimitiveTypeToOpenGL(primitive, + NULL, + errorMessageOut); +} + +/** + * Convert a Workbench Line Primitive to an OpenGL primitive. + * OpenGL has limits on the width of the lines that it draws. This method will + * convert the lines into polygons so that any line width may be drawn. + * + * @param mode + * Mode for conversion + * @param primitive + * A graphics primitive with a primitive type that is one of the + * WORKBENCH_LINE* types. + * @param triangleVertexIndicesToLineVertexIndicesOut + * Allows conversion from a triangle vertex index to original line vertex index (ignored if NULL) + * @param errorMessageOut + * Upon exit contains an error message if conversion failed. + * @return + * A graphics primitive with an OPENGL_TRIANGLES type that draws the lines + * using triangles. NULL if error. + */ +GraphicsPrimitive* +GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(const GraphicsPrimitive* primitive, + std::vector* triangleVertexIndicesToLineVertexIndicesOut, + AString& errorMessageOut) { CaretAssert(primitive); errorMessageOut.clear(); @@ -250,7 +280,10 @@ joinType); GraphicsPrimitive* primitiveOut = lineConversion.convertLinesToPolygons(errorMessageOut); - + + if (triangleVertexIndicesToLineVertexIndicesOut != NULL) { + *triangleVertexIndicesToLineVertexIndicesOut = lineConversion.m_triangleVertexIndicesToLineVertexIndices; + } return primitiveOut; } @@ -1258,7 +1291,7 @@ void GraphicsOpenGLPolylineTriangles::addJoinTriangles() { - for (const auto tj : m_triangleJoins) { + for (const auto& tj : m_triangleJoins) { CaretAssertVectorIndex(m_vertexWindowXYZ, tj.m_windowVertexIndex * 3 + 2); const float* v1 = &m_vertexWindowXYZ[tj.m_windowVertexIndex*3]; @@ -1379,39 +1412,103 @@ } const bool restartEnabledFlag = true; - + + GraphicsPrimitive* outputPrimitive(NULL); + switch (m_colorType) { + case ColorType::BYTE_RGBA_PER_VERTEX: + case ColorType::BYTE_RGBA_SOLID: + outputPrimitive = m_primitiveByteColor.get(); + break; + case ColorType::FLOAT_RGBA_PER_VERTEX: + case ColorType::FLOAT_RGBA_SOLID: + outputPrimitive = m_primitiveFloatColor.get(); + break; + } + CaretAssert(outputPrimitive); + switch (m_lineType) { case LineType::LINES: { const int32_t numLineSegments = (numVertices / 2); for (int32_t i = 0; i < numLineSegments; i++) { + const int32_t numOutputVerticesBefore(outputPrimitive->getNumberOfVertices() / 3); + const int32_t i2 = i * 2; createTrianglesFromWindowVertices(i2, i2 + 1); + + updateOutputVertexIndicesToInputVertexIndices(outputPrimitive, + numOutputVerticesBefore, + i2); } } break; case LineType::LINE_LOOP: + { for (int32_t i = 0; i < numVerticesMinusOne; i++) { + const int32_t numOutputVerticesBefore(outputPrimitive->getNumberOfVertices() / 3); + CaretAssertVectorIndex(restartVertexFlags, i + 1); if (restartVertexFlags[i + 1]) { if (restartEnabledFlag) continue; } + createTrianglesFromWindowVertices(i, i + 1); + + updateOutputVertexIndicesToInputVertexIndices(outputPrimitive, + numOutputVerticesBefore, + i); } + + const int32_t numOutputVerticesBefore(outputPrimitive->getNumberOfVertices() / 3); + createTrianglesFromWindowVertices(numVerticesMinusOne, 0); + + updateOutputVertexIndicesToInputVertexIndices(outputPrimitive, + numOutputVerticesBefore, + numVerticesMinusOne); + } break; case LineType::LINE_STRIP: + for (int32_t i = 0; i < numVerticesMinusOne; i++) { + const int32_t numOutputVerticesBefore(outputPrimitive->getNumberOfVertices() / 3); + CaretAssertVectorIndex(restartVertexFlags, i + 1); if (restartVertexFlags[i + 1]) { if (restartEnabledFlag) continue; } + createTrianglesFromWindowVertices(i, i + 1); + + updateOutputVertexIndicesToInputVertexIndices(outputPrimitive, + numOutputVerticesBefore, + i); } break; } } +/** + * Update conversion from output primitive vertex indices to input vertex indices + * + * @param outputPrimitive + * The output primitive + * @param numOutputVerticesBefore + * Number of vertices prior to conversion of two points to a polyline + * @param inputVertexIndex + * Index of the input vertex + */ +void +GraphicsOpenGLPolylineTriangles::updateOutputVertexIndicesToInputVertexIndices(const GraphicsPrimitive* outputPrimitive, + const int32_t numOutputVerticesBefore, + const int32_t inputVertexIndex) +{ + const int32_t numOutputVerticesAfter(outputPrimitive->getNumberOfVertices() / 3); + for (int32_t i = numOutputVerticesBefore; i < numOutputVerticesAfter; i++) { + m_triangleVertexIndicesToLineVertexIndices.push_back(inputVertexIndex); + } +} + /** * Get a description of this object's content. * @return String describing this object's content. diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsOpenGLPolylineTriangles.h connectome-workbench-1.5.0/src/Graphics/GraphicsOpenGLPolylineTriangles.h --- connectome-workbench-1.4.2/src/Graphics/GraphicsOpenGLPolylineTriangles.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsOpenGLPolylineTriangles.h 2021-02-16 19:46:47.000000000 +0000 @@ -43,6 +43,10 @@ static GraphicsPrimitive* convertWorkbenchLinePrimitiveTypeToOpenGL(const GraphicsPrimitive* primitive, AString& errorMessageOut); + static GraphicsPrimitive* convertWorkbenchLinePrimitiveTypeToOpenGL(const GraphicsPrimitive* primitive, + std::vector* triangleVertexIndicesToLineVertexIndicesOut, + AString& errorMessageOut); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -224,6 +228,10 @@ const int32_t primitiveVertexTwoIndex, uint8_t rgbaOut[4]) const; + void updateOutputVertexIndicesToInputVertexIndices(const GraphicsPrimitive* outputPrimitive, + const int32_t numOutputVerticesBefore, + const int32_t inputVertexIndex); + const std::vector& m_inputXYZ; const std::vector& m_inputFloatRGBA; @@ -246,6 +254,13 @@ std::vector m_vertexWindowInputIndices; + /* + * Maps a vertex in the output triangles to a vertex + * from the input line. Typically used be identification + * operations. + */ + std::vector m_triangleVertexIndicesToLineVertexIndices; + bool m_debugFlag = false; int m_savedPolygonMode[2]; diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitive.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitive.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitive.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitive.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,16 +23,24 @@ #include "GraphicsPrimitive.h" #undef __GRAPHICS_PRIMITIVE_DECLARE__ +#include +#include +#include + #include "BoundingBox.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "DescriptiveStatistics.h" #include "EventManager.h" #include "GraphicsEngineDataOpenGL.h" #include "GraphicsPrimitiveV3f.h" #include "GraphicsPrimitiveV3fC4f.h" #include "GraphicsPrimitiveV3fC4ub.h" #include "GraphicsPrimitiveV3fN3f.h" +#include "GraphicsPrimitiveV3fN3fC4ub.h" #include "GraphicsPrimitiveV3fT3f.h" +#include "MathFunctions.h" +#include "Matrix4x4Interface.h" using namespace caret; @@ -58,6 +66,10 @@ * Type of vertex coloring * @param textureDataType * Data type of texture coordinates. + * @param textureWrappingType + * Type of texture wrapping + * @param textureFilteringType + * Type of texture filtering * @param primitiveType * Type of primitive drawn (triangles, lines, etc.) */ @@ -66,6 +78,8 @@ const ColorDataType colorDataType, const VertexColorType vertexColorType, const TextureDataType textureDataType, + const TextureWrappingType textureWrappingType, + const TextureFilteringType textureFilteringType, const PrimitiveType primitiveType) : CaretObject(), EventListenerInterface(), @@ -74,10 +88,12 @@ m_colorDataType(colorDataType), m_vertexColorType(vertexColorType), m_textureDataType(textureDataType), + m_textureWrappingType(textureWrappingType), + m_textureFilteringType(textureFilteringType), m_primitiveType(primitiveType), m_boundingBoxValid(false) { - + invalidateVertexMeasurements(); } /** @@ -101,9 +117,12 @@ m_colorDataType(obj.m_colorDataType), m_vertexColorType(obj.m_vertexColorType), m_textureDataType(obj.m_textureDataType), + m_textureWrappingType(obj.m_textureWrappingType), + m_textureFilteringType(obj.m_textureFilteringType), m_primitiveType(obj.m_primitiveType), m_boundingBoxValid(false) { + invalidateVertexMeasurements(); this->copyHelperGraphicsPrimitive(obj); } @@ -131,6 +150,8 @@ m_textureImageBytesRGBA = obj.m_textureImageBytesRGBA; m_textureImageWidth = obj.m_textureImageWidth; m_textureImageHeight = obj.m_textureImageHeight; + invalidateVertexMeasurements(); + m_graphicsEngineDataForOpenGL.reset(); } @@ -180,8 +201,8 @@ m_floatTextureSTR.reserve(numberOfVertices * 3); break; } - - m_boundingBoxValid = false; + + invalidateVertexMeasurements(); } /** @@ -293,6 +314,19 @@ bool GraphicsPrimitive::isValid() const { + switch (m_releaseInstanceDataMode) { + case ReleaseInstanceDataMode::COMPLETED: + /* + * Instance data has been removed + */ + return true; + break; + case ReleaseInstanceDataMode::DISABLED: + break; + case ReleaseInstanceDataMode::ENABLED: + break; + } + switch (m_vertexDataType) { case VertexDataType::FLOAT_XYZ: break; @@ -853,8 +887,8 @@ break; } - m_boundingBoxValid = false; - + invalidateVertexMeasurements(); + /* * The triangle strip primitve vertces are filled after two * vertices are added after requesting a restart @@ -895,6 +929,23 @@ void GraphicsPrimitive::replaceFloatXYZ(const std::vector& xyz) { + switch (m_releaseInstanceDataMode) { + case ReleaseInstanceDataMode::COMPLETED: + { + const QString msg("All XYZ Data in primitive cannot be replaced with different sized data. " + "Instance data was removed to save memory. " + "setReleaseInstanceDataMode() should not be called for this primitive."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + break; + case ReleaseInstanceDataMode::DISABLED: + break; + case ReleaseInstanceDataMode::ENABLED: + break; + } + if (xyz.size() == m_xyz.size()) { m_xyz = xyz; @@ -908,7 +959,58 @@ CaretAssertMessage(0, msg); } - m_boundingBoxValid = false; + invalidateVertexMeasurements(); +} + +/** + * Get the Y-components from the XYZ coordinates + * @param yComponentsOut + * Output containing the Y-components (this method will resize to correct number of elements) + */ +void +GraphicsPrimitive::getFloatYComponents(std::vector& yComponentsOut) const +{ + const int32_t numXYZ = getNumberOfVertices(); + yComponentsOut.resize(numXYZ); + + for (int32_t i = 0; i < numXYZ; i++) { + const int32_t i3 = (i * 3); + CaretAssertVectorIndex(yComponentsOut, i); + CaretAssertVectorIndex(m_xyz, i3 + 1); + yComponentsOut[i] = m_xyz[i3 + 1]; + } +} + +/** + * Set the Y-components from the XYZ coordinates + * @param yComponents + * The Y-components + */ +void +GraphicsPrimitive::setFloatYComponents(const std::vector& yComponents) +{ + const int32_t numXYZ = getNumberOfVertices(); + if (numXYZ != static_cast(yComponents.size())) { + const QString msg("Number of XYZ=" + + QString::number(numXYZ) + + " but number of input Y-components=" + + QString::number(yComponents.size())); + CaretLogSevere(msg); + CaretAssertMessage(0, msg); + return; + } + + for (int32_t i = 0; i < numXYZ; i++) { + const int32_t i3 = (i * 3); + CaretAssertVectorIndex(yComponents, i); + CaretAssertVectorIndex(m_xyz, i3 + 1); + m_xyz[i3 + 1] = yComponents[i]; + } + + invalidateVertexMeasurements(); + if (m_graphicsEngineDataForOpenGL != NULL) { + m_graphicsEngineDataForOpenGL->invalidateCoordinates(); + } } /** @@ -923,6 +1025,23 @@ GraphicsPrimitive::replaceVertexFloatXYZ(const int32_t vertexIndex, const float xyz[3]) { + switch (m_releaseInstanceDataMode) { + case ReleaseInstanceDataMode::COMPLETED: + { + const QString msg("A vertex's XYZ Data in primitive cannot be replaced. " + "Instance data was removed to save memory. " + "setReleaseInstanceDataMode() should not be called for this primitive."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + break; + case ReleaseInstanceDataMode::DISABLED: + break; + case ReleaseInstanceDataMode::ENABLED: + break; + } + const int32_t offset = vertexIndex * 3; CaretAssertVectorIndex(m_xyz, offset + 2); m_xyz[offset] = xyz[0]; @@ -935,6 +1054,47 @@ } /** + * Replace the XYZ vertices in this primitive with vertices from other primitive + * and also transform the vertex using the given matrix. + * + * @param primitive + * Primitive whose vertices are copied. + * @param matrix + * Matrix used for transforming vertices + */ +void +GraphicsPrimitive::replaceAndTransformVertices(const GraphicsPrimitive* primitive, + const Matrix4x4Interface& matrix) +{ + CaretAssert(primitive); + const int32_t numVertices = std::min(getNumberOfVertices(), + primitive->getNumberOfVertices()); + for (int32_t i = 0; i < numVertices; i++) { + const std::vector& primitiveXYZ = primitive->getFloatXYZ(); + const int32_t i3(i * 3); + float xyz[3] { primitiveXYZ[i3], primitiveXYZ[i3 + 1], primitiveXYZ[i3 + 2] }; + matrix.multiplyPoint3(xyz); + replaceVertexFloatXYZ(i, xyz); + } +} + + +/** + * Transform all vertices using the given matrix. + * @param matrix + * Matrix for transformation of vertices + */ +void +GraphicsPrimitive::transformVerticesFloatXYZ(const Matrix4x4Interface& matrix) +{ + const float numVertices = getNumberOfVertices(); + for (int32_t i = 0; i < numVertices; i++) { + const int32_t i3(i * 3); + matrix.multiplyPoint3(&m_xyz[i3]); + } +} + +/** * Get the float RGBA coloring for a vertex. * * @param vertexIndex @@ -977,6 +1137,23 @@ GraphicsPrimitive::replaceVertexFloatRGBA(const int32_t vertexIndex, const float rgba[4]) { + switch (m_releaseInstanceDataMode) { + case ReleaseInstanceDataMode::COMPLETED: + { + const QString msg("A vertex's RGBA Data in primitive cannot be replaced. " + "Instance data was removed to save memory. " + "setReleaseInstanceDataMode() should not be called for this primitive."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + break; + case ReleaseInstanceDataMode::DISABLED: + break; + case ReleaseInstanceDataMode::ENABLED: + break; + } + const int32_t i4 = vertexIndex * 4; switch (m_colorDataType) { case ColorDataType::NONE: @@ -1043,6 +1220,23 @@ GraphicsPrimitive::replaceVertexByteRGBA(const int32_t vertexIndex, const uint8_t rgba[4]) { + switch (m_releaseInstanceDataMode) { + case ReleaseInstanceDataMode::COMPLETED: + { + const QString msg("A vertex's RGBA Data in primitive cannot be replaced. " + "Instance data was removed to save memory. " + "setReleaseInstanceDataMode() should not be called for this primitive."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + break; + case ReleaseInstanceDataMode::DISABLED: + break; + case ReleaseInstanceDataMode::ENABLED: + break; + } + const int32_t i4 = vertexIndex * 4; switch (m_colorDataType) { case ColorDataType::NONE: @@ -1075,6 +1269,23 @@ void GraphicsPrimitive::replaceAllVertexSolidByteRGBA(const uint8_t rgba[4]) { + switch (m_releaseInstanceDataMode) { + case ReleaseInstanceDataMode::COMPLETED: + { + const QString msg("All RGBA Data in primitive cannot be replaced. " + "Instance data was removed to save memory. " + "setReleaseInstanceDataMode() should not be called for this primitive."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + break; + case ReleaseInstanceDataMode::DISABLED: + break; + case ReleaseInstanceDataMode::ENABLED: + break; + } + switch (m_colorDataType) { case ColorDataType::NONE: CaretAssert(0); @@ -1111,6 +1322,23 @@ void GraphicsPrimitive::replaceAllVertexSolidFloatRGBA(const float rgba[4]) { + switch (m_releaseInstanceDataMode) { + case ReleaseInstanceDataMode::COMPLETED: + { + const QString msg("All RGBA Data in primitive cannot be replaced. " + "Instance data was removed to save memory. " + "setReleaseInstanceDataMode() should not be called for this primitive."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + break; + case ReleaseInstanceDataMode::DISABLED: + break; + case ReleaseInstanceDataMode::ENABLED: + break; + } + switch (m_colorDataType) { case ColorDataType::NONE: CaretAssert(0); @@ -1163,10 +1391,10 @@ for (int32_t i = 0; i < numberOfVertices; i++) { const int32_t i3 = i * 3; CaretAssertVectorIndex(m_xyz, i3 + 2); - m_boundingBox->update(&m_xyz[i3]); + m_boundingBox->updateExcludeNanInf(&m_xyz[i3]); } - m_boundingBoxValid = true; + m_boundingBoxValid = m_boundingBox->isValid2D(); } boundingBoxOut = *m_boundingBox; @@ -1330,7 +1558,7 @@ } } - m_boundingBoxValid = false; + invalidateVertexMeasurements(); } /** @@ -1674,6 +1902,315 @@ } /** + * Get the mean and standard deviation for the Y-values + * @param yMeanOut + * Output with mean + * @param yStandardDeviationOut + * Output with standard deviation + */ +void +GraphicsPrimitive::getMeanAndStandardDeviationForY(float& yMeanOut, + float& yStandardDeviationOut) const +{ + /* + * Standard deviation is always non-negative so use + * a negative values as invalid or not-computed + */ + if (m_yStandardDeviation < 0.0) { + const int32_t numVertices = getNumberOfVertices(); + if (numVertices <= 0) { + yMeanOut = 0.0; + yStandardDeviationOut = 1.0; + return; + } + + std::vector yValues; + yValues.resize(numVertices); + for (int32_t i = 0; i < numVertices; i++) { + yValues[i] = m_xyz[(i * 3) + 1]; + } + + DescriptiveStatistics stats; + stats.update(yValues); + m_yMean = stats.getMean(); + m_yStandardDeviation = stats.getPopulationStandardDeviation(); + } + + yMeanOut = m_yMean; + yStandardDeviationOut = m_yStandardDeviation; +} + +/** + * Apply a new mean and/or deviation to the Y-components + * @param applyNewMeanFlag + * Apply the new mean + * @param newMean + * Value for new mean + * @param applyNewDeviationFlag + * Apply the new deviation + * @param newDeviation + * Value for new deviation + * @param applyAbsoluteValueFlag + * Make result absolute value + * @param haveNanInfFlagOut + * Output: True if Not a Number or Infinity found in the data + */ +void +GraphicsPrimitive::applyNewMeanAndDeviationToYComponents(const bool applyNewMeanFlag, + const float newMean, + const bool applyNewDeviationFlag, + const float newDeviation, + const bool applyAbsoluteValueFlag, + bool& haveNanInfFlagOut) +{ + haveNanInfFlagOut = false; + if ( ! (applyNewMeanFlag + || applyNewDeviationFlag + || applyAbsoluteValueFlag)) { + return; + } + + std::vector data; + getFloatYComponents(data); + + for (auto& d : data) { + if ( ! MathFunctions::isNumeric(d)) { + haveNanInfFlagOut = true; + break; + } + } + + if (haveNanInfFlagOut) { + applyNewMeanAndDeviationToYComponentsWithNaNs(data, + applyNewMeanFlag, + newMean, + applyNewDeviationFlag, + newDeviation, + applyAbsoluteValueFlag); + } + else { + applyNewMeanAndDeviationToYComponentsNoNaNs(data, + applyNewMeanFlag, + newMean, + applyNewDeviationFlag, + newDeviation, + applyAbsoluteValueFlag); + } + + setFloatYComponents(data); +} + +/** + * Apply a new mean and/or deviation to the Y-components that have NaNs or INFs + * @param applyNewMeanFlag + * Apply the new mean + * @param newMean + * Value for new mean + * @param applyNewDeviationFlag + * Apply the new deviation + * @param newDeviation + * Value for new deviation + * @param applyAbsoluteValueFlag + * Make result absolute value + */ +void +GraphicsPrimitive::applyNewMeanAndDeviationToYComponentsWithNaNs(std::vector& data, + const bool applyNewMeanFlag, + const float newMean, + const bool applyNewDeviationFlag, + const float newDeviation, + const bool applyAbsoluteValueFlag) +{ + + /* + * Sum of data while excluding invalid numbers + */ + int32_t numData(0); + double dataSum(0.0); + for (auto& d : data) { + if (MathFunctions::isNumeric(d)) { + if (applyAbsoluteValueFlag) { + if (d < 0.0) { + d = -d; + } + } + dataSum += d; + numData++; + } + } + + if (numData <= 0) { + /* + * All data is NaN of INF + */ + return; + } + + /* + * Compute mean + */ + const double numValuesDouble(numData); + const double dataMean(dataSum / numValuesDouble); + + /* + * Subtract mean from data + */ + for (auto& d : data) { + d -= dataMean; + } + + if (applyNewDeviationFlag) { + /* + * Compute deviation of data. + * Note: mean has been already been subtracted from data + */ + double sumSQ(0.0); + for (auto& d : data) { + if (MathFunctions::isNumeric(d)) { + sumSQ += (d * d); + } + } + const double variance(sumSQ / numValuesDouble); + const double dataDeviation((variance > 0.0) + ? (std::sqrt(variance)) + : 0.0); + + /* + * Data = (Data / dataDeviation) * newDevation + * => Data * (newDeviation / dataDeviation); + */ + const double deviationRatio((dataDeviation != 0.0) + ? (newDeviation / dataDeviation) + : newDeviation); + for (auto& d : data) { + d *= deviationRatio; + } + } + + if (applyNewMeanFlag) { + for (auto& d : data) { + d += newMean; + } + } + else { + for (auto& d : data) { + d += dataMean; + } + } +} + +/** + * Apply a new mean and/or deviation to the Y-components that DO NOT have NaNs or INFs + * @param applyNewMeanFlag + * Apply the new mean + * @param newMean + * Value for new mean + * @param applyNewDeviationFlag + * Apply the new deviation + * @param newDeviation + * Value for new deviation + * @param applyAbsoluteValueFlag + * Make result absolute value + */ +void +GraphicsPrimitive::applyNewMeanAndDeviationToYComponentsNoNaNs(std::vector& data, + const bool applyNewMeanFlag, + const float newMean, + const bool applyNewDeviationFlag, + const float newDeviation, + const bool applyAbsoluteValueFlag) +{ + const int32_t num = static_cast(data.size()); + if (num < 1) { + return; + } + const double numValuesDouble(num); + + /* + * Compute mean + */ + double dataSum(0.0); + for (auto& d : data) { + if (applyAbsoluteValueFlag) { + if (d < 0.0) { + d = -d; + } + } + dataSum += d; + } + const double dataMean(dataSum / numValuesDouble); + + /* + * Subtract mean from data + */ + for (auto& d : data) { + d -= dataMean; + } + + if (applyNewDeviationFlag) { + /* + * Compute deviation of data. + * Note: mean has been already been subtracted from data + */ + double sumSQ(0.0); + for (auto& d : data) { + sumSQ += (d * d); + } + const double variance(sumSQ / numValuesDouble); + const double dataDeviation((variance > 0.0) + ? (std::sqrt(variance)) + : 0.0); + + /* + * Data = (Data / dataDeviation) * newDevation + * => Data * (newDeviation / dataDeviation); + */ + const double deviationRatio((dataDeviation != 0.0) + ? (newDeviation / dataDeviation) + : newDeviation); + for (auto& d : data) { + d *= deviationRatio; + } + } + + if (applyNewMeanFlag) { + for (auto& d : data) { + d += newMean; + } + } + else { + for (auto& d : data) { + d += dataMean; + } + } +} + +/** + * @return A description of the mean/deviation operations. + */ +AString +GraphicsPrimitive::getNewMeanDeviationOperationDescriptionInHtml() +{ + AString txt; + + txt.appendWithNewLine(""); + txt.appendWithNewLine("Order of data elements transformation"); + txt.appendWithNewLine("
    "); + txt.appendWithNewLine("
  1. If Absolute Values is checked, convert data elements to absolute values."); + txt.appendWithNewLine("
  2. Subtract Data Mean from data elements."); + txt.appendWithNewLine("
  3. if New Deviation is checked, multiply data elements by " + "(New Deviation / Data Deviation>)."); + txt.appendWithNewLine("
  4. If New Mean is checked, add New Mean to data elements. " + "Otherwise, add Data Mean to data elements."); + txt.appendWithNewLine("
"); + txt.appendWithNewLine("Note: NaNs are ignored in all computations."); + txt.appendWithNewLine(""); + + return txt; +} + + +/** * Get the OpenGL graphics engine data in this instance. * * @return @@ -1749,6 +2286,20 @@ } /** + * @return A new primitive for XYZ with normals and RGBS. Caller is responsible + * for deleting the returned pointer. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3fN3fC4ub* +GraphicsPrimitive::newPrimitiveV3fN3fC4ub(const GraphicsPrimitive::PrimitiveType primitiveType) +{ + GraphicsPrimitiveV3fN3fC4ub* primitive = new GraphicsPrimitiveV3fN3fC4ub(primitiveType); + return primitive; +} + +/** * @return A new primitive for XYZ with solid color unsigned byte RGBA. Caller is responsible * for deleting the returned pointer. * @@ -1792,16 +2343,84 @@ return primitive; } +/** + * @return A new primitive with XYZ and texture STR. Caller is responsible for + * deleting the returned pointer. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + * @param imageBytesRGBA + * Bytes containing the image data. + * @param imageWidth + * Width of the actual image. + * @param imageHeight + * Height of the image. + * @param textureWrappingType + * Type of texture wrapping + * @param textureFilteringType + * Type of texture filtering + */ GraphicsPrimitiveV3fT3f* GraphicsPrimitive::newPrimitiveV3fT3f(const GraphicsPrimitive::PrimitiveType primitiveType, - const uint8_t* imageBytesRGBA, - const int32_t imageWidth, - const int32_t imageHeight) + const uint8_t* imageBytesRGBA, + const int32_t imageWidth, + const int32_t imageHeight, + const TextureWrappingType textureWrappingType, + const TextureFilteringType textureFilteringType) { GraphicsPrimitiveV3fT3f* primitive = new GraphicsPrimitiveV3fT3f(primitiveType, imageBytesRGBA, imageWidth, - imageHeight); + imageHeight, + textureWrappingType, + textureFilteringType); return primitive; } +/** + * Called after buffers have been loaded and may release instance data. + * If NOT called after buffers have been loaded, bad things may happen. + */ +void +GraphicsPrimitive::setOpenGLBuffersHaveBeenLoadedByGraphicsEngine() +{ + switch (m_releaseInstanceDataMode) { + case ReleaseInstanceDataMode::COMPLETED: + return; + break; + case ReleaseInstanceDataMode::DISABLED: + return; + break; + case ReleaseInstanceDataMode::ENABLED: + { + /* + * Note: calling ".clear()" on a vector will set the size + * of the vector to zero but does not free the memory. Instead, + * use swap() which causes deallocation of the memory. + * See http://www.cplusplus.com/reference/vector/vector/clear/ + */ + std::vector().swap(m_xyz); + std::vector().swap(m_floatRGBA); + std::vector().swap(m_floatNormalVectorXYZ); + std::vector().swap(m_unsignedByteRGBA); + std::vector().swap(m_floatTextureSTR); + std::vector().swap(m_textureImageBytesRGBA); + + m_releaseInstanceDataMode = ReleaseInstanceDataMode::COMPLETED; + } + break; + } +} + +/** + * Invalidate vertex measurements + */ +void +GraphicsPrimitive::invalidateVertexMeasurements() +{ + m_boundingBoxValid = false; + m_yMean = 0.0; + m_yStandardDeviation = -1.0; +} + + diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitive.h connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitive.h --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitive.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitive.h 2021-02-16 19:46:47.000000000 +0000 @@ -29,7 +29,6 @@ #include "CaretObject.h" #include "EventListenerInterface.h" - namespace caret { class BoundingBox; @@ -38,7 +37,9 @@ class GraphicsPrimitiveV3fC4f; class GraphicsPrimitiveV3fC4ub; class GraphicsPrimitiveV3fN3f; + class GraphicsPrimitiveV3fN3fC4ub; class GraphicsPrimitiveV3fT3f; + class Matrix4x4Interface; class GraphicsPrimitive : public CaretObject, public EventListenerInterface { @@ -96,6 +97,26 @@ }; /** + * Texture wrapping type + */ + enum class TextureWrappingType { + /** Clamp so max STR is 1.0 (default) */ + CLAMP, + /** Repeat so max STR is greater than 1.0) */ + REPEAT + }; + + /** + * Texture filtering type + */ + enum class TextureFilteringType { + /* Nearest texel - no interpolation */ + NEAREST, + /* Linear interpolate using nearest texels */ + LINEAR + }; + + /** * Type of primitives for drawing. There are NO primitives equivalent to * OpenGL's GL_QUAD_STRIP and GL_POLYGON. The reason is that these * primitive types are removed in 3.x (probably 3.2). @@ -255,6 +276,26 @@ }; /** + * Modes for releasing instance data after OpenGL buffers have been loaded. + * For large primitives, this can save substantial memory. Do not use if + * data will be updated. + */ + enum class ReleaseInstanceDataMode { + /** + * Release of instance data has been completed + */ + COMPLETED, + /** + * Release of instance data is disabled (DEFAULT) + */ + DISABLED, + /** + * Release is enabled but not yet completed (OpenGL buffers not loaded yet) + */ + ENABLED + }; + + /** * Hint to graphics engine on how the primitive is used. * The hint must be set prior to loading of buffers. */ @@ -282,6 +323,8 @@ const ColorDataType colorDataType, const VertexColorType vertexColorType, const TextureDataType textureDataType, + const TextureWrappingType textureWrappingType, + const TextureFilteringType textureFilteringType, const PrimitiveType primitiveType); GraphicsPrimitive(const GraphicsPrimitive& obj); @@ -299,6 +342,8 @@ static GraphicsPrimitiveV3fN3f* newPrimitiveV3fN3f(const GraphicsPrimitive::PrimitiveType primitiveType, const uint8_t unsignedByteRGBA[4]); + static GraphicsPrimitiveV3fN3fC4ub* newPrimitiveV3fN3fC4ub(const GraphicsPrimitive::PrimitiveType primitiveType); + static GraphicsPrimitiveV3fC4f* newPrimitiveV3fC4f(const GraphicsPrimitive::PrimitiveType primitiveType); static GraphicsPrimitiveV3fC4ub* newPrimitiveV3fC4ub(const GraphicsPrimitive::PrimitiveType primitiveType); @@ -306,7 +351,9 @@ static GraphicsPrimitiveV3fT3f* newPrimitiveV3fT3f(const GraphicsPrimitive::PrimitiveType primitiveType, const uint8_t* imageBytesRGBA, const int32_t imageWidth, - const int32_t imageHeight); + const int32_t imageHeight, + const TextureWrappingType textureWrappingType, + const TextureFilteringType textureFilteringType); virtual ~GraphicsPrimitive(); @@ -360,11 +407,33 @@ inline PrimitiveType getPrimitiveType() const { return m_primitiveType; } /** + * @return Mode for release of instance data (xyz, normals, coloring, etc) after buffers are loaded + */ + ReleaseInstanceDataMode getReleaseInstanceDataMode() const { return m_releaseInstanceDataMode; } + + /** + * Set the mode for release of instance data (xyz, normals, coloring, etc) after buffers are loaded + */ + void setReleaseInstanceDataMode(const ReleaseInstanceDataMode releaseDataMode) { + m_releaseInstanceDataMode = releaseDataMode; + } + + /** * @return Data type of texture. */ inline TextureDataType getTextureDataType() const { return m_textureDataType; } /** + * @return Type of texture wrapping + */ + inline TextureWrappingType getTextureWrappingType() const { return m_textureWrappingType; } + + /** + * @return Type of texture filtering + */ + inline TextureFilteringType getTextureFilteringType() const { return m_textureFilteringType; } + + /** * @return The float coordinates. */ const std::vector& getFloatXYZ() const { return m_xyz; } @@ -374,6 +443,10 @@ void replaceFloatXYZ(const std::vector& xyz); + void getFloatYComponents(std::vector& yComponentsOut) const; + + void setFloatYComponents(const std::vector& yComponents); + /** * @return The number of vertices */ @@ -382,6 +455,11 @@ void replaceVertexFloatXYZ(const int32_t vertexIndex, const float xyz[3]); + void replaceAndTransformVertices(const GraphicsPrimitive* primitive, + const Matrix4x4Interface& matrix); + + void transformVerticesFloatXYZ(const Matrix4x4Interface& matrix); + void getVertexFloatRGBA(const int32_t vertexIndex, float rgbaOut[4]) const; @@ -435,6 +513,18 @@ void simplfyLines(const int32_t skipVertexCount); + void getMeanAndStandardDeviationForY(float& yMeanOut, + float& yStandardDeviationOut) const; + + void applyNewMeanAndDeviationToYComponents(const bool applyNewMeanFlag, + const float newMean, + const bool applyNewDeviationFlag, + const float newDeviation, + const bool applyAbsoluteValueFlag, + bool& haveNanInfFlagOut); + + static AString getNewMeanDeviationOperationDescriptionInHtml(); + protected: AString toStringPrivate(const bool includeAllDataFlag) const; @@ -458,6 +548,8 @@ AString getVertexColorTypeAsText(const VertexColorType vertexColorType) const; + void invalidateVertexMeasurements(); + const VertexDataType m_vertexDataType; const NormalVectorDataType m_normalVectorDataType; @@ -468,8 +560,14 @@ const TextureDataType m_textureDataType; + const TextureWrappingType m_textureWrappingType; + + const TextureFilteringType m_textureFilteringType; + const PrimitiveType m_primitiveType; + ReleaseInstanceDataMode m_releaseInstanceDataMode = ReleaseInstanceDataMode::DISABLED; + mutable bool m_boundingBoxValid = false; UsageType m_usageTypeCoordinates = UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES; @@ -513,6 +611,22 @@ void fillTriangleStripPrimitiveRestartVertices(); + void setOpenGLBuffersHaveBeenLoadedByGraphicsEngine(); + + void applyNewMeanAndDeviationToYComponentsNoNaNs(std::vector& data, + const bool applyNewMeanFlag, + const float newMean, + const bool applyNewDeviationFlag, + const float newDeviation, + const bool applyAbsoluteValueFlag); + + void applyNewMeanAndDeviationToYComponentsWithNaNs(std::vector& data, + const bool applyNewMeanFlag, + const float newMean, + const bool applyNewDeviationFlag, + const float newDeviation, + const bool applyAbsoluteValueFlag); + std::vector m_xyz; std::vector m_floatNormalVectorXYZ; @@ -525,6 +639,10 @@ std::vector m_textureImageBytesRGBA; + mutable float m_yMean = 0.0; + + mutable float m_yStandardDeviation = -1.0; + friend class GraphicsEngineDataOpenGL; friend class GraphicsOpenGLPolylineTriangles; friend class GraphicsPrimitiveSelectionHelper; diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -125,13 +125,14 @@ if (numberOfVertices > 0) { const int32_t numberOfPrimitives = (numberOfVertices / m_numberOfVerticesPerPrimitive) - m_vertexOffsetForPrimitive; - const int32_t maxPrimitivesSupported = 255 * 255 * 255; + const int32_t maxPrimitivesSupported = 255 * 255 * 255; // PROBABLY 256**3 - 1 if (numberOfPrimitives > maxPrimitivesSupported) { const AString msg("Number of primitives for selection=" + AString::number(numberOfPrimitives) + " exceeds maximum allowed=" - + AString::number(maxPrimitivesSupported)); - CaretAssertMessage(0, msg); + + AString::number(maxPrimitivesSupported) + + ". Identification will not function for some data"); + m_selectionEncodedRGBA.clear(); CaretLogSevere(msg); } else { diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fC4f.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fC4f.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fC4f.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fC4f.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -46,6 +46,8 @@ ColorDataType::FLOAT_RGBA, VertexColorType::PER_VERTEX_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -46,6 +46,8 @@ ColorDataType::UNSIGNED_BYTE_RGBA, VertexColorType::PER_VERTEX_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3f.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3f.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3f.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3f.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -55,6 +55,8 @@ ColorDataType::FLOAT_RGBA, VertexColorType::SOLID_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { m_floatSolidRGBA[0] = rgba[0]; @@ -78,6 +80,8 @@ ColorDataType::UNSIGNED_BYTE_RGBA, VertexColorType::SOLID_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { m_unsignedByteSolidRGBA[0] = rgba[0]; diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,139 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GRAPHICS_PRIMITIVE_V3F_N3F_C4UB_DECLARE__ +#include "GraphicsPrimitiveV3fN3fC4ub.h" +#undef __GRAPHICS_PRIMITIVE_V3F_N3F_C4UB_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::GraphicsPrimitiveV3fN3fC4ub + * \brief Primitive containing XYZ and Float RGBA. + * \ingroup Graphics + */ + +/** + * Constructor. + * + * @param primitiveType + * Type of primitive drawn (triangles, lines, etc.) + */ +GraphicsPrimitiveV3fN3fC4ub::GraphicsPrimitiveV3fN3fC4ub(const PrimitiveType primitiveType) +: GraphicsPrimitive(VertexDataType::FLOAT_XYZ, + NormalVectorDataType::FLOAT_XYZ, + ColorDataType::UNSIGNED_BYTE_RGBA, + VertexColorType::PER_VERTEX_RGBA, + TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, + primitiveType) +{ + +} + +/** + * Destructor. + */ +GraphicsPrimitiveV3fN3fC4ub::~GraphicsPrimitiveV3fN3fC4ub() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +GraphicsPrimitiveV3fN3fC4ub::GraphicsPrimitiveV3fN3fC4ub(const GraphicsPrimitiveV3fN3fC4ub& obj) +: GraphicsPrimitive(obj) +{ + this->copyHelperGraphicsPrimitiveV3fN3fC4ub(obj); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +GraphicsPrimitiveV3fN3fC4ub::copyHelperGraphicsPrimitiveV3fN3fC4ub(const GraphicsPrimitiveV3fN3fC4ub& /*obj*/) +{ + +} + +/** + * Add a vertex. + * + * @param xyz + * Coordinate of vertex. + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +void +GraphicsPrimitiveV3fN3fC4ub::addVertex(const float xyz[3], + const float normalXYZ[3], + const uint8_t rgba[4]) +{ + addVertexProtected(xyz, + normalXYZ, + NULL, + rgba, + NULL); +} + +/** + * Add a vertex. + * + * @param x + * X-coordinate of vertex. + * @param y + * Y-coordinate of vertex. + * @param z + * Z-coordinate of vertex. + * @param rgba + * RGBA color components ranging 0.0 to 1.0. + */ +void +GraphicsPrimitiveV3fN3fC4ub::addVertex(const float x, + const float y, + const float z, + const float normalXYZ[3], + const uint8_t rgba[4]) +{ + const float xyz[] { x, y, z }; + addVertex(xyz, + normalXYZ, + rgba); +} + +/** + * Clone this primitive. + */ +GraphicsPrimitive* +GraphicsPrimitiveV3fN3fC4ub::clone() const +{ + GraphicsPrimitiveV3fN3fC4ub* obj = new GraphicsPrimitiveV3fN3fC4ub(*this); + return obj; +} + diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.h connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.h --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,69 @@ +#ifndef __GRAPHICS_PRIMITIVE_V3F_N3F_C4UB_H__ +#define __GRAPHICS_PRIMITIVE_V3F_N3F_C4UB_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "GraphicsPrimitive.h" + +namespace caret { + + class GraphicsPrimitiveV3fN3fC4ub : public GraphicsPrimitive { + + public: + GraphicsPrimitiveV3fN3fC4ub(const PrimitiveType primitiveType); + + virtual ~GraphicsPrimitiveV3fN3fC4ub(); + + GraphicsPrimitiveV3fN3fC4ub(const GraphicsPrimitiveV3fN3fC4ub& obj); + + void addVertex(const float xyz[3], + const float normalXYZ[3], + const uint8_t rgba[4]); + + void addVertex(const float x, + const float y, + const float z, + const float normalXYZ[3], + const uint8_t rgba[4]); + + virtual GraphicsPrimitive* clone() const; + + // ADD_NEW_METHODS_HERE + + private: + GraphicsPrimitiveV3fN3fC4ub& operator=(const GraphicsPrimitiveV3fN3fC4ub& obj); + + void copyHelperGraphicsPrimitiveV3fN3fC4ub(const GraphicsPrimitiveV3fN3fC4ub& obj); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GRAPHICS_PRIMITIVE_V3F_N3F_C4UB_DECLARE__ + // +#endif // __GRAPHICS_PRIMITIVE_V3F_N3F_C4UB_DECLARE__ + +} // namespace +#endif //__GRAPHICS_PRIMITIVE_V3F_N3F_C4UB_H__ diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fN3f.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fN3f.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fN3f.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fN3f.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -55,6 +55,8 @@ ColorDataType::FLOAT_RGBA, VertexColorType::SOLID_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { m_floatSolidRGBA[0] = rgba[0]; @@ -78,6 +80,8 @@ ColorDataType::UNSIGNED_BYTE_RGBA, VertexColorType::SOLID_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { m_unsignedByteSolidRGBA[0] = rgba[0]; diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fT3f.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fT3f.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fT3f.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fT3f.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -45,16 +45,24 @@ * Width of the actual image. * @param imageHeight * Height of the image. + * @param textureWrappingType + * Type of texture wrapping + * @param textureFilteringType + * Type of texture filtering */ GraphicsPrimitiveV3fT3f::GraphicsPrimitiveV3fT3f(const PrimitiveType primitiveType, const uint8_t* imageBytesRGBA, const int32_t imageWidth, - const int32_t imageHeight) + const int32_t imageHeight, + const TextureWrappingType textureWrappingType, + const TextureFilteringType textureFilteringType) : GraphicsPrimitive(VertexDataType::FLOAT_XYZ, NormalVectorDataType::NONE, ColorDataType::NONE, VertexColorType::NONE, TextureDataType::FLOAT_STR, + textureWrappingType, + textureFilteringType, primitiveType) { setTextureImage(imageBytesRGBA, diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fT3f.h connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fT3f.h --- connectome-workbench-1.4.2/src/Graphics/GraphicsPrimitiveV3fT3f.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsPrimitiveV3fT3f.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,7 +37,9 @@ GraphicsPrimitiveV3fT3f(const PrimitiveType primitiveType, const uint8_t* imageBytesRGBA, const int32_t imageWidth, - const int32_t imageHeight); + const int32_t imageHeight, + const TextureWrappingType textureWrappingType, + const TextureFilteringType textureFilteringType); virtual ~GraphicsPrimitiveV3fT3f(); diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsShape.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsShape.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsShape.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsShape.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -27,11 +27,14 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "EventManager.h" +#include "EventOpenGLObjectToWindowTransform.h" #include "GraphicsEngineDataOpenGL.h" #include "GraphicsPrimitive.h" #include "GraphicsPrimitiveV3f.h" #include "GraphicsPrimitiveV3fC4ub.h" #include "GraphicsPrimitiveV3fN3f.h" +#include "GraphicsUtilitiesOpenGL.h" #include "MathFunctions.h" #include "Matrix4x4.h" @@ -481,44 +484,7 @@ drawSpheresByteColor(xyz, 1, rgba, - diameter); - -// const int32_t numLatLonDivisions = 10; -// -// GraphicsPrimitive* spherePrimitive = NULL; -// for (const auto iter : s_byteSpherePrimitives) { -// const auto& key = iter.first; -// if ((key == numLatLonDivisions)) { -// spherePrimitive = iter.second; -// CaretAssert(spherePrimitive); -// break; -// } -// } -// -// if (spherePrimitive == NULL) { -// const bool useStripsFlag = true; -// if (useStripsFlag) { -// spherePrimitive = createSpherePrimitiveTriangleStrips(numLatLonDivisions); -// } -// else { -// spherePrimitive = createSpherePrimitiveTriangles(numLatLonDivisions); -// } -// /* colors may change but not coordinates/normals */ -// spherePrimitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_MANY_TIMES); -// spherePrimitive->setUsageTypeColors(GraphicsPrimitive::UsageType::MODIFIED_MANY_DRAWN_MANY_TIMES); -// s_byteSpherePrimitives.insert(std::make_pair(numLatLonDivisions, -// spherePrimitive)); -// } -// -// CaretAssert(spherePrimitive); -// -// spherePrimitive->replaceAllVertexSolidByteRGBA(rgba); -// -// glPushMatrix(); -// glTranslatef(xyz[0], xyz[1], xyz[2]); -// glScalef(diameter, diameter, diameter); -// GraphicsEngineDataOpenGL::draw(spherePrimitive); -// glPopMatrix(); + diameter); } /** @@ -543,7 +509,7 @@ const int32_t numLatLonDivisions = 10; GraphicsPrimitive* spherePrimitive = NULL; - for (const auto iter : s_byteSpherePrimitives) { + for (const auto& iter : s_byteSpherePrimitives) { const auto& key = iter.first; if ((key == numLatLonDivisions)) { spherePrimitive = iter.second; @@ -600,7 +566,7 @@ const int32_t numberOfDivisions = 20; GraphicsPrimitive* circlePrimitive = NULL; - for (const auto keyPrim : s_byteCirclePrimitives) { + for (const auto& keyPrim : s_byteCirclePrimitives) { if (keyPrim.first == numberOfDivisions) { circlePrimitive = keyPrim.second; break; @@ -631,6 +597,189 @@ } /** + * Draw a filled circle at the given XYZ coordinate using a diameter + * that is a percentage of the viewport's height. + * + * @param xyz + * XYZ-coordinate of circle + * @param rgba + * Color for drawing. + * @param diameterPercentageOfViewportHeight + * Diameter of the circle. + * @param windowXYZOut + * Optional parameter. If not NULL, contains OpenGL window XYZ of the shape. + * X-component is positive if value coordinate is valid, else negative indicates invalid. + */ +void +GraphicsShape::drawCircleFilledPercentViewportHeight(const float xyz[3], + const uint8_t rgba[4], + const float diameterPercentageOfViewportHeight, + std::array* windowXYZOut) +{ + const float unusedSecondDiameter(0.0); + drawShapePercentViewportHeight(xyz, + rgba, + Shape::CIRCLE_FILLED, + diameterPercentageOfViewportHeight, + unusedSecondDiameter, + windowXYZOut); +} + +/** + * Draw a ring at the given XYZ coordinate using sizes + * that are a percentage of the viewport's height. + * + * @param xyz + * XYZ-coordinate of circle + * @param rgba + * Color for drawing. + * @param innerDiameterPercentageOfViewportHeight + * Inner diameter of the ring. + * @param outerDiameterPercentageOfViewportHeight + * Outer diameter of the ring. + * @param windowXYZOut + * Optional parameter. If not NULL, contains OpenGL window XYZ of the shape. + * X-component is positive if value coordinate is valid, else negative indicates invalid. + */ +void +GraphicsShape::drawRingPercentViewportHeight(const float xyz[3], + const uint8_t rgba[4], + const float innerDiameterPercentageOfViewportHeight, + const float outerDiameterPercentageOfViewportHeight, + std::array* windowXYZOut) +{ + drawShapePercentViewportHeight(xyz, + rgba, + Shape::RING, + innerDiameterPercentageOfViewportHeight, + outerDiameterPercentageOfViewportHeight, + windowXYZOut); +} + +/** + * Draw a filled circle at the given XYZ coordinate using a diameter + * that is a percentage of the viewport's height. + * + * @param xyz + * XYZ-coordinate of circle + * @param rgba + * Color for drawing. + * @param diameterPercentageOfViewportHeight + * Diameter of the circle. + * @param windowXYZOut + * Optional parameter. If not NULL, contains OpenGL window XYZ of the shape. + * X-component is positive if value coordinate is valid, else negative indicates invalid. + */ +void +GraphicsShape::drawSquarePercentViewportHeight(const float xyz[3], + const uint8_t rgba[4], + const float diameterPercentageOfViewportHeight, + std::array* windowXYZOut) +{ + const float unusedSecondDiameter(0.0); + drawShapePercentViewportHeight(xyz, + rgba, + Shape::SQUARE, + diameterPercentageOfViewportHeight, + unusedSecondDiameter, + windowXYZOut); +} + +/** + * Draw a shape at the given XYZ coordinate using a diameter + * that is a percentage of the viewport's height. + * + * @param xyz + * XYZ-coordinate of circle + * @param rgba + * Color for drawing. + * @param shape + * Shape that is drawn + * @param diameterPercentageOfViewportHeight + * Diameter of the shape. + * @param diameterTwoPercentageOfViewportHeight + * Diameter of the shape for (outer for ring, unused by others). + * @param windowXYZOut + * Optional parameter. If not NULL, contains OpenGL window XYZ of the shape. + * X-component is positive if value coordinate is valid, else negative indicates invalid. + */ +void +GraphicsShape::drawShapePercentViewportHeight(const float xyz[3], + const uint8_t rgba[4], + const Shape shape, + const float diameterPercentageOfViewportHeight, + const float diameterTwoPercentageOfViewportHeight, + std::array* windowXYZOut) +{ + /* + * Invalidate optional output window coordinate + */ + if (windowXYZOut != NULL) { + windowXYZOut->at(0) = -1.0f; + } + + /* + * The scale of X and Y may be very different and drawing a shape + * in model space requires scaling the circle separately in X and Y. + * It is easier to draw the shape in window space. + */ + EventOpenGLObjectToWindowTransform xform(EventOpenGLObjectToWindowTransform::SpaceType::MODEL); + EventManager::get()->sendEvent(xform.getPointer()); + if (xform.isValid()) { + float windowXYZ[3]; + xform.transformPoint(xyz, + windowXYZ); + + std::array vp = xform.getViewport(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(vp[0], vp[0] + vp[2], + vp[1], vp[1] + vp[3], + -10.0, 10.0); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + const float pixelSize = GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToPixels(diameterPercentageOfViewportHeight); + const float pixelSizeTwo = GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToPixels(diameterTwoPercentageOfViewportHeight); + + switch (shape) { + case Shape::CIRCLE_FILLED: + GraphicsShape::drawCircleFilled(windowXYZ, + rgba, + pixelSize); + break; + case Shape::SQUARE: + GraphicsShape::drawSquare(windowXYZ, + rgba, + pixelSize); + break; + case Shape::RING: + GraphicsShape::drawRing(windowXYZ, + rgba, + pixelSize / 2, + pixelSizeTwo / 2); + break; + } + + if (windowXYZOut != NULL) { + windowXYZOut->at(0) = windowXYZ[0]; + windowXYZOut->at(1) = windowXYZ[1]; + windowXYZOut->at(2) = windowXYZ[2]; + } + + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } +} + + +/** * Draw a squares at the given XYZ coordinates * * @param xyz @@ -784,7 +933,7 @@ const int32_t numberOfDivisions = 20; GraphicsPrimitive* ringPrimitive = NULL; - for (const auto keyPrim : s_byteRingPrimitives) { + for (const auto& keyPrim : s_byteRingPrimitives) { if (keyPrim.first.matches(numberOfDivisions, innerRadius, outerRadius)) { @@ -829,7 +978,6 @@ /* * Setup step size based upon number of points around circle */ - // const int32_t numberOfPoints = 8; const float step = (2.0 * M_PI) / numberOfDivisions; const uint8_t rgba[] { 255, 255, 255, 255 }; diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsShape.h connectome-workbench-1.5.0/src/Graphics/GraphicsShape.h --- connectome-workbench-1.4.2/src/Graphics/GraphicsShape.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsShape.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,7 +21,7 @@ */ /*LICENSE_END*/ - +#include #include #include @@ -111,10 +111,20 @@ const uint8_t rgba[4], const float diameter); + static void drawCircleFilledPercentViewportHeight(const float xyz[3], + const uint8_t rgba[4], + const float diameterPercentageOfViewportHeight, + std::array* windowXYZOut = NULL); + static void drawSquare(const float xyz[3], const uint8_t rgba[4], const float diameter); + static void drawSquarePercentViewportHeight(const float xyz[3], + const uint8_t rgba[4], + const float diameterPercentageOfViewportHeight, + std::array* windowXYZOut = NULL); + static void drawSquares(const float xyz[], const int32_t numberOfSquares, const uint8_t rgba[4], @@ -125,6 +135,12 @@ const double innerRadius, const double outerRadius); + static void drawRingPercentViewportHeight(const float xyz[3], + const uint8_t rgba[4], + const float innerDiameterPercentageOfViewportHeight, + const float outerDiameterPercentageOfViewportHeight, + std::array* windowXYZOut = NULL); + static void deleteAllPrimitives(); static void drawOutlineRectangleVerticesInMiddle(const double bottomLeft[3], @@ -160,6 +176,12 @@ virtual AString toString() const; private: + enum class Shape { + CIRCLE_FILLED, + RING, + SQUARE, + }; + class RingKey { public: RingKey(const int32_t numberOfDivisions, @@ -244,6 +266,13 @@ const uint8_t rgba[4], bool verticesInMiddleFlag); + static void drawShapePercentViewportHeight(const float xyz[3], + const uint8_t rgba[4], + const Shape shape, + const float diameterPercentageOfViewportHeight, + const float diameterTwoPercentageOfViewportHeight, + std::array* windowXYZOut); + static std::unique_ptr s_byteSquarePrimitive; static std::map s_byteSpherePrimitives; diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsUtilitiesOpenGL.cxx connectome-workbench-1.5.0/src/Graphics/GraphicsUtilitiesOpenGL.cxx --- connectome-workbench-1.4.2/src/Graphics/GraphicsUtilitiesOpenGL.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsUtilitiesOpenGL.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -130,6 +130,28 @@ } /** + * Converts percentage of viewport height to pixels. + * The current transformations must be for drawing in millimeters. + * + * @param percentOfViewportHeight + * The value in percentage of viewport height. + * @return + * Millimeters + */ +float +GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToPixels(const float percentOfViewportHeight) +{ + GLint vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + + float pixels = 0; + if (vp[3] > 0) { + pixels = (percentOfViewportHeight / 100.0) * static_cast(vp[3]); + } + return pixels; +} + +/** * Converts millimeters to a percentage of the viewport height. * The current transformations must be for drawing in millimeters. * @@ -291,4 +313,115 @@ return errorInfo; } +/** + * Set the OpenGL major and minor version + * @param majorVersion + * Major version + * @param majorVersion + * Major version + */ +void +GraphicsUtilitiesOpenGL::setMajorMinorVersion(const int32_t majorVersion, + const int32_t minorVersion) +{ + s_majorVersion = majorVersion; + s_minorVersion = minorVersion; +} + +/** + * @return True if the OpenGL major and minor version same or greater + * @param majorVersion + * Major version + * @param majorVersion + * Major version + */ +bool +GraphicsUtilitiesOpenGL::isVersionOrGreater(const int32_t majorVersion, + const int32_t minorVersion) +{ + /* + * Version unknown, assume valid + */ + if (s_majorVersion <= 0) { + return true; + } + + if (s_majorVersion > majorVersion) { + return true; + } + else if (s_majorVersion == majorVersion) { + if (s_minorVersion >= minorVersion) { + return true; + } + } + + return false; +} + +/** + * @return The OpenGL Major Version + */ +int32_t +GraphicsUtilitiesOpenGL::getMajorVersion() +{ + return s_majorVersion; +} + +/** + * @return The OpenGL Minor Version + */ +int32_t +GraphicsUtilitiesOpenGL::getMinorVersion() +{ + return s_minorVersion; +} + +/** + * @return The OpenGL version in form . + */ +QString +GraphicsUtilitiesOpenGL::getVersion() +{ + const QString txt(AString::number(s_majorVersion) + + "." + + AString::number(s_minorVersion)); + return txt; +} + +/** + * Set the OpenGL maximum texture dimensions + * @param widthHeightMaximumDimension + * The maximum texture dimension for width or height (texture s or t) + * @param depthMaximumDimension + * The maximum texture dimension for depth (texture r) + */ +void +GraphicsUtilitiesOpenGL::setMaximumTextureDimension(const int32_t widthHeightMaximumDimension, + const int32_t depthMaximumDimension) +{ + if (widthHeightMaximumDimension > 0) { + s_textureWidthHeightMaximumDimension = widthHeightMaximumDimension; + } + if (depthMaximumDimension) { + s_textureDepthMaximumDimension = depthMaximumDimension; + } +} + +/** + * @return The maximum texture dimension for width or height (texture s or t) + */ +int32_t +GraphicsUtilitiesOpenGL::getTextureWidthHeightMaximumDimension() +{ + return s_textureWidthHeightMaximumDimension; +} + +/** + * @return The maximum texture dimension for depth (texture r) + */ +int32_t +GraphicsUtilitiesOpenGL::getTextureDepthMaximumDimension() +{ + return s_textureDepthMaximumDimension; +} diff -Nru connectome-workbench-1.4.2/src/Graphics/GraphicsUtilitiesOpenGL.h connectome-workbench-1.5.0/src/Graphics/GraphicsUtilitiesOpenGL.h --- connectome-workbench-1.4.2/src/Graphics/GraphicsUtilitiesOpenGL.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Graphics/GraphicsUtilitiesOpenGL.h 2021-02-16 19:46:47.000000000 +0000 @@ -39,6 +39,8 @@ static float convertPercentageOfViewportHeightToMillimeters(const float percentOfViewportHeight); + static float convertPercentageOfViewportHeightToPixels(const float percentOfViewportHeight); + static float convertMillimetersToPixels(const float millimeters); static float convertPixelsToPercentageOfViewportHeight(const float pixels); @@ -49,6 +51,19 @@ static std::unique_ptr getOpenGLError(const AString& message = ""); + static bool isVersionOrGreater(const int32_t majorVersion, + const int32_t minorVersion); + + static int32_t getMajorVersion(); + + static int32_t getMinorVersion(); + + static QString getVersion(); + + static int32_t getTextureWidthHeightMaximumDimension(); + + static int32_t getTextureDepthMaximumDimension(); + private: GraphicsUtilitiesOpenGL(); @@ -58,10 +73,32 @@ GraphicsUtilitiesOpenGL& operator=(const GraphicsUtilitiesOpenGL&); + static void setMajorMinorVersion(const int32_t majorVersion, + const int32_t minorVersion); + + static void setMaximumTextureDimension(const int32_t widthHeightMaximumDimension, + const int32_t depthMaximumDimension); + + static int32_t s_majorVersion; + + static int32_t s_minorVersion; + + static int32_t s_textureWidthHeightMaximumDimension; + + static int32_t s_textureDepthMaximumDimension; + + friend class BrainOpenGL; }; #ifdef __GRAPHICS_UTILITIES_OPEN_G_L_DECLARE__ - // + int32_t GraphicsUtilitiesOpenGL::s_majorVersion = 0; + int32_t GraphicsUtilitiesOpenGL::s_minorVersion = 0; + + /* + * Default texture dimensions to typical values + */ + int32_t GraphicsUtilitiesOpenGL::s_textureWidthHeightMaximumDimension = 16384; + int32_t GraphicsUtilitiesOpenGL::s_textureDepthMaximumDimension = 2048; #endif // __GRAPHICS_UTILITIES_OPEN_G_L_DECLARE__ } // namespace diff -Nru connectome-workbench-1.4.2/src/GuiQt/AboutWorkbenchDialog.cxx connectome-workbench-1.5.0/src/GuiQt/AboutWorkbenchDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/AboutWorkbenchDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AboutWorkbenchDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -34,6 +34,7 @@ #include "ApplicationInformation.h" #include "BrainOpenGLWidget.h" #include "CaretAssert.h" +#include "ImageFile.h" #include "WuQDataEntryDialog.h" #include "WuQtUtilities.h" @@ -76,13 +77,12 @@ workbenchFont.setPointSize(32); workbenchLabel->setFont(workbenchFont); - const QString labelStyle = ("QLabel { " - " font: 20px bold " - "}"); QLabel* hcpWebsiteLabel = new QLabel("" - "Visit the Human Connectome Project" + "Visit the Human Connectome Project" ""); - hcpWebsiteLabel->setStyleSheet(labelStyle); + QFont hcpWebsiteFont(hcpWebsiteLabel->font()); + hcpWebsiteFont.setPointSize(16); + hcpWebsiteLabel->setFont(hcpWebsiteFont); hcpWebsiteLabel->setAlignment(Qt::AlignCenter); QObject::connect(hcpWebsiteLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(websiteLinkActivated(const QString&))); @@ -173,7 +173,30 @@ } informationData.push_back(QString("Style Name: " + styleName)); - + std::vector imageReadExtensions, imageWriteExtensions; + ImageFile::getQtSupportedImageFileExtensions(imageReadExtensions, + imageWriteExtensions); + informationData.push_back("Qt Readable Images: " + + AString::join(imageReadExtensions, ", ")); + informationData.push_back("Qt Writable Images: " + + AString::join(imageWriteExtensions, ", ")); + + AString imageWriteDefaultExtension; + ImageFile::getWorkbenchSupportedImageFileExtensions(imageReadExtensions, + imageWriteExtensions, + imageWriteDefaultExtension); + informationData.push_back("Workbench Readable Images: " + + AString::join(imageReadExtensions, ", ")); + informationData.push_back("Workbench Writable Images: " + + AString::join(imageWriteExtensions, ", ")); + informationData.push_back("Default Image Type: " + + imageWriteDefaultExtension); + + std::vector movieReadExtensions; + DataFileTypeEnum::getQtSupportedMovieFileExtensions(movieReadExtensions); + informationData.push_back("Qt Readable Movies (QMovie): " + + AString::join(movieReadExtensions, ", ")); + WuQDataEntryDialog ded("More " + appInfo.getName() + " Information", this, true); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationBackgroundTypeWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationBackgroundTypeWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationBackgroundTypeWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationBackgroundTypeWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,270 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_BACKGROUND_TYPE_WIDGET_DECLARE__ +#include "AnnotationBackgroundTypeWidget.h" +#undef __ANNOTATION_BACKGROUND_TYPE_WIDGET_DECLARE__ + +#include +#include +#include + +#include "AnnotationBrowserTab.h" +#include "AnnotationManager.h" +#include "AnnotationRedoUndoCommand.h" +#include "Brain.h" +#include "CaretAssert.h" +#include "EnumComboBoxTemplate.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "GuiManager.h" +#include "ModelChartTwo.h" +#include "TileTabsLayoutBackgroundTypeEnum.h" +#include "TileTabsManualTabGeometryWidget.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationBackgroundTypeWidget + * \brief Widget for editing annotation coordinate + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param + * @param whichCoordinate + * Which coordinate, one (or only), or two + * @param browserWindowIndex + * Index of browser window + * @param parent + * Parent widget + */ +AnnotationBackgroundTypeWidget::AnnotationBackgroundTypeWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, + const int32_t browserWindowIndex, + QWidget* parent) +: QWidget(parent), +m_userInputMode(userInputMode), +m_parentWidgetType(parentWidgetType), +m_browserWindowIndex(browserWindowIndex) +{ + switch (m_parentWidgetType) { + case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: + break; + case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: + CaretAssert(0); + break; + } + + QLabel* backgroundLabel = new QLabel("Background"); + m_TileTabsLayoutBackgroundTypeEnumComboBox = new EnumComboBoxTemplate(this); + m_TileTabsLayoutBackgroundTypeEnumComboBox->setup(); + QObject::connect(m_TileTabsLayoutBackgroundTypeEnumComboBox, SIGNAL(itemActivated()), + this, SLOT(tileTabsLayoutBackgroundTypeEnumComboBoxItemActivated())); + + QLabel* stackingOrderLabel = new QLabel("Order:"); + m_stackingOrderSpinBox = new QSpinBox(); + m_stackingOrderSpinBox->setRange(0, 1000); + m_stackingOrderSpinBox->setSingleStep(1); + m_stackingOrderSpinBox->setToolTip(TileTabsManualTabGeometryWidget::getStackOrderToolTipText()); + QObject::connect(m_stackingOrderSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &AnnotationBackgroundTypeWidget::stackingOrderValueChanged); + + QGridLayout* layout = new QGridLayout(this); + layout->setColumnStretch(0, 0); + layout->setColumnStretch(1, 100); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); + int32_t row(0); + layout->addWidget(backgroundLabel, + row, 0, 1, 2, Qt::AlignHCenter); + row++; + layout->addWidget(m_TileTabsLayoutBackgroundTypeEnumComboBox->getWidget(), + row, 0, 1, 2); + row++; + layout->addWidget(stackingOrderLabel, + row, 0); + layout->addWidget(m_stackingOrderSpinBox, + row, 1); + + setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +AnnotationBackgroundTypeWidget::~AnnotationBackgroundTypeWidget() +{ +} + +/** + * Update with the given annotation coordinate. + * + * @param coordinate. + */ +void +AnnotationBackgroundTypeWidget::updateContent(std::vector& selectedBrowserTabAnnotations) +{ + m_annotationBrowserTabs = selectedBrowserTabAnnotations; + + bool firstFlag(true); + int32_t firstOrderValue(false); + bool haveMultipleStackingOrderValuesFlag(false); + bool haveMultipleBackgroundValuesFlag(false); + TileTabsLayoutBackgroundTypeEnum::Enum firstBackgroundValue = TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG; + + if (! m_annotationBrowserTabs.empty()) { + for (auto ann : m_annotationBrowserTabs) { + if (firstFlag) { + firstBackgroundValue = ann->getBackgroundType(); + firstFlag = false; + firstOrderValue = ann->getStackingOrder(); + } + else { + if (firstBackgroundValue != ann->getBackgroundType()) { + haveMultipleBackgroundValuesFlag = true; + } + if (firstOrderValue != ann->getStackingOrder()) { + haveMultipleStackingOrderValuesFlag = true; + } + } + } + setEnabled(true); + } + else { + setEnabled(false); + } + + m_TileTabsLayoutBackgroundTypeEnumComboBox->setSelectedItem(firstBackgroundValue); + + const int32_t opaqueIndex = TileTabsLayoutBackgroundTypeEnum::toIntegerCode(TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG); + AString opaqueText(TileTabsLayoutBackgroundTypeEnum::toGuiName(TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG)); + const int32_t transparentIndex = TileTabsLayoutBackgroundTypeEnum::toIntegerCode(TileTabsLayoutBackgroundTypeEnum::TRANSPARENT_BG); + AString transparentText(TileTabsLayoutBackgroundTypeEnum::toGuiName(TileTabsLayoutBackgroundTypeEnum::TRANSPARENT_BG)); + + if (haveMultipleBackgroundValuesFlag) { + switch (firstBackgroundValue) { + case TileTabsLayoutBackgroundTypeEnum::OPAQUE_BG: + opaqueText.append("+"); + break; + case TileTabsLayoutBackgroundTypeEnum::TRANSPARENT_BG: + transparentText.append("+"); + break; + } + } + + /* + * Need to block signals as 'setValue' causes signal change + */ + QSignalBlocker spinBlocker(m_stackingOrderSpinBox); + m_stackingOrderSpinBox->setValue(firstOrderValue); + if (haveMultipleStackingOrderValuesFlag) { + m_stackingOrderSpinBox->setSuffix("+"); + } + else { + m_stackingOrderSpinBox->setSuffix(""); + } + + m_TileTabsLayoutBackgroundTypeEnumComboBox->getComboBox()->setItemText(opaqueIndex, + opaqueText); + m_TileTabsLayoutBackgroundTypeEnumComboBox->getComboBox()->setItemText(transparentIndex, + transparentText); +} + +/** + * Called when background type is changed + */ +void +AnnotationBackgroundTypeWidget::tileTabsLayoutBackgroundTypeEnumComboBoxItemActivated() +{ + if ( ! m_annotationBrowserTabs.empty()) { + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + std::vector annotations(m_annotationBrowserTabs.begin(), + m_annotationBrowserTabs.end()); + + const TileTabsLayoutBackgroundTypeEnum::Enum newValue = m_TileTabsLayoutBackgroundTypeEnumComboBox->getSelectedItem(); + undoCommand->setBrowserTabBackground(newValue, annotations); + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + + AString errorMessage; + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + + /* + * Need to update since the annotations may have had different backgrounds + * and need to remove the "+" symbol + */ + std::vector annCopy = m_annotationBrowserTabs; + updateContent(annCopy); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); + } +} + +/** + * Called when stacking order value changed for one browser tab annotation + * + * @param value + * New stacking order value + */ +void +AnnotationBackgroundTypeWidget::stackingOrderValueChanged(int value) +{ + if ( ! m_annotationBrowserTabs.empty()) { + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + std::vector annotations(m_annotationBrowserTabs.begin(), + m_annotationBrowserTabs.end()); + + undoCommand->setModeStackingOrderBrowserTab(value, annotations); + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + + AString errorMessage; + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + + /* + * Need to update since the annotations may have had different stacking orders + * and need to remove the "+" symbol + */ + std::vector annCopy = m_annotationBrowserTabs; + updateContent(annCopy); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); + } +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationBackgroundTypeWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationBackgroundTypeWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationBackgroundTypeWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationBackgroundTypeWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,90 @@ +#ifndef __ANNOTATION_BACKGROUND_TYPE_WIDGET_H__ +#define __ANNOTATION_BACKGROUND_TYPE_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "AnnotationWidgetParentEnum.h" +#include "UserInputModeEnum.h" + +class QSpinBox; + +namespace caret { + + class AnnotationBrowserTab; + class EnumComboBoxTemplate; + + class AnnotationBackgroundTypeWidget: public QWidget { + + Q_OBJECT + + public: + enum WhichCoordinate { + COORDINATE_ONE, + COORDINATE_TWO + }; + + AnnotationBackgroundTypeWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, + const int32_t browserWindowIndex, + QWidget* parent = 0); + + virtual ~AnnotationBackgroundTypeWidget(); + + + // ADD_NEW_METHODS_HERE + + void updateContent(std::vector& selectedBrowserTabAnnotations); + + private slots: + void tileTabsLayoutBackgroundTypeEnumComboBoxItemActivated(); + + void stackingOrderValueChanged(int value); + + private: + AnnotationBackgroundTypeWidget(const AnnotationBackgroundTypeWidget&); + + AnnotationBackgroundTypeWidget& operator=(const AnnotationBackgroundTypeWidget&); + + // ADD_NEW_MEMBERS_HERE + + const UserInputModeEnum::Enum m_userInputMode; + + const AnnotationWidgetParentEnum::Enum m_parentWidgetType; + + const int32_t m_browserWindowIndex; + + std::vector m_annotationBrowserTabs; + + EnumComboBoxTemplate* m_TileTabsLayoutBackgroundTypeEnumComboBox; + + QSpinBox* m_stackingOrderSpinBox; + + }; + +#ifdef __ANNOTATION_BACKGROUND_TYPE_WIDGET_DECLARE__ + // +#endif // __ANNOTATION_BACKGROUND_TYPE_WIDGET_DECLARE__ + +} // namespace +#endif //__ANNOTATION_BACKGROUND_TYPE_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationBoundsWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationBoundsWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationBoundsWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationBoundsWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,364 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_COORDINATE_WIDGET_DECLARE__ +#include "AnnotationBoundsWidget.h" +#undef __ANNOTATION_COORDINATE_WIDGET_DECLARE__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "AnnotationBrowserTab.h" +#include "AnnotationManager.h" +#include "AnnotationRedoUndoCommand.h" +#include "Brain.h" +#include "CaretAssert.h" +#include "EventBrowserWindowContent.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventUserInterfaceUpdate.h" +#include "EventManager.h" +#include "GuiManager.h" +#include "MathFunctions.h" +#include "WuQFactory.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationBoundsWidget + * \brief Widget for editing annotation coordinate + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param + * @param whichCoordinate + * Which coordinate, one (or only), or two + * @param browserWindowIndex + * Index of browser window + * @param parent + * Parent widget + */ +AnnotationBoundsWidget::AnnotationBoundsWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, + const int32_t browserWindowIndex, + QWidget* parent) +: QWidget(parent), +m_userInputMode(userInputMode), +m_parentWidgetType(parentWidgetType), +m_browserWindowIndex(browserWindowIndex) +{ + QString colonString; + switch (m_parentWidgetType) { + case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: + colonString = ":"; + break; + case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: + CaretAssert(0); + break; + } + + QLabel* boundsLabel = new QLabel("Bounds"); + + QLabel* xMinCoordLabel = new QLabel("Min X" + colonString); + m_xMinCoordSpinBox = createSpinBox(); + QObject::connect(m_xMinCoordSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &AnnotationBoundsWidget::xMinValueChanged); + + QLabel* xMaxCoordLabel = new QLabel("Max X" + colonString); + m_xMaxCoordSpinBox = createSpinBox(); + QObject::connect(m_xMaxCoordSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &AnnotationBoundsWidget::xMaxValueChanged); + + QLabel* yMinCoordLabel = new QLabel("Min Y" + colonString); + m_yMinCoordSpinBox = createSpinBox(); + QObject::connect(m_yMinCoordSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &AnnotationBoundsWidget::yMinValueChanged); + + QLabel* yMaxCoordLabel = new QLabel("Max Y" + colonString); + m_yMaxCoordSpinBox = createSpinBox(); + QObject::connect(m_yMaxCoordSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &AnnotationBoundsWidget::yMaxValueChanged); + + QGridLayout* coordinateLayout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(coordinateLayout, 2, 0); + int32_t row(0); + coordinateLayout->addWidget(boundsLabel, row, 0, 1, 4, Qt::AlignHCenter); + row++; + coordinateLayout->addWidget(xMinCoordLabel, row, 0); + coordinateLayout->addWidget(m_xMinCoordSpinBox, row, 1); + row++; + coordinateLayout->addWidget(xMaxCoordLabel, row, 0); + coordinateLayout->addWidget(m_xMaxCoordSpinBox, row, 1); + row++; + coordinateLayout->addWidget(yMinCoordLabel, row, 0); + coordinateLayout->addWidget(m_yMinCoordSpinBox, row, 1); + row++; + coordinateLayout->addWidget(yMaxCoordLabel, row, 0); + coordinateLayout->addWidget(m_yMaxCoordSpinBox, row, 1); + + setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); +} + +QDoubleSpinBox* +AnnotationBoundsWidget::createSpinBox() +{ + QDoubleSpinBox* spinBox = new QDoubleSpinBox(); + spinBox->setMinimum(-100.0); + spinBox->setMaximum( 200.0); + spinBox->setSingleStep(0.1); + spinBox->setDecimals(1); + spinBox->setToolTip("0.0% => Bottom of window\n" + "100.0% => Top of window"); + spinBox->setSuffix("%"); + + return spinBox; +} + + +/** + * Destructor. + */ +AnnotationBoundsWidget::~AnnotationBoundsWidget() +{ +} + +/** + * Update with the given annotation coordinate. + * + * @param annotationBrowserTabs. + */ +void +AnnotationBoundsWidget::updateContent(std::vector& annotationBrowserTabs) +{ + bool haveMultipleMinXValuesFlag(false); + bool haveMultipleMaxXValuesFlag(false); + bool haveMultipleMinYValuesFlag(false); + bool haveMultipleMaxYValuesFlag(false); + + float valueMinX(0.0); + float valueMaxX(0.0); + float valueMinY(0.0); + float valueMaxY(0.0); + + bool firstFlag(true); + + m_annotationBrowserTabs = annotationBrowserTabs; + for (auto abt : annotationBrowserTabs) { + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + abt->getBounds2D(minX, maxX, minY, maxY); + + if (firstFlag) { + valueMinX = minX; + valueMaxX = maxX; + valueMinY = minY; + valueMaxY = maxY; + firstFlag = false; + } + else { + if (minX != valueMinX) { + haveMultipleMinXValuesFlag = true; + } + if (maxX != valueMaxX) { + haveMultipleMaxXValuesFlag = true; + } + if (minY != valueMinY) { + haveMultipleMinYValuesFlag = true; + } + if (maxY != valueMaxY) { + haveMultipleMaxYValuesFlag = true; + } + + valueMinX = std::min(valueMinX, minX); + valueMaxX = std::max(valueMaxX, maxX); + valueMinY = std::min(valueMinY, minY); + valueMaxY = std::max(valueMaxY, maxY); + } + } + + /* + * If multiple annotations are selected, + * a plus sign (+) is appeneded to the value + */ + + m_xMinCoordSpinBox->blockSignals(true); + m_xMinCoordSpinBox->setValue(valueMinX); + if (haveMultipleMinXValuesFlag) { + m_xMinCoordSpinBox->setSuffix("%+"); + } + else { + m_xMinCoordSpinBox->setSuffix("%"); + } + m_xMinCoordSpinBox->blockSignals(false); + + m_xMaxCoordSpinBox->blockSignals(true); + m_xMaxCoordSpinBox->setValue(valueMaxX); + if (haveMultipleMaxXValuesFlag) { + m_xMaxCoordSpinBox->setSuffix("%+"); + } + else { + m_xMaxCoordSpinBox->setSuffix("%"); + } + m_xMaxCoordSpinBox->blockSignals(false); + + m_yMinCoordSpinBox->blockSignals(true); + m_yMinCoordSpinBox->setValue(valueMinY); + if (haveMultipleMinYValuesFlag) { + m_yMinCoordSpinBox->setSuffix("%+"); + } + else { + m_yMinCoordSpinBox->setSuffix("%"); + } + m_yMinCoordSpinBox->blockSignals(false); + + m_yMaxCoordSpinBox->blockSignals(true); + m_yMaxCoordSpinBox->setValue(valueMaxY); + if (haveMultipleMaxYValuesFlag) { + m_yMaxCoordSpinBox->setSuffix("%+"); + } + else { + m_yMaxCoordSpinBox->setSuffix("%"); + } + m_yMaxCoordSpinBox->blockSignals(false); + + setEnabled( ! m_annotationBrowserTabs.empty()); +} + +/** + * Gets called when x-min value is changed. + * + * @param value + */ +void +AnnotationBoundsWidget::xMinValueChanged(const double value) +{ + valueChangedHelper(m_xMinCoordSpinBox, + value); + m_xMinCoordSpinBox->setFocus(); +} + +/** + * Gets called when x-max value is changed. + * + * @param value + */ +void +AnnotationBoundsWidget::xMaxValueChanged(const double value) +{ + valueChangedHelper(m_xMaxCoordSpinBox, + value); + m_xMaxCoordSpinBox->setFocus(); +} + +/** + * Gets called when y-min value is changed. + * + * @param value + */ +void +AnnotationBoundsWidget::yMinValueChanged(const double value) +{ + valueChangedHelper(m_yMinCoordSpinBox, + value); + m_yMinCoordSpinBox->setFocus(); +} + +/** + * Gets called when y-max value is changed. + * + * @param value + */ +void +AnnotationBoundsWidget::yMaxValueChanged(const double value) +{ + valueChangedHelper(m_yMaxCoordSpinBox, + value); + m_yMaxCoordSpinBox->setFocus(); +} + +/** + * Helper for bounds value changed + * + * @param spinBox + * Spin box that had value changed + * @param value + * New value + */ +void +AnnotationBoundsWidget::valueChangedHelper(QDoubleSpinBox* spinBox, + float value) +{ + if ( ! m_annotationBrowserTabs.empty()) { + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + std::vector annotations(m_annotationBrowserTabs.begin(), + m_annotationBrowserTabs.end()); + + if (spinBox == m_xMinCoordSpinBox) { + undoCommand->setBoundsMinX2D(value, annotations); + } + else if (spinBox == m_xMaxCoordSpinBox) { + undoCommand->setBoundsMaxX2D(value, annotations); + } + else if (spinBox == m_yMinCoordSpinBox) { + undoCommand->setBoundsMinY2D(value, annotations); + } + else if (spinBox == m_yMaxCoordSpinBox) { + undoCommand->setBoundsMaxY2D(value, annotations); + } + else { + delete undoCommand; + CaretAssert(0); + return; + } + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + + AString errorMessage; + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + + /* + * Update as annotation might adjust/limit coordinates + */ + updateContent(m_annotationBrowserTabs); + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + } +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationBoundsWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationBoundsWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationBoundsWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationBoundsWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,96 @@ +#ifndef __ANNOTATION_BOUNDS_WIDGET_H__ +#define __ANNOTATION_BOUNDS_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "AnnotationWidgetParentEnum.h" +#include "UserInputModeEnum.h" + +class QDoubleSpinBox; + +namespace caret { + + class AnnotationBrowserTab; + + class AnnotationBoundsWidget : public QWidget { + + Q_OBJECT + + public: + AnnotationBoundsWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, + const int32_t browserWindowIndex, + QWidget* parent = 0); + + virtual ~AnnotationBoundsWidget(); + + + // ADD_NEW_METHODS_HERE + + void updateContent(std::vector& annotationBrowserTabs); + + private slots: + void xMinValueChanged(const double value); + + void xMaxValueChanged(const double value); + + void yMinValueChanged(const double value); + + void yMaxValueChanged(const double value); + + private: + AnnotationBoundsWidget(const AnnotationBoundsWidget&); + + AnnotationBoundsWidget& operator=(const AnnotationBoundsWidget&); + + QDoubleSpinBox* createSpinBox(); + + void valueChangedHelper(QDoubleSpinBox* spinBox, + float value); + + // ADD_NEW_MEMBERS_HERE + + const UserInputModeEnum::Enum m_userInputMode; + + const AnnotationWidgetParentEnum::Enum m_parentWidgetType; + + const int32_t m_browserWindowIndex; + + QDoubleSpinBox* m_xMinCoordSpinBox; + + QDoubleSpinBox* m_xMaxCoordSpinBox; + + QDoubleSpinBox* m_yMinCoordSpinBox; + + QDoubleSpinBox* m_yMaxCoordSpinBox; + + std::vector m_annotationBrowserTabs; + }; + +#ifdef __ANNOTATION_BOUNDS_WIDGET_DECLARE__ + // +#endif // __ANNOTATION_BOUNDS_WIDGET_DECLARE__ + +} // namespace +#endif //__ANNOTATION_BOUNDS_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationChangeCoordinateDialog.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationChangeCoordinateDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationChangeCoordinateDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationChangeCoordinateDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -30,6 +30,7 @@ #include "Annotation.h" #include "AnnotationCoordinate.h" +#include "AnnotationCoordinateInformation.h" #include "AnnotationCoordinateSelectionWidget.h" #include "CaretAssert.h" #include "WuQMessageBox.h" diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationChangeCoordinateDialog.h connectome-workbench-1.5.0/src/GuiQt/AnnotationChangeCoordinateDialog.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationChangeCoordinateDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationChangeCoordinateDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include "UserInputModeAnnotations.h" #include "WuQDialogModal.h" diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationColorWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationColorWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationColorWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationColorWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -32,8 +32,8 @@ #include #include "AnnotationManager.h" -#include "AnnotationOneDimensionalShape.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" +#include "AnnotationOneCoordinateShape.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" #include "BrainOpenGL.h" @@ -308,7 +308,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -507,6 +508,8 @@ case AnnotationTypeEnum::BOX: allowBothColorsNoneFlag = false; break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -517,6 +520,11 @@ case AnnotationTypeEnum::OVAL: allowBothColorsNoneFlag = false; break; + case AnnotationTypeEnum::POLY_LINE: + allowBothColorsNoneFlag = false; + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: break; } @@ -577,7 +585,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -678,7 +687,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateCenterXYWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateCenterXYWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateCenterXYWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateCenterXYWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,384 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_DECLARE__ +#include "AnnotationCoordinateCenterXYWidget.h" +#undef __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_DECLARE__ + +#include +#include +#include + +#include +#include +#include +#include + +#include "AnnotationColorBar.h" +#include "AnnotationManager.h" +#include "AnnotationCoordinate.h" +#include "AnnotationMultiCoordinateShape.h" +#include "AnnotationRedoUndoCommand.h" +#include "AnnotationOneCoordinateShape.h" +#include "AnnotationTwoCoordinateShape.h" +#include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlaySet.h" +#include "EventBrowserWindowContent.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventOverlaySettingsEditorDialogRequest.h" +#include "GuiManager.h" +#include "MathFunctions.h" +#include "ModelChartTwo.h" +#include "WuQFactory.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationCoordinateCenterXYWidget + * \brief Widget for editing annotation coordinate + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param + * @param whichCoordinate + * Which coordinate, one (or only), or two + * @param browserWindowIndex + * Index of browser window + * @param parent + * Parent widget + */ +AnnotationCoordinateCenterXYWidget::AnnotationCoordinateCenterXYWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, + const WhichCoordinate whichCoordinate, + const int32_t browserWindowIndex, + QWidget* parent) +: QWidget(parent), +m_userInputMode(userInputMode), +m_parentWidgetType(parentWidgetType), +m_whichCoordinate(whichCoordinate), +m_browserWindowIndex(browserWindowIndex) +{ + + QString colonString; + switch (m_parentWidgetType) { + case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: + colonString = ":"; + break; + case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: + CaretAssert(0); + break; + } + + QLabel* centerLabel = new QLabel("Center"); + const int digitsRightOfDecimal = 1; + QLabel* xCoordLabel = new QLabel(" X" + colonString); + m_xCoordSpinBox = new QDoubleSpinBox(); + m_xCoordSpinBox->setMinimum(-100.0); + m_xCoordSpinBox->setMaximum( 200.0); + m_xCoordSpinBox->setSingleStep(0.1); + m_xCoordSpinBox->setDecimals(digitsRightOfDecimal); + QObject::connect(m_xCoordSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &AnnotationCoordinateCenterXYWidget::xValueChanged); + WuQtUtilities::setWordWrappedToolTip(m_xCoordSpinBox, + "X-coordinate of annotation\n" + " STEREOTAXIC: Stereotaxic X-Coordinate\n" + " TAB and WINDOW X-Range: [0.0%, 100.0%]\n" + " 0.0% => Left side of tab/window\n" + " 100.0% => Right side of tab/window\n"); + + QLabel* yCoordLabel = new QLabel(" Y" + colonString); + m_yCoordSpinBox = new QDoubleSpinBox(); + m_yCoordSpinBox->setMinimum(-100.0); + m_yCoordSpinBox->setMaximum( 200.0); + m_yCoordSpinBox->setSingleStep(0.1); + m_yCoordSpinBox->setDecimals(digitsRightOfDecimal); + QObject::connect(m_yCoordSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &AnnotationCoordinateCenterXYWidget::yValueChanged); + WuQtUtilities::setWordWrappedToolTip(m_yCoordSpinBox, + "Y-coordinate of annotation\n" + " STEREOTAXIC: Stereotaxic Y-Coordinate\n" + " TAB and WINDOW Y-Range: [0.0%, 100.0%]\n" + " 0.0% => Bottom of tab/window\n" + " 100.0% => Top of tab/window\n"); + + + const float spinBoxMaximumWidth = 80.0f; + m_xCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); + m_yCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); + + QGridLayout* layout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); + int32_t row(0); + layout->addWidget(centerLabel, row, 0, 1, 2, Qt::AlignHCenter); + row++; + layout->addWidget(xCoordLabel, row, 0); + layout->addWidget(m_xCoordSpinBox, row, 1); + row++; + layout->addWidget(yCoordLabel, row, 0); + layout->addWidget(m_yCoordSpinBox, row, 1); + + setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +AnnotationCoordinateCenterXYWidget::~AnnotationCoordinateCenterXYWidget() +{ +} + +/** + * @return coordinates selected for editing + */ +std::vector +AnnotationCoordinateCenterXYWidget::getSelectedCoordinates() +{ + std::vector coordsOut; + + /* + * Do not allow editing of a multi-coord annotation's coordinates + */ + for (auto ann : m_annotations) { + if (ann->castToMultiCoordinateShape() != NULL) { + return coordsOut; + } + } + + for (auto ann : m_annotations) { + AnnotationTwoCoordinateShape* twoCoordShape = ann->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* oneCoordShape = ann->castToOneCoordinateShape(); + + AnnotationCoordinate* ac(NULL); + switch (m_whichCoordinate) { + case COORDINATE_ONE: + if (twoCoordShape != NULL) { + ac = twoCoordShape->getStartCoordinate(); + } + else if (oneCoordShape != NULL) { + ac = oneCoordShape->getCoordinate(); + } + break; + case COORDINATE_TWO: + if (twoCoordShape != NULL) { + ac = twoCoordShape->getEndCoordinate(); + } + break; + } + + if (ac != NULL) { + coordsOut.push_back(ac); + } + } + + return coordsOut; +} + +/** + * Update with the given annotation coordinate. + * + * @param coordinate. + */ +void +AnnotationCoordinateCenterXYWidget::updateContent(std::vector& selectedAnnotations) +{ + m_annotations = selectedAnnotations; + + bool firstFlag(true); + bool haveMultipleXValuesFlag(false); + bool haveMultipleYValuesFlag(false); + float xValue(0.0); + float yValue(0.0); + + std::vector selectedCoords = getSelectedCoordinates(); + if (! selectedCoords.empty()) { + for (auto ac : selectedCoords) { + CaretAssert(ac); + + float xyz[3]; + ac->getXYZ(xyz); + const float x = xyz[0]; + const float y = xyz[1]; + + if (firstFlag) { + xValue = x; + yValue = y; + firstFlag = false; + } + else { + if (x != xValue) { + haveMultipleXValuesFlag = true; + } + if (y != yValue) { + haveMultipleYValuesFlag = true; + } + + xValue = std::min(xValue, x); + yValue = std::min(yValue, y); + } + } + + setEnabled(true); + } + else { + setEnabled(false); + } + + QSignalBlocker xBlocker(m_xCoordSpinBox); + m_xCoordSpinBox->setValue(xValue); + if (haveMultipleXValuesFlag) { + m_xCoordSpinBox->setSuffix("%+"); + } + else { + m_xCoordSpinBox->setSuffix("%"); + } + + QSignalBlocker yBlocker(m_yCoordSpinBox); + m_yCoordSpinBox->setValue(yValue); + if (haveMultipleYValuesFlag) { + m_yCoordSpinBox->setSuffix("%+"); + } + else { + m_yCoordSpinBox->setSuffix("%"); + } +} + +/** + * Called when the X-value is changed + * + * @param value + * New value + */ +void +AnnotationCoordinateCenterXYWidget::xValueChanged(double value) +{ + processValueChanged(m_xCoordSpinBox, + value); +} + +/** + * Called when the Y-value is changed + * + * @param value + * New value + */ +void +AnnotationCoordinateCenterXYWidget::yValueChanged(double value) +{ + processValueChanged(m_yCoordSpinBox, + value); +} + +/** + * Called when an X- or Y-Value is changed + * + * @param spinBox + * The spin box that was changed + * @param value + * The new value + */ +void +AnnotationCoordinateCenterXYWidget::processValueChanged(QDoubleSpinBox* spinBox, + const double value) +{ + if (! m_annotations.empty()) { + for (auto ann : m_annotations) { + AnnotationTwoCoordinateShape* twoCoordShape = dynamic_cast(ann); + AnnotationOneCoordinateShape* oneCoordShape = dynamic_cast(ann); + AnnotationMultiCoordinateShape* multiCoordShape = ann->castToMultiCoordinateShape(); + + if (multiCoordShape != NULL) { + /* + * Skip + */ + continue; + } + + AnnotationCoordinate* ac(NULL); + switch (m_whichCoordinate) { + case COORDINATE_ONE: + if (twoCoordShape != NULL) { + ac = twoCoordShape->getStartCoordinate(); + } + else if (oneCoordShape != NULL) { + ac = oneCoordShape->getCoordinate(); + } + break; + case COORDINATE_TWO: + if (twoCoordShape != NULL) { + ac = twoCoordShape->getEndCoordinate(); + } + break; + } + + AnnotationCoordinate coordinateCopy(*ac); + float xyz[3]; + coordinateCopy.getXYZ(xyz); + + if (spinBox == m_xCoordSpinBox) { + xyz[0] = value; + } + else if (spinBox == m_yCoordSpinBox) { + xyz[1] = value; + } + coordinateCopy.setXYZ(xyz); + + std::vector annForCommand { ann }; + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + switch (m_whichCoordinate) { + case COORDINATE_ONE: + undoCommand->setModeCoordinateOne(coordinateCopy, + annForCommand); + break; + case COORDINATE_TWO: + undoCommand->setModeCoordinateTwo(coordinateCopy, + annForCommand); + break; + } + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + + AString errorMessage; + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + } + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); + } +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateCenterXYWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateCenterXYWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateCenterXYWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateCenterXYWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,99 @@ +#ifndef __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_H__ +#define __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "AnnotationCoordinateSpaceEnum.h" +#include "AnnotationWidgetParentEnum.h" +#include "UserInputModeEnum.h" + +class QDoubleSpinBox; + +namespace caret { + + class Annotation; + class AnnotationCoordinate; + + class AnnotationCoordinateCenterXYWidget: public QWidget { + + Q_OBJECT + + public: + enum WhichCoordinate { + COORDINATE_ONE, + COORDINATE_TWO + }; + + AnnotationCoordinateCenterXYWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, + const WhichCoordinate whichCoordinate, + const int32_t browserWindowIndex, + QWidget* parent = 0); + + virtual ~AnnotationCoordinateCenterXYWidget(); + + + // ADD_NEW_METHODS_HERE + + void updateContent(std::vector& selectedAnnotations); + + private slots: + void xValueChanged(double value); + + void yValueChanged(double value); + + private: + AnnotationCoordinateCenterXYWidget(const AnnotationCoordinateCenterXYWidget&); + + AnnotationCoordinateCenterXYWidget& operator=(const AnnotationCoordinateCenterXYWidget&); + + void processValueChanged(QDoubleSpinBox* spinBox, + const double value); + + std::vector getSelectedCoordinates(); + + // ADD_NEW_MEMBERS_HERE + + const UserInputModeEnum::Enum m_userInputMode; + + const AnnotationWidgetParentEnum::Enum m_parentWidgetType; + + const WhichCoordinate m_whichCoordinate; + + const int32_t m_browserWindowIndex; + + QDoubleSpinBox* m_xCoordSpinBox; + + QDoubleSpinBox* m_yCoordSpinBox; + + std::vector m_annotations; + + }; + +#ifdef __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_DECLARE__ + // +#endif // __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_DECLARE__ + +} // namespace +#endif //__ANNOTATION_COORDINATE_CENTER_XY_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateInformation.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateInformation.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateInformation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateInformation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,8 +25,9 @@ #include "Annotation.h" #include "AnnotationCoordinate.h" -#include "AnnotationOneDimensionalShape.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" +#include "AnnotationMultiCoordinateShape.h" +#include "AnnotationOneCoordinateShape.h" #include "BrainOpenGLWidget.h" #include "BrainOpenGLViewportContent.h" #include "BrowserTabContent.h" @@ -221,6 +222,124 @@ } } +/** + * Get the valid coordinate spaces for all annotation coordinate information. + * The space must be valid for all coordinates, AND, if tab or window + * space the tab or window indices must also be the same. + * + * @param coordInfoMulti + * All coordinate information. + * @param spacesOut + * Output containing spaces valid for both. + */ +void +AnnotationCoordinateInformation::getValidCoordinateSpaces(const std::vector>& coordInfoMulti, + std::vector& spacesOut) +{ + spacesOut.clear(); + + const int32_t numCoordInfo(static_cast(coordInfoMulti.size())); + if (numCoordInfo <= 0) { + return; + } + CaretAssertVectorIndex(coordInfoMulti, 0); + const auto firstCoordInfo(coordInfoMulti[0].get()); + + std::vector allSpaces; + AnnotationCoordinateSpaceEnum::getAllEnums(allSpaces); + + for (const auto space : allSpaces) { + switch (space) { + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::CHART: + case AnnotationCoordinateSpaceEnum::SPACER: + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + case AnnotationCoordinateSpaceEnum::SURFACE: + case AnnotationCoordinateSpaceEnum::TAB: + case AnnotationCoordinateSpaceEnum::WINDOW: + /* + * See if space for first coord info is valid + */ + if (firstCoordInfo->isCoordinateSpaceValid(space)) { + bool addItFlag = true; + + for(int32_t i = 1; i < numCoordInfo; i++) { + CaretAssertVectorIndex(coordInfoMulti, i); + const auto coordInfo(coordInfoMulti[i].get()); + + /* + * See if same space is valid for second coord info + */ + addItFlag = coordInfo->isCoordinateSpaceValid(space); + + switch (space) { + case AnnotationCoordinateSpaceEnum::CHART: + /* + * Both coord info's must be in the SAME TAB + */ + if (firstCoordInfo->m_tabSpaceInfo.m_index != coordInfo->m_tabSpaceInfo.m_index) { + addItFlag = false; + } + break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (firstCoordInfo->m_spacerTabSpaceInfo.m_spacerTabIndex != coordInfo->m_spacerTabSpaceInfo.m_spacerTabIndex) { + addItFlag = false; + } + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + /* + * Both coord info's must be in the SAME TAB + */ + if (firstCoordInfo->m_tabSpaceInfo.m_index != coordInfo->m_tabSpaceInfo.m_index) { + addItFlag = false; + } + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + /* + * Both coord info's must be on same surface and + * in the SAME TAB + */ + if ((firstCoordInfo->m_tabSpaceInfo.m_index != coordInfo->m_tabSpaceInfo.m_index) + || (firstCoordInfo->m_surfaceSpaceInfo.m_numberOfNodes != coordInfo->m_surfaceSpaceInfo.m_numberOfNodes) + || (firstCoordInfo->m_surfaceSpaceInfo.m_structure != coordInfo->m_surfaceSpaceInfo.m_structure)) { + addItFlag = false; + } + break; + case AnnotationCoordinateSpaceEnum::TAB: + if (firstCoordInfo->m_tabSpaceInfo.m_index != coordInfo->m_tabSpaceInfo.m_index) { + addItFlag = false; + } + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + //if (coordInfoOne->m_windowIndex != coordInfoTwo->m_windowIndex) { + // addItFlag = false; + //} + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + if (firstCoordInfo->m_windowSpaceInfo.m_index != coordInfo->m_windowSpaceInfo.m_index) { + addItFlag = false; + } + break; + } + + /* + * If an annotation does not match space of first, + * then the space is not valid + */ + if ( ! addItFlag) { + break; + } + } + + if (addItFlag) { + spacesOut.push_back(space); + } + } + break; + } + } +} /** * Get the different types of coordinates at the given mouse location. @@ -340,6 +459,7 @@ coordInfoOut.m_tabSpaceInfo.m_index = tabContent->getTabNumber(); coordInfoOut.m_tabSpaceInfo.m_width = tabViewport[2]; coordInfoOut.m_tabSpaceInfo.m_height = tabViewport[3]; + coordInfoOut.m_tabSpaceInfo.m_validFlag = true; } } @@ -405,6 +525,7 @@ coordInfoOut.m_windowSpaceInfo.m_index = viewportContent->getWindowIndex(); coordInfoOut.m_windowSpaceInfo.m_width = windowViewport[2]; coordInfoOut.m_windowSpaceInfo.m_height = windowViewport[3]; + coordInfoOut.m_windowSpaceInfo.m_validFlag = true; /* * Normalize window coordinates (width and height range [0, 100] @@ -430,27 +551,33 @@ AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(Annotation* annotation, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const AnnotationCoordinateInformation* coordInfoOne, - const AnnotationCoordinateInformation* coordInfoTwo) + const AnnotationCoordinateInformation* coordInfoTwo, + const std::vector>& coordInfoMulti) { CaretAssert(annotation); bool validCoordinateFlag = false; - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); - - if (oneDimAnn != NULL) { - validCoordinateFlag = setOneDimAnnotationCoordinatesForSpace(oneDimAnn, + AnnotationTwoCoordinateShape* twoCoordAnn = annotation->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* oneCoordAnn = annotation->castToOneCoordinateShape(); + AnnotationMultiCoordinateShape* multiCoordAnn = annotation->castToMultiCoordinateShape(); + if (twoCoordAnn != NULL) { + validCoordinateFlag = setOneDimAnnotationCoordinatesForSpace(twoCoordAnn, coordinateSpace, coordInfoOne, coordInfoTwo); } - else if (twoDimAnn != NULL) { - validCoordinateFlag = setTwoDimAnnotationCoordinatesForSpace(twoDimAnn, + else if (oneCoordAnn != NULL) { + validCoordinateFlag = setTwoDimAnnotationCoordinatesForSpace(oneCoordAnn, coordinateSpace, coordInfoOne, coordInfoTwo); } + else if (multiCoordAnn != NULL) { + validCoordinateFlag = setMultiDimAnnotationCoordinatesForSpace(multiCoordAnn, + coordinateSpace, + coordInfoMulti); + } return validCoordinateFlag; } @@ -468,7 +595,7 @@ * Data for the second coordinate. */ bool -AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(AnnotationOneDimensionalShape* annotation, +AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(AnnotationTwoCoordinateShape* annotation, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo) @@ -667,7 +794,7 @@ * Data for the optional second coordinate. */ bool -AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(AnnotationTwoDimensionalShape* annotation, +AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(AnnotationOneCoordinateShape* annotation, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* optionalCoordInfoTwo) @@ -880,3 +1007,309 @@ return validCoordinateFlag; } +/** + * Set the coordinates for the multi-coordinate annotation. + * + * @param annotation + * The annotation. + * @param coordinateSpace + * The coordinate space. + * @param coordInfoMulti + * Info for the coordinates + */ +bool +AnnotationCoordinateInformation::setMultiDimAnnotationCoordinatesForSpace(AnnotationMultiCoordinateShape* annotation, + const AnnotationCoordinateSpaceEnum::Enum space, + const std::vector>& coordInfoMulti) +{ + if (coordInfoMulti.empty()) { + return false; + } + + CaretAssert(annotation); + +// AnnotationCoordinate* startCoordinate = annotation->getStartCoordinate(); +// CaretAssert(startCoordinate); +// AnnotationCoordinate* endCoordinate = annotation->getEndCoordinate(); +// CaretAssert(endCoordinate); +// + /* + * Set annotation parameters using first coord info + */ + bool validSpaceFlag(false); + CaretAssertVectorIndex(coordInfoMulti, 0); + const auto& firstCoordInfo = coordInfoMulti[0]; + switch (space) { + case AnnotationCoordinateSpaceEnum::CHART: + if (firstCoordInfo->m_chartSpaceInfo.m_validFlag) { + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::CHART); + validSpaceFlag = true; + } + break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (firstCoordInfo->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { + annotation->setSpacerTabIndex(firstCoordInfo->m_spacerTabSpaceInfo.m_spacerTabIndex); + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SPACER); + validSpaceFlag = true; + } + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + if (firstCoordInfo->m_modelSpaceInfo.m_validFlag) { + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); + validSpaceFlag = true; + } + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + if (firstCoordInfo->m_surfaceSpaceInfo.m_validFlag) { + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); + validSpaceFlag = true; + } + break; + case AnnotationCoordinateSpaceEnum::TAB: + if (firstCoordInfo->m_tabSpaceInfo.m_validFlag) { + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); + annotation->setTabIndex(firstCoordInfo->m_tabSpaceInfo.m_index); + validSpaceFlag = true; + } + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + if (firstCoordInfo->m_windowSpaceInfo.m_index >= 0) { + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); + annotation->setWindowIndex(firstCoordInfo->m_windowSpaceInfo.m_index); + validSpaceFlag = true; + } + break; + } + + if ( ! validSpaceFlag) { + return false; + } + + /* + * Set coordinates + */ + std::vector> coordinates; + + for (auto& coordInfo : coordInfoMulti) { + AnnotationCoordinate* ac(new AnnotationCoordinate(AnnotationAttributesDefaultTypeEnum::USER)); + switch (space) { + case AnnotationCoordinateSpaceEnum::CHART: + if (coordInfo->m_chartSpaceInfo.m_validFlag) { + ac->setXYZ(coordInfo->m_chartSpaceInfo.m_xyz); + } + break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (coordInfo->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { + ac->setXYZ(coordInfo->m_spacerTabSpaceInfo.m_xyz); + } + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + if (coordInfo->m_modelSpaceInfo.m_validFlag) { + ac->setXYZ(coordInfo->m_modelSpaceInfo.m_xyz); + } + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + if (coordInfo->m_surfaceSpaceInfo.m_validFlag) { + const float surfaceOffsetLength = ac->getSurfaceOffsetLength(); + const AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = ac->getSurfaceOffsetVectorType(); + ac->setSurfaceSpace(coordInfo->m_surfaceSpaceInfo.m_structure, + coordInfo->m_surfaceSpaceInfo.m_numberOfNodes, + coordInfo->m_surfaceSpaceInfo.m_nodeIndex, + surfaceOffsetLength, + surfaceOffsetVector); + } + break; + case AnnotationCoordinateSpaceEnum::TAB: + if (coordInfo->m_tabSpaceInfo.m_validFlag) { + ac->setXYZ(coordInfo->m_tabSpaceInfo.m_xyz); + } + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + CaretAssert(0); + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + if (coordInfo->m_windowSpaceInfo.m_index >= 0) { + ac->setXYZ(coordInfo->m_windowSpaceInfo.m_xyz); + } + break; + } + + std::unique_ptr ptr(ac); + coordinates.push_back(std::move(ptr)); + } + + annotation->replaceAllCoordinates(coordinates); + + return (annotation->getNumberOfCoordinates() > 0); + +// switch (space) { +// case AnnotationCoordinateSpaceEnum::CHART: +// if (coordInfoOne->m_chartSpaceInfo.m_validFlag) { +// startCoordinate->setXYZ(coordInfoOne->m_chartSpaceInfo.m_xyz); +// annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::CHART); +// +// validCoordinateFlag = true; +// +// if (coordInfoTwo != NULL) { +// if (coordInfoTwo->m_chartSpaceInfo.m_validFlag) { +// if (endCoordinate != NULL) { +// endCoordinate->setXYZ(coordInfoTwo->m_chartSpaceInfo.m_xyz); +// } +// } +// } +// } +// break; +// case AnnotationCoordinateSpaceEnum::SPACER: +// if (coordInfoOne->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { +// startCoordinate->setXYZ(coordInfoOne->m_spacerTabSpaceInfo.m_xyz); +// annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SPACER); +// annotation->setSpacerTabIndex(coordInfoOne->m_spacerTabSpaceInfo.m_spacerTabIndex); +// +// validCoordinateFlag = true; +// +// +// if (coordInfoTwo != NULL) { +// if (coordInfoTwo->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { +// if (endCoordinate != NULL) { +// endCoordinate->setXYZ(coordInfoTwo->m_spacerTabSpaceInfo.m_xyz); +// } +// } +// } +// else if (endCoordinate != NULL) { +// double xyz[3] = { +// coordInfoOne->m_spacerTabSpaceInfo.m_xyz[0], +// coordInfoOne->m_spacerTabSpaceInfo.m_xyz[1], +// coordInfoOne->m_spacerTabSpaceInfo.m_xyz[2] +// }; +// if (xyz[1] > 50.0) { +// xyz[1] -= 25.0; +// endCoordinate->setXYZ(xyz); +// } +// else { +// xyz[1] += 25.0; +// endCoordinate->setXYZ(coordInfoOne->m_spacerTabSpaceInfo.m_xyz); +// startCoordinate->setXYZ(xyz); +// } +// } +// } +// break; +// case AnnotationCoordinateSpaceEnum::STEREOTAXIC: +// if (coordInfoOne->m_modelSpaceInfo.m_validFlag) { +// startCoordinate->setXYZ(coordInfoOne->m_modelSpaceInfo.m_xyz); +// annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); +// +// validCoordinateFlag = true; +// +// if (coordInfoTwo != NULL) { +// if (coordInfoTwo->m_modelSpaceInfo.m_validFlag) { +// if (endCoordinate != NULL) { +// endCoordinate->setXYZ(coordInfoTwo->m_modelSpaceInfo.m_xyz); +// } +// } +// } +// } +// break; +// case AnnotationCoordinateSpaceEnum::SURFACE: +// if (coordInfoOne->m_surfaceSpaceInfo.m_validFlag) { +// const float surfaceOffsetLength = startCoordinate->getSurfaceOffsetLength(); +// const AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = startCoordinate->getSurfaceOffsetVectorType(); +// startCoordinate->setSurfaceSpace(coordInfoOne->m_surfaceSpaceInfo.m_structure, +// coordInfoOne->m_surfaceSpaceInfo.m_numberOfNodes, +// coordInfoOne->m_surfaceSpaceInfo.m_nodeIndex, +// surfaceOffsetLength, +// surfaceOffsetVector); +// annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); +// +// validCoordinateFlag = true; +// +// if (coordInfoTwo != NULL) { +// if (coordInfoTwo->m_surfaceSpaceInfo.m_validFlag) { +// if (endCoordinate != NULL) { +// endCoordinate->setSurfaceSpace(coordInfoTwo->m_surfaceSpaceInfo.m_structure, +// coordInfoTwo->m_surfaceSpaceInfo.m_numberOfNodes, +// coordInfoTwo->m_surfaceSpaceInfo.m_nodeIndex, +// surfaceOffsetLength, +// surfaceOffsetVector); +// } +// } +// } +// } +// break; +// case AnnotationCoordinateSpaceEnum::TAB: +// if (coordInfoOne->m_tabSpaceInfo.m_index >= 0) { +// startCoordinate->setXYZ(coordInfoOne->m_tabSpaceInfo.m_xyz); +// annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); +// annotation->setTabIndex(coordInfoOne->m_tabSpaceInfo.m_index); +// +// validCoordinateFlag = true; +// +// +// if (coordInfoTwo != NULL) { +// if (coordInfoTwo->m_tabSpaceInfo.m_index >= 0) { +// if (endCoordinate != NULL) { +// endCoordinate->setXYZ(coordInfoTwo->m_tabSpaceInfo.m_xyz); +// } +// } +// } +// else if (endCoordinate != NULL) { +// double xyz[3] = { +// coordInfoOne->m_tabSpaceInfo.m_xyz[0], +// coordInfoOne->m_tabSpaceInfo.m_xyz[1], +// coordInfoOne->m_tabSpaceInfo.m_xyz[2] +// }; +// if (xyz[1] > 50.0) { +// xyz[1] -= 25.0; +// endCoordinate->setXYZ(xyz); +// } +// else { +// xyz[1] += 25.0; +// endCoordinate->setXYZ(coordInfoOne->m_tabSpaceInfo.m_xyz); +// startCoordinate->setXYZ(xyz); +// } +// } +// } +// break; +// case AnnotationCoordinateSpaceEnum::VIEWPORT: +// CaretAssert(0); +// break; +// case AnnotationCoordinateSpaceEnum::WINDOW: +// if (coordInfoOne->m_windowSpaceInfo.m_index >= 0) { +// startCoordinate->setXYZ(coordInfoOne->m_windowSpaceInfo.m_xyz); +// annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); +// annotation->setWindowIndex(coordInfoOne->m_windowSpaceInfo.m_index); +// +// validCoordinateFlag = true; +// +// if (coordInfoTwo != NULL) { +// if (coordInfoTwo->m_windowSpaceInfo.m_index >= 0) { +// if (endCoordinate != NULL) { +// endCoordinate->setXYZ(coordInfoTwo->m_windowSpaceInfo.m_xyz); +// } +// } +// } +// else if (endCoordinate != NULL) { +// double xyz[3] = { +// coordInfoOne->m_windowSpaceInfo.m_xyz[0], +// coordInfoOne->m_windowSpaceInfo.m_xyz[1], +// coordInfoOne->m_windowSpaceInfo.m_xyz[2] +// }; +// if (xyz[1] > 50.0) { +// xyz[1] -= 25.0; +// endCoordinate->setXYZ(xyz); +// } +// else { +// xyz[1] += 25.0; +// endCoordinate->setXYZ(coordInfoOne->m_windowSpaceInfo.m_xyz); +// startCoordinate->setXYZ(xyz); +// } +// } +// } +// break; +// } +// +// return validCoordinateFlag; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateInformation.h connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateInformation.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateInformation.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateInformation.h 2021-02-16 19:46:47.000000000 +0000 @@ -31,8 +31,9 @@ namespace caret { class Annotation; - class AnnotationOneDimensionalShape; - class AnnotationTwoDimensionalShape; + class AnnotationTwoCoordinateShape; + class AnnotationOneCoordinateShape; + class AnnotationMultiCoordinateShape; class BrainOpenGLWidget; class BrainOpenGLViewportContent; class MouseEvent; @@ -48,10 +49,12 @@ void reset(); + static void getValidCoordinateSpaces(const std::vector>& coordInfoMulti, + std::vector& spacesOut); + static void getValidCoordinateSpaces(const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo, std::vector& spacesOut); - static void createCoordinateInformationFromXY(const MouseEvent& mouseEvent, AnnotationCoordinateInformation& coordInfoOut); @@ -69,7 +72,8 @@ static bool setAnnotationCoordinatesForSpace(Annotation* annotation, const AnnotationCoordinateSpaceEnum::Enum space, const AnnotationCoordinateInformation* coordInfoOne, - const AnnotationCoordinateInformation* coordInfoTwo); + const AnnotationCoordinateInformation* coordInfoTwo, + const std::vector>& coordInfoMulti); class SpaceInfo { public: @@ -131,15 +135,19 @@ AnnotationCoordinateInformation& operator=(const AnnotationCoordinateInformation&); - static bool setOneDimAnnotationCoordinatesForSpace(AnnotationOneDimensionalShape* annotation, + static bool setOneDimAnnotationCoordinatesForSpace(AnnotationTwoCoordinateShape* annotation, const AnnotationCoordinateSpaceEnum::Enum space, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo); - static bool setTwoDimAnnotationCoordinatesForSpace(AnnotationTwoDimensionalShape* annotation, + static bool setTwoDimAnnotationCoordinatesForSpace(AnnotationOneCoordinateShape* annotation, const AnnotationCoordinateSpaceEnum::Enum space, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo); + + static bool setMultiDimAnnotationCoordinatesForSpace(AnnotationMultiCoordinateShape* annotation, + const AnnotationCoordinateSpaceEnum::Enum space, + const std::vector>& coordInfoMulti); // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -35,10 +35,11 @@ #include "AnnotationCoordinateInformation.h" #include "AnnotationImage.h" #include "AnnotationManager.h" -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationMultiCoordinateShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationPercentSizeText.h" #include "AnnotationRedoUndoCommand.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" #include "Brain.h" #include "BrainStructure.h" #include "CaretAssert.h" @@ -95,6 +96,9 @@ enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; + case AnnotationTypeEnum::BROWSER_TAB: + enableTabSpaceFlag = false; + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -112,6 +116,13 @@ enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; + case AnnotationTypeEnum::POLY_LINE: + enableChartSpaceFlag = true; + enableModelSpaceFlag = true; + enableSurfaceSpaceFlag = true; + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: enableChartSpaceFlag = true; enableModelSpaceFlag = true; @@ -532,8 +543,8 @@ CaretPointer redoAnnotation(annotation->clone()); - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation.getPointer()); - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(redoAnnotation.getPointer()); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(redoAnnotation.getPointer()); + AnnotationOneCoordinateShape* twoDimShape = dynamic_cast(redoAnnotation.getPointer()); AnnotationCoordinate* coordinate = NULL; @@ -752,9 +763,32 @@ command->setDescription("Change Coordinate"); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + switch (m_annotationType) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssert(0); + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } + AString errorMessage; - if ( ! annotationManager->applyCommand(command, - errorMessage)) { + if ( ! annotationManager->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + command, + errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } @@ -787,25 +821,35 @@ return false; } - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); + AnnotationTwoCoordinateShape* oneDimAnn = annotation->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* twoDimAnn = annotation->castToOneCoordinateShape(); + AnnotationMultiCoordinateShape* multiCoordAnn = annotation->castToMultiCoordinateShape(); bool validCoordsFlag = false; if (oneDimAnn != NULL) { validCoordsFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(oneDimAnn, - coordinateSpace, - &m_coordInfo, - m_optionalSecondCoordInfo); + coordinateSpace, + &m_coordInfo, + m_optionalSecondCoordInfo, + m_optionalMultiCoordInfo); } else if (twoDimAnn != NULL) { validCoordsFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(twoDimAnn, - coordinateSpace, - &m_coordInfo, - m_optionalSecondCoordInfo); + coordinateSpace, + &m_coordInfo, + m_optionalSecondCoordInfo, + m_optionalMultiCoordInfo); + } + else if (multiCoordAnn != NULL) { + validCoordsFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(multiCoordAnn, + coordinateSpace, + &m_coordInfo, + m_optionalSecondCoordInfo, + m_optionalMultiCoordInfo); } else { - const QString msg("PROGRAM ERROR: Annotation is neither one nor two dimensional"); + const QString msg("PROGRAM ERROR: Annotation is of unknown base type"); CaretAssertMessage(0, msg); CaretLogSevere(msg); errorMessageOut = msg; @@ -813,7 +857,7 @@ } if ( ! validCoordsFlag) { - errorMessageOut = "Failed to set coordinates for annotatin."; + errorMessageOut = "Failed to set coordinates for annotation."; } updateAnnotationDisplayProperties(annotation); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateSelectionWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateSelectionWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateSelectionWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateSelectionWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,9 +21,10 @@ */ /*LICENSE_END*/ - +#include #include +//#include "AnnotationCoordinateInformation.h" #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationTypeEnum.h" #include "UserInputModeAnnotations.h" @@ -36,8 +37,6 @@ class Annotation; class AnnotationCoordinate; class AnnotationImage; - class AnnotationOneDimensionalShape; - class AnnotationTwoDimensionalShape; class AnnotationCoordinateSelectionWidget : public QWidget { @@ -74,10 +73,6 @@ QRadioButton* createRadioButtonForSpace(const AnnotationCoordinateSpaceEnum::Enum space); -// void setOneDimAnnotationCoordinates(AnnotationOneDimensionalShape* annotation); -// -// void setTwoDimAnnotationCoordinates(AnnotationTwoDimensionalShape* annotation); - QButtonGroup* m_spaceButtonGroup; const AnnotationTypeEnum::Enum m_annotationType; @@ -86,6 +81,8 @@ const AnnotationCoordinateInformation* m_optionalSecondCoordInfo; + std::vector> m_optionalMultiCoordInfo; + static const QString s_SPACE_PROPERTY_NAME; diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -121,7 +121,7 @@ } AString indicesString; - for (const auto s : tabIndices) { + for (const auto& s : tabIndices) { if (indicesString.isEmpty()) { indicesString.append(":"); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinatesWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinatesWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinatesWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinatesWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,859 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_COORDINATES_WIDGET_DECLARE__ +#include "AnnotationCoordinatesWidget.h" +#undef __ANNOTATION_COORDINATES_WIDGET_DECLARE__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AnnotationColorBar.h" +#include "AnnotationManager.h" +#include "AnnotationCoordinate.h" +#include "AnnotationTwoCoordinateShape.h" +#include "AnnotationRedoUndoCommand.h" +#include "AnnotationScaleBar.h" +#include "AnnotationOneCoordinateShape.h" +#include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlaySet.h" +#include "EnumComboBoxTemplate.h" +#include "EventBrowserWindowContent.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventOverlaySettingsEditorDialogRequest.h" +#include "GuiManager.h" +#include "MathFunctions.h" +#include "ModelChartTwo.h" +#include "StructureEnumComboBox.h" +#include "WuQFactory.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::AnnotationCoordinatesWidget + * \brief Widget for editing annotation coordinate + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param userInputMode + * The user input mode + * @param parentWidgetType + * Type of parent widget + * @param browserWindowIndex + * Index of browser window + * @param parent + * Parent widget + */ +AnnotationCoordinatesWidget::AnnotationCoordinatesWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, + const int32_t browserWindowIndex, + QWidget* parent) +: QWidget(parent), +m_userInputMode(userInputMode), +m_parentWidgetType(parentWidgetType), +m_browserWindowIndex(browserWindowIndex) +{ + + m_annotation = NULL; + + QString colonString; + switch (m_parentWidgetType) { + case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: + colonString = ":"; + break; + case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: + CaretAssert(0); + break; + } + QLabel* xCoordLabel = new QLabel(" X" + colonString); + QLabel* yCoordLabel = new QLabel(" Y" + colonString); + m_zCoordLabel = new QLabel(" Z" + colonString); + QLabel* surfaceVertexLabel = new QLabel("Vertex:"); + + createCoordinateWidgets(0); + createCoordinateWidgets(1); + + m_surfaceWidget = new QWidget(); + QGridLayout* surfaceLayout = new QGridLayout(m_surfaceWidget); + WuQtUtilities::setLayoutSpacingAndMargins(surfaceLayout, 2, 0); + surfaceLayout->addWidget(surfaceVertexLabel, 0, 0); + surfaceLayout->addWidget(m_surfaceStructureComboBox->getWidget(), 0, 1); + surfaceLayout->addWidget(m_surfaceNodeIndexSpinBox[0], 0, 2); + surfaceLayout->addWidget(m_surfaceNodeIndexSpinBox[1], 1, 2); + if (m_surfaceOffsetVectorTypeComboBox != NULL) { + surfaceLayout->addWidget(m_surfaceOffsetVectorTypeComboBox->getWidget(), 0, 3); + } + surfaceLayout->addWidget(m_surfaceOffsetLengthSpinBox[0], 0, 4); + surfaceLayout->addWidget(m_surfaceOffsetLengthSpinBox[1], 1, 4); + surfaceLayout->setColumnStretch(surfaceLayout->columnCount(), 100); + + m_coordinateWidget = new QWidget(); + QGridLayout* coordinateLayout = new QGridLayout(m_coordinateWidget); + WuQtUtilities::setLayoutSpacingAndMargins(coordinateLayout, 2, 0); + coordinateLayout->addWidget(xCoordLabel, 0, 0); + coordinateLayout->addWidget(m_xCoordSpinBox[0], 0, 1); + coordinateLayout->addWidget(m_xCoordSpinBox[1], 1, 1); + coordinateLayout->addWidget(yCoordLabel, 0, 2); + coordinateLayout->addWidget(m_yCoordSpinBox[0], 0, 3); + coordinateLayout->addWidget(m_yCoordSpinBox[1], 1, 3); + coordinateLayout->addWidget(m_zCoordLabel, 0, 4); + coordinateLayout->addWidget(m_zCoordSpinBox[0], 0, 5); + coordinateLayout->addWidget(m_zCoordSpinBox[1], 1, 5); + coordinateLayout->setColumnStretch(coordinateLayout->columnCount(), 100); + + m_stackedLayout = new QStackedLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(m_stackedLayout, 2, 2); + m_stackedLayout->addWidget(m_surfaceWidget); + m_stackedLayout->addWidget(m_coordinateWidget); + + setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +AnnotationCoordinatesWidget::~AnnotationCoordinatesWidget() +{ +} + +/** + * Create the widgets for the given coordinate index + * @param coordinateIndex + * Index of the coordinate + */ +void +AnnotationCoordinatesWidget::createCoordinateWidgets(const int32_t coordinateIndex) +{ + if (coordinateIndex == 0) { + m_surfaceStructureComboBox = new StructureEnumComboBox(this); + m_surfaceStructureComboBox->listOnlyValidStructures(); + m_surfaceStructureComboBox->getWidget()->setToolTip("Select surface structure"); + QObject::connect(m_surfaceStructureComboBox, SIGNAL(structureSelected(const StructureEnum::Enum)), + this, SLOT(valueChangedCoordinateOne())); + } + + m_surfaceNodeIndexSpinBox[coordinateIndex] = new QSpinBox(); + m_surfaceNodeIndexSpinBox[coordinateIndex]->setRange(0, 1000000); + m_surfaceNodeIndexSpinBox[coordinateIndex]->setSingleStep(1); + m_surfaceNodeIndexSpinBox[coordinateIndex]->setToolTip("Select surface vertex"); + switch (coordinateIndex) { + case 0: + QObject::connect(m_surfaceNodeIndexSpinBox[coordinateIndex], SIGNAL(valueChanged(int)), + this, SLOT(valueChangedCoordinateOne())); + break; + case 1: + QObject::connect(m_surfaceNodeIndexSpinBox[coordinateIndex], SIGNAL(valueChanged(int)), + this, SLOT(valueChangedCoordinateTwo())); + break; + default: + CaretAssert(0); + break; + } + + m_surfaceOffsetLengthSpinBox[coordinateIndex] = new QDoubleSpinBox(); + m_surfaceOffsetLengthSpinBox[coordinateIndex]->setRange(0.0, 999.0); + m_surfaceOffsetLengthSpinBox[coordinateIndex]->setSingleStep(0.1); + m_surfaceOffsetLengthSpinBox[coordinateIndex]->setToolTip("Offset of annotation from surface vertex"); + switch (coordinateIndex) { + case 0: + QObject::connect(m_surfaceOffsetLengthSpinBox[coordinateIndex], SIGNAL(valueChanged(double)), + this, SLOT(surfaceOffsetLengthValueOneChanged(double))); + break; + case 1: + QObject::connect(m_surfaceOffsetLengthSpinBox[coordinateIndex], SIGNAL(valueChanged(double)), + this, SLOT(surfaceOffsetLengthValueTwoChanged(double))); + break; + default: + CaretAssert(0); + break; + } + + m_xCoordSpinBox[coordinateIndex] = createCoordinateSpinBox(coordinateIndex, "X", 0); + + m_yCoordSpinBox[coordinateIndex] = createCoordinateSpinBox(coordinateIndex, "Y", 1); + + m_zCoordSpinBox[coordinateIndex] = createCoordinateSpinBox(coordinateIndex, "Z", 2); + + const float spinBoxMaximumWidth = 80.0f; + m_xCoordSpinBox[coordinateIndex]->setMaximumWidth(spinBoxMaximumWidth); + m_yCoordSpinBox[coordinateIndex]->setMaximumWidth(spinBoxMaximumWidth); + m_zCoordSpinBox[coordinateIndex]->setMaximumWidth(spinBoxMaximumWidth); + + if (coordinateIndex == 0) { + m_surfaceOffsetVectorTypeComboBox = new EnumComboBoxTemplate(this); + m_surfaceOffsetVectorTypeComboBox->setup(); + QObject::connect(m_surfaceOffsetVectorTypeComboBox, SIGNAL(itemActivated()), + this, SLOT(surfaceOffsetVectorTypeChanged())); + m_surfaceOffsetVectorTypeComboBox->getWidget()->setFixedWidth(55); + m_surfaceOffsetVectorTypeComboBox->getWidget()->setToolTip("Vector for surface offset:\n" + " C - Centroid thru Vertex, Faces Viewer\n" + " N - Vertex Normal, Faces Viewer\n" + " T - Tangent, Rotates with Surface"); + } +} + +/** + * @return Coordinate for given indx + * @param const int32_t coordinateIndex + * Index of coordinate + */ +AnnotationCoordinate* +AnnotationCoordinatesWidget::getCoordinate(const int32_t coordinateIndex) +{ + AnnotationCoordinate* ac = NULL; + + + if (m_annotation != NULL) { + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(m_annotation); + AnnotationOneCoordinateShape* twoDimShape = dynamic_cast(m_annotation); + + switch (coordinateIndex) { + case 0: + if (oneDimShape != NULL) { + ac = oneDimShape->getStartCoordinate(); + } + else if (twoDimShape != NULL) { + ac = twoDimShape->getCoordinate(); + } + break; + case 1: + if (oneDimShape != NULL) { + ac = oneDimShape->getEndCoordinate(); + } + break; + default: + CaretAssert(0); + break; + } + } + + return ac; +} + + +/** + * Update with the given annotation coordinate. + * + * @param coordinate. + */ +void +AnnotationCoordinatesWidget::updateContent(Annotation* annotation) +{ + m_annotation = NULL; + if (annotation != NULL) { + if (annotation->testProperty(Annotation::Property::COORDINATE)) { + m_annotation = annotation; + } + } + + m_surfaceStructureComboBox->listOnlyValidStructures(); + + updateCoordinate(0, + getCoordinate(0)); + updateCoordinate(1, + getCoordinate(1)); + + bool surfaceSpaceFlag(false); + bool viewportSpaceFlag(false); + if (m_annotation != NULL) { + switch (m_annotation->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + surfaceSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + viewportSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + if (viewportSpaceFlag) { + setEnabled(false); + } + else { + setEnabled(true); + } + } + else { + setEnabled(false); + } + + if (surfaceSpaceFlag) { + m_stackedLayout->setCurrentWidget(m_surfaceWidget); + } + else { + m_stackedLayout->setCurrentWidget(m_coordinateWidget); + } +} + +/** + * Update coordinate for given index + * + * @param coordinateIndex + * Index of coordinate + * @param coordinate + * The coordinate + */ +void +AnnotationCoordinatesWidget::updateCoordinate(const int32_t coordinateIndex, + const AnnotationCoordinate* coordinate) +{ + bool surfaceFlag(false); + bool xyzFlag(false); + + double xMin = 0.0; + double xMax = 0.0; + double yMin = 0.0; + double yMax = 0.0; + double zMin = 0.0; + double zMax = 0.0; + double xStep = 0.1; + double yStep = 0.1; + double zStep = 0.1; + QString xSuffix; + QString ySuffix; + QString zSuffix; + float xyz[3] { 0.0, 0.0, 0.0 }; + + int32_t digitsRightOfDecimalX = 2; + int32_t digitsRightOfDecimalY = 2; + int32_t digitsRightOfDecimalZ = 2; + + QString zLabelText(" Z:"); + + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfNodes = -1; + int32_t surfaceNodeIndex = -1; + float surfaceOffsetLength = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); + AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + + if (coordinate != NULL) { + xyzFlag = true; + coordinate->getXYZ(xyz); + + const double percentageMinimum = 0.0; + const double percentageMaximum = 100.0; + const double zDepthMinimum = -1000.0; + const double zDepthMaximum = 1000.0; + const double coordinateMinimum = -std::numeric_limits::max(); + const double coordinateMaximum = std::numeric_limits::max(); + switch (m_annotation->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + { + xMin = coordinateMinimum; + xMax = coordinateMaximum; + yMin = coordinateMinimum; + yMax = coordinateMaximum; + zMin = coordinateMinimum; + zMax = coordinateMaximum; + digitsRightOfDecimalX = 3; + digitsRightOfDecimalY = 3; + xStep = 1.0; + yStep = 1.0; + + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); + CaretAssert(bbw); + BrowserTabContent* browserTabContent = bbw->getBrowserTabContent(); + if (browserTabContent != NULL) { + ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); + const int32_t tabIndex = browserTabContent->getTabNumber(); + if (modelChartTwo != NULL) { + ChartTwoOverlaySet* chartOverlaySet = NULL; + switch (modelChartTwo->getSelectedChartTwoDataType(tabIndex)) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + + if (chartOverlaySet != NULL) { + float xAxisMin = std::numeric_limits::max(); + float xAxisMax = -std::numeric_limits::max(); + float yAxisMin = std::numeric_limits::max(); + float yAxisMax = -std::numeric_limits::max(); + + std::vector axes; + chartOverlaySet->getDisplayedChartAxes(axes); + const int32_t numAxes = static_cast(axes.size()); + for (int32_t i = 0; i < numAxes; i++) { + float rangeMin(0), rangeMax(0); + axes[i]->getDataRange(rangeMin, rangeMax); + + if (rangeMax > rangeMin) { + const ChartAxisLocationEnum::Enum axisLocation = axes[i]->getAxisLocation(); + switch (axisLocation) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + xAxisMin = std::min(xAxisMin, rangeMin); + xAxisMax = std::max(xAxisMax, rangeMax); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + yAxisMin = std::min(yAxisMin, rangeMin); + yAxisMax = std::max(yAxisMax, rangeMax); + break; + } + } + } + + if (xAxisMax > xAxisMin) { + const float range(xAxisMax - xAxisMin); + int32_t digits = 6 - static_cast(std::round(std::log10(range))); + digitsRightOfDecimalX = MathFunctions::clamp(digits, 3, 6); + xStep = range * 0.001f; + } + if (yAxisMax > yAxisMin) { + const float range(yAxisMax - yAxisMin); + int32_t digits = 6 - static_cast(std::round(std::log10(range))); + digitsRightOfDecimalY = MathFunctions::clamp(digits, 3, 6); + yStep = range * 0.001f; + } + } + } + } + } + break; + case AnnotationCoordinateSpaceEnum::SPACER: + xMin = percentageMinimum; + xMax = percentageMaximum; + yMin = percentageMinimum; + yMax = percentageMaximum; + zMin = zDepthMinimum; + zMax = zDepthMaximum; + xSuffix = "%"; + ySuffix = "%"; + zSuffix = "%"; + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + xMin = coordinateMinimum; + xMax = coordinateMaximum; + yMin = coordinateMinimum; + yMax = coordinateMaximum; + zMin = coordinateMinimum; + zMax = coordinateMaximum; + xStep = 1.0; + yStep = 1.0; + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + surfaceFlag = true; + xyzFlag = false; + break; + case AnnotationCoordinateSpaceEnum::TAB: + xMin = percentageMinimum; + xMax = percentageMaximum; + yMin = percentageMinimum; + yMax = percentageMaximum; + zMin = 0.0; + zMax = zDepthMaximum; + xSuffix = "%"; + ySuffix = "%"; + digitsRightOfDecimalZ = 0; + zStep = 1.0; + zLabelText = " O:"; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + /* + * Cannot move + */ + xMin = xyz[0]; + xMax = xyz[0]; + yMin = xyz[1]; + yMax = xyz[1]; + zMin = xyz[2]; + zMax = xyz[2]; + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + xMin = percentageMinimum; + xMax = percentageMaximum; + yMin = percentageMinimum; + yMax = percentageMaximum; + zMin = 0.0; + zMax = zDepthMaximum; + xSuffix = "%"; + ySuffix = "%"; + digitsRightOfDecimalZ = 0; + zStep = 1.0; + zLabelText = " O:"; + break; + } + } + + m_xCoordSpinBox[coordinateIndex]->blockSignals(true); + m_xCoordSpinBox[coordinateIndex]->setRange(xMin, + xMax); + m_xCoordSpinBox[coordinateIndex]->setSingleStep(xStep); + m_xCoordSpinBox[coordinateIndex]->setSuffix(xSuffix); + m_xCoordSpinBox[coordinateIndex]->setValue(xyz[0]); + m_xCoordSpinBox[coordinateIndex]->setDecimals(digitsRightOfDecimalX); + m_xCoordSpinBox[coordinateIndex]->blockSignals(false); + m_xCoordSpinBox[coordinateIndex]->setEnabled(xyzFlag); + + m_yCoordSpinBox[coordinateIndex]->blockSignals(true); + m_yCoordSpinBox[coordinateIndex]->setRange(yMin, + yMax); + m_yCoordSpinBox[coordinateIndex]->setSingleStep(yStep); + m_yCoordSpinBox[coordinateIndex]->setSuffix(ySuffix); + m_yCoordSpinBox[coordinateIndex]->setValue(xyz[1]); + m_yCoordSpinBox[coordinateIndex]->setDecimals(digitsRightOfDecimalY); + m_yCoordSpinBox[coordinateIndex]->blockSignals(false); + m_yCoordSpinBox[coordinateIndex]->setEnabled(xyzFlag); + + m_zCoordSpinBox[coordinateIndex]->blockSignals(true); + m_zCoordSpinBox[coordinateIndex]->setRange(zMin, + zMax); + m_zCoordSpinBox[coordinateIndex]->setSingleStep(zStep); + m_zCoordSpinBox[coordinateIndex]->setSuffix(zSuffix); + m_zCoordSpinBox[coordinateIndex]->setValue(xyz[2]); + m_zCoordSpinBox[coordinateIndex]->setDecimals(digitsRightOfDecimalZ); + m_zCoordSpinBox[coordinateIndex]->blockSignals(false); + m_zCoordSpinBox[coordinateIndex]->setEnabled(xyzFlag); + if (coordinateIndex == 0) { + m_zCoordLabel->setText(zLabelText); + } + + if (m_annotation != NULL) { + if (m_annotation->getType() == AnnotationTypeEnum::BROWSER_TAB) { + m_zCoordSpinBox[coordinateIndex]->setEnabled(false); + } + } + + if (surfaceFlag) { + coordinate->getSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex, + surfaceOffsetLength, + surfaceOffsetVector); + if (coordinateIndex == 0) { + m_surfaceStructureComboBox->setSelectedStructure(structure); + m_surfaceOffsetVectorTypeComboBox->setSelectedItem(surfaceOffsetVector); + } + + m_surfaceNodeIndexSpinBox[coordinateIndex]->blockSignals(true); + m_surfaceNodeIndexSpinBox[coordinateIndex]->setValue(surfaceNodeIndex); + m_surfaceNodeIndexSpinBox[coordinateIndex]->blockSignals(false); + + m_surfaceOffsetLengthSpinBox[coordinateIndex]->blockSignals(true); + m_surfaceOffsetLengthSpinBox[coordinateIndex]->setValue(surfaceOffsetLength); + m_surfaceOffsetLengthSpinBox[coordinateIndex]->blockSignals(false); + + AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(surfaceOffsetVector); + AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(surfaceOffsetLength); + } + if (coordinateIndex == 0) { + m_surfaceStructureComboBox->getWidget()->setEnabled(surfaceFlag); + m_surfaceOffsetVectorTypeComboBox->getWidget()->setEnabled(surfaceFlag); + } + m_surfaceNodeIndexSpinBox[coordinateIndex]->setEnabled(surfaceFlag); + m_surfaceOffsetLengthSpinBox[coordinateIndex]->setEnabled(surfaceFlag); +} + +/** + * Called when surface offset one value changed. + * + * @param value + * New value. + */ +void +AnnotationCoordinatesWidget::surfaceOffsetLengthValueOneChanged(double value) +{ + AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(value); + valueChangedCoordinateOne(); +} + +/** + * Called when surface offset Two value changed. + * + * @param value + * New value. + */ +void +AnnotationCoordinatesWidget::surfaceOffsetLengthValueTwoChanged(double value) +{ + AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(value); + valueChangedCoordinateTwo(); +} + +/** + * Called when surface offset vector type is changed. + */ +void +AnnotationCoordinatesWidget::surfaceOffsetVectorTypeChanged() +{ + CaretAssert(m_surfaceOffsetVectorTypeComboBox); + AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(m_surfaceOffsetVectorTypeComboBox->getSelectedItem()); + valueChangedCoordinateOne(); + valueChangedCoordinateTwo(); +} + +/** + * Gets called when a coordinate value is changed. + */ +void +AnnotationCoordinatesWidget::valueChangedCoordinateOne() +{ + valueChangedCoordinate(0); +} +/** + * Gets called when a coordinate value is changed. + */ +void +AnnotationCoordinatesWidget::valueChangedCoordinateTwo() +{ + valueChangedCoordinate(1); +} + +/** + * Gets called when a coordinate value is changed. + * @param coordinateIndex + * Index of coordinate + */ +void +AnnotationCoordinatesWidget::valueChangedCoordinate(const int32_t coordinateIndex) +{ + const AnnotationCoordinate* coordinate = getCoordinate(coordinateIndex); + if ((m_annotation != NULL) + && (coordinate != NULL)) { + bool surfaceFlag = false; + switch (m_annotation->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + surfaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + + switch (m_parentWidgetType) { + case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: + { + AnnotationCoordinate coordinateCopy(*coordinate); + + if (surfaceFlag) { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfNodes = -1; + int32_t surfaceNodeIndex = -1; + float surfaceOffsetLength = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); + AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + coordinate->getSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex, + surfaceOffsetLength, + surfaceOffsetVector); + structure = m_surfaceStructureComboBox->getSelectedStructure(); + surfaceOffsetVector = m_surfaceOffsetVectorTypeComboBox->getSelectedItem(); + surfaceNodeIndex = m_surfaceNodeIndexSpinBox[coordinateIndex]->value(); + surfaceOffsetLength = m_surfaceOffsetLengthSpinBox[coordinateIndex]->value(); + + coordinateCopy.setSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex, + surfaceOffsetLength, + surfaceOffsetVector); + } + else { + float xyz[3] = { + (float)m_xCoordSpinBox[coordinateIndex]->value(), + (float)m_yCoordSpinBox[coordinateIndex]->value(), + (float)m_zCoordSpinBox[coordinateIndex]->value() + }; + coordinateCopy.setXYZ(xyz); + } + + std::vector selectedAnnotations; + selectedAnnotations.push_back(m_annotation); + + bool updateUserInterfaceFlag = false; + for (std::vector::iterator annIter = selectedAnnotations.begin(); + annIter != selectedAnnotations.end(); + annIter++) { + Annotation* ann = *annIter; + if (ann->getType() == AnnotationTypeEnum::COLOR_BAR) { + AnnotationColorBar* colorBar = dynamic_cast(ann); + CaretAssert(colorBar); + colorBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); + updateUserInterfaceFlag = true; + } + else if (ann->getType() == AnnotationTypeEnum::SCALE_BAR) { + AnnotationScaleBar* scaleBar = ann->castToScaleBar(); + CaretAssert(scaleBar); + scaleBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); + updateUserInterfaceFlag = true; + } + } + + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + switch (coordinateIndex) { + case 0: + undoCommand->setModeCoordinateOne(coordinateCopy, + selectedAnnotations); + break; + case 1: + undoCommand->setModeCoordinateTwo(coordinateCopy, + selectedAnnotations); + break; + default: + CaretAssert(0); + break; + } + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + + AString errorMessage; + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); + if (updateUserInterfaceFlag) { + EventManager::get()->sendEvent(EventOverlaySettingsEditorDialogRequest(EventOverlaySettingsEditorDialogRequest::MODE_UPDATE_ALL, + m_browserWindowIndex, + NULL, + (CaretMappableDataFile*)NULL, + -1).getPointer()); + } + } + break; + case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: + CaretAssert(0); + break; + } + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } +} + +/** + * @return Spin box with axis character in tool tip + * @param coordinateIndex + * Index of coordinate + * @param axisCharacter + * Character for axis + * @param xyzIndex + * Index 0 (X), 1 (Y), 2(Z) + */ +QDoubleSpinBox* +AnnotationCoordinatesWidget::createCoordinateSpinBox(const int32_t coordinateIndex, + const QString& axisCharacter, + const int32_t xyzIndex) +{ + const int digitsRightOfDecimal = 1; + + QDoubleSpinBox* sb(NULL); + switch (coordinateIndex) { + case 0: + sb = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, + 100.0, + 0.1, + digitsRightOfDecimal, + this, + SLOT(valueChangedCoordinateOne())); + break; + case 1: + sb = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, + 100.0, + 0.1, + digitsRightOfDecimal, + this, + SLOT(valueChangedCoordinateTwo())); + break; + default: + break; + } + + AString tabWindowText; + AString coordOrderText; + switch (xyzIndex) { + case 0: + coordOrderText = (axisCharacter + "-Coordinate"); + tabWindowText = (" 0.0% => Left side of tab/window\n" + " 100.0% => Right side of tab/window"); + break; + case 1: + coordOrderText = (axisCharacter + "-Coordinate"); + tabWindowText = (" 0.0% => Bottom of tab/window\n" + " 100.0% => Top of tab/window"); + break; + case 2: + coordOrderText = "Order"; + tabWindowText = (" Increase to move behind\n" + " Decrease to move in front"); + break; + } + + CaretAssert(sb); + WuQtUtilities::setWordWrappedToolTip(sb, + " STEREOTAXIC: " + axisCharacter + "-Coordinate\n" + " TAB and WINDOW: " + coordOrderText + "\n" + + tabWindowText); + return sb; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinatesWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinatesWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinatesWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinatesWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,130 @@ +#ifndef __ANNOTATION_COORDINATES_WIDGET_H__ +#define __ANNOTATION_COORDINATES_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "AnnotationCoordinateSpaceEnum.h" +#include "AnnotationWidgetParentEnum.h" +#include "UserInputModeEnum.h" + +class QDoubleSpinBox; +class QLabel; +class QSpinBox; +class QStackedLayout; + +namespace caret { + + class Annotation; + class AnnotationCoordinate; + class EnumComboBoxTemplate; + class StructureEnumComboBox; + + class AnnotationCoordinatesWidget : public QWidget { + + Q_OBJECT + + public: + AnnotationCoordinatesWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, + const int32_t browserWindowIndex, + QWidget* parent = 0); + + virtual ~AnnotationCoordinatesWidget(); + + + // ADD_NEW_METHODS_HERE + + void updateContent(Annotation* annotation); + + private slots: + void valueChangedCoordinateOne(); + + void valueChangedCoordinateTwo(); + + void surfaceOffsetLengthValueOneChanged(double); + + void surfaceOffsetLengthValueTwoChanged(double); + + void surfaceOffsetVectorTypeChanged(); + + private: + AnnotationCoordinatesWidget(const AnnotationCoordinatesWidget&); + + AnnotationCoordinatesWidget& operator=(const AnnotationCoordinatesWidget&); + + AnnotationCoordinate* getCoordinate(const int32_t coordinateIndex); + + QDoubleSpinBox* createCoordinateSpinBox(const int32_t coordinateIndex, + const QString& axisCharacter, + const int32_t xyzIndex); + + void createCoordinateWidgets(const int32_t coordinateIndex); + + void updateCoordinate(const int32_t coordinateIndex, + const AnnotationCoordinate* coordinate); + + void valueChangedCoordinate(const int32_t coordinateIndex); + + // ADD_NEW_MEMBERS_HERE + + const UserInputModeEnum::Enum m_userInputMode; + + const AnnotationWidgetParentEnum::Enum m_parentWidgetType; + + const int32_t m_browserWindowIndex; + + QStackedLayout* m_stackedLayout; + + QWidget* m_surfaceWidget; + + QWidget* m_coordinateWidget; + + StructureEnumComboBox* m_surfaceStructureComboBox; + + QSpinBox* m_surfaceNodeIndexSpinBox[2]; + + EnumComboBoxTemplate* m_surfaceOffsetVectorTypeComboBox; + + QDoubleSpinBox* m_surfaceOffsetLengthSpinBox[2]; + + QDoubleSpinBox* m_xCoordSpinBox[2]; + + QDoubleSpinBox* m_yCoordSpinBox[2]; + + QLabel* m_zCoordLabel; + + QDoubleSpinBox* m_zCoordSpinBox[2]; + + Annotation* m_annotation; + + AString m_plusButtonToolTipText; + + }; + +#ifdef __ANNOTATION_COORDINATES_WIDGET_DECLARE__ + // +#endif // __ANNOTATION_COORDINATES_WIDGET_DECLARE__ + +} // namespace +#endif //__ANNOTATION_COORDINATES_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateWidget.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,695 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __ANNOTATION_COORDINATE_WIDGET_DECLARE__ -#include "AnnotationCoordinateWidget.h" -#undef __ANNOTATION_COORDINATE_WIDGET_DECLARE__ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "AnnotationColorBar.h" -#include "AnnotationManager.h" -#include "AnnotationCoordinate.h" -#include "AnnotationOneDimensionalShape.h" -#include "AnnotationRedoUndoCommand.h" -#include "AnnotationTwoDimensionalShape.h" -#include "Brain.h" -#include "BrainBrowserWindow.h" -#include "BrowserTabContent.h" -#include "CaretAssert.h" -#include "ChartTwoCartesianAxis.h" -#include "ChartTwoOverlaySet.h" -#include "EnumComboBoxTemplate.h" -#include "EventBrowserWindowContent.h" -#include "EventGraphicsUpdateAllWindows.h" -#include "EventManager.h" -#include "EventOverlaySettingsEditorDialogRequest.h" -#include "GuiManager.h" -#include "MathFunctions.h" -#include "ModelChartTwo.h" -#include "StructureEnumComboBox.h" -#include "WuQFactory.h" -#include "WuQMessageBox.h" -#include "WuQtUtilities.h" - -using namespace caret; - - - -/** - * \class caret::AnnotationCoordinateWidget - * \brief Widget for editing annotation coordinate - * \ingroup GuiQt - */ - -/** - * Constructor. - * - * @param - * @param whichCoordinate - * Which coordinate, one (or only), or two - * @param browserWindowIndex - * Index of browser window - * @param parent - * Parent widget - */ -AnnotationCoordinateWidget::AnnotationCoordinateWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, - const WhichCoordinate whichCoordinate, - const int32_t browserWindowIndex, - QWidget* parent) -: QWidget(parent), -m_parentWidgetType(parentWidgetType), -m_whichCoordinate(whichCoordinate), -m_browserWindowIndex(browserWindowIndex) -{ - - m_annotation = NULL; - - QString colonString; - switch (m_parentWidgetType) { - case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - colonString = ":"; - break; - case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: - CaretAssert(0); - break; - } - - QLabel* surfaceVertexLabel = new QLabel("Vertex:"); - m_surfaceStructureComboBox = new StructureEnumComboBox(this); - m_surfaceStructureComboBox->listOnlyValidStructures(); - m_surfaceStructureComboBox->getWidget()->setToolTip("Select surface structure"); - QObject::connect(m_surfaceStructureComboBox, SIGNAL(structureSelected(const StructureEnum::Enum)), - this, SLOT(valueChanged())); - - m_surfaceNodeIndexSpinBox = new QSpinBox(); - m_surfaceNodeIndexSpinBox->setRange(0, 1000000); - m_surfaceNodeIndexSpinBox->setSingleStep(1); - m_surfaceNodeIndexSpinBox->setToolTip("Select surface vertex"); - QObject::connect(m_surfaceNodeIndexSpinBox, SIGNAL(valueChanged(int)), - this, SLOT(valueChanged())); - - m_surfaceOffsetLengthSpinBox = new QDoubleSpinBox(); - m_surfaceOffsetLengthSpinBox->setRange(0.0, 999.0); - m_surfaceOffsetLengthSpinBox->setSingleStep(0.1); - m_surfaceOffsetLengthSpinBox->setToolTip("Offset of annotation from surface vertex"); - QObject::connect(m_surfaceOffsetLengthSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(surfaceOffsetLengthValueChanged(double))); - - const int digitsRightOfDecimal = 1; - QLabel* xCoordLabel = new QLabel(" X" + colonString); - m_xCoordSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 0.1, digitsRightOfDecimal, - this, SLOT(valueChanged())); - WuQtUtilities::setWordWrappedToolTip(m_xCoordSpinBox, - "X-coordinate of annotation\n" - " STEREOTAXIC: Stereotaxic X-Coordinate\n" - " TAB and WINDOW X-Range: [0.0%, 100.0%]\n" - " 0.0% => Left side of tab/window\n" - " 100.0% => Right side of tab/window\n"); - - QLabel* yCoordLabel = new QLabel(" Y" + colonString); - m_yCoordSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 0.1, digitsRightOfDecimal, - this, SLOT(valueChanged())); - WuQtUtilities::setWordWrappedToolTip(m_yCoordSpinBox, - "Y-coordinate of annotation\n" - " STEREOTAXIC: Stereotaxic Y-Coordinate\n" - " TAB and WINDOW Y-Range: [0.0%, 100.0%]\n" - " 0.0% => Bottom of tab/window\n" - " 100.0% => Top of tab/window\n"); - - QLabel* zCoordLabel = new QLabel(" Z" + colonString); - m_zCoordSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-100.0, 100.0, 0.1, digitsRightOfDecimal, - this, SLOT(valueChanged())); - WuQtUtilities::setWordWrappedToolTip(m_zCoordSpinBox, - "Z-coordinate of annotation\n" - " STEREOTAXIC: Stereotaxic Z-Coordinate\n" - " TAB and WINDOW DEPTH Range: [0.0%, 100.0%]\n" - " 0.0% => Toward's viewer\n" - " 100.0% => Away from viewer\n"); - - const float spinBoxMaximumWidth = 80.0f; - m_xCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); - m_yCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); - m_zCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); - - m_surfaceOffsetVectorTypeComboBox = NULL; - switch (m_whichCoordinate) { - case COORDINATE_ONE: - m_surfaceOffsetVectorTypeComboBox = new EnumComboBoxTemplate(this); - m_surfaceOffsetVectorTypeComboBox->setup(); - QObject::connect(m_surfaceOffsetVectorTypeComboBox, SIGNAL(itemActivated()), - this, SLOT(surfaceOffsetVectorTypeChanged())); - m_surfaceOffsetVectorTypeComboBox->getWidget()->setFixedWidth(45); - m_surfaceOffsetVectorTypeComboBox->getWidget()->setToolTip("Vector for surface offset:\n" - " C - Centroid thru Vertex, Faces Viewer\n" - " N - Vertex Normal, Faces Viewer\n" - " T - Tangent, Rotates with Surface"); - break; - case COORDINATE_TWO: - break; - } - - m_plusButtonToolTipText = ("Click the mouse to set the new location for the coordinate.\n" - "After clicking the mouse, a dialog allows selection of the\n" - "coordinate space."); - - - QToolButton* setCoordinateToolButton = NULL; - switch (m_parentWidgetType) { - case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - /** disabled as change space cause grouping problems. setCoordinateToolButton = new QToolButton(); */ - break; - case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: - CaretAssert(0); - break; - } - - if (setCoordinateToolButton != NULL) { - QAction* setCoordinateAction = WuQtUtilities::createAction("+", - ("After pressing this button: \n" - + m_plusButtonToolTipText), - this, - this, - SLOT(setCoordinateActionTriggered())); - setCoordinateToolButton->setDefaultAction(setCoordinateAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(setCoordinateToolButton); - } - - m_surfaceWidget = new QWidget(); - QHBoxLayout* surfaceLayout = new QHBoxLayout(m_surfaceWidget); - WuQtUtilities::setLayoutSpacingAndMargins(surfaceLayout, 2, 0); - surfaceLayout->addWidget(surfaceVertexLabel); - surfaceLayout->addWidget(m_surfaceStructureComboBox->getWidget()); - surfaceLayout->addWidget(m_surfaceNodeIndexSpinBox); - if (m_surfaceOffsetVectorTypeComboBox != NULL) { - surfaceLayout->addWidget(m_surfaceOffsetVectorTypeComboBox->getWidget()); - } - surfaceLayout->addWidget(m_surfaceOffsetLengthSpinBox); - - m_coordinateWidget = new QWidget(); - QHBoxLayout* coordinateLayout = new QHBoxLayout(m_coordinateWidget); - WuQtUtilities::setLayoutSpacingAndMargins(coordinateLayout, 2, 0); - coordinateLayout->addWidget(xCoordLabel); - coordinateLayout->addWidget(m_xCoordSpinBox); - coordinateLayout->addWidget(yCoordLabel); - coordinateLayout->addWidget(m_yCoordSpinBox); - coordinateLayout->addWidget(zCoordLabel); - coordinateLayout->addWidget(m_zCoordSpinBox); - - - QHBoxLayout* layout = new QHBoxLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); - layout->addWidget(m_surfaceWidget); - layout->addWidget(m_coordinateWidget); - if (setCoordinateToolButton != NULL) { - layout->addWidget(setCoordinateToolButton); - } - - setSizePolicy(QSizePolicy::Fixed, - QSizePolicy::Fixed); -} - -/** - * Destructor. - */ -AnnotationCoordinateWidget::~AnnotationCoordinateWidget() -{ -} - -AnnotationCoordinate* -AnnotationCoordinateWidget::getCoordinate() -{ - AnnotationCoordinate* ac = NULL; - - - if (m_annotation != NULL) { - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(m_annotation); - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(m_annotation); - - switch (m_whichCoordinate) { - case COORDINATE_ONE: - if (oneDimShape != NULL) { - ac = oneDimShape->getStartCoordinate(); - } - else if (twoDimShape != NULL) { - ac = twoDimShape->getCoordinate(); - } - break; - case COORDINATE_TWO: - if (oneDimShape != NULL) { - ac = oneDimShape->getEndCoordinate(); - } - break; - } - } - - return ac; -} - - -/** - * Update with the given annotation coordinate. - * - * @param coordinate. - */ -void -AnnotationCoordinateWidget::updateContent(Annotation* annotation) -{ - m_annotation = NULL; - if (annotation != NULL) { - if (annotation->testProperty(Annotation::Property::COORDINATE)) { - m_annotation = annotation; - } - } - - m_surfaceStructureComboBox->listOnlyValidStructures(); - - const AnnotationCoordinate* coordinate = getCoordinate(); - - bool surfaceFlag = false; - - if (coordinate != NULL) { - float xyz[3]; - coordinate->getXYZ(xyz); - - bool viewportSpaceFlag = false; - const double percentageMinimum = 0.0; - const double percentageMaximum = 100.0; - const double zDepthMinimum = 0.0; - const double zDepthMaximum = 100.0; - const double coordinateMinimum = -std::numeric_limits::max(); - const double coordinateMaximum = std::numeric_limits::max(); - double xMin = 0.0; - double xMax = 0.0; - double yMin = 0.0; - double yMax = 0.0; - double zMin = 0.0; - double zMax = 0.0; - double xStep = 0.1; - double yStep = 0.1; - double zStep = 0.1; - QString suffix; - int32_t digitsRightOfDecimalX = 2; - int32_t digitsRightOfDecimalY = 2; - int32_t digitsRightOfDecimalZ = 2; - switch (m_annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::CHART: - { - xMin = coordinateMinimum; - xMax = coordinateMaximum; - yMin = coordinateMinimum; - yMax = coordinateMaximum; - zMin = coordinateMinimum; - zMax = coordinateMaximum; - digitsRightOfDecimalX = 3; - digitsRightOfDecimalX = 3; - xStep = 1.0; - yStep = 1.0; - - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); - CaretAssert(bbw); - BrowserTabContent* browserTabContent = bbw->getBrowserTabContent(); - if (browserTabContent != NULL) { - ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); - const int32_t tabIndex = browserTabContent->getTabNumber(); - if (modelChartTwo != NULL) { - ChartTwoOverlaySet* chartOverlaySet = NULL; - switch (modelChartTwo->getSelectedChartTwoDataType(tabIndex)) { - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: - chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: - chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: - break; - } - - if (chartOverlaySet != NULL) { - float xAxisMin = std::numeric_limits::max(); - float xAxisMax = -std::numeric_limits::max(); - float yAxisMin = std::numeric_limits::max(); - float yAxisMax = -std::numeric_limits::max(); - - std::vector axes; - chartOverlaySet->getDisplayedChartAxes(axes); - const int32_t numAxes = static_cast(axes.size()); - for (int32_t i = 0; i < numAxes; i++) { - float rangeMin(0), rangeMax(0); - axes[i]->getDataRange(rangeMin, rangeMax); - - if (rangeMax > rangeMin) { - const ChartAxisLocationEnum::Enum axisLocation = axes[i]->getAxisLocation(); - switch (axisLocation) { - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - xAxisMin = std::min(xAxisMin, rangeMin); - xAxisMax = std::max(xAxisMax, rangeMax); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - yAxisMin = std::min(yAxisMin, rangeMin); - yAxisMax = std::max(yAxisMax, rangeMax); - break; - } - } - } - - if (xAxisMax > xAxisMin) { - const float range(xAxisMax - xAxisMin); - int32_t digits = 6 - static_cast(std::round(std::log10(range))); - digitsRightOfDecimalX = MathFunctions::clamp(digits, 3, 6); - xStep = range * 0.001f; - } - if (yAxisMax > yAxisMin) { - const float range(yAxisMax - yAxisMin); - int32_t digits = 6 - static_cast(std::round(std::log10(range))); - digitsRightOfDecimalY = MathFunctions::clamp(digits, 3, 6); - yStep = range * 0.001f; - } - } - } - } - } - break; - case AnnotationCoordinateSpaceEnum::SPACER: - xMin = percentageMinimum; - xMax = percentageMaximum; - yMin = percentageMinimum; - yMax = percentageMaximum; - zMin = zDepthMinimum; - zMax = zDepthMaximum; - suffix = "%"; - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - xMin = coordinateMinimum; - xMax = coordinateMaximum; - yMin = coordinateMinimum; - yMax = coordinateMaximum; - zMin = coordinateMinimum; - zMax = coordinateMaximum; - xStep = 1.0; - yStep = 1.0; - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - surfaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::TAB: - xMin = percentageMinimum; - xMax = percentageMaximum; - yMin = percentageMinimum; - yMax = percentageMaximum; - zMin = zDepthMinimum; - zMax = zDepthMaximum; - suffix = "%"; - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - /* - * Cannot move - */ - xMin = xyz[0]; - xMax = xyz[0]; - yMin = xyz[1]; - yMax = xyz[1]; - zMin = xyz[2]; - zMax = xyz[2]; - viewportSpaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - xMin = percentageMinimum; - xMax = percentageMaximum; - yMin = percentageMinimum; - yMax = percentageMaximum; - zMin = zDepthMinimum; - zMax = zDepthMaximum; - suffix = "%"; - break; - } - - m_xCoordSpinBox->blockSignals(true); - m_xCoordSpinBox->setRange(xMin, - xMax); - m_xCoordSpinBox->setSingleStep(xStep); - m_xCoordSpinBox->setSuffix(suffix); - m_xCoordSpinBox->setValue(xyz[0]); - m_xCoordSpinBox->setDecimals(digitsRightOfDecimalX); - m_xCoordSpinBox->blockSignals(false); - - m_yCoordSpinBox->blockSignals(true); - m_yCoordSpinBox->setRange(yMin, - yMax); - m_yCoordSpinBox->setSingleStep(yStep); - m_yCoordSpinBox->setSuffix(suffix); - m_yCoordSpinBox->setValue(xyz[1]); - m_yCoordSpinBox->setDecimals(digitsRightOfDecimalY); - m_yCoordSpinBox->blockSignals(false); - - m_zCoordSpinBox->blockSignals(true); - m_zCoordSpinBox->setRange(zMin, - zMax); - m_zCoordSpinBox->setSingleStep(zStep); - m_zCoordSpinBox->setSuffix(suffix); - m_zCoordSpinBox->setValue(xyz[2]); - m_zCoordSpinBox->setDecimals(digitsRightOfDecimalZ); - m_zCoordSpinBox->blockSignals(false); - - if (surfaceFlag) { - StructureEnum::Enum structure = StructureEnum::INVALID; - int32_t surfaceNumberOfNodes = -1; - int32_t surfaceNodeIndex = -1; - float surfaceOffsetLength = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); - AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; - coordinate->getSurfaceSpace(structure, - surfaceNumberOfNodes, - surfaceNodeIndex, - surfaceOffsetLength, - surfaceOffsetVector); - m_surfaceStructureComboBox->setSelectedStructure(structure); - m_surfaceNodeIndexSpinBox->blockSignals(true); - m_surfaceNodeIndexSpinBox->setValue(surfaceNodeIndex); - m_surfaceNodeIndexSpinBox->blockSignals(false); - - if (m_surfaceOffsetVectorTypeComboBox != NULL) { - m_surfaceOffsetVectorTypeComboBox->setSelectedItem(surfaceOffsetVector); - } - - m_surfaceOffsetLengthSpinBox->blockSignals(true); - m_surfaceOffsetLengthSpinBox->setValue(surfaceOffsetLength); - m_surfaceOffsetLengthSpinBox->blockSignals(false); - - AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(surfaceOffsetVector); - AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(surfaceOffsetLength); - } - - if (viewportSpaceFlag) { - setEnabled(false); - } - else { - setEnabled(true); - } - } - else { - setEnabled(false); - } - - m_surfaceWidget->setVisible(surfaceFlag); - m_coordinateWidget->setVisible( ! surfaceFlag); -} - -/** - * Called when surface offset value changed. - * - * @param value - * New value. - */ -void -AnnotationCoordinateWidget::surfaceOffsetLengthValueChanged(double value) -{ - const AnnotationCoordinate* coordinate = getCoordinate(); - if ((m_annotation != NULL) - && (coordinate != NULL)) { - AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(value); - valueChanged(); - } -} - -/** - * Called when surface offset vector type is changed. - */ -void -AnnotationCoordinateWidget::surfaceOffsetVectorTypeChanged() -{ - const AnnotationCoordinate* coordinate = getCoordinate(); - if ((m_annotation != NULL) - && (coordinate != NULL)) { - CaretAssert(m_surfaceOffsetVectorTypeComboBox); - AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(m_surfaceOffsetVectorTypeComboBox->getSelectedItem()); - valueChanged(); - } -} - -/** - * Gets called when a coordinate value is changed. - */ -void -AnnotationCoordinateWidget::valueChanged() -{ - const AnnotationCoordinate* coordinate = getCoordinate(); - if ((m_annotation != NULL) - && (coordinate != NULL)) { - bool surfaceFlag = false; - switch (m_annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::CHART: - break; - case AnnotationCoordinateSpaceEnum::SPACER: - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - surfaceFlag = true; - break; - case AnnotationCoordinateSpaceEnum::TAB: - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - break; - } - - switch (m_parentWidgetType) { - case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - { - AnnotationCoordinate coordinateCopy(*coordinate); - - if (surfaceFlag) { - StructureEnum::Enum structure = StructureEnum::INVALID; - int32_t surfaceNumberOfNodes = -1; - int32_t surfaceNodeIndex = -1; - float surfaceOffsetLength = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); - AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; - coordinate->getSurfaceSpace(structure, - surfaceNumberOfNodes, - surfaceNodeIndex, - surfaceOffsetLength, - surfaceOffsetVector); - structure = m_surfaceStructureComboBox->getSelectedStructure(); - surfaceNodeIndex = m_surfaceNodeIndexSpinBox->value(); - surfaceOffsetLength = m_surfaceOffsetLengthSpinBox->value(); - - if (m_surfaceOffsetVectorTypeComboBox != NULL) { - surfaceOffsetVector = m_surfaceOffsetVectorTypeComboBox->getSelectedItem(); - } - - coordinateCopy.setSurfaceSpace(structure, - surfaceNumberOfNodes, - surfaceNodeIndex, - surfaceOffsetLength, - surfaceOffsetVector); - } - else { - float xyz[3] = { - (float)m_xCoordSpinBox->value(), - (float)m_yCoordSpinBox->value(), - (float)m_zCoordSpinBox->value() - }; - coordinateCopy.setXYZ(xyz); - } - - std::vector selectedAnnotations; - selectedAnnotations.push_back(m_annotation); - - bool updateUserInterfaceFlag = false; - for (std::vector::iterator annIter = selectedAnnotations.begin(); - annIter != selectedAnnotations.end(); - annIter++) { - Annotation* ann = *annIter; - if (ann->getType() == AnnotationTypeEnum::COLOR_BAR) { - AnnotationColorBar* colorBar = dynamic_cast(ann); - CaretAssert(colorBar); - colorBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); - updateUserInterfaceFlag = true; - } - } - - AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); - switch (m_whichCoordinate) { - case COORDINATE_ONE: - undoCommand->setModeCoordinateOne(coordinateCopy, - selectedAnnotations); - break; - case COORDINATE_TWO: - undoCommand->setModeCoordinateTwo(coordinateCopy, - selectedAnnotations); - break; - } - AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - - AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, - errorMessage)) { - WuQMessageBox::errorOk(this, - errorMessage); - } - - EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); - if (updateUserInterfaceFlag) { - EventManager::get()->sendEvent(EventOverlaySettingsEditorDialogRequest(EventOverlaySettingsEditorDialogRequest::MODE_UPDATE_ALL, - m_browserWindowIndex, - NULL, - (CaretMappableDataFile*)NULL, - -1).getPointer()); - } - } - break; - case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: - CaretAssert(0); - break; - } - - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - } -} - -/** - * Called when the set coordinate button is clicked. - */ -void -AnnotationCoordinateWidget::setCoordinateActionTriggered() -{ - QPoint middlePos(width() / 2, - height() / 2); - QToolTip::showText(mapToGlobal(middlePos), - m_plusButtonToolTipText, - this); - - signalSelectCoordinateWithMouse(); -} diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCoordinateWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCoordinateWidget.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ -#ifndef __ANNOTATION_COORDINATE_WIDGET_H__ -#define __ANNOTATION_COORDINATE_WIDGET_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include - -#include "AnnotationCoordinateSpaceEnum.h" -#include "AnnotationWidgetParentEnum.h" - -class QDoubleSpinBox; -class QSpinBox; - -namespace caret { - - class Annotation; - class AnnotationCoordinate; - class EnumComboBoxTemplate; - class StructureEnumComboBox; - - class AnnotationCoordinateWidget : public QWidget { - - Q_OBJECT - - public: - enum WhichCoordinate { - COORDINATE_ONE, - COORDINATE_TWO - }; - - AnnotationCoordinateWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, - const WhichCoordinate whichCoordinate, - const int32_t browserWindowIndex, - QWidget* parent = 0); - - virtual ~AnnotationCoordinateWidget(); - - - // ADD_NEW_METHODS_HERE - - void updateContent(Annotation* annotation); - - signals: - void signalSelectCoordinateWithMouse(); - - private slots: - void valueChanged(); - - void setCoordinateActionTriggered(); - - void surfaceOffsetLengthValueChanged(double); - - void surfaceOffsetVectorTypeChanged(); - - private: - AnnotationCoordinateWidget(const AnnotationCoordinateWidget&); - - AnnotationCoordinateWidget& operator=(const AnnotationCoordinateWidget&); - - AnnotationCoordinate* getCoordinate(); - - // ADD_NEW_MEMBERS_HERE - - const AnnotationWidgetParentEnum::Enum m_parentWidgetType; - - const WhichCoordinate m_whichCoordinate; - - const int32_t m_browserWindowIndex; - - QWidget* m_surfaceWidget; - - QWidget* m_coordinateWidget; - - StructureEnumComboBox* m_surfaceStructureComboBox; - - QSpinBox* m_surfaceNodeIndexSpinBox; - - EnumComboBoxTemplate* m_surfaceOffsetVectorTypeComboBox; - - QDoubleSpinBox* m_surfaceOffsetLengthSpinBox; - - QDoubleSpinBox* m_xCoordSpinBox; - - QDoubleSpinBox* m_yCoordSpinBox; - - QDoubleSpinBox* m_zCoordSpinBox; - - Annotation* m_annotation; - - AString m_plusButtonToolTipText; - - }; - -#ifdef __ANNOTATION_COORDINATE_WIDGET_DECLARE__ - // -#endif // __ANNOTATION_COORDINATE_WIDGET_DECLARE__ - -} // namespace -#endif //__ANNOTATION_COORDINATE_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCreateDialog.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationCreateDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCreateDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCreateDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -77,6 +77,8 @@ * * @param mouseEvent * The mouse event indicating where user clicked in the window + * @param drawingCoordinates + * Coordinates of annotation that was drawn * @param annotationSpace * Space of annotation being created. * @param annotationType @@ -89,12 +91,14 @@ */ Annotation* AnnotationCreateDialog::newAnnotationFromSpaceAndType(const MouseEvent& mouseEvent, + const std::vector& drawingCoordinates, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile) { Annotation* newAnnotation = newAnnotationFromSpaceTypeAndCoords(MODE_NEW_ANNOTATION_TYPE_CLICK, mouseEvent, + drawingCoordinates, annotationSpace, annotationType, annotationFile); @@ -109,6 +113,8 @@ * * @param mouseEvent * The mouse event indicating where user clicked in the window + * @param drawingCoordinates + * Coordinates of annotation that was drawn * @param annotationSpace * Space of annotation being created. * @param annotationType @@ -121,12 +127,14 @@ */ Annotation* AnnotationCreateDialog::newAnnotationFromSpaceTypeAndBounds(const MouseEvent& mouseEvent, - const AnnotationCoordinateSpaceEnum::Enum annotationSpace, - const AnnotationTypeEnum::Enum annotationType, - AnnotationFile* annotationFile) + const std::vector& drawingCoordinates, + const AnnotationCoordinateSpaceEnum::Enum annotationSpace, + const AnnotationTypeEnum::Enum annotationType, + AnnotationFile* annotationFile) { Annotation* newAnnotation = newAnnotationFromSpaceTypeAndCoords(MODE_NEW_ANNOTATION_TYPE_FROM_BOUNDS, mouseEvent, + drawingCoordinates, annotationSpace, annotationType, annotationFile); @@ -142,6 +150,8 @@ * The mode. * @param mouseEvent * The mouse event indicating where user clicked in the window + *@param drawingCoordinates + * Coordinates of annotation that was drawn * @param annotationSpace * Space of annotation being created. * @param annotationType @@ -159,6 +169,7 @@ Annotation* AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, const MouseEvent& mouseEvent, + const std::vector& drawingCoordinates, const AnnotationCoordinateSpaceEnum::Enum annotationSpaceIn, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile) @@ -173,6 +184,7 @@ } NewAnnotationInfo newInfo(mouseEvent, + drawingCoordinates, annotationSpaceIn, annotationType, useBothFlag, @@ -185,13 +197,15 @@ return NULL; } - bool needImageOrTextFlag = false; switch (annotationType) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssertMessage(0, "Browser Tabs do not get created !!!"); + break; case AnnotationTypeEnum::COLOR_BAR: - CaretAssertMessage(0, "Colorbars do not get created !!!"); + CaretAssertMessage(0, "Color bars do not get created !!!"); break; case AnnotationTypeEnum::IMAGE: needImageOrTextFlag = true; @@ -200,6 +214,11 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + CaretAssertMessage(0, "Scale bars do not get created !!!"); + break; case AnnotationTypeEnum::TEXT: needImageOrTextFlag = true; break; @@ -212,7 +231,7 @@ } else { AString errorMessage; - Annotation* newAnn = createAnnotation(newInfo, newInfo.m_selectedSpace, /*annotationSpace,*/ errorMessage); + Annotation* newAnn = createAnnotation(newInfo, newInfo.m_selectedSpace, errorMessage); if (newAnn != NULL) { DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->updateForNewAnnotation(newAnn); @@ -356,6 +375,8 @@ switch (newAnnotationInfo.m_annotationType) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -370,6 +391,10 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: break; } @@ -378,21 +403,48 @@ const bool validFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(newAnnotation, annotationSpace, &newAnnotationInfo.m_coordOneInfo, - coordTwo); + coordTwo, + newAnnotationInfo.m_coordMultiInfo); if (validFlag) { if ((newAnnotationInfo.m_percentageWidth > 0) && (newAnnotationInfo.m_percentageHeight > 0)) { - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(newAnnotation); + AnnotationOneCoordinateShape* twoDimShape = dynamic_cast(newAnnotation); if (twoDimShape != NULL) { twoDimShape->setWidth(newAnnotationInfo.m_percentageWidth); twoDimShape->setHeight(newAnnotationInfo.m_percentageHeight); } } + int32_t tabIndex(newAnnotationInfo.m_coordOneInfo.m_tabSpaceInfo.m_index); + switch (newAnnotationInfo.m_annotationType) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + if (annotationSpace == AnnotationCoordinateSpaceEnum::CHART) { + if ( ! newAnnotationInfo.m_coordMultiInfo.empty()) { + tabIndex = newAnnotationInfo.m_coordMultiInfo[0]->m_tabSpaceInfo.m_index; + } + } + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } finishAnnotationCreation(newAnnotationInfo.m_annotationFile, newAnnotation, newAnnotationInfo.m_mouseEvent.getBrowserWindowIndex(), - newAnnotationInfo.m_coordOneInfo.m_tabSpaceInfo.m_index); + tabIndex); return newAnnotation; } @@ -603,12 +655,13 @@ /* * Setup file selection dialog. */ - CaretFileDialog fd(this); + CaretFileDialog fd(CaretFileDialog::Mode::MODE_OPEN, + this); fd.setAcceptMode(CaretFileDialog::AcceptOpen); - fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::IMAGE)); + fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilterForReading(DataFileTypeEnum::IMAGE)); fd.setFileMode(CaretFileDialog::ExistingFile); fd.setViewMode(CaretFileDialog::List); - fd.setLabelText(CaretFileDialog::Accept, "Choose"); // OK button shows Insert + fd.setLabelText(CaretFileDialog::Accept, "Choose"); /* OK button shows Insert */ fd.restoreDialogSettings(fileDialogSettingsName); AString errorMessages; @@ -788,9 +841,33 @@ undoCommand->setModeCreateAnnotation(annotationFile, annotation); + CaretAssert(annotation); + UserInputModeEnum::Enum inputMode = UserInputModeEnum::Enum::ANNOTATIONS; + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + inputMode = UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING; + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } AString errorMessage; - if ( ! annotationManager->applyCommand(undoCommand, - errorMessage)) { + if ( ! annotationManager->applyCommand(inputMode, + undoCommand, + errorMessage)) { WuQMessageBox::errorOk(GuiManager::get()->getBrowserWindowByWindowIndex(browswerWindowIndex), errorMessage); } @@ -818,6 +895,8 @@ * * @param mouseEvent * The mouse event. + * @param drawingCoordinates + * Coordinates of annotation that was drawn * @param selectedSpace * The space selected by the user. * @param annotationType @@ -828,6 +907,7 @@ * File to which annotation is added. */ AnnotationCreateDialog::NewAnnotationInfo::NewAnnotationInfo(const MouseEvent& mouseEvent, + const std::vector& drawingCoordinates, const AnnotationCoordinateSpaceEnum::Enum selectedSpace, const AnnotationTypeEnum::Enum annotationType, const bool useBothCoordinatesFromMouseFlag, @@ -843,11 +923,66 @@ m_coordOneInfo.reset(); m_coordTwoInfo.reset(); m_coordTwoInfoValid = false; + m_coordMultiInfo.clear(); m_percentageWidth = -1; m_percentageHeight = -1; + bool multiCoordAnnFlag(false); + switch (annotationType) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + multiCoordAnnFlag = true; + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } - if (useBothCoordinatesFromMouseFlag) { + if (multiCoordAnnFlag) { + const bool newFlag(true); + if (newFlag) { + for (const auto& vec3D : drawingCoordinates) { + std::unique_ptr ptr(new AnnotationCoordinateInformation()); + AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, + vec3D[0], + vec3D[1], + *ptr.get()); + m_coordMultiInfo.push_back(std::move(ptr)); + } + + AnnotationCoordinateInformation::getValidCoordinateSpaces(m_coordMultiInfo, + m_validSpaces); + } + else { + const int32_t numXY(mouseEvent.getXyHistoryCount()); + for (int32_t i = 0; i < numXY; i++) { + const auto xy(mouseEvent.getHistoryAtIndex(i)); + std::unique_ptr ptr(new AnnotationCoordinateInformation()); + AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, + xy.m_x, + xy.m_y, + *ptr.get()); + m_coordMultiInfo.push_back(std::move(ptr)); + } + } + + AnnotationCoordinateInformation::getValidCoordinateSpaces(m_coordMultiInfo, + m_validSpaces); + + } + else if (useBothCoordinatesFromMouseFlag) { AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, mouseEvent.getX(), mouseEvent.getY(), @@ -880,6 +1015,8 @@ switch (annotationType) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -889,6 +1026,10 @@ break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: break; } @@ -942,6 +1083,9 @@ case AnnotationTypeEnum::BOX: useAverageFlag = true; break; + case AnnotationTypeEnum::BROWSER_TAB: + useAverageFlag = true; + break; case AnnotationTypeEnum::COLOR_BAR: useAverageFlag = true; break; @@ -954,6 +1098,12 @@ case AnnotationTypeEnum::LINE: useAverageFlag = false; break; + case AnnotationTypeEnum::POLY_LINE: + useAverageFlag = false; + break; + case AnnotationTypeEnum::SCALE_BAR: + useAverageFlag = true; + break; case AnnotationTypeEnum::TEXT: useTextAligmentFlag = true; break; diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationCreateDialog.h connectome-workbench-1.5.0/src/GuiQt/AnnotationCreateDialog.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationCreateDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationCreateDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,9 +21,12 @@ */ /*LICENSE_END*/ +#include + #include "AnnotationCoordinateInformation.h" #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationTypeEnum.h" +#include "Vector3D.h" #include "WuQDialogModal.h" class QButtonGroup; @@ -41,11 +44,13 @@ public: static Annotation* newAnnotationFromSpaceAndType(const MouseEvent& mouseEvent, + const std::vector& drawingCoordinates, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile); static Annotation* newAnnotationFromSpaceTypeAndBounds(const MouseEvent& mouseEvent, + const std::vector& drawingCoordinates, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile); @@ -73,6 +78,7 @@ class NewAnnotationInfo { public: NewAnnotationInfo(const MouseEvent& mouseEvent, + const std::vector& drawingCoordinates, const AnnotationCoordinateSpaceEnum::Enum selectedSpace, const AnnotationTypeEnum::Enum annotationType, const bool useBothCoordinatesFromMouseFlag, @@ -103,6 +109,8 @@ AnnotationCoordinateInformation m_coordTwoInfo; + std::vector> m_coordMultiInfo; + bool m_coordTwoInfoValid; float m_percentageWidth; @@ -113,6 +121,7 @@ static Annotation* newAnnotationFromSpaceTypeAndCoords(const Mode mode, const MouseEvent& mouseEvent, + const std::vector& drawingCoordinates, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile); @@ -150,10 +159,6 @@ Annotation* m_annotationThatWasCreated; -// float m_annotationFromBoundsWidth; -// -// float m_annotationFromBoundsHeight; - QButtonGroup* m_annotationSpaceButtonGroup; QTextEdit* m_textEdit; @@ -166,10 +171,6 @@ int32_t m_imageWidth; int32_t m_imageHeight; -// AnnotationCoordinateInformation m_coordInfo; -// -// AnnotationCoordinateInformation m_coordTwoInfo; - static const int s_MAXIMUM_THUMB_NAIL_SIZE; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationDeleteWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationDeleteWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationDeleteWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationDeleteWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -152,6 +152,28 @@ std::vector deleteAnnotations; for (auto a : selectedAnnotations) { if (a->testProperty(Annotation::Property::DELETION)) { + switch (a->getType()) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssert(0); + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } + deleteAnnotations.push_back(a); } } @@ -161,8 +183,9 @@ undoCommand->setModeDeleteAnnotations(deleteAnnotations); AString errorMessage; - if ( ! annotationManager->applyCommand(undoCommand, - errorMessage)) { + if ( ! annotationManager->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, + errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationFontWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationFontWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationFontWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationFontWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -606,7 +606,8 @@ convertToAnnotations(m_annotationsFontColor)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -726,7 +727,8 @@ convertToAnnotations(m_annotationsFontStyle)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(command, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -754,7 +756,8 @@ convertToAnnotations(m_annotationsFontStyle)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(command, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -784,7 +787,8 @@ convertToAnnotations(m_annotationsFontName)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(command, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -817,7 +821,8 @@ getSurfaceMontageRowCount()); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(command, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -877,7 +882,8 @@ convertToAnnotations(m_annotationsFontStyle)); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(command, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationFormatWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationFormatWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationFormatWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationFormatWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -42,14 +42,18 @@ /** * Constructor. * + * @param userInputMode + * The user input mode * @param browserWindowIndex * Index of the browser window. * @param parent * Parent of this widget. */ -AnnotationFormatWidget::AnnotationFormatWidget(const int32_t browserWindowIndex, - QWidget* parent) +AnnotationFormatWidget::AnnotationFormatWidget(const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, + QWidget* parent) : QWidget(parent), +m_userInputMode(userInputMode), m_browserWindowIndex(browserWindowIndex) { QLabel* formatLabel = new QLabel("Format"); @@ -58,7 +62,7 @@ QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); - layout->addWidget(formatLabel); + layout->addWidget(formatLabel, 0, Qt::AlignHCenter); layout->addWidget(arrangeButton); layout->addStretch(); @@ -76,11 +80,13 @@ /** * Update with the given annotation. * - * @param annotation. + * @param annotations + * The selected annotations */ void -AnnotationFormatWidget::updateContent(Annotation* /*annotation*/) +AnnotationFormatWidget::updateContent(const std::vector& annotations) { + setEnabled( ! annotations.empty()); } /** @@ -89,7 +95,8 @@ QWidget* AnnotationFormatWidget::createArrangeMenuToolButton() { - AnnotationMenuArrange* arrangeMenu = new AnnotationMenuArrange(m_browserWindowIndex); + AnnotationMenuArrange* arrangeMenu = new AnnotationMenuArrange(m_userInputMode, + m_browserWindowIndex); QAction* arrangeAction = new QAction("Arrange", this); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationFormatWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationFormatWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationFormatWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationFormatWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,8 @@ #include +#include "UserInputModeEnum.h" + namespace caret { class Annotation; @@ -34,7 +36,8 @@ Q_OBJECT public: - AnnotationFormatWidget(const int32_t browserWindowIndex, + AnnotationFormatWidget(const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationFormatWidget(); @@ -42,7 +45,7 @@ // ADD_NEW_METHODS_HERE - void updateContent(Annotation* annotation); + void updateContent(const std::vector& annotations); private: AnnotationFormatWidget(const AnnotationFormatWidget&); @@ -51,6 +54,8 @@ QWidget* createArrangeMenuToolButton(); + const UserInputModeEnum::Enum m_userInputMode; + const int32_t m_browserWindowIndex; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationInsertNewWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationInsertNewWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationInsertNewWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationInsertNewWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -80,16 +80,19 @@ * Shape buttons */ m_shapeActionGroup = new QActionGroup(this); - QToolButton* shapeBoxToolButton = createShapeToolButton(AnnotationTypeEnum::BOX, - m_shapeActionGroup); - QToolButton* shapeImageToolButton = createShapeToolButton(AnnotationTypeEnum::IMAGE, - m_shapeActionGroup); - QToolButton* shapeLineToolButton = createShapeToolButton(AnnotationTypeEnum::LINE, - m_shapeActionGroup); - QToolButton* shapeOvalToolButton = createShapeToolButton(AnnotationTypeEnum::OVAL, - m_shapeActionGroup); - QToolButton* shapeTextToolButton = createShapeToolButton(AnnotationTypeEnum::TEXT, - m_shapeActionGroup); + QToolButton* shapeBoxToolButton = createShapeToolButton(AnnotationTypeEnum::BOX, + m_shapeActionGroup); + QToolButton* shapeImageToolButton = createShapeToolButton(AnnotationTypeEnum::IMAGE, + m_shapeActionGroup); + QToolButton* shapeLineToolButton = createShapeToolButton(AnnotationTypeEnum::LINE, + m_shapeActionGroup); + QToolButton* shapePolyLineToolButton = createShapeToolButton(AnnotationTypeEnum::POLY_LINE, + m_shapeActionGroup); + m_polyLineToolButton = shapePolyLineToolButton; + QToolButton* shapeOvalToolButton = createShapeToolButton(AnnotationTypeEnum::OVAL, + m_shapeActionGroup); + QToolButton* shapeTextToolButton = createShapeToolButton(AnnotationTypeEnum::TEXT, + m_shapeActionGroup); QObject::connect(m_shapeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(spaceOrShapeActionTriggered())); @@ -125,6 +128,7 @@ shapeImageToolButton->setMaximumSize(mw, mh); shapeLineToolButton->setMaximumSize(mw, mh); shapeOvalToolButton->setMaximumSize(mw, mh); + shapePolyLineToolButton->setMaximumSize(mw, mh); shapeTextToolButton->setMaximumSize(mw, mh); chartSpaceToolButton->setMaximumSize(mw, mh); @@ -197,10 +201,12 @@ 3, 4); gridLayout->addWidget(shapeLineToolButton, 3, 5); - gridLayout->addWidget(shapeOvalToolButton, + gridLayout->addWidget(shapePolyLineToolButton, 3, 6); - gridLayout->addWidget(shapeTextToolButton, + gridLayout->addWidget(shapeOvalToolButton, 3, 7); + gridLayout->addWidget(shapeTextToolButton, + 3, 8); } else { QLabel* insertLabel = new QLabel("Insert New"); @@ -249,10 +255,12 @@ 3, 4); gridLayout->addWidget(shapeLineToolButton, 3, 5); - gridLayout->addWidget(shapeOvalToolButton, + gridLayout->addWidget(shapePolyLineToolButton, 3, 6); - gridLayout->addWidget(shapeTextToolButton, + gridLayout->addWidget(shapeOvalToolButton, 3, 7); + gridLayout->addWidget(shapeTextToolButton, + 3, 8); } setSizePolicy(QSizePolicy::Fixed, @@ -351,6 +359,8 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: stereotaxicSpaceValidFlag = true; surfaceSpaceValidFlag = true; @@ -466,8 +476,112 @@ action->setData(AnnotationTypeEnum::toIntegerCode(annotationType)); - action->setToolTip(typeGuiName - + " annotation"); +// AString toolTipText(typeGuiName +// + " annotation"); + AString typeText; + AString clickText; + AString dragText; + switch (annotationType) { + case AnnotationTypeEnum::BOX: + typeText = "Box Annotation"; + clickText = ("Click the mouse to create a box with a default size. " + "Change the box by moving its corners or edges."); + dragText = ("Press and hold the left mouse button down at a corner of the box. " + "While continuing to hold the mouse button down, drag the mouse to " + "another corner of the box and release the mouse button to create the box."); + break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssert(0); + break; + case AnnotationTypeEnum::COLOR_BAR: + CaretAssert(0); + break; + case AnnotationTypeEnum::IMAGE: + typeText = "Image Annotation"; + clickText = ("Click the mouse and a dialog is displayed. In the dialog, click the " + "Choose Image File button and select an image file. Click the OK button " + "to finish creation of the image annotation. Adjust the size of the image " + "by moving the edges or corners."); + dragText = ("Press and hold the left mouse button down at a corner for the image. " + "While continuing to hold the mouse button down, drag the mouse to " + "another corner for the image and release the mouse button. In the " + "dialog that is displayed, click the " + "Choose Image File button and select an image file. Click the OK button " + "to finish creation of the image annotation."); + break; + case AnnotationTypeEnum::LINE: + typeText = "Line Segment Annotation"; + clickText = ("Click the mouse to create a line with a default size. " + "Change the line by moving its end points."); + dragText = ("Press and hold the left mouse button down at one end of the line. " + "While continuing to hold the mouse button down, drag the mouse to " + "the other end of the line and release the mouse button to create the line."); + + break; + case AnnotationTypeEnum::OVAL: + typeText = "Oval Annotation"; + clickText = ("Click the mouse to create an oval with a default size. " + "Change the oval by moving the bounds of the oval."); + dragText = ("Press and hold the left mouse button down at a side of the oval. " + "While continuing to hold the mouse button down, drag the mouse to " + "another side of the oval and release the mouse button to create the oval."); + break; + case AnnotationTypeEnum::POLY_LINE: + clickText = ("Click the mouse to draw each vertex of the polyline and " + "shift-click the mouse to draw the last vertex and finish the polyline."); + dragText = ("Move the mouse to the first vertex of the polyline. While holding " + "down the left mouse button, drag the mouse to draw the polyline and " + "release the left mouse button to finish the polyline. This method will " + "create a polyline with many vertices and there may be a pause when " + "the mouse button is released to finish the polyline."); + typeText = ("Polyline Annotation. A polyline is a series of line segments connected at their end points. " + "When this button is clicked, a menu will appear for selecting the method used " + "to draw the polyline."); + m_polyLineDrawClicksToolTipText = ("" + clickText + ""); + m_polyLineDrawDragToolTipText = ("" + dragText + ""); + break; + case AnnotationTypeEnum::SCALE_BAR: + CaretAssert(0); + break; + case AnnotationTypeEnum::TEXT: + { + typeText = "Text Annotation"; + clickText = ("Click the mouse at the location for the text. After the mouse is clicked, " + "a dialog is displayed for entry of the text. Enter text in the dialog " + "and click the OK button to finish. The resulting text is offset from the mouse-click location using the " + "current Text Alignment and Orientation selections in the toolbar."); + dragText = ("Press and hold the left mouse button down at a box corner for the text. " + "While continuing to hold the mouse button down, drag the mouse to " + "another corner of the box and release the mouse button. In the dialog " + "that appears, enter the text and click the OK button to finish. " + "The Text Alignment and Orientation options affect the location of the " + "text within the box formed by the mouse actions."); + } + break; + } + + AString toolTip(""); + if ( ! typeText.isEmpty()) { + toolTip.appendWithNewLine(typeText + "

"); + } + if ( ( ! clickText.isEmpty()) + && ( ! dragText.isEmpty())) { + toolTip.appendWithNewLine("There are two methods for creating this type of annotation:

"); + clickText.insert(0, "(1) "); + dragText.insert(0, "(2) "); + } + if ( ! clickText.isEmpty()) { + toolTip.appendWithNewLine(clickText); + } + if ( ! dragText.isEmpty()) { + if ( ! clickText.isEmpty()) { + toolTip.appendWithNewLine("

"); + } + toolTip.appendWithNewLine(dragText); + } + toolTip.appendWithNewLine(""); + + action->setToolTip(toolTip); action->setCheckable(true); action->setChecked(false); @@ -519,11 +633,56 @@ &shapeValidFlag); CaretAssert(shapeValidFlag); + EventAnnotationCreateNewType::PolyLineDrawingMode polyLineDrawingMode + = EventAnnotationCreateNewType::PolyLineDrawingMode::DISCRETE; + + switch (annShape) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + { + CaretAssert(m_polyLineToolButton); + QMenu menu; + menu.setToolTipsVisible(true); + QAction* dragAction = menu.addAction("Drag (Continuous)"); + dragAction->setToolTip(m_polyLineDrawDragToolTipText); + QAction* clicksAction = menu.addAction("Clicks (Discrete)"); + clicksAction->setToolTip(m_polyLineDrawClicksToolTipText); + + QAction* selectedAction = menu.exec(m_polyLineToolButton->mapToGlobal(QPoint(0,0))); + if (selectedAction == dragAction) { + polyLineDrawingMode = EventAnnotationCreateNewType::PolyLineDrawingMode::CONTINUOUS; + } + else if (selectedAction == clicksAction) { + polyLineDrawingMode = EventAnnotationCreateNewType::PolyLineDrawingMode::DISCRETE; + } + else { + return; + } + } + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } + DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->setDisplayAnnotations(true); EventManager::get()->sendEvent(EventAnnotationCreateNewType(annotationFile, annSpace, - annShape).getPointer()); + annShape, + polyLineDrawingMode).getPointer()); } /** @@ -561,6 +720,9 @@ case AnnotationTypeEnum::BOX: painter->drawRect(1, 1, width - 2, height - 2); break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssertMessage(0, "No pixmap for browser tab as user does not create them like other annotations"); + break; case AnnotationTypeEnum::COLOR_BAR: CaretAssertMessage(0, "No pixmap for colorbar as user does not create them like other annotations"); break; @@ -614,6 +776,21 @@ case AnnotationTypeEnum::OVAL: painter->drawEllipse(1, 1, width - 1, height - 1); break; + case AnnotationTypeEnum::POLY_LINE: + { + const int hh(height / 2); + const int hw(width / 2); + QPolygon polyLine; + polyLine.push_back(QPoint(2, hh)); + polyLine.push_back(QPoint(6, 3)); + polyLine.push_back(QPoint(hw + 5, height - 3)); + polyLine.push_back(QPoint(width - 2, hh - 3)); + painter->drawPolyline(polyLine); + } + break; + case AnnotationTypeEnum::SCALE_BAR: + CaretAssertMessage(0, "No pixmap for scale bar as user does not create them like other annotations"); + break; case AnnotationTypeEnum::TEXT: { QFont font = painter->font(); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationInsertNewWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationInsertNewWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationInsertNewWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationInsertNewWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -84,6 +84,12 @@ AnnotationMenuFileSelection* m_fileSelectionMenu; + QToolButton* m_polyLineToolButton = NULL; + + AString m_polyLineDrawClicksToolTipText; + + AString m_polyLineDrawDragToolTipText; + static AString s_previousImageFileDirectory; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationLineArrowTipsWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationLineArrowTipsWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationLineArrowTipsWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationLineArrowTipsWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -163,7 +163,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -186,7 +187,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationMenuArrange.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationMenuArrange.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationMenuArrange.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationMenuArrange.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -27,16 +27,25 @@ #include "Annotation.h" #include "AnnotationArrangerInputs.h" +#include "AnnotationBrowserTab.h" #include "AnnotationManager.h" +#include "AnnotationStackingOrderOperation.h" +#include "AnnotationStackingOrderTypeEnum.h" #include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "BrowserWindowContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DisplayPropertiesAnnotationTextSubstitution.h" #include "EventAnnotationGrouping.h" +#include "EventBrowserWindowContent.h" #include "EventGetBrainOpenGLTextRenderer.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" +#include "EventUserInterfaceUpdate.h" #include "GuiManager.h" +#include "UserInputModeTileTabsManualLayoutContextMenu.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -53,26 +62,48 @@ /** * Constructor. * + * @param userInputMode + * The user input mode * @param parent * The parent widget. * @param browserWindowIndex * Index of the browser window. */ -AnnotationMenuArrange::AnnotationMenuArrange(const int32_t browserWindowIndex, +AnnotationMenuArrange::AnnotationMenuArrange(const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, QWidget* parent) : QMenu(parent), +m_userInputMode(userInputMode), m_browserWindowIndex(browserWindowIndex) { + switch (m_userInputMode) { + case UserInputModeEnum::Enum::ANNOTATIONS: + m_menuMode = MenuMode::ANNOTATIONS; + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + m_menuMode = MenuMode::TILE_TABS; + break; + case UserInputModeEnum::Enum::BORDERS: + case UserInputModeEnum::Enum::FOCI: + case UserInputModeEnum::Enum::IMAGE: + case UserInputModeEnum::Enum::INVALID: + case UserInputModeEnum::Enum::VIEW: + case UserInputModeEnum::Enum::VOLUME_EDIT: + CaretAssert(0); + return; + break; + } + addAlignmentSelections(); - addSeparator(); - addDistributeSelections(); - addSeparator(); + addOrderingSelections(); addGroupingSelections(); + addTileTabsSelections(); + QObject::connect(this, SIGNAL(aboutToShow()), this, SLOT(menuAboutToShow())); QObject::connect(this, SIGNAL(triggered(QAction*)), @@ -92,6 +123,10 @@ void AnnotationMenuArrange::addAlignmentSelections() { + if ( ! actions().isEmpty()) { + addSeparator(); + } + std::vector alignments; AnnotationAlignmentEnum::getAllEnums(alignments); @@ -116,6 +151,10 @@ void AnnotationMenuArrange::addDistributeSelections() { + if ( ! actions().isEmpty()) { + addSeparator(); + } + std::vector distributes; AnnotationDistributeEnum::getAllEnums(distributes); @@ -140,6 +179,21 @@ void AnnotationMenuArrange::addGroupingSelections() { + switch (m_menuMode) { + case MenuMode::ANNOTATIONS: + break; + case MenuMode::TILE_TABS: + /* + * No grouping for tile tabs + */ + return; + break; + } + + if ( ! actions().isEmpty()) { + addSeparator(); + } + m_groupAction = NULL; m_regroupAction = NULL; m_ungroupAction = NULL; @@ -178,6 +232,47 @@ CaretAssert(m_ungroupAction); } +/** + * Add distribution items to the menu. + */ +void +AnnotationMenuArrange::addTileTabsSelections() +{ + switch (m_menuMode) { + case MenuMode::ANNOTATIONS: + break; + case MenuMode::TILE_TABS: + { + if ( ! actions().isEmpty()) { + addSeparator(); + } + + m_tileTabsShrinkAndExpandToFillAction = addAction(UserInputModeTileTabsManualLayoutContextMenu::getShinkAndExpandTabMenuItemText()); + } + break; + } +} + +/** + * Add ordering for tile tabs mode + */ +void +AnnotationMenuArrange::addOrderingSelections() +{ + if ( ! actions().isEmpty()) { + addSeparator(); + } + + m_orderingBringToFrontAction = addAction("Bring to Front"); + + m_orderingBringForwardAction = addAction("Bring Forward"); + + m_orderingSendToBackAction = addAction("Send to Back"); + + m_orderingSendBackwardAction = addAction("Send Backward"); +} + + /* * Gets called when the menu is about to show * so that its menu items can be enabled/disabled. @@ -187,15 +282,93 @@ { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - m_groupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, - AnnotationGroupingModeEnum::GROUP)); - m_regroupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, - AnnotationGroupingModeEnum::REGROUP)); - m_ungroupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, - AnnotationGroupingModeEnum::UNGROUP)); + if (m_groupAction != NULL) { + m_groupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, + AnnotationGroupingModeEnum::GROUP)); + } + if (m_regroupAction != NULL) { + m_regroupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, + AnnotationGroupingModeEnum::REGROUP)); + } + if (m_ungroupAction != NULL) { + m_ungroupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, + AnnotationGroupingModeEnum::UNGROUP)); + } + + switch (m_menuMode) { + case MenuMode::ANNOTATIONS: + { + bool oneAnnWithCorrectSpaceFlag(false); + std::vector selectedAnnotations = annMan->getAnnotationsSelectedForEditing(m_browserWindowIndex); + if (selectedAnnotations.size() == 1) { + CaretAssertVectorIndex(selectedAnnotations, 0); + const Annotation* ann = selectedAnnotations[0]; + CaretAssert(ann); + switch (ann->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + oneAnnWithCorrectSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + oneAnnWithCorrectSpaceFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + oneAnnWithCorrectSpaceFlag = true; + break; + } + } + + CaretAssert(m_orderingSendToBackAction); + m_orderingSendToBackAction->setEnabled(oneAnnWithCorrectSpaceFlag); + + CaretAssert(m_orderingSendBackwardAction); + m_orderingSendBackwardAction->setEnabled(oneAnnWithCorrectSpaceFlag); + + CaretAssert(m_orderingBringForwardAction); + m_orderingBringForwardAction->setEnabled(oneAnnWithCorrectSpaceFlag); + + CaretAssert(m_orderingBringToFrontAction); + m_orderingBringToFrontAction->setEnabled(oneAnnWithCorrectSpaceFlag); + } + break; + case MenuMode::TILE_TABS: + { + std::unique_ptr windowEvent = EventBrowserWindowContent::getWindowContent(m_browserWindowIndex); + EventManager::get()->sendEvent(windowEvent->getPointer()); + BrowserWindowContent* bwc = windowEvent->getBrowserWindowContent(); + CaretAssert(bwc); + int32_t numSelected = 0; + if (bwc->isManualModeTileTabsConfigurationEnabled()) { + numSelected = annMan->getAnnotationsSelectedForEditing(m_browserWindowIndex).size(); + } + const bool oneSelectedFlag(numSelected == 1); + + CaretAssert(m_tileTabsShrinkAndExpandToFillAction); + m_tileTabsShrinkAndExpandToFillAction->setEnabled(oneSelectedFlag); + + CaretAssert(m_orderingSendToBackAction); + m_orderingSendToBackAction->setEnabled(oneSelectedFlag); + + CaretAssert(m_orderingSendBackwardAction); + m_orderingSendBackwardAction->setEnabled(oneSelectedFlag); + + CaretAssert(m_orderingBringForwardAction); + m_orderingBringForwardAction->setEnabled(oneSelectedFlag); + + CaretAssert(m_orderingBringToFrontAction); + m_orderingBringToFrontAction->setEnabled(oneSelectedFlag); + } + break; + } } - /** * Gets called when the user selects a menu item. */ @@ -226,6 +399,12 @@ else if (validGroupingFlag) { applyGrouping(annGroup); } + else if (processTileTabsMenu(action)) { + /* If true returned, a tile tabs menu item was selected */ + } + else if (processOrderingMenuItem(action)) { + /* If true returned, an ordering menu item was selected */ + } else { const AString msg("Unrecognized Enum name in Annotation Align Menu \"" + enumName @@ -234,10 +413,206 @@ CaretLogSevere(msg); } - EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } +/** + * Process the action if it is on the Tile Tabs Menu + * + * @param actionSelected + * Action selected by user + * @return + * True if the action was on this menu, else false. + */ +bool +AnnotationMenuArrange::processTileTabsMenu(QAction* actionSelected) +{ + bool menuSelectedFlag(false); + + if ((m_tileTabsShrinkAndExpandToFillAction != NULL) + && (actionSelected == m_tileTabsShrinkAndExpandToFillAction)) { + processShrinkAndExpandTabMenuItem(); + menuSelectedFlag = true; + } + + return menuSelectedFlag; +} + +/** + * Process the action if it is on the Tile Tabs Menu + * + * @param actionSelected + * Action selected by user + * @return + * True if the action was on this menu, else false. + */ +bool +AnnotationMenuArrange::processOrderingMenuItem(QAction* actionSelected) +{ + bool menuSelectedFlag(false); + + switch (m_menuMode) { + case MenuMode::ANNOTATIONS: + CaretAssert(m_orderingBringToFrontAction); + CaretAssert(m_orderingBringForwardAction); + CaretAssert(m_orderingSendBackwardAction); + CaretAssert(m_orderingSendToBackAction); + + if (actionSelected == m_orderingBringToFrontAction) { + processAnnotationOrderOperation(AnnotationStackingOrderTypeEnum::BRING_TO_FRONT); + menuSelectedFlag = true; + } + else if (actionSelected == m_orderingBringForwardAction) { + processAnnotationOrderOperation(AnnotationStackingOrderTypeEnum::BRING_FORWARD); + menuSelectedFlag = true; + } + else if (actionSelected == m_orderingSendBackwardAction) { + processAnnotationOrderOperation(AnnotationStackingOrderTypeEnum::SEND_BACKWARD); + menuSelectedFlag = true; + } + else if (actionSelected == m_orderingSendToBackAction) { + processAnnotationOrderOperation(AnnotationStackingOrderTypeEnum::SEND_TO_BACK); + menuSelectedFlag = true; + } + break; + case MenuMode::TILE_TABS: + { + CaretAssert(m_orderingBringToFrontAction); + CaretAssert(m_orderingBringForwardAction); + CaretAssert(m_orderingSendBackwardAction); + CaretAssert(m_orderingSendToBackAction); + + if (actionSelected == m_orderingBringToFrontAction) { + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_TO_FRONT); + menuSelectedFlag = true; + } + else if (actionSelected == m_orderingBringForwardAction) { + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_FORWARD); + menuSelectedFlag = true; + } + else if (actionSelected == m_orderingSendBackwardAction) { + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_BACKWARD); + menuSelectedFlag = true; + } + else if (actionSelected == m_orderingSendToBackAction) { + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_TO_BACK); + menuSelectedFlag = true; + } + } + break; + } + + return menuSelectedFlag; +} + +/** + * Called to process an annotation order operation + * + * @param orderType + * The ordering type + */ +void +AnnotationMenuArrange::processAnnotationOrderOperation(const AnnotationStackingOrderTypeEnum::Enum orderType) +{ + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + std::vector selectedAnnotations = annMan->getAnnotationsSelectedForEditing(m_browserWindowIndex); + if (selectedAnnotations.size() == 1) { + CaretAssertVectorIndex(selectedAnnotations, 0); + Annotation* selectedAnn = selectedAnnotations[0]; + std::vector sameSpaceAnnotations = annMan->getAnnotationsDrawnInSameWindowAndSpace(selectedAnn, + m_browserWindowIndex); + if ( ! sameSpaceAnnotations.empty()) { + sameSpaceAnnotations.push_back(selectedAnn); + + AString errorMessage; + if ( ! annMan->applyStackingOrder(sameSpaceAnnotations, + selectedAnn, + orderType, + m_browserWindowIndex, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + } + } +} + +/** + * Called to process a tile tab operation + * + * @param operation + * The operation + */ +void +AnnotationMenuArrange::processWindowTileTabOperation(const EventBrowserWindowTileTabOperation::Operation operation) +{ + std::vector emptyBrowserTabs; + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); + CaretAssert(bbw); + BrowserWindowContent* bwc = bbw->getBrowerWindowContent(); + CaretAssert(bwc); + + int32_t tabIndex(-1); + if (bwc->isManualModeTileTabsConfigurationEnabled()) { + std::vector selectedAnnotations = annMan->getAnnotationsSelectedForEditing(m_browserWindowIndex); + if (selectedAnnotations.size() == 1) { + CaretAssertVectorIndex(selectedAnnotations, 0); + Annotation* ann = selectedAnnotations[0]; + CaretAssert(ann); + AnnotationBrowserTab* abt = dynamic_cast(ann); + if (abt != NULL) { + tabIndex = abt->getTabIndex(); + } + else { + CaretLogSevere("Program Error: Selected annotation is not a browser tab"); + } + } + else { + CaretLogSevere("Program Error: Only one tab should be selected for an ordering operation"); + return; + } + } + + int windowViewport[4] { 0, 0, 10, 10 }; + const int32_t mouseX(50); + const int32_t mouseY(50); + EventBrowserWindowTileTabOperation tileTabOperation(operation, + bbw, + m_browserWindowIndex, + tabIndex, + windowViewport, + mouseX, + mouseY, + emptyBrowserTabs); + EventManager::get()->sendEvent(tileTabOperation.getPointer()); +} + +/** + * Expand tab selected from menu + */ +void +AnnotationMenuArrange::processShrinkAndExpandTabMenuItem() +{ + BrainBrowserWindow* window = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); + CaretAssert(window); + std::vector allTabContent; + window->getAllTabContent(allTabContent); + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + AString errorMessage; + const bool result = annMan->shrinkAndExpandSelectedBrowserTabAnnotation(allTabContent, + m_browserWindowIndex, + m_userInputMode, + errorMessage); + if ( ! result) { + WuQMessageBox::errorOk(this, + errorMessage); + } + +} /** * Apply alignment selection. @@ -266,7 +641,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->alignAnnotations(alignMod, + if ( ! annMan->alignAnnotations(m_userInputMode, + alignMod, alignment, errorMessage)) { WuQMessageBox::errorOk(this, @@ -304,7 +680,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->distributeAnnotations(distributeMod, + if ( ! annMan->distributeAnnotations(m_userInputMode, + distributeMod, distribute, errorMessage)) { WuQMessageBox::errorOk(this, @@ -327,7 +704,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyGroupingMode(m_browserWindowIndex, + if ( ! annMan->applyGroupingMode(m_userInputMode, + m_browserWindowIndex, grouping, errorMessage)) { WuQMessageBox::errorOk(this, @@ -338,6 +716,7 @@ EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } + /** * Create an alignment pixmap. * diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationMenuArrange.h connectome-workbench-1.5.0/src/GuiQt/AnnotationMenuArrange.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationMenuArrange.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationMenuArrange.h 2021-02-16 19:46:47.000000000 +0000 @@ -29,7 +29,10 @@ #include "AnnotationAlignmentEnum.h" #include "AnnotationDistributeEnum.h" #include "AnnotationGroupingModeEnum.h" - +#include "AnnotationStackingOrderTypeEnum.h" +#include "EventBrowserWindowTileTabOperation.h" +#include "UserInputModeEnum.h" +#include "UserInputModeTileTabsManualLayoutContextMenu.h" namespace caret { @@ -38,7 +41,8 @@ Q_OBJECT public: - AnnotationMenuArrange(const int32_t browserWindowIndex, + AnnotationMenuArrange(const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationMenuArrange(); @@ -52,12 +56,21 @@ void menuAboutToShow(); private: + enum class MenuMode { + ANNOTATIONS, + TILE_TABS + }; + void addAlignmentSelections(); void addDistributeSelections(); void addGroupingSelections(); + void addTileTabsSelections(); + + void addOrderingSelections(); + void applyAlignment(const AnnotationAlignmentEnum::Enum alignment); void applyDistribute(const AnnotationDistributeEnum::Enum distribute); @@ -88,13 +101,37 @@ const qreal x, const qreal y); + bool processTileTabsMenu(QAction* actionSelected); + + bool processOrderingMenuItem(QAction* actionSelected); + + void processShrinkAndExpandTabMenuItem(); + + void processWindowTileTabOperation(const EventBrowserWindowTileTabOperation::Operation operation); + + void processAnnotationOrderOperation(const AnnotationStackingOrderTypeEnum::Enum orderType); + + const UserInputModeEnum::Enum m_userInputMode; + const int32_t m_browserWindowIndex; - QAction* m_groupAction; + MenuMode m_menuMode = MenuMode::ANNOTATIONS; + + QAction* m_groupAction = NULL; + + QAction* m_regroupAction = NULL; + + QAction* m_ungroupAction = NULL; + + QAction* m_tileTabsShrinkAndExpandToFillAction = NULL; + + QAction* m_orderingBringToFrontAction = NULL; + + QAction* m_orderingBringForwardAction = NULL; - QAction* m_regroupAction; + QAction* m_orderingSendToBackAction = NULL; - QAction* m_ungroupAction; + QAction* m_orderingSendBackwardAction = NULL; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationMenuFileSelection.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationMenuFileSelection.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationMenuFileSelection.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationMenuFileSelection.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -113,9 +113,10 @@ /* * Setup file selection dialog. */ - CaretFileDialog fd(this); + CaretFileDialog fd(CaretFileDialog::Mode::MODE_SAVE, + this); fd.setAcceptMode(CaretFileDialog::AcceptSave); - fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::ANNOTATION)); + fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilterForWriting(DataFileTypeEnum::ANNOTATION)); fd.setFileMode(CaretFileDialog::AnyFile); fd.setViewMode(CaretFileDialog::List); fd.setLabelText(CaretFileDialog::Accept, "Choose"); // OK button shows Insert diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationNameWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationNameWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationNameWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationNameWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,193 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __ANNOTATION_NAME_WIDGET_DECLARE__ +#include "AnnotationNameWidget.h" +#undef __ANNOTATION_NAME_WIDGET_DECLARE__ + +#include +#include +#include + +#include "AnnotationBrowserTab.h" +#include "AnnotationMenuArrange.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "WuQtUtilities.h" + +using namespace caret; + +/** + * \class caret::AnnotationNameWidget + * \brief Widget for formatting annotations. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param userInputMode + * The user input mode + * @param browserWindowIndex + * Index of the browser window. + * @param parent + * Parent of this widget. + */ +AnnotationNameWidget::AnnotationNameWidget(const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, + QWidget* parent) +: QWidget(parent), +m_userInputMode(userInputMode), +m_browserWindowIndex(browserWindowIndex) +{ + m_nameLabel = new QLabel(); + QLabel* editTabLabel = NULL; + + m_visibilityCheckBox = NULL; + if (m_userInputMode == UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING) { + editTabLabel = new QLabel("Edit Tab(s)"); + m_visibilityCheckBox = new QCheckBox("Draw\nContent"); + m_visibilityCheckBox->setTristate(false); + QObject::connect(m_visibilityCheckBox, &QCheckBox::stateChanged, + this, &AnnotationNameWidget::visibilityCheckStateChanged); + } + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(2, 2, 2, 2); + if (editTabLabel != NULL) { + layout->addWidget(editTabLabel); + } + layout->addWidget(m_nameLabel, 0, Qt::AlignLeft); + if (m_visibilityCheckBox != NULL) { + layout->addWidget(m_visibilityCheckBox, 0, Qt::AlignLeft); + } + layout->addStretch(); + + setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +AnnotationNameWidget::~AnnotationNameWidget() +{ +} + +/** + * Update with the given annotation. + * + * @param annotations + * The selected annotations + */ +void +AnnotationNameWidget::updateContent(const std::vector& annotations) +{ + m_browserTabAnnotations.clear(); + for (auto ann : annotations) { + if (ann != NULL) { + AnnotationBrowserTab* abt = dynamic_cast(ann); + if (abt != NULL) { + m_browserTabAnnotations.push_back(abt); + } + } + } + + int32_t visibleCount(0); + int32_t hiddenCount(0); + AString nameText; + const int32_t numberOfAnnotations = static_cast(m_browserTabAnnotations.size()); + + for (auto browserTabAnnotation : m_browserTabAnnotations) { + CaretAssert(browserTabAnnotation); + const BrowserTabContent* tabContent = browserTabAnnotation->getBrowserTabContent(); + if (tabContent != NULL) { + if (numberOfAnnotations == 1) { + nameText = tabContent->getTabName(); + } + else { + if ( ! nameText.isEmpty()) { + nameText.append(","); + } + nameText.append(QString::number(tabContent->getTabNumber() + 1)); + } + } + + if (browserTabAnnotation->isBrowserTabDisplayed()) { + visibleCount++; + } + else { + hiddenCount++; + } + } + + setEnabled(numberOfAnnotations > 0); + + m_nameLabel->setText(nameText); + + if (m_visibilityCheckBox != NULL) { + /* + * Need to block signals as QCheckBox::setCheckState() + * causes a emission of a signal + */ + QSignalBlocker checkBoxBlocker(m_visibilityCheckBox); + if ((visibleCount > 0) + && (hiddenCount > 0)) { + /* Unchecked if status is mixed */ + m_visibilityCheckBox->setCheckState(Qt::Unchecked); + } + else if (visibleCount > 0) { + m_visibilityCheckBox->setCheckState(Qt::Checked); + } + else { + m_visibilityCheckBox->setCheckState(Qt::Unchecked); + } + } +} + +/** + * Called when state of visibility checkbox is changed + */ +void +AnnotationNameWidget::visibilityCheckStateChanged(int state) +{ + Qt::CheckState checkState = static_cast(state); + switch (checkState) { + case Qt::Unchecked: + for (auto bta : m_browserTabAnnotations) { + bta->setBrowserTabDisplayed(false); + } + break; + case Qt::PartiallyChecked: + break; + case Qt::Checked: + for (auto bta : m_browserTabAnnotations) { + bta->setBrowserTabDisplayed(true); + } + break; + } + + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationNameWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationNameWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationNameWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationNameWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,81 @@ +#ifndef __ANNOTATION_NAME_WIDGET_H__ +#define __ANNOTATION_NAME_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include + +#include "UserInputModeEnum.h" + +class QCheckBox; +class QLabel; + +namespace caret { + + class Annotation; + class AnnotationBrowserTab; + + class AnnotationNameWidget : public QWidget { + + Q_OBJECT + + public: + AnnotationNameWidget(const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, + QWidget* parent = 0); + + virtual ~AnnotationNameWidget(); + + + // ADD_NEW_METHODS_HERE + + void updateContent(const std::vector& annotations); + + private slots: + void visibilityCheckStateChanged(int state); + + private: + AnnotationNameWidget(const AnnotationNameWidget&); + + AnnotationNameWidget& operator=(const AnnotationNameWidget&); + + const UserInputModeEnum::Enum m_userInputMode; + + const int32_t m_browserWindowIndex; + + QLabel* m_nameLabel; + + QCheckBox* m_visibilityCheckBox = NULL; + + std::vector m_browserTabAnnotations; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __ANNOTATION_NAME_WIDGET_DECLARE__ + // +#endif // __ANNOTATION_NAME_WIDGET_DECLARE__ + +} // namespace +#endif //__ANNOTATION_NAME_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationPasteDialog.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationPasteDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationPasteDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationPasteDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -30,10 +30,12 @@ #include #include "Annotation.h" +#include "AnnotationCoordinateInformation.h" #include "AnnotationCoordinateSelectionWidget.h" #include "AnnotationFile.h" #include "AnnotationManager.h" -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationMultiCoordinateShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationPercentSizeText.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" @@ -46,6 +48,7 @@ #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "GuiManager.h" +#include "MathFunctions.h" #include "ModelSurfaceMontage.h" #include "MouseEvent.h" #include "WuQMessageBox.h" @@ -94,7 +97,7 @@ bool validCoordsFlag = false; - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(annotation); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(annotation); if (oneDimShape != NULL) { /* * Pasting line while preserving its orientation only @@ -104,11 +107,22 @@ coordInfo); } + AnnotationMultiCoordinateShape* multiCoordShape = annotation->castToMultiCoordinateShape(); + if (multiCoordShape != NULL) { + /* + * Pasting poly line while only works for tab and window spaces + */ + validCoordsFlag = pasteMultiCoordinateShape(multiCoordShape, + coordInfo); + } + + std::vector> emptyMultiCoordInfo; if (! validCoordsFlag) { validCoordsFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(annotation, annotation->getCoordinateSpace(), &coordInfo, - NULL); + NULL, + emptyMultiCoordInfo); } @@ -116,8 +130,30 @@ AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModePasteAnnotation(annotationFile, annotation); + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssert(0); + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } AString errorMessage; - if ( ! annotationManager->applyCommand(undoCommand, + if ( ! annotationManager->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(mouseEvent.getOpenGLWidget(), errorMessage); @@ -287,7 +323,7 @@ * True if the shape's coordinate was updated for pasting, else false. */ bool -AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationOneDimensionalShape* oneDimShape, +AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationTwoCoordinateShape* oneDimShape, AnnotationCoordinateInformation& coordInfo) { @@ -399,6 +435,120 @@ } /** + * Paste a multi-coordinate shape (poly-line) The first coordinate is pasted at + * the coordinate in 'coordInfo'. + * + * @param multiCoordShape + * multi-coordinate shape that will be pasted. + * @param coordInfo + * Coordinate information that will be used for the shape's 'start' coordinate. + * @return + * True if the shape's coordinate was updated for pasting, else false. + */ +bool +AnnotationPasteDialog::pasteMultiCoordinateShape(AnnotationMultiCoordinateShape* multiCoordShape, + AnnotationCoordinateInformation& coordInfo) +{ + const int32_t numCoords = multiCoordShape->getNumberOfCoordinates(); + if (numCoords < 2) { + return false; + } + + bool tabFlag = false; + bool windowFlag = false; + + switch (multiCoordShape->getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + tabFlag = true; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + windowFlag = true; + break; + } + + bool validCoordsFlag = false; + + if (tabFlag + || windowFlag) { + float startXYZ[3]; + multiCoordShape->getCoordinate(0)->getXYZ(startXYZ); + + const float originalStartXYZ[3] = { + startXYZ[0], + startXYZ[1], + startXYZ[2] + }; + + if (tabFlag + && (coordInfo.m_tabSpaceInfo.m_index >= 0)) { + startXYZ[0] = coordInfo.m_tabSpaceInfo.m_xyz[0]; + startXYZ[1] = coordInfo.m_tabSpaceInfo.m_xyz[1]; + startXYZ[2] = coordInfo.m_tabSpaceInfo.m_xyz[2]; + multiCoordShape->setTabIndex(coordInfo.m_tabSpaceInfo.m_index); + validCoordsFlag = true; + } + else if (windowFlag + && (coordInfo.m_windowSpaceInfo.m_index >= 0)) { + startXYZ[0] = coordInfo.m_windowSpaceInfo.m_xyz[0]; + startXYZ[1] = coordInfo.m_windowSpaceInfo.m_xyz[1]; + startXYZ[2] = coordInfo.m_windowSpaceInfo.m_xyz[2]; + multiCoordShape->setWindowIndex(coordInfo.m_windowSpaceInfo.m_index); + validCoordsFlag = true; + } + + if (validCoordsFlag) { + /* + * Set first coordinate + */ + multiCoordShape->getCoordinate(0)->setXYZ(startXYZ); + + /* + * Set additional coordinates + */ + for (int32_t iCoord = 1; iCoord < numCoords; iCoord++) { + float ptXYZ[3]; + multiCoordShape->getCoordinate(iCoord)->getXYZ(ptXYZ); + + const float diffXYZ[3] = { + startXYZ[0] - originalStartXYZ[0], + startXYZ[1] - originalStartXYZ[1], + startXYZ[2] - originalStartXYZ[2] + }; + if (validCoordsFlag) { + ptXYZ[0] += diffXYZ[0]; // + originalStartXYZ[0]; + ptXYZ[1] += diffXYZ[1]; //+ originalStartXYZ[1]; + ptXYZ[2] += diffXYZ[2]; // + originalStartXYZ[2]; + + /* + * Tab/Window coordinates are percentage ranging [0.0, 100.0] + * Need to "clip" lines if they exceed the viewport's edges + */ + const float minCoord = 1.0; + const float maxCoord = 99.0; + + ptXYZ[0] = MathFunctions::limitRange(ptXYZ[0], minCoord, maxCoord); + ptXYZ[1] = MathFunctions::limitRange(ptXYZ[1], minCoord, maxCoord); + + multiCoordShape->getCoordinate(iCoord)->setXYZ(ptXYZ); + } + } + } + } + + return validCoordsFlag; +} + +/** * Gets called when the OK button is clicked. */ void @@ -438,8 +588,30 @@ AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModePasteAnnotation(m_annotationFile, annotationPointer); - if ( ! annotationManager->applyCommand(undoCommand, - errorMessage)) { + switch (annotationPointer->getType()) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + CaretAssert(0); + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } + if ( ! annotationManager->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, + errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationPasteDialog.h connectome-workbench-1.5.0/src/GuiQt/AnnotationPasteDialog.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationPasteDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationPasteDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -32,7 +32,8 @@ class AnnotationCoordinateInformation; class AnnotationCoordinateSelectionWidget; class AnnotationFile; - class AnnotationOneDimensionalShape; + class AnnotationMultiCoordinateShape; + class AnnotationTwoCoordinateShape; class MouseEvent; class AnnotationPasteDialog : public WuQDialogModal { @@ -62,9 +63,12 @@ AnnotationPasteDialog& operator=(const AnnotationPasteDialog&); - static bool pasteOneDimensionalShape(AnnotationOneDimensionalShape* oneDimShape, + static bool pasteOneDimensionalShape(AnnotationTwoCoordinateShape* oneDimShape, AnnotationCoordinateInformation& coordInfo); + static bool pasteMultiCoordinateShape(AnnotationMultiCoordinateShape* multiCoordShape, + AnnotationCoordinateInformation& coordInfo); + virtual void okButtonClicked(); void adjustTextAnnotationFontHeight(const AnnotationCoordinateSpaceEnum::Enum previousSpace, diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationRedoUndoWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationRedoUndoWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationRedoUndoWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationRedoUndoWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -50,10 +50,20 @@ /** * Constructor. + * + * @param userInputMode + * The user input mode + * @param browserWindowIndex + * The browser window index + * @param parent + * The parent widget. */ -AnnotationRedoUndoWidget::AnnotationRedoUndoWidget(const int32_t browserWindowIndex, +AnnotationRedoUndoWidget::AnnotationRedoUndoWidget(const Qt::Orientation orientation, + const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), +m_userInputMode(userInputMode), m_browserWindowIndex(browserWindowIndex) { QLabel* titleLabel = new QLabel("Edit"); @@ -78,12 +88,24 @@ QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); - gridLayout->addWidget(titleLabel, - 0, 0, 1, 1, Qt::AlignHCenter); - gridLayout->addWidget(redoToolButton, - 1, 0, Qt::AlignHCenter); - gridLayout->addWidget(undoToolButton, - 2, 0, Qt::AlignHCenter); + switch (orientation) { + case Qt::Horizontal: + gridLayout->addWidget(titleLabel, + 0, 0, 1, 2, Qt::AlignHCenter); + gridLayout->addWidget(redoToolButton, + 1, 0, Qt::AlignHCenter); + gridLayout->addWidget(undoToolButton, + 1, 1, Qt::AlignHCenter); + break; + case Qt::Vertical: + gridLayout->addWidget(titleLabel, + 0, 0, Qt::AlignHCenter); + gridLayout->addWidget(redoToolButton, + 1, 0, Qt::AlignHCenter); + gridLayout->addWidget(undoToolButton, + 2, 0, Qt::AlignHCenter); + break; + } setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -98,21 +120,26 @@ } /** - * Update with the given line annotation. + * Update with the selected annotations. * - * @param annotationLine + * @param annotations + * The selected annotations */ void -AnnotationRedoUndoWidget::updateContent() +AnnotationRedoUndoWidget::updateContent(const std::vector& annotations) { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); + CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(m_userInputMode); m_redoAction->setEnabled(undoStack->canRedo()); m_redoAction->setToolTip(undoStack->redoText()); m_undoAction->setEnabled(undoStack->canUndo()); m_undoAction->setToolTip(undoStack->undoText()); + + setEnabled(( ! annotations.empty()) + || m_redoAction->isEnabled() + || m_undoAction->isEnabled()); } @@ -123,7 +150,7 @@ AnnotationRedoUndoWidget::redoActionTriggered() { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); + CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(m_userInputMode); AString errorMessage; if ( ! undoStack->redoInWindow(m_browserWindowIndex, @@ -143,7 +170,7 @@ AnnotationRedoUndoWidget::undoActionTriggered() { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); + CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(m_userInputMode); AString errorMessage; if ( ! undoStack->undoInWindow(m_browserWindowIndex, diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationRedoUndoWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationRedoUndoWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationRedoUndoWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationRedoUndoWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -26,21 +26,24 @@ #include - +#include "UserInputModeEnum.h" namespace caret { + class Annotation; class AnnotationRedoUndoWidget : public QWidget { Q_OBJECT public: - AnnotationRedoUndoWidget(const int32_t browserWindowIndex, + AnnotationRedoUndoWidget(const Qt::Orientation orientation, + const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationRedoUndoWidget(); - void updateContent(); + void updateContent(const std::vector& annotations); private slots: void redoActionTriggered(); @@ -55,6 +58,8 @@ AnnotationRedoUndoWidget& operator=(const AnnotationRedoUndoWidget&); + const UserInputModeEnum::Enum m_userInputMode; + const int32_t m_browserWindowIndex; QAction* m_redoAction; diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationRotationWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationRotationWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationRotationWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationRotationWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -32,9 +32,9 @@ #include "AnnotationManager.h" -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationRedoUndoCommand.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGetViewportSize.h" @@ -64,9 +64,11 @@ * @param parent * Parent of this widget. */ -AnnotationRotationWidget::AnnotationRotationWidget(const int32_t browserWindowIndex, +AnnotationRotationWidget::AnnotationRotationWidget(const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), +m_userInputMode(userInputMode), m_browserWindowIndex(browserWindowIndex) { QLabel* rotationLabel = new QLabel(" R:"); @@ -102,10 +104,10 @@ * stereotaxic space for rotation angle. * */ -AnnotationOneDimensionalShape* +AnnotationTwoCoordinateShape* AnnotationRotationWidget::getValidOneDimAnnotation(Annotation* annotation) { - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); + AnnotationTwoCoordinateShape* oneDimAnn = dynamic_cast(annotation); if (oneDimAnn != NULL) { bool validSpaceFlag = false; @@ -158,8 +160,8 @@ Annotation* ann = annotations[i]; if (ann->testProperty(Annotation::Property::ROTATION)) { if (ann->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(ann); - AnnotationOneDimensionalShape* oneDimAnn = getValidOneDimAnnotation(ann); + AnnotationOneCoordinateShape* twoDimAnn = dynamic_cast(ann); + AnnotationTwoCoordinateShape* oneDimAnn = getValidOneDimAnnotation(ann); float angle = 0.0; float angleValid = false; @@ -273,7 +275,8 @@ m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationRotationWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationRotationWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationRotationWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationRotationWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -26,19 +26,22 @@ #include #include +#include "UserInputModeEnum.h" + class QDoubleSpinBox; namespace caret { class Annotation; - class AnnotationOneDimensionalShape; + class AnnotationTwoCoordinateShape; class AnnotationRotationWidget : public QWidget { Q_OBJECT public: - AnnotationRotationWidget(const int32_t browserWindowIndex, + AnnotationRotationWidget(const UserInputModeEnum::Enum userInputMode, + const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationRotationWidget(); @@ -56,10 +59,12 @@ AnnotationRotationWidget& operator=(const AnnotationRotationWidget&); - AnnotationOneDimensionalShape* getValidOneDimAnnotation(Annotation* annotation); + AnnotationTwoCoordinateShape* getValidOneDimAnnotation(Annotation* annotation); std::vector m_annotations; + const UserInputModeEnum::Enum m_userInputMode; + const int32_t m_browserWindowIndex; QDoubleSpinBox* m_rotationSpinBox; diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -34,7 +34,6 @@ #include "DisplayGroupAndTabItemViewController.h" #include "DisplayGroupEnumComboBox.h" #include "DisplayPropertiesAnnotation.h" -#include "EventGetOrSetUserInputModeProcessor.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationTextAlignmentWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationTextAlignmentWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationTextAlignmentWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationTextAlignmentWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -305,7 +305,8 @@ annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -339,7 +340,8 @@ annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationTextEditorDialog.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationTextEditorDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationTextEditorDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationTextEditorDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -133,7 +133,8 @@ undoCommand->setModeTextCharacters(text, annotationVector); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationTextEditorWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationTextEditorWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationTextEditorWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationTextEditorWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -38,6 +38,7 @@ #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" +#include "UserInputModeEnum.h" #include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -82,6 +83,13 @@ QObject::connect(m_annotationTextConnectTypeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(annotationTextConnectTypeEnumComboBoxItemActivated())); + /* + * Limit text edit width to width of combo box + */ + const int32_t width = m_annotationTextConnectTypeEnumComboBox->getWidget()->sizeHint().width(); + m_annotationTextConnectTypeEnumComboBox->getWidget()->setFixedWidth(width); + m_textLineEdit->setFixedWidth(width); + QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(textLabel, 0, Qt::AlignHCenter); @@ -90,7 +98,6 @@ setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); - setMaximumWidth(200); } /** @@ -196,7 +203,8 @@ undoCommand->setModeTextCharacters(s, selectedAnnotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -241,7 +249,8 @@ selectedAnnotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationTextOrientationWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationTextOrientationWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationTextOrientationWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationTextOrientationWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -189,7 +189,8 @@ annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationTextSubstitutionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationTextSubstitutionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationTextSubstitutionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationTextSubstitutionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -39,7 +39,6 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "DisplayPropertiesAnnotationTextSubstitution.h" -#include "EventGetOrSetUserInputModeProcessor.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventMapYokingSelectMap.h" #include "EventSurfaceColoringInvalidate.h" @@ -365,47 +364,6 @@ fgc->m_valueIndexSpinBox->setVisible(visibleFlag); fgc->m_fileNameLabel->setVisible(visibleFlag); } - - - - -// const DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); -// -// BrowserTabContent* browserTabContent = -// GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); -// if (browserTabContent == NULL) { -// return; -// } -// const int32_t browserTabIndex = browserTabContent->getTabNumber(); -// -// m_displayAnnotationsCheckBox->setChecked(dpa->isDisplayAnnotations()); -// m_displayTextAnnotationsCheckBox->setChecked(dpa->isDisplayTextAnnotations()); -// m_displayWindowAnnotationInSingleTabViewsCheckBox->setChecked(dpa->isDisplayWindowAnnotationsInSingleTabViews(m_browserWindowIndex)); -// -// Brain* brain = GuiManager::get()->getBrain(); -// std::vector annotationFiles; -// brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); -// -// std::vector fileItems; -// for (std::vector::iterator fileIter = annotationFiles.begin(); -// fileIter != annotationFiles.end(); -// fileIter++) { -// AnnotationFile* annFile = *fileIter; -// fileItems.push_back(annFile); -// } -// -// const DisplayGroupEnum::Enum displayGroup = dpa->getDisplayGroupForTab(browserTabIndex); -// m_displayGroupComboBox->setSelectedDisplayGroup(displayGroup); -// -// EventGetOrSetUserInputModeProcessor inputModeEvent(m_browserWindowIndex); -// EventManager::get()->sendEvent(inputModeEvent.getPointer()); -// UserInputModeAbstract::UserInputMode mode = inputModeEvent.getUserInputMode(); -// const bool annotationsValidFlag = (mode == UserInputModeAbstract::ANNOTATIONS); -// -// m_selectionViewController->updateContent(fileItems, -// displayGroup, -// browserTabIndex, -// annotationsValidFlag); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationWidthHeightWidget.cxx connectome-workbench-1.5.0/src/GuiQt/AnnotationWidthHeightWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/AnnotationWidthHeightWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationWidthHeightWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -24,13 +24,14 @@ #undef __ANNOTATION_WIDTH_HEIGHT_WIDGET_DECLARE__ #include +#include #include #include #include "AnnotationBox.h" #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" @@ -60,10 +61,13 @@ * @param parent * Parent of this widget. */ -AnnotationWidthHeightWidget::AnnotationWidthHeightWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, +AnnotationWidthHeightWidget::AnnotationWidthHeightWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, const int32_t browserWindowIndex, + const Qt::Orientation orientation, QWidget* parent) : QWidget(parent), +m_userInputMode(userInputMode), m_parentWidgetType(parentWidgetType), m_browserWindowIndex(browserWindowIndex) { @@ -92,12 +96,33 @@ WuQtUtilities::setWordWrappedToolTip(m_heightSpinBox, "Percentage height of 2D Shapes (Box, Image, Oval)"); - QHBoxLayout* layout = new QHBoxLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); - layout->addWidget(widthLabel); - layout->addWidget(m_widthSpinBox); - layout->addWidget(heightLabel); - layout->addWidget(m_heightSpinBox); + switch (orientation) { + case Qt::Horizontal: + { + QHBoxLayout* layout = new QHBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); + layout->addWidget(widthLabel); + layout->addWidget(m_widthSpinBox); + layout->addWidget(heightLabel); + layout->addWidget(m_heightSpinBox); + } + break; + case Qt::Vertical: + { + QLabel* sizeLabel = new QLabel("Size"); + QGridLayout* layout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); + int32_t row(0); + layout->addWidget(sizeLabel, row, 0, 1, 2, Qt::AlignHCenter); + row++; + layout->addWidget(widthLabel, row, 0); + layout->addWidget(m_widthSpinBox, row, 1); + row++; + layout->addWidget(heightLabel, row, 0); + layout->addWidget(m_heightSpinBox, row, 1); + } + break; + } setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -117,9 +142,38 @@ * Two dimensional annotation. */ void -AnnotationWidthHeightWidget::updateContent(std::vector& annotations2D) +AnnotationWidthHeightWidget::updateContent(std::vector& annotations2D) { - m_annotations2D = annotations2D; + m_annotations2D.clear(); + for (auto a2d : annotations2D) { + bool includeFlag(true); + + switch (a2d->getType()) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + /* Scale bar width/height not adjustable */ + includeFlag = false; + break; + case AnnotationTypeEnum::TEXT: + break; + } + if (includeFlag) { + m_annotations2D.push_back(a2d); + } + } if ( ! m_annotations2D.empty()) { float widthValue = 0.0; @@ -172,6 +226,7 @@ else { m_widthSpinBox->setSuffix("%"); } + m_widthSpinBox->setEnabled(true); m_widthSpinBox->blockSignals(false); m_heightSpinBox->blockSignals(true); @@ -187,8 +242,8 @@ switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - AnnotationTwoDimensionalShape::setUserDefaultWidth(widthValue); - AnnotationTwoDimensionalShape::setUserDefaultHeight(heightValue); + AnnotationOneCoordinateShape::setUserDefaultWidth(widthValue); + AnnotationOneCoordinateShape::setUserDefaultHeight(heightValue); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); @@ -223,7 +278,8 @@ annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -233,7 +289,7 @@ switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - AnnotationTwoDimensionalShape::setUserDefaultHeight(value); + AnnotationOneCoordinateShape::setUserDefaultHeight(value); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); @@ -255,12 +311,14 @@ { std::vector annotations(m_annotations2D.begin(), m_annotations2D.end()); + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTwoDimWidth(value, annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -269,7 +327,7 @@ switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: - AnnotationTwoDimensionalShape::setUserDefaultWidth(value); + AnnotationOneCoordinateShape::setUserDefaultWidth(value); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); diff -Nru connectome-workbench-1.4.2/src/GuiQt/AnnotationWidthHeightWidget.h connectome-workbench-1.5.0/src/GuiQt/AnnotationWidthHeightWidget.h --- connectome-workbench-1.4.2/src/GuiQt/AnnotationWidthHeightWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/AnnotationWidthHeightWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -27,20 +27,23 @@ #include #include "AnnotationWidgetParentEnum.h" +#include "UserInputModeEnum.h" class QDoubleSpinBox; namespace caret { - class AnnotationTwoDimensionalShape; + class AnnotationOneCoordinateShape; class AnnotationWidthHeightWidget : public QWidget { Q_OBJECT public: - AnnotationWidthHeightWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, + AnnotationWidthHeightWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, const int32_t browserWindowIndex, + const Qt::Orientation orientation, QWidget* parent = 0); virtual ~AnnotationWidthHeightWidget(); @@ -48,7 +51,7 @@ // ADD_NEW_METHODS_HERE - void updateContent(std::vector& annotations2D); + void updateContent(std::vector& annotations2D); private slots: void widthValueChanged(double value); @@ -62,6 +65,8 @@ // ADD_NEW_MEMBERS_HERE + const UserInputModeEnum::Enum m_userInputMode; + const AnnotationWidgetParentEnum::Enum m_parentWidgetType; const int32_t m_browserWindowIndex; @@ -70,7 +75,7 @@ QDoubleSpinBox* m_heightSpinBox; - std::vector m_annotations2D; + std::vector m_annotations2D; }; diff -Nru connectome-workbench-1.4.2/src/GuiQt/BalsaDatabaseManager.cxx connectome-workbench-1.5.0/src/GuiQt/BalsaDatabaseManager.cxx --- connectome-workbench-1.4.2/src/GuiQt/BalsaDatabaseManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BalsaDatabaseManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -28,6 +28,7 @@ #include +#include "ApplicationInformation.h" #include "BalsaUserRoles.h" #include "CaretAssert.h" #include "CaretHttpManager.h" @@ -340,7 +341,7 @@ errorMessageOut = ("Upload failed. (Http Code=" + AString::number(responseHttpCode) + ").\n\n" - "Either you do now have ownership/permission to edit the study or " + "Either you do not have ownership/permission to edit the study or " "the study has been submitted for curation.\n\n" "Use your web browser to login to BALSA to view the study and " "check its permissions. If the the study has been submitted for " @@ -1441,7 +1442,7 @@ if (getAllStudyInformation(studyInformation, errorMessage)) { - for (const auto info : studyInformation) { + for (const auto& info : studyInformation) { if (info.getStudyID() == studyID) { return true; } @@ -1551,6 +1552,8 @@ return false; } + CaretLogFine("Uploading to BALSA with Version " + ApplicationInformation().getVersion()); + enum ProgressEnum { PROGRESS_NONE, PROGRESS_LOGIN, diff -Nru connectome-workbench-1.4.2/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx connectome-workbench-1.5.0/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -704,7 +704,7 @@ errorMessage)) { if ( ! m_userRoles->isSubmitter()) { cursor.restoreCursor(); - const AString msg("Login to BALSA was successful. However, your have not agreed " + const AString msg("Login to BALSA was successful. However, you have not agreed " "to the BALSA Submission Terms and Conditions that are required " "to upload data to BALSA. You will need to login to BALSA " "using your web browser and complete this agreement."); @@ -1149,6 +1149,7 @@ if ( ! bsi.isEmpty()) { m_balsaStudyIDLineEdit->setText(bsi.getStudyID()); m_balsaStudyTitleLineEdit->setText(bsi.getStudyTitle()); + m_extractDirectoryNameLineEdit->setText(bsi.getStudyExtractionDirectoryName()); } validateUploadData(); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/BalsaStudyInformation.cxx connectome-workbench-1.5.0/src/GuiQt/BalsaStudyInformation.cxx --- connectome-workbench-1.4.2/src/GuiQt/BalsaStudyInformation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BalsaStudyInformation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -52,12 +52,16 @@ * The Study Identifier * @param studyTitle * The Study Title + * @param studyExtractionDirectoryName + * The extraction directory name */ BalsaStudyInformation::BalsaStudyInformation(const AString& studyID, - const AString& studyTitle) + const AString& studyTitle, + const AString& studyExtractionDirectoryName) : CaretObject(), m_studyID(studyID), m_studyTitle(studyTitle), +m_studyExtractionDirectoryName(studyExtractionDirectoryName), m_editableStatus(false) { } @@ -76,6 +80,11 @@ && (titleIter != jsonObject.end())) { m_studyID = (*idIter).toString(); m_studyTitle = (*titleIter).toString(); + + QJsonObject::const_iterator extractIter = jsonObject.find("extract"); + if (extractIter != jsonObject.end()) { + m_studyExtractionDirectoryName = (*extractIter).toString(); + } QJsonObject::const_iterator statusIter = jsonObject.find("status"); if (statusIter != jsonObject.end()) { @@ -149,6 +158,7 @@ { m_studyID = obj.m_studyID; m_studyTitle = obj.m_studyTitle; + m_studyExtractionDirectoryName = obj.m_studyExtractionDirectoryName; m_editableStatus = obj.m_editableStatus; } @@ -202,6 +212,15 @@ } /** + * @return extraction directory name + */ +AString +BalsaStudyInformation::getStudyExtractionDirectoryName() const +{ + return m_studyExtractionDirectoryName; +} + +/** * @return True is the study is editable. */ bool diff -Nru connectome-workbench-1.4.2/src/GuiQt/BalsaStudyInformation.h connectome-workbench-1.5.0/src/GuiQt/BalsaStudyInformation.h --- connectome-workbench-1.4.2/src/GuiQt/BalsaStudyInformation.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BalsaStudyInformation.h 2021-02-16 19:46:47.000000000 +0000 @@ -36,7 +36,8 @@ BalsaStudyInformation(); BalsaStudyInformation(const AString& studyID, - const AString& studyTitle); + const AString& studyTitle, + const AString& studyExtractionDirectoryName); virtual ~BalsaStudyInformation(); @@ -56,6 +57,8 @@ void setStudyTitle(const AString& studyTitle); + AString getStudyExtractionDirectoryName() const; + bool isEditable() const; // ADD_NEW_METHODS_HERE @@ -71,6 +74,8 @@ /** study title*/ AString m_studyTitle; + AString m_studyExtractionDirectoryName; + /** editable status (true is YES) */ bool m_editableStatus = false; diff -Nru connectome-workbench-1.4.2/src/GuiQt/BalsaStudySelectionDialog.cxx connectome-workbench-1.5.0/src/GuiQt/BalsaStudySelectionDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/BalsaStudySelectionDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BalsaStudySelectionDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -242,8 +242,10 @@ return; } } + QString dummyExtractionDirectoryName; m_studyInformation.emplace_back(studyID, - title); + title, + dummyExtractionDirectoryName); loadStudyTitleTableWidget(title); } else { diff -Nru connectome-workbench-1.4.2/src/GuiQt/BorderPropertiesEditorDialog.cxx connectome-workbench-1.5.0/src/GuiQt/BorderPropertiesEditorDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/BorderPropertiesEditorDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BorderPropertiesEditorDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -339,7 +339,7 @@ if (borderFile->getStructure() == borderStructure) { const AString name = borderFile->getFileNameNoPath(); m_borderFileSelectionComboBox->addItem(name, - qVariantFromValue((void*)borderFile)); + QVariant::fromValue((void*)borderFile)); if (s_previousBorderFile == borderFile) { defaultFileComboIndex = m_borderFileSelectionComboBox->count() - 1; } diff -Nru connectome-workbench-1.4.2/src/GuiQt/BorderSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/BorderSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/BorderSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BorderSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -207,13 +207,12 @@ "Select border coloring type"); QLabel* standardColorLabel = new QLabel("Standard Color"); - m_standardColorComboBox = new CaretColorEnumComboBox("", - QIcon(), - (m_objectNamePrefix - + ":Color"), - "Set border standard color", - this); + m_standardColorComboBox = new CaretColorEnumComboBox(this); + m_standardColorComboBox->getWidget()->setObjectName((m_objectNamePrefix + + ":Color")); m_standardColorComboBox->getWidget()->setToolTip("Select the standard color"); + WuQMacroManager::instance()->addMacroSupportToObject(m_standardColorComboBox->getComboBox(), + "Select border standard color"); QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(processAttributesChanges())); diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowComboBox.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowComboBox.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowComboBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowComboBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -108,7 +108,7 @@ break; } m_comboBox->setItemData(i, - qVariantFromValue((void*)bbw)); + QVariant::fromValue((void*)bbw)); if (bbw == selectedWindow) { defaultIndex = i; diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindow.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindow.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindow.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindow.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -63,16 +63,17 @@ #include "DataFileException.h" #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesImages.h" -#include "DisplayPropertiesVolume.h" #include "EventBrowserWindowNew.h" #include "CaretLogger.h" #include "ElapsedTimer.h" #include "EventGetViewportSize.h" +#include "EventBrowserTabReopenAvailable.h" #include "EventBrowserWindowCreateTabs.h" #include "EventBrowserWindowContent.h" +#include "EventBrowserWindowGetTabs.h" +#include "EventBrowserTabIndicesGetAllViewed.h" #include "EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h" #include "EventDataFileRead.h" -#include "EventMacDockMenuUpdate.h" #include "EventManager.h" #include "EventModelGetAll.h" #include "EventGetOrSetUserInputModeProcessor.h" @@ -81,7 +82,7 @@ #include "EventGraphicsUpdateOneWindow.h" #include "EventSpecFileReadDataFiles.h" #include "EventSurfaceColoringInvalidate.h" -#include "EventTileTabsConfigurationModification.h" +#include "EventTileTabsGridConfigurationModification.h" #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "FociProjectionDialog.h" @@ -93,6 +94,7 @@ #include "ModelWholeBrain.h" #include "PlainTextStringBuilder.h" #include "ProgressReportingDialog.h" +#include "RecentFilesDialog.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" @@ -109,8 +111,9 @@ #include "Surface.h" #include "SurfaceMontageConfigurationAbstract.h" #include "SurfaceSelectionViewController.h" -#include "TileTabsConfiguration.h" -#include "TileTabsConfigurationModifier.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "TileTabsLayoutManualConfiguration.h" +#include "TileTabsGridConfigurationModifier.h" #include "WuQDataEntryDialog.h" #include "WuQDoubleSpinBox.h" #include "WuQMacroManager.h" @@ -246,12 +249,6 @@ createMenus(); - if (s_enableMacDuplicateMenuBarFlag) { - s_enableMacDuplicateMenuBarFlag = false; - - m_toolbar->insertDuplicateMenuBar(this); - } - m_toolbar->updateToolBar(); processShowOverlayToolBox(m_overlayToolBoxAction->isChecked()); @@ -270,6 +267,8 @@ m_defaultWindowComponentStatus.isToolBarDisplayed = m_showToolBarAction->isChecked(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_GET_TABS); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_AND_MAPS_IN_DISPLAYED_OVERLAYS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_VIEWPORT_SIZE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); @@ -341,6 +340,38 @@ event->setEventProcessed(); } } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_GET_TABS) { + EventBrowserWindowGetTabs* tabEvent = dynamic_cast(event); + CaretAssert(tabEvent); + if (tabEvent->getBrowserWindowIndex() == m_browserWindowIndex) { + std::vector tabContent; + m_toolbar->getAllTabContent(tabContent); + for (auto tab : tabContent) { + tabEvent->addBrowserTab(tab); + } + tabEvent->setEventProcessed(); + } + } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL_VIEWED) { + EventBrowserTabIndicesGetAllViewed* allViewedEvent = dynamic_cast(event); + CaretAssert(allViewedEvent); + + std::vector tabContent; + if (isTileTabsSelected()) { + m_toolbar->getAllTabContent(tabContent); + } + else { + BrowserTabContent* btc = getBrowserTabContent(); + if (btc != NULL) { + tabContent.push_back(btc); + } + } + for (auto tab : tabContent) { + CaretAssert(tab); + allViewedEvent->addBrowserTabIndex(tab->getTabNumber()); + } + allViewedEvent->setEventProcessed(); + } else if (event->getEventType()== EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_AND_MAPS_IN_DISPLAYED_OVERLAYS) { EventCaretMappableDataFilesAndMapsInDisplayedOverlays* filesEvent = dynamic_cast(event); CaretAssert(filesEvent); @@ -510,6 +541,26 @@ } } break; + case EventGetViewportSize::MODE_WINDOW_FROM_TAB_INDEX: + for (std::vector::iterator vpIter = allViewportContent.begin(); + vpIter != allViewportContent.end(); + vpIter++) { + const BrainOpenGLViewportContent* vpContent = *vpIter; + if (vpContent != NULL) { + BrowserTabContent* btc = vpContent->getBrowserTabContent(); + if (btc != NULL) { + if (btc->getTabNumber() == viewportSizeEvent->getIndex()) { + /* + * Found Tab so report Window viewport containing tab + */ + vpContent->getWindowViewport(viewport); + viewportValid = true; + break; + } + } + } + } + break; } if ( ! viewportValid) { @@ -535,10 +586,11 @@ if (viewportValid) { viewportSizeEvent->setViewportSize(viewport); + viewportSizeEvent->setEventProcessed(); } } else if (event->getEventType() == EventTypeEnum::EVENT_TILE_TABS_MODIFICATION) { - EventTileTabsConfigurationModification* modEvent = dynamic_cast(event); + EventTileTabsGridConfigurationModification* modEvent = dynamic_cast(event); CaretAssert(modEvent); if (modEvent->getWindowIndex() == this->m_browserWindowIndex) { @@ -1344,6 +1396,17 @@ WuQMacroManager::instance()->addMacroSupportToObject(m_duplicateTabAction, ("Duplicate tab in Window " + QString::number(m_browserWindowIndex + 1))); + + m_reopenLastClosedTabAction = + WuQtUtilities::createAction("Reopen Last Closed Tab", + "Reopen the last closed tab", + Qt::CTRL + Qt::SHIFT + Qt::Key_T, + this); + QObject::connect(m_reopenLastClosedTabAction, &QAction::triggered, + this, [=] { guiManager->processReopenLastClosedTab(this); }); + m_reopenLastClosedTabAction->setObjectName(m_objectNamePrefix + + ":Menu:ReopenLastClosedTabAction"); /* NOTE: No Macro support for this item */ + m_openFileAction = WuQtUtilities::createAction("Open File...", "Open a data file including a spec file located on the computer", @@ -1362,6 +1425,14 @@ SLOT(processDataFileLocationOpen())); m_openLocationAction->setShortcutContext(Qt::ApplicationShortcut); + m_openRecentAction = + WuQtUtilities::createAction("Open Recent...", + "Open a recent file or directory", + Qt::CTRL + Qt::SHIFT + Qt::Key_O, + this, + this, + SLOT(processOpenRecent())); + m_manageFilesAction = WuQtUtilities::createAction("Save/Manage Files...", "Save and Manage Loaded Files", @@ -1384,7 +1455,7 @@ Qt::CTRL + Qt::Key_W, this, m_toolbar, - SLOT(closeSelectedTab())); + SLOT(closeSelectedTabFromFileMenu())); m_closeWithoutConfirmationFlag = false; m_closeWindowActionConfirmTitle = "Close Window..."; @@ -1446,7 +1517,11 @@ this, this, SLOT(processSplitBorderFiles())); - + + m_dataPaletteEditorDialogAction = new QAction("Palette Editor..."); + QObject::connect(m_dataPaletteEditorDialogAction, &QAction::triggered, + guiManager, [=]() { guiManager->processShowPaletteEditorDialog(this); } ); + m_viewFullScreenAction = WuQtUtilities::createAction("Full Screen", "View using all of screen", Qt::CTRL+Qt::Key_F, @@ -1492,10 +1567,15 @@ this); m_viewCustomTileTabsConfigurationAction->setCheckable(true); + m_viewManualTileTabsConfigurationAction = new QAction("Manual", + this); + m_viewManualTileTabsConfigurationAction->setCheckable(true); + QActionGroup* autoCustomGroup = new QActionGroup(this); autoCustomGroup->setExclusive(true); autoCustomGroup->addAction(m_viewAutomaticTileTabsConfigurationAction); autoCustomGroup->addAction(m_viewCustomTileTabsConfigurationAction); + autoCustomGroup->addAction(m_viewManualTileTabsConfigurationAction); QObject::connect(autoCustomGroup, &QActionGroup::triggered, this, &BrainBrowserWindow::processViewTileTabsAutomaticCustomTriggered); @@ -1571,6 +1651,13 @@ this, SLOT(processHcpWebsiteInBrowser())); + m_helpHcpUsersAction = + WuQtUtilities::createAction("Workbench Support (Ask a Question)...", + "Get Workbench Support at HCP Users Group", + this, + this, + SLOT(processHcpUsersGroup())); + m_helpHcpFeatureRequestAction = WuQtUtilities::createAction("Submit HCP Software Feature Request...", "Go to HCP Feature Request Website in your computer's web browser", @@ -1602,13 +1689,24 @@ m_connectToConnectomeDatabaseAction->setEnabled(false); m_developerGraphicsTimingAction = - WuQtUtilities::createAction("Time Graphics Update", + WuQtUtilities::createAction(("Time Graphics Update for " + + AString::number(m_developerTimingIterations) + + " Iterations"), "Show the average time for updating the windows graphics", this, this, SLOT(processDevelopGraphicsTiming())); - m_developerExportVtkFileAction = + m_developerGraphicsTimingDurationAction = + WuQtUtilities::createAction(("Time Graphics Update for " + + AString::number(m_developerTimingDuration, 'f', 0) + + " Seconds"), + "Show the average time for updating the windows graphics", + this, + this, + SLOT(processDevelopGraphicsTimingDuration())); + + m_developerExportVtkFileAction = WuQtUtilities::createAction("Export to VTK File", "Export model(s) to VTK File", this, @@ -1628,6 +1726,7 @@ * Create the menu bar and add menus to it. */ QMenuBar* menubar = menuBar(); + menubar->addMenu(createMenuFile()); menubar->addMenu(createMenuEdit()); @@ -1679,6 +1778,7 @@ m_developerExportVtkFileAction->setVisible(false); menu->addAction(m_developerGraphicsTimingAction); + menu->addAction(m_developerGraphicsTimingDurationAction); std::vector developerFlags; DeveloperFlagsEnum::getAllEnums(developerFlags); @@ -1714,6 +1814,8 @@ } } + menu->addAction(m_dataPaletteEditorDialogAction); + return menu; } @@ -1787,6 +1889,7 @@ CaretAssert(balsaDialog); balsaDialog->show(); } +#endif /* * Update graphics and GUI @@ -1794,12 +1897,6 @@ EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); -#else - if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_BALSA) { - WuQMessageBox::informationOk(this, - "Software was built without Qt WebKit, BALSA test not available, see src/CMakeLists.txt"); - } -#endif } else { CaretLogSevere("Failed to find develper flag for reading menu: " @@ -1827,25 +1924,11 @@ menu->addAction(m_newWindowAction); menu->addAction(m_newTabAction); menu->addAction(m_duplicateTabAction); + menu->addAction(m_reopenLastClosedTabAction); menu->addSeparator(); menu->addAction(m_openFileAction); + menu->addAction(m_openRecentAction); menu->addAction(m_openLocationAction); - - m_recentSpecFileMenuOpenConfirmTitle = "Open Recent Spec File"; - m_recentSpecFileMenuLoadNoConfirmTitle = "Load All Files in Recent Spec File"; - - m_recentSpecFileMenu = menu->addMenu(m_recentSpecFileMenuOpenConfirmTitle); - QObject::connect(m_recentSpecFileMenu, SIGNAL(aboutToShow()), - this, SLOT(processRecentSpecFileMenuAboutToBeDisplayed())); - QObject::connect(m_recentSpecFileMenu, SIGNAL(triggered(QAction*)), - this, SLOT(processRecentSpecFileMenuSelection(QAction*))); - - m_recentSceneFileMenu = menu->addMenu("Open Recent Scene File"); - QObject::connect(m_recentSceneFileMenu, SIGNAL(aboutToShow()), - this, SLOT(processRecentSceneFileMenuAboutToBeDisplayed())); - QObject::connect(m_recentSceneFileMenu, SIGNAL(triggered(QAction*)), - this, SLOT(processRecentSceneFileMenuSelection(QAction*))); - menu->addAction(m_manageFilesAction); menu->addAction(m_closeSpecFileAction); menu->addSeparator(); @@ -1918,16 +2001,18 @@ BrainBrowserWindowEditMenuItemEnum::DELETER); - QAction* selectAllAction = NULL; + QAction* deselectAllAction = NULL; const bool addSelectAllFlag = true; if (addSelectAllFlag) { - selectAllAction = addItemToEditMenu(m_editMenu, - BrainBrowserWindowEditMenuItemEnum::SELECT_ALL); + deselectAllAction = addItemToEditMenu(m_editMenu, + BrainBrowserWindowEditMenuItemEnum::DESELECT_ALL); + addItemToEditMenu(m_editMenu, + BrainBrowserWindowEditMenuItemEnum::SELECT_ALL); } m_editMenu->insertSeparator(cutAction); - if (selectAllAction != NULL) { - m_editMenu->insertSeparator(selectAllAction); + if (deselectAllAction != NULL) { + m_editMenu->insertSeparator(deselectAllAction); } QObject::connect(m_editMenu, SIGNAL(aboutToShow()), @@ -2030,6 +2115,8 @@ break; case BrainBrowserWindowEditMenuItemEnum::DELETER: break; + case BrainBrowserWindowEditMenuItemEnum::DESELECT_ALL: + break; case BrainBrowserWindowEditMenuItemEnum::PASTE: if (pasteText.isEmpty()) { action->setText(BrainBrowserWindowEditMenuItemEnum::toGuiName(editMenuItem)); @@ -2154,12 +2241,14 @@ { if (isMacOptionKeyDown()) { m_closeWindowAction->setText(m_closeWindowActionNoConfirmTitle); - m_recentSpecFileMenu->setTitle(m_recentSpecFileMenuLoadNoConfirmTitle); } else { m_closeWindowAction->setText(m_closeWindowActionConfirmTitle); - m_recentSpecFileMenu->setTitle(m_recentSpecFileMenuOpenConfirmTitle); } + + EventBrowserTabReopenAvailable reopenAvailableEvent; + EventManager::get()->sendEvent(reopenAvailableEvent.getPointer()); + m_reopenLastClosedTabAction->setEnabled(reopenAvailableEvent.isReopenValid()); } void @@ -2198,235 +2287,6 @@ return keyDown; } - -/** - * Called when Open Recent Spec File Menu is about to be displayed - * and creates the content of the menu. - */ -void -BrainBrowserWindow::processRecentSpecFileMenuAboutToBeDisplayed() -{ - m_recentSpecFileMenu->clear(); - - const int32_t numRecentSpecFiles = BrainBrowserWindow::loadRecentSpecFileMenu(m_recentSpecFileMenu); - - if (numRecentSpecFiles > 0) { - m_recentSpecFileMenu->addSeparator(); - QAction* action = new QAction("Clear Menu", - m_recentSpecFileMenu); - action->setData("CLEAR_CLEAR"); - m_recentSpecFileMenu->addAction(action); - } -} - -/** - * Load a menu with recent spec files. This method only ADDS - * items to the menu, nothing is removed or cleared. - * - * @param recentSpecFileMenu - * Menu to which recent spec files are added. - * @return - * Returns the number of recent spec files added to the menu. - */ -int32_t -BrainBrowserWindow::loadRecentSpecFileMenu(QMenu* recentSpecFileMenu) -{ - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - std::vector recentSpecFiles; - prefs->getPreviousSpecFiles(recentSpecFiles); - - const int32_t numRecentSpecFiles = static_cast(recentSpecFiles.size()); - for (int32_t i = 0; i < numRecentSpecFiles; i++) { - AString actionName; - AString actionFullPath; - if (DataFile::isFileOnNetwork(recentSpecFiles[i])) { - actionName = recentSpecFiles[i]; - actionFullPath = recentSpecFiles[i]; - } - else { - FileInformation fileInfo(recentSpecFiles[i]); - QString path = fileInfo.getPathName(); - QString name = fileInfo.getFileName(); - if (path.isEmpty() == false) { - name += (" (" + path + ")"); - } - actionName = name; - actionFullPath = fileInfo.getAbsoluteFilePath(); - } - - QAction* action = new QAction(actionName, - recentSpecFileMenu); - /* - * If this "setData()" action changes you will need to update: - * (1) BrainBrowserWindow::processRecentSpecFileMenuSelection - * (2) MacDockMenu::menuActionTriggered - */ - action->setData(actionFullPath); - recentSpecFileMenu->addAction(action); - } - - return numRecentSpecFiles; -} - -/** - * Called when an item is selected from the recent spec file - * menu. - * @param itemAction - * Action of the menu item that was selected. - */ -void -BrainBrowserWindow::processRecentSpecFileMenuSelection(QAction* itemAction) -{ - const AString specFileName = itemAction->data().toString(); - if (specFileName == "CLEAR_CLEAR") { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->clearPreviousSpecFiles(); - return; - } - - if ( ! specFileName.isEmpty()) { - - SpecFile specFile; - try { - specFile.readFile(specFileName); - SessionManager::get()->getCaretPreferences()->addToPreviousSpecFiles(specFileName); - - - if (m_recentSpecFileMenu->title() == m_recentSpecFileMenuOpenConfirmTitle) { - if (GuiManager::get()->processShowOpenSpecFileDialog(&specFile, - this)) { - m_toolbar->addDefaultTabsAfterLoadingSpecFile(); - } - } - else if (m_recentSpecFileMenu->title() == m_recentSpecFileMenuLoadNoConfirmTitle) { - std::vector fileNamesToLoad; - fileNamesToLoad.push_back(specFileName); - loadFilesFromCommandLine(fileNamesToLoad, - BrainBrowserWindow::LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE); - m_toolbar->addDefaultTabsAfterLoadingSpecFile(); - } - else { - CaretAssert(0); - } - } - catch (const DataFileException& e) { - QMessageBox::critical(this, - "ERROR", - e.whatString()); - return; - } - - EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); - EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - } -} - - - -/** - * Called when Open Recent Scene File Menu is about to be displayed - * and creates the content of the menu. - */ -void -BrainBrowserWindow::processRecentSceneFileMenuAboutToBeDisplayed() -{ - m_recentSceneFileMenu->clear(); - - const int32_t numRecentSceneFiles = BrainBrowserWindow::loadRecentSceneFileMenu(m_recentSceneFileMenu); - - if (numRecentSceneFiles > 0) { - m_recentSceneFileMenu->addSeparator(); - QAction* action = new QAction("Clear Menu", - m_recentSceneFileMenu); - action->setData("CLEAR_CLEAR"); - m_recentSceneFileMenu->addAction(action); - } -} - -/** - * Load a menu with recent scene files. This method only ADDS - * items to the menu, nothing is removed or cleared. - * - * @param recentSceneFileMenu - * Menu to which recent scene files are added. - * @return - * Returns the number of recent scene files added to the menu. - */ -int32_t -BrainBrowserWindow::loadRecentSceneFileMenu(QMenu* recentSceneFileMenu) -{ - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - std::vector recentSceneFiles; - prefs->getPreviousSceneFiles(recentSceneFiles); - - const int32_t numRecentSceneFiles = static_cast(recentSceneFiles.size()); - for (int32_t i = 0; i < numRecentSceneFiles; i++) { - AString actionName; - AString actionFullPath; - if (DataFile::isFileOnNetwork(recentSceneFiles[i])) { - actionName = recentSceneFiles[i]; - actionFullPath = recentSceneFiles[i]; - } - else { - FileInformation fileInfo(recentSceneFiles[i]); - QString path = fileInfo.getPathName(); - QString name = fileInfo.getFileName(); - if (path.isEmpty() == false) { - name += (" (" + path + ")"); - } - actionName = name; - actionFullPath = fileInfo.getAbsoluteFilePath(); - } - - QAction* action = new QAction(actionName, - recentSceneFileMenu); - /* - * If this "setData()" action changes you will need to update: - * (1) BrainBrowserWindow::processRecentSceneFileMenuSelection - * (2) MacDockMenu::menuActionTriggered - */ - action->setData(actionFullPath); - recentSceneFileMenu->addAction(action); - } - - return numRecentSceneFiles; -} - -/** - * Called when an item is selected from the recent scene file - * menu. - * @param itemAction - * Action of the menu item that was selected. - */ -void -BrainBrowserWindow::processRecentSceneFileMenuSelection(QAction* itemAction) -{ - const AString sceneFileName = itemAction->data().toString(); - if (sceneFileName == "CLEAR_CLEAR") { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->clearPreviousSceneFiles(); - return; - } - - if ( ! sceneFileName.isEmpty()) { - std::vector filenamesVector; - filenamesVector.push_back(sceneFileName); - std::vector dataFileTypes; - dataFileTypes.push_back(DataFileTypeEnum::SCENE); - loadFiles(this, - filenamesVector, - dataFileTypes, - LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE, - "", - ""); - - EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); - EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - } -} - /** * Called when view menu is about to show. */ @@ -2450,19 +2310,24 @@ m_viewTileTabsAction->setText("Enter Tile Tabs"); } - m_viewAutomaticTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsGridModeEnum::AUTOMATIC, - true)); - m_viewCustomTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsGridModeEnum::CUSTOM, - true)); - + m_viewAutomaticTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID, + true)); + m_viewCustomTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID, + true)); + m_viewManualTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsLayoutConfigurationTypeEnum::MANUAL, + true)); + BrowserWindowContent* bwc = getBrowerWindowContent(); switch (bwc->getTileTabsConfigurationMode()) { - case TileTabsGridModeEnum::AUTOMATIC: + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: m_viewAutomaticTileTabsConfigurationAction->setChecked(true); break; - case TileTabsGridModeEnum::CUSTOM: + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: m_viewCustomTileTabsConfigurationAction->setChecked(true); break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + m_viewManualTileTabsConfigurationAction->setChecked(true); + break; } } @@ -2476,17 +2341,20 @@ * Include the number of rows and columns. */ AString -BrainBrowserWindow::getTileTabsConfigurationLabelText(const TileTabsGridModeEnum::Enum configurationMode, +BrainBrowserWindow::getTileTabsConfigurationLabelText(const TileTabsLayoutConfigurationTypeEnum::Enum configurationMode, const bool includeRowsAndColumnsIn) const { bool includeRowsAndColumns = includeRowsAndColumnsIn; AString modeLabel; switch (configurationMode) { - case TileTabsGridModeEnum::AUTOMATIC: - modeLabel = "Automatic"; + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + modeLabel = "Automatic Grid"; + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + modeLabel = "Custom Grid"; break; - case TileTabsGridModeEnum::CUSTOM: - modeLabel = "Custom"; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + modeLabel = "Manual"; break; } @@ -2496,35 +2364,52 @@ const int32_t windowTabCount = static_cast(windowTabIndices.size()); AString errorText; + bool customGridDefaultFlag(false); switch (configurationMode) { - case TileTabsGridModeEnum::AUTOMATIC: - TileTabsConfiguration::getRowsAndColumnsForNumberOfTabs(windowTabCount, + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + TileTabsLayoutGridConfiguration::getRowsAndColumnsForNumberOfTabs(windowTabCount, configRowCount, configColCount); break; - case TileTabsGridModeEnum::CUSTOM: + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: { - const TileTabsConfiguration* customConfig = getBrowerWindowContent()->getCustomTileTabsConfiguration(); - configRowCount = customConfig->getNumberOfRows(); - configColCount = customConfig->getNumberOfColumns(); - const int32_t customTabCount = (configRowCount - * configColCount); - const int32_t hiddenCount = windowTabCount - customTabCount; - if (hiddenCount > 0) { - errorText = " (configuration too small for all tabs)"; - includeRowsAndColumns = false; + const TileTabsLayoutGridConfiguration* customConfig = getBrowerWindowContent()->getCustomGridTileTabsConfiguration(); + if (customConfig->isCustomDefaultFlag()) { + customGridDefaultFlag = true; + TileTabsLayoutGridConfiguration::getRowsAndColumnsForNumberOfTabs(windowTabCount, + configRowCount, + configColCount); + } + else { + configRowCount = customConfig->getNumberOfRows(); + configColCount = customConfig->getNumberOfColumns(); + const int32_t customTabCount = (configRowCount + * configColCount); + const int32_t hiddenCount = windowTabCount - customTabCount; + if (hiddenCount > 0) { + errorText = " (configuration too small for all tabs)"; + includeRowsAndColumns = false; + } } } break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + includeRowsAndColumns = false; + break; } AString rowsColumnsText; if (includeRowsAndColumns) { - rowsColumnsText = (" (" - + AString::number(configRowCount) - + " Rows, " - + AString::number(configColCount) - + " Columns)"); + if (customGridDefaultFlag) { + rowsColumnsText = " (Defaults to automatic grid when selected)"; + } + else { + rowsColumnsText = (" (" + + AString::number(configRowCount) + + " Rows, " + + AString::number(configColCount) + + " Columns)"); + } } const AString textLabel(modeLabel @@ -2541,7 +2426,7 @@ * Event with modification information. */ void -BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsConfigurationModification* modEvent) +BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsGridConfigurationModification* modEvent) { CaretAssert(modEvent); @@ -2554,8 +2439,9 @@ vpContent = m_openGLWidget->getViewportContent(); } - TileTabsConfigurationModifier modifier(vpContent, - modEvent); + TileTabsGridConfigurationModifier modifier(vpContent, + m_browserWindowIndex, + modEvent); AString errorMessage; if (! modifier.run(errorMessage)) { @@ -2574,27 +2460,6 @@ } /** - * Update the tile tabs configuration menu just before it is shown. - */ -void -BrainBrowserWindow::processViewTileTabsLoadUserConfigurationMenuAboutToShow() -{ - /* - * QMenu::clear will delete the actions that were in the menu - */ - m_viewTileTabsLoadUserConfigurationMenu->clear(); - m_viewCustomTileTabsConfigurationActions.clear(); - - const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - std::vector tileTabsConfigs = prefs->getTileTabsConfigurationsSortedByName(); - for (auto config : tileTabsConfigs) { - QAction* action = m_viewTileTabsLoadUserConfigurationMenu->addAction(config->getName()); - m_viewCustomTileTabsConfigurationActions.push_back(std::make_pair(action, - const_cast(config))); - } -} - -/** * Called when automatic/custom menu item is selected. * * @param action @@ -2605,10 +2470,13 @@ { BrowserWindowContent* bwc = getBrowerWindowContent(); if (action == m_viewAutomaticTileTabsConfigurationAction) { - bwc->setTileTabsConfigurationMode(TileTabsGridModeEnum::AUTOMATIC); + bwc->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID); } else if (action == m_viewCustomTileTabsConfigurationAction) { - bwc->setTileTabsConfigurationMode(TileTabsGridModeEnum::CUSTOM); + bwc->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID); + } + else if (action == m_viewManualTileTabsConfigurationAction) { + bwc->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::MANUAL); } else { CaretAssert(0); @@ -2619,49 +2487,6 @@ } /** - * Process an item selected from the tile tabs configuration menu. - */ -void -BrainBrowserWindow::processViewTileTabsLoadUserConfigurationMenuItemTriggered(QAction* action) -{ - CaretAssert(action); - - BrowserWindowContent* bwc = getBrowerWindowContent(); - bwc->setTileTabsConfigurationMode(TileTabsGridModeEnum::CUSTOM); - - TileTabsConfiguration* tileTabsConfig = NULL; - for (auto& config : m_viewCustomTileTabsConfigurationActions) { - if (config.first == action) { - tileTabsConfig = config.second; - CaretAssert(tileTabsConfig); - break; - } - } - - CaretAssert(tileTabsConfig); - - bwc->getCustomTileTabsConfiguration()->copy(*tileTabsConfig); - - EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); -} - -/** - * @return Instance of the tile tabs configuration menu. - */ -QMenu* -BrainBrowserWindow::createMenuViewTileTabsLoadUserConfiguration() -{ - QMenu* menu = new QMenu("Load Custom With User Configuration"); - QObject::connect(menu, &QMenu::aboutToShow, - this, &BrainBrowserWindow::processViewTileTabsLoadUserConfigurationMenuAboutToShow); - QObject::connect(menu, &QMenu::triggered, - this, &BrainBrowserWindow::processViewTileTabsLoadUserConfigurationMenuItemTriggered); - - return menu; -} - -/** * Create the view menu. * @return the view menu. */ @@ -2671,16 +2496,19 @@ QMenu* menu = new QMenu("View", this); QObject::connect(menu, SIGNAL(aboutToShow()), this, SLOT(processViewMenuAboutToShow())); - - QMenu* tileTabsModeMenu = new QMenu("Tile Tabs Configuration Mode"); - tileTabsModeMenu->addAction(m_viewAutomaticTileTabsConfigurationAction); - tileTabsModeMenu->addAction(m_viewCustomTileTabsConfigurationAction); + + const bool showTileTabsModeMenuFlag(false); + QMenu* tileTabsModeMenu(NULL); + if (showTileTabsModeMenuFlag) { + tileTabsModeMenu = new QMenu("Tile Tabs Configuration Layout"); + tileTabsModeMenu->addAction(m_viewAutomaticTileTabsConfigurationAction); + tileTabsModeMenu->addAction(m_viewCustomTileTabsConfigurationAction); + tileTabsModeMenu->addAction(m_viewManualTileTabsConfigurationAction); + } m_viewMoveFeaturesToolBoxMenu = createMenuViewMoveFeaturesToolBox(); m_viewMoveOverlayToolBoxMenu = createMenuViewMoveOverlayToolBox(); - m_viewTileTabsLoadUserConfigurationMenu = createMenuViewTileTabsLoadUserConfiguration(); - menu->addAction(m_showToolBarAction); menu->addMenu(m_viewMoveFeaturesToolBoxMenu); menu->addMenu(m_viewMoveOverlayToolBoxMenu); @@ -2692,8 +2520,9 @@ menu->addSeparator(); menu->addAction(m_viewTileTabsAction); menu->addAction(m_viewTileTabsConfigurationDialogAction); - menu->addMenu(tileTabsModeMenu); - menu->addMenu(m_viewTileTabsLoadUserConfigurationMenu); + if (tileTabsModeMenu != NULL) { + menu->addMenu(tileTabsModeMenu); + } return menu; } @@ -2805,17 +2634,24 @@ { QMenu* menu = new QMenu("Surface", this); - menu->addAction("Information...", - this, - SLOT(processSurfaceMenuInformation())); - - menu->addAction("Properties...", - this, - SLOT(processShowSurfacePropertiesDialog())); - - menu->addAction("Primary Anatomical...", - this, - SLOT(processSurfaceMenuPrimaryAnatomical())); + + QAction* infoAction = menu->addAction("Information...", + this, + SLOT(processSurfaceMenuInformation())); + infoAction->setToolTip("Display information about the surface(s) in the selected tab including: " + "Surface Type, Number of Triangles/Vertices, and Extent."); + + QAction* propertiesAction = menu->addAction("Properties...", + this, + SLOT(processShowSurfacePropertiesDialog())); + propertiesAction->setToolTip("Edit surface properties including opacity and default color."); + + QAction* primaryAnatAction = menu->addAction("Primary Anatomical...", + this, + SLOT(processSurfaceMenuPrimaryAnatomical())); + primaryAnatAction->setToolTip("Set surfaces used for border projection, foci projection, and " + "selection of surfaces for coordinate translation to and from " + "volumes."); return menu; } @@ -2964,10 +2800,22 @@ menu->addAction(m_bringAllToFrontAction); menu->addAction(m_tileWindowsAction); + QObject::connect(menu, &QMenu::aboutToShow, + this, &BrainBrowserWindow::processWindowMenuAboutToShow); + return menu; } /** + * Called when window window menu is about to show + */ +void +BrainBrowserWindow::processWindowMenuAboutToShow() +{ + m_informationDialogAction->setEnabled(GuiManager::get()->getInformationDisplayDialogEnabledAction()->isEnabled()); +} + +/** * Create the help menu. * @return the help menu. */ @@ -2985,6 +2833,7 @@ QAction* helpAction = GuiManager::get()->getHelpViewerDialogDisplayAction(); menu->addAction(helpAction->text(), this, SLOT(processShowHelpInformation())); + menu->addAction(m_helpHcpUsersAction); menu->addSeparator(); menu->addAction(m_helpHcpWebsiteAction); menu->addAction(m_helpWorkbenchBugReportAction); @@ -3034,12 +2883,11 @@ ElapsedTimer et; et.start(); - const float numTimes(10.0); - for (int32_t i = 0; i < numTimes; i++) { + for (int32_t i = 0; i < m_developerTimingIterations; i++) { EventManager::get()->sendEvent(EventGraphicsTimingOneWindow(m_browserWindowIndex).getPointer()); } - const float time = et.getElapsedTimeSeconds() / numTimes; + const float time = et.getElapsedTimeSeconds() / m_developerTimingIterations; const AString timeString = AString::number(time, 'f', 5); AString fpsString; @@ -3055,6 +2903,38 @@ WuQMessageBox::informationOk(this, msg); } +/** + * Time the graphics drawing for duration + */ +void +BrainBrowserWindow::processDevelopGraphicsTimingDuration() +{ + ElapsedTimer durationTimer; + durationTimer.start(); + + int32_t iterations(0); + while (durationTimer.getElapsedTimeSeconds() < m_developerTimingDuration) { + EventManager::get()->sendEvent(EventGraphicsTimingOneWindow(m_browserWindowIndex).getPointer()); + iterations++; + } + const float actualDuration(durationTimer.getElapsedTimeSeconds()); + + if (iterations > 0) { + const float fps(iterations / actualDuration); + const float frameTime(actualDuration / iterations); + + const AString text("Frames Per Second: " + + AString::number(fps, 'f', 6) + + "\nAverage Time: " + + AString::number(frameTime, 'f', 6) + + "\nIterations: " + + AString::number(iterations) + + "\nDuration (s): " + + AString::number(actualDuration, 'f', 3)); + WuQMessageBox::informationOk(this, + text); + } +} /** * Export to VTK file. @@ -3094,7 +2974,8 @@ if (surfaceFiles.empty() == false) { QString vtkSurfaceFileFilter = "VTK Poly Data File (*.vtp)"; - CaretFileDialog cfd(this, + CaretFileDialog cfd(CaretFileDialog::Mode::MODE_SAVE, + this, "Export to VTK File", GuiManager::get()->getBrain()->getCurrentDirectory(), vtkSurfaceFileFilter); @@ -3284,7 +3165,7 @@ { if (s_previousOpenFileNameFilter.isEmpty()) { s_previousOpenFileNameFilter = - DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION); + DataFileTypeEnum::toQFileDialogFilterForReading(DataFileTypeEnum::SPECIFICATION); } /* @@ -3298,26 +3179,38 @@ for (std::vector::const_iterator iter = dataFileTypes.begin(); iter != dataFileTypes.end(); iter++) { - AString filterName = DataFileTypeEnum::toQFileDialogFilter(*iter); + AString filterName = DataFileTypeEnum::toQFileDialogFilterForReading(*iter); filenameFilterList.append(filterName); } /* * Setup file selection dialog. */ - CaretFileDialog fd(this); + CaretFileDialog fd(CaretFileDialog::Mode::MODE_OPEN, + this); fd.setAcceptMode(CaretFileDialog::AcceptOpen); fd.setNameFilters(filenameFilterList); fd.setFileMode(CaretFileDialog::ExistingFiles); - fd.setViewMode(CaretFileDialog::List); fd.selectNameFilter(s_previousOpenFileNameFilter); - if (s_previousOpenFileDirectory.isEmpty() == false) { + if ( ! s_previousOpenFileDirectory.isEmpty()) { FileInformation fileInfo(s_previousOpenFileDirectory); if (fileInfo.exists()) { fd.setDirectory(s_previousOpenFileDirectory); } } + /* + * First time dialog is displayed, use list order. + * Subsequent usage will use "details" or "list" from + * whatever user last selected. This functionality is + * handled by the default implementation of QFileDialog. + */ + static bool firstTimeFlag(true); + if (firstTimeFlag) { + firstTimeFlag = false; + fd.setViewMode(CaretFileDialog::List); + } + if ( ! s_previousOpenFileGeometry.isEmpty()) { fd.restoreGeometry(s_previousOpenFileGeometry); } @@ -3337,17 +3230,78 @@ filenamesVector.push_back(name); } + std::vector dataFileTypesDummyNotUsed; + loadFiles(this, + filenamesVector, + dataFileTypesDummyNotUsed, + LOAD_SPEC_FILE_WITH_DIALOG, + "", + ""); + + for (auto name : filenamesVector) { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->addToRecentFilesAndOrDirectories(name); + } + } + s_previousOpenFileNameFilter = fd.selectedNameFilter(); + s_previousOpenFileDirectory = fd.directory().absolutePath(); + s_previousOpenFileGeometry = fd.saveGeometry(); + } +} + +/** + * Process the Open Recent menu item + */ +void +BrainBrowserWindow::processOpenRecent() +{ + AString directoryOrFileName; + int32_t sceneIndex(-1); + const RecentFilesDialog::ResultModeEnum result = RecentFilesDialog::runDialog(RecentFilesDialog::RunMode::OPEN_RECENT, + directoryOrFileName, + sceneIndex, + this); + + switch (result) { + case RecentFilesDialog::ResultModeEnum::CANCEL: + break; + case RecentFilesDialog::ResultModeEnum::LOAD_FILES_IN_SPEC_FILE: + loadFilesFromCommandLine({ directoryOrFileName }, + BrainBrowserWindow::LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE); + break; + case RecentFilesDialog::ResultModeEnum::LOAD_SCENE_FROM_SCENE_FILE: + loadSceneFromCommandLine(directoryOrFileName, + AString::number(sceneIndex), + BrainBrowserWindow::LoadSceneFromCommandLineDialogMode::SHOW_YES); + break; + case RecentFilesDialog::ResultModeEnum::OPEN_DIRECTORY: + s_previousOpenFileDirectory = directoryOrFileName; + processDataFileOpen(); + break; + case RecentFilesDialog::ResultModeEnum::OPEN_FILE: + { + bool validFlag(false); + DataFileTypeEnum::fromFileExtension(directoryOrFileName, &validFlag); + if ( ! validFlag) { + WuQMessageBox::errorOk(this, ("File is not a supported file type: " + + directoryOrFileName)); + } + else { + std::vector filenames; + filenames.push_back(directoryOrFileName); std::vector dataFileTypesDummyNotUsed; loadFiles(this, - filenamesVector, + filenames, dataFileTypesDummyNotUsed, LOAD_SPEC_FILE_WITH_DIALOG, "", ""); + } } - s_previousOpenFileNameFilter = fd.selectedNameFilter(); - s_previousOpenFileDirectory = fd.directory().absolutePath(); - s_previousOpenFileGeometry = fd.saveGeometry(); + break; + case RecentFilesDialog::ResultModeEnum::OPEN_OTHER: + processDataFileOpen(); + break; } } @@ -3421,6 +3375,12 @@ loadSpecFileMode, userName, password); + + for (auto name : filenames) { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->addToRecentFilesAndOrDirectories(name); + } + } /** @@ -3430,10 +3390,13 @@ * @param sceneNameOrNumber * Name or number of scene. Name takes precedence over number. * Scene numbers start at one. + * @param sceneDialogMode + * Mode for showing/closing scene dialog after scene loads */ void BrainBrowserWindow::loadSceneFromCommandLine(const AString& sceneFileName, - const AString& sceneNameOrNumber) + const AString& sceneNameOrNumber, + const LoadSceneFromCommandLineDialogMode sceneDialogMode) { std::vector filenames; filenames.push_back(sceneFileName); @@ -3466,7 +3429,14 @@ } if (scene != NULL) { - const bool showSceneDialogFlag(true); + bool showSceneDialogFlag(false); + switch (sceneDialogMode) { + case LoadSceneFromCommandLineDialogMode::SHOW_NO: + break; + case LoadSceneFromCommandLineDialogMode::SHOW_YES: + showSceneDialogFlag = true; + break; + } GuiManager::get()->processShowSceneDialogAndScene(this, sf, scene, @@ -3493,6 +3463,15 @@ /* NOTE: File warning dialog is performed by scene dialog */ } +/** + * Load the given directory in the Open Data File Dialog + */ +void +BrainBrowserWindow::loadDirectoryFromCommandLine(const AString& directoryName) +{ + s_previousOpenFileDirectory = directoryName; + processDataFileOpen(); +} /** * Load data files. If there are errors, an error message dialog @@ -3645,7 +3624,7 @@ specFileName = fileInfo.getAbsoluteFilePath(); } if (fileInfo.exists()) { - SessionManager::get()->getCaretPreferences()->addToPreviousSpecFiles(specFileName); + SessionManager::get()->getCaretPreferences()->addToRecentFilesAndOrDirectories(specFileName); } specFile.readFile(specFileName); @@ -3841,8 +3820,6 @@ GuiManager::get()->processShowSceneDialog(this); } - EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); - showDataFileReadWarningsDialog(); return successFlag; @@ -4058,6 +4035,17 @@ } /** + * Reopen the last closed tab in this window + * @param reopenTabEvent + * The reopen event + */ +void +BrainBrowserWindow::reopenLastClosedTab(EventBrowserTabReopenClosed& reopenTabEvent) +{ + m_toolbar->reopenLastClosedTab(reopenTabEvent); +} + +/** * Called when move all tabs to one window is selected. */ void @@ -4103,7 +4091,7 @@ if (m_toolbar->tabBar->count() > 1) { QAction* toNewWindowAction = new QAction("New Window", m_moveSelectedTabToWindowMenu); - toNewWindowAction->setData(qVariantFromValue((void*)NULL)); + toNewWindowAction->setData(QVariant::fromValue((void*)NULL)); m_moveSelectedTabToWindowMenu->addAction(toNewWindowAction); } @@ -4112,7 +4100,7 @@ if (browserWindows[i] != this) { QAction* action = new QAction(browserWindows[i]->windowTitle(), m_moveSelectedTabToWindowMenu); - action->setData(qVariantFromValue((void*)browserWindows[i])); + action->setData(QVariant::fromValue((void*)browserWindows[i])); m_moveSelectedTabToWindowMenu->addAction(action); } } @@ -4427,6 +4415,20 @@ } /** + * Get the Brain OpenGL Viewport content for all tabs + * + * @param viewportContentOut + * Contains viewport content on exit + */ +void +BrainBrowserWindow::getAllBrainOpenGLViewportContent(std::vector& viewportContentOut) const +{ + viewportContentOut.clear(); + + viewportContentOut = m_openGLWidget->getViewportContent(); +} + +/** * Returns a popup menu for the main window. * Overrides that in QMainWindow and prevents the * default context menu from appearing. @@ -4468,6 +4470,16 @@ } /** + * Load the HCP Website into the user's web browser. + */ +void +BrainBrowserWindow::processHcpUsersGroup() +{ + QUrl url("https://groups.google.com/a/humanconnectome.org/g/hcp-users"); + QDesktopServices::openUrl(url); +} + +/** * Report a Workbench bug. */ void @@ -4974,17 +4986,3 @@ this); } } - -/** - * Set the enabled status for enabling mac duplicate menu bar for - * the next created toolbar. - */ -void -BrainBrowserWindow::setEnableMacDuplicateMenuBar(bool status) -{ - s_enableMacDuplicateMenuBarFlag = status; -} - - - - diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -126,6 +126,11 @@ "Delete", noShortCutKeySequence)); + enumData.push_back(BrainBrowserWindowEditMenuItemEnum(DESELECT_ALL, + "DESELECT_ALL", + "Deselect All", + (Qt::CTRL + Qt::SHIFT + Qt::Key_A))); + enumData.push_back(BrainBrowserWindowEditMenuItemEnum(PASTE, "PASTE", "Paste", diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -42,6 +42,8 @@ CUT, /** Delete (note: 'DELETE' is reserved word on Windows) */ DELETER, + /** Deselect All */ + DESELECT_ALL, /** Paste */ PASTE, /** Paste Special */ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindow.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindow.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindow.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindow.h 2021-02-16 19:46:47.000000000 +0000 @@ -32,7 +32,7 @@ #include "DataFileTypeEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" -#include "TileTabsGridModeEnum.h" +#include "TileTabsLayoutConfigurationTypeEnum.h" class QAction; class QActionGroup; @@ -44,12 +44,14 @@ class BrainBrowserWindowToolBar; class BrainBrowserWindowOrientedToolBox; class BrainOpenGLWidget; + class BrainOpenGLViewportContent; class BrowserWindowContent; class BrowserTabContent; - class EventTileTabsConfigurationModification; + class EventBrowserTabReopenClosed; + class EventTileTabsGridConfigurationModification; class PlainTextStringBuilder; class SceneClassAssistant; - class TileTabsConfiguration; + class TileTabsLayoutBaseConfiguration; /** @@ -83,6 +85,8 @@ void removeAndReturnAllTabs(std::vector& allTabContent); + void getAllBrainOpenGLViewportContent(std::vector& viewportContentOut) const; + int32_t getBrowserWindowIndex() const; bool isTileTabsSelected() const; @@ -99,11 +103,24 @@ LOAD_SPEC_FILE_WITH_DIALOG_VIA_COMMAND_LINE }; + /** + * For loading scene from command line + */ + enum class LoadSceneFromCommandLineDialogMode { + /** After loading scene, show the scene dialog */ + SHOW_YES, + /** After loading scene, close the scene dialog */ + SHOW_NO + }; + void loadFilesFromCommandLine(const std::vector& filenames, const LoadSpecFileMode loadSpecFileMode); void loadSceneFromCommandLine(const AString& sceneFileName, - const AString& sceneNameOrNumber); + const AString& sceneNameOrNumber, + const LoadSceneFromCommandLineDialogMode sceneDialogMode); + + void loadDirectoryFromCommandLine(const AString& directoryName); bool loadFilesFromNetwork(QWidget* parentForDialogs, const std::vector& filenames, @@ -134,10 +151,6 @@ virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; - static int32_t loadRecentSpecFileMenu(QMenu* recentSpecFileMenu); - - static int32_t loadRecentSceneFileMenu(QMenu* recentSpecFileMenu); - float getOpenGLWidgetAspectRatio() const; bool changeInputModeToAnnotationsWarningDialog(); @@ -150,13 +163,13 @@ bool isOpenGLContextSharingValid() const; - AString getTileTabsConfigurationLabelText(const TileTabsGridModeEnum::Enum configurationMode, + AString getTileTabsConfigurationLabelText(const TileTabsLayoutConfigurationTypeEnum::Enum configurationMode, const bool includeRowsAndColumns) const; void resizeDockWidgets(const QList &docks, const QList &sizes, Qt::Orientation orientation); - static void setEnableMacDuplicateMenuBar(bool status); - + void reopenLastClosedTab(EventBrowserTabReopenClosed& reopenTabEvent); + protected: void closeEvent(QCloseEvent* event); void keyPressEvent(QKeyEvent* event); @@ -169,6 +182,7 @@ void processDuplicateTab(); void processDataFileLocationOpen(); void processDataFileOpen(); + void processOpenRecent(); void processManageSaveLoadedFiles(); void processCaptureImage(); void processMovieRecording(); @@ -185,8 +199,6 @@ void processShowIdentifyBrainordinateDialog(); void processGapsAndMargins(); - void processViewTileTabsLoadUserConfigurationMenuAboutToShow(); - void processViewTileTabsLoadUserConfigurationMenuItemTriggered(QAction* action); void processViewTileTabsAutomaticCustomTriggered(QAction* action); @@ -202,12 +214,6 @@ void processMoveSelectedTabToWindowMenuAboutToBeDisplayed(); void processMoveSelectedTabToWindowMenuSelection(QAction*); - void processRecentSceneFileMenuAboutToBeDisplayed(); - void processRecentSceneFileMenuSelection(QAction*); - - void processRecentSpecFileMenuAboutToBeDisplayed(); - void processRecentSpecFileMenuSelection(QAction*); - void processShowOverlayToolBox(bool); void processShowFeaturesToolBox(bool); void processOverlayHorizontalToolBoxVisibilityChanged(bool); @@ -216,6 +222,7 @@ void processFileMenuAboutToShow(); void processDataMenuAboutToShow(); void processViewMenuAboutToShow(); + void processWindowMenuAboutToShow(); void processSurfaceMenuInformation(); void processSurfaceMenuPrimaryAnatomical(); @@ -224,6 +231,7 @@ void processConnectToConnectomeDataBase(); void processHcpWebsiteInBrowser(); + void processHcpUsersGroup(); void processHcpFeatureRequestWebsiteInBrowser(); void processReportWorkbenchBug(); @@ -231,7 +239,8 @@ void processShowVolumePropertiesDialog(); void processDevelopGraphicsTiming(); - + void processDevelopGraphicsTimingDuration(); + void processDevelopExportVtkFile(); void developerMenuAboutToShow(); void developerMenuFlagTriggered(QAction*); @@ -297,7 +306,6 @@ QMenu* createMenuView(); QMenu* createMenuViewMoveOverlayToolBox(); QMenu* createMenuViewMoveFeaturesToolBox(); - QMenu* createMenuViewTileTabsLoadUserConfiguration(); QMenu* createMenuConnect(); QMenu* createMenuData(); QMenu* createMenuSurface(); @@ -335,7 +343,7 @@ void saveBrowserWindowContentForScene(); - void modifyTileTabsConfiguration(EventTileTabsConfigurationModification* modEvent); + void modifyTileTabsConfiguration(EventTileTabsGridConfigurationModification* modEvent); /** Index of this window */ const int32_t m_browserWindowIndex; @@ -354,10 +362,14 @@ QAction* m_duplicateTabAction; + QAction* m_reopenLastClosedTabAction; + QAction* m_openFileAction; QAction* m_openLocationAction; + QAction* m_openRecentAction; + QAction* m_manageFilesAction; QAction* m_closeSpecFileAction; @@ -383,7 +395,6 @@ QMenu* m_viewMoveFeaturesToolBoxMenu; QMenu* m_viewMoveOverlayToolBoxMenu; - QMenu* m_viewTileTabsLoadUserConfigurationMenu; QAction* m_viewFullScreenAction; QAction* m_viewTileTabsAction; @@ -391,7 +402,8 @@ QAction* m_viewTileTabsConfigurationDialogAction; QAction* m_viewAutomaticTileTabsConfigurationAction; QAction* m_viewCustomTileTabsConfigurationAction; - std::vector> m_viewCustomTileTabsConfigurationActions; + QAction* m_viewManualTileTabsConfigurationAction; + std::vector> m_viewCustomTileTabsConfigurationActions; QAction* m_gapsAndMarginsAction; @@ -414,12 +426,14 @@ QAction* m_connectToConnectomeDatabaseAction; QAction* m_helpHcpWebsiteAction; + QAction* m_helpHcpUsersAction; QAction* m_helpHcpFeatureRequestAction; QAction* m_helpWorkbenchBugReportAction; QAction* m_developMenuAction; QActionGroup* m_developerFlagsActionGroup; QAction* m_developerGraphicsTimingAction; + QAction* m_developerGraphicsTimingDurationAction; QAction* m_developerExportVtkFileAction; QAction* m_overlayToolBoxAction; @@ -433,15 +447,10 @@ QAction* m_dataFociProjectAction; QAction* m_dataBorderFilesSplitAction; + QAction* m_dataPaletteEditorDialogAction; QMenu* m_moveSelectedTabToWindowMenu; - QMenu* m_recentSpecFileMenu; - AString m_recentSpecFileMenuOpenConfirmTitle; - AString m_recentSpecFileMenuLoadNoConfirmTitle; - - QMenu* m_recentSceneFileMenu; - QMenu* m_editMenu; QAction* m_editMenuRedoAction; QAction* m_editMenuUndoAction; @@ -477,8 +486,9 @@ bool m_keyEventProcessingFlag = false; - static bool s_enableMacDuplicateMenuBarFlag; + const float m_developerTimingDuration = 10.0; + const int32_t m_developerTimingIterations = 10; }; #ifdef __BRAIN_BROWSER_WINDOW_DECLARE__ std::set BrainBrowserWindow::s_brainBrowserWindows; @@ -489,8 +499,6 @@ bool BrainBrowserWindow::s_firstWindowFlag = true; int32_t BrainBrowserWindow::s_sceneFileFirstWindowX = -1; int32_t BrainBrowserWindow::s_sceneFileFirstWindowY = -1; - - bool BrainBrowserWindow::s_enableMacDuplicateMenuBarFlag = false; #endif // __BRAIN_BROWSER_WINDOW_DECLARE__ } diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include "ChartToolBoxViewController.h" #include "CiftiConnectivityMatrixViewController.h" #include "DeveloperFlagsEnum.h" +#include "DynConnViewController.h" #include "EventBrowserWindowDrawingContent.h" #include "EventGetOrSetUserInputModeProcessor.h" #include "EventManager.h" @@ -51,10 +53,13 @@ #include "FiberOrientationSelectionViewController.h" #include "FociSelectionViewController.h" #include "GuiManager.h" +#include "IdentificationDisplayWidget.h" #include "ImageSelectionViewController.h" #include "LabelSelectionViewController.h" +#include "MediaOverlaySetViewController.h" #include "OverlaySetViewController.h" #include "SceneClass.h" +#include "ScenePrimitiveArray.h" #include "SceneWindowGeometry.h" #include "SessionManager.h" #include "VolumeDynamicConnectivityFile.h" @@ -148,6 +153,7 @@ m_fociSelectionViewController = NULL; m_imageSelectionViewController = NULL; m_labelSelectionViewController = NULL; + m_mediaSelectionViewController = NULL; m_overlaySetViewController = NULL; m_volumeSurfaceOutlineSetViewController = NULL; @@ -183,6 +189,7 @@ m_fociTabIndex = -1; m_imageTabIndex = -1; m_labelTabIndex = -1; + m_mediaTabIndex = -1; m_overlayTabIndex = -1; m_volumeSurfaceOutlineTabIndex = -1; @@ -277,6 +284,15 @@ } if (isOverlayToolBox) { + m_mediaSelectionViewController = new MediaOverlaySetViewController(orientation, + browserWindowIndex, + objectNamePrefix, + this); + m_mediaTabIndex = addToTabWidget(m_mediaSelectionViewController, + "Media"); + } + + if (isOverlayToolBox) { m_volumeSurfaceOutlineSetViewController = new VolumeSurfaceOutlineSetViewController(orientation, m_browserWindowIndex, objectNamePrefix, @@ -284,8 +300,26 @@ m_volumeSurfaceOutlineTabIndex = addToTabWidget(m_volumeSurfaceOutlineSetViewController, "Vol/Surf Outline"); } - - setWidget(m_tabWidget); + + if (isOverlayToolBox) { +#ifdef __SHOW_DYNCONN_PROTOTYPE__ + DynConnViewController* dynConn = new DynConnViewController(); + addToTabWidget(dynConn, "DynConn"); +#endif // __SHOW_DYNCONN_PROTOTYPE__ + } + + switch (toolBoxType) { + case TOOL_BOX_FEATURES: + setWidget(m_tabWidget); + break; + case TOOL_BOX_OVERLAYS_HORIZONTAL: + setWidget(createSplitterAndIdentificationWidget(Qt::Horizontal)); + break; + case TOOL_BOX_OVERLAYS_VERTICAL: + setWidget(createSplitterAndIdentificationWidget(Qt::Vertical)); + break; + } + if (orientation == Qt::Horizontal) { setMinimumHeight(200); @@ -293,7 +327,7 @@ } else { if (isOverlayToolBox) { - setMinimumWidth(300); + setMinimumWidth(100); setMaximumWidth(800); } else { @@ -317,6 +351,59 @@ } /** + * @return Widget containing splitter and identification widget + * @param orientation + * Orientation for the widget + */ +QWidget* +BrainBrowserWindowOrientedToolBox::createSplitterAndIdentificationWidget(const Qt::Orientation orientation) +{ + float idWidgetPercentage(0.0f); + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + switch (prefs->getIdentificationDisplayMode()) { + case IdentificationDisplayModeEnum::DEBUG_MODE: + break; + case IdentificationDisplayModeEnum::DIALOG: + break; + case IdentificationDisplayModeEnum::LEGACY_DIALOG: + break; + case IdentificationDisplayModeEnum::OVERLAY_TOOLBOX: + switch (orientation) { + case Qt::Horizontal: + idWidgetPercentage = 0.3f; + break; + case Qt::Vertical: + idWidgetPercentage = 0.4f; + break; + } + break; + } + + switch (orientation) { + case Qt::Horizontal: + m_identificationWidget = new IdentificationDisplayWidget(IdentificationDisplayWidget::Location::HorizontalToolBox); + break; + case Qt::Vertical: + m_identificationWidget = new IdentificationDisplayWidget(IdentificationDisplayWidget::Location::VerticalToolBox); + break; + } + CaretAssert(m_identificationWidget); + + const int bigSize(1000); + const int idWidgetSize(bigSize * idWidgetPercentage); + const int tabWidgetSize(bigSize - idWidgetSize); + + m_splitterWidget = new QSplitter(orientation); + m_splitterWidget->setChildrenCollapsible(true); + m_splitterWidget->addWidget(m_tabWidget); + m_splitterWidget->addWidget(m_identificationWidget); + m_splitterWidget->setSizes( { tabWidgetSize, idWidgetSize } ); + + return m_splitterWidget; +} + + +/** * Place widget into a scroll area and then into the tab widget. * @param page * Widget that is added. @@ -490,6 +577,41 @@ sceneClass->addClass(m_labelSelectionViewController->saveToScene(sceneAttributes, "m_labelSelectionViewController")); } + + bool saveSplitterFlag(false); + switch (SessionManager::get()->getCaretPreferences()->getIdentificationDisplayMode()) { + case IdentificationDisplayModeEnum::DEBUG_MODE: + saveSplitterFlag = true; + break; + case IdentificationDisplayModeEnum::DIALOG: + break; + case IdentificationDisplayModeEnum::LEGACY_DIALOG: + break; + case IdentificationDisplayModeEnum::OVERLAY_TOOLBOX: + saveSplitterFlag = true; + break; + } + + /* + * Only save splitter (separates overlays and ID) if the + * ID mode is for overlay toolbox. Since ID is a preference + * and user's may have different selections for the ID information + * location, we do not want to hide the id text in the toolbox. + */ + if (saveSplitterFlag) { + if (m_splitterWidget != NULL) { + QList splitterSizes = m_splitterWidget->sizes(); + const int32_t numSizes = splitterSizes.size(); + if (numSizes > 0) { + std::vector sizesVector; + for (int32_t i = 0; i < numSizes; i++) { + sizesVector.push_back(splitterSizes[i]); + } + + sceneClass->addIntegerArray("splitterSizes", &sizesVector[0], sizesVector.size()); + } + } + } return sceneClass; } @@ -622,6 +744,18 @@ } #endif } + + if (m_splitterWidget != NULL) { + const ScenePrimitiveArray* splitterSceneArray = sceneClass->getPrimitiveArray("splitterSizes"); + if (splitterSceneArray != NULL) { + const int32_t numElements = splitterSceneArray->getNumberOfArrayElements(); + QList splitterSizes; + for (int32_t i = 0; i < numElements; i++) { + splitterSizes.push_back(splitterSceneArray->integerValue(i)); + } + m_splitterWidget->setSizes(splitterSizes); + } + } } /** @@ -762,11 +896,6 @@ break; case DataFileTypeEnum::VOLUME_DYNAMIC: haveConnFiles = true; -// haveVolumes = true; -// const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); -// if (volDynConnFile != NULL) { -// haveConnFiles = true; -// } break; } } @@ -778,6 +907,7 @@ */ int defaultTabIndex = -1; bool enableLayers = true; + bool enableMedia = false; bool enableVolumeSurfaceOutline = false; bool enableChartOne = false; bool enableChartTwo = false; @@ -788,6 +918,16 @@ switch (windowContent->getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + defaultTabIndex = m_mediaTabIndex; + enableMedia = true; + enableLayers = false; + enableVolumeSurfaceOutline = false; + haveBorders = false; + haveFibers = false; + haveFoci = false; + haveLabels = false; + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: defaultTabIndex = m_overlayTabIndex; break; @@ -831,9 +971,9 @@ EventManager::get()->sendEvent(inputModeEvent.getPointer()); const UserInputModeEnum::Enum inputMode = inputModeEvent.getUserInputMode(); switch (inputMode) { - case UserInputModeEnum::ANNOTATIONS: + case UserInputModeEnum::Enum::ANNOTATIONS: break; - case UserInputModeEnum::BORDERS: + case UserInputModeEnum::Enum::BORDERS: /* * Enable borders tab if the input mode is 'borders' so that user * can edit border point size while drawing a border before any @@ -841,15 +981,17 @@ */ haveBorders = true; break; - case UserInputModeEnum::FOCI: + case UserInputModeEnum::Enum::FOCI: + break; + case UserInputModeEnum::Enum::IMAGE: break; - case UserInputModeEnum::IMAGE: + case UserInputModeEnum::Enum::INVALID: break; - case UserInputModeEnum::INVALID: + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: break; - case UserInputModeEnum::VIEW: + case UserInputModeEnum::Enum::VIEW: break; - case UserInputModeEnum::VOLUME_EDIT: + case UserInputModeEnum::Enum::VOLUME_EDIT: break; } @@ -881,6 +1023,7 @@ if (m_labelTabIndex >= 0) m_tabWidget->setTabEnabled(m_labelTabIndex, haveLabels); if (m_overlayTabIndex >= 0) m_tabWidget->setTabEnabled(m_overlayTabIndex, enableLayers); + if (m_mediaTabIndex >= 0) m_tabWidget->setTabEnabled(m_mediaTabIndex, enableMedia); if (m_annotationTabWidget != NULL) { const int32_t numTabs = m_annotationTabWidget->count(); diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowOrientedToolBox.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowOrientedToolBox.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowOrientedToolBox.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowOrientedToolBox.h 2021-02-16 19:46:47.000000000 +0000 @@ -28,6 +28,7 @@ #include "EventListenerInterface.h" #include "SceneableInterface.h" +class QSplitter; class QTabWidget; namespace caret { @@ -39,8 +40,10 @@ class CiftiConnectivityMatrixViewController; class FiberOrientationSelectionViewController; class FociSelectionViewController; + class IdentificationDisplayWidget; class ImageSelectionViewController; class LabelSelectionViewController; + class MediaOverlaySetViewController; class OverlaySetViewController; class VolumeSurfaceOutlineSetViewController; class WuQTabWidgetWithSizeHint; @@ -89,6 +92,8 @@ int addToTabWidget(QWidget* page, const QString& label); + QWidget* createSplitterAndIdentificationWidget(const Qt::Orientation orientation); + OverlaySetViewController* m_overlaySetViewController; AnnotationSelectionViewController* m_annotationViewController; @@ -111,6 +116,8 @@ LabelSelectionViewController* m_labelSelectionViewController; + MediaOverlaySetViewController* m_mediaSelectionViewController; + VolumeSurfaceOutlineSetViewController* m_volumeSurfaceOutlineSetViewController; WuQTabWidgetWithSizeHint* m_tabWidget; @@ -141,8 +148,14 @@ int32_t m_labelTabIndex; + int32_t m_mediaTabIndex; + int32_t m_volumeSurfaceOutlineTabIndex; + QSplitter* m_splitterWidget = NULL; + + IdentificationDisplayWidget* m_identificationWidget = NULL; + int m_sizeHintWidth = -1; int m_sizeHintHeight = -1; diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarAllSurface.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarAllSurface.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarAllSurface.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarAllSurface.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,637 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_ALL_SURFACE_DECLARE__ +#include "BrainBrowserWindowToolBarAllSurface.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_ALL_SURFACE_DECLARE__ + +#include +#include +#include +#include +#include +#include + +#include "Brain.h" +#include "BrainBrowserWindowToolBar.h" +#include "BrainStructure.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "GuiManager.h" +#include "ModelWholeBrain.h" +#include "Surface.h" +#include "SurfaceTypeEnum.h" +#include "WuQFactory.h" +#include "WuQMacroManager.h" +#include "WuQtUtilities.h" +#include "WuQWidgetObjectGroup.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarAllSurface + * \brief Toolbar component for ALL view surface selection + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param parentObjectName + * Name of the parent object + * @param parentToolBar + * The parent toolbar + */ +BrainBrowserWindowToolBarAllSurface::BrainBrowserWindowToolBarAllSurface(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar), +m_parentToolBar(parentToolBar) +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(parentObjectName + + ":All:"); + + this->wholeBrainSurfaceTypeComboBox = WuQFactory::newComboBox(); + WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceTypeComboBox, + "Select the geometric type of surface for display"); + QObject::connect(this->wholeBrainSurfaceTypeComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(wholeBrainSurfaceTypeComboBoxIndexChanged(int))); + this->wholeBrainSurfaceTypeComboBox->setObjectName(objectNamePrefix + + "SurfaceType"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceTypeComboBox, + "Select all view surface type"); + + /* + * Left + */ + this->wholeBrainSurfaceLeftCheckBox = new QCheckBox(" "); + WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceLeftCheckBox, + "Enable/Disable display of the left cortical surface"); + QObject::connect(this->wholeBrainSurfaceLeftCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(wholeBrainSurfaceLeftCheckBoxStateChanged(int))); + this->wholeBrainSurfaceLeftCheckBox->setObjectName(objectNamePrefix + + "EnableLeft"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceLeftCheckBox, + "Enable all view left surface"); + + QToolButton* wholeBrainLeftSurfaceToolButton = new QToolButton(); + QAction* leftSurfaceAction = WuQtUtilities::createAction("Left", + "Select the whole brain left surface", + wholeBrainLeftSurfaceToolButton, + this, + SLOT(wholeBrainSurfaceLeftToolButtonTriggered(bool))); + WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainLeftSurfaceToolButton); + wholeBrainLeftSurfaceToolButton->setDefaultAction(leftSurfaceAction); + + /* + * Left menu is displayed when tool button is clicked + */ + this->wholeBrainSurfaceLeftMenu = new QMenu(wholeBrainLeftSurfaceToolButton); + QObject::connect(this->wholeBrainSurfaceLeftMenu, &QMenu::triggered, + this, &BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceLeftMenuTriggered); + this->wholeBrainSurfaceLeftMenu->setObjectName(objectNamePrefix + + "SelectLeftSurfaceMenu"); + this->wholeBrainSurfaceLeftMenu->setToolTip("Select all view left surface"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceLeftMenu, + "Select all view left surface"); + + /* + * Right + */ + this->wholeBrainSurfaceRightCheckBox = new QCheckBox(" "); + WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceRightCheckBox, + "Enable/Disable display of the right cortical surface"); + QObject::connect(this->wholeBrainSurfaceRightCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(wholeBrainSurfaceRightCheckBoxStateChanged(int))); + this->wholeBrainSurfaceRightCheckBox->setObjectName(objectNamePrefix + + "EnableRight"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceRightCheckBox, + "Enable all view right surface"); + + QToolButton* wholeBrainRightSurfaceToolButton = new QToolButton(); + QAction* rightSurfaceAction = WuQtUtilities::createAction("Right", + "Select the whole brain right surface", + wholeBrainRightSurfaceToolButton, + this, + SLOT(wholeBrainSurfaceRightToolButtonTriggered(bool))); + WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainRightSurfaceToolButton); + wholeBrainRightSurfaceToolButton->setDefaultAction(rightSurfaceAction); + + /* + * Right menu is displayed when tool button is clicked + */ + this->wholeBrainSurfaceRightMenu = new QMenu(wholeBrainRightSurfaceToolButton); + QObject::connect(this->wholeBrainSurfaceRightMenu, &QMenu::triggered, + this, &BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceRightMenuTriggered); + this->wholeBrainSurfaceRightMenu->setObjectName(objectNamePrefix + + "SelectRightSurfaceMenu"); + this->wholeBrainSurfaceRightMenu->setToolTip("Select all view right surface"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceRightMenu, + "Select all view right surface"); + + /* + * Cerebellum + */ + this->wholeBrainSurfaceCerebellumCheckBox = new QCheckBox(" "); + WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceCerebellumCheckBox, + "Enable/Disable display of the cerebellum surface"); + QObject::connect(this->wholeBrainSurfaceCerebellumCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(wholeBrainSurfaceCerebellumCheckBoxStateChanged(int))); + this->wholeBrainSurfaceCerebellumCheckBox->setObjectName(objectNamePrefix + + "EnableCerebellum"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceCerebellumCheckBox, + "Enable all view cerebellum"); + + QToolButton* wholeBrainCerebellumSurfaceToolButton = new QToolButton(); + QAction* cerebellumSurfaceAction = WuQtUtilities::createAction("Cerebellum", + "Select the whole brain cerebellum surface", + wholeBrainCerebellumSurfaceToolButton, + this, + SLOT(wholeBrainSurfaceCerebellumToolButtonTriggered(bool))); + WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainCerebellumSurfaceToolButton); + wholeBrainCerebellumSurfaceToolButton->setDefaultAction(cerebellumSurfaceAction); + + /* + * Cerebellum menu is displayed when tool button is clicked + */ + this->wholeBrainSurfaceCerebellumMenu = new QMenu(wholeBrainCerebellumSurfaceToolButton); + QObject::connect(this->wholeBrainSurfaceCerebellumMenu, &QMenu::triggered, + this, &BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceCerebellumMenuTriggered); + this->wholeBrainSurfaceCerebellumMenu->setObjectName(objectNamePrefix + + "SelectCerebellumSurfaceMenu"); + this->wholeBrainSurfaceCerebellumMenu->setToolTip("Select all view cerebellum surface"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceCerebellumMenu, + "Select all view cerebellum surface"); + + /* + * Left/Right separation + */ + const int separationSpinngerWidth = 48; + this->wholeBrainSurfaceSeparationLeftRightSpinBox = WuQFactory::newDoubleSpinBox(); + this->wholeBrainSurfaceSeparationLeftRightSpinBox->setDecimals(0); + this->wholeBrainSurfaceSeparationLeftRightSpinBox->setFixedWidth(separationSpinngerWidth); + this->wholeBrainSurfaceSeparationLeftRightSpinBox->setMinimum(-100000.0); + this->wholeBrainSurfaceSeparationLeftRightSpinBox->setMaximum(100000.0); + WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceSeparationLeftRightSpinBox, + "Adjust the separation of the left and right cortical surfaces"); + QObject::connect(this->wholeBrainSurfaceSeparationLeftRightSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double))); + this->wholeBrainSurfaceSeparationLeftRightSpinBox->setObjectName(objectNamePrefix + + "LeftRightSeparation"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationLeftRightSpinBox, + "Set all view left/right separation"); + + /* + * Cerebellum separation + */ + this->wholeBrainSurfaceSeparationCerebellumSpinBox = WuQFactory::newDoubleSpinBox(); + this->wholeBrainSurfaceSeparationCerebellumSpinBox->setDecimals(0); + this->wholeBrainSurfaceSeparationCerebellumSpinBox->setFixedWidth(separationSpinngerWidth); + this->wholeBrainSurfaceSeparationCerebellumSpinBox->setMinimum(-100000.0); + this->wholeBrainSurfaceSeparationCerebellumSpinBox->setMaximum(100000.0); + WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceSeparationCerebellumSpinBox, + "Adjust the separation of the cerebellum from the left and right cortical surfaces"); + QObject::connect(this->wholeBrainSurfaceSeparationCerebellumSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double))); + this->wholeBrainSurfaceSeparationCerebellumSpinBox->setObjectName(objectNamePrefix + + "CortexCerebellumSeparation"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationCerebellumSpinBox, + "Set all view cerebral/cerebellum separation"); + + this->wholeBrainSurfaceMatchCheckBox = new QCheckBox("Match"); + WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceMatchCheckBox, + "Match position and size of all surfaces to primary anatomical. Useful for " + "animation (surface interpolation) and recording movies."); + QObject::connect(this->wholeBrainSurfaceMatchCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceMatchCheckBoxClicked); + this->wholeBrainSurfaceMatchCheckBox->setObjectName(objectNamePrefix + + "MatchSurface"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceMatchCheckBox, + "Match position and size of all surfaces to primary anatomical"); + + wholeBrainLeftSurfaceToolButton->setText("L"); + wholeBrainRightSurfaceToolButton->setText("R"); + wholeBrainCerebellumSurfaceToolButton->setText("C"); + + QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setVerticalSpacing(2); + gridLayout->setHorizontalSpacing(2); + gridLayout->addWidget(this->wholeBrainSurfaceTypeComboBox, 0, 0, 1, 6); + gridLayout->addWidget(this->wholeBrainSurfaceLeftCheckBox, 1, 0); + gridLayout->addWidget(wholeBrainLeftSurfaceToolButton, 1, 1); + gridLayout->addWidget(this->wholeBrainSurfaceRightCheckBox, 2, 0); + gridLayout->addWidget(wholeBrainRightSurfaceToolButton, 2, 1); + gridLayout->addWidget(this->wholeBrainSurfaceCerebellumCheckBox, 3, 0); + gridLayout->addWidget(wholeBrainCerebellumSurfaceToolButton, 3, 1); + gridLayout->addWidget(this->wholeBrainSurfaceSeparationLeftRightSpinBox, 1, 2, 2, 1); + gridLayout->addWidget(this->wholeBrainSurfaceSeparationCerebellumSpinBox, 3, 2); + gridLayout->addWidget(this->wholeBrainSurfaceMatchCheckBox, 4, 0, 1, 6); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); + layout->addLayout(gridLayout); + + addToWidgetGroup(this->wholeBrainSurfaceTypeComboBox); + addToWidgetGroup(this->wholeBrainSurfaceLeftCheckBox); + addToWidgetGroup(wholeBrainLeftSurfaceToolButton); + addToWidgetGroup(this->wholeBrainSurfaceRightCheckBox); + addToWidgetGroup(wholeBrainRightSurfaceToolButton); + addToWidgetGroup(this->wholeBrainSurfaceCerebellumCheckBox); + addToWidgetGroup(wholeBrainCerebellumSurfaceToolButton); + addToWidgetGroup(this->wholeBrainSurfaceSeparationLeftRightSpinBox); + addToWidgetGroup(this->wholeBrainSurfaceSeparationCerebellumSpinBox); + addToWidgetGroup(this->wholeBrainSurfaceMatchCheckBox); + +// QWidget* w = this->createToolWidget("Surface Viewing", +// widget, +// WIDGET_PLACEMENT_LEFT, +// WIDGET_PLACEMENT_TOP, +// 0); +// w->setVisible(false); +// return w; + +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarAllSurface::~BrainBrowserWindowToolBarAllSurface() +{ +} + +/** + * Update the surface montage options widget. + * + * @param browserTabContent + * The active model display controller (may be NULL). + */ +void +BrainBrowserWindowToolBarAllSurface::updateContent(BrowserTabContent* browserTabContent) +{ + ModelWholeBrain* wholeBrainModel = browserTabContent->getDisplayedWholeBrainModel(); + if (wholeBrainModel != NULL) { + const int32_t tabNumber = browserTabContent->getTabNumber(); + + blockAllSignals(true); + + std::vector availableSurfaceTypes; + wholeBrainModel->getAvailableSurfaceTypes(availableSurfaceTypes); + + const SurfaceTypeEnum::Enum selectedSurfaceType = wholeBrainModel->getSelectedSurfaceType(tabNumber); + + int32_t defaultIndex = 0; + this->wholeBrainSurfaceTypeComboBox->clear(); + int32_t numSurfaceTypes = static_cast(availableSurfaceTypes.size()); + for (int32_t i = 0; i < numSurfaceTypes; i++) { + const SurfaceTypeEnum::Enum st = availableSurfaceTypes[i]; + if (st == selectedSurfaceType) { + defaultIndex = this->wholeBrainSurfaceTypeComboBox->count(); + } + const AString name = SurfaceTypeEnum::toGuiName(st); + const int integerCode = SurfaceTypeEnum::toIntegerCode(st); + this->wholeBrainSurfaceTypeComboBox->addItem(name, + integerCode); + } + if (defaultIndex < this->wholeBrainSurfaceTypeComboBox->count()) { + this->wholeBrainSurfaceTypeComboBox->setCurrentIndex(defaultIndex); + } + + this->wholeBrainSurfaceLeftCheckBox->setChecked(browserTabContent->isWholeBrainLeftEnabled()); + this->wholeBrainSurfaceRightCheckBox->setChecked(browserTabContent->isWholeBrainRightEnabled()); + this->wholeBrainSurfaceCerebellumCheckBox->setChecked(browserTabContent->isWholeBrainCerebellumEnabled()); + + updateAllWholeBrainSurfaceMenus(); + + this->wholeBrainSurfaceSeparationLeftRightSpinBox->setValue(browserTabContent->getWholeBrainLeftRightSeparation()); + this->wholeBrainSurfaceSeparationCerebellumSpinBox->setValue(browserTabContent->getWholeBrainCerebellumSeparation()); + this->wholeBrainSurfaceMatchCheckBox->setChecked(wholeBrainModel->getBrain()->isSurfaceMatchingToAnatomical()); + + blockAllSignals(false); + } +} + +/** + * Called when the whole brain surface type combo box is changed. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceTypeComboBoxIndexChanged(int /*indx*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + const int32_t tabIndex = btc->getTabNumber(); + + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + int32_t comboBoxIndex = this->wholeBrainSurfaceTypeComboBox->currentIndex(); + if (comboBoxIndex >= 0) { + const int32_t integerCode = this->wholeBrainSurfaceTypeComboBox->itemData(comboBoxIndex).toInt(); + bool isValid = false; + const SurfaceTypeEnum::Enum surfaceType = SurfaceTypeEnum::fromIntegerCode(integerCode, &isValid); + if (isValid) { + wholeBrainModel->setSelectedSurfaceType(tabIndex, surfaceType); + m_parentToolBar->updateVolumeIndicesWidget(btc); /* slices may get deselected */ + this->updateAllWholeBrainSurfaceMenus(); + this->updateGraphicsWindowAndYokedWindows(); + } + } +} + +/** + * Called when whole brain surface left check box is toggled. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceLeftCheckBoxStateChanged(int /*state*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + btc->setWholeBrainLeftEnabled(this->wholeBrainSurfaceLeftCheckBox->isChecked()); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Update all whole brain surface selection menus + */ +void +BrainBrowserWindowToolBarAllSurface::updateAllWholeBrainSurfaceMenus() +{ + updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceLeftMenu, + StructureEnum::CORTEX_LEFT); + updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceRightMenu, + StructureEnum::CORTEX_RIGHT); + updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceCerebellumMenu, + StructureEnum::CEREBELLUM); +} + +/** + * Update the menu to contain surface for the given structure + * + * @param menu + * Menu that is updated + * @param structure + * Structure for surfaces + */ +void +BrainBrowserWindowToolBarAllSurface::updateWholeBrainSurfaceMenu(QMenu* menu, + const StructureEnum::Enum structure) +{ + CaretAssert(menu); + menu->clear(); + + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + const int32_t tabIndex = btc->getTabNumber(); + + Brain* brain = GuiManager::get()->getBrain(); + BrainStructure* brainStructure = brain->getBrainStructure(structure, false); + if (brainStructure != NULL) { + std::vector surfaces; + brainStructure->getSurfacesOfType(wholeBrainModel->getSelectedSurfaceType(tabIndex), + surfaces); + + const int32_t numSurfaces = static_cast(surfaces.size()); + if (numSurfaces > 0) { + Surface* selectedSurface = wholeBrainModel->getSelectedSurface(structure, + tabIndex); + for (int32_t i = 0; i < numSurfaces; i++) { + QString name = surfaces[i]->getFileNameNoPath(); + QAction* action = new QAction(name); + action->setCheckable(true); + if (surfaces[i] == selectedSurface) { + action->setChecked(true); + } + action->setData(QVariant::fromValue((void*)surfaces[i])); + menu->addAction(action); + } + } + } +} + +/** + * Called when the left surface tool button is pressed. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceLeftToolButtonTriggered(bool /*checked*/) +{ + updateAllWholeBrainSurfaceMenus(); + if ( ! this->wholeBrainSurfaceLeftMenu->isEmpty()) { + this->wholeBrainSurfaceLeftMenu->exec(QCursor::pos()); + } +} + +/** + * Called when left surface is selected from menu + * + * @param action + * Action that was selected + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceLeftMenuTriggered(QAction* action) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + if (action != NULL) { + QVariant data = action->data(); + void* p = data.value(); + Surface* surface = (Surface*)p; + wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_LEFT, + btc->getTabNumber(), + surface); + this->updateGraphicsWindowAndYokedWindows(); + } +} + +/** + * Called when the right surface tool button is pressed. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceRightToolButtonTriggered(bool /*checked*/) +{ + updateAllWholeBrainSurfaceMenus(); + if ( ! this->wholeBrainSurfaceRightMenu->isEmpty()) { + this->wholeBrainSurfaceRightMenu->exec(QCursor::pos()); + } +} + +/** + * Called when right surface is selected from menu + * + * @param action + * Action that was selected + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceRightMenuTriggered(QAction* action) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + if (action != NULL) { + QVariant data = action->data(); + void* p = data.value(); + Surface* surface = (Surface*)p; + wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_RIGHT, + btc->getTabNumber(), + surface); + this->updateGraphicsWindowAndYokedWindows(); + } +} + + +/** + * Called when the cerebellum surface tool button is pressed. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceCerebellumToolButtonTriggered(bool /*checked*/) +{ + updateAllWholeBrainSurfaceMenus(); + if ( ! this->wholeBrainSurfaceCerebellumMenu->isEmpty()) { + this->wholeBrainSurfaceCerebellumMenu->exec(QCursor::pos()); + } +} + +/** + * Called when cerebellum surface is selected from menu + * + * @param action + * Action that was selected + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceCerebellumMenuTriggered(QAction* action) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + if (action != NULL) { + QVariant data = action->data(); + void* p = data.value(); + Surface* surface = (Surface*)p; + wholeBrainModel->setSelectedSurface(StructureEnum::CEREBELLUM, + btc->getTabNumber(), + surface); + this->updateGraphicsWindowAndYokedWindows(); + } +} + + +/** + * Called when whole brain surface right checkbox is toggled. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceRightCheckBoxStateChanged(int /*state*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + btc->setWholeBrainRightEnabled(this->wholeBrainSurfaceRightCheckBox->isChecked()); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when whole brain cerebellum check box is toggled. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceCerebellumCheckBoxStateChanged(int /*state*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + btc->setWholeBrainCerebellumEnabled(this->wholeBrainSurfaceCerebellumCheckBox->isChecked()); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when whole brain separation left/right spin box value is changed. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double /*d*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + btc->setWholeBrainLeftRightSeparation(this->wholeBrainSurfaceSeparationLeftRightSpinBox->value()); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when whole brain left&right/cerebellum spin box value is changed. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double /*d*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + btc->setWholeBrainCerebellumSeparation(this->wholeBrainSurfaceSeparationCerebellumSpinBox->value()); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when match check box is clicked + * + * @param checked + * New checked status. + */ +void +BrainBrowserWindowToolBarAllSurface::wholeBrainSurfaceMatchCheckBoxClicked(bool checked) +{ + Brain* brain = GuiManager::get()->getBrain(); + CaretAssert(brain); + + brain->setSurfaceMatchingToAnatomical(checked); + this->updateGraphicsWindowAndYokedWindows(); + this->updateUserInterface(); +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarAllSurface.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarAllSurface.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarAllSurface.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarAllSurface.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,98 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_ALL_SURFACE_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_ALL_SURFACE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "BrainBrowserWindowToolBarComponent.h" +#include "StructureEnum.h" + +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QMenu; + +namespace caret { + class WuQWidgetObjectGroup; + + class BrainBrowserWindowToolBarAllSurface : public BrainBrowserWindowToolBarComponent { + + Q_OBJECT + + public: + BrainBrowserWindowToolBarAllSurface(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarAllSurface(); + + BrainBrowserWindowToolBarAllSurface(const BrainBrowserWindowToolBarAllSurface&) = delete; + + BrainBrowserWindowToolBarAllSurface& operator=(const BrainBrowserWindowToolBarAllSurface&) = delete; + + virtual void updateContent(BrowserTabContent* browserTabContent) override; + + // ADD_NEW_METHODS_HERE + + private slots: + void wholeBrainSurfaceTypeComboBoxIndexChanged(int indx); + void wholeBrainSurfaceLeftCheckBoxStateChanged(int state); + void wholeBrainSurfaceRightCheckBoxStateChanged(int state); + void wholeBrainSurfaceCerebellumCheckBoxStateChanged(int state); + void wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double d); + void wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double d); + void wholeBrainSurfaceLeftToolButtonTriggered(bool checked); + void wholeBrainSurfaceRightToolButtonTriggered(bool checked); + void wholeBrainSurfaceCerebellumToolButtonTriggered(bool checked); + void wholeBrainSurfaceMatchCheckBoxClicked(bool checked); + + void wholeBrainSurfaceLeftMenuTriggered(QAction*); + void wholeBrainSurfaceRightMenuTriggered(QAction*); + void wholeBrainSurfaceCerebellumMenuTriggered(QAction*); + + private: + BrainBrowserWindowToolBar* m_parentToolBar; + + QComboBox* wholeBrainSurfaceTypeComboBox; + QCheckBox* wholeBrainSurfaceLeftCheckBox; + QCheckBox* wholeBrainSurfaceRightCheckBox; + QCheckBox* wholeBrainSurfaceCerebellumCheckBox; + QMenu* wholeBrainSurfaceLeftMenu; + QMenu* wholeBrainSurfaceRightMenu; + QMenu* wholeBrainSurfaceCerebellumMenu; + QDoubleSpinBox* wholeBrainSurfaceSeparationLeftRightSpinBox; + QDoubleSpinBox* wholeBrainSurfaceSeparationCerebellumSpinBox; + QCheckBox* wholeBrainSurfaceMatchCheckBox; + void updateAllWholeBrainSurfaceMenus(); + void updateWholeBrainSurfaceMenu(QMenu* menu, + const StructureEnum::Enum structure); + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_ALL_SURFACE_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_ALL_SURFACE_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_ALL_SURFACE_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -38,7 +38,6 @@ #include "EnumComboBoxTemplate.h" #include "CaretMappableDataFile.h" #include "ChartTwoMatrixDisplayProperties.h" -#include "EventChartTwoAttributesChanged.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" @@ -114,6 +113,10 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + m_stackedWidget->setCurrentWidget(m_cartesianChartAttributesWidget); + m_cartesianChartAttributesWidget->updateContent(); + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: m_stackedWidget->setCurrentWidget(m_cartesianChartAttributesWidget); m_cartesianChartAttributesWidget->updateContent(); @@ -254,42 +257,7 @@ EventListenerInterface() { m_brainBrowserWindowToolBarChartAttributes = brainBrowserWindowToolBarChartAttributes; - - QLabel* cellWidthLabel = new QLabel("Cell Width"); - const float minPercent = 1.0; - const float maxPercent = 100000.0; - m_cellWidthPercentageSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(minPercent, - maxPercent, - 1.0, - 1, - this, - SLOT(valueChanged())); - m_cellWidthPercentageSpinBox->setToolTip("Percentage of tab width filled with matrix"); - m_cellWidthPercentageSpinBox->setKeyboardTracking(false); - m_cellWidthPercentageSpinBox->setSuffix("%"); - m_cellWidthPercentageSpinBox->setObjectName(parentObjectName - + ":CellWidth"); - WuQMacroManager::instance()->addMacroSupportToObject(m_cellWidthPercentageSpinBox, - "Set matrix chart cell width"); - - QLabel* cellHeightLabel = new QLabel("Cell Height"); - m_cellHeightPercentageSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(minPercent, - maxPercent, - 1.0, - 1, - this, - SLOT(valueChanged())); - m_cellHeightPercentageSpinBox->setToolTip("Percentage of tab height filled with matrix"); - m_cellHeightPercentageSpinBox->setKeyboardTracking(false); - m_cellHeightPercentageSpinBox->setSuffix("%"); - m_cellHeightPercentageSpinBox->setObjectName(parentObjectName - + ":CellHeight"); - WuQMacroManager::instance()->addMacroSupportToObject(m_cellHeightPercentageSpinBox, - "Set matrix chart cell height"); - - WuQtUtilities::matchWidgetWidths(m_cellHeightPercentageSpinBox, - m_cellWidthPercentageSpinBox); - + m_highlightSelectionCheckBox = new QCheckBox("Highlight Selection"); m_highlightSelectionCheckBox->setToolTip("Highlight selected row/column in the matrix"); QObject::connect(m_highlightSelectionCheckBox, SIGNAL(clicked(bool)), @@ -308,23 +276,11 @@ WuQMacroManager::instance()->addMacroSupportToObject(m_displayGridLinesCheckBox, "Enable matrix chart grid outline"); - m_manualWidgetsGroup = new WuQWidgetObjectGroup(this); - m_manualWidgetsGroup->add(m_cellWidthPercentageSpinBox); - m_manualWidgetsGroup->add(m_cellHeightPercentageSpinBox); - m_manualWidgetsGroup->add(m_displayGridLinesCheckBox); - const int32_t COLUMN_LABEL = 0; - const int32_t COLUMN_WIDGET = 1; QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); int32_t rowIndex = gridLayout->rowCount(); - gridLayout->addWidget(cellWidthLabel, rowIndex, COLUMN_LABEL); - gridLayout->addWidget(m_cellWidthPercentageSpinBox, rowIndex, COLUMN_WIDGET); - rowIndex++; - gridLayout->addWidget(cellHeightLabel, rowIndex, COLUMN_LABEL); - gridLayout->addWidget(m_cellHeightPercentageSpinBox, rowIndex, COLUMN_WIDGET); - rowIndex++; gridLayout->addWidget(m_highlightSelectionCheckBox, rowIndex, COLUMN_LABEL, 1, 2, Qt::AlignLeft); rowIndex++; gridLayout->addWidget(m_displayGridLinesCheckBox, rowIndex, COLUMN_LABEL, 1, 2, Qt::AlignLeft); @@ -361,14 +317,6 @@ { ChartTwoMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableTwoMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { - m_cellWidthPercentageSpinBox->blockSignals(true); - m_cellWidthPercentageSpinBox->setValue(matrixDisplayProperties->getCellPercentageZoomWidth()); - m_cellWidthPercentageSpinBox->blockSignals(false); - - m_cellHeightPercentageSpinBox->blockSignals(true); - m_cellHeightPercentageSpinBox->setValue(matrixDisplayProperties->getCellPercentageZoomHeight()); - m_cellHeightPercentageSpinBox->blockSignals(false); - m_highlightSelectionCheckBox->blockSignals(true); m_highlightSelectionCheckBox->setChecked(matrixDisplayProperties->isSelectedRowColumnHighlighted()); m_highlightSelectionCheckBox->blockSignals(false); @@ -387,8 +335,6 @@ { ChartTwoMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableTwoMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { - matrixDisplayProperties->setCellPercentageZoomWidth(m_cellWidthPercentageSpinBox->value()); - matrixDisplayProperties->setCellPercentageZoomHeight(m_cellHeightPercentageSpinBox->value()); matrixDisplayProperties->setSelectedRowColumnHighlighted(m_highlightSelectionCheckBox->isChecked()); matrixDisplayProperties->setGridLinesDisplayed(m_displayGridLinesCheckBox->isChecked()); diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h 2021-02-16 19:46:47.000000000 +0000 @@ -121,16 +121,9 @@ private: BrainBrowserWindowToolBarChartTwoAttributes* m_brainBrowserWindowToolBarChartAttributes; - QDoubleSpinBox* m_cellWidthPercentageSpinBox; - - QDoubleSpinBox* m_cellHeightPercentageSpinBox; - QCheckBox* m_highlightSelectionCheckBox; - QCheckBox* m_displayGridLinesCheckBox; - - WuQWidgetObjectGroup* m_manualWidgetsGroup; - + QCheckBox* m_displayGridLinesCheckBox; }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ATTRIBUTES_DECLARE__ // diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,883 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ -#include "BrainBrowserWindowToolBarChartTwoAxes.h" -#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ - -#include "AnnotationPercentSizeText.h" -#include "BrowserTabContent.h" -#include "CaretAssert.h" -#include "CaretDataFile.h" -#include "CaretDataFileSelectionModel.h" -#include "CaretMappableDataFile.h" -#include "ChartTwoCartesianAxis.h" -#include "ChartTwoOverlay.h" -#include "ChartTwoOverlaySet.h" -#include "ChartableTwoFileBaseChart.h" -#include "ChartableTwoFileDelegate.h" -#include "ChartableTwoFileHistogramChart.h" -#include "ChartableTwoFileLineSeriesChart.h" -#include "EnumComboBoxTemplate.h" -#include "EventBrowserWindowGraphicsRedrawn.h" -#include "EventChartTwoAttributesChanged.h" -#include "EventGraphicsUpdateAllWindows.h" -#include "EventManager.h" -#include "ModelChartTwo.h" -#include "WuQDataEntryDialog.h" -#include "WuQFactory.h" -#include "WuQDoubleSpinBox.h" -#include "WuQMacroManager.h" -#include "WuQWidgetObjectGroup.h" -#include "WuQtUtilities.h" - -using namespace caret; - - - -/** - * \class caret::BrainBrowserWindowToolBarChartTwoAxes - * \brief Controls for chart attributes. - * \ingroup GuiQt - */ - -/** - * Constructor. - * - * @param parentToolBar - * The parent toolbar. - * @param parentObjectName - * Name of parent object for macros - */ -BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar, - const QString& parentObjectName) -: BrainBrowserWindowToolBarComponent(parentToolBar) -{ - m_chartOverlaySet = NULL; - m_chartAxis = NULL; - - WuQMacroManager* macroManager = WuQMacroManager::instance(); - const QString objectNamePrefix(parentObjectName - + ":ChartAxes:"); - - /* - * 'Show' checkboxes - */ - m_axisDisplayedByUserCheckBox = new QCheckBox("Axis"); - m_axisDisplayedByUserCheckBox->setToolTip("Show/hide the axis"); - QObject::connect(m_axisDisplayedByUserCheckBox, &QCheckBox::clicked, - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); - m_axisDisplayedByUserCheckBox->setObjectName(objectNamePrefix - + "ShowAxis"); - macroManager->addMacroSupportToObject(m_axisDisplayedByUserCheckBox, - "Enable chart axis"); - - - m_showTickMarksCheckBox = new QCheckBox("Ticks"); - QObject::connect(m_showTickMarksCheckBox, &QCheckBox::clicked, - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); - m_showTickMarksCheckBox->setToolTip("Show ticks along the axis"); - m_showTickMarksCheckBox->setObjectName(objectNamePrefix - + "ShowTicks"); - macroManager->addMacroSupportToObject(m_showTickMarksCheckBox, - "Enable chart axis ticks"); - - m_showLabelCheckBox = new QCheckBox("Label"); - QObject::connect(m_showLabelCheckBox, &QCheckBox::clicked, - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); - m_showLabelCheckBox->setToolTip("Show label on axis"); - m_showLabelCheckBox->setObjectName(objectNamePrefix - + "ShowLabel"); - macroManager->addMacroSupportToObject(m_showLabelCheckBox, - "Enable chart axis label"); - - m_showNumericsCheckBox = new QCheckBox("Nums"); - QObject::connect(m_showNumericsCheckBox, &QCheckBox::clicked, - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); - m_showNumericsCheckBox->setToolTip("Show numeric scale values on axis"); - m_showNumericsCheckBox->setObjectName(objectNamePrefix - + "ShowNumerics"); - macroManager->addMacroSupportToObject(m_showNumericsCheckBox, - "Enable chart axis numerics"); - - m_rotateNumericsCheckBox = new QCheckBox("Rotate"); - QObject::connect(m_rotateNumericsCheckBox, &QCheckBox::clicked, - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); - m_rotateNumericsCheckBox->setToolTip("Rotate numeric scale values on axis"); - m_rotateNumericsCheckBox->setObjectName(objectNamePrefix - + "EnableNumericsRotate"); - macroManager->addMacroSupportToObject(m_rotateNumericsCheckBox, - "Enable rotation of chart axis numerics"); - - /* - * Axes selection - */ - m_axisComboBox = new EnumComboBoxTemplate(this); - m_axisComboBox->setup(); - QObject::connect(m_axisComboBox, &EnumComboBoxTemplate::itemActivated, - this, &BrainBrowserWindowToolBarChartTwoAxes::axisChanged); - m_axisComboBox->getWidget()->setToolTip("Choose axis for editing"); - m_axisComboBox->getWidget()->setObjectName(objectNamePrefix - + "ChooseAxis"); - macroManager->addMacroSupportToObject(m_axisComboBox->getComboBox(), - "Select chart axis"); - - /* - * Controls for layer selection and label editing - */ - m_axisLabelToolButton = new QToolButton(); - m_axisLabelToolButton->setText("Edit Label..."); - QObject::connect(m_axisLabelToolButton, &QToolButton::clicked, - this, &BrainBrowserWindowToolBarChartTwoAxes::axisLabelToolButtonClicked); - WuQtUtilities::setToolButtonStyleForQt5Mac(m_axisLabelToolButton); - m_axisLabelToolButton->setToolTip("Edit the axis name for the file in the selected overlay"); - m_axisLabelToolButton->setObjectName(objectNamePrefix - + "EditAxis"); - macroManager->addMacroSupportToObject(m_axisLabelToolButton, - "Edit chart axis label"); - - QLabel* axisLabelFromOverlayLabel = new QLabel("Label From File In"); - m_axisLabelFromOverlayComboBox = new QComboBox(); - m_axisLabelFromOverlayComboBox->setToolTip("Label for axis is from file in selected layer"); - QObject::connect(m_axisLabelFromOverlayComboBox, static_cast(&QComboBox::activated), - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); - m_axisLabelFromOverlayComboBox->setObjectName(objectNamePrefix - + "LabelFromOverlay"); - macroManager->addMacroSupportToObject(m_axisLabelFromOverlayComboBox, - "Select chart axis overlay source"); - - /* - * Range controls - */ - const AString rangeTooltip("Auto - Adjusts axis range to fit data with some\n" - " padding so that scale value are usually\n" - " whole numbers\n" - "Data - Axis range is limited to minimum and \n" - " maximum values of the data\n" - "User - Axis range is controlled by user"); - m_autoUserRangeComboBox = new EnumComboBoxTemplate(this); - m_autoUserRangeComboBox->setup(); - QObject::connect(m_autoUserRangeComboBox, &EnumComboBoxTemplate::itemActivated, - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); - m_autoUserRangeComboBox->getWidget()->setToolTip(rangeTooltip); - m_autoUserRangeComboBox->getWidget()->setObjectName(objectNamePrefix - + "RangeMode"); - macroManager->addMacroSupportToObject(m_autoUserRangeComboBox->getWidget(), - "Select chart axis range mode"); - - m_userMinimumValueSpinBox = new WuQDoubleSpinBox(this); - m_userMinimumValueSpinBox->setDecimalsModeAuto(); - m_userMinimumValueSpinBox->setSingleStepPercentage(1.0); - QObject::connect(m_userMinimumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoAxes::axisMinimumValueChanged); - m_userMinimumValueSpinBox->setToolTip("Set user scaling axis minimum value"); - m_userMinimumValueSpinBox->getWidget()->setObjectName(objectNamePrefix - + "ScaleMinimum"); - macroManager->addMacroSupportToObject(m_userMinimumValueSpinBox->getWidget(), - "Set chart axis minimum"); - - m_userMaximumValueSpinBox = new WuQDoubleSpinBox(this); - m_userMaximumValueSpinBox->setDecimalsModeAuto(); - m_userMaximumValueSpinBox->setSingleStepPercentage(1.0); - QObject::connect(m_userMaximumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoAxes::axisMaximumValueChanged); - m_userMaximumValueSpinBox->setToolTip("Set user scaling axis maximum value"); - m_userMaximumValueSpinBox->getWidget()->setObjectName(objectNamePrefix - + "ScaleMaximum"); - macroManager->addMacroSupportToObject(m_userMaximumValueSpinBox->getWidget(), - "See chart axis maximum"); - - /* - * Format controls - */ - m_userNumericFormatComboBox = new EnumComboBoxTemplate(this); - m_userNumericFormatComboBox->setup(); - QObject::connect(m_userNumericFormatComboBox, &EnumComboBoxTemplate::itemActivated, - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); - m_userNumericFormatComboBox->getWidget()->setToolTip("Choose format of axis scale numeric values"); - m_userNumericFormatComboBox->getWidget()->setObjectName(objectNamePrefix - + "Format"); - macroManager->addMacroSupportToObject(m_userNumericFormatComboBox->getWidget(), - "Select chart axis numeric format"); - - m_userDigitsRightOfDecimalSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, 10, 1); - QObject::connect(m_userDigitsRightOfDecimalSpinBox, static_cast(&QSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); - m_userDigitsRightOfDecimalSpinBox->setToolTip("Set digits right of decimal for\ndecimal or scientific format"); - m_userDigitsRightOfDecimalSpinBox->setObjectName(objectNamePrefix - + "DigitsRightOfDecimal"); - macroManager->addMacroSupportToObject(m_userDigitsRightOfDecimalSpinBox, - "Set chart axis digits right of decimal"); - - m_numericSubdivisionsModeComboBox = new EnumComboBoxTemplate(this); - m_numericSubdivisionsModeComboBox->setup(); - QObject::connect(m_numericSubdivisionsModeComboBox, &EnumComboBoxTemplate::itemActivated, - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); - m_numericSubdivisionsModeComboBox->getWidget()->setToolTip("Numeric subdivisions mode"); - m_numericSubdivisionsModeComboBox->getWidget()->setObjectName(objectNamePrefix - + "NumericSubdivisionsMode"); - macroManager->addMacroSupportToObject(m_numericSubdivisionsModeComboBox->getWidget(), - "Set chart axis numeric subdivisions mode"); - - m_userSubdivisionsSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, 100, 1); - QObject::connect(m_userSubdivisionsSpinBox, static_cast(&QSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); - m_userSubdivisionsSpinBox->setToolTip("Set subdivisions on the axis when Auto is not checked"); - m_userSubdivisionsSpinBox->setObjectName(objectNamePrefix - + "NumberOfSubdivisions"); - macroManager->addMacroSupportToObject(m_userSubdivisionsSpinBox, - "Set chart axis number of subivisions"); - - /* - * Size spin boxes - */ - m_labelSizeSpinBox = new WuQDoubleSpinBox(this); - m_labelSizeSpinBox->setRangePercentage(0.0, 100.0); - QObject::connect(m_labelSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); - m_labelSizeSpinBox->setToolTip("Set height of label as percentage of tab height for selected axis"); - m_labelSizeSpinBox->getWidget()->setObjectName(objectNamePrefix - + "LabelHeight"); - macroManager->addMacroSupportToObject(m_labelSizeSpinBox->getWidget(), - "Set chart axis label height"); - - m_numericsSizeSpinBox = new WuQDoubleSpinBox(this); - m_numericsSizeSpinBox->setRangePercentage(0.0, 100.0); - QObject::connect(m_numericsSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); - m_numericsSizeSpinBox->setToolTip("Set height of numeric values as percentage of tab height for selected axis"); - m_numericsSizeSpinBox->getWidget()->setObjectName(objectNamePrefix - + "NumericValueHeight"); - macroManager->addMacroSupportToObject(m_numericsSizeSpinBox->getWidget(), - "Set chart axis numerics height"); - - m_linesTicksSizeSpinBox = new WuQDoubleSpinBox(this); - m_linesTicksSizeSpinBox->setRangePercentage(0.0, 100.0); - QObject::connect(m_linesTicksSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoAxes::axisLineThicknessChanged); - m_linesTicksSizeSpinBox->setToolTip("Set thickness of axis lines as percentage of tab height for ALL axes"); - m_linesTicksSizeSpinBox->getWidget()->setObjectName(objectNamePrefix - + "TicksSize"); - macroManager->addMacroSupportToObject(m_linesTicksSizeSpinBox->getWidget(), - "Set chart axis ticks height"); - - m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); - m_paddingSizeSpinBox->setRangePercentage(0.0, 100.0); - QObject::connect(m_paddingSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); - m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height for selected axis"); - m_paddingSizeSpinBox->getWidget()->setObjectName(objectNamePrefix - + "PaddingSize"); - macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget(), - "Set chart axis padding height"); - - /* - * Group widgets for blocking signals - */ - m_widgetGroup = new WuQWidgetObjectGroup(this); - m_widgetGroup->add(m_axisComboBox); - m_widgetGroup->add(m_axisDisplayedByUserCheckBox); - m_widgetGroup->add(m_axisLabelToolButton); - m_widgetGroup->add(m_autoUserRangeComboBox->getWidget()); - m_widgetGroup->add(m_userMinimumValueSpinBox); - m_widgetGroup->add(m_userMaximumValueSpinBox); - m_widgetGroup->add(m_showTickMarksCheckBox); - m_widgetGroup->add(m_showLabelCheckBox); - m_widgetGroup->add(m_showNumericsCheckBox); - m_widgetGroup->add(m_rotateNumericsCheckBox); - m_widgetGroup->add(m_axisLabelFromOverlayComboBox); - m_widgetGroup->add(m_userNumericFormatComboBox->getWidget()); - m_widgetGroup->add(m_userDigitsRightOfDecimalSpinBox); - m_widgetGroup->add(m_numericSubdivisionsModeComboBox->getWidget()); - m_widgetGroup->add(m_userSubdivisionsSpinBox); - m_widgetGroup->add(m_labelSizeSpinBox); - m_widgetGroup->add(m_numericsSizeSpinBox); - m_widgetGroup->add(m_linesTicksSizeSpinBox); - m_widgetGroup->add(m_paddingSizeSpinBox); - - /* - * Size layout - */ - QWidget* sizesWidget = new QWidget(); - QGridLayout* sizesLayout = new QGridLayout(sizesWidget); - WuQtUtilities::setLayoutSpacingAndMargins(sizesLayout, 2, 0); - int32_t sizesRow = 0; - sizesLayout->addWidget(new QLabel("Label"), sizesRow, 0); - sizesLayout->addWidget(m_labelSizeSpinBox->getWidget(), sizesRow, 1); - sizesRow++; - sizesLayout->addWidget(new QLabel("Scale"), sizesRow, 0); - sizesLayout->addWidget(m_numericsSizeSpinBox->getWidget(), sizesRow, 1); - sizesRow++; - sizesLayout->addWidget(new QLabel("Pad"), sizesRow, 0); - sizesLayout->addWidget(m_paddingSizeSpinBox->getWidget(), sizesRow, 1); - sizesRow++; - sizesLayout->addWidget(new QLabel("Lines"), sizesRow, 0); - sizesLayout->addWidget(m_linesTicksSizeSpinBox->getWidget(), sizesRow, 1); - sizesRow++; - - /* - * Show widgets layout - */ - const bool displayLabelShowAtTopFlag = false; - QWidget* showWidget = new QWidget(); - QGridLayout* showLayout = new QGridLayout(showWidget); - WuQtUtilities::setLayoutSpacingAndMargins(showLayout, 1, 0); - int32_t axisRow = 0; - if (displayLabelShowAtTopFlag) { - showLayout->addWidget(new QLabel("Show"), axisRow, 0, Qt::AlignHCenter); - axisRow++; - } - showLayout->addWidget(m_axisDisplayedByUserCheckBox, axisRow, 0); - axisRow++; - showLayout->addWidget(m_showLabelCheckBox, axisRow, 0); - axisRow++; - showLayout->addWidget(m_showNumericsCheckBox, axisRow, 0); - axisRow++; - showLayout->addWidget(m_rotateNumericsCheckBox, axisRow, 0); - axisRow++; - showLayout->addWidget(m_showTickMarksCheckBox, axisRow, 0); - axisRow++; - showWidget->setSizePolicy(showWidget->sizePolicy().horizontalPolicy(), - QSizePolicy::Fixed); - - /* - * Range widgets layout - */ - QWidget* rangeWidget = new QWidget(); - QGridLayout* rangeLayout = new QGridLayout(rangeWidget); - WuQtUtilities::setLayoutSpacingAndMargins(rangeLayout, 3, 0); - int rangeRow = 0; - rangeLayout->addWidget(new QLabel("Range"), rangeRow, 0); - m_autoUserRangeComboBox->getComboBox()->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); - rangeLayout->addWidget(m_autoUserRangeComboBox->getWidget(), rangeRow, 1); - rangeRow++; - m_userMinimumValueSpinBox->setFixedWidth(90); - m_userMaximumValueSpinBox->setFixedWidth(90); - rangeLayout->addWidget(new QLabel("Max"), rangeRow, 0); - rangeLayout->addWidget(m_userMaximumValueSpinBox->getWidget(), rangeRow, 1); - rangeRow++; - rangeLayout->addWidget(new QLabel("Min"), rangeRow, 0); - rangeLayout->addWidget(m_userMinimumValueSpinBox->getWidget(), rangeRow, 1); - - /* - * Numerics widgets layout - */ - QWidget* numericsWidget = new QWidget(); - QGridLayout* numericsLayout = new QGridLayout(numericsWidget); - WuQtUtilities::setLayoutSpacingAndMargins(numericsLayout, 3, 0); - int numericsRow = 0; - numericsLayout->addWidget(new QLabel("Format"), numericsRow, 0); - numericsLayout->addWidget(m_userNumericFormatComboBox->getWidget(), numericsRow, 1); - numericsRow++; - numericsLayout->addWidget(new QLabel("Decimals"), numericsRow, 0); - numericsLayout->addWidget(m_userDigitsRightOfDecimalSpinBox, numericsRow, 1); - numericsRow++; - numericsLayout->addWidget(new QLabel("Subdivisions"), numericsRow, 0); - numericsLayout->addWidget(m_numericSubdivisionsModeComboBox->getWidget(), numericsRow, 1); - numericsRow++; - numericsLayout->addWidget(m_userSubdivisionsSpinBox, numericsRow, 1); - - /* - * Top layout - */ - QHBoxLayout* topLayout = new QHBoxLayout(); - WuQtUtilities::setLayoutSpacingAndMargins(topLayout, 3, 0); - topLayout->addWidget(new QLabel("Edit Axis ")); - topLayout->addWidget(m_axisComboBox->getWidget()); - topLayout->addSpacing(10); - topLayout->addStretch(); - topLayout->addWidget(axisLabelFromOverlayLabel); - topLayout->addWidget(m_axisLabelFromOverlayComboBox); - topLayout->addSpacing(10); - topLayout->addWidget(m_axisLabelToolButton); - - /* - * Grid layout containing layouts - */ - QGridLayout* gridLayout = new QGridLayout(); - gridLayout->setHorizontalSpacing(4); - gridLayout->setVerticalSpacing(1); - gridLayout->setContentsMargins(0, 0, 0, 0); -// WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); - gridLayout->addLayout(topLayout, 0, 0, 1, 7); - gridLayout->addWidget(showWidget, 1, 0, Qt::AlignTop); - gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 1, 1); - gridLayout->addWidget(sizesWidget, 1, 2, Qt::AlignTop); - gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 1, 3); - gridLayout->addWidget(rangeWidget, 1, 4, Qt::AlignTop); - gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 1, 5); - gridLayout->addWidget(numericsWidget, 1, 6, Qt::AlignTop); - - QVBoxLayout* layout = new QVBoxLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); //2); - layout->addLayout(gridLayout); - layout->addStretch(); - - EventManager::get()->addEventListener(this, - EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); -} - -/** - * Destructor. - */ -BrainBrowserWindowToolBarChartTwoAxes::~BrainBrowserWindowToolBarChartTwoAxes() -{ - EventManager::get()->removeEventFromListener(this, - EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); -} - -/** - * Receive an event. - * - * @param event - * The event. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::receiveEvent(Event* event) -{ - if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN) { - CaretAssert(dynamic_cast(event)); - updateContent(getTabContentFromSelectedTab()); - } - else { - BrainBrowserWindowToolBarComponent::receiveEvent(event); - } -} - -/** - * Update content of this tool bar component. - * - * @param browserTabContent - * Content of the browser tab. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::updateContent(BrowserTabContent* browserTabContent) -{ - if (browserTabContent == NULL) { - setEnabled(false); - return; - } - - updateControls(browserTabContent); -} - -/** - * Get the selection data. - * - * @param browserTabContent - * The tab content. - * @param chartOverlaySetOut - * The chart overlay set (may be NULL) - * @param validAxesLocationsOut - * The valid axes locations. - * @param selectedAxisOut - * Output with selected axis (may be NULL) - */ -void -BrainBrowserWindowToolBarChartTwoAxes::getSelectionData(BrowserTabContent* browserTabContent, - ChartTwoOverlaySet* &chartOverlaySetOut, - std::vector& validAxesLocationsOut, - ChartTwoCartesianAxis* &selectedAxisOut) const -{ - chartOverlaySetOut = NULL; - validAxesLocationsOut.clear(); - selectedAxisOut = NULL; - - if (browserTabContent == NULL) { - return; - } - - const ChartAxisLocationEnum::Enum lastSelectedAxis = getSelectedAxisLocation(); - - if (browserTabContent != NULL) { - ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); - const int32_t tabIndex = browserTabContent->getTabNumber(); - if (modelChartTwo != NULL) { - switch (modelChartTwo->getSelectedChartTwoDataType(tabIndex)) { - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: - chartOverlaySetOut = modelChartTwo->getChartTwoOverlaySet(tabIndex); - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: - chartOverlaySetOut = modelChartTwo->getChartTwoOverlaySet(tabIndex); - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: - break; - } - - if (chartOverlaySetOut != NULL) { - int32_t defaultAxisIndex = -1; - - std::vector axes; - chartOverlaySetOut->getDisplayedChartAxes(axes); - const int32_t numAxes = static_cast(axes.size()); - for (int32_t i = 0; i < numAxes; i++) { - const ChartAxisLocationEnum::Enum axisLocation = axes[i]->getAxisLocation(); - if (lastSelectedAxis == axisLocation) { - defaultAxisIndex = i; - } - validAxesLocationsOut.push_back(axisLocation); - } - CaretAssert(validAxesLocationsOut.size() == axes.size()); - - if (defaultAxisIndex < 0) { - /* - * If selected axis not found, switch to opposite axis - * User may have switched vertical axis from left to right - */ - ChartAxisLocationEnum::Enum oppositeAxis = ChartAxisLocationEnum::getOppositeAxis(lastSelectedAxis); - for (int32_t i = 0; i < numAxes; i++) { - if (oppositeAxis == axes[i]->getAxisLocation()) { - defaultAxisIndex = i; - break; - } - } - } - - if ( ! axes.empty()) { - if (defaultAxisIndex < 0) { - defaultAxisIndex = 0; - } - CaretAssertVectorIndex(axes, defaultAxisIndex); - selectedAxisOut = axes[defaultAxisIndex]; - } - } - } - } -} - -/** - * Called when axis is changed. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::axisChanged() -{ - updateContent(getTabContentFromSelectedTab()); -} - -/** - * @return The selected axes location (will return left even if no valid selection). - */ -ChartAxisLocationEnum::Enum -BrainBrowserWindowToolBarChartTwoAxes::getSelectedAxisLocation() const -{ - ChartAxisLocationEnum::Enum axis = ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT; - - if (m_axisComboBox->getComboBox()->count() > 0) { - axis = m_axisComboBox->getSelectedItem(); - } - - return axis; -} - - -void -BrainBrowserWindowToolBarChartTwoAxes::updateControls(BrowserTabContent* browserTabContent) -{ - m_chartOverlaySet = NULL; - std::vector validAxesLocations; - ChartTwoCartesianAxis* selectedAxis = NULL; - - getSelectionData(browserTabContent, - m_chartOverlaySet, - validAxesLocations, - selectedAxis); - - m_widgetGroup->blockAllSignals(true); - - m_chartAxis = selectedAxis; - if ((m_chartOverlaySet != NULL) - && (m_chartAxis != NULL)) { - m_axisComboBox->setupWithItems(validAxesLocations); - m_axisComboBox->setSelectedItem(m_chartAxis->getAxisLocation()); - - m_axisDisplayedByUserCheckBox->setChecked(m_chartAxis->isDisplayedByUser()); - m_autoUserRangeComboBox->setSelectedItem(m_chartAxis->getScaleRangeMode()); - float rangeMin(0.0f), rangeMax(0.0f); - m_chartAxis->getDataRange(rangeMin, rangeMax); - m_userMinimumValueSpinBox->setRangeExceedable(rangeMin, rangeMax); - m_userMinimumValueSpinBox->setValue(m_chartAxis->getUserScaleMinimumValue()); - m_userMaximumValueSpinBox->setRangeExceedable(rangeMin, rangeMax); - m_userMaximumValueSpinBox->setValue(m_chartAxis->getUserScaleMaximumValue()); - m_showTickMarksCheckBox->setChecked(m_chartAxis->isShowTickmarks()); - m_showLabelCheckBox->setChecked(m_chartAxis->isShowLabel()); - m_showNumericsCheckBox->setChecked(m_chartAxis->isNumericsTextDisplayed()); - m_rotateNumericsCheckBox->setChecked(m_chartAxis->isNumericsTextRotated()); - const NumericFormatModeEnum::Enum numericFormat = m_chartAxis->getUserNumericFormat(); - m_userNumericFormatComboBox->setSelectedItem(numericFormat); - m_userDigitsRightOfDecimalSpinBox->setValue(m_chartAxis->getUserDigitsRightOfDecimal()); - m_userDigitsRightOfDecimalSpinBox->setEnabled(numericFormat != NumericFormatModeEnum::AUTO); - m_numericSubdivisionsModeComboBox->setSelectedItem(m_chartAxis->getNumericSubdivsionsMode()); - m_userSubdivisionsSpinBox->setValue(m_chartAxis->getUserNumberOfSubdivisions()); - m_userSubdivisionsSpinBox->setEnabled( m_chartAxis->getNumericSubdivsionsMode() == ChartTwoNumericSubdivisionsModeEnum::USER); - - m_labelSizeSpinBox->setValue(m_chartAxis->getLabelTextSize()); - m_numericsSizeSpinBox->setValue(m_chartAxis->getNumericsTextSize()); - m_linesTicksSizeSpinBox->setValue(m_chartOverlaySet->getAxisLineThickness()); - m_paddingSizeSpinBox->setValue(m_chartAxis->getPaddingSize()); - - const int32_t overlayCount = m_chartOverlaySet->getNumberOfDisplayedOverlays(); - int32_t selectedOverlayIndex = m_chartAxis->getLabelOverlayIndex(overlayCount); - const int32_t comboBoxCount = m_axisLabelFromOverlayComboBox->count(); - if (overlayCount < comboBoxCount) { - m_axisLabelFromOverlayComboBox->setMaxCount(overlayCount); - } - else if (overlayCount > comboBoxCount) { - for (int32_t j = comboBoxCount; j < overlayCount; j++) { - m_axisLabelFromOverlayComboBox->addItem("Layer " + AString::number(j + 1)); - } - } - - if ((selectedOverlayIndex >= 0) - && (selectedOverlayIndex < m_axisLabelFromOverlayComboBox->count())) { - m_axisLabelFromOverlayComboBox->setCurrentIndex(selectedOverlayIndex); - } - setEnabled(true); - } - else { - setEnabled(false); - } - - m_widgetGroup->blockAllSignals(false); -} - -/** - * Called when the axis line thickness changes. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::axisLineThicknessChanged(double) -{ - if (m_chartOverlaySet != NULL) { - m_chartOverlaySet->setAxisLineThickness(m_linesTicksSizeSpinBox->value()); - - const BrowserTabContent* tabContent = getTabContentFromSelectedTab(); - CaretAssert(tabContent); - - const YokingGroupEnum::Enum yokingGroup = tabContent->getChartModelYokingGroup(); - if (yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { - const ModelChartTwo* modelChartTwo = tabContent->getDisplayedChartTwoModel(); - CaretAssert(modelChartTwo); - const int32_t tabIndex = tabContent->getTabNumber(); - EventChartTwoAttributesChanged attributesEvent; - attributesEvent.setLineThicknessChanged(yokingGroup, - modelChartTwo->getSelectedChartTwoDataType(tabIndex), - m_linesTicksSizeSpinBox->value()); - EventManager::get()->sendEvent(attributesEvent.getPointer()); - } - } - - updateGraphics(); - - updateContent(getTabContentFromSelectedTab()); -} - - -/** - * Called when a widget is changed by the user. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::valueChanged() -{ - CaretAssert(m_chartAxis); - if (m_chartAxis != NULL) { - m_chartAxis->setLabelOverlayIndex(m_axisLabelFromOverlayComboBox->currentIndex()); - m_chartAxis->setDisplayedByUser(m_axisDisplayedByUserCheckBox->isChecked()); - m_chartAxis->setScaleRangeMode(m_autoUserRangeComboBox->getSelectedItem()); - m_chartAxis->setUserScaleMinimumValue(m_userMinimumValueSpinBox->value()); - m_chartAxis->setUserScaleMaximumValue(m_userMaximumValueSpinBox->value()); - m_chartAxis->setShowTickmarks(m_showTickMarksCheckBox->isChecked()); - m_chartAxis->setShowLabel(m_showLabelCheckBox->isChecked()); - m_chartAxis->setNumericsTextDisplayed(m_showNumericsCheckBox->isChecked()); - m_chartAxis->setNumericsTextRotated(m_rotateNumericsCheckBox->isChecked()); - m_chartAxis->setUserNumericFormat(m_userNumericFormatComboBox->getSelectedItem()); - m_chartAxis->setUserDigitsRightOfDecimal(m_userDigitsRightOfDecimalSpinBox->value()); - m_chartAxis->setNumericSubdivsionsMode(m_numericSubdivisionsModeComboBox->getSelectedItem()); - m_chartAxis->setUserNumberOfSubdivisions(m_userSubdivisionsSpinBox->value()); - - m_chartAxis->setLabelTextSize(m_labelSizeSpinBox->value()); - m_chartAxis->setNumericsTextSize(m_numericsSizeSpinBox->value()); - m_chartAxis->setPaddingSize(m_paddingSizeSpinBox->value()); - - const BrowserTabContent* tabContent = getTabContentFromSelectedTab(); - CaretAssert(tabContent); - - const YokingGroupEnum::Enum yokingGroup = tabContent->getChartModelYokingGroup(); - if (yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { - const ModelChartTwo* modelChartTwo = tabContent->getDisplayedChartTwoModel(); - CaretAssert(modelChartTwo); - const int32_t tabIndex = tabContent->getTabNumber(); - EventChartTwoAttributesChanged attributesEvent; - attributesEvent.setCartesianAxisChanged(yokingGroup, - modelChartTwo->getSelectedChartTwoDataType(tabIndex), - m_chartAxis); - EventManager::get()->sendEvent(attributesEvent.getPointer()); - } - } - - updateGraphics(); - - updateContent(getTabContentFromSelectedTab()); -} - -/** - * Update the graphics. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::updateGraphics() -{ - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); -} - -/** - * Called when the minimum value is changed. - * - * @param minimumValue - * New minimum value. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::axisMinimumValueChanged(double minimumValue) -{ - if (m_chartAxis != NULL) { - /* - * If the minimum or maximum value is modified by user, - * ensure Auto/User Range selection is USER - */ - m_autoUserRangeComboBox->getWidget()->blockSignals(true); - m_autoUserRangeComboBox->setSelectedItem(ChartTwoAxisScaleRangeModeEnum::USER); - m_autoUserRangeComboBox->getWidget()->blockSignals(false); - - /* - * Ensure maximum value is always greater than or equal to minimum - */ - if (minimumValue > m_userMaximumValueSpinBox->value()) { - m_userMaximumValueSpinBox->getWidget()->blockSignals(true); - m_userMaximumValueSpinBox->setValue(minimumValue); - m_userMaximumValueSpinBox->getWidget()->blockSignals(false); - } - - valueChanged(); - } -} - -/** - * Called when the maximum value is changed. - * - * @param maximumValue - * New maximum value. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::axisMaximumValueChanged(double maximumValue) -{ - if (m_chartAxis != NULL) { - /* - * If the minimum or maximum value is modified by user, - * ensure Auto/User Range selection is USER - */ - m_autoUserRangeComboBox->getWidget()->blockSignals(true); - m_autoUserRangeComboBox->setSelectedItem(ChartTwoAxisScaleRangeModeEnum::USER); - m_autoUserRangeComboBox->getWidget()->blockSignals(false); - - /* - * Ensure minimum value is always less than or equal to maximum - */ - if (maximumValue < m_userMinimumValueSpinBox->value()) { - m_userMinimumValueSpinBox->getWidget()->blockSignals(true); - m_userMinimumValueSpinBox->setValue(maximumValue); - m_userMinimumValueSpinBox->getWidget()->blockSignals(false); - } - - valueChanged(); - } -} - -/** - * Called when a widget is changed by a slot using a bool parameter. - * Parameters must match when using function pointers. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool(bool) -{ - valueChanged(); -} - -/** - * Called when a widget is changed by a slot using a double parameter. - * Parameters must match when using function pointers. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble(double) -{ - valueChanged(); -} - -/** - * Called when a widget is changed by a slot using a int parameter. - * Parameters must match when using function pointers. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt(int) -{ - valueChanged(); -} - -/** - * Called when name toolbutton is clicked to change axis label. - */ -void -BrainBrowserWindowToolBarChartTwoAxes::axisLabelToolButtonClicked(bool) -{ - ChartTwoOverlaySet* chartOverlaySet = NULL; - std::vector validAxesLocations; - ChartTwoCartesianAxis* selectedAxis = NULL; - - getSelectionData(getTabContentFromSelectedTab(), - chartOverlaySet, - validAxesLocations, - selectedAxis); - - if ((chartOverlaySet != NULL) - && (selectedAxis != NULL)) { - WuQDataEntryDialog newNameDialog("Axis Label", - m_axisLabelToolButton); - QLineEdit* lineEdit = newNameDialog.addLineEditWidget("Label"); - lineEdit->setText(chartOverlaySet->getAxisLabel(selectedAxis)); - if (newNameDialog.exec() == WuQDataEntryDialog::Accepted) { - const AString name = lineEdit->text().trimmed(); - chartOverlaySet->setAxisLabel(selectedAxis, name); - valueChanged(); - } - } -} - - - - diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ -#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "BrainBrowserWindowToolBarComponent.h" -#include "ChartAxisLocationEnum.h" - -class QCheckBox; -class QComboBox; -class QDoubleSpinBox; -class QLabel; -class QSpinBox; -class QToolButton; - -namespace caret { - - class AnnotationPercentSizeText; - class ChartTwoCartesianAxis; - class ChartTwoCartesianAxisWidget; - class ChartTwoOverlaySet; - class EnumComboBoxTemplate; - class WuQDoubleSpinBox; - class WuQWidgetObjectGroup; - - class BrainBrowserWindowToolBarChartTwoAxes : public BrainBrowserWindowToolBarComponent { - Q_OBJECT - - public: - BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar, - const QString& parentObjectName); - - virtual ~BrainBrowserWindowToolBarChartTwoAxes(); - - virtual void updateContent(BrowserTabContent* browserTabContent); - - virtual void receiveEvent(Event* event); - - // ADD_NEW_METHODS_HERE - - private slots: - void axisChanged(); - - void valueChanged(); - - void valueChangedBool(bool); - - void valueChangedDouble(double); - - void valueChangedInt(int); - - void axisMinimumValueChanged(double); - - void axisMaximumValueChanged(double); - - void axisLabelToolButtonClicked(bool); - - void axisLineThicknessChanged(double); - - private: - BrainBrowserWindowToolBarChartTwoAxes(const BrainBrowserWindowToolBarChartTwoAxes&); - - BrainBrowserWindowToolBarChartTwoAxes& operator=(const BrainBrowserWindowToolBarChartTwoAxes&); - - ChartAxisLocationEnum::Enum getSelectedAxisLocation() const; - - void getSelectionData(BrowserTabContent* browserTabContent, - ChartTwoOverlaySet* &chartOverlaySetOut, - std::vector& validAxesLocationsOut, - ChartTwoCartesianAxis* &selectedAxisOut) const; - - void updateControls(BrowserTabContent* browserTabContent); - - void updateGraphics(); - - // ADD_NEW_MEMBERS_HERE - - QCheckBox* m_axisDisplayedByUserCheckBox; - - EnumComboBoxTemplate* m_axisComboBox; - - QToolButton* m_axisLabelToolButton; - - QComboBox* m_axisLabelFromOverlayComboBox; - - EnumComboBoxTemplate* m_autoUserRangeComboBox; - - WuQDoubleSpinBox* m_userMinimumValueSpinBox; - - WuQDoubleSpinBox* m_userMaximumValueSpinBox; - - QCheckBox* m_showTickMarksCheckBox; - - QCheckBox* m_showLabelCheckBox; - - QCheckBox* m_showNumericsCheckBox; - - QCheckBox* m_rotateNumericsCheckBox; - - EnumComboBoxTemplate* m_userNumericFormatComboBox; - - QSpinBox* m_userDigitsRightOfDecimalSpinBox; - - EnumComboBoxTemplate* m_numericSubdivisionsModeComboBox; - - QSpinBox* m_userSubdivisionsSpinBox; - - WuQDoubleSpinBox* m_labelSizeSpinBox; - - WuQDoubleSpinBox* m_numericsSizeSpinBox; - - WuQDoubleSpinBox* m_linesTicksSizeSpinBox; - - WuQDoubleSpinBox* m_paddingSizeSpinBox; - - ChartTwoOverlaySet* m_chartOverlaySet; - - ChartTwoCartesianAxis* m_chartAxis; - - WuQWidgetObjectGroup* m_widgetGroup; - - }; - -#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ - // -#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ - -} // namespace -#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,1000 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include +#include +#include +#include +#include + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTED_AXES_DECLARE__ +#include "BrainBrowserWindowToolBarChartTwoOrientedAxes.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTED_AXES_DECLARE__ + +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "ChartTwoAxisPropertiesEditorDialog.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoCartesianOrientedAxes.h" +#include "ChartTwoOverlaySet.h" +#include "ChartTwoTitle.h" +#include "ChartTwoTitleEditorWidget.h" +#include "EnumComboBoxTemplate.h" +#include "EventBrowserTabGet.h" +#include "EventBrowserWindowGraphicsRedrawn.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "ModelChartTwo.h" +#include "WuQDoubleSpinBox.h" +#include "WuQMacroManager.h" +#include "WuQTrueFalseComboBox.h" +#include "WuQWidgetObjectGroup.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarChartTwoOrientedAxes + * \brief Controls for chart horizontal and vertical axes. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parentToolBar + * The parent toolbar. + * @param parentObjectName + * Name of parent object for macros + */ +BrainBrowserWindowToolBarChartTwoOrientedAxes::BrainBrowserWindowToolBarChartTwoOrientedAxes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) +: BrainBrowserWindowToolBarComponent(parentToolBar), +m_objectNamePrefix(parentObjectName + + ":ChartAxes:") +{ + /* + * Horizontal range + */ + auto horizontalWidgets = createAxesWidgets(ChartTwoAxisOrientationTypeEnum::HORIZONTAL); + m_horizontalRangeModeComboBox = std::get<0>(horizontalWidgets); + m_horizontalUserMinimumValueSpinBox = std::get<1>(horizontalWidgets); + m_horizontalUserMaximumValueSpinBox = std::get<2>(horizontalWidgets); + m_horizontalTransformEnabledComboBox = std::get<3>(horizontalWidgets); + m_horizontalRangeResetToolButton = std::get<4>(horizontalWidgets); + + /* + * Vertical range + */ + auto verticalWidgets = createAxesWidgets(ChartTwoAxisOrientationTypeEnum::VERTICAL); + m_verticalRangeModeComboBox = std::get<0>(verticalWidgets); + m_verticalUserMinimumValueSpinBox = std::get<1>(verticalWidgets); + m_verticalUserMaximumValueSpinBox = std::get<2>(verticalWidgets); + m_verticalTransformEnabledComboBox = std::get<3>(verticalWidgets); + m_verticalRangeResetToolButton = std::get<4>(verticalWidgets); + + /* + * Left Axis display and edit + */ + std::tuple leftWidgets = createAxisEditing(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); + m_leftAxisCheckBox = std::get<0>(leftWidgets); + m_leftAxisEditToolButton = std::get<1>(leftWidgets); + + /* + * Right Axis display and edit + */ + std::tuple rightWidgets = createAxisEditing(ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT); + m_rightAxisCheckBox = std::get<0>(rightWidgets); + m_rightAxisEditToolButton = std::get<1>(rightWidgets); + + /* + * Bottom Axis display and edit + */ + std::tuple bottomWidgets = createAxisEditing(ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM); + m_bottomAxisCheckBox = std::get<0>(bottomWidgets); + m_bottomAxisEditToolButton = std::get<1>(bottomWidgets); + + /* + * Top Axis display and edit + */ + std::tuple topWidgets = createAxisEditing(ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP); + m_topAxisCheckBox = std::get<0>(topWidgets); + m_topAxisEditToolButton = std::get<1>(topWidgets); + + /* + * Title display and edit + */ + m_titleCheckBox = new QCheckBox("Title"); + QObject::connect(m_titleCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::titleCheckBoxClicked); + m_titleCheckBox->setToolTip("Display the chart title"); + m_titleEditToolButton = new QToolButton(); + m_titleEditToolButton->setText("Edit"); + m_titleEditToolButton->setToolTip("Edit the chart title"); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_titleEditToolButton); + QObject::connect(m_titleEditToolButton, &QToolButton::clicked, + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::titleEditToolButtonClicked); + + m_titleEditorWidget = new ChartTwoTitleEditorWidget(m_titleEditToolButton, + m_objectNamePrefix); + QWidgetAction* titleEditorWidgetAction = new QWidgetAction(m_titleEditToolButton); + titleEditorWidgetAction->setDefaultWidget(m_titleEditorWidget); + + m_titleEditMenu = new QMenu(m_titleEditToolButton); + m_titleEditMenu->addAction(titleEditorWidgetAction); + QObject::connect(m_titleEditMenu, &QMenu::aboutToShow, + this, [=]() { titleEditorMenuAboutToShow(); } ); + + /* + * Range widgets layout + */ + int32_t columnCounter(0); + const int32_t COLUMN_LABEL(columnCounter++); + const int32_t COLUMN_HORIZ_ONE(columnCounter++); + const int32_t COLUMN_HORIZ_TWO(columnCounter++); + const int32_t COLUMN_VERT_ONE(columnCounter++); + const int32_t COLUMN_VERT_TWO(columnCounter++); + const int32_t COLUMN_LINE(columnCounter++); + const int32_t COLUMN_CHECKBOX(columnCounter++); + const int32_t COLUMN_EDIT(columnCounter++); + QGridLayout* rangeLayout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(rangeLayout, 3, 0); + int row(0); + rangeLayout->addWidget(new QLabel("Horizontal"), row, COLUMN_HORIZ_ONE, 1, 2); + rangeLayout->addWidget(new QLabel("Vertical"), row, COLUMN_VERT_ONE, 1, 2); + rangeLayout->addWidget(m_leftAxisCheckBox, row, COLUMN_CHECKBOX); + rangeLayout->addWidget(m_leftAxisEditToolButton, row, COLUMN_EDIT); + row++; + + rangeLayout->addWidget(new QLabel("Range"), row, COLUMN_LABEL); + rangeLayout->addWidget(m_horizontalRangeModeComboBox->getWidget(), row, COLUMN_HORIZ_ONE); + rangeLayout->addWidget(m_horizontalRangeResetToolButton, row, COLUMN_HORIZ_TWO); + rangeLayout->addWidget(m_verticalRangeModeComboBox->getWidget(), row, COLUMN_VERT_ONE); + rangeLayout->addWidget(m_verticalRangeResetToolButton, row, COLUMN_VERT_TWO); + rangeLayout->addWidget(m_rightAxisCheckBox, row, COLUMN_CHECKBOX); + rangeLayout->addWidget(m_rightAxisEditToolButton, row, COLUMN_EDIT); + + row++; + rangeLayout->addWidget(new QLabel("Max"), row, COLUMN_LABEL); + rangeLayout->addWidget(m_horizontalUserMaximumValueSpinBox->getWidget(), row, COLUMN_HORIZ_ONE, 1, 2); + rangeLayout->addWidget(m_verticalUserMaximumValueSpinBox->getWidget(), row, COLUMN_VERT_ONE, 1, 2); + rangeLayout->addWidget(m_topAxisCheckBox, row, COLUMN_CHECKBOX); + rangeLayout->addWidget(m_topAxisEditToolButton, row, COLUMN_EDIT); + + row++; + rangeLayout->addWidget(new QLabel("Min"), row, COLUMN_LABEL); + rangeLayout->addWidget(m_horizontalUserMinimumValueSpinBox->getWidget(), row, COLUMN_HORIZ_ONE, 1, 2); + rangeLayout->addWidget(m_verticalUserMinimumValueSpinBox->getWidget(), row, COLUMN_VERT_ONE, 1, 2); + rangeLayout->addWidget(m_bottomAxisCheckBox, row, COLUMN_CHECKBOX); + rangeLayout->addWidget(m_bottomAxisEditToolButton, row, COLUMN_EDIT); + row++; + rangeLayout->addWidget(new QLabel("Xform"), row, COLUMN_LABEL); + rangeLayout->addWidget(m_horizontalTransformEnabledComboBox->getWidget(), row, COLUMN_HORIZ_ONE, 1, 2); + rangeLayout->addWidget(m_verticalTransformEnabledComboBox->getWidget(), row, COLUMN_VERT_ONE, 1, 2); + rangeLayout->addWidget(m_titleCheckBox, row, COLUMN_CHECKBOX); + rangeLayout->addWidget(m_titleEditToolButton, row, COLUMN_EDIT); + row++; + + rangeLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), + 0, COLUMN_LINE, + row, 1); + + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_TOOLBAR_CHART_ORIENTED_AXES_UPDATE); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarChartTwoOrientedAxes::~BrainBrowserWindowToolBarChartTwoOrientedAxes() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Receive an event. + * + * @param event + * The event. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN) { + CaretAssert(dynamic_cast(event)); + /* + * Disable due to using lots of CPU 8/14/2020 + * updateContent(getTabContentFromSelectedTab()); + */ + } + else if (event->getEventType() == EventTypeEnum::EVENT_TOOLBAR_CHART_ORIENTED_AXES_UPDATE) { + updateContent(getTabContentFromSelectedTab()); + } + else { + BrainBrowserWindowToolBarComponent::receiveEvent(event); + } +} + +/** + * @return Widgets for an axis + * @param orientation + * Orientation of the axis + */ +std::tuple +BrainBrowserWindowToolBarChartTwoOrientedAxes::createAxesWidgets(const ChartTwoAxisOrientationTypeEnum::Enum orientation) +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + + const QString macroWidgetName(m_objectNamePrefix + + ChartTwoAxisOrientationTypeEnum::toGuiName(orientation) + + ":"); + /* + * Range controls + */ + const AString rangeTooltip("Auto - Adjusts axis range to fit data with some\n" + " padding so that scale value are usually\n" + " whole numbers\n" + "Data - Axis range is limited to minimum and \n" + " maximum values of the data\n" + "User - Axis range is controlled by user\n" + "Yoke - Axes with same \"Yoke x\" are synchronized\n" + " to have the same Min and Max values"); + EnumComboBoxTemplate* rangeModeComboBox = new EnumComboBoxTemplate(this); + rangeModeComboBox->getComboBox()->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); + rangeModeComboBox->setup(); + switch (orientation) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + QObject::connect(rangeModeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalRangeModeChanged); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + QObject::connect(rangeModeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalRangeModeChanged); + break; + } + rangeModeComboBox->getWidget()->setToolTip(rangeTooltip); + rangeModeComboBox->getWidget()->setObjectName(macroWidgetName + + "RangeMode"); + macroManager->addMacroSupportToObject(rangeModeComboBox->getWidget(), + "Select chart axis range mode"); + + WuQDoubleSpinBox* userMinimumValueSpinBox = new WuQDoubleSpinBox(this); + userMinimumValueSpinBox->setDecimalsModeAuto(); + userMinimumValueSpinBox->setSingleStepPercentage(1.0); + switch (orientation) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + QObject::connect(userMinimumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalAxisMinimumValueChanged); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + QObject::connect(userMinimumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalAxisMinimumValueChanged); + break; + } + userMinimumValueSpinBox->setToolTip("Set user scaling axis minimum value"); + userMinimumValueSpinBox->getWidget()->setObjectName(macroWidgetName + + "ScaleMinimum"); + macroManager->addMacroSupportToObject(userMinimumValueSpinBox->getWidget(), + "Set chart axis minimum"); + + WuQDoubleSpinBox* userMaximumValueSpinBox = new WuQDoubleSpinBox(this); + userMaximumValueSpinBox->setDecimalsModeAuto(); + userMaximumValueSpinBox->setSingleStepPercentage(1.0); + switch (orientation) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + QObject::connect(userMaximumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalAxisMaximumValueChanged); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + QObject::connect(userMaximumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalAxisMaximumValueChanged); + break; + } + userMaximumValueSpinBox->setToolTip("Set user scaling axis maximum value"); + userMaximumValueSpinBox->getWidget()->setObjectName(macroWidgetName + + "ScaleMaximum"); + macroManager->addMacroSupportToObject(userMaximumValueSpinBox->getWidget(), + "Set chart axis maximum"); + + WuQTrueFalseComboBox* transformEnabledComboBox = new WuQTrueFalseComboBox("On", + "Off", + this); + transformEnabledComboBox->setToolTip("Enable mouse panning and zooming for this orientation"); + switch (orientation) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + QObject::connect(transformEnabledComboBox, &WuQTrueFalseComboBox::statusChanged, + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalTransformEnabledChecked); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + QObject::connect(transformEnabledComboBox, &WuQTrueFalseComboBox::statusChanged, + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalTransformEnabledChecked); + break; + } + + QToolButton* resetToolButton = new QToolButton(); + resetToolButton->setText("R"); + resetToolButton->setToolTip("" + "Reset the Min/Max Values:
" + " AUTO - No action is taken
" + " DATA - No action is taken
" + " USER - Resets to range of DATA in axis
" + " YOKE - Resets to range of DATA from all yoked axes" + ""); + WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); + switch (orientation) { + case ChartTwoAxisOrientationTypeEnum::HORIZONTAL: + QObject::connect(resetToolButton, &QToolButton::clicked, + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalRangeResetToolButton); + break; + case ChartTwoAxisOrientationTypeEnum::VERTICAL: + QObject::connect(resetToolButton, &QToolButton::clicked, + this, &BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalRangeResetToolButton); + break; + } + + /* + * Group widgets for blocking signals + */ + m_widgetGroup = new WuQWidgetObjectGroup(this); + m_widgetGroup->add(rangeModeComboBox->getWidget()); + m_widgetGroup->add(userMinimumValueSpinBox); + m_widgetGroup->add(userMaximumValueSpinBox); + m_widgetGroup->add(transformEnabledComboBox); + m_widgetGroup->add(resetToolButton); + + return std::make_tuple(rangeModeComboBox, + userMinimumValueSpinBox, + userMaximumValueSpinBox, + transformEnabledComboBox, + resetToolButton); +} + +/** + * Create the axis editing widget + * @param axis + * Axis for widget + * @return Tupe containing widgets. + */ +std::tuple +BrainBrowserWindowToolBarChartTwoOrientedAxes::createAxisEditing(const ChartAxisLocationEnum::Enum axis) +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString macroWidgetName(m_objectNamePrefix + + ChartAxisLocationEnum::toGuiName(axis) + + ":"); + + QCheckBox* checkBox = new QCheckBox(); + checkBox->setText(ChartAxisLocationEnum::toGuiName(axis)); + checkBox->setToolTip("Enable display of the " + + ChartAxisLocationEnum::toGuiName(axis) + + " axis"); + QObject::connect(checkBox, &QCheckBox::clicked, + this, [=](bool checkedStatus) { axisCheckBoxOnOffClicked(axis, + checkedStatus); }); + checkBox->setObjectName(macroWidgetName + + "OnOffCheckBox"); + macroManager->addMacroSupportToObject(checkBox, + checkBox->toolTip()); + + QToolButton* toolButton = new QToolButton(); + WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); + toolButton->setText("Edit"); + toolButton->setToolTip("Edit the attributes of the " + + ChartAxisLocationEnum::toGuiName(axis) + + " axis"); + QObject::connect(toolButton, &QToolButton::clicked, + this, [=]() { axisToolButtonEditClicked(axis, toolButton); }); + toolButton->setObjectName(macroWidgetName + + "EditToolButton"); + macroManager->addMacroSupportToObject(toolButton, + toolButton->toolTip()); + + return std::make_tuple(checkBox, + toolButton); +} + +/** + * Called when an axis on/off checkbox is clicked + * @param axis + * Axis of checkbox that was clicked + * @param checkedStatus + * Checked status of button + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::axisCheckBoxOnOffClicked(const ChartAxisLocationEnum::Enum axis, + const bool checkedStatus) +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + + m_widgetGroup->blockAllSignals(true); + + + if ((overlaySet != NULL) + && (horizontalAxis != NULL) + && (verticalAxis != NULL)) { + switch (axis) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + horizontalAxis->getLeftOrBottomAxis()->setDisplayedByUser(checkedStatus); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + verticalAxis->getLeftOrBottomAxis()->setDisplayedByUser(checkedStatus); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + verticalAxis->getRightOrTopAxis()->setDisplayedByUser(checkedStatus); + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + horizontalAxis->getRightOrTopAxis()->setDisplayedByUser(checkedStatus); + break; + } + } + + updateGraphics(); +} + +ChartTwoAxisPropertiesEditorDialog* +BrainBrowserWindowToolBarChartTwoOrientedAxes::createPropertiesEditorDialog(ChartAxisLocationEnum::Enum axis, + const AString& dialogName, + QWidget* parentToolButton) +{ + ChartTwoAxisPropertiesEditorDialog* dialog = new ChartTwoAxisPropertiesEditorDialog(axis, + m_objectNamePrefix + dialogName + ":", + parentToolButton); + dialog->move(parentToolButton->mapToGlobal(QPoint(parentToolButton->width() + 5, 0))); + return dialog; +} + +/** + * Called when an axis edit button is clicked + * @param axis + * Axis of button that was clicked + * @param parentToolButton + * Parent tool button of the menu + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::axisToolButtonEditClicked(const ChartAxisLocationEnum::Enum axis, + QToolButton* parentToolButton) +{ + ChartTwoOverlaySet* chartOverlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxes(NULL); + ChartTwoCartesianOrientedAxes* verticalAxes(NULL); + + getSelectionData(chartOverlaySet, + horizontalAxes, + verticalAxes); + + if (chartOverlaySet != NULL) { + ChartTwoAxisPropertiesEditorDialog* editorDialog = NULL; + ChartTwoCartesianAxis* cartesianAxis(NULL); + switch (axis) { + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: + { + if (m_bottomAxisEditorDialog == NULL) { + m_bottomAxisEditorDialog = createPropertiesEditorDialog(axis, + "bottomAxisEditorDialog:", + m_bottomAxisEditToolButton); + } + editorDialog = m_bottomAxisEditorDialog; + cartesianAxis = horizontalAxes->getLeftOrBottomAxis(); + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: + { + if (m_leftAxisEditorDialog == NULL) { + m_leftAxisEditorDialog = createPropertiesEditorDialog(axis, + "leftAxisEditorDialog:", + m_leftAxisEditToolButton); + } + editorDialog = m_leftAxisEditorDialog; + cartesianAxis = verticalAxes->getLeftOrBottomAxis(); + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: + { + if (m_rightAxisEditorDialog == NULL) { + m_rightAxisEditorDialog = createPropertiesEditorDialog(axis, + "rightAxisEditorDialog:", + m_rightAxisEditToolButton); + } + editorDialog = m_rightAxisEditorDialog; + cartesianAxis = verticalAxes->getRightOrTopAxis(); + } + break; + case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: + { + if (m_topAxisEditorDialog == NULL) { + m_topAxisEditorDialog = createPropertiesEditorDialog(axis, + "topAxisEditorDialog::", + m_topAxisEditToolButton); + } + editorDialog = m_topAxisEditorDialog; + cartesianAxis = horizontalAxes->getRightOrTopAxis(); + } + break; + } + + if ( ! editorDialog->isVisible()) { + editorDialog->updateControls(chartOverlaySet, + cartesianAxis); + editorDialog->move(parentToolButton->mapToGlobal(QPoint(parentToolButton->width(), 0))); + editorDialog->show(); + } + } +} + + +/** + * Update content of this tool bar component. + * + * @param browserTabContent + * Content of the browser tab. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::updateContent(BrowserTabContent* browserTabContent) +{ + m_browserTabIndex = -1; + + if (browserTabContent == NULL) { + setEnabled(false); + return; + } + + m_browserTabIndex = browserTabContent->getTabNumber(); + updateControls(); +} + +/** + * Get the selection data. + * + * @param chartOverlaySetOut + * The chart overlay set (may be NULL) + * @param validAxesLocationsOut + * The valid axes locations. + * @param selectedAxisOut + * Output with selected axis (may be NULL) + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::getSelectionData(ChartTwoOverlaySet* &chartOverlaySetOut, + ChartTwoCartesianOrientedAxes* &horizontalAxesOut, + ChartTwoCartesianOrientedAxes* &verticalAxesOut) const +{ + chartOverlaySetOut = NULL; + horizontalAxesOut = NULL; + verticalAxesOut = NULL; + + EventBrowserTabGet tabEvent(m_browserTabIndex); + EventManager::get()->sendEvent(tabEvent.getPointer()); + + BrowserTabContent* browserTabContent = tabEvent.getBrowserTab(); + if (browserTabContent == NULL) { + return; + } + + if (browserTabContent != NULL) { + ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); + const int32_t tabIndex = browserTabContent->getTabNumber(); + if (modelChartTwo != NULL) { + switch (modelChartTwo->getSelectedChartTwoDataType(tabIndex)) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + chartOverlaySetOut = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + chartOverlaySetOut = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + chartOverlaySetOut = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + chartOverlaySetOut = modelChartTwo->getChartTwoOverlaySet(tabIndex); + break; + } + } + + if (chartOverlaySetOut != NULL) { + horizontalAxesOut = chartOverlaySetOut->getHorizontalAxes(); + verticalAxesOut = chartOverlaySetOut->getVerticalAxes(); + } + } +} + +/** + * Update the controls in this editor + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::updateControls() +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + + m_widgetGroup->blockAllSignals(true); + + + if ((overlaySet != NULL) + && (horizontalAxis != NULL) + && (verticalAxis != NULL)) { + /* + * Update horizontal + */ + m_bottomAxisCheckBox->setChecked(horizontalAxis->getLeftOrBottomAxis()->isDisplayedByUser()); + m_topAxisCheckBox->setChecked(horizontalAxis->getRightOrTopAxis()->isDisplayedByUser()); + m_horizontalRangeModeComboBox->setSelectedItem(horizontalAxis->getScaleRangeMode()); + + float horizRangeMin(0.0), horizRangeMax(0.0); + horizontalAxis->getDataRange(horizRangeMin, + horizRangeMax); + float horizMinValue(0.0), horizMaxValue(0.0); + horizontalAxis->getUserScaleMinimumMaximumValues(horizMinValue, + horizMaxValue); + m_horizontalUserMinimumValueSpinBox->setRangeExceedable(horizRangeMin, + horizRangeMax); + m_horizontalUserMinimumValueSpinBox->setValue(horizMinValue); + m_horizontalUserMaximumValueSpinBox->setRangeExceedable(horizRangeMin, + horizRangeMax); + m_horizontalUserMaximumValueSpinBox->setValue(horizMaxValue); + m_horizontalTransformEnabledComboBox->setStatus(horizontalAxis->isTransformationEnabled()); + + /* + * Update vertical + */ + m_leftAxisCheckBox->setChecked(verticalAxis->getLeftOrBottomAxis()->isDisplayedByUser()); + m_rightAxisCheckBox->setChecked(verticalAxis->getRightOrTopAxis()->isDisplayedByUser()); + m_verticalRangeModeComboBox->setSelectedItem(verticalAxis->getScaleRangeMode()); + + float vertRangeMin(0.0), vertRangeMax(0.0); + verticalAxis->getDataRange(vertRangeMin, + vertRangeMax); + float vertMinValue(0.0), vertMaxValue(0.0); + verticalAxis->getUserScaleMinimumMaximumValues(vertMinValue, + vertMaxValue); + m_verticalUserMinimumValueSpinBox->setRangeExceedable(vertRangeMin, + vertRangeMax); + m_verticalUserMinimumValueSpinBox->setValue(vertMinValue); + m_verticalUserMaximumValueSpinBox->setRangeExceedable(vertRangeMin, + vertRangeMax); + m_verticalUserMaximumValueSpinBox->setValue(vertMaxValue); + m_verticalTransformEnabledComboBox->setStatus(verticalAxis->isTransformationEnabled()); + + /* + * Chart title + */ + ChartTwoTitle* chartTitle = overlaySet->getChartTitle(); + m_titleCheckBox->setChecked(chartTitle->isDisplayed()); + + setEnabled(true); + } + else { + setEnabled(false); + } + + + m_widgetGroup->blockAllSignals(false); +} + +/** + * Called when a widget is changed by the user. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::valueChanged() +{ + updateGraphics(); + + updateContent(getTabContentFromSelectedTab()); +} + +/** + * Update the graphics. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::updateGraphics() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Called when the horizontal range mode is changed + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalRangeModeChanged() +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + + if (horizontalAxis != NULL) { + horizontalAxis->setScaleRangeModeFromGUI(m_horizontalRangeModeComboBox->getSelectedItem()); + valueChanged(); + } +} + +/** + * Called when the vertical range mode is changed + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalRangeModeChanged() +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + + if (verticalAxis != NULL) { + verticalAxis->setScaleRangeModeFromGUI(m_verticalRangeModeComboBox->getSelectedItem()); + valueChanged(); + } +} + + +/** + * Called when the minimum value is changed. + * + * @param minimumValue + * New minimum value. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalAxisMinimumValueChanged(double minimumValue) +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + if (horizontalAxis != NULL) { + horizontalAxis->setUserScaleMinimumValueFromGUI(minimumValue); + valueChanged(); + } +} + +/** + * Called when the maximum value is changed. + * + * @param maximumValue + * New maximum value. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalAxisMaximumValueChanged(double maximumValue) +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + if (horizontalAxis != NULL) { + horizontalAxis->setUserScaleMaximumValueFromGUI(maximumValue); + valueChanged(); + } +} + +/** + * Called when horizontal transform enabled checkbox is changed + * @param checked + * New checked status + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalTransformEnabledChecked(bool checked) +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + if (horizontalAxis != NULL) { + horizontalAxis->setTransformationEnabled(checked); + } +} + +/** + * Called when reset horizontal range toolbutton is clicked + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::horizontalRangeResetToolButton() +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + if (horizontalAxis != NULL) { + horizontalAxis->resetUserScaleRange(); + valueChanged(); + } +} + +/** + * Called when the minimum value is changed. + * + * @param minimumValue + * New minimum value. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalAxisMinimumValueChanged(double minimumValue) +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + if (verticalAxis != NULL) { + verticalAxis->setUserScaleMinimumValueFromGUI(minimumValue); + valueChanged(); + } +} + +/** + * Called when the maximum value is changed. + * + * @param maximumValue + * New maximum value. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalAxisMaximumValueChanged(double maximumValue) +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + if (verticalAxis != NULL) { + verticalAxis->setUserScaleMaximumValueFromGUI(maximumValue); + valueChanged(); + } +} + +/** + * Called when horizontal transform enabled checkbox is changed + * @param checked + * New checked status + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalTransformEnabledChecked(bool checked) +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + if (verticalAxis != NULL) { + verticalAxis->setTransformationEnabled(checked); + } +} + +/** + * Called when reset vertical range toolbutton is clicked + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::verticalRangeResetToolButton() +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + if (verticalAxis != NULL) { + verticalAxis->resetUserScaleRange(); + valueChanged(); + } +} + +/** + * Called when a widget is changed by a slot using a bool parameter. + * Parameters must match when using function pointers. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::valueChangedBool(bool) +{ + valueChanged(); +} + +/** + * Called when a widget is changed by a slot using a double parameter. + * Parameters must match when using function pointers. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::valueChangedDouble(double) +{ + valueChanged(); +} + +/** + * Called when a widget is changed by a slot using a int parameter. + * Parameters must match when using function pointers. + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::valueChangedInt(int) +{ + valueChanged(); +} + +/** + * Called when title check box is clicked + * @param checked + * New checked status + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::titleCheckBoxClicked(bool checked) +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + + if (overlaySet != NULL) { + ChartTwoTitle* chartTitle = overlaySet->getChartTitle(); + chartTitle->setDisplayed(checked); + updateGraphics(); + } +} + +/** + * Called when title edit tool button is clicked + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::titleEditToolButtonClicked() +{ + m_titleEditMenu->exec(m_titleEditToolButton->mapToGlobal(QPoint(0, m_titleEditToolButton->height()))); +} + +/** + * Called when title editor menu is about to show + */ +void +BrainBrowserWindowToolBarChartTwoOrientedAxes::titleEditorMenuAboutToShow() +{ + ChartTwoOverlaySet* overlaySet(NULL); + ChartTwoCartesianOrientedAxes* horizontalAxis(NULL); + ChartTwoCartesianOrientedAxes* verticalAxis(NULL); + getSelectionData(overlaySet, + horizontalAxis, + verticalAxis); + + m_titleEditorWidget->updateControls(overlaySet); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,195 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTED_AXES_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTED_AXES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "BrainBrowserWindowToolBarComponent.h" +#include "ChartAxisLocationEnum.h" +#include "ChartTwoAxisOrientationTypeEnum.h" + +class QCheckBox; +class QDoubleSpinBox; +class QMenu; +class QToolButton; + +namespace caret { + + class ChartTwoAxisPropertiesEditorDialog; + class ChartTwoAxisPropertiesEditorWidget; + class ChartTwoCartesianOrientedAxes; + class ChartTwoOverlaySet; + class ChartTwoTitleEditorWidget; + class EnumComboBoxTemplate; + class WuQDoubleSpinBox; + class WuQTrueFalseComboBox; + class WuQWidgetObjectGroup; + + class BrainBrowserWindowToolBarChartTwoOrientedAxes : public BrainBrowserWindowToolBarComponent { + Q_OBJECT + + public: + BrainBrowserWindowToolBarChartTwoOrientedAxes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); + + virtual ~BrainBrowserWindowToolBarChartTwoOrientedAxes(); + + virtual void updateContent(BrowserTabContent* browserTabContent); + + virtual void receiveEvent(Event* event); + + // ADD_NEW_METHODS_HERE + + private slots: + void valueChanged(); + + void valueChangedBool(bool); + + void valueChangedDouble(double); + + void valueChangedInt(int); + + void horizontalRangeModeChanged(); + + void verticalRangeModeChanged(); + + void horizontalAxisMinimumValueChanged(double); + + void horizontalAxisMaximumValueChanged(double); + + void horizontalTransformEnabledChecked(bool); + + void horizontalRangeResetToolButton(); + + void verticalAxisMinimumValueChanged(double); + + void verticalAxisMaximumValueChanged(double); + + void verticalTransformEnabledChecked(bool); + + void verticalRangeResetToolButton(); + + void axisCheckBoxOnOffClicked(const ChartAxisLocationEnum::Enum axis, + const bool checkedStatus); + + void axisToolButtonEditClicked(const ChartAxisLocationEnum::Enum axis, + QToolButton* parentToolButton); + + void titleCheckBoxClicked(bool checked); + + void titleEditToolButtonClicked(); + + void titleEditorMenuAboutToShow(); + + private: + BrainBrowserWindowToolBarChartTwoOrientedAxes(const BrainBrowserWindowToolBarChartTwoOrientedAxes&); + + BrainBrowserWindowToolBarChartTwoOrientedAxes& operator=(const BrainBrowserWindowToolBarChartTwoOrientedAxes&); + + void getSelectionData(ChartTwoOverlaySet* &chartOverlaySetOut, + ChartTwoCartesianOrientedAxes* &horizontalAxesOut, + ChartTwoCartesianOrientedAxes* &verticalAxesOut) const; + + void updateControls(); + + void updateGraphics(); + + const AString m_objectNamePrefix; + + std::tuple createAxesWidgets(const ChartTwoAxisOrientationTypeEnum::Enum orientation); + + std::tuple createAxisEditing(const ChartAxisLocationEnum::Enum axis); + + ChartTwoAxisPropertiesEditorDialog* createPropertiesEditorDialog(ChartAxisLocationEnum::Enum axis, + const AString& dialogName, + QWidget* parentToolButton); + + // ADD_NEW_MEMBERS_HERE + + int32_t m_browserTabIndex = -1; + + EnumComboBoxTemplate* m_horizontalRangeModeComboBox; + + WuQDoubleSpinBox* m_horizontalUserMinimumValueSpinBox; + + WuQDoubleSpinBox* m_horizontalUserMaximumValueSpinBox; + + WuQTrueFalseComboBox* m_horizontalTransformEnabledComboBox; + + QToolButton* m_horizontalRangeResetToolButton; + + EnumComboBoxTemplate* m_verticalRangeModeComboBox; + + WuQDoubleSpinBox* m_verticalUserMinimumValueSpinBox; + + WuQDoubleSpinBox* m_verticalUserMaximumValueSpinBox; + + WuQTrueFalseComboBox* m_verticalTransformEnabledComboBox; + + QToolButton* m_verticalRangeResetToolButton; + + QCheckBox* m_leftAxisCheckBox; + + QToolButton* m_leftAxisEditToolButton; + + ChartTwoAxisPropertiesEditorDialog* m_leftAxisEditorDialog = NULL; + + QCheckBox* m_rightAxisCheckBox; + + QToolButton* m_rightAxisEditToolButton; + + ChartTwoAxisPropertiesEditorDialog* m_rightAxisEditorDialog = NULL; + + QCheckBox* m_bottomAxisCheckBox; + + QToolButton* m_bottomAxisEditToolButton; + + ChartTwoAxisPropertiesEditorDialog* m_bottomAxisEditorDialog = NULL;; + + QCheckBox* m_topAxisCheckBox; + + QToolButton* m_topAxisEditToolButton; + + ChartTwoAxisPropertiesEditorDialog* m_topAxisEditorDialog = NULL; + + QCheckBox* m_titleCheckBox; + + QToolButton* m_titleEditToolButton; + + ChartTwoTitleEditorWidget* m_titleEditorWidget; + + QMenu* m_titleEditMenu; + + WuQWidgetObjectGroup* m_widgetGroup; + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTED_AXES_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTED_AXES_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_ORIENTED_AXES_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,250 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2017 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ -#include "BrainBrowserWindowToolBarChartTwoTitle.h" -#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ - -#include -#include -#include -#include -#include -#include -#include - -#include "AnnotationPercentSizeText.h" -#include "BrainBrowserWindowToolBar.h" -#include "BrowserTabContent.h" -#include "CaretAssert.h" -#include "ChartTwoOverlaySet.h" -#include "ChartTwoTitle.h" -#include "EventChartTwoAttributesChanged.h" -#include "EventManager.h" -#include "ModelChartTwo.h" -#include "WuQDataEntryDialog.h" -#include "WuQDoubleSpinBox.h" -#include "WuQMacroManager.h" -#include "WuQtUtilities.h" - -using namespace caret; - - - -/** - * \class caret::BrainBrowserWindowToolBarChartTwoTitle - * \brief Toolbar component for chart orientation. - * \ingroup GuiQt - */ - -/** - * Constructor. - * - * @param parentToolBar - * The parent toolbar - * @param parentObjectName - * Name of parent object for macros - */ -BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar, - const QString& parentObjectName) -: BrainBrowserWindowToolBarComponent(parentToolBar) -{ - WuQMacroManager* macroManager = WuQMacroManager::instance(); - const QString objectNamePrefix(parentObjectName - + ":ChartTitle:"); - - m_showTitleCheckBox = new QCheckBox("Show Title"); - m_showTitleCheckBox->setToolTip("Show the title at the top of the chart"); - QObject::connect(m_showTitleCheckBox, &QCheckBox::clicked, - this, &BrainBrowserWindowToolBarChartTwoTitle::showTitleCheckBoxClicked); - m_showTitleCheckBox->setObjectName(objectNamePrefix - + "Show"); - macroManager->addMacroSupportToObject(m_showTitleCheckBox, - "Show chart title"); - - QToolButton* editTitleToolButton = new QToolButton; - QAction* editTitleAction = new QAction("Edit Title...", editTitleToolButton); - editTitleAction->setToolTip("Edit the chart title in a dialog"); - QObject::connect(editTitleAction, &QAction::triggered, - this, &BrainBrowserWindowToolBarChartTwoTitle::editTitleActionTriggered); - editTitleAction->setObjectName(objectNamePrefix - + "Edit"); - macroManager->addMacroSupportToObject(editTitleAction, - "Display dialog to edit chart title"); - WuQtUtilities::setToolButtonStyleForQt5Mac(editTitleToolButton); - editTitleToolButton->setDefaultAction(editTitleAction); - - m_titleSizeSpinBox = new WuQDoubleSpinBox(this); - m_titleSizeSpinBox->setRangePercentage(0.0, 100.0); - QObject::connect(m_titleSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged); - m_titleSizeSpinBox->setToolTip("Set height of title as percentage of tab height"); - m_titleSizeSpinBox->getWidget()->setObjectName(objectNamePrefix - + "Height"); - macroManager->addMacroSupportToObject(m_titleSizeSpinBox->getWidget(), - "Set chart title height"); - - m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); - m_paddingSizeSpinBox->setRangePercentage(0.0, 100.0); - QObject::connect(m_paddingSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), - this, &BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged); - m_paddingSizeSpinBox->getWidget()->setObjectName(objectNamePrefix - + "Padding"); - macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget(), - "Set chart padding"); - - m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height"); - QGridLayout* layout = new QGridLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 3, 0); //4, 5); - int row = 0; - layout->addWidget(m_showTitleCheckBox, row, 0, 1, 2, Qt::AlignHCenter); - row++; - layout->addWidget(new QLabel("Size"), row, 0); - layout->addWidget(m_titleSizeSpinBox->getWidget(), row, 1); - row++; - layout->addWidget(new QLabel("Pad"), row, 0); - layout->addWidget(m_paddingSizeSpinBox->getWidget(), row, 1); - row++; - layout->addWidget(editTitleToolButton, row, 0, 1, 2, Qt::AlignHCenter); - row++; - - setSizePolicy(QSizePolicy::Fixed, - QSizePolicy::Fixed); -} - -/** - * Destructor. - */ -BrainBrowserWindowToolBarChartTwoTitle::~BrainBrowserWindowToolBarChartTwoTitle() -{ -} - -/** - * Update content of this tool bar component. - * - * @param browserTabContent - * Content of the browser tab. - */ -void -BrainBrowserWindowToolBarChartTwoTitle::updateContent(BrowserTabContent* browserTabContent) -{ - m_chartOverlaySet = NULL; - - if (browserTabContent != NULL) { - ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); - if (modelChartTwo != NULL) { - const int32_t tabIndex = browserTabContent->getTabNumber(); - m_chartOverlaySet = modelChartTwo->getChartTwoOverlaySet(tabIndex); - } - } - - if (m_chartOverlaySet != NULL) { - setEnabled(true); - const ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); - m_showTitleCheckBox->setChecked(chartTitle->isDisplayed()); - - m_titleSizeSpinBox->blockSignals(true); - m_titleSizeSpinBox->setValue(chartTitle->getTextSize()); - m_titleSizeSpinBox->blockSignals(false); - - m_paddingSizeSpinBox->blockSignals(true); - m_paddingSizeSpinBox->setValue(chartTitle->getPaddingSize()); - m_paddingSizeSpinBox->blockSignals(false); - } - else { - setEnabled(false); - } -} - -/** - * Called when show title checkbox is clicked. - * - * @param checked - * Status of checkbox. - */ -void -BrainBrowserWindowToolBarChartTwoTitle::showTitleCheckBoxClicked(bool checked) -{ - if (m_chartOverlaySet != NULL) { - ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); - chartTitle->setDisplayed(checked); - this->performUpdating(); - } -} - -/** - * Called when a size spin box value is changed - */ -void -BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged(double) -{ - if (m_chartOverlaySet != NULL) { - ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); - chartTitle->setTextSize(m_titleSizeSpinBox->value()); - chartTitle->setPaddingSize(m_paddingSizeSpinBox->value()); - this->performUpdating(); - } -} - -/** - * Called when edit title action is triggered. - */ -void -BrainBrowserWindowToolBarChartTwoTitle::editTitleActionTriggered() -{ - if (m_chartOverlaySet != NULL) { - WuQDataEntryDialog newNameDialog("Chart Title", - this); - QLineEdit* lineEdit = newNameDialog.addLineEditWidget("Title"); - lineEdit->setText(m_chartOverlaySet->getChartTitle()->getText()); - if (newNameDialog.exec() == WuQDataEntryDialog::Accepted) { - const AString name = lineEdit->text().trimmed(); - m_chartOverlaySet->getChartTitle()->setText(name); - if ( ! name.isEmpty()) { - m_chartOverlaySet->getChartTitle()->setDisplayed(true); - m_showTitleCheckBox->setChecked(true); - } - this->performUpdating(); - } - } -} - -void -BrainBrowserWindowToolBarChartTwoTitle::performUpdating() -{ - const BrowserTabContent* tabContent = getTabContentFromSelectedTab(); - CaretAssert(tabContent); - - const YokingGroupEnum::Enum yokingGroup = tabContent->getChartModelYokingGroup(); - if (yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { - const ModelChartTwo* modelChartTwo = tabContent->getDisplayedChartTwoModel(); - CaretAssert(modelChartTwo); - const int32_t tabIndex = tabContent->getTabNumber(); - - EventChartTwoAttributesChanged attributesEvent; - attributesEvent.setTitleChanged(yokingGroup, - modelChartTwo->getSelectedChartTwoDataType(tabIndex), - m_chartOverlaySet->getChartTitle()); - EventManager::get()->sendEvent(attributesEvent.getPointer()); - } - - this->updateGraphicsWindowAndYokedWindows(); -} diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ -#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2017 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "BrainBrowserWindowToolBarComponent.h" - -class QCheckBox; - -namespace caret { - - class ChartTwoOverlaySet; - class WuQDoubleSpinBox; - - class BrainBrowserWindowToolBarChartTwoTitle : public BrainBrowserWindowToolBarComponent { - Q_OBJECT - - public: - BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar, - const QString& parentObjectName); - - virtual ~BrainBrowserWindowToolBarChartTwoTitle(); - - virtual void updateContent(BrowserTabContent* browserTabContent); - - // ADD_NEW_METHODS_HERE - - private slots: - void editTitleActionTriggered(); - - void showTitleCheckBoxClicked(bool); - - void sizeSpinBoxValueChanged(double); - - private: - BrainBrowserWindowToolBarChartTwoTitle(const BrainBrowserWindowToolBarChartTwoTitle&); - - BrainBrowserWindowToolBarChartTwoTitle& operator=(const BrainBrowserWindowToolBarChartTwoTitle&); - - void performUpdating(); - - QCheckBox* m_showTitleCheckBox; - - WuQDoubleSpinBox* m_titleSizeSpinBox; - - WuQDoubleSpinBox* m_paddingSizeSpinBox; - - - ChartTwoOverlaySet* m_chartOverlaySet; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ - // -#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ - -} // namespace -#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -73,8 +73,7 @@ QRadioButton* rb = new QRadioButton(ChartTwoDataTypeEnum::toGuiName(ct)); m_chartTypeButtonGroup->addButton(rb, m_chartTypeRadioButtons.size()); - rb->setToolTip("Set chart to " - + rb->text()); + rb->setToolTip(ChartTwoDataTypeEnum::toToolTipText(ct)); QString chartTypeName = rb->text(); chartTypeName = chartTypeName.replace(" ", ""); diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,222 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#include -#include -#include -#include - - -#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_DECLARE__ -#include "BrainBrowserWindowToolBarClipping.h" -#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_DECLARE__ - -#include "BrowserTabContent.h" -#include "CaretAssert.h" -#include "GuiManager.h" -#include "WuQMacroManager.h" -#include "WuQtUtilities.h" - -using namespace caret; - - - -/** - * \class caret::BrainBrowserWindowToolBarClipping - * \brief Clipping group for toolbar. - * \ingroup GuiQt - */ - -/** - * Constructor. - * - * @param browserWindowIndex - * Index of window - * @param parentToolBar - * The parent toolbar - * @param parentObjectNamePrefix - * Name of parent object - */ -BrainBrowserWindowToolBarClipping::BrainBrowserWindowToolBarClipping(const int32_t browserWindowIndex, - BrainBrowserWindowToolBar* parentToolBar, - const QString& parentObjectNamePrefix) -: BrainBrowserWindowToolBarComponent(parentToolBar), -m_browserWindowIndex(browserWindowIndex), -m_parentToolBar(parentToolBar) -{ - WuQMacroManager* macroManager = WuQMacroManager::instance(); - CaretAssert(macroManager); - - const QString objectNamePrefix(parentObjectNamePrefix - + ":Clipping"); - - m_xClippingEnabledCheckBox = new QCheckBox("X"); - QObject::connect(m_xClippingEnabledCheckBox, SIGNAL(clicked(bool)), - this, SLOT(clippingCheckBoxCheckStatusChanged())); - m_xClippingEnabledCheckBox->setToolTip("Enable X clipping plane"); - m_xClippingEnabledCheckBox->setObjectName(objectNamePrefix - + ":EnableX"); - macroManager->addMacroSupportToObject(m_xClippingEnabledCheckBox, - "Enable X clipping plane"); - - m_yClippingEnabledCheckBox = new QCheckBox("Y"); - QObject::connect(m_yClippingEnabledCheckBox, SIGNAL(clicked(bool)), - this, SLOT(clippingCheckBoxCheckStatusChanged())); - m_yClippingEnabledCheckBox->setToolTip("Enable Y clipping plane"); - m_yClippingEnabledCheckBox->setObjectName(objectNamePrefix - + ":EnableY"); - macroManager->addMacroSupportToObject(m_yClippingEnabledCheckBox, - "Enable Y clipping plane"); - - m_zClippingEnabledCheckBox = new QCheckBox("Z"); - QObject::connect(m_zClippingEnabledCheckBox, SIGNAL(clicked(bool)), - this, SLOT(clippingCheckBoxCheckStatusChanged())); - m_zClippingEnabledCheckBox->setToolTip("Enable Z clipping plane"); - m_zClippingEnabledCheckBox->setObjectName(objectNamePrefix - + ":EnableZ"); - macroManager->addMacroSupportToObject(m_zClippingEnabledCheckBox, - "Enable Z clipping"); - - m_surfaceClippingEnabledCheckBox = new QCheckBox("Surface"); - QObject::connect(m_surfaceClippingEnabledCheckBox, SIGNAL(clicked(bool)), - this, SLOT(clippingCheckBoxCheckStatusChanged())); - m_surfaceClippingEnabledCheckBox->setToolTip("Enable Clipping of Surface"); - m_surfaceClippingEnabledCheckBox->setObjectName(objectNamePrefix - + ":EnableSurface"); - macroManager->addMacroSupportToObject(m_surfaceClippingEnabledCheckBox, - "Enable surface clipping"); - - m_volumeClippingEnabledCheckBox = new QCheckBox("Volume"); - QObject::connect(m_volumeClippingEnabledCheckBox, SIGNAL(clicked(bool)), - this, SLOT(clippingCheckBoxCheckStatusChanged())); - m_volumeClippingEnabledCheckBox->setToolTip("Enable Clipping of Volume Slices"); - m_volumeClippingEnabledCheckBox->setObjectName(objectNamePrefix - + ":EnableVolume"); - macroManager->addMacroSupportToObject(m_volumeClippingEnabledCheckBox, - "Enable volume clipping"); - - m_featuresClippingEnabledCheckBox = new QCheckBox("Features"); - QObject::connect(m_featuresClippingEnabledCheckBox, SIGNAL(clicked(bool)), - this, SLOT(clippingCheckBoxCheckStatusChanged())); - m_featuresClippingEnabledCheckBox->setToolTip("Enable Clipping of Features"); - m_featuresClippingEnabledCheckBox->setObjectName(objectNamePrefix - + ":EnableFeatures"); - macroManager->addMacroSupportToObject(m_featuresClippingEnabledCheckBox, - "Enable features clipping"); - - QToolButton* setupToolButton = new QToolButton(); - setupToolButton->setText("Setup"); - QObject::connect(setupToolButton, SIGNAL(clicked()), - this, SLOT(setupClippingPushButtonClicked())); - WuQtUtilities::setToolButtonStyleForQt5Mac(setupToolButton); - setupToolButton->setToolTip("Display Clipping Planes Setup Dialog"); - setupToolButton->setObjectName(objectNamePrefix - + ":ShowSetupDialog"); - macroManager->addMacroSupportToObject(setupToolButton, - "Display clipping planes dialog"); - - QGridLayout* gridLayout = new QGridLayout(this); - gridLayout->setHorizontalSpacing(6); - gridLayout->setVerticalSpacing(4); - gridLayout->setContentsMargins(1, 1, 1, 1); - int32_t rowIndex = gridLayout->rowCount(); - gridLayout->addWidget(m_xClippingEnabledCheckBox, rowIndex, 0); - gridLayout->addWidget(m_yClippingEnabledCheckBox, rowIndex, 1); - gridLayout->addWidget(m_zClippingEnabledCheckBox, rowIndex, 2); - rowIndex++; - gridLayout->addWidget(m_featuresClippingEnabledCheckBox, rowIndex, 0, 1, 3); - rowIndex++; - gridLayout->addWidget(m_surfaceClippingEnabledCheckBox, rowIndex, 0, 1, 3); - rowIndex++; - gridLayout->addWidget(m_volumeClippingEnabledCheckBox, rowIndex, 0, 1, 3); - rowIndex++; - gridLayout->addWidget(setupToolButton, rowIndex, 0, 1, 3, Qt::AlignHCenter); -} - -/** - * Destructor. - */ -BrainBrowserWindowToolBarClipping::~BrainBrowserWindowToolBarClipping() -{ -} - -/** - * Update the surface montage options widget. - * - * @param browserTabContent - * The active model display controller (may be NULL). - */ -void -BrainBrowserWindowToolBarClipping::updateContent(BrowserTabContent* browserTabContent) -{ - bool xEnabled; - bool yEnabled; - bool zEnabled; - bool surfaceEnabled; - bool volumeEnabled; - bool featuresEnabled; - browserTabContent->getClippingPlaneEnabled(xEnabled, - yEnabled, - zEnabled, - surfaceEnabled, - volumeEnabled, - featuresEnabled); - - m_xClippingEnabledCheckBox->setChecked(xEnabled); - m_yClippingEnabledCheckBox->setChecked(yEnabled); - m_zClippingEnabledCheckBox->setChecked(zEnabled); - - m_surfaceClippingEnabledCheckBox->setChecked(surfaceEnabled); - m_volumeClippingEnabledCheckBox->setChecked(volumeEnabled); - m_featuresClippingEnabledCheckBox->setChecked(featuresEnabled); -} - -/** - * Called when a clipping checkbox status is changed. - */ -void -BrainBrowserWindowToolBarClipping::clippingCheckBoxCheckStatusChanged() -{ - BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); - if (browserTabContent != NULL) { - browserTabContent->setClippingPlaneEnabled(m_xClippingEnabledCheckBox->isChecked(), - m_yClippingEnabledCheckBox->isChecked(), - m_zClippingEnabledCheckBox->isChecked(), - m_surfaceClippingEnabledCheckBox->isChecked(), - m_volumeClippingEnabledCheckBox->isChecked(), - m_featuresClippingEnabledCheckBox->isChecked()); - - this->updateGraphicsWindowAndYokedWindows(); - } -} - -/** - * Called when the setup clipping push button is clicked. - */ -void -BrainBrowserWindowToolBarClipping::setupClippingPushButtonClicked() -{ - BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); - GuiManager::get()->processShowClippingPlanesDialog(browserWindow); -} - - diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarClipping.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarClipping.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarClipping.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarClipping.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_H__ -#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#include - -#include "BrainBrowserWindowToolBarComponent.h" - - - -namespace caret { - - class BrainBrowserWindowToolBarClipping : public BrainBrowserWindowToolBarComponent { - Q_OBJECT - - public: - BrainBrowserWindowToolBarClipping(const int32_t browserWindowIndex, - BrainBrowserWindowToolBar* parentToolBar, - const QString& objectNamePrefix); - - virtual ~BrainBrowserWindowToolBarClipping(); - - virtual void updateContent(BrowserTabContent* browserTabContent); - - // ADD_NEW_METHODS_HERE - - private slots: - void clippingCheckBoxCheckStatusChanged(); - - void setupClippingPushButtonClicked(); - - private: - BrainBrowserWindowToolBarClipping(const BrainBrowserWindowToolBarClipping&); - - BrainBrowserWindowToolBarClipping& operator=(const BrainBrowserWindowToolBarClipping&); - - const int32_t m_browserWindowIndex; - - BrainBrowserWindowToolBar* m_parentToolBar; - - QCheckBox* m_xClippingEnabledCheckBox; - - QCheckBox* m_yClippingEnabledCheckBox; - - QCheckBox* m_zClippingEnabledCheckBox; - - QCheckBox* m_surfaceClippingEnabledCheckBox; - - QCheckBox* m_volumeClippingEnabledCheckBox; - - QCheckBox* m_featuresClippingEnabledCheckBox; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_DECLARE__ - // -#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_DECLARE__ - -} // namespace -#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -155,4 +155,12 @@ m_parentToolBar->updateUserInterface(); } +/** + * @return Parent window toolbar + */ +BrainBrowserWindowToolBar* +BrainBrowserWindowToolBarComponent::getParentToolBar() +{ + return m_parentToolBar; +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarComponent.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarComponent.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarComponent.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarComponent.h 2021-02-16 19:46:47.000000000 +0000 @@ -65,6 +65,8 @@ void blockAllSignals(const bool status); + BrainBrowserWindowToolBar* getParentToolBar(); + public: // ADD_NEW_METHODS_HERE diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBar.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBar.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBar.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBar.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -48,24 +49,29 @@ #include #include +#include "AnnotationBrowserTab.h" +#include "AnnotationCoordinate.h" #include "AnnotationManager.h" +#include "AnnotationStackingOrderOperation.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainBrowserWindowToolBar.h" +#include "BrainBrowserWindowToolBarAllSurface.h" #include "BrainBrowserWindowToolBarChartAttributes.h" #include "BrainBrowserWindowToolBarChartAxes.h" #include "BrainBrowserWindowToolBarChartTwoAttributes.h" -#include "BrainBrowserWindowToolBarChartTwoAxes.h" #include "BrainBrowserWindowToolBarChartTwoOrientation.h" -#include "BrainBrowserWindowToolBarChartTwoTitle.h" +#include "BrainBrowserWindowToolBarChartTwoOrientedAxes.h" #include "BrainBrowserWindowToolBarChartTwoType.h" #include "BrainBrowserWindowToolBarChartType.h" -#include "BrainBrowserWindowToolBarClipping.h" +#include "BrainBrowserWindowToolBarOrientation.h" #include "BrainBrowserWindowToolBarSlicePlane.h" #include "BrainBrowserWindowToolBarSliceSelection.h" +#include "BrainBrowserWindowToolBarSurface.h" #include "BrainBrowserWindowToolBarSurfaceMontage.h" #include "BrainBrowserWindowToolBarTab.h" #include "BrainBrowserWindowToolBarTabPopUpMenu.h" +#include "BrainBrowserWindowToolBarView.h" #include "BrainBrowserWindowToolBarVolumeMontage.h" #include "BrainOpenGLWidget.h" #include "BrainStructure.h" @@ -78,16 +84,23 @@ #include "CursorDisplayScoped.h" #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesBorders.h" -#include "EventBrowserTabNewClone.h" +#include "EventBrowserTabClose.h" +#include "EventBrowserTabCloseInToolBar.h" #include "EventBrowserTabDelete.h" +#include "EventBrowserTabDeleteInToolBar.h" #include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" #include "EventBrowserTabGetAllViewed.h" #include "EventBrowserTabNew.h" +#include "EventBrowserTabNewClone.h" +#include "EventBrowserTabNewInGUI.h" +#include "EventBrowserTabReopenClosed.h" +#include "EventBrowserTabSelectInWindow.h" #include "EventBrowserWindowDrawingContent.h" #include "EventBrowserWindowCreateTabs.h" #include "EventBrowserWindowNew.h" #include "EventBrowserWindowTileTabOperation.h" +#include "EventUserInputModeGet.h" #include "EventGetOrSetUserInputModeProcessor.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventGraphicsUpdateAllWindows.h" @@ -99,10 +112,10 @@ #include "EventUpdateYokedWindows.h" #include "GuiManager.h" #include "LockAspectWarningDialog.h" -#include "MacDuplicateMenuBar.h" #include "Model.h" #include "ModelChart.h" #include "ModelChartTwo.h" +#include "ModelMedia.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelSurfaceSelector.h" @@ -120,8 +133,20 @@ #include "SurfaceSelectionModel.h" #include "SurfaceSelectionViewController.h" #include "StructureSurfaceSelectionControl.h" -#include "TileTabsConfiguration.h" +#include "TileTabsLayoutGridConfiguration.h" #include "UserInputModeAbstract.h" +#include "UserInputModeAnnotations.h" +#include "UserInputModeAnnotationsWidget.h" +#include "UserInputModeBorders.h" +#include "UserInputModeBordersWidget.h" +#include "UserInputModeFoci.h" +#include "UserInputModeFociWidget.h" +#include "UserInputModeImage.h" +#include "UserInputModeImageWidget.h" +#include "UserInputModeTileTabsManualLayout.h" +#include "UserInputModeView.h" +#include "UserInputModeVolumeEdit.h" +#include "UserInputModeVolumeEditWidget.h" #include "VolumeFile.h" #include "VolumeSliceViewPlaneEnum.h" #include "VolumeSurfaceOutlineSetModel.h" @@ -162,7 +187,8 @@ QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, const QString& objectNamePrefix, BrainBrowserWindow* parentBrainBrowserWindow) -: QToolBar(parentBrainBrowserWindow) +: QToolBar(parentBrainBrowserWindow), +m_parentBrainBrowserWindow(parentBrainBrowserWindow) { this->browserWindowIndex = browserWindowIndex; m_tabIndexForTileTabsHighlighting = -1; @@ -170,17 +196,6 @@ this->isContructorFinished = false; this->isDestructionInProgress = false; - this->viewOrientationLeftIcon = NULL; - this->viewOrientationRightIcon = NULL; - this->viewOrientationAnteriorIcon = NULL; - this->viewOrientationPosteriorIcon = NULL; - this->viewOrientationDorsalIcon = NULL; - this->viewOrientationVentralIcon = NULL; - this->viewOrientationLeftLateralIcon = NULL; - this->viewOrientationLeftMedialIcon = NULL; - this->viewOrientationRightLateralIcon = NULL; - this->viewOrientationRightMedialIcon = NULL; - /* * Needed for saving and restoring window state in main window */ @@ -428,26 +443,62 @@ this->chartAttributesWidget = createChartAttributesWidget(); this->chartTwoOrientationWidget = createChartTwoOrientationWidget(); this->chartTwoAttributesWidget = createChartTwoAttributesWidget(); - this->chartTwoAxesWidget = createChartTwoAxesWidget(); - this->chartTwoTitleWidget = createChartTwoTitleWidget(); + this->chartTwoOrientedAxesWidget = createChartTwoOrientedAxisWidget(); this->chartTypeWidget = createChartTypeWidget(); this->chartTypeTwoWidget = createChartTypeTwoWidget(); this->wholeBrainSurfaceOptionsWidget = this->createWholeBrainSurfaceOptionsWidget(); this->volumeIndicesWidget = this->createVolumeIndicesWidget(); this->modeWidget = this->createModeWidget(); - this->windowWidget = this->createTabOptionsWidget(toolBarLockWindowAndAllTabAspectRatioButton); + this->tabMiscWidget = this->createTabOptionsWidget(toolBarLockWindowAndAllTabAspectRatioButton); this->singleSurfaceSelectionWidget = this->createSingleSurfaceOptionsWidget(); this->surfaceMontageSelectionWidget = this->createSurfaceMontageOptionsWidget(); - m_clippingOptionsWidget = createClippingOptionsWidget(); this->volumeMontageWidget = this->createVolumeMontageWidget(); this->volumePlaneWidget = this->createVolumePlaneWidget(); + this->userInputAnnotationsModeProcessor = new UserInputModeAnnotations(browserWindowIndex); + this->userInputBordersModeProcessor = new UserInputModeBorders(browserWindowIndex); + this->userInputFociModeProcessor = new UserInputModeFoci(browserWindowIndex); + this->userInputImageModeProcessor = new UserInputModeImage(browserWindowIndex); + this->userInputVolumeEditModeProcessor = new UserInputModeVolumeEdit(browserWindowIndex); + this->userInputTileTabsManualLayoutProcessor = new UserInputModeTileTabsManualLayout(browserWindowIndex); + this->userInputViewModeProcessor = new UserInputModeView(browserWindowIndex); + this->selectedUserInputProcessor = this->userInputViewModeProcessor; + this->selectedUserInputProcessor->initialize(); + + this->annotateModeWidget = createToolWidget("", + this->userInputAnnotationsModeProcessor->getWidgetForToolBar(), + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 0); + this->bordersModeWidget = this->userInputBordersModeProcessor->getWidgetForToolBar(); + this->fociModeWidget = createToolWidget("Foci Operations", + this->userInputFociModeProcessor->getWidgetForToolBar(), + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 0); + this->imageModeWidget = createToolWidget("Image Operations", + this->userInputImageModeProcessor->getWidgetForToolBar(), + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 0); + this->tileModeWidget = createToolWidget("", + this->userInputTileTabsManualLayoutProcessor->getWidgetForToolBar(), + WIDGET_PLACEMENT_LEFT, + WIDGET_PLACEMENT_TOP, + 0); + + this->volumeModeWidget = this->userInputVolumeEditModeProcessor->getWidgetForToolBar(); + + /* * Layout the toolbar's widgets. */ m_toolbarWidget = new QWidget(); this->toolbarWidgetLayout = new QHBoxLayout(m_toolbarWidget); - WuQtUtilities::setLayoutSpacingAndMargins(this->toolbarWidgetLayout, 2, 1); + this->toolbarWidgetLayout->setContentsMargins(0, 0, 0, 0); + this->toolbarWidgetLayout->setSpacing(0); + + this->toolbarWidgetLayout->addWidget(this->modeWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->viewWidget, 0, Qt::AlignLeft); @@ -475,34 +526,27 @@ this->toolbarWidgetLayout->addWidget(this->chartTwoAttributesWidget, 0, Qt::AlignLeft); - this->toolbarWidgetLayout->addWidget(this->chartTwoAxesWidget, 0, Qt::AlignLeft); - - this->toolbarWidgetLayout->addWidget(this->chartTwoTitleWidget, 0, Qt::AlignLeft); + this->toolbarWidgetLayout->addWidget(this->chartTwoOrientedAxesWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->chartAttributesWidget, 0, Qt::AlignLeft); - this->toolbarWidgetLayout->addWidget(this->modeWidget, 0, Qt::AlignLeft); + this->toolbarWidgetLayout->addWidget(this->annotateModeWidget, 0, Qt::AlignLeft); + + this->toolbarWidgetLayout->addWidget(this->bordersModeWidget, 0, Qt::AlignLeft); - this->toolbarWidgetLayout->addWidget(m_clippingOptionsWidget, 0, Qt::AlignLeft); + this->toolbarWidgetLayout->addWidget(this->fociModeWidget, 0, Qt::AlignLeft); + + this->toolbarWidgetLayout->addWidget(this->imageModeWidget, 0, Qt::AlignLeft); + + this->toolbarWidgetLayout->addWidget(this->tileModeWidget, 0, Qt::AlignLeft); + + this->toolbarWidgetLayout->addWidget(this->volumeModeWidget, 0, Qt::AlignLeft); + + this->toolbarWidgetLayout->addWidget(this->tabMiscWidget, 0, Qt::AlignLeft); - this->toolbarWidgetLayout->addWidget(this->windowWidget, 0, Qt::AlignLeft); - this->toolbarWidgetLayout->addStretch(); /* - * Widget below toolbar for user input mode mouse controls - */ - this->userInputControlsWidgetLayout = new QHBoxLayout(); - this->userInputControlsWidgetLayout->addSpacing(5); - WuQtUtilities::setLayoutSpacingAndMargins(this->userInputControlsWidgetLayout, 0, 0); - this->userInputControlsWidget = new QWidget(); - QVBoxLayout* userInputLayout = new QVBoxLayout(this->userInputControlsWidget); - WuQtUtilities::setLayoutSpacingAndMargins(userInputLayout, 2, 0); - userInputLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); - userInputLayout->addLayout(this->userInputControlsWidgetLayout); - userInputControlsWidgetActiveInputWidget = NULL; - - /* * Arrange the tabbar and the toolbar vertically. */ QWidget* w = new QWidget(); @@ -510,7 +554,6 @@ WuQtUtilities::setLayoutSpacingAndMargins(m_toolBarMainLayout, 1, 0); m_toolBarMainLayout->addWidget(this->tabBarWidget); m_toolBarMainLayout->addWidget(m_toolbarWidget); - m_toolBarMainLayout->addWidget(this->userInputControlsWidget); this->addWidget(w); @@ -529,15 +572,29 @@ } } + /* + * The height of the toolbar is set here. Prior to calling + * updateToolBar(), all widgets still have a 'visible' status + * and thus the sizeHint().height() is the maximum height. + */ + const int32_t toolBarHeight = m_toolbarWidget->sizeHint().height(); + m_toolbarWidget->setFixedHeight(toolBarHeight); + this->updateToolBar(); this->isContructorFinished = true; m_tileTabsHighlightingTimerEnabledFlag = true; + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_CLOSE_IN_TOOL_BAR); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE_IN_TOOL_BAR); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW_IN_GUI); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL_VIEWED); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_SELECT_IN_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_CREATE_TABS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_USER_INPUT_MODE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); QObject::connect(this->tabBar, &WuQTabBar::mousePressedSignal, @@ -553,91 +610,26 @@ { this->isDestructionInProgress = true; - if (this->viewOrientationLeftIcon != NULL) { - delete this->viewOrientationLeftIcon; - this->viewOrientationLeftIcon = NULL; - } - if (this->viewOrientationRightIcon != NULL) { - delete this->viewOrientationRightIcon; - this->viewOrientationRightIcon = NULL; - } - - if (this->viewOrientationAnteriorIcon != NULL) { - delete this->viewOrientationAnteriorIcon; - this->viewOrientationAnteriorIcon = NULL; - } - - if (this->viewOrientationPosteriorIcon != NULL) { - delete this->viewOrientationPosteriorIcon; - this->viewOrientationPosteriorIcon = NULL; - } - - if (this->viewOrientationDorsalIcon != NULL) { - delete this->viewOrientationDorsalIcon; - this->viewOrientationDorsalIcon = NULL; - } - - if (this->viewOrientationVentralIcon != NULL) { - delete this->viewOrientationVentralIcon; - this->viewOrientationVentralIcon = NULL; - } - - if (this->viewOrientationLeftLateralIcon != NULL) { - delete this->viewOrientationLeftLateralIcon; - this->viewOrientationLeftLateralIcon = NULL; - } - - if (this->viewOrientationLeftMedialIcon != NULL) { - delete this->viewOrientationLeftMedialIcon; - this->viewOrientationLeftMedialIcon = NULL; - } - - if (this->viewOrientationRightLateralIcon != NULL) { - delete this->viewOrientationRightLateralIcon; - this->viewOrientationRightLateralIcon = NULL; - } - - if (this->viewOrientationRightMedialIcon != NULL) { - delete this->viewOrientationRightMedialIcon; - this->viewOrientationRightMedialIcon = NULL; - } EventManager::get()->removeAllEventsFromListener(this); - this->viewWidgetGroup->clear(); - this->orientationWidgetGroup->clear(); - this->wholeBrainSurfaceOptionsWidgetGroup->clear(); this->modeWidgetGroup->clear(); - this->singleSurfaceSelectionWidgetGroup->clear(); for (int i = (this->tabBar->count() - 1); i >= 0; i--) { - this->tabClosed(i); + this->tabClosed(i, + RemoveTabMode::DELETE_TAB_CONTENT); } - this->isDestructionInProgress = false; -} - -/** - * Insert a duplicate of the application's menu at the top of the toolbar. - * This is only enabled on MacOSX. - * - * @param mainWindow - * Main window whose menus are copied. - */ -void -BrainBrowserWindowToolBar::insertDuplicateMenuBar(QMainWindow* mainWindow) -{ -#ifndef CARET_OS_MACOSX - return; -#endif - MacDuplicateMenuBar* menuBar = new MacDuplicateMenuBar(mainWindow, - this); - CaretAssert(menuBar); + delete this->userInputViewModeProcessor; + delete this->userInputAnnotationsModeProcessor; + delete this->userInputBordersModeProcessor; + delete this->userInputFociModeProcessor; + delete this->userInputImageModeProcessor; + delete this->userInputTileTabsManualLayoutProcessor; + delete this->userInputVolumeEditModeProcessor; + this->selectedUserInputProcessor = NULL; /* DO NOT DELETE since it does not own the object to which it points */ - /* - * Insert at top of tool bar - */ - m_toolBarMainLayout->insertWidget(0, menuBar); + this->isDestructionInProgress = false; } /** @@ -751,6 +743,42 @@ } /** + * Reopen the last closed tab in this window + * @param reopenTabEvent + * The reopen event + */ +void +BrainBrowserWindowToolBar::reopenLastClosedTab(EventBrowserTabReopenClosed& reopenTabEvent) +{ + if (reopenTabEvent.isError()) { + return; + } + else { + BrowserTabContent* tabContent = reopenTabEvent.getBrowserTabContent(); + CaretAssert(tabContent); + const int32_t tabBarPosition = tabContent->getClosedTabWindowTabBarPositionIndex(); + const int32_t windowIndex = tabContent->getClosedTabWindowIndex(); + if (windowIndex == this->browserWindowIndex) { + insertTabContentPrivate(InsertTabMode::AT_TAB_BAR_INDEX, + tabContent, + tabBarPosition); + } + else { + insertTabContentPrivate(InsertTabMode::APPEND, + tabContent, + -1); + } + } + + this->updateToolBar(); + + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); +} + + +/** * Replace the current browser tabs with the given browser tabs. Tabs will be added * or removed as needed. * @@ -777,7 +805,7 @@ const int32_t beforeNumTabs = this->tabBar->count(); for (int32_t iTab = 0; iTab < beforeNumTabs; iTab++) { this->tabBar->setTabData(iTab, - qVariantFromValue((void*)NULL)); + QVariant::fromValue((void*)NULL)); } /* @@ -806,7 +834,7 @@ BrowserTabContent* btc = browserTabs[iTab]; CaretAssert(btc); this->tabBar->setTabData(iTab, - qVariantFromValue((void*)btc)); + QVariant::fromValue((void*)btc)); this->updateTabName(iTab); } @@ -825,11 +853,14 @@ /** * Adds a new tab. + * + * @return + * Pointer to new tab content or NULL if tab could not be inserted */ -void +BrowserTabContent* BrainBrowserWindowToolBar::addNewTab() { - insertNewTabAtTabBarIndex(-1); + return insertNewTabAtTabBarIndex(-1); } /** @@ -837,12 +868,14 @@ * * @param tabBarIndex * Index in the tab bar. If invalid, tab is appended to the end of the toolbar. + * @return + * Pointer to new tab content or NULL if tab could not be inserted */ -void +BrowserTabContent* BrainBrowserWindowToolBar::insertNewTabAtTabBarIndex(const int32_t tabBarIndex) { if ( ! allowAddingNewTab()) { - return; + return NULL; } /* @@ -858,7 +891,7 @@ QMessageBox::critical(this, "", errorMessage); - return; + return NULL; } if (tabBarIndex >= 0){ @@ -877,6 +910,8 @@ EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); + + return tabContent; } @@ -912,16 +947,18 @@ } break; } - - this->tabBar->setTabData(newTabIndex, - qVariantFromValue((void*)browserTabContent)); - - const int32_t numOpenTabs = this->tabBar->count(); - this->tabBar->setTabsClosable(numOpenTabs > 1); - - this->updateTabName(newTabIndex); - - this->tabBar->setCurrentIndex(newTabIndex); + + if (newTabIndex >= 0) { + this->tabBar->setTabData(newTabIndex, + QVariant::fromValue((void*)browserTabContent)); + + const int32_t numOpenTabs = this->tabBar->count(); + this->tabBar->setTabsClosable(numOpenTabs > 1); + + this->updateTabName(newTabIndex); + + this->tabBar->setCurrentIndex(newTabIndex); + } this->tabBar->blockSignals(false); } @@ -943,9 +980,8 @@ return true; } - BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); - CaretAssert(browserWindow); - BrowserWindowContent* browserWindowContent = browserWindow->getBrowerWindowContent(); + CaretAssert(m_parentBrainBrowserWindow); + BrowserWindowContent* browserWindowContent = m_parentBrainBrowserWindow->getBrowerWindowContent(); CaretAssert(browserWindowContent); /* @@ -956,20 +992,23 @@ } /* - * Automatic configuration always shows all tabs + * Automatic configuration and manual configuration always show all tabs */ switch (browserWindowContent->getTileTabsConfigurationMode()) { - case TileTabsGridModeEnum::AUTOMATIC: + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: return true; break; - case TileTabsGridModeEnum::CUSTOM: + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + return true; break; } /* * Is there space in the current configuration? */ - const TileTabsConfiguration* tileTabs = browserWindowContent->getCustomTileTabsConfiguration(); + const TileTabsLayoutGridConfiguration* tileTabs = browserWindowContent->getCustomGridTileTabsConfiguration(); CaretAssert(tileTabs); const int32_t tileTabsCount = tileTabs->getNumberOfColumns() * tileTabs->getNumberOfRows(); if (getNumberOfTabs() < tileTabsCount) { @@ -1007,9 +1046,8 @@ void BrainBrowserWindowToolBar::showMacroDialog() { - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); - CaretAssert(bbw); - WuQMacroManager::instance()->showMacrosDialog(bbw); + CaretAssert(m_parentBrainBrowserWindow); + WuQMacroManager::instance()->showMacrosDialog(m_parentBrainBrowserWindow); } /** @@ -1064,6 +1102,7 @@ ModelChart* chartModel = NULL; ModelChartTwo* chartTwoModel = NULL; + ModelMedia* mediaModel(NULL); ModelSurfaceMontage* surfaceMontageModel = NULL; ModelVolume* volumeModel = NULL; ModelWholeBrain* wholeBrainModel = NULL; @@ -1134,14 +1173,17 @@ else if (dynamic_cast(*iter) != NULL) { wholeBrainModel = dynamic_cast(*iter); } - else if (dynamic_cast(*iter)) { + else if (dynamic_cast(*iter) != NULL) { if (includeOldChartFlag) { chartModel = dynamic_cast(*iter); } } - else if (dynamic_cast(*iter)) { + else if (dynamic_cast(*iter) != NULL) { chartTwoModel = dynamic_cast(*iter); } + else if (dynamic_cast(*iter) != NULL) { + mediaModel = dynamic_cast(*iter); + } else { CaretAssertMessage(0, AString("Unknow controller type: ") + (*iter)->getNameForGUI(true)); } @@ -1191,6 +1233,9 @@ if (cerebellumSurfaceModel != NULL) { numberOfTabsNeeded++; } + if (mediaModel != NULL) { + numberOfTabsNeeded++; + } const int32_t numberOfTabsToAdd = numberOfTabsNeeded - this->tabBar->count(); for (int32_t i = 0; i < numberOfTabsToAdd; i++) { @@ -1220,6 +1265,8 @@ rightSurfaceModel); tabIndex = loadIntoTab(tabIndex, cerebellumSurfaceModel); + tabIndex = loadIntoTab(tabIndex, + mediaModel); const int numTabs = this->tabBar->count(); if (numTabs > 0) { @@ -1247,6 +1294,8 @@ switch (btc->getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: if (surfaceTabIndex < 0) { surfaceTabIndex = i; @@ -1377,8 +1426,9 @@ } else { lastParent = eventNewWindow.getBrowserWindowCreated(); - this->tabBar->setTabData(i, qVariantFromValue((void*)NULL)); - this->tabClosed(i); + this->tabBar->setTabData(i, QVariant::fromValue((void*)NULL)); + this->tabClosed(i, + RemoveTabMode::INGORE_TAB_CONTENT); } } } @@ -1408,8 +1458,9 @@ if (btc != NULL) { allTabContent.push_back(btc); } - this->tabBar->setTabData(i, qVariantFromValue((void*)NULL)); - this->tabClosed(i); + this->tabBar->setTabData(i, QVariant::fromValue((void*)NULL)); + this->tabClosed(i, + RemoveTabMode::INGORE_TAB_CONTENT); } } @@ -1451,8 +1502,9 @@ void* p = this->tabBar->tabData(i).value(); BrowserTabContent* btc = (BrowserTabContent*)p; if (btc == browserTabContent) { - this->tabBar->setTabData(i, qVariantFromValue((void*)NULL)); - this->tabClosed(i); + this->tabBar->setTabData(i, QVariant::fromValue((void*)NULL)); + this->tabClosed(i, + RemoveTabMode::INGORE_TAB_CONTENT); if (this->tabBar->count() <= 0) { EventManager::get()->removeAllEventsFromListener(this); } @@ -1562,7 +1614,7 @@ * called by the BrowswerWindow's File Menu. */ void -BrainBrowserWindowToolBar::closeSelectedTab() +BrainBrowserWindowToolBar::closeSelectedTabFromFileMenu() { tabCloseSelected(this->tabBar->currentIndex()); } @@ -1611,9 +1663,8 @@ this->updateToolBox(); emit viewedModelChanged(); - BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); - if (browserWindow != NULL) { - if (browserWindow->isTileTabsSelected()) { + if (m_parentBrainBrowserWindow != NULL) { + if (m_parentBrainBrowserWindow->isTileTabsSelected()) { const BrowserTabContent* btc = getTabContentFromSelectedTab(); if (btc != NULL) { if (m_tileTabsHighlightingTimerEnabledFlag) { @@ -1660,38 +1711,47 @@ * Called when tab bar mouse pressed. */ void -BrainBrowserWindowToolBar::tabBarMousePressedSlot() +BrainBrowserWindowToolBar::tabBarMousePressedSlot(QMouseEvent* event) { /* + * Only block if LEFT button is used without modifiers. Otherwise, if user + * performs right-click with mouse to display menu, the "mouseReleased" is + * never called by Qt. In addition, we don't want to block signals when + * the pop-up menu is displayed. + * * Dragging tabs is slow because as the tab is moved, there are many graphics * and user-interface updates each time the tab changes. So, disable these updates * during the time the mouse is pressed and until it is released. Note that we * block the "all windows graphics" update but not the individual window update. * The individual window graphics update is needed to draw the box around the selected tab. */ - EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); - EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, true); + if (event->button() == Qt::LeftButton) { + EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, true); + } } /** * Called when tab bar mouse released. */ void -BrainBrowserWindowToolBar::tabBarMouseReleasedSlot() +BrainBrowserWindowToolBar::tabBarMouseReleasedSlot(QMouseEvent* event) { /* * Enable user-interface updates and graphics drawing since any tab * dragging has finished. */ - EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); - EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, false); - EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); - - /** - * Causes graphics and user-interface to update. - * A box is drawn around selected tab. - */ - selectedTabChanged(tabBar->currentIndex()); + if (event->button() == Qt::LeftButton) { + EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, false); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); + + /** + * Causes graphics and user-interface to update. + * A box is drawn around selected tab. + */ + selectedTabChanged(tabBar->currentIndex()); + } } /** @@ -1705,7 +1765,8 @@ { if ((tabIndex >= 0) && (tabIndex < this->tabBar->count())) { - tabClosed(tabIndex); + tabClosed(tabIndex, + RemoveTabMode::CLOSE_TAB_CONTENT_FOR_REOPENING); } this->updateGraphicsWindow(); } @@ -1715,12 +1776,16 @@ * * @param tabIndex * Index of tab that was closed. + * @param removeTabMode + * Mode for removing tab */ void -BrainBrowserWindowToolBar::tabClosed(int tabIndex) +BrainBrowserWindowToolBar::tabClosed(int tabIndex, + const RemoveTabMode removeTabMode) { CaretAssertArrayIndex(this-tabBar->tabData(), this->tabBar->count(), tabIndex); - this->removeTab(tabIndex); + this->removeTab(tabIndex, + removeTabMode); if (this->isDestructionInProgress == false) { this->updateToolBar(); @@ -1730,6 +1795,16 @@ } /** + * Emit the viewedModelChanged() signal + * This method is used by friend classes + */ +void +BrainBrowserWindowToolBar::emitViewModelChangedSignal() +{ + emit viewedModelChanged(); +} + +/** * Called when a tab has been moved. * * @param from @@ -1746,9 +1821,13 @@ /** * Remove the tab at the given index. * @param index + * Index of the tab + * @param removeTabMode + * Mode for removing tab */ void -BrainBrowserWindowToolBar::removeTab(int tabIndex) +BrainBrowserWindowToolBar::removeTab(int tabIndex, + const RemoveTabMode removeTabMode) { CaretAssertArrayIndex(this-tabBar->tabData(), this->tabBar->count(), tabIndex); @@ -1756,9 +1835,35 @@ if (p != NULL) { BrowserTabContent* btc = (BrowserTabContent*)p; - EventBrowserTabDelete deleteTabEvent(btc, - btc->getTabNumber()); - EventManager::get()->sendEvent(deleteTabEvent.getPointer()); + switch (removeTabMode) { + case RemoveTabMode::CLOSE_TAB_CONTENT_FOR_REOPENING: + { + EventBrowserTabClose closeTabEvent(btc, + btc->getTabNumber(), + this->browserWindowIndex, + tabIndex); + EventManager::get()->sendEvent(closeTabEvent.getPointer()); + if (closeTabEvent.isError()) { + WuQMessageBox::errorOk(this, + closeTabEvent.getErrorMessage()); + } + } + break; + case RemoveTabMode::DELETE_TAB_CONTENT: + { + EventBrowserTabDelete deleteTabEvent(btc, + btc->getTabNumber(), + this->browserWindowIndex); + EventManager::get()->sendEvent(deleteTabEvent.getPointer()); + if (deleteTabEvent.isError()) { + WuQMessageBox::errorOk(this, + deleteTabEvent.getErrorMessage()); + } + } + break; + case RemoveTabMode::INGORE_TAB_CONTENT: + break; + } } this->tabBar->blockSignals(true); @@ -1798,7 +1903,8 @@ EventManager::get()->sendEvent(getAllModelsEvent.getPointer()); if (getAllModelsEvent.getFirstModel() == NULL) { for (int i = (this->tabBar->count() - 1); i >= 0; i--) { - this->removeTab(i); + this->removeTab(i, + RemoveTabMode::DELETE_TAB_CONTENT); } } @@ -1810,7 +1916,6 @@ bool showWholeBrainSurfaceOptionsWidget = false; bool showSingleSurfaceOptionsWidget = false; bool showSurfaceMontageOptionsWidget = false; - bool showClippingOptionsWidget = false; bool showVolumeIndicesWidget = false; bool showVolumePlaneWidget = false; bool showVolumeMontageWidget = false; @@ -1822,100 +1927,215 @@ bool showChartTwoOrientationWidget = false; bool showChartTwoAttributesWidget = false; bool showChartTwoAxesWidget = false; - bool showChartTwoTitleWidget = false; bool showModeWidget = true; - bool showWindowWidget = true; + bool showViewWidget = true; + bool showTabMiscWidget = true; + bool showAnnotateModeWidget(false); + bool showBorderModeWidget(false); + bool showFociModeWidget(false); + bool showImageModeWidget(false); + bool showTileModeWidget(false); + bool showVolumeModeWidget(false); + + bool showViewModeWidgetsFlag(false); + + /* + * Viewed models may not be compatible + * with all user input modes + */ + bool borderCompatibleViewFlag(false); + bool fociCompatibleViewFlag(false); + bool imageCompatibleViewFlag(false); + bool volumeEditCompatibleViewFlag(false); switch (viewModel) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: - showOrientationWidget = true; - showSingleSurfaceOptionsWidget = true; - showClippingOptionsWidget = true; + borderCompatibleViewFlag = true; + fociCompatibleViewFlag = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: - showOrientationWidget = true; - showSurfaceMontageOptionsWidget = true; - showClippingOptionsWidget = true; + borderCompatibleViewFlag = true; + fociCompatibleViewFlag = true; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: - showVolumeIndicesWidget = true; - showVolumePlaneWidget = true; - showVolumeMontageWidget = true; - showClippingOptionsWidget = true; + imageCompatibleViewFlag = true; + volumeEditCompatibleViewFlag = true; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: - showOrientationWidget = true; - showWholeBrainSurfaceOptionsWidget = true; - showVolumeIndicesWidget = true; - showClippingOptionsWidget = true; + borderCompatibleViewFlag = true; + fociCompatibleViewFlag = true; break; case ModelTypeEnum::MODEL_TYPE_CHART: - { - ModelChart* modelChart = browserTabContent->getDisplayedChartOneModel(); - if (modelChart != NULL) { - showChartOneTypeWidget = true; - switch (modelChart->getSelectedChartOneDataType(browserTabContent->getTabNumber())) { - case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: - break; - case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: - showChartOneAttributesWidget = true; - break; - case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: - showChartOneAttributesWidget = true; - break; - case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: - showChartOneAxesWidget = true; - showChartOneAttributesWidget = true; - break; - case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: - showChartOneAxesWidget = true; - showChartOneAttributesWidget = true; - break; - case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: - showChartOneAxesWidget = true; - showChartOneAttributesWidget = true; - break; - } - } - } break; case ModelTypeEnum::MODEL_TYPE_CHART_TWO: - { - ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); - if (modelChartTwo != NULL) { - switch (modelChartTwo->getSelectedChartTwoDataType(browserTabContent->getTabNumber())) { - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: - showChartTwoAxesWidget = true; - showChartTwoTitleWidget = true; - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: - showChartTwoAxesWidget = true; - showChartTwoTitleWidget = true; - break; - case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: - showChartTwoOrientationWidget = true; - showChartTwoAttributesWidget = true; - showChartTwoTitleWidget = true; - break; + break; + } + + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + const ToolBarWidthModeEnum::Enum widthMode = prefs->getToolBarWidthMode(); + bool wideFlag(false); + switch (widthMode) { + case ToolBarWidthModeEnum::STANDARD: + wideFlag = false; + break; + case ToolBarWidthModeEnum::WIDE: + wideFlag = true; + break; + } + + /* + * Enable widgets for selected input mode + */ + CaretAssert(this->selectedUserInputProcessor); + switch (this->selectedUserInputProcessor->getUserInputMode()) { + case UserInputModeEnum::Enum::ANNOTATIONS: + if (wideFlag) { + showViewModeWidgetsFlag = true; + } + showAnnotateModeWidget = true; + break; + case UserInputModeEnum::Enum::BORDERS: + showViewModeWidgetsFlag = true; + showBorderModeWidget = borderCompatibleViewFlag; + break; + case UserInputModeEnum::Enum::FOCI: + showViewModeWidgetsFlag = true; + showFociModeWidget = fociCompatibleViewFlag; + break; + case UserInputModeEnum::Enum::IMAGE: + showViewModeWidgetsFlag = true; + showImageModeWidget = imageCompatibleViewFlag; + break; + case UserInputModeEnum::Enum::INVALID: + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + if (wideFlag) { + showViewModeWidgetsFlag = true; + } + showTileModeWidget = true; + break; + case UserInputModeEnum::Enum::VIEW: + showViewModeWidgetsFlag = true; + break; + case UserInputModeEnum::Enum::VOLUME_EDIT: + showViewModeWidgetsFlag = true; + showVolumeModeWidget = volumeEditCompatibleViewFlag; + break; + } + + /* + * Note that view mode widgets are shown in other modes. + */ + if (showViewModeWidgetsFlag) { + showViewWidget = true; + + switch (viewModel) { + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + showOrientationWidget = true; + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + showOrientationWidget = true; + showSingleSurfaceOptionsWidget = true; + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + showOrientationWidget = true; + showSurfaceMontageOptionsWidget = true; + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + showVolumeIndicesWidget = true; + showVolumePlaneWidget = true; + showVolumeMontageWidget = true; + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + showOrientationWidget = true; + showWholeBrainSurfaceOptionsWidget = true; + showVolumeIndicesWidget = true; + break; + case ModelTypeEnum::MODEL_TYPE_CHART: + { + ModelChart* modelChart = browserTabContent->getDisplayedChartOneModel(); + if (modelChart != NULL) { + showChartOneTypeWidget = true; + switch (modelChart->getSelectedChartOneDataType(browserTabContent->getTabNumber())) { + case ChartOneDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: + showChartOneAttributesWidget = true; + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: + showChartOneAttributesWidget = true; + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: + showChartOneAxesWidget = true; + showChartOneAttributesWidget = true; + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: + showChartOneAxesWidget = true; + showChartOneAttributesWidget = true; + break; + case ChartOneDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: + showChartOneAxesWidget = true; + showChartOneAttributesWidget = true; + break; + } + } + } + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + { + ModelChartTwo* modelChartTwo = browserTabContent->getDisplayedChartTwoModel(); + if (modelChartTwo != NULL) { + switch (modelChartTwo->getSelectedChartTwoDataType(browserTabContent->getTabNumber())) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + showChartTwoAxesWidget = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + showChartTwoAxesWidget = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + showChartTwoAxesWidget = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + showChartTwoAxesWidget = true; + showChartTwoOrientationWidget = true; + showChartTwoAttributesWidget = true; + break; + } + + showChartTwoTypeWidget = true; } - - showChartTwoTypeWidget = true; } + break; } - break; } /* + * WB-900 + * Do not show chart orientation widget since charts no longer use + * transformation matrices but instead pan/zoom by adjusting the + * axis min/max. Need to work on conversion (if possible) from + * matrix to axis min/max. + */ + showChartTwoOrientationWidget = false; + + + /* * Need to turn off display of all widgets, * otherwise, the toolbar width may be overly * expanded with empty space as other widgets * are turned on and off. */ + this->modeWidget->setVisible(false); + this->viewWidget->setVisible(false); this->orientationWidget->setVisible(false); this->wholeBrainSurfaceOptionsWidget->setVisible(false); this->singleSurfaceSelectionWidget->setVisible(false); @@ -1926,15 +2146,30 @@ this->chartAttributesWidget->setVisible(false); this->chartTwoOrientationWidget->setVisible(false); this->chartTwoAttributesWidget->setVisible(false); - this->chartTwoAxesWidget->setVisible(false); - this->chartTwoTitleWidget->setVisible(false); + this->chartTwoOrientedAxesWidget->setVisible(false); this->volumeIndicesWidget->setVisible(false); this->volumePlaneWidget->setVisible(false); this->volumeMontageWidget->setVisible(false); - this->modeWidget->setVisible(false); - this->windowWidget->setVisible(false); - m_clippingOptionsWidget->setVisible(false); + this->tabMiscWidget->setVisible(false); + + this->annotateModeWidget->setVisible(false); + this->bordersModeWidget->setVisible(false); + this->fociModeWidget->setVisible(false); + this->imageModeWidget->setVisible(false); + this->tileModeWidget->setVisible(false); + this->volumeModeWidget->setVisible(false); + + updateToolBarComponents(browserTabContent); + + this->annotateModeWidget->setVisible(showAnnotateModeWidget); + this->bordersModeWidget->setVisible(showBorderModeWidget); + this->fociModeWidget->setVisible(showFociModeWidget); + this->imageModeWidget->setVisible(showImageModeWidget); + this->tileModeWidget->setVisible(showTileModeWidget); + this->volumeModeWidget->setVisible(showVolumeModeWidget); + this->modeWidget->setVisible(showModeWidget); + this->viewWidget->setVisible(showViewWidget); this->orientationWidget->setVisible(showOrientationWidget); this->wholeBrainSurfaceOptionsWidget->setVisible(showWholeBrainSurfaceOptionsWidget); this->singleSurfaceSelectionWidget->setVisible(showSingleSurfaceOptionsWidget); @@ -1945,22 +2180,18 @@ this->chartAttributesWidget->setVisible(showChartOneAttributesWidget); this->chartTwoOrientationWidget->setVisible(showChartTwoOrientationWidget); this->chartTwoAttributesWidget->setVisible(showChartTwoAttributesWidget); - this->chartTwoAxesWidget->setVisible(showChartTwoAxesWidget); - this->chartTwoTitleWidget->setVisible(showChartTwoTitleWidget); + this->chartTwoOrientedAxesWidget->setVisible(showChartTwoAxesWidget); this->volumeIndicesWidget->setVisible(showVolumeIndicesWidget); this->volumePlaneWidget->setVisible(showVolumePlaneWidget); this->volumeMontageWidget->setVisible(showVolumeMontageWidget); - this->modeWidget->setVisible(showModeWidget); - m_clippingOptionsWidget->setVisible(showClippingOptionsWidget); - this->windowWidget->setVisible(showWindowWidget); - + this->tabMiscWidget->setVisible(showTabMiscWidget); + updateToolBarComponents(browserTabContent); this->updateAllTabNames(); - BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); - if (browserWindow != NULL) { - if (browserWindow->isFullScreen()) { + if (m_parentBrainBrowserWindow != NULL) { + if (m_parentBrainBrowserWindow->isFullScreen()) { this->setVisible(false); } else { @@ -1968,20 +2199,6 @@ } } - /* - * Try to avoid resizing of Toolbar widget (view, orientation, etc) - * height when models are changed. Let it grow but never shrink. - */ - if (isVisible()) { - if (m_toolbarWidget->isVisible()) { - const int sizeHintHeight = m_toolbarWidget->sizeHint().height(); - const int actualHeight = m_toolbarWidget->height(); - if (sizeHintHeight >= actualHeight) { - m_toolbarWidget->setFixedHeight(sizeHintHeight); - } - } - } - m_performingUpdateFlag = false; } @@ -2006,13 +2223,12 @@ this->updateChartAttributesWidget(browserTabContent); this->updateChartTwoOrientationWidget(browserTabContent); this->updateChartTwoAttributesWidget(browserTabContent); - this->updateChartTwoAxesWidget(browserTabContent); - this->updateChartTwoTitleWidget(browserTabContent); + this->updateChartTwoOrientedAxesWidget(browserTabContent); this->updateVolumeMontageWidget(browserTabContent); this->updateVolumePlaneWidget(browserTabContent); this->updateModeWidget(browserTabContent); + this->updateViewWidget(browserTabContent); this->updateTabOptionsWidget(browserTabContent); - this->updateClippingOptionsWidget(browserTabContent); } } @@ -2024,87 +2240,11 @@ QWidget* BrainBrowserWindowToolBar::createViewWidget() { - WuQMacroManager* macroManager = WuQMacroManager::instance(); - const QString objectNamePrefix(m_objectNamePrefix - + ":ViewMode"); - - this->viewModeChartOneRadioButton = new QRadioButton("Chart Old"); - this->viewModeChartOneRadioButton->setToolTip("Show Old Chart View"); - this->viewModeChartOneRadioButton->setObjectName(objectNamePrefix - + ":ChartOld"); - macroManager->addMacroSupportToObject(this->viewModeChartOneRadioButton, - "Select Chart Old View"); - - this->viewModeChartTwoRadioButton = new QRadioButton("Chart"); - this->viewModeChartTwoRadioButton->setToolTip("Show Chart View"); - this->viewModeChartTwoRadioButton->setObjectName(objectNamePrefix - + ":Chart"); - macroManager->addMacroSupportToObject(this->viewModeChartTwoRadioButton, - "Select Chart View"); - - this->viewModeSurfaceRadioButton = new QRadioButton("Surface"); - this->viewModeSurfaceRadioButton->setToolTip("Show Surace View"); - this->viewModeSurfaceRadioButton->setObjectName(objectNamePrefix - + ":Surface"); - macroManager->addMacroSupportToObject(this->viewModeSurfaceRadioButton, - "Select surface view"); - - this->viewModeSurfaceMontageRadioButton = new QRadioButton("Montage"); - this->viewModeSurfaceMontageRadioButton->setToolTip("Show Montage View"); - this->viewModeSurfaceMontageRadioButton->setObjectName(objectNamePrefix - + ":Montage"); - macroManager->addMacroSupportToObject(this->viewModeSurfaceMontageRadioButton, - "Select surface montage view"); - - this->viewModeVolumeRadioButton = new QRadioButton("Volume"); - this->viewModeVolumeRadioButton->setToolTip("Show Volume View"); - this->viewModeVolumeRadioButton->setObjectName(objectNamePrefix - + ":Volume"); - macroManager->addMacroSupportToObject(this->viewModeVolumeRadioButton, - "Select volume view"); - - this->viewModeWholeBrainRadioButton = new QRadioButton("All"); - this->viewModeWholeBrainRadioButton->setToolTip("Show All View"); - this->viewModeWholeBrainRadioButton->setObjectName(objectNamePrefix - + ":All"); - macroManager->addMacroSupportToObject(this->viewModeWholeBrainRadioButton, - "Select all view"); - - QWidget* widget = new QWidget(); - QVBoxLayout* layout = new QVBoxLayout(widget); -#ifdef CARET_OS_MACOSX - WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); -#else - WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 2); -#endif - layout->addWidget(this->viewModeSurfaceMontageRadioButton); - layout->addWidget(this->viewModeVolumeRadioButton); - layout->addWidget(this->viewModeWholeBrainRadioButton); - layout->addWidget(this->viewModeChartTwoRadioButton); - layout->addWidget(this->viewModeSurfaceRadioButton); - layout->addWidget(this->viewModeChartOneRadioButton); - - QButtonGroup* viewModeRadioButtonGroup = new QButtonGroup(this); - viewModeRadioButtonGroup->addButton(this->viewModeChartOneRadioButton); - viewModeRadioButtonGroup->addButton(this->viewModeChartTwoRadioButton); - viewModeRadioButtonGroup->addButton(this->viewModeSurfaceRadioButton); - viewModeRadioButtonGroup->addButton(this->viewModeSurfaceMontageRadioButton); - viewModeRadioButtonGroup->addButton(this->viewModeVolumeRadioButton); - viewModeRadioButtonGroup->addButton(this->viewModeWholeBrainRadioButton); - QObject::connect(viewModeRadioButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), - this, SLOT(viewModeRadioButtonClicked(QAbstractButton*))); - - this->viewWidgetGroup = new WuQWidgetObjectGroup(this); - this->viewWidgetGroup->add(this->viewModeChartOneRadioButton); - this->viewWidgetGroup->add(this->viewModeChartTwoRadioButton); - this->viewWidgetGroup->add(this->viewModeSurfaceRadioButton); - this->viewWidgetGroup->add(this->viewModeSurfaceMontageRadioButton); - this->viewWidgetGroup->add(this->viewModeVolumeRadioButton); - this->viewWidgetGroup->add(this->viewModeWholeBrainRadioButton); - - QWidget* w = this->createToolWidget("View", - widget, - WIDGET_PLACEMENT_NONE, + m_viewToolBarComponent = new BrainBrowserWindowToolBarView(m_objectNamePrefix, + this); + QWidget* w = this->createToolWidget("Display", + m_viewToolBarComponent, + WIDGET_PLACEMENT_NONE, WIDGET_PLACEMENT_TOP, 0); return w; @@ -2125,54 +2265,7 @@ if (browserTabContent != NULL) { modelType = browserTabContent->getSelectedModelType(); } - - this->viewWidgetGroup->blockAllSignals(true); - - /* - * Enable buttons for valid types - */ - if (browserTabContent != NULL) { - this->viewModeSurfaceRadioButton->setEnabled(browserTabContent->isSurfaceModelValid()); - this->viewModeSurfaceMontageRadioButton->setEnabled(browserTabContent->isSurfaceMontageModelValid()); - this->viewModeVolumeRadioButton->setEnabled(browserTabContent->isVolumeSliceModelValid()); - this->viewModeWholeBrainRadioButton->setEnabled(browserTabContent->isWholeBrainModelValid()); - this->viewModeChartOneRadioButton->setEnabled(browserTabContent->isChartOneModelValid()); - this->viewModeChartTwoRadioButton->setEnabled(browserTabContent->isChartTwoModelValid()); - } - else { - this->viewModeSurfaceRadioButton->setEnabled(false); - this->viewModeSurfaceMontageRadioButton->setEnabled(false); - this->viewModeVolumeRadioButton->setEnabled(false); - this->viewModeWholeBrainRadioButton->setEnabled(false); - this->viewModeChartOneRadioButton->setEnabled(false); - this->viewModeChartTwoRadioButton->setEnabled(false); - } - - switch (modelType) { - case ModelTypeEnum::MODEL_TYPE_INVALID: - break; - case ModelTypeEnum::MODEL_TYPE_SURFACE: - this->viewModeSurfaceRadioButton->setChecked(true); - break; - case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: - this->viewModeSurfaceMontageRadioButton->setChecked(true); - break; - case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: - this->viewModeVolumeRadioButton->setChecked(true); - break; - case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: - this->viewModeWholeBrainRadioButton->setChecked(true); - break; - case ModelTypeEnum::MODEL_TYPE_CHART: - this->viewModeChartOneRadioButton->setChecked(true); - break; - case ModelTypeEnum::MODEL_TYPE_CHART_TWO: - this->viewModeChartTwoRadioButton->setChecked(true); - break; - } - - this->viewWidgetGroup->blockAllSignals(false); - + m_viewToolBarComponent->updateContent(browserTabContent); return modelType; } @@ -2184,249 +2277,10 @@ QWidget* BrainBrowserWindowToolBar::createOrientationWidget() { - WuQMacroManager* macroManager = WuQMacroManager::instance(); - CaretAssert(macroManager); - const QString objectNamePrefix(m_objectNamePrefix - + ":Orientation:"); - - this->viewOrientationLeftIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left.png"); - this->viewOrientationRightIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right.png"); - this->viewOrientationAnteriorIcon = WuQtUtilities::loadIcon(":/ToolBar/view-anterior.png"); - this->viewOrientationPosteriorIcon = WuQtUtilities::loadIcon(":/ToolBar/view-posterior.png"); - this->viewOrientationDorsalIcon = WuQtUtilities::loadIcon(":/ToolBar/view-dorsal.png"); - this->viewOrientationVentralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-ventral.png"); - this->viewOrientationLeftLateralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left-lateral.png"); - this->viewOrientationLeftMedialIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left-medial.png"); - this->viewOrientationRightLateralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right-lateral.png"); - this->viewOrientationRightMedialIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right-medial.png"); - - this->orientationLeftOrLateralToolButtonAction = WuQtUtilities::createAction("L", - "View from a LEFT perspective", - this, - this, - SLOT(orientationLeftOrLateralToolButtonTriggered(bool))); - if (this->viewOrientationLeftIcon != NULL) { - this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftIcon); - } - else { - this->orientationLeftOrLateralToolButtonAction->setIconText("L"); - } - this->orientationLeftOrLateralToolButtonAction->setObjectName(objectNamePrefix - + "LeftOrLateralView"); - macroManager->addMacroSupportToObject(this->orientationLeftOrLateralToolButtonAction, - "Select left or lateral orientation"); - - this->orientationRightOrMedialToolButtonAction = WuQtUtilities::createAction("R", - "View from a RIGHT perspective", - this, - this, - SLOT(orientationRightOrMedialToolButtonTriggered(bool))); - if (this->viewOrientationRightIcon != NULL) { - this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightIcon); - } - else { - this->orientationRightOrMedialToolButtonAction->setIconText("R"); - } - this->orientationRightOrMedialToolButtonAction->setObjectName(objectNamePrefix - + "RightOrMedialView"); - macroManager->addMacroSupportToObject(this->orientationRightOrMedialToolButtonAction, - "Select right or medial orientation"); - - this->orientationAnteriorToolButtonAction = WuQtUtilities::createAction("A", - "View from an ANTERIOR perspective", - this, - this, - SLOT(orientationAnteriorToolButtonTriggered(bool))); - if (this->viewOrientationAnteriorIcon != NULL) { - this->orientationAnteriorToolButtonAction->setIcon(*this->viewOrientationAnteriorIcon); - } - else { - this->orientationAnteriorToolButtonAction->setIconText("A"); - } - this->orientationAnteriorToolButtonAction->setObjectName(objectNamePrefix - + "AnteriorView"); - macroManager->addMacroSupportToObject(this->orientationAnteriorToolButtonAction, - "Select anterior orientation"); - - this->orientationPosteriorToolButtonAction = WuQtUtilities::createAction("P", - "View from a POSTERIOR perspective", - this, - this, - SLOT(orientationPosteriorToolButtonTriggered(bool))); - if (this->viewOrientationPosteriorIcon != NULL) { - this->orientationPosteriorToolButtonAction->setIcon(*this->viewOrientationPosteriorIcon); - } - else { - this->orientationPosteriorToolButtonAction->setIconText("P"); - } - this->orientationPosteriorToolButtonAction->setObjectName(objectNamePrefix - + "PosteriorView"); - macroManager->addMacroSupportToObject(this->orientationPosteriorToolButtonAction, - "Select posterior orientation"); - - this->orientationDorsalToolButtonAction = WuQtUtilities::createAction("D", - "View from a DORSAL perspective", - this, - this, - SLOT(orientationDorsalToolButtonTriggered(bool))); - if (this->viewOrientationDorsalIcon != NULL) { - this->orientationDorsalToolButtonAction->setIcon(*this->viewOrientationDorsalIcon); - } - else { - this->orientationDorsalToolButtonAction->setIconText("D"); - } - this->orientationDorsalToolButtonAction->setObjectName(objectNamePrefix - + "DorsalView"); - macroManager->addMacroSupportToObject(this->orientationDorsalToolButtonAction, - "Select dorsal orientation"); - - this->orientationVentralToolButtonAction = WuQtUtilities::createAction("V", - "View from a VENTRAL perspective", - this, - this, - SLOT(orientationVentralToolButtonTriggered(bool))); - if (this->viewOrientationVentralIcon != NULL) { - this->orientationVentralToolButtonAction->setIcon(*this->viewOrientationVentralIcon); - } - else { - this->orientationVentralToolButtonAction->setIconText("V"); - } - this->orientationVentralToolButtonAction->setObjectName(objectNamePrefix - + "VentralView"); - macroManager->addMacroSupportToObject(this->orientationVentralToolButtonAction, - "Select ventral orientation"); - - - this->orientationLateralMedialToolButtonAction = WuQtUtilities::createAction("LM", - "View from a Lateral/Medial perspective", - this, - this, - SLOT(orientationLateralMedialToolButtonTriggered(bool))); - this->orientationLateralMedialToolButtonAction->setObjectName(objectNamePrefix - + "LateralMedialView"); - macroManager->addMacroSupportToObject(this->orientationLateralMedialToolButtonAction, - "Select lateral/medial orientation"); - - this->orientationDorsalVentralToolButtonAction = WuQtUtilities::createAction("DV", - "View from a Dorsal/Ventral perspective", - this, - this, - SLOT(orientationDorsalVentralToolButtonTriggered(bool))); - this->orientationDorsalVentralToolButtonAction->setObjectName(objectNamePrefix - + "DorsalVentralView"); - macroManager->addMacroSupportToObject(this->orientationDorsalVentralToolButtonAction, - "Select dorsal/ventral orientation"); - - this->orientationAnteriorPosteriorToolButtonAction = WuQtUtilities::createAction("AP", - "View from a Anterior/Posterior perspective", - this, - this, - SLOT(orientationAnteriorPosteriorToolButtonTriggered(bool))); - this->orientationAnteriorPosteriorToolButtonAction->setObjectName(objectNamePrefix - + "AnteriorPosteriorView"); - macroManager->addMacroSupportToObject(this->orientationAnteriorPosteriorToolButtonAction, - "Select anterior/posterior orientation"); - - this->orientationResetToolButtonAction = WuQtUtilities::createAction("R\nE\nS\nE\nT", - "Reset the view to dorsal and remove any panning or zooming", - this, - this, - SLOT(orientationResetToolButtonTriggered(bool))); - this->orientationResetToolButtonAction->setObjectName(objectNamePrefix - + "ResetView"); - macroManager->addMacroSupportToObject(this->orientationResetToolButtonAction, - "Reset to default orientation"); - - this->orientationLeftOrLateralToolButton = new QToolButton(); - this->orientationLeftOrLateralToolButton->setDefaultAction(this->orientationLeftOrLateralToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationLeftOrLateralToolButton); - this->orientationLeftOrLateralToolButtonAction->setParent(this->orientationLeftOrLateralToolButton); - - this->orientationRightOrMedialToolButton = new QToolButton(); - this->orientationRightOrMedialToolButton->setDefaultAction(this->orientationRightOrMedialToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationRightOrMedialToolButton); - orientationRightOrMedialToolButtonAction->setParent(orientationRightOrMedialToolButton); - - this->orientationAnteriorToolButton = new QToolButton(); - this->orientationAnteriorToolButton->setDefaultAction(this->orientationAnteriorToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationAnteriorToolButton); - this->orientationAnteriorToolButtonAction->setParent(this->orientationAnteriorToolButton); - - this->orientationPosteriorToolButton = new QToolButton(); - this->orientationPosteriorToolButton->setDefaultAction(this->orientationPosteriorToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationPosteriorToolButton); - this->orientationPosteriorToolButtonAction->setParent(this->orientationPosteriorToolButton); - - this->orientationDorsalToolButton = new QToolButton(); - this->orientationDorsalToolButton->setDefaultAction(this->orientationDorsalToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationDorsalToolButton); - this->orientationDorsalToolButtonAction->setParent(this->orientationDorsalToolButton); - - this->orientationVentralToolButton = new QToolButton(); - this->orientationVentralToolButton->setDefaultAction(this->orientationVentralToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationVentralToolButton); - this->orientationVentralToolButtonAction->setParent(this->orientationVentralToolButton); - - this->orientationLateralMedialToolButton = new QToolButton(); - this->orientationLateralMedialToolButton->setDefaultAction(this->orientationLateralMedialToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationLateralMedialToolButton); - orientationLateralMedialToolButtonAction->setParent(orientationLateralMedialToolButton); - - this->orientationDorsalVentralToolButton = new QToolButton(); - this->orientationDorsalVentralToolButton->setDefaultAction(this->orientationDorsalVentralToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationDorsalVentralToolButton); - orientationDorsalVentralToolButtonAction->setParent(orientationDorsalVentralToolButton); - - this->orientationAnteriorPosteriorToolButton = new QToolButton(); - this->orientationAnteriorPosteriorToolButton->setDefaultAction(this->orientationAnteriorPosteriorToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationAnteriorPosteriorToolButton); - orientationAnteriorPosteriorToolButtonAction->setParent(orientationAnteriorPosteriorToolButton); - - WuQtUtilities::matchWidgetWidths(this->orientationLateralMedialToolButton, - this->orientationDorsalVentralToolButton, - this->orientationAnteriorPosteriorToolButton); - - QToolButton* orientationResetToolButton = new QToolButton(); - orientationResetToolButton->setDefaultAction(this->orientationResetToolButtonAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(orientationResetToolButton); - - this->orientationCustomViewSelectToolButton = new QToolButton(); - this->orientationCustomViewSelectToolButton->setDefaultAction(this->customViewAction); - this->orientationCustomViewSelectToolButton->setSizePolicy(QSizePolicy::Minimum, - QSizePolicy::Fixed); - WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationCustomViewSelectToolButton); - - QGridLayout* buttonGridLayout = new QGridLayout(); - buttonGridLayout->setColumnStretch(3, 100); - WuQtUtilities::setLayoutSpacingAndMargins(buttonGridLayout, 0, 0); - buttonGridLayout->addWidget(this->orientationLeftOrLateralToolButton, 0, 0); - buttonGridLayout->addWidget(this->orientationRightOrMedialToolButton, 0, 1); - buttonGridLayout->addWidget(this->orientationDorsalToolButton, 1, 0); - buttonGridLayout->addWidget(this->orientationVentralToolButton, 1, 1); - buttonGridLayout->addWidget(this->orientationAnteriorToolButton, 2, 0); - buttonGridLayout->addWidget(this->orientationPosteriorToolButton, 2, 1); - buttonGridLayout->addWidget(this->orientationLateralMedialToolButton, 0, 2); - buttonGridLayout->addWidget(this->orientationDorsalVentralToolButton, 1, 2); - buttonGridLayout->addWidget(this->orientationAnteriorPosteriorToolButton, 2, 2); - buttonGridLayout->addWidget(this->orientationCustomViewSelectToolButton, 3, 0, 1, 5, Qt::AlignHCenter); - buttonGridLayout->addWidget(orientationResetToolButton, 0, 4, 3, 1); - - QWidget* w = new QWidget(); - QVBoxLayout* layout = new QVBoxLayout(w); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); - layout->addLayout(buttonGridLayout); - - this->orientationWidgetGroup = new WuQWidgetObjectGroup(this); - this->orientationWidgetGroup->add(this->orientationLeftOrLateralToolButtonAction); - this->orientationWidgetGroup->add(this->orientationRightOrMedialToolButtonAction); - this->orientationWidgetGroup->add(this->orientationAnteriorToolButtonAction); - this->orientationWidgetGroup->add(this->orientationPosteriorToolButtonAction); - this->orientationWidgetGroup->add(this->orientationDorsalToolButtonAction); - this->orientationWidgetGroup->add(this->orientationVentralToolButtonAction); - this->orientationWidgetGroup->add(this->orientationResetToolButtonAction); - + m_orientationToolBarComponent = new BrainBrowserWindowToolBarOrientation(m_objectNamePrefix, + this); QWidget* orientWidget = this->createToolWidget("Orientation", - w, + m_orientationToolBarComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 0); @@ -2448,370 +2302,22 @@ return; } - const int32_t tabIndex = browserTabContent->getTabNumber(); - - this->orientationWidgetGroup->blockAllSignals(true); - - const Model* mdc = this->getDisplayedModel(); - if (mdc != NULL) { - const ModelSurface* mdcs = dynamic_cast(mdc); - const ModelSurfaceMontage* mdcsm = dynamic_cast(mdc); - const ModelVolume* mdcv = dynamic_cast(mdc); - const ModelWholeBrain* mdcwb = dynamic_cast(mdc); - - bool rightFlag = false; - bool leftFlag = false; - bool leftRightFlag = false; - - bool enableDualViewOrientationButtons = false; - bool showDualViewOrientationButtons = false; - bool showSingleViewOrientationButtons = false; - - if (mdcs != NULL) { - const Surface* surface = mdcs->getSurface(); - const StructureEnum::Enum structure = surface->getStructure(); - if (StructureEnum::isLeft(structure)) { - leftFlag = true; - } - else if (StructureEnum::isRight(structure)) { - rightFlag = true; - } - else { - leftRightFlag = true; - } - - showSingleViewOrientationButtons = true; - } - else if (mdcsm != NULL) { - AString latMedLeftRightText = "LM"; - AString latMedLeftRightToolTipText = "View from a Lateral/Medial perspective"; - switch (mdcsm->getSelectedConfigurationType(tabIndex)) { - case SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION: - latMedLeftRightText = "LR"; - latMedLeftRightToolTipText = "View from a Right/Left Perspective"; - enableDualViewOrientationButtons = true; - break; - case SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION: - enableDualViewOrientationButtons = true; - break; - case SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION: - break; - } - - this->orientationLateralMedialToolButtonAction->setText(latMedLeftRightText); - WuQtUtilities::setToolTipAndStatusTip(this->orientationLateralMedialToolButtonAction, - latMedLeftRightToolTipText); - - showDualViewOrientationButtons = true; - } - else if (mdcv != NULL) { - /* nothing */ - } - else if (mdcwb != NULL) { - leftRightFlag = true; - showSingleViewOrientationButtons = true; - } - else { - CaretAssertMessage(0, "Unknown model display controller type"); - } - - if (rightFlag || leftFlag) { - if (rightFlag) { - if (this->viewOrientationRightLateralIcon != NULL) { - this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationRightLateralIcon); - } - else { - this->orientationLeftOrLateralToolButtonAction->setIconText("L"); - } - if (this->viewOrientationRightMedialIcon != NULL) { - this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightMedialIcon); - } - else { - this->orientationRightOrMedialToolButtonAction->setIconText("M"); - } - } - else if (leftFlag) { - if (this->viewOrientationLeftLateralIcon != NULL) { - this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftLateralIcon); - } - else { - this->orientationLeftOrLateralToolButtonAction->setIconText("L"); - } - if (this->viewOrientationLeftMedialIcon != NULL) { - this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationLeftMedialIcon); - } - else { - this->orientationRightOrMedialToolButtonAction->setIconText("M"); - } - } - WuQtUtilities::setToolTipAndStatusTip(this->orientationLeftOrLateralToolButtonAction, - "View from a LATERAL perspective"); - WuQtUtilities::setToolTipAndStatusTip(this->orientationRightOrMedialToolButtonAction, - "View from a MEDIAL perspective"); - } - else if (leftRightFlag) { - if (this->viewOrientationLeftIcon != NULL) { - this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftIcon); - } - else { - this->orientationLeftOrLateralToolButtonAction->setIconText("L"); - } - if (this->viewOrientationRightIcon != NULL) { - this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightIcon); - } - else { - this->orientationRightOrMedialToolButtonAction->setIconText("R"); - } - WuQtUtilities::setToolTipAndStatusTip(this->orientationLeftOrLateralToolButtonAction, - "View from a LEFT perspective"); - WuQtUtilities::setToolTipAndStatusTip(this->orientationRightOrMedialToolButtonAction, - "View from a RIGHT perspective"); - } - - /* - * The dual view buttons are not need for a flat map montage. - * However, if they are hidden, their space is not reallocated and the - * Reset button remains on the right and it looks weird. So, - * display them but disable them when a flat map montage. - */ - this->orientationLateralMedialToolButton->setVisible(showDualViewOrientationButtons); - this->orientationDorsalVentralToolButton->setVisible(showDualViewOrientationButtons); - this->orientationAnteriorPosteriorToolButton->setVisible(showDualViewOrientationButtons); - - this->orientationLateralMedialToolButton->setEnabled(enableDualViewOrientationButtons); - this->orientationDorsalVentralToolButton->setEnabled(enableDualViewOrientationButtons); - this->orientationAnteriorPosteriorToolButton->setEnabled(enableDualViewOrientationButtons); - - - this->orientationLeftOrLateralToolButton->setVisible(showSingleViewOrientationButtons); - this->orientationRightOrMedialToolButton->setVisible(showSingleViewOrientationButtons); - this->orientationDorsalToolButton->setVisible(showSingleViewOrientationButtons); - this->orientationVentralToolButton->setVisible(showSingleViewOrientationButtons); - this->orientationAnteriorToolButton->setVisible(showSingleViewOrientationButtons); - this->orientationPosteriorToolButton->setVisible(showSingleViewOrientationButtons); - } - this->orientationWidgetGroup->blockAllSignals(false); -} - -/** - * Create the whole brain surface options widget. - * - * @return The whole brain surface options widget. - */ -QWidget* -BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() -{ - WuQMacroManager* macroManager = WuQMacroManager::instance(); - const QString objectNamePrefix(m_objectNamePrefix - + ":All:"); - - this->wholeBrainSurfaceTypeComboBox = WuQFactory::newComboBox(); - WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceTypeComboBox, - "Select the geometric type of surface for display"); - QObject::connect(this->wholeBrainSurfaceTypeComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(wholeBrainSurfaceTypeComboBoxIndexChanged(int))); - this->wholeBrainSurfaceTypeComboBox->setObjectName(objectNamePrefix - + "SurfaceType"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceTypeComboBox, - "Select all view surface type"); - - /* - * Left - */ - this->wholeBrainSurfaceLeftCheckBox = new QCheckBox(" "); - WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceLeftCheckBox, - "Enable/Disable display of the left cortical surface"); - QObject::connect(this->wholeBrainSurfaceLeftCheckBox, SIGNAL(stateChanged(int)), - this, SLOT(wholeBrainSurfaceLeftCheckBoxStateChanged(int))); - this->wholeBrainSurfaceLeftCheckBox->setObjectName(objectNamePrefix - + "EnableLeft"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceLeftCheckBox, - "Enable all view left surface"); - - QToolButton* wholeBrainLeftSurfaceToolButton = new QToolButton(); - QAction* leftSurfaceAction = WuQtUtilities::createAction("Left", - "Select the whole brain left surface", - wholeBrainLeftSurfaceToolButton, - this, - SLOT(wholeBrainSurfaceLeftToolButtonTriggered(bool))); - WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainLeftSurfaceToolButton); - wholeBrainLeftSurfaceToolButton->setDefaultAction(leftSurfaceAction); -// leftSurfaceAction->setObjectName(objectNamePrefix -// + "SelectLeft"); -// macroManager->addMacroSupportToObject(leftSurfaceAction, -// "Select all view left surface"); - - /* - * Left menu is displayed when tool button is clicked - */ - this->wholeBrainSurfaceLeftMenu = new QMenu(wholeBrainLeftSurfaceToolButton); - QObject::connect(this->wholeBrainSurfaceLeftMenu, &QMenu::triggered, - this, &BrainBrowserWindowToolBar::wholeBrainSurfaceLeftMenuTriggered); - this->wholeBrainSurfaceLeftMenu->setObjectName(objectNamePrefix - + "SelectLeftSurfaceMenu"); - this->wholeBrainSurfaceLeftMenu->setToolTip("Select all view left surface"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceLeftMenu, - "Select all view left surface"); - - /* - * Right - */ - this->wholeBrainSurfaceRightCheckBox = new QCheckBox(" "); - WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceRightCheckBox, - "Enable/Disable display of the right cortical surface"); - QObject::connect(this->wholeBrainSurfaceRightCheckBox, SIGNAL(stateChanged(int)), - this, SLOT(wholeBrainSurfaceRightCheckBoxStateChanged(int))); - this->wholeBrainSurfaceRightCheckBox->setObjectName(objectNamePrefix - + "EnableRight"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceRightCheckBox, - "Enable all view right surface"); - - QToolButton* wholeBrainRightSurfaceToolButton = new QToolButton(); - QAction* rightSurfaceAction = WuQtUtilities::createAction("Right", - "Select the whole brain right surface", - wholeBrainRightSurfaceToolButton, - this, - SLOT(wholeBrainSurfaceRightToolButtonTriggered(bool))); - WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainRightSurfaceToolButton); - wholeBrainRightSurfaceToolButton->setDefaultAction(rightSurfaceAction); -// rightSurfaceAction->setObjectName(objectNamePrefix -// + "SelectRight"); -// macroManager->addMacroSupportToObject(rightSurfaceAction, -// "Select all view right surface"); - - /* - * Right menu is displayed when tool button is clicked - */ - this->wholeBrainSurfaceRightMenu = new QMenu(wholeBrainRightSurfaceToolButton); - QObject::connect(this->wholeBrainSurfaceRightMenu, &QMenu::triggered, - this, &BrainBrowserWindowToolBar::wholeBrainSurfaceRightMenuTriggered); - this->wholeBrainSurfaceRightMenu->setObjectName(objectNamePrefix - + "SelectRightSurfaceMenu"); - this->wholeBrainSurfaceRightMenu->setToolTip("Select all view right surface"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceRightMenu, - "Select all view right surface"); - - /* - * Cerebellum - */ - this->wholeBrainSurfaceCerebellumCheckBox = new QCheckBox(" "); - WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceCerebellumCheckBox, - "Enable/Disable display of the cerebellum surface"); - QObject::connect(this->wholeBrainSurfaceCerebellumCheckBox, SIGNAL(stateChanged(int)), - this, SLOT(wholeBrainSurfaceCerebellumCheckBoxStateChanged(int))); - this->wholeBrainSurfaceCerebellumCheckBox->setObjectName(objectNamePrefix - + "EnableCerebellum"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceCerebellumCheckBox, - "Enable all view cerebellum"); - - QToolButton* wholeBrainCerebellumSurfaceToolButton = new QToolButton(); - QAction* cerebellumSurfaceAction = WuQtUtilities::createAction("Cerebellum", - "Select the whole brain cerebellum surface", - wholeBrainCerebellumSurfaceToolButton, - this, - SLOT(wholeBrainSurfaceCerebellumToolButtonTriggered(bool))); - WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainCerebellumSurfaceToolButton); - wholeBrainCerebellumSurfaceToolButton->setDefaultAction(cerebellumSurfaceAction); -// cerebellumSurfaceAction->setObjectName(objectNamePrefix -// + "SurfaceCerebellum"); -// macroManager->addMacroSupportToObject(cerebellumSurfaceAction, -// "Select all view cerebellum surface"); - - /* - * Cerebellum menu is displayed when tool button is clicked - */ - this->wholeBrainSurfaceCerebellumMenu = new QMenu(wholeBrainCerebellumSurfaceToolButton); - QObject::connect(this->wholeBrainSurfaceCerebellumMenu, &QMenu::triggered, - this, &BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumMenuTriggered); - this->wholeBrainSurfaceCerebellumMenu->setObjectName(objectNamePrefix - + "SelectCerebellumSurfaceMenu"); - this->wholeBrainSurfaceCerebellumMenu->setToolTip("Select all view cerebellum surface"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceCerebellumMenu, - "Select all view cerebellum surface"); - - /* - * Left/Right separation - */ - const int separationSpinngerWidth = 48; - this->wholeBrainSurfaceSeparationLeftRightSpinBox = WuQFactory::newDoubleSpinBox(); - this->wholeBrainSurfaceSeparationLeftRightSpinBox->setDecimals(0); - this->wholeBrainSurfaceSeparationLeftRightSpinBox->setFixedWidth(separationSpinngerWidth); - this->wholeBrainSurfaceSeparationLeftRightSpinBox->setMinimum(-100000.0); - this->wholeBrainSurfaceSeparationLeftRightSpinBox->setMaximum(100000.0); - WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceSeparationLeftRightSpinBox, - "Adjust the separation of the left and right cortical surfaces"); - QObject::connect(this->wholeBrainSurfaceSeparationLeftRightSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double))); - this->wholeBrainSurfaceSeparationLeftRightSpinBox->setObjectName(objectNamePrefix - + "LeftRightSeparation"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationLeftRightSpinBox, - "Set all view left/right separation"); - - /* - * Cerebellum separation - */ - this->wholeBrainSurfaceSeparationCerebellumSpinBox = WuQFactory::newDoubleSpinBox(); - this->wholeBrainSurfaceSeparationCerebellumSpinBox->setDecimals(0); - this->wholeBrainSurfaceSeparationCerebellumSpinBox->setFixedWidth(separationSpinngerWidth); - this->wholeBrainSurfaceSeparationCerebellumSpinBox->setMinimum(-100000.0); - this->wholeBrainSurfaceSeparationCerebellumSpinBox->setMaximum(100000.0); - WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceSeparationCerebellumSpinBox, - "Adjust the separation of the cerebellum from the left and right cortical surfaces"); - QObject::connect(this->wholeBrainSurfaceSeparationCerebellumSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double))); - this->wholeBrainSurfaceSeparationCerebellumSpinBox->setObjectName(objectNamePrefix - + "CortexCerebellumSeparation"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationCerebellumSpinBox, - "Set all view cerebral/cerebellum separation"); - - this->wholeBrainSurfaceMatchCheckBox = new QCheckBox("Match"); - WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceMatchCheckBox, - "Match position and size of all surfaces to primary anatomical. Useful for " - "animation (surface interpolation) and recording movies."); - QObject::connect(this->wholeBrainSurfaceMatchCheckBox, &QCheckBox::clicked, - this, &BrainBrowserWindowToolBar::wholeBrainSurfaceMatchCheckBoxClicked); - this->wholeBrainSurfaceMatchCheckBox->setObjectName(objectNamePrefix - + "MatchSurface"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceMatchCheckBox, - "Match position and size of all surfaces to primary anatomical"); - - wholeBrainLeftSurfaceToolButton->setText("L"); - wholeBrainRightSurfaceToolButton->setText("R"); - wholeBrainCerebellumSurfaceToolButton->setText("C"); - - QGridLayout* gridLayout = new QGridLayout(); - gridLayout->setVerticalSpacing(2); - gridLayout->setHorizontalSpacing(2); - gridLayout->addWidget(this->wholeBrainSurfaceTypeComboBox, 0, 0, 1, 6); - gridLayout->addWidget(this->wholeBrainSurfaceLeftCheckBox, 1, 0); - gridLayout->addWidget(wholeBrainLeftSurfaceToolButton, 1, 1); - gridLayout->addWidget(this->wholeBrainSurfaceRightCheckBox, 2, 0); - gridLayout->addWidget(wholeBrainRightSurfaceToolButton, 2, 1); - gridLayout->addWidget(this->wholeBrainSurfaceCerebellumCheckBox, 3, 0); - gridLayout->addWidget(wholeBrainCerebellumSurfaceToolButton, 3, 1); - gridLayout->addWidget(this->wholeBrainSurfaceSeparationLeftRightSpinBox, 1, 2, 2, 1); - gridLayout->addWidget(this->wholeBrainSurfaceSeparationCerebellumSpinBox, 3, 2); - gridLayout->addWidget(this->wholeBrainSurfaceMatchCheckBox, 4, 0, 1, 6); - - QWidget* widget = new QWidget(); - QVBoxLayout* layout = new QVBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); - layout->addLayout(gridLayout); - - this->wholeBrainSurfaceOptionsWidgetGroup = new WuQWidgetObjectGroup(this); - this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceTypeComboBox); - this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceLeftCheckBox); - this->wholeBrainSurfaceOptionsWidgetGroup->add(wholeBrainLeftSurfaceToolButton); - this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceRightCheckBox); - this->wholeBrainSurfaceOptionsWidgetGroup->add(wholeBrainRightSurfaceToolButton); - this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceCerebellumCheckBox); - this->wholeBrainSurfaceOptionsWidgetGroup->add(wholeBrainCerebellumSurfaceToolButton); - this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceSeparationLeftRightSpinBox); - this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceSeparationCerebellumSpinBox); - this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceMatchCheckBox); + m_orientationToolBarComponent->updateContent(browserTabContent); +} + +/** + * Create the whole brain surface options widget. + * + * @return The whole brain surface options widget. + */ +QWidget* +BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() +{ + m_allSurfaceToolBarComponent = new BrainBrowserWindowToolBarAllSurface(m_objectNamePrefix, + this); QWidget* w = this->createToolWidget("Surface Viewing", - widget, + m_allSurfaceToolBarComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 0); @@ -2832,46 +2338,7 @@ return; } - ModelWholeBrain* wholeBrainModel = browserTabContent->getDisplayedWholeBrainModel(); - if (wholeBrainModel != NULL) { - const int32_t tabNumber = browserTabContent->getTabNumber(); - - this->wholeBrainSurfaceOptionsWidgetGroup->blockAllSignals(true); - - std::vector availableSurfaceTypes; - wholeBrainModel->getAvailableSurfaceTypes(availableSurfaceTypes); - - const SurfaceTypeEnum::Enum selectedSurfaceType = wholeBrainModel->getSelectedSurfaceType(tabNumber); - - int32_t defaultIndex = 0; - this->wholeBrainSurfaceTypeComboBox->clear(); - int32_t numSurfaceTypes = static_cast(availableSurfaceTypes.size()); - for (int32_t i = 0; i < numSurfaceTypes; i++) { - const SurfaceTypeEnum::Enum st = availableSurfaceTypes[i]; - if (st == selectedSurfaceType) { - defaultIndex = this->wholeBrainSurfaceTypeComboBox->count(); - } - const AString name = SurfaceTypeEnum::toGuiName(st); - const int integerCode = SurfaceTypeEnum::toIntegerCode(st); - this->wholeBrainSurfaceTypeComboBox->addItem(name, - integerCode); - } - if (defaultIndex < this->wholeBrainSurfaceTypeComboBox->count()) { - this->wholeBrainSurfaceTypeComboBox->setCurrentIndex(defaultIndex); - } - - this->wholeBrainSurfaceLeftCheckBox->setChecked(browserTabContent->isWholeBrainLeftEnabled()); - this->wholeBrainSurfaceRightCheckBox->setChecked(browserTabContent->isWholeBrainRightEnabled()); - this->wholeBrainSurfaceCerebellumCheckBox->setChecked(browserTabContent->isWholeBrainCerebellumEnabled()); - - updateAllWholeBrainSurfaceMenus(); - - this->wholeBrainSurfaceSeparationLeftRightSpinBox->setValue(browserTabContent->getWholeBrainLeftRightSeparation()); - this->wholeBrainSurfaceSeparationCerebellumSpinBox->setValue(browserTabContent->getWholeBrainCerebellumSeparation()); - this->wholeBrainSurfaceMatchCheckBox->setChecked(wholeBrainModel->getBrain()->isSurfaceMatchingToAnatomical()); - - this->wholeBrainSurfaceOptionsWidgetGroup->blockAllSignals(false); - } + m_allSurfaceToolBarComponent->updateContent(browserTabContent); } /** @@ -2919,79 +2386,66 @@ /* * Annotations */ - this->modeInputModeAnnotationsAction = WuQtUtilities::createAction("Annotate", - "Perform annotate operations with mouse", - this); - this->modeInputModeAnnotationsAction->setCheckable(true); - QToolButton* inputModeAnnotationsToolButton = new QToolButton(); - inputModeAnnotationsToolButton->setDefaultAction(this->modeInputModeAnnotationsAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeAnnotationsToolButton); - this->modeInputModeAnnotationsAction->setObjectName(m_objectNamePrefix + this->modeInputModeAnnotationsRadioButton = new QRadioButton("Annotate"); + this->modeInputModeAnnotationsRadioButton->setToolTip("Perform annotate operations with mouse"); + this->modeInputModeAnnotationsRadioButton->setObjectName(m_objectNamePrefix + ":Mode:Annotate"); - /* * Borders */ - this->modeInputModeBordersAction = WuQtUtilities::createAction("Border", - "Perform border operations with mouse", - this); - this->modeInputModeBordersAction->setCheckable(true); - QToolButton* inputModeBordersToolButton = new QToolButton(); - inputModeBordersToolButton->setDefaultAction(this->modeInputModeBordersAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeBordersToolButton); - this->modeInputModeBordersAction->setObjectName(m_objectNamePrefix + this->modeInputModeBordersRadioButton = new QRadioButton("Edit Borders"); + this->modeInputModeBordersRadioButton->setToolTip("Perform border operations with mouse"); + this->modeInputModeBordersRadioButton->setObjectName(m_objectNamePrefix + ":Mode:Border"); /* * Foci */ - this->modeInputModeFociAction = WuQtUtilities::createAction("Foci", - "Perform foci operations with mouse", - this); - this->modeInputModeFociAction->setCheckable(true); - QToolButton* inputModeFociToolButton = new QToolButton(); - inputModeFociToolButton->setDefaultAction(this->modeInputModeFociAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeFociToolButton); - this->modeInputModeFociAction->setObjectName(m_objectNamePrefix + this->modeInputModeFociRadioButton = new QRadioButton("Edit Foci"); + this->modeInputModeFociRadioButton->setToolTip("Perform foci operations with mouse"); + this->modeInputModeFociRadioButton->setObjectName(m_objectNamePrefix + ":Mode:Foci"); /* * Image */ - const bool showImageButtonFlag = false; - this->modeInputModeImageAction = NULL; - QToolButton* inputModeImageToolButton = NULL; + const bool showImageButtonFlag(false); + this->modeInputModeImageRadioButton = NULL; if (showImageButtonFlag) { - modeInputModeImageAction = WuQtUtilities::createAction("Image", - "Edit Image Control Points", - this); - this->modeInputModeImageAction->setCheckable(true); - inputModeImageToolButton = new QToolButton(); - inputModeImageToolButton->setDefaultAction(this->modeInputModeImageAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeImageToolButton); - this->modeInputModeImageAction->setObjectName(m_objectNamePrefix + this->modeInputModeImageRadioButton = new QRadioButton("Image"); + this->modeInputModeImageRadioButton->setToolTip("Edit Image Control Points"); + this->modeInputModeImageRadioButton->setObjectName(m_objectNamePrefix + ":Mode:Image"); } /* + * Tile tabs manual layout editing + */ + const AString tileToolTip("" + "Edit Tile Tabs Manual Configuration

" + "This button is enabled when:
" + " * Tile Tabs is enabled (View Menu -> Enter Tile Tabs)
" + " * Selected Tile Tab Active Configuration Type is Manual (View Menu -> Edit Tile Tabs Configuration)" + ""); + this->modeInputModeTileTabsManualLayoutRadioButton = new QRadioButton("Tile Layout"); + this->modeInputModeTileTabsManualLayoutRadioButton->setToolTip(tileToolTip); + this->modeInputModeTileTabsManualLayoutRadioButton->setObjectName(m_objectNamePrefix + + "Mode:TileTabsManualLayout"); + + /* * Volume Edit */ - this->modeInputVolumeEditAction = WuQtUtilities::createAction("Volume", - "Edit volume voxels", - this); - this->modeInputVolumeEditAction->setCheckable(true); - QToolButton* inputModeVolumeEditButton = new QToolButton(); - inputModeVolumeEditButton->setDefaultAction(this->modeInputVolumeEditAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeVolumeEditButton); - this->modeInputVolumeEditAction->setObjectName(m_objectNamePrefix + this->modeInputVolumeEditRadioButton = new QRadioButton("Edit Voxels"); + this->modeInputVolumeEditRadioButton->setToolTip("Edit volume voxels"); + this->modeInputVolumeEditRadioButton->setObjectName(m_objectNamePrefix + ":Mode:Volume"); /* * View Mode */ - this->modeInputModeViewAction = WuQtUtilities::createAction("View", - "Perform viewing operations with mouse\n" + this->modeInputModeViewRadioButton = new QRadioButton("View"); + this->modeInputModeViewRadioButton->setToolTip("Perform viewing operations with mouse\n" "\n" "Identify: Click left mouse button (might cause rotation)\n" #ifdef CARET_OS_MACOSX @@ -3000,100 +2454,72 @@ "Identify: Click left mouse button while keyboard shift and controls keys are down (prevents rotation)\n" #endif // CARET_OS_MACOSX "Pan: Move mouse with left mouse button down and keyboard shift key down\n" - "Rotate: Move mouse with left mouse button down\n" + "Rotate: Move mouse with left mouse button down (Except Volume Orthogonal)\n" + "Slice Scolling: Move mouse with left mouse button down (Volume Orthogonal Only)\n" #ifdef CARET_OS_MACOSX - "Zoom: Move mouse with left mouse button down and keyboard apple key down", + "Zoom: Move mouse with left mouse button down and keyboard apple key down" #else // CARET_OS_MACOSX - "Zoom: Move mouse with left mouse button down and keyboard control key down", + "Zoom: Move mouse with left mouse button down and keyboard control key down" #endif // CARET_OS_MACOSX - this); - this->modeInputModeViewAction->setCheckable(true); - QToolButton* inputModeViewToolButton = new QToolButton(); - inputModeViewToolButton->setDefaultAction(this->modeInputModeViewAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeViewToolButton); - this->modeInputModeViewAction->setObjectName(m_objectNamePrefix + ); + this->modeInputModeViewRadioButton->setObjectName(m_objectNamePrefix + ":Mode:View"); - WuQtUtilities::matchWidgetWidths(inputModeAnnotationsToolButton, - inputModeBordersToolButton, - inputModeViewToolButton, - inputModeVolumeEditButton); - inputModeFociToolButton->setSizePolicy(QSizePolicy::Preferred, - inputModeFociToolButton->sizePolicy().verticalPolicy()); - if (inputModeImageToolButton != NULL) { - inputModeImageToolButton->setSizePolicy(QSizePolicy::Preferred, - inputModeImageToolButton->sizePolicy().verticalPolicy()); - } - - /* - * Layout for input modes - */ - QWidget* inputModeWidget = new QWidget(); - QGridLayout* inputModeLayout = new QGridLayout(inputModeWidget); - int modeRow = 0; - WuQtUtilities::setLayoutSpacingAndMargins(inputModeLayout, 2, 2); - inputModeLayout->addWidget(inputModeAnnotationsToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); - modeRow++; - inputModeLayout->addWidget(inputModeBordersToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); - modeRow++; - if (inputModeImageToolButton != NULL) { - inputModeLayout->addWidget(inputModeFociToolButton, modeRow, 0); - inputModeLayout->addWidget(inputModeImageToolButton, modeRow, 1); - } - else { - inputModeLayout->addWidget(inputModeFociToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); - } - modeRow++; - inputModeLayout->addWidget(inputModeViewToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); - modeRow++; - inputModeLayout->addWidget(inputModeVolumeEditButton, modeRow, 0, 1, 2, Qt::AlignHCenter); - modeRow++; - - this->modeInputModeActionGroup = new QActionGroup(this); - this->modeInputModeActionGroup->addAction(this->modeInputModeAnnotationsAction); - this->modeInputModeActionGroup->addAction(this->modeInputModeBordersAction); - this->modeInputModeActionGroup->addAction(this->modeInputModeFociAction); - if (this->modeInputModeImageAction != NULL) { - this->modeInputModeActionGroup->addAction(this->modeInputModeImageAction); - } - this->modeInputModeActionGroup->addAction(this->modeInputModeViewAction); - this->modeInputModeActionGroup->addAction(this->modeInputVolumeEditAction); - QObject::connect(this->modeInputModeActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(modeInputModeActionTriggered(QAction*))); - this->modeInputModeActionGroup->setExclusive(true); + this->modeInputModeRadioButtonGroup = new QButtonGroup(this); + this->modeInputModeRadioButtonGroup->addButton(this->modeInputModeAnnotationsRadioButton); + this->modeInputModeRadioButtonGroup->addButton(this->modeInputModeBordersRadioButton); + this->modeInputModeRadioButtonGroup->addButton(this->modeInputModeFociRadioButton); + if (this->modeInputModeImageRadioButton != NULL) { + this->modeInputModeRadioButtonGroup->addButton(this->modeInputModeImageRadioButton); + } + this->modeInputModeRadioButtonGroup->addButton(this->modeInputModeViewRadioButton); + this->modeInputModeRadioButtonGroup->addButton(this->modeInputModeTileTabsManualLayoutRadioButton); + this->modeInputModeRadioButtonGroup->addButton(this->modeInputVolumeEditRadioButton); + QObject::connect(this->modeInputModeRadioButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &BrainBrowserWindowToolBar::modeInputModeRadioButtonClicked); + this->modeInputModeRadioButtonGroup->setExclusive(true); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeAnnotationsAction, + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeAnnotationsRadioButton, "Select annotate mode"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeBordersAction, + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeBordersRadioButton, "Select border mode"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeFociAction, + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeFociRadioButton, "Select foci mode"); - if (modeInputModeImageAction != NULL) { - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeImageAction, + if (modeInputModeImageRadioButton != NULL) { + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeImageRadioButton, "Select image mode"); } - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeViewAction, + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeTileTabsManualLayoutRadioButton, + "Select Tile Tabs Manual Layout Editing"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeViewRadioButton, "Select view mode"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputVolumeEditAction, + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputVolumeEditRadioButton, "Select volume mode"); -// this->modeInputModeActionGroup->setObjectName(m_objectNamePrefix -// + ":Mode_Action_Group"); -// WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeActionGroup, -// "Selects Mode for Mouse Operations"); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); - layout->addWidget(inputModeWidget, 0, Qt::AlignHCenter); - layout->addStretch(); +#ifdef CARET_OS_MACOSX + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); +#else + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 2); +#endif + layout->addWidget(this->modeInputModeViewRadioButton); + layout->addWidget(this->modeInputModeTileTabsManualLayoutRadioButton); + layout->addWidget(this->modeInputModeAnnotationsRadioButton); + layout->addWidget(this->modeInputModeBordersRadioButton); + layout->addWidget(this->modeInputModeFociRadioButton); + if (this->modeInputModeImageRadioButton != NULL) { + layout->addWidget(this->modeInputModeImageRadioButton); + } + layout->addWidget(this->modeInputVolumeEditRadioButton); this->modeWidgetGroup = new WuQWidgetObjectGroup(this); - this->modeWidgetGroup->add(this->modeInputModeActionGroup); + this->modeWidgetGroup->add(this->modeInputModeRadioButtonGroup); QWidget* w = this->createToolWidget("Mode", widget, - WIDGET_PLACEMENT_LEFT, - WIDGET_PLACEMENT_NONE, + WIDGET_PLACEMENT_RIGHT, + WIDGET_PLACEMENT_TOP, 0); return w; } @@ -3104,24 +2530,21 @@ * Action of tool button that was clicked. */ void -BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) +BrainBrowserWindowToolBar::modeInputModeRadioButtonClicked(QAbstractButton* button) { BrowserTabContent* tabContent = this->getTabContentFromSelectedTab(); if (tabContent == NULL) { return; } - EventGetOrSetUserInputModeProcessor getInputModeEvent(this->browserWindowIndex); - EventManager::get()->sendEvent(getInputModeEvent.getPointer()); - const UserInputModeEnum::Enum currentMode = getInputModeEvent.getUserInputMode(); - - UserInputModeEnum::Enum inputMode = UserInputModeEnum::INVALID; - - if (action == this->modeInputModeAnnotationsAction) { - if (currentMode != UserInputModeEnum::ANNOTATIONS) { - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); - CaretAssert(bbw); - if ( ! bbw->changeInputModeToAnnotationsWarningDialog()) { + const UserInputModeEnum::Enum currentMode = this->selectedUserInputProcessor->getUserInputMode(); + + UserInputModeEnum::Enum inputMode = UserInputModeEnum::Enum::INVALID; + + if (button == this->modeInputModeAnnotationsRadioButton) { + if (currentMode != UserInputModeEnum::Enum::ANNOTATIONS) { + CaretAssert(m_parentBrainBrowserWindow); + if ( ! m_parentBrainBrowserWindow->changeInputModeToAnnotationsWarningDialog()) { /* * Since mode is rejected, need to update toolbar */ @@ -3129,10 +2552,10 @@ return; } } - inputMode = UserInputModeEnum::ANNOTATIONS; + inputMode = UserInputModeEnum::Enum::ANNOTATIONS; } - else if (action == this->modeInputModeBordersAction) { - inputMode = UserInputModeEnum::BORDERS; + else if (button == this->modeInputModeBordersRadioButton) { + inputMode = UserInputModeEnum::Enum::BORDERS; /* * If borders are not displayed, display them @@ -3145,22 +2568,33 @@ dpb->setDisplayed(displayGroup, browserTabIndex, true); - this->updateUserInterface(); - this->updateGraphicsWindow(); } } - else if (action == this->modeInputModeFociAction) { - inputMode = UserInputModeEnum::FOCI; + else if (button == this->modeInputModeFociRadioButton) { + inputMode = UserInputModeEnum::Enum::FOCI; } - else if ((action == this->modeInputModeImageAction) - && (this->modeInputModeImageAction != NULL)) { - inputMode = UserInputModeEnum::IMAGE; + else if ((button == this->modeInputModeImageRadioButton) + && (this->modeInputModeImageRadioButton != NULL)) { + inputMode = UserInputModeEnum::Enum::IMAGE; + } + else if (button == this->modeInputModeTileTabsManualLayoutRadioButton) { + inputMode = UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING; + + CaretAssert(m_parentBrainBrowserWindow); + BrowserWindowContent* browserWindowContent = m_parentBrainBrowserWindow->getBrowerWindowContent(); + CaretAssert(browserWindowContent); + if ( ! browserWindowContent->isTileTabsEnabled()) { + browserWindowContent->setTileTabsEnabled(true); + } + if (browserWindowContent->getTileTabsConfigurationMode() != TileTabsLayoutConfigurationTypeEnum::MANUAL) { + browserWindowContent->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::MANUAL); + } } - else if (action == this->modeInputVolumeEditAction) { - inputMode = UserInputModeEnum::VOLUME_EDIT; + else if (button == this->modeInputVolumeEditRadioButton) { + inputMode = UserInputModeEnum::Enum::VOLUME_EDIT; } - else if (action == this->modeInputModeViewAction) { - inputMode = UserInputModeEnum::VIEW; + else if (button == this->modeInputModeViewRadioButton) { + inputMode = UserInputModeEnum::Enum::VIEW; } else { CaretAssertMessage(0, "Tools input mode action is invalid, new action added???"); @@ -3168,8 +2602,9 @@ EventManager::get()->sendEvent(EventGetOrSetUserInputModeProcessor(this->browserWindowIndex, inputMode).getPointer()); - EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); - + this->updateUserInterface(); + this->updateGraphicsWindow(); + this->updateModeWidget(tabContent); this->updateDisplayedModeUserInputWidget(); } @@ -3187,37 +2622,39 @@ return; } + CaretAssert(m_parentBrainBrowserWindow); + this->modeWidgetGroup->blockAllSignals(true); - EventGetOrSetUserInputModeProcessor getInputModeEvent(this->browserWindowIndex); - EventManager::get()->sendEvent(getInputModeEvent.getPointer()); - - switch (getInputModeEvent.getUserInputMode()) { - case UserInputModeEnum::INVALID: + switch (this->selectedUserInputProcessor->getUserInputMode()) { + case UserInputModeEnum::Enum::INVALID: /* may get here when program is exiting and widgets are being destroyed */ break; - case UserInputModeEnum::ANNOTATIONS: - this->modeInputModeAnnotationsAction->setChecked(true); + case UserInputModeEnum::Enum::ANNOTATIONS: + this->modeInputModeAnnotationsRadioButton->setChecked(true); break; - case UserInputModeEnum::BORDERS: - this->modeInputModeBordersAction->setChecked(true); + case UserInputModeEnum::Enum::BORDERS: + this->modeInputModeBordersRadioButton->setChecked(true); break; - case UserInputModeEnum::FOCI: - this->modeInputModeFociAction->setChecked(true); + case UserInputModeEnum::Enum::FOCI: + this->modeInputModeFociRadioButton->setChecked(true); break; - case UserInputModeEnum::IMAGE: - if (this->modeInputModeImageAction != NULL) { - this->modeInputModeImageAction->setChecked(true); + case UserInputModeEnum::Enum::IMAGE: + if (this->modeInputModeImageRadioButton != NULL) { + this->modeInputModeImageRadioButton->setChecked(true); } break; - case UserInputModeEnum::VOLUME_EDIT: - this->modeInputVolumeEditAction->setChecked(true); + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + this->modeInputModeTileTabsManualLayoutRadioButton->setChecked(true); + break; + case UserInputModeEnum::Enum::VOLUME_EDIT: + this->modeInputVolumeEditRadioButton->setChecked(true); break; - case UserInputModeEnum::VIEW: - this->modeInputModeViewAction->setChecked(true); + case UserInputModeEnum::Enum::VIEW: + this->modeInputModeViewRadioButton->setChecked(true); break; } - + this->modeWidgetGroup->blockAllSignals(false); this->updateDisplayedModeUserInputWidget(); @@ -3226,65 +2663,22 @@ void BrainBrowserWindowToolBar::updateDisplayedModeUserInputWidget() { - EventGetOrSetUserInputModeProcessor getInputModeEvent(this->browserWindowIndex); - EventManager::get()->sendEvent(getInputModeEvent.getPointer()); - - UserInputModeAbstract* userInputProcessor = getInputModeEvent.getUserInputProcessor(); - QWidget* userInputWidget = userInputProcessor->getWidgetForToolBar(); - - /* - * If a widget is display and needs to change, - * remove the old widget. - */ - if (this->userInputControlsWidgetActiveInputWidget != NULL) { - if (userInputWidget != this->userInputControlsWidgetActiveInputWidget) { + switch (this->selectedUserInputProcessor->getUserInputMode()) { + case UserInputModeEnum::Enum::ANNOTATIONS: + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + break; + case UserInputModeEnum::Enum::BORDERS: + case UserInputModeEnum::Enum::FOCI: + case UserInputModeEnum::Enum::IMAGE: + case UserInputModeEnum::Enum::INVALID: + case UserInputModeEnum::Enum::VIEW: + case UserInputModeEnum::Enum::VOLUME_EDIT: /* - * Remove the current input widget: - * (1) Set its visibility to false - * (2) Remove the widget from the toolbar's layout - * (3) Set its parent to NULL. - * - * Why is the parent set to NULL? - * - * When a widget is put into a layout, the widget is put into - * a QWidgetItem (subclass of QLayoutItem). - * - * QLayout::removeWidget() will delete the QWidgetItem but - * it does not reset the parent for the widget that was in - * QWidgetItem. So the user will need to delete the widget - * unless it is placed into a layout. - * - * After removing the widget, set the widget's parent to NULL. - * As a result, when the input receiver (owner of the widget) - * is deleted, it can examine the parent, and, if the parent - * is NULL, it can delete the widget preventing a memory link - * and a possible crash. + * Delete all selected annotations and update graphics and UI. */ - this->userInputControlsWidgetActiveInputWidget->setVisible(false); - this->userInputControlsWidgetLayout->removeWidget(this->userInputControlsWidgetActiveInputWidget); - this->userInputControlsWidgetActiveInputWidget->setParent(NULL); - this->userInputControlsWidgetActiveInputWidget = NULL; - } - } - if (this->userInputControlsWidgetActiveInputWidget == NULL) { - if (userInputWidget != NULL) { - this->userInputControlsWidgetActiveInputWidget = userInputWidget; - this->userInputControlsWidgetActiveInputWidget->setVisible(true); - this->userInputControlsWidgetLayout->addWidget(this->userInputControlsWidgetActiveInputWidget); - this->userInputControlsWidget->setVisible(true); - this->userInputControlsWidgetLayout->update(); - } - else { - this->userInputControlsWidget->setVisible(false); - } - } - - if (userInputProcessor->getUserInputMode() != UserInputModeEnum::ANNOTATIONS) { - /* - * Delete all selected annotations and update graphics and UI. - */ - AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); - annotationManager->deselectAllAnnotationsForEditing(this->browserWindowIndex); + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + annotationManager->deselectAllAnnotationsForEditing(this->browserWindowIndex); + break; } } @@ -3304,7 +2698,7 @@ this, m_objectNamePrefix); - QWidget* w = this->createToolWidget("Tab", + QWidget* w = this->createToolWidget("Misc", m_tabOptionsComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, @@ -3321,7 +2715,7 @@ void BrainBrowserWindowToolBar::updateTabOptionsWidget(BrowserTabContent* browserTabContent) { - if (this->windowWidget->isHidden()) { + if (this->tabMiscWidget->isHidden()) { return; } @@ -3366,42 +2760,6 @@ } /** - * Create the chart two title widget. - * - * @return - * Widget containing the chart title. - */ -QWidget* -BrainBrowserWindowToolBar::createChartTwoTitleWidget() -{ - m_chartTwoTitleToolBarComponent = new BrainBrowserWindowToolBarChartTwoTitle(this, - m_objectNamePrefix); - QWidget* w = this->createToolWidget("Chart Title", - m_chartTwoTitleToolBarComponent, - WIDGET_PLACEMENT_LEFT, - WIDGET_PLACEMENT_TOP, - 100); - w->setVisible(false); - return w; -} -/** - * Update the chart title widget. - * - * @param browserTabContent - * The active tab content. - */ -void -BrainBrowserWindowToolBar::updateChartTwoTitleWidget(BrowserTabContent* browserTabContent) -{ - if (this->chartTwoTitleWidget->isHidden()) { - return; - } - - m_chartTwoTitleToolBarComponent->updateContent(browserTabContent); -} - - -/** * Create the chart type two widget. * * @return @@ -3579,18 +2937,18 @@ } /** - * Create the chart two axes widget. + * Create the chart two oriented axes widget. * * @return - * Widget containing the chart two axes. + * Widget containing the chart two oriented axes. */ QWidget* -BrainBrowserWindowToolBar::createChartTwoAxesWidget() +BrainBrowserWindowToolBar::createChartTwoOrientedAxisWidget() { - this->m_chartTwoAxesToolBarComponent = new BrainBrowserWindowToolBarChartTwoAxes(this, - m_objectNamePrefix); + this->m_chartTwoOrientedAxesToolBarComponent = new BrainBrowserWindowToolBarChartTwoOrientedAxes(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart Axes", - this->m_chartTwoAxesToolBarComponent, + this->m_chartTwoOrientedAxesToolBarComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 100); @@ -3599,18 +2957,18 @@ } /** - * Update the chart axes widget. + * Update the chart oriented axes widget. * * @param browserTabContent * The active model display (may be NULL). */ void -BrainBrowserWindowToolBar::updateChartTwoAxesWidget(BrowserTabContent* browserTabContent) +BrainBrowserWindowToolBar::updateChartTwoOrientedAxesWidget(BrowserTabContent* browserTabContent) { - if (this->chartTwoAxesWidget->isHidden()) { + if (this->chartTwoOrientedAxesWidget->isHidden()) { return; } - m_chartTwoAxesToolBarComponent->updateContent(browserTabContent); + m_chartTwoOrientedAxesToolBarComponent->updateContent(browserTabContent); } /** @@ -3621,38 +2979,10 @@ QWidget* BrainBrowserWindowToolBar::createSingleSurfaceOptionsWidget() { - QLabel* structureSurfaceLabel = new QLabel("Brain Structure and Surface: "); - - /* - * Note: Macro support is in StructureSurfaceSelectionControl - */ - this->surfaceSurfaceSelectionControl = new StructureSurfaceSelectionControl(false, - m_objectNamePrefix - + ":Surface", - "surface view", - this); - QObject::connect(this->surfaceSurfaceSelectionControl, - SIGNAL(selectionChanged(const StructureEnum::Enum, - ModelSurface*)), - this, - SLOT(surfaceSelectionControlChanged(const StructureEnum::Enum, - ModelSurface*))); - - this->surfaceSurfaceSelectionControl->setMinimumWidth(150); - this->surfaceSurfaceSelectionControl->setMaximumWidth(1200); - - QWidget* widget = new QWidget(); - QVBoxLayout* layout = new QVBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); - layout->addWidget(structureSurfaceLabel); - layout->addWidget(this->surfaceSurfaceSelectionControl); - layout->addStretch(); - - this->singleSurfaceSelectionWidgetGroup = new WuQWidgetObjectGroup(this); - this->singleSurfaceSelectionWidgetGroup->add(this->surfaceSurfaceSelectionControl); - - QWidget* w = this->createToolWidget("Selection", - widget, + m_surfaceToolBarComponent = new BrainBrowserWindowToolBarSurface(m_objectNamePrefix, + this); + QWidget* w = this->createToolWidget("Selection", + m_surfaceToolBarComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 100); @@ -3672,11 +3002,7 @@ return; } - this->singleSurfaceSelectionWidgetGroup->blockAllSignals(true); - - this->surfaceSurfaceSelectionControl->updateControl(browserTabContent->getSurfaceModelSelector()); - - this->singleSurfaceSelectionWidgetGroup->blockAllSignals(false); + m_surfaceToolBarComponent->updateContent(browserTabContent); } /** @@ -3713,40 +3039,6 @@ } /** - * @return Create and return the clipping options component. - */ -QWidget* -BrainBrowserWindowToolBar::createClippingOptionsWidget() -{ - m_clippingToolBarComponent = new BrainBrowserWindowToolBarClipping(this->browserWindowIndex, - this, - m_objectNamePrefix); - QWidget* w = this->createToolWidget("Clipping", - m_clippingToolBarComponent, - WIDGET_PLACEMENT_LEFT, - WIDGET_PLACEMENT_TOP, - 100); - w->setVisible(false); - return w; -} - -/** - * Update the clipping options widget. - * - * @param browserTabContent - * The active browser tab. - */ -void -BrainBrowserWindowToolBar::updateClippingOptionsWidget(BrowserTabContent* browserTabContent) -{ - if (m_clippingOptionsWidget->isHidden()) { - return; - } - - m_clippingToolBarComponent->updateContent(browserTabContent); -} - -/** * Create the volume montage widget. * * @return The volume montage widget. @@ -3819,77 +3111,134 @@ } /** - * Create a tool widget which is a group of widgets with + * Create a tool widget which is a group of widgets with * a descriptive label added. * * @param name - * Name for the descriptive label. For a multi-line label, + * Name for the descriptive label. For a multi-line label, * separate the lines with an HTML "
" tag. * @param childWidget * Child widget that is in the tool widget. * @param verticalBarPlacement - * Where to place a vertical bar. Values other than right or + * Where to place a vertical bar. Values other than right or * left are ignored in which case no vertical bar is displayed. * @param contentPlacement * Where to place widget which must be top or bottom. * @return The tool widget. */ -QWidget* +QWidget* BrainBrowserWindowToolBar::createToolWidget(const QString& name, QWidget* childWidget, const WidgetPlacement verticalBarPlacement, const WidgetPlacement contentPlacement, const int /*horizontalStretching*/) { - QLabel* nameLabel = new QLabel("

" + name + "
"); - nameLabel->setFixedHeight(nameLabel->sizeHint().height()); + QLabel* nameLabel(NULL); + if ( ! name.isEmpty()) { + nameLabel = new QLabel("
" + name + "
"); + nameLabel->setFixedHeight(nameLabel->sizeHint().height()); + } + + int32_t columnWidget(-1); + int32_t columnVerticalBar(-1); + int32_t columnCount(-1); + switch(verticalBarPlacement) { + case WIDGET_PLACEMENT_LEFT: + columnVerticalBar = 0; + columnWidget = 1; + columnCount = 2; + break; + case WIDGET_PLACEMENT_RIGHT: + columnVerticalBar = 1; + columnWidget = 0; + columnCount = 2; + break; + default: + columnWidget = 0; + columnCount = 1; + break; + } + CaretAssert(columnWidget >= 0); + CaretAssert(columnCount >= 1); - QWidget* w = new QWidget(); - QGridLayout* layout = new QGridLayout(w); - layout->setColumnStretch(0, 100); - layout->setColumnStretch(1, 100); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); + int32_t rowWidget(-1); + int32_t rowEmpty(-1); + int32_t rowLabel(-1); + int32_t rowCount(-1); switch (contentPlacement) { case WIDGET_PLACEMENT_BOTTOM: - layout->setRowStretch(0, 100); - layout->setRowStretch(1, 0); - layout->addWidget(childWidget, 1, 0, 1, 2); + rowEmpty = 0; + rowWidget = 1; + if (nameLabel != NULL) { + rowLabel = 2; + rowCount = 3; + } + else { + rowCount = 2; + } break; case WIDGET_PLACEMENT_TOP: - layout->setRowStretch(1, 100); - layout->setRowStretch(0, 0); - layout->addWidget(childWidget, 0, 0, 1, 2); + rowEmpty = 1; + rowWidget = 0; + if (nameLabel != NULL) { + rowLabel = 2; + rowCount = 3; + } + else { + rowCount = 3; + } break; case WIDGET_PLACEMENT_NONE: - layout->setRowStretch(0, 0); - layout->addWidget(childWidget, 0, 0, 1, 2); + rowWidget = 0; + if (nameLabel != NULL) { + rowLabel = 1; + rowCount = 2; + } + else { + rowCount = 1; + } break; default: CaretAssert(0); + break; } - layout->addWidget(nameLabel, 2, 0, 1, 2, Qt::AlignHCenter); + CaretAssert(rowWidget >= 0); + CaretAssert(rowCount >= 1); - const bool addVerticalBarOnLeftSide = (verticalBarPlacement == WIDGET_PLACEMENT_LEFT); - const bool addVerticalBarOnRightSide = (verticalBarPlacement == WIDGET_PLACEMENT_RIGHT); + QWidget* w = new QWidget(); + QGridLayout* layout = new QGridLayout(w); + layout->setContentsMargins(0, 0, 0, 0); + if (columnVerticalBar >= 0) { + layout->setHorizontalSpacing(2); + } + else { + layout->setHorizontalSpacing(0); + } + layout->setVerticalSpacing(0); - if (addVerticalBarOnLeftSide - || addVerticalBarOnRightSide) { - QWidget* w2 = new QWidget(); - QHBoxLayout* horizLayout = new QHBoxLayout(w2); - WuQtUtilities::setLayoutSpacingAndMargins(horizLayout, 0, 0); - if (addVerticalBarOnLeftSide) { - horizLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0); - horizLayout->addSpacing(3); - } - const int widgetStretchFactor = 100; - horizLayout->addWidget(w, widgetStretchFactor); - if (addVerticalBarOnRightSide) { - horizLayout->addSpacing(3); - horizLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0); - } - w = w2; + for (int32_t iRow = 0; iRow < rowCount; iRow++) { + layout->setRowStretch(iRow, 0); } - + if (rowEmpty >= 0) { + layout->setRowStretch(rowEmpty, 100); + } + if (columnVerticalBar >= 0) { + layout->addWidget(WuQtUtilities::createVerticalLineWidget(), + 0, columnVerticalBar, rowCount, 1); + } + + layout->addWidget(childWidget, + rowWidget, columnWidget); + if (nameLabel != NULL) { + CaretAssert(rowLabel >= 0); + layout->addWidget(nameLabel, + rowLabel, 0, 1, columnCount, Qt::AlignHCenter); + } + + int left(1), right(1), bottom(0), top(0); + w->getContentsMargins(NULL, &top, NULL, &bottom); + w->setContentsMargins(left, top, right, bottom); + return w; } @@ -3921,571 +3270,131 @@ EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).addToolBox().getPointer()); } + /** - * Called when a view mode is selected. + * Called when the scene tool button is clicked to show scene dialog */ -void -BrainBrowserWindowToolBar::viewModeRadioButtonClicked(QAbstractButton*) +void +BrainBrowserWindowToolBar::sceneToolButtonClicked() { - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - if (btc == NULL) { - return; - } + GuiManager::get()->getSceneDialogDisplayAction()->trigger(); +} + +/** + * Called when custom view is triggered and displays Custom View Menu. + */ +void +BrainBrowserWindowToolBar::customViewActionTriggered() +{ + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->readCustomViews(); + const std::vector > customViewNameAndComments = prefs->getCustomViewNamesAndComments(); - if (this->viewModeChartOneRadioButton->isChecked()) { - btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_CHART); - } - else if (this->viewModeChartTwoRadioButton->isChecked()) { - btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_CHART_TWO); - } - else if (this->viewModeSurfaceRadioButton->isChecked()) { - btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_SURFACE); - } - else if (this->viewModeSurfaceMontageRadioButton->isChecked()) { - btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE); - } - else if (this->viewModeVolumeRadioButton->isChecked()) { - btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES); - } - else if (this->viewModeWholeBrainRadioButton->isChecked()) { - btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN); + QMenu menu; + + QAction* editAction = menu.addAction("Create and Edit..."); + editAction->setToolTip("Add and delete Custom Views.\n" + "Edit model transformations."); + + const int32_t numViews = static_cast(customViewNameAndComments.size()); + if (numViews > 0) { + menu.addSeparator(); } - else { - btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_INVALID); + for (int32_t i = 0; i < numViews; i++) { + QAction* action = menu.addAction(customViewNameAndComments[i].first); + action->setToolTip(WuQtUtilities::createWordWrappedToolTipText(customViewNameAndComments[i].second)); } - this->updateToolBar(); - this->updateTabName(-1); - this->updateToolBox(); - emit viewedModelChanged(); - this->updateGraphicsWindow(); + QAction* selectedAction = menu.exec(QCursor::pos()); + if (selectedAction != NULL) { + if (selectedAction == editAction) { + CaretAssert(m_parentBrainBrowserWindow); + GuiManager::get()->processShowCustomViewDialog(m_parentBrainBrowserWindow); + } + else { + const AString customViewName = selectedAction->text(); + + ModelTransform modelTransform; + if (prefs->getCustomView(customViewName, modelTransform)) { + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->setTransformationsFromModelTransform(modelTransform); + this->updateGraphicsWindowAndYokedWindows(); + } + } + } } /** - * Called when orientation left or lateral button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationLeftOrLateralToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->leftView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when orientation right or medial button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationRightOrMedialToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->rightView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when orientation anterior button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationAnteriorToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->anteriorView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when orientation posterior button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationPosteriorToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->posteriorView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when orientation dorsal button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationDorsalToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->dorsalView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when orientation ventral button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationVentralToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->ventralView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when orientation reset button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationResetToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->resetView(); - - Model* mdc = btc->getModelForDisplay(); - if (mdc != NULL) { - this->updateVolumeIndicesWidget(btc); - this->updateGraphicsWindowAndYokedWindows(); - } -} - -/** - * Called when orientation lateral/medial button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationLateralMedialToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->leftView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when orientation dorsal/ventral button is pressed. - */ -void -BrainBrowserWindowToolBar::orientationDorsalVentralToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->dorsalView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when orientation anterior/posterior button is pressed. + * Receive events from the event manager. + * + * @param event + * Event sent by event manager. */ void -BrainBrowserWindowToolBar::orientationAnteriorPosteriorToolButtonTriggered(bool /*checked*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->anteriorView(); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when the scene tool button is clicked to show scene dialog - */ -void -BrainBrowserWindowToolBar::sceneToolButtonClicked() -{ - GuiManager::get()->getSceneDialogDisplayAction()->trigger(); -} - -/** - * Called when custom view is triggered and displays Custom View Menu. - */ -void -BrainBrowserWindowToolBar::customViewActionTriggered() +BrainBrowserWindowToolBar::receiveEvent(Event* event) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->readCustomViews(); - const std::vector > customViewNameAndComments = prefs->getCustomViewNamesAndComments(); - - QMenu menu; - - QAction* editAction = menu.addAction("Create and Edit..."); - editAction->setToolTip("Add and delete Custom Views.\n" - "Edit model transformations."); - - const int32_t numViews = static_cast(customViewNameAndComments.size()); - if (numViews > 0) { - menu.addSeparator(); - } - for (int32_t i = 0; i < numViews; i++) { - QAction* action = menu.addAction(customViewNameAndComments[i].first); - action->setToolTip(WuQtUtilities::createWordWrappedToolTipText(customViewNameAndComments[i].second)); - } - - QAction* selectedAction = menu.exec(QCursor::pos()); - if (selectedAction != NULL) { - if (selectedAction == editAction) { - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); - GuiManager::get()->processShowCustomViewDialog(bbw); - } - else { - const AString customViewName = selectedAction->text(); - - ModelTransform modelTransform; - if (prefs->getCustomView(customViewName, modelTransform)) { - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - btc->setTransformationsFromModelTransform(modelTransform); - this->updateGraphicsWindowAndYokedWindows(); + if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_CLOSE_IN_TOOL_BAR) { + EventBrowserTabCloseInToolBar* closeTabInGuiEvent = dynamic_cast(event); + CaretAssert(closeTabInGuiEvent); + + BrowserTabContent* tabToClose = closeTabInGuiEvent->getBrowserTab(); + CaretAssert(tabToClose); + + for (int32_t iTab = 0; iTab < this->tabBar->count(); iTab++) { + if (tabToClose == getTabContentFromTab(iTab)) { + if ((iTab >= 0) + && (iTab < this->tabBar->count())) { + tabClosed(iTab, + RemoveTabMode::CLOSE_TAB_CONTENT_FOR_REOPENING); + } + this->updateGraphicsWindow(); + closeTabInGuiEvent->setEventProcessed(); + break; } } } -} - -/** - * Called when the whole brain surface type combo box is changed. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceTypeComboBoxIndexChanged(int /*indx*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - const int32_t tabIndex = btc->getTabNumber(); - - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - int32_t comboBoxIndex = this->wholeBrainSurfaceTypeComboBox->currentIndex(); - if (comboBoxIndex >= 0) { - const int32_t integerCode = this->wholeBrainSurfaceTypeComboBox->itemData(comboBoxIndex).toInt(); - bool isValid = false; - const SurfaceTypeEnum::Enum surfaceType = SurfaceTypeEnum::fromIntegerCode(integerCode, &isValid); - if (isValid) { - wholeBrainModel->setSelectedSurfaceType(tabIndex, surfaceType); - this->updateVolumeIndicesWidget(btc); /* slices may get deselected */ - this->updateAllWholeBrainSurfaceMenus(); - this->updateGraphicsWindowAndYokedWindows(); - } - } -} - -/** - * Called when whole brain surface left check box is toggled. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceLeftCheckBoxStateChanged(int /*state*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - btc->setWholeBrainLeftEnabled(this->wholeBrainSurfaceLeftCheckBox->isChecked()); - this->updateGraphicsWindowAndYokedWindows(); -} + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE_IN_TOOL_BAR) { + EventBrowserTabDeleteInToolBar* deleteTabInGuiEvent = dynamic_cast(event); + CaretAssert(deleteTabInGuiEvent); -/** - * Update all whole brain surface selection menus - */ -void -BrainBrowserWindowToolBar::updateAllWholeBrainSurfaceMenus() -{ - updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceLeftMenu, - StructureEnum::CORTEX_LEFT); - updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceRightMenu, - StructureEnum::CORTEX_RIGHT); - updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceCerebellumMenu, - StructureEnum::CEREBELLUM); -} - -/** - * Update the menu to contain surface for the given structure - * - * @param menu - * Menu that is updated - * @param structure - * Structure for surfaces - */ -void -BrainBrowserWindowToolBar::updateWholeBrainSurfaceMenu(QMenu* menu, - const StructureEnum::Enum structure) -{ - CaretAssert(menu); - menu->clear(); - - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - const int32_t tabIndex = btc->getTabNumber(); - - Brain* brain = GuiManager::get()->getBrain(); - BrainStructure* brainStructure = brain->getBrainStructure(structure, false); - if (brainStructure != NULL) { - std::vector surfaces; - brainStructure->getSurfacesOfType(wholeBrainModel->getSelectedSurfaceType(tabIndex), - surfaces); + BrowserTabContent* tabToDelete = deleteTabInGuiEvent->getBrowserTab(); + CaretAssert(tabToDelete); - const int32_t numSurfaces = static_cast(surfaces.size()); - if (numSurfaces > 0) { - Surface* selectedSurface = wholeBrainModel->getSelectedSurface(structure, - tabIndex); - for (int32_t i = 0; i < numSurfaces; i++) { - QString name = surfaces[i]->getFileNameNoPath(); - QAction* action = new QAction(name); - action->setCheckable(true); - if (surfaces[i] == selectedSurface) { - action->setChecked(true); + for (int32_t iTab = 0; iTab < this->tabBar->count(); iTab++) { + if (tabToDelete == getTabContentFromTab(iTab)) { + if ((iTab >= 0) + && (iTab < this->tabBar->count())) { + tabClosed(iTab, + RemoveTabMode::DELETE_TAB_CONTENT); } - action->setData(qVariantFromValue((void*)surfaces[i])); - menu->addAction(action); + this->updateGraphicsWindow(); + deleteTabInGuiEvent->setEventProcessed(); + break; } } } -} - -/** - * Called when the left surface tool button is pressed. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*checked*/) -{ - updateAllWholeBrainSurfaceMenus(); - if ( ! this->wholeBrainSurfaceLeftMenu->isEmpty()) { - this->wholeBrainSurfaceLeftMenu->exec(QCursor::pos()); - } -} - -/** - * Called when left surface is selected from menu - * - * @param action - * Action that was selected - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceLeftMenuTriggered(QAction* action) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - if (action != NULL) { - QVariant data = action->data(); - void* p = data.value(); - Surface* surface = (Surface*)p; - wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_LEFT, - btc->getTabNumber(), - surface); - this->updateGraphicsWindowAndYokedWindows(); - } -} - -/** - * Called when the right surface tool button is pressed. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceRightToolButtonTriggered(bool /*checked*/) -{ - updateAllWholeBrainSurfaceMenus(); - if ( ! this->wholeBrainSurfaceRightMenu->isEmpty()) { - this->wholeBrainSurfaceRightMenu->exec(QCursor::pos()); - } -} - -/** - * Called when right surface is selected from menu - * - * @param action - * Action that was selected - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceRightMenuTriggered(QAction* action) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - if (action != NULL) { - QVariant data = action->data(); - void* p = data.value(); - Surface* surface = (Surface*)p; - wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_RIGHT, - btc->getTabNumber(), - surface); - this->updateGraphicsWindowAndYokedWindows(); - } -} - - -/** - * Called when the cerebellum surface tool button is pressed. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumToolButtonTriggered(bool /*checked*/) -{ - updateAllWholeBrainSurfaceMenus(); - if ( ! this->wholeBrainSurfaceCerebellumMenu->isEmpty()) { - this->wholeBrainSurfaceCerebellumMenu->exec(QCursor::pos()); - } -} - -/** - * Called when cerebellum surface is selected from menu - * - * @param action - * Action that was selected - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumMenuTriggered(QAction* action) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - if (action != NULL) { - QVariant data = action->data(); - void* p = data.value(); - Surface* surface = (Surface*)p; - wholeBrainModel->setSelectedSurface(StructureEnum::CEREBELLUM, - btc->getTabNumber(), - surface); - this->updateGraphicsWindowAndYokedWindows(); - } -} - - -/** - * Called when whole brain surface right checkbox is toggled. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceRightCheckBoxStateChanged(int /*state*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - btc->setWholeBrainRightEnabled(this->wholeBrainSurfaceRightCheckBox->isChecked()); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when whole brain cerebellum check box is toggled. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumCheckBoxStateChanged(int /*state*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - btc->setWholeBrainCerebellumEnabled(this->wholeBrainSurfaceCerebellumCheckBox->isChecked()); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when whole brain separation left/right spin box value is changed. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double /*d*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - btc->setWholeBrainLeftRightSeparation(this->wholeBrainSurfaceSeparationLeftRightSpinBox->value()); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when whole brain left&right/cerebellum spin box value is changed. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double /*d*/) -{ - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - - ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); - if (wholeBrainModel == NULL) { - return; - } - - btc->setWholeBrainCerebellumSeparation(this->wholeBrainSurfaceSeparationCerebellumSpinBox->value()); - this->updateGraphicsWindowAndYokedWindows(); -} - -/** - * Called when match check box is clicked - * - * @param checked - * New checked status. - */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceMatchCheckBoxClicked(bool checked) -{ - Brain* brain = GuiManager::get()->getBrain(); - CaretAssert(brain); - - brain->setSurfaceMatchingToAnatomical(checked); - this->updateGraphicsWindowAndYokedWindows(); - this->updateUserInterface(); -} - -/** - * Called when a single surface control is changed. - * @param structure - * Structure that is selected. - * @param surfaceModel - * Model that is selected. - */ -void -BrainBrowserWindowToolBar::surfaceSelectionControlChanged( - const StructureEnum::Enum structure, - ModelSurface* surfaceModel) -{ - if (surfaceModel != NULL) { - BrowserTabContent* btc = this->getTabContentFromSelectedTab(); - ModelSurfaceSelector* surfaceModelSelector = btc->getSurfaceModelSelector(); - surfaceModelSelector->setSelectedStructure(structure); - surfaceModelSelector->setSelectedSurfaceModel(surfaceModel); - EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); - this->updateUserInterface(); - this->updateGraphicsWindow(); + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_NEW_IN_GUI) { + EventBrowserTabNewInGUI* newTabEvent = dynamic_cast(event); + CaretAssert(newTabEvent); + + BrowserTabContent* newTabContent = addNewTab(); + newTabEvent->setBrowserTab(newTabContent); + newTabEvent->setEventProcessed(); } - - this->updateTabName(-1); -} - -/** - * Receive events from the event manager. - * - * @param event - * Event sent by event manager. - */ -void -BrainBrowserWindowToolBar::receiveEvent(Event* event) -{ - if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET) { + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET) { EventBrowserWindowDrawingContent* getModelEvent = dynamic_cast(event); CaretAssert(getModelEvent); if (getModelEvent->getBrowserWindowIndex() == this->browserWindowIndex) { - BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); - - if (browserWindow != NULL) { + if (m_parentBrainBrowserWindow != NULL) { const int32_t numTabs = this->tabBar->count(); for (int32_t i = 0; i < numTabs; i++) { BrowserTabContent* btc = this->getTabContentFromTab(i); getModelEvent->addBrowserTab(btc); } - BrowserWindowContent* windowContent = browserWindow->getBrowerWindowContent(); + BrowserWindowContent* windowContent = m_parentBrainBrowserWindow->getBrowerWindowContent(); getModelEvent->setBrowserWindowContent(windowContent); if (windowContent->isTileTabsEnabled()) { @@ -4501,6 +3410,70 @@ getModelEvent->setEventProcessed(); } } + else if (event->getEventType() == EventTypeEnum::EVENT_GET_USER_INPUT_MODE) { + EventUserInputModeGet* modeEvent = dynamic_cast(event); + CaretAssert(modeEvent); + + if (modeEvent->getWindowIndex() == this->browserWindowIndex) { + modeEvent->setUserInputMode(this->selectedUserInputProcessor->getUserInputMode()); + modeEvent->setEventProcessed(); + } + } + else if (event->getEventType() == EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { + EventGetOrSetUserInputModeProcessor* inputModeEvent = + dynamic_cast(event); + CaretAssert(inputModeEvent); + + if (inputModeEvent->getWindowIndex() == this->browserWindowIndex) { + if (inputModeEvent->isGetUserInputMode()) { + inputModeEvent->setUserInputProcessor(this->selectedUserInputProcessor); + } + else if (inputModeEvent->isSetUserInputMode()) { + UserInputModeAbstract* newUserInputProcessor = NULL; + switch (inputModeEvent->getUserInputMode()) { + case UserInputModeEnum::Enum::INVALID: + CaretAssertMessage(0, "INVALID is NOT allowed for user input mode"); + break; + case UserInputModeEnum::Enum::ANNOTATIONS: + newUserInputProcessor = this->userInputAnnotationsModeProcessor; + break; + case UserInputModeEnum::Enum::BORDERS: + newUserInputProcessor = this->userInputBordersModeProcessor; + break; + case UserInputModeEnum::Enum::FOCI: + newUserInputProcessor = this->userInputFociModeProcessor; + break; + case UserInputModeEnum::Enum::IMAGE: + newUserInputProcessor = this->userInputImageModeProcessor; + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + newUserInputProcessor = this->userInputTileTabsManualLayoutProcessor; + break; + case UserInputModeEnum::Enum::VOLUME_EDIT: + newUserInputProcessor = this->userInputVolumeEditModeProcessor; + break; + case UserInputModeEnum::Enum::VIEW: + newUserInputProcessor = this->userInputViewModeProcessor; + break; + } + + if ((newUserInputProcessor == this->userInputAnnotationsModeProcessor) + || (this->selectedUserInputProcessor == this->userInputAnnotationsModeProcessor)) { + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + CaretAssert(annMan); + annMan->deselectAllAnnotationsForEditing(this->browserWindowIndex); + } + if (newUserInputProcessor != NULL) { + if (newUserInputProcessor != this->selectedUserInputProcessor) { + this->selectedUserInputProcessor->finish(); + this->selectedUserInputProcessor = newUserInputProcessor; + this->selectedUserInputProcessor->initialize(); + } + } + } + inputModeEvent->setEventProcessed(); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); @@ -4548,36 +3521,7 @@ EventBrowserWindowTileTabOperation* tileTabsEvent = dynamic_cast(event); CaretAssert(tileTabsEvent); - - if (tileTabsEvent->getWindowIndex() == this->browserWindowIndex) { - const int32_t browserTabIndex = tileTabsEvent->getBrowserTabIndex(); - int32_t tabBarIndex = getTabBarIndexWithBrowserTabIndex(browserTabIndex); - const EventBrowserWindowTileTabOperation::Operation operation = tileTabsEvent->getOperation(); - switch (operation) { - case EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_AFTER: - if (tabBarIndex >= 0) { - insertNewTabAtTabBarIndex(tabBarIndex + 1); - } - break; - case EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_BEFORE: - if (tabBarIndex >= 0) { - insertNewTabAtTabBarIndex(tabBarIndex); - } - break; - case EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB: - if (tabBarIndex >= 0) { - m_tileTabsHighlightingTimerEnabledFlag = false; - this->tabBar->setCurrentIndex(tabBarIndex); - m_tileTabsHighlightingTimerEnabledFlag = true; - } - break; - case EventBrowserWindowTileTabOperation::OPERATION_REPLACE_TABS: - replaceBrowserTabs(tileTabsEvent->getBrowserTabsForReplaceOperation()); - break; - } - - tileTabsEvent->setEventProcessed(); - } + processTileTabOperationEvent(tileTabsEvent); } else if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS) { EventUpdateYokedWindows* yokeUpdateEvent = @@ -4599,9 +3543,8 @@ EventBrowserTabGetAllViewed* viewedTabsEvent = dynamic_cast(event); CaretAssert(viewedTabsEvent); - BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); - if (browserWindow != NULL) { - if (browserWindow->isTileTabsSelected()) { + if (m_parentBrainBrowserWindow != NULL) { + if (m_parentBrainBrowserWindow->isTileTabsSelected()) { const int32_t numTabs = this->tabBar->count(); for (int32_t i = 0; i < numTabs; i++) { BrowserTabContent* btc = this->getTabContentFromTab(i); @@ -4618,12 +3561,164 @@ viewedTabsEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_SELECT_IN_WINDOW) { + EventBrowserTabSelectInWindow* selectTabEvent = dynamic_cast(event); + CaretAssert(selectTabEvent); + + const int32_t browserTabIndex = selectTabEvent->getBrowserTabIndex(); + const int32_t tabIndex = getTabBarIndexWithBrowserTabIndex(browserTabIndex); + if (tabIndex >= 0) { + this->tabBar->setCurrentIndex(tabIndex); + selectedTabChanged(tabIndex); + selectTabEvent->setEventProcessed(); + } + } else { } } /** + * Process a tile tab operation event. + * + * @param tileTabOperation + */ +void +BrainBrowserWindowToolBar::processTileTabOperationEvent(EventBrowserWindowTileTabOperation* tileTabsEvent) +{ + if (tileTabsEvent->getWindowIndex() == this->browserWindowIndex) { + const int32_t browserTabIndex = tileTabsEvent->getBrowserTabIndex(); + int32_t tabBarIndex = getTabBarIndexWithBrowserTabIndex(browserTabIndex); + const EventBrowserWindowTileTabOperation::Operation operation = tileTabsEvent->getOperation(); + switch (operation) { + case EventBrowserWindowTileTabOperation::OPERATION_GRID_NEW_TAB_AFTER: + if (tabBarIndex >= 0) { + insertNewTabAtTabBarIndex(tabBarIndex + 1); + } + break; + case EventBrowserWindowTileTabOperation::OPERATION_GRID_NEW_TAB_BEFORE: + if (tabBarIndex >= 0) { + insertNewTabAtTabBarIndex(tabBarIndex); + } + break; + case EventBrowserWindowTileTabOperation::OPERATION_MANUAL_NEW_TAB: + { + BrowserTabContent* btc = addNewTab(); + if (btc != NULL) { + int32_t windowViewport[4]; + tileTabsEvent->getWindowViewport(windowViewport); + + const float windowWidth = windowViewport[2]; + const float windowHeight = windowViewport[3]; + + if ((windowWidth > 0) + && (windowHeight > 0)) { + const float mouseX(tileTabsEvent->getMouseX()); + const float mouseY(tileTabsEvent->getMouseY()); + + AnnotationBrowserTab* tabAnnotation = btc->getManualLayoutBrowserTabAnnotation(); + CaretAssert(tabAnnotation); + float xyz[3]; + tabAnnotation->getCoordinate()->getXYZ(xyz); + xyz[0] = (mouseX / windowWidth) * 100.0; + xyz[1] = (mouseY / windowHeight) * 100.0; + tabAnnotation->getCoordinate()->setXYZ(xyz); + } + + updateGraphicsWindow(); + } + } + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_TO_FRONT: + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_FORWARD: + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_TO_BACK: + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_BACKWARD: + { + AnnotationStackingOrderTypeEnum::Enum orderingOperation = AnnotationStackingOrderTypeEnum::BRING_TO_FRONT; + switch (operation) { + case EventBrowserWindowTileTabOperation::OPERATION_GRID_NEW_TAB_AFTER: + case EventBrowserWindowTileTabOperation::OPERATION_GRID_NEW_TAB_BEFORE: + case EventBrowserWindowTileTabOperation::OPERATION_MANUAL_NEW_TAB: + CaretAssert(0); + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_TO_FRONT: + orderingOperation = AnnotationStackingOrderTypeEnum::BRING_TO_FRONT; + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_FORWARD: + orderingOperation = AnnotationStackingOrderTypeEnum::BRING_FORWARD; + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_TO_BACK: + orderingOperation = AnnotationStackingOrderTypeEnum::SEND_TO_BACK; + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_BACKWARD: + orderingOperation = AnnotationStackingOrderTypeEnum::SEND_BACKWARD; + break; + case EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB: + case EventBrowserWindowTileTabOperation::OPERATION_REPLACE_TABS: + CaretAssert(0); + break; + } + + std::vector allTabContent; + getAllTabContent(allTabContent); + std::vector annotations; + Annotation* selectedAnnotation(NULL); + for (auto tc : allTabContent) { + AnnotationBrowserTab* abt = tc->getManualLayoutBrowserTabAnnotation(); + CaretAssert(abt); + if (tc->getTabNumber() == browserTabIndex) { + selectedAnnotation = abt; + } + annotations.push_back(tc->getManualLayoutBrowserTabAnnotation()); + } + + if (selectedAnnotation != NULL) { + AString errorMessage; + AnnotationStackingOrderOperation modifier(AnnotationStackingOrderOperation::Mode::MODE_APPLY_NEW_ORDER_TO_ANNOTAIONS, + annotations, + selectedAnnotation, + this->browserWindowIndex); + if (! modifier.runOrdering(orderingOperation, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + } + else { + WuQMessageBox::errorOk(this, + ("PROGRAM ERROR: Unable to find tab with number " + + AString::number(browserTabIndex + 1))); + } + } + break; + case EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB: + if (tabBarIndex >= 0) { + /* + * Highlighting places outline around tab in graphics region + */ + const bool highlightTabWithOutlineFlag(true); + if ( ! highlightTabWithOutlineFlag) { + m_tileTabsHighlightingTimerEnabledFlag = false; + } + this->tabBar->setCurrentIndex(tabBarIndex); + if ( ! highlightTabWithOutlineFlag) { + m_tileTabsHighlightingTimerEnabledFlag = true; + } + } + break; + case EventBrowserWindowTileTabOperation::OPERATION_REPLACE_TABS: + replaceBrowserTabs(tileTabsEvent->getBrowserTabsForReplaceOperation()); + break; + } + + tileTabsEvent->setEventProcessed(); + + updateGraphicsWindow(); + } +} + + +/** * Get the tab bar index containing the browser tab index. * * @param browserTabIndex @@ -4829,7 +3924,8 @@ */ const int32_t numberOfOpenTabs = this->tabBar->count(); for (int32_t iClose = (numberOfOpenTabs - 1); iClose >= 0; iClose--) { - this->tabClosed(iClose); + this->tabClosed(iClose, + RemoveTabMode::DELETE_TAB_CONTENT); } /* diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBar.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBar.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBar.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBar.h 2021-02-16 19:46:47.000000000 +0000 @@ -26,6 +26,7 @@ #include #include +#include "BrainBrowserWindowToolBarOrientation.h" #include "EnumComboBoxTemplate.h" #include "EventListenerInterface.h" #include "ModelTypeEnum.h" @@ -51,22 +52,25 @@ namespace caret { + class BrainBrowserWindowToolBarAllSurface; class BrainBrowserWindowToolBarChartAxes; class BrainBrowserWindowToolBarChartAttributes; class BrainBrowserWindowToolBarChartTwoAttributes; - class BrainBrowserWindowToolBarChartTwoAxes; class BrainBrowserWindowToolBarChartTwoOrientation; - class BrainBrowserWindowToolBarChartTwoTitle; + class BrainBrowserWindowToolBarChartTwoOrientedAxes; class BrainBrowserWindowToolBarChartTwoType; class BrainBrowserWindowToolBarChartType; - class BrainBrowserWindowToolBarClipping; class BrainBrowserWindowToolBarSlicePlane; class BrainBrowserWindowToolBarSliceSelection; + class BrainBrowserWindowToolBarSurface; class BrainBrowserWindowToolBarSurfaceMontage; class BrainBrowserWindowToolBarTab; + class BrainBrowserWindowToolBarView; class BrainBrowserWindowToolBarVolumeMontage; class BrainBrowserWindow; class BrowserTabContent; + class EventBrowserTabReopenClosed; + class EventBrowserWindowTileTabOperation; class Model; class ModelSurface; class ModelVolumeInterface; @@ -75,6 +79,14 @@ class Surface; class SurfaceSelectionViewController; class StructureSurfaceSelectionControl; + class UserInputModeAnnotations; + class UserInputModeBorders; + class UserInputModeFoci; + class UserInputModeImage; + class UserInputModeTileTabsManualLayout; + class UserInputModeView; + class UserInputModeVolumeEdit; + class UserInputModeAbstract; class WuQTabBar; class WuQWidgetObjectGroup; @@ -93,7 +105,9 @@ ~BrainBrowserWindowToolBar(); - void addNewTab(); + BrowserTabContent* addNewTab(); + + void reopenLastClosedTab(EventBrowserTabReopenClosed& reopenTabEvent); void addNewDuplicatedTab(BrowserTabContent* browserTabContentToBeCloned); @@ -105,18 +119,12 @@ int32_t getNumberOfTabs() const; - void insertDuplicateMenuBar(QMainWindow* mainWindow); - virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); - signals: - void viewedModelChanged(); - - private: enum WidgetPlacement { WIDGET_PLACEMENT_NONE, WIDGET_PLACEMENT_BOTTOM, @@ -125,6 +133,17 @@ WIDGET_PLACEMENT_TOP }; + static QWidget* createToolWidget(const QString& name, + QWidget* childWidget, + const WidgetPlacement verticalBarPlacement, + const WidgetPlacement contentPlacement, + const int horizontalStretching); + + signals: + void viewedModelChanged(); + + private: + BrainBrowserWindowToolBar(const BrainBrowserWindowToolBar&); BrainBrowserWindowToolBar& operator=(const BrainBrowserWindowToolBar&); @@ -144,6 +163,7 @@ void updateToolBox(); void updateAllTabNames(); void updateTabName(const int32_t tabIndex); + void emitViewModelChangedSignal(); /* for use by friend classes */ QWidget* createViewWidget(); QWidget* createOrientationWidget(); @@ -155,13 +175,11 @@ QWidget* createChartAttributesWidget(); QWidget* createChartTwoOrientationWidget(); QWidget* createChartTwoAttributesWidget(); - QWidget* createChartTwoAxesWidget(); - QWidget* createChartTwoTitleWidget(); + QWidget* createChartTwoOrientedAxisWidget(); QWidget* createChartTypeWidget(); QWidget* createChartTypeTwoWidget(); QWidget* createSingleSurfaceOptionsWidget(); QWidget* createSurfaceMontageOptionsWidget(); - QWidget* createClippingOptionsWidget(); QWidget* createVolumeMontageWidget(); QWidget* createVolumePlaneWidget(); @@ -176,30 +194,21 @@ void updateChartAxesWidget(BrowserTabContent* browserTabContent); void updateChartAttributesWidget(BrowserTabContent* browserTabContent); void updateChartTwoAttributesWidget(BrowserTabContent* browserTabContent); - void updateChartTwoAxesWidget(BrowserTabContent* browserTabContent); + void updateChartTwoOrientedAxesWidget(BrowserTabContent* browserTabContent); void updateChartTwoOrientationWidget(BrowserTabContent* browserTabContent); - void updateChartTwoTitleWidget(BrowserTabContent* browserTabContent); void updateChartTypeWidget(BrowserTabContent* browserTabContent); void updateChartTypeTwoWidget(BrowserTabContent* browserTabContent); void updateVolumeMontageWidget(BrowserTabContent* browserTabContent); void updateVolumePlaneWidget(BrowserTabContent* browserTabContent); - void updateClippingOptionsWidget(BrowserTabContent* browserTabContent); - - QWidget* createToolWidget(const QString& name, - QWidget* childWidget, - const WidgetPlacement verticalBarPlacement, - const WidgetPlacement contentPlacement, - const int horizontalStretching); QWidget* viewWidget; QWidget* orientationWidget; QWidget* wholeBrainSurfaceOptionsWidget; QWidget* volumeIndicesWidget; QWidget* modeWidget; - QWidget* windowWidget; + QWidget* tabMiscWidget; QWidget* singleSurfaceSelectionWidget; QWidget* surfaceMontageSelectionWidget; - QWidget* m_clippingOptionsWidget; QWidget* volumeMontageWidget; QWidget* volumePlaneWidget; QWidget* chartTypeWidget; @@ -208,14 +217,26 @@ QWidget* chartAttributesWidget; QWidget* chartTwoOrientationWidget; QWidget* chartTwoAttributesWidget; - QWidget* chartTwoAxesWidget; - QWidget* chartTwoTitleWidget; + QWidget* chartTwoOrientedAxesWidget; + + UserInputModeAbstract* selectedUserInputProcessor = NULL; + UserInputModeAnnotations* userInputAnnotationsModeProcessor; + UserInputModeView* userInputViewModeProcessor; + UserInputModeBorders* userInputBordersModeProcessor; + UserInputModeFoci* userInputFociModeProcessor; + UserInputModeImage* userInputImageModeProcessor; + UserInputModeTileTabsManualLayout* userInputTileTabsManualLayoutProcessor; + UserInputModeVolumeEdit* userInputVolumeEditModeProcessor; + + /* DO NOT delete these as the corresponding processor is the parent */ + QWidget* annotateModeWidget; + QWidget* bordersModeWidget; + QWidget* fociModeWidget; + QWidget* imageModeWidget; + QWidget* tileModeWidget; + QWidget* volumeModeWidget; - WuQWidgetObjectGroup* viewWidgetGroup; - WuQWidgetObjectGroup* orientationWidgetGroup; - WuQWidgetObjectGroup* wholeBrainSurfaceOptionsWidgetGroup; WuQWidgetObjectGroup* modeWidgetGroup; - WuQWidgetObjectGroup* singleSurfaceSelectionWidgetGroup; QVBoxLayout* m_toolBarMainLayout; @@ -226,23 +247,16 @@ QWidget* tabBarWidget; WuQTabBar* tabBar; - /** Widget displayed at bottom of toolbar for mouse input controls */ - QWidget* userInputControlsWidget; - - /** Layout for widget displayed at bottom of toolbar for mouse input controls */ - QHBoxLayout* userInputControlsWidgetLayout; - - /** Is set to the user input widget provided by the user input processor */ - QWidget* userInputControlsWidgetActiveInputWidget; - void removeAndReturnAllTabs(std::vector& allTabContent); void getAllTabContent(std::vector& allTabContent) const; void removeTabWithContent(BrowserTabContent* browserTabContent); + void processTileTabOperationEvent(EventBrowserWindowTileTabOperation* tileTabsEvent); + public slots: - void closeSelectedTab(); + void closeSelectedTabFromFileMenu(); void moveTabsToNewWindows(); @@ -265,8 +279,8 @@ void tabMoved(int, int); void tabCloseSelected(int); void showTabMenu(const QPoint& pos); - void tabBarMousePressedSlot(); - void tabBarMouseReleasedSlot(); + void tabBarMousePressedSlot(QMouseEvent* event); + void tabBarMouseReleasedSlot(QMouseEvent* event); private: enum class InsertTabMode { @@ -274,164 +288,87 @@ AT_TAB_BAR_INDEX }; + /** + * Remove tab mode + */ + enum class RemoveTabMode { + /** Close tab but allow it to be reopened later */ + CLOSE_TAB_CONTENT_FOR_REOPENING, + /** Delete the tab, CANNOT be reopened */ + DELETE_TAB_CONTENT, + /** Ignore tab content, caller may delete it or transfer it (content attached to QTab may be NULL too) */ + INGORE_TAB_CONTENT + }; + bool allowAddingNewTab(); void insertTabContentPrivate(const InsertTabMode insertTabMode, BrowserTabContent* browserTabContent, const int32_t tabBarIndex); + + void removeTab(int index, + const RemoveTabMode removeTabMode); - void removeTab(int index); - void tabClosed(int index); + void tabClosed(int index, + const RemoveTabMode removeTabMode); - void insertNewTabAtTabBarIndex(int32_t tabBarIndex); + BrowserTabContent* insertNewTabAtTabBarIndex(int32_t tabBarIndex); void insertAndCloneTabContentAtTabBarIndex(const BrowserTabContent* tabContentToBeCloned, const int32_t tabBarIndex); void replaceBrowserTabs(const std::vector& browserTabs); BrowserTabContent* createNewTab(AString& errorMessage); - QRadioButton* viewModeSurfaceRadioButton; - QRadioButton* viewModeSurfaceMontageRadioButton; - QRadioButton* viewModeVolumeRadioButton; - QRadioButton* viewModeWholeBrainRadioButton; - QRadioButton* viewModeChartOneRadioButton; - QRadioButton* viewModeChartTwoRadioButton; - QAction* customViewAction; private slots: - void viewModeRadioButtonClicked(QAbstractButton*); - void customViewActionTriggered(); void sceneToolButtonClicked(); private: - QAction* orientationLateralMedialToolButtonAction; - QAction* orientationDorsalVentralToolButtonAction; - QAction* orientationAnteriorPosteriorToolButtonAction; - - QToolButton* orientationLateralMedialToolButton; - QToolButton* orientationDorsalVentralToolButton; - QToolButton* orientationAnteriorPosteriorToolButton; - - QAction* orientationLeftOrLateralToolButtonAction; - QAction* orientationRightOrMedialToolButtonAction; - QAction* orientationAnteriorToolButtonAction; - QAction* orientationPosteriorToolButtonAction; - QAction* orientationDorsalToolButtonAction; - QAction* orientationVentralToolButtonAction; - - QToolButton* orientationLeftOrLateralToolButton; - QToolButton* orientationRightOrMedialToolButton; - QToolButton* orientationAnteriorToolButton; - QToolButton* orientationPosteriorToolButton; - QToolButton* orientationDorsalToolButton; - QToolButton* orientationVentralToolButton; - - QAction* orientationResetToolButtonAction; - QToolButton* orientationCustomViewSelectToolButton; - - QIcon* viewOrientationLeftIcon; - QIcon* viewOrientationRightIcon; - QIcon* viewOrientationAnteriorIcon; - QIcon* viewOrientationPosteriorIcon; - QIcon* viewOrientationDorsalIcon; - QIcon* viewOrientationVentralIcon; - QIcon* viewOrientationLeftLateralIcon; - QIcon* viewOrientationLeftMedialIcon; - QIcon* viewOrientationRightLateralIcon; - QIcon* viewOrientationRightMedialIcon; QToolButton* m_movieToolButton = NULL; - private slots: - void orientationLeftOrLateralToolButtonTriggered(bool checked); - void orientationRightOrMedialToolButtonTriggered(bool checked); - void orientationAnteriorToolButtonTriggered(bool checked); - void orientationPosteriorToolButtonTriggered(bool checked); - void orientationDorsalToolButtonTriggered(bool checked); - void orientationVentralToolButtonTriggered(bool checked); - void orientationResetToolButtonTriggered(bool checked); - - void orientationLateralMedialToolButtonTriggered(bool checked); - void orientationDorsalVentralToolButtonTriggered(bool checked); - void orientationAnteriorPosteriorToolButtonTriggered(bool checked); - private: - QComboBox* wholeBrainSurfaceTypeComboBox; - QCheckBox* wholeBrainSurfaceLeftCheckBox; - QCheckBox* wholeBrainSurfaceRightCheckBox; - QCheckBox* wholeBrainSurfaceCerebellumCheckBox; - QMenu* wholeBrainSurfaceLeftMenu; - QMenu* wholeBrainSurfaceRightMenu; - QMenu* wholeBrainSurfaceCerebellumMenu; - QDoubleSpinBox* wholeBrainSurfaceSeparationLeftRightSpinBox; - QDoubleSpinBox* wholeBrainSurfaceSeparationCerebellumSpinBox; - QCheckBox* wholeBrainSurfaceMatchCheckBox; - void updateAllWholeBrainSurfaceMenus(); - void updateWholeBrainSurfaceMenu(QMenu* menu, - const StructureEnum::Enum structure); - - private slots: - void wholeBrainSurfaceTypeComboBoxIndexChanged(int indx); - void wholeBrainSurfaceLeftCheckBoxStateChanged(int state); - void wholeBrainSurfaceRightCheckBoxStateChanged(int state); - void wholeBrainSurfaceCerebellumCheckBoxStateChanged(int state); - void wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double d); - void wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double d); - void wholeBrainSurfaceLeftToolButtonTriggered(bool checked); - void wholeBrainSurfaceRightToolButtonTriggered(bool checked); - void wholeBrainSurfaceCerebellumToolButtonTriggered(bool checked); - void wholeBrainSurfaceMatchCheckBoxClicked(bool checked); - - void wholeBrainSurfaceLeftMenuTriggered(QAction*); - void wholeBrainSurfaceRightMenuTriggered(QAction*); - void wholeBrainSurfaceCerebellumMenuTriggered(QAction*); - - private: - StructureSurfaceSelectionControl* surfaceSurfaceSelectionControl; - - private slots: - void surfaceSelectionControlChanged(const StructureEnum::Enum, - ModelSurface*); - - private: + BrainBrowserWindowToolBarAllSurface* m_allSurfaceToolBarComponent; BrainBrowserWindowToolBarChartAxes* m_chartAxisToolBarComponent; BrainBrowserWindowToolBarChartTwoType* m_chartTwoTypeToolBarComponent; BrainBrowserWindowToolBarChartType* m_chartTypeToolBarComponent; BrainBrowserWindowToolBarChartAttributes* m_chartAttributesToolBarComponent; - BrainBrowserWindowToolBarChartTwoAxes* m_chartTwoAxesToolBarComponent; + BrainBrowserWindowToolBarChartTwoOrientedAxes* m_chartTwoOrientedAxesToolBarComponent; BrainBrowserWindowToolBarChartTwoOrientation* m_chartTwoOrientationToolBarComponent; BrainBrowserWindowToolBarChartTwoAttributes* m_chartTwoAttributesToolBarComponent; - BrainBrowserWindowToolBarChartTwoTitle* m_chartTwoTitleToolBarComponent; - + BrainBrowserWindowToolBarOrientation* m_orientationToolBarComponent; + BrainBrowserWindowToolBarSurface* m_surfaceToolBarComponent; BrainBrowserWindowToolBarSurfaceMontage* m_surfaceMontageToolBarComponent; - - BrainBrowserWindowToolBarClipping* m_clippingToolBarComponent; + BrainBrowserWindowToolBarView* m_viewToolBarComponent; BrainBrowserWindowToolBarSlicePlane* m_slicePlaneComponent; BrainBrowserWindowToolBarSliceSelection* m_sliceSelectionComponent; BrainBrowserWindowToolBarVolumeMontage* m_volumeMontageComponent; BrainBrowserWindowToolBarTab* m_tabOptionsComponent; private slots: - void modeInputModeActionTriggered(QAction*); + void modeInputModeRadioButtonClicked(QAbstractButton*); private: void updateDisplayedModeUserInputWidget(); - QActionGroup* modeInputModeActionGroup; - QAction* modeInputModeAnnotationsAction; - QAction* modeInputModeBordersAction; - QAction* modeInputModeFociAction; - QAction* modeInputModeImageAction; - QAction* modeInputModeViewAction; - QAction* modeInputVolumeEditAction; + QButtonGroup* modeInputModeRadioButtonGroup; + QRadioButton* modeInputModeAnnotationsRadioButton; + QRadioButton* modeInputModeBordersRadioButton; + QRadioButton* modeInputModeFociRadioButton; + QRadioButton* modeInputModeImageRadioButton; + QRadioButton* modeInputModeViewRadioButton; + QRadioButton* modeInputModeTileTabsManualLayoutRadioButton; + QRadioButton* modeInputVolumeEditRadioButton; private: QAction* toolBarToolButtonAction; QAction* toolBoxToolButtonAction; + BrainBrowserWindow* m_parentBrainBrowserWindow = NULL; + int32_t browserWindowIndex; private slots: @@ -439,19 +376,21 @@ private: friend class BrainBrowserWindow; + friend class BrainBrowserWindowToolBarAllSurface; friend class BrainBrowserWindowToolBarChartAxes; - friend class BrainBrowserWindowToolBarChartTwoAxes; friend class BrainBrowserWindowToolBarChartTwoOrientation; friend class BrainBrowserWindowToolBarChartTwoAttributes; - friend class BrainBrowserWindowToolBarChartTwoType; friend class BrainBrowserWindowToolBarChartType; - friend class BrainBrowserWindowToolBarClipping; friend class BrainBrowserWindowToolBarComponent; + friend class BrainBrowserWindowToolBarMode; + friend class BrainBrowserWindowToolBarOrientation; friend class BrainBrowserWindowToolBarSurfaceMontage; friend class BrainBrowserWindowToolBarSlicePlane; friend class BrainBrowserWindowToolBarSliceSelection; + friend class BrainBrowserWindowToolBarSurface; friend class BrainBrowserWindowToolBarTab; + friend class BrainBrowserWindowToolBarView; friend class BrainBrowserWindowToolBarVolumeMontage; /** diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarOrientation.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarOrientation.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarOrientation.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarOrientation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,632 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_ORIENTATION_DECLARE__ +#include "BrainBrowserWindowToolBarOrientation.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_ORIENTATION_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + +#include +#include +#include +#include + +#include "BrainBrowserWindowToolBar.h" +#include "BrowserTabContent.h" +#include "Model.h" +#include "ModelMedia.h" +#include "ModelSurface.h" +#include "ModelSurfaceMontage.h" +#include "ModelVolume.h" +#include "ModelWholeBrain.h" +#include "Surface.h" +#include "StructureEnum.h" +#include "WuQMacroManager.h" +#include "WuQtUtilities.h" + + +/** + * \class caret::BrainBrowserWindowToolBarOrientation + * \brief Toolbar component for orientation controls + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param parentObjectName + * Name of the parent object + * @param parentToolBar + * The parent toolbar + */ +BrainBrowserWindowToolBarOrientation::BrainBrowserWindowToolBarOrientation(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar) +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + const QString objectNamePrefix(parentObjectName + + ":Orientation:"); + + this->viewOrientationLeftIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left.png"); + this->viewOrientationRightIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right.png"); + this->viewOrientationAnteriorIcon = WuQtUtilities::loadIcon(":/ToolBar/view-anterior.png"); + this->viewOrientationPosteriorIcon = WuQtUtilities::loadIcon(":/ToolBar/view-posterior.png"); + this->viewOrientationDorsalIcon = WuQtUtilities::loadIcon(":/ToolBar/view-dorsal.png"); + this->viewOrientationVentralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-ventral.png"); + this->viewOrientationLeftLateralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left-lateral.png"); + this->viewOrientationLeftMedialIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left-medial.png"); + this->viewOrientationRightLateralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right-lateral.png"); + this->viewOrientationRightMedialIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right-medial.png"); + + this->orientationLeftOrLateralToolButtonAction = WuQtUtilities::createAction("L", + "View from a LEFT perspective", + this, + this, + SLOT(orientationLeftOrLateralToolButtonTriggered(bool))); + if (this->viewOrientationLeftIcon != NULL) { + this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftIcon); + } + else { + this->orientationLeftOrLateralToolButtonAction->setIconText("L"); + } + this->orientationLeftOrLateralToolButtonAction->setObjectName(objectNamePrefix + + "LeftOrLateralView"); + macroManager->addMacroSupportToObject(this->orientationLeftOrLateralToolButtonAction, + "Select left or lateral orientation"); + + this->orientationRightOrMedialToolButtonAction = WuQtUtilities::createAction("R", + "View from a RIGHT perspective", + this, + this, + SLOT(orientationRightOrMedialToolButtonTriggered(bool))); + if (this->viewOrientationRightIcon != NULL) { + this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightIcon); + } + else { + this->orientationRightOrMedialToolButtonAction->setIconText("R"); + } + this->orientationRightOrMedialToolButtonAction->setObjectName(objectNamePrefix + + "RightOrMedialView"); + macroManager->addMacroSupportToObject(this->orientationRightOrMedialToolButtonAction, + "Select right or medial orientation"); + + this->orientationAnteriorToolButtonAction = WuQtUtilities::createAction("A", + "View from an ANTERIOR perspective", + this, + this, + SLOT(orientationAnteriorToolButtonTriggered(bool))); + if (this->viewOrientationAnteriorIcon != NULL) { + this->orientationAnteriorToolButtonAction->setIcon(*this->viewOrientationAnteriorIcon); + } + else { + this->orientationAnteriorToolButtonAction->setIconText("A"); + } + this->orientationAnteriorToolButtonAction->setObjectName(objectNamePrefix + + "AnteriorView"); + macroManager->addMacroSupportToObject(this->orientationAnteriorToolButtonAction, + "Select anterior orientation"); + + this->orientationPosteriorToolButtonAction = WuQtUtilities::createAction("P", + "View from a POSTERIOR perspective", + this, + this, + SLOT(orientationPosteriorToolButtonTriggered(bool))); + if (this->viewOrientationPosteriorIcon != NULL) { + this->orientationPosteriorToolButtonAction->setIcon(*this->viewOrientationPosteriorIcon); + } + else { + this->orientationPosteriorToolButtonAction->setIconText("P"); + } + this->orientationPosteriorToolButtonAction->setObjectName(objectNamePrefix + + "PosteriorView"); + macroManager->addMacroSupportToObject(this->orientationPosteriorToolButtonAction, + "Select posterior orientation"); + + this->orientationDorsalToolButtonAction = WuQtUtilities::createAction("D", + "View from a DORSAL perspective", + this, + this, + SLOT(orientationDorsalToolButtonTriggered(bool))); + if (this->viewOrientationDorsalIcon != NULL) { + this->orientationDorsalToolButtonAction->setIcon(*this->viewOrientationDorsalIcon); + } + else { + this->orientationDorsalToolButtonAction->setIconText("D"); + } + this->orientationDorsalToolButtonAction->setObjectName(objectNamePrefix + + "DorsalView"); + macroManager->addMacroSupportToObject(this->orientationDorsalToolButtonAction, + "Select dorsal orientation"); + + this->orientationVentralToolButtonAction = WuQtUtilities::createAction("V", + "View from a VENTRAL perspective", + this, + this, + SLOT(orientationVentralToolButtonTriggered(bool))); + if (this->viewOrientationVentralIcon != NULL) { + this->orientationVentralToolButtonAction->setIcon(*this->viewOrientationVentralIcon); + } + else { + this->orientationVentralToolButtonAction->setIconText("V"); + } + this->orientationVentralToolButtonAction->setObjectName(objectNamePrefix + + "VentralView"); + macroManager->addMacroSupportToObject(this->orientationVentralToolButtonAction, + "Select ventral orientation"); + + + this->orientationLateralMedialToolButtonAction = WuQtUtilities::createAction("LM", + "View from a Lateral/Medial perspective", + this, + this, + SLOT(orientationLateralMedialToolButtonTriggered(bool))); + this->orientationLateralMedialToolButtonAction->setObjectName(objectNamePrefix + + "LateralMedialView"); + macroManager->addMacroSupportToObject(this->orientationLateralMedialToolButtonAction, + "Select lateral/medial orientation"); + + this->orientationDorsalVentralToolButtonAction = WuQtUtilities::createAction("DV", + "View from a Dorsal/Ventral perspective", + this, + this, + SLOT(orientationDorsalVentralToolButtonTriggered(bool))); + this->orientationDorsalVentralToolButtonAction->setObjectName(objectNamePrefix + + "DorsalVentralView"); + macroManager->addMacroSupportToObject(this->orientationDorsalVentralToolButtonAction, + "Select dorsal/ventral orientation"); + + this->orientationAnteriorPosteriorToolButtonAction = WuQtUtilities::createAction("AP", + "View from a Anterior/Posterior perspective", + this, + this, + SLOT(orientationAnteriorPosteriorToolButtonTriggered(bool))); + this->orientationAnteriorPosteriorToolButtonAction->setObjectName(objectNamePrefix + + "AnteriorPosteriorView"); + macroManager->addMacroSupportToObject(this->orientationAnteriorPosteriorToolButtonAction, + "Select anterior/posterior orientation"); + + this->orientationResetToolButtonAction = WuQtUtilities::createAction("R\nE\nS\nE\nT", + "Reset the view to lateral and remove any panning or zooming", + this, + this, + SLOT(orientationResetToolButtonTriggered(bool))); + this->orientationResetToolButtonAction->setObjectName(objectNamePrefix + + "ResetView"); + macroManager->addMacroSupportToObject(this->orientationResetToolButtonAction, + "Reset to default orientation"); + + this->orientationLeftOrLateralToolButton = new QToolButton(); + this->orientationLeftOrLateralToolButton->setDefaultAction(this->orientationLeftOrLateralToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationLeftOrLateralToolButton); + this->orientationLeftOrLateralToolButtonAction->setParent(this->orientationLeftOrLateralToolButton); + + this->orientationRightOrMedialToolButton = new QToolButton(); + this->orientationRightOrMedialToolButton->setDefaultAction(this->orientationRightOrMedialToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationRightOrMedialToolButton); + orientationRightOrMedialToolButtonAction->setParent(orientationRightOrMedialToolButton); + + this->orientationAnteriorToolButton = new QToolButton(); + this->orientationAnteriorToolButton->setDefaultAction(this->orientationAnteriorToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationAnteriorToolButton); + this->orientationAnteriorToolButtonAction->setParent(this->orientationAnteriorToolButton); + + this->orientationPosteriorToolButton = new QToolButton(); + this->orientationPosteriorToolButton->setDefaultAction(this->orientationPosteriorToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationPosteriorToolButton); + this->orientationPosteriorToolButtonAction->setParent(this->orientationPosteriorToolButton); + + this->orientationDorsalToolButton = new QToolButton(); + this->orientationDorsalToolButton->setDefaultAction(this->orientationDorsalToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationDorsalToolButton); + this->orientationDorsalToolButtonAction->setParent(this->orientationDorsalToolButton); + + this->orientationVentralToolButton = new QToolButton(); + this->orientationVentralToolButton->setDefaultAction(this->orientationVentralToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationVentralToolButton); + this->orientationVentralToolButtonAction->setParent(this->orientationVentralToolButton); + + this->orientationLateralMedialToolButton = new QToolButton(); + this->orientationLateralMedialToolButton->setDefaultAction(this->orientationLateralMedialToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationLateralMedialToolButton); + orientationLateralMedialToolButtonAction->setParent(orientationLateralMedialToolButton); + + this->orientationDorsalVentralToolButton = new QToolButton(); + this->orientationDorsalVentralToolButton->setDefaultAction(this->orientationDorsalVentralToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationDorsalVentralToolButton); + orientationDorsalVentralToolButtonAction->setParent(orientationDorsalVentralToolButton); + + this->orientationAnteriorPosteriorToolButton = new QToolButton(); + this->orientationAnteriorPosteriorToolButton->setDefaultAction(this->orientationAnteriorPosteriorToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationAnteriorPosteriorToolButton); + orientationAnteriorPosteriorToolButtonAction->setParent(orientationAnteriorPosteriorToolButton); + + WuQtUtilities::matchWidgetWidths(this->orientationLateralMedialToolButton, + this->orientationDorsalVentralToolButton, + this->orientationAnteriorPosteriorToolButton); + + QToolButton* orientationResetToolButton = new QToolButton(); + orientationResetToolButton->setDefaultAction(this->orientationResetToolButtonAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(orientationResetToolButton); + + this->orientationCustomViewSelectToolButton = new QToolButton(); + this->orientationCustomViewSelectToolButton->setDefaultAction(getParentToolBar()->customViewAction); + this->orientationCustomViewSelectToolButton->setSizePolicy(QSizePolicy::Minimum, + QSizePolicy::Fixed); + WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationCustomViewSelectToolButton); + + QGridLayout* buttonGridLayout = new QGridLayout(); + buttonGridLayout->setColumnStretch(3, 100); + WuQtUtilities::setLayoutSpacingAndMargins(buttonGridLayout, 0, 0); + buttonGridLayout->addWidget(this->orientationLeftOrLateralToolButton, 0, 0); + buttonGridLayout->addWidget(this->orientationRightOrMedialToolButton, 0, 1); + buttonGridLayout->addWidget(this->orientationDorsalToolButton, 1, 0); + buttonGridLayout->addWidget(this->orientationVentralToolButton, 1, 1); + buttonGridLayout->addWidget(this->orientationAnteriorToolButton, 2, 0); + buttonGridLayout->addWidget(this->orientationPosteriorToolButton, 2, 1); + buttonGridLayout->addWidget(this->orientationLateralMedialToolButton, 0, 2); + buttonGridLayout->addWidget(this->orientationDorsalVentralToolButton, 1, 2); + buttonGridLayout->addWidget(this->orientationAnteriorPosteriorToolButton, 2, 2); + buttonGridLayout->addWidget(this->orientationCustomViewSelectToolButton, 3, 0, 1, 5, Qt::AlignHCenter); + buttonGridLayout->addWidget(orientationResetToolButton, 0, 4, 3, 1); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); + layout->addLayout(buttonGridLayout); + + addToWidgetGroup(this->orientationLeftOrLateralToolButtonAction); + addToWidgetGroup(this->orientationRightOrMedialToolButtonAction); + addToWidgetGroup(this->orientationAnteriorToolButtonAction); + addToWidgetGroup(this->orientationPosteriorToolButtonAction); + addToWidgetGroup(this->orientationDorsalToolButtonAction); + addToWidgetGroup(this->orientationVentralToolButtonAction); + addToWidgetGroup(this->orientationResetToolButtonAction); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarOrientation::~BrainBrowserWindowToolBarOrientation() +{ + if (this->viewOrientationLeftIcon != NULL) { + delete this->viewOrientationLeftIcon; + this->viewOrientationLeftIcon = NULL; + } + if (this->viewOrientationRightIcon != NULL) { + delete this->viewOrientationRightIcon; + this->viewOrientationRightIcon = NULL; + } + + if (this->viewOrientationAnteriorIcon != NULL) { + delete this->viewOrientationAnteriorIcon; + this->viewOrientationAnteriorIcon = NULL; + } + + if (this->viewOrientationPosteriorIcon != NULL) { + delete this->viewOrientationPosteriorIcon; + this->viewOrientationPosteriorIcon = NULL; + } + + if (this->viewOrientationDorsalIcon != NULL) { + delete this->viewOrientationDorsalIcon; + this->viewOrientationDorsalIcon = NULL; + } + + if (this->viewOrientationVentralIcon != NULL) { + delete this->viewOrientationVentralIcon; + this->viewOrientationVentralIcon = NULL; + } + + if (this->viewOrientationLeftLateralIcon != NULL) { + delete this->viewOrientationLeftLateralIcon; + this->viewOrientationLeftLateralIcon = NULL; + } + + if (this->viewOrientationLeftMedialIcon != NULL) { + delete this->viewOrientationLeftMedialIcon; + this->viewOrientationLeftMedialIcon = NULL; + } + + if (this->viewOrientationRightLateralIcon != NULL) { + delete this->viewOrientationRightLateralIcon; + this->viewOrientationRightLateralIcon = NULL; + } + + if (this->viewOrientationRightMedialIcon != NULL) { + delete this->viewOrientationRightMedialIcon; + this->viewOrientationRightMedialIcon = NULL; + } +} + +/** + * Update the surface montage options widget. + * + * @param browserTabContent + * The active model display controller (may be NULL). + */ +void +BrainBrowserWindowToolBarOrientation::updateContent(BrowserTabContent* browserTabContent) +{ + const int32_t tabIndex = browserTabContent->getTabNumber(); + + blockAllSignals(true); + + const Model* mdc = getParentToolBar()->getDisplayedModel(); + if (mdc != NULL) { + const ModelMedia* mdm = dynamic_cast(mdc); + const ModelSurface* mdcs = dynamic_cast(mdc); + const ModelSurfaceMontage* mdcsm = dynamic_cast(mdc); + const ModelVolume* mdcv = dynamic_cast(mdc); + const ModelWholeBrain* mdcwb = dynamic_cast(mdc); + + bool rightFlag = false; + bool leftFlag = false; + bool leftRightFlag = false; + + bool enableDualViewOrientationButtons = false; + bool showDualViewOrientationButtons = false; + bool showSingleViewOrientationButtons = false; + + if (mdcs != NULL) { + const Surface* surface = mdcs->getSurface(); + const StructureEnum::Enum structure = surface->getStructure(); + if (StructureEnum::isLeft(structure)) { + leftFlag = true; + } + else if (StructureEnum::isRight(structure)) { + rightFlag = true; + } + else { + leftRightFlag = true; + } + + showSingleViewOrientationButtons = true; + } + else if (mdcsm != NULL) { + AString latMedLeftRightText = "LM"; + AString latMedLeftRightToolTipText = "View from a Lateral/Medial perspective"; + switch (mdcsm->getSelectedConfigurationType(tabIndex)) { + case SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION: + latMedLeftRightText = "LR"; + latMedLeftRightToolTipText = "View from a Right/Left Perspective"; + enableDualViewOrientationButtons = true; + break; + case SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION: + enableDualViewOrientationButtons = true; + break; + case SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION: + break; + } + + this->orientationLateralMedialToolButtonAction->setText(latMedLeftRightText); + WuQtUtilities::setToolTipAndStatusTip(this->orientationLateralMedialToolButtonAction, + latMedLeftRightToolTipText); + + showDualViewOrientationButtons = true; + } + else if (mdcv != NULL) { + /* nothing */ + } + else if (mdcwb != NULL) { + leftRightFlag = true; + showSingleViewOrientationButtons = true; + } + else if (mdm != NULL) { + /* nothing */ + } + else { + CaretAssertMessage(0, "Unknown model display controller type"); + } + + if (rightFlag || leftFlag) { + if (rightFlag) { + if (this->viewOrientationRightLateralIcon != NULL) { + this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationRightLateralIcon); + } + else { + this->orientationLeftOrLateralToolButtonAction->setIconText("L"); + } + if (this->viewOrientationRightMedialIcon != NULL) { + this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightMedialIcon); + } + else { + this->orientationRightOrMedialToolButtonAction->setIconText("M"); + } + } + else if (leftFlag) { + if (this->viewOrientationLeftLateralIcon != NULL) { + this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftLateralIcon); + } + else { + this->orientationLeftOrLateralToolButtonAction->setIconText("L"); + } + if (this->viewOrientationLeftMedialIcon != NULL) { + this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationLeftMedialIcon); + } + else { + this->orientationRightOrMedialToolButtonAction->setIconText("M"); + } + } + WuQtUtilities::setToolTipAndStatusTip(this->orientationLeftOrLateralToolButtonAction, + "View from a LATERAL perspective"); + WuQtUtilities::setToolTipAndStatusTip(this->orientationRightOrMedialToolButtonAction, + "View from a MEDIAL perspective"); + } + else if (leftRightFlag) { + if (this->viewOrientationLeftIcon != NULL) { + this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftIcon); + } + else { + this->orientationLeftOrLateralToolButtonAction->setIconText("L"); + } + if (this->viewOrientationRightIcon != NULL) { + this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightIcon); + } + else { + this->orientationRightOrMedialToolButtonAction->setIconText("R"); + } + WuQtUtilities::setToolTipAndStatusTip(this->orientationLeftOrLateralToolButtonAction, + "View from a LEFT perspective"); + WuQtUtilities::setToolTipAndStatusTip(this->orientationRightOrMedialToolButtonAction, + "View from a RIGHT perspective"); + } + + /* + * The dual view buttons are not need for a flat map montage. + * However, if they are hidden, their space is not reallocated and the + * Reset button remains on the right and it looks weird. So, + * display them but disable them when a flat map montage. + */ + this->orientationLateralMedialToolButton->setVisible(showDualViewOrientationButtons); + this->orientationDorsalVentralToolButton->setVisible(showDualViewOrientationButtons); + this->orientationAnteriorPosteriorToolButton->setVisible(showDualViewOrientationButtons); + + this->orientationLateralMedialToolButton->setEnabled(enableDualViewOrientationButtons); + this->orientationDorsalVentralToolButton->setEnabled(enableDualViewOrientationButtons); + this->orientationAnteriorPosteriorToolButton->setEnabled(enableDualViewOrientationButtons); + + + this->orientationLeftOrLateralToolButton->setVisible(showSingleViewOrientationButtons); + this->orientationRightOrMedialToolButton->setVisible(showSingleViewOrientationButtons); + this->orientationDorsalToolButton->setVisible(showSingleViewOrientationButtons); + this->orientationVentralToolButton->setVisible(showSingleViewOrientationButtons); + this->orientationAnteriorToolButton->setVisible(showSingleViewOrientationButtons); + this->orientationPosteriorToolButton->setVisible(showSingleViewOrientationButtons); + } + + blockAllSignals(false); +} + +/** + * Called when orientation left or lateral button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationLeftOrLateralToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->leftView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when orientation right or medial button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationRightOrMedialToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->rightView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when orientation anterior button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationAnteriorToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->anteriorView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when orientation posterior button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationPosteriorToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->posteriorView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when orientation dorsal button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationDorsalToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->dorsalView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when orientation ventral button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationVentralToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->ventralView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when orientation reset button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationResetToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->resetView(); + + Model* mdc = btc->getModelForDisplay(); + if (mdc != NULL) { + getParentToolBar()->updateVolumeIndicesWidget(btc); + getParentToolBar()->updateGraphicsWindowAndYokedWindows(); + } +} + +/** + * Called when orientation lateral/medial button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationLateralMedialToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->leftView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when orientation dorsal/ventral button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationDorsalVentralToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->dorsalView(); + this->updateGraphicsWindowAndYokedWindows(); +} + +/** + * Called when orientation anterior/posterior button is pressed. + */ +void +BrainBrowserWindowToolBarOrientation::orientationAnteriorPosteriorToolButtonTriggered(bool /*checked*/) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + btc->anteriorView(); + this->updateGraphicsWindowAndYokedWindows(); +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarOrientation.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarOrientation.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarOrientation.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarOrientation.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,114 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_ORIENTATION_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_ORIENTATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "BrainBrowserWindowToolBarComponent.h" + +class QIcon; +class QToolButton; + + +namespace caret { + + class BrainBrowserWindowToolBarOrientation : public BrainBrowserWindowToolBarComponent { + + Q_OBJECT + + public: + BrainBrowserWindowToolBarOrientation(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarOrientation(); + + BrainBrowserWindowToolBarOrientation(const BrainBrowserWindowToolBarOrientation&) = delete; + + BrainBrowserWindowToolBarOrientation& operator=(const BrainBrowserWindowToolBarOrientation&) = delete; + + virtual void updateContent(BrowserTabContent* browserTabContent) override; + + + // ADD_NEW_METHODS_HERE + + private slots: + void orientationLeftOrLateralToolButtonTriggered(bool checked); + void orientationRightOrMedialToolButtonTriggered(bool checked); + void orientationAnteriorToolButtonTriggered(bool checked); + void orientationPosteriorToolButtonTriggered(bool checked); + void orientationDorsalToolButtonTriggered(bool checked); + void orientationVentralToolButtonTriggered(bool checked); + void orientationResetToolButtonTriggered(bool checked); + + void orientationLateralMedialToolButtonTriggered(bool checked); + void orientationDorsalVentralToolButtonTriggered(bool checked); + void orientationAnteriorPosteriorToolButtonTriggered(bool checked); + + private: + QAction* orientationLateralMedialToolButtonAction; + QAction* orientationDorsalVentralToolButtonAction; + QAction* orientationAnteriorPosteriorToolButtonAction; + + QToolButton* orientationLateralMedialToolButton; + QToolButton* orientationDorsalVentralToolButton; + QToolButton* orientationAnteriorPosteriorToolButton; + + QAction* orientationLeftOrLateralToolButtonAction; + QAction* orientationRightOrMedialToolButtonAction; + QAction* orientationAnteriorToolButtonAction; + QAction* orientationPosteriorToolButtonAction; + QAction* orientationDorsalToolButtonAction; + QAction* orientationVentralToolButtonAction; + + QToolButton* orientationLeftOrLateralToolButton; + QToolButton* orientationRightOrMedialToolButton; + QToolButton* orientationAnteriorToolButton; + QToolButton* orientationPosteriorToolButton; + QToolButton* orientationDorsalToolButton; + QToolButton* orientationVentralToolButton; + + QAction* orientationResetToolButtonAction; + QToolButton* orientationCustomViewSelectToolButton; + + QIcon* viewOrientationLeftIcon; + QIcon* viewOrientationRightIcon; + QIcon* viewOrientationAnteriorIcon; + QIcon* viewOrientationPosteriorIcon; + QIcon* viewOrientationDorsalIcon; + QIcon* viewOrientationVentralIcon; + QIcon* viewOrientationLeftLateralIcon; + QIcon* viewOrientationLeftMedialIcon; + QIcon* viewOrientationRightLateralIcon; + QIcon* viewOrientationRightMedialIcon; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_ORIENTATION_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_ORIENTATION_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_ORIENTATION_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -361,36 +361,12 @@ if (vf != NULL) { /* - * Test selected file to see if it is an oblique volume file (not a CIFTI file) - */ - bool obliqueVolumeFlag = false; - if ( ! vf->getVolumeSpace().isPlumb()) { - obliqueVolumeFlag = true; - } - - /* * Update slice projection type for allowed projection types */ std::vector validSliceProjections; - validSliceProjections.push_back(VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); - if ( ! obliqueVolumeFlag) { - validSliceProjections.push_back(VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL); - } + browserTabContent->getValidSliceProjectionTypes(validSliceProjections); m_volumeSliceProjectionTypeEnumComboBox->setupWithItems(validSliceProjections); - /* - * If volume is oblique, change its projection type to oblique - */ - switch (browserTabContent->getSliceProjectionType()) { - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - break; - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - if (obliqueVolumeFlag) { - browserTabContent->setSliceProjectionType(VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); - } - break; - } - m_volumeIndicesAxialCheckBox->setChecked(browserTabContent->isSliceAxialEnabled()); m_volumeIndicesCoronalCheckBox->setChecked(browserTabContent->isSliceCoronalEnabled()); m_volumeIndicesParasagittalCheckBox->setChecked(browserTabContent->isSliceParasagittalEnabled()); diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarSurface.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarSurface.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarSurface.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarSurface.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,131 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_DECLARE__ +#include "BrainBrowserWindowToolBarSurface.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_DECLARE__ + +#include +#include + +#include "BrainBrowserWindowToolBar.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "ModelSurfaceSelector.h" +#include "StructureSurfaceSelectionControl.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarSurface + * \brief Toolbar component for Surface View mode surface selection + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param parentObjectName + * Name of the parent object + * @param parentToolBar + * The parent toolbar + */ +BrainBrowserWindowToolBarSurface::BrainBrowserWindowToolBarSurface(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar) +{ + QLabel* structureSurfaceLabel = new QLabel("Brain Structure and Surface: "); + + /* + * Note: Macro support is in StructureSurfaceSelectionControl + */ + this->surfaceSurfaceSelectionControl = new StructureSurfaceSelectionControl(false, + parentObjectName + + ":Surface", + "surface view", + this); + QObject::connect(this->surfaceSurfaceSelectionControl, + SIGNAL(selectionChanged(const StructureEnum::Enum, + ModelSurface*)), + this, + SLOT(surfaceSelectionControlChanged(const StructureEnum::Enum, + ModelSurface*))); + + this->surfaceSurfaceSelectionControl->setMinimumWidth(150); + this->surfaceSurfaceSelectionControl->setMaximumWidth(1200); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); + layout->addWidget(structureSurfaceLabel); + layout->addWidget(this->surfaceSurfaceSelectionControl); + layout->addStretch(); + + addToWidgetGroup(this->surfaceSurfaceSelectionControl); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarSurface::~BrainBrowserWindowToolBarSurface() +{ +} + +/** + * Update the surface montage options widget. + * + * @param browserTabContent + * The active model display controller (may be NULL). + */ +void +BrainBrowserWindowToolBarSurface::updateContent(BrowserTabContent* browserTabContent) +{ + blockAllSignals(true); + this->surfaceSurfaceSelectionControl->updateControl(browserTabContent->getSurfaceModelSelector()); + blockAllSignals(false); +} + +/** + * Called when a single surface control is changed. + * @param structure + * Structure that is selected. + * @param surfaceModel + * Model that is selected. + */ +void +BrainBrowserWindowToolBarSurface::surfaceSelectionControlChanged( + const StructureEnum::Enum structure, + ModelSurface* surfaceModel) +{ + if (surfaceModel != NULL) { + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + ModelSurfaceSelector* surfaceModelSelector = btc->getSurfaceModelSelector(); + surfaceModelSelector->setSelectedStructure(structure); + surfaceModelSelector->setSelectedSurfaceModel(surfaceModel); + + invalidateColoringAndUpdateGraphicsWindow(); + updateUserInterface(); + } + + getParentToolBar()->updateTabName(-1); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarSurface.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarSurface.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarSurface.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarSurface.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,68 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "BrainBrowserWindowToolBarComponent.h" +#include "ModelSurface.h" +#include "StructureEnum.h" + + +namespace caret { + + class StructureSurfaceSelectionControl; + + class BrainBrowserWindowToolBarSurface : public BrainBrowserWindowToolBarComponent { + Q_OBJECT + public: + BrainBrowserWindowToolBarSurface(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarSurface(); + + BrainBrowserWindowToolBarSurface(const BrainBrowserWindowToolBarSurface&) = delete; + + BrainBrowserWindowToolBarSurface& operator=(const BrainBrowserWindowToolBarSurface&) = delete; + + virtual void updateContent(BrowserTabContent* browserTabContent) override; + + private slots: + void surfaceSelectionControlChanged(const StructureEnum::Enum, + ModelSurface*); + + // ADD_NEW_METHODS_HERE + + private: + StructureSurfaceSelectionControl* surfaceSurfaceSelectionControl; + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -33,7 +33,6 @@ #include "CaretAssert.h" #include "EnumComboBoxTemplate.h" #include "EventManager.h" -#include "EventSurfaceColoringInvalidate.h" #include "ModelSurfaceMontage.h" #include "SurfaceMontageConfigurationCerebellar.h" #include "SurfaceMontageConfigurationCerebral.h" diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarTab.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarTab.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarTab.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarTab.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -26,17 +26,24 @@ #include #include #include +#include #include +#include +#include #include #include +#include +#include "AnnotationScaleBar.h" #include "BrainBrowserWindow.h" #include "BrainBrowserWindowToolBar.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "ClippingPlanesWidget.h" #include "EnumComboBoxTemplate.h" #include "GuiManager.h" +#include "ScaleBarWidget.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" #include "YokingGroupEnum.h" @@ -73,6 +80,9 @@ m_parentToolBar(parentToolBar), m_lockWindowAndAllTabAspectButton(toolBarLockWindowAndAllTabAspectRatioButton) { + m_objectNamePrefix = (objectNamePrefix + + ":Tab"); + setObjectName(m_objectNamePrefix); m_yokingGroupComboBox = new EnumComboBoxTemplate(this); m_yokingGroupComboBox->setup(); m_yokingGroupComboBox->getWidget()->setStatusTip("Select a yoking group (linked views)"); @@ -81,40 +91,118 @@ "Surface Yoking is applied to Surface, Surface Montage\n" "and Whole Brain. Volume Yoking is applied to Volumes.")); QComboBox* encapComboBox = m_yokingGroupComboBox->getComboBox(); - encapComboBox->setObjectName(objectNamePrefix - + ":Tab:YokingGroup"); +// WuQtUtilities::replaceComboBoxItemNames(encapComboBox, "Group", "Yoke"); + encapComboBox->setObjectName(m_objectNamePrefix + + ":YokingGroup"); WuQMacroManager::instance()->addMacroSupportToObject(encapComboBox, "Select yoking group"); - m_yokeToLabel = new QLabel("Yoking:"); + m_yokeToLabel = new QLabel("Yoke"); QObject::connect(m_yokingGroupComboBox, SIGNAL(itemActivated()), this, SLOT(yokeToGroupComboBoxIndexChanged())); + /* + * Lighting action and button + */ + QIcon shadingIcon; + const bool shadingIconValid = WuQtUtilities::loadIcon(":/ToolBar/lighting.png", + shadingIcon); const AString lightToolTip = WuQtUtilities::createWordWrappedToolTipText("Enables shading on surfaces. Shading affects " "palette colors (but not by much) and in rare circumstances it " "may be helpful to turn shading off."); - m_lightingEnabledCheckBox = new QCheckBox("Shading"); - m_lightingEnabledCheckBox->setToolTip(lightToolTip); - QObject::connect(m_lightingEnabledCheckBox, &QCheckBox::clicked, + + m_lightingAction = new QAction(this); + m_lightingAction->setCheckable(true); + if (shadingIconValid) { + m_lightingAction->setIcon(shadingIcon); + } + else { + m_lightingAction->setText("L"); + } + m_lightingAction->setToolTip(lightToolTip); + m_lightingAction->setObjectName(m_objectNamePrefix + + ":EnableShading"); + QObject::connect(m_lightingAction, &QAction::triggered, this, &BrainBrowserWindowToolBarTab::lightingEnabledCheckBoxChecked); - m_lightingEnabledCheckBox->setObjectName(objectNamePrefix - + ":Tab:EnableShading"); - WuQMacroManager::instance()->addMacroSupportToObject(m_lightingEnabledCheckBox, + WuQMacroManager::instance()->addMacroSupportToObject(m_lightingAction, "Enable shading"); - + + QToolButton* lightingToolButton = new QToolButton(); + lightingToolButton->setDefaultAction(m_lightingAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(lightingToolButton); + + /* + * Clipping action and button + */ + QIcon clippingIcon; + const bool clippingIconValid = + WuQtUtilities::loadIcon(":/ToolBar/clipping.png", + clippingIcon); + + m_clippingPlanesAction = new QAction(this); + m_clippingPlanesAction->setCheckable(true); + if (clippingIconValid) { + m_clippingPlanesAction->setIcon(clippingIcon); + } + else { + m_clippingPlanesAction->setText("C"); + } + m_clippingPlanesAction->setToolTip("Enable clipping planes; click arrow to edit"); + m_clippingPlanesAction->setMenu(createClippingPlanesMenu()); + m_clippingPlanesAction->setObjectName(m_objectNamePrefix + + ":ClippingPlanes"); + WuQMacroManager::instance()->addMacroSupportToObject(m_clippingPlanesAction, + "Enable clipping planes"); + QObject::connect(m_clippingPlanesAction, &QAction::triggered, + this, &BrainBrowserWindowToolBarTab::clippingPlanesActionToggled); + + QToolButton* clippingPlanesToolButton = new QToolButton(); + clippingPlanesToolButton->setDefaultAction(m_clippingPlanesAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(clippingPlanesToolButton); + + /* + * Scale bar action and button + */ + QToolButton* scaleBarToolButton = new QToolButton(); + QPixmap scaleBarPixmap = createScaleBarPixmap(scaleBarToolButton); + m_scaleBarAction = new QAction(this); + m_scaleBarAction->setCheckable(true); + m_scaleBarAction->setToolTip("Enable scale bar; click arrow to edit"); + m_scaleBarAction->setIcon(QIcon(scaleBarPixmap)); + m_scaleBarAction->setMenu(createScaleBarMenu()); + QObject::connect(m_scaleBarAction, &QAction::triggered, + this, &BrainBrowserWindowToolBarTab::scaleBarActionToggled); + + scaleBarToolButton->setDefaultAction(m_scaleBarAction); + WuQtUtilities::setToolButtonStyleForQt5Mac(scaleBarToolButton); + m_macroRecordingLabel = new QLabel(""); + QHBoxLayout* yokeLayout = new QHBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(yokeLayout, 2, 0); + yokeLayout->addWidget(m_yokeToLabel); + yokeLayout->addWidget(m_yokingGroupComboBox->getWidget()); + yokeLayout->addStretch(); + + QHBoxLayout* buttonsLayout = new QHBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(buttonsLayout, 2, 2); + buttonsLayout->addWidget(lightingToolButton); + buttonsLayout->addWidget(clippingPlanesToolButton); + buttonsLayout->addWidget(scaleBarToolButton); + buttonsLayout->addStretch(); + QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 0); - layout->addWidget(m_yokeToLabel); - layout->addWidget(m_yokingGroupComboBox->getWidget()); - layout->addWidget(m_lockWindowAndAllTabAspectButton); - layout->addWidget(m_lightingEnabledCheckBox); - layout->addWidget(m_macroRecordingLabel); + layout->addLayout(yokeLayout); + layout->addWidget(m_lockWindowAndAllTabAspectButton, 0, Qt::AlignLeft); + layout->addLayout(buttonsLayout); + layout->addWidget(m_macroRecordingLabel, 0, Qt::AlignLeft); addToWidgetGroup(m_yokeToLabel); addToWidgetGroup(m_yokingGroupComboBox->getWidget()); - addToWidgetGroup(m_lightingEnabledCheckBox); + addToWidgetGroup(lightingToolButton); + addToWidgetGroup(clippingPlanesToolButton); + addToWidgetGroup(scaleBarToolButton); addToWidgetGroup(m_macroRecordingLabel); } @@ -141,6 +229,7 @@ blockAllSignals(true); bool chartFlag = false; + bool mediaFlag(false); switch (browserTabContent->getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_CHART: chartFlag = true; @@ -150,6 +239,9 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + mediaFlag = true; + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: @@ -161,15 +253,21 @@ } if (chartFlag) { - m_yokeToLabel->setText("Chart Yoking:"); m_yokingGroupComboBox->setSelectedItem(browserTabContent->getChartModelYokingGroup()); + m_yokeToLabel->setEnabled(false); + m_yokingGroupComboBox->getWidget()->setEnabled(false); + } + else if (mediaFlag) { + m_yokeToLabel->setEnabled(false); + m_yokingGroupComboBox->getWidget()->setEnabled(false); } else { - m_yokeToLabel->setText("Yoking:"); + m_yokeToLabel->setEnabled(true); + m_yokingGroupComboBox->getWidget()->setEnabled(true); m_yokingGroupComboBox->setSelectedItem(browserTabContent->getBrainModelYokingGroup()); } - m_lightingEnabledCheckBox->setChecked(browserTabContent->isLightingEnabled()); + m_lightingAction->setChecked(browserTabContent->isLightingEnabled()); m_macroRecordingLabel->setText(""); switch (WuQMacroManager::instance()->getMode()) { @@ -183,6 +281,9 @@ break; } + m_clippingPlanesAction->setChecked(browserTabContent->isClippingPlanesEnabled()); + m_scaleBarAction->setChecked(browserTabContent->getScaleBar()->isDisplayed()); + blockAllSignals(false); } @@ -225,6 +326,9 @@ break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + //CaretAssertToDoWarning(); + break; case ModelTypeEnum::MODEL_TYPE_SURFACE: break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: @@ -247,3 +351,168 @@ this->updateGraphicsWindow(); } + +/** + * @return Instance of the clipping menu + */ +QMenu* +BrainBrowserWindowToolBarTab::createClippingPlanesMenu() +{ + m_clippingPlanesWidget = new ClippingPlanesWidget(m_objectNamePrefix); + QWidgetAction* clippingPlanesAction = new QWidgetAction(this); + clippingPlanesAction->setDefaultWidget(m_clippingPlanesWidget); + + QMenu* menu = new QMenu(this); + menu->addAction(clippingPlanesAction); + QObject::connect(menu, &QMenu::aboutToShow, + this, &BrainBrowserWindowToolBarTab::clippingPlanesMenuAboutToShow); + return menu; +} + +/** + * Called when clipping planes action is toggled + * @param checked + * New checked status + */ +void +BrainBrowserWindowToolBarTab::clippingPlanesActionToggled(bool checked) +{ + BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); + if (browserTabContent != NULL) { + browserTabContent->setClippingPlanesEnabled(checked); + updateContent(browserTabContent); + updateGraphicsWindow(); + } +} + +/** + * Called when clipping menu is about to show + */ +void +BrainBrowserWindowToolBarTab::clippingPlanesMenuAboutToShow() +{ + int32_t tabIndex = -1; + BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); + if (browserTabContent != NULL) { + tabIndex = browserTabContent->getTabNumber(); + } + + m_clippingPlanesWidget->updateContent(tabIndex); +} + +/** + * @return Instance of the scale bar menu + */ +QMenu* +BrainBrowserWindowToolBarTab::createScaleBarMenu() +{ + m_scaleBarWidget = new ScaleBarWidget(m_objectNamePrefix); + QWidgetAction* scaleBarAction = new QWidgetAction(this); + scaleBarAction->setDefaultWidget(m_scaleBarWidget); + + QMenu* menu = new QMenu(this); + menu->addAction(scaleBarAction); + QObject::connect(menu, &QMenu::aboutToShow, + this, &BrainBrowserWindowToolBarTab::scaleBarMenuAboutToShow); + return menu; +} + +/** + * Called when scale bar action is toggled + * @param checked + * New checked status + */ +void +BrainBrowserWindowToolBarTab::scaleBarActionToggled(bool checked) +{ + BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); + if (browserTabContent != NULL) { + browserTabContent->getScaleBar()->setDisplayed(checked); + updateContent(browserTabContent); + updateGraphicsWindow(); + } +} + +/** + * Called when scale bar is about to show + */ +void +BrainBrowserWindowToolBarTab::scaleBarMenuAboutToShow() +{ + BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); + m_scaleBarWidget->updateContent(browserTabContent); +} + + +/** + * Create an ruler icon for Scale Bar tool button + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @return + * Pixmap with icon + */ + +QPixmap +BrainBrowserWindowToolBarTab::createScaleBarPixmap(const QWidget* widget) +{ + CaretAssert(widget); + + /* + * Create a small, square pixmap that will contain + * the foreground color around the pixmap's perimeter. + */ + float width = 24.0; + float height = 24.0; + float margin = 1.0; + + QPixmap pixmap(static_cast(width), + static_cast(height)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, + pixmap); + + const float leftX(margin); + const float rightX(width - margin); + const float centerX((leftX + rightX) / 2.0); + const float leftCenterX((leftX + centerX) / 2.0); + const float rightCenterX((rightX + centerX) / 2.0); + const float y(margin + 2.0); + const float smallTickHeight(8.0); + const float mediumTickHeight(smallTickHeight * 1.3); + const float bigTickHeight(smallTickHeight * 1.65); + + QPen pen = painter->pen(); + pen.setWidth(4); + painter->setPen(pen); + + /* bottom line */ + painter->drawLine(QLineF(leftX, y, + rightX, y)); + + pen.setWidth(2); + painter->setPen(pen); + + /* left tick */ + painter->drawLine(QLineF(leftX, y, + leftX, y + bigTickHeight)); + + /* right tick */ + painter->drawLine(QLineF(rightX, margin, + rightX, y + bigTickHeight)); + + /* center tick */ + painter->drawLine(QLineF(centerX, y, + centerX, y + mediumTickHeight)); + + /* left-center tick */ + painter->drawLine(QLineF(leftCenterX, y, + leftCenterX, y + smallTickHeight)); + + /* right-center tick */ + painter->drawLine(QLineF(rightCenterX, y, + rightCenterX, y + smallTickHeight)); + + return pixmap; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarTab.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarTab.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarTab.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarTab.h 2021-02-16 19:46:47.000000000 +0000 @@ -25,13 +25,17 @@ #include "BrainBrowserWindowToolBarComponent.h" +class QAction; class QCheckBox; class QLabel; +class QMenu; class QToolButton; namespace caret { class BrainBrowserWindowToolBar; + class ClippingPlanesWidget; class EnumComboBoxTemplate; + class ScaleBarWidget; class BrainBrowserWindowToolBarTab : public BrainBrowserWindowToolBarComponent { Q_OBJECT @@ -54,11 +58,27 @@ void lightingEnabledCheckBoxChecked(bool checked); + void clippingPlanesActionToggled(bool checked); + + void clippingPlanesMenuAboutToShow(); + + void scaleBarActionToggled(bool checked); + + void scaleBarMenuAboutToShow(); + private: BrainBrowserWindowToolBarTab(const BrainBrowserWindowToolBarTab&); BrainBrowserWindowToolBarTab& operator=(const BrainBrowserWindowToolBarTab&); + QMenu* createClippingPlanesMenu(); + + QMenu* createScaleBarMenu(); + + QPixmap createScaleBarPixmap(const QWidget* widget); + + QString m_objectNamePrefix; + QLabel* m_yokeToLabel; EnumComboBoxTemplate* m_yokingGroupComboBox; @@ -69,10 +89,18 @@ QToolButton* m_lockWindowAndAllTabAspectButton; - QCheckBox* m_lightingEnabledCheckBox; + QAction* m_lightingAction; QLabel* m_macroRecordingLabel; + QAction* m_clippingPlanesAction; + + ClippingPlanesWidget* m_clippingPlanesWidget; + + QAction* m_scaleBarAction; + + ScaleBarWidget* m_scaleBarWidget; + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,9 +23,22 @@ #include "BrainBrowserWindowToolBarTabPopUpMenu.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_POP_UP_MENU_DECLARE__ +#include "AnnotationBrowserTab.h" +#include "AnnotationManager.h" +#include "Brain.h" #include "BrainBrowserWindowToolBar.h" +#include "BrowserTabContent.h" +#include "BrowserWindowContent.h" #include "CaretAssert.h" +#include "EventBrowserTabGet.h" +#include "EventBrowserWindowContent.h" +#include "EventGetOrSetUserInputModeProcessor.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" #include "WuQTabBar.h" + using namespace caret; @@ -63,6 +76,40 @@ m_numberOfTabs = m_toolBar->tabBar->count(); m_activeTabIndex = m_toolBar->tabBar->currentIndex(); + m_browserWindowIndex = m_toolBar->browserWindowIndex; + std::unique_ptr windowContentEvent = EventBrowserWindowContent::getWindowContent(m_browserWindowIndex); + EventManager::get()->sendEvent(windowContentEvent->getPointer()); + const BrowserWindowContent* browserWindowContent = windowContentEvent->getBrowserWindowContent(); + + if (m_tabIndexUnderMouse >= 0) { + BrowserTabContent* tabContent = m_toolBar->getTabContentFromTab(m_tabIndexUnderMouse); + m_selectedBrowserTabAnnotation = NULL; + m_browserTabContentIndex = -1; + if (tabContent != NULL) { + m_browserTabContentIndex = tabContent->getTabNumber(); + + m_selectedBrowserTabAnnotation = tabContent->getManualLayoutBrowserTabAnnotation(); + } + } + + if (browserWindowContent != NULL) { + if (browserWindowContent->isManualModeTileTabsConfigurationEnabled()) { + if (m_selectedBrowserTabAnnotation != NULL) { + addItem(MenuItem::MANUAL_LAYOUT_SELECT_FOR_EDITING); + + if (m_selectedBrowserTabAnnotation->isBrowserTabDisplayed()) { + addItem(MenuItem::MANUAL_LAYOUT_SET_VISIBLE, + "Hide Tab Content in Window"); + } + else { + addItem(MenuItem::MANUAL_LAYOUT_SET_VISIBLE, + "Show Tab Content in Window"); + } + addSeparator(); + } + } + } + addItem(MenuItem::CREATE_NEW_TAB_BEFORE); addItem(MenuItem::CREATE_NEW_TAB_AFTER); addSeparator(); @@ -96,18 +143,19 @@ * Item to be added to menu. */ void -BrainBrowserWindowToolBarTabPopUpMenu::addItem(const MenuItem menuItem) +BrainBrowserWindowToolBarTabPopUpMenu::addItem(const MenuItem menuItem, + const QString& overrideMenuItemText) { QString thisTabName = "This Tab"; QString activeTabName = "Active Tab"; + const int32_t activeTabIndex = m_toolBar->tabBar->currentIndex(); const bool includeNamesInMenuFlag = false; if (includeNamesInMenuFlag) { if (m_tabIndexUnderMouse >= 0) { thisTabName = ("Tab \"" + m_toolBar->tabBar->tabText(m_tabIndexUnderMouse) + "\""); } - const int32_t activeTabIndex = m_toolBar->tabBar->currentIndex(); if (activeTabIndex >= 0) { activeTabName = ("Tab \"" + m_toolBar->tabBar->tabText(activeTabIndex) + "\""); } @@ -136,6 +184,12 @@ case MenuItem::DUPLICATE_TAB_AT_END: text = "Duplicate " + thisTabName + " at End"; break; + case MenuItem::MANUAL_LAYOUT_SELECT_FOR_EDITING: + text = "Select for Manual Layout Editing"; + break; + case MenuItem::MANUAL_LAYOUT_SET_VISIBLE: + text = "Visible in Manual Layout"; + break; case MenuItem::MOVE_TAB_TO_BEGINNING: text = "Move " + thisTabName + " to Beginning"; break; @@ -153,6 +207,10 @@ break; } + if ( ! overrideMenuItemText.isEmpty()) { + text = overrideMenuItemText; + } + QAction* action = addAction(text); action->setData(static_cast(menuItem)); action->setEnabled(isEnabled(menuItem)); @@ -168,6 +226,9 @@ BrainBrowserWindowToolBarTabPopUpMenu::menuItemSelected(QAction* action) { if (action != NULL) { + bool updateGraphicsFlag(false); + bool updateUserIterfaceFlag(false); + const BrowserTabContent* activeTabContent = m_toolBar->getTabContentFromTab(m_activeTabIndex); MenuItem menuItem = static_cast(action->data().toInt()); @@ -201,6 +262,25 @@ case MenuItem::DUPLICATE_TAB_AT_END: duplicateToIndex = m_numberOfTabs; break; + case MenuItem::MANUAL_LAYOUT_SELECT_FOR_EDITING: + if (m_selectedBrowserTabAnnotation != NULL) { + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + CaretAssert(annMan); + annMan->selectAnnotationForEditing(m_browserWindowIndex, + AnnotationManager::SELECTION_MODE_EXTENDED, + true, + m_selectedBrowserTabAnnotation); + updateGraphicsFlag = true; + updateUserIterfaceFlag = true; + } + break; + case MenuItem::MANUAL_LAYOUT_SET_VISIBLE: + if (m_selectedBrowserTabAnnotation != NULL) { + m_selectedBrowserTabAnnotation->setBrowserTabDisplayed( ! m_selectedBrowserTabAnnotation->isBrowserTabDisplayed()); + updateGraphicsFlag = true; + updateUserIterfaceFlag = true; + } + break; case MenuItem::MOVE_TAB_TO_BEGINNING: moveToIndex = 0; break; @@ -259,6 +339,13 @@ if (updatedActiveTabIndex >= 0) { m_toolBar->tabBar->setCurrentIndex(updatedActiveTabIndex); } + + if (updateGraphicsFlag) { + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); + } + if (updateUserIterfaceFlag) { + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + } } } @@ -295,6 +382,36 @@ enabledFlag = true; } break; + case MenuItem::MANUAL_LAYOUT_SELECT_FOR_EDITING: + { + EventGetOrSetUserInputModeProcessor inputProcessorEvent(m_browserWindowIndex); + EventManager::get()->sendEvent(inputProcessorEvent.getPointer()); + switch (inputProcessorEvent.getUserInputMode()) { + case UserInputModeEnum::Enum::ANNOTATIONS: + break; + case UserInputModeEnum::Enum::BORDERS: + break; + case UserInputModeEnum::Enum::FOCI: + break; + case UserInputModeEnum::Enum::IMAGE: + break; + case UserInputModeEnum::Enum::INVALID: + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + if (m_selectedBrowserTabAnnotation != NULL) { + enabledFlag = true; + } + break; + case UserInputModeEnum::Enum::VIEW: + break; + case UserInputModeEnum::Enum::VOLUME_EDIT: + break; + } + } + break; + case MenuItem::MANUAL_LAYOUT_SET_VISIBLE: + enabledFlag = (m_selectedBrowserTabAnnotation != NULL); + break; case MenuItem::MOVE_TAB_TO_BEGINNING: if (m_tabIndexUnderMouse > 0) { enabledFlag = true; diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h 2021-02-16 19:46:47.000000000 +0000 @@ -29,6 +29,7 @@ namespace caret { + class AnnotationBrowserTab; class BrainBrowserWindowToolBar; class BrainBrowserWindowToolBarTabPopUpMenu : public QMenu { @@ -50,6 +51,8 @@ private: enum class MenuItem { NONE, + MANUAL_LAYOUT_SELECT_FOR_EDITING, + MANUAL_LAYOUT_SET_VISIBLE, CREATE_NEW_TAB_BEFORE, CREATE_NEW_TAB_AFTER, DUPLICATE_TAB_AT_BEGINNING, @@ -67,7 +70,8 @@ BrainBrowserWindowToolBarTabPopUpMenu& operator=(const BrainBrowserWindowToolBarTabPopUpMenu&); - void addItem(const MenuItem menuItem); + void addItem(const MenuItem menuItem, + const QString& overrideMenuItemText = ""); bool isEnabled(const MenuItem menuItem) const; @@ -79,6 +83,12 @@ int32_t m_activeTabIndex; + int32_t m_browserTabContentIndex = -1; + + int32_t m_browserWindowIndex = -1; + + AnnotationBrowserTab* m_selectedBrowserTabAnnotation = NULL; + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarView.cxx connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarView.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarView.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarView.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,279 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_VIEW_DECLARE__ +#include "BrainBrowserWindowToolBarView.h" +#undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_VIEW_DECLARE__ + +#include +#include +#include + +#include "Brain.h" +#include "BrainBrowserWindowToolBar.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "GuiManager.h" +#include "SessionManager.h" +#include "WuQMacroManager.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::BrainBrowserWindowToolBarView + * \brief Toolbar component for selection of the View Mode + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parentObjectName + * Name of the parent object + * @param parentToolBar + * The parent toolbar + */ +BrainBrowserWindowToolBarView::BrainBrowserWindowToolBarView(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar) +: BrainBrowserWindowToolBarComponent(parentToolBar) +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(parentObjectName + + ":ViewMode"); + + this->viewModeChartOneRadioButton = new QRadioButton("Chart Old"); + this->viewModeChartOneRadioButton->setToolTip("Show Old Chart View"); + this->viewModeChartOneRadioButton->setObjectName(objectNamePrefix + + ":ChartOld"); + macroManager->addMacroSupportToObject(this->viewModeChartOneRadioButton, + "Select Chart Old View"); + /* + * Chart Old is displayed only if an old scene was loaded + * that has a chart one model selected + */ + this->viewModeChartOneRadioButton->setVisible(false); + + this->viewModeChartTwoRadioButton = new QRadioButton("Chart"); + this->viewModeChartTwoRadioButton->setToolTip("Show Chart View"); + this->viewModeChartTwoRadioButton->setObjectName(objectNamePrefix + + ":Chart"); + macroManager->addMacroSupportToObject(this->viewModeChartTwoRadioButton, + "Select Chart View of Histogram, Lines, Matrices"); + + this->viewModeSurfaceRadioButton = new QRadioButton("Surface"); + this->viewModeSurfaceRadioButton->setToolTip("Show Surface View"); + this->viewModeSurfaceRadioButton->setObjectName(objectNamePrefix + + ":Surface"); + macroManager->addMacroSupportToObject(this->viewModeSurfaceRadioButton, + "Select surface view of single left/right/cerebellum"); + + this->viewModeSurfaceMontageRadioButton = new QRadioButton("Montage"); + this->viewModeSurfaceMontageRadioButton->setToolTip("Show Montage View"); + this->viewModeSurfaceMontageRadioButton->setObjectName(objectNamePrefix + + ":Montage"); + macroManager->addMacroSupportToObject(this->viewModeSurfaceMontageRadioButton, + "Select surface montage view of multiple views of left/right/cerebellum"); + + this->viewModeVolumeRadioButton = new QRadioButton("Volume"); + this->viewModeVolumeRadioButton->setToolTip("Show Volume View"); + this->viewModeVolumeRadioButton->setObjectName(objectNamePrefix + + ":Volume"); + macroManager->addMacroSupportToObject(this->viewModeVolumeRadioButton, + "Select volume slice view"); + + this->viewModeWholeBrainRadioButton = new QRadioButton("All"); + this->viewModeWholeBrainRadioButton->setToolTip("Show All View"); + this->viewModeWholeBrainRadioButton->setObjectName(objectNamePrefix + + ":All"); + macroManager->addMacroSupportToObject(this->viewModeWholeBrainRadioButton, + "Select all view that shows 3D view of surfaces and volume cubes/slices"); + + this->viewModeMediaRadioButton = new QRadioButton("Media"); + this->viewModeMediaRadioButton->setToolTip("Show Media View"); + this->viewModeMediaRadioButton->setObjectName(objectNamePrefix + + ":Media"); + macroManager->addMacroSupportToObject(this->viewModeMediaRadioButton, + "Select Media View of Images"); + + QVBoxLayout* layout = new QVBoxLayout(this); +#ifdef CARET_OS_MACOSX + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); +#else + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 2); +#endif + layout->addWidget(this->viewModeSurfaceMontageRadioButton); + layout->addWidget(this->viewModeVolumeRadioButton); + layout->addWidget(this->viewModeWholeBrainRadioButton); + layout->addWidget(this->viewModeChartTwoRadioButton); + layout->addWidget(this->viewModeSurfaceRadioButton); + layout->addWidget(this->viewModeMediaRadioButton); + layout->addWidget(this->viewModeChartOneRadioButton); + + QButtonGroup* viewModeRadioButtonGroup = new QButtonGroup(this); + viewModeRadioButtonGroup->addButton(this->viewModeChartOneRadioButton); + viewModeRadioButtonGroup->addButton(this->viewModeChartTwoRadioButton); + viewModeRadioButtonGroup->addButton(this->viewModeSurfaceRadioButton); + viewModeRadioButtonGroup->addButton(this->viewModeSurfaceMontageRadioButton); + viewModeRadioButtonGroup->addButton(this->viewModeVolumeRadioButton); + viewModeRadioButtonGroup->addButton(this->viewModeWholeBrainRadioButton); + viewModeRadioButtonGroup->addButton(this->viewModeMediaRadioButton); + QObject::connect(viewModeRadioButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), + this, SLOT(viewModeRadioButtonClicked(QAbstractButton*))); + + addToWidgetGroup(this->viewModeChartOneRadioButton); + addToWidgetGroup(this->viewModeChartTwoRadioButton); + addToWidgetGroup(this->viewModeSurfaceRadioButton); + addToWidgetGroup(this->viewModeSurfaceMontageRadioButton); + addToWidgetGroup(this->viewModeVolumeRadioButton); + addToWidgetGroup(this->viewModeWholeBrainRadioButton); + addToWidgetGroup(this->viewModeMediaRadioButton); +} + +/** + * Destructor. + */ +BrainBrowserWindowToolBarView::~BrainBrowserWindowToolBarView() +{ +} + +/** + * Update the surface montage options widget. + * + * @param browserTabContent + * The active model display controller (may be NULL). + */ +void +BrainBrowserWindowToolBarView::updateContent(BrowserTabContent* browserTabContent) +{ + ModelTypeEnum::Enum modelType = ModelTypeEnum::MODEL_TYPE_INVALID; + if (browserTabContent != NULL) { + modelType = browserTabContent->getSelectedModelType(); + } + + blockAllSignals(true); + + /* + * Enable buttons for valid types + */ + bool hideChartOneFlag(true); + if (browserTabContent != NULL) { + this->viewModeSurfaceRadioButton->setEnabled(browserTabContent->isSurfaceModelValid()); + this->viewModeSurfaceMontageRadioButton->setEnabled(browserTabContent->isSurfaceMontageModelValid()); + this->viewModeVolumeRadioButton->setEnabled(browserTabContent->isVolumeSliceModelValid()); + this->viewModeWholeBrainRadioButton->setEnabled(browserTabContent->isWholeBrainModelValid()); + this->viewModeChartOneRadioButton->setEnabled(browserTabContent->isChartOneModelValid()); + this->viewModeChartTwoRadioButton->setEnabled(browserTabContent->isChartTwoModelValid()); + this->viewModeMediaRadioButton->setEnabled(browserTabContent->isMediaModelValid()); + if (SessionManager::get()->hasSceneWithChartOld()) { + hideChartOneFlag = false; + } + else { + hideChartOneFlag = true; + } + } + else { + hideChartOneFlag = true; + this->viewModeSurfaceRadioButton->setEnabled(false); + this->viewModeSurfaceMontageRadioButton->setEnabled(false); + this->viewModeVolumeRadioButton->setEnabled(false); + this->viewModeWholeBrainRadioButton->setEnabled(false); + this->viewModeChartOneRadioButton->setEnabled(false); + this->viewModeChartTwoRadioButton->setEnabled(false); + this->viewModeMediaRadioButton->setEnabled(false); + } + + this->viewModeChartOneRadioButton->setHidden(hideChartOneFlag); + + switch (modelType) { + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + this->viewModeMediaRadioButton->setChecked(true); + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + this->viewModeSurfaceRadioButton->setChecked(true); + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + this->viewModeSurfaceMontageRadioButton->setChecked(true); + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + this->viewModeVolumeRadioButton->setChecked(true); + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + this->viewModeWholeBrainRadioButton->setChecked(true); + break; + case ModelTypeEnum::MODEL_TYPE_CHART: + this->viewModeChartOneRadioButton->setChecked(true); + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + this->viewModeChartTwoRadioButton->setChecked(true); + break; + } + + blockAllSignals(false); +} + +/** + * Called when a view mode is selected. + */ +void +BrainBrowserWindowToolBarView::viewModeRadioButtonClicked(QAbstractButton*) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + if (btc == NULL) { + return; + } + + if (this->viewModeChartOneRadioButton->isChecked()) { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_CHART); + } + else if (this->viewModeChartTwoRadioButton->isChecked()) { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_CHART_TWO); + } + else if (this->viewModeSurfaceRadioButton->isChecked()) { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_SURFACE); + } + else if (this->viewModeSurfaceMontageRadioButton->isChecked()) { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE); + } + else if (this->viewModeVolumeRadioButton->isChecked()) { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES); + } + else if (this->viewModeWholeBrainRadioButton->isChecked()) { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN); + } + else if (this->viewModeMediaRadioButton->isChecked()) { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA); + } + else { + btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_INVALID); + } + + getParentToolBar()->updateToolBar(); + getParentToolBar()->updateTabName(-1); + getParentToolBar()->updateToolBox(); + getParentToolBar()->emitViewModelChangedSignal(); + getParentToolBar()->updateGraphicsWindow(); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarView.h connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarView.h --- connectome-workbench-1.4.2/src/GuiQt/BrainBrowserWindowToolBarView.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainBrowserWindowToolBarView.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,74 @@ +#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_VIEW_H__ +#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_VIEW_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "BrainBrowserWindowToolBarComponent.h" +#include "ModelTypeEnum.h" + +class QAbstractButton; +class QRadioButton; + +namespace caret { + + class BrainBrowserWindowToolBarView : public BrainBrowserWindowToolBarComponent { + Q_OBJECT + + public: + BrainBrowserWindowToolBarView(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar); + + virtual ~BrainBrowserWindowToolBarView(); + + BrainBrowserWindowToolBarView(const BrainBrowserWindowToolBarView&) = delete; + + BrainBrowserWindowToolBarView& operator=(const BrainBrowserWindowToolBarView&) = delete; + + virtual void updateContent(BrowserTabContent* browserTabContent) override; + + // ADD_NEW_METHODS_HERE + + private slots: + void viewModeRadioButtonClicked(QAbstractButton*); + + private: + QRadioButton* viewModeSurfaceRadioButton; + QRadioButton* viewModeSurfaceMontageRadioButton; + QRadioButton* viewModeVolumeRadioButton; + QRadioButton* viewModeWholeBrainRadioButton; + QRadioButton* viewModeChartOneRadioButton; + QRadioButton* viewModeChartTwoRadioButton; + QRadioButton* viewModeMediaRadioButton; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_VIEW_DECLARE__ + // +#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_VIEW_DECLARE__ + +} // namespace +#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_VIEW_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainOpenGLWidget.cxx connectome-workbench-1.5.0/src/GuiQt/BrainOpenGLWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/BrainOpenGLWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainOpenGLWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -27,12 +27,14 @@ #include #include +#include #include #include #include #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET #endif #include +#include #include #include @@ -60,14 +62,17 @@ #include "EventManager.h" #include "EventBrowserWindowDrawingContent.h" #include "EventBrowserWindowGraphicsRedrawn.h" +#include "EventGetOrSetUserInputModeProcessor.h" #include "EventGraphicsTimingOneWindow.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" -#include "EventGetOrSetUserInputModeProcessor.h" +#include "EventGraphicsWindowShowToolTip.h" #include "EventIdentificationRequest.h" #include "EventMovieManualModeRecording.h" #include "EventUserInterfaceUpdate.h" +#include "EventUserInputModeGet.h" #include "FtglFontTextRenderer.h" +#include "GestureEvent.h" #include "GuiManager.h" #include "ImageFile.h" #include "KeyEvent.h" @@ -83,11 +88,13 @@ #include "SelectionItemVoxelEditing.h" #include "SessionManager.h" #include "Surface.h" -#include "TileTabsConfiguration.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "TileTabsLayoutManualConfiguration.h" #include "UserInputModeAnnotations.h" #include "UserInputModeBorders.h" #include "UserInputModeFoci.h" #include "UserInputModeImage.h" +#include "UserInputModeTileTabsManualLayout.h" #include "UserInputModeView.h" #include "UserInputModeVolumeEdit.h" #include "WuQMacroManager.h" @@ -132,8 +139,6 @@ + AString::number(windowIndex + 1) + ":OpenGLWidget"); - this->borderBeingDrawn = new Border(); - m_mousePositionValid = false; m_mousePositionEvent.grabNew(new MouseEvent(NULL, NULL, @@ -144,17 +149,9 @@ 0, 0, 0, + m_mouseHistoryXY, false)); - this->userInputAnnotationsModeProcessor = new UserInputModeAnnotations(windowIndex); - this->userInputBordersModeProcessor = new UserInputModeBorders(this->borderBeingDrawn, - windowIndex); - this->userInputFociModeProcessor = new UserInputModeFoci(windowIndex); - this->userInputImageModeProcessor = new UserInputModeImage(windowIndex); - this->userInputVolumeEditModeProcessor = new UserInputModeVolumeEdit(windowIndex); - this->userInputViewModeProcessor = new UserInputModeView(); - this->selectedUserInputProcessor = this->userInputViewModeProcessor; - this->selectedUserInputProcessor->initialize(); this->mousePressX = -10000; this->mousePressY = -10000; this->mouseNewDraggingStartedFlag = false; @@ -173,7 +170,7 @@ EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_TIMING_ONE_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_REQUEST); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IMAGE_CAPTURE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MOVIE_RECORDING_MANUAL_MODE_CAPTURE); @@ -215,6 +212,11 @@ #endif s_brainOpenGLWidgets.insert(this); + + /* + * Get pinch gesture event + */ + grabGesture(Qt::PinchGesture); } /** @@ -226,16 +228,6 @@ this->clearDrawingViewportContents(); - delete this->userInputViewModeProcessor; - delete this->userInputAnnotationsModeProcessor; - delete this->userInputBordersModeProcessor; - delete this->userInputFociModeProcessor; - delete this->userInputImageModeProcessor; - delete this->userInputVolumeEditModeProcessor; - this->selectedUserInputProcessor = NULL; /* DO NOT DELETE since it does not own the object to which it points */ - - delete this->borderBeingDrawn; - EventManager::get()->removeAllEventsFromListener(this); s_brainOpenGLWidgets.erase(this); @@ -424,15 +416,6 @@ } /** - * @return Pointer to the border that is being drawn. - */ -Border* -BrainOpenGLWidget::getBorderBeingDrawn() -{ - return this->borderBeingDrawn; -} - -/** * Clear the contents for drawing into the viewports. */ void @@ -450,7 +433,7 @@ /* * Set the cursor to that requested by the user input processor */ - CursorEnum::Enum cursor = this->selectedUserInputProcessor->getCursor(); + CursorEnum::Enum cursor = getSelectedInputProcessor()->getCursor(); GuiManager::get()->getCursorManager()->setCursorForWidget(this, cursor); @@ -484,7 +467,7 @@ windowContent); s_singletonOpenGL->drawModels(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + getSelectedInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, windowContent.getAllTabViewports()); @@ -537,6 +520,13 @@ return; } + const int32_t windowViewportBeforeAspectLocking[4] = { + windowViewportIn[0], + windowViewportIn[1], + windowViewportIn[2], + windowViewportIn[3] + }; + int32_t windowViewport[4] = { windowViewportIn[0], windowViewportIn[1], @@ -552,12 +542,16 @@ } } + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + /* * Highlighting of border points */ s_singletonOpenGL->setDrawHighlightedEndPoints(false); - if (this->selectedUserInputProcessor == this->userInputBordersModeProcessor) { - s_singletonOpenGL->setDrawHighlightedEndPoints(this->userInputBordersModeProcessor->isHighlightBorderEndPoints()); + if (inputProcessor->getUserInputMode() == UserInputModeEnum::Enum::BORDERS) { + UserInputModeBorders* borderInputProcessor = dynamic_cast(inputProcessor); + CaretAssert(borderInputProcessor); + s_singletonOpenGL->setDrawHighlightedEndPoints(borderInputProcessor->isHighlightBorderEndPoints()); } const GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); @@ -571,46 +565,96 @@ BrowserWindowContent* browserWindowContent = getModelEvent.getBrowserWindowContent(); CaretAssert(browserWindowContent); - if (browserWindowContent->isTileTabsEnabled() - && (numberOfTabs > 1)) { + /* + * Prior to WB-859 Manual Tile Tabs Layout System: BrainOpenGLViewportContent::createViewportContentForTileTabs() was only + * called IF tile tabs was enabled AND there was more than one tab. However, we want a manual layout to function correctly, + * even if there is only one tab. So when tile tabs enabled and one tab, draw as single tab for Automatic Grid and Custom Grid + * but draw using tile tabs for a Manual Configuration. + */ + bool drawUsingTileTabsFlag(false); + if (browserWindowContent->isTileTabsEnabled()) { + if (numberOfTabs > 1) { + drawUsingTileTabsFlag = true; + } + else if (numberOfTabs == 1) { + switch (browserWindowContent->getTileTabsConfigurationMode()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + drawUsingTileTabsFlag = true; + break; + } + } + } + + if (drawUsingTileTabsFlag) { const int32_t windowWidth = windowViewport[2]; const int32_t windowHeight = windowViewport[3]; - std::vector rowHeights; - std::vector columnsWidths; - - /* - * Determine if default configuration for tiles - */ - TileTabsConfiguration* tileTabsConfiguration = browserWindowContent->getSelectedTileTabsConfiguration(); - CaretAssert(tileTabsConfiguration); - - /* - * Get the sizes of the tab tiles from the tile tabs configuration - */ - if (tileTabsConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, - windowHeight, - numberOfTabs, - browserWindowContent->getTileTabsConfigurationMode(), - rowHeights, - columnsWidths)) { - - /* - * Create the viewport drawing contents for all tabs - */ - - std::vector tabViewportContent = BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabs, - browserWindowContent, - gapsAndMargins, - windowViewport, - this->windowIndex, - getModelEvent.getTabIndexForTileTabsHighlighting()); - for (auto tabvp : tabViewportContent) { - windowContent.addTabViewport(tabvp); + const TileTabsLayoutConfigurationTypeEnum::Enum tileTabsConfigType = browserWindowContent->getTileTabsConfigurationMode(); + switch (tileTabsConfigType) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + { + std::vector rowHeights; + std::vector columnsWidths; + + /* + * Determine if default configuration for tiles + */ + TileTabsLayoutBaseConfiguration* tileTabsConfiguration = browserWindowContent->getSelectedTileTabsGridConfiguration(); + CaretAssert(tileTabsConfiguration); + + TileTabsLayoutGridConfiguration* gridConfiguration = tileTabsConfiguration->castToGridConfiguration(); + CaretAssert(gridConfiguration); + if (gridConfiguration != NULL) { + /* + * Get the sizes of the tab tiles from the tile tabs configuration + */ + if (gridConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, + windowHeight, + numberOfTabs, + browserWindowContent->getTileTabsConfigurationMode(), + rowHeights, + columnsWidths)) { + + /* + * Create the viewport drawing contents for all tabs + */ + std::vector tabViewportContent = BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabs, + browserWindowContent, + gapsAndMargins, + windowViewportBeforeAspectLocking, + windowViewport, + this->windowIndex, + getModelEvent.getTabIndexForTileTabsHighlighting()); + for (auto tabvp : tabViewportContent) { + windowContent.addTabViewport(tabvp); + } + } + else { + CaretLogSevere("Tile Tabs Row/Column sizing failed !!!"); + } + } } - } - else { - CaretLogSevere("Tile Tabs Row/Column sizing failed !!!"); + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + /* + * Create the viewport drawing contents for all tabs + */ + std::vector tabViewportContent = BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabs, + browserWindowContent, + gapsAndMargins, + windowViewportBeforeAspectLocking, + windowViewport, + this->windowIndex, + getModelEvent.getTabIndexForTileTabsHighlighting()); + for (auto tabvp : tabViewportContent) { + windowContent.addTabViewport(tabvp); + } + break; } } else if (numberOfTabs >= 1) { @@ -618,6 +662,7 @@ getModelEvent.getSelectedBrowserTabContent(), gapsAndMargins, this->windowIndex, + windowViewportBeforeAspectLocking, windowViewport); windowContent.addTabViewport(vc); } @@ -627,6 +672,7 @@ NULL, gapsAndMargins, windowIndex, + windowViewportBeforeAspectLocking, windowViewport); windowContent.setWindowViewport(windowViewportContent); } @@ -658,14 +704,18 @@ getDrawingWindowContent(windowViewport, m_windowContent); - if (this->selectedUserInputProcessor == userInputBordersModeProcessor) { - s_singletonOpenGL->setBorderBeingDrawn(this->borderBeingDrawn); + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + const UserInputModeEnum::Enum inputMode = inputProcessor->getUserInputMode(); + if (inputMode == UserInputModeEnum::Enum::BORDERS) { + UserInputModeBorders* borderInputMode = dynamic_cast(inputProcessor); + CaretAssert(borderInputMode); + s_singletonOpenGL->setBorderBeingDrawn(borderInputMode->getBorderBeingDrawn()); } else { s_singletonOpenGL->setBorderBeingDrawn(NULL); } s_singletonOpenGL->drawModels(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + inputMode, GuiManager::get()->getBrain(), m_contextShareGroupPointer, m_windowContent.getAllTabViewports()); @@ -685,8 +735,8 @@ bool BrainOpenGLWidget::event(QEvent* event) { - if (SessionManager::get()->getDataToolTipsManager()->isEnabled()) { - if (event->type() == QEvent::ToolTip) { + if (event->type() == QEvent::ToolTip) { + if (SessionManager::get()->getDataToolTipsManager()->isEnabled()) { QHelpEvent* helpEvent = static_cast(event); CaretAssert(helpEvent); @@ -729,7 +779,18 @@ return true; } } - + else if (event->type() == QEvent::Gesture) { + if (SessionManager::get()->getCaretPreferences()->isGuiGesturesEnabled()) { + /* + * Qt doc (https://doc.qt.io/qt-5/gestures-overview.html#) shows using static_cast not dynamic_cast. + */ + QGestureEvent* gestureEvent = static_cast(event); + if (processGestureEvent(gestureEvent)) { + return true; + } + } + } + #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET return QOpenGLWidget::event(event); #else @@ -737,6 +798,109 @@ #endif } +/** + * Process a gesture event (pinch or zoom on trackpad) + */ +bool +BrainOpenGLWidget::processGestureEvent(QGestureEvent* gestureEvent) +{ + /* + * Disable until more time available for proper implementation + */ + const bool enabledFlag(true); + if ( ! enabledFlag) { + return false; + } + + QGesture* pinchGesture = gestureEvent->gesture(Qt::PinchGesture); + if (pinchGesture != NULL) { + QPinchGesture* pinch = static_cast(pinchGesture); + const QPointF startPoint = pinch->startCenterPoint(); + + GestureEvent::State gestureState = GestureEvent::State::START; + bool validFlag(false); + switch (pinch->state()) { + case Qt::GestureCanceled: + break; + case Qt::GestureFinished: + gestureState = GestureEvent::State::END; + validFlag = true; + break; + case Qt::GestureStarted: + gestureState = GestureEvent::State::START; + validFlag = true; + break; + case Qt::GestureUpdated: + gestureState = GestureEvent::State::UPDATE; + validFlag = true; + break; + case Qt::NoGesture: + break; + } + + const int gestureStartX = startPoint.x(); + const int gestureStartY = height() - startPoint.y(); + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(gestureStartX, + gestureStartY); + if (viewportContent == NULL) { + validFlag = false; + } + + if (validFlag) { + CaretAssert(viewportContent); + + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + + QPinchGesture::ChangeFlags changeFlags = pinch->changeFlags(); + if (changeFlags & QPinchGesture::ScaleFactorChanged) { + float deltaDegrees = pinch->scaleFactor(); + if (deltaDegrees > 1.0) { + deltaDegrees = 1; + } + else { + deltaDegrees = -1; + } + + + /* + * Use location of mouse press so that the model + * being manipulated does not change if mouse moves + * out of its viewport without releasing the mouse + * button. + */ + if (viewportContent != NULL) { + GestureEvent gestureEvent(viewportContent, + this, + this->windowIndex, + gestureStartX, + gestureStartY, + gestureState, + GestureEvent::Type::PINCH, + pinch->scaleFactor()); + inputProcessor->gestureEvent(gestureEvent); + } + } + else if (changeFlags & QPinchGesture::RotationAngleChanged) { + GestureEvent gestureEvent(viewportContent, + this, + this->windowIndex, + gestureStartX, + gestureStartY, + gestureState, + GestureEvent::Type::ROTATE, + pinch->rotationAngle() - pinch->lastRotationAngle()); + + inputProcessor->gestureEvent(gestureEvent); + } + + gestureEvent->accept(); + return true; + } + } + + return false; +} + /** * Receive Content Menu events from Qt. @@ -752,6 +916,7 @@ const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { + std::vector emptyHistoryXY; MouseEvent mouseEvent(viewportContent, this, this->windowIndex, @@ -761,11 +926,14 @@ 0, mouseX, mouseY, + emptyHistoryXY, false); - this->selectedUserInputProcessor->showContextMenu(mouseEvent, - contextMenuEvent->globalPos(), - this); + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + + inputProcessor->showContextMenu(mouseEvent, + contextMenuEvent->globalPos(), + this); } } @@ -857,6 +1025,7 @@ const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, wheelY); if (viewportContent != NULL) { + std::vector emptyHistoryXY; MouseEvent mouseEvent(viewportContent, this, this->windowIndex, @@ -864,10 +1033,14 @@ wheelY, 0, deltaDegrees, - 0, - 0, + wheelX, + wheelY, + emptyHistoryXY, this->mouseNewDraggingStartedFlag); - this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); + + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + + inputProcessor->mouseLeftDragWithCtrl(mouseEvent); } we->accept(); @@ -924,13 +1097,35 @@ Qt::KeyboardModifiers keyModifiers = e->modifiers(); const bool shiftKeyDownFlag = ((keyModifiers & Qt::ShiftModifier) != 0); - KeyEvent keyEvent(this, + bool mouseValidFlag(false); + const QPoint mousePos = mapFromGlobal(QCursor::pos()); + int32_t mouseX = mousePos.x(); + int32_t mouseY = height() - mousePos.y(); + if ((mouseX >= 0) + && (mouseX < width()) + && (mouseY >= 0) + && (mouseY < height())) { + mouseValidFlag = true; + } + + const BrainOpenGLViewportContent* viewportContent(NULL); + if (mouseValidFlag) { + viewportContent = getViewportContentAtXY(mouseX, + mouseY); + } + KeyEvent keyEvent(viewportContent, + this, this->windowIndex, e->key(), + mouseX, + mouseY, + mouseValidFlag, m_newKeyPressStartedFlag, shiftKeyDownFlag); - const bool keyWasProcessedFlag = this->selectedUserInputProcessor->keyPressEvent(keyEvent); + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + + const bool keyWasProcessedFlag = inputProcessor->keyPressEvent(keyEvent); e->accept(); @@ -1002,6 +1197,10 @@ this->mouseMovementMinimumY = mouseY; this->mouseMovementMaximumY = mouseY; + m_mouseHistoryXY.clear(); + m_mouseHistoryXY.emplace_back(mouseX, + mouseY); + /* * The user may intend to increase the size of a toolbox * but instead misses the edge of the toolbox when trying @@ -1029,10 +1228,13 @@ 0, this->mousePressX, this->mousePressY, + m_mouseHistoryXY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { - this->selectedUserInputProcessor->mouseLeftPress(mouseEvent); + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + + inputProcessor->mouseLeftPress(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { /* not implemented this->selectedUserInputProcessor->mouseLeftPressWithShift(mouseEvent); */ @@ -1042,6 +1244,7 @@ else { this->mousePressX = -10000; this->mousePressY = -10000; + m_mouseHistoryXY.clear(); } me->accept(); @@ -1082,6 +1285,8 @@ const int absDX = (dx >= 0) ? dx : -dx; const int absDY = (dy >= 0) ? dy : -dy; + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + { /* * Mouse button RELEASE event @@ -1098,8 +1303,9 @@ 0, this->mousePressX, this->mousePressY, + m_mouseHistoryXY, this->mouseNewDraggingStartedFlag); - this->selectedUserInputProcessor->mouseLeftRelease(mouseEvent); + inputProcessor->mouseLeftRelease(mouseEvent); } } @@ -1123,17 +1329,18 @@ dy, this->mousePressX, this->mousePressY, + m_mouseHistoryXY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { - this->selectedUserInputProcessor->mouseLeftClick(mouseEvent); + inputProcessor->mouseLeftClick(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { - this->selectedUserInputProcessor->mouseLeftClickWithShift(mouseEvent); + inputProcessor->mouseLeftClickWithShift(mouseEvent); } else if (keyModifiers == (Qt::ShiftModifier | Qt::ControlModifier)) { - this->selectedUserInputProcessor->mouseLeftClickWithCtrlShift(mouseEvent); + inputProcessor->mouseLeftClickWithCtrlShift(mouseEvent); } } } @@ -1141,6 +1348,7 @@ this->mousePressX = -10000; this->mousePressY = -10000; + m_mouseHistoryXY.clear(); this->isMousePressedNearToolBox = false; me->accept(); @@ -1181,6 +1389,7 @@ const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { + std::vector emptyHistoryXY; MouseEvent mouseEvent(viewportContent, this, this->windowIndex, @@ -1190,9 +1399,10 @@ 0, this->mousePressX, this->mousePressY, + emptyHistoryXY, this->mouseNewDraggingStartedFlag); - this->selectedUserInputProcessor->mouseLeftDoubleClick(mouseEvent); + getSelectedInputProcessor()->mouseLeftDoubleClick(mouseEvent); } } } @@ -1213,7 +1423,7 @@ { m_mousePositionValid = false; - this->selectedUserInputProcessor->setMousePosition(m_mousePositionEvent, + getSelectedInputProcessor()->setMousePosition(m_mousePositionEvent, m_mousePositionValid); } @@ -1243,6 +1453,30 @@ } /** + * Get the viewport content at the given location WITHOUT aspect locking. + * @param x + * X-coordinate. + * @param y + * Y-coordinate. + */ +const BrainOpenGLViewportContent* +BrainOpenGLWidget::getViewportContentManualLayoutWithoutLockAspectAtXY(const int x, + const int y) +{ + const BrainOpenGLViewportContent* tabViewportContent = m_windowContent.getTabViewportManualLayoutWithoutAspectLocking(x, y); + if (tabViewportContent != NULL) { + return tabViewportContent; + } + + /* + * If not in a tab, then use the window viewport information. + * This allows selection of annotations in window space that are not + * within a tab (tab may be small in height due to lock aspect). + */ + return m_windowContent.getWindowViewport(); +} + +/** * Get all viewport content. If tile tabs is ON, the output will contain * viewport content for all tabs. Otherwise, the output will contain viewport * content for only the selected tab. @@ -1290,7 +1524,7 @@ if (idViewport != NULL) { s_singletonOpenGL->selectModel(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + getSelectedInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, idViewport, @@ -1335,8 +1569,27 @@ BrainOpenGLWidget::performIdentificationAnnotations(const int x, const int y) { - const BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); - + const UserInputModeEnum::Enum inputMode = getSelectedInputMode(); + bool manLayoutFlag(false); + switch (inputMode) { + case UserInputModeEnum::Enum::ANNOTATIONS: + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + manLayoutFlag = true; + break; + case UserInputModeEnum::Enum::BORDERS: + case UserInputModeEnum::Enum::FOCI: + case UserInputModeEnum::Enum::IMAGE: + case UserInputModeEnum::Enum::INVALID: + case UserInputModeEnum::Enum::VIEW: + case UserInputModeEnum::Enum::VOLUME_EDIT: + break; + } + + const BrainOpenGLViewportContent* idViewport = (manLayoutFlag + ? this->getViewportContentManualLayoutWithoutLockAspectAtXY(x, y) + : this->getViewportContentAtXY(x, y)); + this->makeCurrent(); CaretLogFine("Performing selection"); SelectionManager* idManager = GuiManager::get()->getBrain()->getSelectionManager(); @@ -1355,7 +1608,7 @@ const int idY = y - vp[1]; */ s_singletonOpenGL->selectModel(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + inputMode, GuiManager::get()->getBrain(), m_contextShareGroupPointer, idViewport, @@ -1422,7 +1675,7 @@ const int idY = y - vp[1]; */ s_singletonOpenGL->selectModel(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + getSelectedInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, idViewport, @@ -1472,7 +1725,7 @@ if (projectionViewport != NULL) { s_singletonOpenGL->projectToModel(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + getSelectedInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, projectionViewport, @@ -1526,6 +1779,8 @@ const int mouseX = me->x(); const int mouseY = this->windowHeight[this->windowIndex] - me->y(); + UserInputModeAbstract* inputProcessor = getSelectedInputProcessor(); + if (mouseButtons == Qt::LeftButton) { this->mouseMovementMinimumX = std::min(this->mouseMovementMinimumX, mouseX); this->mouseMovementMaximumX = std::max(this->mouseMovementMaximumX, mouseX); @@ -1539,6 +1794,9 @@ if ((absDX > 0) || (absDY > 0)) { + m_mouseHistoryXY.emplace_back(mouseX, + mouseY); + /* * Use location of mouse press so that the model * being manipulated does not change if mouse moves @@ -1557,23 +1815,24 @@ dy, this->mousePressX, this->mousePressY, + m_mouseHistoryXY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { - this->selectedUserInputProcessor->mouseLeftDrag(mouseEvent); + inputProcessor->mouseLeftDrag(mouseEvent); } else if (keyModifiers == Qt::ControlModifier) { - this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); + inputProcessor->mouseLeftDragWithCtrl(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { - this->selectedUserInputProcessor->mouseLeftDragWithShift(mouseEvent); + inputProcessor->mouseLeftDragWithShift(mouseEvent); } else if (keyModifiers == Qt::AltModifier) { - this->selectedUserInputProcessor->mouseLeftDragWithAlt(mouseEvent); + inputProcessor->mouseLeftDragWithAlt(mouseEvent); } else if (keyModifiers == (Qt::ShiftModifier | Qt::ControlModifier)) { - this->selectedUserInputProcessor->mouseLeftDragWithCtrlShift(mouseEvent); + inputProcessor->mouseLeftDragWithCtrlShift(mouseEvent); } this->mouseNewDraggingStartedFlag = false; @@ -1596,13 +1855,14 @@ 0, this->mousePressX, this->mousePressY, + m_mouseHistoryXY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { - this->selectedUserInputProcessor->mouseMove(mouseEvent); + inputProcessor->mouseMove(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { - this->selectedUserInputProcessor->mouseMoveWithShift(mouseEvent); + inputProcessor->mouseMoveWithShift(mouseEvent); } } } @@ -1619,9 +1879,10 @@ 0, this->mousePressX, this->mousePressY, + m_mouseHistoryXY, this->mouseNewDraggingStartedFlag)); - this->selectedUserInputProcessor->setMousePosition(m_mousePositionEvent, + inputProcessor->setMousePosition(m_mousePositionEvent, m_mousePositionValid); } else { @@ -1651,8 +1912,6 @@ EventBrainReset* brainResetEvent = dynamic_cast(event); CaretAssert(brainResetEvent); - this->borderBeingDrawn->clear(); - brainResetEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_TIMING_ONE_WINDOW) { @@ -1685,7 +1944,12 @@ if (updateOneEvent->getWindowIndex() == this->windowIndex) { updateOneEvent->setEventProcessed(); - doUpdateGraphicsFlag = true; + if (updateOneEvent->isRepaint()) { + doRepaintGraphicsFlag = true; + } + else { + doUpdateGraphicsFlag = true; + } } else { /* @@ -1704,56 +1968,48 @@ } } } - else if (event->getEventType() == EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { - EventGetOrSetUserInputModeProcessor* inputModeEvent = - dynamic_cast(event); - CaretAssert(inputModeEvent); - - if (inputModeEvent->getWindowIndex() == this->windowIndex) { - if (inputModeEvent->isGetUserInputMode()) { - inputModeEvent->setUserInputProcessor(this->selectedUserInputProcessor); - } - else if (inputModeEvent->isSetUserInputMode()) { - UserInputModeAbstract* newUserInputProcessor = NULL; - switch (inputModeEvent->getUserInputMode()) { - case UserInputModeEnum::INVALID: - CaretAssertMessage(0, "INVALID is NOT allowed for user input mode"); - break; - case UserInputModeEnum::ANNOTATIONS: - newUserInputProcessor = this->userInputAnnotationsModeProcessor; - break; - case UserInputModeEnum::BORDERS: - newUserInputProcessor = this->userInputBordersModeProcessor; - break; - case UserInputModeEnum::FOCI: - newUserInputProcessor = this->userInputFociModeProcessor; - break; - case UserInputModeEnum::IMAGE: - newUserInputProcessor = this->userInputImageModeProcessor; - break; - case UserInputModeEnum::VOLUME_EDIT: - newUserInputProcessor = this->userInputVolumeEditModeProcessor; - break; - case UserInputModeEnum::VIEW: - newUserInputProcessor = this->userInputViewModeProcessor; - break; - } - - if ((newUserInputProcessor == this->userInputAnnotationsModeProcessor) - || (this->selectedUserInputProcessor == this->userInputAnnotationsModeProcessor)) { - AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - CaretAssert(annMan); - annMan->deselectAllAnnotationsForEditing(this->windowIndex); - } - if (newUserInputProcessor != NULL) { - if (newUserInputProcessor != this->selectedUserInputProcessor) { - this->selectedUserInputProcessor->finish(); - this->selectedUserInputProcessor = newUserInputProcessor; - this->selectedUserInputProcessor->initialize(); - } - } + else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP) { + EventGraphicsWindowShowToolTip* tipEvent = dynamic_cast(event); + CaretAssert(tipEvent); + + if (tipEvent->getWindowIndex() == this->windowIndex) { + std::array windowXYZ = tipEvent->getWindowXYZ(); + + /* + * Origin is at top left for Qt, bottom left for OpenGL + */ + switch (tipEvent->getWindowOrigin()) { + case EventGraphicsWindowShowToolTip::WindowOrigin::BOTTOM_LEFT: + windowXYZ[1] = height() - windowXYZ[1]; + break; + case EventGraphicsWindowShowToolTip::WindowOrigin::TOP_LEFT: + break; } - inputModeEvent->setEventProcessed(); + + m_selectedChartPointToolTipInfo.m_position = mapToGlobal(QPoint(windowXYZ[0], + windowXYZ[1])); + m_selectedChartPointToolTipInfo.m_text = tipEvent->getText(); + + /* + * We cannot call QToolTip::showText() from here. If this event is sent + * when the user changes the value in the index spin box in Chart Layers, + * the event is sent when the mouse is pressed. When the user releases the + * mouse button, the tooltip is removed by Qt and thus, from the user's persepctive, + * the tooltip will go away almost immediately. + * + * Instead, use a QTimer to wait a short amount of time and then display the + * tooltip containing the selected chart points XY coordinate. We do a few + * single shot timers since we want the tooltip to display quickly but + * we don't know how long until the user releases the mouse. + * + * The first parameter to QTimer::singleShot() is the delay in millisecond + * until the method is called. + */ + QTimer::singleShot(500, this, &BrainOpenGLWidget::showSelectedChartPointToolTip); + QTimer::singleShot(750, this, &BrainOpenGLWidget::showSelectedChartPointToolTip); + QTimer::singleShot(1000, this, &BrainOpenGLWidget::showSelectedChartPointToolTip); + + tipEvent->setEventProcessed(); } } else if (event->getEventType() == EventTypeEnum::EVENT_IMAGE_CAPTURE) { @@ -1807,7 +2063,7 @@ CaretAssert(guiUpdateEvent); guiUpdateEvent->setEventProcessed(); - this->selectedUserInputProcessor->update(); + getSelectedInputProcessor()->update(); } else { @@ -1995,6 +2251,18 @@ } /** + * Show a tooltip containing the selected chart point's coordinate. + */ +void +BrainOpenGLWidget::showSelectedChartPointToolTip() +{ + if ( ! m_selectedChartPointToolTipInfo.m_text.isEmpty()) { + QToolTip::showText(m_selectedChartPointToolTipInfo.m_position, + m_selectedChartPointToolTipInfo.m_text); + } +} + +/** * Perform an immediate repaint of the graphics */ void @@ -2253,4 +2521,26 @@ m_mousePositionValid = false; } +/* + * @return The selected input processor + */ +UserInputModeAbstract* +BrainOpenGLWidget::getSelectedInputProcessor() const +{ + EventGetOrSetUserInputModeProcessor getInputModeEvent(this->windowIndex); + EventManager::get()->sendEvent(getInputModeEvent.getPointer()); + UserInputModeAbstract* inputProcessor = getInputModeEvent.getUserInputProcessor(); + CaretAssert(inputProcessor); + return inputProcessor; +} + +/* + * @return The selected input mode + */ +UserInputModeEnum::Enum +BrainOpenGLWidget::getSelectedInputMode() const +{ + return getSelectedInputProcessor()->getUserInputMode(); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/BrainOpenGLWidget.h connectome-workbench-1.5.0/src/GuiQt/BrainOpenGLWidget.h --- connectome-workbench-1.4.2/src/GuiQt/BrainOpenGLWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/BrainOpenGLWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -45,14 +45,16 @@ #include "BrainOpenGLWindowContent.h" #include "CaretPointer.h" #include "EventListenerInterface.h" +#include "MouseEvent.h" +#include "UserInputModeEnum.h" #include "WuQMacroMouseEventWidgetInterface.h" +class QGestureEvent; class QMouseEvent; class QWidget; namespace caret { - class Border; class BrainOpenGL; class BrainOpenGLViewportContent; class BrowserTabContent; @@ -60,14 +62,7 @@ class SelectionItemAnnotation; class SelectionManager; class Model; - class MouseEvent; class SurfaceProjectedItem; - class UserInputModeAnnotations; - class UserInputModeBorders; - class UserInputModeFoci; - class UserInputModeImage; - class UserInputModeView; - class UserInputModeVolumeEdit; class UserInputModeAbstract; class VolumeFile; @@ -102,8 +97,6 @@ const int y, SurfaceProjectedItem& projectionOut); - Border* getBorderBeingDrawn(); - static void initializeDefaultGLFormat(); QString getOpenGLInformation(); @@ -148,6 +141,9 @@ virtual void leaveEvent(QEvent* e); + private slots: + void showSelectedChartPointToolTip(); + private: std::vector getDrawingViewportContent(const int32_t windowViewportIn[4]) const; @@ -160,6 +156,9 @@ const BrainOpenGLViewportContent* getViewportContentAtXY(const int x, const int y); + const BrainOpenGLViewportContent* getViewportContentManualLayoutWithoutLockAspectAtXY(const int x, + const int y); + void checkForMiddleMouseButton(Qt::MouseButtons& mouseButtons, Qt::MouseButton& button, Qt::KeyboardModifiers& keyModifiers, @@ -169,6 +168,12 @@ void repaintGraphics(); + bool processGestureEvent(QGestureEvent* gestureEvent); + + UserInputModeAbstract* getSelectedInputProcessor() const; + + UserInputModeEnum::Enum getSelectedInputMode() const; + const int32_t windowIndex; BrainOpenGLWindowContent m_windowContent; @@ -193,17 +198,9 @@ int lastMouseY; - bool m_newKeyPressStartedFlag; - - UserInputModeAbstract* selectedUserInputProcessor; - UserInputModeAnnotations* userInputAnnotationsModeProcessor; - UserInputModeView* userInputViewModeProcessor; - UserInputModeBorders* userInputBordersModeProcessor; - UserInputModeFoci* userInputFociModeProcessor; - UserInputModeImage* userInputImageModeProcessor; - UserInputModeVolumeEdit* userInputVolumeEditModeProcessor; + std::vector m_mouseHistoryXY; - Border* borderBeingDrawn; + bool m_newKeyPressStartedFlag; bool m_mousePositionValid; CaretPointer m_mousePositionEvent; @@ -212,6 +209,13 @@ void* m_contextShareGroupPointer = NULL; + struct SelectedChartPointToolTipInfo { + QPoint m_position; + QString m_text; + }; + + SelectedChartPointToolTipInfo m_selectedChartPointToolTipInfo; + static bool s_defaultGLFormatInitialized; static std::set s_brainOpenGLWidgets; diff -Nru connectome-workbench-1.4.2/src/GuiQt/CardinalDirectionEnumMenu.cxx connectome-workbench-1.5.0/src/GuiQt/CardinalDirectionEnumMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/CardinalDirectionEnumMenu.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CardinalDirectionEnumMenu.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,207 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CARDINAL_DIRECTION_ENUM_MENU_DECLARE__ +#include "CardinalDirectionEnumMenu.h" +#undef __CARDINAL_DIRECTION_ENUM_MENU_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "WuQtUtilities.h" + +using namespace caret; + +/** + * \class caret::CardinalDirectionEnumMenu + * \brief Menu for selection of the standard cardinal directions + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +CardinalDirectionEnumMenu::CardinalDirectionEnumMenu() +: QMenu() +{ + const std::set options; + initializeCardinalDirectionEnumMenu(options); +} + +/** + * Constructor for menu that may provide optional cardinal direction enums. + * + * @param options + * Options for cardinal direction enums. + */ +CardinalDirectionEnumMenu::CardinalDirectionEnumMenu(const std::set& options) +: QMenu() +{ + initializeCardinalDirectionEnumMenu(options); +} + +/** + * Destructor. + */ +CardinalDirectionEnumMenu::~CardinalDirectionEnumMenu() +{ +} + +/** + * Initialize instance that may have optional cardinal direction enums. + * + * @param options + * Options for cardinal direction enums. + */ +void +CardinalDirectionEnumMenu::initializeCardinalDirectionEnumMenu(const std::set& options) +{ + std::vector cardinalDirections; + CardinalDirectionEnum::getAllEnums(cardinalDirections, + options); + + for (const auto cd : cardinalDirections) { + const AString name = CardinalDirectionEnum::toGuiName(cd); + + /* + * Add cardinal direction action to menu and make it checkable + */ + QAction* action = addAction(name); + action->setCheckable(true); + action->setData(CardinalDirectionEnum::toIntegerCode(cd)); + QObject::connect(action, &QAction::triggered, + [=] { actionSelected(action); }); + + switch (cd) { + case CardinalDirectionEnum::AUTO: + addSeparator(); + break; + case CardinalDirectionEnum::EAST: + break; + case CardinalDirectionEnum::NORTH: + break; + case CardinalDirectionEnum::NORTHEAST: + break; + case CardinalDirectionEnum::NORTHWEST: + break; + case CardinalDirectionEnum::SOUTH: + break; + case CardinalDirectionEnum::SOUTHEAST: + break; + case CardinalDirectionEnum::SOUTHWEST: + break; + case CardinalDirectionEnum::WEST: + break; + } + } +} + +/** + * Set the selected cardinal direction. + * + * @param cardinalDirection + * New selected cardinal direction. + */ +void +CardinalDirectionEnumMenu::setSelectedCardinalDirection(const CardinalDirectionEnum::Enum cardinalDirection) +{ + blockSignals(true); + + const int integerCode = CardinalDirectionEnum::toIntegerCode(cardinalDirection); + + /* + * Set checkbox next to selected cardinal direction. + */ + QList actionList = actions(); + QListIterator actionIter(actionList); + while (actionIter.hasNext()) { + QAction* action = actionIter.next(); + CaretAssert(action); + const int actionIntegerCode = action->data().toInt(); + if (actionIntegerCode == integerCode) { + action->setChecked(true); + } + else { + action->setChecked(false); + } + } + + blockSignals(false); +} + +/** + * Gets called when an action is selected. + * + * @param action + * Action that is selected. + */ +void +CardinalDirectionEnumMenu::actionSelected(QAction* action) +{ + const int integerCode = action->data().toInt(); + bool valid = false; + const CardinalDirectionEnum::Enum cardinalDirection = CardinalDirectionEnum::fromIntegerCode(integerCode, + &valid); + if (valid) { + /* + * Update checkboxes + */ + setSelectedCardinalDirection(cardinalDirection); + + /* + * Emit cardinal direction changed signal. + */ + emit cardinalDirectionSelected(cardinalDirection); + } + else { + CaretLogSevere("Invalid CardinalDirectionEnum integer code=" + + AString::number(integerCode)); + } +} + +/** + * @return The selected cardinal direction. + */ +CardinalDirectionEnum::Enum +CardinalDirectionEnumMenu::getSelectedCardinalDirection() +{ + QList actionList = actions(); + QListIterator actionIter(actionList); + while (actionIter.hasNext()) { + QAction* action = actionIter.next(); + CaretAssert(action); + + if (action->isChecked()) { + const int actionIntegerCode = action->data().toInt(); + bool valid = false; + CardinalDirectionEnum::Enum enumSelected = CardinalDirectionEnum::fromIntegerCode(actionIntegerCode, &valid); + if (valid) { + return enumSelected; + } + } + } + + CaretAssertMessage(0, + "Did not find selected cardinal direction."); + + return CardinalDirectionEnum::AUTO; +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/CardinalDirectionEnumMenu.h connectome-workbench-1.5.0/src/GuiQt/CardinalDirectionEnumMenu.h --- connectome-workbench-1.4.2/src/GuiQt/CardinalDirectionEnumMenu.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CardinalDirectionEnumMenu.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,70 @@ +#ifndef __CARDINAL_DIRECTION_ENUM_MENU_H__ +#define __CARDINAL_DIRECTION_ENUM_MENU_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "CardinalDirectionEnum.h" + +namespace caret { + + class CardinalDirectionEnumMenu : public QMenu { + + Q_OBJECT + + public: + CardinalDirectionEnumMenu(); + + CardinalDirectionEnumMenu(const std::set& options); + + virtual ~CardinalDirectionEnumMenu(); + + CardinalDirectionEnum::Enum getSelectedCardinalDirection(); + + void setSelectedCardinalDirection(const CardinalDirectionEnum::Enum cardinalDirection); + + // ADD_NEW_METHODS_HERE + + signals: + void cardinalDirectionSelected(const CardinalDirectionEnum::Enum); + + private slots: + void actionSelected(QAction* action); + + private: + CardinalDirectionEnumMenu(const CardinalDirectionEnumMenu&); + + CardinalDirectionEnumMenu& operator=(const CardinalDirectionEnumMenu&); + + void initializeCardinalDirectionEnumMenu(const std::set& options); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CARDINAL_DIRECTION_ENUM_MENU_DECLARE__ + // +#endif // __CARDINAL_DIRECTION_ENUM_MENU_DECLARE__ + +} // namespace +#endif //__CARDINAL_DIRECTION_ENUM_MENU_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretColorEnumComboBox.cxx connectome-workbench-1.5.0/src/GuiQt/CaretColorEnumComboBox.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretColorEnumComboBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretColorEnumComboBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -24,8 +24,10 @@ #undef __CARET_COLOR_ENUM_COMBOBOX_DECLARE__ #include +#include #include "CaretAssert.h" +#include "CaretLogger.h" #include "WuQMacroManager.h" #include "WuQtUtilities.h" @@ -47,111 +49,56 @@ * Parent object. */ CaretColorEnumComboBox::CaretColorEnumComboBox(QObject* parent) -: WuQWidget(parent) +: CaretColorEnumComboBox(CustomColorModeEnum::DISABLED, + NoneColorModeEnum::DISABLED, + parent) { - initializeCaretColorComboBox("", - NULL); + /* delegating constructor above */ } /** * Constructor. * - * @param customColorSelectionName - * CaretColorEnum::CUSTOM is added to the combo with this name as the text. - * Text must NOT be empty. + * @param customColorMode + * Mode for custom color + * @param noneColorMode + * Mode for none color * @param parent * Parent object. */ -CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelectionName, +CaretColorEnumComboBox::CaretColorEnumComboBox(const CustomColorModeEnum customColorMode, + const NoneColorModeEnum noneColorMode, QObject* parent) -: WuQWidget(parent) -{ - CaretAssert( ! customColorSelectionName.isEmpty()); - initializeCaretColorComboBox(customColorSelectionName, - NULL); -} - -/** - * Constructor. - * - * @param customColorSelectionName - * CaretColorEnum::CUSTOM is added to the combo with this name as the text. - * Text must NOT be empty. - * @param customColorSelectionIcon - * ICon for custom color. - * @param parent - * Parent object. - */ -CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelectionName, - const QIcon& customColorSelectionIcon, - QObject* parent) -: WuQWidget(parent) -{ - CaretAssert( ! customColorSelectionName.isEmpty()); - initializeCaretColorComboBox(customColorSelectionName, - &customColorSelectionIcon); -} - -/** - * Constructor. - * - * @param customColorSelectionName - * If not empty, CaretColorEnum::CUSTOM is added to the combo with this name as the text. - * @param customColorSelectionIcon - * Icon for custom color. - * @param objectNameForMacros - * If not empty, name is used to set this combo box for macro support - * @param objectDescriptiveNameForMacros - * Descriptive name for macros - * @param parent - * Parent object. - */ -CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelectionName, - const QIcon& customColorSelectionIcon, - const QString& objectNameForMacros, - const QString& objectDescriptiveNameForMacros, - QObject* parent) -: WuQWidget(parent) -{ - initializeCaretColorComboBox(customColorSelectionName, - &customColorSelectionIcon); - - if ( ! objectNameForMacros.isEmpty()) { - this->colorComboBox->setObjectName(objectNameForMacros); - this->colorComboBox->setToolTip("Select Color"); - WuQMacroManager::instance()->addMacroSupportToObject(this->colorComboBox, - objectDescriptiveNameForMacros); - } -} - -/** - * Destructor. - */ -CaretColorEnumComboBox::~CaretColorEnumComboBox() +: WuQWidget(parent), +m_customColorMode(customColorMode), +m_noneColorMode(noneColorMode) { + m_customColorRGB.fill(255); /* white */ -} - -/** - * Initialize instance that may have optional caret color enums. - * - * @param customColorSelectionName - * If NOT empty, CaretColorEnum::CUSTOM is added with text from this name. - * @param customColorSelectionIcon - * ICON for custom color (ignored if NULL) - */ -void -CaretColorEnumComboBox::initializeCaretColorComboBox(const AString& customColorSelectionName, - const QIcon* customColorSelectionIcon) -{ this->colorComboBox = new QComboBox(); std::vector colors; int64_t caretColorOptions = 0; - if ( ! customColorSelectionName.isEmpty()) { - caretColorOptions |= CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR; + switch (m_customColorMode) { + case CustomColorModeEnum::DISABLED: + break; + case CustomColorModeEnum::EDITABLE: + caretColorOptions |= CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR; + break; + case CustomColorModeEnum::FIXED: + caretColorOptions |= CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR; + break; + } + + switch (m_noneColorMode) { + case NoneColorModeEnum::DISABLED: + break; + case NoneColorModeEnum::ENABLED: + caretColorOptions |= CaretColorEnum::OPTION_INCLUDE_NONE_COLOR; + break; } + CaretColorEnum::getColorAndOptionalEnums(colors, caretColorOptions); @@ -161,10 +108,12 @@ const int32_t indx = this->colorComboBox->count(); AString name = CaretColorEnum::toGuiName(colorEnum); if (colorEnum == CaretColorEnum::CUSTOM) { - if ( ! customColorSelectionName.isEmpty()) { - name = customColorSelectionName; - } + m_customColorIndex = this->colorComboBox->count(); } + else if (colorEnum == CaretColorEnum::NONE) { + m_noneColorIndex = this->colorComboBox->count(); + } + this->colorComboBox->addItem(name); this->colorComboBox->setItemData(indx, CaretColorEnum::toIntegerCode(colorEnum)); @@ -172,33 +121,24 @@ /* * Create an icon with the color. */ - float rgba[4]; - CaretColorEnum::toRGBAFloat(colorEnum, rgba); + uint8_t rgba[4]; + CaretColorEnum::toRGBAByte(colorEnum, rgba); if (colorEnum == CaretColorEnum::NONE) { - rgba[3] = 0.0; + rgba[3] = 0; } else if (colorEnum == CaretColorEnum::CUSTOM) { - /* - * If NO icon for CUSTOM - */ - if (customColorSelectionIcon == NULL) { - rgba[3] = 0.0; - } + rgba[3] = 0; } else { - rgba[3] = 1.0; + rgba[3] = 255; } if (rgba[3] > 0.0) { - QPixmap pm(WuQtUtilities::createCaretColorEnumPixmap(getWidget(), 10, 10, colorEnum, rgba, false)); - QIcon icon(pm); - if (colorEnum == CaretColorEnum::CUSTOM) { - if (customColorSelectionIcon != NULL) { - icon = *customColorSelectionIcon; - } - } - this->colorComboBox->setItemIcon(indx, - icon); + setIconColor(indx, rgba); +// QPixmap pm(WuQtUtilities::createCaretColorEnumPixmap(getWidget(), 10, 10, colorEnum, rgba, false)); +// QIcon icon(pm); +// this->colorComboBox->setItemIcon(indx, +// icon); } } @@ -208,6 +148,74 @@ } /** + * Set the icon color + * @param index + * Index in the combo box + * @param rgba + * RGBA components + */ +void +CaretColorEnumComboBox::setIconColor(const int32_t index, + const uint8_t rgba[4]) +{ + if (index >= 0) { + const float floatRGBA[4] = { + rgba[0] / 255.0f, + rgba[1] / 255.0f, + rgba[2] / 255.0f, + rgba[3] / 255.0f + }; + QPixmap pm(WuQtUtilities::createCaretColorEnumPixmap(getWidget(), 10, 10, CaretColorEnum::CUSTOM, floatRGBA, false)); + QIcon icon(pm); + this->colorComboBox->setItemIcon(index, + icon); + } +} + +/** + * Destructor. + */ +CaretColorEnumComboBox::~CaretColorEnumComboBox() +{ + +} + + +/** + * Override the custom color name + * @param customColorName + * Name for use with custom color + */ +void +CaretColorEnumComboBox::setCustomColorName(const AString& customColorName) +{ + if (m_customColorIndex < 0) { + CaretLogWarning("Attempt to set custom color name but custom color was not enabled"); + return; + } + + this->colorComboBox->setItemText(m_customColorIndex, + customColorName); +} + +/** + * Override the custom color icon + * @param customColorIcon + * Icon for use with custom color + */ +void +CaretColorEnumComboBox::setCustomColorIcon(const QIcon& customColorIcon) +{ + if (m_customColorIndex < 0) { + CaretLogWarning("Attempt to set custom color name but custom color was not enabled"); + return; + } + + this->colorComboBox->setItemIcon(m_customColorIndex, + customColorIcon); +} + +/** * @return The actual widget. */ QWidget* @@ -259,6 +267,61 @@ } /** + * Get the custom color. + * @param rgbOut + * Output with current custom color. + */ +void +CaretColorEnumComboBox::getCustomColor(std::array& rgbOut) const +{ + rgbOut = m_customColorRGB; +} + +/** + * Get the custom color. + * @param rgbaOut + * Output with current custom color. + */ +void +CaretColorEnumComboBox::getCustomColor(std::array& rgbaOut) const +{ + rgbaOut[0] = m_customColorRGB[0]; + rgbaOut[1] = m_customColorRGB[1]; + rgbaOut[2] = m_customColorRGB[2]; + rgbaOut[3] = 255; +} + + +/** + * Set the custom color. + * @param rgb + * New custom color. + */ +void +CaretColorEnumComboBox::setCustomColor(const std::array& rgb) +{ + m_customColorRGB = rgb; + uint8_t rgba[4] = { + m_customColorRGB[0], m_customColorRGB[1], m_customColorRGB[2], 255 + }; + setIconColor(m_customColorIndex, rgba); +} + +/** + * Set the custom color. + * @param rgba + * New custom color. + */ +void +CaretColorEnumComboBox::setCustomColor(const std::array& rgba) +{ + m_customColorRGB[0] = rgba[0]; + m_customColorRGB[1] = rgba[1]; + m_customColorRGB[2] = rgba[2]; + setCustomColor(m_customColorRGB); +} + +/** * Called when a color is selected. * @param indx * Index of item selected. @@ -268,6 +331,35 @@ { const int32_t integerCode = this->colorComboBox->itemData(indx).toInt(); CaretColorEnum::Enum color = CaretColorEnum::fromIntegerCode(integerCode, NULL); + + if (color == CaretColorEnum::CUSTOM) { + switch (m_customColorMode) { + case CustomColorModeEnum::DISABLED: + break; + case CustomColorModeEnum::EDITABLE: + { + const QColor initialColor(m_customColorRGB[0], + m_customColorRGB[1], + m_customColorRGB[2]); + + QColorDialog colorDialog(getWidget()); + colorDialog.setOption(QColorDialog::DontUseNativeDialog); + colorDialog.setWindowTitle("Choose Color"); + colorDialog.setCurrentColor(initialColor); + + if (colorDialog.exec() == QColorDialog::Accepted) { + const QColor newColor = colorDialog.currentColor(); + m_customColorRGB[0] = newColor.red(); + m_customColorRGB[1] = newColor.green(); + m_customColorRGB[2] = newColor.blue(); + } + } + break; + case CustomColorModeEnum::FIXED: + break; + } + } + emit colorSelected(color); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretColorEnumComboBox.h connectome-workbench-1.5.0/src/GuiQt/CaretColorEnumComboBox.h --- connectome-workbench-1.4.2/src/GuiQt/CaretColorEnumComboBox.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretColorEnumComboBox.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include "CaretColorEnum.h" #include "WuQWidget.h" @@ -34,27 +35,62 @@ Q_OBJECT public: - CaretColorEnumComboBox(QObject* parent); - - CaretColorEnumComboBox(const AString& customColorSelectionName, - QObject* parent); + /** + * Custom color mode + */ + enum class CustomColorModeEnum { + /** + * No custom color + */ + DISABLED, + /** + * Custom's color editable by user (pop-up when selected) + */ + EDITABLE, + /** + * Custom's color is fixed and set throught function call + */ + FIXED + }; + + /** + * None color mode + */ + enum class NoneColorModeEnum { + /** + * No None color + */ + DISABLED, + /** + * Custom's color editable by user (pop-up when selected) + */ + ENABLED + }; - CaretColorEnumComboBox(const AString& customColorSelectionName, - const QIcon& customColorSelectionIcon, - QObject* parent); + CaretColorEnumComboBox(QObject* parent); - CaretColorEnumComboBox(const AString& customColorSelectionName, - const QIcon& customColorSelectionIcon, - const QString& objectNameForMacros, - const QString& objectDescriptiveNameForMacros, + CaretColorEnumComboBox(const CustomColorModeEnum customColorMode, + const NoneColorModeEnum noneColorMode, QObject* parent); - + virtual ~CaretColorEnumComboBox(); CaretColorEnum::Enum getSelectedColor(); void setSelectedColor(const CaretColorEnum::Enum color); + void getCustomColor(std::array& rgbOut) const; + + void getCustomColor(std::array& rgbaOut) const; + + void setCustomColor(const std::array& rgb); + + void setCustomColor(const std::array& rgba); + + void setCustomColorName(const AString& customColorName); + + void setCustomColorIcon(const QIcon& customColorIcon); + QWidget* getWidget(); QComboBox* getComboBox(); @@ -71,11 +107,20 @@ CaretColorEnumComboBox& operator=(const CaretColorEnumComboBox&); private: + void setIconColor(const int32_t index, + const uint8_t rgba[4]); + + const CustomColorModeEnum m_customColorMode; + + const NoneColorModeEnum m_noneColorMode; + QComboBox* colorComboBox; - void initializeCaretColorComboBox(const AString& customColorSelectionName, - const QIcon* customColorSelectionIcon); + int32_t m_customColorIndex = -1; + + int32_t m_noneColorIndex = -1; + std::array m_customColorRGB; }; #ifdef __CARET_COLOR_ENUM_COMBOBOX_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretColorEnumMenu.cxx connectome-workbench-1.5.0/src/GuiQt/CaretColorEnumMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretColorEnumMenu.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretColorEnumMenu.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -116,10 +116,13 @@ if (colorEnum == CaretColorEnum::CUSTOM) { m_customColorAction = action; } + + QObject::connect(action, &QAction::triggered, + [=] { colorActionSelected(action); }); } - QObject::connect(this, SIGNAL(triggered(QAction*)), - this, SLOT(colorActionSelected(QAction*))); +// QObject::connect(this, SIGNAL(triggered(QAction*)), +// this, SLOT(colorActionSelected(QAction*))); } /** diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretColorToolButton.cxx connectome-workbench-1.5.0/src/GuiQt/CaretColorToolButton.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretColorToolButton.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretColorToolButton.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,212 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CARET_COLOR_TOOLBUTTON_DECLARE__ +#include "CaretColorToolButton.h" +#undef __CARET_COLOR_TOOLBUTTON_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretColorEnumMenu.h" +#include "CaretLogger.h" +#include "WuQMacroManager.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::CaretColorToolButton + * \brief Control for selection of Caret Color enumerated types. + * + * Control for selection of Caret Color enumerated types. + */ + +/** + * Constructor. + * + * @param parent + * Parent widget. + */ +CaretColorToolButton::CaretColorToolButton(QWidget* parent) +: CaretColorToolButton(CustomColorMode::DISABLED, + NoneColorMode::DISABLED, + parent) +{ + /* delegating constructor above */ +} + +/** + * Constructor. + * + * @param customColorMode + * Mode for custom color + * @param noneColorMode + * Mode for none color + * @param parent + * Parent object. + */ +CaretColorToolButton::CaretColorToolButton(const CustomColorMode customColorMode, + const NoneColorMode noneColorMode, + QWidget* parent) +: QToolButton(parent), +m_customColorMode(customColorMode), +m_noneColorMode(noneColorMode) +{ + int64_t caretColorOptions = 0; + switch (m_customColorMode) { + case CustomColorMode::DISABLED: + break; + case CustomColorMode::EDITABLE: + caretColorOptions |= CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR; + break; + case CustomColorMode::FIXED: + caretColorOptions |= CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR; + break; + } + + switch (m_noneColorMode) { + case NoneColorMode::DISABLED: + break; + case NoneColorMode::ENABLED: + caretColorOptions |= CaretColorEnum::OPTION_INCLUDE_NONE_COLOR; + break; + } + m_caretColorEnumMenu = new CaretColorEnumMenu(caretColorOptions); + + QObject::connect(this, &QToolButton::clicked, + this, &CaretColorToolButton::toolButtonClicked); + + m_caretColor.setCaretColorEnum(CaretColorEnum::BLACK); +} + +/** + * Called when the button is clicked to pop-up a menu for color selection + */ +void +CaretColorToolButton::toolButtonClicked() +{ + setSelectedColor(m_caretColor); + QAction* action = m_caretColorEnumMenu->exec(this->mapToGlobal(QPoint(0,0))); + if (action != NULL) { + CaretColorEnum::Enum colorEnum = m_caretColorEnumMenu->getSelectedColor(); + caretColorMenuSelected(colorEnum); + } +} + +/** + * Set the icon color + * @param rgba + * RGBA components + */ +void +CaretColorToolButton::updateIconColor() +{ + const std::array rgbaF = m_caretColor.getFloatRGBA(); + QPixmap pm(WuQtUtilities::createCaretColorEnumPixmap(this, 10, 10, + CaretColorEnum::CUSTOM, + rgbaF.data(), false)); + QIcon icon(pm); + this->setIcon(icon); +} + +/** + * Destructor. + */ +CaretColorToolButton::~CaretColorToolButton() +{ + +} + +/** + * @return The selected color. + */ +CaretColor +CaretColorToolButton::getSelectedColor() +{ + return m_caretColor; +} + +/** + * Set the selected color. + * @param color + * New color for selection. + */ +void +CaretColorToolButton::setSelectedColor(const CaretColor& color) +{ + m_caretColor = color; + m_caretColorEnumMenu->setSelectedColor(m_caretColor.getCaretColorEnum()); + m_caretColorEnumMenu->setCustomIconColor(m_caretColor.getCustomColorFloatRGBA().data()); + updateIconColor(); +} + +/** + * Called when a color is selected. + * @param colorEnum + * Color selected + */ +void +CaretColorToolButton::caretColorMenuSelected(const CaretColorEnum::Enum colorEnum) +{ + if (colorEnum == CaretColorEnum::CUSTOM) { + switch (m_customColorMode) { + case CustomColorMode::DISABLED: + break; + case CustomColorMode::EDITABLE: + { + std::array rgba = m_caretColor.getCustomColorRGBA(); + const QColor initialColor(rgba[0], + rgba[1], + rgba[2]); + + QColorDialog colorDialog(this); + colorDialog.setOption(QColorDialog::DontUseNativeDialog); + colorDialog.setWindowTitle("Choose Color"); + colorDialog.setCurrentColor(initialColor); + + if (colorDialog.exec() == QColorDialog::Accepted) { + const QColor newColor = colorDialog.currentColor(); + rgba[0] = newColor.red(); + rgba[1] = newColor.green(); + rgba[2] = newColor.blue(); + m_caretColor.setCustomColorRGBA(rgba); + } + else { + return; + } + } + break; + case CustomColorMode::FIXED: + break; + } + } + + m_caretColor.setCaretColorEnum(colorEnum); + updateIconColor(); + emit colorSelected(m_caretColor); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretColorToolButton.h connectome-workbench-1.5.0/src/GuiQt/CaretColorToolButton.h --- connectome-workbench-1.4.2/src/GuiQt/CaretColorToolButton.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretColorToolButton.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,115 @@ +#ifndef __CARET_COLOR_TOOLBUTTON__H_ +#define __CARET_COLOR_TOOLBUTTON__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include + +#include "CaretColor.h" +#include "WuQWidget.h" + +namespace caret { + + class CaretColor; + class CaretColorEnumMenu; + + class CaretColorToolButton : public QToolButton { + + Q_OBJECT + + public: + /** + * Custom color mode + */ + enum class CustomColorMode { + /** + * No custom color + */ + DISABLED, + /** + * Custom's color editable by user (pop-up when selected) + */ + EDITABLE, + /** + * Custom's color is fixed and set throught function call + */ + FIXED + }; + + /** + * None color mode + */ + enum class NoneColorMode { + /** + * No None color + */ + DISABLED, + /** + * Custom's color editable by user (pop-up when selected) + */ + ENABLED + }; + + CaretColorToolButton(QWidget* parent = 0); + + CaretColorToolButton(const CustomColorMode customColorMode, + const NoneColorMode noneColorMode, + QWidget* parent = 0); + + virtual ~CaretColorToolButton(); + + CaretColor getSelectedColor(); + + void setSelectedColor(const CaretColor& color); + + signals: + void colorSelected(const CaretColor& caretColor); + + private slots: + void caretColorMenuSelected(const CaretColorEnum::Enum colorEnum); + + void toolButtonClicked(); + + private: + CaretColorToolButton(const CaretColorToolButton&); + + CaretColorToolButton& operator=(const CaretColorToolButton&); + + private: + void updateIconColor(); + + const CustomColorMode m_customColorMode; + + const NoneColorMode m_noneColorMode; + + CaretColorEnumMenu* m_caretColorEnumMenu = NULL; + + CaretColor m_caretColor; + }; + +#ifdef __CARET_COLOR_TOOLBUTTON_DECLARE__ + // +#endif // __CARET_COLOR_TOOLBUTTON_DECLARE__ + +} // namespace +#endif //__CARET_COLOR_TOOLBUTTON__H_ diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretDataFileSelectionComboBox.cxx connectome-workbench-1.5.0/src/GuiQt/CaretDataFileSelectionComboBox.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretDataFileSelectionComboBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretDataFileSelectionComboBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -131,7 +131,7 @@ CaretAssertVectorIndex(displayNames, iFile); m_comboBox->addItem(displayNames[iFile], - qVariantFromValue((void*)cdf)); + QVariant::fromValue((void*)cdf)); if (cdf == selectedFile) { defaultIndex = m_comboBox->count() - 1; diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretFileDialog.cxx connectome-workbench-1.5.0/src/GuiQt/CaretFileDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretFileDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretFileDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -28,7 +28,13 @@ #undef __CARET_FILE_DIALOG_DECLARE__ #include "Brain.h" +#include "CaretAssert.h" +#include "CaretPreferences.h" #include "GuiManager.h" +#include "RecentFileItem.h" +#include "RecentFileItemsFilter.h" +#include "RecentFileItemsContainer.h" +#include "SessionManager.h" using namespace caret; @@ -98,33 +104,66 @@ } /** + * Must override sort() and then call the model's sort(). Without this, columns sort as strings + * and date/size sorting does not work properly. + * + * From: https://stackoverflow.com/questions/53789779/qfiledialog-with-proxy-model-sort-by-date-actually-uses-alphabetical-order-of-da + */ +void +FilterFilesProxyModel::sort(int column, Qt::SortOrder order) +{ + sourceModel()->sort(column, + order); +} + +/** * \class caret::CaretFileDialog * \brief Adds additional functionality over Qt's QFileDialog. */ /** * Constructor. + * @param mode + * The mode for open/save + * @param parent + * Parent on which this dialog is displayed. + * @param f + * Qt's window flags */ -CaretFileDialog::CaretFileDialog(QWidget* parent, +CaretFileDialog::CaretFileDialog(const Mode mode, + QWidget* parent, Qt::WindowFlags f) : QFileDialog(parent, - f) + f), +m_mode(mode) { this->initializeCaretFileDialog(); this->setDirectory(GuiManager::get()->getBrain()->getCurrentDirectory()); } + /** * Constructor. + * @param mode + * The mode for open/save + * @param parent + * Parent on which this dialog is displayed. + * @param caption + * Caption for dialog (if not provided a default caption is shown) + * @param directory + * Directory show by dialog (Brain's current directory if empty string) + * @param filter */ -CaretFileDialog::CaretFileDialog(QWidget* parent, +CaretFileDialog::CaretFileDialog(const Mode mode, + QWidget* parent, const QString& caption, const QString& directory, const QString& filter) : QFileDialog(parent, caption, directory, - filter) + filter), +m_mode(mode) { this->initializeCaretFileDialog(); @@ -176,6 +215,25 @@ QObject::connect(this, SIGNAL(filterSelected(const QString&)), this, SLOT(fileFilterWasChanged(const QString&))); + + /* + * Add recent directories to history + */ + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + std::vector recentDirectories; + const bool favoritesFirstFlag(false); + prefs->getRecentDirectoriesForOpenFileDialogHistory(favoritesFirstFlag, + recentDirectories); + const int32_t displayCount = std::min(static_cast(recentDirectories.size()), + 10); + + QStringList historyList; + for (int32_t i = 0; i < displayCount; i++) { + CaretAssertVectorIndex(recentDirectories, i); + historyList.push_front(recentDirectories[i]); + } + + setHistory(historyList); } /** @@ -243,7 +301,8 @@ QString *selectedFilter, Options options ) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_OPEN, + parent, caption, dir, filter); @@ -291,11 +350,12 @@ Options options) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_OPEN, + parent, caption, dir, - DataFileTypeEnum::toQFileDialogFilter(dataFileType)); - cfd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); + DataFileTypeEnum::toQFileDialogFilterForReading(dataFileType)); + cfd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilterForReading(dataFileType)); cfd.setOptions(options); cfd.setAcceptMode(CaretFileDialog::AcceptOpen); cfd.setFileMode(CaretFileDialog::AnyFile); @@ -338,7 +398,8 @@ QString *selectedFilter, Options options) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_SAVE, + parent, caption, dir, filter); @@ -389,11 +450,12 @@ const QString &dir, Options options) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_SAVE, + parent, caption, dir, - DataFileTypeEnum::toQFileDialogFilter(dataFileType)); - cfd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); + DataFileTypeEnum::toQFileDialogFilterForWriting(dataFileType)); + cfd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilterForWriting(dataFileType)); cfd.setOptions(options); cfd.setAcceptMode(QFileDialog::AcceptSave); cfd.setFileMode(CaretFileDialog::AnyFile); @@ -441,11 +503,12 @@ const QString& directoryOrFileName, QWidget *parent) { - CaretFileDialog fd(parent); + CaretFileDialog fd(Mode::MODE_SAVE, + parent); if (dataFileType != DataFileTypeEnum::UNKNOWN) { - fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); + fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilterForWriting(dataFileType)); } - fd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); + fd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilterForWriting(dataFileType)); fd.setAcceptMode(CaretFileDialog::AcceptSave); fd.setFileMode(CaretFileDialog::AnyFile); fd.setViewMode(CaretFileDialog::List); @@ -502,7 +565,8 @@ const QString &dir, Options options) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_SAVE, + parent, caption, dir, ""); @@ -549,7 +613,8 @@ QString *selectedFilter, Options options) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_OPEN, + parent, caption, dir, filter); diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretFileDialogExtendable.cxx connectome-workbench-1.5.0/src/GuiQt/CaretFileDialogExtendable.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretFileDialogExtendable.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretFileDialogExtendable.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -1,178 +1,178 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include -#include -#include -#include - -#define __CARET_FILE_DIALOG_EXTENDABLE_DECLARE__ -#include "CaretFileDialogExtendable.h" -#undef __CARET_FILE_DIALOG_EXTENDABLE_DECLARE__ - -#include "CaretAssert.h" -#include "CaretFileDialog.h" -using namespace caret; - - - -/** - * \class caret::CaretFileDialogExtendable - * \brief A File Dialog that can have a widget added to it. - * - * Embeds a QFileDialog inside a dialog so that a widget - * can be inserted below the FileDialog. All public methods - * from QFileDialog are duplicated and go straight to - * the embedded QFileDialog. - */ - -/** - * Constructor. - */ -CaretFileDialogExtendable::CaretFileDialogExtendable(QWidget* parent, - Qt::WindowFlags f) -: QDialog(parent, - f) -{ - Qt::WindowFlags flags = 0; - m_caretFileDialog = new CaretFileDialogPrivate(NULL, - flags); - - createDialog(); -} -/** - * Constructor. - */ -CaretFileDialogExtendable::CaretFileDialogExtendable(QWidget* parent, - const QString& caption, - const QString& directory, - const QString& filter) -: QDialog(parent) -{ - m_caretFileDialog = new CaretFileDialogPrivate(NULL, - caption, - directory, - filter); - - createDialog(); -} - - -/** - * Destructor. - */ -CaretFileDialogExtendable::~CaretFileDialogExtendable() -{ - -} - -/** - * Create the dialog. - */ -void -CaretFileDialogExtendable::createDialog() -{ - QObject::connect(m_caretFileDialog, SIGNAL(currentChanged(const QString&)), - this, SIGNAL(currentChanged(const QString&))); - - QObject::connect(m_caretFileDialog, SIGNAL( directoryEntered(const QString&)), - this, SIGNAL( directoryEntered(const QString&))); - - QObject::connect(m_caretFileDialog, SIGNAL(fileSelected(const QString&)), - this, SIGNAL(fileSelected(const QString&))); - - QObject::connect(m_caretFileDialog, SIGNAL(filesSelected(const QStringList&)), - this, SIGNAL(filesSelected(const QStringList&))); - - QObject::connect(m_caretFileDialog, SIGNAL(filterSelected(const QString&)), - this, SIGNAL(filterSelected(const QString&))); - - QObject::connect(m_caretFileDialog, SIGNAL(finished(int)), - this, SLOT(fileDialogFinished(int))); - - m_caretFileDialog->setAttribute(Qt::WA_DeleteOnClose, false); - m_caretFileDialog->setSizeGripEnabled(false); - - m_dialogLayout = new QVBoxLayout(this); - m_dialogLayout->addWidget(m_caretFileDialog); -} - -/** - * Gets called when child file dialog issues its finished signal. - * @param result - * Result of dialog. - */ -void -CaretFileDialogExtendable::fileDialogFinished(int result) -{ - this->setResult(result); - this->done(result); -} - - -/** - * Add a widget at the bottom to this file dialog. - * @param widget - * Widget that is added. - */ -void -CaretFileDialogExtendable::addWidget(QWidget* widget) -{ - CaretAssert(widget); - m_dialogLayout->addWidget(widget); -} - - -//================================================================================== - -CaretFileDialogPrivate::CaretFileDialogPrivate(QWidget* parent, - Qt::WindowFlags /*f*/) -: CaretFileDialog(parent, - Qt::Widget) -{ -} - -CaretFileDialogPrivate::CaretFileDialogPrivate(QWidget* parent, - const QString& caption, - const QString& directory, - const QString& filter) -: CaretFileDialog(parent, - caption, - directory, - filter) -{ -} - -CaretFileDialogPrivate::~CaretFileDialogPrivate() -{ -} - -void -CaretFileDialogPrivate::closeEvent(QCloseEvent* event) -{ - event->ignore(); -} - -void -CaretFileDialogPrivate::done(int result) -{ - CaretFileDialog::done(result); -} +// +///*LICENSE_START*/ +///* +// * Copyright (C) 2014 Washington University School of Medicine +// * +// * This program is free software; you can redistribute it and/or modify +// * it under the terms of the GNU General Public License as published by +// * the Free Software Foundation; either version 2 of the License, or +// * (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have received a copy of the GNU General Public License along +// * with this program; if not, write to the Free Software Foundation, Inc., +// * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// */ +///*LICENSE_END*/ +// +//#include +//#include +//#include +//#include +// +//#define __CARET_FILE_DIALOG_EXTENDABLE_DECLARE__ +//#include "CaretFileDialogExtendable.h" +//#undef __CARET_FILE_DIALOG_EXTENDABLE_DECLARE__ +// +//#include "CaretAssert.h" +//#include "CaretFileDialog.h" +//using namespace caret; +// +// +// +///** +// * \class caret::CaretFileDialogExtendable +// * \brief A File Dialog that can have a widget added to it. +// * +// * Embeds a QFileDialog inside a dialog so that a widget +// * can be inserted below the FileDialog. All public methods +// * from QFileDialog are duplicated and go straight to +// * the embedded QFileDialog. +// */ +// +///** +// * Constructor. +// */ +//CaretFileDialogExtendable::CaretFileDialogExtendable(QWidget* parent, +// Qt::WindowFlags f) +//: QDialog(parent, +// f) +//{ +// Qt::WindowFlags flags = 0; +// m_caretFileDialog = new CaretFileDialogPrivate(NULL, +// flags); +// +// createDialog(); +//} +///** +// * Constructor. +// */ +//CaretFileDialogExtendable::CaretFileDialogExtendable(QWidget* parent, +// const QString& caption, +// const QString& directory, +// const QString& filter) +//: QDialog(parent) +//{ +// m_caretFileDialog = new CaretFileDialogPrivate(NULL, +// caption, +// directory, +// filter); +// +// createDialog(); +//} +// +// +///** +// * Destructor. +// */ +//CaretFileDialogExtendable::~CaretFileDialogExtendable() +//{ +// +//} +// +///** +// * Create the dialog. +// */ +//void +//CaretFileDialogExtendable::createDialog() +//{ +// QObject::connect(m_caretFileDialog, SIGNAL(currentChanged(const QString&)), +// this, SIGNAL(currentChanged(const QString&))); +// +// QObject::connect(m_caretFileDialog, SIGNAL( directoryEntered(const QString&)), +// this, SIGNAL( directoryEntered(const QString&))); +// +// QObject::connect(m_caretFileDialog, SIGNAL(fileSelected(const QString&)), +// this, SIGNAL(fileSelected(const QString&))); +// +// QObject::connect(m_caretFileDialog, SIGNAL(filesSelected(const QStringList&)), +// this, SIGNAL(filesSelected(const QStringList&))); +// +// QObject::connect(m_caretFileDialog, SIGNAL(filterSelected(const QString&)), +// this, SIGNAL(filterSelected(const QString&))); +// +// QObject::connect(m_caretFileDialog, SIGNAL(finished(int)), +// this, SLOT(fileDialogFinished(int))); +// +// m_caretFileDialog->setAttribute(Qt::WA_DeleteOnClose, false); +// m_caretFileDialog->setSizeGripEnabled(false); +// +// m_dialogLayout = new QVBoxLayout(this); +// m_dialogLayout->addWidget(m_caretFileDialog); +//} +// +///** +// * Gets called when child file dialog issues its finished signal. +// * @param result +// * Result of dialog. +// */ +//void +//CaretFileDialogExtendable::fileDialogFinished(int result) +//{ +// this->setResult(result); +// this->done(result); +//} +// +// +///** +// * Add a widget at the bottom to this file dialog. +// * @param widget +// * Widget that is added. +// */ +//void +//CaretFileDialogExtendable::addWidget(QWidget* widget) +//{ +// CaretAssert(widget); +// m_dialogLayout->addWidget(widget); +//} +// +// +////================================================================================== +// +//CaretFileDialogPrivate::CaretFileDialogPrivate(QWidget* parent, +// Qt::WindowFlags /*f*/) +//: CaretFileDialog(parent, +// Qt::Widget) +//{ +//} +// +//CaretFileDialogPrivate::CaretFileDialogPrivate(QWidget* parent, +// const QString& caption, +// const QString& directory, +// const QString& filter) +//: CaretFileDialog(parent, +// caption, +// directory, +// filter) +//{ +//} +// +//CaretFileDialogPrivate::~CaretFileDialogPrivate() +//{ +//} +// +//void +//CaretFileDialogPrivate::closeEvent(QCloseEvent* event) +//{ +// event->ignore(); +//} +// +//void +//CaretFileDialogPrivate::done(int result) +//{ +// CaretFileDialog::done(result); +//} diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretFileDialog.h connectome-workbench-1.5.0/src/GuiQt/CaretFileDialog.h --- connectome-workbench-1.4.2/src/GuiQt/CaretFileDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretFileDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -34,14 +34,21 @@ Q_OBJECT public: - CaretFileDialog(QWidget* parent, - Qt::WindowFlags f); + enum class Mode { + MODE_OPEN, + MODE_SAVE + }; - CaretFileDialog(QWidget* parent = 0, - const QString& caption = QString(), - const QString& directory = QString(), - const QString& filter = QString()); + CaretFileDialog(const Mode mode, + QWidget* parent, + Qt::WindowFlags f); + CaretFileDialog(const Mode mode, + QWidget* parent = 0, + const QString& caption = QString(), + const QString& directory = QString(), + const QString& filter = QString()); + virtual ~CaretFileDialog(); // modal method to get open file name @@ -110,6 +117,8 @@ void initializeCaretFileDialog(); + const Mode m_mode; + FilterFilesProxyModel* m_filterFilesProxyModel; class PreviousDialogSettings { @@ -145,8 +154,9 @@ void setDataFileTypeForFiltering(const DataFileTypeEnum::Enum dataFileType); protected: - bool filterAcceptsRow ( int sourceRow, const QModelIndex & sourceParent ) const; + bool filterAcceptsRow ( int sourceRow, const QModelIndex & sourceParent ) const override; + void sort(int column, Qt::SortOrder order) override; private: DataFileTypeEnum::Enum m_dataFileType; diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretFileRemoteDialog.cxx connectome-workbench-1.5.0/src/GuiQt/CaretFileRemoteDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretFileRemoteDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretFileRemoteDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -45,7 +45,6 @@ #include "EventDataFileRead.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" -#include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ProgressReportingDialog.h" @@ -261,7 +260,7 @@ const int numStandardData = static_cast(m_standardData.size()); for (int i = 0; i < numStandardData; i++) { m_standardFileComboBox->addItem(m_standardData[i].m_userFriendlyName, - qVariantFromValue(i)); + QVariant::fromValue(i)); } m_standardFileComboBox->blockSignals(false); diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx connectome-workbench-1.5.0/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -281,7 +281,7 @@ iter++) { CaretMappableDataFile* cmdf = *iter; this->mapFileComboBox->addItem(cmdf->getFileNameNoPath(), - qVariantFromValue((void*)cmdf)); + QVariant::fromValue((void*)cmdf)); } if ((selectedFileIndex >= 0) @@ -974,7 +974,7 @@ } m_mapFileStructureComboBox->setSelectedStructure(ps->m_structure); - const int fileIndex = this->mapFileComboBox->findData(qVariantFromValue((void*)ps->m_mapFile)); + const int fileIndex = this->mapFileComboBox->findData(QVariant::fromValue((void*)ps->m_mapFile)); if (fileIndex >= 0) { this->setMapFileComboBoxCurrentIndex(fileIndex); diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretResultDialog.cxx connectome-workbench-1.5.0/src/GuiQt/CaretResultDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/CaretResultDialog.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretResultDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,119 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CARET_RESULT_DIALOG_DECLARE__ +#include "CaretResultDialog.h" +#undef __CARET_RESULT_DIALOG_DECLARE__ + +#include + +#include "CaretAssert.h" + +using namespace caret; + + + +/** + * \class caret::CaretResultDialog + * \brief Convenience for display of a 'CaretResult' instance in a message box + * \ingroup GuiQt + * + */ + +/** + * Run the 'dialog'. A dialog is displayed if there is an error or if the description is not empty. + * @param caretResult + * The caret result instance + * @param parent + * Parent for any dialog that is dispalyed. + * @return True if the CaretResult is NO error, else false. + */ +bool +CaretResultDialog::run(const std::unique_ptr& caretResult, + QWidget* parent) +{ + QString description = caretResult->getErrorDescription(); + + QString dialogTitle; + QMessageBox::Icon dialogIcon(QMessageBox::NoIcon); + bool successFlag(false); + bool showDialogFlag(false); + switch (caretResult->getErrorStatusCode()) { + case CaretResult::GENERIC_ERROR: + showDialogFlag = true; + if (description.isEmpty()) { + description = "Unknown error occurred."; + } + dialogIcon = QMessageBox::Critical; + break; + case CaretResult::NO_ERROR: + successFlag = true; + if ( ! description.isEmpty()) { + showDialogFlag = true; + } + dialogIcon = QMessageBox::Information; + break; + } + + if (showDialogFlag) { + QMessageBox msgBox(dialogIcon, + dialogTitle, + description, + QMessageBox::Ok, + parent); + msgBox.exec(); + } + return successFlag; +} + +/** + * Run the 'dialog'. A dialog is displayed if there is an error or if the description is not empty. + * @param caretResult + * The caret result instance + * @param parent + * Parent for any dialog that is dispalyed. + * @return True if the result instance is no error, else false. + */ +bool +CaretResultDialog::isError(const std::unique_ptr& caretResult, + QWidget* parent) +{ + return ( ! CaretResultDialog::run(caretResult, + parent)); +} + +/** + * Run the 'dialog'. A dialog is displayed if there is an error or if the description is not empty. + * @param caretResult + * The caret result instance + * @param parent + * Parent for any dialog that is dispalyed. + * @return True if the result instance is no error, else false. + */ +bool +CaretResultDialog::isSuccess(const std::unique_ptr& caretResult, + QWidget* parent) +{ + return CaretResultDialog::run(caretResult, + parent); +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/CaretResultDialog.h connectome-workbench-1.5.0/src/GuiQt/CaretResultDialog.h --- connectome-workbench-1.4.2/src/GuiQt/CaretResultDialog.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CaretResultDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,59 @@ +#ifndef __CARET_RESULT_DIALOG_H__ +#define __CARET_RESULT_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretResult.h" + +class QWidget; + +namespace caret { + + class CaretResultDialog { + + public: + static bool isError(const std::unique_ptr& caretResult, + QWidget* parent); + + static bool isSuccess(const std::unique_ptr& caretResult, + QWidget* parent); + + + // ADD_NEW_METHODS_HERE + + private: + static bool run(const std::unique_ptr& caretResult, + QWidget* parent); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CARET_RESULT_DIALOG_DECLARE__ + // +#endif // __CARET_RESULT_DIALOG_DECLARE__ + +} // namespace +#endif //__CARET_RESULT_DIALOG_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartHistoryViewController.cxx connectome-workbench-1.5.0/src/GuiQt/ChartHistoryViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartHistoryViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartHistoryViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -595,7 +595,7 @@ else if (selectedAction == removeAction) { chartModel->removeChartAtIndex(indx); } - else { + else if (selectedAction != NULL) { CaretAssertMessage(0, "Has a new action been added but not processed?"); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartLinesSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/ChartLinesSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartLinesSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartLinesSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -46,7 +46,6 @@ #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventPaletteColorMappingEditorDialogRequest.h" -#include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MapYokingGroupComboBox.h" @@ -222,7 +221,7 @@ const bool checkBoxStatus = chartBrainFile->isLineSeriesChartingEnabled(browserTabIndex); - QVariant brainordinateFilePointerVariant = qVariantFromValue((void*)chartBrainFile); + QVariant brainordinateFilePointerVariant = QVariant::fromValue((void*)chartBrainFile); CaretMappableDataFile* caretMappableDataFile = chartBrainFile->getLineSeriesChartCaretMappableDataFile(); diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -762,8 +762,6 @@ parcelMapFileLayout->addWidget(mapNameComboBox, 3, 1); } break; - default: - CaretAssert(0); } return groupBox; diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -60,7 +60,6 @@ #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventPaletteColorMappingEditorDialogRequest.h" -#include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MapYokingGroupComboBox.h" diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.cxx connectome-workbench-1.5.0/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,87 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_AXIS_PROPERTIES_EDITOR_DIALOG_DECLARE__ +#include "ChartTwoAxisPropertiesEditorDialog.h" +#undef __CHART_TWO_AXIS_PROPERTIES_EDITOR_DIALOG_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "ChartTwoAxisPropertiesEditorWidget.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoAxisPropertiesEditorDialog + * \brief Dialog for editing chart two axeis properties + * \ingroup GuiQt + * + * This is not a traditional dialog. It has no close buttons nor titlebar + * and uses the Qt::Popup window attribute. + */ + +/** + * Constructor. + * @param axisLocation + * Location of the axis + * @param parentObjectName + * Name of parent for macros + * @param parent + * The parent widget + */ +ChartTwoAxisPropertiesEditorDialog::ChartTwoAxisPropertiesEditorDialog(const ChartAxisLocationEnum::Enum axisLocation, + const QString& parentObjectName, + QWidget* parent) +: QDialog(parent, + Qt::Popup) /* use 'Popup' attribute so not borders */ +{ + m_editorWidget = new ChartTwoAxisPropertiesEditorWidget(axisLocation, + parentObjectName, + parent); + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout , 0, 0); + layout->addWidget(m_editorWidget); +} + +/** + * Destructor. + */ +ChartTwoAxisPropertiesEditorDialog::~ChartTwoAxisPropertiesEditorDialog() +{ +} + +/** + * @param chartOverlaySet + * Update with this chart overlay set + * @param chartAxis + * Axis for updating dialog + */ +void +ChartTwoAxisPropertiesEditorDialog::updateControls(ChartTwoOverlaySet* chartOverlaySet, + ChartTwoCartesianAxis* chartAxis) +{ + m_editorWidget->updateControls(chartOverlaySet, + chartAxis); +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.h connectome-workbench-1.5.0/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.h --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,68 @@ +#ifndef __CHART_TWO_AXIS_PROPERTIES_EDITOR_DIALOG_H__ +#define __CHART_TWO_AXIS_PROPERTIES_EDITOR_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "ChartAxisLocationEnum.h" + +namespace caret { + + class ChartTwoAxisPropertiesEditorWidget; + class ChartTwoCartesianAxis; + class ChartTwoOverlaySet; + + class ChartTwoAxisPropertiesEditorDialog : public QDialog { + + Q_OBJECT + + public: + ChartTwoAxisPropertiesEditorDialog(const ChartAxisLocationEnum::Enum axisLocation, + const QString& parentObjectName, + QWidget* parent); + + ChartTwoAxisPropertiesEditorDialog(); + + virtual ~ChartTwoAxisPropertiesEditorDialog(); + + ChartTwoAxisPropertiesEditorDialog(const ChartTwoAxisPropertiesEditorDialog&) = delete; + + ChartTwoAxisPropertiesEditorDialog& operator=(const ChartTwoAxisPropertiesEditorDialog&) = delete; + + void updateControls(ChartTwoOverlaySet* chartOverlaySet, + ChartTwoCartesianAxis* chartAxis); + + private: + ChartTwoAxisPropertiesEditorWidget* m_editorWidget; + + }; + +#ifdef __CHART_TWO_AXIS_PROPERTIES_EDITOR_DIALOG_DECLARE__ + // +#endif // __CHART_TWO_AXIS_PROPERTIES_EDITOR_DIALOG_DECLARE__ + +} // namespace +#endif //__CHART_TWO_AXIS_PROPERTIES_EDITOR_DIALOG_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.cxx connectome-workbench-1.5.0/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,620 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_DECLARE__ +#include "ChartTwoAxisPropertiesEditorWidget.h" +#undef __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_DECLARE__ + +#include "CaretAssert.h" +#include "ChartTwoCartesianAxis.h" +#include "ChartTwoCartesianCustomSubdivisionsEditorWidget.h" +#include "ChartTwoOverlay.h" +#include "ChartTwoOverlaySet.h" +#include "ChartableTwoFileBaseChart.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileHistogramChart.h" +#include "DisplayGroupEnumComboBox.h" +#include "EnumComboBoxTemplate.h" +#include "EventChartTwoCartesianAxisDisplayGroup.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "ModelChartTwo.h" +#include "WuQDataEntryDialog.h" +#include "WuQFactory.h" +#include "WuQDoubleSpinBox.h" +#include "WuQMacroManager.h" +#include "WuQSpinBox.h" +#include "WuQWidgetObjectGroup.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * Constructor. + * + * @param parent + * The parent + * @param axisLocation + * Location of the axis + * @param parentObjectName + * Name of parent object for macros + */ +ChartTwoAxisPropertiesEditorWidget::ChartTwoAxisPropertiesEditorWidget(const ChartAxisLocationEnum::Enum axisLocation, + const QString& parentObjectName, + QWidget* parent) +: QWidget(parent), +m_chartOverlaySet(NULL), +m_chartAxis(NULL), +m_axisLocation(axisLocation) +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(parentObjectName + + ":ChartAxesEditor:" + + ChartAxisLocationEnum::toGuiName(axisLocation) + + ":"); + + m_showTickMarksCheckBox = new QCheckBox("Ticks"); + QObject::connect(m_showTickMarksCheckBox, &QCheckBox::clicked, + [=](bool clicked) { m_chartAxis->setShowTickmarks(clicked); valueChanged(); }); + m_showTickMarksCheckBox->setToolTip("Show ticks along the axis"); + m_showTickMarksCheckBox->setObjectName(objectNamePrefix + + "ShowTicks"); + macroManager->addMacroSupportToObject(m_showTickMarksCheckBox, + "Enable chart axis ticks"); + + m_showLabelCheckBox = new QCheckBox("Title"); + QObject::connect(m_showLabelCheckBox, &QCheckBox::clicked, + [=](bool clicked) { m_chartAxis->setShowLabel(clicked); valueChanged(); }); + m_showLabelCheckBox->setToolTip("Show title on axis"); + m_showLabelCheckBox->setObjectName(objectNamePrefix + + "ShowLabel"); + macroManager->addMacroSupportToObject(m_showLabelCheckBox, + "Enable chart axis title"); + + m_showNumericsCheckBox = new QCheckBox("Labels"); + QObject::connect(m_showNumericsCheckBox, &QCheckBox::clicked, + [=](bool clicked) { m_chartAxis->setNumericsTextDisplayed(clicked); valueChanged(); }); + m_showNumericsCheckBox->setToolTip("Show labels (numeric values) on axis"); + m_showNumericsCheckBox->setObjectName(objectNamePrefix + + "ShowNumerics"); + macroManager->addMacroSupportToObject(m_showNumericsCheckBox, + "Enable chart axis labels"); + + m_rotateNumericsCheckBox = new QCheckBox("Rotate"); + QObject::connect(m_rotateNumericsCheckBox, &QCheckBox::clicked, + [=](bool clicked) { m_chartAxis->setNumericsTextRotated(clicked); valueChanged(); }); + m_rotateNumericsCheckBox->setToolTip("Rotate numeric scale values on axis"); + m_rotateNumericsCheckBox->setObjectName(objectNamePrefix + + "EnableNumericsRotate"); + macroManager->addMacroSupportToObject(m_rotateNumericsCheckBox, + "Enable rotation of chart axis numerics"); + + /* + * Controls for layer selection and label editing + */ + m_axisLabelToolButton = new QToolButton(); + m_axisLabelToolButton->setText("Edit Title..."); + QObject::connect(m_axisLabelToolButton, &QToolButton::clicked, + this, &ChartTwoAxisPropertiesEditorWidget::axisLabelToolButtonClicked); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_axisLabelToolButton); + m_axisLabelToolButton->setToolTip("Edit the axis name for the file in the selected overlay"); + m_axisLabelToolButton->setObjectName(objectNamePrefix + + "EditAxis"); + macroManager->addMacroSupportToObject(m_axisLabelToolButton, + "Edit chart axis label"); + + m_axisLabelFromOverlayComboBox = new QComboBox(); + m_axisLabelFromOverlayComboBox->setToolTip("Title for axis is from file in selected layer"); + QObject::connect(m_axisLabelFromOverlayComboBox, static_cast(&QComboBox::activated), + [=](int index) { m_chartAxis->setLabelOverlayIndex(index); valueChanged(); }); + m_axisLabelFromOverlayComboBox->setObjectName(objectNamePrefix + + "LabelFromOverlay"); + macroManager->addMacroSupportToObject(m_axisLabelFromOverlayComboBox, + "Select chart axis title overlay source"); + + /* + * Numerics controls + */ + m_userNumericFormatComboBox = new EnumComboBoxTemplate(this); + m_userNumericFormatComboBox->setup(); + QObject::connect(m_userNumericFormatComboBox, &EnumComboBoxTemplate::itemActivated, + [=]() { m_chartAxis->setUserNumericFormat(m_userNumericFormatComboBox->getSelectedItem()); valueChanged(); }); + m_userNumericFormatComboBox->getWidget()->setToolTip("Choose format of axis scale numeric values"); + m_userNumericFormatComboBox->getWidget()->setObjectName(objectNamePrefix + + "Format"); + macroManager->addMacroSupportToObject(m_userNumericFormatComboBox->getWidget(), + "Select chart axis numeric format"); + + m_userDigitsRightOfDecimalSpinBox = new WuQSpinBox(); + m_userDigitsRightOfDecimalSpinBox->setRange(0, 10); + m_userDigitsRightOfDecimalSpinBox->setSingleStep(1); + QObject::connect(m_userDigitsRightOfDecimalSpinBox, static_cast(&WuQSpinBox::valueChanged), + [=](int value) { m_chartAxis->setUserDigitsRightOfDecimal(value); valueChanged(); }); + m_userDigitsRightOfDecimalSpinBox->setToolTip("Set digits right of decimal for\ndecimal or scientific format"); + m_userDigitsRightOfDecimalSpinBox->setObjectName(objectNamePrefix + + "DigitsRightOfDecimal"); + macroManager->addMacroSupportToObject(m_userDigitsRightOfDecimalSpinBox, + "Set chart axis digits right of decimal"); + + m_numericSubdivisionsModeComboBox = new EnumComboBoxTemplate(this); + m_numericSubdivisionsModeComboBox->setup(); + QObject::connect(m_numericSubdivisionsModeComboBox, &EnumComboBoxTemplate::itemActivated, + [=]() { m_chartAxis->setNumericSubdivsionsMode(m_numericSubdivisionsModeComboBox->getSelectedItem()); valueChanged();} ); + m_numericSubdivisionsModeComboBox->getWidget()->setToolTip("Numeric subdivisions mode"); + m_numericSubdivisionsModeComboBox->getWidget()->setObjectName(objectNamePrefix + + "NumericSubdivisionsMode"); + macroManager->addMacroSupportToObject(m_numericSubdivisionsModeComboBox->getWidget(), + "Set chart axis numeric subdivisions mode"); + + m_userSubdivisionsSpinBox = new WuQSpinBox(); + m_userSubdivisionsSpinBox->setRange(0, 99); + m_userSubdivisionsSpinBox->setSingleStep(1); + QObject::connect(m_userSubdivisionsSpinBox, static_cast(&WuQSpinBox::valueChanged), + [=](int value) { m_chartAxis->setUserNumberOfSubdivisions(value); valueChanged(); }); + m_userSubdivisionsSpinBox->setToolTip("Set subdivisions on the axis when Auto is not checked"); + m_userSubdivisionsSpinBox->setObjectName(objectNamePrefix + + "NumberOfSubdivisions"); + macroManager->addMacroSupportToObject(m_userSubdivisionsSpinBox, + "Set chart axis number of subivisions"); + + /* + * Size spin boxes + */ + m_labelSizeSpinBox = new WuQDoubleSpinBox(this); + m_labelSizeSpinBox->setRangePercentage(0.0, 99.0); + m_labelSizeSpinBox->setDecimals(1); + QObject::connect(m_labelSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + [=](double value) { m_chartAxis->setLabelTextSize(value); valueChanged(); }); + m_labelSizeSpinBox->setToolTip("Set height of title as percentage of tab height for selected axis"); + m_labelSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "LabelHeight"); + macroManager->addMacroSupportToObject(m_labelSizeSpinBox->getWidget(), + "Set chart axis title height"); + + m_numericsSizeSpinBox = new WuQDoubleSpinBox(this); + m_numericsSizeSpinBox->setRangePercentage(0.0, 99.0); + m_numericsSizeSpinBox->setDecimals(1); + QObject::connect(m_numericsSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + [=](double value) { m_chartAxis->setNumericsTextSize(value); valueChanged(); }); + m_numericsSizeSpinBox->setToolTip("Set height of labels (numeric values) as percentage of tab height for selected axis"); + m_numericsSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "NumericValueHeight"); + macroManager->addMacroSupportToObject(m_numericsSizeSpinBox->getWidget(), + "Set chart axis numerics height"); + + const QString linesToolTip(WuQtUtilities::createWordWrappedToolTipText("This is a TAB property (all axes in this tab use this line thickness). " + "The line thickness is a percentage of the tab's height.")); + QLabel* linesLabel = new QLabel("Lines"); + linesLabel->setToolTip(linesToolTip); + m_linesTicksSizeSpinBox = new WuQDoubleSpinBox(this); + m_linesTicksSizeSpinBox->setDecimals(1); + m_linesTicksSizeSpinBox->setRangePercentage(0.0, 99.0); + QObject::connect(m_linesTicksSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &ChartTwoAxisPropertiesEditorWidget::axisLineThicknessChanged); + m_linesTicksSizeSpinBox->setToolTip(linesToolTip); + m_linesTicksSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "TicksSize"); + macroManager->addMacroSupportToObject(m_linesTicksSizeSpinBox->getWidget(), + "Set chart axis ticks height"); + + m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); + m_paddingSizeSpinBox->setDecimals(1); + m_paddingSizeSpinBox->setRangePercentage(0.0, 99.0); + QObject::connect(m_paddingSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + [=](double value) { m_chartAxis->setPaddingSize(value); valueChanged(); }); + m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height for selected axis"); + m_paddingSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "PaddingSize"); + macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget(), + "Set chart axis padding height"); + + /* + * Group widgets for blocking signals + */ + m_widgetGroup = new WuQWidgetObjectGroup(this); + m_widgetGroup->add(m_axisLabelToolButton); + m_widgetGroup->add(m_showTickMarksCheckBox); + m_widgetGroup->add(m_showLabelCheckBox); + m_widgetGroup->add(m_showNumericsCheckBox); + m_widgetGroup->add(m_rotateNumericsCheckBox); + m_widgetGroup->add(m_axisLabelFromOverlayComboBox); + m_widgetGroup->add(m_userNumericFormatComboBox->getWidget()); + m_widgetGroup->add(m_userDigitsRightOfDecimalSpinBox); + m_widgetGroup->add(m_numericSubdivisionsModeComboBox->getWidget()); + m_widgetGroup->add(m_userSubdivisionsSpinBox); + m_widgetGroup->add(m_labelSizeSpinBox); + m_widgetGroup->add(m_numericsSizeSpinBox); + m_widgetGroup->add(m_linesTicksSizeSpinBox); + m_widgetGroup->add(m_paddingSizeSpinBox); + + /* + * Size layout + */ + QWidget* sizesWidget = new QWidget(); + QGridLayout* sizesLayout = new QGridLayout(sizesWidget); + WuQtUtilities::setLayoutSpacingAndMargins(sizesLayout, 4, 0); + int32_t sizesRow = 0; + sizesLayout->addWidget(new QLabel("Sizes"), sizesRow, 0, 1, 2, Qt::AlignHCenter); + sizesRow++; + sizesLayout->addWidget(new QLabel("Labels"), sizesRow, 0); + sizesLayout->addWidget(m_numericsSizeSpinBox->getWidget(), sizesRow, 1); + sizesRow++; + sizesLayout->addWidget(new QLabel("Pad"), sizesRow, 0); + sizesLayout->addWidget(m_paddingSizeSpinBox->getWidget(), sizesRow, 1); + sizesRow++; + sizesLayout->addWidget(new QLabel("Title"), sizesRow, 0); + sizesLayout->addWidget(m_labelSizeSpinBox->getWidget(), sizesRow, 1); + sizesRow++; + sizesLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), sizesRow, 0, 1, 2); + sizesRow++; + sizesLayout->addWidget(linesLabel, sizesRow, 0); + sizesLayout->addWidget(m_linesTicksSizeSpinBox->getWidget(), sizesRow, 1); + sizesRow++; + + /* + * Show widgets layout + */ + QWidget* showWidget = new QWidget(); + QGridLayout* showLayout = new QGridLayout(showWidget); + WuQtUtilities::setLayoutSpacingAndMargins(showLayout, 4, 0); + int32_t axisRow = 0; + showLayout->addWidget(new QLabel("Show"), axisRow, 0, Qt::AlignHCenter); + axisRow++; + showLayout->addWidget(m_showNumericsCheckBox, axisRow, 0); + axisRow++; + showLayout->addWidget(m_rotateNumericsCheckBox, axisRow, 0); + axisRow++; + showLayout->addWidget(m_showTickMarksCheckBox, axisRow, 0); + axisRow++; + showLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), axisRow, 0); + axisRow++; + showLayout->addWidget(m_showLabelCheckBox, axisRow, 0); + axisRow++; + showWidget->setSizePolicy(showWidget->sizePolicy().horizontalPolicy(), + QSizePolicy::Fixed); + + QHBoxLayout* subdivLayout = new QHBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(subdivLayout, 3, 0); + subdivLayout->addWidget(new QLabel("Subdiv")); + subdivLayout->addWidget(m_numericSubdivisionsModeComboBox->getWidget()); + subdivLayout->addWidget(m_userSubdivisionsSpinBox); + subdivLayout->addStretch(); + + /* + * Numerics widgets layout + */ + QWidget* stdNumericsWidget = new QWidget(); + QGridLayout* stdNumericsLayout = new QGridLayout(stdNumericsWidget); + WuQtUtilities::setLayoutSpacingAndMargins(stdNumericsLayout, 6, 0); + int stdNumericsRow = 0; + stdNumericsLayout->addWidget(new QLabel("Format"), stdNumericsRow, 0); + stdNumericsLayout->addWidget(m_userNumericFormatComboBox->getWidget(), stdNumericsRow, 1); + stdNumericsLayout->addWidget(new QLabel("Decimals"), stdNumericsRow, 2); + stdNumericsLayout->addWidget(m_userDigitsRightOfDecimalSpinBox, stdNumericsRow, 3); + stdNumericsRow++; + stdNumericsLayout->addLayout(subdivLayout, stdNumericsRow, 0, 1, 4); + stdNumericsWidget->setFixedSize(stdNumericsWidget->sizeHint()); + + /* + * Custom axis numerics editor widget + */ + m_customSubdivisionsEditorWidget = new ChartTwoCartesianCustomSubdivisionsEditorWidget(); + QObject::connect(m_customSubdivisionsEditorWidget, &ChartTwoCartesianCustomSubdivisionsEditorWidget::widgetSizeChanged, + [=]() { + QWidget* parent = parentWidget(); + if (parent != NULL) { + parent->adjustSize(); + } + }); + + /* + * Subdivisions mode + */ + m_chartSubdivisionsModeEnumComboBox = new EnumComboBoxTemplate(this); + m_chartSubdivisionsModeEnumComboBox->setup(); + QObject::connect(m_chartSubdivisionsModeEnumComboBox, &EnumComboBoxTemplate::itemActivated, + this, &ChartTwoAxisPropertiesEditorWidget::chartSubdivisionsModeEnumComboBoxItemActivated); + /* + * Numerics layout for its tab bar and stacked widget + */ + m_numericsStackedWidget = new QStackedWidget(); + m_numericsStackedWidgetStandardSubdivsionsIndex = m_numericsStackedWidget->addWidget(stdNumericsWidget); + m_numericsStackedWidgetCustomSubdivsionsIndex = m_numericsStackedWidget->addWidget(m_customSubdivisionsEditorWidget); + + QWidget* numericsWidget = new QWidget(); + QGridLayout* numericsLayout = new QGridLayout(numericsWidget); + WuQtUtilities::setLayoutSpacingAndMargins(numericsLayout, 0, 0); + numericsLayout->addWidget(new QLabel("Numerics"), 0, 0, 1, 3, Qt::AlignHCenter); + numericsLayout->addWidget(new QLabel("Mode"), 1, 0); + numericsLayout->addWidget(m_chartSubdivisionsModeEnumComboBox->getWidget(), 1, 1); + numericsLayout->addWidget(m_numericsStackedWidget, 2, 0, 1, 3, Qt::AlignLeft); + numericsLayout->setColumnStretch(0, 0); + numericsLayout->setColumnStretch(1, 0); + numericsLayout->setColumnStretch(2, 100); + + /* + * Title layout + */ + QWidget* labelWidget = new QWidget(); + QGridLayout* labelLayout = new QGridLayout(labelWidget); + WuQtUtilities::setLayoutSpacingAndMargins(labelLayout, 3, 0); + int labelRow(0); + labelLayout->addWidget(new QLabel("Axis Title"), labelRow, 0); + labelRow++; + labelLayout->addWidget(m_axisLabelFromOverlayComboBox, labelRow, 0); + labelRow++; + labelLayout->addWidget(m_axisLabelToolButton, labelRow, 0); + labelRow++; + + /* + * Grid layout containing layouts + */ + QHBoxLayout* controlsLayout = new QHBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(controlsLayout, 3, 4); + controlsLayout->addWidget(showWidget, 0, Qt::AlignTop); + controlsLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + controlsLayout->addWidget(sizesWidget, 0, Qt::AlignTop); + controlsLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + controlsLayout->addWidget(numericsWidget, 0, Qt::AlignTop); + controlsLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + controlsLayout->addWidget(labelWidget, 0, Qt::AlignTop); + controlsLayout->addStretch(); + + QLabel* groupLabel = new QLabel("Group"); + m_displayGroupComboBox = new DisplayGroupEnumComboBox(this, + (objectNamePrefix + + ":DisplayGroup"), + "AxisDisplayGroup"); + QObject::connect(m_displayGroupComboBox, &DisplayGroupEnumComboBox::displayGroupSelected, + this, &ChartTwoAxisPropertiesEditorWidget::displayGroupSelected); + + QHBoxLayout* groupLayout = new QHBoxLayout(); + groupLayout->addWidget(groupLabel); + groupLayout->addWidget(m_displayGroupComboBox->getWidget()); + groupLayout->addStretch(); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 3, 0); + layout->addLayout(groupLayout); + layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); + layout->addLayout(controlsLayout); +} + +/** + * Destructor. + */ +ChartTwoAxisPropertiesEditorWidget::~ChartTwoAxisPropertiesEditorWidget() +{ +} + +/** + * Update the controls + * @param chartOverlaySet + * The chart overlay set + * @param chartAxis + * The chart axis + */ +void +ChartTwoAxisPropertiesEditorWidget::updateControls(ChartTwoOverlaySet* chartOverlaySet, + ChartTwoCartesianAxis* chartAxis) +{ + m_widgetGroup->blockAllSignals(true); + + m_chartOverlaySet = chartOverlaySet; + m_chartAxis = chartAxis; + + if ((m_chartOverlaySet != NULL) + && (m_chartAxis != NULL)) { + m_displayGroupComboBox->setSelectedDisplayGroup(m_chartAxis->getDisplayGroup()); + + const ChartTwoCartesianSubdivisionsModeEnum::Enum subdivisionsMode = m_chartAxis->getSubdivisionsMode(); + m_chartSubdivisionsModeEnumComboBox->setSelectedItem(subdivisionsMode); + switch (subdivisionsMode) { + case ChartTwoCartesianSubdivisionsModeEnum::CUSTOM: + m_numericsStackedWidget->setCurrentIndex(m_numericsStackedWidgetCustomSubdivsionsIndex); + break; + case ChartTwoCartesianSubdivisionsModeEnum::STANDARD: + m_numericsStackedWidget->setCurrentIndex(m_numericsStackedWidgetStandardSubdivsionsIndex); + break; + } + + m_showTickMarksCheckBox->setChecked(m_chartAxis->isShowTickmarks()); + m_showLabelCheckBox->setChecked(m_chartAxis->isShowLabel()); + m_showNumericsCheckBox->setChecked(m_chartAxis->isNumericsTextDisplayed()); + m_rotateNumericsCheckBox->setChecked(m_chartAxis->isNumericsTextRotated()); + const NumericFormatModeEnum::Enum numericFormat = m_chartAxis->getUserNumericFormat(); + m_userNumericFormatComboBox->setSelectedItem(numericFormat); + m_userDigitsRightOfDecimalSpinBox->setValue(m_chartAxis->getUserDigitsRightOfDecimal()); + m_userDigitsRightOfDecimalSpinBox->setEnabled(numericFormat != NumericFormatModeEnum::AUTO); + m_numericSubdivisionsModeComboBox->setSelectedItem(m_chartAxis->getNumericSubdivsionsMode()); + m_userSubdivisionsSpinBox->setValue(m_chartAxis->getUserNumberOfSubdivisions()); + m_userSubdivisionsSpinBox->setEnabled( m_chartAxis->getNumericSubdivsionsMode() == ChartTwoNumericSubdivisionsModeEnum::USER); + + m_labelSizeSpinBox->setValue(m_chartAxis->getLabelTextSize()); + m_numericsSizeSpinBox->setValue(m_chartAxis->getNumericsTextSize()); + m_linesTicksSizeSpinBox->setValue(m_chartOverlaySet->getAxisLineThickness()); + m_paddingSizeSpinBox->setValue(m_chartAxis->getPaddingSize()); + + const int32_t overlayCount = m_chartOverlaySet->getNumberOfDisplayedOverlays(); + int32_t selectedOverlayIndex = m_chartAxis->getLabelOverlayIndex(overlayCount); + const int32_t comboBoxCount = m_axisLabelFromOverlayComboBox->count(); + if (overlayCount < comboBoxCount) { + m_axisLabelFromOverlayComboBox->setMaxCount(overlayCount); + } + else if (overlayCount > comboBoxCount) { + for (int32_t j = comboBoxCount; j < overlayCount; j++) { + m_axisLabelFromOverlayComboBox->addItem(" From Layer " + AString::number(j + 1)); + } + } + + if ((selectedOverlayIndex >= 0) + && (selectedOverlayIndex < m_axisLabelFromOverlayComboBox->count())) { + m_axisLabelFromOverlayComboBox->setCurrentIndex(selectedOverlayIndex); + } + setEnabled(true); + } + else { + setEnabled(false); + } + + ChartTwoCartesianCustomSubdivisions* customSubdivsions = ((m_chartAxis != NULL) + ? m_chartAxis->getCustomSubdivisions() + : NULL); + m_customSubdivisionsEditorWidget->updateContent(customSubdivsions); + + m_widgetGroup->blockAllSignals(false); + + layout()->invalidate(); + adjustSize(); + parentWidget()->adjustSize(); +} + +/** + * Called when subdivisions mode is changed + */ +void +ChartTwoAxisPropertiesEditorWidget::chartSubdivisionsModeEnumComboBoxItemActivated() +{ + CaretAssert(m_chartAxis); + if (m_chartAxis != NULL) { + const ChartTwoCartesianSubdivisionsModeEnum::Enum subdivisionsMode = m_chartSubdivisionsModeEnumComboBox->getSelectedItem(); + m_chartAxis->setSubdivisionsMode(subdivisionsMode); + /* + * Need to update controls as some items (such as numeric format + * combo box) affect enabled status of items (numeric digits + * right of decimal) + */ + valueChanged(); + } +} + + +/** + * Called when the axis line thickness changes. + */ +void +ChartTwoAxisPropertiesEditorWidget::axisLineThicknessChanged(double) +{ + /* + * Line thickness is in parent overlay set + */ + if (m_chartOverlaySet != NULL) { + m_chartOverlaySet->setAxisLineThickness(m_linesTicksSizeSpinBox->value()); + updateGraphics(); + } +} + + +/** + * Called when a widget is changed by the user. + */ +void +ChartTwoAxisPropertiesEditorWidget::valueChanged() +{ + CaretAssert(m_chartAxis); + if (m_chartAxis != NULL) { + /* + * Need to update controls as some items (such as numeric format + * combo box) affect enabled status of items (numeric digits + * right of decimal) + */ + updateControls(m_chartOverlaySet, + m_chartAxis); + } + + updateGraphics(); +} + +/** + * Update the graphics. + */ +void +ChartTwoAxisPropertiesEditorWidget::updateGraphics() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Called when name toolbutton is clicked to change axis label. + */ +void +ChartTwoAxisPropertiesEditorWidget::axisLabelToolButtonClicked(bool) +{ + if ((m_chartOverlaySet != NULL) + && (m_chartAxis != NULL)) { + WuQDataEntryDialog newNameDialog("Axis Title", + m_axisLabelToolButton); + QLineEdit* lineEdit = newNameDialog.addLineEditWidget("Title"); + lineEdit->setText(m_chartOverlaySet->getAxisLabel(m_chartAxis)); + if (newNameDialog.exec() == WuQDataEntryDialog::Accepted) { + const AString name = lineEdit->text().trimmed(); + m_chartOverlaySet->setAxisLabel(m_chartAxis, name); + valueChanged(); + } + } +} + +/** + * Called when the label display group combo box is changed. + * @param displayGroup + * Newly selected group + */ +void +ChartTwoAxisPropertiesEditorWidget::displayGroupSelected(const DisplayGroupEnum::Enum displayGroup) +{ + if (m_chartAxis != NULL) { + /* + * Newly selected group is NOT TAB so it must be a GROUP + */ + if (displayGroup != DisplayGroupEnum::DISPLAY_GROUP_TAB) { + /* + * New group is different from old group so have + * transitioned from TAB to a GROUP or from + * a GROUP to a DIFFERENT GROUP + */ + if (displayGroup != m_chartAxis->getDisplayGroup()) { + /* + * if NO axes are yoked to the display group, this axis parameters + * are copied to the display group + */ + EventChartTwoCartesianAxisDisplayGroup::initializeUnyokedDisplayGroupAxis(displayGroup, + m_chartAxis); + } + } + m_chartAxis->setDisplayGroup(displayGroup); + valueChanged(); + } +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.h connectome-workbench-1.5.0/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.h --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,140 @@ +#ifndef __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_H__ +#define __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include "ChartAxisLocationEnum.h" +#include "DisplayGroupEnum.h" + +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QLabel; +class QStackedWidget; +class QTabBar; +class QToolButton; + +namespace caret { + + class AnnotationPercentSizeText; + class ChartTwoCartesianAxis; + class ChartTwoCartesianAxisWidget; + class ChartTwoCartesianCustomSubdivisionsEditorWidget; + class ChartTwoOverlaySet; + class DisplayGroupEnumComboBox; + class EnumComboBoxTemplate; + class WuQDoubleSpinBox; + class WuQSpinBox; + class WuQWidgetObjectGroup; + + class ChartTwoAxisPropertiesEditorWidget : public QWidget { + Q_OBJECT + + public: + ChartTwoAxisPropertiesEditorWidget(const ChartAxisLocationEnum::Enum axisLocation, + const QString& parentObjectName, + QWidget* parent = 0); + + virtual ~ChartTwoAxisPropertiesEditorWidget(); + + void updateControls(ChartTwoOverlaySet* chartOverlaySet, + ChartTwoCartesianAxis* chartAxis); + + // ADD_NEW_METHODS_HERE + + private slots: + void valueChanged(); + + void axisLabelToolButtonClicked(bool); + + void axisLineThicknessChanged(double); + + void chartSubdivisionsModeEnumComboBoxItemActivated(); + + void displayGroupSelected(const DisplayGroupEnum::Enum); + + private: + ChartTwoAxisPropertiesEditorWidget(const ChartTwoAxisPropertiesEditorWidget&); + + ChartTwoAxisPropertiesEditorWidget& operator=(const ChartTwoAxisPropertiesEditorWidget&); + + void updateGraphics(); + + // ADD_NEW_MEMBERS_HERE + + ChartTwoOverlaySet* m_chartOverlaySet; + + ChartTwoCartesianAxis* m_chartAxis; + + const ChartAxisLocationEnum::Enum m_axisLocation; + + EnumComboBoxTemplate* m_chartSubdivisionsModeEnumComboBox; + + QToolButton* m_axisLabelToolButton; + + QComboBox* m_axisLabelFromOverlayComboBox; + + QCheckBox* m_showTickMarksCheckBox; + + QCheckBox* m_showLabelCheckBox; + + QCheckBox* m_showNumericsCheckBox; + + QCheckBox* m_rotateNumericsCheckBox; + + EnumComboBoxTemplate* m_userNumericFormatComboBox; + + WuQSpinBox* m_userDigitsRightOfDecimalSpinBox; + + EnumComboBoxTemplate* m_numericSubdivisionsModeComboBox; + + WuQSpinBox* m_userSubdivisionsSpinBox; + + WuQDoubleSpinBox* m_labelSizeSpinBox; + + WuQDoubleSpinBox* m_numericsSizeSpinBox; + + WuQDoubleSpinBox* m_linesTicksSizeSpinBox; + + WuQDoubleSpinBox* m_paddingSizeSpinBox; + + QStackedWidget* m_numericsStackedWidget; + + int32_t m_numericsStackedWidgetStandardSubdivsionsIndex = -1; + + int32_t m_numericsStackedWidgetCustomSubdivsionsIndex = -1; + + DisplayGroupEnumComboBox* m_displayGroupComboBox; + + ChartTwoCartesianCustomSubdivisionsEditorWidget* m_customSubdivisionsEditorWidget; + + WuQWidgetObjectGroup* m_widgetGroup; + + }; + +#ifdef __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_DECLARE__ + // +#endif // __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_DECLARE__ + +} // namespace +#endif //__CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.cxx connectome-workbench-1.5.0/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,283 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_EDITOR_WIDGET_DECLARE__ +#include "ChartTwoCartesianCustomSubdivisionsEditorWidget.h" +#undef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_EDITOR_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretResult.h" +#include "CaretResultDialog.h" +#include "ChartTwoCartesianCustomSubdivisions.h" +#include "ChartTwoCartesianCustomSubdivisionsLabel.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "WuQDoubleSpinBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoCartesianCustomSubdivisionsEditorWidget + * \brief Widget for editing a chart two custom axis + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parent + * The parent widget + */ +ChartTwoCartesianCustomSubdivisionsEditorWidget::ChartTwoCartesianCustomSubdivisionsEditorWidget(QWidget* parent) +: QWidget(parent) +{ + m_rowsGridLayout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(m_rowsGridLayout, 4, 0); + const int gridRow = m_rowsGridLayout->rowCount(); + m_rowsGridLayout->addWidget(new QLabel("Edit"), + gridRow, GRID_COLUMN_CONSTRUCTION); + m_rowsGridLayout->addWidget(new QLabel("Value (Descending)"), + gridRow, GRID_COLUMN_VALUE); + m_rowsGridLayout->addWidget(new QLabel("Label"), + gridRow, GRID_COLUMN_TEXT); +} + +/** + * Destructor. + */ +ChartTwoCartesianCustomSubdivisionsEditorWidget::~ChartTwoCartesianCustomSubdivisionsEditorWidget() +{ +} + +/** + * Update the widget with the given axis + * @param axis + * Axis for editing in this widget + */ +void +ChartTwoCartesianCustomSubdivisionsEditorWidget::updateContent(ChartTwoCartesianCustomSubdivisions* subdivisions) +{ + m_subdivisions = subdivisions; + loadAxisIntoWidgets(); +} + +void +ChartTwoCartesianCustomSubdivisionsEditorWidget::loadAxisIntoWidgets() +{ + const int32_t numLabels = ((m_subdivisions != NULL) + ? m_subdivisions->getNumberOfLabels() + : 0); + const int32_t numRows = static_cast(m_rows.size()); + + /* + * If needed, add rows + */ + for (int32_t iRow = numRows; iRow < numLabels; iRow++) { + /* + * Construction Tool Button + * Note: macro support is on each action in menu in 'createConstructionMenu' + */ + QIcon constructionIcon; + const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", + constructionIcon); + QToolButton* constructionToolButton = new QToolButton(); + constructionToolButton->setToolTip("Add/Move/Remove Layers"); + if (constructionIconValid) { + constructionToolButton->setIcon(constructionIcon); + } + else { + constructionToolButton->setText("C"); + } + QMenu* constructionMenu = createConstructionMenu(iRow); + QObject::connect(constructionToolButton, &QToolButton::clicked, + [=]() { constructionMenu->exec(constructionToolButton->mapToGlobal(QPoint(0, 0))); } ); + + const float spinBoxMax(1.0e10); + WuQDoubleSpinBox* spinBox = new WuQDoubleSpinBox(this); + spinBox->setRange(-spinBoxMax, spinBoxMax); + spinBox->setSingleStep(1.0); + spinBox->setDecimals(5); + spinBox->setFixedWidth(120); + QObject::connect(spinBox, &WuQDoubleSpinBox::valueChanged, + [=](double value) { this->valueSpinBoxValueChanged(iRow, value); }); + + QLineEdit* lineEdit = new QLineEdit; + lineEdit->setFixedWidth(100); + QObject::connect(lineEdit, &QLineEdit::textEdited, + [=](const QString& text) { this->labelLineEditTextChanged(iRow, text); }); + + std::unique_ptr rowPtr(new Row(iRow, + constructionToolButton, + spinBox, + lineEdit)); + m_rows.push_back(std::move(rowPtr)); + + const int gridRow = m_rowsGridLayout->rowCount(); + m_rowsGridLayout->addWidget(constructionToolButton, + gridRow, GRID_COLUMN_CONSTRUCTION); + m_rowsGridLayout->addWidget(spinBox->getWidget(), + gridRow, GRID_COLUMN_VALUE); + m_rowsGridLayout->addWidget(lineEdit, + gridRow, GRID_COLUMN_TEXT); + } + + updateRangesOfValueSpinBoxes(); + + const int32_t newNumRows = static_cast(m_rows.size()); + for (int32_t iRow = 0; iRow < newNumRows; iRow++) { + CaretAssertVectorIndex(m_rows, iRow); + + const bool validRowFlag(iRow < numLabels); + if (validRowFlag) { + m_rows[iRow]->m_valueSpinBox->setValue(m_subdivisions->getLabelNumericValue(iRow)); + m_rows[iRow]->m_labelLineEdit->setText(m_subdivisions->getLabelText(iRow)); + } + + m_rows[iRow]->m_constructionToolButton->setVisible(validRowFlag); + m_rows[iRow]->m_valueSpinBox->getWidget()->setVisible(validRowFlag); + m_rows[iRow]->m_labelLineEdit->setVisible(validRowFlag); + } + + setEnabled(m_subdivisions != NULL); + + adjustSize(); + emit widgetSizeChanged(); +} + +/** + * Update the ranges of the value spin boxes + */ +void +ChartTwoCartesianCustomSubdivisionsEditorWidget::updateRangesOfValueSpinBoxes() +{ + const int32_t numLabels = ((m_subdivisions != NULL) + ? m_subdivisions->getNumberOfLabels() + : 0); + for (int32_t iRow = 0; iRow < numLabels; iRow++) { + CaretAssertVectorIndex(m_rows, iRow); + float rangeMin(0.0), rangeMax(0.0); + m_subdivisions->getRangeForLabelAtIndex(iRow, rangeMin, rangeMax); + m_rows[iRow]->m_valueSpinBox->setRange(rangeMin, rangeMax); + } +} + +/** + * Create a construction menu for the given row + * @param rowIndex + * Index of the row containing the menu + */ +QMenu* +ChartTwoCartesianCustomSubdivisionsEditorWidget::createConstructionMenu(const int32_t rowIndex) +{ + QMenu* menu = new QMenu(); + menu->addAction("Add Label Above", + [=]() { constructionMenuItemSelected(rowIndex, ConstructionType::INSERT_ABOVE); }); + menu->addAction("Add Label Below", + [=]() { constructionMenuItemSelected(rowIndex, ConstructionType::INSERT_BELOW); }); + menu->addAction("Remove This Label", + [=]() { constructionMenuItemSelected(rowIndex, ConstructionType::REMOVE); }); + return menu; +} + +/** + * Called when an item is selected from a row's construction menu + * @param rowIndex + * Index of the row + * @param constructionType + * Enum identifying type of construction + */ +void +ChartTwoCartesianCustomSubdivisionsEditorWidget::constructionMenuItemSelected(const int32_t rowIndex, + const ConstructionType constructionType) +{ + std::unique_ptr result; + switch (constructionType) { + case ConstructionType::INSERT_ABOVE: + result = m_subdivisions->insertLabelAbove(rowIndex); + break; + case ConstructionType::INSERT_BELOW: + result = m_subdivisions->insertLabelBelow(rowIndex); + break; + case ConstructionType::REMOVE: + result = m_subdivisions->removeLabelAtIndex(rowIndex); + break; + } + + if (CaretResultDialog::isSuccess(result, + this)) { + loadAxisIntoWidgets(); + updateGraphics(); + } +} + + +/** + * Called when a row's spinbox value is changed + * @param rowIndex + * Index of the row + * @param value + * New value for a label + */ +void +ChartTwoCartesianCustomSubdivisionsEditorWidget::valueSpinBoxValueChanged(const int32_t rowIndex, + const float value) +{ + m_subdivisions->setLabelNumericValue(rowIndex, + value); + updateRangesOfValueSpinBoxes(); + updateGraphics(); +} + +/** + * Called when a row's spinbox value is changed + * @param rowIndex + * Index of the row + * @param text + * New text for a label + */ +void +ChartTwoCartesianCustomSubdivisionsEditorWidget::labelLineEditTextChanged(const int32_t rowIndex, + const QString& text) +{ + m_subdivisions->setLabelText(rowIndex, + text); + updateGraphics(); +} + +/** + * Update the graphics + */ +void +ChartTwoCartesianCustomSubdivisionsEditorWidget::updateGraphics() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.h connectome-workbench-1.5.0/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.h --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,123 @@ +#ifndef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_EDITOR_WIDGET_H__ +#define __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_EDITOR_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +class QGridLayout; +class QLineEdit; +class QMenu; +class QToolButton; + +namespace caret { + class EnumComboBoxTemplate; + class WuQDoubleSpinBox; + + class ChartTwoCartesianCustomSubdivisions; + + class ChartTwoCartesianCustomSubdivisionsEditorWidget : public QWidget { + + Q_OBJECT + + public: + ChartTwoCartesianCustomSubdivisionsEditorWidget(QWidget* parent = 0); + + virtual ~ChartTwoCartesianCustomSubdivisionsEditorWidget(); + + ChartTwoCartesianCustomSubdivisionsEditorWidget(const ChartTwoCartesianCustomSubdivisionsEditorWidget&) = delete; + + ChartTwoCartesianCustomSubdivisionsEditorWidget& operator=(const ChartTwoCartesianCustomSubdivisionsEditorWidget&) = delete; + + void updateContent(ChartTwoCartesianCustomSubdivisions* subdivisions); + + // ADD_NEW_METHODS_HERE + + signals: + void widgetSizeChanged(); + + protected: + + private: + class Row { + public: + Row(const int32_t rowIndex, + QToolButton* constructionToolButton, + WuQDoubleSpinBox* valueSpinBox, + QLineEdit* labelLineEdit) + : m_rowIndex(rowIndex), + m_constructionToolButton(constructionToolButton), + m_valueSpinBox(valueSpinBox), + m_labelLineEdit(labelLineEdit) { } + + const int32_t m_rowIndex; + QToolButton* m_constructionToolButton; + WuQDoubleSpinBox* m_valueSpinBox; + QLineEdit* m_labelLineEdit; + }; + + enum class ConstructionType { + INSERT_ABOVE, + INSERT_BELOW, + REMOVE + }; + + void loadAxisIntoWidgets(); + + void valueSpinBoxValueChanged(const int32_t rowIndex, + const float value); + + void labelLineEditTextChanged(const int32_t rowIndex, + const QString& text); + + void constructionMenuItemSelected(const int32_t rowIndex, + const ConstructionType constructionType); + + void updateRangesOfValueSpinBoxes(); + + void updateGraphics(); + + QMenu* createConstructionMenu(const int32_t rowIndex); + + ChartTwoCartesianCustomSubdivisions* m_subdivisions = NULL; + + QGridLayout* m_rowsGridLayout; + + std::vector> m_rows; + + static constexpr int32_t GRID_COLUMN_CONSTRUCTION = 0; + + static constexpr int32_t GRID_COLUMN_VALUE = 1; + + static constexpr int32_t GRID_COLUMN_TEXT = 2; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_EDITOR_WIDGET_DECLARE__ + // +#endif // __CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_EDITOR_WIDGET_DECLARE__ + +} // namespace +#endif //__CHART_TWO_CARTESIAN_CUSTOM_SUBDIVISIONS_EDITOR_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoLineLayerNormalizationWidget.cxx connectome-workbench-1.5.0/src/GuiQt/ChartTwoLineLayerNormalizationWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoLineLayerNormalizationWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoLineLayerNormalizationWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,276 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_LINE_LAYER_NORMALIZATION_WIDGET_DECLARE__ +#include "ChartTwoLineLayerNormalizationWidget.h" +#undef __CHART_TWO_LINE_LAYER_NORMALIZATION_WIDGET_DECLARE__ + +#include + +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "ChartTwoDataCartesian.h" +#include "ChartTwoOverlay.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventUserInterfaceUpdate.h" +#include "EventManager.h" +#include "GraphicsPrimitiveV3f.h" +#include "WuQtUtilities.h" + +using namespace caret; + +/** + * \class caret::ChartTwoLineLayerNormalizationWidget + * \brief Widget for editing chart line layer normalization parameters + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +ChartTwoLineLayerNormalizationWidget::ChartTwoLineLayerNormalizationWidget() +: QWidget() +{ + m_newMeanEnabledCheckBox = new QCheckBox("New Mean"); + QObject::connect(m_newMeanEnabledCheckBox, &QCheckBox::clicked, + this, &ChartTwoLineLayerNormalizationWidget::newMeanEnabledCheckBoxClicked); + m_newMeanSpinBox = new QDoubleSpinBox(); + m_newMeanSpinBox->setDecimals(4); + m_newMeanSpinBox->setSingleStep(0.1); + m_newMeanSpinBox->setRange(-std::numeric_limits::max(), + std::numeric_limits::max()); + m_newMeanSpinBox->setMaximumWidth(100); + QObject::connect(m_newMeanSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &ChartTwoLineLayerNormalizationWidget::newMeanValueChanged); + + m_newDeviationEnabledCheckBox = new QCheckBox("New Deviation"); + QObject::connect(m_newDeviationEnabledCheckBox, &QCheckBox::clicked, + this, &ChartTwoLineLayerNormalizationWidget::newDeviationEnabledCheckBoxClicked); + m_newDeviationSpinBox = new QDoubleSpinBox(); + m_newDeviationSpinBox->setDecimals(4); + m_newDeviationSpinBox->setSingleStep(0.1); + m_newDeviationSpinBox->setRange(-std::numeric_limits::max(), + std::numeric_limits::max()); + m_newDeviationSpinBox->setMaximumWidth(100); + QObject::connect(m_newDeviationSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &ChartTwoLineLayerNormalizationWidget::newDeviationValueChanged); + + m_absoluteValueEnabledCheckBox = new QCheckBox("Absolute Values"); + QObject::connect(m_absoluteValueEnabledCheckBox, &QCheckBox::clicked, + this, &ChartTwoLineLayerNormalizationWidget::absoluteValueEnabledCheckBoxClicked); + + m_meanDevLabel = new QLabel(""); + + QLabel* descripionLabel = new QLabel(GraphicsPrimitive::getNewMeanDeviationOperationDescriptionInHtml()); + descripionLabel->setWordWrap(true); + descripionLabel->setMaximumWidth(400); + + QGridLayout* layout = new QGridLayout(this); + layout->setColumnStretch(0, 0); + layout->setColumnStretch(1, 100); + int32_t row(0); + layout->addWidget(m_absoluteValueEnabledCheckBox, row, 0, 1, 2, Qt::AlignLeft); + row++; + layout->addWidget(m_newMeanEnabledCheckBox, row, 0); + layout->addWidget(m_newMeanSpinBox, row, 1, Qt::AlignLeft); + row++; + layout->addWidget(m_newDeviationEnabledCheckBox, row, 0); + layout->addWidget(m_newDeviationSpinBox, row, 1, Qt::AlignLeft); + row++; + layout->addWidget(m_meanDevLabel, row, 0, 1, 2, Qt::AlignLeft); + row++; + layout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 2); + row++; + layout->addWidget(descripionLabel, row, 0, 1, 2, Qt::AlignLeft); + row++; + + m_blockUpdatesFlag = false; +} + +/** + * Destructor. + */ +ChartTwoLineLayerNormalizationWidget::~ChartTwoLineLayerNormalizationWidget() +{ +} + +/** + * Update the content of the widget + * @param chartTwoOverlay + */ +void +ChartTwoLineLayerNormalizationWidget::updateContent(ChartTwoOverlay* chartTwoOverlay) +{ + if (m_blockUpdatesFlag) { + return; + } + + m_chartTwoOverlay = chartTwoOverlay; + + QString meanDevText; + + bool validFlag(false); + if (m_chartTwoOverlay != NULL) { + m_absoluteValueEnabledCheckBox->setChecked(m_chartTwoOverlay->isLineChartNormalizationAbsoluteValueEnabled()); + + m_newMeanEnabledCheckBox->setChecked(m_chartTwoOverlay->isLineChartNewMeanEnabled()); + QSignalBlocker meanBlocker(m_newMeanSpinBox); + m_newMeanSpinBox->setValue(m_chartTwoOverlay->getLineChartNewMeanValue()); + + m_newDeviationEnabledCheckBox->setChecked(m_chartTwoOverlay->isLineChartNewDeviationEnabled()); + QSignalBlocker devBlocker(m_newDeviationSpinBox); + m_newDeviationSpinBox->setValue(m_chartTwoOverlay->getLineChartNewDeviationValue()); + + float mean(0.0), dev(0.0); + const ChartTwoDataCartesian* cartData = m_chartTwoOverlay->getLineLayerChartMapFileCartesianData(); + CaretAssert(cartData); + const GraphicsPrimitiveV3f* primitive = cartData->getGraphicsPrimitive(); + primitive->getMeanAndStandardDeviationForY(mean, dev); + + const QString muCharacter("Data Mean"); //QChar(0x03BC)); + const QString sigmaCharacter("Data Deviation"); //QChar(0x03C3)); + meanDevText = (muCharacter + ": " + + QString::number(mean, 'f', 4) + + ", " + sigmaCharacter + ": " + + QString::number(dev, 'f', 4)); + validFlag = true; + } + + this->setEnabled(validFlag); + + m_meanDevLabel->setText(meanDevText); +} + +/** + * Called when a value is changed + */ +void +ChartTwoLineLayerNormalizationWidget::valueChanged() +{ + if (m_chartTwoOverlay != NULL) { + m_chartTwoOverlay->setLineChartNewMeanEnabled(m_newMeanEnabledCheckBox->isChecked()); + m_chartTwoOverlay->setLineChartNewMeanValue(m_newMeanSpinBox->value()); + m_chartTwoOverlay->setLineChartNewDeviationEnabled(m_newDeviationEnabledCheckBox->isChecked()); + m_chartTwoOverlay->setLineChartNewDeviationValue(m_newDeviationSpinBox->value()); + + updateGraphics(); + } +} + +/** + * Called when new mean checkbox is clicked + * @param clicked + * New clicked status + */ +void +ChartTwoLineLayerNormalizationWidget::newMeanEnabledCheckBoxClicked(bool clicked) +{ + m_chartTwoOverlay->setLineChartNewMeanEnabled(clicked); + updateGraphics(); + updateToolBarChartAxes(); +} + +/** + * Called when new deviation checkbox is clicked + * @param clicked + * New clicked status + */ +void +ChartTwoLineLayerNormalizationWidget::newDeviationEnabledCheckBoxClicked(bool clicked) +{ + m_chartTwoOverlay->setLineChartNewDeviationEnabled(clicked); + updateGraphics(); + updateToolBarChartAxes(); +} + +/** + * Called when new deviation checkbox is clicked + * @param clicked + * New clicked status + */ +void +ChartTwoLineLayerNormalizationWidget::absoluteValueEnabledCheckBoxClicked(bool clicked) +{ + m_chartTwoOverlay->setLineChartNormalizationAbsoluteValueEnabled(clicked); + updateGraphics(); + updateToolBarChartAxes(); +} + +/** + * Called when new mean value is changed + * @param value + * New value + */ +void +ChartTwoLineLayerNormalizationWidget::newMeanValueChanged(double value) +{ + m_chartTwoOverlay->setLineChartNewMeanValue(value); + updateGraphics(); + updateToolBarChartAxes(); +} + +/** + * Called when new deviation value is changed + * @param value + * New value + */ +void +ChartTwoLineLayerNormalizationWidget::newDeviationValueChanged(double value) +{ + m_chartTwoOverlay->setLineChartNewDeviationValue(value); + updateGraphics(); + updateToolBarChartAxes(); +} + +/** + * Called when new mean value is changed + * @param value + * New value + */ +void +ChartTwoLineLayerNormalizationWidget::updateGraphics() +{ + + m_blockUpdatesFlag = true; + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + m_blockUpdatesFlag = false; +} + +/** + * Called when new mean value is changed + * @param value + * New value + */ +void +ChartTwoLineLayerNormalizationWidget::updateToolBarChartAxes() +{ + m_blockUpdatesFlag = true; + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_TOOLBAR_CHART_ORIENTED_AXES_UPDATE); + m_blockUpdatesFlag = false; + +} + + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoLineLayerNormalizationWidget.h connectome-workbench-1.5.0/src/GuiQt/ChartTwoLineLayerNormalizationWidget.h --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoLineLayerNormalizationWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoLineLayerNormalizationWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,98 @@ +#ifndef __CHART_TWO_LINE_LAYER_NORMALIZATION_WIDGET_H__ +#define __CHART_TWO_LINE_LAYER_NORMALIZATION_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QCheckBox; +class QLabel; +class QDoubleSpinBox; + +namespace caret { + + class ChartTwoOverlay; + + class ChartTwoLineLayerNormalizationWidget : public QWidget { + + Q_OBJECT + + public: + ChartTwoLineLayerNormalizationWidget(); + + virtual ~ChartTwoLineLayerNormalizationWidget(); + + ChartTwoLineLayerNormalizationWidget(const ChartTwoLineLayerNormalizationWidget&) = delete; + + ChartTwoLineLayerNormalizationWidget& operator=(const ChartTwoLineLayerNormalizationWidget&) = delete; + + void updateContent(ChartTwoOverlay* chartTwoOverlay); + + // ADD_NEW_METHODS_HERE + + private slots: + void valueChanged(); + + void newMeanEnabledCheckBoxClicked(bool clicked); + + void newDeviationEnabledCheckBoxClicked(bool clicked); + + void absoluteValueEnabledCheckBoxClicked(bool clicked); + + void newMeanValueChanged(double value); + + void newDeviationValueChanged(double value); + + private: + void updateGraphics(); + + void updateToolBarChartAxes(); + + ChartTwoOverlay* m_chartTwoOverlay = NULL; + + QCheckBox* m_newMeanEnabledCheckBox; + + QDoubleSpinBox* m_newMeanSpinBox; + + QCheckBox* m_newDeviationEnabledCheckBox; + + QDoubleSpinBox* m_newDeviationSpinBox; + + QLabel* m_meanDevLabel; + + QCheckBox* m_absoluteValueEnabledCheckBox; + + bool m_blockUpdatesFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_LINE_LAYER_NORMALIZATION_WIDGET_DECLARE__ + // +#endif // __CHART_TWO_LINE_LAYER_NORMALIZATION_WIDGET_DECLARE__ + +} // namespace +#endif //__CHART_TWO_LINE_LAYER_NORMALIZATION_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoOverlaySetViewController.cxx connectome-workbench-1.5.0/src/GuiQt/ChartTwoOverlaySetViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoOverlaySetViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoOverlaySetViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -36,14 +37,17 @@ #include "BrainConstants.h" #include "BrowserTabContent.h" #include "CaretAssert.h" +#include "CaretColorToolButton.h" #include "ChartTwoOverlay.h" #include "ChartTwoOverlaySet.h" #include "ChartTwoOverlayViewController.h" +#include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MapYokingGroupComboBox.h" +#include "WuQDoubleSpinBox.h" #include "WuQGridLayoutGroup.h" #include "WuQtUtilities.h" @@ -102,26 +106,24 @@ } if (orientation == Qt::Horizontal) { - static const int COLUMN_ON = 0; - static const int COLUMN_LOAD = 1; - static const int COLUMN_SETTINGS_EDIT = 2; - static const int COLUMN_SETTINGS_COLOR_BAR = 3; - static const int COLUMN_SETTINGS_CONSTRUCTION = 4; - static const int COLUMN_MATRIX_VIEW = 5; - static const int COLUMN_AXIS = 6; - static const int COLUMN_FILE = 7; - static const int COLUMN_YOKE = 8; - static const int COLUMN_MAP_INDEX = 9; - static const int COLUMN_ALL_MAPS = 10; - static const int COLUMN_MAP_NAME = 11; + int32_t columnCounter(0); + static const int COLUMN_ON = columnCounter++; + static const int COLUMN_LOAD = columnCounter++; + static const int COLUMN_SETTINGS = columnCounter++; + static const int COLUMN_LINE_WIDTH = columnCounter++; + static const int COLUMN_OPACITY = columnCounter++; + static const int COLUMN_POINT = columnCounter++; + static const int COLUMN_FILE = columnCounter++; + static const int COLUMN_YOKE = columnCounter++; + static const int COLUMN_MAP_INDEX = columnCounter++; + static const int COLUMN_ALL_MAPS = columnCounter++; + static const int COLUMN_MAP_NAME = columnCounter++; gridLayout->setColumnStretch(COLUMN_ON, 0); gridLayout->setColumnStretch(COLUMN_LOAD, 0); - gridLayout->setColumnStretch(COLUMN_SETTINGS_EDIT, 0); - gridLayout->setColumnStretch(COLUMN_SETTINGS_COLOR_BAR, 0); - gridLayout->setColumnStretch(COLUMN_SETTINGS_CONSTRUCTION, 0); - gridLayout->setColumnStretch(COLUMN_MATRIX_VIEW, 0); - gridLayout->setColumnStretch(COLUMN_AXIS, 0); + gridLayout->setColumnStretch(COLUMN_SETTINGS, 0); + gridLayout->setColumnStretch(COLUMN_OPACITY, 0); + gridLayout->setColumnStretch(COLUMN_LINE_WIDTH, 0); gridLayout->setColumnStretch(COLUMN_FILE, 100); gridLayout->setColumnStretch(COLUMN_YOKE, 0); gridLayout->setColumnStretch(COLUMN_ALL_MAPS, 0); @@ -129,36 +131,60 @@ gridLayout->setColumnStretch(COLUMN_MAP_NAME, 100); QLabel* onLabel = new QLabel("On"); - QLabel* loadLabel = new QLabel("Load"); + m_loadLabel = new QLabel("Load"); QLabel* settingsLabel = new QLabel("Settings"); + m_opacityLabel = new QLabel("Opacity"); + m_lineWidthLabel = new QLabel("Width"); + m_pointLabel = new QLabel("Point"); QLabel* fileLabel = new QLabel("File"); QLabel* yokeLabel = new QLabel("Yoke"); - QLabel* allMapsLabel = new QLabel("All"); + m_allMapsLabel = new QLabel("All"); m_mapRowOrColumnIndexLabel = new QLabel("Index"); m_mapRowOrColumnNameLabel = new QLabel("Name"); int row = gridLayout->rowCount(); gridLayout->addWidget(onLabel, row, COLUMN_ON, Qt::AlignHCenter); - gridLayout->addWidget(loadLabel, row, COLUMN_LOAD, Qt::AlignHCenter); - gridLayout->addWidget(settingsLabel, row, COLUMN_SETTINGS_EDIT, 1, 5, Qt::AlignHCenter); + gridLayout->addWidget(m_loadLabel, row, COLUMN_LOAD, Qt::AlignHCenter); + gridLayout->addWidget(settingsLabel, row, COLUMN_SETTINGS, Qt::AlignHCenter); + gridLayout->addWidget(m_lineWidthLabel, row, COLUMN_LINE_WIDTH, Qt::AlignHCenter); + gridLayout->addWidget(m_opacityLabel, row, COLUMN_OPACITY, Qt::AlignHCenter); + gridLayout->addWidget(m_pointLabel, row, COLUMN_POINT, Qt::AlignHCenter); gridLayout->addWidget(fileLabel, row, COLUMN_FILE, Qt::AlignHCenter); gridLayout->addWidget(yokeLabel, row, COLUMN_YOKE, Qt::AlignHCenter); - gridLayout->addWidget(allMapsLabel, row, COLUMN_ALL_MAPS, Qt::AlignHCenter); + gridLayout->addWidget(m_allMapsLabel, row, COLUMN_ALL_MAPS, Qt::AlignHCenter); gridLayout->addWidget(m_mapRowOrColumnIndexLabel, row, COLUMN_MAP_INDEX, Qt::AlignHCenter); gridLayout->addWidget(m_mapRowOrColumnNameLabel, row, COLUMN_MAP_NAME, Qt::AlignHCenter); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { - row = gridLayout->rowCount(); ChartTwoOverlayViewController* covc = m_chartOverlayViewControllers[i]; + + QWidget* settingsWidget = new QWidget(); + QHBoxLayout* settingsLayout = new QHBoxLayout(settingsWidget); + settingsLayout->setContentsMargins(0, 0, 0, 0); + settingsLayout->setSpacing(3); + settingsLayout->addWidget(covc->m_settingsToolButton); + settingsLayout->addWidget(covc->m_colorBarToolButton); + settingsLayout->addWidget(covc->m_constructionToolButton); + settingsLayout->addWidget(covc->m_matrixTriangularViewModeToolButton); + settingsLayout->addWidget(covc->m_lineLayerColorToolButton); + settingsLayout->addWidget(covc->m_lineLayerToolTipOffsetToolButton); + settingsLayout->addWidget(covc->m_lineLayerNormalizationToolButton); + + QWidget* pointWidget = new QWidget(); + QHBoxLayout* pointLayout = new QHBoxLayout(pointWidget); + pointLayout->setContentsMargins(0, 0, 0, 0); + pointLayout->addWidget(covc->m_lineLayerActiveComboBox->getWidget()); + pointLayout->addWidget(covc->m_selectedPointIndexSpinBox); + + row = gridLayout->rowCount(); WuQGridLayoutGroup* glg = m_chartOverlayGridLayoutGroups[i]; glg->addWidget(covc->m_enabledCheckBox, row, COLUMN_ON, Qt::AlignHCenter); glg->addWidget(covc->m_lineSeriesLoadingEnabledCheckBox, row, COLUMN_LOAD, Qt::AlignHCenter); - glg->addWidget(covc->m_settingsToolButton, row, COLUMN_SETTINGS_EDIT, Qt::AlignHCenter); - glg->addWidget(covc->m_colorBarToolButton, row, COLUMN_SETTINGS_COLOR_BAR, Qt::AlignHCenter); - glg->addWidget(covc->m_constructionToolButton, row, COLUMN_SETTINGS_CONSTRUCTION, Qt::AlignHCenter); - glg->addWidget(covc->m_matrixTriangularViewModeToolButton, row, COLUMN_MATRIX_VIEW, Qt::AlignHCenter); - glg->addWidget(covc->m_axisLocationToolButton, row, COLUMN_AXIS, Qt::AlignHCenter); + glg->addWidget(settingsWidget, row, COLUMN_SETTINGS, Qt::AlignHCenter); + glg->addWidget(covc->m_lineLayerWidthSpinBox->getWidget(), row, COLUMN_LINE_WIDTH); + glg->addWidget(covc->m_matrixOpacitySpinBox->getWidget(), row, COLUMN_OPACITY); + glg->addWidget(pointWidget, row, COLUMN_POINT); glg->addWidget(covc->m_mapFileComboBox, row, COLUMN_FILE); glg->addWidget(covc->m_mapRowOrColumnYokingGroupComboBox->getWidget(), row, COLUMN_YOKE, Qt::AlignHCenter); glg->addWidget(covc->m_allMapsCheckBox, row, COLUMN_ALL_MAPS, Qt::AlignHCenter); @@ -167,29 +193,29 @@ } } else { - static const int ROW_ONE_COLUMN_ON = 0; - static const int ROW_ONE_COLUMN_SETTINGS_EDIT = 1; - static const int ROW_ONE_COLUMN_SETTINGS_COLOR_BAR = 2; - static const int ROW_ONE_COLUMN_SETTINGS_CONSTRUCTION = 3; - static const int ROW_ONE_COLUMN_MATRIX_VIEW = 4; - static const int ROW_ONE_COLUMN_AXIS = 5; - static const int ROW_ONE_COLUMN_FILE_LABEL = 6; - static const int ROW_ONE_COLUMN_FILE_COMBO_BOX = 7; - - static const int ROW_TWO_COLUMN_LOAD = 0; - static const int ROW_TWO_COLUMN_YOKE = 2; - static const int ROW_TWO_COLUMN_ALL_MAPS = 4; - static const int ROW_TWO_COLUMN_MAP_INDEX = 6; - static const int ROW_TWO_COLUMN_MAP_NAME = 7; - - gridLayout->setColumnStretch(0, 0); - gridLayout->setColumnStretch(1, 0); - gridLayout->setColumnStretch(2, 0); - gridLayout->setColumnStretch(3, 0); - gridLayout->setColumnStretch(4, 0); - gridLayout->setColumnStretch(5, 0); - gridLayout->setColumnStretch(6, 0); - gridLayout->setColumnStretch(7, 100); + int32_t columnCounter(0); + static const int COLUMN_ONE = columnCounter++; + static const int COLUMN_TWO = columnCounter++; + static const int COLUMN_THREE = columnCounter++; + + for (int32_t i = 0; i < columnCounter; i++) { + gridLayout->setColumnStretch(i, 0); + } + gridLayout->setColumnStretch(COLUMN_THREE, 100); + gridLayout->setVerticalSpacing(0); + + const bool showTitlesFlag(true); + if (showTitlesFlag) { + m_pointLabel = new QLabel("Point"); + + const int titleRow(gridLayout->rowCount()); + gridLayout->addWidget(new QLabel("Settings"), + titleRow, COLUMN_ONE, Qt::AlignHCenter); + gridLayout->addWidget(m_pointLabel, + titleRow, COLUMN_TWO, Qt::AlignHCenter); + gridLayout->addWidget(new QLabel("Yoke / File / Map"), + titleRow, COLUMN_THREE, Qt::AlignLeft); + } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { WuQGridLayoutGroup* glg = m_chartOverlayGridLayoutGroups[i]; @@ -207,22 +233,49 @@ ChartTwoOverlayViewController* covc = m_chartOverlayViewControllers[i]; - QLabel* fileLabel = new QLabel("File"); - glg->addWidget(covc->m_enabledCheckBox, row, ROW_ONE_COLUMN_ON, Qt::AlignLeft); - glg->addWidget(covc->m_settingsToolButton, row, ROW_ONE_COLUMN_SETTINGS_EDIT, Qt::AlignHCenter); - glg->addWidget(covc->m_colorBarToolButton, row, ROW_ONE_COLUMN_SETTINGS_COLOR_BAR, Qt::AlignHCenter); - glg->addWidget(covc->m_constructionToolButton, row, ROW_ONE_COLUMN_SETTINGS_CONSTRUCTION, Qt::AlignHCenter); - glg->addWidget(covc->m_matrixTriangularViewModeToolButton, row, ROW_ONE_COLUMN_MATRIX_VIEW, Qt::AlignHCenter); - glg->addWidget(covc->m_axisLocationToolButton, row, ROW_ONE_COLUMN_AXIS, Qt::AlignHCenter); - glg->addWidget(fileLabel, row, ROW_ONE_COLUMN_FILE_LABEL, Qt::AlignHCenter); - glg->addWidget(covc->m_mapFileComboBox, row, ROW_ONE_COLUMN_FILE_COMBO_BOX, 1, 2); - row++; + QWidget* topLeftWidget = new QWidget(); + QHBoxLayout* topLeftLayout = new QHBoxLayout(topLeftWidget); + topLeftLayout->setContentsMargins(0, 0, 0, 0); + topLeftLayout->setSpacing(3); + topLeftLayout->addWidget(covc->m_enabledCheckBox); + topLeftLayout->addWidget(covc->m_lineSeriesLoadingEnabledCheckBox); + topLeftLayout->addWidget(covc->m_lineLayerWidthSpinBox->getWidget()); + topLeftLayout->addWidget(covc->m_matrixOpacitySpinBox->getWidget()); + + QWidget* topRightWidget = new QWidget(); + QHBoxLayout* topRightLayout = new QHBoxLayout(topRightWidget); + topRightLayout->setContentsMargins(0, 0, 0, 0); + topRightLayout->setSpacing(3); + topRightLayout->addWidget(covc->m_mapRowOrColumnYokingGroupComboBox->getWidget(), 0); + topRightLayout->addWidget(covc->m_mapFileComboBox, 100); - glg->addWidget(covc->m_lineSeriesLoadingEnabledCheckBox, row, ROW_TWO_COLUMN_LOAD, Qt::AlignLeft); - glg->addWidget(covc->m_mapRowOrColumnYokingGroupComboBox->getWidget(), row, ROW_TWO_COLUMN_YOKE, 1, 2, Qt::AlignHCenter); - glg->addWidget(covc->m_allMapsCheckBox, row, ROW_TWO_COLUMN_ALL_MAPS, 1, 2, Qt::AlignHCenter); - glg->addWidget(covc->m_mapRowOrColumnIndexSpinBox, row, ROW_TWO_COLUMN_MAP_INDEX, Qt::AlignHCenter); - glg->addWidget(covc->m_mapRowOrColumnNameComboBox, row, ROW_TWO_COLUMN_MAP_NAME); + QWidget* bottomLeftWidget = new QWidget(); + QHBoxLayout* bottomLeftLayout = new QHBoxLayout(bottomLeftWidget); + bottomLeftLayout->setContentsMargins(0, 0, 0, 0); + bottomLeftLayout->setSpacing(3); + bottomLeftLayout->addWidget(covc->m_settingsToolButton); + bottomLeftLayout->addWidget(covc->m_colorBarToolButton); + bottomLeftLayout->addWidget(covc->m_constructionToolButton); + bottomLeftLayout->addWidget(covc->m_matrixTriangularViewModeToolButton); + bottomLeftLayout->addWidget(covc->m_lineLayerColorToolButton); + bottomLeftLayout->addWidget(covc->m_lineLayerToolTipOffsetToolButton); + bottomLeftLayout->addWidget(covc->m_lineLayerNormalizationToolButton); + + QWidget* bottomRightWidget = new QWidget(); + QHBoxLayout* bottomRightLayout = new QHBoxLayout(bottomRightWidget); + bottomRightLayout->setContentsMargins(0, 0, 0, 0); + bottomRightLayout->setSpacing(3); + bottomRightLayout->addWidget(covc->m_allMapsCheckBox, 0); + bottomRightLayout->addWidget(covc->m_mapRowOrColumnIndexSpinBox, 0); + bottomRightLayout->addWidget(covc->m_mapRowOrColumnNameComboBox, 100); + + glg->addWidget(topLeftWidget, row, COLUMN_ONE, Qt::AlignLeft); + glg->addWidget(covc->m_lineLayerActiveComboBox->getWidget(), row, COLUMN_TWO); + glg->addWidget(topRightWidget, row, COLUMN_THREE); + row++; + glg->addWidget(bottomLeftWidget, row, COLUMN_ONE, Qt::AlignLeft); + glg->addWidget(covc->m_selectedPointIndexSpinBox, row, COLUMN_TWO); + glg->addWidget(bottomRightWidget, row, COLUMN_THREE); } } @@ -291,42 +344,65 @@ const int32_t numberOfDisplayedOverlays = chartOverlaySet->getNumberOfDisplayedOverlays(); for (int32_t i = 0; i < numberOfOverlays; i++) { + bool showAllMapsLabelFlag(false); + bool showLoadLabelFlag(false); + bool showLineWidthLabelFlag(false); + bool showOpacityLabelFlag(false); + bool showPointLabelFlag(false); + switch (chartOverlaySet->getChartTwoDataType()) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + showAllMapsLabelFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + showLineWidthLabelFlag = true; + showPointLabelFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + showLoadLabelFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + showOpacityLabelFlag = true; + break; + } + if (m_allMapsLabel != NULL) { + m_allMapsLabel->setVisible(showAllMapsLabelFlag); + } + if (m_loadLabel != NULL) { + m_loadLabel->setVisible(showLoadLabelFlag); + } + if (m_lineWidthLabel != NULL) { + m_lineWidthLabel->setVisible(showLineWidthLabelFlag); + } + if (m_opacityLabel != NULL) { + m_opacityLabel->setVisible(showOpacityLabelFlag); + } + if (m_pointLabel != NULL) { + m_pointLabel->setVisible(showPointLabelFlag); + } + ChartTwoOverlay* chartOverlay = NULL; if (chartOverlaySet != NULL) { chartOverlay = chartOverlaySet->getOverlay(i); } - m_chartOverlayViewControllers[i]->updateViewController(chartOverlay); bool displayOverlay = (chartOverlay != NULL); if (i >= numberOfDisplayedOverlays) { displayOverlay = false; } + CaretAssertVectorIndex(m_chartOverlayGridLayoutGroups, i); m_chartOverlayGridLayoutGroups[i]->setVisible(displayOverlay); + + if (displayOverlay) { + /* + * Need to do AFTER setting visibility + */ + CaretAssertVectorIndex(m_chartOverlayViewControllers, i); + m_chartOverlayViewControllers[i]->updateViewController(chartOverlay); + } } - -// const ChartTwoOverlay* primaryOverlay = chartOverlaySet->getPrimaryOverlay(); -// if (primaryOverlay != NULL) { -// AString mapRowOrColumnName = "Map"; -// switch (primaryOverlay->getChartTwoDataType()) { -// case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: -// break; -// case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: -// break; -// case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: -// break; -// case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: -// mapRowOrColumnName = "Row"; -// break; -// } -// -// if (m_mapRowOrColumnIndexLabel != NULL) { -// m_mapRowOrColumnIndexLabel->setText(mapRowOrColumnName+ " Index"); -// } -// if (m_mapRowOrColumnNameLabel != NULL) { -// m_mapRowOrColumnNameLabel->setText(mapRowOrColumnName + " Name"); -// } -// } } /** @@ -357,6 +433,7 @@ if (chartOverlaySet != NULL) { chartOverlaySet->insertOverlayAbove(overlayIndex); this->updateColoringAndGraphics(); + this->updateViewController(); } } @@ -372,6 +449,7 @@ if (chartOverlaySet != NULL) { chartOverlaySet->insertOverlayBelow(overlayIndex); this->updateColoringAndGraphics(); + updateViewController(); } } diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoOverlaySetViewController.h connectome-workbench-1.5.0/src/GuiQt/ChartTwoOverlaySetViewController.h --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoOverlaySetViewController.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoOverlaySetViewController.h 2021-02-16 19:46:47.000000000 +0000 @@ -79,6 +79,16 @@ std::vector m_chartOverlayGridLayoutGroups; + QLabel* m_allMapsLabel = NULL; + + QLabel* m_loadLabel = NULL; + + QLabel* m_opacityLabel = NULL; + + QLabel* m_lineWidthLabel = NULL; + + QLabel* m_pointLabel = NULL; + QScrollArea* m_scrollArea; QLabel* m_mapRowOrColumnIndexLabel = NULL; diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoOverlayViewController.cxx connectome-workbench-1.5.0/src/GuiQt/ChartTwoOverlayViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoOverlayViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoOverlayViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -31,38 +31,48 @@ #include #include #include +#include #include #include - -#include "CaretAssert.h" -using namespace caret; +#include #include "AnnotationColorBar.h" #include "Brain.h" +#include "BrainBrowserWindow.h" +#include "CardinalDirectionEnumMenu.h" +#include "CaretAssert.h" +#include "CaretColorToolButton.h" +#include "CaretColorEnumMenu.h" #include "CaretMappableDataFile.h" +#include "ChartTwoLineLayerNormalizationWidget.h" #include "ChartTwoOverlay.h" #include "ChartableTwoFileDelegate.h" -#include "ChartableTwoFileHistogramChart.h" -#include "ChartableTwoFileLineSeriesChart.h" -#include "ChartableTwoFileMatrixChart.h" +#include "CursorDisplayScoped.h" +#include "ElapsedTimer.h" +#include "EnumComboBoxTemplate.h" #include "EventDataFileReload.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "EventMapYokingSelectMap.h" #include "EventOverlaySettingsEditorDialogRequest.h" +#include "EventProgressUpdate.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "FilePathNamePrefixCompactor.h" #include "GuiManager.h" #include "MapYokingGroupComboBox.h" +#include "ProgressReportingDialog.h" #include "UsernamePasswordWidget.h" +#include "WuQDoubleSpinBox.h" #include "WuQFactory.h" #include "WuQMacroManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" +using namespace caret; + /** * \class caret::ChartTwoOverlayViewController * \brief View controller for a chart overlay @@ -91,15 +101,16 @@ : QObject(parent), m_browserWindowIndex(browserWindowIndex), m_chartOverlayIndex(chartOverlayIndex), -m_chartOverlay(NULL) +m_chartOverlay(NULL), +m_parentObjectName(parentObjectName) { int minComboBoxWidth = 200; - int maxComboBoxWidth = 100000; //400; + int maxComboBoxWidth = 100000; if (orientation == Qt::Horizontal) { minComboBoxWidth = 50; maxComboBoxWidth = 100000; } - const QComboBox::SizeAdjustPolicy comboSizePolicy = QComboBox::AdjustToContentsOnFirstShow; //QComboBox::AdjustToContents; + const QComboBox::SizeAdjustPolicy comboSizePolicy = QComboBox::AdjustToContentsOnFirstShow; WuQMacroManager* macroManager = WuQMacroManager::instance(); CaretAssert(macroManager); @@ -112,7 +123,7 @@ /* * Enabled Check Box */ - const QString enabledCheckboxText = ((orientation == Qt::Horizontal) ? " " : "On"); + const QString enabledCheckboxText = ((orientation == Qt::Horizontal) ? " " : "On "); m_enabledCheckBox = new QCheckBox(enabledCheckboxText); m_enabledCheckBox->setObjectName(objectNamePrefix + "OnOff"); @@ -125,7 +136,7 @@ /* * Line Series Enabled Check Box */ - const QString loadingCheckboxText = ((orientation == Qt::Horizontal) ? " " : "Load"); + const QString loadingCheckboxText = ((orientation == Qt::Horizontal) ? " " : "Load "); m_lineSeriesLoadingEnabledCheckBox = new QCheckBox(loadingCheckboxText); QObject::connect(m_lineSeriesLoadingEnabledCheckBox, &QCheckBox::clicked, this, &ChartTwoOverlayViewController::lineSeriesLoadingEnabledCheckBoxClicked); @@ -186,20 +197,20 @@ QIcon constructionIcon; const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", constructionIcon); - m_constructionAction = WuQtUtilities::createAction("C", - "Add/Move/Remove Layers", - this); + m_constructionToolButton = new QToolButton(); + m_constructionToolButton->setToolTip("Add/Move/Remove Layers"); if (constructionIconValid) { - m_constructionAction->setIcon(constructionIcon); + m_constructionToolButton->setIcon(constructionIcon); } - m_constructionToolButton = new QToolButton(); - QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton, + else { + m_constructionToolButton->setText("C"); + } + m_constructionMenu = createConstructionMenu(m_constructionToolButton, (objectNamePrefix + "ConstructionMenu:"), descriptivePrefix); - m_constructionAction->setMenu(constructionMenu); - m_constructionToolButton->setDefaultAction(m_constructionAction); - m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); + QObject::connect(m_constructionToolButton, &QToolButton::clicked, + this, &ChartTwoOverlayViewController::constructionToolButtonClicked); /* * Matrix triangular view mode button @@ -216,23 +227,130 @@ m_matrixTriangularViewModeAction->setMenu(matrixTriangularViewModeMenu); m_matrixTriangularViewModeToolButton->setDefaultAction(m_matrixTriangularViewModeAction); m_matrixTriangularViewModeToolButton->setPopupMode(QToolButton::InstantPopup); - /* - * Axis location button - * Note: macro support is on each action in menu in createMatrixTriangularViewModeMenu + * Matrix opacity spin box */ - m_axisLocationToolButton = new QToolButton(); - m_axisLocationAction = WuQtUtilities::createAction("A", - "Select location of vertical axis for the selected file", - m_axisLocationToolButton); - QMenu* axisLocationMenu = createAxisLocationMenu(m_axisLocationToolButton, - (objectNamePrefix - + "VerticalAxisLocationMenu:"), - descriptivePrefix); - m_axisLocationAction->setMenu(axisLocationMenu); - m_axisLocationToolButton->setDefaultAction(m_axisLocationAction); - m_axisLocationToolButton->setPopupMode(QToolButton::InstantPopup); + m_matrixOpacitySpinBox = new WuQDoubleSpinBox(this); + m_matrixOpacitySpinBox->setToolTip("Set opacity for matrix"); + m_matrixOpacitySpinBox->setRange(0.0, 1.0); + m_matrixOpacitySpinBox->setDecimals(2); + m_matrixOpacitySpinBox->setSingleStep(0.05); + QObject::connect(m_matrixOpacitySpinBox, &WuQDoubleSpinBox::valueChanged, + this, &ChartTwoOverlayViewController::matrixOpacityValueChanged); + + /* + * Line layer color tool button + */ + m_lineLayerColorToolButton = new CaretColorToolButton(CaretColorToolButton::CustomColorMode::EDITABLE, + CaretColorToolButton::NoneColorMode::DISABLED); + QObject::connect(m_lineLayerColorToolButton, &CaretColorToolButton::colorSelected, + this, &ChartTwoOverlayViewController::lineLayerColorSelected); + m_lineLayerColorToolButton->setToolTip("Set color for line layer charts"); + + /* + * Line layer tooltip offset button + */ + const QString offTT("Set offset of tooltip containing (index, x, y) from selected point " + "with cardinal and ordinal directions"); + m_lineLayerToolTipOffsetToolButton = new QToolButton(); + if (m_useIconInLineLayerToolTipOffsetButtonFlag) { + m_lineLayerToolTipOffsetToolButton->setIcon(createCardinalDirectionPixmap(m_lineLayerToolTipOffsetToolButton)); + } + WuQtUtilities::setWordWrappedToolTip(m_lineLayerToolTipOffsetToolButton, + offTT); + QObject::connect(m_lineLayerToolTipOffsetToolButton, &QToolButton::clicked, + this, &ChartTwoOverlayViewController::lineLayerToolTipOffsetToolButtonClicked); + + + /* + * Line layer normalization widget and menu + */ + m_lineLayerNormalizationWidget = new ChartTwoLineLayerNormalizationWidget(); + QWidgetAction* normalizationWidgetAction = new QWidgetAction(this); + normalizationWidgetAction->setDefaultWidget(m_lineLayerNormalizationWidget); + + m_lineLayerNormalizationToolButton = new QToolButton(); + m_lineLayerNormalizationMenu = new QMenu(m_lineLayerNormalizationToolButton); + m_lineLayerNormalizationMenu->addAction(normalizationWidgetAction); + QObject::connect(m_lineLayerNormalizationMenu, &QMenu::aboutToShow, + this, &ChartTwoOverlayViewController::lineLayerNormalizationMenuAboutToShow); + QObject::connect(m_lineLayerNormalizationMenu, &QMenu::aboutToHide, + this, &ChartTwoOverlayViewController::lineLayerNormalizationMenuAboutToHide); + + /* + * Line layer normalization action + */ + const int greekMu(0x03bc); + const int greekSigma(0x03c3); + const QString normActionText(QString(greekMu) + + QString(greekSigma)); + const QString normActionToolTipText("" + "Transform chart data elements" + ""); + m_lineLayerNormalizationToolButton->setText(normActionText); + m_lineLayerNormalizationToolButton->setToolTip(normActionToolTipText); + m_lineLayerNormalizationToolButton->setObjectName(objectNamePrefix + + ":LineLayerNormalizationToolButton"); + WuQMacroManager::instance()->addMacroSupportToObject(m_lineLayerNormalizationToolButton, + "Adjust Line Layer Normalization"); + + WuQtUtilities::setToolButtonStyleForQt5Mac(m_lineLayerNormalizationToolButton); + QObject::connect(m_lineLayerNormalizationToolButton, &QToolButton::clicked, + this, &ChartTwoOverlayViewController::lineLayerNormalizationToolButtonClicked); + + /* + * Match button sizes + */ + std::vector toolButtons { + m_lineLayerColorToolButton, + m_lineLayerToolTipOffsetToolButton + } ; + WuQtUtilities::matchWidgetSizes(toolButtons); + m_lineLayerNormalizationToolButton->setFixedHeight(m_lineLayerColorToolButton->height()); + + /* + * Line layer width + */ + m_lineLayerWidthSpinBox = new WuQDoubleSpinBox(this); + m_lineLayerWidthSpinBox->setToolTip("Set line width for line layer charts"); + m_lineLayerWidthSpinBox->setRangePercentage(0.0, 100.0); + m_lineLayerWidthSpinBox->setSingleStepPercentage(0.1); + m_lineLayerWidthSpinBox->setDecimals(1); + m_lineLayerWidthSpinBox->getWidget()->setFixedWidth(60); + QObject::connect(m_lineLayerWidthSpinBox, &WuQDoubleSpinBox::valueChanged, + this, &ChartTwoOverlayViewController::lineLayerLineWidthChanged); + + /* + * Seledted point check box and spin box + */ + const QString spinToolTipText("Set index of selected point. Index may also be set " + "by clicking the mouse over the line in the chart " + "graphics. Index can be " + "incremented by placing mouse over the " + "line in the chart graphics and pressing the right or up " + "arrow keys and decremented using the left or down " + "arrow keys (it may be necessary to click in the " + "chart graphics for the arrow keys to function)."); + const QString activeToolTip("OFF - No symbol displayed\n" + "ON - Ring drawn at selected point\n" + "ACTIVE - Circle drawn at selected point\n" + " arrow right/left arrow keys\n" + " in chart drawing region\n" + " increment/decrement point index"); + m_lineLayerActiveComboBox = new EnumComboBoxTemplate(this); + m_lineLayerActiveComboBox->setup(); + QObject::connect(m_lineLayerActiveComboBox, SIGNAL(itemActivated()), + this, SLOT(lineLayerActiveModeEnumComboBoxItemActivated())); + m_lineLayerActiveComboBox->setToolTip(activeToolTip); + + m_selectedPointIndexSpinBox = new QSpinBox(); + m_selectedPointIndexSpinBox->setToolTip("Set index of selected point"); + WuQtUtilities::setWordWrappedToolTip(m_selectedPointIndexSpinBox, + spinToolTipText); + m_selectedPointIndexSpinBox->setSingleStep(1); + QObject::connect(m_selectedPointIndexSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &ChartTwoOverlayViewController::selectedPointIndexSpinBoxValueChanged); /* * Map file Selection Check Box @@ -287,7 +405,7 @@ QObject::connect(m_mapRowOrColumnIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(mapRowOrColumnIndexSpinBoxValueChanged(int))); m_mapRowOrColumnIndexSpinBox->setToolTip("Select map/row/column by its index"); - m_mapRowOrColumnIndexSpinBox->setRange(1, 9999); // fix size for 4 digits + m_mapRowOrColumnIndexSpinBox->setRange(1, 9999); /* fix size for 4 digits */ m_mapRowOrColumnIndexSpinBox->setFixedSize(m_mapRowOrColumnIndexSpinBox->sizeHint()); m_mapRowOrColumnIndexSpinBox->setRange(1, 1); m_mapRowOrColumnIndexSpinBox->setValue(1); @@ -360,7 +478,7 @@ if (m_chartOverlay == NULL) { return; } - + void* pointer = m_mapFileComboBox->itemData(indx).value(); CaretMappableDataFile* file = (CaretMappableDataFile*)pointer; m_chartOverlay->setSelectionData(file, -1); @@ -369,6 +487,11 @@ m_mapRowOrColumnYokingGroupComboBox->validateYokingChange(m_chartOverlay); updateUserInterfaceAndGraphicsWindow(); updateOverlaySettingsEditor(); + + /* + * User interface update may cause loss of focus so restore it + */ + m_mapFileComboBox->setFocus(); } /** @@ -496,7 +619,8 @@ return; } m_chartOverlay->setEnabled(checked); - + updateViewController(m_chartOverlay); + const MapYokingGroupEnum::Enum mapYoking = m_chartOverlay->getMapYokingGroup(); if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { CaretMappableDataFile* mapFile = NULL; @@ -717,7 +841,7 @@ AString dataTypeName = DataFileTypeEnum::toOverlayTypeName(dataFile->getDataFileType()); CaretAssertVectorIndex(displayNames, i); m_mapFileComboBox->addItem(displayNames[i], - qVariantFromValue((void*)dataFile)); + QVariant::fromValue((void*)dataFile)); if (dataFile == selectedFile) { selectedFileIndex = i; } @@ -757,6 +881,8 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -828,6 +954,8 @@ break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: @@ -851,11 +979,6 @@ m_colorBarAction->blockSignals(false); /* - * Update construction button - */ - m_constructionAction->setEnabled(true); - - /* * Update matrix triangular view mode */ m_matrixTriangularViewModeAction->setEnabled(false); @@ -874,23 +997,98 @@ m_matrixTriangularViewModeAction->setEnabled(true); } } + + /* + * Update matrix opacity + */ + if (validOverlayAndFileFlag) { + m_matrixOpacitySpinBox->setValue(m_chartOverlay->getMatrixOpacity()); + } /* - * Update vertical axis location + * Update line layer color, offset, and width tool button */ - m_axisLocationAction->setEnabled(false); + m_lineLayerColorToolButton->setEnabled(false); + m_lineLayerWidthSpinBox->getWidget()->setEnabled(false); + m_lineLayerToolTipOffsetToolButton->setEnabled(false); + m_lineLayerNormalizationToolButton->setEnabled(false); if (validOverlayAndFileFlag) { - if (m_chartOverlay->isCartesianVerticalAxisLocationSupported()) { - m_axisLocationAction->setEnabled(true); - const ChartAxisLocationEnum::Enum axisLocation = m_chartOverlay->getCartesianVerticalAxisLocation(); - for (auto& almd : m_axisLocationMenuData) { - if (std::get<0>(almd) == axisLocation) { - updateAxisLocationAction(axisLocation); - break; - } - } + m_lineLayerColorToolButton->setSelectedColor(m_chartOverlay->getLineLayerColor()); + m_lineLayerWidthSpinBox->setValue(m_chartOverlay->getLineLayerLineWidth()); + if (m_chartOverlay->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER) { + m_lineLayerColorToolButton->setEnabled(true); + m_lineLayerWidthSpinBox->getWidget()->setEnabled(true); + m_lineLayerToolTipOffsetToolButton->setEnabled(true); + m_lineLayerNormalizationToolButton->setEnabled(true); } } + updateLineLayerToolTipOffsetToolButton(); + + /* + * Update selected point checkbox and index + */ + bool pointValidFlag(false); + if (validOverlayAndFileFlag) { + m_lineLayerActiveComboBox->setSelectedItem(m_chartOverlay->getLineChartActiveMode()); + m_selectedPointIndexSpinBox->setRange(0, m_chartOverlay->getSelectedLineChartNumberOfPoints() - 1); + QSignalBlocker spinBlocker(m_selectedPointIndexSpinBox); + m_selectedPointIndexSpinBox->setValue(m_chartOverlay->getSelectedLineChartPointIndex()); + if (m_chartOverlay->getChartTwoDataType() == ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER) { + pointValidFlag = true; + } + } + m_lineLayerActiveComboBox->getWidget()->setEnabled(pointValidFlag); + m_selectedPointIndexSpinBox->setEnabled(pointValidFlag); + + bool showAllMapsCheckbBoxFlag(false); + bool showColorBarButtonFlag(false); + bool showLineLayerColorButtonFlag(false); + bool showLineLayerOffsetButtonFlag(false); + bool showLineLayerWidthButtonFlag(false); + bool showLineLayerNormalizationButtonFlag(false); + bool showSelectedPointControlsFlag(false); + bool showLineSeriesLoadingCheckBoxFlag(false); + bool showMatrixDiagonalButtonFlag(false); + bool showMatrixOpacityFlag(false); + bool showSettingsButtonFlag(false); + switch (m_chartOverlay->getChartTwoDataType()) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + showAllMapsCheckbBoxFlag = true; + showColorBarButtonFlag = true; + showSettingsButtonFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + showLineLayerColorButtonFlag = true; + showLineLayerOffsetButtonFlag = true; + showLineLayerWidthButtonFlag = true; + showLineLayerNormalizationButtonFlag = true; + showSelectedPointControlsFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + showLineSeriesLoadingCheckBoxFlag = true; + showSettingsButtonFlag = true; + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + showColorBarButtonFlag = true; + showMatrixDiagonalButtonFlag = true; + showMatrixOpacityFlag = true; + showSettingsButtonFlag = true; + break; + } + m_allMapsCheckBox->setVisible(showAllMapsCheckbBoxFlag); + m_colorBarToolButton->setVisible(showColorBarButtonFlag); + m_lineLayerColorToolButton->setVisible(showLineLayerColorButtonFlag); + m_lineLayerToolTipOffsetToolButton->setVisible(showLineLayerOffsetButtonFlag); + m_lineLayerNormalizationToolButton->setVisible(showLineLayerNormalizationButtonFlag); + m_lineLayerWidthSpinBox->getWidget()->setVisible(showLineLayerWidthButtonFlag); + m_lineSeriesLoadingEnabledCheckBox->setVisible(showLineSeriesLoadingCheckBoxFlag); + m_matrixTriangularViewModeToolButton->setVisible(showMatrixDiagonalButtonFlag); + m_matrixOpacitySpinBox->getWidget()->setVisible(showMatrixOpacityFlag); + m_lineLayerActiveComboBox->getWidget()->setVisible(showSelectedPointControlsFlag); + m_selectedPointIndexSpinBox->setVisible(showSelectedPointControlsFlag); + m_settingsToolButton->setVisible(showSettingsButtonFlag); } /** @@ -921,34 +1119,6 @@ } /** - * Update the axis location button. - * - * @param axisLocation - * Axis location. - */ -void -ChartTwoOverlayViewController::updateAxisLocationAction(const ChartAxisLocationEnum::Enum axisLocation) -{ - CaretAssert(m_axisLocationAction); - m_axisLocationAction->blockSignals(true); - for (auto& almd : m_axisLocationMenuData) { - if (std::get<0>(almd) == axisLocation) { - QPixmap pixmap = std::get<2>(almd); - if ( ! pixmap.isNull()) { - m_axisLocationAction->setIcon(pixmap); - m_axisLocationAction->setText(""); - } - else { - m_axisLocationAction->setText("A"); - } - break; - } - } - m_axisLocationAction->blockSignals(false); -} - - -/** * Update graphics and GUI after selections made */ void @@ -1058,71 +1228,159 @@ } /** - * Create the axis location menu. - * @param parent - * Parent widget. - * @param parentObjectName - * Name of parent object for macros + * Called when matrix opacity changed + * @param value + * New opacity value */ -QMenu* -ChartTwoOverlayViewController::createAxisLocationMenu(QWidget* widget, - const QString& parentObjectName, - const QString& descriptivePrefix) +void +ChartTwoOverlayViewController::matrixOpacityValueChanged(double value) { - std::vector axisLocations; - axisLocations.push_back(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); - axisLocations.push_back(ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT); - - QMenu* menu = new QMenu(widget); - QObject::connect(menu, &QMenu::triggered, - this, &ChartTwoOverlayViewController::menuAxisLocationTriggered); - - QActionGroup* actionGroup = new QActionGroup(this); - actionGroup->setExclusive(true); - - for (auto axis: axisLocations) { - QAction* action = menu->addAction(ChartAxisLocationEnum::toGuiName(axis)); - action->setCheckable(true); - action->setData((int)ChartAxisLocationEnum::toIntegerCode(axis)); - QPixmap pixmap = createAxisLocationPixmap(menu, axis); - action->setIcon(pixmap); - actionGroup->addAction(action); - - QString objName = (parentObjectName - + ChartAxisLocationEnum::toGuiName(axis)); - objName = objName.replace(" ", ""); - action->setObjectName(objName); - WuQMacroManager::instance()->addMacroSupportToObject(action, - "Select chart axis location for " + descriptivePrefix); - - m_axisLocationMenuData.push_back(std::make_tuple(axis, action, pixmap)); + if (m_chartOverlay != NULL) { + m_chartOverlay->setMatrixOpacity(value); + this->updateGraphicsWindow(); } - - return menu; } /** - * Called when an item is selected on axis location menu. - * - * @action - * Action of menu item selected. + * Called when line layer color is changed. + * @param color + * New color */ void -ChartTwoOverlayViewController::menuAxisLocationTriggered(QAction* action) +ChartTwoOverlayViewController::lineLayerColorSelected(const CaretColor& caretColor) { - const QVariant itemData = action->data(); - CaretAssert(itemData.isValid()); - bool valid = false; - ChartAxisLocationEnum::Enum axisLocation = ChartAxisLocationEnum::fromIntegerCode(itemData.toInt(), &valid); - - if (valid) { - m_chartOverlay->setCartesianVerticalAxisLocation(axisLocation); - updateAxisLocationAction(axisLocation); + if (m_chartOverlay != NULL) { + m_chartOverlay->setLineLayerColor(caretColor); this->updateGraphicsWindow(); } } /** + * Called when line layer line width is changed. + * @param lineWidth + * New line width + */ +void +ChartTwoOverlayViewController::lineLayerLineWidthChanged(const float lineWidth) +{ + if (m_chartOverlay != NULL) { + m_chartOverlay->setLineLayerLineWidth(lineWidth); + this->updateGraphicsWindow(); + } +} + +/** + * Called when line cardinal direction tool button clicked + */ +void +ChartTwoOverlayViewController::lineLayerToolTipOffsetToolButtonClicked() +{ + if (m_chartOverlay != NULL) { + QWidget* parentWidget(m_lineLayerToolTipOffsetToolButton->parentWidget()); + QPoint menuXY(m_lineLayerToolTipOffsetToolButton->pos()); + + std::set options { CardinalDirectionEnum::Options::INCLUDE_AUTO }; + CardinalDirectionEnumMenu menu(options); + menu.setSelectedCardinalDirection(m_chartOverlay->getSelectedLineChartTextOffset()); + QAction* actionSelected = menu.exec(parentWidget->mapToGlobal(menuXY)); + if (actionSelected != NULL) { + m_chartOverlay->setSelectedLineChartTextOffset(menu.getSelectedCardinalDirection()); + updateLineLayerToolTipOffsetToolButton(); + this->updateGraphicsWindow(); + } + } +} + +/** + * Update the line layer tooltip offset button text + */ +void +ChartTwoOverlayViewController::updateLineLayerToolTipOffsetToolButton() +{ + if (m_chartOverlay != NULL) { + if (m_useIconInLineLayerToolTipOffsetButtonFlag) { + m_lineLayerToolTipOffsetToolButton->setText(""); + } + else { + const AString txt = CardinalDirectionEnum::toGuiShortName(m_chartOverlay->getSelectedLineChartTextOffset()); + m_lineLayerToolTipOffsetToolButton->setText(txt); + } + } +} + +/** + * Called when selected point display check box changed + * @param selected + * New selection status + */ +void +ChartTwoOverlayViewController::lineLayerActiveModeEnumComboBoxItemActivated() +{ + if (m_chartOverlay != NULL) { + const ChartTwoOverlayActiveModeEnum::Enum mode = m_lineLayerActiveComboBox->getSelectedItem(); + m_chartOverlay->setLineChartActiveMode(mode); + updateViewController(m_chartOverlay); + updateGraphicsWindow(); + } +} + +/** + * Called when line layer normalization menu is about to hide + */ +void +ChartTwoOverlayViewController::lineLayerNormalizationMenuAboutToHide() +{ +} + +/** + * Called when line layer normalization menu is about to show + */ +void +ChartTwoOverlayViewController::lineLayerNormalizationMenuAboutToShow() +{ + m_lineLayerNormalizationWidget->updateContent(m_chartOverlay); +} + +/** + * Called when line layer normalization button is clicked + */ +void +ChartTwoOverlayViewController::lineLayerNormalizationToolButtonClicked() +{ + m_lineLayerNormalizationMenu->exec(QPoint(m_lineLayerNormalizationToolButton->mapToGlobal(QPoint(0, m_lineLayerNormalizationToolButton->height())))); +} + +/** + * Called when selected point index spin box value changed + * @param index + * New point index + */ +void +ChartTwoOverlayViewController::selectedPointIndexSpinBoxValueChanged(int index) +{ + if (m_chartOverlay != NULL) { + m_chartOverlay->setSelectedLineChartPointIndex(index); + + if (m_chartOverlay->isEnabled()) { + /* + * Graphics updates are normally asynchronous (a graphics update is + * 'scheduled' by Qt and may take place after the graphics update + * event returns). Since we are getting the window position of + * the selected point, and this window position is set in the + * graphics code, we do a 'repaint' which is synchronous + * (the event will not return until after the graphics have updated). + * If we did not do this, the window position may be 'stale' (from + * a previous graphics update) + */ + const bool doRepaintFlag(true); + EventGraphicsUpdateOneWindow graphicsEvent(m_browserWindowIndex, + doRepaintFlag); + EventManager::get()->sendEvent(graphicsEvent.getPointer()); + } + } +} + +/** * Create the construction menu. * @param parent * Parent widget. @@ -1223,11 +1481,29 @@ macroManager->addMacroSupportToObject(copyMapNameAction, "Copy map name to clipboard from " + descriptivePrefix); + menu->addSeparator(); + QAction* preColorAllFilesAction = menu->addAction("Pre-Color All Files"); + QObject::connect(preColorAllFilesAction, &QAction::triggered, + this, &ChartTwoOverlayViewController::menuConstructionPreColorAllFiles); + preColorAllFilesAction->setObjectName(menuActionNamePrefix + + "PreColorAllFiles"); + macroManager->addMacroSupportToObject(preColorAllFilesAction, + "Pre-Color All Files In Overlay"); + return menu; } /** + * Called when construction tool button is clicked + */ +void +ChartTwoOverlayViewController::constructionToolButtonClicked() +{ + m_constructionMenu->exec(QPoint(m_constructionToolButton->mapToGlobal(QPoint(0, m_constructionToolButton->height())))); +} + +/** * Called when construction menu is about to be displayed. */ void @@ -1407,63 +1683,6 @@ } /** - * Create a axis location mode pixmap. - * - * @param widget - * To color the pixmap with backround and foreground, - * the palette from the given widget is used. - * @param axisLocation - * Axis location represented by the icon. - * @return - * Pixmap for matrix view mode. - */ -QPixmap -ChartTwoOverlayViewController::createAxisLocationPixmap(QWidget* widget, - const ChartAxisLocationEnum::Enum axisLocation) -{ - CaretAssert(widget); - - /* - * Create a small, square pixmap that will contain - * the foreground color around the pixmap's perimeter. - */ - const qreal iconSize = 24.0; - const qreal minValue = 2.0; - const qreal maxValue = iconSize - minValue; - - QPixmap pixmap(static_cast(iconSize), - static_cast(iconSize)); - QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, - pixmap); - - QPen pen = painter->pen(); - pen.setWidthF(2.0); - painter->setPen(pen); - - const int offsetFromEdge = 3; - switch (axisLocation) { - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: - painter->drawLine(QPointF(minValue, minValue + offsetFromEdge), - QPointF(maxValue, minValue + offsetFromEdge)); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: - painter->drawLine(QPointF(minValue + offsetFromEdge, minValue), - QPointF(minValue + offsetFromEdge, maxValue)); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: - painter->drawLine(QPointF(maxValue - offsetFromEdge, minValue), - QPointF(maxValue - offsetFromEdge, maxValue)); - break; - case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: - painter->drawLine(QPointF(minValue, maxValue - offsetFromEdge), - QPointF(maxValue, maxValue - offsetFromEdge)); - break; - } - - return pixmap; -} - -/** * Create a matrix view mode pixmap. * * @param widget @@ -1543,3 +1762,193 @@ return pixmap; } +/** + * Create a cardinal direction pixmap. + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @return + * Pixmap for cardinal direction. + */ +QPixmap +ChartTwoOverlayViewController::createCardinalDirectionPixmap(QWidget* widget) +{ + CaretAssert(widget); + + /* + * Create a small, square pixmap that will contain + * the foreground color around the pixmap's perimeter. + */ + const qreal iconSize = 32.0; + const qreal minValue = 1.0; + const qreal maxX((iconSize - minValue) / 2.0); + const qreal maxY(maxX); + const QPointF left(-maxX, 0.0); + const QPointF right(maxX, 0.0); + const QPointF top(0.0, maxY); + const QPointF bottom(0.0, -maxY); + + QPixmap pixmap(static_cast(iconSize), + static_cast(iconSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginCenter(widget, + pixmap); + + QPen pen = painter->pen(); + pen.setWidthF(2.0); + painter->setPen(pen); + + /* + * Horizontal and vertical lines + */ + painter->drawLine(left, right); + painter->drawLine(bottom, top); + + /* + * Arrow tips + */ + const float tip(4.0); + painter->drawLine(left.x(), left.y(), left.x() + tip, left.y() + tip); + painter->drawLine(left.x(), left.y(), left.x() + tip, left.y() - tip); + painter->drawLine(right.x(), right.y(), right.x() - tip, right.y() + tip); + painter->drawLine(right.x(), right.y(), right.x() - tip, right.y() - tip); + painter->drawLine(bottom.x(), bottom.y(), bottom.x() - tip, bottom.y() + tip); + painter->drawLine(bottom.x(), bottom.y(), bottom.x() + tip, bottom.y() + tip); + painter->drawLine(top.x(), top.y(), top.x() - tip, top.y() - tip); + painter->drawLine(top.x(), top.y(), top.x() + tip, top.y() - tip); + + return pixmap; +} + +/** + * Pre-color all files. Some files, such as large scalar matrix files, may take + * time the first time they are displayed that is spent coloring the matrix + * and creating an OpenGL texture used when drawing. + */ +void +ChartTwoOverlayViewController::menuConstructionPreColorAllFiles() +{ + QWidget* parentWidget(m_mapFileComboBox); + + if ( ! m_enabledCheckBox->isChecked()) { + WuQMessageBox::errorOk(parentWidget, + "The layer must be On (checkbox on left)"); + return; + } + + const int32_t numFiles = m_mapFileComboBox->count(); + if (numFiles <= 0) { + return; + } + + QPoint dialogXY(parentWidget->x() + parentWidget->width(), + parentWidget->y()); + ProgressReportingDialog progressDialog("Pre-Color Files", + "Starting", + parentWidget); + progressDialog.move(parentWidget->mapToGlobal(dialogXY)); + + const int32_t currentFileIndex = m_mapFileComboBox->currentIndex(); + + for (int32_t i = 0; i < numFiles; i++) { + EventProgressUpdate progEvent(1, + numFiles, + (i + 1), + ("Coloring: " + + m_mapFileComboBox->currentText())); + EventManager::get()->sendEvent(progEvent.getPointer()); + + if (progressDialog.wasCanceled()) { + break; + } + + QSignalBlocker blocker(m_mapFileComboBox); + m_mapFileComboBox->setCurrentIndex(i); + fileComboBoxSelected(i); + + /* + * Need to to a graphics update with a repaint (a synchronous + * graphics update) so that file is actually drawn. + */ + EventGraphicsUpdateOneWindow graphicsEvent(m_browserWindowIndex, + true); + EventManager::get()->sendEvent(graphicsEvent.getPointer()); + QApplication::processEvents(); + } + + QSignalBlocker blocker(m_mapFileComboBox); + m_mapFileComboBox->setCurrentIndex(currentFileIndex); + fileComboBoxSelected(currentFileIndex); + + m_mapFileComboBox->clearFocus(); +} + +/** + * @return a pixmap containing a normal distribution curve (bell curve) + * @param widget + * Widget used for foreground/background colors + */ +QPixmap +ChartTwoOverlayViewController::createNormalizationPixmap(QWidget* widget) +{ + CaretAssert(widget); + + const int32_t iconSize(24); + QPixmap pixmap(static_cast(iconSize), + static_cast(iconSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, + pixmap); + + QPen pen = painter->pen(); + pen.setWidthF(2.0); + painter->setPen(pen); + + /* + * XY points used in bezier functions + */ + float t(2.0); + QPoint leftEnd(t, t); + QPoint mid((iconSize / 2), iconSize - (t * 3)); + QPoint rightEnd(iconSize - t, t); + QPoint leftMid((leftEnd.x() + mid.x()) / 2.0, + (leftEnd.y() + mid.y()) / 2.0); + QPoint rightMid((rightEnd.x() + mid.x()) / 2.0, + (rightEnd.y() + mid.y()) / 2.0); + + /* + * Control points for left half of curve + */ + QPoint leftCP2(leftEnd.x(), mid.y()); + QPoint leftCP1(mid.x(), leftEnd.y()); + + /* + * Control points for right half of curve + */ + QPoint rightCP2(rightEnd.x(), mid.y()); + QPoint rightCP1(mid.x(), rightEnd.y()); + + QPainterPath path; + + /* + * left half of curve + */ + path.moveTo(leftEnd); + path.cubicTo(leftCP1, + leftCP2, + mid); + + /* + * right half of curve + */ + path.moveTo(rightEnd); + path.cubicTo(rightCP1, + rightCP2, + mid); + + /* + * Drawn a curve that approximates the normal distribution (bell curve) + */ + painter->drawPath(path); + + return pixmap; +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoOverlayViewController.h connectome-workbench-1.5.0/src/GuiQt/ChartTwoOverlayViewController.h --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoOverlayViewController.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoOverlayViewController.h 2021-02-16 19:46:47.000000000 +0000 @@ -25,7 +25,6 @@ #include #include -#include "ChartAxisLocationEnum.h" #include "ChartTwoMatrixTriangularViewingModeEnum.h" class QAction; @@ -37,8 +36,13 @@ class QToolButton; namespace caret { + class CaretColor; + class CaretColorToolButton; + class ChartTwoLineLayerNormalizationWidget; class ChartTwoOverlay; + class EnumComboBoxTemplate; class MapYokingGroupComboBox; + class WuQDoubleSpinBox; class WuQGridLayoutGroup; class ChartTwoOverlayViewController : public QObject { @@ -105,9 +109,29 @@ void menuConstructionAboutToShow(); + void menuConstructionPreColorAllFiles(); + void menuMatrixTriangularViewModeTriggered(QAction* action); - void menuAxisLocationTriggered(QAction* action); + void matrixOpacityValueChanged(double value); + + void constructionToolButtonClicked(); + + void lineLayerColorSelected(const CaretColor& caretColor); + + void lineLayerLineWidthChanged(const float lineWidth); + + void lineLayerToolTipOffsetToolButtonClicked(); + + void lineLayerActiveModeEnumComboBoxItemActivated(); + + void lineLayerNormalizationMenuAboutToHide(); + + void lineLayerNormalizationMenuAboutToShow(); + + void lineLayerNormalizationToolButtonClicked(); + + void selectedPointIndexSpinBoxValueChanged(int index); private: ChartTwoOverlayViewController(const ChartTwoOverlayViewController&); @@ -128,10 +152,6 @@ const QString& parentObjectName, const QString& descriptivePrefix); - QMenu* createAxisLocationMenu(QWidget* widget, - const QString& parentObjectName, - const QString& descriptivePrefix); - void validateYokingSelection(); void updateOverlaySettingsEditor(); @@ -139,12 +159,13 @@ QPixmap createMatrixTriangularViewModePixmap(QWidget* widget, const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode); - QPixmap createAxisLocationPixmap(QWidget* widget, - const ChartAxisLocationEnum::Enum axisLocation); + QPixmap createCardinalDirectionPixmap(QWidget* widget); void updateMatrixTriangularViewModeAction(const ChartTwoMatrixTriangularViewingModeEnum::Enum matrixViewMode); - void updateAxisLocationAction(const ChartAxisLocationEnum::Enum axisLocation); + void updateLineLayerToolTipOffsetToolButton(); + + QPixmap createNormalizationPixmap(QWidget *widget); const int32_t m_browserWindowIndex; @@ -152,6 +173,8 @@ ChartTwoOverlay* m_chartOverlay; + const AString m_parentObjectName; + QCheckBox* m_enabledCheckBox; QCheckBox* m_lineSeriesLoadingEnabledCheckBox; @@ -164,20 +187,26 @@ QAction* m_colorBarAction; - QAction* m_constructionAction; + QMenu* m_constructionMenu; QToolButton* m_matrixTriangularViewModeToolButton; QAction* m_matrixTriangularViewModeAction; - QAction* m_axisLocationAction; + CaretColorToolButton* m_lineLayerColorToolButton; + + QToolButton* m_lineLayerToolTipOffsetToolButton; + + bool m_useIconInLineLayerToolTipOffsetButtonFlag = false; + + QToolButton* m_lineLayerNormalizationToolButton; - QToolButton* m_axisLocationToolButton; + ChartTwoLineLayerNormalizationWidget* m_lineLayerNormalizationWidget; + + QMenu* m_lineLayerNormalizationMenu; std::vector> m_matrixViewMenuData; - std::vector> m_axisLocationMenuData; - QComboBox* m_mapFileComboBox; MapYokingGroupComboBox* m_mapRowOrColumnYokingGroupComboBox; @@ -192,6 +221,14 @@ QAction* m_constructionReloadFileAction; + WuQDoubleSpinBox* m_lineLayerWidthSpinBox = NULL; + + EnumComboBoxTemplate* m_lineLayerActiveComboBox = NULL; + + WuQDoubleSpinBox* m_matrixOpacitySpinBox = NULL; + + QSpinBox* m_selectedPointIndexSpinBox = NULL; + // ADD_NEW_MEMBERS_HERE friend class ChartTwoOverlaySetViewController; diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoTitleEditorWidget.cxx connectome-workbench-1.5.0/src/GuiQt/ChartTwoTitleEditorWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoTitleEditorWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoTitleEditorWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,181 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CHART_TWO_TITLE_EDITOR_WIDGET_DECLARE__ +#include "ChartTwoTitleEditorWidget.h" +#undef __CHART_TWO_TITLE_EDITOR_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include + +#include "AnnotationPercentSizeText.h" +#include "BrainBrowserWindowToolBar.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "ChartTwoOverlaySet.h" +#include "ChartTwoTitle.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "ModelChartTwo.h" +#include "WuQDataEntryDialog.h" +#include "WuQDoubleSpinBox.h" +#include "WuQMacroManager.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::ChartTwoTitleEditorWidget + * \brief Widget for editing chart title + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parent + * The parent widget + * @param parentObjectName + * Name of parent object for macros + */ +ChartTwoTitleEditorWidget::ChartTwoTitleEditorWidget(QWidget* parent, + const QString& parentObjectName) +: QWidget(parent) +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(parentObjectName + + ":ChartTitle:"); + + m_textLineEdit = new QLineEdit(); + QObject::connect(m_textLineEdit, &QLineEdit::textEdited, + this, &ChartTwoTitleEditorWidget::textLineEditChanged); + + m_titleSizeSpinBox = new WuQDoubleSpinBox(this); + m_titleSizeSpinBox->setRangePercentage(0.0, 99.0); + QObject::connect(m_titleSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &ChartTwoTitleEditorWidget::sizeSpinBoxValueChanged); + m_titleSizeSpinBox->setToolTip("Set height of title as percentage of tab height"); + m_titleSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "Height"); + macroManager->addMacroSupportToObject(m_titleSizeSpinBox->getWidget(), + "Set chart title height"); + + m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); + m_paddingSizeSpinBox->setRangePercentage(0.0, 99.0); + QObject::connect(m_paddingSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), + this, &ChartTwoTitleEditorWidget::sizeSpinBoxValueChanged); + m_paddingSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "Padding"); + macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget(), + "Set chart padding"); + + m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height"); + QGridLayout* layout = new QGridLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 3, 0); + int row = 0; + layout->addWidget(new QLabel("Text"), row, 0); + layout->addWidget(m_textLineEdit, row, 1); + row++; + layout->addWidget(new QLabel("Size"), row, 0); + layout->addWidget(m_titleSizeSpinBox->getWidget(), row, 1); + row++; + layout->addWidget(new QLabel("Pad"), row, 0); + layout->addWidget(m_paddingSizeSpinBox->getWidget(), row, 1); + row++; +} + +/** + * Destructor. + */ +ChartTwoTitleEditorWidget::~ChartTwoTitleEditorWidget() +{ +} + +/** + * Update content of this tool bar component. + * + * @param chartTwoOverlaySet + * The chart two overlay set. + */ +void +ChartTwoTitleEditorWidget::updateControls(ChartTwoOverlaySet* chartTwoOverlaySet) +{ + m_chartOverlaySet = chartTwoOverlaySet; + + if (m_chartOverlaySet != NULL) { + setEnabled(true); + const ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); + m_textLineEdit->setText(m_chartOverlaySet->getChartTitle()->getText()); + + m_titleSizeSpinBox->blockSignals(true); + m_titleSizeSpinBox->setValue(chartTitle->getTextSize()); + m_titleSizeSpinBox->blockSignals(false); + + m_paddingSizeSpinBox->blockSignals(true); + m_paddingSizeSpinBox->setValue(chartTitle->getPaddingSize()); + m_paddingSizeSpinBox->blockSignals(false); + + m_textLineEdit->setFocus(); + } + else { + setEnabled(false); + } +} + +/** + * Called when a size spin box value is changed + */ +void +ChartTwoTitleEditorWidget::sizeSpinBoxValueChanged(double) +{ + if (m_chartOverlaySet != NULL) { + ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); + chartTitle->setTextSize(m_titleSizeSpinBox->value()); + chartTitle->setPaddingSize(m_paddingSizeSpinBox->value()); + updateGraphics(); + } +} + +/** + * Called when title text is edited + */ +void +ChartTwoTitleEditorWidget::textLineEditChanged(const QString& text) +{ + if (m_chartOverlaySet != NULL) { + ChartTwoTitle* chartTitle = m_chartOverlaySet->getChartTitle(); + chartTitle->setText(text); + updateGraphics(); + } +} + +void +ChartTwoTitleEditorWidget::updateGraphics() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/ChartTwoTitleEditorWidget.h connectome-workbench-1.5.0/src/GuiQt/ChartTwoTitleEditorWidget.h --- connectome-workbench-1.4.2/src/GuiQt/ChartTwoTitleEditorWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ChartTwoTitleEditorWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,77 @@ +#ifndef __CHART_TWO_TITLE_EDITOR_WIDGET_H__ +#define __CHART_TWO_TITLE_EDITOR_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +class QLineEdit; + +namespace caret { + + class BrowserTabContent; + class ChartTwoOverlaySet; + class WuQDoubleSpinBox; + + class ChartTwoTitleEditorWidget : public QWidget { + Q_OBJECT + + public: + ChartTwoTitleEditorWidget(QWidget* parent, + const QString& parentObjectName); + + virtual ~ChartTwoTitleEditorWidget(); + + virtual void updateControls(ChartTwoOverlaySet* chartTwoOverlaySet); + + // ADD_NEW_METHODS_HERE + + private slots: + void textLineEditChanged(const QString& text); + + void sizeSpinBoxValueChanged(double); + + private: + ChartTwoTitleEditorWidget(const ChartTwoTitleEditorWidget&); + + ChartTwoTitleEditorWidget& operator=(const ChartTwoTitleEditorWidget&); + + void updateGraphics(); + + QLineEdit* m_textLineEdit; + + WuQDoubleSpinBox* m_titleSizeSpinBox; + + WuQDoubleSpinBox* m_paddingSizeSpinBox; + + ChartTwoOverlaySet* m_chartOverlaySet; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CHART_TWO_TITLE_EDITOR_WIDGET_DECLARE__ + // +#endif // __CHART_TWO_TITLE_EDITOR_WIDGET_DECLARE__ + +} // namespace +#endif //__CHART_TWO_TITLE_EDITOR_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/CiftiConnectivityMatrixViewController.cxx connectome-workbench-1.5.0/src/GuiQt/CiftiConnectivityMatrixViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/CiftiConnectivityMatrixViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CiftiConnectivityMatrixViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -289,7 +289,7 @@ checkBox->setChecked(checkStatus); checkBox->setProperty(FILE_POINTER_PROPERTY_NAME, - qVariantFromValue((void*)files[i])); + QVariant::fromValue((void*)files[i])); const CiftiConnectivityMatrixDenseDynamicFile* dynConnFile = dynamic_cast(files[i]); if (dynConnFile != NULL) { @@ -382,7 +382,7 @@ } comboBox->addItem(orientFile->getFileNameNoPath(), - qVariantFromValue((void*)orientFile)); + QVariant::fromValue((void*)orientFile)); } } diff -Nru connectome-workbench-1.4.2/src/GuiQt/ClippingPlanesDialog.cxx connectome-workbench-1.5.0/src/GuiQt/ClippingPlanesDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/ClippingPlanesDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ClippingPlanesDialog.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,510 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __CLIPPING_PLANES_DIALOG_DECLARE__ -#include "ClippingPlanesDialog.h" -#undef __CLIPPING_PLANES_DIALOG_DECLARE__ - -#include -#include -#include -#include -#include - -#include "BrainBrowserWindow.h" -#include "BrainBrowserWindowComboBox.h" -#include "BrowserTabContent.h" -#include "CaretAssert.h" -#include "EventGraphicsUpdateAllWindows.h" -#include "EventManager.h" -#include "EventUserInterfaceUpdate.h" -#include "GuiManager.h" -#include "WuQtUtilities.h" -#include "WuQWidgetObjectGroup.h" - -using namespace caret; - - - -/** - * \class caret::ClippingPlanesDialog - * \brief Dialog for adjusting clipping planes transformation. - * \ingroup GuiQt - */ - -/** - * Constructor. - */ -ClippingPlanesDialog::ClippingPlanesDialog(QWidget* parent) -: WuQDialogNonModal("Clipping Planes", - parent) -{ - m_blockDialogUpdate = true; - - /*------------------------------------------------------------------------*/ - /* - * Create widgets - */ - - /* - * Window number - */ - QLabel* windowLabel = new QLabel("Workbench Window: "); - m_browserWindowComboBox = new BrainBrowserWindowComboBox(BrainBrowserWindowComboBox::STYLE_NUMBER, - this); - m_browserWindowComboBox->getWidget()->setFixedWidth(50); - QObject::connect(m_browserWindowComboBox, SIGNAL(browserWindowSelected(BrainBrowserWindow*)), - this, SLOT(browserWindowComboBoxValueChanged(BrainBrowserWindow*))); - QHBoxLayout* windowLayout = new QHBoxLayout(); - windowLayout->addWidget(windowLabel); - windowLayout->addWidget(m_browserWindowComboBox->getWidget()); - windowLayout->addStretch(); - - /* - * X, Y, Z column labels - */ - QLabel* xColumnLabel = new QLabel("X"); - QLabel* yColumnLabel = new QLabel("Y"); - QLabel* zColumnLabel = new QLabel("Z"); - - const int spinBoxWidth = 90; - /* - * Panning - */ - const double panStep = 1.0; - QLabel* panLabel = new QLabel("Pan:"); - m_xPanDoubleSpinBox = new QDoubleSpinBox; - m_xPanDoubleSpinBox->setMinimum(-100000.0); - m_xPanDoubleSpinBox->setMaximum( 100000.0); - m_xPanDoubleSpinBox->setSingleStep(panStep); - m_xPanDoubleSpinBox->setDecimals(2); - m_xPanDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_xPanDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - m_yPanDoubleSpinBox = new QDoubleSpinBox; - m_yPanDoubleSpinBox->setMinimum(-100000.0); - m_yPanDoubleSpinBox->setMaximum( 100000.0); - m_yPanDoubleSpinBox->setSingleStep(panStep); - m_yPanDoubleSpinBox->setDecimals(2); - m_yPanDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_yPanDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - m_zPanDoubleSpinBox = new QDoubleSpinBox; - m_zPanDoubleSpinBox->setMinimum(-100000.0); - m_zPanDoubleSpinBox->setMaximum( 100000.0); - m_zPanDoubleSpinBox->setSingleStep(panStep); - m_zPanDoubleSpinBox->setDecimals(2); - m_zPanDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_zPanDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - - - /* - * Rotation - */ - const double rotationMinimum = -360.0; - const double rotationMaximum = 360.0; - const double rotateStep = 1.0; - QLabel* rotateLabel = new QLabel("Rotate: "); - m_xRotateDoubleSpinBox = new QDoubleSpinBox; - m_xRotateDoubleSpinBox->setWrapping(true); - m_xRotateDoubleSpinBox->setMinimum(rotationMinimum); - m_xRotateDoubleSpinBox->setMaximum(rotationMaximum); - m_xRotateDoubleSpinBox->setSingleStep(rotateStep); - m_xRotateDoubleSpinBox->setDecimals(2); - m_xRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_xRotateDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - m_yRotateDoubleSpinBox = new QDoubleSpinBox; - m_yRotateDoubleSpinBox->setWrapping(true); - m_yRotateDoubleSpinBox->setMinimum(rotationMinimum); - m_yRotateDoubleSpinBox->setMaximum(rotationMaximum); - m_yRotateDoubleSpinBox->setSingleStep(rotateStep); - m_yRotateDoubleSpinBox->setDecimals(2); - m_yRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_yRotateDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - m_zRotateDoubleSpinBox = new QDoubleSpinBox; - m_zRotateDoubleSpinBox->setWrapping(true); - m_zRotateDoubleSpinBox->setMinimum(rotationMinimum); - m_zRotateDoubleSpinBox->setMaximum(rotationMaximum); - m_zRotateDoubleSpinBox->setSingleStep(rotateStep); - m_zRotateDoubleSpinBox->setDecimals(2); - m_zRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_zRotateDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - - /* - * Thickness - */ - const double thicknessMinimum = 0.0; - const double thicknessMaximum = 1000000.0; - const double thicknessStep = 1.0; - QLabel* thicknessLabel = new QLabel("Thickness (mm)"); - - m_xThicknessDoubleSpinBox = new QDoubleSpinBox(); - m_xThicknessDoubleSpinBox->setMinimum(thicknessMinimum); - m_xThicknessDoubleSpinBox->setMaximum(thicknessMaximum); - m_xThicknessDoubleSpinBox->setSingleStep(thicknessStep); - m_xThicknessDoubleSpinBox->setDecimals(2); - m_xThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_xThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - m_yThicknessDoubleSpinBox = new QDoubleSpinBox(); - m_yThicknessDoubleSpinBox->setMinimum(thicknessMinimum); - m_yThicknessDoubleSpinBox->setMaximum(thicknessMaximum); - m_yThicknessDoubleSpinBox->setSingleStep(thicknessStep); - m_yThicknessDoubleSpinBox->setDecimals(2); - m_yThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_yThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - m_zThicknessDoubleSpinBox = new QDoubleSpinBox(); - m_zThicknessDoubleSpinBox->setMinimum(thicknessMinimum); - m_zThicknessDoubleSpinBox->setMaximum(thicknessMaximum); - m_zThicknessDoubleSpinBox->setSingleStep(thicknessStep); - m_zThicknessDoubleSpinBox->setDecimals(2); - m_zThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); - QObject::connect(m_zThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(clippingValueChanged())); - - - m_clippingWidgetGroup = new WuQWidgetObjectGroup(this); - m_clippingWidgetGroup->add(m_xPanDoubleSpinBox); - m_clippingWidgetGroup->add(m_yPanDoubleSpinBox); - m_clippingWidgetGroup->add(m_zPanDoubleSpinBox); - m_clippingWidgetGroup->add(m_xRotateDoubleSpinBox); - m_clippingWidgetGroup->add(m_yRotateDoubleSpinBox); - m_clippingWidgetGroup->add(m_zRotateDoubleSpinBox); - m_clippingWidgetGroup->add(m_xThicknessDoubleSpinBox); - m_clippingWidgetGroup->add(m_yThicknessDoubleSpinBox); - m_clippingWidgetGroup->add(m_zThicknessDoubleSpinBox); - - /* - * Show clipping box checkbox - */ - m_displayClippingBoxCheckBox = new QCheckBox("Show Clipping Box Outline"); - QObject::connect(m_displayClippingBoxCheckBox, SIGNAL(clicked(bool)), - this, SLOT(clippingValueChanged())); - - /*------------------------------------------------------------------------*/ - /* - * Layout widgets - */ - /* - * Columns for grid layout - */ - int column = 0; - const int COLUMN_LABEL = column++; - const int COLUMN_X = column++; - const int COLUMN_Y = column++; - const int COLUMN_Z = column++; - - QWidget* gridWidget = new QWidget(); - QGridLayout* gridLayout = new QGridLayout(gridWidget); - WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 4); - int row = 0; - gridLayout->addWidget(xColumnLabel, - row, - COLUMN_X, - Qt::AlignHCenter); - gridLayout->addWidget(yColumnLabel, - row, - COLUMN_Y, - Qt::AlignHCenter); - gridLayout->addWidget(zColumnLabel, - row, - COLUMN_Z, - Qt::AlignHCenter); - row++; - gridLayout->addWidget(panLabel, - row, - COLUMN_LABEL); - gridLayout->addWidget(m_xPanDoubleSpinBox, - row, - COLUMN_X); - gridLayout->addWidget(m_yPanDoubleSpinBox, - row, - COLUMN_Y); - gridLayout->addWidget(m_zPanDoubleSpinBox, - row, - COLUMN_Z); - row++; - - gridLayout->addWidget(rotateLabel, - row, - COLUMN_LABEL); - gridLayout->addWidget(m_xRotateDoubleSpinBox, - row, - COLUMN_X); - gridLayout->addWidget(m_yRotateDoubleSpinBox, - row, - COLUMN_Y); - gridLayout->addWidget(m_zRotateDoubleSpinBox, - row, - COLUMN_Z); - row++; - - gridLayout->addWidget(thicknessLabel, - row, - COLUMN_LABEL); - gridLayout->addWidget(m_xThicknessDoubleSpinBox, - row, - COLUMN_X); - gridLayout->addWidget(m_yThicknessDoubleSpinBox, - row, - COLUMN_Y); - gridLayout->addWidget(m_zThicknessDoubleSpinBox, - row, - COLUMN_Z); - - /*------------------------------------------------------------------------*/ - /* - * Finish up - */ - QWidget* widget = new QWidget(); - QVBoxLayout* layout = new QVBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 4); - layout->addLayout(windowLayout); - layout->addWidget(m_displayClippingBoxCheckBox); - layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); - layout->addWidget(gridWidget); - widget->setFixedSize(widget->sizeHint()); - - /* - * Remove apply button by using an empty name - */ - setApplyButtonText(""); - - m_resetPushButton = addUserPushButton("Reset", QDialogButtonBox::NoRole); - - setCentralWidget(widget, - WuQDialog::SCROLL_AREA_NEVER); - - /* - * No auto default button processing (Qt highlights button) - */ - disableAutoDefaultForAllPushButtons(); - - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); - - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - - m_blockDialogUpdate = false; -} - -/** - * Destructor. - */ -ClippingPlanesDialog::~ClippingPlanesDialog() -{ - EventManager::get()->removeAllEventsFromListener(this); -} - -/** - * Called when a user (added) push button is clicked. - * - * @param userPushButton - * Button that was clicked. - */ -WuQDialogNonModal::DialogUserButtonResult -ClippingPlanesDialog::userButtonPressed(QPushButton* userPushButton) -{ - if (userPushButton == m_resetPushButton) { - BrainBrowserWindow* bbw = m_browserWindowComboBox->getSelectedBrowserWindow(); - if (bbw != NULL) { - BrowserTabContent* btc = bbw->getBrowserTabContent(); - if (btc != NULL) { - btc->resetClippingPlaneTransformation(); - updateContent(btc->getTabNumber()); - updateGraphicsWindow(); - } - } - } - else { - CaretAssert(0); - } - - return WuQDialogNonModal::RESULT_NONE; -} - -/** - * Called when window number combo box value changed. - */ -void -ClippingPlanesDialog::browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow) -{ - int32_t windowIndex = -1; - if (browserWindow != NULL) { - windowIndex = browserWindow->getBrowserWindowIndex(); - } - - updateContent(windowIndex); -} - - -/** - * Called when a clipping value is changed. - */ -void -ClippingPlanesDialog::clippingValueChanged() -{ - const float panning[3] = { - (float)m_xPanDoubleSpinBox->value(), - (float)m_yPanDoubleSpinBox->value(), - (float)m_zPanDoubleSpinBox->value() - }; - - const float rotation[3] = { - (float)m_xRotateDoubleSpinBox->value(), - (float)m_yRotateDoubleSpinBox->value(), - (float)m_zRotateDoubleSpinBox->value() - }; - - const float thickness[3] ={ - (float)m_xThicknessDoubleSpinBox->value(), - (float)m_yThicknessDoubleSpinBox->value(), - (float)m_zThicknessDoubleSpinBox->value() - }; - - /* - * Update, set, and validate selected browser window - */ - BrainBrowserWindow* bbw = m_browserWindowComboBox->getSelectedBrowserWindow(); - if (bbw != NULL) { - BrowserTabContent* btc = bbw->getBrowserTabContent(); - if (btc != NULL) { - btc->setClippingPlaneTransformation(panning, - rotation, - thickness, - m_displayClippingBoxCheckBox->isChecked()); - updateGraphicsWindow(); - } - } -} - - -/** - * Update the selected graphics window. - */ -void -ClippingPlanesDialog::updateGraphicsWindow() -{ - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); -} - -/** - * Gets called when the dialog gains focus. - */ -void -ClippingPlanesDialog::focusGained() -{ - updateDialog(); -} - -/** - * Update the dialog. - */ -void -ClippingPlanesDialog::updateDialog() -{ - m_browserWindowComboBox->updateComboBox(); - updateContent(m_browserWindowComboBox->getSelectedBrowserWindowIndex()); -} - - -/** - * Update the content in the dialog - * @param browserWindowIndexIn - * Index of the browser window. - */ -void -ClippingPlanesDialog::updateContent(const int32_t browserWindowIndexIn) -{ - - /* - * May get updates when graphics are redrawn by this dialog - * and not doing this could result in infinite loop - */ - if (m_blockDialogUpdate) { - return; - } - - /* - * Update, set, and validate selected browser window - */ - m_browserWindowComboBox->updateComboBox(); - m_browserWindowComboBox->setBrowserWindowByIndex(browserWindowIndexIn); - const int32_t browserWindowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); - - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); - if (bbw != NULL) { - BrowserTabContent* btc = bbw->getBrowserTabContent(); - if (btc != NULL) { - m_clippingWidgetGroup->blockAllSignals(true); - - float panning[3]; - float rotation[3]; - float thickness[3]; - bool displayClippingBox; - btc->getClippingPlaneTransformation(panning, - rotation, - thickness, - displayClippingBox); - - m_xPanDoubleSpinBox->setValue(panning[0]); - m_yPanDoubleSpinBox->setValue(panning[1]); - m_zPanDoubleSpinBox->setValue(panning[2]); - - m_xRotateDoubleSpinBox->setValue(rotation[0]); - m_yRotateDoubleSpinBox->setValue(rotation[1]); - m_zRotateDoubleSpinBox->setValue(rotation[2]); - - m_xThicknessDoubleSpinBox->setValue(thickness[0]); - m_yThicknessDoubleSpinBox->setValue(thickness[1]); - m_zThicknessDoubleSpinBox->setValue(thickness[2]); - - m_displayClippingBoxCheckBox->setChecked(displayClippingBox); - - m_clippingWidgetGroup->blockAllSignals(false); - } - } -} - -/** - * Receive events from the event manager. - * - * @param event - * Event sent by event manager. - */ -void -ClippingPlanesDialog::receiveEvent(Event* event) -{ - if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { - EventUserInterfaceUpdate* updateEvent = - dynamic_cast(event); - CaretAssert(updateEvent); - - updateEvent->setEventProcessed(); - - updateDialog(); - } -} diff -Nru connectome-workbench-1.4.2/src/GuiQt/ClippingPlanesDialog.h connectome-workbench-1.5.0/src/GuiQt/ClippingPlanesDialog.h --- connectome-workbench-1.4.2/src/GuiQt/ClippingPlanesDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ClippingPlanesDialog.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -#ifndef __CLIPPING_PLANES_DIALOG_H__ -#define __CLIPPING_PLANES_DIALOG_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2014 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "WuQDialogNonModal.h" - -#include "EventListenerInterface.h" - -class QCheckBox; -class QDoubleSpinBox; -class QPushButton; - -namespace caret { - class BrainBrowserWindow; - class BrainBrowserWindowComboBox; - class WuQWidgetObjectGroup; - - class ClippingPlanesDialog : public WuQDialogNonModal, public EventListenerInterface { - - Q_OBJECT - - public: - ClippingPlanesDialog(QWidget* parent); - - virtual ~ClippingPlanesDialog(); - - - void updateDialog(); - - void updateContent(const int32_t browserWindowIndex); - - // ADD_NEW_METHODS_HERE - - virtual void receiveEvent(Event* event); - - private slots: - void browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow); - - void clippingValueChanged(); - - protected: - void focusGained(); - - virtual DialogUserButtonResult userButtonPressed(QPushButton* userPushButton); - - private: - void updateGraphicsWindow(); - - ClippingPlanesDialog(const ClippingPlanesDialog&); - - ClippingPlanesDialog& operator=(const ClippingPlanesDialog&); - - QPushButton* m_resetPushButton; - - BrainBrowserWindowComboBox* m_browserWindowComboBox; - - WuQWidgetObjectGroup* m_clippingWidgetGroup; - - QDoubleSpinBox* m_xPanDoubleSpinBox; - - QDoubleSpinBox* m_yPanDoubleSpinBox; - - QDoubleSpinBox* m_zPanDoubleSpinBox; - - QDoubleSpinBox* m_xRotateDoubleSpinBox; - - QDoubleSpinBox* m_yRotateDoubleSpinBox; - - QDoubleSpinBox* m_zRotateDoubleSpinBox; - - QDoubleSpinBox* m_xThicknessDoubleSpinBox; - - QDoubleSpinBox* m_yThicknessDoubleSpinBox; - - QDoubleSpinBox* m_zThicknessDoubleSpinBox; - - QCheckBox* m_displayClippingBoxCheckBox; - - bool m_blockDialogUpdate; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __CLIPPING_PLANES_DIALOG_DECLARE__ - // -#endif // __CLIPPING_PLANES_DIALOG_DECLARE__ - -} // namespace -#endif //__CLIPPING_PLANES_DIALOG_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ClippingPlanesWidget.cxx connectome-workbench-1.5.0/src/GuiQt/ClippingPlanesWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/ClippingPlanesWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ClippingPlanesWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,570 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CLIPPING_PLANES_WIDGET_DECLARE__ +#include "ClippingPlanesWidget.h" +#undef __CLIPPING_PLANES_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventBrowserTabGet.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventUpdateYokedWindows.h" +#include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" +#include "WuQMacroManager.h" +#include "WuQtUtilities.h" +#include "WuQWidgetObjectGroup.h" + +using namespace caret; + + + +/** + * \class caret::ClippingPlanesWidget + * \brief Dialog for adjusting clipping planes transformation. + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +ClippingPlanesWidget::ClippingPlanesWidget(const QString& objectNamePrefix, + QWidget* parent) +: QWidget(parent) +{ + m_objectNamePrefix = (objectNamePrefix + + ":ClippingPlanesWidget"); + setObjectName(m_objectNamePrefix); + + /*------------------------------------------------------------------------*/ + /* + * Create widgets + */ + + QWidget* clippingBoxWidget = createClippingBoxWidget(); + QWidget* clippingAxesWidget = createClippingAxesWidget(); // xyz plane selected + QWidget* clippingDataTypesWidget = createClippingDataTypeWidget(); // surface/volume/features + + /*------------------------------------------------------------------------*/ + QHBoxLayout* boxLayout = new QHBoxLayout(); + boxLayout->addWidget(clippingAxesWidget); + boxLayout->addStretch(); + boxLayout->addWidget(clippingDataTypesWidget); + + /* + * Finish up + */ + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 4); + layout->addLayout(boxLayout); + layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); + layout->addWidget(clippingBoxWidget); + + /* + * Remove apply button by using an empty name + */ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +ClippingPlanesWidget::~ClippingPlanesWidget() +{ +} + +/** + * Called when a user (added) push button is clicked. + * + * @param userPushButton + * Button that was clicked. + */ +void +ClippingPlanesWidget::resetButtonClicked() +{ + BrowserTabContent* btc = getBrowserTabContent(); + if (btc != NULL) { + btc->resetClippingPlaneTransformation(); + updateContent(btc->getTabNumber()); + updateGraphicsWindow(); + } +} + +/** + * Called when a clipping value is changed. + */ +void +ClippingPlanesWidget::clippingValueChanged() +{ + BrowserTabContent* browserTabContent = getBrowserTabContent(); + if (browserTabContent == NULL) { + return; + } + + const float panning[3] = { + (float)m_xPanDoubleSpinBox->value(), + (float)m_yPanDoubleSpinBox->value(), + (float)m_zPanDoubleSpinBox->value() + }; + + const float rotation[3] = { + (float)m_xRotateDoubleSpinBox->value(), + (float)m_yRotateDoubleSpinBox->value(), + (float)m_zRotateDoubleSpinBox->value() + }; + + const float thickness[3] ={ + (float)m_xThicknessDoubleSpinBox->value(), + (float)m_yThicknessDoubleSpinBox->value(), + (float)m_zThicknessDoubleSpinBox->value() + }; + + /* + * Update, set, and validate selected browser window + */ + browserTabContent->setClippingPlaneTransformation(panning, + rotation, + thickness, + m_displayClippingBoxCheckBox->isChecked()); + + browserTabContent->setClippingPlaneEnabled(m_xClippingEnabledCheckBox->isChecked(), + m_yClippingEnabledCheckBox->isChecked(), + m_zClippingEnabledCheckBox->isChecked(), + m_surfaceClippingEnabledCheckBox->isChecked(), + m_volumeClippingEnabledCheckBox->isChecked(), + m_featuresClippingEnabledCheckBox->isChecked()); + + if (browserTabContent->isBrainModelYoked()) { + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } + else { + updateGraphicsWindow(); + } +} + + +/** + * Update the selected graphics window. + */ +void +ClippingPlanesWidget::updateGraphicsWindow() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} + +/** + * @return Browser tab content for current tab index or NULL if invalid tab index + */ +BrowserTabContent* +ClippingPlanesWidget::getBrowserTabContent() +{ + EventBrowserTabGet tabEvent(m_tabIndex); + EventManager::get()->sendEvent(tabEvent.getPointer()); + BrowserTabContent* tabContent(tabEvent.getBrowserTab()); + return tabContent; +} + + +/** + * Update the content in the dialog + * @param browserWindowIndexIn + * Index of the browser window. + */ +void +ClippingPlanesWidget::updateContent(const int32_t tabIndex) +{ + m_tabIndex = tabIndex; + + BrowserTabContent* browserTabContent = getBrowserTabContent(); + if (browserTabContent == NULL) { + setEnabled(false); + return; + } + setEnabled(true); + + QSignalBlocker xPanBlocker(m_xPanDoubleSpinBox); + QSignalBlocker yPanBlocker(m_yPanDoubleSpinBox); + QSignalBlocker zPanBlocker(m_zPanDoubleSpinBox); + + QSignalBlocker xRotateBlocker(m_xRotateDoubleSpinBox); + QSignalBlocker yRotateBlocker(m_yRotateDoubleSpinBox); + QSignalBlocker zRotateBlocker(m_zRotateDoubleSpinBox); + + QSignalBlocker xThicknessBlocker(m_xThicknessDoubleSpinBox); + QSignalBlocker yThicknessBlocker(m_yThicknessDoubleSpinBox); + QSignalBlocker zThicknessBlocker(m_zThicknessDoubleSpinBox); + + float panning[3]; + float rotation[3]; + float thickness[3]; + bool displayClippingBox; + browserTabContent->getClippingPlaneTransformation(panning, + rotation, + thickness, + displayClippingBox); + + m_xPanDoubleSpinBox->setValue(panning[0]); + m_yPanDoubleSpinBox->setValue(panning[1]); + m_zPanDoubleSpinBox->setValue(panning[2]); + + m_xRotateDoubleSpinBox->setValue(rotation[0]); + m_yRotateDoubleSpinBox->setValue(rotation[1]); + m_zRotateDoubleSpinBox->setValue(rotation[2]); + + m_xThicknessDoubleSpinBox->setValue(thickness[0]); + m_yThicknessDoubleSpinBox->setValue(thickness[1]); + m_zThicknessDoubleSpinBox->setValue(thickness[2]); + + m_displayClippingBoxCheckBox->setChecked(displayClippingBox); + + bool xEnabled; + bool yEnabled; + bool zEnabled; + bool surfaceEnabled; + bool volumeEnabled; + bool featuresEnabled; + browserTabContent->getClippingPlaneEnabled(xEnabled, + yEnabled, + zEnabled, + surfaceEnabled, + volumeEnabled, + featuresEnabled); + + m_xClippingEnabledCheckBox->setChecked(xEnabled); + m_yClippingEnabledCheckBox->setChecked(yEnabled); + m_zClippingEnabledCheckBox->setChecked(zEnabled); + + m_surfaceClippingEnabledCheckBox->setChecked(surfaceEnabled); + m_volumeClippingEnabledCheckBox->setChecked(volumeEnabled); + m_featuresClippingEnabledCheckBox->setChecked(featuresEnabled); +} + +/** + * @return Instance of the clipping box widget + */ +QWidget* +ClippingPlanesWidget::createClippingBoxWidget() +{ + /* + * Show clipping box checkbox + */ + m_displayClippingBoxCheckBox = new QCheckBox("Show Clipping Box Outline"); + QObject::connect(m_displayClippingBoxCheckBox, SIGNAL(clicked(bool)), + this, SLOT(clippingValueChanged())); + + /* + * X, Y, Z column labels + */ + QLabel* xColumnLabel = new QLabel("X"); + QLabel* yColumnLabel = new QLabel("Y"); + QLabel* zColumnLabel = new QLabel("Z"); + + const int spinBoxWidth = 90; + /* + * Panning + */ + const double panStep = 1.0; + QLabel* panLabel = new QLabel("Pan:"); + m_xPanDoubleSpinBox = new QDoubleSpinBox; + m_xPanDoubleSpinBox->setMinimum(-100000.0); + m_xPanDoubleSpinBox->setMaximum( 100000.0); + m_xPanDoubleSpinBox->setSingleStep(panStep); + m_xPanDoubleSpinBox->setDecimals(2); + m_xPanDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_xPanDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + m_yPanDoubleSpinBox = new QDoubleSpinBox; + m_yPanDoubleSpinBox->setMinimum(-100000.0); + m_yPanDoubleSpinBox->setMaximum( 100000.0); + m_yPanDoubleSpinBox->setSingleStep(panStep); + m_yPanDoubleSpinBox->setDecimals(2); + m_yPanDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_yPanDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + m_zPanDoubleSpinBox = new QDoubleSpinBox; + m_zPanDoubleSpinBox->setMinimum(-100000.0); + m_zPanDoubleSpinBox->setMaximum( 100000.0); + m_zPanDoubleSpinBox->setSingleStep(panStep); + m_zPanDoubleSpinBox->setDecimals(2); + m_zPanDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_zPanDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + + + /* + * Rotation + */ + const double rotationMinimum = -360.0; + const double rotationMaximum = 360.0; + const double rotateStep = 1.0; + QLabel* rotateLabel = new QLabel("Rotate: "); + m_xRotateDoubleSpinBox = new QDoubleSpinBox; + m_xRotateDoubleSpinBox->setWrapping(true); + m_xRotateDoubleSpinBox->setMinimum(rotationMinimum); + m_xRotateDoubleSpinBox->setMaximum(rotationMaximum); + m_xRotateDoubleSpinBox->setSingleStep(rotateStep); + m_xRotateDoubleSpinBox->setDecimals(2); + m_xRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_xRotateDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + m_yRotateDoubleSpinBox = new QDoubleSpinBox; + m_yRotateDoubleSpinBox->setWrapping(true); + m_yRotateDoubleSpinBox->setMinimum(rotationMinimum); + m_yRotateDoubleSpinBox->setMaximum(rotationMaximum); + m_yRotateDoubleSpinBox->setSingleStep(rotateStep); + m_yRotateDoubleSpinBox->setDecimals(2); + m_yRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_yRotateDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + m_zRotateDoubleSpinBox = new QDoubleSpinBox; + m_zRotateDoubleSpinBox->setWrapping(true); + m_zRotateDoubleSpinBox->setMinimum(rotationMinimum); + m_zRotateDoubleSpinBox->setMaximum(rotationMaximum); + m_zRotateDoubleSpinBox->setSingleStep(rotateStep); + m_zRotateDoubleSpinBox->setDecimals(2); + m_zRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_zRotateDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + + /* + * Thickness + */ + const double thicknessMinimum = 0.0; + const double thicknessMaximum = 1000000.0; + const double thicknessStep = 1.0; + QLabel* thicknessLabel = new QLabel("Thickness (mm)"); + + m_xThicknessDoubleSpinBox = new QDoubleSpinBox(); + m_xThicknessDoubleSpinBox->setMinimum(thicknessMinimum); + m_xThicknessDoubleSpinBox->setMaximum(thicknessMaximum); + m_xThicknessDoubleSpinBox->setSingleStep(thicknessStep); + m_xThicknessDoubleSpinBox->setDecimals(2); + m_xThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_xThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + m_yThicknessDoubleSpinBox = new QDoubleSpinBox(); + m_yThicknessDoubleSpinBox->setMinimum(thicknessMinimum); + m_yThicknessDoubleSpinBox->setMaximum(thicknessMaximum); + m_yThicknessDoubleSpinBox->setSingleStep(thicknessStep); + m_yThicknessDoubleSpinBox->setDecimals(2); + m_yThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_yThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + m_zThicknessDoubleSpinBox = new QDoubleSpinBox(); + m_zThicknessDoubleSpinBox->setMinimum(thicknessMinimum); + m_zThicknessDoubleSpinBox->setMaximum(thicknessMaximum); + m_zThicknessDoubleSpinBox->setSingleStep(thicknessStep); + m_zThicknessDoubleSpinBox->setDecimals(2); + m_zThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_zThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(clippingValueChanged())); + + QToolButton* resetToolButton = new QToolButton(); + WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); + resetToolButton->setText("Reset"); + QObject::connect(resetToolButton, &QToolButton::clicked, + this, &ClippingPlanesWidget::resetButtonClicked); + + /*------------------------------------------------------------------------*/ + /* + * Layout widgets + */ + /* + * Columns for grid layout + */ + int column = 0; + const int COLUMN_LABEL = column++; + const int COLUMN_X = column++; + const int COLUMN_Y = column++; + const int COLUMN_Z = column++; + + QGroupBox* groupBox = new QGroupBox("Clipping Box"); + QGridLayout* gridLayout = new QGridLayout(groupBox); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 4); + int row = 0; + gridLayout->addWidget(xColumnLabel, + row, + COLUMN_X, + Qt::AlignHCenter); + gridLayout->addWidget(yColumnLabel, + row, + COLUMN_Y, + Qt::AlignHCenter); + gridLayout->addWidget(zColumnLabel, + row, + COLUMN_Z, + Qt::AlignHCenter); + row++; + gridLayout->addWidget(panLabel, + row, + COLUMN_LABEL); + gridLayout->addWidget(m_xPanDoubleSpinBox, + row, + COLUMN_X); + gridLayout->addWidget(m_yPanDoubleSpinBox, + row, + COLUMN_Y); + gridLayout->addWidget(m_zPanDoubleSpinBox, + row, + COLUMN_Z); + row++; + + gridLayout->addWidget(rotateLabel, + row, + COLUMN_LABEL); + gridLayout->addWidget(m_xRotateDoubleSpinBox, + row, + COLUMN_X); + gridLayout->addWidget(m_yRotateDoubleSpinBox, + row, + COLUMN_Y); + gridLayout->addWidget(m_zRotateDoubleSpinBox, + row, + COLUMN_Z); + row++; + + gridLayout->addWidget(thicknessLabel, + row, + COLUMN_LABEL); + gridLayout->addWidget(m_xThicknessDoubleSpinBox, + row, + COLUMN_X); + gridLayout->addWidget(m_yThicknessDoubleSpinBox, + row, + COLUMN_Y); + gridLayout->addWidget(m_zThicknessDoubleSpinBox, + row, + COLUMN_Z); + + row++; + gridLayout->addWidget(m_displayClippingBoxCheckBox, + row, 0, 1, 2, Qt::AlignLeft); + gridLayout->addWidget(resetToolButton, + row, 2, 1, 2, Qt::AlignCenter); + row++; + + return groupBox; +} + +/** + * @return Instance of the clipping axes widget + */ +QWidget* +ClippingPlanesWidget::createClippingAxesWidget() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + + m_xClippingEnabledCheckBox = new QCheckBox("X"); + QObject::connect(m_xClippingEnabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(clippingValueChanged())); + m_xClippingEnabledCheckBox->setToolTip("Enable X clipping plane"); + m_xClippingEnabledCheckBox->setObjectName(m_objectNamePrefix + + ":EnableX"); + macroManager->addMacroSupportToObject(m_xClippingEnabledCheckBox, + "Enable X clipping plane"); + + m_yClippingEnabledCheckBox = new QCheckBox("Y"); + QObject::connect(m_yClippingEnabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(clippingValueChanged())); + m_yClippingEnabledCheckBox->setToolTip("Enable Y clipping plane"); + m_yClippingEnabledCheckBox->setObjectName(m_objectNamePrefix + + ":EnableY"); + macroManager->addMacroSupportToObject(m_yClippingEnabledCheckBox, + "Enable Y clipping plane"); + + m_zClippingEnabledCheckBox = new QCheckBox("Z"); + QObject::connect(m_zClippingEnabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(clippingValueChanged())); + m_zClippingEnabledCheckBox->setToolTip("Enable Z clipping plane"); + m_zClippingEnabledCheckBox->setObjectName(m_objectNamePrefix + + ":EnableZ"); + macroManager->addMacroSupportToObject(m_zClippingEnabledCheckBox, + "Enable Z clipping"); + + QWidget* groupBox = new QGroupBox("Enable Clipping Planes"); + QHBoxLayout* layout = new QHBoxLayout(groupBox); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 4); + layout->addWidget(m_xClippingEnabledCheckBox); + layout->addWidget(m_yClippingEnabledCheckBox); + layout->addWidget(m_zClippingEnabledCheckBox); + + return groupBox; +} + +/** + * @return Instance of the clipping data type widget + */ +QWidget* +ClippingPlanesWidget::createClippingDataTypeWidget() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + + m_surfaceClippingEnabledCheckBox = new QCheckBox("Surface"); + QObject::connect(m_surfaceClippingEnabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(clippingValueChanged())); + m_surfaceClippingEnabledCheckBox->setToolTip("Enable Clipping of Surface"); + m_surfaceClippingEnabledCheckBox->setObjectName(m_objectNamePrefix + + ":EnableSurface"); + macroManager->addMacroSupportToObject(m_surfaceClippingEnabledCheckBox, + "Enable surface clipping"); + + m_volumeClippingEnabledCheckBox = new QCheckBox("Volume"); + QObject::connect(m_volumeClippingEnabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(clippingValueChanged())); + m_volumeClippingEnabledCheckBox->setToolTip("Enable Clipping of Volume Slices"); + m_volumeClippingEnabledCheckBox->setObjectName(m_objectNamePrefix + + ":EnableVolume"); + macroManager->addMacroSupportToObject(m_volumeClippingEnabledCheckBox, + "Enable volume clipping"); + + m_featuresClippingEnabledCheckBox = new QCheckBox("Features"); + QObject::connect(m_featuresClippingEnabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(clippingValueChanged())); + m_featuresClippingEnabledCheckBox->setToolTip("Enable Clipping of Features"); + m_featuresClippingEnabledCheckBox->setObjectName(m_objectNamePrefix + + ":EnableFeatures"); + macroManager->addMacroSupportToObject(m_featuresClippingEnabledCheckBox, + "Enable features clipping"); + + QWidget* groupBox = new QGroupBox("Clipping Affects"); + QHBoxLayout* layout = new QHBoxLayout(groupBox); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 4); + layout->addWidget(m_surfaceClippingEnabledCheckBox); + layout->addWidget(m_volumeClippingEnabledCheckBox); + layout->addWidget(m_featuresClippingEnabledCheckBox); + + return groupBox; +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/ClippingPlanesWidget.h connectome-workbench-1.5.0/src/GuiQt/ClippingPlanesWidget.h --- connectome-workbench-1.4.2/src/GuiQt/ClippingPlanesWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ClippingPlanesWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,114 @@ +#ifndef __CLIPPING_PLANES_WIDGET_H__ +#define __CLIPPING_PLANES_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "EventListenerInterface.h" + +class QCheckBox; +class QDoubleSpinBox; + +namespace caret { + class BrowserTabContent; + class WuQWidgetObjectGroup; + + class ClippingPlanesWidget : public QWidget { + + Q_OBJECT + + public: + ClippingPlanesWidget(const QString& objectNamePrefix, + QWidget* parent = 0); + + virtual ~ClippingPlanesWidget(); + + void updateContent(const int32_t tabIndex); + + // ADD_NEW_METHODS_HERE + private slots: + void clippingValueChanged(); + + void resetButtonClicked(); + + private: + void updateGraphicsWindow(); + + ClippingPlanesWidget(const ClippingPlanesWidget&); + + ClippingPlanesWidget& operator=(const ClippingPlanesWidget&); + + QWidget* createClippingBoxWidget(); + + QWidget* createClippingAxesWidget(); + + QWidget* createClippingDataTypeWidget(); + + BrowserTabContent* getBrowserTabContent(); + + int32_t m_tabIndex = -1; + + QDoubleSpinBox* m_xPanDoubleSpinBox; + + QDoubleSpinBox* m_yPanDoubleSpinBox; + + QDoubleSpinBox* m_zPanDoubleSpinBox; + + QDoubleSpinBox* m_xRotateDoubleSpinBox; + + QDoubleSpinBox* m_yRotateDoubleSpinBox; + + QDoubleSpinBox* m_zRotateDoubleSpinBox; + + QDoubleSpinBox* m_xThicknessDoubleSpinBox; + + QDoubleSpinBox* m_yThicknessDoubleSpinBox; + + QDoubleSpinBox* m_zThicknessDoubleSpinBox; + + QCheckBox* m_displayClippingBoxCheckBox; + + QCheckBox* m_xClippingEnabledCheckBox; + + QCheckBox* m_yClippingEnabledCheckBox; + + QCheckBox* m_zClippingEnabledCheckBox; + + QCheckBox* m_surfaceClippingEnabledCheckBox; + + QCheckBox* m_volumeClippingEnabledCheckBox; + + QCheckBox* m_featuresClippingEnabledCheckBox; + + QString m_objectNamePrefix; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CLIPPING_PLANES_WIDGET_DECLARE__ + // +#endif // __CLIPPING_PLANES_WIDGET_DECLARE__ + +} // namespace +#endif //__CLIPPING_PLANES_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/CMakeLists.txt connectome-workbench-1.5.0/src/GuiQt/CMakeLists.txt --- connectome-workbench-1.4.2/src/GuiQt/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -44,11 +44,14 @@ SET(MOC_INPUT_HEADER_FILES AboutWorkbenchDialog.h + AnnotationBackgroundTypeWidget.h + AnnotationBoundsWidget.h AnnotationChangeCoordinateDialog.h AnnotationColorWidget.h + AnnotationCoordinateCenterXYWidget.h AnnotationCoordinateSelectionWidget.h AnnotationCoordinateSpaceWidget.h - AnnotationCoordinateWidget.h + AnnotationCoordinatesWidget.h AnnotationCreateDialog.h AnnotationDeleteWidget.h AnnotationFontWidget.h @@ -57,10 +60,11 @@ AnnotationLineArrowTipsWidget.h AnnotationMenuArrange.h AnnotationMenuFileSelection.h + AnnotationNameWidget.h + AnnotationPasteDialog.h AnnotationRedoUndoWidget.h AnnotationRotationWidget.h AnnotationSelectionViewController.h - AnnotationPasteDialog.h AnnotationTextAlignmentWidget.h AnnotationTextEditorDialog.h AnnotationTextEditorWidget.h @@ -78,26 +82,30 @@ BrainBrowserWindowComboBox.h BrainBrowserWindowOrientedToolBox.h BrainBrowserWindowToolBar.h + BrainBrowserWindowToolBarAllSurface.h BrainBrowserWindowToolBarChartAttributes.h BrainBrowserWindowToolBarChartAxes.h BrainBrowserWindowToolBarChartTwoAttributes.h - BrainBrowserWindowToolBarChartTwoAxes.h BrainBrowserWindowToolBarChartTwoOrientation.h - BrainBrowserWindowToolBarChartTwoTitle.h + BrainBrowserWindowToolBarChartTwoOrientedAxes.h BrainBrowserWindowToolBarChartTwoType.h BrainBrowserWindowToolBarChartType.h - BrainBrowserWindowToolBarClipping.h BrainBrowserWindowToolBarComponent.h + BrainBrowserWindowToolBarOrientation.h BrainBrowserWindowToolBarSlicePlane.h BrainBrowserWindowToolBarSliceSelection.h + BrainBrowserWindowToolBarSurface.h BrainBrowserWindowToolBarSurfaceMontage.h BrainBrowserWindowToolBarTab.h BrainBrowserWindowToolBarTabPopUpMenu.h + BrainBrowserWindowToolBarView.h BrainBrowserWindowToolBarVolumeMontage.h BrainOpenGLWidget.h BugReportDialog.h + CardinalDirectionEnumMenu.h CaretColorEnumComboBox.h CaretColorEnumMenu.h + CaretColorToolButton.h CaretDataFileSelectionComboBox.h CaretFileDialog.h CaretFileDialogExtendable.h @@ -108,19 +116,25 @@ ChartLinesSelectionViewController.h ChartMatrixParcelSelectionViewController.h ChartMatrixSeriesSelectionViewController.h + ChartTwoAxisPropertiesEditorDialog.h + ChartTwoAxisPropertiesEditorWidget.h + ChartTwoCartesianCustomSubdivisionsEditorWidget.h + ChartTwoLineLayerNormalizationWidget.h ChartTwoOverlaySetViewController.h ChartTwoOverlayViewController.h + ChartTwoTitleEditorWidget.h ChartSelectionViewController.h ChartToolBoxViewController.h CiftiConnectivityMatrixViewController.h CiftiParcelSelectionComboBox.h - ClippingPlanesDialog.h + ClippingPlanesWidget.h ColorEditorWidget.h CopyPaletteColorMappingToFilesDialog.h CustomViewDialog.h DataFileContentCopyMoveDialog.h DisplayGroupAndTabItemViewController.h DisplayGroupEnumComboBox.h + DynConnViewController.h EnumComboBoxTemplate.h FiberOrientationSelectionViewController.h FiberSamplesOpenGLWidget.h @@ -134,6 +148,10 @@ GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h + IdentificationDisplayDialog.h + IdentificationDisplayWidget.h + IdentificationFileFilteringRow.h + IdentificationFileFilteringTableWidget.h IdentifyBrainordinateDialog.h ImageCaptureDialog.h ImageFileConvertToVolumeFileDialog.h @@ -144,7 +162,6 @@ LockAspectWarningDialog.h MacApplication.h MacDockMenu.h - MacDuplicateMenuBar.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -154,6 +171,8 @@ MapSettingsPaletteColorMappingWidget.h MapSettingsParcelsWidget.h MapYokingGroupComboBox.h + MediaOverlayViewController.h + MediaOverlaySetViewController.h MetaDataEditorDialog.h MetaDataEditorWidget.h MovieDialog.h @@ -163,13 +182,22 @@ OverlaySettingsEditorDialog.h OverlayViewController.h PaletteColorMappingEditorDialog.h + PaletteCreateNewDialog.h + PaletteEditorDialog.h + PaletteEditorRangeRow.h + PaletteEditorRangeWidget.h + PaletteSelectionWidget.h PlotMagnifier.h PlotPanner.h PreferencesDialog.h + PreferencesRecentFilesWidget.h ProgressReportingDialog.h ProgressReportingFromEvent.h ProgressReportingWithSlots.h + RecentFilesDialog.h + RecentFilesTableWidget.h RegionOfInterestCreateFromBorderDialog.h + ScaleBarWidget.h SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h @@ -179,21 +207,23 @@ SceneReplaceAllDialog.h SceneShowOptionsDialog.h SpecFileManagementDialog.h - SplashScreen.h StructureEnumComboBox.h StructureSurfaceSelectionControl.h SurfacePropertiesEditorDialog.h SurfaceSelectionViewController.h ThresholdingSetMapsDialog.h + TileTabGridRowColumnWidgets.h TileTabsConfigurationDialog.h + TileTabsManualTabGeometryWidget.h UserInputModeAnnotationsContextMenu.h UserInputModeAnnotationsWidget.h UserInputModeBordersWidget.h UserInputModeFociWidget.h UserInputModeImageWidget.h + UserInputModeTileTabsManualLayoutContextMenu.h UserInputModeViewContextMenu.h + UserInputModeViewContextTileTabsSubMenu.h UserInputModeVolumeEditWidget.h - UserInputTileTabsContextMenu.h UsernamePasswordWidget.h VolumeFileCreateDialog.h VolumePropertiesEditorDialog.h @@ -203,6 +233,7 @@ WbMacroHelper.h WbMacroWidgetActionsManager.h WuQCollapsibleWidget.h + WuQColorEditorWidget.h WuQDataEntryDialog.h WuQDialog.h WuQDialogModal.h @@ -230,6 +261,7 @@ WuQMacroWidgetAction.h WuQMessageBox.h WuQwtPlot.h + WuQScrollArea.h WuQSpecialIncrementDoubleSpinBox.h WuQSpinBox.h WuQSpinBoxGroup.h @@ -239,8 +271,10 @@ WuQTabWidgetWithSizeHint.h WuQTextEditorDialog.h WuQTimedMessageDisplay.h + WuQToolTipHelper.h WuQTreeWidget.h WuQTrueFalseComboBox.h + WuQValueChangedSignalWatcher.h #WuQWebView.h WuQWidget.h WuQWidgetObjectGroup.h @@ -262,12 +296,15 @@ # SET(SOURCE_FILES AboutWorkbenchDialog.h +AnnotationBackgroundTypeWidget.h +AnnotationBoundsWidget.h AnnotationChangeCoordinateDialog.h AnnotationColorWidget.h +AnnotationCoordinateCenterXYWidget.h AnnotationCoordinateInformation.h AnnotationCoordinateSelectionWidget.h AnnotationCoordinateSpaceWidget.h -AnnotationCoordinateWidget.h +AnnotationCoordinatesWidget.h AnnotationCreateDialog.h AnnotationDeleteWidget.h AnnotationFontWidget.h @@ -276,6 +313,7 @@ AnnotationLineArrowTipsWidget.h AnnotationMenuArrange.h AnnotationMenuFileSelection.h +AnnotationNameWidget.h AnnotationPasteDialog.h AnnotationRedoUndoWidget.h AnnotationRotationWidget.h @@ -303,43 +341,53 @@ BrainBrowserWindowEditMenuItemEnum.h BrainBrowserWindowOrientedToolBox.h BrainBrowserWindowToolBar.h +BrainBrowserWindowToolBarAllSurface.h BrainBrowserWindowToolBarChartAttributes.h BrainBrowserWindowToolBarChartAxes.h BrainBrowserWindowToolBarChartTwoAttributes.h -BrainBrowserWindowToolBarChartTwoAxes.h BrainBrowserWindowToolBarChartTwoOrientation.h -BrainBrowserWindowToolBarChartTwoTitle.h +BrainBrowserWindowToolBarChartTwoOrientedAxes.h BrainBrowserWindowToolBarChartTwoType.h BrainBrowserWindowToolBarChartType.h -BrainBrowserWindowToolBarClipping.h BrainBrowserWindowToolBarComponent.h +BrainBrowserWindowToolBarOrientation.h BrainBrowserWindowToolBarSlicePlane.h BrainBrowserWindowToolBarSliceSelection.h +BrainBrowserWindowToolBarSurface.h BrainBrowserWindowToolBarSurfaceMontage.h BrainBrowserWindowToolBarTab.h BrainBrowserWindowToolBarTabPopUpMenu.h +BrainBrowserWindowToolBarView.h BrainBrowserWindowToolBarVolumeMontage.h BrainOpenGLWidget.h BugReportDialog.h +CardinalDirectionEnumMenu.h CaretColorEnumComboBox.h CaretColorEnumMenu.h +CaretColorToolButton.h CaretDataFileSelectionComboBox.h CaretFileDialog.h CaretFileDialogExtendable.h CaretFileRemoteDialog.h CaretMappableDataFileAndMapSelector.h CaretMappableDataFileAndMapSelectorObject.h +CaretResultDialog.h ChartHistoryViewController.h ChartLinesSelectionViewController.h ChartMatrixParcelSelectionViewController.h ChartMatrixSeriesSelectionViewController.h +ChartTwoAxisPropertiesEditorDialog.h +ChartTwoAxisPropertiesEditorWidget.h +ChartTwoCartesianCustomSubdivisionsEditorWidget.h +ChartTwoLineLayerNormalizationWidget.h ChartTwoOverlaySetViewController.h ChartTwoOverlayViewController.h +ChartTwoTitleEditorWidget.h ChartSelectionViewController.h ChartToolBoxViewController.h CiftiConnectivityMatrixViewController.h CiftiParcelSelectionComboBox.h -ClippingPlanesDialog.h +ClippingPlanesWidget.h ColorEditorWidget.h CopyPaletteColorMappingToFilesDialog.h CursorDisplayScoped.h @@ -350,9 +398,13 @@ DisplayGroupAndTabItemTreeWidgetItem.h DisplayGroupAndTabItemViewController.h DisplayGroupEnumComboBox.h +DynConnViewController.h EnumComboBoxTemplate.h EventAnnotationCreateNewType.h EventAnnotationGetDrawnInWindow.h +EventBrowserTabCloseInToolBar.h +EventBrowserTabDeleteInToolBar.h +EventBrowserTabNewInGUI.h EventBrowserWindowDrawingContent.h EventBrowserWindowCreateTabs.h EventBrowserWindowGraphicsRedrawn.h @@ -362,10 +414,10 @@ EventGraphicsTimingOneWindow.h EventGraphicsUpdateAllWindows.h EventGraphicsUpdateOneWindow.h +EventGraphicsWindowShowToolTip.h EventHelpViewerDisplay.h EventIdentificationRequest.h EventImageCapture.h -EventMacDockMenuUpdate.h EventMovieManualModeRecording.h EventOperatingSystemRequestOpenDataFile.h EventOverlaySettingsEditorDialogRequest.h @@ -381,6 +433,7 @@ FociPropertiesEditorDialog.h FociSelectionViewController.h GapsAndMarginsDialog.h +GestureEvent.h GiftiLabelTableEditor.h GiftiLabelTableSelectionComboBox.h GroupAndNameHierarchyTreeWidgetItem.h @@ -388,6 +441,10 @@ GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h +IdentificationDisplayDialog.h +IdentificationDisplayWidget.h +IdentificationFileFilteringRow.h +IdentificationFileFilteringTableWidget.h IdentifyBrainordinateDialog.h ImageCaptureDialog.h ImageFileConvertToVolumeFileDialog.h @@ -399,7 +456,6 @@ LockAspectWarningDialog.h MacApplication.h MacDockMenu.h -MacDuplicateMenuBar.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -409,6 +465,8 @@ MapSettingsPaletteColorMappingWidget.h MapSettingsParcelsWidget.h MapYokingGroupComboBox.h +MediaOverlayViewController.h +MediaOverlaySetViewController.h MetaDataEditorDialog.h MetaDataEditorWidget.h MouseEvent.h @@ -419,14 +477,24 @@ OverlaySettingsEditorDialog.h OverlayViewController.h PaletteColorMappingEditorDialog.h +PaletteCreateNewDialog.h +PaletteEditorDialog.h +PaletteEditorRangeRow.h +PaletteEditorRangeWidget.h +PalettePixmapPainter.h +PaletteSelectionWidget.h PlotMagnifier.h PlotPanner.h PreferencesDialog.h +PreferencesRecentFilesWidget.h ProgressReportingDialog.h ProgressReportingFromEvent.h ProgressReportingWithSlots.h QGLWidgetTextRenderer.h +RecentFilesDialog.h +RecentFilesTableWidget.h RegionOfInterestCreateFromBorderDialog.h +ScaleBarWidget.h SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h @@ -438,14 +506,15 @@ SceneShowOptionsDialog.h SceneWindowGeometry.h SpecFileManagementDialog.h -SplashScreen.h StructureEnumComboBox.h StructureSurfaceSelectionControl.h SurfacePropertiesEditorDialog.h SurfaceSelectionViewController.h ThresholdingSetMapsDialog.h +TileTabGridRowColumnWidgets.h TileTabsConfigurationDialog.h -TileTabsConfigurationModifier.h +TileTabsGridConfigurationModifier.h +TileTabsManualTabGeometryWidget.h UserInputModeAbstract.h UserInputModeAnnotations.h UserInputModeAnnotationsContextMenu.h @@ -456,11 +525,13 @@ UserInputModeFoci.h UserInputModeImageWidget.h UserInputModeImage.h +UserInputModeTileTabsManualLayout.h +UserInputModeTileTabsManualLayoutContextMenu.h UserInputModeView.h UserInputModeViewContextMenu.h +UserInputModeViewContextTileTabsSubMenu.h UserInputModeVolumeEdit.h UserInputModeVolumeEditWidget.h -UserInputTileTabsContextMenu.h UsernamePasswordWidget.h ViewModeEnum.h VolumeFileCreateDialog.h @@ -480,11 +551,13 @@ WbMacroCustomOperationIncrementRotation.h WbMacroCustomOperationIncrementVolumeSlice.h WbMacroCustomOperationManager.h +WbMacroCustomOperationSurfaceDefaultColor.h WbMacroCustomOperationTypeEnum.h WbMacroHelper.h WbMacroWidgetActionNames.h WbMacroWidgetActionsManager.h WuQCollapsibleWidget.h +WuQColorEditorWidget.h WuQDataEntryDialog.h WuQDialog.h WuQDialogModal.h @@ -516,6 +589,7 @@ WuQMacroWidgetAction.h WuQMessageBox.h WuQwtPlot.h +WuQScrollArea.h WuQSpecialIncrementDoubleSpinBox.h WuQSpinBox.h WuQSpinBoxGroup.h @@ -525,8 +599,10 @@ WuQTabWidgetWithSizeHint.h WuQTextEditorDialog.h WuQTimedMessageDisplay.h +WuQToolTipHelper.h WuQTreeWidget.h WuQTrueFalseComboBox.h +WuQValueChangedSignalWatcher.h #WuQWebView.h WuQWidget.h WuQWidgetDisabler.h @@ -535,12 +611,15 @@ ZipSceneFileDialog.h AboutWorkbenchDialog.cxx +AnnotationBackgroundTypeWidget.cxx +AnnotationBoundsWidget.cxx AnnotationChangeCoordinateDialog.cxx AnnotationColorWidget.cxx +AnnotationCoordinateCenterXYWidget.cxx AnnotationCoordinateInformation.cxx AnnotationCoordinateSelectionWidget.cxx AnnotationCoordinateSpaceWidget.cxx -AnnotationCoordinateWidget.cxx +AnnotationCoordinatesWidget.cxx AnnotationCreateDialog.cxx AnnotationDeleteWidget.cxx AnnotationFontWidget.cxx @@ -549,6 +628,7 @@ AnnotationLineArrowTipsWidget.cxx AnnotationMenuArrange.cxx AnnotationMenuFileSelection.cxx +AnnotationNameWidget.cxx AnnotationPasteDialog.cxx AnnotationRedoUndoWidget.cxx AnnotationRotationWidget.cxx @@ -576,43 +656,53 @@ BrainBrowserWindowEditMenuItemEnum.cxx BrainBrowserWindowOrientedToolBox.cxx BrainBrowserWindowToolBar.cxx +BrainBrowserWindowToolBarAllSurface.cxx BrainBrowserWindowToolBarChartAttributes.cxx BrainBrowserWindowToolBarChartAxes.cxx BrainBrowserWindowToolBarChartTwoAttributes.cxx -BrainBrowserWindowToolBarChartTwoAxes.cxx BrainBrowserWindowToolBarChartTwoOrientation.cxx -BrainBrowserWindowToolBarChartTwoTitle.cxx +BrainBrowserWindowToolBarChartTwoOrientedAxes.cxx BrainBrowserWindowToolBarChartTwoType.cxx BrainBrowserWindowToolBarChartType.cxx -BrainBrowserWindowToolBarClipping.cxx BrainBrowserWindowToolBarComponent.cxx +BrainBrowserWindowToolBarOrientation.cxx BrainBrowserWindowToolBarSlicePlane.cxx BrainBrowserWindowToolBarSliceSelection.cxx +BrainBrowserWindowToolBarSurface.cxx BrainBrowserWindowToolBarSurfaceMontage.cxx BrainBrowserWindowToolBarTab.cxx BrainBrowserWindowToolBarTabPopUpMenu.cxx +BrainBrowserWindowToolBarView.cxx BrainBrowserWindowToolBarVolumeMontage.cxx BrainOpenGLWidget.cxx BugReportDialog.cxx +CardinalDirectionEnumMenu.cxx CaretColorEnumComboBox.cxx CaretColorEnumMenu.cxx +CaretColorToolButton.cxx CaretDataFileSelectionComboBox.cxx CaretFileDialog.cxx CaretFileDialogExtendable.cxx CaretFileRemoteDialog.cxx CaretMappableDataFileAndMapSelector.cxx CaretMappableDataFileAndMapSelectorObject.cxx +CaretResultDialog.cxx ChartHistoryViewController.cxx ChartLinesSelectionViewController.cxx ChartMatrixParcelSelectionViewController.cxx ChartMatrixSeriesSelectionViewController.cxx +ChartTwoAxisPropertiesEditorDialog.cxx +ChartTwoAxisPropertiesEditorWidget.cxx +ChartTwoCartesianCustomSubdivisionsEditorWidget.cxx +ChartTwoLineLayerNormalizationWidget.cxx ChartTwoOverlaySetViewController.cxx ChartTwoOverlayViewController.cxx +ChartTwoTitleEditorWidget.cxx ChartSelectionViewController.cxx ChartToolBoxViewController.cxx CiftiConnectivityMatrixViewController.cxx CiftiParcelSelectionComboBox.cxx -ClippingPlanesDialog.cxx +ClippingPlanesWidget.cxx ColorEditorWidget.cxx CopyPaletteColorMappingToFilesDialog.cxx CursorDisplayScoped.cxx @@ -623,6 +713,10 @@ DisplayGroupAndTabItemTreeWidgetItem.cxx DisplayGroupAndTabItemViewController.cxx DisplayGroupEnumComboBox.cxx +DynConnViewController.cxx +EventBrowserTabCloseInToolBar.cxx +EventBrowserTabDeleteInToolBar.cxx +EventBrowserTabNewInGUI.cxx EventAnnotationCreateNewType.cxx EventAnnotationGetDrawnInWindow.cxx EventBrowserWindowDrawingContent.cxx @@ -634,10 +728,10 @@ EventGraphicsTimingOneWindow.cxx EventGraphicsUpdateAllWindows.cxx EventGraphicsUpdateOneWindow.cxx +EventGraphicsWindowShowToolTip.cxx EventHelpViewerDisplay.cxx EventIdentificationRequest.cxx EventImageCapture.cxx -EventMacDockMenuUpdate.cxx EventMovieManualModeRecording.cxx EventOperatingSystemRequestOpenDataFile.cxx EventOverlaySettingsEditorDialogRequest.cxx @@ -653,6 +747,7 @@ FociPropertiesEditorDialog.cxx FociSelectionViewController.cxx GapsAndMarginsDialog.cxx +GestureEvent.cxx GiftiLabelTableEditor.cxx GiftiLabelTableSelectionComboBox.cxx GroupAndNameHierarchyTreeWidgetItem.cxx @@ -660,6 +755,10 @@ GuiManager.cxx HelpViewerDialog.cxx HyperLinkTextBrowser.cxx +IdentificationDisplayDialog.cxx +IdentificationDisplayWidget.cxx +IdentificationFileFilteringRow.cxx +IdentificationFileFilteringTableWidget.cxx IdentifyBrainordinateDialog.cxx ImageCaptureDialog.cxx ImageFileConvertToVolumeFileDialog.cxx @@ -671,7 +770,6 @@ LockAspectWarningDialog.cxx MacApplication.cxx MacDockMenu.cxx -MacDuplicateMenuBar.cxx MapSettingsChartTwoLineHistoryWidget.cxx MapSettingsColorBarPaletteOptionsWidget.cxx MapSettingsColorBarWidget.cxx @@ -681,6 +779,8 @@ MapSettingsPaletteColorMappingWidget.cxx MapSettingsParcelsWidget.cxx MapYokingGroupComboBox.cxx +MediaOverlayViewController.cxx +MediaOverlaySetViewController.cxx MetaDataEditorDialog.cxx MetaDataEditorWidget.cxx MouseEvent.cxx @@ -691,14 +791,24 @@ OverlayViewController.cxx OverlaySettingsEditorDialog.cxx PaletteColorMappingEditorDialog.cxx +PaletteCreateNewDialog.cxx +PaletteEditorDialog.cxx +PaletteEditorRangeRow.cxx +PaletteEditorRangeWidget.cxx +PalettePixmapPainter.cxx +PaletteSelectionWidget.cxx PlotMagnifier.cxx PlotPanner.cxx PreferencesDialog.cxx +PreferencesRecentFilesWidget.cxx ProgressReportingDialog.cxx ProgressReportingFromEvent.cxx ProgressReportingWithSlots.cxx QGLWidgetTextRenderer.cxx +RecentFilesDialog.cxx +RecentFilesTableWidget.cxx RegionOfInterestCreateFromBorderDialog.cxx +ScaleBarWidget.cxx SceneBasePathWidget.cxx SceneCreateReplaceDialog.cxx SceneDialog.cxx @@ -710,14 +820,15 @@ SceneShowOptionsDialog.cxx SceneWindowGeometry.cxx SpecFileManagementDialog.cxx -SplashScreen.cxx StructureEnumComboBox.cxx StructureSurfaceSelectionControl.cxx SurfacePropertiesEditorDialog.cxx SurfaceSelectionViewController.cxx ThresholdingSetMapsDialog.cxx +TileTabGridRowColumnWidgets.cxx TileTabsConfigurationDialog.cxx -TileTabsConfigurationModifier.cxx +TileTabsGridConfigurationModifier.cxx +TileTabsManualTabGeometryWidget.cxx UserInputModeAbstract.cxx UserInputModeAnnotations.cxx UserInputModeAnnotationsContextMenu.cxx @@ -728,11 +839,13 @@ UserInputModeFociWidget.cxx UserInputModeImage.cxx UserInputModeImageWidget.cxx +UserInputModeTileTabsManualLayout.cxx +UserInputModeTileTabsManualLayoutContextMenu.cxx UserInputModeView.cxx UserInputModeViewContextMenu.cxx +UserInputModeViewContextTileTabsSubMenu.cxx UserInputModeVolumeEdit.cxx UserInputModeVolumeEditWidget.cxx -UserInputTileTabsContextMenu.cxx UsernamePasswordWidget.cxx ViewModeEnum.cxx VolumeFileCreateDialog.cxx @@ -752,10 +865,12 @@ WbMacroCustomOperationIncrementRotation.cxx WbMacroCustomOperationIncrementVolumeSlice.cxx WbMacroCustomOperationManager.cxx +WbMacroCustomOperationSurfaceDefaultColor.cxx WbMacroCustomOperationTypeEnum.cxx WbMacroHelper.cxx WbMacroWidgetActionsManager.cxx WuQCollapsibleWidget.cxx +WuQColorEditorWidget.cxx WuQDataEntryDialog.cxx WuQDialog.cxx WuQDialogModal.cxx @@ -784,6 +899,7 @@ WuQMacroWidgetAction.cxx WuQMessageBox.cxx WuQwtPlot.cxx +WuQScrollArea.cxx WuQSpecialIncrementDoubleSpinBox.cxx WuQSpinBox.cxx WuQSpinBoxGroup.cxx @@ -793,8 +909,10 @@ WuQTabWidgetWithSizeHint.cxx WuQTextEditorDialog.cxx WuQTimedMessageDisplay.cxx +WuQToolTipHelper.cxx WuQTreeWidget.cxx WuQTrueFalseComboBox.cxx +WuQValueChangedSignalWatcher.cxx #WuQWebView.cxx WuQWidget.cxx WuQWidgetDisabler.cxx diff -Nru connectome-workbench-1.4.2/src/GuiQt/CustomViewDialog.cxx connectome-workbench-1.5.0/src/GuiQt/CustomViewDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/CustomViewDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CustomViewDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -317,7 +317,7 @@ void CustomViewDialog::moveTransformToCustomView(ModelTransform& modelTransform) { - double panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, zoom, rightFlatX, rightFlatY, rightFlatZoom; + double panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, flatRotate, zoom, rightFlatX, rightFlatY, rightFlatZoom; getTransformationControlValues(panX, panY, panZ, @@ -327,6 +327,7 @@ obRotX, obRotY, obRotZ, + flatRotate, zoom, rightFlatX, rightFlatY, @@ -344,12 +345,16 @@ float obliqueRotationMatrixArray[4][4]; obliqueRotationMatrix.getMatrix(obliqueRotationMatrixArray); - + Matrix4x4 flatRotationMatrix; + flatRotationMatrix.setRotation(0.0, 0.00, flatRotate); + float flatRotationMatrixArray[4][4]; + flatRotationMatrix.getMatrix(flatRotationMatrixArray); modelTransform.setPanningRotationMatrixAndZoom(panX, panY, panZ, rotationMatrixArray, obliqueRotationMatrixArray, + flatRotationMatrixArray, zoom, rightFlatX, rightFlatY, @@ -370,13 +375,14 @@ ModelTransform modelTransform; if (prefs->getCustomView(customViewName, modelTransform)) { float panX, panY, panZ, rotationMatrixArray[4][4], - obliqueRotationMatrixArray[4][4], zoom, + obliqueRotationMatrixArray[4][4], flatRotationMatrixArray[4][4], zoom, rightFlatX, rightFlatY, rightFlatZoom; modelTransform.getPanningRotationMatrixAndZoom(panX, panY, panZ, rotationMatrixArray, obliqueRotationMatrixArray, + flatRotationMatrixArray, zoom, rightFlatX, rightFlatY, @@ -394,9 +400,14 @@ double obRotX, obRotY, obRotZ; obliqueRotationMatrix.getRotation(obRotX, obRotY, obRotZ); + Matrix4x4 flatRotationMatrix; + flatRotationMatrix.setMatrix(flatRotationMatrixArray); + double flatRotX, flatRotY, flatRotZ; + flatRotationMatrix.getRotation(flatRotX, flatRotY, flatRotZ); + setTransformationControlValues(panX, panY, panZ, rotX, rotY, rotZ, - obRotX, obRotY, obRotZ, zoom, + obRotX, obRotY, obRotZ, flatRotZ, zoom, rightFlatX, rightFlatY, rightFlatZoom); transformValueChanged(); @@ -520,6 +531,20 @@ this, SLOT(transformValueChanged())); /* + * Flat rotation + */ + QLabel* flatRotateLabel = new QLabel("Flat Rotation"); + m_flatRotationDoubleSpinBox = new QDoubleSpinBox; + m_flatRotationDoubleSpinBox->setWrapping(true); + m_flatRotationDoubleSpinBox->setMinimum(rotationMinimum); + m_flatRotationDoubleSpinBox->setMaximum(rotationMaximum); + m_flatRotationDoubleSpinBox->setSingleStep(rotateStep); + m_flatRotationDoubleSpinBox->setDecimals(2); + m_flatRotationDoubleSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_flatRotationDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(transformValueChanged())); + + /* * Zoom */ const double zoomStep = 0.01; @@ -575,6 +600,7 @@ m_transformWidgetGroup->add(m_xObliqueRotateDoubleSpinBox); m_transformWidgetGroup->add(m_yObliqueRotateDoubleSpinBox); m_transformWidgetGroup->add(m_zObliqueRotateDoubleSpinBox); + m_transformWidgetGroup->add(m_flatRotationDoubleSpinBox); m_transformWidgetGroup->add(m_zoomDoubleSpinBox); m_transformWidgetGroup->add(m_xRightFlatMapSpinBox); m_transformWidgetGroup->add(m_yRightFlatMapSpinBox); @@ -657,6 +683,14 @@ COLUMN_Z); row++; + gridLayout->addWidget(flatRotateLabel, + row, + COLUMN_LABEL); + gridLayout->addWidget(m_flatRotationDoubleSpinBox, + row, + COLUMN_X); + row++; + gridLayout->addWidget(zoomLabel, row, COLUMN_LABEL); @@ -709,7 +743,7 @@ void CustomViewDialog::transformValueChanged() { - double panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, zoom, rightFlatX, rightFlatY, rightFlatZoom; + double panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, flatRotate, zoom, rightFlatX, rightFlatY, rightFlatZoom; getTransformationControlValues(panX, panY, panZ, @@ -719,6 +753,7 @@ obRotX, obRotY, obRotZ, + flatRotate, zoom, rightFlatX, rightFlatY, @@ -740,9 +775,14 @@ float obliqueRotationMatrixArray[4][4]; obliqueRotationMatrix.getMatrix(obliqueRotationMatrixArray); + Matrix4x4 flatRotationMatrix; + flatRotationMatrix.setRotation(0.0, 0.0, flatRotate); + float flatRotationMatrixArray[4][4]; + flatRotationMatrix.getMatrix(flatRotationMatrixArray); + ModelTransform modelTransform; modelTransform.setPanningRotationMatrixAndZoom(panX, panY, panZ, - rotationMatrixArray, obliqueRotationMatrixArray, zoom, + rotationMatrixArray, obliqueRotationMatrixArray, flatRotationMatrixArray, zoom, rightFlatX, rightFlatY, rightFlatZoom); btc->setTransformationsFromModelTransform(modelTransform); updateGraphicsWindow(); @@ -824,6 +864,10 @@ double obRotX, obRotY, obRotZ; obliqueRotationMatrix.getRotation(obRotX, obRotY, obRotZ); + Matrix4x4 flatRotationMatrix = btc->getFlatRotationMatrix(); + double flatRotX, flatRotY, flatRotZ; + flatRotationMatrix.getRotation(flatRotX, flatRotY, flatRotZ); + float rightFlatX, rightFlatY; btc->getRightCortexFlatMapOffset(rightFlatX, rightFlatY); @@ -838,6 +882,7 @@ obRotX, obRotY, obRotZ, + flatRotZ, zooming, rightFlatX, rightFlatY, @@ -867,6 +912,14 @@ * Y rotation * @param rotZ * Z rotation + * @param objRotX + * X rotation + * @param objRotY + * Y rotation + * @param objRotZ + * Z rotation + * @param flatRotation + * Flat rotation * @param zoom * Zooming * @param rightFlatX @@ -886,6 +939,7 @@ double& obRotX, double& obRotY, double& obRotZ, + double& flatRotation, double& zoom, double& rightFlatX, double& rightFlatY, @@ -903,6 +957,8 @@ obRotY = m_yObliqueRotateDoubleSpinBox->value(); obRotZ = m_zObliqueRotateDoubleSpinBox->value(); + flatRotation = m_flatRotationDoubleSpinBox->value(); + zoom = m_zoomDoubleSpinBox->value(); rightFlatX = m_xRightFlatMapSpinBox->value(); @@ -923,6 +979,14 @@ * Y rotation * @param rotZ * Z rotation + * @param objRotX + * X rotation + * @param objRotY + * Y rotation + * @param objRotZ + * Z rotation + * @param flatRotation + * Flat rotation * @param zoom * Zooming * @param rightFlatX @@ -942,6 +1006,7 @@ const double obRotX, const double obRotY, const double obRotZ, + const double flatRotation, const double zoom, const double rightFlatX, const double rightFlatY, @@ -961,6 +1026,8 @@ m_yObliqueRotateDoubleSpinBox->setValue(obRotY); m_zObliqueRotateDoubleSpinBox->setValue(obRotZ); + m_flatRotationDoubleSpinBox->setValue(flatRotation); + m_zoomDoubleSpinBox->setValue(zoom); m_xRightFlatMapSpinBox->setValue(rightFlatX); diff -Nru connectome-workbench-1.4.2/src/GuiQt/CustomViewDialog.h connectome-workbench-1.5.0/src/GuiQt/CustomViewDialog.h --- connectome-workbench-1.4.2/src/GuiQt/CustomViewDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/CustomViewDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -96,6 +96,7 @@ double& obRotX, double& obRotY, double& obRotZ, + double& flatRotate, double& zoom, double& rightFlatX, double& rightFlatY, @@ -110,6 +111,7 @@ const double obRotX, const double obRotY, const double obRotZ, + const double flatRotate, const double zoom, const double rightFlatX, const double rightFlatY, @@ -153,6 +155,8 @@ QDoubleSpinBox* m_zObliqueRotateDoubleSpinBox; + QDoubleSpinBox* m_flatRotationDoubleSpinBox; + QDoubleSpinBox* m_zoomDoubleSpinBox; QDoubleSpinBox* m_xRightFlatMapSpinBox; diff -Nru connectome-workbench-1.4.2/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx connectome-workbench-1.5.0/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx --- connectome-workbench-1.4.2/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -181,7 +181,7 @@ { setData(NAME_COLUMN, Qt::UserRole, - qVariantFromValue(displayGroupAndTabItem)); + QVariant::fromValue(displayGroupAndTabItem)); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/DynConnViewController.cxx connectome-workbench-1.5.0/src/GuiQt/DynConnViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/DynConnViewController.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/DynConnViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,162 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __DYN_CONN_VIEW_CONTROLLER_DECLARE__ +#include "DynConnViewController.h" +#undef __DYN_CONN_VIEW_CONTROLLER_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "EventManager.h" +using namespace caret; + + + +/** + * \class caret::DynConnViewController + * \brief Prototype for dynamic connectivity controller + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +DynConnViewController::DynConnViewController() +: QWidget() +{ + std::vector types { "Correlation", "Covariance", "Fisher Z" }; + + QStringList labels; + labels + << "Mode" + << "Load" + << "Layer" + << "Demean" + << "Copy " + << "Name"; + + QStandardItemModel* model = new QStandardItemModel(0, 6); + model->setHorizontalHeaderLabels(labels); + + std::vector topModelIndices; + + const int32_t numFiles = 3; + for (int32_t i = 0; i < numFiles; i++) { + QString name("File_" + + AString::number(i + 1)); + QStandardItem* topItem = new QStandardItem(); + topItem->setText(name + + ".dtseries.nii (/mnt/myelin/caret7_gui_design/data/temp)"); + + for (auto nt : types) { + const bool addNamesFlag(false); + QStandardItem* loadItem = new QStandardItem(); + loadItem->setCheckable(true); + if (addNamesFlag) { + loadItem->setText("Load"); + } + + QStandardItem* layerItem = new QStandardItem(); + layerItem->setCheckable(true); + if (addNamesFlag) { + layerItem->setText("Layer"); + } + + QStandardItem* demeanItem = new QStandardItem(); + demeanItem->setCheckable(true); + if (addNamesFlag) { + demeanItem->setText("Demean"); + } + + QStandardItem* copyItem = new QStandardItem(); + copyItem->setText("Copy"); + + QStandardItem* typeItem = new QStandardItem(); + typeItem->setText(nt); + + QStandardItem* fileItem = new QStandardItem(); + fileItem->setText(nt + + "_" + + name + + ".dynconn.nii"); + + QList items; + items.push_back(typeItem); + items.push_back(loadItem); + items.push_back(layerItem); + items.push_back(demeanItem); + items.push_back(copyItem); + items.push_back(fileItem); + + topItem->appendRow(items); + } + + model->appendRow(topItem); + topModelIndices.push_back(model->indexFromItem(topItem)); + } + + m_treeView = new QTreeView(); + m_treeView->setModel(model); + + const int32_t numRows(static_cast(topModelIndices.size())); + for (int32_t i = 0; i < numRows; i++) { + m_treeView->setFirstColumnSpanned(i, m_treeView->rootIndex(), true); + } + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(m_treeView); +/* EventManager::get()->addEventListener(this, EventTypeEnum::); */ + + for (int32_t i = 0; i < (model->columnCount() - 1); i++) { + m_treeView->resizeColumnToContents(i); + } + +} + +/** + * Destructor. + */ +DynConnViewController::~DynConnViewController() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +DynConnViewController::receiveEvent(Event* /*event*/) +{ +// if (event->getEventType() == EventTypeEnum::) { +// eventName = dynamic_cast(event); +// CaretAssert(eventName); +// +// event->setEventProcessed(); +// } +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/DynConnViewController.h connectome-workbench-1.5.0/src/GuiQt/DynConnViewController.h --- connectome-workbench-1.4.2/src/GuiQt/DynConnViewController.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/DynConnViewController.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,67 @@ +#ifndef __DYN_CONN_VIEW_CONTROLLER_H__ +#define __DYN_CONN_VIEW_CONTROLLER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include +#include + +#include "EventListenerInterface.h" + + +namespace caret { + + class DynConnViewController : public QWidget, public EventListenerInterface { + + Q_OBJECT + + public: + DynConnViewController(); + + virtual ~DynConnViewController(); + + DynConnViewController(const DynConnViewController&) = delete; + + DynConnViewController& operator=(const DynConnViewController&) = delete; + + + // ADD_NEW_METHODS_HERE + + virtual void receiveEvent(Event* event); + + private: + + QTreeView* m_treeView; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __DYN_CONN_VIEW_CONTROLLER_DECLARE__ + // +#endif // __DYN_CONN_VIEW_CONTROLLER_DECLARE__ + +} // namespace +#endif //__DYN_CONN_VIEW_CONTROLLER_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventAnnotationCreateNewType.cxx connectome-workbench-1.5.0/src/GuiQt/EventAnnotationCreateNewType.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventAnnotationCreateNewType.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventAnnotationCreateNewType.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -39,19 +39,25 @@ /** * Constructor. * + * @param annotationFile + * File for annotation * @param annotationSpace * Space for new annotation. * @param annotationType * Type for new annotation. + * @param polyLineDrawingMode + * Mode for drawing polyline * */ EventAnnotationCreateNewType::EventAnnotationCreateNewType(AnnotationFile* annotationFile, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, - const AnnotationTypeEnum::Enum annotationType) + const AnnotationTypeEnum::Enum annotationType, + const PolyLineDrawingMode polyLineDrawingMode) : Event(EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE), m_annotationFile(annotationFile), m_annotationSpace(annotationSpace), -m_annotationType(annotationType) +m_annotationType(annotationType), +m_polyLineDrawingMode(polyLineDrawingMode) { } @@ -90,4 +96,13 @@ return m_annotationType; } +/** + * @return Mode for polyline drawing + */ +EventAnnotationCreateNewType::PolyLineDrawingMode +EventAnnotationCreateNewType::getPolyLineDrawingMode() const +{ + return m_polyLineDrawingMode; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventAnnotationCreateNewType.h connectome-workbench-1.5.0/src/GuiQt/EventAnnotationCreateNewType.h --- connectome-workbench-1.4.2/src/GuiQt/EventAnnotationCreateNewType.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventAnnotationCreateNewType.h 2021-02-16 19:46:47.000000000 +0000 @@ -34,9 +34,15 @@ class EventAnnotationCreateNewType : public Event { public: + enum PolyLineDrawingMode { + CONTINUOUS, + DISCRETE + }; + EventAnnotationCreateNewType(AnnotationFile* annotationFile, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, - const AnnotationTypeEnum::Enum annotationType); + const AnnotationTypeEnum::Enum annotationType, + const PolyLineDrawingMode polyLineDrawingMode); virtual ~EventAnnotationCreateNewType(); @@ -46,6 +52,8 @@ AnnotationTypeEnum::Enum getAnnotationType() const; + PolyLineDrawingMode getPolyLineDrawingMode() const; + // ADD_NEW_METHODS_HERE private: @@ -59,6 +67,8 @@ const AnnotationTypeEnum::Enum m_annotationType; + const PolyLineDrawingMode m_polyLineDrawingMode; + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabCloseInToolBar.cxx connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabCloseInToolBar.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabCloseInToolBar.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabCloseInToolBar.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,69 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretAssert.h" +#include "EventBrowserTabCloseInToolBar.h" + +using namespace caret; + +/** + * Constructor. + */ +EventBrowserTabCloseInToolBar::EventBrowserTabCloseInToolBar(BrowserTabContent* browserTab, + const int32_t browserTabIndex) +: Event(EventTypeEnum::EVENT_BROWSER_TAB_CLOSE_IN_TOOL_BAR), +m_browserTab(browserTab), +m_browserTabIndex(browserTabIndex) +{ + CaretAssert(browserTab); +} + +/** + * Destructor. + */ +EventBrowserTabCloseInToolBar::~EventBrowserTabCloseInToolBar() +{ + +} + +/** + * Get the browser tab that is to be deleted. + * Note that this may point to a browser tab that + * has been deleted and using the pointer in this + * case could be a disaster. + * + * @return + * Pointer to browser tab that is to be deleted. + */ +BrowserTabContent* +EventBrowserTabCloseInToolBar::getBrowserTab() +{ + return m_browserTab; +} + +/** + * @return Index of browser tab being deleted. + */ +int32_t +EventBrowserTabCloseInToolBar::getBrowserTabIndex() const +{ + return m_browserTabIndex; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabCloseInToolBar.h connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabCloseInToolBar.h --- connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabCloseInToolBar.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabCloseInToolBar.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,57 @@ +#ifndef __EVENT_BROWSER_TAB_CLOSE_FROM_TOOLBAR_H__ +#define __EVENT_BROWSER_TAB_CLOSE_FROM_TOOLBAR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "Event.h" + +namespace caret { + + class BrowserTabContent; + + /// Event for deleting a browser tab + class EventBrowserTabCloseInToolBar : public Event { + + public: + EventBrowserTabCloseInToolBar(BrowserTabContent* browserTab, + const int32_t browserTabIndex); + + virtual ~EventBrowserTabCloseInToolBar(); + + BrowserTabContent* getBrowserTab(); + + int32_t getBrowserTabIndex() const; + + private: + EventBrowserTabCloseInToolBar(const EventBrowserTabCloseInToolBar&); + + EventBrowserTabCloseInToolBar& operator=(const EventBrowserTabCloseInToolBar&); + + BrowserTabContent* m_browserTab; + + const int32_t m_browserTabIndex; + }; + +} // namespace + +#endif // __EVENT_BROWSER_TAB_CLOSE_FROM_TOOLBAR_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabDeleteInToolBar.cxx connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabDeleteInToolBar.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabDeleteInToolBar.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabDeleteInToolBar.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,69 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretAssert.h" +#include "EventBrowserTabDeleteInToolBar.h" + +using namespace caret; + +/** + * Constructor. + */ +EventBrowserTabDeleteInToolBar::EventBrowserTabDeleteInToolBar(BrowserTabContent* browserTab, + const int32_t browserTabIndex) +: Event(EventTypeEnum::EVENT_BROWSER_TAB_DELETE_IN_TOOL_BAR), +m_browserTab(browserTab), +m_browserTabIndex(browserTabIndex) +{ + CaretAssert(browserTab); +} + +/** + * Destructor. + */ +EventBrowserTabDeleteInToolBar::~EventBrowserTabDeleteInToolBar() +{ + +} + +/** + * Get the browser tab that is to be deleted. + * Note that this may point to a browser tab that + * has been deleted and using the pointer in this + * case could be a disaster. + * + * @return + * Pointer to browser tab that is to be deleted. + */ +BrowserTabContent* +EventBrowserTabDeleteInToolBar::getBrowserTab() +{ + return m_browserTab; +} + +/** + * @return Index of browser tab being deleted. + */ +int32_t +EventBrowserTabDeleteInToolBar::getBrowserTabIndex() const +{ + return m_browserTabIndex; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabDeleteInToolBar.h connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabDeleteInToolBar.h --- connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabDeleteInToolBar.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabDeleteInToolBar.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,57 @@ +#ifndef __EVENT_BROWSER_TAB_DELETE_FROM_TOOLBAR_H__ +#define __EVENT_BROWSER_TAB_DELETE_FROM_TOOLBAR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "Event.h" + +namespace caret { + + class BrowserTabContent; + + /// Event for deleting a browser tab + class EventBrowserTabDeleteInToolBar : public Event { + + public: + EventBrowserTabDeleteInToolBar(BrowserTabContent* browserTab, + const int32_t browserTabIndex); + + virtual ~EventBrowserTabDeleteInToolBar(); + + BrowserTabContent* getBrowserTab(); + + int32_t getBrowserTabIndex() const; + + private: + EventBrowserTabDeleteInToolBar(const EventBrowserTabDeleteInToolBar&); + + EventBrowserTabDeleteInToolBar& operator=(const EventBrowserTabDeleteInToolBar&); + + BrowserTabContent* m_browserTab; + + const int32_t m_browserTabIndex; + }; + +} // namespace + +#endif // __EVENT_BROWSER_TAB_DELETE_FROM_TOOLBAR_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabNewInGUI.cxx connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabNewInGUI.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabNewInGUI.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabNewInGUI.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,69 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "EventBrowserTabNewInGUI.h" + +using namespace caret; + +/** + * Constructor. + */ +EventBrowserTabNewInGUI::EventBrowserTabNewInGUI() +: Event(EventTypeEnum::EVENT_BROWSER_TAB_NEW_IN_GUI) +{ + this->browserTab = NULL; +} + +/** + * Destructor. + */ +EventBrowserTabNewInGUI::~EventBrowserTabNewInGUI() +{ + +} + +/** + * Get the browser tab that was created. + * + * @return + * Pointer to browser tab that was created or + * NULL if a browser tab could not be created. + */ +BrowserTabContent* +EventBrowserTabNewInGUI::getBrowserTab() +{ + return this->browserTab; +} + +/** + * Set the created browser tab. + * + * @param browserTab + * Browser tab that was created. + */ +void +EventBrowserTabNewInGUI::setBrowserTab(BrowserTabContent* browserTab) +{ + this->browserTab = browserTab; +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabNewInGUI.h connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabNewInGUI.h --- connectome-workbench-1.4.2/src/GuiQt/EventBrowserTabNewInGUI.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventBrowserTabNewInGUI.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,54 @@ +#ifndef __EVENT_BROWSER_TAB_NEW_IN_GUI_H__ +#define __EVENT_BROWSER_TAB_NEW_IN_GUI_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "Event.h" + +namespace caret { + + class BrowserTabContent; + + /// Event for creating a new browser tab + class EventBrowserTabNewInGUI : public Event { + + public: + EventBrowserTabNewInGUI(); + + virtual ~EventBrowserTabNewInGUI(); + + BrowserTabContent* getBrowserTab(); + + void setBrowserTab(BrowserTabContent* browserTab); + + private: + EventBrowserTabNewInGUI(const EventBrowserTabNewInGUI&); + + EventBrowserTabNewInGUI& operator=(const EventBrowserTabNewInGUI&); + + BrowserTabContent* browserTab; + }; + +} // namespace + +#endif // __EVENT_BROWSER_TAB_NEW_IN_GUI_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventBrowserWindowTileTabOperation.cxx connectome-workbench-1.5.0/src/GuiQt/EventBrowserWindowTileTabOperation.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventBrowserWindowTileTabOperation.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventBrowserWindowTileTabOperation.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -50,26 +50,59 @@ * Index of the window. * @param browserTabIndex * Index of the browser tab. + * @param windowViewport + * The window viewport + * @param mouseX + * X-position of mouse + * @param mouseY + * Y-position of mouse + * @param browserTabsForReplaceOperation + * Tabs for replacement */ EventBrowserWindowTileTabOperation::EventBrowserWindowTileTabOperation(const Operation operation, QWidget* parentWidget, const int32_t windowIndex, const int32_t browserTabIndex, + const int32_t windowViewport[4], + const int32_t mouseX, + const int32_t mouseY, const std::vector& browserTabsForReplaceOperation) : Event(EventTypeEnum::EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION), m_operation(operation), m_parentWidget(parentWidget), m_windowIndex(windowIndex), m_browserTabIndex(browserTabIndex), +m_mouseX(mouseX), +m_mouseY(mouseY), m_browserTabsForReplaceOperation(browserTabsForReplaceOperation) { CaretAssert(m_parentWidget); CaretAssert(m_windowIndex >= 0); + + m_windowViewport[0] = windowViewport[0]; + m_windowViewport[1] = windowViewport[1]; + m_windowViewport[2] = windowViewport[2]; + m_windowViewport[3] = windowViewport[3]; + switch (m_operation) { - case OPERATION_NEW_TAB_AFTER: + case OPERATION_GRID_NEW_TAB_AFTER: + CaretAssert(m_browserTabIndex >= 0); + break; + case OPERATION_GRID_NEW_TAB_BEFORE: + CaretAssert(m_browserTabIndex >= 0); + break; + case OPERATION_MANUAL_NEW_TAB: + break; + case OPERATION_ORDER_BRING_TO_FRONT: CaretAssert(m_browserTabIndex >= 0); break; - case OPERATION_NEW_TAB_BEFORE: + case OPERATION_ORDER_BRING_FORWARD: + CaretAssert(m_browserTabIndex >= 0); + break; + case OPERATION_ORDER_SEND_TO_BACK: + CaretAssert(m_browserTabIndex >= 0); + break; + case OPERATION_ORDER_SEND_BACKWARD: CaretAssert(m_browserTabIndex >= 0); break; case OPERATION_REPLACE_TABS: @@ -102,11 +135,17 @@ const int32_t windowIndex, const int32_t browserTabIndex) { + const int32_t dummyMouseX(-1); + const int32_t dummyMouseY(-1); + const int32_t dummyWindowViewport[4] { -1, -1, -1, -1 }; std::vector emptyBrowserTabs; EventBrowserWindowTileTabOperation tabOperation(Operation::OPERATION_SELECT_TAB, parentWidget, windowIndex, browserTabIndex, + dummyWindowViewport, + dummyMouseX, + dummyMouseY, emptyBrowserTabs); EventManager::get()->sendEvent(tabOperation.getPointer()); @@ -153,4 +192,36 @@ return m_browserTabsForReplaceOperation; } +/** + * @return Mouse X-coordinate (invalid if negative) + */ +int +EventBrowserWindowTileTabOperation::getMouseX() const +{ + return m_mouseX; +} + +/** + * @return Mouse Y-coordinate (invalid if negative) + */ +int +EventBrowserWindowTileTabOperation::getMouseY() const +{ + return m_mouseY; +} + +/** + * Get the window viewport + * + * @param windowViewport + * Output containing window viewport (negative values if invalid) + */ +void +EventBrowserWindowTileTabOperation::getWindowViewport(int32_t windowViewportOut[4]) const +{ + windowViewportOut[0] = m_windowViewport[0]; + windowViewportOut[1] = m_windowViewport[1]; + windowViewportOut[2] = m_windowViewport[2]; + windowViewportOut[3] = m_windowViewport[3]; +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventBrowserWindowTileTabOperation.h connectome-workbench-1.5.0/src/GuiQt/EventBrowserWindowTileTabOperation.h --- connectome-workbench-1.4.2/src/GuiQt/EventBrowserWindowTileTabOperation.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventBrowserWindowTileTabOperation.h 2021-02-16 19:46:47.000000000 +0000 @@ -41,8 +41,13 @@ const int32_t windowIndex, const int32_t browserTabIndex); enum Operation { - OPERATION_NEW_TAB_AFTER, - OPERATION_NEW_TAB_BEFORE, + OPERATION_GRID_NEW_TAB_AFTER, + OPERATION_GRID_NEW_TAB_BEFORE, + OPERATION_MANUAL_NEW_TAB, + OPERATION_ORDER_BRING_TO_FRONT, + OPERATION_ORDER_BRING_FORWARD, + OPERATION_ORDER_SEND_TO_BACK, + OPERATION_ORDER_SEND_BACKWARD, OPERATION_REPLACE_TABS, OPERATION_SELECT_TAB }; @@ -51,6 +56,9 @@ QWidget* parentWidget, const int32_t windowIndex, const int32_t browserTabIndex, + const int32_t windowViewport[4], + const int32_t mouseX, + const int32_t mouseY, const std::vector& browserTabsForReplaceOperation); virtual ~EventBrowserWindowTileTabOperation(); @@ -63,6 +71,12 @@ const std::vector getBrowserTabsForReplaceOperation() const; + void getWindowViewport(int32_t windowViewportOut[4]) const; + + int getMouseX() const; + + int getMouseY() const; + // ADD_NEW_METHODS_HERE private: @@ -78,8 +92,14 @@ const int32_t m_browserTabIndex; + const int32_t m_mouseX; + + const int32_t m_mouseY; + const std::vector m_browserTabsForReplaceOperation; + int m_windowViewport[4]; + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx connectome-workbench-1.5.0/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -55,7 +55,7 @@ : Event(EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { this->userInputProcessor = NULL; - this->userInputMode = UserInputModeEnum::INVALID; + this->userInputMode = UserInputModeEnum::Enum::INVALID; this->windowIndex = windowIndex; this->modeGetOrSet = GET; } diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventGraphicsUpdateOneWindow.cxx connectome-workbench-1.5.0/src/GuiQt/EventGraphicsUpdateOneWindow.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventGraphicsUpdateOneWindow.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventGraphicsUpdateOneWindow.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -30,11 +30,18 @@ /** * Constructor. + * + * @param windowIndex + * Index of window + * @param doRepaint + * if true, do a repaint instead of update */ -EventGraphicsUpdateOneWindow::EventGraphicsUpdateOneWindow(const int32_t windowIndex) +EventGraphicsUpdateOneWindow::EventGraphicsUpdateOneWindow(const int32_t windowIndex, + const bool doRepaint) : Event(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW) { - this->windowIndex = windowIndex; + this->windowIndex = windowIndex; + this->m_doRepaintFlag = doRepaint; } /* @@ -45,3 +52,13 @@ } +/** + * @return Indicates a repaint (instead of updates) is + * to be performed. + */ +bool +EventGraphicsUpdateOneWindow::isRepaint() const +{ + return m_doRepaintFlag; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventGraphicsUpdateOneWindow.h connectome-workbench-1.5.0/src/GuiQt/EventGraphicsUpdateOneWindow.h --- connectome-workbench-1.4.2/src/GuiQt/EventGraphicsUpdateOneWindow.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventGraphicsUpdateOneWindow.h 2021-02-16 19:46:47.000000000 +0000 @@ -30,13 +30,16 @@ class EventGraphicsUpdateOneWindow : public Event { public: - EventGraphicsUpdateOneWindow(const int32_t windowIndex); + EventGraphicsUpdateOneWindow(const int32_t windowIndex, + const bool doRepaint = false); virtual ~EventGraphicsUpdateOneWindow(); /// get the index of the window that is to be updated. int32_t getWindowIndex() const { return this->windowIndex; } + bool isRepaint() const; + private: EventGraphicsUpdateOneWindow(const EventGraphicsUpdateOneWindow&); @@ -44,6 +47,8 @@ /** index of window for update */ int32_t windowIndex; + + bool m_doRepaintFlag = false; }; } // namespace diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventGraphicsWindowShowToolTip.cxx connectome-workbench-1.5.0/src/GuiQt/EventGraphicsWindowShowToolTip.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventGraphicsWindowShowToolTip.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventGraphicsWindowShowToolTip.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP_DECLARE__ +#include "EventGraphicsWindowShowToolTip.h" +#undef __EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventGraphicsWindowShowToolTip + * \brief Show a toolip in the graphics region of a window + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param windowIndex + * Index of the window + * @param windowOrigin + * Location of origin in window for coordinate + * @param windowXYZ + * The coordinate in window for display of tooltip + * @param text + * Text to display in tooltip + */ +EventGraphicsWindowShowToolTip::EventGraphicsWindowShowToolTip(const int32_t windowIndex, + const WindowOrigin windowOrigin, + const std::array& windowXYZ, + const QString& text) +: Event(EventTypeEnum::EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP), +m_windowIndex(windowIndex), +m_windowOrigin(windowOrigin), +m_windowXYZ(windowXYZ), +m_text(text) +{ + +} + +/** + * Destructor. + */ +EventGraphicsWindowShowToolTip::~EventGraphicsWindowShowToolTip() +{ +} + +/** + * @return Origin of window coordinate + */ +EventGraphicsWindowShowToolTip::WindowOrigin +EventGraphicsWindowShowToolTip::getWindowOrigin() const +{ + return m_windowOrigin; +} + +/** + * @return Index of window in which to show tooltip + */ +int32_t +EventGraphicsWindowShowToolTip::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * @return Coordinate for placement of tooltip + */ +std::array +EventGraphicsWindowShowToolTip::getWindowXYZ() const +{ + return m_windowXYZ; +} + +/** + * @return Text displayed in tooltip + */ +QString +EventGraphicsWindowShowToolTip::getText() const +{ + return m_text; +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventGraphicsWindowShowToolTip.h connectome-workbench-1.5.0/src/GuiQt/EventGraphicsWindowShowToolTip.h --- connectome-workbench-1.4.2/src/GuiQt/EventGraphicsWindowShowToolTip.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventGraphicsWindowShowToolTip.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,81 @@ +#ifndef __EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP_H__ +#define __EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "Event.h" + + + +namespace caret { + + class EventGraphicsWindowShowToolTip : public Event { + + public: + enum class WindowOrigin { + BOTTOM_LEFT, + TOP_LEFT + }; + + EventGraphicsWindowShowToolTip(const int32_t windowIndex, + const WindowOrigin windowOrigin, + const std::array& windowXYZ, + const QString& text); + + virtual ~EventGraphicsWindowShowToolTip(); + + EventGraphicsWindowShowToolTip(const EventGraphicsWindowShowToolTip&) = delete; + + EventGraphicsWindowShowToolTip& operator=(const EventGraphicsWindowShowToolTip&) = delete; + + WindowOrigin getWindowOrigin() const; + + int32_t getWindowIndex() const; + + std::array getWindowXYZ() const; + + QString getText() const; + + // ADD_NEW_METHODS_HERE + + private: + const int32_t m_windowIndex; + + const WindowOrigin m_windowOrigin; + + const std::array& m_windowXYZ; + + const QString m_text; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP_DECLARE__ + // +#endif // __EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP_DECLARE__ + +} // namespace +#endif //__EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventMacDockMenuUpdate.cxx connectome-workbench-1.5.0/src/GuiQt/EventMacDockMenuUpdate.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventMacDockMenuUpdate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventMacDockMenuUpdate.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ -#include "EventMacDockMenuUpdate.h" -#undef __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ - -#include "CaretAssert.h" -#include "EventTypeEnum.h" - -using namespace caret; - - - -/** - * \class caret::EventMacDockMenuUpdate - * \brief - * \ingroup GuiQt - * - * - */ - -/** - * Constructor. - */ -EventMacDockMenuUpdate::EventMacDockMenuUpdate() -: Event(EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE) -{ - -} - -/** - * Destructor. - */ -EventMacDockMenuUpdate::~EventMacDockMenuUpdate() -{ -} - diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventMacDockMenuUpdate.h connectome-workbench-1.5.0/src/GuiQt/EventMacDockMenuUpdate.h --- connectome-workbench-1.4.2/src/GuiQt/EventMacDockMenuUpdate.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventMacDockMenuUpdate.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -#ifndef __EVENT_MAC_DOCK_MENU_UPDATE_H__ -#define __EVENT_MAC_DOCK_MENU_UPDATE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2015 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include "Event.h" - - - -namespace caret { - - class EventMacDockMenuUpdate : public Event { - - public: - EventMacDockMenuUpdate(); - - virtual ~EventMacDockMenuUpdate(); - - - // ADD_NEW_METHODS_HERE - - private: - EventMacDockMenuUpdate(const EventMacDockMenuUpdate&); - - EventMacDockMenuUpdate& operator=(const EventMacDockMenuUpdate&); - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ - // -#endif // __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ - -} // namespace -#endif //__EVENT_MAC_DOCK_MENU_UPDATE_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/EventUpdateInformationWindows.cxx connectome-workbench-1.5.0/src/GuiQt/EventUpdateInformationWindows.cxx --- connectome-workbench-1.4.2/src/GuiQt/EventUpdateInformationWindows.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/EventUpdateInformationWindows.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -46,7 +46,8 @@ if ( ! informationText.isEmpty()) { IdentificationManager* idManager = GuiManager::get()->getBrain()->getIdentificationManager(); CaretAssert(idManager); - idManager->addIdentifiedItem(new IdentifiedItem(informationText)); + idManager->addIdentifiedItem(new IdentifiedItem(informationText, + informationText)); } m_important = true; diff -Nru connectome-workbench-1.4.2/src/GuiQt/FiberOrientationSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/FiberOrientationSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/FiberOrientationSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/FiberOrientationSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -172,6 +172,15 @@ QObject::connect(m_minimumMagnitudeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + QLabel* maximumUncertaintyLabel = new QLabel("Maximum Uncertainty"); + m_maximumUncertaintySpinBox = WuQFactory::newDoubleSpinBox(); + m_maximumUncertaintySpinBox->setRange(0.0, std::numeric_limits::max()); + m_maximumUncertaintySpinBox->setDecimals(3); + m_maximumUncertaintySpinBox->setSingleStep(0.001); + m_maximumUncertaintySpinBox->setToolTip("Maximum uncertainty for displaying fibers"); + QObject::connect(m_maximumUncertaintySpinBox, SIGNAL(valueChanged(double)), + this, SLOT(processAttributesChanges())); + QLabel* lengthMultiplierLabel = new QLabel("Length Multiplier"); m_lengthMultiplierSpinBox = WuQFactory::newDoubleSpinBox(); m_lengthMultiplierSpinBox->setRange(0.0, std::numeric_limits::max()); @@ -229,6 +238,9 @@ gridLayout->addWidget(minimumMagnitudeLabel, row, 0); gridLayout->addWidget(m_minimumMagnitudeSpinBox , row, 1); row++; + gridLayout->addWidget(maximumUncertaintyLabel, row, 0); + gridLayout->addWidget(m_maximumUncertaintySpinBox, row, 1); + row++; gridLayout->addWidget(lengthMultiplierLabel, row, 0); gridLayout->addWidget(m_lengthMultiplierSpinBox , row, 1); row++; @@ -291,6 +303,10 @@ browserTabIndex, m_minimumMagnitudeSpinBox->value()); + dpfo->setMaximumUncertainty(displayGroup, + browserTabIndex, + m_maximumUncertaintySpinBox->value()); + dpfo->setLengthMultiplier(displayGroup, browserTabIndex, m_lengthMultiplierSpinBox->value()); @@ -428,6 +444,8 @@ browserTabIndex)); m_minimumMagnitudeSpinBox->setValue(dpfo->getMinimumMagnitude(displayGroup, browserTabIndex)); + m_maximumUncertaintySpinBox->setValue(dpfo->getMaximumUncertainty(displayGroup, + browserTabIndex)); m_drawWithMagnitudeCheckBox->setChecked(dpfo->isDrawWithMagnitude(displayGroup, browserTabIndex)); m_coloringTypeComboBox->setSelectedItem(dpfo->getColoringType(displayGroup, diff -Nru connectome-workbench-1.4.2/src/GuiQt/FiberOrientationSelectionViewController.h connectome-workbench-1.5.0/src/GuiQt/FiberOrientationSelectionViewController.h --- connectome-workbench-1.4.2/src/GuiQt/FiberOrientationSelectionViewController.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/FiberOrientationSelectionViewController.h 2021-02-16 19:46:47.000000000 +0000 @@ -96,6 +96,8 @@ QDoubleSpinBox* m_minimumMagnitudeSpinBox; + QDoubleSpinBox* m_maximumUncertaintySpinBox; + QDoubleSpinBox* m_lengthMultiplierSpinBox; QDoubleSpinBox* m_fanMultiplierSpinBox; diff -Nru connectome-workbench-1.4.2/src/GuiQt/FociPropertiesEditorDialog.cxx connectome-workbench-1.5.0/src/GuiQt/FociPropertiesEditorDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/FociPropertiesEditorDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/FociPropertiesEditorDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -409,7 +409,7 @@ FociFile* fociFile = brain->getFociFile(i); const AString name = fociFile->getFileNameNoPath(); m_fociFileSelectionComboBox->addItem(name, - qVariantFromValue((void*)fociFile)); + QVariant::fromValue((void*)fociFile)); if (selectedFociFile == fociFile) { defaultFileComboIndex = m_fociFileSelectionComboBox->count() - 1; } @@ -543,18 +543,13 @@ const int32_t unassignedNameKey = fociFile->getNameColorTable()->getUnassignedLabelKey(); const int32_t selectedNameKey = m_nameComboBox->getSelectedLabelKey(); if (selectedNameKey == unassignedNameKey) { - errorMessage += "Choose or create a name for the border"; + errorMessage += "Choose or create a name for the focus.\n"; } } const QString className = m_classComboBox->getSelectedLabelName(); - if ((m_xCoordSpinBox->value() == 0.0) - && (m_yCoordSpinBox->value() == 0.0) - && (m_zCoordSpinBox->value() == 0.0)) { - errorMessage += "Coordinates are invalid (all zeros)\n"; - } /* * Error? */ diff -Nru connectome-workbench-1.4.2/src/GuiQt/FociSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/FociSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/FociSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/FociSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -186,6 +186,18 @@ macroManager->addMacroSupportToObject(m_pasteOntoSurfaceCheckBox, "Enable paste foci onto surface"); + QLabel* projectionLabel = new QLabel("Coordinate Type"); + m_drawingProjectionTypeComboBox = new EnumComboBoxTemplate(this); + m_drawingProjectionTypeComboBox->setup(); + QObject::connect(m_drawingProjectionTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &FociSelectionViewController::processAttributesChanges); + m_drawingProjectionTypeComboBox->getComboBox()->setToolTip("Select foci projection type"); + m_drawingProjectionTypeComboBox->getComboBox()->setObjectName(m_objectNamePrefix + + ":ProjectionType"); + macroManager->addMacroSupportToObject(m_drawingProjectionTypeComboBox->getComboBox(), + "Select foci projection type"); + m_drawingProjectionTypeComboBox->setToolTip(FociDrawingProjectionTypeEnum::getToolTip()); + QLabel* coloringLabel = new QLabel("Coloring"); m_coloringTypeComboBox = new EnumComboBoxTemplate(this); m_coloringTypeComboBox->setupgetWidget()->setObjectName((m_objectNamePrefix + + ":Color")); m_standardColorComboBox->getWidget()->setToolTip("Select the standard color"); + WuQMacroManager::instance()->addMacroSupportToObject(m_standardColorComboBox->getComboBox(), + "Select foci standard color"); QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(processAttributesChanges())); @@ -228,28 +239,28 @@ macroManager->addMacroSupportToObject(m_drawTypeComboBox, "Select foci drawing style"); - float minLineWidth = 0; - float maxLineWidth = 1000; - //BrainOpenGL::getMinMaxLineWidth(minLineWidth, - // maxLineWidth); - + QLabel* symbolSizeTypeLabel = new QLabel("Diameter Type:"); + m_symbolSizeTypeComboBox = new EnumComboBoxTemplate(this); + m_symbolSizeTypeComboBox->setup(); + QObject::connect(m_symbolSizeTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &FociSelectionViewController::symbolSizeTypeComboBoxActivated); + m_symbolSizeTypeComboBox->setToolTip(IdentificationSymbolSizeTypeEnum::getToolTip("focus")); + QLabel* pointSizeLabel = new QLabel("Symbol Diameter"); m_sizeSpinBox = WuQFactory::newDoubleSpinBox(); m_sizeSpinBox->setFixedWidth(80); - m_sizeSpinBox->setRange(minLineWidth, - maxLineWidth); - m_sizeSpinBox->setSingleStep(1.0); + m_sizeSpinBox->setRange(0.1, 1000.0); + m_sizeSpinBox->setSingleStep(0.1); m_sizeSpinBox->setDecimals(1); - m_sizeSpinBox->setToolTip("Adjust the diameter of foci"); m_sizeSpinBox->setSuffix("mm"); + m_sizeSpinBox->setToolTip("Adjust the diameter of foci"); QObject::connect(m_sizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_sizeSpinBox->setObjectName(m_objectNamePrefix + ":Diameter"); macroManager->addMacroSupportToObject(m_sizeSpinBox, "Set foci size"); - - + QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 8, 2); @@ -260,6 +271,9 @@ row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 2); row++; + gridLayout->addWidget(projectionLabel, row, 0); + gridLayout->addWidget(m_drawingProjectionTypeComboBox->getComboBox(), row, 1); + row++; gridLayout->addWidget(coloringLabel, row, 0); gridLayout->addWidget(m_coloringTypeComboBox->getWidget(), row, 1); row++; @@ -269,6 +283,9 @@ gridLayout->addWidget(drawAsLabel, row, 0); gridLayout->addWidget(m_drawTypeComboBox , row, 1); row++; + gridLayout->addWidget(symbolSizeTypeLabel, row, 0); + gridLayout->addWidget(m_symbolSizeTypeComboBox->getWidget(), row, 1); + row++; gridLayout->addWidget(pointSizeLabel, row, 0); gridLayout->addWidget(m_sizeSpinBox, row, 1); gridWidget->setSizePolicy(QSizePolicy::Fixed, @@ -296,6 +313,7 @@ const int selectedDrawTypeIndex = m_drawTypeComboBox->currentIndex(); const int drawTypeInteger = m_drawTypeComboBox->itemData(selectedDrawTypeIndex).toInt(); const FociDrawingTypeEnum::Enum selectedDrawingType = static_cast(drawTypeInteger); + const FociDrawingProjectionTypeEnum::Enum selectedDrawingProjectionType = m_drawingProjectionTypeComboBox->getSelectedItem(); const CaretColorEnum::Enum standardColorType = m_standardColorComboBox->getSelectedColor(); BrowserTabContent* browserTabContent = @@ -315,15 +333,29 @@ dpf->setPasteOntoSurface(displayGroup, browserTabIndex, m_pasteOntoSurfaceCheckBox->isChecked()); + dpf->setDrawingProjectionType(displayGroup, + browserTabIndex, + selectedDrawingProjectionType); dpf->setColoringType(displayGroup, browserTabIndex, selectedColoringType); dpf->setStandardColorType(displayGroup, browserTabIndex, standardColorType); - dpf->setFociSize(displayGroup, - browserTabIndex, - m_sizeSpinBox->value()); + + switch (dpf->getFociSymbolSizeType(displayGroup, browserTabIndex)) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + dpf->setFociSizeMillimeters(displayGroup, + browserTabIndex, + m_sizeSpinBox->value()); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + dpf->setFociSizePercentage(displayGroup, + browserTabIndex, + m_sizeSpinBox->value()); + break; + } + dpf->setDrawingType(displayGroup, browserTabIndex, selectedDrawingType); @@ -366,6 +398,38 @@ } /** + * Called when symbol size type combo box changed + */ +void +FociSelectionViewController::symbolSizeTypeComboBoxActivated() +{ + BrowserTabContent* browserTabContent = + GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); + if (browserTabContent == NULL) { + return; + } + + const int32_t browserTabIndex = browserTabContent->getTabNumber(); + Brain* brain = GuiManager::get()->getBrain(); + DisplayPropertiesFoci* dpf = brain->getDisplayPropertiesFoci(); + const DisplayGroupEnum::Enum displayGroup = dpf->getDisplayGroupForTab(browserTabIndex); + + dpf->setFociSymbolSizeType(displayGroup, + browserTabIndex, + m_symbolSizeTypeComboBox->getSelectedItem()); + + /* + * Update controls since size type has changed + */ + updateFociViewController(); + + /* + * Apply the changes. + */ + processFociSelectionChanges(); +} + +/** * Update the foci widget. */ void @@ -418,6 +482,8 @@ m_coloringTypeComboBox->setSelectedItem(dpf->getColoringType(displayGroup, browserTabIndex)); + m_drawingProjectionTypeComboBox->setSelectedItem(dpf->getDrawingProjectionType(displayGroup, + browserTabIndex)); m_standardColorComboBox->setSelectedColor(dpf->getStandardColorType(displayGroup, browserTabIndex)); const FociDrawingTypeEnum::Enum selectedDrawingType = dpf->getDrawingType(displayGroup, @@ -432,10 +498,24 @@ } m_drawTypeComboBox->setCurrentIndex(selectedDrawingTypeIndex); + m_symbolSizeTypeComboBox->setSelectedItem(dpf->getFociSymbolSizeType(displayGroup, + browserTabIndex)); + m_sizeSpinBox->blockSignals(true); - m_sizeSpinBox->setValue(dpf->getFociSize(displayGroup, + m_sizeSpinBox->setValue(dpf->getFociSizeMillimeters(displayGroup, browserTabIndex)); - m_sizeSpinBox->blockSignals(false); + switch (dpf->getFociSymbolSizeType(displayGroup, browserTabIndex)) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + m_sizeSpinBox->setValue(dpf->getFociSizeMillimeters(displayGroup, + browserTabIndex)); + m_sizeSpinBox->setSuffix("mm"); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + m_sizeSpinBox->setValue(dpf->getFociSizePercentage(displayGroup, + browserTabIndex)); + m_sizeSpinBox->setSuffix("%"); + break; + } m_sizeSpinBox->blockSignals(false); } /** diff -Nru connectome-workbench-1.4.2/src/GuiQt/FociSelectionViewController.h connectome-workbench-1.5.0/src/GuiQt/FociSelectionViewController.h --- connectome-workbench-1.4.2/src/GuiQt/FociSelectionViewController.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/FociSelectionViewController.h 2021-02-16 19:46:47.000000000 +0000 @@ -70,6 +70,8 @@ void processAttributesChanges(); + void symbolSizeTypeComboBoxActivated(); + private: FociSelectionViewController(const FociSelectionViewController&); @@ -97,12 +99,16 @@ DisplayGroupEnumComboBox* m_fociDisplayGroupComboBox; + EnumComboBoxTemplate* m_drawingProjectionTypeComboBox; + EnumComboBoxTemplate* m_coloringTypeComboBox; CaretColorEnumComboBox* m_standardColorComboBox; QDoubleSpinBox* m_lineWidthSpinBox; + EnumComboBoxTemplate* m_symbolSizeTypeComboBox; + QDoubleSpinBox* m_sizeSpinBox; QComboBox* m_drawTypeComboBox; diff -Nru connectome-workbench-1.4.2/src/GuiQt/GestureEvent.cxx connectome-workbench-1.5.0/src/GuiQt/GestureEvent.cxx --- connectome-workbench-1.4.2/src/GuiQt/GestureEvent.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/GestureEvent.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,203 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GESTURE_EVENT_DECLARE__ +#include "GestureEvent.h" +#undef __GESTURE_EVENT_DECLARE__ + +#include "BrainOpenGLViewportContent.h" +#include "BrainOpenGLWidget.h" + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::GestureEvent + * \brief Contains information about a gesture event, typically from a track pad + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param viewportContent + * Content of viewport. + * @param openGLWidget + * OpenGL Widget in which mouse activity took place. + * @param browserWindowIndex + * Index of the browser winddow in which mouse activity took place. + * @param startCenterX + * X-coord at start of gesture + * @param startCenterY + * Y-coord at start of gesture + * @param state + * State of the gesture + * @param type + * Type of the gesture + * @param dataValue + * Data value (pinch or rotation) + */ +GestureEvent::GestureEvent(const BrainOpenGLViewportContent* viewportContent, + BrainOpenGLWidget* openGLWidget, + const int32_t browserWindowIndex, + const int32_t startCenterX, + const int32_t startCenterY, + const State state, + const Type type, + const float dataValue) +: CaretObject(), +m_openGLWidget(openGLWidget), +m_browserWindowIndex(browserWindowIndex), +m_startCenterX(startCenterX), +m_startCenterY(startCenterY), +m_state(state), +m_type(type), +m_dataValue(dataValue) +{ + if (viewportContent != NULL) { + m_viewportContent.reset(new BrainOpenGLViewportContent(*viewportContent)); + } +} + +/** + * Destructor. + */ +GestureEvent::~GestureEvent() +{ +} + +/** + * @return The viewport content in which the mouse was pressed. + */ +BrainOpenGLViewportContent* +GestureEvent::getViewportContent() const +{ + return m_viewportContent.get(); +} + +/** + * @return The OpenGL Widget in which the mouse event occurred. + */ +BrainOpenGLWidget* +GestureEvent::getOpenGLWidget() const +{ + return m_openGLWidget; +} + +/** + * @return Index of the browser window in which the + * event took place. + */ +int32_t +GestureEvent::getBrowserWindowIndex() const +{ + return m_browserWindowIndex; +} + +/** + * Get the X-coordinate of the mouse. + * @return The X-coordinate. + * + */ +int32_t +GestureEvent::getStartCenterX() const +{ + return m_startCenterX; +} + +/** + * Get the Y-coordinate of the mouse. + * Origin is at the BOTTOM of the widget !!! + * @return The Y-coordinate. + * + */ +int32_t +GestureEvent::getStartCenterY() const +{ + return m_startCenterY; +} + +/** + * Get the Global X-coordinate of where the mouse was pressed. + * @return The Global X-coordinate. + * + * @param x + * X-coordinate in widget (0 is left side of widget) + * @param y + * Y-coordinate in widget (0 is bottom side of widget) + * @param outGlobalX + * Output with global X-coordinate + * @param outGlobalY + * Output with global Y-coordinate + */ +void +GestureEvent::getGlobalXY(const int32_t x, + const int32_t y, + int32_t& outGlobalX, + int32_t& outGlobalY) const +{ + CaretAssert(m_openGLWidget); + const int32_t yOriginTop = m_openGLWidget->height() - y; + const QPoint globalPoint = m_openGLWidget->mapToGlobal(QPoint(x, + yOriginTop)); + outGlobalX = globalPoint.x(); + outGlobalY = globalPoint.y(); +} + +/** + * @return The State of the gesture + */ +GestureEvent::State +GestureEvent::getState() const +{ + return m_state; +} + +/** + * @return The Type of the gesture + */ +GestureEvent::Type +GestureEvent::getType() const +{ + return m_type; +} + +/** + * @return The data value (Pinch is scale factor, bigger if > 1, smaller if < 1; + * Rotate is change in rotation angle) + */ +float +GestureEvent::getValue() const +{ + return m_dataValue; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GestureEvent::toString() const +{ + return "GestureEvent"; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/GestureEvent.h connectome-workbench-1.5.0/src/GuiQt/GestureEvent.h --- connectome-workbench-1.4.2/src/GuiQt/GestureEvent.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/GestureEvent.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,117 @@ +#ifndef __GESTURE_EVENT_H__ +#define __GESTURE_EVENT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "CaretObject.h" + + + +namespace caret { + + class BrainOpenGLViewportContent; + class BrainOpenGLWidget; + + class GestureEvent : public CaretObject { + + public: + enum class State { + END, + START, + UPDATE + }; + + enum class Type { + PINCH, + ROTATE + }; + + GestureEvent(const BrainOpenGLViewportContent* viewportContent, + BrainOpenGLWidget* openGLWidget, + const int32_t browserWindowIndex, + const int32_t startCenterX, + const int32_t startCenterY, + const State state, + const Type type, + const float dataValue); + + virtual ~GestureEvent(); + + GestureEvent(const GestureEvent& obj) = delete; + + GestureEvent& operator=(const GestureEvent& obj) = delete; + + BrainOpenGLViewportContent* getViewportContent() const; + + BrainOpenGLWidget* getOpenGLWidget() const; + + int32_t getBrowserWindowIndex() const; + + int32_t getStartCenterX() const; + + int32_t getStartCenterY() const; + + State getState() const; + + Type getType() const; + + void getGlobalXY(const int32_t x, + const int32_t y, + int32_t& outGlobalX, + int32_t& outGlobalY) const; + + float getValue() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + std::unique_ptr m_viewportContent; + + BrainOpenGLWidget* m_openGLWidget; + + const int32_t m_browserWindowIndex; + + const int32_t m_startCenterX; + + const int32_t m_startCenterY; + + const State m_state; + + const Type m_type; + + const float m_dataValue; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GESTURE_EVENT_DECLARE__ + // +#endif // __GESTURE_EVENT_DECLARE__ + +} // namespace +#endif //__GESTURE_EVENT_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/GiftiLabelTableEditor.cxx connectome-workbench-1.5.0/src/GuiQt/GiftiLabelTableEditor.cxx --- connectome-workbench-1.4.2/src/GuiQt/GiftiLabelTableEditor.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/GiftiLabelTableEditor.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -642,7 +642,7 @@ setWidgetItemIconColor(colorItem, rgba); colorItem->setData(Qt::UserRole, - qVariantFromValue((void*)gl)); + QVariant::fromValue((void*)gl)); m_labelSelectionListWidget->addItem(colorItem); if (selectedName == gl->getName()) { diff -Nru connectome-workbench-1.4.2/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx connectome-workbench-1.5.0/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx --- connectome-workbench-1.4.2/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -161,7 +161,7 @@ label->getBlue())); QIcon icon(pm); - QVariant userData = qVariantFromValue((void*)label); + QVariant userData = QVariant::fromValue((void*)label); m_comboBox->setItemData(i, userData); m_comboBox->setItemIcon(i, icon); } @@ -266,7 +266,7 @@ label->getBlue())); QIcon icon(pm); - QVariant userData = qVariantFromValue((void*)label); + QVariant userData = QVariant::fromValue((void*)label); m_comboBox->addItem(icon, labelName, @@ -342,7 +342,7 @@ GiftiLabelTableSelectionComboBox::setSelectedLabel(const GiftiLabel* label) { if (label != NULL) { - QVariant userData = qVariantFromValue((void*)label); + QVariant userData = QVariant::fromValue((void*)label); int indx = m_comboBox->findData(userData); if (indx >= 0) { m_comboBox->setCurrentIndex(indx); diff -Nru connectome-workbench-1.4.2/src/GuiQt/GuiManager.cxx connectome-workbench-1.5.0/src/GuiQt/GuiManager.cxx --- connectome-workbench-1.4.2/src/GuiQt/GuiManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/GuiManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -30,6 +30,7 @@ #include #include #include +#include #define __GUI_MANAGER_DEFINE__ #include "GuiManager.h" @@ -46,6 +47,7 @@ #include "BugReportDialog.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "CaretPreferences.h" #include "CaretMappableDataFile.h" #include "ChartableTwoFileDelegate.h" #include "ChartableTwoFileMatrixChart.h" @@ -56,7 +58,6 @@ #include "CiftiFiberTrajectoryManager.h" #include "CiftiConnectivityMatrixParcelFile.h" #include "CiftiScalarDataSeriesFile.h" -#include "ClippingPlanesDialog.h" #include "CursorDisplayScoped.h" #include "CursorManager.h" #include "CustomViewDialog.h" @@ -67,13 +68,13 @@ #include "EventAnnotationGetDrawnInWindow.h" #include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" +#include "EventBrowserTabReopenClosed.h" #include "EventBrowserWindowNew.h" #include "EventShowDataFileReadWarningsDialog.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventHelpViewerDisplay.h" #include "EventIdentificationHighlightLocation.h" -#include "EventMacDockMenuUpdate.h" #include "EventManager.h" #include "EventMapYokingSelectMap.h" #include "EventModelGetAll.h" @@ -87,6 +88,9 @@ #include "FociPropertiesEditorDialog.h" #include "GapsAndMarginsDialog.h" #include "HelpViewerDialog.h" +#include "HtmlTableBuilder.h" +#include "IdentificationDisplayDialog.h" +#include "IdentificationFilter.h" #include "IdentifiedItemNode.h" #include "IdentifiedItemVoxel.h" #include "IdentificationManager.h" @@ -102,6 +106,7 @@ #include "MovieDialog.h" #include "MovieRecordingDialog.h" #include "PaletteColorMappingEditorDialog.h" +#include "PaletteEditorDialog.h" #include "PreferencesDialog.h" #include "Scene.h" #include "SceneAttributes.h" @@ -112,6 +117,7 @@ #include "SceneWindowGeometry.h" #include "SelectionManager.h" #include "SelectionItemChartMatrix.h" +#include "SelectionItemChartTwoLineLayerVerticalNearest.h" #include "SelectionItemChartTwoMatrix.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemSurfaceNode.h" @@ -135,8 +141,6 @@ #include "WuQMessageBox.h" #include "WuQtUtilities.h" -#include "CaretAssert.h" - using namespace caret; /** @@ -178,13 +182,13 @@ this->allowBrowserWindowsToCloseWithoutConfirmation = false; m_bugReportDialog = NULL; - m_clippingPlanesDialog = NULL; m_customViewDialog = NULL; m_gapsAndMarginsDialog = NULL; this->imageCaptureDialog = NULL; this->movieDialog = NULL; m_movieRecordingDialog = NULL; m_informationDisplayDialog = NULL; + m_identificationDisplayDialog = NULL; m_identifyBrainordinateDialog = NULL; this->preferencesDialog = NULL; this->connectomeDatabaseWebView = NULL; @@ -195,6 +199,7 @@ m_surfacePropertiesEditorDialog = NULL; m_volumePropertiesEditorDialog = NULL; m_tileTabsConfigurationDialog = NULL; + m_paletteEditorDialog = NULL; this->cursorManager = new CursorManager(); @@ -207,6 +212,8 @@ "caret::WuQTabWidgetWithSizeHint"); WuQMacroWidgetTypeEnum::addWidgetClassNameAlias("QTabBar", "caret::WuQTabBar"); + WuQMacroWidgetTypeEnum::addWidgetClassNameAlias("QSpinBox", + "caret::WuQSpinBox"); WuQMacroManager::instance()->addParentObject(this); WuQMacroManager::instance()->setMacroHelper(new WbMacroHelper(this)); WuQMacroManager::instance()->setCustomCommandManager(new WbMacroCustomOperationManager()); @@ -227,8 +234,10 @@ m_informationDisplayDialogEnabledAction = WuQtUtilities::createAction("Information...", - "Enables display of the Information Window\n" - "when new information is available", + ("Enables display of the Information Window " + "when new information is available.

" + "This is disabled " + "when information window is located in the Overlay Toolbox."), this, this, SLOT(showHideInfoWindowSelected(bool))); @@ -305,6 +314,24 @@ "Display Scene Dialog"); /* + * Menu for scene dialog action + * Disabled because in the Window Menu, it creates a sub menu with + * listing of scenes but there is no menu item to display the dialog. + */ + const bool allowSceneMenuFlag(false); + m_sceneDialogDisplayActionMenu = NULL; + if (allowSceneMenuFlag) { + m_sceneDialogDisplayActionMenu = new QMenu(); + m_sceneDialogDisplayAction->setMenu(m_sceneDialogDisplayActionMenu); + QAction::connect(m_sceneDialogDisplayActionMenu, &QMenu::aboutToShow, + this, &GuiManager::sceneDialogDisplayMenuAboutToShow); + QAction::connect(m_sceneDialogDisplayActionMenu, &QMenu::triggered, + this, &GuiManager::sceneDialogDisplayMenuTriggered); + QAction::connect(m_sceneDialogDisplayActionMenu, &QMenu::hovered, + this, &GuiManager::sceneDialogDisplayMenuHovered); + } + + /* * Help dialog action */ m_helpViewerDialogDisplayAction = WuQtUtilities::createAction("Workbench Help...", @@ -342,7 +369,6 @@ EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_OVERLAY_SETTINGS_EDITOR_SHOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW); @@ -367,6 +393,14 @@ FociPropertiesEditorDialog::deleteStaticMembers(); + /* + * Mac Dock Menu does not have parent so need to delete it + */ + if (m_mackDockMenu != NULL) { + delete m_mackDockMenu; + m_mackDockMenu = NULL; + } + for (std::set::iterator iter = m_parentlessNonModalDialogs.begin(); iter != m_parentlessNonModalDialogs.end(); iter++) { @@ -521,8 +555,6 @@ this->reparentNonModalDialogs(brainBrowserWindow); } } - - EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); } return isBrowserWindowAllowedToClose; @@ -674,7 +706,7 @@ return NULL; } - EventManager::get()->blockEvent(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET, true); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_ANNOTATION_BARS_GET, true); int32_t windowIndex = -1; @@ -751,8 +783,19 @@ bbw->resetGraphicsWidgetMinimumSize(); - EventManager::get()->blockEvent(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET, false); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_ANNOTATION_BARS_GET, false); + /* + * Create Mac Dock Menu when first window is created. + * DO NOT give it a parent since a window used as parent + * could close but other windows remain open. + */ +#ifdef CARET_OS_MACOSX + if (m_mackDockMenu == NULL) { + m_mackDockMenu = new MacDockMenu(); + } +#endif // CARET_OS_MACOSX + return bbw; } @@ -1368,7 +1411,9 @@ /* * Draw the given window */ - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(windowIndex).getPointer()); + const bool doRepaintFlag(true); + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(windowIndex, + doRepaintFlag).getPointer()); /* * Find annotations that were drawn in the given window. @@ -1412,8 +1457,6 @@ const int h = std::min(bbw->height(), preferredMaxHeight); bbw->resize(w, h); - - EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); } else if ((event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS) || (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW)) { @@ -1421,14 +1464,6 @@ overlayEditor->updateChartLinesInDialog(); } } - else if (event->getEventType() == EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE) { - EventMacDockMenuUpdate* macDockMenuEvent = dynamic_cast(event); - CaretAssert(event); - - MacDockMenu::createUpdateMacDockMenu(); - - macDockMenuEvent->setEventProcessed(); - } else if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS) { EventUpdateInformationWindows* infoEvent = dynamic_cast(event); @@ -1474,7 +1509,7 @@ overlayEditorIter != m_overlaySettingsEditors.end(); overlayEditorIter++) { OverlaySettingsEditorDialog* med = *overlayEditorIter; - if (med->isDoNotReplaceSelected() == false) { + if ( ! med->isDoNotReplaceSelected()) { overlayEditor = med; break; } @@ -1538,26 +1573,8 @@ EventOperatingSystemRequestOpenDataFile* openFileEvent = dynamic_cast(event); CaretAssert(openFileEvent); - - BrainBrowserWindow* bbw = getActiveBrowserWindow(); - if (bbw != NULL) { - std::vector filenamesVector; - std::vector dataFileTypeVectorNotUsed; - filenamesVector.push_back(openFileEvent->getDataFileName()); - bbw->loadFiles(bbw, - filenamesVector, - dataFileTypeVectorNotUsed, - BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG, - "", - ""); - } - else { - /* - * Browser window has not yet been created. - * After it is created, the file will be opened. - */ - m_nameOfDataFileToOpenAfterStartup = openFileEvent->getDataFileName(); - } + processOpenDataFileEvent(openFileEvent); + openFileEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW) { EventPaletteColorMappingEditorDialogRequest* paletteEditEvent = @@ -1625,6 +1642,7 @@ if (m_movieRecordingDialog != NULL) { m_movieRecordingDialog->updateDialog(); } + updateInformationDisplayDialogAction(); } } @@ -1821,6 +1839,97 @@ } /** + * Called when an action (scene) is hovered from the scene dialog action's menu + * @param action + * Action that was selected + */ +void +GuiManager::sceneDialogDisplayMenuHovered(QAction* action) +{ + if (this->sceneDialog != NULL) { + SceneFile* sceneFile = this->sceneDialog->getSelectedSceneFile(); + if (sceneFile != NULL) { + const int32_t numScenes = sceneFile->getNumberOfScenes(); + if (action != NULL) { + const int32_t sceneIndex = action->data().toInt(); + if ((sceneIndex >= 0) + && (sceneIndex < numScenes)) { + const Scene* scene = sceneFile->getSceneAtIndex(sceneIndex); + CaretAssert(scene); + const AString description = scene->getDescription(); + if (description.isEmpty()) { + QToolTip::hideText(); + } + else { + /* + * Show tooltip containing scene description + * below mouse (note: positive Y is down in Qt coords) + */ + const AString wrappedText(WuQtUtilities::createWordWrappedToolTipText(description)); + const QPoint toolTipXY(QCursor::pos() + QPoint(0, 15)); + QToolTip::showText(toolTipXY, + wrappedText); + } + } + } + } + } +} + +/** + * Called when an action (scene) is selected from the scene dialog action's menu + * @param action + * Action that was selected + */ +void +GuiManager::sceneDialogDisplayMenuTriggered(QAction* action) +{ + if (this->sceneDialog != NULL) { + SceneFile* sceneFile = this->sceneDialog->getSelectedSceneFile(); + if (sceneFile != NULL) { + const int32_t numScenes = sceneFile->getNumberOfScenes(); + if (action != NULL) { + const int32_t sceneIndex = action->data().toInt(); + if ((sceneIndex >= 0) + && (sceneIndex < numScenes)) { + SceneDialog::displaySceneWithErrorMessageDialog(getActiveBrowserWindow(), + sceneFile, + sceneFile->getSceneAtIndex(sceneIndex)); + } + } + } + } +} + +/** + * Called when the scene dialog action's menu is about to show + */ +void +GuiManager::sceneDialogDisplayMenuAboutToShow() +{ + m_sceneDialogDisplayActionMenu->clear(); + + const Scene* activeScene = getBrain()->getActiveScene(); + if (this->sceneDialog != NULL) { + SceneFile* sceneFile = this->sceneDialog->getSelectedSceneFile(); + if (sceneFile != NULL) { + const int32_t numScenes = sceneFile->getNumberOfScenes(); + for (int32_t i = 0; i < numScenes; i++) { + const Scene* scene = sceneFile->getSceneAtIndex(i); + CaretAssert(scene); + const AString name("(" + AString::number(i+1) + "): " + + scene->getName()); + QAction* action = m_sceneDialogDisplayActionMenu->addAction(name); + action->setData(i); + action->setCheckable(true); + action->setChecked(scene == activeScene); + } + } + } +} + + +/** * @return The action for showing/hiding the scene dialog. */ QAction* @@ -1946,6 +2055,7 @@ * @param showSceneDialogFlag * If true, update the scene dialog. Otherwise, load the scene * without showing the scene dialog + * @return True if scene was loaded successfully, else false */ void GuiManager::processShowSceneDialogAndScene(BrainBrowserWindow* browserWindow, @@ -2007,7 +2117,15 @@ sceneFile, scene); } - + + /* + * Close scene dialog if it was not requested for display + */ + if ( ! showSceneDialogFlag) { + if (this->sceneDialog != NULL) { + this->sceneDialog->close(); + } + } } /** @@ -2259,23 +2377,38 @@ void GuiManager::showHideInfoWindowSelected(bool status) { - QString text("Show Information Window"); if (status) { - text = "Hide Information Window"; - if (m_informationDisplayDialogEnabledAction->signalsBlocked() == false) { + if ( ! m_informationDisplayDialogEnabledAction->signalsBlocked()) { this->processShowInformationDisplayDialog(true); } } - - text += ("\n\n" - "When this button is 'on', the information window\n" - "is automatically displayed when an identification\n" - "operation (mouse click over surface or volume slice)\n" - "is performed. "); - m_informationDisplayDialogEnabledAction->setToolTip(text); } - +/* Update the information display dialog action + * + */ +void +GuiManager::updateInformationDisplayDialogAction() +{ + bool enabledFlag(false); + switch(SessionManager::get()->getCaretPreferences()->getIdentificationDisplayMode()) { + case IdentificationDisplayModeEnum::DIALOG: + enabledFlag = true; + break; + case IdentificationDisplayModeEnum::LEGACY_DIALOG: + enabledFlag = true; + break; + case IdentificationDisplayModeEnum::OVERLAY_TOOLBOX: + enabledFlag = false; + break; + case IdentificationDisplayModeEnum::DEBUG_MODE: + enabledFlag = true; + break; + } + + m_informationDisplayDialogEnabledAction->setEnabled(enabledFlag); +} + /** * Show the information display window. * @param forceDisplayOfDialog @@ -2285,28 +2418,75 @@ void GuiManager::processShowInformationDisplayDialog(const bool forceDisplayOfDialog) { - if (m_informationDisplayDialog == NULL) { - std::vector bbws = this->getAllOpenBrainBrowserWindows(); - if (bbws.empty() == false) { - BrainBrowserWindow* parentWindow = bbws[0]; + bool showOldDialog(false); + bool showNewDialog(false); + + switch(SessionManager::get()->getCaretPreferences()->getIdentificationDisplayMode()) { + case IdentificationDisplayModeEnum::DIALOG: + showNewDialog = true; + break; + case IdentificationDisplayModeEnum::LEGACY_DIALOG: + showOldDialog = true; + break; + case IdentificationDisplayModeEnum::OVERLAY_TOOLBOX: + break; + case IdentificationDisplayModeEnum::DEBUG_MODE: + showNewDialog = true; + showOldDialog = true; + break; + } + + if (showOldDialog) { + if (m_informationDisplayDialog == NULL) { + std::vector bbws = this->getAllOpenBrainBrowserWindows(); + if ( ! bbws.empty()) { + BrainBrowserWindow* parentWindow = bbws[0]; #ifdef CARET_OS_MACOSX - m_informationDisplayDialog = new InformationDisplayDialog(parentWindow); - this->addNonModalDialog(m_informationDisplayDialog); + m_informationDisplayDialog = new InformationDisplayDialog(parentWindow); + this->addNonModalDialog(m_informationDisplayDialog); #else // CARET_OS_MACOSX - m_informationDisplayDialog = new InformationDisplayDialog(NULL); - addParentLessNonModalDialog(m_informationDisplayDialog); + m_informationDisplayDialog = new InformationDisplayDialog(NULL); + addParentLessNonModalDialog(m_informationDisplayDialog); #endif // CARET_OS_MACOSX - m_informationDisplayDialog->resize(600, 200); - m_informationDisplayDialog->setSaveWindowPositionForNextTime(true); - WuQtUtilities::moveWindowToSideOfParent(parentWindow, - m_informationDisplayDialog); + m_informationDisplayDialog->resize(600, 200); + m_informationDisplayDialog->setSaveWindowPositionForNextTime(true); + WuQtUtilities::moveWindowToSideOfParent(parentWindow, + m_informationDisplayDialog); + } + } + + if (forceDisplayOfDialog + || m_informationDisplayDialogEnabledAction->isChecked()) { + if (m_informationDisplayDialog != NULL) { + m_informationDisplayDialog->showDialog(); + } } } - if (forceDisplayOfDialog - || m_informationDisplayDialogEnabledAction->isChecked()) { - if (m_informationDisplayDialog != NULL) { - m_informationDisplayDialog->showDialog(); + if (showNewDialog) { + if (m_identificationDisplayDialog == NULL) { + std::vector bbws = this->getAllOpenBrainBrowserWindows(); + if ( ! bbws.empty()) { + BrainBrowserWindow* parentWindow = bbws[0]; +#ifdef CARET_OS_MACOSX + m_identificationDisplayDialog = new IdentificationDisplayDialog(parentWindow); + this->addNonModalDialog(m_identificationDisplayDialog); +#else // CARET_OS_MACOSX + m_identificationDisplayDialog = new IdentificationDisplayDialog(NULL); + addParentLessNonModalDialog(m_identificationDisplayDialog); +#endif // CARET_OS_MACOSX + m_identificationDisplayDialog->resize(600, 200); + m_identificationDisplayDialog->setSaveWindowPositionForNextTime(true); + WuQtUtilities::moveWindowToSideOfParent(parentWindow, + m_identificationDisplayDialog); + } + } + + if (forceDisplayOfDialog + || m_informationDisplayDialogEnabledAction->isChecked()) { + if (m_identificationDisplayDialog != NULL) { + m_identificationDisplayDialog->showDialog(); + } } } } @@ -2350,7 +2530,6 @@ } m_identifyBrainordinateDialog = new IdentifyBrainordinateDialog(idDialogParent); - //m_identifyBrainordinateDialog->setSaveWindowPositionForNextTime(true); this->addNonModalDialog(m_identifyBrainordinateDialog); QObject::connect(m_identifyBrainordinateDialog, SIGNAL(dialogWasClosed()), this, SLOT(identifyBrainordinateDialogWasClosed())); @@ -2425,26 +2604,6 @@ m_parentlessNonModalDialogs.insert(dialog); } - -/** - * Show the clipping planes dialog. - * @param browserWindow - * Window on which dialog was requested. - */ -void -GuiManager::processShowClippingPlanesDialog(BrainBrowserWindow* browserWindow) -{ - if (m_clippingPlanesDialog == NULL) { - m_clippingPlanesDialog = new ClippingPlanesDialog(browserWindow); - this->addNonModalDialog(m_clippingPlanesDialog); - } - - const int32_t browserWindowIndex = browserWindow->getBrowserWindowIndex(); - m_clippingPlanesDialog->updateContent(browserWindowIndex); - m_clippingPlanesDialog->showDialog(); -} - - /** * Show the custom view dialog. * @param browserWindow @@ -2599,6 +2758,24 @@ } /** + * Show the palette editor dialog + * @param browserWindow + * Parent for the dialog + */ +void +GuiManager::processShowPaletteEditorDialog(BrainBrowserWindow* browserWindow) +{ + if (m_paletteEditorDialog == NULL) { + CaretAssert(browserWindow); + m_paletteEditorDialog = new PaletteEditorDialog(browserWindow); + addNonModalDialog(m_paletteEditorDialog); + } + m_paletteEditorDialog->updateDialog(); + m_paletteEditorDialog->showDialog(); +} + + +/** * sets animation start time for Time Course Dialogs */ void GuiManager::updateAnimationStartTime(double /*value*/) @@ -2675,9 +2852,31 @@ /* * Save information window */ - if (m_informationDisplayDialog != NULL) { - sceneClass->addClass(m_informationDisplayDialog->saveToScene(sceneAttributes, - "m_informationDisplayDialog")); + switch (SessionManager::get()->getCaretPreferences()->getIdentificationDisplayMode()) { + case IdentificationDisplayModeEnum::DIALOG: + if (m_identificationDisplayDialog != NULL) { + sceneClass->addClass(m_identificationDisplayDialog->saveToScene(sceneAttributes, + "m_identificationDisplayDialog")); + } + break; + case IdentificationDisplayModeEnum::LEGACY_DIALOG: + if (m_informationDisplayDialog != NULL) { + sceneClass->addClass(m_informationDisplayDialog->saveToScene(sceneAttributes, + "m_informationDisplayDialog")); + } + break; + case IdentificationDisplayModeEnum::OVERLAY_TOOLBOX: + break; + case IdentificationDisplayModeEnum::DEBUG_MODE: + if (m_informationDisplayDialog != NULL) { + sceneClass->addClass(m_informationDisplayDialog->saveToScene(sceneAttributes, + "m_informationDisplayDialog")); + } + if (m_identificationDisplayDialog != NULL) { + sceneClass->addClass(m_identificationDisplayDialog->saveToScene(sceneAttributes, + "m_identificationDisplayDialog")); + } + break; } /* @@ -2802,7 +3001,7 @@ */ EventModelGetAll getAllModelsEvent; EventManager::get()->sendEvent(getAllModelsEvent.getPointer()); - const bool haveModels = (getAllModelsEvent.getModels().empty() == false); + const bool haveModels = ( ! getAllModelsEvent.getModels().empty()); ElapsedTimer timer; timer.start(); @@ -2891,11 +3090,13 @@ if (m_informationDisplayDialog == NULL) { processShowInformationWindow(); } - else if (m_informationDisplayDialog->isVisible() == false) { + else if ( ! m_informationDisplayDialog->isVisible()) { processShowInformationWindow(); } - m_informationDisplayDialog->restoreFromScene(sceneAttributes, - infoWindowClass); + if (m_informationDisplayDialog != NULL) { + m_informationDisplayDialog->restoreFromScene(sceneAttributes, + infoWindowClass); + } } else { if (m_informationDisplayDialog != NULL) { @@ -2908,6 +3109,39 @@ } /* + * Restore identification window + */ + progressEvent.setProgressMessage("Restoring Identification Window"); + EventManager::get()->sendEvent(progressEvent.getPointer()); + const SceneClass* idWindowClass = sceneClass->getClass("m_identificationDisplayDialog"); + if (idWindowClass != NULL) { + if (m_identificationDisplayDialog == NULL) { + processShowInformationWindow(); + } + else if ( ! m_identificationDisplayDialog->isVisible()) { + processShowInformationWindow(); + } + if (m_identificationDisplayDialog != NULL) { + m_identificationDisplayDialog->restoreFromScene(sceneAttributes, + idWindowClass); + } + } + else { + if (m_identificationDisplayDialog != NULL) { + /* + * Will clear text + */ + m_identificationDisplayDialog->restoreFromScene(sceneAttributes, + NULL); + } + } + + /* + * Restore Information Widgets (newer replacement for information display dialog) + */ + EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); + + /* * Restore surface properties */ progressEvent.setProgressMessage("Restoring Surface Properties Window"); @@ -2917,7 +3151,7 @@ if (m_surfacePropertiesEditorDialog == NULL) { processShowSurfacePropertiesEditorDialog(firstBrowserWindow); } - else if (m_surfacePropertiesEditorDialog->isVisible() == false) { + else if ( ! m_surfacePropertiesEditorDialog->isVisible()) { processShowSurfacePropertiesEditorDialog(firstBrowserWindow); } m_surfacePropertiesEditorDialog->restoreFromScene(sceneAttributes, @@ -3028,6 +3262,9 @@ bool updateGraphicsFlag = false; bool updateInformationFlag = false; std::vector ciftiLoadingInfo; + HtmlTableBuilder ciftiLoadingInfoTableBuilder(HtmlTableBuilder::HtmlVersion::V4_01, + 2); + ciftiLoadingInfoTableBuilder.setTitlePlain("CIFTI Data Loading"); const QString breakAndIndent("
    "); SelectionItemSurfaceNodeIdentificationSymbol* nodeIdSymbol = selectionManager->getSurfaceNodeIdentificationSymbol(); @@ -3064,7 +3301,7 @@ * node near the voxel coordinate, if it is close by. */ bool nodeIdentificationCreatedFromVoxelIdentificationFlag = false; - if (idNode->isValid() == false) { + if ( ! idNode->isValid()) { if (idVoxel->isValid()) { double doubleXYZ[3]; idVoxel->getModelXYZ(doubleXYZ); @@ -3098,7 +3335,7 @@ /* * NOT: node id was NOT created from voxel ID */ - if (nodeIdentificationCreatedFromVoxelIdentificationFlag == false) { + if ( ! nodeIdentificationCreatedFromVoxelIdentificationFlag) { Surface* surface = idNode->getSurface(); const int32_t nodeIndex = idNode->getNodeNumber(); try { @@ -3107,12 +3344,14 @@ ciftiConnectivityManager->loadDataForSurfaceNode(brain, surface, nodeIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); ciftiFiberTrajectoryManager->loadDataForSurfaceNode(brain, surface, nodeIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); chartingDataManager->loadChartForSurfaceNode(surface, nodeIndex); @@ -3162,10 +3401,12 @@ try { ciftiConnectivityManager->loadDataForVoxelAtCoordinate(brain, xyz, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); ciftiFiberTrajectoryManager->loadDataForVoxelAtCoordinate(brain, xyz, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); } catch (const DataFileException& e) { cursor.restoreCursor(); @@ -3213,7 +3454,8 @@ ciftiParcelFile, rowIndex, columnIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); } catch (const DataFileException& e) { @@ -3265,7 +3507,8 @@ ciftiConnectivityManager->loadRowOrColumnFromConnectivityMatrixFile(matrixFile, rowIndex, columnIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); updateGraphicsFlag = true; } @@ -3349,7 +3592,8 @@ ciftiParcelFile, rowIndex, colIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); } catch (const DataFileException& e) { @@ -3369,7 +3613,8 @@ ciftiConnectivityManager->loadRowOrColumnFromConnectivityMatrixFile(matrixFile, rowIndex, colIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); updateGraphicsFlag = true; } @@ -3435,10 +3680,23 @@ } } + SelectionItemChartTwoLineLayerVerticalNearest* layerChartID = selectionManager->getChartTwoLineLayerVerticalNearestIdentification(); + if (layerChartID->isValid()) { + /* + * Note: Selection is performed in UserInputModeView + */ + } + /* * Generate identification manager */ - const AString identificationMessage = selectionManager->getIdentificationText(brain); + const AString identificationMessage = selectionManager->getSimpleIdentificationText(brain); + AString formattedIdentificationMessage = selectionManager->getFormattedIdentificationText(brain, + tabIndex); + AString ciftiLoadingFormattedMessage; + if (identificationManager->getIdentificationFilter()->isShowCiftiLoadingEnabled()) { + ciftiLoadingFormattedMessage = ciftiLoadingInfoTableBuilder.getAsHtmlTable(); + } bool issuedIdentificationLocationEvent = false; if (idNode->isValid()) { @@ -3469,6 +3727,7 @@ } identifiedItem = new IdentifiedItemNode(identificationMessage, + formattedIdentificationMessage, surface->getStructure(), surface->getNumberOfNodes(), nodeIndex); @@ -3477,8 +3736,8 @@ * if it WAS NOT created from the voxel identification * location. */ - if (nodeIdentificationCreatedFromVoxelIdentificationFlag == false) { - if (issuedIdentificationLocationEvent == false) { + if ( ! nodeIdentificationCreatedFromVoxelIdentificationFlag) { + if ( ! issuedIdentificationLocationEvent) { EventIdentificationHighlightLocation idLocation(tabIndex, xyz, EventIdentificationHighlightLocation::LOAD_FIBER_ORIENTATION_SAMPLES_MODE_YES); @@ -3498,10 +3757,11 @@ if (identifiedItem == NULL) { identifiedItem = new IdentifiedItemVoxel(identificationMessage, + formattedIdentificationMessage, xyz); } - if (issuedIdentificationLocationEvent == false) { + if ( ! issuedIdentificationLocationEvent) { EventIdentificationHighlightLocation idLocation(tabIndex, xyz, EventIdentificationHighlightLocation::LOAD_FIBER_ORIENTATION_SAMPLES_MODE_YES); @@ -3517,13 +3777,15 @@ } if (identifiedItem == NULL) { - if (identificationMessage.isEmpty() == false) { - identifiedItem = new IdentifiedItem(identificationMessage); + if ( (! identificationMessage.isEmpty()) + || ( ! formattedIdentificationMessage.isEmpty())) { + identifiedItem = new IdentifiedItem(identificationMessage, + formattedIdentificationMessage); } } AString ciftiInfo; - if (ciftiLoadingInfo.empty() == false) { + if ( ! ciftiLoadingInfo.empty()) { IdentificationStringBuilder ciftiIdStringBuilder; ciftiIdStringBuilder.addLine(false, "CIFTI data loaded", " "); for (std::vector::iterator iter = ciftiLoadingInfo.begin(); @@ -3534,12 +3796,14 @@ ciftiInfo = ciftiIdStringBuilder.toString(); } - if (ciftiInfo.isEmpty() == false) { + if ( ! ciftiInfo.isEmpty()) { if (identifiedItem != NULL) { - identifiedItem->appendText(ciftiInfo); + identifiedItem->appendText(ciftiInfo, + ciftiLoadingFormattedMessage); } else { - identifiedItem = new IdentifiedItem(ciftiInfo); + identifiedItem = new IdentifiedItem(ciftiInfo, + ciftiLoadingFormattedMessage); } } @@ -3560,4 +3824,160 @@ } } +/** + * Reopen the last closed tab + * @param parentWindow + * Window from which Reopen Tab was selected + */ +void +GuiManager::processReopenLastClosedTab(BrainBrowserWindow* parentWindow) +{ + CaretAssert(parentWindow); + + EventBrowserTabReopenClosed reopenEvent; + EventManager::get()->sendEvent(reopenEvent.getPointer()); + if (reopenEvent.isError()) { + WuQMessageBox::errorOk(parentWindow, + reopenEvent.getErrorMessage()); + } + else { + BrowserTabContent* tabContent = reopenEvent.getBrowserTabContent(); + CaretAssert(tabContent); + if (tabContent != NULL) { + const int32_t windowIndex = tabContent->getClosedTabWindowIndex(); + BrainBrowserWindow* window = getBrowserWindowByWindowIndex(windowIndex); + if (window == NULL) { + /* + * User may have closed window that contained the tab + */ + window = parentWindow; + } + + CaretAssert(window); + window->reopenLastClosedTab(reopenEvent); + } + } +} + +/** + * Process the open data file event. This event is a Mac event issued when a Workbench + * associated data file is opened from MacOS Finder. If there is no window, that indicates + * no wb_view was running when file was opened so we save the name of the file and the + * when the browser window is created, it will load the file. + * + * @param openDataFileEvent + * The open data file event + */ +void +GuiManager::processOpenDataFileEvent(EventOperatingSystemRequestOpenDataFile* openDataFileEvent) +{ + CaretAssert(openDataFileEvent); + BrainBrowserWindow* bbw = getActiveBrowserWindow(); + if (bbw != NULL) { + FileOpenFromOpSysTypeEnum::Enum openType = SessionManager::get()->getCaretPreferences()->getFileOpenFromOpSysType(); + switch (openType) { + case FileOpenFromOpSysTypeEnum::ASK_USER: + { + /* + * Launch dialog allowing user to open the file in + * 'this' wb_view or open file in a new wb_view instance. + */ + QMessageBox msgBox(bbw); + msgBox.setWindowTitle("Open File"); + msgBox.setIcon(QMessageBox::Question); + msgBox.setText("Would you like to open the file(s) in a " + "new wb_view or in this wb_view ?"); + QPushButton* newWbViewPB = msgBox.addButton("New wb_view", QMessageBox::AcceptRole); + QPushButton* thisWbViewPB = msgBox.addButton("This wb_view", QMessageBox::AcceptRole); + QPushButton* cancelPB = msgBox.addButton("Cancel", QMessageBox::RejectRole); + msgBox.exec(); + + QAbstractButton* button = msgBox.clickedButton(); + if (button == newWbViewPB) { + openType = FileOpenFromOpSysTypeEnum::IN_NEW_WB_VIEW; + } + else if (button == thisWbViewPB) { + openType = FileOpenFromOpSysTypeEnum::IN_CURRENT_WB_VIEW; + } + else if (button == cancelPB) { + return; + } + else { + CaretAssertMessage(0, "PushButton clicked test failed"); + } + } + break; + case FileOpenFromOpSysTypeEnum::IN_CURRENT_WB_VIEW: + break; + case FileOpenFromOpSysTypeEnum::IN_NEW_WB_VIEW: + break; + } + + switch (openType) { + case FileOpenFromOpSysTypeEnum::ASK_USER: + CaretAssert(0); + break; + case FileOpenFromOpSysTypeEnum::IN_CURRENT_WB_VIEW: + { + std::vector filenamesVector; + std::vector dataFileTypeVectorNotUsed; + filenamesVector.push_back(openDataFileEvent->getDataFileName()); + bbw->loadFiles(bbw, + filenamesVector, + dataFileTypeVectorNotUsed, + BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG, + "", + ""); + } + break; + case FileOpenFromOpSysTypeEnum::IN_NEW_WB_VIEW: + { + QString filename(openDataFileEvent->getDataFileName()); + FileInformation fileInfo(filename); + const QStringList parameters(filename); + const QString workingDirectory(FileInformation(filename).getAbsolutePath()); + + GuiManager::startNewWbViewInstance(parameters, + workingDirectory, + bbw); + return; + } + break; + } + } + else { + /* + * Browser window has not yet been created. + * After it is created, the file will be opened. + */ + m_nameOfDataFileToOpenAfterStartup = openDataFileEvent->getDataFileName(); + } + +} + +/** + * Start a new instance of wb_view that is separate from this instance of wb_view. + * See QProcess documentation for more info. + * + * @param parameters + * Parameters passed to wb_view + * @param workingDirectory + * The working directory for the new instance. If empty, the working directory of 'this' wb_view is used + * @param parent + * Parent widget for any error dialogs. + */ +bool +GuiManager::startNewWbViewInstance(const QStringList& parameters, + const QString& workingDirectory, + QWidget* parent) +{ + if ( ! QProcess::startDetached(QApplication::applicationFilePath(), + parameters, + workingDirectory)) { + WuQMessageBox::errorOk(parent, + "Failed to start new wb_view, reason unknown"); + } + + return true; +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/GuiManager.h connectome-workbench-1.5.0/src/GuiQt/GuiManager.h --- connectome-workbench-1.4.2/src/GuiQt/GuiManager.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/GuiManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -46,19 +46,22 @@ class BrowserTabContent; class BugReportDialog; class ChartTwoLineSeriesHistoryDialog; - class ClippingPlanesDialog; class CursorManager; class CustomViewDialog; + class EventOperatingSystemRequestOpenDataFile; class GapsAndMarginsDialog; class HelpViewerDialog; class IdentifyBrainordinateDialog; + class IdentificationDisplayDialog; class ImageFile; class ImageCaptureDialog; class InformationDisplayDialog; class MovieRecordingDialog; class OverlaySettingsEditorDialog; + class MacDockMenu; class Model; class PaletteColorMappingEditorDialog; + class PaletteEditorDialog; class PreferencesDialog; class Scene; class SceneDialog; @@ -93,6 +96,10 @@ static void updateSurfaceColoring(); + static bool startNewWbViewInstance(const QStringList& parameters, + const QString& workingDirectory, + QWidget* parent); + Brain* getBrain() const; int32_t getNumberOfOpenBrainBrowserWindows() const; @@ -142,7 +149,6 @@ void processShowBugReportDialog(BrainBrowserWindow* browserWindow, const AString& openGLInformation); - void processShowClippingPlanesDialog(BrainBrowserWindow* browserWindow); void processShowCustomViewDialog(BrainBrowserWindow* browserWindow); void processShowGapsAndMarginsDialog(BrainBrowserWindow* browserWindow); void processShowImageCaptureDialog(BrainBrowserWindow* browserWindow); @@ -170,6 +176,8 @@ void updateAnimationStartTime(double value); + void processShowPaletteEditorDialog(BrainBrowserWindow* browserWindow); + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -182,6 +190,9 @@ SelectionManager* selectionManager, QWidget* parentWidget); + void processReopenLastClosedTab(BrainBrowserWindow* parentWindow); + + /* * Mode used when testing for modified files */ @@ -211,6 +222,12 @@ void sceneDialogDisplayActionTriggered(bool); + void sceneDialogDisplayMenuHovered(QAction* action); + + void sceneDialogDisplayMenuTriggered(QAction* action); + + void sceneDialogDisplayMenuAboutToShow(); + void showHelpDialogActionToggled(bool); private slots: @@ -252,6 +269,10 @@ QPixmap createDataToolTipsIcon(const QWidget* widget); + void processOpenDataFileEvent(EventOperatingSystemRequestOpenDataFile* openDataFileEvent); + + void updateInformationDisplayDialogAction(); + /** One instance of the GuiManager */ static GuiManager* singletonGuiManager; @@ -267,12 +288,12 @@ /** Editor for palette color mapping editing */ PaletteColorMappingEditorDialog* m_paletteColorMappingEditor; + PaletteEditorDialog* m_paletteEditorDialog = NULL; + ChartTwoLineSeriesHistoryDialog* m_chartTwoLineSeriesHistoryDialog; TileTabsConfigurationDialog* m_tileTabsConfigurationDialog; - ClippingPlanesDialog* m_clippingPlanesDialog; - CustomViewDialog* m_customViewDialog; ImageCaptureDialog* imageCaptureDialog; @@ -289,10 +310,14 @@ IdentifyBrainordinateDialog* m_identifyBrainordinateDialog; + IdentificationDisplayDialog* m_identificationDisplayDialog = NULL; + SceneDialog* sceneDialog; QAction* m_sceneDialogDisplayAction; + QMenu* m_sceneDialogDisplayActionMenu; + SurfacePropertiesEditorDialog* m_surfacePropertiesEditorDialog; VolumePropertiesEditorDialog* m_volumePropertiesEditorDialog; @@ -313,6 +338,8 @@ HelpViewerDialog* m_helpViewerDialog; + MacDockMenu* m_mackDockMenu = NULL; + /** * Tracks non-modal dialogs that are created only one time * and may need to be reparented if the original parent, a diff -Nru connectome-workbench-1.4.2/src/GuiQt/IdentificationDisplayDialog.cxx connectome-workbench-1.5.0/src/GuiQt/IdentificationDisplayDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/IdentificationDisplayDialog.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/IdentificationDisplayDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,131 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_DISPLAY_DIALOG_DECLARE__ +#include "IdentificationDisplayDialog.h" +#undef __IDENTIFICATION_DISPLAY_DIALOG_DECLARE__ + +#include "CaretAssert.h" +#include "IdentificationDisplayWidget.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::IdentificationDisplayDialog + * \brief Dialog for display of identification information + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @parent + * The parent widget + */ +IdentificationDisplayDialog::IdentificationDisplayDialog(QWidget* parent) +: WuQDialogNonModal("Identification", + parent) +{ + setApplyButtonText(""); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + + m_identificationWidget = new IdentificationDisplayWidget(IdentificationDisplayWidget::Location::Dialog); + + setCentralWidget(m_identificationWidget, + SCROLL_AREA_NEVER); +} + +/** + * Destructor. + */ +IdentificationDisplayDialog::~IdentificationDisplayDialog() +{ +} + +/** + * Update the dialog + */ +void +IdentificationDisplayDialog::updateDialog() +{ + +} +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +IdentificationDisplayDialog::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "IdentificationDisplayDialog", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +IdentificationDisplayDialog::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/IdentificationDisplayDialog.h connectome-workbench-1.5.0/src/GuiQt/IdentificationDisplayDialog.h --- connectome-workbench-1.4.2/src/GuiQt/IdentificationDisplayDialog.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/IdentificationDisplayDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,90 @@ +#ifndef __IDENTIFICATION_DISPLAY_DIALOG_H__ +#define __IDENTIFICATION_DISPLAY_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQDialogNonModal.h" + +#include "SceneableInterface.h" + + +namespace caret { + class IdentificationDisplayWidget; + class SceneClassAssistant; + + class IdentificationDisplayDialog : public WuQDialogNonModal, public SceneableInterface { + + Q_OBJECT + + public: + IdentificationDisplayDialog(QWidget* parent = 0); + + virtual ~IdentificationDisplayDialog(); + + IdentificationDisplayDialog(const IdentificationDisplayDialog&) = delete; + + IdentificationDisplayDialog& operator=(const IdentificationDisplayDialog&) = delete; + + void updateDialog(); + + + // ADD_NEW_METHODS_HERE + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + std::unique_ptr m_sceneAssistant; + + IdentificationDisplayWidget* m_identificationWidget; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __IDENTIFICATION_DISPLAY_DIALOG_DECLARE__ + // +#endif // __IDENTIFICATION_DISPLAY_DIALOG_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_DISPLAY_DIALOG_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/IdentificationDisplayWidget.cxx connectome-workbench-1.5.0/src/GuiQt/IdentificationDisplayWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/IdentificationDisplayWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/IdentificationDisplayWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,863 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_DISPLAY_WIDGET_DECLARE__ +#include "IdentificationDisplayWidget.h" +#undef __IDENTIFICATION_DISPLAY_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Brain.h" +#include "CaretAssert.h" +#include "CaretColorEnumComboBox.h" +#include "EnumComboBoxTemplate.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventUpdateInformationWindows.h" +#include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" +#include "IdentificationFileFilteringTableWidget.h" +#include "IdentificationFilter.h" +#include "IdentificationHistoryManager.h" +#include "IdentificationManager.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" +#include "WuQValueChangedSignalWatcher.h" +#include "WuQtUtilities.h" +using namespace caret; + +/** + * \class caret::IdentificationDisplayWidget + * \brief Widget for display of identification data + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param location + * Location of widget + * @param + * The parent widget + */ +IdentificationDisplayWidget::IdentificationDisplayWidget(const Location location, + QWidget* parent) +: QWidget(parent), +m_location(location) +{ + m_infoWidget = createInfoWidget(); + + m_filteringFilesWidget = createFilteringFilesWidget(); + m_filteringSettingsWidget = createFilteringSettingsWidget(); + m_symbolsWidget = createSymbolsWidget(); + m_chartLineLayerSymbolWidget = createChartLineLayerSymbolsWidget(); + + m_tabWidget = new QTabWidget(); + const int32_t infoIndex = m_tabWidget->addTab(m_infoWidget, "Info"); + const int32_t chartIndex = m_tabWidget->addTab(m_chartLineLayerSymbolWidget, "Chart"); + const int32_t filesIndex = m_tabWidget->addTab(m_filteringFilesWidget, "Files"); + const int32_t filterIndex = m_tabWidget->addTab(m_filteringSettingsWidget, "Filter"); + const int32_t symbolsIndex = m_tabWidget->addTab(m_symbolsWidget, "Symbols"); + + m_tabWidget->setTabToolTip(infoIndex, + "Display results of identification operations"); + m_tabWidget->setTabToolTip(chartIndex, + "Controls for chart identification"); + m_tabWidget->setTabToolTip(filesIndex, + "Select specific files for identification; Enables " + "identification of files that are NOT in an overlay"); + m_tabWidget->setTabToolTip(filterIndex, + "Select tabs (all or selected)for identification data; " + "Enable feature (borders/foci) identification"); + m_tabWidget->setTabToolTip(symbolsIndex, + "Control the size/color/types of symbols shown " + "on surfaces and volume slices"); + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); + layout->addWidget(m_tabWidget, 100); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS); + + updateContent(true); +} + +/** + * Destructor. + */ +IdentificationDisplayWidget::~IdentificationDisplayWidget() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +IdentificationDisplayWidget::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS) { + EventUpdateInformationWindows* textEvent = + dynamic_cast(event); + CaretAssert(textEvent); + textEvent->setEventProcessed(); + + updateContent(true); + } +} + +/** + * Update the widget's content + * @param scrollTextToEndFlag + * If true, scroll text to bottom + */ +void +IdentificationDisplayWidget::updateContent(const bool scrollTextToEndFlag) +{ + updateSymbolsWidget(); + updateFilteringWidget(); + updateInfoWidget(scrollTextToEndFlag); + updateChartLineLayerSymbolsWidget(); +} + +/** + * Update the display widget's content + * @param scrollTextToEndFlag + * If true, scroll text to bottom + */ +void +IdentificationDisplayWidget::updateInfoWidget(const bool scrollTextToEndFlag) +{ + IdentificationHistoryManager* historyManager = getHistoryManager(); + QSignalBlocker lastBlocker(m_infoShowHistoryCountSpinBox); + m_infoShowHistoryCountSpinBox->setValue(historyManager->getShowLastHistoryCount()); + + /* + * Maintain or move scroll bar position + */ + QScrollBar* sb = m_infoTextBrowser->verticalScrollBar(); + CaretAssert(sb); + int32_t positionValue = sb->value(); + m_infoTextBrowser->setHtml(historyManager->getHtml()); + if (scrollTextToEndFlag) { + positionValue = sb->maximum(); + positionValue = std::max(positionValue, + 0); + } + positionValue = std::min(positionValue, + sb->maximum()); + sb->setValue(positionValue); +} + +/** + * @return Instance of the display widget + */ +QWidget* +IdentificationDisplayWidget::createInfoWidget() +{ + m_infoTextBrowser = new QTextBrowser(); + m_infoTextBrowser->setLineWrapMode(QTextEdit::NoWrap); + m_infoTextBrowser->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + QToolButton* clearHistoryToolButton = new QToolButton(); + clearHistoryToolButton->setText("Clear"); + clearHistoryToolButton->setToolTip("Remove all information text"); + QObject::connect(clearHistoryToolButton, &QToolButton::clicked, + this, &IdentificationDisplayWidget::infoClearHistoryToolButtonClicked); + + QToolButton* copyHistoryToolButton = new QToolButton(); + copyHistoryToolButton->setText("Copy"); + copyHistoryToolButton->setToolTip("Copy the SELECTED text to the Clipboard"); + QObject::connect(copyHistoryToolButton, &QToolButton::clicked, + m_infoTextBrowser, &QTextBrowser::copy); + QObject::connect(m_infoTextBrowser, &QTextBrowser::copyAvailable, + copyHistoryToolButton, &QToolButton::setEnabled); + copyHistoryToolButton->setEnabled(false); + + AString removeButtonText; + switch (m_location) { + case Location::Dialog: + case Location::HorizontalToolBox: + removeButtonText = "Remove\nSymbols"; + break; + case Location::VerticalToolBox: + removeButtonText = "Remove Symbols"; + break; + } + QToolButton* removeSymbolsButton = new QToolButton(); + removeSymbolsButton->setText(removeButtonText); + + removeSymbolsButton->setToolTip("Remove identification symbols on surfaces and volume slices"); + QObject::connect(removeSymbolsButton, &QToolButton::clicked, + this, &IdentificationDisplayWidget::infoRemoveSymbolsButtonClicked); + + const QString showToolTip("Selects number of recent identification operations displayed in the information text. " + "Selections are All, 1, 2, etc."); + m_infoShowHistoryCountSpinBox = new QSpinBox(); + m_infoShowHistoryCountSpinBox->setRange(0, 99); + m_infoShowHistoryCountSpinBox->setSingleStep(1); + m_infoShowHistoryCountSpinBox->setSpecialValueText("Show All"); + m_infoShowHistoryCountSpinBox->setToolTip(showToolTip); + m_infoShowHistoryCountSpinBox->setPrefix("Show "); + QObject::connect(m_infoShowHistoryCountSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &IdentificationDisplayWidget::infoShowHistoryCountSpinBoxValueChanged); + m_infoShowHistoryCountSpinBox->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + + QGroupBox* historyGroupBox = new QGroupBox(); + historyGroupBox->setAlignment(Qt::AlignHCenter); + QLabel* historyLabel(NULL); + Qt::Alignment historyAlignment = Qt::Alignment(); + switch (m_location) { + case Location::Dialog: + { + historyGroupBox->setTitle("History"); + QVBoxLayout* historyLayout = new QVBoxLayout(historyGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(historyLayout, 2, 4); + historyLayout->addWidget(clearHistoryToolButton, 0, Qt::AlignHCenter); + historyLayout->addWidget(copyHistoryToolButton, 0, Qt::AlignHCenter); + historyLayout->addWidget(m_infoShowHistoryCountSpinBox, 0, Qt::AlignHCenter); + } + break; + case Location::HorizontalToolBox: + { + historyGroupBox->setTitle("History"); + QVBoxLayout* historyLayout = new QVBoxLayout(historyGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(historyLayout, 2, 4); + historyLayout->addWidget(clearHistoryToolButton, 0, Qt::AlignHCenter); + historyLayout->addWidget(copyHistoryToolButton, 0, Qt::AlignHCenter); + historyLayout->addWidget(m_infoShowHistoryCountSpinBox, 0, Qt::AlignHCenter); + } + break; + case Location::VerticalToolBox: + { + historyGroupBox->setTitle("History"); + QHBoxLayout* historyLayout = new QHBoxLayout(historyGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(historyLayout, 5, 6); + historyLayout->addWidget(clearHistoryToolButton, 0); + historyLayout->addWidget(copyHistoryToolButton, 0); + historyLayout->addWidget(m_infoShowHistoryCountSpinBox, 0); + historyAlignment = Qt::AlignLeft; + } + break; + } + + QBoxLayout* historySymbolLayout(NULL); + Qt::Alignment symbolAlignment = Qt::Alignment(); + switch (m_location) { + case Location::Dialog: + case Location::HorizontalToolBox: + historySymbolLayout = new QVBoxLayout(); + symbolAlignment = Qt::AlignHCenter; + break; + case Location::VerticalToolBox: + historySymbolLayout = new QVBoxLayout(); + break; + } + CaretAssert(historySymbolLayout); + WuQtUtilities::setLayoutSpacingAndMargins(historySymbolLayout, 4, 0); + if (historyLabel != NULL) { + historySymbolLayout->addWidget(historyLabel); + } + historySymbolLayout->addWidget(historyGroupBox, 0, historyAlignment); + historySymbolLayout->addWidget(removeSymbolsButton, 0, symbolAlignment); + historySymbolLayout->addStretch(); + + QWidget* widget = new QWidget(); + QBoxLayout* layout(NULL); + switch (m_location) { + case Location::Dialog: + case Location::HorizontalToolBox: + layout = new QHBoxLayout(widget); + break; + case Location::VerticalToolBox: + layout = new QVBoxLayout(widget); + break; + } + CaretAssert(layout); + WuQtUtilities::setLayoutSpacingAndMargins(layout, + 0, + 2); + layout->addWidget(m_infoTextBrowser, 100); + layout->addLayout(historySymbolLayout, 0); + + return widget; +} + +/** + * Called when history spin box is changed. + * + * @param value + * New value + */ +void +IdentificationDisplayWidget::infoShowHistoryCountSpinBoxValueChanged(int value) +{ + IdentificationHistoryManager* historyManager = getHistoryManager(); + historyManager->setShowLastHistoryCount(value); + updateInfoWidget(false); +} + +/** + * Called when clear history tool button is clicked + */ +void IdentificationDisplayWidget::infoClearHistoryToolButtonClicked() +{ + IdentificationHistoryManager* historyManager = getHistoryManager(); + historyManager->clearHistory(); + updateInfoWidget(false); +} + +/** + * Called when remove symbols button is clicked. + */ +void +IdentificationDisplayWidget::infoRemoveSymbolsButtonClicked() +{ + Brain* brain = GuiManager::get()->getBrain(); + IdentificationManager* idManager = brain->getIdentificationManager(); + idManager->removeAllIdentifiedSymbols(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * @return The identification history manager + */ +IdentificationHistoryManager* +IdentificationDisplayWidget::getHistoryManager() +{ + return GuiManager::get()->getBrain()->getIdentificationManager()->getIdentificationHistoryManager(); +} + +/** + * @return Instance of the filtering files widget + */ +QWidget* +IdentificationDisplayWidget::createFilteringFilesWidget() +{ + m_fileFilteringTableWidget = new IdentificationFileFilteringTableWidget(); + QGroupBox* filesGroupBox = new QGroupBox("File Identification"); + QVBoxLayout* filesLayout = new QVBoxLayout(filesGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(filesLayout, 0, 0); + filesLayout->addWidget(m_fileFilteringTableWidget, 100); + + + return filesGroupBox; +} + + +/** + * @return Instance of the filtering widget + */ +QWidget* +IdentificationDisplayWidget::createFilteringSettingsWidget() +{ + QWidget* tabFilterWidget = new QGroupBox("Show Files from Enabled Overlays"); + QVBoxLayout* tabFilterLayout = new QVBoxLayout(tabFilterWidget); + m_tabFilterButtonGroup = new QButtonGroup(this); + QObject::connect(m_tabFilterButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &IdentificationDisplayWidget::tabFilterRadioButtonClicked); + + std::vector tabFilterEnums; + IdentificationFilterTabSelectionEnum::getAllEnums(tabFilterEnums); + for (auto tf : tabFilterEnums) { + QRadioButton* rb = new QRadioButton(IdentificationFilterTabSelectionEnum::toGuiName(tf)); + rb->setToolTip(IdentificationFilterTabSelectionEnum::toToolTip(tf)); + /* second argument is integer ID that encodes the integer for tab filtering enum */ + m_tabFilterButtonGroup->addButton(rb, + IdentificationFilterTabSelectionEnum::toIntegerCode(tf)); + tabFilterLayout->addWidget(rb); + } + + m_filteringCiftiLoadingCheckBox = new QCheckBox("CIFTI Row/Column"); + m_filteringCiftiLoadingCheckBox->setToolTip("Show the row/column for any CIFTI File data loaded " + "in response to an identification operation"); + + m_filteringBorderCheckBox = new QCheckBox("Border"); + m_filteringBorderCheckBox->setToolTip("Enable identification of borders"); + + m_filteringFociCheckBox = new QCheckBox("Focus"); + m_filteringFociCheckBox->setToolTip("Enable identification of foci"); + + WuQValueChangedSignalWatcher* signalWatcher = new WuQValueChangedSignalWatcher(this); + QObject::connect(signalWatcher, &WuQValueChangedSignalWatcher::valueChanged, + this, &IdentificationDisplayWidget::filteringChanged); + signalWatcher->addObject(m_filteringCiftiLoadingCheckBox); + signalWatcher->addObject(m_filteringBorderCheckBox); + signalWatcher->addObject(m_filteringFociCheckBox); + + QWidget* showDataWidget = new QGroupBox("Data Identification"); + QVBoxLayout* showLayout = new QVBoxLayout(showDataWidget); + showLayout->addWidget(m_filteringBorderCheckBox); + showLayout->addWidget(m_filteringCiftiLoadingCheckBox); + showLayout->addWidget(m_filteringFociCheckBox); + + Qt::Alignment alignment = Qt::Alignment(); + QWidget* widget = new QWidget(); + QBoxLayout* layout(NULL); + switch (m_location) { + case Location::Dialog: + alignment = Qt::AlignTop; + layout = new QHBoxLayout(widget); + break; + case Location::HorizontalToolBox: + case Location::VerticalToolBox: + alignment = Qt::AlignLeft; + layout = new QVBoxLayout(widget); + break; + } + + CaretAssert(layout); + layout->setSpacing(2); + layout->addWidget(tabFilterWidget, 0, alignment); + layout->addWidget(showDataWidget, 0, alignment); + layout->addStretch(); + + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setWidget(widget); + return scrollArea; +} + +/** + * Called when a tab filtering radio button is clicked + * @param buttonID + * ID of button that was clicked + */ +void +IdentificationDisplayWidget::tabFilterRadioButtonClicked(int32_t buttonID) +{ + bool validFlag(false); + const IdentificationFilterTabSelectionEnum::Enum tabFilter = IdentificationFilterTabSelectionEnum::fromIntegerCode(buttonID, + &validFlag); + IdentificationManager* idManager = GuiManager::get()->getBrain()->getIdentificationManager(); + IdentificationFilter* filter = idManager->getIdentificationFilter(); + filter->setTabFiltering(tabFilter); +} + +/** + * Update the filtering widget's content + */ +void +IdentificationDisplayWidget::updateFilteringWidget() +{ + const IdentificationManager* idManager = GuiManager::get()->getBrain()->getIdentificationManager(); + const IdentificationFilter* filter = idManager->getIdentificationFilter(); + + const IdentificationFilterTabSelectionEnum::Enum tabFilter = filter->getTabFiltering(); + const int32_t integerCode = IdentificationFilterTabSelectionEnum::toIntegerCode(tabFilter); + QAbstractButton* filterAbstractButton = m_tabFilterButtonGroup->button(integerCode); + CaretAssert(filterAbstractButton); + QRadioButton* filterRadioButton = qobject_cast(filterAbstractButton); + CaretAssert(filterRadioButton); + filterRadioButton->setChecked(true); + + m_filteringCiftiLoadingCheckBox->setChecked(filter->isShowCiftiLoadingEnabled()); + m_filteringBorderCheckBox->setChecked(filter->isShowBorderEnabled()); + m_filteringFociCheckBox->setChecked(filter->isShowFociEnabled()); + + m_fileFilteringTableWidget->updateContent(); +} + +/** + * Called when a filtering item is changed + */ +void +IdentificationDisplayWidget::filteringChanged() +{ + IdentificationManager* idManager = GuiManager::get()->getBrain()->getIdentificationManager(); + IdentificationFilter* filter = idManager->getIdentificationFilter(); + filter->setShowCiftiLoadingEnabled(m_filteringCiftiLoadingCheckBox->isChecked()); + filter->setShowBorderEnabled(m_filteringBorderCheckBox->isChecked()); + filter->setShowFociEnabled(m_filteringFociCheckBox->isChecked()); +} + +/** + * Update the symbols widget's content + */ +void +IdentificationDisplayWidget::updateSymbolsWidget() +{ + Brain* brain = GuiManager::get()->getBrain(); + IdentificationManager* info = brain->getIdentificationManager(); + + m_symbolsShowSurfaceIdCheckBox->setChecked(info->isShowSurfaceIdentificationSymbols()); + m_symbolsShowVolumeIdCheckBox->setChecked(info->isShowVolumeIdentificationSymbols()); + m_symbolsSurfaceContralateralVertexCheckBox->setChecked(info->isContralateralIdentificationEnabled()); + + m_symbolsIdColorComboBox->setSelectedColor(info->getIdentificationSymbolColor()); + m_symbolsContralateralIdColorComboBox->setSelectedColor(info->getIdentificationContralateralSymbolColor()); + m_symbolSizeTypeComboBox->setSelectedItem(info->getIdentificationSymbolSizeType()); + + QSignalBlocker symbolSizeBlocker(m_symbolsIdDiameterSpinBox); + QSignalBlocker recentSymbolSizeBlocker(m_symbolsMostRecentIdDiameterSpinBox); + switch (info->getIdentificationSymbolSizeType()) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + m_symbolsIdDiameterSpinBox->setValue(info->getIdentificationSymbolSize()); + m_symbolsMostRecentIdDiameterSpinBox->setValue(info->getMostRecentIdentificationSymbolSize()); + m_symbolsIdDiameterSpinBox->setSuffix("mm"); + m_symbolsMostRecentIdDiameterSpinBox->setSuffix("mm"); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + m_symbolsIdDiameterSpinBox->setValue(info->getIdentificationSymbolPercentageSize()); + m_symbolsMostRecentIdDiameterSpinBox->setValue(info->getMostRecentIdentificationSymbolPercentageSize()); + m_symbolsIdDiameterSpinBox->setSuffix("%"); + m_symbolsMostRecentIdDiameterSpinBox->setSuffix("%"); + break; + } +} + +/** + * @return Instance of the symbols widget + */ +QWidget* +IdentificationDisplayWidget::createSymbolsWidget() +{ + m_symbolsShowSurfaceIdCheckBox = new QCheckBox("Show Surface ID Symbols"); + m_symbolsShowSurfaceIdCheckBox->setToolTip("Enable display of surface identification symbols"); + + m_symbolsShowVolumeIdCheckBox = new QCheckBox("Show Volume ID Symbols"); + m_symbolsShowVolumeIdCheckBox->setToolTip("Enable display of volume identification symbols"); + + m_symbolsSurfaceContralateralVertexCheckBox = new QCheckBox("Show Surface Contralateral"); + m_symbolsSurfaceContralateralVertexCheckBox->setToolTip("Enable display of contralateral surface identification symbols"); + + QLabel* idSymbolColorLabel = new QLabel("ID Color:"); + m_symbolsIdColorComboBox = new CaretColorEnumComboBox(CaretColorEnumComboBox::CustomColorModeEnum::DISABLED, + CaretColorEnumComboBox::NoneColorModeEnum::DISABLED, + this); + m_symbolsIdColorComboBox->setToolTip("Set color of identification symbols shown on surfaces and volumes"); + + QLabel* contralateralIdSymbolColorLabel = new QLabel("Contralateral:"); + m_symbolsContralateralIdColorComboBox = new CaretColorEnumComboBox(CaretColorEnumComboBox::CustomColorModeEnum::DISABLED, + CaretColorEnumComboBox::NoneColorModeEnum::DISABLED, + this); + m_symbolsContralateralIdColorComboBox->setToolTip("Set color of identification symbol on contralateral surface"); + + QLabel* symbolSizeTypeLabel = new QLabel("Diameter Type:"); + m_symbolSizeTypeComboBox = new EnumComboBoxTemplate(this); + m_symbolSizeTypeComboBox->setup(); + QObject::connect(m_symbolSizeTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &IdentificationDisplayWidget::symbolSizeTypeComboBoxActivated); + m_symbolSizeTypeComboBox->setToolTip(IdentificationSymbolSizeTypeEnum::getToolTip("identification")); + + QLabel* symbolDiameterLabel = new QLabel("Diameter:"); + m_symbolsIdDiameterSpinBox = new QDoubleSpinBox(); + m_symbolsIdDiameterSpinBox->setRange(0.1, 1000.0); + m_symbolsIdDiameterSpinBox->setSingleStep(0.1); + m_symbolsIdDiameterSpinBox->setDecimals(1); + m_symbolsIdDiameterSpinBox->setSuffix("mm"); + m_symbolsIdDiameterSpinBox->setToolTip("Set diameter of identification symbols"); + + QLabel* mostRecentSymbolDiameterLabel = new QLabel("Most Recent:"); + m_symbolsMostRecentIdDiameterSpinBox = new QDoubleSpinBox(); + m_symbolsMostRecentIdDiameterSpinBox->setRange(0.1, 1000.0); + m_symbolsMostRecentIdDiameterSpinBox->setSingleStep(0.1); + m_symbolsMostRecentIdDiameterSpinBox->setDecimals(1); + m_symbolsMostRecentIdDiameterSpinBox->setSuffix("mm"); + m_symbolsMostRecentIdDiameterSpinBox->setToolTip("Set diamater of most recent identification symbol"); + + WuQValueChangedSignalWatcher* signalWatcher = new WuQValueChangedSignalWatcher(this); + QObject::connect(signalWatcher, &WuQValueChangedSignalWatcher::valueChanged, + this, &IdentificationDisplayWidget::symbolChanged); + signalWatcher->addObject(m_symbolsShowSurfaceIdCheckBox); + signalWatcher->addObject(m_symbolsShowVolumeIdCheckBox); + signalWatcher->addObject(m_symbolsSurfaceContralateralVertexCheckBox); + signalWatcher->addObject(m_symbolsIdColorComboBox); + signalWatcher->addObject(m_symbolsContralateralIdColorComboBox); + signalWatcher->addObject(m_symbolsIdDiameterSpinBox); + signalWatcher->addObject(m_symbolsMostRecentIdDiameterSpinBox); + + QVBoxLayout* showLayout = new QVBoxLayout(); + showLayout->addWidget(m_symbolsShowSurfaceIdCheckBox); + showLayout->addWidget(m_symbolsShowVolumeIdCheckBox); + showLayout->addWidget(m_symbolsSurfaceContralateralVertexCheckBox); + showLayout->addStretch(); + + QGridLayout* symbolLayout = new QGridLayout(); + int32_t row(0); + symbolLayout->addWidget(idSymbolColorLabel, + row, 0); + symbolLayout->addWidget(m_symbolsIdColorComboBox->getWidget(), + row, 1); + row++; + symbolLayout->addWidget(contralateralIdSymbolColorLabel, + row, 0); + symbolLayout->addWidget(m_symbolsContralateralIdColorComboBox->getWidget(), + row, 1); + row++; + symbolLayout->addWidget(symbolSizeTypeLabel, + row, 0); + symbolLayout->addWidget(m_symbolSizeTypeComboBox->getWidget(), + row, 1); + row++; + symbolLayout->addWidget(symbolDiameterLabel, + row, 0); + symbolLayout->addWidget(m_symbolsIdDiameterSpinBox, + row, 1); + row++; + symbolLayout->addWidget(mostRecentSymbolDiameterLabel, + row, 0); + symbolLayout->addWidget(m_symbolsMostRecentIdDiameterSpinBox, + row, 1); + row++; + symbolLayout->setRowStretch(row, 100); + + Qt::Alignment alignment = Qt::Alignment(); + QWidget* widget = new QWidget(); + QBoxLayout* boxLayout(NULL); + switch (m_location) { + case Location::Dialog: + alignment = Qt::AlignTop; + boxLayout = new QHBoxLayout(widget); + break; + case Location::HorizontalToolBox: + case Location::VerticalToolBox: + alignment = Qt::AlignLeft; + boxLayout = new QVBoxLayout(widget); + break; + } + CaretAssert(boxLayout); + + WuQtUtilities::setLayoutSpacingAndMargins(boxLayout, 4, 4); + boxLayout->addLayout(symbolLayout); + boxLayout->addSpacing(10); + boxLayout->addLayout(showLayout); + boxLayout->addStretch(); + + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setWidget(widget); + + return scrollArea; +} + +/** + * Called when a symbols item is changed + */ +void +IdentificationDisplayWidget::symbolChanged() +{ + Brain* brain = GuiManager::get()->getBrain(); + IdentificationManager* info = brain->getIdentificationManager(); + + info->setShowSurfaceIdentificationSymbols(m_symbolsShowSurfaceIdCheckBox->isChecked()); + info->setShowVolumeIdentificationSymbols(m_symbolsShowVolumeIdCheckBox->isChecked()); + info->setContralateralIdentificationEnabled(m_symbolsSurfaceContralateralVertexCheckBox->isChecked()); + info->setIdentificationSymbolColor(m_symbolsIdColorComboBox->getSelectedColor()); + info->setIdentificationContralateralSymbolColor(m_symbolsContralateralIdColorComboBox->getSelectedColor()); + switch (info->getIdentificationSymbolSizeType()) { + case IdentificationSymbolSizeTypeEnum::MILLIMETERS: + info->setIdentificationSymbolSize(m_symbolsIdDiameterSpinBox->value()); + info->setMostRecentIdentificationSymbolSize(m_symbolsMostRecentIdDiameterSpinBox->value()); + break; + case IdentificationSymbolSizeTypeEnum::PERCENTAGE: + info->setIdentificationSymbolPercentageSize(m_symbolsIdDiameterSpinBox->value()); + info->setMostRecentIdentificationSymbolPercentageSize(m_symbolsMostRecentIdDiameterSpinBox->value()); + break; + } + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Called when symbol size type combo box changed + */ +void +IdentificationDisplayWidget::symbolSizeTypeComboBoxActivated() +{ + Brain* brain = GuiManager::get()->getBrain(); + IdentificationManager* info = brain->getIdentificationManager(); + info->setIdentificationSymbolSizeType(m_symbolSizeTypeComboBox->getSelectedItem()); + updateSymbolsWidget(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +IdentificationDisplayWidget::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "IdentificationDisplayWidget", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +IdentificationDisplayWidget::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + +/** + * @return Instance of the line layer chart symbols widget + */ +QWidget* +IdentificationDisplayWidget::createChartLineLayerSymbolsWidget() +{ + QLabel* symbolDiameterLabel = new QLabel("Symbol Diameter:"); + m_chartLineLayerSymbolSizeSpinBox = new QDoubleSpinBox(); + m_chartLineLayerSymbolSizeSpinBox->setRange(0.1, 1000.0); + m_chartLineLayerSymbolSizeSpinBox->setSingleStep(0.1); + m_chartLineLayerSymbolSizeSpinBox->setDecimals(1); + m_chartLineLayerSymbolSizeSpinBox->setSuffix("%"); + m_chartLineLayerSymbolSizeSpinBox->setToolTip("Size of selected chart lines point symbol as a percentage\n" + "of viewport height"); + + QLabel* toolTipTextSizeLabel = new QLabel("Text Height:"); + m_chartLineLayerToolTipTextSizeSpinBox = new QDoubleSpinBox(); + m_chartLineLayerToolTipTextSizeSpinBox->setRange(0.1, 1000.0); + m_chartLineLayerToolTipTextSizeSpinBox->setSingleStep(0.1); + m_chartLineLayerToolTipTextSizeSpinBox->setDecimals(1); + m_chartLineLayerToolTipTextSizeSpinBox->setSuffix("%"); + m_chartLineLayerToolTipTextSizeSpinBox->setToolTip("Size of selected chart lines point tooltip text as a\n" + "percentage of viewport height"); + + WuQValueChangedSignalWatcher* signalWatcher = new WuQValueChangedSignalWatcher(this); + QObject::connect(signalWatcher, &WuQValueChangedSignalWatcher::valueChanged, + this, &IdentificationDisplayWidget::chartLineLayerSymbolChanged); + signalWatcher->addObject(m_chartLineLayerSymbolSizeSpinBox); + signalWatcher->addObject(m_chartLineLayerToolTipTextSizeSpinBox); + + QGridLayout* symbolLayout = new QGridLayout(); + int32_t row(0); + symbolLayout->addWidget(symbolDiameterLabel, + row, 0); + symbolLayout->addWidget(m_chartLineLayerSymbolSizeSpinBox, + row, 1); + row++; + symbolLayout->addWidget(toolTipTextSizeLabel, + row, 0); + symbolLayout->addWidget(m_chartLineLayerToolTipTextSizeSpinBox, + row, 1); + row++; + + Qt::Alignment alignment = Qt::Alignment(); + QWidget* widget = new QWidget(); + QBoxLayout* boxLayout(NULL); + switch (m_location) { + case Location::Dialog: + alignment = Qt::AlignTop; + boxLayout = new QHBoxLayout(widget); + break; + case Location::HorizontalToolBox: + case Location::VerticalToolBox: + alignment = Qt::AlignLeft; + boxLayout = new QVBoxLayout(widget); + break; + } + CaretAssert(boxLayout); + + WuQtUtilities::setLayoutSpacingAndMargins(boxLayout, 4, 4); + boxLayout->addLayout(symbolLayout); + boxLayout->addStretch(); + + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setWidget(widget); + + return scrollArea; +} + +/** + * Called when a symbols item is changed + */ +void +IdentificationDisplayWidget::chartLineLayerSymbolChanged() +{ + Brain* brain = GuiManager::get()->getBrain(); + IdentificationManager* info = brain->getIdentificationManager(); + + info->setChartLineLayerSymbolSize(m_chartLineLayerSymbolSizeSpinBox->value()); + info->setChartLineLayerToolTipTextSize(m_chartLineLayerToolTipTextSizeSpinBox->value()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Update the chart line layer symbols widget + */ +void +IdentificationDisplayWidget::updateChartLineLayerSymbolsWidget() +{ + Brain* brain = GuiManager::get()->getBrain(); + IdentificationManager* info = brain->getIdentificationManager(); + + QSignalBlocker symbolBlocker(m_chartLineLayerSymbolSizeSpinBox); + m_chartLineLayerSymbolSizeSpinBox->setValue(info->getChartLineLayerSymbolSize()); + QSignalBlocker toolTipBlocker(m_chartLineLayerToolTipTextSizeSpinBox); + m_chartLineLayerToolTipTextSizeSpinBox->setValue(info->getChartLineLayerToolTipTextSize()); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/IdentificationDisplayWidget.h connectome-workbench-1.5.0/src/GuiQt/IdentificationDisplayWidget.h --- connectome-workbench-1.4.2/src/GuiQt/IdentificationDisplayWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/IdentificationDisplayWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,194 @@ +#ifndef __IDENTIFICATION_DISPLAY_WIDGET_H__ +#define __IDENTIFICATION_DISPLAY_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "EventListenerInterface.h" +#include "SceneableInterface.h" + +class QAbstractButton; +class QButtonGroup; +class QCheckBox; +class QDoubleSpinBox; +class QRadioButton; +class QSpinBox; +class QTabWidget; +class QTextBrowser; + +namespace caret { + class CaretColorEnumComboBox; + class EnumComboBoxTemplate; + class IdentificationFileFilteringTableWidget; + class IdentificationHistoryManager; + class SceneClassAssistant; + + class IdentificationDisplayWidget : public QWidget, public EventListenerInterface, public SceneableInterface { + + Q_OBJECT + + public: + enum class Location { + Dialog, + HorizontalToolBox, + VerticalToolBox + }; + IdentificationDisplayWidget(const Location location, + QWidget* parent = 0); + + virtual ~IdentificationDisplayWidget(); + + IdentificationDisplayWidget(const IdentificationDisplayWidget&) = delete; + + IdentificationDisplayWidget& operator=(const IdentificationDisplayWidget&) = delete; + + // ADD_NEW_METHODS_HERE + + virtual void receiveEvent(Event* event); + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private slots: + void filteringChanged(); + + void symbolChanged(); + + void infoShowHistoryCountSpinBoxValueChanged(int value); + + void infoClearHistoryToolButtonClicked(); + + void infoRemoveSymbolsButtonClicked(); + + void tabFilterRadioButtonClicked(int buttonID); + + void chartLineLayerSymbolChanged(); + + void symbolSizeTypeComboBoxActivated(); + + private: + void updateContent(const bool scrollTextToEndFlag); + + std::unique_ptr m_sceneAssistant; + + QWidget* createInfoWidget(); + + QWidget* createFilteringSettingsWidget(); + + QWidget* createFilteringFilesWidget(); + + QWidget* createSymbolsWidget(); + + QWidget* createChartLineLayerSymbolsWidget(); + + void updateInfoWidget(const bool scrollTextToEndFlag); + + void updateFilteringWidget(); + + void updateSymbolsWidget(); + + void updateChartLineLayerSymbolsWidget(); + + IdentificationHistoryManager* getHistoryManager(); + + const Location m_location; + + QTabWidget* m_tabWidget; + + QWidget* m_infoWidget; + + QTextBrowser* m_infoTextBrowser; + + QSpinBox* m_infoShowHistoryCountSpinBox; + + QWidget* m_filteringFilesWidget; + + QWidget* m_filteringSettingsWidget; + + QButtonGroup* m_tabFilterButtonGroup; + + std::vector m_tabFilteringRadioButtons; + + QCheckBox* m_filteringCiftiLoadingCheckBox; + + QCheckBox* m_filteringBorderCheckBox; + + QCheckBox* m_filteringFociCheckBox; + + IdentificationFileFilteringTableWidget* m_fileFilteringTableWidget = NULL; + + QWidget* m_symbolsWidget; + + QCheckBox* m_symbolsShowSurfaceIdCheckBox; + + QCheckBox* m_symbolsShowVolumeIdCheckBox; + + CaretColorEnumComboBox* m_symbolsIdColorComboBox; + + CaretColorEnumComboBox* m_symbolsContralateralIdColorComboBox; + + EnumComboBoxTemplate* m_symbolSizeTypeComboBox; + + QDoubleSpinBox* m_symbolsIdDiameterSpinBox; + + QDoubleSpinBox* m_symbolsMostRecentIdDiameterSpinBox; + + QCheckBox* m_symbolsSurfaceContralateralVertexCheckBox; + + QWidget* m_chartLineLayerSymbolWidget; + + QDoubleSpinBox* m_chartLineLayerSymbolSizeSpinBox; + + QDoubleSpinBox* m_chartLineLayerToolTipTextSizeSpinBox; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __IDENTIFICATION_DISPLAY_WIDGET_DECLARE__ + // +#endif // __IDENTIFICATION_DISPLAY_WIDGET_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_DISPLAY_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/IdentificationFileFilteringRow.cxx connectome-workbench-1.5.0/src/GuiQt/IdentificationFileFilteringRow.cxx --- connectome-workbench-1.4.2/src/GuiQt/IdentificationFileFilteringRow.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/IdentificationFileFilteringRow.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,174 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_FILE_FILTERING_ROW_DECLARE__ +#include "IdentificationFileFilteringRow.h" +#undef __IDENTIFICATION_FILE_FILTERING_ROW_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "EnumComboBoxTemplate.h" +#include "FileIdentificationAttributes.h" +#include "FileIdentificationMapSelectionEnum.h" +#include "WuQValueChangedSignalWatcher.h" +#include "WuQWidgetObjectGroup.h" + +using namespace caret; + + + +/** + * \class caret::IdentificationFileFilteringRow + * \brief Contains a row for file filtering selections + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param gridLayout + * Layout for widgets + */ +IdentificationFileFilteringRow::IdentificationFileFilteringRow(QGridLayout *gridLayout) +: QObject() +{ + m_enabledCheckBox = new QCheckBox(); + + m_displayedLabel = new QLabel(" "); + m_displayedLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + m_fileNameLabel = new QLabel(); + + m_mapModeComboBox = new EnumComboBoxTemplate(this); + m_mapModeComboBox->setup(); + + m_mapNameComboBox = new QComboBox(); + + m_signalWatcher = new WuQValueChangedSignalWatcher(this); + QObject::connect(m_signalWatcher, &WuQValueChangedSignalWatcher::valueChanged, + this, &IdentificationFileFilteringRow::widgetValueChanged); + m_signalWatcher->addObject(m_enabledCheckBox); + m_signalWatcher->addObject(m_mapModeComboBox->getComboBox()); + m_signalWatcher->addObject(m_mapNameComboBox); + + m_objectGroup = new WuQWidgetObjectGroup(this); + m_objectGroup->add(m_enabledCheckBox); + m_objectGroup->add(m_displayedLabel); + m_objectGroup->add(m_fileNameLabel); + m_objectGroup->add(m_mapModeComboBox->getWidget()); + m_objectGroup->add(m_mapNameComboBox); + + const int row = gridLayout->rowCount(); + gridLayout->addWidget(m_enabledCheckBox, + row, COLUMN_ENABLED_CHECKBOX); + gridLayout->addWidget(m_displayedLabel, + row, COLUMN_DISPLAYED_LABEL); + gridLayout->addWidget(m_fileNameLabel, + row, COLUMN_FILE_NAME_LABEL); + gridLayout->addWidget(m_mapModeComboBox->getWidget(), + row, COLUMN_MAP_MODE_COMBO_BOX); + gridLayout->addWidget(m_mapNameComboBox, + row, COLUMN_MAP_NAME_COMBO_BOX); +} + +/** + * Destructor. + */ +IdentificationFileFilteringRow::~IdentificationFileFilteringRow() +{ +} + +/** + * Add column titles to the given layout + * @param gridLayout + * Layout receiving tiltles + */ +void +IdentificationFileFilteringRow::addGridLayoutColumnTitles(QGridLayout* gridLayout) +{ + const int row(0); + gridLayout->addWidget(new QLabel("Show"), + row, COLUMN_ENABLED_CHECKBOX); + gridLayout->addWidget(new QLabel("Displayed"), + row, COLUMN_DISPLAYED_LABEL); + gridLayout->addWidget(new QLabel("File Name"), + row, COLUMN_FILE_NAME_LABEL); + gridLayout->addWidget(new QLabel("Map Mode"), + row, COLUMN_MAP_MODE_COMBO_BOX); + gridLayout->addWidget(new QLabel("Selected Map Name"), + row, COLUMN_MAP_NAME_COMBO_BOX); +} + + +/** + * Update with the given map file + * @param mapFile + * The map file + */ +void +IdentificationFileFilteringRow::updateContent(CaretMappableDataFile* mapFile, + const bool fileDisplayedFlag) +{ + m_mapFile = mapFile; + + m_objectGroup->setVisible(m_mapFile != NULL); + + if (m_mapFile != NULL) { + const FileIdentificationAttributes* atts = m_mapFile->getFileIdentificationAttributes(); + CaretAssert(atts); + + m_enabledCheckBox->setChecked(atts->isEnabled()); + if (fileDisplayedFlag) { + m_displayedLabel->setText("YES"); + } + else { + m_displayedLabel->setText(" "); + } + m_fileNameLabel->setText(m_mapFile->getFileNameNoPath()); + m_mapModeComboBox->setSelectedItem(atts->getMapSelectionMode()); + + m_mapNameComboBox->clear(); + const int32_t numMaps = m_mapFile->getNumberOfMaps(); + for (int32_t i = 0; i < numMaps; i++) { + m_mapNameComboBox->addItem(m_mapFile->getMapName(i)); + } + m_mapNameComboBox->setCurrentIndex(atts->getMapIndex()); + } +} + +/** + * Called when a wldget is changed by the user + */ +void +IdentificationFileFilteringRow::widgetValueChanged() +{ + if (m_mapFile != NULL) { + FileIdentificationAttributes* atts = m_mapFile->getFileIdentificationAttributes(); + atts->setEnabled(m_enabledCheckBox->isChecked()); + atts->setMapSelectionMode(m_mapModeComboBox->getSelectedItem()); + atts->setMapIndex(m_mapNameComboBox->currentIndex()); + } +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/IdentificationFileFilteringRow.h connectome-workbench-1.5.0/src/GuiQt/IdentificationFileFilteringRow.h --- connectome-workbench-1.4.2/src/GuiQt/IdentificationFileFilteringRow.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/IdentificationFileFilteringRow.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,101 @@ +#ifndef __IDENTIFICATION_FILE_FILTERING_ROW_H__ +#define __IDENTIFICATION_FILE_FILTERING_ROW_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QCheckBox; +class QComboBox; +class QGridLayout; +class QLabel; + + + +namespace caret { + class CaretMappableDataFile; + class EnumComboBoxTemplate; + class WuQValueChangedSignalWatcher; + class WuQWidgetObjectGroup; + + class IdentificationFileFilteringRow : public QObject { + + Q_OBJECT + + public: + IdentificationFileFilteringRow(QGridLayout *gridLayout); + + virtual ~IdentificationFileFilteringRow(); + + IdentificationFileFilteringRow(const IdentificationFileFilteringRow&) = delete; + + IdentificationFileFilteringRow& operator=(const IdentificationFileFilteringRow&) = delete; + + static void addGridLayoutColumnTitles(QGridLayout* gridLayout); + + void updateContent(CaretMappableDataFile* mapFile, + const bool fileDisplayedFlag); + + // ADD_NEW_METHODS_HERE + + private slots: + void widgetValueChanged(); + + private: + enum COLUMNS { + COLUMN_DISPLAYED_LABEL, + COLUMN_FILE_NAME_LABEL, + COLUMN_ENABLED_CHECKBOX, + COLUMN_MAP_MODE_COMBO_BOX, + COLUMN_MAP_NAME_COMBO_BOX + }; + + CaretMappableDataFile* m_mapFile = NULL; + + QCheckBox* m_enabledCheckBox; + + QLabel* m_displayedLabel; + + QLabel* m_fileNameLabel; + + EnumComboBoxTemplate* m_mapModeComboBox; + + QComboBox* m_mapNameComboBox; + + WuQValueChangedSignalWatcher* m_signalWatcher; + + WuQWidgetObjectGroup* m_objectGroup; + + // ADD_NEW_MEMBERS_HERE + + friend class IdentificationFileFilteringTableWidget; + }; + +#ifdef __IDENTIFICATION_FILE_FILTERING_ROW_DECLARE__ + // +#endif // __IDENTIFICATION_FILE_FILTERING_ROW_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_FILE_FILTERING_ROW_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/IdentificationFileFilteringTableWidget.cxx connectome-workbench-1.5.0/src/GuiQt/IdentificationFileFilteringTableWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/IdentificationFileFilteringTableWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/IdentificationFileFilteringTableWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,469 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __IDENTIFICATION_FILE_FILTERING_TABLE_WIDGET_DECLARE__ +#include "IdentificationFileFilteringTableWidget.h" +#undef __IDENTIFICATION_FILE_FILTERING_TABLE_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "EnumComboBoxTemplate.h" +#include "EventCaretMappableDataFilesGet.h" +#include "EventGetDisplayedDataFiles.h" +#include "EventManager.h" +#include "FileIdentificationAttributes.h" +#include "FilePathNamePrefixCompactor.h" +#include "SessionManager.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::IdentificationFileFilteringTableWidget + * \brief Widget containing data files for identificaiton selection + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +IdentificationFileFilteringTableWidget::IdentificationFileFilteringTableWidget() +: QTableWidget() +{ + setAlternatingRowColors(true); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + QObject::connect(this, &QTableWidget::cellClicked, + this, &IdentificationFileFilteringTableWidget::tableCellClicked); +} + +/** + * Destructor. + */ +IdentificationFileFilteringTableWidget::~IdentificationFileFilteringTableWidget() +{ +} + +/** + * Update number of rows in table + * @param numberOfFiles + * Number of files for table + */ +void +IdentificationFileFilteringTableWidget::updateTableRows(const int32_t numberOfFiles) +{ + const int32_t numExistingRows = rowCount(); + + if (numberOfFiles != numExistingRows) { + /* + * Resize table to match number of files + */ + setRowCount(numberOfFiles); + setColumnCount(COLUMN_COUNT); + + if (numExistingRows > numberOfFiles) { + /* + * Remove extra items + */ + m_mapFiles.resize(numberOfFiles); + m_mapModeComboBoxes.resize(numberOfFiles); + m_mapNameComboBoxes.resize(numberOfFiles); + } + } + +// if (numberOfFiles > numExistingRows) { +// setRowCount(numberOfFiles); +// setColumnCount(COLUMN_COUNT); +// } + + /* + * If needed, add additional rows + */ + for (int32_t iRow = numExistingRows; iRow < numberOfFiles; iRow++) { + m_mapFiles.push_back(NULL); + + for (int32_t iCol = 0; iCol < COLUMN_COUNT; iCol++) { + QWidget* widget(NULL); + QTableWidgetItem* tableItem(NULL); + bool checkedItemFlag(false); + const COLUMNS column = static_cast(iCol); + switch (column) { + case COLUMN_COUNT: + CaretAssert(0); + break; + case COLUMN_DISPLAYED_LABEL: + tableItem = new QTableWidgetItem(); + break; + case COLUMN_ENABLED_CHECKBOX: + tableItem = new QTableWidgetItem(); + checkedItemFlag = true; + break; + case COLUMN_FILE_NAME_LABEL: + tableItem = new QTableWidgetItem(); + break; + case COLUMN_MAP_MODE_COMBO_BOX: + { + EnumComboBoxTemplate* mapModeComboBox = new EnumComboBoxTemplate(this); + m_mapModeComboBoxes.push_back(mapModeComboBox); + + mapModeComboBox->setup(); + QObject::connect(mapModeComboBox, &EnumComboBoxTemplate::itemActivated, + [=] { mapModeComboBoxChanged(iRow); }); + + widget = mapModeComboBox->getComboBox(); + } + break; + case COLUMN_MAP_NAME_COMBO_BOX: + { + QComboBox* comboBox = new QComboBox(); + m_mapNameComboBoxes.push_back(comboBox); + QObject::connect(comboBox, QOverload::of(&QComboBox::activated), + [=] { mapNameComboBoxChanged(iRow); }); + + widget = comboBox; + + } + break; + } + + if (tableItem != NULL) { + Qt::ItemFlags flags(tableItem->flags()); + flags.setFlag(Qt::ItemIsUserCheckable, + checkedItemFlag); + tableItem->setFlags(flags); + tableItem->setData(Qt::UserRole, iRow); + setItem(iRow, iCol, tableItem); + } + else if (widget != NULL) { + setCellWidget(iRow, iCol, widget); + } + else { + CaretAssert(0); + } + } + } + +// /* +// * Keep but clear extra cell items +// */ +// const int32_t numRows = rowCount(); +// for (int32_t iRow = 0; iRow < numRows; iRow++) { +// CaretAssertVectorIndex(m_mapFiles, iRow); +// m_mapFiles[iRow] = NULL; +// +// for (int32_t iCol = 0; iCol < COLUMN_COUNT; iCol++) { +// const COLUMNS column = static_cast(iCol); +// +// QTableWidgetItem* tableItem(item(iRow, iCol)); +// QWidget* widget(cellWidget(iRow, iCol)); +// switch (column) { +// case COLUMN_COUNT: +// CaretAssert(0); +// break; +// case COLUMN_DISPLAYED_LABEL: +// CaretAssert(tableItem); +// break; +// case COLUMN_ENABLED_CHECKBOX: +// CaretAssert(tableItem); +// break; +// case COLUMN_FILE_NAME_LABEL: +// CaretAssert(tableItem); +// break; +// case COLUMN_MAP_MODE_COMBO_BOX: +// CaretAssert(widget); +// break; +// case COLUMN_MAP_NAME_COMBO_BOX: +// CaretAssert(widget); +// break; +// } +// +// if (iRow < numberOfFiles) { +// if (tableItem != NULL) { +// Qt::ItemFlags flags = tableItem->flags(); +// flags.setFlag(Qt::ItemIsEnabled, +// true); +// tableItem->setFlags(flags); +// } +// +// if (widget != NULL) { +// widget->setEnabled(true); +// widget->setVisible(true); +// } +// } +// else { +// if (tableItem != NULL) { +// Qt::ItemFlags flags = tableItem->flags(); +// flags.setFlag(Qt::ItemIsEnabled, +// false); +// tableItem->setFlags(flags); +// +// tableItem->setText(""); +// +// if (flags.testFlag(Qt::ItemIsUserCheckable)) { +// tableItem->setCheckState(Qt::Unchecked); +// } +// } +// +// if (widget != NULL) { +// CaretAssert(tableItem == NULL); +// widget->setEnabled(false); +// widget->setVisible(false); +// } +// } +// } +// } +} + +/** + * @return Name for column index + * @param columnIndex + * Index of column + */ +AString +IdentificationFileFilteringTableWidget::getColumnName(const int32_t columnIndex) const +{ + AString name; + + const COLUMNS column = static_cast(columnIndex); + switch (column) { + case COLUMN_COUNT: + name = "PROGRAM ERROR"; + CaretAssert(0); + break; + case COLUMN_DISPLAYED_LABEL: + name = "Displayed"; + break; + case COLUMN_ENABLED_CHECKBOX: + name = "Show"; + break; + case COLUMN_FILE_NAME_LABEL: + name = "Filename"; + break; + case COLUMN_MAP_MODE_COMBO_BOX: + name = "Map Mode"; + break; + case COLUMN_MAP_NAME_COMBO_BOX: + name = "Map Name"; + break; + } + + return name; +} + + +/** + * Update the content with data files + */ +void +IdentificationFileFilteringTableWidget::updateContent() +{ + EventGetDisplayedDataFiles displayedFilesEvent(EventGetDisplayedDataFiles::Mode::FILES_IN_VIEWED_TABS); + EventManager::get()->sendEvent(displayedFilesEvent.getPointer()); + + EventCaretMappableDataFilesGet mapFilesEvent; + EventManager::get()->sendEvent(mapFilesEvent.getPointer()); + + std::vector allMapFiles; + mapFilesEvent.getAllFilesSortedByName(allMapFiles); + + const int32_t numFiles = static_cast(allMapFiles.size()); + + updateTableRows(numFiles); + + CaretAssert(rowCount() >= numFiles); + + /* + * There may be files with identical names but different paths. + * This function will find files with identical names and append + * the minimum path to disambiguate these files + */ + std::vector displayNames; + FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(allMapFiles, + displayNames); + + for (int32_t iRow = 0; iRow < numFiles; iRow++) { + CaretAssertVectorIndex(allMapFiles, iRow); + CaretMappableDataFile* cmdf = allMapFiles[iRow]; + CaretAssert(cmdf); + + CaretAssertVectorIndex(m_mapFiles, iRow); + m_mapFiles[iRow] = cmdf; + + FileIdentificationAttributes* idAtts = cmdf->getFileIdentificationAttributes(); + CaretAssert(idAtts); + + for (int32_t iCol = 0; iCol < COLUMN_COUNT; iCol++) { + const COLUMNS column = static_cast(iCol); + + QTableWidgetItem* tableItem(item(iRow, iCol)); + QWidget* widget(cellWidget(iRow, iCol)); + switch (column) { + case COLUMN_COUNT: + CaretAssert(0); + break; + case COLUMN_DISPLAYED_LABEL: + CaretAssert(tableItem); + if (displayedFilesEvent.isDataFileDisplayed(cmdf)) { + tableItem->setText("Yes"); + } + else { + tableItem->setText(" "); + } + break; + case COLUMN_ENABLED_CHECKBOX: + CaretAssert(tableItem); + if (idAtts->isEnabled()) { + tableItem->setCheckState(Qt::Checked); + } + else { + tableItem->setCheckState(Qt::Unchecked); + } + break; + case COLUMN_FILE_NAME_LABEL: + CaretAssert(tableItem); + CaretAssertVectorIndex(displayNames, iRow); + tableItem->setText(displayNames[iRow]); + break; + case COLUMN_MAP_MODE_COMBO_BOX: + { + CaretAssert(widget); + QComboBox* comboBox = qobject_cast(widget); + CaretAssert(comboBox); + const FileIdentificationMapSelectionEnum::Enum mapMode = idAtts->getMapSelectionMode(); + const int32_t integerValue = FileIdentificationMapSelectionEnum::toIntegerCode(mapMode); + comboBox->setCurrentIndex(integerValue); + } + break; + case COLUMN_MAP_NAME_COMBO_BOX: + { + CaretAssert(widget); + QComboBox* comboBox = qobject_cast(widget); + CaretAssert(comboBox); + comboBox->clear(); + for (int32_t jMap = 0; jMap < cmdf->getNumberOfMaps(); jMap++) { + comboBox->addItem(cmdf->getMapName(jMap)); + } + comboBox->setCurrentIndex(idAtts->getMapIndex()); + } + break; + } + } + } + + /* + * First time files inserted? + */ + if (m_lastNumberOfFiles == 0) { + if (numFiles > 0) { + QStringList columnNames; + for (int32_t i = 0; i < COLUMN_COUNT; i++) { + columnNames << getColumnName(i); + } + setHorizontalHeaderLabels(columnNames); + + resizeColumnsToContents(); + horizontalHeader()->setSectionResizeMode(COLUMN_DISPLAYED_LABEL, QHeaderView::ResizeToContents); + horizontalHeader()->setSectionResizeMode(COLUMN_ENABLED_CHECKBOX, QHeaderView::ResizeToContents); + horizontalHeader()->setSectionResizeMode(COLUMN_MAP_MODE_COMBO_BOX, QHeaderView::ResizeToContents); + horizontalHeader()->setSectionResizeMode(COLUMN_MAP_NAME_COMBO_BOX, QHeaderView::ResizeToContents); + } + } + + m_lastNumberOfFiles = numFiles; +} + +/** + * Called when a table item is clicked + * @param row + * Row clicked + * @param column + * Column clicked + */ +void +IdentificationFileFilteringTableWidget::tableCellClicked(int row, int column) +{ + QTableWidgetItem* cellItem = item(row, column); + if (cellItem != NULL) { + if (column == COLUMN_ENABLED_CHECKBOX) { + bool checkedFlag(false); + switch (cellItem->checkState()) { + case Qt::Checked: + checkedFlag = true; + break; + case Qt::PartiallyChecked: + CaretAssert(0); + break; + case Qt::Unchecked: + checkedFlag = false; + break; + } + + FileIdentificationAttributes* atts = m_mapFiles[row]->getFileIdentificationAttributes(); + CaretAssert(atts); + atts->setEnabled(checkedFlag); + } + } +} + +/** + * Called when a map mode combo box is changed + * @param row + * Row containing the map mode combo box + */ +void +IdentificationFileFilteringTableWidget::mapModeComboBoxChanged(int row) +{ + CaretAssertVectorIndex(m_mapModeComboBoxes, row); + const FileIdentificationMapSelectionEnum::Enum value = + m_mapModeComboBoxes[row]->getSelectedItem(); + + CaretAssertVectorIndex(m_mapFiles, row); + FileIdentificationAttributes* atts = m_mapFiles[row]->getFileIdentificationAttributes(); + CaretAssert(atts); + atts->setMapSelectionMode(value); +} + + +/** + * Called when a map name combo box is changed + * @param row + * Row containing the map name combo box + */ +void +IdentificationFileFilteringTableWidget::mapNameComboBoxChanged(int row) +{ + CaretAssertVectorIndex(m_mapNameComboBoxes, row); + const int32_t mapIndex = m_mapNameComboBoxes[row]->currentIndex(); + + CaretAssertVectorIndex(m_mapFiles, row); + FileIdentificationAttributes* atts = m_mapFiles[row]->getFileIdentificationAttributes(); + CaretAssert(atts); + atts->setMapIndex(mapIndex); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/IdentificationFileFilteringTableWidget.h connectome-workbench-1.5.0/src/GuiQt/IdentificationFileFilteringTableWidget.h --- connectome-workbench-1.4.2/src/GuiQt/IdentificationFileFilteringTableWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/IdentificationFileFilteringTableWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,92 @@ +#ifndef __IDENTIFICATION_FILE_FILTERING_TABLE_WIDGET_H__ +#define __IDENTIFICATION_FILE_FILTERING_TABLE_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "AString.h" + +class QComboBox; +class QTableWidgetItem; + +namespace caret { + class CaretMappableDataFile; + class EnumComboBoxTemplate; + + class IdentificationFileFilteringTableWidget : public QTableWidget { + + Q_OBJECT + + public: + IdentificationFileFilteringTableWidget(); + + virtual ~IdentificationFileFilteringTableWidget(); + + IdentificationFileFilteringTableWidget(const IdentificationFileFilteringTableWidget&) = delete; + + IdentificationFileFilteringTableWidget& operator=(const IdentificationFileFilteringTableWidget&) = delete; + + void updateContent(); + + // ADD_NEW_METHODS_HERE + + private slots: + void tableCellClicked(int row, int column); + + void mapModeComboBoxChanged(int row); + + void mapNameComboBoxChanged(int row); + + private: + enum COLUMNS { + COLUMN_ENABLED_CHECKBOX = 0, + COLUMN_FILE_NAME_LABEL = 1, + COLUMN_DISPLAYED_LABEL = 2, + COLUMN_MAP_MODE_COMBO_BOX = 3, + COLUMN_MAP_NAME_COMBO_BOX = 4, + COLUMN_COUNT = 5 + }; + + AString getColumnName(const int32_t) const; + + void updateTableRows(const int32_t numberOfFiles); + + int32_t m_lastNumberOfFiles = 0; + + std::vector m_mapFiles; + + std::vector m_mapModeComboBoxes; + + std::vector m_mapNameComboBoxes; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __IDENTIFICATION_FILE_FILTERING_TABLE_WIDGET_DECLARE__ + // +#endif // __IDENTIFICATION_FILE_FILTERING_TABLE_WIDGET_DECLARE__ + +} // namespace +#endif //__IDENTIFICATION_FILE_FILTERING_TABLE_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ImageCaptureDialog.cxx connectome-workbench-1.5.0/src/GuiQt/ImageCaptureDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/ImageCaptureDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ImageCaptureDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -64,8 +64,6 @@ using namespace caret; - - /** * \class caret::ImageCaptureDialog * \brief Dialog for capturing images. @@ -973,8 +971,8 @@ std::vector imageFileFilters; AString defaultFileFilter; - ImageFile::getImageFileFilters(imageFileFilters, - defaultFileFilter); + ImageFile::getSaveQFileDialogImageFilters(imageFileFilters, + defaultFileFilter); QString filters; for (std::vector::iterator filterIterator = imageFileFilters.begin(); filterIterator != imageFileFilters.end(); @@ -992,6 +990,8 @@ filters, &defaultFileFilter); if (name.isEmpty() == false) { + name = DataFileTypeEnum::addFileExtensionIfMissing(name, + DataFileTypeEnum::IMAGE); ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setImageFileName(name); updateDestinationSection(); @@ -1112,16 +1112,17 @@ } if (m_saveImageToFileCheckBox->isChecked()) { - std::vector imageFileExtensions; - AString defaultFileExtension; - ImageFile::getImageFileExtensions(imageFileExtensions, - defaultFileExtension); + std::vector readImageFileExtensions, writeImageFileExtensions; + AString defaultImageExtension; + ImageFile::getWorkbenchSupportedImageFileExtensions(readImageFileExtensions, + writeImageFileExtensions, + defaultImageExtension); CaretAssert( ! imageFileName.isEmpty()); bool validExtension = false; - for (std::vector::iterator extensionIterator = imageFileExtensions.begin(); - extensionIterator != imageFileExtensions.end(); + for (std::vector::iterator extensionIterator = writeImageFileExtensions.begin(); + extensionIterator != writeImageFileExtensions.end(); extensionIterator++) { if (imageFileName.endsWith(*extensionIterator)) { validExtension = true; @@ -1129,13 +1130,14 @@ } if ( ! validExtension) { - if (defaultFileExtension.isEmpty() == false) { - imageFileName += ("." + defaultFileExtension); + if (defaultImageExtension.isEmpty() == false) { + imageFileName += ("." + defaultImageExtension); } } try { imageFile.writeFile(imageFileName); + SessionManager::get()->getCaretPreferences()->addToRecentFilesAndOrDirectories(imageFileName); } catch (const DataFileException& /*e*/) { QString msg("Unable to save: " + imageFileName); diff -Nru connectome-workbench-1.4.2/src/GuiQt/ImageSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/ImageSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/ImageSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ImageSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -442,6 +442,7 @@ for (int32_t i = 0; i < numToAdd; i++) { const int buttonID = static_cast(m_imageRadioButtons.size()); QRadioButton* rb = new QRadioButton(""); + const int32_t buttonNumber(m_imageRadioButtons.size() + 1); m_imageRadioButtons.push_back(rb); const int row = m_imageRadioButtonLayout->rowCount(); @@ -453,10 +454,10 @@ const QString buttonName(m_objectNamePrefix + ":Selection:Image" - + QString("%1").arg((int)i+1, 2, 10, QLatin1Char('0'))); + + QString("%1").arg((int)buttonNumber, 2, 10, QLatin1Char('0'))); rb->setObjectName(buttonName); - const QString descriptiveName("Select Image" - + QString("%1").arg(i + 1)); + const QString descriptiveName("Select Image " + + QString("%1").arg(buttonNumber)); WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(rb, descriptiveName, ""); diff -Nru connectome-workbench-1.4.2/src/GuiQt/KeyEvent.cxx connectome-workbench-1.5.0/src/GuiQt/KeyEvent.cxx --- connectome-workbench-1.4.2/src/GuiQt/KeyEvent.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/KeyEvent.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -18,9 +18,12 @@ */ /*LICENSE_END*/ +#include #include #include +#include "BrainOpenGLWidget.h" +#include "CaretAssert.h" #include "KeyEvent.h" using namespace caret; @@ -33,29 +36,67 @@ /** * Constructor. + * @param viewportContent + * Viewport content at location of mouse * @param openGLWidget * OpenGL Widget in which key activity took place. * @param browserWindowIndex * Index of the browser winddow in which key activity took place. * @param keyCode * The key that was pressed. See Qt::Key for list of codes. + * @param mouseX + * Mouse location X + * @param mouseY + * Mouse location Y + * @param mouseValidFlag + * Mouse location valid * @param firstKeyPressFlag * True if this is the first key press when key is held down for a period of time * @param shiftKeyDownFlag * True if the shift key is down */ -KeyEvent::KeyEvent(BrainOpenGLWidget* openGLWidget, +KeyEvent::KeyEvent(const BrainOpenGLViewportContent* viewportContent, + BrainOpenGLWidget* openGLWidget, const int32_t browserWindowIndex, const int32_t keyCode, + const int32_t mouseX, + const int32_t mouseY, + const bool mouseValidFlag, const bool firstKeyPressFlag, const bool shiftKeyDownFlag) : CaretObject(), m_openGLWidget(openGLWidget), m_browserWindowIndex(browserWindowIndex), m_keyCode(keyCode), +m_mouseX(mouseX), +m_mouseY(mouseY), +m_mouseXYValid(mouseValidFlag), m_firstKeyPressFlag(firstKeyPressFlag), m_shiftKeyDownFlag(shiftKeyDownFlag) { + CaretAssert(m_openGLWidget); + + /* + * MUST copy viewport content as it may be deleted by caller + * prior to this instance being deleted + */ + m_viewportContent = NULL; + if (viewportContent != NULL) { + m_viewportContent = new BrainOpenGLViewportContent(*viewportContent); + } + +// const QPoint mousePos = m_openGLWidget->mapFromGlobal(QCursor::pos()); +// m_mouseX = mousePos.x(); +// m_mouseY = m_openGLWidget->height() - mousePos.y(); +// if ((m_mouseX >= 0) +// && (m_mouseX < m_openGLWidget->width()) +// && (m_mouseY >= 0) +// && (m_mouseY < m_openGLWidget->height())) { +// m_mouseXYValid = true; +// } +// else { +// m_mouseXYValid = false; +// } } /** @@ -63,6 +104,9 @@ */ KeyEvent::~KeyEvent() { + if (m_viewportContent != NULL) { + delete m_viewportContent; + } } /** @@ -74,6 +118,15 @@ } /** + * @return The viewport content in which the mouse was pressed. + */ +BrainOpenGLViewportContent* +KeyEvent::getViewportContent() const +{ + return m_viewportContent; +} + +/** * @return The OpenGL Widget in which the key event occurred. */ BrainOpenGLWidget* @@ -137,4 +190,18 @@ return m_shiftKeyDownFlag; } +/* + * Get the mouse X/Y cooordinate in the OpenGL widget (origin is bottom left) + * @param mouseXYOut + * Output with mouse X/Y + * @return True if mouse X/Y is valid, else false + */ +bool +KeyEvent::getMouseXY(std::array& mouseXYOut) const +{ + mouseXYOut[0] = m_mouseX; + mouseXYOut[1] = m_mouseY; + return m_mouseXYValid; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/KeyEvent.h connectome-workbench-1.5.0/src/GuiQt/KeyEvent.h --- connectome-workbench-1.4.2/src/GuiQt/KeyEvent.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/KeyEvent.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,14 +21,16 @@ */ /*LICENSE_END*/ -#include "CaretObject.h" - +#include #include +#include "CaretObject.h" + class QKeyEvent; namespace caret { + class BrainOpenGLViewportContent; class BrainOpenGLWidget; /** @@ -37,9 +39,13 @@ class KeyEvent : public CaretObject { public: - KeyEvent(BrainOpenGLWidget* openGLWidget, + KeyEvent(const BrainOpenGLViewportContent* viewportContent, + BrainOpenGLWidget* openGLWidget, const int32_t browserWindowIndex, const int32_t keyCode, + const int32_t mouseX, + const int32_t mouseY, + const bool mouseValidFlag, const bool firstKeyPressFlag, const bool shiftKeyDownFlag); @@ -55,6 +61,8 @@ public: AString toString() const; + BrainOpenGLViewportContent* getViewportContent() const; + BrainOpenGLWidget* getOpenGLWidget() const; int32_t getBrowserWindowIndex() const; @@ -65,16 +73,27 @@ bool isShiftKeyDownFlag() const; + bool getMouseXY(std::array& mouseXYOut) const; + private: + BrainOpenGLViewportContent* m_viewportContent; + BrainOpenGLWidget* m_openGLWidget; const int32_t m_browserWindowIndex; const int32_t m_keyCode; + int32_t m_mouseX = -1; + + int32_t m_mouseY = -1; + + bool m_mouseXYValid = false; + const bool m_firstKeyPressFlag; const bool m_shiftKeyDownFlag; + }; } // namespace diff -Nru connectome-workbench-1.4.2/src/GuiQt/MacApplication.cxx connectome-workbench-1.5.0/src/GuiQt/MacApplication.cxx --- connectome-workbench-1.4.2/src/GuiQt/MacApplication.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MacApplication.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -31,6 +31,7 @@ #include "CaretAssert.h" #include "EventManager.h" #include "EventOperatingSystemRequestOpenDataFile.h" +#include "GuiManager.h" using namespace caret; @@ -88,6 +89,17 @@ eventWasProcessed = true; } break; + case QEvent::Close: + { + /* + * Must ignore event or it will continue to get sent to other objects + * including windows and may result in crash + */ + event->ignore(); + GuiManager::get()->exitProgram(GuiManager::get()->getActiveBrowserWindow()); + eventWasProcessed = true; + } + break; default: eventWasProcessed = QApplication::event(event); break; diff -Nru connectome-workbench-1.4.2/src/GuiQt/MacDockMenu.cxx connectome-workbench-1.5.0/src/GuiQt/MacDockMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/MacDockMenu.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MacDockMenu.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -27,6 +27,7 @@ #include "Brain.h" #include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventBrowserWindowNew.h" #include "EventGraphicsUpdateOneWindow.h" @@ -61,64 +62,21 @@ /** * Constructor. */ -MacDockMenu::MacDockMenu(QWidget* parent) -: QMenu(parent) +MacDockMenu::MacDockMenu() +: QMenu(0) { /* - * Recent Spec Files + * Will update menu when is "about to be shown */ - const int32_t firstRecentSpecFileIndex = actions().size(); - const int32_t numRecentSpecFiles = BrainBrowserWindow::loadRecentSpecFileMenu(this); - - QList menuActions = actions(); - - const int32_t numberOfRecentSpecFileActions = menuActions.size() - firstRecentSpecFileIndex; - for (int32_t i = firstRecentSpecFileIndex; i < numberOfRecentSpecFileActions; i++) { - m_recentSpecFileActions.push_back(menuActions.at(i)); - } - - if (numRecentSpecFiles > 0) { - addSeparator(); - } - - /* - * Name of spec file - */ - const AString specFileName = GuiManager::get()->getBrain()->getSpecFile()->getFileNameNoPath(); - - /* - * Open Windows - */ - const std::vector browserWindows = GuiManager::get()->getAllOpenBrainBrowserWindows(); - if ( ! browserWindows.empty()) { - for (std::vector::const_iterator bwIter = browserWindows.begin(); - bwIter != browserWindows.end(); - bwIter++) { - const BrainBrowserWindow* bbw = *bwIter; - const AString title = (bbw->windowTitle() - + " " - + specFileName); - - - QAction* action = addAction(title); - action->setData(bbw->getBrowserWindowIndex()); - - m_browserWindowActions.push_back(action); - } + QObject::connect(this, &QMenu::aboutToShow, + this, &MacDockMenu::menuAboutToShow); - addSeparator(); - } - - /* - * Create New Window + /** + * Tell Qt that this is the Dock Menu (a QMenu method) */ - m_newBrowserWindowAction = addAction("New Window"); - - /* - * Connect the menu to a slot for processing menu selections - */ - QObject::connect(this, SIGNAL(triggered(QAction*)), - this, SLOT(menuActionTriggered(QAction*))); +#ifdef CARET_OS_MACOSX + setAsDockMenu(); +#endif // CARET_OS_MACOSX } /** @@ -126,117 +84,85 @@ */ MacDockMenu::~MacDockMenu() { - std::cout << "Deleting Mac Dock Menu" << std::endl; } /** - * Called when an item in the menu is selected. - * - * @param action - * Menu's action that was triggered. + * Called when menu is about to show */ void -MacDockMenu::menuActionTriggered(QAction* action) +MacDockMenu::menuAboutToShow() { - if (action == m_newBrowserWindowAction) { - createNewBrowserWindow(); - } - else if (std::find(m_recentSpecFileActions.begin(), - m_recentSpecFileActions.end(), - action) != m_recentSpecFileActions.end()) { - const AString specFileName = action->data().toString(); - std::vector fileNamesToLoad; - fileNamesToLoad.push_back(specFileName); - - BrainBrowserWindow* browserWindow = GuiManager::get()->getActiveBrowserWindow(); - browserWindow->loadFilesFromCommandLine(fileNamesToLoad, - BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG); - } - else if (std::find(m_browserWindowActions.begin(), - m_browserWindowActions.end(), - action) != m_browserWindowActions.end()) { - const int32_t browserWindowIndex = action->data().toInt(); - - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); - if (bbw != NULL) { - bbw->activateWindow(); - bbw->raise(); - } - } - else if (action != NULL) { - CaretAssertMessage(0, ("Mac Dock menu action not processed: " - + action->text())); - } + /* + * Remove all menu items + */ + clear(); + + QAction* startNewWbViewAction = addAction("Start New wb_view"); + QObject::connect(startNewWbViewAction, &QAction::triggered, + this, &MacDockMenu::startNewWbViewMenuItemTriggered); + + addBrowserWindowActions(); } /** - * Factory method to create/update the Mac Dock Menu. - * On non-Mac platforms, this method does nothing. - * - * With Qt Menus, the "aboutToShow()" signal is used to - * update the content of the menu so that the menu - * can be updated just before it is displayed. However, - * when a menu is added to the Dock, it is converted - * to a platform native menu so the "aboutToShow()" signal - * does not get issued. So, in Workbench, a Workbench event - * is issued when something happens that requires an - * update to the Dock Menu. This results in a new menu - * being created and replacing the current Dock menu. + * Called when start new wb_View menu item is triggered */ void -MacDockMenu::createUpdateMacDockMenu() +MacDockMenu::startNewWbViewMenuItemTriggered() +{ + QStringList noParameters; + QString noWorkingDirectory; + QWidget* parentWidget = GuiManager::get()->getActiveBrowserWindow(); + + GuiManager::startNewWbViewInstance(noParameters, + noWorkingDirectory, + parentWidget); +} + +/** + * Add actions for selecting browser windows + */ +void +MacDockMenu::addBrowserWindowActions() { - const bool DISABLE_DOCK_MENU = true; - if (DISABLE_DOCK_MENU) { - return; - } -#ifdef CARET_OS_MACOSX - BrainBrowserWindow* browserWindow = GuiManager::get()->getActiveBrowserWindow(); - if (browserWindow == NULL) { - return; - } - - MacDockMenu* menu = new MacDockMenu(); - qt_mac_set_dock_menu(menu); - /* - * Previously created menus probably should be deleted but - * it is not clear if QT takes ownership of the menu - * which could result in a "double delete". In Qt 4.8.x, - * the code does not show the menu being deleted at - * any time but if that changes a crash would result. - * - * Another problem is that selection from the Dock - * menu may cause the Dock menu to get recreated. + * Only if 2 or more windows open (clicking Dock icon will show single window) */ -// if (s_previousMacDockMenu != NULL) { -// delete s_previousMacDockMenu; -// } -// s_previousMacDockMenu = menu; -#endif // CARET_OS_MACOSX - + const std::vector browserWindows = GuiManager::get()->getAllOpenBrainBrowserWindows(); + if (browserWindows.size() >= 2) { + /* + * Separator from new wb_view item + */ + addSeparator(); + + for (const auto bbw : browserWindows) { + AString title(bbw->windowTitle()); + const BrowserTabContent* selectedTab = bbw->getBrowserTabContent(); + if (selectedTab != NULL) { + title.append(" " + + selectedTab->getTabName()); + } + const int32_t windowIndex(bbw->getBrowserWindowIndex()); + + QAction* action = addAction(title); + QObject::connect(action, &QAction::triggered, + this, [=]() { browserWindowActionSelected(windowIndex); }); + } + } } /** - * Gets called to create a new browser window. + * Called when a browser window action is selected + * @param browserWindowIndex + * Index of window */ void -MacDockMenu::createNewBrowserWindow() +MacDockMenu::browserWindowActionSelected(const int32_t browserWindowIndex) { - EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW, - true); - BrainBrowserWindow* browserWindow = GuiManager::get()->getActiveBrowserWindow(); - EventBrowserWindowNew eventNewBrowser(browserWindow, NULL); - EventManager::get()->sendEvent(eventNewBrowser.getPointer()); - if (eventNewBrowser.isError()) { - QMessageBox::critical(browserWindow, - "", - eventNewBrowser.getErrorMessage()); - return; + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); + if (bbw != NULL) { + bbw->activateWindow(); + bbw->raise(); } - const int32_t newWindowIndex = eventNewBrowser.getBrowserWindowCreated()->getBrowserWindowIndex(); - EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(newWindowIndex).getPointer()); - EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW, - false); - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(newWindowIndex).getPointer()); } + diff -Nru connectome-workbench-1.4.2/src/GuiQt/MacDockMenu.h connectome-workbench-1.5.0/src/GuiQt/MacDockMenu.h --- connectome-workbench-1.4.2/src/GuiQt/MacDockMenu.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MacDockMenu.h 2021-02-16 19:46:47.000000000 +0000 @@ -33,40 +33,31 @@ Q_OBJECT public: - static void createUpdateMacDockMenu(); - + MacDockMenu(); + virtual ~MacDockMenu(); - // ADD_NEW_METHODS_HERE private slots: + void browserWindowActionSelected(const int32_t browserWindowIndex); - void menuActionTriggered(QAction*); + void menuAboutToShow(); - private: - MacDockMenu(QWidget* parent = 0); + void startNewWbViewMenuItemTriggered(); + private: MacDockMenu(const MacDockMenu&); MacDockMenu& operator=(const MacDockMenu&); - void createNewBrowserWindow(); - - QAction* m_newBrowserWindowAction; - - std::vector m_recentSpecFileActions; - - std::vector m_browserWindowActions; - - static MacDockMenu* s_previousMacDockMenu; + void addBrowserWindowActions(); // ADD_NEW_MEMBERS_HERE }; #ifdef __MAC_DOCK_MENU_DECLARE__ - MacDockMenu* MacDockMenu::s_previousMacDockMenu = NULL; #endif // __MAC_DOCK_MENU_DECLARE__ } // namespace diff -Nru connectome-workbench-1.4.2/src/GuiQt/MacDuplicateMenuBar.cxx connectome-workbench-1.5.0/src/GuiQt/MacDuplicateMenuBar.cxx --- connectome-workbench-1.4.2/src/GuiQt/MacDuplicateMenuBar.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MacDuplicateMenuBar.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,156 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2019 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __MAC_DUPLICATE_MENU_BAR_DECLARE__ -#include "MacDuplicateMenuBar.h" -#undef __MAC_DUPLICATE_MENU_BAR_DECLARE__ - -#include - -#include -#include -#include -#include -#include - - -#include "AString.h" -#include "CaretAssert.h" -using namespace caret; - - - -/** - * \class caret::MacDuplicateMenuBar - * \brief A duplicate of the mac menu bar placed in the main window - * \ingroup GuiQt - * - * The menu on macs is separate from the main window and placed at the - * top of the display. When creating screen images, such as for tutorials, - * it makes it difficult to include the menu bar in the image. This class - * creates a copy of the menu bar in a widget that can be added to - * the main window. - */ - -/** - * Constructor. - * - * @param mainWindow - * Main window whose menu bar is copied. - * @param parent - * Optional parent widget. - */ -MacDuplicateMenuBar::MacDuplicateMenuBar(QMainWindow* mainWindow, - QWidget* parent) -: QWidget(parent) -{ - QHBoxLayout* layout = new QHBoxLayout(this); - QMargins margins = layout->contentsMargins(); - margins.setTop(0); - margins.setBottom(0); - layout->setContentsMargins(margins); - - CaretAssert(mainWindow); - - /* - * Examine contents of menu bar and duplicate all items in it - */ - QList menuList = mainWindow->menuBar()->actions(); - QListIterator iter(menuList); - while (iter.hasNext()) { - QAction* action = iter.next(); - QMenu* menu = action->menu(); - if (menu != NULL) { - QMenu* dupMenu = duplicateMenu(menu); - if (dupMenu != NULL) { - /* - * Cannot add a menu bar to the window so each menu - * is attached to a new QToolButton - */ - QToolButton* tb = new QToolButton(); - tb->setPopupMode(QToolButton::InstantPopup); - tb->setText(menu->title()); - tb->setMenu(dupMenu); - layout->addWidget(tb); - } - } - } - layout->addStretch(); -} - -/** - * Destructor. - */ -MacDuplicateMenuBar::~MacDuplicateMenuBar() -{ -} - -/** - * Recursively duplicate the given menu. - * - * @param copyFromMenu - * Menu that is examined and copied - * @return - * Pointer to duplicated menu or NULL if failed to duplicate. - */ -QMenu* -MacDuplicateMenuBar::duplicateMenu(QMenu* copyFromMenu) -{ - const bool printFlag(false); - - if (printFlag) { - std::cout << std::endl; - std::cout << m_indentText << "Menu: " << copyFromMenu->title() << std::endl; - } - m_indentText.append(" "); - - QMenu* newMenu = new QMenu(copyFromMenu->title()); - QList actionList = copyFromMenu->actions(); - QListIterator iter(actionList); - while (iter.hasNext()) { - QAction* action = iter.next(); - QMenu* subMenu = action->menu(); - if (subMenu != NULL) { - QMenu* dupMenu = duplicateMenu(subMenu); - if (dupMenu != NULL) { - newMenu->addMenu(dupMenu); - } - } - else if (action->isSeparator()) { - if (printFlag) { - std::cout << m_indentText << "Separator" << std::endl; - } - newMenu->addSeparator(); - } - else { - if (printFlag) { - std::cout << m_indentText << "Item: " << action->text() << std::endl; - } - newMenu->addAction(action); - } - } - - m_indentText.resize(m_indentText.length() - 3); - - return newMenu; -} - - diff -Nru connectome-workbench-1.4.2/src/GuiQt/MacDuplicateMenuBar.h connectome-workbench-1.5.0/src/GuiQt/MacDuplicateMenuBar.h --- connectome-workbench-1.4.2/src/GuiQt/MacDuplicateMenuBar.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MacDuplicateMenuBar.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -#ifndef __MAC_DUPLICATE_MENU_BAR_H__ -#define __MAC_DUPLICATE_MENU_BAR_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2019 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - -#include - -class QMainWindow; -class QMenu; -namespace caret { - - class MacDuplicateMenuBar : public QWidget { - - Q_OBJECT - - public: - MacDuplicateMenuBar(QMainWindow* mainWindow, - QWidget* parent = 0); - - virtual ~MacDuplicateMenuBar(); - - MacDuplicateMenuBar(const MacDuplicateMenuBar&) = delete; - - MacDuplicateMenuBar& operator=(const MacDuplicateMenuBar&) = delete; - - - // ADD_NEW_METHODS_HERE - - private: - QMenu* duplicateMenu(QMenu* menu); - - QString m_indentText; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __MAC_DUPLICATE_MENU_BAR_DECLARE__ - // -#endif // __MAC_DUPLICATE_MENU_BAR_DECLARE__ - -} // namespace -#endif //__MAC_DUPLICATE_MENU_BAR_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx connectome-workbench-1.5.0/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -403,7 +403,7 @@ : Qt::Unchecked); CaretAssertVectorIndex(m_colorComboBoxes, iRow); - m_colorComboBoxes[iRow]->setSelectedColor(historyData->getColor()); + m_colorComboBoxes[iRow]->setSelectedColor(historyData->getColorEnum()); CaretAssertVectorIndex(m_lineWidthSpinBoxes, iRow); m_lineWidthSpinBoxes[iRow]->setValue(historyData->getLineWidth()); @@ -543,7 +543,7 @@ if (lineSeriesHistory != NULL) { ChartTwoDataCartesian* data = lineSeriesHistory->getHistoryItem(rowIndex); CaretAssert(data); - data->setColor(m_colorComboBoxes[rowIndex]->getSelectedColor()); + data->setColorEnum(m_colorComboBoxes[rowIndex]->getSelectedColor()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx connectome-workbench-1.5.0/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -57,12 +57,18 @@ : QGroupBox("Color Bar and Palette Settings", parent) { + const QString applyToolTip("When checked, these color bar and palette settings " + "are applied to all maps in the file. If unchecked, " + "a unique setting is allowed for each map in the file.\n" + "\n" + "If this checkbox is disabled (grayed out), the data " + "file uses one color bar and palette setting for all " + "maps. One example is a Data-Series file so that particular " + "data values in all maps are identically colored."); m_applyToAllMapsCheckBox = new QCheckBox("Apply to All Maps"); QObject::connect(m_applyToAllMapsCheckBox, &QCheckBox::clicked, this, &MapSettingsColorBarPaletteOptionsWidget::applyToAllMapsCheckBoxClicked); - m_applyToAllMapsCheckBox->setToolTip("When checked, these color bar and palette settings\n" - "are applied to all maps in the file.\n" - "When disabled, the file uses one palette for all maps."); + WuQtUtilities::setWordWrappedToolTip(m_applyToAllMapsCheckBox, applyToolTip); m_applyToAllFilesAction = new QAction(this); m_applyToAllFilesAction->setText("Apply to Files..."); diff -Nru connectome-workbench-1.4.2/src/GuiQt/MapSettingsColorBarWidget.cxx connectome-workbench-1.5.0/src/GuiQt/MapSettingsColorBarWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/MapSettingsColorBarWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MapSettingsColorBarWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -34,7 +34,9 @@ #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "EnumComboBoxTemplate.h" +#include "EventGetViewportSize.h" #include "EventGraphicsUpdateAllWindows.h" +#include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "MapSettingsColorBarPaletteOptionsWidget.h" #include "NumericFormatModeEnum.h" @@ -157,7 +159,7 @@ std::vector annotationVector; annotationVector.push_back(m_colorBar); - std::vector annotationTwoDimVector; + std::vector annotationTwoDimVector; annotationTwoDimVector.push_back(m_colorBar); m_paletteOptionsWidget->updateEditor(m_caretMappableDataFile, @@ -218,10 +220,66 @@ void MapSettingsColorBarWidget::annotationCoordinateSpaceEnumComboBoxItemActivated() { - const AnnotationCoordinateSpaceEnum::Enum coordinateSpace = m_annotationCoordinateSpaceEnumComboBox->getSelectedItem(); + const AnnotationCoordinateSpaceEnum::Enum newCoordinateSpace = m_annotationCoordinateSpaceEnumComboBox->getSelectedItem(); if (m_colorBar != NULL) { - m_colorBar->setCoordinateSpace(coordinateSpace); + if (m_colorBar->getPositionMode() == AnnotationColorBarPositionModeEnum::MANUAL) { + bool tabToWindowFlag(false); + bool windowToTabFlag(false); + if ((m_colorBar->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) + && (newCoordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW)) { + tabToWindowFlag = true; + } + if ((m_colorBar->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::WINDOW) + && (newCoordinateSpace == AnnotationCoordinateSpaceEnum::TAB)) { + windowToTabFlag = true; + } + + if (tabToWindowFlag + || windowToTabFlag) { + /* + * Transitioning between TAB to WINDOW space + * Change percentage width/height so pixel width/height + * does not change + */ + const int32_t tabIndex = m_colorBar->getTabIndex(); + EventGetViewportSize tabSizeEvent(EventGetViewportSize::MODE_TAB_AFTER_MARGINS_INDEX, + tabIndex); + EventManager::get()->sendEvent(tabSizeEvent.getPointer()); + if (tabSizeEvent.getEventProcessCount() > 0) { + EventGetViewportSize windowSizeEvent(EventGetViewportSize::MODE_WINDOW_FROM_TAB_INDEX, + tabIndex); + EventManager::get()->sendEvent(windowSizeEvent.getPointer()); + if (windowSizeEvent.getEventProcessCount() > 0) { + int32_t tabViewport[4]; + tabSizeEvent.getViewportSize(tabViewport); + int32_t windowViewport[4]; + windowSizeEvent.getViewportSize(windowViewport); + + const bool matchPositionFlag(true); + const bool matchSizeFlag(true); + if (tabToWindowFlag) { + m_colorBar->matchPixelPositionAndSizeInNewViewport(tabViewport, + windowViewport, + matchPositionFlag, + matchSizeFlag); + } + else if (windowToTabFlag) { + m_colorBar->matchPixelPositionAndSizeInNewViewport(windowViewport, + tabViewport, + matchPositionFlag, + matchSizeFlag); + } + else { + CaretAssert(0); + } + } + } + } + } + + m_colorBar->setCoordinateSpace(newCoordinateSpace); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } } @@ -252,7 +310,7 @@ " region in the graphics area\n" "WINDOW - Allows colorbar in any\n" " location in the graphics area"); - + QWidget* modeSpaceWidget = new QWidget(); QGridLayout* modeSpaceLayout = new QGridLayout(modeSpaceWidget); modeSpaceLayout->setContentsMargins(0, 0, 0, 0); diff -Nru connectome-workbench-1.4.2/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx connectome-workbench-1.5.0/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -355,7 +355,7 @@ defaultIndex = i; } m_colorSelectionComboBox->addItem(item->getName(), - qVariantFromValue((void*)item)); + QVariant::fromValue((void*)item)); } if ((defaultIndex >= 0) && (defaultIndex < m_colorSelectionComboBox->count())) { diff -Nru connectome-workbench-1.4.2/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx connectome-workbench-1.5.0/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -61,6 +61,7 @@ #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" +#include "PalettePixmapPainter.h" #include "ThresholdingSetMapsDialog.h" #include "VolumeFile.h" #include "WuQWidgetObjectGroup.h" @@ -158,6 +159,8 @@ layout->addWidget(leftWidget, 0); layout->addWidget(rightWidget, 100); + updatePaletteNameComboBox(); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } @@ -848,14 +851,12 @@ colorBarIcon); QLabel* barsColorLabel = new QLabel("Bars Color"); + m_histogramBarsColorComboBox = new CaretColorEnumComboBox(CaretColorEnumComboBox::CustomColorModeEnum::FIXED, + CaretColorEnumComboBox::NoneColorModeEnum::DISABLED, + this); + m_histogramBarsColorComboBox->setCustomColorName("Palette"); if (colorBarIconValid) { - m_histogramBarsColorComboBox = new CaretColorEnumComboBox("Palette", - colorBarIcon, - this); - } - else { - m_histogramBarsColorComboBox = new CaretColorEnumComboBox("Palette", - this); + m_histogramBarsColorComboBox->setCustomColorIcon(colorBarIcon); } WuQtUtilities::setToolTipAndStatusTip(m_histogramBarsColorComboBox->getWidget(), "Set histogram bars coloring"); @@ -863,14 +864,12 @@ this, SLOT(applyAndUpdate())); QLabel* envelopeColorLabel = new QLabel("Envelope"); - if (colorBarIconValid) { - m_histogramEnvelopeColorComboBox = new CaretColorEnumComboBox("Palette", - colorBarIcon, - this); - } - else { - m_histogramEnvelopeColorComboBox = new CaretColorEnumComboBox("Palette", + m_histogramEnvelopeColorComboBox = new CaretColorEnumComboBox(CaretColorEnumComboBox::CustomColorModeEnum::FIXED, + CaretColorEnumComboBox::NoneColorModeEnum::DISABLED, this); + m_histogramEnvelopeColorComboBox->setCustomColorName("Palette"); + if (colorBarIconValid) { + m_histogramEnvelopeColorComboBox->setCustomColorIcon(colorBarIcon); } WuQtUtilities::setToolTipAndStatusTip(m_histogramEnvelopeColorComboBox->getWidget(), "Set histogram envelope coloring"); @@ -1384,8 +1383,12 @@ /* * Spin box width (fixed may have much larger data values) */ - const int percentSpinBoxWidth = 75; - const int fixedSpinBoxWidth = 82; + const int fixedSpinBoxWidth = 90; + const int percentageDigitsRightOfDecimal(2); + const int fixedDigitsRightOfDecimal(4); + const QSizePolicy fixedPolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + /* * Percentage mapping */ @@ -1393,53 +1396,53 @@ WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, - 2, + percentageDigitsRightOfDecimal, this, SLOT(scaleAutoPercentageNegativeMaximumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentageNegativeMaximumSpinBox, "Map percentile (NOT percentage) most negative value to -1.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoPercentageNegativeMaximumSpinBox); - this->scaleAutoPercentageNegativeMaximumSpinBox->setFixedWidth(percentSpinBoxWidth); + this->scaleAutoPercentageNegativeMaximumSpinBox->setSizePolicy(fixedPolicy); this->scaleAutoPercentageNegativeMinimumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, - 2, + percentageDigitsRightOfDecimal, this, SLOT(scaleAutoPercentageNegativeMinimumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentageNegativeMinimumSpinBox, "Map percentile (NOT percentage) least negative value to 0.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoPercentageNegativeMinimumSpinBox); - this->scaleAutoPercentageNegativeMinimumSpinBox->setFixedWidth(percentSpinBoxWidth); + this->scaleAutoPercentageNegativeMinimumSpinBox->setSizePolicy(fixedPolicy); this->scaleAutoPercentagePositiveMinimumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, - 2, + percentageDigitsRightOfDecimal, this, SLOT(scaleAutoPercentagePositiveMinimumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentagePositiveMinimumSpinBox, "Map percentile (NOT percentage) least positive value to 0.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoPercentagePositiveMinimumSpinBox); - this->scaleAutoPercentagePositiveMinimumSpinBox->setFixedWidth(percentSpinBoxWidth); + this->scaleAutoPercentagePositiveMinimumSpinBox->setSizePolicy(fixedPolicy); this->scaleAutoPercentagePositiveMaximumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, - 2, + percentageDigitsRightOfDecimal, this, SLOT(scaleAutoPercentagePositiveMaximumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentagePositiveMaximumSpinBox, "Map percentile (NOT percentage) most positive value to 1.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoPercentagePositiveMaximumSpinBox); - this->scaleAutoPercentagePositiveMaximumSpinBox->setFixedWidth(percentSpinBoxWidth); + this->scaleAutoPercentagePositiveMaximumSpinBox->setSizePolicy(fixedPolicy); /* * Absolute percentage mapping @@ -1448,27 +1451,26 @@ WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, - 2, + percentageDigitsRightOfDecimal, this, SLOT(scaleAutoAbsolutePercentageMinimumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoAbsolutePercentageMinimumSpinBox, "Map percentile (NOT percentage) least absolute value to 0.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoAbsolutePercentageMinimumSpinBox); - this->scaleAutoAbsolutePercentageMinimumSpinBox->setFixedWidth(percentSpinBoxWidth); - + scaleAutoAbsolutePercentageMinimumSpinBox->setSizePolicy(fixedPolicy); this->scaleAutoAbsolutePercentageMaximumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, - 2, + percentageDigitsRightOfDecimal, this, SLOT(scaleAutoAbsolutePercentageMaximumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoAbsolutePercentageMaximumSpinBox, "Map percentile (NOT percentage) most absolute value to 1.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoAbsolutePercentageMaximumSpinBox); - this->scaleAutoAbsolutePercentageMaximumSpinBox->setFixedWidth(percentSpinBoxWidth); + this->scaleAutoAbsolutePercentageMaximumSpinBox->setSizePolicy(fixedPolicy); /* * Fixed mapping @@ -1477,7 +1479,7 @@ WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-BIG_NUMBER, 0.0, 1.0, - 2, + fixedDigitsRightOfDecimal, this, SLOT(scaleFixedNegativeMaximumValueChanged(double))); @@ -1490,7 +1492,7 @@ WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-BIG_NUMBER, 0.0, 1.0, - 2, + fixedDigitsRightOfDecimal, this, SLOT(scaleFixedNegativeMinimumValueChanged(double))); @@ -1503,7 +1505,7 @@ WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, BIG_NUMBER, 1.0, - 2, + fixedDigitsRightOfDecimal, this, SLOT(scaleFixedPositiveMinimumValueChanged(double))); @@ -1516,7 +1518,7 @@ WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, BIG_NUMBER, 1.0, - 2, + fixedDigitsRightOfDecimal, this, SLOT(scaleFixedPositiveMaximumValueChanged(double))); @@ -1540,9 +1542,9 @@ QWidget* colorMappingWidget = new QWidget(); QGridLayout* colorMappingLayout = new QGridLayout(colorMappingWidget); colorMappingLayout->setColumnStretch(0, 0); - colorMappingLayout->setColumnStretch(1, 100); - colorMappingLayout->setColumnStretch(2, 100); - colorMappingLayout->setColumnStretch(3, 100); + colorMappingLayout->setColumnStretch(1, 0); + colorMappingLayout->setColumnStretch(2, 0); + colorMappingLayout->setColumnStretch(3, 0); colorMappingLayout->setColumnStretch(4, 100); this->setLayoutSpacingAndMargins(colorMappingLayout); colorMappingLayout->addWidget(this->scaleAutoRadioButton, 0, 0, Qt::AlignHCenter); @@ -1615,7 +1617,7 @@ this->setLayoutSpacingAndMargins(paletteLayout); paletteLayout->addWidget(paletteSelectionWidget); paletteLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); - paletteLayout->addWidget(colorMappingWidget); + paletteLayout->addWidget(colorMappingWidget, 0, Qt::AlignLeft); paletteLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); paletteLayout->addWidget(displayModeWidget); paletteGroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -1771,10 +1773,57 @@ } } - this->scalePositiveMaximumValueLabel->setText(QString::number(posMaxLabelValue, 'f', 2)); - this->scalePositiveMinimumValueLabel->setText(QString::number(posMinLabelValue, 'f', 2)); - this->scaleNegativeMinimumValueLabel->setText(QString::number(negMinLabelValue, 'f', 2)); - this->scaleNegativeMaximumValueLabel->setText(QString::number(negMaxLabelValue, 'f', 2)); + const int32_t digitsRightOfDecimal(4); + this->scalePositiveMaximumValueLabel->setText(QString::number(posMaxLabelValue, 'f', digitsRightOfDecimal)); + this->scalePositiveMinimumValueLabel->setText(QString::number(posMinLabelValue, 'f', digitsRightOfDecimal)); + this->scaleNegativeMinimumValueLabel->setText(QString::number(negMinLabelValue, 'f', digitsRightOfDecimal)); + this->scaleNegativeMaximumValueLabel->setText(QString::number(negMaxLabelValue, 'f', digitsRightOfDecimal)); +} + +void +MapSettingsPaletteColorMappingWidget::updatePaletteNameComboBox() +{ + this->paletteNameComboBox->clear(); + + PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); + + bool firstValidPixmapFlag(true); + const int32_t numPalettes = paletteFile->getNumberOfPalettes(); + for (int32_t i = 0; i < numPalettes; i++) { + Palette* palette = paletteFile->getPalette(i); + const AString name = palette->getName(); + /* + * Second parameter is user data. In the future, there may be user-editable + * palettes and it is possible there may be palettes with the same name. + * Thus, the user-data may change to a unique-identifier that is different + * than the palette name. + */ + const AString paletteUniqueID(name); + + const bool showColorMappingFlag(true); + if (showColorMappingFlag) { + PalettePixmapPainter palettePainter(palette, + PalettePixmapPainter::Mode::INTERPOLATE_ON); + QPixmap pixmap = palettePainter.getPixmap(); + if (pixmap.isNull()) { + this->paletteNameComboBox->addItem(name, + paletteUniqueID); + } + else { + if (firstValidPixmapFlag) { + firstValidPixmapFlag = false; + this->paletteNameComboBox->setIconSize(pixmap.size()); + } + this->paletteNameComboBox->addItem(pixmap, + " " + name, + paletteUniqueID); + } + } + else { + this->paletteNameComboBox->addItem(name, + paletteUniqueID); + } + } } /** @@ -1811,28 +1860,19 @@ + this->caretMappableDataFile->getMapName(this->mapFileIndex); this->setWindowTitle(title); - this->paletteNameComboBox->clear(); - - - this->paletteColorMapping = this->caretMappableDataFile->getMapPaletteColorMapping(this->mapFileIndex); + this->paletteColorMapping = this->caretMappableDataFile->getMapPaletteColorMapping(this->mapFileIndex); if (this->paletteColorMapping != NULL) { - PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); - - int defaultIndex = 0; - const int32_t numPalettes = paletteFile->getNumberOfPalettes(); - for (int32_t i = 0; i < numPalettes; i++) { - Palette* palette = paletteFile->getPalette(i); - const AString name = palette->getName(); - if (name == this->paletteColorMapping->getSelectedPaletteName()) { - defaultIndex = i; + int32_t paletteComboBoxIndex(0); + const AString paletteName = this->paletteColorMapping->getSelectedPaletteName(); + for (int32_t i = 0; i < this->paletteNameComboBox->count(); i++) { + if (this->paletteNameComboBox->itemData(i).toString() == paletteName) { + paletteComboBoxIndex = i; + break; } - this->paletteNameComboBox->addItem(name, - name); } - - if (defaultIndex < this->paletteNameComboBox->count()) { - this->paletteNameComboBox->setCurrentIndex(defaultIndex); + if (paletteComboBoxIndex < this->paletteNameComboBox->count()) { + this->paletteNameComboBox->setCurrentIndex(paletteComboBoxIndex); } bool isPercentageSpinBoxesEnabled = false; diff -Nru connectome-workbench-1.4.2/src/GuiQt/MapSettingsPaletteColorMappingWidget.h connectome-workbench-1.5.0/src/GuiQt/MapSettingsPaletteColorMappingWidget.h --- connectome-workbench-1.4.2/src/GuiQt/MapSettingsPaletteColorMappingWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MapSettingsPaletteColorMappingWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -132,6 +132,8 @@ const Histogram* getHistogram(const FastStatistics* statisticsForAll) const; + void updatePaletteNameComboBox(); + PaletteColorMapping* paletteColorMapping; QComboBox* paletteNameComboBox; diff -Nru connectome-workbench-1.4.2/src/GuiQt/MediaOverlaySetViewController.cxx connectome-workbench-1.5.0/src/GuiQt/MediaOverlaySetViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/MediaOverlaySetViewController.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MediaOverlaySetViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,319 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include +#include +#include +#include + +#define __MEDIA_OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ +#include "MediaOverlaySetViewController.h" +#undef __MEDIA_OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ + +#include "BrainConstants.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "EventManager.h" +#include "EventSurfaceColoringInvalidate.h" +#include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" +#include "MediaOverlaySet.h" +#include "MediaOverlayViewController.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::MediaOverlaySetViewController + * \brief View Controller for an overlay set. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param orientation + * Orientation for layout + * @param browserWindowIndex + * Index of browser window that contains this view controller. + * @param parentObjectName + * Name of parent + * @param parent + * Parent widget. + */ +MediaOverlaySetViewController::MediaOverlaySetViewController(const Qt::Orientation orientation, + const int32_t browserWindowIndex, + const QString& parentObjectName, + QWidget* parent) +: QWidget(parent) +{ + m_browserWindowIndex = browserWindowIndex; + + QWidget* gridWidget = new QWidget(); + QGridLayout* gridLayout = new QGridLayout(gridWidget); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); + if (orientation == Qt::Horizontal) { + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 0); + gridLayout->setColumnStretch(4, 0); + gridLayout->setColumnStretch(5, 100); + gridLayout->setColumnStretch(6, 0); + gridLayout->setColumnStretch(7, 0); + gridLayout->setColumnStretch(8, 100); + + QLabel* onLabel = new QLabel("On"); + QLabel* settingsLabel = new QLabel("Settings"); + QLabel* opacityLabel = new QLabel("Opacity"); + QLabel* fileLabel = new QLabel("File"); + QLabel* yokeLabel = new QLabel("Yoke"); + QLabel* mapLabel = new QLabel("Frame"); + + const int row = gridLayout->rowCount(); + gridLayout->addWidget(onLabel, row, 0, Qt::AlignHCenter); + gridLayout->addWidget(settingsLabel, row, 1, 1, 3, Qt::AlignHCenter); + gridLayout->addWidget(opacityLabel, row, 4, Qt::AlignHCenter); + gridLayout->addWidget(fileLabel, row, 5, Qt::AlignHCenter); + gridLayout->addWidget(yokeLabel, row, 6, Qt::AlignHCenter); + gridLayout->addWidget(mapLabel, row, 8, 1, 2, Qt::AlignHCenter); + } + else { + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 0); + gridLayout->setColumnStretch(4, 0); + gridLayout->setColumnStretch(5, 0); + gridLayout->setColumnStretch(6, 100); + } + + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { + MediaOverlayViewController* ovc = new MediaOverlayViewController(orientation, + gridLayout, + browserWindowIndex, + i, + parentObjectName, + this); + m_overlayViewControllers.push_back(ovc); + + QObject::connect(ovc, SIGNAL(requestAddOverlayAbove(const int32_t)), + this, SLOT(processAddOverlayAbove(const int32_t))); + QObject::connect(ovc, SIGNAL(requestAddOverlayBelow(const int32_t)), + this, SLOT(processAddOverlayBelow(const int32_t))); + QObject::connect(ovc, SIGNAL(requestRemoveOverlay(const int32_t)), + this, SLOT(processRemoveOverlay(const int32_t))); + QObject::connect(ovc, SIGNAL(requestMoveOverlayUp(const int32_t)), + this, SLOT(processMoveOverlayUp(const int32_t))); + QObject::connect(ovc, SIGNAL(requestMoveOverlayDown(const int32_t)), + this, SLOT(processMoveOverlayDown(const int32_t))); + } + + if (orientation == Qt::Horizontal) { + QVBoxLayout* verticalLayout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(verticalLayout, 2, 2); + verticalLayout->addWidget(gridWidget); + verticalLayout->addStretch(); + } + else { + /* + * Resolve WB-649 + */ + QVBoxLayout* verticalLayout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(verticalLayout, 1, 1); + verticalLayout->addWidget(gridWidget); + verticalLayout->addStretch(); + } + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); +} + +/** + * Destructor. + */ +MediaOverlaySetViewController::~MediaOverlaySetViewController() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * @return The overlay set in this view controller. + */ +MediaOverlaySet* +MediaOverlaySetViewController::getOverlaySet() +{ + MediaOverlaySet* overlaySet = NULL; + BrowserTabContent* browserTabContent = + GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); + if (browserTabContent != NULL) { + overlaySet = browserTabContent->getMediaOverlaySet(); + } + + return overlaySet; +} + +/** + * Update this overlay set view controller using the given overlay set. + */ +void +MediaOverlaySetViewController::updateViewController() +{ + MediaOverlaySet* overlaySet = this->getOverlaySet(); + if (overlaySet == NULL) { + return; + } + + const int32_t numberOfOverlays = static_cast(m_overlayViewControllers.size()); + const int32_t numberOfDisplayedOverlays = overlaySet->getNumberOfDisplayedOverlays(); + + for (int32_t i = 0; i < numberOfOverlays; i++) { + MediaOverlay* overlay = NULL; + if (overlaySet != NULL) { + overlay = overlaySet->getOverlay(i); + } + m_overlayViewControllers[i]->updateViewController(overlay); + + bool displayOverlay = (overlay != NULL); + if (i >= numberOfDisplayedOverlays) { + displayOverlay = false; + } + m_overlayViewControllers[i]->setVisible(displayOverlay); + } +} + +/** + * Receive events from the event manager. + * + * @param event + * Event sent by event manager. + */ +void +MediaOverlaySetViewController::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { + EventUserInterfaceUpdate* uiEvent = + dynamic_cast(event); + CaretAssert(uiEvent); + + if (uiEvent->isUpdateForWindow(m_browserWindowIndex)) { + if (uiEvent->isToolBoxUpdate()) { + this->updateViewController(); + uiEvent->setEventProcessed(); + } + } + } +} + +/** + * Add an overlay above the overlay with the given index. + * @param overlayIndex + * Index of overlay that will have an overlay added above it. + */ +void +MediaOverlaySetViewController::processAddOverlayAbove(const int32_t overlayIndex) +{ + MediaOverlaySet* overlaySet = getOverlaySet(); + if (overlaySet != NULL) { + overlaySet->insertOverlayAbove(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Add an overlay below the overlay with the given index. + * @param overlayIndex + * Index of overlay that will have an overlay added below it. + */ +void +MediaOverlaySetViewController::processAddOverlayBelow(const int32_t overlayIndex) +{ + MediaOverlaySet* overlaySet = getOverlaySet(); + if (overlaySet != NULL) { + overlaySet->insertOverlayBelow(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Remove an overlay above the overlay with the given index. + * @param overlayIndex + * Index of overlay that will be removed + */ +void +MediaOverlaySetViewController::processRemoveOverlay(const int32_t overlayIndex) +{ + MediaOverlaySet* overlaySet = getOverlaySet(); + if (overlaySet != NULL) { + overlaySet->removeDisplayedOverlay(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Remove an overlay above the overlay with the given index. + * @param overlayIndex + * Index of overlay that will be removed + */ +void +MediaOverlaySetViewController::processMoveOverlayDown(const int32_t overlayIndex) +{ + MediaOverlaySet* overlaySet = getOverlaySet(); + if (overlaySet != NULL) { + overlaySet->moveDisplayedOverlayDown(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Remove an overlay above the overlay with the given index. + * @param overlayIndex + * Index of overlay that will be removed + */ +void +MediaOverlaySetViewController::processMoveOverlayUp(const int32_t overlayIndex) +{ + MediaOverlaySet* overlaySet = getOverlaySet(); + if (overlaySet != NULL) { + overlaySet->moveDisplayedOverlayUp(overlayIndex); + this->updateColoringAndGraphics(); + } +} + +/** + * Update surface coloring and graphics after overlay changes. + */ +void +MediaOverlaySetViewController::updateColoringAndGraphics() +{ + this->updateViewController(); + + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); + EventGraphicsUpdateOneWindow graphicsUpdate(m_browserWindowIndex); + EventManager::get()->sendEvent(graphicsUpdate.getPointer()); +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/MediaOverlaySetViewController.h connectome-workbench-1.5.0/src/GuiQt/MediaOverlaySetViewController.h --- connectome-workbench-1.4.2/src/GuiQt/MediaOverlaySetViewController.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MediaOverlaySetViewController.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,87 @@ +#ifndef __MEDIA_OVERLAY_SET_VIEW_CONTROLLER__H_ +#define __MEDIA_OVERLAY_SET_VIEW_CONTROLLER__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include +#include + +#include "EventListenerInterface.h" + +class QScrollArea; +class QSpinBox; + +namespace caret { + + class MediaOverlaySet; + class MediaOverlayViewController; + + class MediaOverlaySetViewController : public QWidget, public EventListenerInterface { + + Q_OBJECT + + public: + MediaOverlaySetViewController(const Qt::Orientation orientation, + const int32_t browserWindowIndex, + const QString& parentObjectName, + QWidget* parent = 0); + + virtual ~MediaOverlaySetViewController(); + + void receiveEvent(Event* event); + + private slots: + void processAddOverlayAbove(const int32_t overlayIndex); + + void processAddOverlayBelow(const int32_t overlayIndex); + + void processRemoveOverlay(const int32_t overlayIndex); + + void processMoveOverlayDown(const int32_t overlayIndex); + + void processMoveOverlayUp(const int32_t overlayIndex); + + private: + MediaOverlaySetViewController(const MediaOverlaySetViewController&); + + MediaOverlaySetViewController& operator=(const MediaOverlaySetViewController&); + + MediaOverlaySet* getOverlaySet(); + + void updateViewController(); + + void updateColoringAndGraphics(); + + std::vector m_overlayViewControllers; + + int32_t m_browserWindowIndex; + + QScrollArea* m_scrollArea; + }; + +#ifdef __MEDIA_OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ + // +#endif // __MEDIA_OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ + +} // namespace +#endif //__MEDIA_OVERLAY_SET_VIEW_CONTROLLER__H_ diff -Nru connectome-workbench-1.4.2/src/GuiQt/MediaOverlayViewController.cxx connectome-workbench-1.5.0/src/GuiQt/MediaOverlayViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/MediaOverlayViewController.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MediaOverlayViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,1117 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __MEDIA_OVERLAY_VIEW_CONTROLLER_DECLARE__ +#include "MediaOverlayViewController.h" +#undef __MEDIA_OVERLAY_VIEW_CONTROLLER_DECLARE__ + +#include "CaretMappableDataFile.h" +#include "EventDataFileReload.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "EventManager.h" +#include "EventMapYokingSelectMap.h" +#include "EventOverlaySettingsEditorDialogRequest.h" +#include "EventSurfaceColoringInvalidate.h" +#include "EventUserInterfaceUpdate.h" +#include "FileInformation.h" +#include "FilePathNamePrefixCompactor.h" +#include "GuiManager.h" +#include "MapYokingGroupComboBox.h" +#include "MediaFile.h" +#include "MediaOverlay.h" +#include "UsernamePasswordWidget.h" +#include "WuQFactory.h" +#include "WuQMacroManager.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" +#include "WuQGridLayoutGroup.h" +#include "WuQWidgetObjectGroup.h" + +using namespace caret; + +/** + * \class caret::MediaOverlayViewController + * \brief View Controller for a media overlay. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param orientation + * Orientation of overlay (horizontal/vertical) + * @param gridLayout + * Layout for widegets + * @param browserWindowIndex + * Index of browser window in which this view controller resides. + * @param overlayIndex + * Index of the overlay + * @param parentObjectName + * Name of parent object for macros + * @param parent + * The parent widget. + */ +MediaOverlayViewController::MediaOverlayViewController(const Qt::Orientation orientation, + QGridLayout* gridLayout, + const int32_t browserWindowIndex, + const int32_t overlayIndex, + const QString& parentObjectName, + QObject* parent) +: QObject(parent), + browserWindowIndex(browserWindowIndex), + m_overlayIndex(overlayIndex) +{ + m_mediaOverlay = NULL; + m_constructionReloadFileAction = NULL; + + int minComboBoxWidth = 200; + int maxComboBoxWidth = 100000; + if (orientation == Qt::Horizontal) { + minComboBoxWidth = 50; + maxComboBoxWidth = 100000; + } + const QComboBox::SizeAdjustPolicy comboSizePolicy = QComboBox::AdjustToContentsOnFirstShow; + + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + QString objectNamePrefix = QString(parentObjectName + + ":MediaOverlay%1" + + ":").arg((int)(overlayIndex + 1), 2, 10, QLatin1Char('0')); + const QString descriptivePrefix = QString("overlay %1").arg(overlayIndex + 1); + /* + * Enabled Check Box + */ + const QString checkboxText = ((orientation == Qt::Horizontal) ? " " : " "); + this->enabledCheckBox = new QCheckBox(checkboxText); + this->enabledCheckBox->setObjectName(objectNamePrefix + + "OnOff"); + QObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(enabledCheckBoxClicked(bool))); + this->enabledCheckBox->setToolTip("Enables display of this overlay"); + macroManager->addMacroSupportToObject(this->enabledCheckBox, + "Enable " + descriptivePrefix); + + /* + * File Selection Combo Box + */ + this->fileComboBox = WuQFactory::newComboBox(); + this->fileComboBox->setObjectName(objectNamePrefix + + "FileSelection"); + this->fileComboBox->setMinimumWidth(minComboBoxWidth); + this->fileComboBox->setMaximumWidth(maxComboBoxWidth); + QObject::connect(this->fileComboBox, SIGNAL(activated(int)), + this, SLOT(fileComboBoxSelected(int))); + this->fileComboBox->setToolTip("Selects file for this overlay"); + this->fileComboBox->setSizeAdjustPolicy(comboSizePolicy); + macroManager->addMacroSupportToObject(this->fileComboBox, + ("Select file in " + descriptivePrefix)); + + /* + * Frame Index Spin Box + */ + m_frameIndexSpinBox = WuQFactory::newSpinBox(); + m_frameIndexSpinBox->setMinimumWidth(50); + this->m_frameIndexSpinBox->setObjectName(objectNamePrefix + + "FrameIndex"); + QObject::connect(m_frameIndexSpinBox, SIGNAL(valueChanged(int)), + this, SLOT(frameIndexSpinBoxValueChanged(int))); + m_frameIndexSpinBox->setToolTip("Select frame by its index"); + macroManager->addMacroSupportToObject(m_frameIndexSpinBox, + ("Select " + descriptivePrefix + " frame index")); + + /* + * Frame Name Combo Box + */ + this->frameNameComboBox = WuQFactory::newComboBox(); + this->frameNameComboBox->setObjectName(objectNamePrefix + + "FrameSelection"); + this->frameNameComboBox->setMinimumWidth(minComboBoxWidth); + this->frameNameComboBox->setMaximumWidth(maxComboBoxWidth); + QObject::connect(this->frameNameComboBox, SIGNAL(activated(int)), + this, SLOT(frameNameComboBoxSelected(int))); + this->frameNameComboBox->setToolTip("Select frame by its name"); + this->frameNameComboBox->setSizeAdjustPolicy(comboSizePolicy); + macroManager->addMacroSupportToObject(this->frameNameComboBox, + ("Select " + descriptivePrefix + " frame name")); + + /* + * Opacity double spin box + */ + this->opacityDoubleSpinBox = WuQFactory::newDoubleSpinBox(); + this->opacityDoubleSpinBox->setObjectName(objectNamePrefix + + "Opacity"); + this->opacityDoubleSpinBox->setMinimum(0.0); + this->opacityDoubleSpinBox->setMaximum(1.0); + this->opacityDoubleSpinBox->setSingleStep(0.05); + this->opacityDoubleSpinBox->setDecimals(2); + this->opacityDoubleSpinBox->setFixedWidth(50); + QObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(opacityDoubleSpinBoxValueChanged(double))); + this->opacityDoubleSpinBox->setToolTip("Opacity (0.0=transparent, 1.0=opaque)"); + macroManager->addMacroSupportToObject(this->opacityDoubleSpinBox, + ("Set " + descriptivePrefix + " opacity")); + + /* + * Settings Tool Button + */ + QIcon settingsIcon; + const bool settingsIconValid = WuQtUtilities::loadIcon(":/LayersPanel/wrench.png", + settingsIcon); + + this->settingsAction = WuQtUtilities::createAction("S", + "Edit settings for this frame and overlay", + this, + this, + SLOT(settingsActionTriggered())); + this->settingsAction->setObjectName(objectNamePrefix + + "ShowSettingsDialog"); + if (settingsIconValid) { + this->settingsAction->setIcon(settingsIcon); + } + QToolButton* settingsToolButton = new QToolButton(); + settingsToolButton->setDefaultAction(this->settingsAction); + macroManager->addMacroSupportToObject(this->settingsAction, + ("Display " + descriptivePrefix + " palette settings")); + + /* + * Construction Tool Button + * Note: macro support is on each action in menu in 'createConstructionMenu' + */ + QIcon constructionIcon; + const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", + constructionIcon); + this->constructionAction = WuQtUtilities::createAction("M", + "Add/Move/Remove Overlays", + this); + if (constructionIconValid) { + this->constructionAction->setIcon(constructionIcon); + } + m_constructionToolButton = new QToolButton(); + QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton, + descriptivePrefix, + (objectNamePrefix + + "ConstructionMenu:")); + this->constructionAction->setMenu(constructionMenu); + m_constructionToolButton->setDefaultAction(this->constructionAction); + m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); + + const AString yokeToolTip = + ("Select a yoking group.\n" + "\n" + "When files with more than one frame are yoked,\n" + "the seleted frames are synchronized by frame index.\n" + "\n" + "If the SAME FILE is in yoked in multiple overlays,\n" + "the overlay enabled statuses are synchronized.\n"); + + /* + * Yoking Group + * Note: macro support is in the class MapYokingGroupComboBox + */ + m_frameYokingGroupComboBox = new MapYokingGroupComboBox(this, + (objectNamePrefix + + "FrameYokingSelection"), + descriptivePrefix); + m_frameYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and frame indices)"); + m_frameYokingGroupComboBox->getWidget()->setToolTip("Yoke to Files"); + QObject::connect(m_frameYokingGroupComboBox, SIGNAL(itemActivated()), + this, SLOT(yokingGroupActivated())); + + /* + * Use layout group so that items can be shown/hidden + */ + this->gridLayoutGroup = new WuQGridLayoutGroup(gridLayout, this); + + if (orientation == Qt::Horizontal) { + int row = this->gridLayoutGroup->rowCount(); + this->gridLayoutGroup->addWidget(this->enabledCheckBox, + row, 0, + Qt::AlignHCenter); + this->gridLayoutGroup->addWidget(settingsToolButton, + row, 1, + Qt::AlignHCenter); + this->gridLayoutGroup->addWidget(m_constructionToolButton, + row, 3); + this->gridLayoutGroup->addWidget(this->opacityDoubleSpinBox, + row, 4); + this->gridLayoutGroup->addWidget(this->fileComboBox, + row, 5); + this->gridLayoutGroup->addWidget(this->m_frameYokingGroupComboBox->getWidget(), + row, 6, + Qt::AlignHCenter); + this->gridLayoutGroup->addWidget(m_frameIndexSpinBox, + row, 7); + this->gridLayoutGroup->addWidget(this->frameNameComboBox, + row, 8); + + } + else { + QFrame* bottomHorizontalLineWidget = new QFrame(); + bottomHorizontalLineWidget->setLineWidth(0); + bottomHorizontalLineWidget->setMidLineWidth(1); + bottomHorizontalLineWidget->setFrameStyle(QFrame::HLine | QFrame::Raised); + + QLabel* fileLabel = new QLabel("File"); + QLabel* frameLabel = new QLabel("Frame"); + + int row = this->gridLayoutGroup->rowCount(); + this->gridLayoutGroup->addWidget(this->enabledCheckBox, + row, 0); + this->gridLayoutGroup->addWidget(settingsToolButton, + row, 1); + this->gridLayoutGroup->addWidget(m_constructionToolButton, + row, 3); + this->gridLayoutGroup->addWidget(fileLabel, + row, 4); + this->gridLayoutGroup->addWidget(this->fileComboBox, + row, 5, 1, 2); + + row++; + this->gridLayoutGroup->addWidget(this->opacityDoubleSpinBox, + row, 0, + 1, 2, + Qt::AlignCenter); + this->gridLayoutGroup->addWidget(this->m_frameYokingGroupComboBox->getWidget(), + row, 2, + 1, 2); + this->gridLayoutGroup->addWidget(frameLabel, + row, 4); + this->gridLayoutGroup->addWidget(m_frameIndexSpinBox, + row, 5); + this->gridLayoutGroup->addWidget(this->frameNameComboBox, + row, 6); + + row++; + this->gridLayoutGroup->addWidget(bottomHorizontalLineWidget, + row, 0, 1, -1); + } +} + +/** + * Destructor. + */ +MediaOverlayViewController::~MediaOverlayViewController() +{ + +} + +/** + * Set the visiblity of this overlay view controller. + */ +void +MediaOverlayViewController::setVisible(bool visible) +{ + this->gridLayoutGroup->setVisible(visible); +} + +/* + * If this overlay ins an overlay settings editor, update its content + */ +void +MediaOverlayViewController::updateOverlaySettingsEditor() +{ + if (m_mediaOverlay == NULL) { + return; + } + + MediaFile* mediaFile = NULL; + int32_t frameIndex = -1; + m_mediaOverlay->getSelectionData(mediaFile, + frameIndex); + + if ((mediaFile != NULL) + && (frameIndex >= 0)) { +// EventOverlaySettingsEditorDialogRequest pcme(EventOverlaySettingsEditorDialogRequest::MODE_OVERLAY_MAP_CHANGED, +// this->browserWindowIndex, +// m_mediaOverlay, +// mediaFile, +// frameIndex); +// EventManager::get()->sendEvent(pcme.getPointer()); + } +} + +/** + * Called when a selection is made from the file combo box. + * @parm indx + * Index of selection. + */ +void +MediaOverlayViewController::fileComboBoxSelected(int indx) +{ + if (m_mediaOverlay == NULL) { + return; + } + + void* pointer = this->fileComboBox->itemData(indx).value(); + MediaFile* file = (MediaFile*)pointer; + m_mediaOverlay->setSelectionData(file, 0); + + validateYokingSelection(); + + //validateYokingSelection(overlay->getYokingGroup()); + // not needed with call to validateYokingSelection: this->updateViewController(this->overlay); + + // called inside validateYokingSelection(); this->updateUserInterfaceAndGraphicsWindow(); + + updateOverlaySettingsEditor(); + updateViewController(m_mediaOverlay); + + updateGraphicsWindow(); +} + +/** + * Called when a selection is made from the frame index spin box. + * @parm indx + * Index of selection. + */ +void +MediaOverlayViewController::frameIndexSpinBoxValueChanged(int indx) +{ + if (m_mediaOverlay == NULL) + { + //TSC: not sure how to put the displayed integer back to 0 where it starts when opening without data files + return; + } + /* + * Get the file that is selected from the file combo box + */ + const int32_t fileIndex = this->fileComboBox->currentIndex(); + void* pointer = this->fileComboBox->itemData(fileIndex).value(); + MediaFile* file = (MediaFile*)pointer; + + /* + * Overlay indices range [0, N-1] but spin box shows [1, N]. + */ + const int overlayIndex = indx - 1; + + m_mediaOverlay->setSelectionData(file, overlayIndex); + + const MapYokingGroupEnum::Enum frameYoking = m_mediaOverlay->getMapYokingGroup(); + if (frameYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { +// EventMapYokingSelectMap selectMapEvent(frameYoking, +// file, +// NULL, +// overlayIndex, +// m_mediaOverlay->isEnabled()); +// EventManager::get()->sendEvent(selectMapEvent.getPointer()); + } + + /* + * Need to update frame name combo box. + */ + frameNameComboBox->blockSignals(true); + if ((overlayIndex >= 0) + && (overlayIndex < frameNameComboBox->count())) { + frameNameComboBox->setCurrentIndex(overlayIndex); + } + frameNameComboBox->blockSignals(false); + + this->updateUserInterface(); + this->updateGraphicsWindow(); + + updateOverlaySettingsEditor(); +} + +/** + * Called when a selection is made from the frame name combo box. + * @parm indx + * Index of selection. + */ +void +MediaOverlayViewController::frameNameComboBoxSelected(int indx) +{ + if (m_mediaOverlay == NULL) { + return; + } + + /* + * Get the file that is selected from the file combo box + */ + const int32_t fileIndex = this->fileComboBox->currentIndex(); + void* pointer = this->fileComboBox->itemData(fileIndex).value(); + MediaFile* file = (MediaFile*)pointer; + + m_mediaOverlay->setSelectionData(file, indx); + + const MapYokingGroupEnum::Enum frameYoking = m_mediaOverlay->getMapYokingGroup(); + if (frameYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { +// EventMapYokingSelectMap selectMapEvent(frameYoking, +// file, +// NULL, +// indx, +// m_mediaOverlay->isEnabled()); +// EventManager::get()->sendEvent(selectMapEvent.getPointer()); + } + + /* + * Need to update frame index spin box. + * Note that the frame index spin box ranges [1, N]. + */ + m_frameIndexSpinBox->blockSignals(true); + m_frameIndexSpinBox->setValue(indx + 1); + m_frameIndexSpinBox->blockSignals(false); + + this->updateUserInterface(); + this->updateGraphicsWindow(); + + updateOverlaySettingsEditor(); +} + +/** + * Called when enabled checkbox state is changed + * @parm checked + * Checked status + */ +void +MediaOverlayViewController::enabledCheckBoxClicked(bool checked) +{ + if (m_mediaOverlay == NULL) { + return; + } + m_mediaOverlay->setEnabled(checked); + + const MapYokingGroupEnum::Enum frameYoking = m_mediaOverlay->getMapYokingGroup(); + if (frameYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { + MediaFile* myFile = NULL; + int32_t myIndex = -1; + m_mediaOverlay->getSelectionData(myFile, + myIndex); + +// EventMapYokingSelectMap selectMapEvent(frameYoking, +// myFile, +// NULL, +// myIndex, +// m_mediaOverlay->isEnabled()); +// EventManager::get()->sendEvent(selectMapEvent.getPointer()); + } + + this->updateUserInterface(); + + this->updateGraphicsWindow(); +} + + +/** + * Called when opacity value is changed. + * @param value + * New value. + */ +void +MediaOverlayViewController::opacityDoubleSpinBoxValueChanged(double value) +{ + if (m_mediaOverlay == NULL) { + return; + } + + m_mediaOverlay->setOpacity(value); + + this->updateGraphicsWindow(); +} + +/** + * Validate yoking when there are changes made to the overlay. + */ +void +MediaOverlayViewController::validateYokingSelection() +{ +// m_frameYokingGroupComboBox->validateYokingChange(m_mediaOverlay); + updateViewController(m_mediaOverlay); + updateGraphicsWindow(); +} + +/** + * Called when the yoking group is changed. + */ +void +MediaOverlayViewController::yokingGroupActivated() +{ + MapYokingGroupEnum::Enum yokingGroup = m_frameYokingGroupComboBox->getMapYokingGroup(); + + /* + * Has yoking group changed? + * TSC: overlay can be null when opened without loaded files + */ + if (m_mediaOverlay != NULL && yokingGroup != m_mediaOverlay->getMapYokingGroup()) { + validateYokingSelection(); + } +} + + +/** + * Called when the settings action is selected. + */ +void +MediaOverlayViewController::settingsActionTriggered() +{ + if (m_mediaOverlay == NULL) { + return; + } + + MediaFile* mediaFile; + int32_t frameIndex = -1; + m_mediaOverlay->getSelectionData(mediaFile, + frameIndex); + if (mediaFile != NULL) { +// EventOverlaySettingsEditorDialogRequest pcme(EventOverlaySettingsEditorDialogRequest::MODE_SHOW_EDITOR, +// this->browserWindowIndex, +// m_mediaOverlay, +// mediaFile, +// frameIndex); +// EventManager::get()->sendEvent(pcme.getPointer()); + } +} + +/** + * Update this view controller using the given overlay. + * @param overlay + * Overlay that is used in this view controller. + */ +void +MediaOverlayViewController::updateViewController(MediaOverlay* overlay) +{ + m_mediaOverlay = overlay; + + this->fileComboBox->clear(); + + /* + * Get the selection information for the overlay. + */ + std::vector mediaFiles; + MediaFile* selectedFile = NULL; + int32_t selectedFrameIndex = -1; + if (m_mediaOverlay != NULL) { + m_mediaOverlay->getSelectionData(mediaFiles, + selectedFile, + selectedFrameIndex); + } + + std::vector caretDataFiles(mediaFiles.begin(), + mediaFiles.end()); + std::vector displayNames; + FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(caretDataFiles, + displayNames); + CaretAssert(mediaFiles.size() == displayNames.size()); + + /* + * Load the file selection combo box. + */ + int32_t selectedFileIndex = -1; + const int32_t numFiles = static_cast(mediaFiles.size()); + for (int32_t i = 0; i < numFiles; i++) { + MediaFile* dataFile = mediaFiles[i]; + + AString dataTypeName = DataFileTypeEnum::toOverlayTypeName(dataFile->getDataFileType()); + CaretAssertVectorIndex(displayNames, i); + this->fileComboBox->addItem(displayNames[i], + QVariant::fromValue((void*)dataFile)); + if (dataFile == selectedFile) { + selectedFileIndex = i; + } + } + if (selectedFileIndex >= 0) { + this->fileComboBox->setCurrentIndex(selectedFileIndex); + } + + /* + * Load the frame selection combo box + */ + int32_t numberOfFrames = 0; + this->frameNameComboBox->blockSignals(true); + this->frameNameComboBox->clear(); + if (selectedFile != NULL) { + numberOfFrames = selectedFile->getNumberOfFrames(); + for (int32_t i = 0; i < numberOfFrames; i++) { + this->frameNameComboBox->addItem(AString::number(i + 1)); + } + this->frameNameComboBox->setCurrentIndex(selectedFrameIndex); + } + this->frameNameComboBox->blockSignals(false); + + /* + * Load the frame index spin box that ranges [1, N]. + */ + m_frameIndexSpinBox->blockSignals(true); + m_frameIndexSpinBox->setRange(1, numberOfFrames); + if (selectedFile != NULL) { + m_frameIndexSpinBox->setValue(selectedFrameIndex + 1); + } + m_frameIndexSpinBox->blockSignals(false); + + /* + * Update enable check box + */ + Qt::CheckState checkState = Qt::Unchecked; + if (m_mediaOverlay != NULL) { + if (m_mediaOverlay->isEnabled()) { + checkState = Qt::Checked; + } + } + this->enabledCheckBox->setCheckState(checkState); + + m_frameYokingGroupComboBox->setMapYokingGroup(overlay->getMapYokingGroup()); + + this->opacityDoubleSpinBox->blockSignals(true); + this->opacityDoubleSpinBox->setValue(m_mediaOverlay->getOpacity()); + this->opacityDoubleSpinBox->blockSignals(false); + + const bool haveFile = (selectedFile != NULL); + bool haveMultipleFrames = false; + bool haveOpacity = false; + if (haveFile) { + haveMultipleFrames = (selectedFile->getNumberOfFrames() > 1); + } + + /** + * Yoking is enabled when either: + * (1) The file frames to both surface and volumes + * (2) The file has multiple frames. + */ + bool haveYoking = false; + if (haveFile) { + if (haveMultipleFrames) { + haveYoking = true; + } + } + + /* + * Update tooltips with full path to file and name of frame + * as names may be too long to fit into combo boxes + */ + AString fileComboBoxToolTip("Select file for this overlay"); + AString nameComboBoxToolTip("Select frame by its name"); + if (selectedFile != NULL) { + FileInformation fileInfo(selectedFile->getFileName()); + fileComboBoxToolTip.append(":\n" + + fileInfo.getFileName() + + "\n" + + fileInfo.getPathName() + + "\n\n" + + "Copy File Name/Path to Clipboard with Construction Menu"); + + nameComboBoxToolTip.append(":\n" + + this->frameNameComboBox->currentText()); + } + this->fileComboBox->setToolTip(fileComboBoxToolTip); + this->frameNameComboBox->setToolTip(nameComboBoxToolTip); + + /* + * Make sure items are enabled at the appropriate time + */ + this->fileComboBox->setEnabled(haveFile); + this->frameNameComboBox->setEnabled(haveFile); + this->m_frameIndexSpinBox->setEnabled(haveMultipleFrames); + this->enabledCheckBox->setEnabled(haveFile); + this->constructionAction->setEnabled(true); + this->opacityDoubleSpinBox->setEnabled(haveOpacity); + this->m_frameYokingGroupComboBox->getWidget()->setEnabled(haveYoking); + this->settingsAction->setEnabled(true); +} + +/** + * Update graphics and GUI after selections made + */ +void +MediaOverlayViewController::updateUserInterfaceAndGraphicsWindow() +{ + updateUserInterface(); + updateGraphicsWindow(); +} + +/** + * Update graphics and GUI after selections made + */ +void +MediaOverlayViewController::updateUserInterface() +{ +// if (this->overlay->getMapYokingGroup() != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { +// EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +// } +// else { +// EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); +// } +} + +/** + * Update graphics after selections made + */ +void +MediaOverlayViewController::updateGraphicsWindow() +{ + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); +// if (this->overlay->getMapYokingGroup() != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { +// EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +// } +// else { + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); +// } +} + +/** + * Create the construction menu. + * @param parent + * Parent widget. + * @param descriptivePrefix + * Descriptive prefix + * @param menuActionNamePrefix + * Prefix for macros + */ +QMenu* +MediaOverlayViewController::createConstructionMenu(QWidget* parent, + const AString& descriptivePrefix, + const AString& menuActionNamePrefix) +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + + QMenu* menu = new QMenu(parent); + QObject::connect(menu, SIGNAL(aboutToShow()), + this, SLOT(menuConstructionAboutToShow())); + + QAction* addAboveAction = menu->addAction("Add Overlay Above", + this, + SLOT(menuAddOverlayAboveTriggered())); + addAboveAction->setObjectName(menuActionNamePrefix + + "AddOverlayAbove"); + addAboveAction->setToolTip("Add an overlay above this overlay"); + macroManager->addMacroSupportToObject(addAboveAction, + ("Add overlay above " + descriptivePrefix)); + + QAction* addBelowAction = menu->addAction("Add Overlay Below", + this, + SLOT(menuAddOverlayBelowTriggered())); + addBelowAction->setObjectName(menuActionNamePrefix + + "AddOverlayBelow"); + addBelowAction->setToolTip("Add an overlay below this overlay"); + macroManager->addMacroSupportToObject(addBelowAction, + ("Add overlay below " + descriptivePrefix)); + + menu->addSeparator(); + + QAction* moveUpAction = menu->addAction("Move Overlay Up", + this, + SLOT(menuMoveOverlayUpTriggered())); + moveUpAction->setObjectName(menuActionNamePrefix + + "MoveOverlayUp"); + moveUpAction->setToolTip("Move this overlay up"); + macroManager->addMacroSupportToObject(moveUpAction, + ("Move " + descriptivePrefix + " up")); + + QAction* moveDownAction = menu->addAction("Move Overlay Down", + this, + SLOT(menuMoveOverlayDownTriggered())); + moveDownAction->setObjectName(menuActionNamePrefix + + "MoveOverlayDown"); + moveDownAction->setToolTip("Move this overlay down"); + macroManager->addMacroSupportToObject(moveDownAction, + ("Move " + descriptivePrefix + " down")); + + menu->addSeparator(); + + QAction* removeAction = menu->addAction("Remove This Overlay", + this, + SLOT(menuRemoveOverlayTriggered())); + removeAction->setObjectName(menuActionNamePrefix + + "RemoveOverlay"); + removeAction->setToolTip("Remove this overlay"); + macroManager->addMacroSupportToObject(removeAction, + ("Remove " + descriptivePrefix)); + + menu->addSeparator(); + + m_constructionReloadFileAction = menu->addAction("Reload Selected File", + this, + SLOT(menuReloadFileTriggered())); + m_constructionReloadFileAction->setObjectName(menuActionNamePrefix + + "ReloadSelectedFile"); + m_constructionReloadFileAction->setToolTip("Reload file in this overlay"); + macroManager->addMacroSupportToObject(m_constructionReloadFileAction, + ("Reload file in " + descriptivePrefix)); + + menu->addSeparator(); + + m_copyPathAndFileNameToClipboardAction = menu->addAction("Copy Path and File Name to Clipboard", + this, + SLOT(menuCopyFileNameToClipBoard())); + m_copyPathAndFileNameToClipboardAction->setObjectName(menuActionNamePrefix + + "CopyPathAndFileNameToClipboard"); + m_copyPathAndFileNameToClipboardAction->setToolTip("Copy path and file name of file in this overlay to clipboard"); + macroManager->addMacroSupportToObject(m_copyPathAndFileNameToClipboardAction, + ("Copy path and filename from " + descriptivePrefix + " to clipboard")); + + QAction* copyFrameNameAction = menu->addAction("Copy Frame Name to Clipboard", + this, + SLOT(menuCopyFrameNameToClipBoard())); + copyFrameNameAction->setObjectName(menuActionNamePrefix + + "CopyFrameNameToClipboard"); + copyFrameNameAction->setToolTip("Copy name of selected frame to the clipboard"); + macroManager->addMacroSupportToObject(copyFrameNameAction, + ("Copy frame namne in " + descriptivePrefix + " to clipboard")); + + return menu; + +} + +/** + * Called when construction menu is about to be displayed. + */ +void +MediaOverlayViewController::menuConstructionAboutToShow() +{ + if (m_mediaOverlay != NULL) { + MediaFile* mediaFile = NULL; + int32_t frameIndex = -1; + m_mediaOverlay->getSelectionData(mediaFile, + frameIndex); + + QString menuText = "Reload Selected File"; + if (mediaFile != NULL) { + if (mediaFile->isModified()) { + QString suffix = " (MODIFIED)"; + menuText += suffix; + } + + bool dynConnFlag(false); + switch (mediaFile->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + dynConnFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::METRIC_DYNAMIC: + dynConnFlag = true; + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + dynConnFlag = true; + break; + } + m_constructionReloadFileAction->setEnabled( ! dynConnFlag); + m_copyPathAndFileNameToClipboardAction->setEnabled( ! dynConnFlag); + } + + m_constructionReloadFileAction->setText(menuText); + } +} + +/** + * Add an overlay above this overlay. + */ +void +MediaOverlayViewController::menuAddOverlayAboveTriggered() +{ + emit requestAddOverlayAbove(m_overlayIndex); +} + +/** + * Add an overlay below this overlay. + */ +void +MediaOverlayViewController::menuAddOverlayBelowTriggered() +{ + emit requestAddOverlayBelow(m_overlayIndex); +} + +/** + * Remove this overlay. + */ +void +MediaOverlayViewController::menuRemoveOverlayTriggered() +{ + emit requestRemoveOverlay(m_overlayIndex); +} + +/** + * Move this overlay down. + */ +void +MediaOverlayViewController::menuMoveOverlayDownTriggered() +{ + emit requestMoveOverlayDown(m_overlayIndex); +} + +/** + * Move this overlay down. + */ +void +MediaOverlayViewController::menuMoveOverlayUpTriggered() +{ + emit requestMoveOverlayUp(m_overlayIndex); +} + +/** + * Copy the file name to the clip board. + */ +void +MediaOverlayViewController::menuCopyFileNameToClipBoard() +{ + if (m_mediaOverlay != NULL) { + MediaFile* mediaFile = NULL; + int32_t frameIndex = -1; + m_mediaOverlay->getSelectionData(mediaFile, + frameIndex); + + if (mediaFile != NULL) { + QApplication::clipboard()->setText(mediaFile->getFileName().trimmed(), + QClipboard::Clipboard); + } + } +} + +/** + * Copy the frame name to the clip board. + */ +void +MediaOverlayViewController::menuCopyFrameNameToClipBoard() +{ + if (m_mediaOverlay != NULL) { + MediaFile* mediaFile = NULL; + int32_t frameIndex = -1; + m_mediaOverlay->getSelectionData(mediaFile, + frameIndex); + + if (mediaFile != NULL) { + if ((frameIndex >= 0) + && (frameIndex < mediaFile->getNumberOfFrames())) { + QApplication::clipboard()->setText(mediaFile->getFrameName(frameIndex).trimmed(), + QClipboard::Clipboard); + } + } + } +} + +/** + * Reload the file in the overlay. + */ +void MediaOverlayViewController::menuReloadFileTriggered() +{ + if (m_mediaOverlay != NULL) { + MediaFile* mediaFile = NULL; + int32_t frameIndex = -1; + m_mediaOverlay->getSelectionData(mediaFile, + frameIndex); + + if (mediaFile != NULL) { + AString username; + AString password; + + if (DataFile::isFileOnNetwork(mediaFile->getFileName())) { + const QString msg("This file is on the network. " + "If accessing the file requires a username and " + "password, enter it here. Otherwise, remove any " + "text from the username and password fields."); + + + if (UsernamePasswordWidget::getUserNameAndPasswordInDialog(m_constructionToolButton, + "Username and Password", + msg, + username, + password)) { + /* nothing */ + } + else { + return; + } + } + + EventDataFileReload reloadEvent(GuiManager::get()->getBrain(), + mediaFile); + reloadEvent.setUsernameAndPassword(username, + password); + EventManager::get()->sendEvent(reloadEvent.getPointer()); + + if (reloadEvent.isError()) { + WuQMessageBox::errorOk(m_constructionToolButton, + reloadEvent.getErrorMessage()); + } + + updateOverlaySettingsEditor(); + + updateUserInterfaceAndGraphicsWindow(); + } + } +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/MediaOverlayViewController.h connectome-workbench-1.5.0/src/GuiQt/MediaOverlayViewController.h --- connectome-workbench-1.4.2/src/GuiQt/MediaOverlayViewController.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MediaOverlayViewController.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,164 @@ +#ifndef __MEDIA_OVERLAY_VIEW_CONTROLLER__H_ +#define __MEDIA_OVERLAY_VIEW_CONTROLLER__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include + +class QAction; +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QGridLayout; +class QMenu; +class QSpinBox; +class QToolButton; + +namespace caret { + + class AString; + class MapYokingGroupComboBox; + class MediaOverlay; + class WuQGridLayoutGroup; + + class MediaOverlayViewController : public QObject { + + Q_OBJECT + + public: + MediaOverlayViewController(const Qt::Orientation orientation, + QGridLayout* gridLayout, + const int32_t browserWindowIndex, + const int32_t overlayIndex, + const QString& parentObjectName, + QObject* parent); + + virtual ~MediaOverlayViewController(); + + void setVisible(bool visible); + + void updateViewController(MediaOverlay* overlay); + + signals: + void requestAddOverlayAbove(const int32_t overlayIndex); + + void requestAddOverlayBelow(const int32_t overlayIndex); + + void requestRemoveOverlay(const int32_t overlayIndex); + + void requestMoveOverlayUp(const int32_t overlayIndex); + + void requestMoveOverlayDown(const int32_t overlayIndex); + + private slots: + void fileComboBoxSelected(int); + + void frameNameComboBoxSelected(int); + + void frameIndexSpinBoxValueChanged(int); + + void enabledCheckBoxClicked(bool); + + void settingsActionTriggered(); + + void opacityDoubleSpinBoxValueChanged(double value); + + void yokingGroupActivated(); + + void menuAddOverlayAboveTriggered(); + + void menuAddOverlayBelowTriggered(); + + void menuRemoveOverlayTriggered(); + + void menuMoveOverlayDownTriggered(); + + void menuMoveOverlayUpTriggered(); + + void menuReloadFileTriggered(); + + void menuCopyFileNameToClipBoard(); + + void menuCopyFrameNameToClipBoard(); + + void menuConstructionAboutToShow(); + + private: + MediaOverlayViewController(const MediaOverlayViewController&); + + MediaOverlayViewController& operator=(const MediaOverlayViewController&); + + void updateUserInterfaceAndGraphicsWindow(); + + void updateUserInterface(); + + void updateGraphicsWindow(); + + QMenu* createConstructionMenu(QWidget* parent, + const AString& descriptivePrefix, + const AString& menuActionNamePrefix); + + void validateYokingSelection(); + + void updateOverlaySettingsEditor(); + + const int32_t browserWindowIndex; + + const int32_t m_overlayIndex; + + MediaOverlay* m_mediaOverlay; + + QCheckBox* enabledCheckBox; + + QComboBox* fileComboBox; + + QComboBox* frameNameComboBox; + + QSpinBox* m_frameIndexSpinBox; + + QDoubleSpinBox* opacityDoubleSpinBox; + + QToolButton* m_constructionToolButton; + + QAction* constructionAction; + + QAction* settingsAction; + + MapYokingGroupComboBox* m_frameYokingGroupComboBox; + + QAction* m_constructionReloadFileAction; + + QAction* m_copyPathAndFileNameToClipboardAction; + + WuQGridLayoutGroup* gridLayoutGroup; + + friend class OverlaySetViewController; + + }; + +#ifdef __MEDIA_OVERLAY_VIEW_CONTROLLER_DECLARE__ + // +#endif // __MEDIA_OVERLAY_VIEW_CONTROLLER_DECLARE__ + +} // namespace +#endif //__MEDIA_OVERLAY_VIEW_CONTROLLER__H_ diff -Nru connectome-workbench-1.4.2/src/GuiQt/MouseEvent.cxx connectome-workbench-1.5.0/src/GuiQt/MouseEvent.cxx --- connectome-workbench-1.4.2/src/GuiQt/MouseEvent.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MouseEvent.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -54,6 +54,8 @@ * X-coordinate of mouse when button was pressed * @param mousePressY * Y-coordinate of mouse when button was pressed + * @param mouseHistoryXY + * History of XY-coordinatdes from time mouse was pressed until released * @param firstDraggingFlag * Should be true the first time in in a mouse dragging operation. */ @@ -66,6 +68,7 @@ const int32_t dy, const int32_t mousePressX, const int32_t mousePressY, + const std::vector& mouseHistoryXY, const bool firstDraggingFlag) : CaretObject() { @@ -87,6 +90,7 @@ m_dy = dy; m_pressX = mousePressX; m_pressY = mousePressY; + m_xyHistory = mouseHistoryXY; m_firstDraggingFlag = firstDraggingFlag; } @@ -296,6 +300,27 @@ } /** + * @return Number of items in XY history + */ +int32_t +MouseEvent::getXyHistoryCount() const +{ + return m_xyHistory.size(); +} + +/** + * @return xy point at given history count + * @param index + * Index of item + */ +MouseEvent::XY +MouseEvent::getHistoryAtIndex(const int32_t index) const +{ + CaretAssertVectorIndex(m_xyHistory, index); + return m_xyHistory[index]; +} + +/** * Get the Global X-coordinate of where the mouse was pressed. * @return The Global X-coordinate. * diff -Nru connectome-workbench-1.4.2/src/GuiQt/MouseEvent.h connectome-workbench-1.5.0/src/GuiQt/MouseEvent.h --- connectome-workbench-1.4.2/src/GuiQt/MouseEvent.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MouseEvent.h 2021-02-16 19:46:47.000000000 +0000 @@ -38,6 +38,20 @@ class MouseEvent : public CaretObject { public: + /** + * Contains mouse X/Y coordinates + */ + class XY { + public: + XY(const int32_t x, + const int32_t y) + : m_x(x), + m_y(y) { } + + int32_t m_x; + int32_t m_y; + }; + MouseEvent(const BrainOpenGLViewportContent* viewportContent, BrainOpenGLWidget* openGLWidget, const int32_t browserWindowIndex, @@ -47,6 +61,7 @@ const int32_t dy, const int32_t mousePressX, const int32_t mousePressY, + const std::vector& mouseHistoryXY, const bool firstDraggingFlag); virtual ~MouseEvent(); @@ -81,6 +96,10 @@ int32_t getPressedY() const; + int32_t getXyHistoryCount() const; + + XY getHistoryAtIndex(const int32_t index) const; + void getGlobalXY(const int32_t x, const int32_t y, int32_t& outGlobalX, @@ -109,6 +128,8 @@ int32_t m_pressY; + std::vector m_xyHistory; + int32_t m_wheelRotation; bool m_firstDraggingFlag; diff -Nru connectome-workbench-1.4.2/src/GuiQt/MovieDialog.cxx connectome-workbench-1.5.0/src/GuiQt/MovieDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/MovieDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MovieDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -229,7 +229,7 @@ // if(!(crop[0]&&crop[1])) GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex)->getViewportSize(crop[0],crop[1]); - unlink(fileName); + unlink(fileName.toLatin1().data()); CaretLogInfo("Rendering movie to:" + fileName); AString ffmpeg = SystemUtilities::getWorkbenchHome() + AString("/ffmpeg "); @@ -263,7 +263,7 @@ for(int i = 0;isize().width(); }*/ - std::vector imageFileExtensions; - AString defaultFileExtension; - ImageFile::getImageFileExtensions(imageFileExtensions, - defaultFileExtension); - - + std::vector readImageFileExtensions, writeImageFileExtensions; + AString defaultImageExtension; + ImageFile::getWorkbenchSupportedImageFileExtensions(readImageFileExtensions, + writeImageFileExtensions, + defaultImageExtension); bool validExtension = false; - for (std::vector::iterator extensionIterator = imageFileExtensions.begin(); - extensionIterator != imageFileExtensions.end(); + for (std::vector::iterator extensionIterator = writeImageFileExtensions.begin(); + extensionIterator != writeImageFileExtensions.end(); extensionIterator++) { if (filename.endsWith(*extensionIterator)) { validExtension = true; @@ -738,8 +737,8 @@ } if (validExtension == false) { - if (defaultFileExtension.isEmpty() == false) { - filename += ("." + defaultFileExtension); + if (defaultImageExtension.isEmpty() == false) { + filename += ("." + defaultImageExtension); } } diff -Nru connectome-workbench-1.4.2/src/GuiQt/MovieRecordingDialog.cxx connectome-workbench-1.5.0/src/GuiQt/MovieRecordingDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/MovieRecordingDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/MovieRecordingDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -40,6 +40,7 @@ #include "BrainBrowserWindowComboBox.h" #include "CaretAssert.h" #include "CaretFileDialog.h" +#include "CaretPreferences.h" #include "CursorDisplayScoped.h" #include "Event.h" #include "EventManager.h" @@ -503,7 +504,10 @@ AString errorMessage; const bool successFlag = movieRecorder->createMovie(filename, errorMessage); - if ( ! successFlag) { + if (successFlag) { + SessionManager::get()->getCaretPreferences()->addToRecentFilesAndOrDirectories(filename); + } + else { WuQMessageBox::errorOk(parent, errorMessage); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/OverlaySettingsEditorDialog.cxx connectome-workbench-1.5.0/src/GuiQt/OverlaySettingsEditorDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/OverlaySettingsEditorDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/OverlaySettingsEditorDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -35,12 +35,11 @@ #include "ChartTwoOverlay.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiConnectivityMatrixParcelFile.h" -#include "EventChartOverlayValidate.h" +#include "EventChartTwoOverlayValidate.h" #include "EventDataFileDelete.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventOverlayValidate.h" -#include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GiftiLabelTableEditor.h" #include "MapSettingsChartTwoLineHistoryWidget.h" @@ -121,7 +120,7 @@ "Layer"); m_lineHistoryWidgetTabIndex = m_tabWidget->addTab(m_lineHistoryWidget, - "Lines"); + "Dyn-Lines"); m_metadataWidgetTabIndex = m_tabWidget->addTab(new QWidget(), "Metadata"); m_tabWidget->setTabEnabled(m_tabWidget->count() - 1, false); @@ -142,7 +141,6 @@ this->setLayoutSpacingAndMargins(layout); layout->addWidget(mapNameWidget); layout->addWidget(m_tabWidget); - //layout->addWidget(windowOptionsWidget); this->setCentralWidget(w, WuQDialog::SCROLL_AREA_NEVER); @@ -310,7 +308,7 @@ bool isParcelsValid = false; bool isFiberTrajectoryValid = false; bool isVolumeLayer = false; - bool isLinesValid = false; + bool isLineHistoryValid = false; QString mapFileName = ""; QString mapName = ""; @@ -407,14 +405,18 @@ else if (m_chartOverlay != NULL) { mapFileName = m_caretMappableDataFile->getFileNameNoPath(); - bool hasMapsFlag = false; - bool hasHistoryFlag = false; + bool hasMapsFlag = false; + bool hasHistoryFlag = false; + bool hasLineLayerFlag = false; switch (m_chartOverlay->getChartTwoDataType()) { case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: hasMapsFlag = true; break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + hasLineLayerFlag = true; + break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: hasHistoryFlag = true; break; @@ -497,12 +499,20 @@ } } else if (hasHistoryFlag) { - isLinesValid = true; + isLineHistoryValid = true; m_lineHistoryWidget->updateContent(m_chartOverlay); if (mapName.isEmpty()) { mapName = "Line Chart History"; } } + else if (hasLineLayerFlag) { + if (mapName.isEmpty()) { + if ((m_selectedMapFileIndex >= 0) + && (m_selectedMapFileIndex < m_caretMappableDataFile->getNumberOfMaps())) { + mapName = m_caretMappableDataFile->getMapName(m_selectedMapFileIndex); + } + } + } } else { CaretAssert(0); @@ -528,7 +538,7 @@ m_tabWidget->setTabEnabled(m_trajectoryWidgetTabIndex, isFiberTrajectoryValid); m_tabWidget->setTabEnabled(m_lineHistoryWidgetTabIndex, - isLinesValid); + isLineHistoryValid); /* * When the selected tab is invalid, we want to select the @@ -588,7 +598,7 @@ } if (m_chartOverlay != NULL) { - EventChartOverlayValidate validateChartOverlayEvent(m_chartOverlay); + EventChartTwoOverlayValidate validateChartOverlayEvent(m_chartOverlay); EventManager::get()->sendEvent(validateChartOverlayEvent.getPointer()); if ( ! validateChartOverlayEvent.isValidChartOverlay()) { m_chartOverlay = NULL; @@ -611,7 +621,7 @@ OverlaySettingsEditorDialog::updateChartLinesInDialog() { if (m_chartOverlay != NULL) { - EventChartOverlayValidate validateChartOverlayEvent(m_chartOverlay); + EventChartTwoOverlayValidate validateChartOverlayEvent(m_chartOverlay); EventManager::get()->sendEvent(validateChartOverlayEvent.getPointer()); if ( ! validateChartOverlayEvent.isValidChartOverlay()) { m_chartOverlay = NULL; diff -Nru connectome-workbench-1.4.2/src/GuiQt/OverlayViewController.cxx connectome-workbench-1.5.0/src/GuiQt/OverlayViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/OverlayViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/OverlayViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -174,8 +174,8 @@ + "Opacity"); this->opacityDoubleSpinBox->setMinimum(0.0); this->opacityDoubleSpinBox->setMaximum(1.0); - this->opacityDoubleSpinBox->setSingleStep(0.10); - this->opacityDoubleSpinBox->setDecimals(1); + this->opacityDoubleSpinBox->setSingleStep(0.05); + this->opacityDoubleSpinBox->setDecimals(2); this->opacityDoubleSpinBox->setFixedWidth(50); QObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(opacityDoubleSpinBoxValueChanged(double))); @@ -697,7 +697,7 @@ AString dataTypeName = DataFileTypeEnum::toOverlayTypeName(dataFile->getDataFileType()); CaretAssertVectorIndex(displayNames, i); this->fileComboBox->addItem(displayNames[i], - qVariantFromValue((void*)dataFile)); + QVariant::fromValue((void*)dataFile)); if (dataFile == selectedFile) { selectedFileIndex = i; } diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteCreateNewDialog.cxx connectome-workbench-1.5.0/src/GuiQt/PaletteCreateNewDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/PaletteCreateNewDialog.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteCreateNewDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,264 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_CREATE_NEW_DIALOG_DECLARE__ +#include "PaletteCreateNewDialog.h" +#undef __PALETTE_CREATE_NEW_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include + +#include "Brain.h" +#include "CaretAssert.h" +#include "GuiManager.h" +#include "Palette.h" +#include "PaletteFile.h" +#include "PaletteNew.h" +#include "PalettePixmapPainter.h" +#include "PaletteSelectionWidget.h" +#include "WuQMessageBox.h" + +using namespace caret; + +/** + * \class caret::PaletteCreateNewDialog + * \brief Dialog for creation of a new palette + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param pixmapMode + * palette pixmap Interpolation mode (on/off) + * @param parent + * The parent widget + */ +PaletteCreateNewDialog::PaletteCreateNewDialog(const PalettePixmapPainter::Mode pixmapMode, + QWidget* parent) +: WuQDialogModal("Create New Palette", + parent), +m_pixmapMode(pixmapMode) +{ + m_copyPaletteRadioButton = new QRadioButton("Copy Palette"); + m_newPaletteRadioButton = new QRadioButton("New Palette"); + + m_paletteSelectionWidget = new PaletteSelectionWidget(); + QObject::connect(m_paletteSelectionWidget, &PaletteSelectionWidget::paletteSelectionChanged, + this, &PaletteCreateNewDialog::paletteSelected); + + QButtonGroup* buttonGroup = new QButtonGroup(); + buttonGroup->addButton(m_copyPaletteRadioButton); + buttonGroup->addButton(m_newPaletteRadioButton); + QObject::connect(buttonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &PaletteCreateNewDialog::typeButtonClicked); + + QLabel* positiveLabel = new QLabel("Positive Control Points"); + m_newPalettePositiveSpinBox = new QSpinBox(); + m_newPalettePositiveSpinBox->setRange(2, 999); + m_newPalettePositiveSpinBox->setSingleStep(1); + m_newPalettePositiveSpinBox->setValue(5); + + QLabel* negativeLabel = new QLabel("Negative Control Points"); + m_newPaletteNegativeSpinBox = new QSpinBox(); + m_newPaletteNegativeSpinBox->setRange(2, 999); + m_newPaletteNegativeSpinBox->setSingleStep(1); + m_newPaletteNegativeSpinBox->setValue(5); + + QWidget* widget = new QWidget(); + QGridLayout* gridLayout = new QGridLayout(widget); + gridLayout->setColumnStretch(3, 100); + int row(0); + gridLayout->addWidget(m_copyPaletteRadioButton, + row, 0, 1, 2); + row++; + gridLayout->addWidget(m_paletteSelectionWidget, + row, 1, 1, 2); + row++; + gridLayout->addWidget(m_newPaletteRadioButton, + row, 0, 1, 2); + row++; + gridLayout->addWidget(positiveLabel, + row, 1); + gridLayout->addWidget(m_newPalettePositiveSpinBox, + row, 2); + row++; + gridLayout->addWidget(negativeLabel, + row, 1); + gridLayout->addWidget(m_newPaletteNegativeSpinBox, + row, 2); + + setCentralWidget(widget, SCROLL_AREA_NEVER); + + m_newPaletteRadioButton->setChecked(true); + typeButtonClicked(buttonGroup->checkedButton()); +} + +/** + * Destructor. + */ +PaletteCreateNewDialog::~PaletteCreateNewDialog() +{ +} + +/** + * Called when a palette is selected + * @param palette + * Palette selected by the user (may be NULL) + */ +void +PaletteCreateNewDialog::paletteSelected() +{ +} + +/** + * Called when OK button clicked + */ +void +PaletteCreateNewDialog::okButtonClicked() +{ + if (m_copyPaletteRadioButton->isChecked()) { + m_palette.reset(); + std::unique_ptr palette = m_paletteSelectionWidget->getSelectedPalette(); + if (palette) { + m_palette.reset(new PaletteNew(*palette)); + } + else { + WuQMessageBox::errorOk(this, + "No palette is selected for copying"); + return; + } + } + else if (m_newPaletteRadioButton->isChecked()) { + m_palette.reset(createPaletteNew("", + m_newPalettePositiveSpinBox->value(), + m_newPaletteNegativeSpinBox->value())); + } + else { + WuQMessageBox::errorOk(this, + "Choose a palette mode"); + return; + } + + WuQDialogModal::okButtonClicked(); +} + +/** + * Called when a palette type button is clicked + * @param button + * Button that was clicked + */ +void +PaletteCreateNewDialog::typeButtonClicked(QAbstractButton* button) +{ + m_paletteSelectionWidget->setEnabled(m_copyPaletteRadioButton->isChecked()); + + m_newPaletteNegativeSpinBox->setEnabled(m_newPaletteRadioButton->isChecked()); + m_newPalettePositiveSpinBox->setEnabled(m_newPaletteRadioButton->isChecked()); + + if (button == m_copyPaletteRadioButton) { + + } + else if (button == m_newPaletteRadioButton) { + + } +} + +/** + * @return The new palette or NULL if no palette + */ +std::unique_ptr +PaletteCreateNewDialog::getPalette() +{ + std::unique_ptr paletteOut; + if (m_palette) { + paletteOut.reset(new PaletteNew(*m_palette)); + } + + return paletteOut; +} + +/** + * @return A new palette containing the given number of positive and negative control points. The + * positive region is shades or red, the negative region is shades for blue, and zero is green. + * + * @param name + * Name for the palette + * @param numberOfPositiveControlPoints + * Number of positive points + * @param numberOfNegativeControlPoints + * Number of negative points + */ +PaletteNew* +PaletteCreateNewDialog::createPaletteNew(const AString& name, + const int32_t numberOfPositiveControlPoints, + const int32_t numberOfNegativeControlPoints) +{ + const int32_t numPos(std::max(2, numberOfPositiveControlPoints)); + const int32_t numNeg(std::max(2, numberOfNegativeControlPoints)); + + const float posStep(1.0 / numPos); + + const float redStep(1.0 / (numPos + 3)); + float redCompontent(redStep * 2.0); + + std::vector posRange; + float posScalar(0.0); + for (int32_t i = 0; i < numPos; i++) { + if (i == (numPos - 1)) { + posScalar = 1.0f; + redCompontent = 1.0f; + } + posRange.emplace_back(posScalar, redCompontent, 0.0f, 0.0f); + + posScalar += posStep; + redCompontent += redStep; + } + + float negStep(1.0 / numNeg); + + float blueStep(1.0 / (numNeg + 3)); + float blueComponent(1.0); + + std::vector negRange; + float negScalar(-1.0); + for (int32_t i = 0; i < numNeg; i++) { + if (i == (numNeg - 1)) { + negScalar = 0.0f; + } + negRange.emplace_back(negScalar, 0.0, 0.0f, blueComponent); + + blueComponent -= blueStep; + negScalar += negStep; + } + + float zeroGreen[3] { 0.0, 1.0, 0.0 }; + PaletteNew* paletteNew = new PaletteNew(posRange, + zeroGreen, + negRange); + paletteNew->setName(name); + + return paletteNew; +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteCreateNewDialog.h connectome-workbench-1.5.0/src/GuiQt/PaletteCreateNewDialog.h --- connectome-workbench-1.4.2/src/GuiQt/PaletteCreateNewDialog.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteCreateNewDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,94 @@ +#ifndef __PALETTE_CREATE_NEW_DIALOG_H__ +#define __PALETTE_CREATE_NEW_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "PalettePixmapPainter.h" +#include "WuQDialogModal.h" + +class QAbstractButton; +class QRadioButton; +class QSpinBox; + +namespace caret { + + class PaletteNew; + class PaletteSelectionWidget; + + class PaletteCreateNewDialog : public WuQDialogModal { + + Q_OBJECT + + public: + PaletteCreateNewDialog(const PalettePixmapPainter::Mode pixmapMode, + QWidget* parent = 0); + + virtual ~PaletteCreateNewDialog(); + + PaletteCreateNewDialog(const PaletteCreateNewDialog&) = delete; + + PaletteCreateNewDialog& operator=(const PaletteCreateNewDialog&) = delete; + + std::unique_ptr getPalette(); + + static PaletteNew* createPaletteNew(const AString& name, + const int32_t numberOfPositiveControlPoints, + const int32_t numberOfNegativeControlPoints); + + // ADD_NEW_METHODS_HERE + + protected: + virtual void okButtonClicked(); + + private slots: + void paletteSelected(); + + void typeButtonClicked(QAbstractButton* button); + + private: + const PalettePixmapPainter::Mode m_pixmapMode; + + PaletteSelectionWidget* m_paletteSelectionWidget; + + QRadioButton* m_copyPaletteRadioButton; + + QRadioButton* m_newPaletteRadioButton; + + QSpinBox* m_newPalettePositiveSpinBox; + + QSpinBox* m_newPaletteNegativeSpinBox; + + std::unique_ptr m_palette; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PALETTE_CREATE_NEW_DIALOG_DECLARE__ + // +#endif // __PALETTE_CREATE_NEW_DIALOG_DECLARE__ + +} // namespace +#endif //__PALETTE_CREATE_NEW_DIALOG_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteEditorDialog.cxx connectome-workbench-1.5.0/src/GuiQt/PaletteEditorDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/PaletteEditorDialog.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteEditorDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,888 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_EDITOR_DIALOG_DECLARE__ +#include "PaletteEditorDialog.h" +#undef __PALETTE_EDITOR_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Brain.h" +#include "CaretAssert.h" +#include "EventManager.h" +#include "GuiManager.h" +#include "Palette.h" +#include "PaletteCreateNewDialog.h" +#include "PaletteEditorRangeWidget.h" +#include "PaletteFile.h" +#include "PaletteGroup.h" +#include "PaletteNew.h" +#include "PalettePixmapPainter.h" +#include "PaletteSelectionWidget.h" +#include "WuQColorEditorWidget.h" +#include "WuQDataEntryDialog.h" +#include "WuQMessageBox.h" +#include "WuQScrollArea.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::PaletteEditorDialog + * \brief Dialog for editing color palettes + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param parent + * The parent widget + */ +PaletteEditorDialog::PaletteEditorDialog(QWidget* parent) +: WuQDialogNonModal("Palette Editor", + parent) +{ + /* + * No apply button + */ + setApplyButtonText(""); + + m_colorEditButtonGroup = new QButtonGroup(this); + m_colorEditButtonGroup->setExclusive(true); + + QWidget* paletteBarWidget = createPaletteBarWidget(); + QWidget* paletteSelectionWidget = createPaletteSelectionWidget(); + QWidget* controlPointsWidget = createControlPointsWidget(); + QWidget* movePaletteButtonsWidget = createMovePaletteButtonsWidget(); + + m_colorEditorWidget = new WuQColorEditorWidget(); + QObject::connect(m_colorEditorWidget, &WuQColorEditorWidget::colorChanged, + this, &PaletteEditorDialog::colorEditorColorChanged); + + QGroupBox* leftGroupBox = new QGroupBox("Palette Editing"); + QGridLayout* leftLayout = new QGridLayout(leftGroupBox); + leftLayout->addWidget(paletteBarWidget, 0, 0, 1, 2, Qt::AlignHCenter); + leftLayout->addWidget(controlPointsWidget, 1, 0); + leftLayout->addWidget(m_colorEditorWidget, 1, 1); + + QWidget* dialogWidget = new QWidget(); + QHBoxLayout* dialogLayout = new QHBoxLayout(dialogWidget); + dialogLayout->addWidget(leftGroupBox); + dialogLayout->addWidget(movePaletteButtonsWidget); + dialogLayout->addWidget(paletteSelectionWidget); + + setCentralWidget(dialogWidget, + ScrollAreaStatus::SCROLL_AREA_NEVER); + + /* + * No resizing of dialog + */ + setFixedSize(sizeHint()); + + disableAutoDefaultForAllPushButtons(); + + const bool demoModeFlag(false); + if (demoModeFlag) { + paletteSelectionWidget->setEnabled(false); + movePaletteButtonsWidget->setEnabled(false); + + std::unique_ptr palette = m_paletteSelectionWidget->getSelectedPalette(); + loadPaletteIntoEditor(palette); + } +} + +/** + * Destructor. + */ +PaletteEditorDialog::~PaletteEditorDialog() +{ +} + +/** + * Color editor changed + * @param red + * New red component + * @param green + * New green component + * @param blue + * New blue component + */ +void +PaletteEditorDialog::colorEditorColorChanged(const QColor& color) +{ + CaretRgb rgb(color.red(), + color.green(), + color.blue()); + + m_positiveRangeWidget->updateControlPointColor(rgb); + m_zeroRangeWidget->updateControlPointColor(rgb); + m_negativeRangeWidget->updateControlPointColor(rgb); +} + +/** + * Update the dialog. + */ +void +PaletteEditorDialog::updateDialog() +{ + m_paletteSelectionWidget->updateContent(); + + updatePaletteMovementButtons(); + + adjustSize(); +} + +/** + * Load a copy of the palette into the dialog + * @param palette + * Palette to load in editor + */ +void +PaletteEditorDialog::loadPaletteIntoEditor(const std::unique_ptr& palette) +{ + std::vector positiveScalars; + std::vector negativeScalars; + std::vector zeroScalars; + + if (palette) { + positiveScalars = palette->getPosRange(); + negativeScalars = palette->getNegRange(); + + float zeroRgb[3]; + palette->getZeroColor(zeroRgb); + zeroScalars.emplace_back(0.0, + zeroRgb); + } + + m_positiveRangeWidget->updateContent(positiveScalars); + m_negativeRangeWidget->updateContent(negativeScalars); + m_zeroRangeWidget->updateContent(zeroScalars); + + clearEditorModified(); + + m_positiveRangeWidget->selectFirstControlPoint(); + + updatePaletteColorBarImage(); +} + +/** + * Clear modified status of palette in the editor + */ +void +PaletteEditorDialog::clearEditorModified() +{ + /* + * Save the initial values so that they can be used to determine if the + * current palette has been modified. We do not use the input scalar colors + * but do use the scalar colors after they have been loaded in the range + * widgets. The range widgets have limited precision (3 decimals) due to + * the spin boxes whereas the input scalar colors have more precision. + * This difference in precesion would cause a differnce. + */ + m_unmodifiedPalette.m_negativeMapping = m_negativeRangeWidget->getScalarColors(); + m_unmodifiedPalette.m_positiveMapping = m_positiveRangeWidget->getScalarColors(); + m_unmodifiedPalette.m_zeroMapping = m_zeroRangeWidget->getScalarColors(); + + updateModifiedLabel(); +} + +/** + * @return True if the current palette has been modified since it was loaded, else false + */ +bool +PaletteEditorDialog::isPaletteModified() const +{ + if (m_unmodifiedPalette.m_negativeMapping != m_negativeRangeWidget->getScalarColors()) { + return true; + } + if (m_unmodifiedPalette.m_positiveMapping != m_positiveRangeWidget->getScalarColors()) { + return true; + } + if (m_unmodifiedPalette.m_zeroMapping != m_zeroRangeWidget->getScalarColors()) { + return true; + } + + return false; +} + + +/** + * @return Palette created from current settings in editor + */ +std::unique_ptr +PaletteEditorDialog::getPaletteFromEditor() const +{ + std::vector pos = m_positiveRangeWidget->getScalarColors(); + std::vector neg = m_negativeRangeWidget->getScalarColors(); + std::vector zero = m_zeroRangeWidget->getScalarColors(); + + std::unique_ptr palettePtr; + + if ((pos.size() >= 2) + && (neg.size() >= 2) + && (zero.size() == 1)) { + PaletteNew* pal = new PaletteNew(pos, + zero[0].color, + neg); + palettePtr.reset(pal); + } + + return palettePtr; +} + +/** + * Update the palette color bar image + */ +void +PaletteEditorDialog::updatePaletteColorBarImage() +{ + QPixmap colorBarPixmap; + + std::unique_ptr palette = getPaletteFromEditor(); + if (palette) { + PalettePixmapPainter palettePainter(palette.get(), + m_colorBarImageLabel->size(), + m_pixmapMode); + colorBarPixmap = palettePainter.getPixmap(); + } + + m_colorBarImageLabel->setPixmap(colorBarPixmap); + + updateModifiedLabel(); +} + +void +PaletteEditorDialog::updateModifiedLabel() +{ + if (isPaletteModified()) { + m_colorBarModifiedLabel->setText("MODIFIED"); + } + else { + m_colorBarModifiedLabel->setText(""); + } +} + +/** + * Slot for editing a color + * @param rgb + * The color for editing + */ +void +PaletteEditorDialog::editColor(const CaretRgb& rgb) +{ + m_colorEditorWidget->setCurrentColor(QColor(rgb.red(), + rgb.green(), + rgb.blue())); + updatePaletteColorBarImage(); +} + +/** + * @return New instance of the control point editing widgets + */ +QWidget* +PaletteEditorDialog::createControlPointsWidget() +{ + m_positiveRangeWidget = new PaletteEditorRangeWidget(PaletteEditorRangeWidget::DataRangeMode::POSITIVE, + m_colorEditButtonGroup, + PaletteEditorRangeWidget::ColumnTitlesMode::SHOW_YES, + this); + QObject::connect(m_positiveRangeWidget, &PaletteEditorRangeWidget::signalEditColorRequested, + this, &PaletteEditorDialog::editColor); + QObject::connect(m_positiveRangeWidget, &PaletteEditorRangeWidget::signalDataChanged, + this, &PaletteEditorDialog::rangeWidgetDataChanged); + QGroupBox* positiveWidget = new QGroupBox("Positive Mapping"); + positiveWidget->setFlat(true); + QVBoxLayout* positiveLayout = new QVBoxLayout(positiveWidget); + WuQtUtilities::setLayoutSpacingAndMargins(positiveLayout, 0, 0); + positiveLayout->addWidget(m_positiveRangeWidget); + + m_zeroRangeWidget = new PaletteEditorRangeWidget(PaletteEditorRangeWidget::DataRangeMode::ZERO, + m_colorEditButtonGroup, + PaletteEditorRangeWidget::ColumnTitlesMode::SHOW_NO, + this); + QObject::connect(m_zeroRangeWidget, &PaletteEditorRangeWidget::signalEditColorRequested, + this, &PaletteEditorDialog::editColor); + QObject::connect(m_zeroRangeWidget, &PaletteEditorRangeWidget::signalDataChanged, + this, &PaletteEditorDialog::rangeWidgetDataChanged); + + QGroupBox* zeroWidget = new QGroupBox(); + zeroWidget->setFlat(true); + QVBoxLayout* zeroLayout = new QVBoxLayout(zeroWidget); + WuQtUtilities::setLayoutSpacingAndMargins(zeroLayout, 0, 0); + zeroLayout->addWidget(m_zeroRangeWidget); + + m_negativeRangeWidget = new PaletteEditorRangeWidget(PaletteEditorRangeWidget::DataRangeMode::NEGATIVE, + m_colorEditButtonGroup, + PaletteEditorRangeWidget::ColumnTitlesMode::SHOW_NO, + this); + QObject::connect(m_negativeRangeWidget, &PaletteEditorRangeWidget::signalEditColorRequested, + this, &PaletteEditorDialog::editColor); + QObject::connect(m_negativeRangeWidget, &PaletteEditorRangeWidget::signalDataChanged, + this, &PaletteEditorDialog::rangeWidgetDataChanged); + QGroupBox* negativeWidget = new QGroupBox("Negative Mapping"); + negativeWidget->setFlat(true); + QVBoxLayout* negativeLayout = new QVBoxLayout(negativeWidget); + WuQtUtilities::setLayoutSpacingAndMargins(negativeLayout, 0, 0); + negativeLayout->addWidget(m_negativeRangeWidget); + + QWidget* widget = new QWidget(); + widget->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addWidget(positiveWidget); + layout->addSpacing(10); + layout->addWidget(zeroWidget); + layout->addSpacing(10); + layout->addWidget(negativeWidget); + + WuQScrollArea* scrollArea = WuQScrollArea::newInstance(240, -1); + scrollArea->setFrameShape(QFrame::NoFrame); + scrollArea->setWidget(widget); + scrollArea->setWidgetResizable(true); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents); + scrollArea->setSizePolicy(QSizePolicy::Fixed, + scrollArea->sizePolicy().verticalPolicy()); + + return scrollArea; +} + +/** + * Called when a range widgets data (scalar/color) is changed + */ +void +PaletteEditorDialog::rangeWidgetDataChanged() +{ + updatePaletteColorBarImage(); +} + +/* + * @return Create the palette widget that shows the current palette being edited + */ +QWidget* +PaletteEditorDialog::createPaletteBarWidget() +{ + m_colorBarImageLabel = new QLabel(); + m_colorBarImageLabel->setFixedHeight(24); + m_colorBarImageLabel->setFixedWidth(500); + + m_colorBarModifiedLabel = new QLabel(); + m_colorBarModifiedLabel->setAlignment(Qt::AlignRight); + m_colorBarModifiedLabel->setFixedWidth(120); + + QWidget* widget = new QWidget(); + QGridLayout* layout = new QGridLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_colorBarImageLabel, 0, 0); + layout->addWidget(m_colorBarModifiedLabel, 0, 1); + + return widget; +} + +/** + * @return Create the widget for selection of palettes + */ +QWidget* +PaletteEditorDialog::createPaletteSelectionWidget() +{ + m_paletteSelectionWidget = new PaletteSelectionWidget(); + QObject::connect(m_paletteSelectionWidget, &PaletteSelectionWidget::paletteSelectionChanged, + this, &PaletteEditorDialog::paletteSelected); + + m_renamePushButton = new QPushButton("Rename..."); + m_renamePushButton->setToolTip("Rename the selected palette"); + QObject::connect(m_renamePushButton, &QPushButton::clicked, + this, &PaletteEditorDialog::renamePushButtonClicked); + + m_deletePushButton = new QPushButton("Delete..."); + m_deletePushButton->setToolTip("Delete the selected palette"); + QObject::connect(m_deletePushButton, &QPushButton::clicked, + this, &PaletteEditorDialog::deletePushButtonClicked); + + std::vector buttonsForSizeMatching; + buttonsForSizeMatching.push_back(m_renamePushButton); + buttonsForSizeMatching.push_back(m_deletePushButton); + + const bool showImportExportButtonsFlag(true); + m_importPushButton = NULL; + m_exportPushButton = NULL; + + if (showImportExportButtonsFlag) { + m_importPushButton = new QPushButton("Import..."); + m_importPushButton->setToolTip("Import a palette from a wb_view palette file"); + QObject::connect(m_importPushButton, &QPushButton::clicked, + this, &PaletteEditorDialog::importPushButtonClicked); + buttonsForSizeMatching.push_back(m_importPushButton); + + m_exportPushButton = new QPushButton("Export..."); + m_exportPushButton->setToolTip("Export the selected palette to a wb_view palette file"); + QObject::connect(m_exportPushButton, &QPushButton::clicked, + this, &PaletteEditorDialog::exportPushButtonClicked); + buttonsForSizeMatching.push_back(m_exportPushButton); + } + + WuQtUtilities::matchWidgetWidths(buttonsForSizeMatching); + + QGroupBox* widget = new QGroupBox("Palette Selection"); + QVBoxLayout* layout = new QVBoxLayout(widget); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 8, 0); + layout->addWidget(m_paletteSelectionWidget, 0, Qt::AlignHCenter); + layout->addWidget(m_renamePushButton, 0, Qt::AlignHCenter); + layout->addWidget(m_deletePushButton, 0, Qt::AlignHCenter); + if (m_importPushButton != NULL) { + layout->addWidget(m_importPushButton, 0, Qt::AlignHCenter); + } + if (m_exportPushButton != NULL) { + layout->addWidget(m_exportPushButton, 0, Qt::AlignHCenter); + } + layout->addStretch(); + + return widget; +} + +/** + * Called when a palette is selected + */ +void +PaletteEditorDialog::paletteSelected() +{ + updatePaletteMovementButtons(); +} + +/** + * NOT USED + */ +QPixmap +PaletteEditorDialog::createIcon(QWidget* widget, + const IconType iconType) +{ + CaretAssert(widget); + + /* + * Create a small, square pixmap that will contain + * the foreground color around the pixmap's perimeter. + */ + float width = 24.0; + float height = 24.0; + + QPixmap pixmap(static_cast(width), + static_cast(height)); + + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, + pixmap); + QPen pen = painter->pen(); + pen.setWidth(2); + painter->setPen(pen); + + /* + * Creates an icon for "up-to-right icon" as below + * + * ----> + * | + * | + */ + painter->drawLine(QLineF(2.0, 2.0, + 2.0, 16.0)); + painter->drawLine(QLineF(2.0, 16.0, + 22.0, 16.0)); + painter->drawLine(QLineF(22.0, 16.0, + 12.0, 22.0)); + painter->drawLine(QLineF(22.0, 16.0, + 12.0, 10.0)); + + QPixmap pixmapOut = pixmap; + + switch (iconType) { + case IconType::ARROW_LEFT_DOWN: + { + /* + * Transforms to a "left-to-down icon" as below + * + * ---- + * | + * \/ + */ + QTransform transform; + transform.scale(-1, 1); + transform.rotate(90, Qt::ZAxis); + transform.translate(width, height); + pixmapOut = pixmap.transformed(transform); + } + break; + case IconType::ARROW_UP_RIGHT: + break; + } + + return pixmapOut; +} + +/** + * @return Instance of the buttons used to move palettes + * into and out of the editor + */ +QWidget* +PaletteEditorDialog::createMovePaletteButtonsWidget() +{ + const QString addToolTip("Add a new palette containing content of the palette editor"); + m_addPalettePushButton = new QPushButton("Add -->"); + WuQtUtilities::setWordWrappedToolTip(m_addPalettePushButton, + addToolTip); + QObject::connect(m_addPalettePushButton, &QPushButton::clicked, + this, &PaletteEditorDialog::addPalettePushButtonClicked); + + const QString replaceToolTip("Replace the selected palette with the content of the palette editor"); + m_replacePalettePushButton = new QPushButton("Replace -->"); + WuQtUtilities::setWordWrappedToolTip(m_replacePalettePushButton, + replaceToolTip); + QObject::connect(m_replacePalettePushButton, &QPushButton::clicked, + this, &PaletteEditorDialog::replacePalettePushButtonClicked); + + const QString loadToolTip("Load palette editor with the selected palette"); + m_loadPalettePushButton = new QPushButton("<-- Load"); + WuQtUtilities::setWordWrappedToolTip(m_loadPalettePushButton, + loadToolTip); + QObject::connect(m_loadPalettePushButton, &QPushButton::clicked, + this, &PaletteEditorDialog::loadPalettePushButtonClicked); + + const QString newToolTip("Create a new palette by (1) copying an existing palette or " + "(2) create a new palette"); + m_newPalettePushButton = new QPushButton("<-- New..."); + WuQtUtilities::setWordWrappedToolTip(m_newPalettePushButton, newToolTip); + QObject::connect(m_newPalettePushButton, &QPushButton::clicked, + this, &PaletteEditorDialog::newPaletteButtonClicked); + + QWidget* widget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addSpacing(50); + layout->addWidget(m_addPalettePushButton); + layout->addWidget(m_replacePalettePushButton); + layout->addWidget(m_loadPalettePushButton); + layout->addSpacing(25); + layout->addWidget(m_newPalettePushButton); + layout->addStretch(); + + return widget; +} + +/** + * Called when Add palette push button is clicked + */ +void +PaletteEditorDialog::addPalettePushButtonClicked() +{ + std::unique_ptr palette = getPaletteFromEditor(); + if ( ! palette) { + return; + } + + PaletteGroup* paletteGroup = m_paletteSelectionWidget->getSelectedPaletteGroup(); + if (paletteGroup == NULL) { + return; + } + + WuQDataEntryDialog dialog("Add Palette", + m_addPalettePushButton); + m_addPaletteDialogLineEdit = dialog.addLineEditWidget("Palette Name"); + QObject::connect(&dialog, &WuQDataEntryDialog::validateData, + this, &PaletteEditorDialog::addPaletteDialogValidateData); + if (dialog.exec() == QInputDialog::Accepted) { + palette->setName(m_addPaletteDialogLineEdit->text().trimmed()); + AString errorMessage; + if ( ! paletteGroup->addPalette(*palette, + errorMessage)) { + WuQMessageBox::errorOk(m_addPalettePushButton, + errorMessage); + } + } + + clearEditorModified(); + + m_paletteSelectionWidget->updateContent(); + updatePaletteMovementButtons(); +} + +/** + * Called to validate data when adding a palette from editor to a group + * @param dataEntryDialog + * + */ +void +PaletteEditorDialog::addPaletteDialogValidateData(WuQDataEntryDialog* addPaletteDialog) +{ + CaretAssert(addPaletteDialog); + bool validFlag(false); + QString errorMessage; + + const AString name = m_addPaletteDialogLineEdit->text().trimmed(); + if (name.isEmpty()) { + errorMessage = "Palette name is missing"; + } + else { + PaletteGroup* paletteGroup = m_paletteSelectionWidget->getSelectedPaletteGroup(); + if (paletteGroup->hasPaletteWithName(name)) { + errorMessage = ("Palette names must be unique. Choose a " + "different palette name."); + } + else { + validFlag = true; + } + + } + addPaletteDialog->setDataValid(validFlag, + errorMessage); +} + +/** + * Called when button for creating a new palette is clicked + */ +void +PaletteEditorDialog::newPaletteButtonClicked() +{ + if ( ! modifiedPaletteWarningDialog()) { + return; + } + + PaletteCreateNewDialog dialog(m_pixmapMode, + this); + if (dialog.exec() == PaletteCreateNewDialog::Accepted) { + loadPaletteIntoEditor(dialog.getPalette()); + updateDialog(); + } +} + + + +/** + * Called when Replace palette push button is clicked + */ +void +PaletteEditorDialog::replacePalettePushButtonClicked() +{ + std::unique_ptr palette = getPaletteFromEditor(); + if ( ! palette) { + return; + } + + PaletteGroup* paletteGroup = m_paletteSelectionWidget->getSelectedPaletteGroup(); + if (paletteGroup == NULL) { + return; + } + + std::unique_ptr oldPalette = m_paletteSelectionWidget->getSelectedPalette(); + if ( ! oldPalette) { + return; + } + + const QString name = oldPalette->getName(); + + if ( ! WuQMessageBox::warningOkCancel(m_replacePalettePushButton, + ("Replace palette " + + name + + " ?"))) { + return; + } + + palette->setName(name); + + AString errorMessage; + if ( ! paletteGroup->replacePalette(*palette, + errorMessage)) { + WuQMessageBox::errorOk(m_replacePalettePushButton, + errorMessage); + } + clearEditorModified(); + + m_paletteSelectionWidget->updateContent(); + updatePaletteMovementButtons(); +} + +/** + * If the palette in the editor has a modified status, warn the user and + * allow the user to continue or cancel. + * @return True if the palette is NOT modified + * True if the Palette is modified and the user chooses to discard the changes + * False if the Palette is modified and the user DOES NOT want to continue + */ +bool +PaletteEditorDialog::modifiedPaletteWarningDialog() +{ + if (isPaletteModified()) { + const QString text("The Palette in the editor has been modified."); + const QString info("Click OK to discard the changes and continue adding/loading " + "the new palette.\n\n" + "Click Cancel to allow saving the palette in the editor."); + if ( ! WuQMessageBox::warningOkCancel(this, + text, + info, + WuQMessageBox::DefaultButtonOkCancel::CANCEL)) { + return false; + } + } + + return true; +} + +/** + * Called when Load palette push button is clicked + */ +void +PaletteEditorDialog::loadPalettePushButtonClicked() +{ + if ( ! modifiedPaletteWarningDialog()) { + return; + } + + loadPaletteIntoEditor(m_paletteSelectionWidget->getSelectedPalette()); + + updateDialog(); +} + +/** + * Called when the delete push button is clicked + */ +void +PaletteEditorDialog::deletePushButtonClicked() +{ + PaletteGroup* paletteGroup = m_paletteSelectionWidget->getSelectedPaletteGroup(); + if (paletteGroup != NULL) { + if (paletteGroup->isEditable()) { + std::unique_ptr palette = m_paletteSelectionWidget->getSelectedPalette(); + if (palette) { + if (WuQMessageBox::warningOkCancel(m_deletePushButton, + ("Delete palette " + + palette->getName() + +" ?"))) { + AString errorMessage; + if ( ! paletteGroup->removePalette(palette->getName(), + errorMessage)) { + WuQMessageBox::errorOk(m_deletePushButton, + errorMessage); + } + else { + updateDialog(); + } + } + } + } + } +} + +/** + * Called when the rename push button is clicked + */ +void +PaletteEditorDialog::renamePushButtonClicked() +{ + PaletteGroup* paletteGroup = m_paletteSelectionWidget->getSelectedPaletteGroup(); + if (paletteGroup != NULL) { + if (paletteGroup->isEditable()) { + std::unique_ptr palette = m_paletteSelectionWidget->getSelectedPalette(); + if (palette) { + bool validFlag(false); + const AString newName = QInputDialog::getText(m_renamePushButton, "Rename Palette", "New Name", + QLineEdit::Normal, + palette->getName(), + &validFlag).trimmed(); + if ( ! newName.isEmpty()) { + AString errorMessage; + if ( ! paletteGroup->renamePalette(palette->getName(), + newName, + errorMessage)) { + WuQMessageBox::errorOk(m_renamePushButton, + errorMessage); + } + else { + updateDialog(); + } + } + } + } + } +} + +/** + * Called when the import push button is clicked + */ +void +PaletteEditorDialog::importPushButtonClicked() +{ + +} + +/** + * Called when the export push button is clicked + */ +void +PaletteEditorDialog::exportPushButtonClicked() +{ + +} + +/** + * Update the palette movement buttons + */ +void +PaletteEditorDialog::updatePaletteMovementButtons() +{ + bool editableGroupFlag(false); + bool paletteSelectedFlag(false); + const PaletteGroup* paletteGroup = m_paletteSelectionWidget->getSelectedPaletteGroup(); + if (paletteGroup != NULL) { + editableGroupFlag = paletteGroup->isEditable(); + std::unique_ptr palette = m_paletteSelectionWidget->getSelectedPalette(); + paletteSelectedFlag = (palette.get() != NULL); + } + + const bool validEditorPaletteFlag = (getPaletteFromEditor().get() != NULL); + + m_addPalettePushButton->setEnabled(editableGroupFlag + && validEditorPaletteFlag); + m_replacePalettePushButton->setEnabled(editableGroupFlag + && paletteSelectedFlag + && validEditorPaletteFlag); + m_loadPalettePushButton->setEnabled(paletteSelectedFlag); + m_newPalettePushButton->setEnabled(true); + + m_renamePushButton->setEnabled(editableGroupFlag + && paletteSelectedFlag); + m_deletePushButton->setEnabled(editableGroupFlag + && paletteSelectedFlag); + if (m_importPushButton != NULL) { + m_importPushButton->setEnabled(editableGroupFlag); + } + if (m_exportPushButton != NULL) { + m_exportPushButton->setEnabled(paletteSelectedFlag); + } +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteEditorDialog.h connectome-workbench-1.5.0/src/GuiQt/PaletteEditorDialog.h --- connectome-workbench-1.4.2/src/GuiQt/PaletteEditorDialog.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteEditorDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,184 @@ + +#ifndef __PALETTE_EDITOR_DIALOG_H__ +#define __PALETTE_EDITOR_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "PaletteNew.h" +#include "PalettePixmapPainter.h" +#include "WuQDialogNonModal.h" + +class QButtonGroup; +class QLabel; +class QLineEdit; +class QPushButton; +class QTabWidget; +class QToolButton; + +namespace caret { + + class CaretRgb; + + class PaletteEditorRangeWidget; + class PaletteGroup; + class PaletteNew; + class PaletteSelectionWidget; + + class WuQColorEditorWidget; + class WuQDataEntryDialog; + class WuQScrollArea; + + class PaletteEditorDialog : public WuQDialogNonModal { + + Q_OBJECT + + public: + PaletteEditorDialog(QWidget* parent); + + virtual ~PaletteEditorDialog(); + + PaletteEditorDialog(const PaletteEditorDialog&) = delete; + + PaletteEditorDialog& operator=(const PaletteEditorDialog&) = delete; + + virtual void updateDialog(); + + // ADD_NEW_METHODS_HERE + + private slots: + void editColor(const CaretRgb& rgb); + + void newPaletteButtonClicked(); + + void colorEditorColorChanged(const QColor& color); + + void paletteSelected(); + + void rangeWidgetDataChanged(); + + void addPalettePushButtonClicked(); + + void replacePalettePushButtonClicked(); + + void loadPalettePushButtonClicked(); + + void addPaletteDialogValidateData(WuQDataEntryDialog* addPaletteDialog); + + void deletePushButtonClicked(); + + void renamePushButtonClicked(); + + void importPushButtonClicked(); + + void exportPushButtonClicked(); + + private: + enum class IconType { + ARROW_LEFT_DOWN, + ARROW_UP_RIGHT + }; + + std::unique_ptr getPaletteFromEditor() const; + + void loadPaletteIntoEditor(const std::unique_ptr& palette); + + QWidget* createControlPointsWidget(); + + QWidget* createPaletteBarWidget(); + + QWidget* createPaletteSelectionWidget(); + + QPixmap createIcon(QWidget* widget, + const IconType iconType); + + void updatePaletteColorBarImage(); + + QWidget* createMovePaletteButtonsWidget(); + + void updatePaletteMovementButtons(); + + void clearEditorModified(); + + bool isPaletteModified() const; + + void updateModifiedLabel(); + + bool modifiedPaletteWarningDialog(); + + PaletteSelectionWidget* m_paletteSelectionWidget; + + PaletteEditorRangeWidget* m_positiveRangeWidget; + + PaletteEditorRangeWidget* m_zeroRangeWidget; + + PaletteEditorRangeWidget* m_negativeRangeWidget; + + QLabel* m_colorBarImageLabel; + + QLabel* m_colorBarModifiedLabel; + + WuQColorEditorWidget* m_colorEditorWidget; + + QButtonGroup* m_colorEditButtonGroup; + + PalettePixmapPainter::Mode m_pixmapMode = PalettePixmapPainter::Mode::INTERPOLATE_ON_LINES_AT_SCALARS; + + QPushButton* m_addPalettePushButton; + + QPushButton* m_replacePalettePushButton; + + QPushButton* m_loadPalettePushButton; + + QPushButton* m_newPalettePushButton; + + QLineEdit* m_addPaletteDialogLineEdit = NULL; + + QPushButton* m_deletePushButton; + + QPushButton* m_renamePushButton; + + QPushButton* m_importPushButton; + + QPushButton* m_exportPushButton; + + struct UnmodifiedPalette { + std::vector m_positiveMapping; + std::vector m_negativeMapping; + std::vector m_zeroMapping; + } m_unmodifiedPalette; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PALETTE_EDITOR_DIALOG_DECLARE__ + // +#endif // __PALETTE_EDITOR_DIALOG_DECLARE__ + +} // namespace +#endif //__PALETTE_EDITOR_DIALOG_H__ + diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteEditorRangeRow.cxx connectome-workbench-1.5.0/src/GuiQt/PaletteEditorRangeRow.cxx --- connectome-workbench-1.4.2/src/GuiQt/PaletteEditorRangeRow.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteEditorRangeRow.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,404 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_EDITOR_RANGE_ROW_DECLARE__ +#include "PaletteEditorRangeRow.h" +#undef __PALETTE_EDITOR_RANGE_ROW_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "WuQGridLayoutGroup.h" +#include "WuQtUtilities.h" + +using namespace caret; + +/** + * \class caret::PaletteEditorRangeRow + * \brief Contains widgets for one row in a palette editor range + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param rowIndex + * Index of this 'row' (zero at top) + * @param rangeMode + * Range of data in this row (pos/neg/zero) + * @param colorEditButtonGroup + * Button group that keeps edit color radio buttons mutually exclusive + * @param parentGridLayout + * Layout for this row + * @param parent + * Parent object + */ +PaletteEditorRangeRow::PaletteEditorRangeRow(const int32_t rowIndex, + const PaletteEditorRangeWidget::DataRangeMode rangeMode, + QButtonGroup* colorEditButtonGroup, + QGridLayout* parentGridLayout, + QObject* parent) +: QObject(parent), +m_rowIndex(rowIndex), +m_rangeMode(rangeMode) +{ + bool posNegFlag(false); + bool zeroFlag(false); + switch (m_rangeMode) { + case PaletteEditorRangeWidget::DataRangeMode::NEGATIVE: + posNegFlag = true; + break; + case PaletteEditorRangeWidget::DataRangeMode::POSITIVE: + posNegFlag = true; + break; + case PaletteEditorRangeWidget::DataRangeMode::ZERO: + zeroFlag = true; + break; + } + + m_gridLayoutGroup = new WuQGridLayoutGroup(parentGridLayout, + this); + + m_colorEditRadioButton = new QRadioButton(""); + m_colorEditRadioButton->setToolTip("Click to edit this control point's color"); + colorEditButtonGroup->addButton(m_colorEditRadioButton); + QObject::connect(m_colorEditRadioButton, &QRadioButton::clicked, + [=] (bool) { emit signalColorEditingRequested(m_rgb); }); + + m_colorSwatchWidget = new QWidget(); + m_colorSwatchWidget->setFixedWidth(40); + m_colorSwatchWidget->setFixedHeight(m_colorEditRadioButton->sizeHint().height() + 4); + + const int row(parentGridLayout->rowCount()); + if (posNegFlag) { + QWidget* constructionWidget = createConstructionToolButton(); + QWidget* scalarWidget = createScalarEditingWidget(); + + m_gridLayoutGroup->addWidget(constructionWidget, + row, COLUMN_CONSTRUCTION); + m_gridLayoutGroup->addWidget(scalarWidget, + row, COLUMN_SCALAR); + } + + if (zeroFlag) { + QLabel* zeroLabel = new QLabel("Zero Mapping"); + m_gridLayoutGroup->addWidget(zeroLabel, + row, COLUMN_CONSTRUCTION, + 1, 2); + parentGridLayout->setColumnStretch(COLUMN_CONSTRUCTION, 100); + parentGridLayout->setColumnStretch(COLUMN_SCALAR, 0); + parentGridLayout->setColumnStretch(COLUMN_COLOR_SWATCH, 0); + parentGridLayout->setColumnStretch(COLUMN_RADIO_BUTTON, 0); + + } + + m_gridLayoutGroup->addWidget(m_colorSwatchWidget, + row, COLUMN_COLOR_SWATCH, Qt::AlignCenter); + m_gridLayoutGroup->addWidget(m_colorEditRadioButton, + row, COLUMN_RADIO_BUTTON); +} + +/** + * Destructor. + */ +PaletteEditorRangeRow::~PaletteEditorRangeRow() +{ +} + +/** + * @return The value of the scalar + */ +float +PaletteEditorRangeRow::getScalar() const +{ + if (m_valueSpinBox != NULL) { + return m_valueSpinBox->value(); + } + return 0.0f; +} + +/** + * @return The RGB color + */ +CaretRgb +PaletteEditorRangeRow::getRgb() const +{ + return m_rgb; +} + +/** + * Set the scalar value + * @param scalar + * New scalar value + */ +void +PaletteEditorRangeRow::setScalar(const float scalar) +{ + int32_t decimals(3); + if (m_valueSpinBox != NULL) { + QSignalBlocker blocker(m_valueSpinBox); + m_valueSpinBox->setValue(scalar); + decimals = m_valueSpinBox->decimals(); + } + + if (m_valueLabel != NULL) { + m_valueLabel->setText(QString::number(scalar, 'f', decimals)); + } +} + +/** + * Set the rgb color and update the color swatch + * + * @param rgb + * New RGB value + */ +void +PaletteEditorRangeRow::setRgb(const CaretRgb& rgb) +{ + m_rgb = rgb; + + for (auto c : m_rgb) { + CaretAssert((c >= 0) + && (c <= 255)); + } + + m_colorSwatchWidget->setStyleSheet("background-color: rgb(" + + AString::number(m_rgb.red()) + + ", " + AString::number(m_rgb.green()) + + ", " + AString::number(m_rgb.blue()) + + ");"); +} + +/** + * Update the RGB color and color swatch if this instance's + * radio button (for color editing) is checked. This gets + * called when the user changes the color in the color editor. + * + * @param rgb + * New RGB value + */ +void +PaletteEditorRangeRow::updateRgbIfRadioButtonChecked(const CaretRgb& rgb) +{ + if (m_colorEditRadioButton->isChecked()) { + setRgb(rgb); + emit signalDataChanged(); + } +} + +/** + * @return New instance of construction menu + */ +QToolButton* +PaletteEditorRangeRow::createConstructionToolButton() +{ + QMenu* menu = new QMenu(); + QObject::connect(menu, &QMenu::triggered, + this, &PaletteEditorRangeRow::constructionMenuTriggered); + + m_constructionInsertAboveAction = menu->addAction("Insert Control Point Above"); + m_constructionInsertBelowAction = menu->addAction("Insert Control Point Below"); + m_constructionRemoveAction = menu->addAction("Remove this Control Point"); + + QIcon constructionIcon; + const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", + constructionIcon); + QToolButton* toolButton = new QToolButton(); + if (constructionIconValid) { + toolButton->setIcon(constructionIcon); + } + else { + toolButton->setText("C"); + } + toolButton->setPopupMode(QToolButton::InstantPopup); + toolButton->setMenu(menu); + + return toolButton; +} + +/** + * Called when an items is selected from the construction menu + * @param action + * Action of item selected + */ +void +PaletteEditorRangeRow::constructionMenuTriggered(QAction* action) +{ + PaletteEditorRangeWidget::ConstructionOperation modOp(PaletteEditorRangeWidget::ConstructionOperation::INSERT_CONTROL_POINT_ABOVE); + + if (action == m_constructionInsertAboveAction) { + modOp = PaletteEditorRangeWidget::ConstructionOperation::INSERT_CONTROL_POINT_ABOVE; + } + else if (action == m_constructionInsertBelowAction) { + modOp = PaletteEditorRangeWidget::ConstructionOperation::INSERT_CONTROL_POINT_BELOW; + } + else if (action == m_constructionRemoveAction) { + modOp = PaletteEditorRangeWidget::ConstructionOperation::REMOVE_CONTROL_POINT; + } + else { + CaretAssert(0); + } + + emit signalConstructionOperationRequested(m_rowIndex, + modOp); +} + +/** + * Called when the control point value is changed + * + * @param value + * New value but ignored as owner of this row can + * call a method to retrieve the scalar value + */ +void +PaletteEditorRangeRow::scalarValueChangedByUser(double /* value */) +{ + emit signalDataChanged(); +} + +/** + * @return New instance of widgets for scalar editing + */ +QWidget* +PaletteEditorRangeRow::createScalarEditingWidget() +{ + m_valueSpinBox = new QDoubleSpinBox(); + m_valueSpinBox->setMinimum(-1.0); + m_valueSpinBox->setMaximum(1.0); + m_valueSpinBox->setDecimals(3); + m_valueSpinBox->setSingleStep(getScalarValueSpinBoxSingleStep()); + QObject::connect(m_valueSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &PaletteEditorRangeRow::scalarValueChangedByUser); + + m_valueLabel = new QLabel(" "); + + m_valueStackedWidget = new QStackedWidget(); + m_valueStackedWidget->addWidget(m_valueSpinBox); + m_valueStackedWidget->addWidget(m_valueLabel); + + return m_valueStackedWidget; +} + +/** + * Set the widgets visible (show/hide) + * @param visibleFlag + * New visibility status + */ +void +PaletteEditorRangeRow::setWidgetsVisible(const bool visibleFlag) +{ + m_gridLayoutGroup->setVisible(visibleFlag); +} + +/** + * Update the range of the scalar value spin box + * @param minValueIn + * Minimum value + * @param maxValueIn + * Maximum value + */ +void +PaletteEditorRangeRow::updateScalarValueRange(const float minValueIn, + const float maxValueIn) +{ + float minValue(minValueIn); + float maxValue(maxValueIn); + + if (maxValue > minValue) { + /* + * Must be slightly less/more so that this scalar + * is not exactly the same as scalar above or below + */ + minValue = minValue + getScalarValueSpinBoxSingleStep(); + maxValue = maxValue - getScalarValueSpinBoxSingleStep(); + if (maxValue < minValue) { + maxValue = (minValueIn + maxValueIn) / 2.0; + minValue = maxValue; + } + } + + if (m_valueSpinBox != NULL) { + QSignalBlocker blocker(m_valueSpinBox); + m_valueSpinBox->setRange(minValue, + maxValue); + } +} + +/** + * Set the widgets visible (show/hide) + * @param visibleFlag + * New visibility status + */ +void +PaletteEditorRangeRow::updateContent(const int32_t numberOfControlPoints) +{ + switch (m_rangeMode) { + case PaletteEditorRangeWidget::DataRangeMode::NEGATIVE: + case PaletteEditorRangeWidget::DataRangeMode::POSITIVE: + { + /* + * Note: Row index 0 is at the top and contains the + * most positive (1.0) or least negative value (-1.0); + */ + bool addAboveValid(false); + bool addBelowValid(false); + bool removeValid(false); + + if (m_rowIndex == 0) { + addBelowValid = true; + m_valueStackedWidget->setCurrentWidget(m_valueLabel); + } + else if (m_rowIndex == (numberOfControlPoints - 1)) { + addAboveValid = true; + m_valueStackedWidget->setCurrentWidget(m_valueLabel); + } + else { + addAboveValid = true; + addBelowValid = true; + removeValid = true; + m_valueStackedWidget->setCurrentWidget(m_valueSpinBox); + } + + m_constructionInsertAboveAction->setEnabled(addAboveValid); + m_constructionInsertBelowAction->setEnabled(addBelowValid); + m_constructionRemoveAction->setEnabled(removeValid); + } + break; + case PaletteEditorRangeWidget::DataRangeMode::ZERO: + break; + } +} + +/** + * Select the radio button + */ +void +PaletteEditorRangeRow::selectRadioButton() +{ + m_colorEditRadioButton->setChecked(true); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteEditorRangeRow.h connectome-workbench-1.5.0/src/GuiQt/PaletteEditorRangeRow.h --- connectome-workbench-1.4.2/src/GuiQt/PaletteEditorRangeRow.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteEditorRangeRow.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,154 @@ +#ifndef __PALETTE_EDITOR_RANGE_ROW_H__ +#define __PALETTE_EDITOR_RANGE_ROW_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include + +#include "CaretRgb.h" +#include "PaletteEditorRangeWidget.h" + +class QButtonGroup; +class QGridLayout; +class QLabel; +class QStackedWidget; + +namespace caret { + + class WuQGridLayoutGroup; + + class PaletteEditorRangeRow : public QObject { + + Q_OBJECT + + public: + PaletteEditorRangeRow(const int32_t rowIndex, + const PaletteEditorRangeWidget::DataRangeMode rangeMode, + QButtonGroup* colorEditButtonGroup, + QGridLayout* parentGridLayout, + QObject* parent); + + virtual ~PaletteEditorRangeRow(); + + PaletteEditorRangeRow(const PaletteEditorRangeRow&) = delete; + + PaletteEditorRangeRow& operator=(const PaletteEditorRangeRow&) = delete; + + float getScalar() const; + + CaretRgb getRgb() const; + + void setWidgetsVisible(const bool visibleFlag); + + void setScalar(const float scalar); + + void setRgb(const CaretRgb& rgb); + + void updateRgbIfRadioButtonChecked(const CaretRgb& rgb); + + void updateScalarValueRange(const float minValue, + const float maxValue); + + void updateContent(const int32_t numberOfControlPoints); + + /** + * Step value (amount spin box value changes when zero clicks + * one of the arrow buttons) + */ + static float getScalarValueSpinBoxSingleStep() { return 0.001f; } + + // ADD_NEW_METHODS_HERE + + void selectRadioButton(); + + signals: + /** + * Emitted when this control point has changed (scalar or color) + */ + void signalDataChanged(); + + /** + * Emitted when the user clicks the radio button to + * select this control point for color editing + */ + void signalColorEditingRequested(const CaretRgb& rgb); + + /** + * Emitted when an items is selected from the construction menu + */ + void signalConstructionOperationRequested(const int32_t rowIndex, + const PaletteEditorRangeWidget::ConstructionOperation operation); + + private slots: + void constructionMenuTriggered(QAction* action); + + void scalarValueChangedByUser(double value); + + private: + // ADD_NEW_MEMBERS_HERE + + QToolButton* createConstructionToolButton(); + + QWidget* createScalarEditingWidget(); + + const int32_t m_rowIndex; + + const PaletteEditorRangeWidget::DataRangeMode m_rangeMode; + + WuQGridLayoutGroup* m_gridLayoutGroup = NULL; + + QStackedWidget* m_valueStackedWidget = NULL; + + QLabel* m_valueLabel = NULL; + + QDoubleSpinBox* m_valueSpinBox = NULL; + + QWidget* m_colorSwatchWidget = NULL; + + QRadioButton* m_colorEditRadioButton = NULL; + + QAction* m_constructionInsertAboveAction = NULL; + + QAction* m_constructionInsertBelowAction = NULL; + + QAction* m_constructionRemoveAction = NULL; + + CaretRgb m_rgb; + + static constexpr int32_t COLUMN_CONSTRUCTION = 0; + + static constexpr int32_t COLUMN_SCALAR = 1; + + static constexpr int32_t COLUMN_COLOR_SWATCH = 2; + + static constexpr int32_t COLUMN_RADIO_BUTTON = 3; + + }; + +#ifdef __PALETTE_EDITOR_RANGE_ROW_DECLARE__ + // +#endif // __PALETTE_EDITOR_RANGE_ROW_DECLARE__ + +} // namespace +#endif //__PALETTE_EDITOR_RANGE_ROW_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteEditorRangeWidget.cxx connectome-workbench-1.5.0/src/GuiQt/PaletteEditorRangeWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/PaletteEditorRangeWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteEditorRangeWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,424 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_EDITOR_RANGE_WIDGET_DECLARE__ +#include "PaletteEditorRangeWidget.h" +#undef __PALETTE_EDITOR_RANGE_WIDGET_DECLARE__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "PaletteEditorRangeRow.h" +#include "PaletteNew.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::PaletteEditorRangeWidget + * \brief Widget for editing a palette's control points + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param dataRangeMode + * Range of data (positive, negative, zero) + * @param colorEditButtonGroup + * Button group for the color edit radio button so that they are mutually exclusive + * @param columnTitlesMode + * Mode for display of column titles + * @param parent + * Parent widget + */ +PaletteEditorRangeWidget::PaletteEditorRangeWidget(const DataRangeMode dataRangeMode, + QButtonGroup* colorEditButtonGroup, + const ColumnTitlesMode columnTitlesMode, + QWidget* parent) +: QWidget(parent), +m_dataRangeMode(dataRangeMode), +m_colorEditButtonGroup(colorEditButtonGroup) +{ + m_controlPointGridLayout = new QGridLayout(this); + m_controlPointGridLayout->setVerticalSpacing(m_controlPointGridLayout->verticalSpacing() / 2); + + switch (columnTitlesMode) { + case ColumnTitlesMode::SHOW_NO: + break; + case ColumnTitlesMode::SHOW_YES: + int32_t row(m_controlPointGridLayout->rowCount()); + m_controlPointGridLayout->addWidget(new QLabel("Control Point"), + row, 0, 1, 2, Qt::AlignHCenter); + m_controlPointGridLayout->addWidget(new QLabel("Color Edit"), + row, 2, 1, 2, Qt::AlignHCenter); + break; + } +} + +/** + * Destructor. + */ +PaletteEditorRangeWidget::~PaletteEditorRangeWidget() +{ +} + +/** + * @return Scalar colors for the range + */ +std::vector +PaletteEditorRangeWidget::getScalarColors() const +{ + std::vector scalarColorsOut; + + for (int32_t i = (m_numberOfValidControlPoints - 1); i >= 0; i--) { + CaretAssertVectorIndex(m_rowWidgets, i); + const PaletteEditorRangeRow* row = m_rowWidgets[i]; + + const CaretRgb rgb = row->getRgb(); + const PaletteNew::ScalarColor scalarColor = + PaletteNew::ScalarColor::fromRGB255(row->getScalar(), + rgb.red(), + rgb.green(), + rgb.blue()); + scalarColorsOut.push_back(scalarColor); + } + + return scalarColorsOut; +} + + +/** + * Update with the given scalar colors + * @param scalarColorsIn + * Update using these scalar colors + */ +void +PaletteEditorRangeWidget::updateContent(const std::vector& scalarColorsIn) +{ + /* + * Note: Inputs are ordered with ascending scalars but + * we want them descending to that greatest value is at top + */ + std::vector scalarColors = scalarColorsIn; + std::reverse(scalarColors.begin(), + scalarColors.end()); + + int32_t numberOfExistingRows(static_cast(m_rowWidgets.size())); + + m_numberOfValidControlPoints = static_cast(scalarColors.size()); + + /* + * Create new rows + */ + for (int32_t i = numberOfExistingRows; i < m_numberOfValidControlPoints; i++) { + + PaletteEditorRangeRow* per = new PaletteEditorRangeRow(i, + m_dataRangeMode, + m_colorEditButtonGroup, + m_controlPointGridLayout, + this); + QObject::connect(per, &PaletteEditorRangeRow::signalColorEditingRequested, + this, &PaletteEditorRangeWidget::signalEditColorRequested); + QObject::connect(per, &PaletteEditorRangeRow::signalConstructionOperationRequested, + this, &PaletteEditorRangeWidget::performConstruction); + QObject::connect(per, &PaletteEditorRangeRow::signalDataChanged, + this, &PaletteEditorRangeWidget::performRowDataChanged); + + m_rowWidgets.push_back(per); + } + + resetScalarSpinBoxRanges(); + + const int32_t numberOfRows(m_rowWidgets.size()); + for (int32_t iRow = 0; iRow < numberOfRows; iRow++) { + bool showRowFlag(false); + CaretAssertVectorIndex(m_rowWidgets, iRow); + PaletteEditorRangeRow* rowWidget = m_rowWidgets[iRow]; + if (iRow < m_numberOfValidControlPoints) { + CaretAssertVectorIndex(scalarColors, iRow); + rowWidget->setScalar(scalarColors[iRow].scalar); + + int32_t red, green, blue; + scalarColors[iRow].toRGB255(red, green, blue); + rowWidget->setRgb(CaretRgb(red, green, blue)); + rowWidget->updateContent(m_numberOfValidControlPoints); + + showRowFlag = true; + } + + rowWidget->setWidgetsVisible(showRowFlag); + } + + updateScalarSpinBoxRanges(); +} + +/** + * Select the first control point + */ +void +PaletteEditorRangeWidget::selectFirstControlPoint() +{ + if (m_numberOfValidControlPoints > 0) { + CaretAssertVectorIndex(m_rowWidgets, 0); + m_rowWidgets[0]->selectRadioButton(); + emit signalEditColorRequested(m_rowWidgets[0]->getRgb()); + } +} + + +/** + * Update the control point color. Typically called as user changes color in the color editor. + * + * @param red + * New red component. + * @param green + * New green component. + * @param blue + * New blue component. + */ +void +PaletteEditorRangeWidget::updateControlPointColor(const CaretRgb& rgb) +{ + /* + * Each row will apply the RGB color only if + * its edit radio button is checked (and they + * are mutually exclusive) + */ + for (auto row : m_rowWidgets) { + row->updateRgbIfRadioButtonChecked(rgb); + } +} + +/** + * @return The average of the two scalar/colors + * @param sc1 + * First scalar color + * @param sc2 + * Second scalar color + */ +PaletteNew::ScalarColor +PaletteEditorRangeWidget::averageScalarColor(const PaletteNew::ScalarColor& sc1, + const PaletteNew::ScalarColor& sc2) const +{ + PaletteNew::ScalarColor scOut((sc1.scalar + sc2.scalar) / 2.0, + (sc1.color[0] + sc2.color[0]) / 2.0, + (sc1.color[1] + sc2.color[1]) / 2.0, + (sc1.color[2] + sc2.color[2]) / 2.0); + return scOut; +} + +/** + * Called when a control point modification is requested + * @param rowIndex + * Index of the row widget + * @param constructionOperation + * The modification operation + */ +void +PaletteEditorRangeWidget::performConstruction(const int32_t rowIndex, + const PaletteEditorRangeWidget::ConstructionOperation constructionOperation) +{ + std::vector scalarColors = getScalarColors(); + const auto numScalarColors = scalarColors.size(); + const int32_t lastControlPointIndex(m_numberOfValidControlPoints - 1); + + int32_t insertAtRowIndex(-1); + switch (constructionOperation) { + case PaletteEditorRangeWidget::ConstructionOperation::INSERT_CONTROL_POINT_ABOVE: + if ((rowIndex > 0) + && (rowIndex <= lastControlPointIndex)) { + insertAtRowIndex = rowIndex - 1; + } + else { + QString msg("Invalid row index=" + + QString::number(rowIndex) + + " for inserting ABOVE row (0 at top) in range containing " + + AString::number(m_numberOfValidControlPoints) + + " rows."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + break; + case PaletteEditorRangeWidget::ConstructionOperation::INSERT_CONTROL_POINT_BELOW: + if ((rowIndex >= 0) + && (rowIndex < lastControlPointIndex)) { + insertAtRowIndex = rowIndex; + } + else { + QString msg("Invalid row index=" + + QString::number(rowIndex) + + " for inserting BELOW row (0 at top) in range containing " + + AString::number(m_numberOfValidControlPoints) + + " rows."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + break; + case PaletteEditorRangeWidget::ConstructionOperation::REMOVE_CONTROL_POINT: + /* + * Not allowed to remove first or last rows + */ + if ((rowIndex > 0) + && (rowIndex < lastControlPointIndex)) { + /* + * Row index 0 is at TOP in GUI + * But ScalarColor[0] is at BOTTOM in the GUI + */ + const int32_t removeAtIndex = (scalarColors.size() - 1 - rowIndex); + scalarColors.erase(scalarColors.begin() + removeAtIndex); + } + else { + QString msg("Invalid row index=" + + QString::number(rowIndex) + + " for REMOVING row in range containing " + + AString::number(m_numberOfValidControlPoints) + + " rows."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + break; + } + + /* + * Row index 0 is at TOP in GUI + * But ScalarColor[0] is at BOTTOM in the GUI + */ + if (insertAtRowIndex >= 0) { + const int32_t insertAtIndex(scalarColors.size() - 1 - insertAtRowIndex); + const int32_t nextIndex(insertAtIndex - 1); + CaretAssertVectorIndex(scalarColors, insertAtIndex); + CaretAssertVectorIndex(scalarColors, nextIndex); + PaletteNew::ScalarColor avgSC = averageScalarColor(scalarColors[insertAtIndex], + scalarColors[nextIndex]); + scalarColors.insert(scalarColors.begin() + insertAtIndex, + avgSC); + } + + if (scalarColors.size() != numScalarColors) { + updateContent(scalarColors); + } + + signalDataChanged(); +} + +/** + * Called when data in a row is changed + */ +void +PaletteEditorRangeWidget::performRowDataChanged() +{ + emit signalDataChanged(); + + updateScalarSpinBoxRanges(); +} + +/** + * Resets the ranges (minimum and maximum) of the scalar + * value spin boxes to the the default for the "range mode". + * This is needed since the user may switch palettes + * and the ranges will be may be incorrect for the new + * palette. + */ +void +PaletteEditorRangeWidget::resetScalarSpinBoxRanges() +{ + float rangeMin(0.0); + float rangeMax(0.0); + switch (m_dataRangeMode) { + case DataRangeMode::NEGATIVE: + rangeMin = -1.0; + break; + case DataRangeMode::POSITIVE: + rangeMax = 1.0; + break; + case DataRangeMode::ZERO: + break; + } + + for (auto row : m_rowWidgets) { + row->updateScalarValueRange(rangeMin, + rangeMax); + } +} + +/** + * The range (minimum and maximum values) for a + * value spin box must be set to ensure that + * the scalar values remain in an ascending order. + */ +void +PaletteEditorRangeWidget::updateScalarSpinBoxRanges() +{ + float rangeMin(0.0); + float rangeMax(0.0); + switch (m_dataRangeMode) { + case DataRangeMode::NEGATIVE: + rangeMin = -1.0; + rangeMax = 0.0; + break; + case DataRangeMode::POSITIVE: + rangeMin = 0.0; + rangeMax = 1.0; + break; + case DataRangeMode::ZERO: + break; + } + + /* + * Note: First row (index == 0) is at top and contains maximum scalar + * Last row at bottom contains minimum scalar + */ + for (int32_t iRow = 0; iRow < m_numberOfValidControlPoints; iRow++) { + float minValue(0.0); + float maxValue(0.0); + if (iRow == 0) { + minValue = rangeMax; + maxValue = rangeMax; + } + else if (iRow == (m_numberOfValidControlPoints - 1)) { + minValue = rangeMin; + maxValue = rangeMin; + } + else { + CaretAssertVectorIndex(m_rowWidgets, iRow - 1); + maxValue = m_rowWidgets[iRow - 1]->getScalar(); + + CaretAssertVectorIndex(m_rowWidgets, iRow + 1); + minValue = m_rowWidgets[iRow + 1]->getScalar(); + } + CaretAssertVectorIndex(m_rowWidgets, iRow); + m_rowWidgets[iRow]->updateScalarValueRange(minValue, maxValue); + } +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteEditorRangeWidget.h connectome-workbench-1.5.0/src/GuiQt/PaletteEditorRangeWidget.h --- connectome-workbench-1.4.2/src/GuiQt/PaletteEditorRangeWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteEditorRangeWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,126 @@ +#ifndef __PALETTE_EDITOR_RANGE_WIDGET_H__ +#define __PALETTE_EDITOR_RANGE_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include + +#include "CaretRgb.h" +#include "PaletteNew.h" + +class QButtonGroup; +class QDoubleSpinBox; +class QGridLayout; +class QLabel; +class QRadioButton; +class QStackedWidget; +class QToolButton; + +namespace caret { + + class PaletteEditorRangeRow; + + class PaletteEditorRangeWidget : public QWidget { + + Q_OBJECT + + public: + enum class DataRangeMode { + NEGATIVE, + POSITIVE, + ZERO + }; + + enum class ConstructionOperation { + INSERT_CONTROL_POINT_ABOVE, + INSERT_CONTROL_POINT_BELOW, + REMOVE_CONTROL_POINT + }; + + enum class ColumnTitlesMode { + SHOW_YES, + SHOW_NO + }; + + PaletteEditorRangeWidget(const DataRangeMode dataRangeMode, + QButtonGroup* colorEditButtonGroup, + const ColumnTitlesMode columnTitlesMode, + QWidget* parent); + + virtual ~PaletteEditorRangeWidget(); + + PaletteEditorRangeWidget(const PaletteEditorRangeWidget&) = delete; + + PaletteEditorRangeWidget& operator=(const PaletteEditorRangeWidget&) = delete; + + std::vector getScalarColors() const; + + void updateContent(const std::vector& scalarColorsIn); + + void updateControlPointColor(const CaretRgb& rgb); + + void selectFirstControlPoint(); + + // ADD_NEW_METHODS_HERE + + signals: + void signalEditColorRequested(const CaretRgb& rgb); + + void signalDataChanged(); + + private slots: + void performConstruction(const int32_t controlPointIndex, + const PaletteEditorRangeWidget::ConstructionOperation constructionOperation); + + void performRowDataChanged(); + + private: + PaletteNew::ScalarColor averageScalarColor(const PaletteNew::ScalarColor& sc1, + const PaletteNew::ScalarColor& sc2) const; + + void resetScalarSpinBoxRanges(); + + void updateScalarSpinBoxRanges(); + + const DataRangeMode m_dataRangeMode; + + QButtonGroup* m_colorEditButtonGroup; + + QGridLayout* m_controlPointGridLayout; + + std::vector m_rowWidgets; + + int32_t m_numberOfValidControlPoints = 0; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PALETTE_EDITOR_RANGE_WIDGET_DECLARE__ + // +#endif // __PALETTE_EDITOR_RANGE_WIDGET_DECLARE__ + +} // namespace +#endif //__PALETTE_EDITOR_RANGE_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/PalettePixmapPainter.cxx connectome-workbench-1.5.0/src/GuiQt/PalettePixmapPainter.cxx --- connectome-workbench-1.4.2/src/GuiQt/PalettePixmapPainter.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PalettePixmapPainter.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,715 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_PIXMAP_PAINTER_DECLARE__ +#include "PalettePixmapPainter.h" +#undef __PALETTE_PIXMAP_PAINTER_DECLARE__ + +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "MathFunctions.h" +#include "Palette.h" +#include "PaletteNew.h" +#include "PaletteScalarAndColor.h" + +using namespace caret; + + + +/** + * \class caret::PalettePixmapPainter + * \brief Generates a pixmap containing a palette's coloring + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param palette + * Palette used to generate the pixmap + * @param mode + * Interpolation mode (on/off) + */ +PalettePixmapPainter::PalettePixmapPainter(const Palette* palette, + const Mode mode) +: PalettePixmapPainter(palette, + QSize(100, 14), + mode) +{ +} + +/** + * Constructor. + * @param palette + * Palette used to generate the pixmap + * @param pixmapSize + * Size for pixmap + * @param mode + * Interpolation mode (on/off) + */ +PalettePixmapPainter::PalettePixmapPainter(const Palette* palette, + const QSize& pixmapSize, + const Mode mode) +: CaretObject(), +m_mode(mode) +{ + CaretAssert(palette); + if (palette == NULL) { + return; + } + + if (palette->getNumberOfScalarsAndColors() == 0) { + return; + } + + const qreal pixmapWidth(pixmapSize.width()); + const qreal pixmapHeight(pixmapSize.height()); + + switch (m_mode) { + case Mode::INTERPOLATE_OFF: + createPalettePixmapInterpolateOff(palette, + pixmapWidth, + pixmapHeight); + break; + case Mode::INTERPOLATE_ON: + createPalettePixmapInterpolateOn(palette, + pixmapWidth, + pixmapHeight); + case Mode::INTERPOLATE_ON_LINES_AT_SCALARS: + createPalettePixmapInterpolateOn(palette, + pixmapWidth, + pixmapHeight); + break; + case Mode::SCALAR_LINES: + CaretLogWarning("Scalar lines not supported for old palette"); + break; + } +} + +/** + * Constructor. + * @param palette + * Palette used to generate the pixmap + * @param pixmapSize + * Size for pixmap + * @param mode + * Interpolation mode (on/off) + */ +PalettePixmapPainter::PalettePixmapPainter(const PaletteNew* palette, + const QSize& pixmapSize, + const Mode mode) +: CaretObject(), +m_mode(mode) +{ + CaretAssert(palette); + if (palette == NULL) { + return; + } + + const qreal pixmapWidth(pixmapSize.width()); + const qreal pixmapHeight(pixmapSize.height()); + + switch (m_mode) { + case Mode::INTERPOLATE_OFF: + createPalettePixmapInterpolateOff(palette, + pixmapWidth, + pixmapHeight); + break; + case Mode::INTERPOLATE_ON: + createPalettePixmapInterpolateOn(palette, + pixmapWidth, + pixmapHeight, + DrawScalarLinesMode::LINES_OFF); + break; + case Mode::INTERPOLATE_ON_LINES_AT_SCALARS: + createPalettePixmapInterpolateOn(palette, + pixmapWidth, + pixmapHeight, + DrawScalarLinesMode::LINES_ON); + break; + case Mode::SCALAR_LINES: + createPalettePixmapScalarLines(palette, + pixmapWidth, + pixmapHeight); + break; + } +} + +/** + * Destructor. + */ +PalettePixmapPainter::~PalettePixmapPainter() +{ +} + +float +PalettePixmapPainter::paletteNonInterpScalarToPixMap(const float pixmapWidth, + const float scalarIn) +{ + float scalar(MathFunctions::limitRange(scalarIn, -1.0f, 1.0f)); + float normalized((scalar + 1.0) / 2.0); + float valueOut(normalized * pixmapWidth); + return valueOut; +} + +/** + * Create the pixmap for the interpolate OFF mode + * @param palette + * The palette + * @param pixmapWidth + * Width of pixmap + * @param pixmapHeight + * Height of pixmap + */ +void +PalettePixmapPainter::createPalettePixmapInterpolateOff(const Palette* palette, + const qreal pixmapWidth, + const qreal pixmapHeight) +{ + m_pixmap = QPixmap(static_cast(pixmapWidth), + static_cast(pixmapHeight)); + + QPainter painter(&m_pixmap); + painter.setPen(Qt::NoPen); + + float rightX(pixmapWidth); + const int32_t numPoints = palette->getNumberOfScalarsAndColors(); + for (int32_t i = 0; i < numPoints; i++) { + const PaletteScalarAndColor* psac = palette->getScalarAndColor(i); + CaretAssert(psac); + + float startX(0); + if (i < (numPoints - 1)) { + startX = paletteNonInterpScalarToPixMap(pixmapWidth, + palette->getScalarAndColor(i + 1)->getScalar()); + } + + /* + * Extend to prevent gaps dues to rounding + */ + QRect rect(startX - 1, 0, /* X, Y */ + rightX - startX + 1, pixmapHeight); /* Width, Height */ + float rgba[4]; + psac->getColor(rgba); + QColor color; + color.setRgbF(rgba[0], rgba[1], rgba[2]); + + QBrush brush(color); + painter.setBrush(brush); + painter.drawRect(rect); + + rightX = startX; + } +} + + +/** + * Create the pixmap for the interpolate ON mode + * @param palette + * The palette + * @param pixmapWidth + * Width of pixmap + * @param pixmapHeight + * Height of pixmap + */ +void +PalettePixmapPainter::createPalettePixmapInterpolateOn(const Palette* palette, + const qreal pixmapWidth, + const qreal pixmapHeight) +{ + m_pixmap = QPixmap(static_cast(pixmapWidth), + static_cast(pixmapHeight)); + + const qreal halfHeight(pixmapHeight / 2.0); + QLinearGradient linearGradient(QPointF(0, halfHeight), + QPointF(pixmapWidth, halfHeight)); + linearGradient.setCoordinateMode(QGradient::LogicalMode); + linearGradient.setSpread(QGradient::PadSpread); + + const int32_t numPoints = palette->getNumberOfScalarsAndColors(); + for (int32_t i = 0; i < numPoints; i++) { + const PaletteScalarAndColor* psac = palette->getScalarAndColor(i); + CaretAssert(psac); + + const float scalar(MathFunctions::limitRange(psac->getScalar(), -1.0f, 1.0f)); + const float gradientValue((scalar / 2.0f) + 0.5f); + float rgba[4]; + psac->getColor(rgba); + QColor color; + color.setRgbF(rgba[0], rgba[1], rgba[2]); + + linearGradient.setColorAt(gradientValue, color); + } + + QPainter painter(&m_pixmap); + painter.setPen(Qt::NoPen); + painter.setBrush(linearGradient); + painter.drawRect(m_pixmap.rect()); +} + +/** + * Create the pixmap for the interpolate ON mode + * @param palette + * The palette + * @param pixmapWidth + * Width of pixmap + * @param pixmapHeight + * Height of pixmap + */ +void +PalettePixmapPainter::createPalettePixmapInterpolateOn(const PaletteNew* palette, + const qreal pixmapWidth, + const qreal pixmapHeight, + const DrawScalarLinesMode drawLinesMode) +{ + m_pixmap = QPixmap(static_cast(pixmapWidth), + static_cast(pixmapHeight)); + + const qreal halfHeight(pixmapHeight / 2.0); + QLinearGradient linearGradient(QPointF(0, halfHeight), + QPointF(pixmapWidth, halfHeight)); + linearGradient.setCoordinateMode(QGradient::LogicalMode); + linearGradient.setSpread(QGradient::PadSpread); + + std::vector posScalarsAndColors = palette->getPosRange(); + std::vector scalarsAndColors = palette->getNegRange(); + scalarsAndColors.insert(scalarsAndColors.end(), + posScalarsAndColors.begin(), + posScalarsAndColors.end()); + + + const int32_t numPoints = static_cast(scalarsAndColors.size()); + for (int32_t i = 0; i < numPoints; i++) { + CaretAssertVectorIndex(scalarsAndColors, i); + const PaletteNew::ScalarColor& sc = scalarsAndColors[i]; + + const float scalar(MathFunctions::limitRange(sc.scalar, -1.0f, 1.0f)); + const float gradientValue((scalar / 2.0f) + 0.5f); + QColor color; + color.setRgbF(sc.color[0], sc.color[1], sc.color[2]); + + linearGradient.setColorAt(gradientValue, color); + } + + QPainter painter(&m_pixmap); + QPen pen = painter.pen(); + painter.setPen(Qt::NoPen); + painter.setBrush(linearGradient); + painter.drawRect(m_pixmap.rect()); + + painter.setBrush(Qt::NoBrush); + + const qreal lineWidth(std::min(pixmapWidth * 0.01, + 2.0)); + switch (drawLinesMode) { + case DrawScalarLinesMode::LINES_OFF: + break; + case DrawScalarLinesMode::LINES_ON: + drawScalarLines(palette, + painter, + pen, + m_pixmap, + lineWidth); + break; + } + + float zeroColor[3]; + palette->getZeroColor(zeroColor); + const int32_t zeroLineWidth(std::min(pixmapWidth * 0.02, + 3.0)); + drawLineInColorBar(painter, + pen, + zeroColor, + (pixmapWidth / 2), + 0, + pixmapHeight, + zeroLineWidth); +} + +/** + * Create the pixmap for the interpolate ON mode + * @param palette + * The palette + * @param pixmapWidth + * Width of pixmap + * @param pixmapHeight + * Height of pixmap + */ +void +PalettePixmapPainter::createPalettePixmapInterpolateOff(const PaletteNew* palette, + const qreal pixmapWidth, + const qreal pixmapHeight) +{ + m_pixmap = QPixmap(static_cast(pixmapWidth), + static_cast(pixmapHeight)); + + QPainter painter(&m_pixmap); + QPen pen = painter.pen(); + painter.setPen(Qt::NoPen); + + painter.fillRect(m_pixmap.rect(), QColor(255, 255, 255)); + + { + float rightX(pixmapWidth); + std::vector scalarsAndColors = palette->getPosRange(); + const int32_t numScalars = static_cast(scalarsAndColors.size()); + for (int32_t i = (numScalars - 1); i >= 0; i--) { + float leftX(0); + if (i == 0) { + /* + * Special case for "zero" color that extends from zero to + * halfway to the "next" color + */ + CaretAssertVectorIndex(scalarsAndColors, i); + leftX = paletteNonInterpScalarToPixMap(pixmapWidth, + scalarsAndColors[i].scalar); + CaretAssertVectorIndex(scalarsAndColors, i + 1); + const float x2 = paletteNonInterpScalarToPixMap(pixmapWidth, + scalarsAndColors[i + 1].scalar); + /* + * Width of "positive zero" is small or no more than halfway to next scalar + */ + rightX = ((leftX + x2) / 2.0); + const float diffX(rightX - leftX); + if (diffX > 10.0) { + rightX = leftX + 10.0; + } + } + else { + CaretAssertVectorIndex(scalarsAndColors, i - 1); + leftX = paletteNonInterpScalarToPixMap(pixmapWidth, + scalarsAndColors[i - 1].scalar); + } + + /* + * Extend to prevent gaps dues to rounding + */ + const float x(leftX); + const float y(0); + const float w(rightX - leftX + 1); + const float h(pixmapHeight); + QRectF rect(x, y, w, h); + + QColor color; + CaretAssertVectorIndex(scalarsAndColors, i); + color.setRgbF(scalarsAndColors[i].color[0], + scalarsAndColors[i].color[1], + scalarsAndColors[i].color[2]); + + QBrush brush(color); + painter.setBrush(brush); + painter.drawRect(rect); + + rightX = leftX; + } + } + + { + float leftX(0); + std::vector scalarsAndColors = palette->getNegRange(); + const int32_t numScalars = static_cast(scalarsAndColors.size()); + for (int32_t i = 0; i < numScalars; i++) { + float rightX(0.0); + if (i == (numScalars - 1)) { + /* + * Special case for "negative zero" + */ + CaretAssertVectorIndex(scalarsAndColors, i - 1); + const float x2 = paletteNonInterpScalarToPixMap(pixmapWidth, + scalarsAndColors[i - 1].scalar); + CaretAssertVectorIndex(scalarsAndColors, i); + rightX = paletteNonInterpScalarToPixMap(pixmapWidth, + scalarsAndColors[i].scalar); + + /* + * Keep width of "negative zero" small, no more than halfway to next scalar + */ + leftX = ((x2 + rightX) / 2.0); + const float diffX(rightX - leftX); + if (diffX > 10.0) { + leftX = rightX - 10.0; + } + } + else { + CaretAssertVectorIndex(scalarsAndColors, i); + leftX = paletteNonInterpScalarToPixMap(pixmapWidth, + scalarsAndColors[i].scalar); + CaretAssertVectorIndex(scalarsAndColors, i + 1); + rightX = paletteNonInterpScalarToPixMap(pixmapWidth, + scalarsAndColors[i + 1].scalar); + } + + /* + * Extend to prevent gaps dues to rounding + */ + QRect rect(leftX, 0, /* X, Y */ + rightX - leftX + 1, pixmapHeight); /* Width, Height */ + + QColor color; + CaretAssertVectorIndex(scalarsAndColors, i); + color.setRgbF(scalarsAndColors[i].color[0], + scalarsAndColors[i].color[1], + scalarsAndColors[i].color[2]); + + QBrush brush(color); + painter.setBrush(brush); + painter.drawRect(rect); + + leftX = rightX; + } + } + + float zeroColor[3]; + palette->getZeroColor(zeroColor); + const int32_t zeroLineWidth(std::min(pixmapWidth * 0.02, + 3.0)); + drawLineInColorBar(painter, + pen, + zeroColor, + (pixmapWidth / 2), + 0, + pixmapHeight, + zeroLineWidth); +} + +/** + * Create the pixmap that has black and white background with colored lines at scalars + * @param palette + * The palette + * @param pixmapWidth + * Width of pixmap + * @param pixmapHeight + * Height of pixmap + */ +void +PalettePixmapPainter::createPalettePixmapScalarLines(const PaletteNew* palette, + const qreal pixmapWidth, + const qreal pixmapHeight) +{ + m_pixmap = QPixmap(static_cast(pixmapWidth), + static_cast(pixmapHeight)); + + QPainter painter(&m_pixmap); + QPen pen = painter.pen(); + + /* + * Top half background is white, bottom half black + */ + QRect whiteRect(m_pixmap.rect()); + whiteRect.setHeight(whiteRect.height() / 2.0); + painter.fillRect(m_pixmap.rect(), QColor(0, 0, 0)); + painter.fillRect(whiteRect, QColor(255, 255, 255)); + + std::vector posScalarsAndColors = palette->getPosRange(); + std::vector scalarsAndColors = palette->getNegRange(); + scalarsAndColors.insert(scalarsAndColors.end(), + posScalarsAndColors.begin(), + posScalarsAndColors.end()); + + const float halfPixmapWidth(pixmapWidth / 2.0); + + const int32_t numPoints = static_cast(scalarsAndColors.size()); + for (int32_t i = 0; i < numPoints; i++) { + CaretAssertVectorIndex(scalarsAndColors, i); + const PaletteNew::ScalarColor& sc = scalarsAndColors[i]; + + const float scalar(MathFunctions::limitRange(sc.scalar, -1.0f, 1.0f)); + const float x((scalar + 1.0) * halfPixmapWidth); + QColor color(sc.color[0], sc.color[1], sc.color[2]); + drawLineInColorBar(painter, pen, sc.color, x, 0, pixmapHeight, 2.0); + } +} + +/** + * Draw lines on pixmap at scalars + * @param palette + * The palette + * @param painter + * The QPainter + * @param pen + * The QPen + * @param pixmap + * The pixmap, + * @param lineWidth + * Width of lines + */ +void +PalettePixmapPainter::drawScalarLines(const PaletteNew* palette, + QPainter& painter, + QPen& pen, + QPixmap& pixmap, + const qreal lineWidth) +{ + std::vector scalarsAndColors = palette->getNegRange(); + int32_t negZeroIndex(scalarsAndColors.size() - 1); + +// float zeroRgb[3]; +// palette->getZeroColor(zeroRgb); +// PaletteNew::ScalarColor zeroScalarAndColor(0.0, zeroRgb); +// scalarsAndColors.push_back(PaletteNew::ScalarColor(0.0, zeroRgb)); + + const int32_t posZeroIndex(scalarsAndColors.size()); + std::vector posScalarsAndColors = palette->getPosRange(); + scalarsAndColors.insert(scalarsAndColors.end(), + posScalarsAndColors.begin(), + posScalarsAndColors.end()); + + const float pixmapWidth(pixmap.width()); + const float halfPixmapWidth(pixmapWidth / 2.0); + + const float halfLineWidth(lineWidth / 2.0); + + const float white[3] { 1.0, 1.0, 1.0 }; + const float black[3] { 0.0, 0.0, 0.0 }; + + const qreal pixmapHeight(m_pixmap.height()); + + const int32_t numPoints = static_cast(scalarsAndColors.size()); + for (int32_t i = 0; i < numPoints; i++) { + CaretAssertVectorIndex(scalarsAndColors, i); + const PaletteNew::ScalarColor& sc = scalarsAndColors[i]; + + const float scalar(MathFunctions::limitRange(sc.scalar, -1.0f, 1.0f)); + float x((scalar + 1.0) * halfPixmapWidth); + QColor color(sc.color[0], sc.color[1], sc.color[2]); + + /* + * No need to draw scalar locations and ends or at zero + * But if we do, the X needs to be adjusted + */ + if (i == negZeroIndex) { + x -= lineWidth; + continue; + } + else if (i == posZeroIndex) { + x += lineWidth; + continue; + } + else if (i == 0) { + x += (lineWidth / 2.0); + continue; + } + else if (i == (numPoints - 1)) { + x -= (lineWidth / 2.0); + continue; + } + + /* + * Want line centered at scalar + */ + //x -= halfLineWidth; + const bool rightLeftFlag(false); + if (rightLeftFlag) { + drawLineInColorBar(painter, + pen, + white, + x - halfLineWidth, + 0, + pixmapHeight, + lineWidth); + drawLineInColorBar(painter, + pen, + black, + x + halfLineWidth, + 0, + pixmapHeight, + lineWidth); + } + else { + const float smallHeight(pixmapHeight / 8.0); + const float doubleSmallHeight(smallHeight * 2.0); + drawLineInColorBar(painter, + pen, + black, + x, + pixmapHeight - doubleSmallHeight, + pixmapHeight, + lineWidth); + drawLineInColorBar(painter, + pen, + white, + x, + pixmapHeight - smallHeight + 1, + pixmapHeight, + lineWidth); + } + } +} + +/** + * Draw a line in the color bar + * @param painter + * The QPainter + * @param pen + * The pen + * @param rgbFloat + * RGB coloring + * @param x + * x-coordinate of line + * @param y + * y-coordinate of line + * @param pixmapHeight + * Height of the colorbar + * @param lineWidth + * Width of the line + */ +void +PalettePixmapPainter::drawLineInColorBar(QPainter& painter, + QPen& pen, + const float rgbFloat[3], + const qreal x, + const qreal y, + const qreal pixmapHeight, + const int32_t lineWidth) +{ + QColor penColor; + penColor.setRgbF(rgbFloat[0], rgbFloat[1], rgbFloat[2]); + painter.setPen(Qt::SolidLine); + pen.setColor(penColor); + pen.setWidth(lineWidth); + painter.setPen(pen); + + painter.drawLine(x, y, x, pixmapHeight); +} + +/** + * @return Pixmap containing palettes colors. + * Test validity with .isNull() + */ +QPixmap +PalettePixmapPainter::getPixmap() const +{ + return m_pixmap; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +PalettePixmapPainter::toString() const +{ + return "PalettePixmapPainter"; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/PalettePixmapPainter.h connectome-workbench-1.5.0/src/GuiQt/PalettePixmapPainter.h --- connectome-workbench-1.4.2/src/GuiQt/PalettePixmapPainter.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PalettePixmapPainter.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,127 @@ +#ifndef __PALETTE_PIXMAP_PAINTER_H__ +#define __PALETTE_PIXMAP_PAINTER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "CaretObject.h" + +namespace caret { + + class Palette; + class PaletteNew; + + class PalettePixmapPainter : public CaretObject { + + public: + enum class Mode { + INTERPOLATE_OFF, + INTERPOLATE_ON, + INTERPOLATE_ON_LINES_AT_SCALARS, + SCALAR_LINES + }; + + PalettePixmapPainter(const Palette* palette, + const Mode mode); + + PalettePixmapPainter(const Palette* palette, + const QSize& pixmapSize, + const Mode mode); + + PalettePixmapPainter(const PaletteNew* palette, + const QSize& pixmapSize, + const Mode mode); + + virtual ~PalettePixmapPainter(); + + PalettePixmapPainter(const PalettePixmapPainter&) = delete; + + PalettePixmapPainter& operator=(const PalettePixmapPainter&) = delete; + + QPixmap getPixmap() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + enum class DrawScalarLinesMode { + LINES_OFF, + LINES_ON + }; + + void createPalettePixmapInterpolateOff(const Palette* palette, + const qreal pixmapWidth, + const qreal pixmapHeight); + + void createPalettePixmapInterpolateOn(const Palette* palette, + const qreal pixmapWidth, + const qreal pixmapHeight); + + float paletteNonInterpScalarToPixMap(const float pixmapWidth, + const float scalarIn); + + void createPalettePixmapInterpolateOff(const PaletteNew* palette, + const qreal pixmapWidth, + const qreal pixmapHeight); + + void createPalettePixmapInterpolateOn(const PaletteNew* palette, + const qreal pixmapWidth, + const qreal pixmapHeight, + const DrawScalarLinesMode drawLinesMode); + + void createPalettePixmapScalarLines(const PaletteNew* palette, + const qreal pixmapWidth, + const qreal pixmapHeight); + + void drawScalarLines(const PaletteNew* palette, + QPainter& painter, + QPen& pen, + QPixmap& pixmap, + const qreal lineWidth); + + void drawLineInColorBar(QPainter& painter, + QPen& pen, + const float rgbFloat[3], + const qreal x, + const qreal y, + const qreal pixmapHeight, + const int32_t lineWidth); + + const Mode m_mode = Mode::INTERPOLATE_ON; + + QPixmap m_pixmap; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PALETTE_PIXMAP_PAINTER_DECLARE__ + // +#endif // __PALETTE_PIXMAP_PAINTER_DECLARE__ + +} // namespace +#endif //__PALETTE_PIXMAP_PAINTER_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteSelectionWidget.cxx connectome-workbench-1.5.0/src/GuiQt/PaletteSelectionWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/PaletteSelectionWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteSelectionWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,297 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_SELECTION_WIDGET_DECLARE__ +#include "PaletteSelectionWidget.h" +#undef __PALETTE_SELECTION_WIDGET_DECLARE__ + +#include +#include +#include +#include + + +#include "CaretAssert.h" +#include "EventManager.h" +#include "EventPaletteGroupsGet.h" +#include "PaletteCreateNewDialog.h" +#include "PaletteGroup.h" +#include "PaletteNew.h" +#include "PalettePixmapPainter.h" + +using namespace caret; + + +/** + * \class caret::PaletteSelectionWidget + * \brief Widget for selection of a palette source and palette from the source + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param parent + * The parent widget + */ +PaletteSelectionWidget::PaletteSelectionWidget(QWidget* parent) +: QWidget(parent) +{ + createUserPalettes(); + + m_paletteGroupComboBox = new QComboBox(); + QObject::connect(m_paletteGroupComboBox, QOverload::of(&QComboBox::activated), + this, &PaletteSelectionWidget::paletteGroupComboBoxActivated); +// m_paletteSourceComboBox->addItem("User Palettes"); + + m_paletteSelectionListWidget = new QListWidget(); + QObject::connect(m_paletteSelectionListWidget, &QListWidget::itemActivated, + this, &PaletteSelectionWidget::paletteListWidgetActivated); + +// QSize iconSize(80, 18); +// +// for (auto& pal : m_userPalettes) { +// PalettePixmapPainter palettePainter(pal.get(), +// iconSize, +// m_pixmapMode); +// QPixmap pixmap = palettePainter.getPixmap(); +// const QString name = pal->getName(); +// if (pixmap.isNull()) { +// +// m_paletteSelectionListWidget->addItem(name); +// } +// else { +// +// m_paletteSelectionListWidget->addItem(new QListWidgetItem(pixmap, +// name)); +// } +// } +// m_paletteSelectionListWidget->setIconSize(iconSize); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(m_paletteGroupComboBox); + layout->addWidget(m_paletteSelectionListWidget); + + if (m_paletteSelectionListWidget->count() > 0) { + m_paletteSelectionListWidget->setCurrentRow(0); + } + + updateContent(); +} + +/** + * Destructor. + */ +PaletteSelectionWidget::~PaletteSelectionWidget() +{ +} + +/** + * Update the content of the selection widget + */ +void +PaletteSelectionWidget::updateContent() +{ + updateGroupSelectionComboBox(); + + updatePaletteSelectionListWidget(); +} + +/** + * Update the group selection combo box + */ +void +PaletteSelectionWidget::updateGroupSelectionComboBox() +{ + /* + * Find the selected group + */ + PaletteGroup* selectedGroup(getSelectedPaletteGroup()); + + m_paletteGroups.clear(); + int32_t defaultIndex(0); + + /* + * Get the valid groups + */ + EventPaletteGroupsGet paletteEvent; + EventManager::get()->sendEvent(paletteEvent.getPointer()); + + /* + * Load the group combo box + */ + m_paletteGroupComboBox->clear(); + std::vector> groups = paletteEvent.getPaletteGroups(); + for (auto groupPtr : groups) { + std::shared_ptr sharedPtr = groupPtr.lock(); + if (sharedPtr) { + PaletteGroup* pg = sharedPtr.get(); + CaretAssert(pg); + if (pg == selectedGroup) { + defaultIndex = m_paletteGroupComboBox->count(); + } + m_paletteGroupComboBox->addItem(pg->getGroupName()); + m_paletteGroups.push_back(groupPtr); + } + } + + if (defaultIndex < m_paletteGroupComboBox->count()) { + m_paletteGroupComboBox->setCurrentIndex(defaultIndex); + } +} + +/** + * Update the palette selection list widget + */ +void +PaletteSelectionWidget::updatePaletteSelectionListWidget() +{ + /* + * Get name of previously selected palette + */ + QString selectedPaletteName; + QListWidgetItem* selectedItem = m_paletteSelectionListWidget->currentItem(); + if (selectedItem != NULL) { + selectedPaletteName = selectedItem->text(); + } + + m_paletteSelectionListWidget->clear(); + + const PaletteGroup* paletteGroup = getSelectedPaletteGroup(); + if (paletteGroup != NULL) { + std::vector palettes; + paletteGroup->getPalettes(palettes); + + int32_t defaultIndex(0); + for (auto p : palettes) { + const QString paletteName = p.getName(); + if (selectedPaletteName == paletteName) { + defaultIndex = m_paletteSelectionListWidget->count(); + } +// m_paletteSelectionListWidget->addItem(paletteName); + + QSize iconSize(80, 18); + + PalettePixmapPainter palettePainter(&p, + iconSize, + PalettePixmapPainter::Mode::INTERPOLATE_ON); + QPixmap pixmap = palettePainter.getPixmap(); + const QString name = p.getName(); + if (pixmap.isNull()) { + + m_paletteSelectionListWidget->addItem(name); + } + else { + + m_paletteSelectionListWidget->addItem(new QListWidgetItem(pixmap, + name)); + } + m_paletteSelectionListWidget->setIconSize(iconSize); + } + + if (defaultIndex < m_paletteSelectionListWidget->count()) { + m_paletteSelectionListWidget->setCurrentRow(defaultIndex); + } + } +} + +/** + * @return Pointer to the selected palette group + */ +PaletteGroup* +PaletteSelectionWidget::getSelectedPaletteGroup() const +{ + PaletteGroup* paletteGroup(NULL); + + const int32_t groupIndex = m_paletteGroupComboBox->currentIndex(); + if ((groupIndex >= 0) + && (groupIndex < m_paletteGroupComboBox->count())) { + std::shared_ptr sharedPtr = m_paletteGroups[groupIndex].lock(); + if (sharedPtr) { + paletteGroup = sharedPtr.get(); + } + } + + + return paletteGroup; +} + +/** + * @return Palette selected or NULL if no palette selected + */ +std::unique_ptr +PaletteSelectionWidget::getSelectedPalette() const +{ + PaletteGroup* selectedGroup = getSelectedPaletteGroup(); + if (selectedGroup == NULL) { + return NULL; + } + + QString selectedPaletteName; + QListWidgetItem* selectedItem = m_paletteSelectionListWidget->currentItem(); + if (selectedItem != NULL) { + selectedPaletteName = selectedItem->text(); + } + + std::unique_ptr palette = selectedGroup->getPaletteWithName(selectedPaletteName); + +// const int32_t paletteIndex = m_paletteSelectionListWidget->currentRow(); +// if ((paletteIndex >= 0) +// && (paletteIndex < m_paletteSelectionListWidget->count())) { +// CaretAssertVectorIndex(m_userPalettes, paletteIndex); +// paletteOut = m_userPalettes[paletteIndex].get(); +// } + + return palette; +} + +void +PaletteSelectionWidget::paletteGroupComboBoxActivated(int /*index*/) +{ + updatePaletteSelectionListWidget(); + emit paletteSelectionChanged(); +} + +/** + * Called when a user palette is selected + * @param item + * Item selected by the user + */ +void +PaletteSelectionWidget::paletteListWidgetActivated(QListWidgetItem* /*item*/) +{ + emit paletteSelectionChanged(); +} + +/** + * Create example palettes for testing + */ +void +PaletteSelectionWidget::createUserPalettes() +{ +// m_userPalettes.clear(); +// +// std::unique_ptr pal55(PaletteCreateNewDialog::createPaletteNew("Pal55", 5, 5)); +// std::unique_ptr pal34(PaletteCreateNewDialog::createPaletteNew("Pal34", 3, 4)); +// +// m_userPalettes.push_back(std::move(pal55)); +// m_userPalettes.push_back(std::move(pal34)); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/PaletteSelectionWidget.h connectome-workbench-1.5.0/src/GuiQt/PaletteSelectionWidget.h --- connectome-workbench-1.4.2/src/GuiQt/PaletteSelectionWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PaletteSelectionWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,100 @@ +#ifndef __PALETTE_SELECTION_WIDGET_H__ +#define __PALETTE_SELECTION_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "PalettePixmapPainter.h" + +class QComboBox; +class QListWidget; +class QListWidgetItem; + +namespace caret { + + class PaletteGroup; + class PaletteNew; + + class PaletteSelectionWidget : public QWidget { + + Q_OBJECT + + public: + PaletteSelectionWidget(QWidget* parent= 0); + + virtual ~PaletteSelectionWidget(); + + PaletteSelectionWidget(const PaletteSelectionWidget&) = delete; + + PaletteSelectionWidget& operator=(const PaletteSelectionWidget&) = delete; + + void updateContent(); + + PaletteGroup* getSelectedPaletteGroup() const; + + std::unique_ptr getSelectedPalette() const; + + // ADD_NEW_METHODS_HERE + + signals: + /** + * Emitted when the user selects a palette + * @param palette + * The palette selected or NULL if no palette selected + */ + void paletteSelectionChanged(); + + private slots: + + void paletteGroupComboBoxActivated(int index); + + void paletteListWidgetActivated(QListWidgetItem* item); + + private: + void createUserPalettes(); + + void updateGroupSelectionComboBox(); + + void updatePaletteSelectionListWidget(); + + PalettePixmapPainter::Mode m_pixmapMode = PalettePixmapPainter::Mode::INTERPOLATE_ON; + + QComboBox* m_paletteGroupComboBox; + + QListWidget* m_paletteSelectionListWidget; + + std::vector> m_paletteGroups; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PALETTE_SELECTION_WIDGET_DECLARE__ + // +#endif // __PALETTE_SELECTION_WIDGET_DECLARE__ + +} // namespace +#endif //__PALETTE_SELECTION_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/PreferencesDialog.cxx connectome-workbench-1.5.0/src/GuiQt/PreferencesDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/PreferencesDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PreferencesDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -44,9 +44,11 @@ #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" +#include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ImageCaptureMethodEnum.h" #include "OpenGLDrawingMethodEnum.h" +#include "PreferencesRecentFilesWidget.h" #include "SessionManager.h" #include "WuQtUtilities.h" #include "WuQFactory.h" @@ -89,6 +91,10 @@ */ m_allWidgets = new WuQWidgetObjectGroup(this); + m_recentFilesWidget = new PreferencesRecentFilesWidget(); + QObject::connect(m_recentFilesWidget, &PreferencesRecentFilesWidget::updateDialog, + this, &PreferencesDialog::recentFilesChanged); + /* * Create the tab widget and all tab content */ @@ -103,6 +109,9 @@ "New Tabs"); tabWidget->addTab(createOpenGLWidget(), "OpenGL"); + tabWidget->addTab(m_recentFilesWidget, + "Recent Files"); + setCentralWidget(tabWidget, WuQDialog::SCROLL_AREA_NEVER); @@ -133,57 +142,80 @@ QSignalMapper* colorSignalMapper) { QString buttonText; + AString buttonToolTip; QWidget* colorSwatchWidget = new QWidget(); switch (prefColor) { case PREF_COLOR_BACKGROUND_ALL: buttonText = "All Background"; + buttonToolTip = "Color for background in All Display"; m_backgroundColorAllWidget = colorSwatchWidget; break; case PREF_COLOR_BACKGROUND_CHART: buttonText = "Chart Background"; + buttonToolTip = "Color for background in Chart Display"; m_backgroundColorChartWidget = colorSwatchWidget; break; case PREF_COLOR_BACKGROUND_SURFACE: buttonText = "Surface Background"; + buttonToolTip = "Color for background in Surface Display"; m_backgroundColorSurfaceWidget = colorSwatchWidget; break; case PREF_COLOR_BACKGROUND_VOLUME: buttonText = "Volume Background"; + buttonToolTip = "Color for background in Volume Display"; m_backgroundColorVolumeWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_ALL: buttonText = "All Foreground"; + buttonToolTip = "Color for foreground (text) in All Display"; m_foregroundColorAllWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_CHART: buttonText = "Chart Foreground"; + buttonToolTip = "Color for foreground (text) in Chart Display"; m_foregroundColorChartWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_SURFACE: buttonText = "Surface Foreground"; + buttonToolTip = "Color for foreground (text) in Surface Display"; m_foregroundColorSurfaceWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_VOLUME: buttonText = "Volume Foreground"; + buttonToolTip = "Color for foreground (text) in Volume Display"; m_foregroundColorVolumeWidget = colorSwatchWidget; break; case PREF_COLOR_CHART_MATRIX_GRID_LINES: buttonText = "Chart Grid Lines"; + buttonToolTip = "Color for grid lines in a Chart Matrix Display"; m_chartMatrixGridLinesColorWidget = colorSwatchWidget; break; case PREF_COLOR_CHART_THRESHOLD: buttonText = "Chart Threshold"; + buttonToolTip = "Color for thresholded regions in Chart Histogram Display"; m_chartHistogramThresholdColorWidget = colorSwatchWidget; break; case PREF_COLOR_BACKGROUND_WINDOW: + buttonToolTip = "Color for background in Window"; buttonText = "Window Background"; m_backgroundColorWindowWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_WINDOW: + buttonToolTip = "Color for foreground (text) in Window Display"; buttonText = "Window Foreground"; m_foregroundColorWindowWidget = colorSwatchWidget; break; + case PREF_COLOR_BACKGROUND_MEDIA: + buttonToolTip = "Color for background in Media Display"; + buttonText = "Media Background"; + m_backgroundColorMediaWidget = colorSwatchWidget; + break; + case PREF_COLOR_FOREGROUND_MEDIA: + buttonToolTip = "Color for foreground (text) in Media Display"; + buttonText = "Media Foreground"; + m_foregroundColorMediaWidget = colorSwatchWidget; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -194,6 +226,7 @@ CaretAssert( ! buttonText.isEmpty()); QPushButton* colorPushButton = new QPushButton(buttonText); + colorPushButton->setToolTip(buttonToolTip); QObject::connect(colorPushButton, SIGNAL(clicked()), colorSignalMapper, SLOT(map())); colorSignalMapper->setMapping(colorPushButton, @@ -247,7 +280,12 @@ addColorButtonAndSwatch(gridLayout, PREF_COLOR_BACKGROUND_SURFACE, colorSignalMapper); - + addColorButtonAndSwatch(gridLayout, + PREF_COLOR_FOREGROUND_MEDIA, + colorSignalMapper); + addColorButtonAndSwatch(gridLayout, + PREF_COLOR_BACKGROUND_MEDIA, + colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_FOREGROUND_VOLUME, colorSignalMapper); @@ -334,6 +372,14 @@ colors.getColorForegroundWindow(rgb); colorSwatchWidget = m_foregroundColorWindowWidget; break; + case PREF_COLOR_FOREGROUND_MEDIA: + colors.getColorForegroundMediaView(rgb); + colorSwatchWidget = m_foregroundColorMediaWidget; + break; + case PREF_COLOR_BACKGROUND_MEDIA: + colors.getColorBackgroundMediaView(rgb); + colorSwatchWidget = m_backgroundColorMediaWidget; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -385,16 +431,6 @@ m_allWidgets->add(m_miscLoggingLevelComboBox); /* - * Splash Screen - */ - m_miscSplashScreenShowAtStartupComboBox = new WuQTrueFalseComboBox("On", - "Off", - this); - QObject::connect(m_miscSplashScreenShowAtStartupComboBox, SIGNAL(statusChanged(bool)), - this, SLOT(miscSplashScreenShowAtStartupComboBoxChanged(bool))); - m_allWidgets->add(m_miscSplashScreenShowAtStartupComboBox); - - /* * Developer Menu */ m_miscDevelopMenuEnabledComboBox = new WuQTrueFalseComboBox("On", @@ -405,6 +441,19 @@ m_allWidgets->add(m_miscDevelopMenuEnabledComboBox); /* + * Gestures enabled + */ + const QString gesturesToolTip("Pinch two fingers to zoom; Rotate with two fingers"); + m_guiGesturesEnabledComboBox = new WuQTrueFalseComboBox("On", + "Off", + this); + WuQtUtilities::setWordWrappedToolTip(m_guiGesturesEnabledComboBox->getWidget(), + gesturesToolTip); + QObject::connect(m_guiGesturesEnabledComboBox, &WuQTrueFalseComboBox::statusChanged, + this, &PreferencesDialog::miscGuiGesturesEnabledComboBoxChanged); + m_allWidgets->add(m_guiGesturesEnabledComboBox); + + /* * Manage Files View Files Type */ m_miscSpecFileDialogViewFilesTypeEnumComboBox = new EnumComboBoxTemplate(this); @@ -413,6 +462,26 @@ this, SLOT(miscSpecFileDialogViewFilesTypeEnumComboBoxItemActivated())); m_allWidgets->add(m_miscSpecFileDialogViewFilesTypeEnumComboBox->getWidget()); + /* + * Toolbar mode + */ + const QString widthToolTip(ToolBarWidthModeEnum::toGuiName(ToolBarWidthModeEnum::WIDE) + + " mode will show \"View\" toolbar components in all Modes but requires " + "a wide monitor (1920 width or greater)."); + m_windowToolBarWidthModeComboBox = new EnumComboBoxTemplate(this); + m_windowToolBarWidthModeComboBox->setup(); + m_windowToolBarWidthModeComboBox->setToolTip(WuQtUtilities::createWordWrappedToolTipText(widthToolTip)); + QObject::connect(m_windowToolBarWidthModeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &PreferencesDialog::miscWindowToolBarWidthModeComboBoxItemActivated); + + const QString fileOpenTip("What to do when a file is opened outside of wb_view from the GUI. " + "On MacOS: Double-clicked in Finder"); + m_fileOpenFromOpSysTypeComboBox = new EnumComboBoxTemplate(this); + m_fileOpenFromOpSysTypeComboBox->setup(); + m_fileOpenFromOpSysTypeComboBox->setToolTip(WuQtUtilities::createWordWrappedToolTipText(fileOpenTip)); + QObject::connect(m_fileOpenFromOpSysTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &PreferencesDialog::miscFileOpenFromOpSysTypeComboBoxItemActivated); + QGridLayout* gridLayout = new QGridLayout(); addWidgetToLayout(gridLayout, "Dynconn As Layer Default: ", @@ -427,8 +496,14 @@ "Show Develop Menu in Menu Bar: ", m_miscDevelopMenuEnabledComboBox->getWidget()); addWidgetToLayout(gridLayout, - "Show Splash Screen at Startup: ", - m_miscSplashScreenShowAtStartupComboBox->getWidget()); + "Enable Trackpad Gestures: ", + m_guiGesturesEnabledComboBox->getWidget()); + addWidgetToLayout(gridLayout, + "Open File from MacOS Finder", + m_fileOpenFromOpSysTypeComboBox->getWidget()); + addWidgetToLayout(gridLayout, + "Window ToolBar Width Mode: ", + m_windowToolBarWidthModeComboBox->getWidget()); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -456,12 +531,15 @@ m_miscDevelopMenuEnabledComboBox->setStatus(prefs->isDevelopMenuEnabled()); - m_miscSplashScreenShowAtStartupComboBox->setStatus(prefs->isSplashScreenEnabled()); - m_yokingDefaultComboBox->setStatus(prefs->isYokingDefaultedOn()); m_miscSpecFileDialogViewFilesTypeEnumComboBox->setSelectedItem(prefs->getManageFilesViewFileType()); + m_guiGesturesEnabledComboBox->setStatus(prefs->isGuiGesturesEnabled()); + + m_windowToolBarWidthModeComboBox->setSelectedItem(prefs->getToolBarWidthMode()); + + m_fileOpenFromOpSysTypeComboBox->setSelectedItem(prefs->getFileOpenFromOpSysType()); } /** @@ -485,6 +563,16 @@ QObject::connect(m_dataToolTipsComboBox, SIGNAL(statusChanged(bool)), this, SLOT(identificationSymbolToggled())); + /* + * Identification Mode + */ + m_identificationModeComboBox = new EnumComboBoxTemplate(this); + m_identificationModeComboBox->setup(); + QObject::connect(m_identificationModeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &PreferencesDialog::identificationModeEnumComboBoxItemActivated); + m_allWidgets->add(m_identificationModeComboBox->getWidget()); + + QGridLayout* gridLayout = new QGridLayout(); int row = gridLayout->rowCount(); gridLayout->addWidget(infoLabel, @@ -498,6 +586,10 @@ addWidgetToLayout(gridLayout, "Show Data Tool Tips: ", m_dataToolTipsComboBox->getWidget()); + addWidgetToLayout(gridLayout, + "Identification Display: ", + m_identificationModeComboBox->getWidget()); + QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -518,6 +610,7 @@ m_surfaceIdentificationSymbolComboBox->setStatus(prefs->isShowSurfaceIdentificationSymbols()); m_volumeIdentificationSymbolComboBox->setStatus(prefs->isShowVolumeIdentificationSymbols()); m_dataToolTipsComboBox->setStatus(prefs->isShowDataToolTipsEnabled()); + m_identificationModeComboBox->setSelectedItem(prefs->getIdentificationDisplayMode()); } /** @@ -533,6 +626,18 @@ } /** + * Gets called when an identification display mode is changed + */ +void +PreferencesDialog::identificationModeEnumComboBoxItemActivated() +{ + const IdentificationDisplayModeEnum::Enum idMode = m_identificationModeComboBox->getSelectedItem(); + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->setIdentificationDisplayMode(idMode); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} + +/** * @return The OpenGL widget. */ QWidget* @@ -664,7 +769,7 @@ m_volumeAllSlicePlanesLayoutComboBox = new EnumComboBoxTemplate(this); m_volumeAllSlicePlanesLayoutComboBox->setup(); QObject::connect(m_volumeAllSlicePlanesLayoutComboBox, SIGNAL(itemActivated()), - this, SLOT(m_volumeAllSlicePlanesLayoutItemActivated())); + this, SLOT(volumeAllSlicePlanesLayoutItemActivated())); m_allWidgets->add(m_volumeAllSlicePlanesLayoutComboBox->getWidget()); @@ -777,6 +882,15 @@ } /** + * Called when changes are made to recent files + */ +void +PreferencesDialog::recentFilesChanged() +{ + updateDialog(); +} + +/** * May be called to update the dialog's content. */ void @@ -790,6 +904,7 @@ updateIdentificationWidget(prefs); updateOpenGLWidget(prefs); updateVolumeWidget(prefs); + m_recentFilesWidget->updateContent(prefs); m_allWidgets->blockAllSignals(false); } @@ -807,7 +922,7 @@ const BackgroundAndForegroundColors colors = prefs->getUserBackgroundAndForegroundColors(); - uint8_t rgb[3]; + uint8_t rgb[3] = { 0, 0, 0 }; AString prefColorName; switch (prefColor) { case PREF_COLOR_BACKGROUND_ALL: @@ -858,6 +973,14 @@ colors.getColorForegroundWindow(rgb); prefColorName = "Foreground - Window"; break; + case PREF_COLOR_BACKGROUND_MEDIA: + colors.getColorBackgroundMediaView(rgb); + prefColorName = "Background - Media"; + break; + case PREF_COLOR_FOREGROUND_MEDIA: + colors.getColorForegroundMediaView(rgb); + prefColorName = "Foreground - Media"; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -917,6 +1040,12 @@ case PREF_COLOR_FOREGROUND_WINDOW: colors.setColorForegroundWindow(rgb); break; + case PREF_COLOR_BACKGROUND_MEDIA: + colors.setColorBackgroundMediaView(rgb); + break; + case PREF_COLOR_FOREGROUND_MEDIA: + colors.setColorForegroundMediaView(rgb); + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -1032,7 +1161,7 @@ * Called when ALL view slice plane layout changed by user */ void -PreferencesDialog::m_volumeAllSlicePlanesLayoutItemActivated() +PreferencesDialog::volumeAllSlicePlanesLayoutItemActivated() { VolumeSliceViewAllPlanesLayoutEnum::Enum layoutValue = m_volumeAllSlicePlanesLayoutComboBox->getSelectedItem(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); @@ -1072,17 +1201,6 @@ } /** - * Called when show splash screen option changed. - * @param value - * New value. - */ -void PreferencesDialog::miscSplashScreenShowAtStartupComboBoxChanged(bool value) -{ - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->setSplashScreenEnabled(value); -} - -/** * Called when dynamic connectivity option changed. * @param value * New value. @@ -1108,6 +1226,42 @@ } /** + * Called when gui gestures enabled changed. + * + * @param value + * New value. + */ +void +PreferencesDialog::miscGuiGesturesEnabledComboBoxChanged(bool value) +{ + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->setGuiGesturesEnabled(value); +} + +/** + * Gets called window toolbar mode is changed + */ +void +PreferencesDialog::miscWindowToolBarWidthModeComboBoxItemActivated() +{ + const ToolBarWidthModeEnum::Enum widthMode = m_windowToolBarWidthModeComboBox->getSelectedItem(); + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->setToolBarWidthMode(widthMode); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} + +/** + * Gets called when file open from O/S type is changed + */ +void +PreferencesDialog::miscFileOpenFromOpSysTypeComboBoxItemActivated() +{ + const FileOpenFromOpSysTypeEnum::Enum openType = m_fileOpenFromOpSysTypeComboBox->getSelectedItem(); + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->setFileOpenFromOpSysType(openType); +} +/** * Gets called when view files type is changed. */ void diff -Nru connectome-workbench-1.4.2/src/GuiQt/PreferencesDialog.h connectome-workbench-1.5.0/src/GuiQt/PreferencesDialog.h --- connectome-workbench-1.4.2/src/GuiQt/PreferencesDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PreferencesDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -36,6 +36,7 @@ class CaretPreferences; class EnumComboBoxTemplate; + class PreferencesRecentFilesWidget; class WuQTrueFalseComboBox; class WuQWidgetObjectGroup; @@ -57,11 +58,11 @@ void miscDevelopMenuEnabledComboBoxChanged(bool value); void miscLoggingLevelComboBoxChanged(int); - void miscSplashScreenShowAtStartupComboBoxChanged(bool value); void miscSpecFileDialogViewFilesTypeEnumComboBoxItemActivated(); - + void miscGuiGesturesEnabledComboBoxChanged(bool value); void miscDynamicConnectivityComboBoxChanged(bool value); - + void miscWindowToolBarWidthModeComboBoxItemActivated(); + void miscFileOpenFromOpSysTypeComboBoxItemActivated(); void openGLDrawingMethodEnumComboBoxItemActivated(); void openGLImageCaptureMethodEnumComboBoxItemActivated(); @@ -70,12 +71,14 @@ void volumeAxesMontageCoordinatesComboBoxToggled(bool value); void volumeMontageCoordinatePrecisionChanged(int value); void volumeIdentificationComboBoxToggled(bool value); - void m_volumeAllSlicePlanesLayoutItemActivated(); + void volumeAllSlicePlanesLayoutItemActivated(); void yokingComboBoxToggled(bool value); void identificationSymbolToggled(); + void identificationModeEnumComboBoxItemActivated(); + void recentFilesChanged(); private: enum PREF_COLOR { PREF_COLOR_BACKGROUND_ALL = 0, @@ -88,9 +91,11 @@ PREF_COLOR_FOREGROUND_VOLUME = 7, PREF_COLOR_CHART_MATRIX_GRID_LINES = 8, PREF_COLOR_CHART_THRESHOLD = 9, - PREF_COLOR_BACKGROUND_WINDOW = 10, - PREF_COLOR_FOREGROUND_WINDOW = 11, - NUMBER_OF_PREF_COLORS = 12 + PREF_COLOR_FOREGROUND_MEDIA = 10, + PREF_COLOR_BACKGROUND_MEDIA = 11, + PREF_COLOR_BACKGROUND_WINDOW = 12, + PREF_COLOR_FOREGROUND_WINDOW = 13, + NUMBER_OF_PREF_COLORS = 14 }; QWidget* createColorsWidget(); @@ -128,20 +133,22 @@ QWidget* m_foregroundColorChartWidget; QWidget* m_foregroundColorSurfaceWidget; QWidget* m_foregroundColorVolumeWidget; + QWidget* m_foregroundColorMediaWidget; QWidget* m_backgroundColorWindowWidget; QWidget* m_backgroundColorAllWidget; QWidget* m_backgroundColorChartWidget; QWidget* m_backgroundColorSurfaceWidget; QWidget* m_backgroundColorVolumeWidget; + QWidget* m_backgroundColorMediaWidget; QWidget* m_chartMatrixGridLinesColorWidget; QWidget* m_chartHistogramThresholdColorWidget; WuQTrueFalseComboBox* m_miscDevelopMenuEnabledComboBox; QComboBox* m_miscLoggingLevelComboBox; - WuQTrueFalseComboBox* m_miscSplashScreenShowAtStartupComboBox; EnumComboBoxTemplate* m_miscSpecFileDialogViewFilesTypeEnumComboBox; - - + WuQTrueFalseComboBox* m_guiGesturesEnabledComboBox; + EnumComboBoxTemplate* m_windowToolBarWidthModeComboBox; + EnumComboBoxTemplate* m_fileOpenFromOpSysTypeComboBox; EnumComboBoxTemplate* m_openGLDrawingMethodEnumComboBox; EnumComboBoxTemplate* m_openGLImageCaptureMethodEnumComboBox; @@ -160,8 +167,11 @@ WuQTrueFalseComboBox* m_surfaceIdentificationSymbolComboBox; WuQTrueFalseComboBox* m_volumeIdentificationSymbolComboBox; WuQTrueFalseComboBox* m_dataToolTipsComboBox; + EnumComboBoxTemplate* m_identificationModeComboBox; WuQWidgetObjectGroup* m_allWidgets; + + PreferencesRecentFilesWidget* m_recentFilesWidget; }; #ifdef __PREFERENCES_DIALOG__H__DECLARE__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/PreferencesRecentFilesWidget.cxx connectome-workbench-1.5.0/src/GuiQt/PreferencesRecentFilesWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/PreferencesRecentFilesWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PreferencesRecentFilesWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,411 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PREFERENCES_RECENT_FILES_WIDGET_DECLARE__ +#include "PreferencesRecentFilesWidget.h" +#undef __PREFERENCES_RECENT_FILES_WIDGET_DECLARE__ + + +/** + * \class caret::PreferencesRecentFilesWidget + * \brief Widget for recent file properties in preferences + * \ingroup GuiQt + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretFileDialog.h" +#include "CaretPreferences.h" +#include "EnumComboBoxTemplate.h" +#include "RecentFilesSystemAccessModeEnum.h" +#include "WuQMessageBox.h" +#include "WuQTrueFalseComboBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + +/** + * Constructor. + */ +PreferencesRecentFilesWidget::PreferencesRecentFilesWidget(QWidget* parent) +: QWidget(parent) +{ + /* + * Show Open Recent Files Dialog at Startup + */ + QLabel* showRecentFilesDialogLabel = new QLabel("Show Open Recent Files Dialog at Startup"); + m_showOpenRecentFilesDialogAtStartupComboBox = new WuQTrueFalseComboBox("On", + "Off", + this); + QObject::connect(m_showOpenRecentFilesDialogAtStartupComboBox, &WuQTrueFalseComboBox::statusChanged, + this, &PreferencesRecentFilesWidget::showOpenRecentFilesDialogAtStartupComboBoxActivated); + m_showOpenRecentFilesDialogAtStartupComboBox->setToolTip("Show the Open Recent Files Dialog when wb_view is started"); + + /* + * Files system access mode + */ + QLabel* fileSystemLabel = new QLabel("File System Access"); + const QString fileSystemToolTip("File system access allows verification of a path's validity " + "and display of last modified time. Disabling file system " + "access may be useful when there are problems with a file " + "system (such as a mounted file systems that is not responding)."); + m_recentFilesSystemAccessModeEnumComboBox = new EnumComboBoxTemplate(this); + m_recentFilesSystemAccessModeEnumComboBox->setup(); + QObject::connect(m_recentFilesSystemAccessModeEnumComboBox, SIGNAL(itemActivated()), + this, SLOT(recentFilesSystemAccessModeEnumComboBoxItemActivated())); + WuQtUtilities::setWordWrappedToolTip(m_recentFilesSystemAccessModeEnumComboBox->getWidget(), + fileSystemToolTip); + + /* + * Recent files + */ + QString recentSceneSpecSpinBoxToolTip; + QString recentSceneSpecClearButtonToolTip; + getMaximumSpinBoxAndClearButtonToolTips("Scene and Spec Files", + recentSceneSpecSpinBoxToolTip, + recentSceneSpecClearButtonToolTip); + QLabel* recentFilesLabel = new QLabel("Recent Files Maximum"); + m_numberOfRecentSceneAndSpecFilesSpinBox = new QSpinBox(); + m_numberOfRecentSceneAndSpecFilesSpinBox->setRange(0, 1000); + QObject::connect(m_numberOfRecentSceneAndSpecFilesSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &PreferencesRecentFilesWidget::numberOfRecentSceneAndSpecFilesSpinBoxValueChanged); + m_numberOfRecentSceneAndSpecFilesSpinBox->setToolTip(recentSceneSpecSpinBoxToolTip); + QPushButton* clearRecentFilesPushButton = new QPushButton("Clear..."); + QObject::connect(clearRecentFilesPushButton, &QPushButton::clicked, + this, &PreferencesRecentFilesWidget::clearRecentSceneAndSpecFilesButtonClicked); + clearRecentFilesPushButton->setToolTip(recentSceneSpecClearButtonToolTip); + + /* + * Recent directories + */ + QString recentDirectoriesSpinBoxToolTip; + QString recentDirectoriesClearButtonToolTip; + getMaximumSpinBoxAndClearButtonToolTips("Directories", + recentDirectoriesSpinBoxToolTip, + recentDirectoriesClearButtonToolTip); + QLabel* recentDirectoriesLabel = new QLabel("Recent Directories Maximum"); + m_numberOfRecentDirectoriesSpinBox = new QSpinBox(); + m_numberOfRecentDirectoriesSpinBox->setRange(0, 1000); + QObject::connect(m_numberOfRecentDirectoriesSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &PreferencesRecentFilesWidget::numberOfRecentDirectoriesSpinBoxValueChanged); + m_numberOfRecentDirectoriesSpinBox->setToolTip(recentDirectoriesSpinBoxToolTip); + QPushButton* clearRecentDirectoriesPushButton = new QPushButton("Clear..."); + QObject::connect(clearRecentDirectoriesPushButton, &QPushButton::clicked, + this, &PreferencesRecentFilesWidget::clearRecentDirectoriesButtonClicked); + clearRecentDirectoriesPushButton->setToolTip(recentDirectoriesClearButtonToolTip); + + /* + * Exclusion Paths + */ + const QString exclusionPathToolTip("If a Directory, Scene File, or Spec File is in an exclusion path, " + "the directory or file is NOT added to recent directories and files " + "listed in the Open Recent Files Dialog. This may be useful if the " + "user is working with some form of protected data."); + m_exclusionPathsListWidget = new QListWidget(); + m_exclusionPathsListWidget->setSelectionMode(QListWidget::ExtendedSelection); + m_exclusionPathsListWidget->setToolTip("" + exclusionPathToolTip + ""); + QPushButton* addExclusionPathPushButton = new QPushButton("Add..."); + QObject::connect(addExclusionPathPushButton, &QPushButton::clicked, + this, &PreferencesRecentFilesWidget::addExclusionPathButtonClicked); + addExclusionPathPushButton->setToolTip("" + "Add an exclusion path.
" + + exclusionPathToolTip + + ""); + QPushButton* removeExclusionPathPushButton = new QPushButton("Remove..."); + QObject::connect(removeExclusionPathPushButton, &QPushButton::clicked, + this, &PreferencesRecentFilesWidget::removeExclusionPathButtonClicked); + removeExclusionPathPushButton->setToolTip("" + "Remove selected exclusion path(s).
" + + exclusionPathToolTip + + ""); + + /* + * Layouts + */ + QWidget* widget = new QWidget(); + QGridLayout* layout = new QGridLayout(widget); + int32_t row(0); + layout->addWidget(showRecentFilesDialogLabel, + row, 0, Qt::AlignRight); + layout->addWidget(m_showOpenRecentFilesDialogAtStartupComboBox->getWidget(), + row, 1, 1, 3); + row++; + layout->addWidget(fileSystemLabel, row, 0, Qt::AlignRight); + layout->addWidget(m_recentFilesSystemAccessModeEnumComboBox->getWidget(), + row, 1, 1, 3); + row++; + layout->addWidget(recentFilesLabel, row, 0, Qt::AlignRight); + layout->addWidget(m_numberOfRecentSceneAndSpecFilesSpinBox, row, 1); + layout->addWidget(clearRecentFilesPushButton, row, 2); + row++; + layout->addWidget(recentDirectoriesLabel, row, 0, Qt::AlignRight); + layout->addWidget(m_numberOfRecentDirectoriesSpinBox, row, 1); + layout->addWidget(clearRecentDirectoriesPushButton, row, 2); + row++; + + QGroupBox* exclusionPathsGroupBox = new QGroupBox("Exclusion Paths"); + QGridLayout* exclusionPathsLayout = new QGridLayout(exclusionPathsGroupBox); + exclusionPathsLayout->addWidget(m_exclusionPathsListWidget, 0, 0, 1, 2); + exclusionPathsLayout->addWidget(addExclusionPathPushButton, 1, 0, Qt::AlignHCenter); + exclusionPathsLayout->addWidget(removeExclusionPathPushButton, 1, 1, Qt::AlignHCenter); + + /* + * Widget layout + */ + QVBoxLayout* widgetLayout = new QVBoxLayout(this); + widgetLayout->addWidget(widget); + widgetLayout->addWidget(exclusionPathsGroupBox); +} + +/** + * Destructor. + */ +PreferencesRecentFilesWidget::~PreferencesRecentFilesWidget() +{ +} + +/** + * Get the tool tips for a recent file type's maximum spin box and clear push button. + * @param typeName + * Type name for spin box and button + * @param spinBoxToolTipOut + * Tooltip for spin box + * @param clearButtonToolTipOut + * Tooltip for clear button + */ +void +PreferencesRecentFilesWidget::getMaximumSpinBoxAndClearButtonToolTips(const QString& typeName, + QString& spinBoxToolTipOut, + QString& clearButtonToolTipOut) const +{ + spinBoxToolTipOut = ("" + "Maximum number of " + typeName + " listed in Open Recent " + "Files Dialog. Note that Favorites are not automatically removed so the " + "number of " + typeName + " listed in the Open Recent " + "Files Dialog may exceed this number." + ""); + + clearButtonToolTipOut = ("" + "Clears history of recently opened " + typeName + " listed in the " + "Open Recent Files Dialog. When this button is clicked, the user is " + "asked to choose between keeping or removing any Favorites that are in the Recent " + + typeName + + "." + + ""); +} + + +/** + * Called when file system access mode is selected by user + */ +void +PreferencesRecentFilesWidget::recentFilesSystemAccessModeEnumComboBoxItemActivated() +{ + if (m_preferences != NULL) { + const auto accessMode = m_recentFilesSystemAccessModeEnumComboBox->getSelectedItem(); + m_preferences->setRecentFilesSystemAccessMode(accessMode); + } +} + +/* + * Update the content in this widget + * @param caretPreferences + * The caret preferences + */ +void +PreferencesRecentFilesWidget::updateContent(CaretPreferences* caretPreferences) +{ + m_preferences = caretPreferences; + CaretAssert(m_preferences); + + m_showOpenRecentFilesDialogAtStartupComboBox->setStatus(m_preferences->isSplashScreenEnabled()); + + const auto accessMode = m_preferences->getRecentFilesSystemAccessMode(); + m_recentFilesSystemAccessModeEnumComboBox->setSelectedItem(accessMode); + + QSignalBlocker filesSpinBlocker(m_numberOfRecentSceneAndSpecFilesSpinBox); + m_numberOfRecentSceneAndSpecFilesSpinBox->setValue(m_preferences->getRecentMaximumNumberOfSceneAndSpecFiles()); + + QSignalBlocker directoriesSpinBlocker(m_numberOfRecentDirectoriesSpinBox); + m_numberOfRecentDirectoriesSpinBox->setValue(m_preferences->getRecentMaximumNumberOfDirectories()); + + std::set exclusionPaths; + m_preferences->readRecentFilesExclusionPaths(exclusionPaths); + + m_exclusionPathsListWidget->clear(); + for (auto ep : exclusionPaths) { + m_exclusionPathsListWidget->addItem(ep); + } +} + +/** + * Called when number of recent files spin box value changed + * @param value + * New value + */ +void +PreferencesRecentFilesWidget::numberOfRecentSceneAndSpecFilesSpinBoxValueChanged(int value) +{ + if (m_preferences != NULL) { + m_preferences->setRecentMaximumNumberOfSceneAndSpecFiles(value); + updateContent(m_preferences); + } +} + +/** + * Called when number of recent directories spin box value changed + * @param value + * New value + */ +void +PreferencesRecentFilesWidget::numberOfRecentDirectoriesSpinBoxValueChanged(int value) +{ + if (m_preferences != NULL) { + m_preferences->setRecentMaximumNumberOfDirectories(value); + updateContent(m_preferences); + } +} + +/** + * Called when clear recent files button clicked + */ +void +PreferencesRecentFilesWidget::clearRecentSceneAndSpecFilesButtonClicked() +{ + if (m_preferences != NULL) { + const AString infoText("" + "Do you want to include favorites when deleting all recent scene and spec files?

" + " Yes - All recent files, including favorites are removed
" + " No - Recent files are removed, excluding any favorites
" + " Cancel - Take no further action" + ""); + const auto yesNoCancelResult = WuQMessageBox::warningYesNoCancel(this, + "Delete Favorites Too?", + infoText); + switch (yesNoCancelResult) { + case WuQMessageBox::RESULT_CANCEL: + break; + case WuQMessageBox::RESULT_NO: + m_preferences->clearRecentSceneAndSpecFiles(false); + break; + case WuQMessageBox::RESULT_YES: + m_preferences->clearRecentSceneAndSpecFiles(true); + break; + } + + updateContent(m_preferences); + } +} + +/** + * Called when clear recent directories button clicked + */ +void +PreferencesRecentFilesWidget::clearRecentDirectoriesButtonClicked() +{ + if (m_preferences != NULL) { + const AString infoText("" + "Do you want to include favorites when deleting all recent directories?

" + " Yes - All recent directories, including favorites are removed
" + " No - Recent directories are removed, excluding any favorites
" + " Cancel - Take no further action" + ""); + const auto yesNoCancelResult = WuQMessageBox::warningYesNoCancel(this, + "Delete Favorites Too?", + infoText); + switch (yesNoCancelResult) { + case WuQMessageBox::RESULT_CANCEL: + break; + case WuQMessageBox::RESULT_NO: + m_preferences->clearRecentDirectories(false); + break; + case WuQMessageBox::RESULT_YES: + m_preferences->clearRecentDirectories(true); + break; + } + + updateContent(m_preferences); + } + +} + +/** + * Called when add exclusion path button clicked + */ +void +PreferencesRecentFilesWidget::addExclusionPathButtonClicked() +{ + if (m_preferences != NULL) { + const QString directoryName = CaretFileDialog::getExistingDirectoryDialog(this, + "Choose Directory"); + if ( ! directoryName.isEmpty()) { + m_preferences->addToRecentFilesExclusionPaths(directoryName); + updateContent(m_preferences); + } + } +} + +/** + * Called when remove exclusion path button clicked + */ +void +PreferencesRecentFilesWidget::removeExclusionPathButtonClicked() +{ + if (m_preferences != NULL) { + std::vector pathsToRemove; + const int32_t numItems = m_exclusionPathsListWidget->count(); + for (int32_t row = 0; row < numItems; row++) { + QListWidgetItem* item = m_exclusionPathsListWidget->item(row); + if (item != NULL) { + if (item->isSelected()) { + pathsToRemove.push_back(item->text()); + } + } + } + + if ( ! pathsToRemove.empty()) { + for (auto& path : pathsToRemove) { + m_preferences->removeFromRecentFilesExclusionPaths(path); + } + updateContent(m_preferences); + } + } +} + +/** + * Called when Show Open Recent Files Dialog Combo Box activated + * @param status + * New status + */ +void +PreferencesRecentFilesWidget::showOpenRecentFilesDialogAtStartupComboBoxActivated(bool status) +{ + if (m_preferences != NULL) { + m_preferences->setSplashScreenEnabled(status); + } +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/PreferencesRecentFilesWidget.h connectome-workbench-1.5.0/src/GuiQt/PreferencesRecentFilesWidget.h --- connectome-workbench-1.4.2/src/GuiQt/PreferencesRecentFilesWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/PreferencesRecentFilesWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,103 @@ +#ifndef __PREFERENCES_RECENT_FILES_WIDGET_H__ +#define __PREFERENCES_RECENT_FILES_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include +#include +#include + + + +namespace caret { + class CaretPreferences; + class EnumComboBoxTemplate; + class WuQTrueFalseComboBox; + + class PreferencesRecentFilesWidget : public QWidget { + + Q_OBJECT + + public: + PreferencesRecentFilesWidget(QWidget* parent = 0); + + virtual ~PreferencesRecentFilesWidget(); + + PreferencesRecentFilesWidget(const PreferencesRecentFilesWidget&) = delete; + + PreferencesRecentFilesWidget& operator=(const PreferencesRecentFilesWidget&) = delete; + + void updateContent(CaretPreferences* preferences); + + // ADD_NEW_METHODS_HERE + + signals: + void updateDialog(); + + private slots: + void recentFilesSystemAccessModeEnumComboBoxItemActivated(); + + void numberOfRecentSceneAndSpecFilesSpinBoxValueChanged(int); + + void numberOfRecentDirectoriesSpinBoxValueChanged(int); + + void clearRecentSceneAndSpecFilesButtonClicked(); + + void clearRecentDirectoriesButtonClicked(); + + void addExclusionPathButtonClicked(); + + void removeExclusionPathButtonClicked(); + + void showOpenRecentFilesDialogAtStartupComboBoxActivated(bool status); + + private: + void getMaximumSpinBoxAndClearButtonToolTips(const QString& typeName, + QString& spinBoxToolTipOut, + QString& clearButtonToolTipOut) const; + + /* DO NOT delete */ + CaretPreferences* m_preferences = NULL; + + EnumComboBoxTemplate* m_recentFilesSystemAccessModeEnumComboBox; + + QSpinBox* m_numberOfRecentSceneAndSpecFilesSpinBox; + + QSpinBox* m_numberOfRecentDirectoriesSpinBox; + + QListWidget* m_exclusionPathsListWidget; + + WuQTrueFalseComboBox* m_showOpenRecentFilesDialogAtStartupComboBox; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PREFERENCES_RECENT_FILES_WIDGET_DECLARE__ + // +#endif // __PREFERENCES_RECENT_FILES_WIDGET_DECLARE__ + +} // namespace +#endif //__PREFERENCES_RECENT_FILES_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/RecentFileNameWidget.cxx connectome-workbench-1.5.0/src/GuiQt/RecentFileNameWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/RecentFileNameWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/RecentFileNameWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,54 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __RECENT_FILE_NAME_WIDGET_DECLARE__ +#include "RecentFileNameWidget.h" +#undef __RECENT_FILE_NAME_WIDGET_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::RecentFileNameWidget + * \brief + * \ingroup GuiQt + * + * + */ + +/** + * Constructor. + */ +RecentFileNameWidget::RecentFileNameWidget() +: QWidget() +{ + +} + +/** + * Destructor. + */ +RecentFileNameWidget::~RecentFileNameWidget() +{ +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/RecentFileNameWidget.h connectome-workbench-1.5.0/src/GuiQt/RecentFileNameWidget.h --- connectome-workbench-1.4.2/src/GuiQt/RecentFileNameWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/RecentFileNameWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,60 @@ +#ifndef __RECENT_FILE_NAME_WIDGET_H__ +#define __RECENT_FILE_NAME_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + + + +namespace caret { + + class RecentFileNameWidget : public QWidget { + + Q_OBJECT + + public: + RecentFileNameWidget(); + + virtual ~RecentFileNameWidget(); + + RecentFileNameWidget(const RecentFileNameWidget&) = delete; + + RecentFileNameWidget& operator=(const RecentFileNameWidget&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __RECENT_FILE_NAME_WIDGET_DECLARE__ + // +#endif // __RECENT_FILE_NAME_WIDGET_DECLARE__ + +} // namespace +#endif //__RECENT_FILE_NAME_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/RecentFilesDialog.cxx connectome-workbench-1.5.0/src/GuiQt/RecentFilesDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/RecentFilesDialog.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/RecentFilesDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,945 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __RECENT_FILES_DIALOG_DECLARE__ +#include "RecentFilesDialog.h" +#undef __RECENT_FILES_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ApplicationInformation.h" +#include "Brain.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "DataFileException.h" +#include "GuiManager.h" +#include "RecentFileItem.h" +#include "RecentFileItemsContainer.h" +#include "RecentFileItemsFilter.h" +#include "RecentFilesTableWidget.h" +#include "Scene.h" +#include "SceneFile.h" +#include "SessionManager.h" +#include "UsernamePasswordWidget.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::RecentFilesDialog + * \brief Dialog for opening recent files and also functions as splash dialog + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param runMode + * Mode for running the dialog + * @param parent + * The parent widget + */ +RecentFilesDialog::RecentFilesDialog(const RunMode runMode, + QWidget* parent) +: QDialog(parent), +m_runMode(runMode) +{ + AString dialogTitle; + ApplicationInformation appInfo; + switch (m_runMode) { + case RunMode::OPEN_RECENT: + dialogTitle = "Open Recent"; + break; + case RunMode::SPLASH_SCREEN: + dialogTitle = (appInfo.getName() + + " " + + appInfo.getVersion()); + break; + } + + setWindowTitle(dialogTitle); + + QWidget* internetButtonsWidget = createInternetButtonsWidget(); + + QWidget* fileTypeButtonsWidget = createFileTypesButtonWidget(); + + QWidget* filesFilteringWidget = createFilesFilteringWidget(); + + m_recentFilesTableWidget = new RecentFilesTableWidget(); + QObject::connect(m_recentFilesTableWidget, &RecentFilesTableWidget::sortingChanged, + this, &RecentFilesDialog::updateFilesTableContent); + QObject::connect(m_recentFilesTableWidget, &RecentFilesTableWidget::loadSceneOrSpecFileFromItem, + this, &RecentFilesDialog::loadSceneOrSpecFileFromItem); + + + + QWidget* dialogButtonWidget = createDialogButtonsWidget(); + + /* + * Must be connected after dialog buttons created since it may alter the Open button status + */ + QObject::connect(m_recentFilesTableWidget, &RecentFilesTableWidget::selectedItemChanged, + this, &RecentFilesDialog::tableWidgetItemClicked); + QObject::connect(m_recentFilesTableWidget, &RecentFilesTableWidget::selectedItemDoubleClicked, + this, &RecentFilesDialog::tableWidgetItemDoubleClicked); + + QGridLayout* dialogLayout = new QGridLayout(this); + QMargins layoutMargins = dialogLayout->contentsMargins(); + layoutMargins.setBottom(0); + layoutMargins.setLeft(0); + layoutMargins.setTop(0); + dialogLayout->setContentsMargins(layoutMargins); + dialogLayout->setHorizontalSpacing(0); + dialogLayout->setVerticalSpacing(0); + dialogLayout->setRowStretch(2, 100); + dialogLayout->setColumnStretch(1, 100); + int row(0); + dialogLayout->addWidget(internetButtonsWidget, 0, 0, 2, 1); + dialogLayout->addWidget(filesFilteringWidget, 0, 1); + row++; + dialogLayout->addWidget(fileTypeButtonsWidget, 2, 0); + dialogLayout->addWidget(m_recentFilesTableWidget, 1, 1, 2, 1); + row++; + dialogLayout->addWidget(dialogButtonWidget, 3, 1); + + m_openPushButton->setAutoDefault(true); + m_openPushButton->setDefault(true); + + Brain* brain = GuiManager::get()->getBrain(); + m_currentDirectoryItemsContainer.reset(RecentFileItemsContainer::newInstanceSceneAndSpecFilesInDirectory(brain->getCurrentDirectory())); + + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + m_recentFilesItemsContainer.reset(RecentFileItemsContainer::newInstanceRecentSceneAndSpecFiles(preferences, + RecentFileItemsContainer::WriteIfModifiedType::WRITE_YES)); + + m_recentDirectoryItemsContainer.reset(RecentFileItemsContainer::newInstanceRecentDirectories(preferences, + RecentFileItemsContainer::WriteIfModifiedType::WRITE_YES)); + + /* + * Favorites is updated when it is selected + */ + std::vector emptyContainers; + m_favoriteItemsContainer.reset(RecentFileItemsContainer::newInstanceFavorites(emptyContainers)); + + /* + * Default to a non-empty container + */ + RecentFileItemsContainerModeEnum::Enum selectedMode = RecentFileItemsContainerModeEnum::RECENT_FILES; + if ( ! m_recentFilesItemsContainer->isEmpty()) { + selectedMode = RecentFileItemsContainerModeEnum::RECENT_FILES; + } + else if ( ! m_recentDirectoryItemsContainer->isEmpty()) { + selectedMode = RecentFileItemsContainerModeEnum::RECENT_DIRECTORIES; + } + else if ( ! m_currentDirectoryItemsContainer->isEmpty()) { + selectedMode = RecentFileItemsContainerModeEnum::DIRECTORY_SCENE_AND_SPEC_FILES; + } + else if ( ! m_favoriteItemsContainer->isEmpty()) { + selectedMode = RecentFileItemsContainerModeEnum::FAVORITES; + } + + QAction* selectedAction = getActionForMode(selectedMode); + selectedAction->trigger(); + + /* + * Enables/disables Open Button + */ + tableWidgetItemClicked(m_recentFilesTableWidget->getSelectedItem()); +} + +/** + * Destructor. + */ +RecentFilesDialog::~RecentFilesDialog() +{ +} + +/** + * Called when a key is pressed + */ +void +RecentFilesDialog::keyPressEvent(QKeyEvent* event) +{ + /* + * Prevents return key in the name matching line edit + * from selecting default button and closing dialog + */ + const int32_t key = event->key(); + if ((key == Qt::Key_Return) + || (key == Qt::Key_Enter)) { + event->ignore(); + } + else { + QDialog::keyPressEvent(event); + } +} + +/** + * Run the dialog + * + * @param runMode + * The run mode (open recent files or splash screen) + * @param nameOut + * Output with name of selected directory or file name + * @param + * Index of scene if result is loading scene from scene file + * @param parent + * Parent for dialog + * @return + * Result enumerated value + */ +RecentFilesDialog::ResultModeEnum +RecentFilesDialog::runDialog(const RunMode runMode, + AString& nameOut, + int32_t& sceneIndexOut, + QWidget* parent) +{ + RecentFilesDialog rfd(runMode, + parent); + rfd.exec(); + + ResultModeEnum resultMode = rfd.getResultMode(); + nameOut = rfd.getSelectedDirectoryOrFileName(); + sceneIndexOut = rfd.getSelectedSceneIndex(); + + return resultMode; +} + +/** + * @return The result mode + */ +RecentFilesDialog::ResultModeEnum +RecentFilesDialog::getResultMode() const +{ + return m_resultMode; +} + +/** + * @return The selected directory or file name + */ +AString +RecentFilesDialog::getSelectedDirectoryOrFileName() const +{ + return m_resultFilePathAndName; +} + +/** + * @return Index of scene if requested loading of scene from scene file + */ +int32_t +RecentFilesDialog::getSelectedSceneIndex() const +{ + return m_resultSceneIndex; +} + +/** + * @return New instance files filtering widget + */ +QWidget* +RecentFilesDialog::createFilesFilteringWidget() +{ + QLabel* listLabel = new QLabel("List: "); + + m_listDirectoriesCheckBox = new QCheckBox("Directories"); + m_listDirectoriesCheckBox->setChecked(true); + QObject::connect(m_listDirectoriesCheckBox, &QCheckBox::clicked, + this, &RecentFilesDialog::listDirectoriesCheckBoxClicked); + m_listDirectoriesCheckBox->setToolTip("Show/hide directories"); + + m_listSceneFilesCheckBox = new QCheckBox("Scene"); + m_listSceneFilesCheckBox->setChecked(true); + QObject::connect(m_listSceneFilesCheckBox, &QCheckBox::clicked, + this, &RecentFilesDialog::listSceneFilesCheckBoxClicked); + m_listSceneFilesCheckBox->setToolTip("Show/hide scene files"); + + m_listSpecFilesCheckBox = new QCheckBox("Spec"); + m_listSpecFilesCheckBox->setChecked(true); + QObject::connect(m_listSpecFilesCheckBox, &QCheckBox::clicked, + this, &RecentFilesDialog::listSpecFilesCheckBoxClicked); + m_listSpecFilesCheckBox->setToolTip("Show/hide spec files"); + + QLabel* nameFilterLabel = new QLabel("Name Filter: "); + m_nameFilterLineEdit = new QLineEdit(); + m_nameFilterLineEdit->setClearButtonEnabled(true); + m_nameFilterLineEdit->setFixedWidth(250); + QObject::connect(m_nameFilterLineEdit, &QLineEdit::textEdited, + this, &RecentFilesDialog::nameFilterTextEdited); + m_nameFilterLineEdit->setToolTip(RecentFileItemsFilter::getMatchingLineEditToolTip()); + + m_showFilePathsCheckBox = new QCheckBox("Show Scene/Spec Paths"); + m_showFilePathsCheckBox->setChecked(true); + QObject::connect(m_showFilePathsCheckBox, &QCheckBox::clicked, + this, &RecentFilesDialog::showFilePathsCheckBoxClicked); + m_showFilePathsCheckBox->setToolTip("Show/hide paths below the names of scene and spec files"); + + QWidget* widget = new QWidget(); + QHBoxLayout* layout = new QHBoxLayout(widget); + layout->addWidget(listLabel); + layout->addWidget(m_listDirectoriesCheckBox); + layout->addWidget(m_listSceneFilesCheckBox); + layout->addWidget(m_listSpecFilesCheckBox); + layout->addSpacing(25); + layout->addWidget(nameFilterLabel); + layout->addWidget(m_nameFilterLineEdit); + layout->addSpacing(25); + layout->addWidget(m_showFilePathsCheckBox); + layout->addStretch(); + + return widget; +} + + +/** + * @return New instance of the dialog buttons + */ +QWidget* +RecentFilesDialog::createDialogButtonsWidget() +{ + bool showTestButtonFlag(false); + QPushButton* testPushButton(NULL); + if (showTestButtonFlag) { + testPushButton = new QPushButton("Test XML Read/Write"); + QObject::connect(testPushButton, &QPushButton::clicked, + this, &RecentFilesDialog::testButtonClicked); + } + + m_loadPushButton = new QPushButton("Load"); + QObject::connect(m_loadPushButton, &QPushButton::clicked, + this, &RecentFilesDialog::loadButtonClicked); + m_loadPushButton->setToolTip("" + "Action depends upon type of item selected:" + "

    " + "
  • Scene File - Displays a menu listing scene names. Selecting a scene name " + "loads the scene without having to use the Scene Dialog." + "
  • Spec File - Loads all files in the spec file without having to use " + "the Open Spec File Dialog." + "
" + ""); + + m_openPushButton = new QPushButton("Open"); + QObject::connect(m_openPushButton, &QPushButton::clicked, + this, &RecentFilesDialog::openButtonClicked); + m_openPushButton->setToolTip("" + "Action depends upon type of item selected:" + "
    " + "
  • Directory - File Dialog is displayed listing contents of directory." + "
  • Scene File - Scene File is opened in the Scene File Dialog for Scene selection." + "
  • Spec File - Files in Spec File are listed in the Spec File Dialog." + "
" + ""); + + QPushButton* openOtherPushButton = new QPushButton("Open Other..."); + QObject::connect(openOtherPushButton, &QPushButton::clicked, + this, &RecentFilesDialog::openOtherButtonClicked); + openOtherPushButton->setToolTip("File Dialog is displayed listing contents of current directory " + "(same action as File Menu -> Open File)"); + + QPushButton* cancelPushButton = new QPushButton("Cancel"); + QObject::connect(cancelPushButton, &QPushButton::clicked, + this, &RecentFilesDialog::cancelButtonClicked); + switch (m_runMode) { + case RunMode::OPEN_RECENT: + cancelPushButton->setToolTip("Closes dialog with no action taken"); + break; + case RunMode::SPLASH_SCREEN: + cancelPushButton->setToolTip("Close this dialog and display Workbench Window"); + break; + } + + QWidget* widget = new QWidget(); + QHBoxLayout* layout = new QHBoxLayout(widget); + layout->addStretch(); + if (testPushButton != NULL) { + layout->addWidget(testPushButton); + layout->addSpacing(50); + } + layout->addWidget(openOtherPushButton); + layout->addWidget(m_openPushButton); + layout->addWidget(m_loadPushButton); + layout->addWidget(cancelPushButton); + + + return widget; +} + +/** + * @return New instance of the file type buttons + */ +QWidget* +RecentFilesDialog::createFileTypesButtonWidget() +{ + m_fileTypeModeActionGroup = new QActionGroup(this); + + std::vector modes; + RecentFileItemsContainerModeEnum::getAllEnums(modes); + + std::vector toolButtons; + for (auto m : modes) { + AString buttonText(RecentFileItemsContainerModeEnum::toGuiButtonName(m)); + if (buttonText.isEmpty()) { + /* Mode is not valid in dialog */ + continue; + } + + QAction* action = m_fileTypeModeActionGroup->addAction(buttonText); + action->setData(RecentFileItemsContainerModeEnum::toName(m)); + action->setCheckable(true); + + QString toolTipText; + switch (m) { + case RecentFileItemsContainerModeEnum::DIRECTORY_SCENE_AND_SPEC_FILES: + toolTipText = ("Choose from Scene and Spec Files in the current directory"); + break; + case RecentFileItemsContainerModeEnum::FAVORITES: + toolTipText = ("Choose from Favorites: favorites are created by clicking " + "the Favorite Icon (star) in the Favorite column for an item"); + break; + case RecentFileItemsContainerModeEnum::OTHER: + break; + case RecentFileItemsContainerModeEnum::RECENT_DIRECTORIES: + toolTipText = ("Choose from directories that have been visitied by " + "the user for opening or saving files"); + break; + case RecentFileItemsContainerModeEnum::RECENT_FILES: + toolTipText = ("Choose from Scene and Spec files recently opened by the user"); + break; + } + WuQtUtilities::setWordWrappedToolTip(action, toolTipText); + + QToolButton* tb = new QToolButton(); + tb->setDefaultAction(action); + tb->setStyleSheet("font : 14px"); /* larger characters */ + toolButtons.push_back(tb); + } + + std::vector toolButtonWidgets(toolButtons.begin(), + toolButtons.end()); + WuQtUtilities::matchWidgetSizes(toolButtonWidgets); + + QObject::connect(m_fileTypeModeActionGroup, &QActionGroup::triggered, + this, &RecentFilesDialog::filesModeActionTriggered); + + QWidget* widget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(widget); + for (auto tb : toolButtons) { + layout->addWidget(tb); + } + layout->addStretch(); + + return widget; +} + +/** + * Called when name filter text is edited by user + * @param text + * Text in the name filter line edit + */ +void +RecentFilesDialog::nameFilterTextEdited(const QString& /*text*/) +{ + updateFilesTableContent(); + m_nameFilterLineEdit->setFocus(); +} + +/** + * Called if List Directories checkbox is clicked + */ +void +RecentFilesDialog::listDirectoriesCheckBoxClicked(bool /*checked*/) +{ + updateFilesTableContent(); +} + +/** + * Called if List Scene Files checkbox is clicked + */ +void +RecentFilesDialog::listSceneFilesCheckBoxClicked(bool /*checked*/) +{ + updateFilesTableContent(); +} + +/** + * Called if List Spec Files checkbox is clicked + */ +void +RecentFilesDialog::listSpecFilesCheckBoxClicked(bool /*checked*/) +{ + updateFilesTableContent(); +} + +/** + * Called if Show File Paths checkbox is clicked + */ +void +RecentFilesDialog::showFilePathsCheckBoxClicked(bool /*checked*/) +{ + updateFilesTableContent(); +} + +/** + * @return New instance of internet buttons widget + */ +QWidget* +RecentFilesDialog::createInternetButtonsWidget() +{ + QToolButton* hcpToolButton = new QToolButton(); + QIcon hcpImage; + if (WuQtUtilities::loadIcon(":/RecentFilesDialog/hcp_image.png", hcpImage)) { + hcpToolButton->setIconSize(QSize(32, 32)); + hcpToolButton->setIcon(hcpImage); + } + else { + hcpToolButton->setText("H"); + } + hcpToolButton->setToolTip("Visit HCP Website"); + QObject::connect(hcpToolButton, &QToolButton::clicked, + this, &RecentFilesDialog::hcpWebsiteButtonClicked); + + QToolButton* twitterToolButton = new QToolButton(); + QIcon twitterIcon; + if (WuQtUtilities::loadIcon(":/RecentFilesDialog/twitter_image.png", + twitterIcon)) { + twitterToolButton->setIconSize(QSize(32, 32)); + twitterToolButton->setIcon(twitterIcon); + } + else { + twitterToolButton->setText("T"); + } + twitterToolButton->setToolTip("Visit HCP Twitter Feed"); + QObject::connect(twitterToolButton, &QToolButton::clicked, + this, &RecentFilesDialog::twitterButtonClicked); + + QWidget* widget = new QWidget(); + QHBoxLayout* layout = new QHBoxLayout(widget); + layout->addStretch(); + layout->addWidget(hcpToolButton); + layout->addWidget(twitterToolButton); + layout->addStretch(); + + return widget; +} + +/** + * Called when the HCP website button is clicked + */ +void +RecentFilesDialog::hcpWebsiteButtonClicked() +{ + websiteLinkActivated("http://www.humanconnectome.org"); +} + +/** + * Called when the Twitter button is clicked + */ +void +RecentFilesDialog::twitterButtonClicked() +{ + websiteLinkActivated("http://twitter.com/#!/HumanConnectome"); +} + +/** + * Called when a label's hyperlink is selected. + * @param link + * The URL. + */ +void +RecentFilesDialog::websiteLinkActivated(const QString& link) +{ + if ( ! link.isEmpty()) { + QDesktopServices::openUrl(QUrl(link)); + } +} + +/** + * Called when a files mode action is selected + * + * @param action + * Action that was selected + */ +void +RecentFilesDialog::filesModeActionTriggered(QAction* /*action*/) +{ + updateFilesTableContent(); +} + +/** + * Update the files table widget's content + */ +void +RecentFilesDialog::updateFilesTableContent() +{ + RecentFileItemsFilter filter; + filter.setNameMatching(m_nameFilterLineEdit->text().trimmed()); + + RecentFileItemsContainer* itemsContainer(NULL); + + switch (getSelectedFilesMode()) { + case RecentFileItemsContainerModeEnum::DIRECTORY_SCENE_AND_SPEC_FILES: + itemsContainer = m_currentDirectoryItemsContainer.get(); + filter.setListSceneFiles(m_listSceneFilesCheckBox->isChecked()); + filter.setListSpecFiles(m_listSpecFilesCheckBox->isChecked()); + break; + case RecentFileItemsContainerModeEnum::FAVORITES: + updateFavoritesContainer(); + itemsContainer = m_favoriteItemsContainer.get(); + filter.setListDirectories(m_listDirectoriesCheckBox->isChecked()); + filter.setListSceneFiles(m_listSceneFilesCheckBox->isChecked()); + filter.setListSpecFiles(m_listSpecFilesCheckBox->isChecked()); + break; + case RecentFileItemsContainerModeEnum::OTHER: + CaretAssertMessage(0, "OTHER not used in dialog"); + break; + case RecentFileItemsContainerModeEnum::RECENT_DIRECTORIES: + itemsContainer = m_recentDirectoryItemsContainer.get(); + filter.setListDirectories(true); + break; + case RecentFileItemsContainerModeEnum::RECENT_FILES: + itemsContainer = m_recentFilesItemsContainer.get(); + filter.setListSceneFiles(m_listSceneFilesCheckBox->isChecked()); + filter.setListSpecFiles(m_listSpecFilesCheckBox->isChecked()); + break; + } + + filter.setShowFilePaths(m_showFilePathsCheckBox->isChecked()); + + m_recentFilesTableWidget->updateContent(itemsContainer, + filter); +} + +/** + * Update the contents of the favorites container + */ +void +RecentFilesDialog::updateFavoritesContainer() +{ + std::vector containers { + m_recentFilesItemsContainer.get(), + m_recentDirectoryItemsContainer.get() + }; + m_favoriteItemsContainer.reset(RecentFileItemsContainer::newInstanceFavorites(containers)); +} + +/** + * @return The selected files mode + */ +RecentFileItemsContainerModeEnum::Enum +RecentFilesDialog::getSelectedFilesMode() const +{ + RecentFileItemsContainerModeEnum::Enum mode =RecentFileItemsContainerModeEnum::RECENT_FILES; + + QAction* selectedAction = m_fileTypeModeActionGroup->checkedAction(); + CaretAssert(selectedAction); + + const AString modeName = selectedAction->data().toString(); + bool validFlag(false); + mode = RecentFileItemsContainerModeEnum::fromName(modeName, &validFlag); + CaretAssert(validFlag); + + return mode; +} + +/** + * @return Action for the given mode + * @param recentFilesMode + * The mode + */ +QAction* +RecentFilesDialog::getActionForMode(const RecentFileItemsContainerModeEnum::Enum recentFilesMode) const +{ + const AString modeName(RecentFileItemsContainerModeEnum::toName(recentFilesMode)); + + QAction* actionOut(NULL); + + QListIterator iter(m_fileTypeModeActionGroup->actions()); + while (iter.hasNext()) { + QAction* action = iter.next(); + const AString actionDataName = action->data().toString(); + if (modeName == actionDataName) { + actionOut = action; + break; + } + } + CaretAssert(actionOut); + + return actionOut; +} + +/** + * Called when Cancel button is clicked + */ +void +RecentFilesDialog::cancelButtonClicked() +{ + m_resultMode = ResultModeEnum::CANCEL; + + reject(); +} + +/** + * Called when Load button is clicked + */ +void +RecentFilesDialog::loadButtonClicked() +{ + RecentFileItem* item = m_recentFilesTableWidget->getSelectedItem(); + if (item == NULL) { + WuQMessageBox::warningOk(m_loadPushButton, "No item selected"); + return; + } + + QPoint centerPoint(m_loadPushButton->width() / 2, + m_loadPushButton->height() / 2); + QPoint point = m_loadPushButton->mapToGlobal(centerPoint); + const bool showMenuForSpecFileFlag(false); + loadSceneOrSpecFileFromItem(item, + point, + showMenuForSpecFileFlag); +} + +/** + * Called when Open button is clicked + */ +void +RecentFilesDialog::openButtonClicked() +{ + RecentFileItem* item = m_recentFilesTableWidget->getSelectedItem(); + if (item == NULL) { + WuQMessageBox::warningOk(m_openPushButton, "No item selected"); + return; + } + + if (item != NULL) { + switch (item->getFileItemType()) { + case RecentFileItemTypeEnum::DIRECTORY: + m_resultMode = ResultModeEnum::OPEN_DIRECTORY; + break; + case RecentFileItemTypeEnum::SCENE_FILE: + m_resultMode = ResultModeEnum::OPEN_FILE; + break; + case RecentFileItemTypeEnum::SPEC_FILE: + m_resultMode = ResultModeEnum::OPEN_FILE; + break; + } + + m_resultFilePathAndName = item->getPathAndFileName(); + } + + accept(); +} + +/** + * Called when Open Other button is clicked + */ +void +RecentFilesDialog::openOtherButtonClicked() +{ + m_resultMode = ResultModeEnum::OPEN_OTHER; + m_resultFilePathAndName = GuiManager::get()->getBrain()->getCurrentDirectory(); + + accept(); +} + +/** + * Called when an item is selected in the table widget + * @param item + * Item that was selected (may be NULL) + */ +void +RecentFilesDialog::tableWidgetItemClicked(RecentFileItem* item) +{ + m_openPushButton->setEnabled(item != NULL); + + bool loadValidFlag(false); + if (item != NULL) { + switch (item->getFileItemType()) { + case RecentFileItemTypeEnum::DIRECTORY: + break; + case RecentFileItemTypeEnum::SCENE_FILE: + loadValidFlag = true; + break; + case RecentFileItemTypeEnum::SPEC_FILE: + loadValidFlag = true; + break; + } + } + + m_loadPushButton->setEnabled(loadValidFlag); +} + +/** + * Called when an item is double-clicked in the table widget + * @param item + * Item that was selected (may be NULL) + */ +void +RecentFilesDialog::tableWidgetItemDoubleClicked(RecentFileItem* item) +{ + if (item != NULL) { + openButtonClicked(); + } +} + +/** + * Test reading/writing + */ +void +RecentFilesDialog::testButtonClicked() +{ + if (m_currentDirectoryItemsContainer != NULL) { + m_currentDirectoryItemsContainer->testXmlReadingAndWriting(); + } +} + +/** + * Load the given scene or spec file + * @param pathAndFileName + * Path and file name of file + * @param sceneIndex + * Index of scene if scene file + */ +void +RecentFilesDialog::loadSceneOrSpecFile(const AString& pathAndFileName, + const int32_t sceneIndex) +{ + bool validFlag; + const DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromFileExtension(pathAndFileName, &validFlag); + switch (dataFileType) { + case DataFileTypeEnum::SCENE: + m_resultMode = ResultModeEnum::LOAD_SCENE_FROM_SCENE_FILE; + m_resultFilePathAndName = pathAndFileName; + m_resultSceneIndex = sceneIndex + 1; /* command line indices start at 1 */ + accept(); + break; + case DataFileTypeEnum::SPECIFICATION: + m_resultMode = ResultModeEnum::LOAD_FILES_IN_SPEC_FILE; + m_resultFilePathAndName = pathAndFileName; + accept(); + break; + default: + CaretAssertMessage(0, ("File type not scene nor spec: " + + DataFileTypeEnum::toName(dataFileType))); + } +} + +/** + * For the given item load the spec file or pop-up menu with scenes for a scene file + * @param item + * Item from which to load files + * @param globalPostion + * Location of mouse + * @param showMenuForSpecFileFlag + * If true, pop-up a menu to confirm loading a spec file + */ +void +RecentFilesDialog::loadSceneOrSpecFileFromItem(RecentFileItem* item, + const QPoint& globalPosition, + const bool showMenuForSpecFileFlag) +{ + switch (item->getFileItemType()) { + case RecentFileItemTypeEnum::DIRECTORY: + break; + case RecentFileItemTypeEnum::SCENE_FILE: + { + /** + * List scenes and allow user to load a scene bypassing the scene dialog + */ + SceneFile sceneFile; + try { + sceneFile.readFile(item->getPathAndFileName()); + const int32_t numScenes = sceneFile.getNumberOfScenes(); + if (numScenes > 0) { + std::vector actions; + QMenu menu(this); + for (int32_t i = 0; i < numScenes; i++) { + actions.push_back(menu.addAction("Load " + + AString::number(i + 1) + + " " + + sceneFile.getSceneAtIndex(i)->getName())); + } + + QAction* selectedAction = menu.exec(globalPosition); + for (int32_t i = 0; i < numScenes; i++) { + CaretAssertVectorIndex(actions, i); + if (selectedAction == actions[i]) { + const Scene* scene = sceneFile.getSceneAtIndex(i); + CaretAssert(scene); + if (scene->hasFilesWithRemotePaths()) { + const QString msg("This scene contains files that are on the network. " + "If accessing the files requires a username and " + "password, enter it here. Otherwise, remove any " + "text from the username and password fields."); + + AString username; + AString password; + if (UsernamePasswordWidget::getUserNameAndPasswordInDialog(m_loadPushButton, + "Username and Password", + msg, + username, + password)) { + CaretDataFile::setFileReadingUsernameAndPassword(username, + password); + } + } + loadSceneOrSpecFile(item->getPathAndFileName(), i); + } + } + } + } + catch (const DataFileException& dfe) { + CaretLogWarning(dfe.whatString()); + } + } + break; + case RecentFileItemTypeEnum::SPEC_FILE: + { + /* + * Allow user to load all files in a spec file while bypassing the spec file dialogt + */ + if (showMenuForSpecFileFlag) { + QMenu menu(this); + QAction* action = menu.addAction("Load all files from spec file"); + QAction* selectedAction = menu.exec(globalPosition); + if (action == selectedAction) { + loadSceneOrSpecFile(item->getPathAndFileName(), 0); + } + } + else { + loadSceneOrSpecFile(item->getPathAndFileName(), 0); + } + } + break; + } +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/RecentFilesDialog.h connectome-workbench-1.5.0/src/GuiQt/RecentFilesDialog.h --- connectome-workbench-1.4.2/src/GuiQt/RecentFilesDialog.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/RecentFilesDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,190 @@ +#ifndef __RECENT_FILES_DIALOG_H__ +#define __RECENT_FILES_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QAction; +class QActionGroup; +class QCheckBox; +class QLineEdit; +class QPushButton; + +#include "RecentFileItemsContainerModeEnum.h" + +namespace caret { + + class RecentFileItem; + class RecentFileItemsContainer; + class RecentFilesTableWidget; + + class RecentFilesDialog : public QDialog { + + Q_OBJECT + + public: + enum class RunMode { + OPEN_RECENT, + SPLASH_SCREEN + }; + + enum class ResultModeEnum { + CANCEL, + LOAD_FILES_IN_SPEC_FILE, + LOAD_SCENE_FROM_SCENE_FILE, + OPEN_DIRECTORY, + OPEN_FILE, + OPEN_OTHER + }; + + static ResultModeEnum runDialog(const RunMode runMode, + AString& nameOut, + int32_t& sceneIndexOut, + QWidget* parent = 0); + + virtual ~RecentFilesDialog(); + + RecentFilesDialog(const RecentFilesDialog&) = delete; + + RecentFilesDialog& operator=(const RecentFilesDialog&) = delete; + + + // ADD_NEW_METHODS_HERE + + private slots: + void cancelButtonClicked(); + + void loadButtonClicked(); + + void openButtonClicked(); + + void openOtherButtonClicked(); + + void testButtonClicked(); + + void filesModeActionTriggered(QAction* action); + + void listDirectoriesCheckBoxClicked(bool checked); + + void listSceneFilesCheckBoxClicked(bool checked); + + void listSpecFilesCheckBoxClicked(bool checked); + + void showFilePathsCheckBoxClicked(bool checked); + + void nameFilterTextEdited(const QString& text); + + void hcpWebsiteButtonClicked(); + + void twitterButtonClicked(); + + void tableWidgetItemClicked(RecentFileItem* item); + + void tableWidgetItemDoubleClicked(RecentFileItem* item); + + void loadSceneOrSpecFileFromItem(RecentFileItem* item, + const QPoint& globalPosition, + const bool showMenuForSpecFileFlag); + + private slots: + void updateFilesTableContent(); + + protected: + virtual void keyPressEvent(QKeyEvent* event) override; + + private: + RecentFilesDialog(const RunMode runMode, + QWidget* parent = 0); + + ResultModeEnum getResultMode() const; + + AString getSelectedDirectoryOrFileName() const; + + int32_t getSelectedSceneIndex() const; + + QWidget* createDialogButtonsWidget(); + + QWidget* createFileTypesButtonWidget(); + + QWidget* createFilesFilteringWidget(); + + QWidget* createInternetButtonsWidget(); + + RecentFileItemsContainerModeEnum::Enum getSelectedFilesMode() const; + + QAction* getActionForMode(const RecentFileItemsContainerModeEnum::Enum recentFilesMode) const; + + void websiteLinkActivated(const QString& link); + + void updateFavoritesContainer(); + + void loadSceneOrSpecFile(const AString& pathAndFileName, + const int32_t sceneIndex); + + const RunMode m_runMode; + + QPushButton* m_loadPushButton; + + QPushButton* m_openPushButton; + + QActionGroup* m_fileTypeModeActionGroup; + + QCheckBox* m_listDirectoriesCheckBox; + + QCheckBox* m_listSceneFilesCheckBox; + + QCheckBox* m_listSpecFilesCheckBox; + + QLineEdit* m_nameFilterLineEdit; + + QCheckBox* m_showFilePathsCheckBox; + + ResultModeEnum m_resultMode = ResultModeEnum::CANCEL; + + AString m_resultFilePathAndName; + + int32_t m_resultSceneIndex = -1; + + RecentFilesTableWidget* m_recentFilesTableWidget; + + std::unique_ptr m_currentDirectoryItemsContainer; + + std::unique_ptr m_recentFilesItemsContainer; + + std::unique_ptr m_recentDirectoryItemsContainer; + + std::unique_ptr m_favoriteItemsContainer; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __RECENT_FILES_DIALOG_DECLARE__ + // +#endif // __RECENT_FILES_DIALOG_DECLARE__ + +} // namespace +#endif //__RECENT_FILES_DIALOG_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/RecentFilesTableWidget.cxx connectome-workbench-1.5.0/src/GuiQt/RecentFilesTableWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/RecentFilesTableWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/RecentFilesTableWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,960 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __RECENT_FILES_TABLE_WIDGET_DECLARE__ +#include "RecentFilesTableWidget.h" +#undef __RECENT_FILES_TABLE_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "RecentFileItem.h" +#include "RecentFileItemsContainer.h" +#include "RecentFileItemsFilter.h" +#include "WuQImageLabel.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::RecentFilesTableWidget + * \brief Widget containing recent file and directory items + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +RecentFilesTableWidget::RecentFilesTableWidget() +: QTableWidget() +{ + setAlternatingRowColors(true); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setSelectionMode(QAbstractItemView::SingleSelection); + + QHeaderView* horizHeader = horizontalHeader(); + horizHeader->setSortIndicatorShown(true); + QObject::connect(horizHeader, &QHeaderView::sortIndicatorChanged, + this, &RecentFilesTableWidget::sortIndicatorClicked); + + QHeaderView* vertHeader = verticalHeader(); + vertHeader->setVisible(false); + + QObject::connect(this, &QTableWidget::cellClicked, + this, &RecentFilesTableWidget::tableCellClicked); + QObject::connect(this, &QTableWidget::cellDoubleClicked, + this, &RecentFilesTableWidget::tableCellDoubleClicked); + + m_favoriteFilledIcon = loadIcon(":/RecentFilesDialog/favorite_filled.png"); + m_favoriteOutlineIcon = loadIcon(":/RecentFilesDialog/favorite_outline.png"); + m_forgetIcon = loadIcon(":/RecentFilesDialog/forget_black.png"); + m_forgetOnIcon = loadIcon(":/RecentFilesDialog/forget_red.png"); + m_shareIcon = loadIcon(":/RecentFilesDialog/share.png"); + + setContextMenuPolicy(Qt::DefaultContextMenu); + + QPalette myPalette = QGuiApplication::palette(); + m_labelHighlightTextColor = myPalette.color(QPalette::Active, QPalette::HighlightedText); +#ifdef CARET_OS_MACOSX + /* + * MacOS does not use palette but instead uses "native theme engine" + * Search for "theme" on Qt help page for QPalette for more info. + */ + m_labelHighlightTextColor = QColor(255, 255, 255); +#endif // CARET_OS_MACOSX +} + +/** + * Destructor. + */ +RecentFilesTableWidget::~RecentFilesTableWidget() +{ +} + +/** + * @return The selected item or NULL if no item is selected + */ +RecentFileItem* +RecentFilesTableWidget::getSelectedItem() +{ + const int32_t row = currentRow(); + if ((row >= 0) + && (row < rowCount())) { + CaretAssertVectorIndex(m_recentItems, row); + return m_recentItems[row]; + } + + return NULL; +} + +/** + * @return The size hint adjusted for width of recent files table + */ +QSize +RecentFilesTableWidget::sizeHint() const +{ + QSize sz = QTableWidget::sizeHint(); + + int32_t x(0); + for (int32_t i = 0; i < columnCount(); i++) { + x += columnWidth(i); + } + sz.setWidth(x); + + return sz; +} + +/** + * Update number of rows in table + * @param numberOfItems + * Number of files for table + */ +void +RecentFilesTableWidget::updateTableDimensions(const int32_t numberOfItems) +{ + const int32_t numExistingRows = rowCount(); + + if (numberOfItems != numExistingRows) { + /* + * Resize table to match number of files and columns + */ + setRowCount(numberOfItems); + setColumnCount(COLUMN_COUNT); + + if (numExistingRows > numberOfItems) { + /* + * Remove extra items + */ + m_recentItems.resize(numberOfItems); + } + } + + /* + * If needed, add additional rows + */ + for (int32_t iRow = numExistingRows; iRow < numberOfItems; iRow++) { + for (int32_t iCol = 0; iCol < COLUMN_COUNT; iCol++) { + QWidget* widget(NULL); + QTableWidgetItem* tableItem(NULL); + const COLUMNS column = static_cast(iCol); + bool selectableFlag(false); + AString toolTipText; + + /* + * Note: Using QLabel's in a QTableWidget is allegedly slower than using QTableWidgetItem's. + * However, if a QTableWidgetItem contains just a icon (image), it was left aligned and + * could not get it to align in the center. Another issue is that QTableWidgetItem's only + * support plain text (not rich text, html). HTML should be possible if a QStyledItemDelegate + * is used. It the delegate, a QTextDocument would be used to render and size the HTML. + */ + switch (column) { + case COLUMN_COUNT: + CaretAssert(0); + break; + case COLUMN_NAME: + { + WuQImageLabel* label = new WuQImageLabel(); + label->setAlignment(Qt::AlignLeft); + label->setTextFormat(Qt::RichText); + widget = label; + + QObject::connect(label, &WuQImageLabel::clicked, + [=] { tableCellClicked(iRow, COLUMN_NAME); }); + QObject::connect(label, &WuQImageLabel::doubleClicked, + [=] { tableCellDoubleClicked(iRow, COLUMN_NAME); }); + } + break; + case COLUMN_DATE_TIME: + tableItem = new QTableWidgetItem(); + toolTipText = "Date last accessed by wb_view"; + selectableFlag = true; + break; + case COLUMN_MODIFIED: + tableItem = new QTableWidgetItem(); + toolTipText = "Date last modified"; + selectableFlag = true; + break; + case COLUMN_FAVORITE: + { + WuQImageLabel* label = new WuQImageLabel(); + label->setAlignment(Qt::AlignCenter); + QObject::connect(label, &WuQImageLabel::clicked, + [=] { if (label->isEnabled()) tableCellClicked(iRow, COLUMN_FAVORITE); }); + QObject::connect(label, &WuQImageLabel::doubleClicked, + [=] { if (label->isEnabled()) tableCellDoubleClicked(iRow, COLUMN_FAVORITE); }); + toolTipText = "Add/Remove this row as a favorite"; + widget = label; + } + break; + case COLUMN_SHARE: + { + WuQImageLabel* label = new WuQImageLabel(); + label->setAlignment(Qt::AlignCenter); + if (m_shareIcon) { + label->setPixmap(m_shareIcon->pixmap(s_pixmapSizeXY - 4, s_pixmapSizeXY - 4)); + } + else { + label->setText("share"); + } + QObject::connect(label, &WuQImageLabel::clicked, + [=] { if (label->isEnabled()) tableCellClicked(iRow, COLUMN_SHARE); }); + QObject::connect(label, &WuQImageLabel::doubleClicked, + [=] { if (label->isEnabled()) tableCellDoubleClicked(iRow, COLUMN_SHARE); }); + toolTipText = "Share path to this directory/file"; + widget = label; + } + break; + case COLUMN_FORGET: + { + WuQImageLabel* label = new WuQImageLabel(); + label->setAlignment(Qt::AlignCenter); + QObject::connect(label, &WuQImageLabel::clicked, + [=] { if (label->isEnabled()) tableCellClicked(iRow, COLUMN_FORGET); }); + QObject::connect(label, &WuQImageLabel::doubleClicked, + [=] { if (label->isEnabled()) tableCellDoubleClicked(iRow, COLUMN_FORGET); }); + toolTipText = ("Forget this row (removed from recent directory/file history)\n" + "Actual directory/file is NOT deleted"); + widget = label; + } + case COLUMN_EMPTY_STRETCH: + /* nothing, intentionally empty to push other columns to the left */ + break; + } + + if (tableItem != NULL) { + Qt::ItemFlags flags(Qt::ItemIsEnabled); + if (selectableFlag) { + flags.setFlag(Qt::ItemIsSelectable, true); + } + tableItem->setFlags(flags); + tableItem->setData(Qt::UserRole, iRow); + if ( ! toolTipText.isEmpty()) { + tableItem->setToolTip(toolTipText); + } + setItem(iRow, iCol, tableItem); + } + else if (widget != NULL) { + if ( ! toolTipText.isEmpty()) { + widget->setToolTip(toolTipText); + } + setCellWidget(iRow, iCol, widget); + } + else if (column != COLUMN_EMPTY_STRETCH) { + CaretAssert(0); + } + } + } +} + +/** + * @return Name for column index + * @param columnIndex + * Index of column + */ +AString +RecentFilesTableWidget::getColumnName(const int32_t columnIndex) const +{ + AString name; + + const COLUMNS column = static_cast(columnIndex); + switch (column) { + case COLUMN_COUNT: + name = "PROGRAM ERROR"; + CaretAssert(0); + break; + case COLUMN_NAME: + name = "Name"; + break; + case COLUMN_DATE_TIME: + name = "Last Accessed by wb_view"; + break; + case COLUMN_MODIFIED: + name = "Last Modified"; + break; + case COLUMN_FAVORITE: + name = "Favorite"; + break; + case COLUMN_SHARE: + name = "Share"; + break; + case COLUMN_FORGET: + name = "Forget"; + break; + case COLUMN_EMPTY_STRETCH: + name = ""; + break; + } + + return name; +} + + +/** + * Update the content with recent file items container + * @param recentFileItemsContainer + * The container + * @param itemsFilter + * Filter for requesting items from the container + */ +void +RecentFilesTableWidget::updateContent(RecentFileItemsContainer* recentFileItemsContainer, + const RecentFileItemsFilter& itemsFilter) +{ + + RecentFileItem* previousSelectedItem = getSelectedItem(); + m_recentItems.clear(); + clearSelectedItem(); + + if (recentFileItemsContainer != m_recentFileItemsContainer) { + previousSelectedItem = NULL; + } + + m_recentFileItemsFilter = itemsFilter; + + m_recentFileItemsContainer = recentFileItemsContainer; + if (m_recentFileItemsContainer != NULL) { + m_recentItems = m_recentFileItemsContainer->getItems(itemsFilter); + sortRecentItems(); + } + + const int32_t numberOfRecentItems = static_cast(m_recentItems.size()); + + updateTableDimensions(numberOfRecentItems); + + CaretAssert(rowCount() == numberOfRecentItems); + + int32_t selectedRowIndex = updateAllRows(previousSelectedItem); + + /* + * First time files inserted? + */ + if (m_lastNumberRecentItems == 0) { + if (numberOfRecentItems > 0) { + QStringList columnNames; + for (int32_t i = 0; i < COLUMN_COUNT; i++) { + columnNames << getColumnName(i); + } + columnNames << ""; + setHorizontalHeaderLabels(columnNames); + + resizeRowsToContents(); + + /* + * Set columns so they resize to first their contents + * except the stretch column that stretches to fill + * any extra space + */ + for (int32_t i = 0; i < COLUMN_COUNT; i++) { + QHeaderView::ResizeMode mode = QHeaderView::ResizeToContents; + const COLUMNS column = static_cast(i); + switch (column) { + case COLUMN_NAME: + break; + case COLUMN_DATE_TIME: + break; + case COLUMN_MODIFIED: + break; + case COLUMN_FAVORITE: + break; + case COLUMN_SHARE: + break; + case COLUMN_FORGET: + break; + case COLUMN_EMPTY_STRETCH: + mode = QHeaderView::Stretch; + break; + case COLUMN_COUNT: + break; + } + horizontalHeader()->setSectionResizeMode(i, mode); + } + } + } + + m_lastNumberRecentItems = numberOfRecentItems; + + if (selectedRowIndex < 0) { + if (numberOfRecentItems > 0) { + selectedRowIndex = 0; + } + } + + update(); + tableCellClicked(selectedRowIndex, COLUMN_NAME); + resizeRowsToContents(); + update(); + updateHeaderSortingKey(); +} + +/** + * Update all rows in the table and return the row index of the given recent item in the table + * @param recentItem + * Pointer to recent for which row is returned (may be NULL) + * @return + * Index of the recentItem or -1 if not found + */ +int32_t +RecentFilesTableWidget::updateAllRows(RecentFileItem* recentItem) +{ + const int32_t numberOfRecentItems = static_cast(m_recentItems.size()); + CaretAssert(rowCount() == numberOfRecentItems); + + int32_t recentItemIndex(-1); + for (int32_t iRow = 0; iRow < numberOfRecentItems; iRow++) { + updateRow(iRow); + CaretAssertVectorIndex(m_recentItems, iRow); + if (m_recentItems[iRow] == recentItem) { + recentItemIndex = iRow; + } + } + + return recentItemIndex; +} + +/** + * Update the content of a row + * @param rowIndex + * Row index + */ +void +RecentFilesTableWidget::updateRow(const int32_t rowIndex) +{ + CaretAssertVectorIndex(m_recentItems, rowIndex); + RecentFileItem* recentItem = m_recentItems[rowIndex]; + CaretAssert(recentItem); + + bool enableFavoriteFlag(false); + bool enableForgetFlag(false); + bool hidePathsFlag(false); + switch (m_recentFileItemsContainer->getMode()) { + case RecentFileItemsContainerModeEnum::DIRECTORY_SCENE_AND_SPEC_FILES: + hidePathsFlag = true; + break; + case RecentFileItemsContainerModeEnum::FAVORITES: + enableFavoriteFlag = true; + break; + case RecentFileItemsContainerModeEnum::OTHER: + break; + case RecentFileItemsContainerModeEnum::RECENT_DIRECTORIES: + enableFavoriteFlag = true; + enableForgetFlag = true; + break; + case RecentFileItemsContainerModeEnum::RECENT_FILES: + enableFavoriteFlag = true; + enableForgetFlag = true; + break; + } + + switch (recentItem->getFileItemType()) { + case RecentFileItemTypeEnum::DIRECTORY: + break; + case RecentFileItemTypeEnum::SCENE_FILE: + break; + case RecentFileItemTypeEnum::SPEC_FILE: + break; + } + + const bool selectedFlag(rowIndex == currentRow()); + + for (int32_t iCol = 0; iCol < COLUMN_COUNT; iCol++) { + const COLUMNS column = static_cast(iCol); + + QTableWidgetItem* tableItem(item(rowIndex, iCol)); + QWidget* widget(cellWidget(rowIndex, iCol)); + switch (column) { + case COLUMN_COUNT: + CaretAssert(0); + break; + case COLUMN_NAME: + { + AString notFoundText; + if (recentItem->isNotFound()) { + notFoundText = (" (not found)"); + } + + CaretAssert(widget); + QLabel* label = qobject_cast(widget); + CaretAssert(label); + AString text(" %2%3
     %4"); + QString pathName(recentItem->getPathName()); + switch (recentItem->getFileItemType()) { + case RecentFileItemTypeEnum::DIRECTORY: + break; + case RecentFileItemTypeEnum::SCENE_FILE: + case RecentFileItemTypeEnum::SPEC_FILE: + if ( ! m_recentFileItemsFilter.isShowFilePaths()) { + pathName = ""; + } + else if (hidePathsFlag) { + pathName = ""; + } + break; + } + + QString colorText; + if (selectedFlag) { + /* + * Note QColor::name() returns color in 3 two-digit hex numbers, #RRGGBB + * Qt supports only a color name or the hex format + */ + colorText = QString(" color=\"%1\"").arg(m_labelHighlightTextColor.name()); + + if ( ! pathName.isEmpty()) { + pathName = QString("%2").arg(m_labelHighlightTextColor.name()).arg(pathName); + } + } + label->setText(text.arg(colorText).arg(recentItem->getFileName()).arg(notFoundText).arg(pathName)); + } + break; + case COLUMN_DATE_TIME: + { + tableItem->setText(recentItem->getLastAccessByWorkbenchDateTimeAsString()); + } + break; + case COLUMN_MODIFIED: + tableItem->setText(recentItem->getLastModifiedDateTimeAsString()); + break; + case COLUMN_FAVORITE: + { + CaretAssert(widget); + QLabel* label = qobject_cast(widget); + CaretAssert(label); + + label->setText(""); + if (enableFavoriteFlag) { + if (recentItem->isFavorite()) { + if (m_favoriteFilledIcon) { + label->setPixmap(m_favoriteFilledIcon->pixmap(s_pixmapSizeXY, s_pixmapSizeXY)); + } + else { + label->setText("Yes"); + } + } + else { + if (m_favoriteOutlineIcon) { + label->setPixmap(m_favoriteOutlineIcon->pixmap(s_pixmapSizeXY, s_pixmapSizeXY)); + } + else { + label->setText("No"); + } + } + } + else { + label->setPixmap(QPixmap()); + } + label->setEnabled(enableFavoriteFlag); + } + break; + case COLUMN_SHARE: + CaretAssert(widget); + break; + case COLUMN_FORGET: + { + CaretAssert(widget); + QLabel* label = qobject_cast(widget); + CaretAssert(label); + + /* + * Note: Same icon is used for forget on/off but may change + */ + label->setText(""); + if (enableForgetFlag) { + if (recentItem->isForget()) { + if (m_forgetOnIcon) { + label->setPixmap(m_forgetOnIcon->pixmap(s_pixmapSizeXY, s_pixmapSizeXY)); + } + else { + label->setText("X"); + } + } + else { + if (m_forgetIcon) { + label->setPixmap(m_forgetIcon->pixmap(s_pixmapSizeXY, s_pixmapSizeXY)); + } + else { + label->setText("X"); + } + } + } + else { + label->setPixmap(QPixmap()); + } + label->setEnabled(enableForgetFlag); + } + break; + case COLUMN_EMPTY_STRETCH: + /* nothing */ + break; + } + } +} + + +/** + * Called when a table item is clicked + * @param row + * Row clicked + * @param column + * Column clicked + */ +void +RecentFilesTableWidget::tableCellClicked(int row, int column) +{ + /* + * Some cells have items empty (no label or icon) when + * column is not valid for a file type + */ + QWidget* w = cellWidget(row, column); + if (w != NULL) { + if ( ! w->isEnabled()) { + return; + } + } + + int32_t rowToSelect(-1); + if ((row >= 0) + && (row < rowCount())) { + const COLUMNS tableColumn = static_cast(column); + CaretAssertVectorIndex(m_recentItems, row); + RecentFileItem* recentItem = m_recentItems[row]; + + rowToSelect = row; + + switch (tableColumn) { + case COLUMN_COUNT: + break; + case COLUMN_FAVORITE: + recentItem->setFavorite( ! recentItem->isFavorite()); + break; + case COLUMN_FORGET: + recentItem->setForget( ! recentItem->isForget()); + break; + case COLUMN_NAME: + break; + case COLUMN_DATE_TIME: + break; + case COLUMN_MODIFIED: + break; + case COLUMN_SHARE: + showShareMenuForRow(row); + break; + case COLUMN_EMPTY_STRETCH: + break; + } + } + /* + * Select both the NAME and DATE/TIME columns in the row + */ + QSignalBlocker blocker(this); + clearSelectedItem(); + + if (rowToSelect >= 0) { + /* + * Must set focus or else the selected item will not be highlighted + * if the table does not have focus + */ + setFocus(); + QItemSelectionModel::SelectionFlags flags; + flags.setFlag(QItemSelectionModel::Select, true); + flags.setFlag(QItemSelectionModel::Rows, true); + setCurrentCell(rowToSelect, COLUMN_NAME, flags); + updateAllRows(NULL); + } + + blocker.unblock(); + + emit selectedItemChanged(getSelectedItem()); +} + +/** + * Clear the selected item + */ +void +RecentFilesTableWidget::clearSelectedItem() +{ + clearSelection(); + setCurrentItem(NULL); +} + + +/** + * Called when a table item is double-clicked + * @param row + * Row clicked + * @param column + * Column clicked + */ +void +RecentFilesTableWidget::tableCellDoubleClicked(int row, int column) +{ + /* + * Some cells have items empty (no label or icon) when + * column is not valid for a file type + */ + QWidget* w = cellWidget(row, column); + if (w != NULL) { + if ( ! w->isEnabled()) { + return; + } + } + + tableCellClicked(row, column); + RecentFileItem* selectedItem(getSelectedItem()); + if (selectedItem != NULL) { + const COLUMNS tableColumn = static_cast(column); + + bool emitFlag(false); + switch (tableColumn) { + case COLUMN_DATE_TIME: + emitFlag = true; + break; + case COLUMN_MODIFIED: + emitFlag = true; + break; + case COLUMN_FAVORITE: + break; + case COLUMN_FORGET: + break; + case COLUMN_NAME: + emitFlag = true; + break; + case COLUMN_SHARE: + break; + case COLUMN_EMPTY_STRETCH: + break; + case COLUMN_COUNT: + break; + } + + if (emitFlag) { + emit selectedItemDoubleClicked(selectedItem); + } + } +} + +/** + * @return Pointer to icon read from the given file name (NULL) if failure to load icon file + * @param iconFileName + * Name of file containing icon + */ +std::unique_ptr +RecentFilesTableWidget::loadIcon(const AString& iconFileName) const +{ + std::unique_ptr iconOut; + + QIcon icon; + if (WuQtUtilities::loadIcon(iconFileName, + icon)) { + iconOut.reset(new QIcon(icon)); + } + + return iconOut; +} + +/* + * Called when a column header sort indicator is clicked by user + * @param logicalIndex + * Index of clicked header + * @param sortOrder + * Order for sorting + */ +void +RecentFilesTableWidget::sortIndicatorClicked(int logicalIndex, + Qt::SortOrder sortOrder) +{ + if (m_recentFileItemsContainer != NULL) { + bool updateFlag(false); + const COLUMNS column = static_cast(logicalIndex); + switch (column) { + case COLUMN_NAME: + switch (sortOrder) { + case Qt::AscendingOrder: + m_sortingKey = RecentFileItemSortingKeyEnum::NAME_ASCENDING; + break; + case Qt::DescendingOrder: + m_sortingKey = RecentFileItemSortingKeyEnum::NAME_DESCENDING; + break; + } + updateFlag = true; + break; + case COLUMN_DATE_TIME: + switch (sortOrder) { + case Qt::AscendingOrder: + m_sortingKey = RecentFileItemSortingKeyEnum::DATE_OLDEST; + break; + case Qt::DescendingOrder: + m_sortingKey = RecentFileItemSortingKeyEnum::DATE_NEWEST; + break; + } + updateFlag = true; + break; + case COLUMN_MODIFIED: + switch (sortOrder) { + case Qt::AscendingOrder: + m_sortingKey = RecentFileItemSortingKeyEnum::MODIFIED_OLDEST; + break; + case Qt::DescendingOrder: + m_sortingKey = RecentFileItemSortingKeyEnum::MODIFIED_NEWEST; + break; + } + updateFlag = true; + break; + case COLUMN_COUNT: + case COLUMN_FAVORITE: + case COLUMN_FORGET: + case COLUMN_SHARE: + case COLUMN_EMPTY_STRETCH: + break; + } + + if (updateFlag) { + clearSelectedItem(); + updateContent(m_recentFileItemsContainer, + m_recentFileItemsFilter); + } + } +} + +/** + * Sort the items + */ +void +RecentFilesTableWidget::sortRecentItems() +{ + QHeaderView* horizHeader = horizontalHeader(); + CaretAssert(horizHeader); + QSignalBlocker headerBlocker(horizHeader); + + RecentFileItemsContainer::sort(m_sortingKey, + m_recentItems); +} + +/** + * Updates the table header's sorting key (up or down arrow indicating column sorted + */ +void +RecentFilesTableWidget::updateHeaderSortingKey() +{ + QHeaderView* horizHeader = horizontalHeader(); + horizHeader->setSortIndicatorShown(true); + switch (m_sortingKey) { + case RecentFileItemSortingKeyEnum::DATE_NEWEST: + horizHeader->setSortIndicator(COLUMN_DATE_TIME, Qt::DescendingOrder); + break; + case RecentFileItemSortingKeyEnum::DATE_OLDEST: + horizHeader->setSortIndicator(COLUMN_DATE_TIME, Qt::AscendingOrder); + break; + case RecentFileItemSortingKeyEnum::MODIFIED_NEWEST: + horizHeader->setSortIndicator(COLUMN_MODIFIED, Qt::DescendingOrder); + break; + case RecentFileItemSortingKeyEnum::MODIFIED_OLDEST: + horizHeader->setSortIndicator(COLUMN_MODIFIED, Qt::AscendingOrder); + break; + case RecentFileItemSortingKeyEnum::NAME_ASCENDING: + horizHeader->setSortIndicator(COLUMN_NAME, Qt::AscendingOrder); + break; + case RecentFileItemSortingKeyEnum::NAME_DESCENDING: + horizHeader->setSortIndicator(COLUMN_NAME, Qt::DescendingOrder); + break; + } +} + +/** + * Show the share menu for the give row index + * @param rowIndex + * Index of row + */ +void +RecentFilesTableWidget::showShareMenuForRow(const int32_t rowIndex) +{ + CaretAssertVectorIndex(m_recentItems, rowIndex); + + QMenu menu(this); + QAction* copyToClipboardAction = menu.addAction("Copy Pathname to Clipboard"); + QAction* shareViaEmailAction = menu.addAction("Email Pathname..."); + + const AString pathName(m_recentItems[rowIndex]->getPathAndFileName()); + + QPoint globalPos = QCursor::pos(); + QAction* selectedAction = menu.exec(globalPos); + if (selectedAction == copyToClipboardAction) { + QClipboard* clipboard = QGuiApplication::clipboard(); + clipboard->setText(pathName); + } + else if (selectedAction == shareViaEmailAction) { + AString subject; + switch (m_recentItems[rowIndex]->getFileItemType()) { + case RecentFileItemTypeEnum::DIRECTORY: + subject = "Directory"; + break; + case RecentFileItemTypeEnum::SCENE_FILE: + subject = "Scene File"; + break; + case RecentFileItemTypeEnum::SPEC_FILE: + subject = "Spec File"; + break; + } + const QString mailArg("mailto:?&subject=" + subject + + "&body=" + pathName); + QDesktopServices::openUrl(QUrl(mailArg, + QUrl::TolerantMode)); + } +} + +/** + * Event handler for custom context menu display + * @param event + * The context menu event + */ +void +RecentFilesTableWidget::contextMenuEvent(QContextMenuEvent *event) +{ + const int32_t row = rowAt(event->y()); + const int32_t column = columnAt(event->x()); + + if ((row >= 0) + && (row < rowCount())) { + if (column == COLUMN_NAME) { + CaretAssertVectorIndex(m_recentItems, row); + RecentFileItem* item = m_recentItems[row]; + + const bool showMenuForSpecFileFlag(true); + emit loadSceneOrSpecFileFromItem(item, + event->globalPos(), + showMenuForSpecFileFlag); + } + } +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/RecentFilesTableWidget.h connectome-workbench-1.5.0/src/GuiQt/RecentFilesTableWidget.h --- connectome-workbench-1.4.2/src/GuiQt/RecentFilesTableWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/RecentFilesTableWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,156 @@ +#ifndef __RECENT_FILES_TABLE_WIDGET_H__ +#define __RECENT_FILES_TABLE_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include + +#include "AString.h" +#include "RecentFileItemSortingKeyEnum.h" +#include "RecentFileItemsFilter.h" + +class QLabel; +class QTableWidgetItem; + +namespace caret { + class CaretMappableDataFile; + class RecentFileItem; + class RecentFileItemsContainer; + + class RecentFilesTableWidget : public QTableWidget { + + Q_OBJECT + + public: + RecentFilesTableWidget(); + + virtual ~RecentFilesTableWidget(); + + RecentFilesTableWidget(const RecentFilesTableWidget&) = delete; + + RecentFilesTableWidget& operator=(const RecentFilesTableWidget&) = delete; + + void updateContent(RecentFileItemsContainer* recentFileItemsContainer, + const RecentFileItemsFilter& itemsFilter); + + RecentFileItem* getSelectedItem(); + + virtual QSize sizeHint() const override; + + // ADD_NEW_METHODS_HERE + + signals: + void loadSceneOrSpecFileFromItem(RecentFileItem* item, + const QPoint& globalPosition, + const bool showMenuForSpecFileFlag); + + void sortingChanged(); + + void selectedItemChanged(RecentFileItem* item); + + void selectedItemDoubleClicked(RecentFileItem* item); + + private slots: + void tableCellClicked(int row, int column); + + void tableCellDoubleClicked(int row, int column); + + void sortIndicatorClicked(int locicalIndex, + Qt::SortOrder sortOrder); + + protected: + virtual void contextMenuEvent(QContextMenuEvent *event) override; + + private: + /* + * The empty stretch column fills any empty space on the right side + * of the dialog. Without it, we can stretch the FORGET column + * but items are centered and it look weird. + */ + enum COLUMNS { + COLUMN_NAME = 0, + COLUMN_DATE_TIME = 1, + COLUMN_MODIFIED = 2, + COLUMN_FAVORITE = 3, + COLUMN_SHARE = 4, + COLUMN_FORGET = 5, + COLUMN_EMPTY_STRETCH = 6, + COLUMN_COUNT = 7 + }; + + AString getColumnName(const int32_t) const; + + void updateTableDimensions(const int32_t numberOfItems); + + int32_t updateAllRows(RecentFileItem* recentItem); + + void updateRow(const int32_t rowIndex); + + std::unique_ptr loadIcon(const AString& iconFileName) const; + + void clearSelectedItem(); + + void sortRecentItems(); + + void updateHeaderSortingKey(); + + void showShareMenuForRow(const int32_t rowIndex); + + int32_t m_lastNumberRecentItems = 0; + + RecentFileItemsContainer* m_recentFileItemsContainer = NULL; + + RecentFileItemsFilter m_recentFileItemsFilter; + + std::vector m_recentItems; + + std::vector m_mapFiles; + + std::unique_ptr m_favoriteFilledIcon; + + std::unique_ptr m_favoriteOutlineIcon; + + std::unique_ptr m_shareIcon; + + std::unique_ptr m_forgetIcon; + + std::unique_ptr m_forgetOnIcon; + + RecentFileItemSortingKeyEnum::Enum m_sortingKey = RecentFileItemSortingKeyEnum::DATE_NEWEST; + + QColor m_labelHighlightTextColor; + + static const int32_t s_pixmapSizeXY; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __RECENT_FILES_TABLE_WIDGET_DECLARE__ + + const int32_t RecentFilesTableWidget::s_pixmapSizeXY = 24; + +#endif // __RECENT_FILES_TABLE_WIDGET_DECLARE__ + +} // namespace +#endif //__RECENT_FILES_TABLE_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ScaleBarWidget.cxx connectome-workbench-1.5.0/src/GuiQt/ScaleBarWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/ScaleBarWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ScaleBarWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,649 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCALE_BAR_WIDGET_DECLARE__ +#include "ScaleBarWidget.h" +#undef __SCALE_BAR_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AnnotationScaleBar.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretColorEnumComboBox.h" +#include "EnumComboBoxTemplate.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "WuQDoubleSpinBox.h" +#include "WuQMacroManager.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::ScaleBarWidget + * \brief Widget for editing a scale bar + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +ScaleBarWidget::ScaleBarWidget(const QString& objectNamePrefix) +: QWidget() +{ + m_objectNamePrefix = (objectNamePrefix + + ":ScaleBarWidget"); + setObjectName(m_objectNamePrefix); + + QWidget* barWidget = createBarWidget(); + QWidget* lengthTextWidget = createLengthTextWidget(); + QWidget* positionWidget = createPositionWidget(); + QWidget* ticksWidget = createTickMarksWidget(); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(barWidget); + layout->addWidget(lengthTextWidget); + layout->addWidget(ticksWidget); + layout->addWidget(positionWidget); +} + +/** + * Destructor. + */ +ScaleBarWidget::~ScaleBarWidget() +{ +} + + +/** + * @return Instance of the length widget + */ +QWidget* +ScaleBarWidget::createBarWidget() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + + QLabel* lengthLabel = new QLabel("Length"); + m_lengthSpinBox = new QDoubleSpinBox(); + m_lengthSpinBox->setToolTip("Set the length of the scale bar in the selected units"); + m_lengthSpinBox->setRange(0.0, 100000.0); + QObject::connect(m_lengthSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &ScaleBarWidget::lengthSpinBoxValueChanged); + m_lengthSpinBox->setObjectName(m_objectNamePrefix + + ":m_lengthSpinBox"); + macroManager->addMacroSupportToObject(m_lengthSpinBox, + "Set Length"); + + QLabel* thicknessLabel = new QLabel("Thickness"); + m_thicknessSpinBox = new QDoubleSpinBox(); + m_thicknessSpinBox->setToolTip("Set the thickness of the scale bar as percentage of viewport height"); + m_thicknessSpinBox->setRange(0.1, 100.0); + m_thicknessSpinBox->setSingleStep(0.1); + m_thicknessSpinBox->setDecimals(1); + m_thicknessSpinBox->setSuffix("%"); + QObject::connect(m_thicknessSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &ScaleBarWidget::thicknessSpinBoxValueChanged); + m_thicknessSpinBox->setObjectName(m_objectNamePrefix + + ":m_thicknessSpinBox"); + macroManager->addMacroSupportToObject(m_thicknessSpinBox, + "Set Length"); + + + QLabel* unitsLabel = new QLabel("Length Units"); + m_lengthUnitsComboBox = new EnumComboBoxTemplate(this); + m_lengthUnitsComboBox->getComboBox()->setToolTip("Select the units for the length of the scale bar"); + m_lengthUnitsComboBox->setup(); + QObject::connect(m_lengthUnitsComboBox, &EnumComboBoxTemplate::itemActivated, + this, &ScaleBarWidget::lengthEnumComboBoxItemActivated); + m_lengthUnitsComboBox->getWidget()->setObjectName(m_objectNamePrefix + + ":m_lengthUnitsComboBox"); + macroManager->addMacroSupportToObject(m_lengthUnitsComboBox->getComboBox(), + "Select Length Units"); + + QLabel* foregroundLabel = new QLabel("Bar Color"); + m_foregroundColorComboBox = new CaretColorEnumComboBox(CaretColorEnumComboBox::CustomColorModeEnum::EDITABLE, + CaretColorEnumComboBox::NoneColorModeEnum::DISABLED, + this); + m_foregroundColorComboBox->setToolTip("Set the color of the scale bar"); + QObject::connect(m_foregroundColorComboBox, &CaretColorEnumComboBox::colorSelected, + this, &ScaleBarWidget::foregroundColorComboBoxSelected); + + QLabel* backgroundLabel = new QLabel("Background"); + m_backgroundColorComboBox = new CaretColorEnumComboBox(CaretColorEnumComboBox::CustomColorModeEnum::EDITABLE, + CaretColorEnumComboBox::NoneColorModeEnum::ENABLED, + this); + m_backgroundColorComboBox->setToolTip("Set the background of the scale bar to a color or NONE (no background)"); + QObject::connect(m_backgroundColorComboBox, &CaretColorEnumComboBox::colorSelected, + this, &ScaleBarWidget::backgroundColorComboBoxSelected); + + QGroupBox* groupBox = new QGroupBox("Scale Bar"); + QGridLayout* gridLayout = new QGridLayout(groupBox); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); + int32_t row(0); + gridLayout->addWidget(lengthLabel, row, 0); + gridLayout->addWidget(m_lengthSpinBox, row, 1); + row++; + gridLayout->addWidget(unitsLabel, row, 0); + gridLayout->addWidget(m_lengthUnitsComboBox->getWidget(), row, 1); + row++; + gridLayout->addWidget(thicknessLabel, row, 0); + gridLayout->addWidget(m_thicknessSpinBox, row, 1); + row++; + gridLayout->addWidget(foregroundLabel, row, 0); + gridLayout->addWidget(m_foregroundColorComboBox->getWidget(), row, 1); + row++; + gridLayout->addWidget(backgroundLabel, row, 0); + gridLayout->addWidget(m_backgroundColorComboBox->getWidget(), row, 1); + row++; + + return groupBox; +} + +/** + * @return Instance of the length widget + */ +QWidget* +ScaleBarWidget::createLengthTextWidget() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + + m_showLengthTextCheckBox = new QCheckBox("Show Length"); + m_showLengthTextCheckBox->setToolTip("Show the length of the scale bar"); + QObject::connect(m_showLengthTextCheckBox, &QCheckBox::clicked, + this, &ScaleBarWidget::showLengthCheckBoxClicked); + m_showLengthTextCheckBox->setObjectName(m_objectNamePrefix + + ":m_showLengthTextCheckBox"); + macroManager->addMacroSupportToObject(m_showLengthTextCheckBox, + "Show Length"); + + m_showLengthUnitsCheckbox = new QCheckBox("Show Length Units"); + m_showLengthUnitsCheckbox->setToolTip("Show the length units after the length"); + QObject::connect(m_showLengthUnitsCheckbox, &QCheckBox::clicked, + this, &ScaleBarWidget::showLengthUnitsCheckBoxClicked); + m_showLengthUnitsCheckbox->setObjectName(m_objectNamePrefix + + ":m_showLengthUnitsCheckbox"); + macroManager->addMacroSupportToObject(m_showLengthUnitsCheckbox, + "Show Length Units"); + + QLabel* locationLabel = new QLabel("Location"); + m_lengthTextLocationComboBox = new EnumComboBoxTemplate(this); + m_lengthTextLocationComboBox->getComboBox()->setToolTip("Set location of length text in scale bar"); + m_lengthTextLocationComboBox->setup(); + QObject::connect(m_lengthTextLocationComboBox, &EnumComboBoxTemplate::itemActivated, + this, &ScaleBarWidget::lengthTextLocationComboBoxActivated); + m_lengthTextLocationComboBox->getComboBox()->setObjectName(m_objectNamePrefix + + ":m_lengthTextLocationComboBox"); + macroManager->addMacroSupportToObject(m_lengthTextLocationComboBox->getComboBox(), + "Set Location of Text"); + + QLabel* fontColorLabel = new QLabel("Text Color"); + m_fontColorComboBox = new CaretColorEnumComboBox(CaretColorEnumComboBox::CustomColorModeEnum::EDITABLE, + CaretColorEnumComboBox::NoneColorModeEnum::DISABLED, + this); + m_fontColorComboBox->setToolTip("Set the color of the length text"); + QObject::connect(m_fontColorComboBox, &CaretColorEnumComboBox::colorSelected, + this, &ScaleBarWidget::fontColorComboBoxSelected); + + + /* + * Combo box for font name selection + */ + QLabel* fontLabel = new QLabel("Font "); + m_fontNameComboBox = new EnumComboBoxTemplate(this); + m_fontNameComboBox->getWidget()->setToolTip("Choose font for length text"); + m_fontNameComboBox->setup(); + QObject::connect(m_fontNameComboBox, &EnumComboBoxTemplate::itemActivated, + this, &ScaleBarWidget::fontNameChanged); + + QLabel* fontSizeLabel = new QLabel("Font Size"); + const AString fontSizeToolTop("" + "Adjusts font height (size), as a percentage of the viewport height, that is " + "converted to a pixel height when the text is drawn. " + "

" + "The numeric value in this control will be RED " + "when the pixel height is estimated to be too small and some or all " + "characters may not be drawn. " + "Reducing the height of the text and/or the height of the window may cause " + "too small text. " + ""); + m_fontSizeSpinBox = new WuQDoubleSpinBox(this); + m_fontSizeSpinBox->setRangePercentage(0.0, 100.0); + WuQtUtilities::setToolTipAndStatusTip(m_fontSizeSpinBox->getWidget(), + fontSizeToolTop); + QObject::connect(m_fontSizeSpinBox, &WuQDoubleSpinBox::valueChanged, + this, &ScaleBarWidget::fontSizeValueChanged); + + /* + * Default palette for size spin box + */ + m_fontSizeSpinBoxDefaultPalette = m_fontSizeSpinBox->getWidget()->palette(); + + /* + * Palette for spin box that colors text in red when the font height is "too small" + */ + m_fontSizeSpinBoxRedTextPalette = m_fontSizeSpinBoxDefaultPalette; + QBrush brush = m_fontSizeSpinBoxRedTextPalette.brush(QPalette::Active, QPalette::Text); + brush.setColor(Qt::red); + m_fontSizeSpinBoxRedTextPalette.setBrush(QPalette::Active, QPalette::Text, brush); + m_fontSizeSpinBoxRedTextPalette.setBrush(QPalette::Active, QPalette::WindowText, brush); + m_fontSizeSpinBoxRedTextPalette.setBrush(QPalette::Active, QPalette::HighlightedText, brush); + + QGroupBox* groupBox = new QGroupBox("Length Text"); + QGridLayout* gridLayout = new QGridLayout(groupBox); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); + int32_t row(0); + gridLayout->addWidget(m_showLengthTextCheckBox, row, 0, 1, 2, Qt::AlignLeft); + row++; + gridLayout->addWidget(m_showLengthUnitsCheckbox, row, 0, 1, 2, Qt::AlignLeft); + row++; + gridLayout->addWidget(fontColorLabel, row, 0); + gridLayout->addWidget(m_fontColorComboBox->getWidget(), row, 1); + row++; + gridLayout->addWidget(fontLabel, row, 0); + gridLayout->addWidget(m_fontNameComboBox->getWidget(), row, 1); + row++; + gridLayout->addWidget(fontSizeLabel, row, 0); + gridLayout->addWidget(m_fontSizeSpinBox->getWidget(), row, 1); + row++; + gridLayout->addWidget(locationLabel, row, 0); + gridLayout->addWidget(m_lengthTextLocationComboBox->getWidget(), row, 1); + row++; + + return groupBox; +} + +/** + * @return Instance of the misc widget + */ +QWidget* +ScaleBarWidget::createPositionWidget() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + + QLabel* positionModeLabel = new QLabel("Mode"); + m_positionModeEnumComboBox = new EnumComboBoxTemplate(this); + m_positionModeEnumComboBox->setup(); + QObject::connect(m_positionModeEnumComboBox, &EnumComboBoxTemplate::itemActivated, + this, &ScaleBarWidget::positionModeEnumComboBoxItemActivated); + m_positionModeEnumComboBox->getWidget()->setToolTip("AUTOMATIC - color bars are stacked\n" + " in lower left corner of Tab/Window\n" + "MANUAL - user must set the X and Y\n" + " coordinates in Annotate mode by\n" + " selecting and dragging the scale bar\n"); + m_positionModeEnumComboBox->getComboBox()->setObjectName(m_objectNamePrefix + + ":m_positionModeEnumComboBox"); + macroManager->addMacroSupportToObject(m_positionModeEnumComboBox->getComboBox(), + "Positionm Mode"); + + QGroupBox* groupBox = new QGroupBox("Position"); + QGridLayout* gridLayout = new QGridLayout(groupBox); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); + int32_t row(0); + gridLayout->addWidget(positionModeLabel, row, 0); + gridLayout->addWidget(m_positionModeEnumComboBox->getWidget(), row, 1); + row++; + + return groupBox; +} + +/** + * @return Instance of the tick marks widget + */ +QWidget* +ScaleBarWidget::createTickMarksWidget() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + + m_showTickMarksCheckBox = new QCheckBox("Show Tick Marks"); + m_showTickMarksCheckBox->setToolTip("Show tick marks on the scale bar"); + QObject::connect(m_showTickMarksCheckBox, &QCheckBox::clicked, + this, &ScaleBarWidget::showTickMarksCheckBoxClicked); + m_showTickMarksCheckBox->setObjectName(m_objectNamePrefix + + ":m_showTickMarksCheckBox"); + macroManager->addMacroSupportToObject(m_showTickMarksCheckBox, + "Show Tick Marks"); + + QLabel* subdivisionsLabel = new QLabel("Subdivisions"); + m_tickMarksSubdivisionsSpinBox = new QSpinBox(); + m_tickMarksSubdivisionsSpinBox->setToolTip("Set the number of subdivisions (space between tick marks)"); + m_tickMarksSubdivisionsSpinBox->setRange(0, 10); + QObject::connect(m_tickMarksSubdivisionsSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &ScaleBarWidget::tickMarksSubdivsionsSpinBoxValueChanged); + m_tickMarksSubdivisionsSpinBox->setObjectName(m_objectNamePrefix + + ":m_tickMarksSubdivisionsSpinBox"); + macroManager->addMacroSupportToObject(m_tickMarksSubdivisionsSpinBox, + "Set Number of Subdivisions"); + + + QGroupBox* groupBox = new QGroupBox("Tick Marks"); + QGridLayout* gridLayout = new QGridLayout(groupBox); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); + int32_t row(0); + gridLayout->addWidget(m_showTickMarksCheckBox, row, 0, 1, 2, Qt::AlignLeft); + row++; + gridLayout->addWidget(subdivisionsLabel, row, 0); + gridLayout->addWidget(m_tickMarksSubdivisionsSpinBox, row, 1); + row++; + + return groupBox; +} + +/** + * Update the widgets content + * @param browserTabContent + * The tab's content + */ +void +ScaleBarWidget::updateContent(BrowserTabContent* browserTabContent) +{ + m_scaleBar = NULL; + if (browserTabContent != NULL) { + m_scaleBar = browserTabContent->getScaleBar(); + } + + if (m_scaleBar != NULL) { + const AnnotationColorBarPositionModeEnum::Enum positionMode = m_scaleBar->getPositionMode(); + m_positionModeEnumComboBox->setSelectedItem(positionMode); + + const AnnotationScaleBarUnitsTypeEnum::Enum unitsType = m_scaleBar->getLengthUnits(); + m_lengthUnitsComboBox->setSelectedItem(unitsType); + + m_showLengthTextCheckBox->setChecked(m_scaleBar->isShowLengthText()); + + m_showLengthUnitsCheckbox->setChecked(m_scaleBar->isShowLengthUnitsText()); + + QSignalBlocker lengthBlocker(m_lengthSpinBox); + m_lengthSpinBox->setValue(m_scaleBar->getLength()); + + QSignalBlocker thicknessBlocker(m_thicknessSpinBox); + m_thicknessSpinBox->setValue(m_scaleBar->getLineWidthPercentage()); + + m_showTickMarksCheckBox->setChecked(m_scaleBar->isShowTickMarks()); + QSignalBlocker ticksSignalBlocker(m_tickMarksSubdivisionsSpinBox); + m_tickMarksSubdivisionsSpinBox->setValue(m_scaleBar->getTickMarksSubdivsions()); + + const AnnotationScaleBarTextLocationEnum::Enum textLocation = m_scaleBar->getLengthTextLocation(); + m_lengthTextLocationComboBox->setSelectedItem(textLocation); + + const CaretColorEnum::Enum foregroundColor = m_scaleBar->getLineColor(); + m_foregroundColorComboBox->setSelectedColor(foregroundColor); + std::array customRGBA; + m_scaleBar->getCustomLineColor(&customRGBA[0]); + m_foregroundColorComboBox->setCustomColor(customRGBA); + + const CaretColorEnum::Enum backgroundColor = m_scaleBar->getBackgroundColor(); + m_backgroundColorComboBox->setSelectedColor(backgroundColor); + m_scaleBar->getCustomBackgroundColor(&customRGBA[0]); + m_backgroundColorComboBox->setCustomColor(customRGBA); + + const CaretColorEnum::Enum fontColor = m_scaleBar->getTextColor(); + m_fontColorComboBox->setSelectedColor(fontColor); + m_scaleBar->getCustomTextColor(&customRGBA[0]); + m_fontColorComboBox->setCustomColor(customRGBA); + + m_fontNameComboBox->setSelectedItem(m_scaleBar->getFont()); + + QSignalBlocker fontSizeBlocker(m_fontSizeSpinBox); + m_fontSizeSpinBox->setValue(m_scaleBar->getFontPercentViewportSize()); + } + + setEnabled(m_scaleBar != NULL); +} + +/** + * Called when show length check box is clicked + * + * @param status + * New checked status + */ +void +ScaleBarWidget::showTickMarksCheckBoxClicked(bool status) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setShowTickMarks(status); + updateGraphics(); + } +} + +/** + * Called when tick marks subdivisions spin box value is changed + * + * @param value + * New value + */ +void +ScaleBarWidget::tickMarksSubdivsionsSpinBoxValueChanged(int value) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setTickMarksSubdivisions(value); + updateGraphics(); + } +} + +/** + * Called length text location value is changed + * + */ +void +ScaleBarWidget::lengthTextLocationComboBoxActivated() +{ + if (m_scaleBar != NULL) { + const AnnotationScaleBarTextLocationEnum::Enum location = m_lengthTextLocationComboBox->getSelectedItem(); + m_scaleBar->setLengthTextLocation(location); + updateGraphics(); + } +} + +/** + * Called when show length check box is clicked + * + * @param status + * New checked status + */ +void +ScaleBarWidget::showLengthCheckBoxClicked(bool status) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setShowLengthText(status); + updateGraphics(); + } +} + +/** + * Called when show length units check box is clicked + * + * @param status + * New checked status + */ +void +ScaleBarWidget::showLengthUnitsCheckBoxClicked(bool status) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setShowLengthUnitsText(status); + updateGraphics(); + } +} + +/** + * Called when length double spin box value is changed + * + * @param value + * New value + */ +void +ScaleBarWidget::lengthSpinBoxValueChanged(double value) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setLength(value); + updateGraphics(); + } +} + +/** + * Called when thickness double spin box value is changed + * + * @param value + * New value + */ +void +ScaleBarWidget::thicknessSpinBoxValueChanged(double value) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setLineWidthPercentage(value); + updateGraphics(); + } +} + +/** + * Caleld when position mode combo box selection is made + */ +void +ScaleBarWidget::positionModeEnumComboBoxItemActivated() +{ + if (m_scaleBar != NULL) { + m_scaleBar->setPositionMode(m_positionModeEnumComboBox->getSelectedItem()); + updateGraphics(); + } +} + +/** + * Caleld when length units combo box selection is made + */ +void +ScaleBarWidget::lengthEnumComboBoxItemActivated() +{ + if (m_scaleBar != NULL) { + m_scaleBar->setLengthUnits(m_lengthUnitsComboBox->getSelectedItem()); + updateGraphics(); + } +} + +/** + * Called when background color is selected + * @param color + * Color selected + */ +void +ScaleBarWidget::backgroundColorComboBoxSelected(const CaretColorEnum::Enum color) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setBackgroundColor(color); + if (color == CaretColorEnum::CUSTOM) { + std::array rgba; + m_backgroundColorComboBox->getCustomColor(rgba); + m_scaleBar->setCustomBackgroundColor(&rgba[0]); + } + updateGraphics(); + } +} + +/** + * Called when foreground color is selected + * @param color + * Color selected + */ +void +ScaleBarWidget::foregroundColorComboBoxSelected(const CaretColorEnum::Enum color) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setLineColor(color); + if (color == CaretColorEnum::CUSTOM) { + std::array rgba; + m_foregroundColorComboBox->getCustomColor(rgba); + m_scaleBar->setCustomLineColor(&rgba[0]); + } + updateGraphics(); + } +} + +/** + * Called when font color is selected + * @param color + * Color selected + */ +void +ScaleBarWidget::fontColorComboBoxSelected(const CaretColorEnum::Enum color) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setTextColor(color); + if (color == CaretColorEnum::CUSTOM) { + std::array rgba; + m_fontColorComboBox->getCustomColor(rgba); + m_scaleBar->setCustomTextColor(&rgba[0]); + } + updateGraphics(); + } +} + +/** + * Gets called when font name changed. + */ +void +ScaleBarWidget::fontNameChanged() +{ + if (m_scaleBar != NULL) { + const AnnotationTextFontNameEnum::Enum fontName = m_fontNameComboBox->getSelectedItem(); + m_scaleBar->setFont(fontName); + updateGraphics(); + } +} + +/** + * Called when font size spin box value is changed + * + * @param value + * New value + */ +void +ScaleBarWidget::fontSizeValueChanged(double value) +{ + if (m_scaleBar != NULL) { + m_scaleBar->setFontPercentViewportSize(value); + + if (m_scaleBar->isFontTooSmallWhenLastDrawn()) { + m_fontSizeSpinBox->getWidget()->setPalette(m_fontSizeSpinBoxRedTextPalette); + } + else { + m_fontSizeSpinBox->getWidget()->setPalette(m_fontSizeSpinBoxDefaultPalette); + } + + updateGraphics(); + } +} + +/** + * Update graphics + */ +void +ScaleBarWidget::updateGraphics() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/ScaleBarWidget.h connectome-workbench-1.5.0/src/GuiQt/ScaleBarWidget.h --- connectome-workbench-1.4.2/src/GuiQt/ScaleBarWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ScaleBarWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,147 @@ +#ifndef __SCALE_BAR_WIDGET_H__ +#define __SCALE_BAR_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "CaretColorEnum.h" + +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QSpinBox; + +namespace caret { + + class AnnotationScaleBar; + class BrowserTabContent; + class CaretColorEnumComboBox; + class EnumComboBoxTemplate; + class WuQDoubleSpinBox; + + class ScaleBarWidget : public QWidget { + + Q_OBJECT + + public: + ScaleBarWidget(const QString& objectNamePrefix); + + virtual ~ScaleBarWidget(); + + ScaleBarWidget(const ScaleBarWidget&) = delete; + + ScaleBarWidget& operator=(const ScaleBarWidget&) = delete; + + void updateContent(BrowserTabContent* browserTabContent); + + // ADD_NEW_METHODS_HERE + + private slots: + void showLengthCheckBoxClicked(bool status); + + void showLengthUnitsCheckBoxClicked(bool status); + + void lengthSpinBoxValueChanged(double value); + + void thicknessSpinBoxValueChanged(double value); + + void positionModeEnumComboBoxItemActivated(); + + void lengthEnumComboBoxItemActivated(); + + void showTickMarksCheckBoxClicked(bool status); + + void tickMarksSubdivsionsSpinBoxValueChanged(int value); + + void lengthTextLocationComboBoxActivated(); + + void backgroundColorComboBoxSelected(const CaretColorEnum::Enum color); + + void foregroundColorComboBoxSelected(const CaretColorEnum::Enum color); + + void fontColorComboBoxSelected(const CaretColorEnum::Enum color); + + void fontNameChanged(); + + void fontSizeValueChanged(double value); + + private: + void updateGraphics(); + + QWidget* createBarWidget(); + + QWidget* createLengthTextWidget(); + + QWidget* createPositionWidget(); + + QWidget* createTickMarksWidget(); + + QString m_objectNamePrefix; + + AnnotationScaleBar* m_scaleBar = NULL; + + QCheckBox* m_showLengthTextCheckBox; + + QCheckBox* m_showLengthUnitsCheckbox; + + QDoubleSpinBox* m_lengthSpinBox; + + QDoubleSpinBox* m_thicknessSpinBox; + + EnumComboBoxTemplate* m_lengthUnitsComboBox; + + QCheckBox* m_showTickMarksCheckBox; + + QSpinBox* m_tickMarksSubdivisionsSpinBox; + + EnumComboBoxTemplate* m_positionModeEnumComboBox; + + EnumComboBoxTemplate* m_lengthTextLocationComboBox; + + CaretColorEnumComboBox* m_backgroundColorComboBox; + + CaretColorEnumComboBox* m_foregroundColorComboBox; + + CaretColorEnumComboBox* m_fontColorComboBox; + + EnumComboBoxTemplate* m_fontNameComboBox; + + WuQDoubleSpinBox* m_fontSizeSpinBox; + + QPalette m_fontSizeSpinBoxDefaultPalette; + + QPalette m_fontSizeSpinBoxRedTextPalette; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCALE_BAR_WIDGET_DECLARE__ + // +#endif // __SCALE_BAR_WIDGET_DECLARE__ + +} // namespace +#endif //__SCALE_BAR_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/SceneCreateReplaceDialog.cxx connectome-workbench-1.5.0/src/GuiQt/SceneCreateReplaceDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/SceneCreateReplaceDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SceneCreateReplaceDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -233,6 +233,7 @@ break; } + m_nameLineEdit->setFocus(); m_nameLineEdit->selectAll(); diff -Nru connectome-workbench-1.4.2/src/GuiQt/SceneDataFileTreeItemModel.cxx connectome-workbench-1.5.0/src/GuiQt/SceneDataFileTreeItemModel.cxx --- connectome-workbench-1.4.2/src/GuiQt/SceneDataFileTreeItemModel.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SceneDataFileTreeItemModel.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -82,7 +82,7 @@ /* * Add the data files */ - for (const auto dataFileInfo : sceneDataFileInfo) { + for (const auto& dataFileInfo : sceneDataFileInfo) { AString pathName; AString pathAndFileName; switch (sortMode) { @@ -174,7 +174,7 @@ if (debugFlag) { std::cout << "Parent directory hierarchy: " << std::endl; - for (const auto s : parentDirectoryHierarchy) { + for (const auto& s : parentDirectoryHierarchy) { std::cout << " " << s << std::endl; } } diff -Nru connectome-workbench-1.4.2/src/GuiQt/SceneDialog.cxx connectome-workbench-1.5.0/src/GuiQt/SceneDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/SceneDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SceneDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -385,11 +385,11 @@ && m_cautionIconValid) { m_sceneFileSelectionComboBox->addItem(m_cautionIcon, name, - qVariantFromValue((void*)sceneFile)); + QVariant::fromValue((void*)sceneFile)); } else { m_sceneFileSelectionComboBox->addItem(name, - qVariantFromValue((void*)sceneFile)); + QVariant::fromValue((void*)sceneFile)); } } @@ -1317,6 +1317,7 @@ const int IMAGE_DISPLAY_WIDTH = 400; std::vector sceneNames; std::vector sceneErrors; + std::vector sceneWarnings; std::vector oldImages; std::vector newImages; @@ -1364,10 +1365,12 @@ * Display the scene */ AString errorMessage; + AString warningMessage; SceneDialog::displayScenePrivateWithErrorMessage(sceneFile, origScene, false, - errorMessage); + errorMessage, + warningMessage); sceneNames.push_back(origScene->getName()); /* @@ -1451,6 +1454,7 @@ newImages.push_back(QImage()); } sceneErrors.push_back(errorMessage); + sceneWarnings.push_back(warningMessage); } } @@ -1460,6 +1464,7 @@ CaretAssert(sceneNames.size() == sceneErrors.size()); CaretAssert(sceneNames.size() == oldImages.size()); CaretAssert(sceneNames.size() == newImages.size()); + CaretAssert(sceneNames.size() == sceneWarnings.size()); showNormalCursor(); @@ -1500,7 +1505,14 @@ gridLayout->addWidget(errorLabel, row, 1, Qt::AlignLeft); } + row++; + if ( ! sceneWarnings[i].isEmpty()) { + QLabel* warningLabel = new QLabel("Scene Warnings: " + sceneWarnings[i]); + warningLabel->setWordWrap(true); + gridLayout->addWidget(warningLabel, + row, 1, Qt::AlignLeft); + } row++; const QSizePolicy::Policy imageSizePolicy = QSizePolicy::Preferred; @@ -1629,6 +1641,7 @@ std::vector sceneNames; std::vector sceneErrors; + std::vector sceneWarnings; std::vector sceneImages; std::vector newImages; @@ -1657,10 +1670,12 @@ } AString errorMessage; + AString warningMessage; SceneDialog::displayScenePrivateWithErrorMessage(sceneFile, origScene, false, - errorMessage); + errorMessage, + warningMessage); /* * Set the active scene */ @@ -1705,6 +1720,7 @@ } sceneErrors.push_back(errorMessage); + sceneWarnings.push_back(warningMessage); } } @@ -1714,6 +1730,7 @@ CaretAssert(sceneNames.size() == sceneImages.size()); CaretAssert(sceneNames.size() == sceneErrors.size()); CaretAssert(sceneNames.size() == newImages.size()); + CaretAssert(sceneNames.size() == sceneWarnings.size()); showNormalCursor(); @@ -1751,6 +1768,14 @@ row = gridLayout->rowCount(); } + if ( ! sceneWarnings[i].isEmpty()) { + QLabel* warningLabel = new QLabel("Scene Warnings: " + sceneWarnings[i]); + warningLabel->setWordWrap(true); + gridLayout->addWidget(warningLabel, + row, 0, 1, 2, Qt::AlignLeft); + + row = gridLayout->rowCount(); + } const QSizePolicy::Policy imageSizePolicy = QSizePolicy::Preferred; QLabel* sceneImageLabel = new QLabel("Image Invalid"); @@ -2214,7 +2239,7 @@ */ m_showFileStructurePushButton = new QPushButton("Show Files and Folders..."); WuQtUtilities::setWordWrappedToolTip(m_showFileStructurePushButton, - "In a dialog, show the organization files and folders contained in the Scene File"); + "In a dialog, show the organization of files and folders contained in the Scene File"); QObject::connect(m_showFileStructurePushButton, &QPushButton::clicked, this, &SceneDialog::showFileStructure); @@ -2254,7 +2279,7 @@ * Upload button */ m_uploadSceneFilePushButton = new QPushButton("Upload..."); - m_uploadSceneFilePushButton->setToolTip("Upload the selected scene file to the BALSA Database"); + m_uploadSceneFilePushButton->setToolTip("Upload the selected scene file to the BALSA Database, balsa.wustl.edu"); QObject::connect(m_uploadSceneFilePushButton, SIGNAL(clicked()), this, SLOT(uploadSceneFileButtonClicked())); @@ -2413,6 +2438,13 @@ void SceneDialog::showSceneButtonClicked() { + /* + * Any clicks on the button are blocked until this method + * returns to prevent 'double clicks" that call this + * method a second time before the first time completes. + */ + QSignalBlocker blocker(m_showScenePushButton); + if ( ! checkForModifiedFiles(GuiManager::TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW, this)) { return; @@ -2542,14 +2574,14 @@ AString nameText; AString sceneIdText; - AString descriptionText; - const int32_t negativeIsUnlimitedNumberOfLines = -1; + AString abbreviatedDescriptionText; + AString fullDescriptionText; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), -1, nameText, sceneIdText, - descriptionText, - negativeIsUnlimitedNumberOfLines); + abbreviatedDescriptionText, + fullDescriptionText); QLabel* nameLabel = new QLabel(nameText); ded.addWidget("", nameLabel); @@ -2558,8 +2590,8 @@ ded.addWidget("", sceneIdLabel); - if (! descriptionText.isEmpty()) { - QLabel* descriptionLabel = new QLabel(descriptionText); + if (! fullDescriptionText.isEmpty()) { + QLabel* descriptionLabel = new QLabel(fullDescriptionText); descriptionLabel->setWordWrap(true); ded.addWidget("", descriptionLabel); @@ -2661,23 +2693,39 @@ Scene* scene, const bool showWaitCursor) { + CaretAssert(scene); AString errorMessage; + AString warningMessage; ElapsedTimer et; et.start(); const bool successFlag = displayScenePrivateWithErrorMessage(sceneFile, - scene, - showWaitCursor, - errorMessage); + scene, + showWaitCursor, + errorMessage, + warningMessage); AString msg = (AString::number(et.getElapsedTimeSeconds()) + " seconds to read Scene " + scene->getName()); CaretLogInfo(msg); if ( ! successFlag) { - WuQMessageBox::errorOk(dialogParent, - errorMessage); + WuQMessageBox::errorDetailedTextOk(dialogParent, + ("Error reading scene: " + + scene->getName()), + errorMessage); + } + + if ( ! warningMessage.isEmpty()) { + warningMessage.replace("\n", "
"); + warningMessage.insert(0, ""); + warningMessage.append(""); + WuQTextEditorDialog::runNonModal("Scene Warnings", + warningMessage, + WuQTextEditorDialog::TextMode::HTML, + WuQTextEditorDialog::WrapMode::YES, + dialogParent); } EventManager::get()->sendEvent(EventShowDataFileReadWarningsDialog().getPointer()); @@ -2695,6 +2743,8 @@ * Show a wait cursor while loading scene * @param errorMessageOut * Contains error information if scene fails to load. + * @param warningMessageOut + * Contains non-fatal warnings encountered while loading scene * @return * true if scene was displayed without error, else false. */ @@ -2702,12 +2752,14 @@ SceneDialog::displayScenePrivateWithErrorMessage(SceneFile* sceneFile, Scene* scene, const bool showWaitCursor, - AString& errorMessageOut) + AString& errorMessageOut, + AString& warningMessageOut) { CaretAssert(sceneFile); CaretAssert(scene); errorMessageOut.clear(); + warningMessageOut.clear(); const AString sceneFileName = sceneFile->getFileName(); @@ -2767,6 +2819,8 @@ SceneClass::setDebugLoggingEnabled(false); + warningMessageOut = sceneAttributes->getSceneLoadWarningMessage(); + cursor.restoreCursor(); /* @@ -2789,7 +2843,7 @@ */ if ( ! sceneFile->isModified()) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSceneFiles(sceneFile->getFileName()); + prefs->addToRecentFilesAndOrDirectories(sceneFile->getFileName()); } } else { @@ -2990,8 +3044,6 @@ EventManager::get()->sendEvent(helpViewerEvent.getPointer()); } - - /* ======================================================================== */ /** * \class caret::SceneClassWidget @@ -3017,7 +3069,7 @@ m_descriptionLabel = new QLabel(); m_descriptionLabel->setWordWrap(true); - + m_sceneIdLabel = new QLabel(); m_previewImageLabel = new QLabel(); @@ -3098,13 +3150,14 @@ AString nameText; AString sceneIdText; AString descriptionText; - const int32_t numLinesToDisplay = 9; + AString abbreviatedDescriptionText; + AString fullDescriptionText; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), sceneIndex, nameText, sceneIdText, - descriptionText, - numLinesToDisplay); + abbreviatedDescriptionText, + fullDescriptionText); if (activeSceneFlag) { m_activeSceneLabel->setText("Current Scene"); @@ -3116,7 +3169,7 @@ } m_nameLabel->setText(nameText); m_sceneIdLabel->setText(sceneIdText); - m_descriptionLabel->setText(descriptionText); + m_descriptionLabel->setText(fullDescriptionText); QByteArray imageByteArray; AString imageBytesFormat; @@ -3197,19 +3250,18 @@ * Text for name. * @param sceneIdTextOut * Text for scene ID. - * @param desciptionTextOut - * Text for description. - * @param maximumLinesInDescription - * Maximum number of lines allowed in description. - * If value is negative, an unlimited number of lines are allowed. + * @param abbreviatedDescriptionTextOut + * Abbreviated text for description. + * @param fullDescriptionTextOut + * Full text for description. */ void SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(const SceneInfo* sceneInfo, const int32_t sceneIndex, AString& nameTextOut, AString& sceneIdTextOut, - AString& descriptionTextOut, - const int32_t maximumLinesInDescription) + AString& abbreviatedDescriptionTextOut, + AString& fullDescriptionTextOut) { CaretAssert(sceneInfo); @@ -3238,14 +3290,27 @@ + sceneInfo->getBalsaSceneID() + ""); - AString description = sceneInfo->getDescription(); - - if (maximumLinesInDescription > 0) { - SceneClassInfoWidget::limitToNumberOfLines(description, - maximumLinesInDescription); + fullDescriptionTextOut = sceneInfo->getDescription(); + abbreviatedDescriptionTextOut = fullDescriptionTextOut; + const int32_t maximumLinesInDescription(9); + SceneClassInfoWidget::limitToNumberOfLines(abbreviatedDescriptionTextOut, + maximumLinesInDescription); + + if ( ! fullDescriptionTextOut.isEmpty()) { + /* + * HTML formatting is needed so text is properly displayed. + * Want to put "Description" at beginning in bold but any + * HTML tags are converted to text. So, after conversion to + * HTML, perform a replace to insert "Description" in bold. + */ + const AString replaceWithDescriptionBoldText = "REPLACE_WITH_DESCRIPTION"; + fullDescriptionTextOut = WuQtUtilities::createWordWrappedToolTipText(replaceWithDescriptionBoldText + + fullDescriptionTextOut); + fullDescriptionTextOut.replace(replaceWithDescriptionBoldText, + "DESCRIPTION: "); } - - if ( ! description.isEmpty()) { + + if ( ! abbreviatedDescriptionTextOut.isEmpty()) { /* * HTML formatting is needed so text is properly displayed. * Want to put "Description" at beginning in bold but any @@ -3253,13 +3318,11 @@ * HTML, perform a replace to insert "Description" in bold. */ const AString replaceWithDescriptionBoldText = "REPLACE_WITH_DESCRIPTION"; - description = WuQtUtilities::createWordWrappedToolTipText(replaceWithDescriptionBoldText - + description); - description.replace(replaceWithDescriptionBoldText, + abbreviatedDescriptionTextOut = WuQtUtilities::createWordWrappedToolTipText(replaceWithDescriptionBoldText + + abbreviatedDescriptionTextOut); + abbreviatedDescriptionTextOut.replace(replaceWithDescriptionBoldText, "DESCRIPTION: "); } - - descriptionTextOut = description; } @@ -3322,4 +3385,3 @@ return false; } - diff -Nru connectome-workbench-1.4.2/src/GuiQt/SceneDialog.h connectome-workbench-1.5.0/src/GuiQt/SceneDialog.h --- connectome-workbench-1.4.2/src/GuiQt/SceneDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SceneDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -35,6 +35,7 @@ class QLineEdit; class QPushButton; class QScrollArea; +class QTextEdit; class QVBoxLayout; namespace caret { @@ -63,6 +64,8 @@ void createDefaultSceneFile(); + SceneFile* getSelectedSceneFile(); + static bool isInformUserAboutScenesOnExit(); static bool checkForModifiedFiles(const GuiManager::TestModifiedMode testMode, @@ -136,8 +139,6 @@ virtual void helpButtonClicked() override; private: - SceneFile* getSelectedSceneFile(); - Scene* getSelectedScene(); void loadSceneFileComboBox(SceneFile* selectedSceneFileIn); @@ -164,10 +165,11 @@ const bool showWaitCursor); static bool displayScenePrivateWithErrorMessage(SceneFile* sceneFile, - Scene* scene, - const bool showWaitCursor, - AString& errorMessageOut); - + Scene* scene, + const bool showWaitCursor, + AString& errorMessageOut, + AString& warningMessageOut); + bool displayNewSceneWarning(); void enableSceneMoveUpAndDownButtons(); @@ -294,8 +296,8 @@ const int32_t sceneIndex, AString& nameTextOut, AString& sceneIdTextOut, - AString& descriptionTextOut, - const int32_t maximumLinesInDescription); + AString& abbreviatedDescriptionTextOut, + AString& fullDescriptionTextOut); signals: /** @@ -347,7 +349,6 @@ bool SceneDialog::s_informUserAboutScenesOnExitFlag = true; bool SceneDialog::s_warnUserWhenCreatingSceneFlag = true; bool SceneDialog::s_useSceneForegroundBackgroundColorsFlag = true; - #endif // __SCENE_DIALOG_DECLARE__ } // namespace diff -Nru connectome-workbench-1.4.2/src/GuiQt/SceneFileInformationDialog.cxx connectome-workbench-1.5.0/src/GuiQt/SceneFileInformationDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/SceneFileInformationDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SceneFileInformationDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -66,34 +66,18 @@ setDeleteWhenClosed(true); setApplyButtonText(""); - AString basePathName; - std::vector missingDataFiles; - AString basePathErrorMessage; - const bool validBasePathFlag = sceneFile->findBaseDirectoryForDataFiles(basePathName, - missingDataFiles, - basePathErrorMessage); - if ( ! validBasePathFlag) { - basePathName = basePathErrorMessage; - } QLabel* basePathLabel = new QLabel("Base Path:"); m_basePathLineEdit = new QLineEdit(); m_basePathLineEdit->setReadOnly(true); - m_basePathLineEdit->setText(basePathName); - m_basePathLineEdit->home(true); - FileInformation sceneFileInfo(sceneFile->getFileName()); QLabel* sceneFileNameLabel = new QLabel("Scene File Name:"); m_sceneFileNameLineEdit = new QLineEdit(); m_sceneFileNameLineEdit->setReadOnly(true); - m_sceneFileNameLineEdit->setText(sceneFileInfo.getFileName()); - m_sceneFileNameLineEdit->home(true); QLabel* sceneFilePathLabel = new QLabel("Scene File Path:"); m_sceneFilePathLineEdit = new QLineEdit(); m_sceneFilePathLineEdit->setReadOnly(true); - m_sceneFilePathLineEdit->setText(sceneFileInfo.getPathName()); - m_sceneFilePathLineEdit->home(true); m_textEdit = new QTextEdit(); @@ -105,15 +89,19 @@ tabWidget->addTab(m_textEdit, "List"); QGridLayout* namesLayout = new QGridLayout(); + int32_t nameLayoutRow(0); namesLayout->setColumnStretch(0, 0); namesLayout->setColumnStretch(1, 100); - namesLayout->addWidget(basePathLabel, 0, 0); - namesLayout->addWidget(m_basePathLineEdit, 0, 1); - namesLayout->addWidget(sceneFileNameLabel, 1, 0); - namesLayout->addWidget(m_sceneFileNameLineEdit, 1, 1); - namesLayout->addWidget(sceneFilePathLabel, 2, 0); - namesLayout->addWidget(m_sceneFilePathLineEdit, 2, 1); - + namesLayout->addWidget(basePathLabel, nameLayoutRow, 0); + namesLayout->addWidget(m_basePathLineEdit, nameLayoutRow, 1); + nameLayoutRow++; + namesLayout->addWidget(sceneFilePathLabel, nameLayoutRow, 0); + namesLayout->addWidget(m_sceneFilePathLineEdit, nameLayoutRow, 1); + nameLayoutRow++; + namesLayout->addWidget(sceneFileNameLabel, nameLayoutRow, 0); + namesLayout->addWidget(m_sceneFileNameLineEdit, nameLayoutRow, 1); + nameLayoutRow++; + QWidget* dialogWidget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); dialogLayout->setSpacing(3); @@ -122,10 +110,9 @@ setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); - displayFilesHierarchy(); - displayFilesList(); - setSizeOfDialogWhenDisplayed(QSize(600, 800)); + + loadContentIntoDialog(); } /** @@ -135,103 +122,43 @@ { } -///** -// * Setup the list of files -// */ -//void -//SceneFileInformationDialog::displayFilesList() -//{ -// const SceneDataFileInfo::SortMode sortMode = SceneDataFileInfo::SortMode::RelativeToSceneFilePath; -// CaretAssert(m_sceneFile); -// -// std::vector fileSceneInfo = m_sceneFile->getAllDataFileInfoFromAllScenes(); -// SceneDataFileInfo::sort(fileSceneInfo, -// sortMode); -// -// if (fileSceneInfo.empty()) { -// WuQMessageBox::errorOk(this, -// "Scene file is empty."); -// return; -// } -// -// AString baseDirectoryName; -// std::vector missingFileNames; -// AString errorMessage; -// const bool validBasePathFlag = m_sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, -// missingFileNames, -// errorMessage); -// -// const AString sceneFileName = m_sceneFile->getFileName(); -// AString text(""); -// -// text.appendWithNewLine("Automatic Base Path: " -// + (validBasePathFlag ? baseDirectoryName : ("INVALID: " + errorMessage)) -// + "

"); -// text.appendWithNewLine("Scene File: " -// + sceneFileName -// + "

"); -// -// bool needListEndElementFlag = false; -// AString lastPathName("bogus ##(*&$UI()#NFGK path name"); -// for (const auto& fileData : fileSceneInfo) { -// AString missingText; -// -// AString pathName; -// switch (sortMode) { -// case SceneDataFileInfo::SortMode::AbsolutePath: -// pathName = fileData.getAbsolutePath(); -// break; -// case SceneDataFileInfo::SortMode::RelativeToBasePath: -// pathName = fileData.getRelativePathToBasePath(); -// if (pathName.isEmpty()) { -// pathName = "Files in Base Path"; -// } -// break; -// case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: -// pathName = fileData.getRelativePathToSceneFile(); -// if (pathName.isEmpty()) { -// pathName = "Files in Scene File Path"; -// } -// break; -// } -// -// if (pathName != lastPathName) { -// if (needListEndElementFlag) { -// text.append(""); -// } -// text.append("

" -// + pathName -// + ""); -// text.append("

    "); -// needListEndElementFlag = true; -// lastPathName = pathName; -// } -// -// if (fileData.isMissing()) { -// missingText = "MISSING "; -// } -// -// text.append("
  • " -// + missingText -// + fileData.getDataFileName() -// + " (" -// + fileData.getSceneIndicesAsString() -// + ")"); -// } -// if (needListEndElementFlag) { -// text.append("
"); -// } -// text.append(""); -// -// m_textEdit->clear(); -// m_textEdit->setHtml(text); -//} +void +SceneFileInformationDialog::loadContentIntoDialog() +{ + CaretAssert(m_sceneFile); + AString basePathName; + SceneFileBasePathTypeEnum::Enum basePathType = SceneFileBasePathTypeEnum::AUTOMATIC; + { + AString errorMessage; + const bool validFlag = m_sceneFile->getSelectedBasePathTypeAndName(basePathType, + basePathName, + errorMessage); + if ( ! validFlag) { + basePathName = ("INVALID " + errorMessage); + } + } + + m_basePathLineEdit->setText(basePathName); + m_basePathLineEdit->home(true); + + FileInformation sceneFileInfo(m_sceneFile->getFileName()); + m_sceneFileNameLineEdit->setText(sceneFileInfo.getFileName()); + m_sceneFileNameLineEdit->home(true); + + m_sceneFilePathLineEdit->setText(sceneFileInfo.getPathName()); + m_sceneFilePathLineEdit->home(true); + + displayFilesHierarchy(); + displayFilesList(basePathType, + basePathName); +} /** * Setup the list of files */ void -SceneFileInformationDialog::displayFilesList() +SceneFileInformationDialog::displayFilesList(const SceneFileBasePathTypeEnum::Enum basePathType, + const AString& basePathName) { const SceneDataFileInfo::SortMode sortMode = SceneDataFileInfo::SortMode::RelativeToSceneFilePath; CaretAssert(m_sceneFile); @@ -246,19 +173,15 @@ return; } - AString baseDirectoryName; - std::vector missingFileNames; - AString errorMessage; - const bool validBasePathFlag = m_sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, - missingFileNames, - errorMessage); - - const AString sceneFileName = m_sceneFile->getFileName(); AString text(""); - - text.appendWithNewLine("Automatic Base Path: " - + (validBasePathFlag ? baseDirectoryName : ("INVALID: " + errorMessage)) + const AString sceneFileName = m_sceneFile->getFileName(); + + text.appendWithNewLine("" + + (SceneFileBasePathTypeEnum::toGuiName(basePathType) + " Base Path") + + ": " + + basePathName + "

"); + text.appendWithNewLine("Scene File: " + sceneFileName + "

"); diff -Nru connectome-workbench-1.4.2/src/GuiQt/SceneFileInformationDialog.h connectome-workbench-1.5.0/src/GuiQt/SceneFileInformationDialog.h --- connectome-workbench-1.4.2/src/GuiQt/SceneFileInformationDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SceneFileInformationDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ #include +#include "SceneFileBasePathTypeEnum.h" #include "WuQDialogNonModal.h" class QLineEdit; @@ -54,7 +55,10 @@ // ADD_NEW_METHODS_HERE private: - void displayFilesList(); + void loadContentIntoDialog(); + + void displayFilesList(const SceneFileBasePathTypeEnum::Enum basePathType, + const AString& basePathName); void displayFilesHierarchy(); diff -Nru connectome-workbench-1.4.2/src/GuiQt/ScenePreviewDialog.cxx connectome-workbench-1.5.0/src/GuiQt/ScenePreviewDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/ScenePreviewDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ScenePreviewDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -95,21 +95,21 @@ AString nameText; AString sceneIdText; - AString descriptionText; - const int32_t negativeIsUnlimitedNumberOfLines = -1; + AString abbreviatedDescriptionText; + AString fullDescriptionText; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), -1, nameText, sceneIdText, - descriptionText, - negativeIsUnlimitedNumberOfLines); + abbreviatedDescriptionText, + fullDescriptionText); QLabel* nameLabel = new QLabel(nameText); QLabel* sceneIdLabel = new QLabel(sceneIdText); QLabel* descriptionLabel = NULL; - if (! descriptionText.isEmpty()) { - descriptionLabel = new QLabel(descriptionText); + if (! fullDescriptionText.isEmpty()) { + descriptionLabel = new QLabel(fullDescriptionText); descriptionLabel->setWordWrap(true); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/SpecFileManagementDialog.cxx connectome-workbench-1.5.0/src/GuiQt/SpecFileManagementDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/SpecFileManagementDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SpecFileManagementDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -52,14 +52,18 @@ #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "CaretPreferences.h" +#include "CaretResult.h" +#include "CaretResultDialog.h" #include "CursorDisplayScoped.h" #include "DataFileContentCopyMoveDialog.h" #include "DataFileContentCopyMoveInterface.h" #include "DataFileException.h" #include "DataFileContentInformation.h" #include "EventBrowserTabGetAllViewed.h" +#include "EventBrowserWindowCreateTabs.h" #include "EventDataFileRead.h" #include "EventDataFileReload.h" +#include "EventDataFileReloadAll.h" #include "EventGetDisplayedDataFiles.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" @@ -298,10 +302,14 @@ bool testForDisplayedDataFiles = false; m_loadScenesPushButton = NULL; + m_reloadAllDataFilesPushButton = NULL; switch (m_dialogMode) { case SpecFileManagementDialog::MODE_MANAGE_FILES: setOkButtonText("Save Checked Files"); setCancelButtonText("Close"); + m_reloadAllDataFilesPushButton = addUserPushButton("Reload All Loaded Files", + QDialogButtonBox::AcceptRole); + m_reloadAllDataFilesPushButton->setToolTip("Reloads all files that are currently loaded"); testForDisplayedDataFiles = true; break; case SpecFileManagementDialog::MODE_OPEN_SPEC_FILE: @@ -309,6 +317,7 @@ setCancelButtonText("Cancel"); m_loadScenesPushButton = addUserPushButton("Load Scenes", QDialogButtonBox::AcceptRole); + m_loadScenesPushButton->setToolTip("Loads all scene files and no other files"); break; case SpecFileManagementDialog::MODE_SAVE_FILES_WHILE_QUITTING: setOkButtonText("Save Selected Files and Exit"); @@ -433,8 +442,6 @@ /* * No scrollbars in dialog since the table widget will have scroll bars */ -// const bool enableScrollBars = false; - setTopBottomAndCentralWidgets(toolbarWidget, centralWidget, NULL, @@ -448,32 +455,6 @@ disableAutoDefaultForAllPushButtons(); -// /* -// * Table widget has a default size of 640 x 480. -// * So estimate the size of the dialog with the table fully -// * expanded. -// */ -// QHeaderView* columnHeader = m_filesTableWidget->horizontalHeader(); -// const int columnHeaderHeight = columnHeader->sizeHint().height(); -// -// int dialogWidth = 10; // start out with a little extra space -// int dialogHeight = 0; -// -// const int numRows = m_filesTableWidget->rowCount(); -// const int numCols = m_filesTableWidget->columnCount(); -// const int cellGap = 1; -// if ((numRows > 0) -// && (numCols > 0)) { -// for (int i = 0; i < numCols; i++) { -// dialogWidth += (m_filesTableWidget->columnWidth(i) + cellGap); -// std::cout << "column width " << i << ": " << m_filesTableWidget->columnWidth(i) << std::endl; -// } -// -// for (int i = 0; i < numRows; i++) { -// dialogHeight += (m_filesTableWidget->rowHeight(i) + cellGap); -// } -// } - const QSize tableSize = WuQtUtilities::estimateTableWidgetSize(m_filesTableWidget); int dialogWidth = std::max(tableSize.width(), @@ -826,10 +807,8 @@ case MODE_SAVE_FILES_WHILE_QUITTING: m_specFileTableRowIndex = numberOfRows; numberOfRows++; -// if ( ! m_brain->getSceneAnnotationFile()->isEmpty()) { m_sceneAnnotationFileRowIndex = numberOfRows; numberOfRows++; -// } break; case MODE_OPEN_SPEC_FILE: break; @@ -1345,25 +1324,6 @@ } } - -///** -// * Less than method for sorting using the sorting key. -// * -// * @param item1 -// * Tested for less than the item2 -// * @param item2 -// * Tested for greater than the item1 -// * @return -// * True if item1 is less than item2, else false. -// */ -//bool -//lessThanForSorting(const SpecFileManagementDialogRowContent *item1, -// const SpecFileManagementDialogRowContent* item2) -//{ -// return (item1->m_sortingKey < item2->m_sortingKey); -//} - - /** * Sort the file content. */ @@ -1781,6 +1741,61 @@ return RESULT_MODAL_ACCEPT; } + else if (userPushButton == m_reloadAllDataFilesPushButton) { + std::vector loadedFiles; + std::unique_ptr result = EventDataFileReloadAll::getReloadableFiles(GuiManager::get()->getBrain(), + loadedFiles); + if (CaretResultDialog::isError(result, + m_reloadAllDataFilesPushButton)) { + return RESULT_NONE; + } + + AString modifiedMessage; + for (auto& df : loadedFiles) { + CaretMappableDataFile* cmdf = dynamic_cast(df); + if (cmdf != NULL) { + if (cmdf->isModifiedExcludingPaletteColorMapping()) { + modifiedMessage.appendWithNewLine(" " + df->getFileNameNoPath()); + } + } + else if (df->isModified()) { + modifiedMessage.appendWithNewLine(" " + df->getFileNameNoPath()); + } + } + + if ( ! modifiedMessage.isEmpty()) { + const QString msg("These files are modified:\n" + + modifiedMessage + + "\n\nDiscard changes and reload files?"); + if ( ! WuQMessageBox::warningOkCancel(m_reloadAllDataFilesPushButton, + msg)) { + return RESULT_NONE; + } + } + + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + + /* + * Events are sent by the 'Brain' that are received by + * the ProgressReportingDialog that cause the dialog to + * be displayed. + */ + ProgressReportingDialog progressDialog("Reload All Files", + "Reloading files", + m_reloadAllDataFilesPushButton); + result = EventDataFileReloadAll::reloadAllFiles(GuiManager::get()->getBrain()); + cursor.restoreCursor(); + CaretResultDialog::isError(result, + m_reloadAllDataFilesPushButton); + cursor.showWaitCursor(); + + getDataFileContentFromSpecFile(); + loadSpecFileContentIntoDialog(); + updateGraphicWindowsAndUserInterface(); + + return RESULT_NONE; + } else { CaretAssert(0); } @@ -1895,6 +1910,10 @@ try { m_brain->writeDataFile(caretDataFile); specFileDataFile->setSavingSelected(false); + + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + CaretAssert(prefs); + prefs->addToRecentFilesAndOrDirectories(caretDataFile->getFileName()); } catch (const DataFileException& e) { errorMessages.appendWithNewLine(e.whatString()); @@ -1915,7 +1934,7 @@ } else { m_specFile->removeAnyFileInformationIfNotInSpecAndNoCaretDataFile(); - AString specFileErrorMessage = writeSpecFile(false); + AString specFileErrorMessage = writeSpecFile(true); if (specFileErrorMessage.isEmpty() == false) { errorMessages.appendWithNewLine(specFileErrorMessage); } @@ -1976,7 +1995,7 @@ try { m_specFile->writeFile(m_specFile->getFileName()); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSpecFiles(m_specFile->getFileName()); + prefs->addToRecentFilesAndOrDirectories(m_specFile->getFileName()); } catch (const DataFileException& e) { errorMessage = e.whatString(); @@ -2138,6 +2157,11 @@ if (reloadEvent.isError()) { errorMessage.appendWithNewLine(reloadEvent.getErrorMessage()); } + else { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + CaretAssert(prefs); + prefs->addToRecentFilesAndOrDirectories(caretDataFile->getFileName()); + } } else { AString username; @@ -2173,6 +2197,14 @@ if (readEvent.isError()) { errorMessage.appendWithNewLine(readEvent.getErrorMessage()); } + else { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + CaretAssert(prefs); + prefs->addToRecentFilesAndOrDirectories(specFileDataFile->getFileName()); + } + + EventBrowserWindowCreateTabs createTabsEvent(EventBrowserWindowCreateTabs::MODE_LOADED_DATA_FILE); + EventManager::get()->sendEvent(createTabsEvent.getPointer()); } updateGraphicWindowsAndUserInterface(); @@ -2487,20 +2519,6 @@ return; } -// QStringList filenameFilterList; -// filenameFilterList.append(DataFileTypeEnum::toQFileDialogFilter(caretDataFile->getDataFileType())); -// CaretFileDialog fd(parent); -// fd.setAcceptMode(CaretFileDialog::AcceptSave); -// fd.setNameFilters(filenameFilterList); -// fd.setFileMode(CaretFileDialog::AnyFile); -// fd.setViewMode(CaretFileDialog::List); -// fd.selectFile(caretDataFile->getFileName()); -// fd.setLabelText(CaretFileDialog::Accept, "Choose"); -// fd.setWindowTitle("Choose File Name"); -// if (fd.exec() == CaretFileDialog::Accepted) { -// QStringList files = fd.selectedFiles(); -// if (files.isEmpty() == false) { -// AString newFileName = files.at(0); if (newFileName != caretDataFile->getFileName()) { /* * Clone current item, remove file from it, @@ -2540,8 +2558,6 @@ */ updateGraphicWindowsAndUserInterface(); } -// } -// } } /** @@ -2567,89 +2583,6 @@ this); } - -///** -// * Called when spec file options tool button is triggered. -// */ -//void -//SpecFileManagementDialog::specFileOptionsActionTriggered() -//{ -// QAction* setFileNameAction = NULL; -// -// QMenu menu; -// QAction* metadataAction = menu.addAction("Edit Metadata..."); -// metadataAction->setEnabled(false); -// switch (m_dialogMode) { -// case MODE_MANAGE_FILES: -// case MODE_SAVE_FILES_WHILE_QUITTING: -// setFileNameAction = menu.addAction("Set File Name..."); -// break; -// case MODE_OPEN_SPEC_FILE: -// break; -// } -// -// QAction* selectedAction = menu.exec(QCursor::pos()); -// -// if (selectedAction == setFileNameAction) { -// QStringList filenameFilterList; -// filenameFilterList.append(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION)); -// CaretFileDialog fd(&menu); -// fd.setAcceptMode(CaretFileDialog::AcceptSave); -// fd.setNameFilters(filenameFilterList); -// fd.setFileMode(CaretFileDialog::AnyFile); -// fd.setViewMode(CaretFileDialog::List); -// fd.selectFile(m_specFile->getFileName()); -// fd.setLabelText(CaretFileDialog::Accept, "Choose"); -// fd.setWindowTitle("Choose Spec File Name"); -// if (fd.exec() == CaretFileDialog::Accepted) { -// QStringList files = fd.selectedFiles(); -// if (files.isEmpty() == false) { -// AString newFileName = files.at(0); -// m_specFile->setFileName(newFileName); -// loadSpecFileContentIntoDialog(); -// } -// } -// } -// else if (selectedAction == metadataAction) { -// -// } -// else if (selectedAction != NULL) { -// CaretAssertMessage(0, -// ("Unhandled Menu Action: " + selectedAction->text())); -// } -//} - -///** -// * Called to choose the name of the spec file. -// */ -//void -//SpecFileManagementDialog::chooseSpecFileNameActionTriggered() -//{ -// QWidget* toolButtonWidget = m_filesTableWidget->cellWidget(m_specFileTableRowIndex, -// m_COLUMN_OPTIONS_TOOLBUTTON); -// CaretAssert(toolButtonWidget); -// -// QStringList filenameFilterList; -// filenameFilterList.append(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION)); -// CaretFileDialog fd(toolButtonWidget); -// fd.setAcceptMode(CaretFileDialog::AcceptSave); -// fd.setNameFilters(filenameFilterList); -// fd.setFileMode(CaretFileDialog::AnyFile); -// fd.setViewMode(CaretFileDialog::List); -// fd.selectFile(m_specFile->getFileName()); -// fd.setLabelText(CaretFileDialog::Accept, "Choose"); -// fd.setWindowTitle("Choose Spec File Name"); -// if (fd.exec() == CaretFileDialog::Accepted) { -// QStringList files = fd.selectedFiles(); -// if (files.isEmpty() == false) { -// AString newFileName = files.at(0); -// m_specFile->setFileName(newFileName); -// loadSpecFileContentIntoDialog(); -// } -// } -//} - - /** * @return Create and return a toolbar for viewing files by type of file. * @param labelOut @@ -2666,7 +2599,7 @@ QAction* fileTypeAllAction = m_fileTypesActionGroup->addAction("All"); fileTypeAllAction->setCheckable(true); - fileTypeAllAction->setData(qVariantFromValue(DataFileTypeEnum::toIntegerCode(DataFileTypeEnum::UNKNOWN))); + fileTypeAllAction->setData(QVariant::fromValue(DataFileTypeEnum::toIntegerCode(DataFileTypeEnum::UNKNOWN))); /* * All types of files @@ -2706,7 +2639,7 @@ QAction* action = m_fileTypesActionGroup->addAction(text); action->setCheckable(true); - action->setData(qVariantFromValue(DataFileTypeEnum::toIntegerCode(dataFileType))); + action->setData(QVariant::fromValue(DataFileTypeEnum::toIntegerCode(dataFileType))); } if (m_fileTypesActionGroup->actions().isEmpty() == false) { @@ -2736,10 +2669,10 @@ this, SLOT(toolBarSelectFilesActionTriggered(QAction*))); QAction* allFilesAction = m_fileSelectionActionGroup->addAction("All"); - allFilesAction->setData(qVariantFromValue(SHOW_FILES_ALL)); + allFilesAction->setData(QVariant::fromValue(SHOW_FILES_ALL)); QAction* noneFilesAction = m_fileSelectionActionGroup->addAction("None"); - noneFilesAction->setData(qVariantFromValue(SHOW_FILES_NONE)); + noneFilesAction->setData(QVariant::fromValue(SHOW_FILES_NONE)); QToolBar* toolbar = createToolBarWithActionGroup("Select Files: ", labelOut, @@ -2762,23 +2695,23 @@ this, SLOT(toolBarManageFilesLoadedNotLoadedActionTriggered(QAction*))); QAction* allFilesAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("All"); - allFilesAction->setData(qVariantFromValue((int)MANAGE_FILES_ALL)); + allFilesAction->setData(QVariant::fromValue((int)MANAGE_FILES_ALL)); allFilesAction->setCheckable(true); QAction* loadedFilesAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("Loaded"); - loadedFilesAction->setData(qVariantFromValue((int)MANAGE_FILES_LOADED)); + loadedFilesAction->setData(QVariant::fromValue((int)MANAGE_FILES_LOADED)); loadedFilesAction->setCheckable(true); QAction* loadedFilesModifiedAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("Loaded:Modified"); - loadedFilesModifiedAction->setData(qVariantFromValue((int)MANAGE_FILES_LOADED_MODIFIED)); + loadedFilesModifiedAction->setData(QVariant::fromValue((int)MANAGE_FILES_LOADED_MODIFIED)); loadedFilesModifiedAction->setCheckable(true); QAction* loadedFilesNotModifiedAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("Loaded:Not Modified"); - loadedFilesNotModifiedAction->setData(qVariantFromValue((int)MANAGE_FILES_LOADED_NOT_MODIFIED)); + loadedFilesNotModifiedAction->setData(QVariant::fromValue((int)MANAGE_FILES_LOADED_NOT_MODIFIED)); loadedFilesNotModifiedAction->setCheckable(true); QAction* notLoadedFilesAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("Not Loaded"); - notLoadedFilesAction->setData(qVariantFromValue((int)MANAGE_FILES_NOT_LOADED)); + notLoadedFilesAction->setData(QVariant::fromValue((int)MANAGE_FILES_NOT_LOADED)); notLoadedFilesAction->setCheckable(true); m_manageFilesLoadedNotLoadedActionGroup->blockSignals(true); @@ -2835,7 +2768,7 @@ StructureEnum::Enum structure = *iter; QAction* action = m_structureActionGroup->addAction(StructureEnum::toGuiName(structure)); action->setCheckable(true); - action->setData(qVariantFromValue(StructureEnum::toIntegerCode(structure))); + action->setData(QVariant::fromValue(StructureEnum::toIntegerCode(structure))); } if (m_structureActionGroup->actions().isEmpty() == false) { @@ -2897,13 +2830,6 @@ void SpecFileManagementDialog::toolBarFileTypeActionTriggered(QAction* /*action*/) { -// if (action != NULL) { -// const int dataValue = action->data().toInt(); -// bool isValid = false; -// const DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromIntegerCode(dataValue, -// &isValid); -// } - loadSpecFileContentIntoDialog(); } @@ -2916,13 +2842,6 @@ void SpecFileManagementDialog::toolBarStructuresActionTriggered(QAction* /*action*/) { -// if (action != NULL) { -// const int dataValue = action->data().toInt(); -// bool isValid = false; -// const StructureEnum::Enum structure = StructureEnum::fromIntegerCode(dataValue, -// &isValid); -// } - loadSpecFileContentIntoDialog(); } @@ -2947,8 +2866,6 @@ void SpecFileManagementDialog::toolBarSelectFilesActionTriggered(QAction* action) { -// m_filesTableWidget->blockSignals(true); - if (action != NULL) { const int dataValue = action->data().toInt(); @@ -2980,8 +2897,6 @@ } } } - -// m_filesTableWidget->blockSignals(false); } /* ======================================================================= */ @@ -3039,13 +2954,6 @@ QString typeName = SpecFileManagementDialog::getEditedDataFileTypeName(dataFileType); /* - * Push surface files to the top??? - */ -// if (dataFileType == DataFileTypeEnum::SURFACE) { -// typeName = "AAAAAA"; -// } - - /* * Push non-specific structures to the bottom */ const StructureEnum::Enum structure = m_specFileDataFile->getStructure(); diff -Nru connectome-workbench-1.4.2/src/GuiQt/SpecFileManagementDialog.h connectome-workbench-1.5.0/src/GuiQt/SpecFileManagementDialog.h --- connectome-workbench-1.4.2/src/GuiQt/SpecFileManagementDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SpecFileManagementDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -111,10 +111,6 @@ void toolBarManageFilesLoadedNotLoadedActionTriggered(QAction* action); -// void chooseSpecFileNameActionTriggered(); - -// void specFileOptionsActionTriggered(); - void fileReloadOrOpenFileActionSelected(int indx); void fileRemoveActionSelected(int indx); @@ -239,7 +235,9 @@ SpecFile* m_specFile; - QPushButton* m_loadScenesPushButton; + QPushButton* m_reloadAllDataFilesPushButton = NULL; + + QPushButton* m_loadScenesPushButton = NULL; QActionGroup* m_fileTypesActionGroup; diff -Nru connectome-workbench-1.4.2/src/GuiQt/SplashScreen.cxx connectome-workbench-1.5.0/src/GuiQt/SplashScreen.cxx --- connectome-workbench-1.4.2/src/GuiQt/SplashScreen.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SplashScreen.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -586,8 +586,9 @@ QTreeWidgetItem* item = new QTreeWidgetItem(itemText); item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); - Qt::ItemFlags flags(Qt::ItemIsEnabled); - item->setFlags(flags); + Qt::ItemFlags flags = item->flags(); + flags.setFlag(Qt::ItemIsSelectable, false); + flags.setFlag(Qt::ItemIsEnabled, true); m_dataFileTreeWidget->addTopLevelItem(item); return item; diff -Nru connectome-workbench-1.4.2/src/GuiQt/StructureSurfaceSelectionControl.cxx connectome-workbench-1.5.0/src/GuiQt/StructureSurfaceSelectionControl.cxx --- connectome-workbench-1.4.2/src/GuiQt/StructureSurfaceSelectionControl.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/StructureSurfaceSelectionControl.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -265,7 +265,7 @@ iter++) { ModelSurface* surfaceController = *iter; this->surfaceControllerSelectionComboBox->addItem(surfaceController->getNameForGUI(allSelected), - qVariantFromValue((void*)surfaceController)); + QVariant::fromValue((void*)surfaceController)); if (selectedSurfaceController == surfaceController) { defaultSurfaceIndex = this->surfaceControllerSelectionComboBox->count() - 1; } diff -Nru connectome-workbench-1.4.2/src/GuiQt/SurfacePropertiesEditorDialog.cxx connectome-workbench-1.5.0/src/GuiQt/SurfacePropertiesEditorDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/SurfacePropertiesEditorDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SurfacePropertiesEditorDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -26,18 +26,28 @@ #include #include +#include #include #include #include +#include using namespace caret; +#include "Brain.h" #include "CaretAssert.h" +#include "DisplayPropertiesSurface.h" +#include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" +#include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" #include "SceneClass.h" #include "SceneWindowGeometry.h" #include "WuQFactory.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" +#include "WbMacroCustomOperationSurfaceDefaultColor.h" #include "WuQMacroManager.h" #include "WuQMacroWidgetAction.h" #include "WbMacroWidgetActionNames.h" @@ -59,6 +69,10 @@ { m_updateInProgress = true; + m_backfaceCullingCheckBox = new QCheckBox("Backface Culling"); + QObject::connect(m_backfaceCullingCheckBox, &QCheckBox::clicked, + this, &SurfacePropertiesEditorDialog::backfaceCullingCheckBoxClicked); + WuQMacroManager* mm = WuQMacroManager::instance(); CaretAssert(mm); @@ -122,25 +136,51 @@ m_opacitySpinBox->setEnabled(false); } + QLabel* surfaceColorLabel = new QLabel("Default Color: "); + QString colorToolTip("Click to set the default surface color that is applied when no overlays color a vertex"); + QToolButton* surfaceSetColorToolButton = new QToolButton(); + surfaceSetColorToolButton->setText("Set..."); + QObject::connect(surfaceSetColorToolButton, &QToolButton::clicked, + this, &SurfacePropertiesEditorDialog::surfaceSetColorToolButtonClicked); + WuQtUtilities::setWordWrappedToolTip(surfaceSetColorToolButton, + colorToolTip); + + QToolButton* surfaceResetColorToolButton = new QToolButton(); + surfaceResetColorToolButton->setText("Reset"); + QObject::connect(surfaceResetColorToolButton, &QToolButton::clicked, + this, &SurfacePropertiesEditorDialog::surfaceResetColorToolButtonClicked); + + m_surfaceColorWidget = new QWidget(); + m_surfaceColorWidget->setMinimumHeight(10); + m_surfaceColorWidget->setMinimumWidth(50); + QWidget* w = new QWidget(); QGridLayout* gridLayout = new QGridLayout(w); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); int row = gridLayout->rowCount(); - gridLayout->addWidget(m_displayNormalVectorsCheckBox, row, 0, 1, 2); + gridLayout->addWidget(m_backfaceCullingCheckBox, row, 0, 1, 3); + row++; + gridLayout->addWidget(m_displayNormalVectorsCheckBox, row, 0, 1, 4); row++; gridLayout->addWidget(surfaceDrawingTypeLabel, row, 0); - gridLayout->addWidget(m_surfaceDrawingTypeComboBox, row, 1); + gridLayout->addWidget(m_surfaceDrawingTypeComboBox, row, 1, 1, 3); row++; gridLayout->addWidget(linkSizeLabel, row, 0); - gridLayout->addWidget(m_linkSizeSpinBox, row, 1); + gridLayout->addWidget(m_linkSizeSpinBox, row, 1, 1, 3); row++; gridLayout->addWidget(nodeSizeLabel, row, 0); - gridLayout->addWidget(m_nodeSizeSpinBox, row, 1); + gridLayout->addWidget(m_nodeSizeSpinBox, row, 1, 1, 3); row++; gridLayout->addWidget(opacityLabel, row, 0); - gridLayout->addWidget(m_opacitySpinBox, row, 1); + gridLayout->addWidget(m_opacitySpinBox, row, 1, 1, 3); + row++; + gridLayout->addWidget(surfaceColorLabel, row, 0); + gridLayout->addWidget(surfaceSetColorToolButton, row, 1); + gridLayout->addWidget(surfaceResetColorToolButton, row, 2); + gridLayout->addWidget(m_surfaceColorWidget, row, 3); row++; + setCentralWidget(w, WuQDialog::SCROLL_AREA_NEVER); @@ -177,6 +217,8 @@ { m_updateInProgress = true; + updateDefaultSurfaceColorWidget(); + WuQMacroManager::instance()->updateValueInWidgetFromMacroWidgetAction(m_surfaceDrawingTypeComboBox, m_linkSizeSpinBox, m_nodeSizeSpinBox, @@ -184,6 +226,10 @@ m_displayNormalVectorsCheckBox); m_updateInProgress = false; + + DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + m_backfaceCullingCheckBox->setChecked(dps->isBackfaceCullingEnabled()); + } /** @@ -203,6 +249,86 @@ } /** + * Called when set surface color tool button is clicked + */ +void +SurfacePropertiesEditorDialog::surfaceSetColorToolButtonClicked() +{ + DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + std::array rgb = dps->getDefaultColorRGB(); + + const QColor initialColor(rgb[0], + rgb[1], + rgb[2]); + + QColorDialog colorDialog(this); + colorDialog.setOption(QColorDialog::DontUseNativeDialog); + colorDialog.setWindowTitle("Default Surface Color RGB"); + colorDialog.setCurrentColor(initialColor); + + if (colorDialog.exec() == QColorDialog::Accepted) { + const QColor newColor = colorDialog.currentColor(); + rgb[0] = newColor.red(); + rgb[1] = newColor.green(); + rgb[2] = newColor.blue(); + dps->setDefaultColorRGB(rgb); + } + + updateDefaultSurfaceColorWidget(); + + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + + /* + * Get new instance of custom macro + */ + WbMacroCustomOperationSurfaceDefaultColor commandOperation; + WuQMacroCommand* command = commandOperation.createCommand(); + if (command->getNumberOfParameters() >= 3) { + command->getParameterAtIndex(0)->setValue(rgb[0]); + command->getParameterAtIndex(1)->setValue(rgb[1]); + command->getParameterAtIndex(2)->setValue(rgb[2]); + + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + if ( ! macroManager->addMacroCommandToRecording(command)) { + /* not recording */ + delete command; + } + } +} + +/** + * Called when reset surface color tool button is clicked + */ +void +SurfacePropertiesEditorDialog::surfaceResetColorToolButtonClicked() +{ + DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + dps->resetDefaultColorRGB(); + + updateDefaultSurfaceColorWidget(); + + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Update the default surface color widget with the color in the surface properties + */ +void +SurfacePropertiesEditorDialog::updateDefaultSurfaceColorWidget() +{ + const DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + const std::array colorRGB = dps->getDefaultColorRGB(); + m_surfaceColorWidget->setStyleSheet("background-color: rgb(" + + AString::number(colorRGB[0]) + + ", " + AString::number(colorRGB[1]) + + ", " + AString::number(colorRGB[2]) + + ");"); +} + +/** * Create a scene for an instance of a class. * * @param sceneAttributes @@ -258,3 +384,14 @@ swg.restoreFromScene(sceneAttributes, sceneClass->getClass("geometry")); } +/** + * Called when backface culling check box clicked + */ +void +SurfacePropertiesEditorDialog::backfaceCullingCheckBoxClicked(bool checked) +{ + DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + dps->setBackfaceCullingEnabled(checked); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/SurfacePropertiesEditorDialog.h connectome-workbench-1.5.0/src/GuiQt/SurfacePropertiesEditorDialog.h --- connectome-workbench-1.4.2/src/GuiQt/SurfacePropertiesEditorDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SurfacePropertiesEditorDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "EventListenerInterface.h" #include "SceneableInterface.h" #include "WuQDialogNonModal.h" @@ -51,13 +53,24 @@ virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); + private slots: + void surfaceSetColorToolButtonClicked(); + + void surfaceResetColorToolButtonClicked(); + + void backfaceCullingCheckBoxClicked(bool checked); + private: SurfacePropertiesEditorDialog(const SurfacePropertiesEditorDialog&); SurfacePropertiesEditorDialog& operator=(const SurfacePropertiesEditorDialog&); + void updateDefaultSurfaceColorWidget(); + QCheckBox* m_displayNormalVectorsCheckBox; + QCheckBox* m_backfaceCullingCheckBox; + QDoubleSpinBox* m_linkSizeSpinBox; QDoubleSpinBox* m_nodeSizeSpinBox; @@ -66,6 +79,8 @@ QDoubleSpinBox* m_opacitySpinBox; + QWidget* m_surfaceColorWidget; + bool m_updateInProgress; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/GuiQt/SurfaceSelectionViewController.cxx connectome-workbench-1.5.0/src/GuiQt/SurfaceSelectionViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/SurfaceSelectionViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/SurfaceSelectionViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -220,7 +220,7 @@ for (int32_t i = 0; i < numSurfaces; i++) { const int32_t indx = this->surfaceComboBox->count(); this->surfaceComboBox->addItem(displayNames[i]); - this->surfaceComboBox->setItemData(indx, qVariantFromValue((void*)surfaces[i])); + this->surfaceComboBox->setItemData(indx, QVariant::fromValue((void*)surfaces[i])); if (surfaces[i] == selectedSurface) { selectedIndex = indx; diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabGridRowColumnWidgets.cxx connectome-workbench-1.5.0/src/GuiQt/TileTabGridRowColumnWidgets.cxx --- connectome-workbench-1.4.2/src/GuiQt/TileTabGridRowColumnWidgets.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabGridRowColumnWidgets.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,392 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __TILE_TAB_GRID_ROW_COLUMN_WIDGETS_DECLARE__ +#include "TileTabGridRowColumnWidgets.h" +#undef __TILE_TAB_GRID_ROW_COLUMN_WIDGETS_DECLARE__ + +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "EnumComboBoxTemplate.h" +#include "TileTabsConfigurationDialog.h" +#include "TileTabsGridRowColumnElement.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "WuQGridLayoutGroup.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::TileTabGridRowColumnWidgets + * \brief Contains info about a tile tabs grid row or column + * \ingroup GuiQt + */ + + +/** + * Constructor. + * + * @param tileTabsConfigurationDialog + * The tile tabs configuration dialog. + * @param rowColumnType + * 'Row' or 'Column' + * @param index + * Index of the row/column + * @param gridLayout + * Gridlayout for widgets + * @param parent + * Parent QObject + */ +TileTabGridRowColumnWidgets::TileTabGridRowColumnWidgets(TileTabsConfigurationDialog* tileTabsConfigurationDialog, + const EventTileTabsGridConfigurationModification::RowColumnType rowColumnType, + const int32_t index, + QGridLayout* gridLayout, + QObject* parent) +: QObject(parent), +m_tileTabsConfigurationDialog(tileTabsConfigurationDialog), +m_rowColumnType(rowColumnType), +m_index(index), +m_element(NULL) +{ + m_indexLabel = new QLabel(QString::number(m_index + 1)); + m_indexLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + const AString rowColText((rowColumnType == EventTileTabsGridConfigurationModification::RowColumnType::ROW) + ? "Row" + : "Column"); + const AString contructionToolTip(WuQtUtilities::createWordWrappedToolTipText("Delete, Duplicate, or Move " + + rowColText)); + const AString contentToolTip(WuQtUtilities::createWordWrappedToolTipText("Content of the " + + rowColText + + ": Spacer (empty space for Annotations) " + "or Tabs (Browser Tabs)")); + const AString typeToolTip(WuQtUtilities::createWordWrappedToolTipText("Type of Stretching: Percent or Weight")); + const AString stretchToolTip(WuQtUtilities::createWordWrappedToolTipText("Value of Stretching Percentage [0, 100] or Stretching Weight")); + + /* + * Construction Tool Button + */ + QIcon constructionIcon; + const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", + constructionIcon); + m_constructionAction = WuQtUtilities::createAction("M", + "Add/Move/Remove", + this); + if (constructionIconValid) { + m_constructionAction->setIcon(constructionIcon); + } + m_constructionToolButton = new QToolButton(); + QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton); + QObject::connect(constructionMenu, &QMenu::aboutToShow, + this, &TileTabGridRowColumnWidgets::constructionMenuAboutToShow); + QObject::connect(constructionMenu, &QMenu::triggered, + this, &TileTabGridRowColumnWidgets::constructionMenuTriggered); + m_constructionAction->setMenu(constructionMenu); + m_constructionToolButton->setDefaultAction(m_constructionAction); + m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); + m_constructionToolButton->setFixedWidth(m_constructionToolButton->sizeHint().width()); + m_constructionToolButton->setToolTip(contructionToolTip); + + /* + * Content type combo box + */ + m_contentTypeComboBox = new EnumComboBoxTemplate(this); + m_contentTypeComboBox->setup(); + QObject::connect(m_contentTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &TileTabGridRowColumnWidgets::contentTypeActivated); + m_contentTypeComboBox->getComboBox()->setFixedWidth(m_contentTypeComboBox->getComboBox()->sizeHint().width()); + m_contentTypeComboBox->getComboBox()->setToolTip(contentToolTip); + + /* + * Stretch type combo box + */ + m_stretchTypeComboBox = new EnumComboBoxTemplate(this); + m_stretchTypeComboBox->setup(); + QObject::connect(m_stretchTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &TileTabGridRowColumnWidgets::stretchTypeActivated); + m_stretchTypeComboBox->getComboBox()->setFixedWidth(m_stretchTypeComboBox->getComboBox()->sizeHint().width()); + m_stretchTypeComboBox->getComboBox()->setToolTip(typeToolTip); + + /* + * Stretch value spin box + */ + m_stretchValueSpinBox = new QDoubleSpinBox(); + m_stretchValueSpinBox->setKeyboardTracking(false); + m_stretchValueSpinBox->setRange(0.0, 1000.0); + m_stretchValueSpinBox->setDecimals(2); + m_stretchValueSpinBox->setSingleStep(0.1); + QObject::connect(m_stretchValueSpinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &TileTabGridRowColumnWidgets::stretchValueChanged); + m_stretchValueSpinBox->setFixedWidth(m_stretchValueSpinBox->sizeHint().width()); + m_stretchValueSpinBox->setToolTip(stretchToolTip); + + m_gridLayoutGroup = new WuQGridLayoutGroup(gridLayout); + const int32_t rowIndex(gridLayout->rowCount()); + int32_t columnIndex(0); + m_gridLayoutGroup->addWidget(m_indexLabel, rowIndex, columnIndex++, Qt::AlignRight); + m_gridLayoutGroup->addWidget(m_constructionToolButton, rowIndex, columnIndex++); + m_gridLayoutGroup->addWidget(m_contentTypeComboBox->getWidget(), rowIndex, columnIndex++); + m_gridLayoutGroup->addWidget(m_stretchTypeComboBox->getWidget(), rowIndex, columnIndex++); + m_gridLayoutGroup->addWidget(m_stretchValueSpinBox, rowIndex, columnIndex++); +} + +/** + * Destructor. + */ +TileTabGridRowColumnWidgets::~TileTabGridRowColumnWidgets() +{ + +} + +/** + * @return The construction menu. + * + * @param toolButton + * The parent toolbutton. + */ +QMenu* +TileTabGridRowColumnWidgets::createConstructionMenu(QToolButton* toolButton) +{ + const AString deleteText((m_rowColumnType == EventTileTabsGridConfigurationModification::RowColumnType::COLUMN) + ? "Delete this Column" + : "Delete this Row"); + + const AString duplicateAfterText((m_rowColumnType == EventTileTabsGridConfigurationModification::RowColumnType::COLUMN) + ? "Duplicate this Column to Right" + : "Duplicate this Row Below"); + + const AString duplicateBeforeText((m_rowColumnType == EventTileTabsGridConfigurationModification::RowColumnType::COLUMN) + ? "Duplicate this Column to Left" + : "Duplicate this Row Above"); + + const AString insertSpacerAfterText((m_rowColumnType == EventTileTabsGridConfigurationModification::RowColumnType::COLUMN) + ? "Insert Spacer Column to Right" + : "Insert Spacer Row Below"); + const AString insertSpacerBeforeText((m_rowColumnType == EventTileTabsGridConfigurationModification::RowColumnType::COLUMN) + ? "Insert Spacer Column to Left" + : "Insert Spacer Row Above"); + const AString moveAfterText((m_rowColumnType == EventTileTabsGridConfigurationModification::RowColumnType::COLUMN) + ? "Move this Column to Right" + : "Move this Row Down"); + + const AString moveBeforeText((m_rowColumnType == EventTileTabsGridConfigurationModification::RowColumnType::COLUMN) + ? "Move this Column to Left" + : "Move this Row Up"); + + m_menuDeleteAction = new QAction(deleteText); + m_menuDeleteAction->setData(static_cast(EventTileTabsGridConfigurationModification::Operation::DELETE_IT)); + + m_menuDuplicateAfterAction = new QAction(duplicateAfterText); + m_menuDuplicateAfterAction->setData(static_cast(EventTileTabsGridConfigurationModification::Operation::DUPLICATE_AFTER)); + + m_menuDuplicateBeforeAction = new QAction(duplicateBeforeText); + m_menuDuplicateBeforeAction->setData(static_cast(EventTileTabsGridConfigurationModification::Operation::DUPLICATE_BEFORE)); + + m_insertSpacerAfterAction = new QAction(insertSpacerAfterText); + m_insertSpacerAfterAction->setData(static_cast(EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_AFTER)); + + m_insertSpacerBeforeAction = new QAction(insertSpacerBeforeText); + m_insertSpacerBeforeAction->setData(static_cast(EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_BEFORE)); + + m_menuMoveAfterAction = new QAction(moveAfterText); + m_menuMoveAfterAction->setData(static_cast(EventTileTabsGridConfigurationModification::Operation::MOVE_AFTER)); + + m_menuMoveBeforeAction = new QAction(moveBeforeText); + m_menuMoveBeforeAction->setData(static_cast(EventTileTabsGridConfigurationModification::Operation::MOVE_BEFORE)); + + QMenu* menu = new QMenu(toolButton); + menu->addAction(m_menuDuplicateBeforeAction); + menu->addAction(m_menuDuplicateAfterAction); + menu->addSeparator(); + menu->addAction(m_insertSpacerBeforeAction); + menu->addAction(m_insertSpacerAfterAction); + menu->addSeparator(); + menu->addAction(m_menuMoveBeforeAction); + menu->addAction(m_menuMoveAfterAction); + menu->addSeparator(); + menu->addAction(m_menuDeleteAction); + + return menu; +} + +/** + * Update with the given row/column element. + */ +void +TileTabGridRowColumnWidgets::updateContent(TileTabsGridRowColumnElement* element) +{ + m_element = element; + const bool showFlag(m_element != NULL); + + if (showFlag) { + m_contentTypeComboBox->setSelectedItem(element->getContentType()); + m_stretchTypeComboBox->setSelectedItem(element->getStretchType()); + QSignalBlocker valueBlocker(m_stretchValueSpinBox); + switch (m_element->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + m_stretchValueSpinBox->setRange(0.0, 100.0); + m_stretchValueSpinBox->setSingleStep(1.0); + m_stretchValueSpinBox->setValue(m_element->getPercentStretch()); + m_stretchValueSpinBox->setSuffix("%"); + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + m_stretchValueSpinBox->setRange(0.0, 1000.0); + m_stretchValueSpinBox->setSingleStep(0.1); + m_stretchValueSpinBox->setValue(m_element->getWeightStretch()); + m_stretchValueSpinBox->setSuffix(""); + break; + } + + /* + * These configuration widgets are placed into a stacked widget. The stacked widget + * is disabled when an automatic configuration is selected. However, when the stacked + * widget is enabled (custom grid selected), the menu action fails to get reenabled and + * that causes the menu to remain disabled. So, enable the menu here so that + * the construction menu is enabled correctly. + */ + m_constructionAction->setEnabled(true); + } + + m_gridLayoutGroup->setVisible(showFlag); +} + +/** + * Called when an item is selected from the construction menu + * + * @param action + * Action that was selected. + */ +void +TileTabGridRowColumnWidgets::constructionMenuTriggered(QAction* action) +{ + if (action != NULL) { + const EventTileTabsGridConfigurationModification::Operation operation + = static_cast(action->data().toInt()); + + /* + * This switch is here so that it will cause a compilation error + * if the operations are changed. + */ + switch (operation) { + case EventTileTabsGridConfigurationModification::Operation::DELETE_IT: + break; + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_AFTER: + break; + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_BEFORE: + break; + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_BEFORE: + break; + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_AFTER: + break; + case EventTileTabsGridConfigurationModification::Operation::MOVE_AFTER: + break; + case EventTileTabsGridConfigurationModification::Operation::MOVE_BEFORE: + break; + } + + EventTileTabsGridConfigurationModification modification(m_tileTabsConfigurationDialog->getCustomTileTabsGridConfiguration(), + m_index, + m_rowColumnType, + operation); + emit modificationRequested(modification); + } + +} + +/** + * Called when construction menu is about to show. + */ +void +TileTabGridRowColumnWidgets::constructionMenuAboutToShow() +{ + const TileTabsLayoutGridConfiguration* config = m_tileTabsConfigurationDialog->getCustomTileTabsGridConfiguration(); + if (config != NULL) { + int32_t numItems(-1); + switch (m_rowColumnType) { + case EventTileTabsGridConfigurationModification::RowColumnType::COLUMN: + numItems = config->getNumberOfColumns(); + break; + case EventTileTabsGridConfigurationModification::RowColumnType::ROW: + numItems = config->getNumberOfRows(); + break; + } + + m_menuDeleteAction->setEnabled(numItems > 1); + m_menuDuplicateAfterAction->setEnabled(numItems >= 1); + m_menuDuplicateBeforeAction->setEnabled(numItems >= 1); + m_menuMoveAfterAction->setEnabled((numItems > 1) + && (m_index < (numItems - 1))); + m_menuMoveBeforeAction->setEnabled((numItems > 1) + && (m_index > 0)); + } +} + + +/** + * Called when content type combo box changed. + */ +void +TileTabGridRowColumnWidgets::contentTypeActivated() +{ + if (m_element != NULL) { + m_element->setContentType(m_contentTypeComboBox->getSelectedItem()); + emit itemChanged(); + } +} + +/** + * Called when stretch type combo box changed. + */ +void +TileTabGridRowColumnWidgets::stretchTypeActivated() +{ + if (m_element != NULL) { + m_element->setStretchType(m_stretchTypeComboBox->getSelectedItem()); + emit itemChanged(); + } +} + +/** + * Called when stretch value changed. + */ +void +TileTabGridRowColumnWidgets::stretchValueChanged(double) +{ + if (m_element != NULL) { + switch (m_element->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + m_element->setPercentStretch(m_stretchValueSpinBox->value()); + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + m_element->setWeightStretch(m_stretchValueSpinBox->value()); + break; + } + emit itemChanged(); + } +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabGridRowColumnWidgets.h connectome-workbench-1.5.0/src/GuiQt/TileTabGridRowColumnWidgets.h --- connectome-workbench-1.4.2/src/GuiQt/TileTabGridRowColumnWidgets.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabGridRowColumnWidgets.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,112 @@ +#ifndef __TILE_TAB_GRID_ROW_COLUMN_WIDGETS_H__ +#define __TILE_TAB_GRID_ROW_COLUMN_WIDGETS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "EventTileTabsGridConfigurationModification.h" + +class QAction; +class QDoubleSpinBox; +class QGridLayout; +class QLabel; +class QMenu; +class QToolButton; + +namespace caret { + + class EnumComboBoxTemplate; + class TileTabsConfigurationDialog; + class TileTabsGridRowColumnElement; + class WuQGridLayoutGroup; + + /** + * Contains widgets for one row or column of stretching. + */ + class TileTabGridRowColumnWidgets : public QObject { + Q_OBJECT + + public: + TileTabGridRowColumnWidgets(TileTabsConfigurationDialog* tileTabsConfigurationDialog, + const EventTileTabsGridConfigurationModification::RowColumnType rowColumnType, + const int32_t index, + QGridLayout* gridLayout, + QObject* parent); + + virtual ~TileTabGridRowColumnWidgets(); + + void updateContent(TileTabsGridRowColumnElement* element); + + signals: + void itemChanged(); + + void modificationRequested(EventTileTabsGridConfigurationModification& modification); + + private slots: + void constructionMenuAboutToShow(); + + void constructionMenuTriggered(QAction*); + + void contentTypeActivated(); + + void stretchTypeActivated(); + + void stretchValueChanged(double); + + private: + QMenu* createConstructionMenu(QToolButton* toolButton); + + TileTabsConfigurationDialog* m_tileTabsConfigurationDialog; + const EventTileTabsGridConfigurationModification::RowColumnType m_rowColumnType; + const int32_t m_index; + TileTabsGridRowColumnElement* m_element; + + QLabel* m_indexLabel; + QAction* m_constructionAction; + QToolButton* m_constructionToolButton; + EnumComboBoxTemplate* m_contentTypeComboBox; + EnumComboBoxTemplate* m_stretchTypeComboBox; + QDoubleSpinBox* m_stretchValueSpinBox; + + QAction* m_menuDeleteAction; + QAction* m_menuDuplicateAfterAction; + QAction* m_menuDuplicateBeforeAction; + QAction* m_insertSpacerAfterAction; + QAction* m_insertSpacerBeforeAction; + QAction* m_menuMoveAfterAction; + QAction* m_menuMoveBeforeAction; + + WuQGridLayoutGroup* m_gridLayoutGroup; + + }; + + +#ifdef __TILE_TAB_GRID_ROW_COLUMN_WIDGETS_DECLARE__ + // +#endif // __TILE_TAB_GRID_ROW_COLUMN_WIDGETS_DECLARE__ + +} // namespace +#endif //__TILE_TAB_GRID_ROW_COLUMN_WIDGETS_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabsConfigurationDialog.cxx connectome-workbench-1.5.0/src/GuiQt/TileTabsConfigurationDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/TileTabsConfigurationDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabsConfigurationDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -19,6 +19,8 @@ */ /*LICENSE_END*/ +#include + #include #include #include @@ -28,10 +30,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -40,26 +44,39 @@ #include "TileTabsConfigurationDialog.h" #undef __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ +#include "AnnotationBrowserTab.h" #include "BrainBrowserWindow.h" #include "BrainBrowserWindowComboBox.h" +#include "BrainOpenGLViewportContent.h" +#include "BrowserTabContent.h" #include "BrowserWindowContent.h" #include "CaretAssert.h" +#include "CaretLogger.h" #include "CaretPreferences.h" #include "EnumComboBoxTemplate.h" +#include "EventBrowserTabDeleteInToolBar.h" +#include "EventBrowserTabNewInGUI.h" #include "EventBrowserWindowGraphicsRedrawn.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventHelpViewerDisplay.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" +#include "MathFunctions.h" #include "SessionManager.h" -#include "TileTabsConfiguration.h" +#include "TileTabsBrowserTabGeometry.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "TileTabsLayoutManualConfiguration.h" +#include "TileTabsManualTabGeometryWidget.h" #include "TileTabsGridRowColumnElement.h" +#include "TileTabGridRowColumnWidgets.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQGridLayoutGroup.h" +#include "WuQDataEntryDialog.h" #include "WuQListWidget.h" #include "WuQMessageBox.h" +#include "WuQTextEditorDialog.h" #include "WuQtUtilities.h" using namespace caret; @@ -82,37 +99,45 @@ : WuQDialogNonModal("Tile Tabs Configurations", parentBrainBrowserWindow) { - m_blockReadConfigurationsFromPreferences = false; + m_blockReadUserConfigurationsFromPreferences = false; m_caretPreferences = SessionManager::get()->getCaretPreferences(); + QWidget* workbenchWindowWidget = createWorkbenchWindowWidget(); - QWidget* dialogWidget = new QWidget(); - QHBoxLayout* configurationLayout = new QHBoxLayout(dialogWidget); - configurationLayout->setSpacing(0); - configurationLayout->addWidget(createActiveConfigurationWidget(), - 0); - configurationLayout->addWidget(createCopyLoadPushButtonsWidget(), - 0, - Qt::AlignTop); - configurationLayout->addWidget(createUserConfigurationSelectionWidget(), - 100, - Qt::AlignTop); setApplyButtonText(""); setStandardButtonText(QDialogButtonBox::Help, "Help"); - updateDialogWithSelectedTileTabsFromWindow(parentBrainBrowserWindow); - - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); - + QWidget* dialogWidget = new QWidget(); + QGridLayout* dialogLayout = new QGridLayout(dialogWidget); + dialogLayout->setColumnStretch(0, 0); + dialogLayout->setColumnStretch(1, 0); + dialogLayout->setColumnStretch(2, 100); + dialogLayout->addWidget(workbenchWindowWidget, + 0, 0, Qt::AlignLeft); + dialogLayout->addWidget(createConfigurationTypeWidget(), + 1, 0); + dialogLayout->addWidget(createConfigurationSettingsWidget(), + 2, 0, 1, 2); + dialogLayout->addWidget(createCopyLoadPushButtonsWidget(), + 0, 1, 2, 1, Qt::AlignBottom); + dialogLayout->addWidget(createUserConfigurationSelectionWidget(), + 0, 2, 3, 1); + setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); - resize(750, + updateDialogWithSelectedTileTabsFromWindow(parentBrainBrowserWindow); + + resize(900, 500); disableAutoDefaultForAllPushButtons(); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE); + } /** @@ -143,6 +168,9 @@ updateDialog(); } } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE) { + updateTemplateUserConfigurationPushButtons(getSelectedConfigurationSourceType()); + } } /** @@ -160,71 +188,214 @@ QWidget* TileTabsConfigurationDialog::createCopyLoadPushButtonsWidget() { - m_replacePushButton = new QPushButton("Replace -->"); - m_replacePushButton->setAutoDefault(false); - WuQtUtilities::setWordWrappedToolTip(m_replacePushButton, - "Replace the Rows, Columns, and Stretch Factors in the User Configuration " - "with those from the Custom Configuration"); - QObject::connect(m_replacePushButton, SIGNAL(clicked()), + m_addConfigurationPushButton = new QPushButton("Add -->"); + m_addConfigurationPushButton->setAutoDefault(false); + WuQtUtilities::setWordWrappedToolTip(m_addConfigurationPushButton, + "Create a new User Configuration containg the Configuration Settings. " + "A dialog is displayed to enter the name."); + QObject::connect(m_addConfigurationPushButton, SIGNAL(clicked()), + this, SLOT(addUserConfigurationPushButtonClicked())); + + m_replaceConfigurationPushButton = new QPushButton("Replace -->"); + m_replaceConfigurationPushButton->setAutoDefault(false); + WuQtUtilities::setWordWrappedToolTip(m_replaceConfigurationPushButton, + "Replace the User Configuration with the Configuration Settings."); + QObject::connect(m_replaceConfigurationPushButton, SIGNAL(clicked()), this, SLOT(replaceUserConfigurationPushButtonClicked())); - m_loadPushButton = new QPushButton("<-- Load"); - m_loadPushButton->setAutoDefault(false); - WuQtUtilities::setWordWrappedToolTip(m_loadPushButton, - "Load the User Configuration's Rows, Columns, and Stretch Factors into " - "the Custom Configuration"); - QObject::connect(m_loadPushButton, SIGNAL(clicked()), + m_loadConfigurationPushButton = new QPushButton("<-- Load"); + m_loadConfigurationPushButton->setAutoDefault(false); + WuQtUtilities::setWordWrappedToolTip(m_loadConfigurationPushButton, + "Load the selected User Configuration into the Configuration Type and Settings."); + QObject::connect(m_loadConfigurationPushButton, SIGNAL(clicked()), this, SLOT(loadIntoActiveConfigurationPushButtonClicked())); + + WuQtUtilities::matchWidgetWidths(m_addConfigurationPushButton, + m_replaceConfigurationPushButton, + m_loadConfigurationPushButton); QWidget* widget = new QWidget(); + widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QVBoxLayout* layout = new QVBoxLayout(widget); - layout->addSpacing(50); - layout->addWidget(m_replacePushButton); - layout->addSpacing(50); - layout->addWidget(m_loadPushButton); - layout->addStretch(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_addConfigurationPushButton, 0, Qt::AlignHCenter); + layout->addWidget(m_replaceConfigurationPushButton, 0, Qt::AlignHCenter); + layout->addSpacing(10); + layout->addWidget(m_loadConfigurationPushButton, 0, Qt::AlignHCenter); return widget; } /** + * @return Name for new tile tabs configuration + * If empty, user cancelled. + */ +AString +TileTabsConfigurationDialog::getNewConfigurationName(QWidget* dialogParent) +{ + AString newTileTabsName; + + bool exitLoop = false; + while (exitLoop == false) { + /* + * Popup dialog to get name for new configuration + */ + WuQDataEntryDialog ded("New Tile Tabs Configuration", + dialogParent); + + QLineEdit* nameLineEdit = ded.addLineEditWidget("Configuration Name"); + nameLineEdit->setText(newTileTabsName); + if (ded.exec() == WuQDataEntryDialog::Accepted) { + /* + * Make sure name is not empty + */ + newTileTabsName = nameLineEdit->text().trimmed(); + if (newTileTabsName.isEmpty()) { + WuQMessageBox::errorOk(dialogParent, + "Empty name is invalid."); + } + else { + /* + * See if a configuration with the user entered name already exists + */ + const TileTabsLayoutBaseConfiguration* configuration = m_caretPreferences->getTileTabsUserConfigurationByName(newTileTabsName); + if (configuration != NULL) { + const QString msg = ("Configuration named \"" + + newTileTabsName + + "\" already exits. Choose a different name."); + WuQMessageBox::errorOk(dialogParent, msg); + } + else { + /* + * Have new name + */ + exitLoop = true; + } + } + } + else { + /* + * User pressed cancel button. + */ + exitLoop = true; + newTileTabsName = ""; + } + } + + return newTileTabsName; +} + +/** * Called when Replace to user configuration pushbutton is clicked. */ void -TileTabsConfigurationDialog::replaceUserConfigurationPushButtonClicked() +TileTabsConfigurationDialog::addUserConfigurationPushButtonClicked() { - m_blockReadConfigurationsFromPreferences = true; - - const TileTabsConfiguration* activeConfiguration = getCustomTileTabsConfiguration(); - CaretAssert(activeConfiguration); + const AString newConfigName = getNewConfigurationName(m_addConfigurationPushButton); + if (newConfigName.isEmpty()) { + return; + } + + m_blockReadUserConfigurationsFromPreferences = true; + + switch (getBrowserWindowContent()->getTileTabsConfigurationMode()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + CaretAssert(0); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + { + const TileTabsLayoutGridConfiguration* activeConfiguration = getCustomTileTabsGridConfiguration(); + CaretAssert(activeConfiguration); + + m_caretPreferences->addTileTabsUserConfiguration(activeConfiguration, + newConfigName); + } + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + { + TileTabsLayoutManualConfiguration* manualConfig = createManualConfigurationFromCurrentTabs(); + if (manualConfig != NULL) { + m_caretPreferences->addTileTabsUserConfiguration(manualConfig, + newConfigName); + delete manualConfig; + } + else { + WuQMessageBox::errorOk(m_addConfigurationPushButton, + "Tile Tabs MUST be enabled."); + } + } + break; + } + + m_blockReadUserConfigurationsFromPreferences = false; + updateDialog(); +} - TileTabsConfiguration* userConfiguration = getSelectedUserTileTabsConfiguration(); - if (userConfiguration == NULL) { - newUserConfigurationButtonClicked(); - userConfiguration = getSelectedUserTileTabsConfiguration(); - if (userConfiguration == NULL) { +/** + * Called when Replace to user configuration pushbutton is clicked. + */ +void +TileTabsConfigurationDialog::replaceUserConfigurationPushButtonClicked() +{ + CaretAssert(getSelectedConfigurationSourceType() == ConfigurationSourceTypeEnum::USER); + + const AString tileTabsUniqueID = getSelectedUserTileTabsConfigurationUniqueIdentifier(); + if (tileTabsUniqueID.isEmpty()) { WuQMessageBox::errorOk(this, - "There are no user configurations"); - m_blockReadConfigurationsFromPreferences = false; + "There are no user configurations, use Add button."); return; - } } else { - const AString msg("Do you want to replace " - + userConfiguration->getName() - + "?"); - if ( ! WuQMessageBox::warningOkCancel(m_replacePushButton, + const AString msg("Do you want to replace the configuration?"); + if ( ! WuQMessageBox::warningOkCancel(m_replaceConfigurationPushButton, msg)) { - m_blockReadConfigurationsFromPreferences = false; + m_blockReadUserConfigurationsFromPreferences = false; return; } } - userConfiguration->copy(*activeConfiguration); - - m_caretPreferences->writeTileTabsConfigurations(); + m_blockReadUserConfigurationsFromPreferences = true; - m_blockReadConfigurationsFromPreferences = false; + switch (getBrowserWindowContent()->getTileTabsConfigurationMode()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + CaretAssert(0); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + { + const TileTabsLayoutGridConfiguration* activeConfiguration = getCustomTileTabsGridConfiguration(); + CaretAssert(activeConfiguration); + + AString errorMessage; + if ( ! m_caretPreferences->replaceTileTabsUserConfiguration(tileTabsUniqueID, + activeConfiguration, + errorMessage)) { + WuQMessageBox::errorOk(m_replaceConfigurationPushButton, + errorMessage); + } + } + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + { + TileTabsLayoutManualConfiguration* manualConfig = createManualConfigurationFromCurrentTabs(); + if (manualConfig != NULL) { + AString errorMessage; + if ( ! m_caretPreferences->replaceTileTabsUserConfiguration(tileTabsUniqueID, + manualConfig, + errorMessage)) { + WuQMessageBox::errorOk(m_replaceConfigurationPushButton, + errorMessage); + } + delete manualConfig; + } + else { + WuQMessageBox::errorOk(m_addConfigurationPushButton, + "Tile Tabs MUST be enabled."); + } + } + break; + } + + m_blockReadUserConfigurationsFromPreferences = false; updateDialog(); } @@ -234,30 +405,432 @@ void TileTabsConfigurationDialog::loadIntoActiveConfigurationPushButtonClicked() { - if (m_automaticConfigurationRadioButton->isChecked()) { + std::unique_ptr configuration; + switch (getSelectedConfigurationSourceType()) { + case TEMPLATE: + { + configuration = getSelectedTemplateConfiguration(); + } + break; + case USER: + { + const AString userConfigID = getSelectedUserTileTabsConfigurationUniqueIdentifier(); + if (userConfigID.isEmpty()) { + WuQMessageBox::errorOk(m_loadConfigurationPushButton, "No user configuration is selected."); + return; + } + + configuration = m_caretPreferences->getCopyOfTileTabsUserConfigurationByUniqueIdentifier(userConfigID); + if ( ! configuration) { + WuQMessageBox::errorOk(m_loadConfigurationPushButton, + ("User configuration with UniqueID=" + + userConfigID + + " was not found")); + return; + } + } + break; + } + + if ( ! configuration) { return; } - TileTabsConfiguration* activeConfiguration = getCustomTileTabsConfiguration(); - CaretAssert(activeConfiguration); + switch (configuration->getLayoutType()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + CaretAssert(0); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + { + TileTabsLayoutGridConfiguration* customGridConfiguration = getCustomTileTabsGridConfiguration(); + CaretAssert(customGridConfiguration); + + const TileTabsLayoutGridConfiguration* userGridConfig = configuration->castToGridConfiguration(); + CaretAssert(userGridConfig); + + if ( ! warnIfGridConfigurationTooSmallDialog(userGridConfig)) { + return; + } + + customGridConfiguration->copy(*userGridConfig); + + getBrowserWindowContent()->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID); + } + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + { + const TileTabsLayoutManualConfiguration* userManualConfig = configuration->castToManualConfiguration(); + CaretAssert(userManualConfig); + + loadIntoManualConfiguration(userManualConfig); + + getBrowserWindowContent()->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::MANUAL); + } + break; + } - const TileTabsConfiguration* userConfiguration = getSelectedUserTileTabsConfiguration(); - if (userConfiguration == NULL) { - WuQMessageBox::errorOk(this, - "There are no user configurations"); - return; + updateDialog(); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + updateGraphicsWindow(); +} + +/* + * Load the given configuration into the manual configuration + * + * @param configuration + * Configuration that is loaded into the manual configuration + * @return + * True if the configuration was loaded or false if user cancelled + */ +bool +TileTabsConfigurationDialog::loadIntoManualConfiguration(const TileTabsLayoutBaseConfiguration* configuration) +{ + CaretAssert(configuration); + + std::vector allTabContent; + getBrowserWindow()->getAllTabContent(allTabContent); + int32_t numBrowserTabs = static_cast(allTabContent.size()); + + std::vector tabIndices; + for (auto tc : allTabContent) { + CaretAssert(tc); + tabIndices.push_back(tc->getTabNumber()); + } + + std::unique_ptr manualConfiguration; + + switch (configuration->getLayoutType()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + { + const TileTabsLayoutGridConfiguration* gridConfig = configuration->castToGridConfiguration(); + CaretAssert(gridConfig); + + manualConfiguration.reset(TileTabsLayoutManualConfiguration::newInstanceFromGridLayout(const_cast(gridConfig), + gridConfig->getLayoutType(), + tabIndices)); + } + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + { + const TileTabsLayoutGridConfiguration* gridConfig = configuration->castToGridConfiguration(); + CaretAssert(gridConfig); + manualConfiguration.reset(TileTabsLayoutManualConfiguration::newInstanceFromGridLayout(const_cast(gridConfig), + gridConfig->getLayoutType(), + tabIndices)); + + } + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + { + const TileTabsLayoutManualConfiguration* manConfig = configuration->castToManualConfiguration(); + CaretAssert(manConfig); + manualConfiguration.reset(manConfig->newCopyWithNewUniqueIdentifier()->castToManualConfiguration()); + } } - activeConfiguration->copy(*userConfiguration); + CaretAssert(manualConfiguration); + const int32_t numberOfConfigurationTabs = manualConfiguration->getNumberOfTabs(); + if (numberOfConfigurationTabs <= 0) { + WuQMessageBox::errorOk(m_loadConfigurationPushButton, + ("User configuration \"" + + configuration->getName() + + "\" is invalid (does not have any tabs)")); + return false; + } + + /* + * Display status of tabs must be set after + * configuration is copied from User Configurations + */ + std::set tabsWithDisplayStatusOn; + std::set tabsWithDisplayStatusOff; - updateStretchFactors(); + if (numBrowserTabs == numberOfConfigurationTabs) { + /* OK */ + } + else { + AString msg("" + "The Window contains " + + AString::number(numBrowserTabs) + + " tabs.
" + "The Layout has space for " + + AString::number(numberOfConfigurationTabs) + + " tabs" + "

"); + if (numBrowserTabs < numberOfConfigurationTabs) { + msg.append("Do you want to add tabs to fill layout?:"); + WuQDataEntryDialog dialog("Load Configuration", + m_loadConfigurationPushButton, + WuQDialogNonModal::SCROLL_AREA_NEVER); + dialog.setTextAtTop(msg, true); + QRadioButton* yesRadioButton = dialog.addRadioButton("Yes, add tabs to fill layout"); + QRadioButton* yesDisableDisplayRadioButton = dialog.addRadioButton("Yes, add tabs but disable display of them"); + QRadioButton* noRadioButton = dialog.addRadioButton("No, discard extra spaces in layout"); + yesDisableDisplayRadioButton->setChecked(true); + if (dialog.exec() == WuQDataEntryDialog::Accepted) { + bool addTabsFlag(false); + bool showTabsFlag(false); + if (yesRadioButton->isChecked()) { + addTabsFlag = true; + showTabsFlag = true; + } + else if (yesDisableDisplayRadioButton->isChecked()) { + addTabsFlag = true; + } + else if (noRadioButton->isChecked()) { + /* No action needed */ + } + else { + CaretAssert(0); + } + + if (addTabsFlag) { + const int32_t numTabsToAdd = numberOfConfigurationTabs - numBrowserTabs; + for (int32_t i = 0; i < numTabsToAdd; i++) { + EventBrowserTabNewInGUI newTabEvent; + EventManager::get()->sendEvent(newTabEvent.getPointer()); + BrowserTabContent* btc = newTabEvent.getBrowserTab(); + updateGraphicsWindow(); + if (btc != NULL) { + if (showTabsFlag) { + tabsWithDisplayStatusOn.insert(btc); + } + else { + tabsWithDisplayStatusOff.insert(btc); + } + } + } + } + } + else { + return false; + } + } + else { + msg.append("Do you want to:"); + + WuQDataEntryDialog dialog("Load Configuration", + m_loadConfigurationPushButton, + WuQDialogNonModal::SCROLL_AREA_NEVER); + dialog.setTextAtTop(msg, true); + QRadioButton* closeTabsRadioButton = dialog.addRadioButton("Close extra tabs"); + QRadioButton* expandLayoutRadioButton = dialog.addRadioButton("Expand layout to include tabs"); + expandLayoutRadioButton->setChecked(true); + if (dialog.exec() == WuQDataEntryDialog::Accepted) { + if (closeTabsRadioButton->isChecked()) { + const int32_t numTabsToDelete = numBrowserTabs - numberOfConfigurationTabs; + int32_t deleteIndex = numBrowserTabs - 1; + for (int32_t i = 0; i < numTabsToDelete; i++) { + CaretAssertVectorIndex(allTabContent, deleteIndex); + EventBrowserTabDeleteInToolBar deleteTabEvent(allTabContent[deleteIndex], + allTabContent[deleteIndex]->getTabNumber()); + EventManager::get()->sendEvent(deleteTabEvent.getPointer()); + updateGraphicsWindow(); + deleteIndex--; + } + } + else if (expandLayoutRadioButton->isChecked()) { + /* Position and size from within tab will be used */ + } + else { + CaretAssert(0); + } + } + else { + return false; + } + } + } + + /* + * Get the tabs again since the number of tabs may have changed + */ + allTabContent.clear(); + getBrowserWindow()->getAllTabContent(allTabContent); + numBrowserTabs = static_cast(allTabContent.size()); + + std::vector tabViewports; + getBrowserWindow()->getAllBrainOpenGLViewportContent(tabViewports); + const int32_t numViewportContent = static_cast(tabViewports.size()); + + for (int32_t i = 0; i < numBrowserTabs; i++) { + CaretAssertVectorIndex(allTabContent, i); + AnnotationBrowserTab* browserTabAnnotation = allTabContent[i]->getManualLayoutBrowserTabAnnotation(); + CaretAssert(browserTabAnnotation); + + if (i < numberOfConfigurationTabs) { + + const TileTabsBrowserTabGeometry* configGeometry = manualConfiguration->getTabInfo(i); + CaretAssert(configGeometry); + + browserTabAnnotation->setFromTileTabsGeometry(configGeometry); + + BrowserTabContent* btc = browserTabAnnotation->getBrowserTabContent(); + if (tabsWithDisplayStatusOn.find(btc) != tabsWithDisplayStatusOn.end()) { + browserTabAnnotation->setBrowserTabDisplayed(true); + } + else if (tabsWithDisplayStatusOff.find(btc) != tabsWithDisplayStatusOff.end()) { + browserTabAnnotation->setBrowserTabDisplayed(false); + } + } + else { + if (i < numViewportContent) { + /* + * Since no geometry available from the user configuration, + * keep this tab in its current window position + */ + const BrainOpenGLViewportContent* tabViewportContent = getViewportContentForTab(allTabContent[i]->getTabNumber()); + if (tabViewportContent != NULL) { + int32_t tabViewport[4]; + tabViewportContent->getTabViewportBeforeApplyingMargins(tabViewport); + int32_t windowViewport[4]; + tabViewportContent->getWindowViewport(windowViewport); + const float tabX(tabViewport[0]); + const float tabY(tabViewport[1]); + const float tabWidth(tabViewport[2]); + const float tabHeight(tabViewport[3]); + const float windowWidth(windowViewport[2]); + const float windowHeight(windowViewport[3]); + tabViewportContent->getWindowViewport(windowViewport); + const float minX((tabX / windowWidth) * 100.0); + const float maxX(((tabX + tabWidth) / windowWidth) * 100.0); + const float minY((tabY / windowHeight) * 100.0); + const float maxY(((tabY + tabHeight) / windowHeight) * 100.0); + + browserTabAnnotation->setBounds2D(minX, maxX, minY, maxY); + } + else { + CaretLogWarning("Unable to find viewport content for tab number " + + AString::number(allTabContent[i]->getTabNumber())); + } + } + } + } + + getBrowserWindowContent()->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::MANUAL); + getBrowserWindowContent()->setWindowAnnotationsStackingOrder(manualConfiguration->getWindowAnnotationsStackingOrder()); + + updateDialog(); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); updateGraphicsWindow(); + + return true; } /** + * If the given grid configuration contains fewer tabs than the number of tabs in the window, + * warn the user, with the option to continue. + * + * @param gridConfiguration + * The grid configuration + * @return + * True if processing should continue (use the configuration), else false indicating cancel. + */ +bool +TileTabsConfigurationDialog::warnIfGridConfigurationTooSmallDialog(const TileTabsLayoutGridConfiguration* gridConfiguration) const +{ + CaretAssert(gridConfiguration); + + std::vector allTabContent; + getBrowserWindow()->getAllTabContent(allTabContent); + int32_t numBrowserTabs = static_cast(allTabContent.size()); + + const int32_t numConfigTabs = gridConfiguration->getNumberOfTabs(); + if (numConfigTabs <= 0) { + WuQMessageBox::errorOk(m_loadConfigurationPushButton, + ("User configuration \"" + + gridConfiguration->getName() + + "\" is invalid (does not have any tabs)")); + return false; + } + + if (gridConfiguration->getLayoutType() == TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID) { + if (gridConfiguration->isCustomDefaultFlag()) { + /* custom grid will get updated to show all tabs */ + return true; + } + } + + if (numConfigTabs < numBrowserTabs) { + AString msg("" + "The Window contains " + + AString::number(numBrowserTabs) + + " tabs.

" + "The Grid contains space for " + + AString::number(numConfigTabs) + + " tabs.

" + "If you continue, not all tabs will be visible but you can edit the configuration to view all tabs." + ""); + return WuQMessageBox::warningOkCancel(m_loadConfigurationPushButton, + msg); + } + + return true; +} + + +/** + * @return the BrainOpenGLViewportContent for the tab with the given index (NULL if not found) + */ +const BrainOpenGLViewportContent* +TileTabsConfigurationDialog::getViewportContentForTab(const int32_t tabIndex) const +{ + std::vector tabViewports; + getBrowserWindow()->getAllBrainOpenGLViewportContent(tabViewports); + + for (const auto tv : tabViewports) { + if (tv->getTabIndex() == tabIndex) { + return tv; + } + } + + return NULL; +} + +/** + * @return A manual configuration from the geometry of the current tabs. If Tile Tabs + * is NOT enabled, NULL is returned. + */ +TileTabsLayoutManualConfiguration* +TileTabsConfigurationDialog::createManualConfigurationFromCurrentTabs() const +{ + const BrainBrowserWindow* window = getBrowserWindow(); + std::vector allTabContent; + window->getAllTabContent(allTabContent); + + TileTabsLayoutManualConfiguration* manualConfig = new TileTabsLayoutManualConfiguration(); + for (const auto btc : allTabContent) { + CaretAssert(btc); + const AnnotationBrowserTab* browserTabAnnotation = btc->getManualLayoutBrowserTabAnnotation(); + CaretAssert(browserTabAnnotation); + TileTabsBrowserTabGeometry* geometry = new TileTabsBrowserTabGeometry(browserTabAnnotation->getTabIndex()); + browserTabAnnotation->getTileTabsGeometry(geometry); + manualConfig->addTabInfo(geometry); + } + + manualConfig->setWindowAnnotationsStackingOrder(getBrowserWindowContent()->getWindowAnnotationsStackingOrder()); + + return manualConfig; +} + + +/** * @return The browser window selected window index. */ +const BrainBrowserWindow* +TileTabsConfigurationDialog::getBrowserWindow() const +{ + m_browserWindowComboBox->updateComboBox(); + /* + * This can be NULL when wb_view is closing. + */ + BrainBrowserWindow* bbw = m_browserWindowComboBox->getSelectedBrowserWindow(); + return bbw; +} +/** + * @return The browser window selected window index. + */ BrainBrowserWindow* TileTabsConfigurationDialog::getBrowserWindow() { @@ -269,8 +842,6 @@ return bbw; } - - /** * @return The browser window content for the selected window index. * May be NULL when no tabs are open. @@ -287,7 +858,21 @@ return bwc; } - +/** + * @return The browser window content for the selected window index. + * May be NULL when no tabs are open. + */ +const BrowserWindowContent* +TileTabsConfigurationDialog::getBrowserWindowContent() const +{ + const BrowserWindowContent* bwc(NULL); + const BrainBrowserWindow* bbw = getBrowserWindow(); + if (bbw != NULL) { + bwc = bbw->getBrowerWindowContent(); + } + + return bwc; +} /** * @return The configuration selection widget. */ @@ -296,20 +881,28 @@ { m_userConfigurationSelectionListWidget = new WuQListWidget(); m_userConfigurationSelectionListWidget->setSelectionMode(QListWidget::SingleSelection); + QObject::connect(m_userConfigurationSelectionListWidget, &QListWidget::itemSelectionChanged, + this, &TileTabsConfigurationDialog::userConfigurationSelectionListWidgetItemChanged); + + loadTemplateLayoutConfigurations(); - QHBoxLayout* selectionLayout = new QHBoxLayout(); - WuQtUtilities::setLayoutMargins(selectionLayout, - 0); - selectionLayout->addWidget(m_userConfigurationSelectionListWidget, 100); - + m_templateConfigurationSelectionListWidget = new WuQListWidget(); + m_templateConfigurationSelectionListWidget->setSelectionMode(QListWidget::SingleSelection); + for (const auto& tc : m_templateLayoutConfigurations) { + m_templateConfigurationSelectionListWidget->addItem(tc->getName() + + " (" + + AString::number(tc->getNumberOfTabs()) + + ")"); + } + if (m_templateConfigurationSelectionListWidget->count() > 0) { + m_templateConfigurationSelectionListWidget->setCurrentRow(0); + } + QObject::connect(m_templateConfigurationSelectionListWidget, &QListWidget::itemSelectionChanged, + this, &TileTabsConfigurationDialog::templateConfigurationSelectionListWidgetItemChanged); + const AString newToolTip = WuQtUtilities::createWordWrappedToolTipText("Create new User Configuration by entering a name.\n" "It will contain rows/columns/factors from the Custom Configuration"); - m_newConfigurationPushButton = new QPushButton("New..."); - m_newConfigurationPushButton->setAutoDefault(false); - m_newConfigurationPushButton->setToolTip(newToolTip); - QObject::connect(m_newConfigurationPushButton, SIGNAL(clicked()), - this, SLOT(newUserConfigurationButtonClicked())); - + m_renameConfigurationPushButton = new QPushButton("Rename..."); m_renameConfigurationPushButton->setToolTip("Rename the selected User Configuration"); m_renameConfigurationPushButton->setAutoDefault(false); @@ -321,17 +914,43 @@ m_deleteConfigurationPushButton->setAutoDefault(false); QObject::connect(m_deleteConfigurationPushButton, SIGNAL(clicked()), this, SLOT(deleteUserConfigurationButtonClicked())); + + m_showConfigurationXmlPushButton = new QPushButton("Show XML..."); + m_showConfigurationXmlPushButton->setToolTip("Show XML representation of User Configuration"); + m_showConfigurationXmlPushButton->setAutoDefault(false); + QObject::connect(m_showConfigurationXmlPushButton, &QPushButton::clicked, + this, &TileTabsConfigurationDialog::showConfigurationXmlPushButtonClicked); + + WuQtUtilities::matchWidgetWidths(m_renameConfigurationPushButton, + m_deleteConfigurationPushButton, + m_showConfigurationXmlPushButton); QGridLayout* buttonsLayout = new QGridLayout(); buttonsLayout->setContentsMargins(0, 0, 0, 0); - buttonsLayout->addWidget(m_newConfigurationPushButton, 0, 0, Qt::AlignRight); - buttonsLayout->addWidget(m_renameConfigurationPushButton, 0, 1, Qt::AlignLeft); - buttonsLayout->addWidget(m_deleteConfigurationPushButton, 1, 0, 1, 2, Qt::AlignHCenter); + buttonsLayout->addWidget(m_renameConfigurationPushButton, 0, 0); + buttonsLayout->addWidget(m_deleteConfigurationPushButton, 0, 1); + buttonsLayout->addWidget(m_showConfigurationXmlPushButton, 1, 0, 1, 2, Qt::AlignHCenter); + + QLabel* previewTextLabel = new QLabel("Configuration Preview"); + m_configurationImagePreviewLabel = new QLabel(); + m_configurationImagePreviewLabel->setScaledContents(true); /* scale pixmap in label to fill space */ + + m_configurationSourceTabWidget = new QTabWidget(); + m_configurationSourceTemplateTabIndex = m_configurationSourceTabWidget->addTab(m_templateConfigurationSelectionListWidget, + "Template"); + m_configurationSourceUserTabIndex = m_configurationSourceTabWidget->addTab(m_userConfigurationSelectionListWidget, + "User"); + m_configurationSourceTabWidget->setCurrentIndex(m_configurationSourceUserTabIndex); + QObject::connect(m_configurationSourceTabWidget, &QTabWidget::tabBarClicked, + this, &TileTabsConfigurationDialog::configurationSourceTabWidgetClicked); - QGroupBox* configurationWidget = new QGroupBox("User Configurations"); + QGroupBox* configurationWidget = new QGroupBox("Configuration Library"); QVBoxLayout* configurationLayout = new QVBoxLayout(configurationWidget); - configurationLayout->addWidget(m_userConfigurationSelectionListWidget, - 100); + configurationLayout->addWidget(m_configurationSourceTabWidget, + 0); + configurationLayout->addWidget(previewTextLabel); + configurationLayout->addWidget(m_configurationImagePreviewLabel, + 0); configurationLayout->addLayout(buttonsLayout, 0); @@ -339,6 +958,203 @@ } /** + * Called when configuration source tab bar is clicked by user + * + * @param index + * Index of item in tab bar + */ +void +TileTabsConfigurationDialog::configurationSourceTabWidgetClicked(int index) +{ + /* + * Note: This method is called before the tab widget updates + * its internal's with the new index. As a result, when + * the selected tab changes, the index passed to this method + * will be different than that returned by QTabWidget::currentIndex() + */ + ConfigurationSourceTypeEnum sourceType = ConfigurationSourceTypeEnum::USER; + if (index == m_configurationSourceUserTabIndex) { + sourceType = ConfigurationSourceTypeEnum::USER; + userConfigurationSelectionListWidgetItemChanged(); + } + else if (index == m_configurationSourceTemplateTabIndex) { + sourceType = ConfigurationSourceTypeEnum::TEMPLATE; + templateConfigurationSelectionListWidgetItemChanged(); + } + + updateTemplateUserConfigurationPushButtons(sourceType); +} + +/** + * @return The selected configuration source type (template or user) + */ +TileTabsConfigurationDialog::ConfigurationSourceTypeEnum +TileTabsConfigurationDialog::getSelectedConfigurationSourceType() const +{ + if (m_configurationSourceUserTabIndex == m_configurationSourceTabWidget->currentIndex()) { + return ConfigurationSourceTypeEnum::USER; + } + else if (m_configurationSourceTemplateTabIndex == m_configurationSourceTabWidget->currentIndex()) { + return ConfigurationSourceTypeEnum::TEMPLATE; + } + + CaretAssert(0); + return ConfigurationSourceTypeEnum::USER; +} + +/** + * @return The selected user configuration + */ +std::unique_ptr +TileTabsConfigurationDialog::getSelectedUserConfiguration() const +{ + std::unique_ptr config; + + AString uuid = getSelectedUserTileTabsConfigurationUniqueIdentifier(); + if ( ! uuid.isEmpty()) { + config = m_caretPreferences->getCopyOfTileTabsUserConfigurationByUniqueIdentifier(uuid); + } + + return config; +} + +/** + * @return The selected template configuration + */ +std::unique_ptr +TileTabsConfigurationDialog::getSelectedTemplateConfiguration() const +{ + std::unique_ptr config; + + const int32_t index = m_templateConfigurationSelectionListWidget->currentIndex().row(); + if (index >= 0) { + CaretAssertVectorIndex(m_templateLayoutConfigurations, index); + config.reset(m_templateLayoutConfigurations[index]->newCopyWithNewUniqueIdentifier()); + } + + return config; +} + +/** + * Called when a configuration is highlighted and shows an outline of the layout in the + * configuration preview label + */ +void +TileTabsConfigurationDialog::templateConfigurationSelectionListWidgetItemChanged() +{ + loadConfigurationPreviewLabel(getSelectedTemplateConfiguration().get()); +} + +/** + * Called when a configuration is highlighted and shows an outline of the layout in the + * configuration preview label + */ +void +TileTabsConfigurationDialog::userConfigurationSelectionListWidgetItemChanged() +{ + std::unique_ptr config = getSelectedUserConfiguration(); + loadConfigurationPreviewLabel(config.get()); +} + +/** + * Load the given configuration into the configuration preview label + * + * @param configuration + * The configuration to show in label + */ +void +TileTabsConfigurationDialog::loadConfigurationPreviewLabel(TileTabsLayoutBaseConfiguration* configuration) +{ + + + QPixmap pixmap(106, 106); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(m_configurationImagePreviewLabel, + pixmap); + painter->translate(3, 3); + + QPen pen = painter->pen(); + pen.setWidth(1); + painter->setPen(pen); + + if (configuration != NULL) { + switch (configuration->getLayoutType()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + { + TileTabsLayoutGridConfiguration* gridConfig = configuration->castToGridConfiguration(); + CaretAssert(gridConfig); + const int32_t numModels(gridConfig->getNumberOfRows() * gridConfig->getNumberOfColumns()); + std::vector rowHeights; + std::vector columnWidths; + const int32_t windowWidth(100); + const int32_t windowHeight(100); + gridConfig->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, + windowHeight, + numModels, + TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID, + rowHeights, + columnWidths); + + const int32_t numRows = static_cast(rowHeights.size()); + const int32_t numCols = static_cast(columnWidths.size()); + int32_t topY(windowHeight); + for (int32_t iRow = 0; iRow < numRows; iRow++) { + int32_t colX(0); + CaretAssertVectorIndex(rowHeights, iRow); + const int32_t height = rowHeights[iRow]; + + for (int32_t jCol = 0; jCol < numCols; jCol++) { + CaretAssertVectorIndex(columnWidths, jCol); + const int width = columnWidths[jCol]; + + QPoint bottomLeft(colX, topY - height); + QPoint bottomRight(colX + width - 1, topY - height); + QPoint topRight(colX + width - 1, topY); + QPoint topLeft(colX, topY); + painter->drawLine(bottomLeft, bottomRight); + painter->drawLine(bottomRight, topRight); + painter->drawLine(topRight, topLeft); + painter->drawLine(topLeft, bottomLeft); + + colX += width; + } + + topY -= height; + + } + } + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + { + TileTabsLayoutManualConfiguration* manConfig = configuration->castToManualConfiguration(); + CaretAssert(manConfig); + const int32_t numTabs = manConfig->getNumberOfTabs(); + for (int32_t i = 0; i < numTabs; i++) { + TileTabsBrowserTabGeometry* geom = manConfig->getTabInfo(i); + CaretAssert(geom); + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + geom->getBounds(minX, maxX, minY, maxY); + + QPointF bottomLeft(minX, minY); + QPointF bottomRight(maxX, minY); + QPointF topRight(maxX, maxY); + QPointF topLeft(minX, maxY); + painter->drawLine(bottomLeft, bottomRight); + painter->drawLine(bottomRight, topRight); + painter->drawLine(topRight, topLeft); + painter->drawLine(topLeft, bottomLeft); + } + } + break; + } + } + + m_configurationImagePreviewLabel->setPixmap(pixmap); + +} + +/** * @return Instance of workbench window widget. */ QWidget* @@ -368,7 +1184,7 @@ * @return The rows/columns stretch layout */ QWidget* -TileTabsConfigurationDialog::createCustomOptionsWidget() +TileTabsConfigurationDialog::createGridCustomOptionsWidget() { const QString toolTip("" "Removes any space between rows and columns in the tile tabs configuration. " @@ -377,14 +1193,14 @@ "problem. In addition, if the Lock Aspect option is selected prior to " "to enabling tile tabs, this option may improve the layout." ""); - m_centeringCorrectionCheckBox = new QCheckBox("Centering Correction"); - m_centeringCorrectionCheckBox->setToolTip(toolTip); - QObject::connect(m_centeringCorrectionCheckBox, &QCheckBox::clicked, + m_gridCenteringCorrectionCheckBox = new QCheckBox("Custom Grid Centering Correction"); + m_gridCenteringCorrectionCheckBox->setToolTip(toolTip); + QObject::connect(m_gridCenteringCorrectionCheckBox, &QCheckBox::clicked, this, &TileTabsConfigurationDialog::centeringCorrectionCheckBoxClicked); QGroupBox* groupBox = new QGroupBox("Options"); QVBoxLayout* layout = new QVBoxLayout(groupBox); - layout->addWidget(m_centeringCorrectionCheckBox); + layout->addWidget(m_gridCenteringCorrectionCheckBox); return groupBox; } @@ -398,7 +1214,7 @@ void TileTabsConfigurationDialog::centeringCorrectionCheckBoxClicked(bool checked) { - TileTabsConfiguration* config = getCustomTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* config = getCustomTileTabsGridConfiguration(); if (config != NULL) { config->setCenteringCorrectionEnabled(checked); updateGraphicsWindow(); @@ -411,9 +1227,9 @@ void TileTabsConfigurationDialog::updateCustomOptionsWidget() { - TileTabsConfiguration* config = getCustomTileTabsConfiguration(); + const TileTabsLayoutGridConfiguration* config = getCustomTileTabsGridConfiguration(); if (config != NULL) { - m_centeringCorrectionCheckBox->setChecked(config->isCenteringCorrectionEnabled()); + m_gridCenteringCorrectionCheckBox->setChecked(config->isCenteringCorrectionEnabled()); } } @@ -421,23 +1237,61 @@ * @return The rows/columns stretch layout */ QWidget* -TileTabsConfigurationDialog::createRowColumnStretchWidget() +TileTabsConfigurationDialog::createGridRowColumnStretchWidget() { - QGroupBox* rowGroupBox = new QGroupBox("Rows"); - rowGroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_rowElementsGridLayout = new QGridLayout(rowGroupBox); - WuQtUtilities::setLayoutSpacingAndMargins(m_rowElementsGridLayout, 4, 2); + /* + * Set number of rows + */ + QLabel* numberOfRowsLabel = new QLabel("Rows"); + m_numberOfGridRowsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, + s_maximumRowsColumns, + 1, + this, + SLOT(gridConfigurationNumberOfRowsOrColumnsChanged())); + m_numberOfGridRowsSpinBox->setToolTip("Number of rows for the tab configuration"); - QGroupBox* columnsGroupBox = new QGroupBox("Columns"); - m_columnElementsGridLayout = new QGridLayout(columnsGroupBox); - WuQtUtilities::setLayoutSpacingAndMargins(m_columnElementsGridLayout, 2, 2); + /* + * Edit content of rows + */ + QGroupBox* rowGroupBox = new QGroupBox(); + rowGroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_gridRowElementsGridLayout = new QGridLayout(rowGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(m_gridRowElementsGridLayout, 4, 2); + + /* + * Set number of columns + */ + QLabel* numberOfColumnsLabel = new QLabel("Columns"); + m_numberOfGridColumnsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, + s_maximumRowsColumns, + 1, + this, + SLOT(gridConfigurationNumberOfRowsOrColumnsChanged())); + m_numberOfGridColumnsSpinBox->setToolTip("Number of columns for the tab configuration"); + QGroupBox* columnsGroupBox = new QGroupBox(); + columnsGroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_gridColumnElementsGridLayout = new QGridLayout(columnsGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(m_gridColumnElementsGridLayout, 2, 2); + + QHBoxLayout* rowsColumnsCountLayout = new QHBoxLayout(); + rowsColumnsCountLayout->setContentsMargins(0, 0, 0, 0); + rowsColumnsCountLayout->addWidget(numberOfRowsLabel); + rowsColumnsCountLayout->addWidget(m_numberOfGridRowsSpinBox); + rowsColumnsCountLayout->addWidget(numberOfColumnsLabel); + rowsColumnsCountLayout->addWidget(m_numberOfGridColumnsSpinBox); + rowsColumnsCountLayout->addStretch(); + + QWidget* customGridOptionsWidget = createGridCustomOptionsWidget(); + customGridOptionsWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + QWidget* widget = new QWidget; QVBoxLayout* layout = new QVBoxLayout(widget); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(rowGroupBox); - layout->addSpacing(6); - layout->addWidget(columnsGroupBox); + layout->setSpacing(4); + layout->addLayout(rowsColumnsCountLayout, 0); + layout->addWidget(rowGroupBox, 0, Qt::AlignLeft); + layout->addWidget(columnsGroupBox, 0, Qt::AlignLeft); + layout->addWidget(customGridOptionsWidget, 0, Qt::AlignLeft); layout->addStretch(); return widget; @@ -450,40 +1304,40 @@ * Current custom configuration. */ void -TileTabsConfigurationDialog::updateRowColumnStretchWidgets(TileTabsConfiguration* configuration) +TileTabsConfigurationDialog::updateRowColumnStretchWidgets(TileTabsLayoutGridConfiguration* configuration) { /* * Update rows */ { const int32_t numRows = configuration->getNumberOfRows(); - int32_t numRowElements = static_cast(m_rowElements.size()); + int32_t numRowElements = static_cast(m_gridRowElements.size()); /** * Add elements as needed. */ const int32_t numToAdd = numRows - numRowElements; for (int32_t iRow = 0; iRow < numToAdd; iRow++) { - addRowColumnStretchWidget(EventTileTabsConfigurationModification::RowColumnType::ROW, - m_rowElementsGridLayout, - m_rowElements); + addRowColumnStretchWidget(EventTileTabsGridConfigurationModification::RowColumnType::ROW, + m_gridRowElementsGridLayout, + m_gridRowElements); } /* * Update widgets with element content */ - numRowElements = static_cast(m_rowElements.size()); + numRowElements = static_cast(m_gridRowElements.size()); for (int32_t iRow = 0; iRow < numRowElements; iRow++) { TileTabsGridRowColumnElement* element(NULL); if (iRow < numRows) { element = configuration->getRow(iRow); } - CaretAssertVectorIndex(m_rowElements, iRow); - m_rowElements[iRow]->updateContent(element); + CaretAssertVectorIndex(m_gridRowElements, iRow); + m_gridRowElements[iRow]->updateContent(element); } - m_rowElementsGridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); + m_gridRowElementsGridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); } /* @@ -491,38 +1345,36 @@ */ { const int32_t numColumns = configuration->getNumberOfColumns(); - int32_t numColumnElements = static_cast(m_columnElements.size()); + int32_t numColumnElements = static_cast(m_gridColumnElements.size()); /** * Add elements as needed. */ const int32_t numToAdd = numColumns - numColumnElements; for (int32_t iColumn = 0; iColumn < numToAdd; iColumn++) { - addRowColumnStretchWidget(EventTileTabsConfigurationModification::RowColumnType::COLUMN, - m_columnElementsGridLayout, - m_columnElements); + addRowColumnStretchWidget(EventTileTabsGridConfigurationModification::RowColumnType::COLUMN, + m_gridColumnElementsGridLayout, + m_gridColumnElements); } /* * Update widgets with element content */ - numColumnElements = static_cast(m_columnElements.size()); + numColumnElements = static_cast(m_gridColumnElements.size()); for (int32_t iColumn = 0; iColumn < numColumnElements; iColumn++) { TileTabsGridRowColumnElement* element(NULL); if (iColumn < numColumns) { element = configuration->getColumn(iColumn); } - CaretAssertVectorIndex(m_columnElements, iColumn); - m_columnElements[iColumn]->updateContent(element); + CaretAssertVectorIndex(m_gridColumnElements, iColumn); + m_gridColumnElements[iColumn]->updateContent(element); } - m_columnElementsGridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); + m_gridColumnElementsGridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); } updateCustomOptionsWidget(); - -// m_customConfigurationWidget->adjustSize(); } /** @@ -536,16 +1388,25 @@ * Container for row/column elements. */ void -TileTabsConfigurationDialog::addRowColumnStretchWidget(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, +TileTabsConfigurationDialog::addRowColumnStretchWidget(const EventTileTabsGridConfigurationModification::RowColumnType rowColumnType, QGridLayout* gridLayout, - std::vector& elementVector) + std::vector& elementVector) { const int32_t index = static_cast(elementVector.size()); if (index == 0) { + QString title("Index"); + switch (rowColumnType) { + case EventTileTabsGridConfigurationModification::RowColumnType::COLUMN: + title = "Column"; + break; + case EventTileTabsGridConfigurationModification::RowColumnType::ROW: + title = "Row"; + break; + } int32_t columnIndex(0); int32_t row = gridLayout->rowCount(); - gridLayout->addWidget(new QLabel("Index"), row, columnIndex++, Qt::AlignRight); - gridLayout->addWidget(new QLabel(" "), row, columnIndex++); + gridLayout->addWidget(new QLabel(title), row, columnIndex++, 1, 2, Qt::AlignHCenter); + columnIndex++; gridLayout->addWidget(new QLabel("Content"), row, columnIndex++, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Type"), row, columnIndex++, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Stretch"), row, columnIndex++, Qt::AlignHCenter); @@ -554,104 +1415,368 @@ } } - TileTabElementWidgets* elementWidget = new TileTabElementWidgets(this, + TileTabGridRowColumnWidgets* elementWidget = new TileTabGridRowColumnWidgets(this, rowColumnType, index, gridLayout, this); - QObject::connect(elementWidget, &TileTabElementWidgets::itemChanged, - this, &TileTabsConfigurationDialog::configurationStretchFactorWasChanged); - QObject::connect(elementWidget, &TileTabElementWidgets::modificationRequested, + QObject::connect(elementWidget, &TileTabGridRowColumnWidgets::itemChanged, + this, &TileTabsConfigurationDialog::gridConfigurationStretchFactorWasChanged); + QObject::connect(elementWidget, &TileTabGridRowColumnWidgets::modificationRequested, this, &TileTabsConfigurationDialog::tileTabsModificationRequested); elementVector.push_back(elementWidget); } +/** + * Update the widget for manual geometry editing + */ +void +TileTabsConfigurationDialog::updateManualGeometryEditorWidget() +{ + BrainBrowserWindow* window = getBrowserWindow(); + CaretAssert(window); + std::vector allBrowserTabs; + window->getAllTabContent(allBrowserTabs); + + const int32_t numTabs = static_cast(allBrowserTabs.size()); + int32_t numWidgets = static_cast(m_manualGeometryEditorWidgets.size()); + + /* + * Add elements as needed + */ + const int32_t numToAdd = numTabs - numWidgets; + for (int32_t j = 0; j < numToAdd; j++) { + addManualGeometryWidget(m_manualGeometryGridLayout, + m_manualGeometryEditorWidgets); + } + + /* + * Update with element contents + */ + numWidgets = static_cast(m_manualGeometryEditorWidgets.size()); + for (int32_t iRow = 0; iRow < numWidgets; iRow++) { + + BrowserTabContent* tabContent(NULL); + if (iRow < numTabs) { + tabContent = allBrowserTabs[iRow]; + } + + m_manualGeometryEditorWidgets[iRow]->updateContent(tabContent); + } + + BrowserWindowContent* bwc = getBrowserWindowContent(); + if (bwc != NULL) { + QSignalBlocker winAnnBlocker(m_manualConfigurationWindowAnnotationsDepthSpinBox); + m_manualConfigurationWindowAnnotationsDepthSpinBox->setValue(bwc->getWindowAnnotationsStackingOrder()); + } + m_manualConfigurationWindowAnnotationsDepthSpinBox->setEnabled(bwc != NULL); + + m_manualGeometryGridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); +} + +/** + * Called when the geomety is changed in a manual configuration + */ +void +TileTabsConfigurationDialog::manualConfigurationGeometryChanged() +{ + updateGraphicsWindow(); +} + +/** + * Add a manual geometry widget + * + * @param gridLayout + * The Qt layout + * @param widgetsVector + * Vector containing the manual editing widgets + */ +void +TileTabsConfigurationDialog::addManualGeometryWidget(QGridLayout* gridLayout, + std::vector& widgetsVector) +{ + const int32_t index = static_cast(widgetsVector.size()); + if (index == 0) { + int32_t columnIndex(0); + int32_t rowIndex = gridLayout->rowCount(); + + gridLayout->setHorizontalSpacing(8); + gridLayout->addWidget(new QLabel("Show"), rowIndex, columnIndex++, Qt::AlignLeft); + const int32_t nameColumn(columnIndex); + gridLayout->addWidget(new QLabel("Tab Name"), rowIndex, columnIndex++, Qt::AlignLeft); + gridLayout->addWidget(new QLabel("Left"), rowIndex, columnIndex++, Qt::AlignLeft); + gridLayout->addWidget(new QLabel("Right"), rowIndex, columnIndex++, Qt::AlignLeft); + gridLayout->addWidget(new QLabel("Bottom"), rowIndex, columnIndex++, Qt::AlignLeft); + gridLayout->addWidget(new QLabel("Top"), rowIndex, columnIndex++, Qt::AlignLeft); + gridLayout->addWidget(new QLabel("Background"), rowIndex, columnIndex++, Qt::AlignLeft); + const int32_t zOrderColumn(columnIndex); + gridLayout->addWidget(new QLabel("Z-Order"), rowIndex, columnIndex++, Qt::AlignLeft); + + m_manualConfigurationWindowAnnotationsDepthSpinBox = new QSpinBox(); + m_manualConfigurationWindowAnnotationsDepthSpinBox->setMinimum(-1000); + m_manualConfigurationWindowAnnotationsDepthSpinBox->setMaximum(1000); + m_manualConfigurationWindowAnnotationsDepthSpinBox->setSingleStep(1); + QObject::connect(m_manualConfigurationWindowAnnotationsDepthSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &TileTabsConfigurationDialog::manualConfigurationWindowAnnotationsDepthSpinBoxValueChanged); + m_manualConfigurationWindowAnnotationsDepthSpinBox->setToolTip(TileTabsManualTabGeometryWidget::getStackOrderToolTipText()); + + const int32_t windowRow(1000); /* will be at bottom */ + gridLayout->addWidget(new QLabel("Window Annotations"), + windowRow, nameColumn, 1, 5, Qt::AlignLeft); + gridLayout->addWidget(m_manualConfigurationWindowAnnotationsDepthSpinBox, + windowRow, zOrderColumn); + } + + TileTabsManualTabGeometryWidget* geometryWidget = new TileTabsManualTabGeometryWidget(this, + index, + gridLayout, + this); + QObject::connect(geometryWidget, &TileTabsManualTabGeometryWidget::itemChanged, + this, &TileTabsConfigurationDialog::manualConfigurationGeometryChanged); + + widgetsVector.push_back(geometryWidget); +} + +/** + * @return New instance of the manual configuration tool button + */ +QToolButton* +TileTabsConfigurationDialog::createManualConfigurationSetToolButton() +{ + m_setManualToAutomaticGridActionText = "Set Bounds of Tabs from Automatic Grid"; + m_setManualToCustomGridActionText = "Set Bounds of Tabs from Custom Grid"; + m_setManualToGridColumnsActionText = "Set Bounds of Tabs from Grid..."; + + const QString toolTipText("" + "Set (replace) the bounds of all tabs using the bounds created by a grid configuration. After " + "choosing one of the selections, the user may edit individual tab bounds as desired." + "

    " + "
  • " + m_setManualToAutomaticGridActionText + " - Tab bounds will be identical to the Automatic Grid" + "
  • " + m_setManualToCustomGridActionText + " - Tab bounds will be identical to the Custom Grid (note that a " + "custom grid may not display all tabs but all tabs will be in the manual configuration)" + "
  • " + m_setManualToGridColumnsActionText + " - Using a dialog, the user is prompted to enter the number of columns in the grid and wb_view will " + "set the number of rows so that the grid contains all tabs. Tabs bounds are then set using the grid" + "
" + ""); + QToolButton* toolButton = new QToolButton(); + toolButton->setText("Set..."); + toolButton->setToolTip(toolTipText); + QObject::connect(toolButton, &QToolButton::clicked, + this, &TileTabsConfigurationDialog::manualConfigurationSetToolButtonClicked); + + return toolButton; +} + +/** + * Called when manual configuration set tool button is clicked + */ +void +TileTabsConfigurationDialog::manualConfigurationSetToolButtonClicked() +{ + QMenu menu(m_manualConfigurationSetButton); + QAction* setAutomaticGridAction = menu.addAction(m_setManualToAutomaticGridActionText); + QAction* setCustomGridAction = menu.addAction(m_setManualToCustomGridActionText); + QAction* setColumnsAction = menu.addAction(m_setManualToGridColumnsActionText); + + QAction* selectedAction = menu.exec(m_manualConfigurationSetButton->mapToGlobal(QPoint(0,0))); + if (selectedAction == setColumnsAction) { + manualConfigurationSetMenuColumnsItemTriggered(); + } + else if (selectedAction == setAutomaticGridAction) { + manualConfigurationSetMenuFromAutomaticItemTriggered(); + } + else if (selectedAction == setCustomGridAction) { + manualConfigurationSetMenuFromCustomItemTriggered(); + } + else if (selectedAction != NULL) { + CaretAssertMessage(0, "Has a new action been added but not processed?"); + } + + updateDialog(); +} + +/** + * Called when manual configuration set columns menu item triggered + */ +void +TileTabsConfigurationDialog::manualConfigurationSetMenuColumnsItemTriggered() +{ + const BrainBrowserWindow* window = getBrowserWindow(); + CaretAssert(window); + + std::vector allTabContent; + window->getAllTabContent(allTabContent); + const int32_t numberOfTabs = static_cast(allTabContent.size()); + + int32_t defaultNumberOfRows(1); + int32_t defaultNumberOfColumns(1); + TileTabsLayoutGridConfiguration::getRowsAndColumnsForNumberOfTabs(numberOfTabs, + defaultNumberOfRows, + defaultNumberOfColumns); + + WuQDataEntryDialog ded("Create Manual Layout from Grid", + m_manualConfigurationSetButton); + QSpinBox* rowsSpinBox = ded.addSpinBox("Rows", + defaultNumberOfRows); + rowsSpinBox->setMinimum(1); + rowsSpinBox->setMaximum(100); + rowsSpinBox->setSingleStep(1); + + QSpinBox* columnsSpinBox = ded.addSpinBox("Columns", + defaultNumberOfColumns); + columnsSpinBox->setMinimum(1); + columnsSpinBox->setMaximum(100); + columnsSpinBox->setSingleStep(1); + + const bool wrapTextFlag(false); + ded.setTextAtTop("Set rows and columns of Grid:", + wrapTextFlag); + if (ded.exec() == WuQDataEntryDialog::Accepted) { + const int32_t numberOfRows = rowsSpinBox->value(); + CaretAssert(numberOfRows >= 1); + const int32_t numberOfColumns = columnsSpinBox->value(); + CaretAssert(numberOfColumns >= 1); + + std::unique_ptr gridConfig(TileTabsLayoutGridConfiguration::newInstanceCustomGrid()); + gridConfig->setNumberOfRows(numberOfRows); + gridConfig->setNumberOfColumns(numberOfColumns); + + loadIntoManualConfiguration(gridConfig.get()); + } +} /** - * @return The active configuration widget. + * Called when manual configuration set from automatic grid menu item is triggered + */ +void +TileTabsConfigurationDialog::manualConfigurationSetMenuFromAutomaticItemTriggered() +{ + BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); + if (browserWindowContent != NULL) { + TileTabsLayoutGridConfiguration* gridConfiguration = browserWindowContent->getAutomaticGridTileTabsConfiguration(); + CaretAssert(gridConfiguration); + loadIntoManualConfiguration(gridConfiguration); + } +} + +/** + * Called when manual configuration set from custom grid menu item is triggered + */ +void +TileTabsConfigurationDialog::manualConfigurationSetMenuFromCustomItemTriggered() +{ + TileTabsLayoutGridConfiguration* gridConfiguration = getCustomTileTabsGridConfiguration(); + CaretAssert(gridConfiguration); + if (gridConfiguration->isCustomDefaultFlag()) { + /* + * If we are here the user has not yet selected the custom grid + * and it defaults to the automatic configuration when selected. + * So, in this case, use the automatic grid. + */ + manualConfigurationSetMenuFromAutomaticItemTriggered(); + } + else { + loadIntoManualConfiguration(gridConfiguration); + } +} + +/** + * Called when manual configuration window annotation depth changed + * @param value + * New value for window annotation depth + */ +void +TileTabsConfigurationDialog::manualConfigurationWindowAnnotationsDepthSpinBoxValueChanged(int value) +{ + BrowserWindowContent* bwc = getBrowserWindowContent(); + CaretAssert(bwc); + bwc->setWindowAnnotationsStackingOrder(value); + updateGraphicsWindow(); +} + + +/** + * Create the active configuration type widget */ QWidget* -TileTabsConfigurationDialog::createActiveConfigurationWidget() +TileTabsConfigurationDialog::createConfigurationTypeWidget() { const AString autoToolTip("Workbench adjusts the number of rows and columns so " "that all tabs are displayed"); - m_automaticConfigurationRadioButton = new QRadioButton("Automatic Configuration"); - m_automaticConfigurationRadioButton->setToolTip(WuQtUtilities::createWordWrappedToolTipText(autoToolTip)); + m_automaticGridConfigurationRadioButton = new QRadioButton("Automatic Configuration"); + m_automaticGridConfigurationRadioButton->setToolTip(WuQtUtilities::createWordWrappedToolTipText(autoToolTip)); const AString customToolTip("User sets the number of row, columns, and stretch factors"); - m_customConfigurationRadioButton = new QRadioButton("Custom Configuration"); - m_customConfigurationRadioButton->setToolTip(WuQtUtilities::createWordWrappedToolTipText(customToolTip)); + m_customGridConfigurationRadioButton = new QRadioButton("Custom Configuration"); + m_customGridConfigurationRadioButton->setToolTip(WuQtUtilities::createWordWrappedToolTipText(customToolTip)); + + const AString manualToolTip("User sets positions and sizes of all tabs"); + m_manualConfigurationRadioButton = new QRadioButton("Manual"); + m_manualConfigurationRadioButton->setToolTip(WuQtUtilities::createWordWrappedToolTipText(manualToolTip)); QButtonGroup* buttonGroup = new QButtonGroup(this); - buttonGroup->addButton(m_automaticConfigurationRadioButton); - buttonGroup->addButton(m_customConfigurationRadioButton); + buttonGroup->addButton(m_automaticGridConfigurationRadioButton); + buttonGroup->addButton(m_customGridConfigurationRadioButton); + buttonGroup->addButton(m_manualConfigurationRadioButton); QObject::connect(buttonGroup, static_cast(&QButtonGroup::buttonClicked), this, &TileTabsConfigurationDialog::automaticCustomButtonClicked); - QLabel* rowsLabel = new QLabel("Rows"); - m_numberOfRowsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, - s_maximumRowsColumns, - 1, - this, - SLOT(configurationNumberOfRowsOrColumnsChanged())); - m_numberOfRowsSpinBox->setToolTip("Number of rows for the tab configuration"); - - QLabel* columnsLabel = new QLabel("Columns"); - m_numberOfColumnsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, - s_maximumRowsColumns, - 1, - this, - SLOT(configurationNumberOfRowsOrColumnsChanged())); - m_numberOfColumnsSpinBox->setToolTip("Number of columns for the tab configuration"); + m_manualConfigurationSetButton = createManualConfigurationSetToolButton(); + + QGroupBox* layoutTypeGroupBox = new QGroupBox("Active Configuration Type"); + QGridLayout* buttonTypeLayout = new QGridLayout(layoutTypeGroupBox); + buttonTypeLayout->setColumnStretch(0, 0); + buttonTypeLayout->setColumnStretch(1, 0); + buttonTypeLayout->setColumnStretch(2, 100); + buttonTypeLayout->addWidget(m_automaticGridConfigurationRadioButton, + 0, 0, 1, 3, Qt::AlignLeft); + buttonTypeLayout->addWidget(m_customGridConfigurationRadioButton, + 1, 0, 1, 3, Qt::AlignLeft); + buttonTypeLayout->addWidget(m_manualConfigurationRadioButton, + 2, 0, 1, 1); + buttonTypeLayout->addWidget(m_manualConfigurationSetButton, + 2, 1, 1, 2, Qt::AlignLeft); + + return layoutTypeGroupBox; +} + +QWidget* +TileTabsConfigurationDialog::createConfigurationSettingsWidget() +{ + m_customGridConfigurationWidget = createGridRowColumnStretchWidget(); + m_customGridConfigurationWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + m_manualGeometryWidget = createManualGeometryEditingWidget(); - m_customConfigurationWidget = createRowColumnStretchWidget(); - m_customConfigurationWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_editConfigurationStackedWidget = new QStackedWidget(); + m_editConfigurationStackedWidget->addWidget(m_customGridConfigurationWidget); + m_editConfigurationStackedWidget->addWidget(m_manualGeometryWidget); - m_customOptionsWidget = createCustomOptionsWidget(); - m_customOptionsWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - - QScrollArea* stretchFactorScrollArea = new QScrollArea(); - stretchFactorScrollArea->setWidget(m_customConfigurationWidget); - stretchFactorScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); -// stretchFactorScrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents); + stretchFactorScrollArea->setWidget(m_editConfigurationStackedWidget); + stretchFactorScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); stretchFactorScrollArea->setWidgetResizable(true); - QGroupBox* widget = new QGroupBox("Tile Tabs Configuration in Workbench Window"); - QGridLayout* widgetLayout = new QGridLayout(widget); - widgetLayout->setColumnStretch(0, 0); - widgetLayout->setColumnStretch(1, 0); - widgetLayout->setColumnStretch(2, 0); - widgetLayout->setColumnStretch(3, 0); - widgetLayout->setColumnStretch(4, 0); - widgetLayout->setColumnStretch(5, 100); - widgetLayout->setColumnMinimumWidth(0, 20); - widgetLayout->addWidget(createWorkbenchWindowWidget(), - 0, 0, 1, 6, - Qt::AlignLeft); - widgetLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), - 1, 0, 1, 6); - widgetLayout->addWidget(m_automaticConfigurationRadioButton, - 2, 0, 1, 6, - Qt::AlignLeft); - widgetLayout->addWidget(m_customConfigurationRadioButton, - 3, 0, 1, 1, - Qt::AlignLeft); - widgetLayout->addWidget(rowsLabel, - 3, 1, 1, 1); - widgetLayout->addWidget(m_numberOfRowsSpinBox, - 3, 2, 1, 1); - widgetLayout->addWidget(columnsLabel, - 3, 3, 1, 1); - widgetLayout->addWidget(m_numberOfColumnsSpinBox, - 3, 4, 1, 1); - widgetLayout->addWidget(stretchFactorScrollArea, - 4, 0, 1, 6); - widgetLayout->addWidget(m_customOptionsWidget, - 5, 0, 1, 6, Qt::AlignLeft); + QGroupBox* layoutSettingsGroupBox = new QGroupBox("Active Configuration Settings"); + QVBoxLayout* settingsGroupBoxLayout = new QVBoxLayout(layoutSettingsGroupBox); + settingsGroupBoxLayout->addWidget(stretchFactorScrollArea); - widget->setFixedWidth(widget->sizeHint().width()); + return layoutSettingsGroupBox; +} + +/** + * @return The rows/columns stretch layout + */ +QWidget* +TileTabsConfigurationDialog::createManualGeometryEditingWidget() +{ + m_manualGeometryGridLayout = new QGridLayout(); + QWidget* widget = new QWidget; + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addLayout(m_manualGeometryGridLayout); + layout->addStretch(); + WuQtUtilities::setLayoutSpacingAndMargins(m_manualGeometryGridLayout, 4, 2); return widget; } @@ -680,21 +1805,31 @@ TileTabsConfigurationDialog::automaticCustomButtonClicked(QAbstractButton* button) { BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); - if (button == m_automaticConfigurationRadioButton) { - browserWindowContent->setTileTabsConfigurationMode(TileTabsGridModeEnum::AUTOMATIC); + if (button == m_automaticGridConfigurationRadioButton) { + browserWindowContent->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID); } - else if (button == m_customConfigurationRadioButton) { - browserWindowContent->setTileTabsConfigurationMode(TileTabsGridModeEnum::CUSTOM); + else if (button == m_customGridConfigurationRadioButton) { + const TileTabsLayoutGridConfiguration* gridConfig = browserWindowContent->getCustomGridTileTabsConfiguration(); + if (gridConfig != NULL) { + if ( ! warnIfGridConfigurationTooSmallDialog(gridConfig)) { + return; + } + } + browserWindowContent->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID); + } + else if (button == m_manualConfigurationRadioButton) { + browserWindowContent->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::MANUAL); } else { CaretAssert(0); } - updateStretchFactors(); + + updateConfigurationEditingWidget(); + updateGridStretchFactors(); + updateManualGeometryEditorWidget(); updateGraphicsWindow(); } - - /** * Update the content of the dialog. If tile tabs is selected in the given * window, the dialog will be initialized with the tile tabs configuration @@ -715,67 +1850,106 @@ } /** - * Read the configurations from the preferences. + * Read the user configurations from the preferences. */ void -TileTabsConfigurationDialog::readConfigurationsFromPreferences() +TileTabsConfigurationDialog::readUserConfigurationsFromPreferences() { - if (m_blockReadConfigurationsFromPreferences) { + if (m_blockReadUserConfigurationsFromPreferences) { return; } - m_caretPreferences->readTileTabsConfigurations(); + m_caretPreferences->readTileTabsUserConfigurations(); } /** - * Update the content of the dialog. + * Read the configurations from the preferences. */ void -TileTabsConfigurationDialog::updateDialog() +TileTabsConfigurationDialog::updateConfigurationEditingWidget() { BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); if (browserWindowContent == NULL) { return; } + bool editingEnabledFlag(false); switch (browserWindowContent->getTileTabsConfigurationMode()) { - case TileTabsGridModeEnum::AUTOMATIC: - m_automaticConfigurationRadioButton->setChecked(true); + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + m_automaticGridConfigurationRadioButton->setChecked(true); + m_editConfigurationStackedWidget->setCurrentWidget(m_customGridConfigurationWidget); break; - case TileTabsGridModeEnum::CUSTOM: - m_customConfigurationRadioButton->setChecked(true); + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + m_customGridConfigurationRadioButton->setChecked(true); + m_editConfigurationStackedWidget->setCurrentWidget(m_customGridConfigurationWidget); + editingEnabledFlag = true; break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + m_manualConfigurationRadioButton->setChecked(true); + m_editConfigurationStackedWidget->setCurrentWidget(m_manualGeometryWidget); + editingEnabledFlag = true; + break; + } + + m_editConfigurationStackedWidget->setEnabled(editingEnabledFlag); +} + + +/** + * Update the content of the dialog. + */ +void +TileTabsConfigurationDialog::updateDialog() +{ + BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); + if (browserWindowContent == NULL) { + return; } - readConfigurationsFromPreferences(); + updateConfigurationEditingWidget(); + + readUserConfigurationsFromPreferences(); + + updateUserConfigurationListWidget(); + + updateGridStretchFactors(); + updateManualGeometryEditorWidget(); + switch (getSelectedConfigurationSourceType()) { + case TEMPLATE: + templateConfigurationSelectionListWidgetItemChanged(); + break; + case USER: + userConfigurationSelectionListWidgetItemChanged(); + break; + } +} + +/** + * Update the user configuration list widget + */ +void +TileTabsConfigurationDialog::updateUserConfigurationListWidget() +{ int defaultIndex = m_userConfigurationSelectionListWidget->currentRow(); QSignalBlocker blocker(m_userConfigurationSelectionListWidget); m_userConfigurationSelectionListWidget->clear(); - std::vector configurations = m_caretPreferences->getTileTabsConfigurationsSortedByName(); - const int32_t numConfig = static_cast(configurations.size()); - for (int32_t i = 0; i < numConfig; i++) { - const TileTabsConfiguration* configuration = configurations[i]; - - AString configName = configuration->getName(); - - configName.append(" (" - + AString::number(configuration->getNumberOfRows()) - + ", " - + AString::number(configuration->getNumberOfColumns()) - + ")"); - + const bool includeManualConfigurationsFlag(true); + std::vector> nameUniqueIDs = + m_caretPreferences->getTileTabsUserConfigurationsNamesAndUniqueIdentifiers(includeManualConfigurationsFlag); + + for (const auto& nameID : nameUniqueIDs) { /* * Second element is user data which contains the Unique ID */ - QListWidgetItem* item = new QListWidgetItem(configName); + QListWidgetItem* item = new QListWidgetItem(nameID.first); item->setData(Qt::UserRole, - QVariant(configuration->getUniqueIdentifier())); + QVariant(nameID.second)); m_userConfigurationSelectionListWidget->addItem(item); } - + const int32_t numItemsInComboBox = m_userConfigurationSelectionListWidget->count(); if (defaultIndex >= numItemsInComboBox) { defaultIndex = numItemsInComboBox - 1; @@ -786,128 +1960,126 @@ if (defaultIndex < m_userConfigurationSelectionListWidget->count()) { m_userConfigurationSelectionListWidget->setCurrentRow(defaultIndex); } - - updateStretchFactors(); } /** - * Update the stretch factors. + * Update the template configuration list widget */ void -TileTabsConfigurationDialog::updateStretchFactors() +TileTabsConfigurationDialog::updateTemplateConfigurationListWidget() { - BrainBrowserWindow* browserWindow = getBrowserWindow(); - m_automaticConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsGridModeEnum::AUTOMATIC, - true)); - m_customConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsGridModeEnum::CUSTOM, - false)); - const TileTabsConfiguration* configuration = getCustomTileTabsConfiguration(); - if (configuration != NULL) { - updateRowColumnStretchWidgets(const_cast(configuration)); - QSignalBlocker rowBlocker(m_numberOfRowsSpinBox); - m_numberOfRowsSpinBox->setValue(configuration->getNumberOfRows()); - QSignalBlocker columnBlocker(m_numberOfColumnsSpinBox); - m_numberOfColumnsSpinBox->setValue(configuration->getNumberOfColumns()); - } + int defaultIndex = m_templateConfigurationSelectionListWidget->currentRow(); + + QSignalBlocker blocker(m_templateConfigurationSelectionListWidget); + m_templateConfigurationSelectionListWidget->clear(); - const bool editableFlag = ( ! m_automaticConfigurationRadioButton->isChecked()); + const bool includeManualConfigurationsFlag(true); + std::vector> nameUniqueIDs = + m_caretPreferences->getTileTabsUserConfigurationsNamesAndUniqueIdentifiers(includeManualConfigurationsFlag); - // This does not re-enable the construction menu - //m_customConfigurationWidget->setEnabled(editableFlag); + for (const auto& nameID : nameUniqueIDs) { + /* + * Second element is user data which contains the Unique ID + */ + QListWidgetItem* item = new QListWidgetItem(nameID.first); + item->setData(Qt::UserRole, + QVariant(nameID.second)); + m_templateConfigurationSelectionListWidget->addItem(item); + } - m_loadPushButton->setEnabled(editableFlag); + const int32_t numItemsInComboBox = m_templateConfigurationSelectionListWidget->count(); + if (defaultIndex >= numItemsInComboBox) { + defaultIndex = numItemsInComboBox - 1; + } + if (defaultIndex < 0) { + defaultIndex = 0; + } + if (defaultIndex < m_templateConfigurationSelectionListWidget->count()) { + m_templateConfigurationSelectionListWidget->setCurrentRow(defaultIndex); + } } /** - * Select the tile tabs configuration with the given name. + * Update the stretch factors. */ void -TileTabsConfigurationDialog::selectTileTabConfigurationByUniqueID(const AString& uniqueID) +TileTabsConfigurationDialog::updateGridStretchFactors() { - const int32_t numItems = m_userConfigurationSelectionListWidget->count(); - for (int32_t i = 0; i < numItems; i++) { - QListWidgetItem* item = m_userConfigurationSelectionListWidget->item(i); - const AString itemID = item->data(Qt::UserRole).toString(); - if (itemID == uniqueID) { - QSignalBlocker blocker(m_userConfigurationSelectionListWidget); - m_userConfigurationSelectionListWidget->setCurrentItem(item); - break; - } + BrainBrowserWindow* browserWindow = getBrowserWindow(); + m_automaticGridConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID, + true)); + m_customGridConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID, + true)); + const TileTabsLayoutGridConfiguration* configuration = getCustomTileTabsGridConfiguration(); + if (configuration != NULL) { + updateRowColumnStretchWidgets(const_cast(configuration)); + QSignalBlocker rowBlocker(m_numberOfGridRowsSpinBox); + m_numberOfGridRowsSpinBox->setValue(configuration->getNumberOfRows()); + QSignalBlocker columnBlocker(m_numberOfGridColumnsSpinBox); + m_numberOfGridColumnsSpinBox->setValue(configuration->getNumberOfColumns()); } + + updateTemplateUserConfigurationPushButtons(getSelectedConfigurationSourceType()); } /** - * Called when new user configuration button is clicked. + * Update the push buttons enabled status + * + * @param sourceType + * The selected configuration source type. */ void -TileTabsConfigurationDialog::newUserConfigurationButtonClicked() +TileTabsConfigurationDialog::updateTemplateUserConfigurationPushButtons(const ConfigurationSourceTypeEnum sourceType) { - AString newTileTabsName; - - AString configurationUniqueID; - - bool exitLoop = false; - while (exitLoop == false) { - /* - * Popup dialog to get name for new configuration - */ - WuQDataEntryDialog ded("New Tile Tabs Configuration", - m_newConfigurationPushButton); - - QLineEdit* nameLineEdit = ded.addLineEditWidget("Configuration Name"); - nameLineEdit->setText(newTileTabsName); - if (ded.exec() == WuQDataEntryDialog::Accepted) { - /* - * Make sure name is not empty - */ - newTileTabsName = nameLineEdit->text().trimmed(); - if (newTileTabsName.isEmpty()) { - WuQMessageBox::errorOk(m_newConfigurationPushButton, - "Enter a name"); + bool addEnabledFlag(false); + bool replaceEnabledFlag(false); + bool deleteRenameEnabledFlag(false); + bool loadEnabledFlag(false); + bool showXmlVisibleFlag(m_caretPreferences->isDevelopMenuEnabled()); + bool showXmlEnabledFlag(false); + + switch (sourceType) { + case TEMPLATE: + { + const bool haveTemplateConfigurationFlag = (getSelectedTemplateConfiguration() != NULL); + if (haveTemplateConfigurationFlag) { + loadEnabledFlag = true; + } + } + break; + case USER: + { + const bool haveUserConfigurationFlag(getSelectedUserConfiguration() != NULL); + if (m_automaticGridConfigurationRadioButton->isChecked()) { + /* Nothing */ } else { - /* - * See if a configuration with the user entered name already exists - */ - TileTabsConfiguration* configuration = m_caretPreferences->getTileTabsConfigurationByName(newTileTabsName); - if (configuration != NULL) { - const QString msg = ("Configuration named \"" - + newTileTabsName - + "\" already exits. Rename it?"); - if (WuQMessageBox::warningYesNo(m_newConfigurationPushButton, - msg)) { - configuration->setName(newTileTabsName); - configurationUniqueID = configuration->getUniqueIdentifier(); - exitLoop = true; - } - } - else { - /* - * New configuration is copy of selected configuration (if available) - */ - const TileTabsConfiguration* selectedConfiguration = getCustomTileTabsConfiguration(); - TileTabsConfiguration* configuration = ((selectedConfiguration != NULL) - ? selectedConfiguration->newCopyWithNewUniqueIdentifier() - : new TileTabsConfiguration()); - configuration->setName(newTileTabsName); - configurationUniqueID = configuration->getUniqueIdentifier(); - m_caretPreferences->addTileTabsConfiguration(configuration); - exitLoop = true; + addEnabledFlag = true; + if (haveUserConfigurationFlag) { + replaceEnabledFlag = true; } } + + if (haveUserConfigurationFlag) { + deleteRenameEnabledFlag = true; + loadEnabledFlag = true; + showXmlEnabledFlag = true; + } } - else { - /* - * User pressed cancel button. - */ - exitLoop = true; - } + break; } - if ( ! configurationUniqueID.isEmpty()) { - updateDialog(); - selectTileTabConfigurationByUniqueID(configurationUniqueID); - } + m_addConfigurationPushButton->setEnabled(addEnabledFlag); + m_replaceConfigurationPushButton->setEnabled(replaceEnabledFlag); + m_loadConfigurationPushButton->setEnabled(loadEnabledFlag); + + m_renameConfigurationPushButton->setEnabled(deleteRenameEnabledFlag); + m_deleteConfigurationPushButton->setEnabled(deleteRenameEnabledFlag); + + m_showConfigurationXmlPushButton->setVisible(showXmlVisibleFlag); + m_showConfigurationXmlPushButton->setEnabled(showXmlVisibleFlag + && showXmlEnabledFlag); + } /** @@ -916,17 +2088,17 @@ void TileTabsConfigurationDialog::deleteUserConfigurationButtonClicked() { - TileTabsConfiguration* configuration = getSelectedUserTileTabsConfiguration(); - if (configuration != NULL) { - const AString uniqueID = configuration->getUniqueIdentifier(); - const QString msg = ("Delete configuration named \"" - + configuration->getName() - + "\" ?"); - if (WuQMessageBox::warningYesNo(m_newConfigurationPushButton, - msg)) { - m_caretPreferences->removeTileTabsConfigurationByUniqueIdentifier(uniqueID); - updateDialog(); + const QString uniqueID = getSelectedUserTileTabsConfigurationUniqueIdentifier(); + const QString msg = ("Delete selected configuration ?"); + if (WuQMessageBox::warningYesNo(m_deleteConfigurationPushButton, + msg)) { + AString errorMessage; + if ( ! m_caretPreferences->removeTileTabsUserConfigurationByUniqueIdentifier(uniqueID, + errorMessage)) { + WuQMessageBox::errorOk(m_deleteConfigurationPushButton, + errorMessage); } + updateDialog(); } } @@ -936,87 +2108,105 @@ void TileTabsConfigurationDialog::renameUserConfigurationButtonClicked() { - TileTabsConfiguration* configuration = getSelectedUserTileTabsConfiguration(); - if (configuration != NULL) { - m_blockReadConfigurationsFromPreferences = true; - + const AString uniqueID(getSelectedUserTileTabsConfigurationUniqueIdentifier()); + if (uniqueID.isEmpty()) { + WuQMessageBox::errorOk(m_renameConfigurationPushButton, + "Selected configuration is missing Unique Identifier (Program Error)"); + return; + } + + std::unique_ptr config = m_caretPreferences->getCopyOfTileTabsUserConfigurationByUniqueIdentifier(uniqueID); + if (config) { + const AString oldName = config->getName(); bool ok = false; - const AString oldName = configuration->getName(); - const AString newName = QInputDialog::getText(m_deleteConfigurationPushButton, + const AString newName = QInputDialog::getText(m_renameConfigurationPushButton, "Rename Configuration", "Name", QLineEdit::Normal, oldName, &ok); if (ok - && (newName.isEmpty() == false)) { - configuration->setName(newName); - m_caretPreferences->writeTileTabsConfigurations(); - m_blockReadConfigurationsFromPreferences = false; + && ( ! newName.isEmpty())) { + m_blockReadUserConfigurationsFromPreferences = true; + AString errorMessage; + if (m_caretPreferences->renameTileTabsUserConfiguration(uniqueID, + newName, + errorMessage)) { + } + else { + WuQMessageBox::errorOk(m_renameConfigurationPushButton, + errorMessage); + } + m_blockReadUserConfigurationsFromPreferences = false; updateDialog(); } - else { - m_blockReadConfigurationsFromPreferences = false; - } + } + else { + WuQMessageBox::errorOk(m_renameConfigurationPushButton, ("Unable to find configuration with unique ID=" + + uniqueID)); } } /** - * @return A pointer to the automatic tile tabs configuration. + * Called when show configuration XML button is clicked */ -TileTabsConfiguration* -TileTabsConfigurationDialog::getAutomaticTileTabsConfiguration() +void +TileTabsConfigurationDialog::showConfigurationXmlPushButtonClicked() { - BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); - TileTabsConfiguration* configuration = browserWindowContent->getAutomaticTileTabsConfiguration(); - CaretAssert(configuration); - return configuration; + std::unique_ptr config = getSelectedUserConfiguration(); + if (config) { + const QString xml = config->encodeInXML(); + WuQTextEditorDialog::runNonModal("XML", + xml, + WuQTextEditorDialog::TextMode::PLAIN, + WuQTextEditorDialog::WrapMode::NO, + m_showConfigurationXmlPushButton); + } } /** * @return A pointer to the custom tile tabs configuration. */ -TileTabsConfiguration* -TileTabsConfigurationDialog::getCustomTileTabsConfiguration() +TileTabsLayoutGridConfiguration* +TileTabsConfigurationDialog::getCustomTileTabsGridConfiguration() { BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); - TileTabsConfiguration* configuration = browserWindowContent->getCustomTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* configuration = browserWindowContent->getCustomGridTileTabsConfiguration(); CaretAssert(configuration); return configuration; } /** - * @return The selected user tile tabs configuration (will be - * NULL if there are no user configurations). + * @return The Unique Identifier of the selected user tile tabs configuration. + * Empty if no configuration is selected. */ -TileTabsConfiguration* -TileTabsConfigurationDialog::getSelectedUserTileTabsConfiguration() +AString +TileTabsConfigurationDialog::getSelectedUserTileTabsConfigurationUniqueIdentifier() const { - TileTabsConfiguration* configuration = NULL; + AString uniqueID; const int32_t indx = m_userConfigurationSelectionListWidget->currentRow(); if ((indx >= 0) && (indx < m_userConfigurationSelectionListWidget->count())) { QListWidgetItem* item = m_userConfigurationSelectionListWidget->item(indx); - const AString itemID = item->data(Qt::UserRole).toString(); - configuration = m_caretPreferences->getTileTabsConfigurationByUniqueIdentifier(itemID); + uniqueID = item->data(Qt::UserRole).toString(); } - return configuration; + return uniqueID; } /** * Called when the number of rows or columns changes. */ void -TileTabsConfigurationDialog::configurationNumberOfRowsOrColumnsChanged() +TileTabsConfigurationDialog::gridConfigurationNumberOfRowsOrColumnsChanged() { - TileTabsConfiguration* configuration = getCustomTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* configuration = getCustomTileTabsGridConfiguration(); if (configuration != NULL) { - configuration->setNumberOfRows(m_numberOfRowsSpinBox->value()); - configuration->setNumberOfColumns(m_numberOfColumnsSpinBox->value()); + configuration->setNumberOfRows(m_numberOfGridRowsSpinBox->value()); + configuration->setNumberOfColumns(m_numberOfGridColumnsSpinBox->value()); - updateStretchFactors(); + updateGridStretchFactors(); updateGraphicsWindow(); } @@ -1026,15 +2216,16 @@ * Called when a configuration stretch factor value is changed. */ void -TileTabsConfigurationDialog::configurationStretchFactorWasChanged() +TileTabsConfigurationDialog::gridConfigurationStretchFactorWasChanged() { - TileTabsConfiguration* configuration = getCustomTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* configuration = getCustomTileTabsGridConfiguration(); if (configuration == NULL) { return; } - updateStretchFactors(); + updateGridStretchFactors(); updateGraphicsWindow(); + } /** @@ -1044,16 +2235,18 @@ * Modification that is requested. */ void -TileTabsConfigurationDialog::tileTabsModificationRequested(EventTileTabsConfigurationModification& modification) +TileTabsConfigurationDialog::tileTabsModificationRequested(EventTileTabsGridConfigurationModification& modification) { - TileTabsConfiguration* configuration = getCustomTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* configuration = getCustomTileTabsGridConfiguration(); if (configuration != NULL) { modification.setWindowIndex(m_browserWindowComboBox->getSelectedBrowserWindowIndex()); EventManager::get()->sendEvent(modification.getPointer()); - updateStretchFactors(); + updateGridStretchFactors(); + + updateManualGeometryEditorWidget(); updateGraphicsWindow(); } @@ -1086,335 +2279,68 @@ /** - * Constructor. - * - * @param tileTabsConfigurationDialog - * The tile tabs configuration dialog. - * @param rowColumnType - * 'Row' or 'Column' - * @param index - * Index of the row/column - * @param gridLayout - * Gridlayout for widgets - * @param parent - * Parent QObject - */ -TileTabElementWidgets::TileTabElementWidgets(TileTabsConfigurationDialog* tileTabsConfigurationDialog, - const EventTileTabsConfigurationModification::RowColumnType rowColumnType, - const int32_t index, - QGridLayout* gridLayout, - QObject* parent) -: QObject(parent), -m_tileTabsConfigurationDialog(tileTabsConfigurationDialog), -m_rowColumnType(rowColumnType), -m_index(index), -m_element(NULL) -{ - m_indexLabel = new QLabel(QString::number(m_index + 1)); - m_indexLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - - const AString rowColText((rowColumnType == EventTileTabsConfigurationModification::RowColumnType::ROW) - ? "Row" - : "Column"); - const AString contructionToolTip(WuQtUtilities::createWordWrappedToolTipText("Delete, Duplicate, or Move " - + rowColText)); - const AString contentToolTip(WuQtUtilities::createWordWrappedToolTipText("Content of the " - + rowColText - + ": Spacer (empty space for Annotations) " - "or Tabs (Browser Tabs)")); - const AString typeToolTip(WuQtUtilities::createWordWrappedToolTipText("Type of Stretching: Percent or Weight")); - const AString stretchToolTip(WuQtUtilities::createWordWrappedToolTipText("Value of Stretching Percentage [0, 100] or Stretching Weight")); - - /* - * Construction Tool Button - */ - QIcon constructionIcon; - const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", - constructionIcon); - m_constructionAction = WuQtUtilities::createAction("M", - "Add/Move/Remove", - this); - if (constructionIconValid) { - m_constructionAction->setIcon(constructionIcon); - } - m_constructionToolButton = new QToolButton(); - QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton); - QObject::connect(constructionMenu, &QMenu::aboutToShow, - this, &TileTabElementWidgets::constructionMenuAboutToShow); - QObject::connect(constructionMenu, &QMenu::triggered, - this, &TileTabElementWidgets::constructionMenuTriggered); - m_constructionAction->setMenu(constructionMenu); - m_constructionToolButton->setDefaultAction(m_constructionAction); - m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); - m_constructionToolButton->setFixedWidth(m_constructionToolButton->sizeHint().width()); - m_constructionToolButton->setToolTip(contructionToolTip); - - /* - * Content type combo box - */ - m_contentTypeComboBox = new EnumComboBoxTemplate(this); - m_contentTypeComboBox->setup(); - QObject::connect(m_contentTypeComboBox, &EnumComboBoxTemplate::itemActivated, - this, &TileTabElementWidgets::contentTypeActivated); - m_contentTypeComboBox->getComboBox()->setFixedWidth(m_contentTypeComboBox->getComboBox()->sizeHint().width()); - m_contentTypeComboBox->getComboBox()->setToolTip(contentToolTip); - - /* - * Stretch type combo box - */ - m_stretchTypeComboBox = new EnumComboBoxTemplate(this); - m_stretchTypeComboBox->setup(); - QObject::connect(m_stretchTypeComboBox, &EnumComboBoxTemplate::itemActivated, - this, &TileTabElementWidgets::stretchTypeActivated); - m_stretchTypeComboBox->getComboBox()->setFixedWidth(m_stretchTypeComboBox->getComboBox()->sizeHint().width()); - m_stretchTypeComboBox->getComboBox()->setToolTip(typeToolTip); - - /* - * Stretch value spin box - */ - m_stretchValueSpinBox = new QDoubleSpinBox(); - m_stretchValueSpinBox->setKeyboardTracking(false); - m_stretchValueSpinBox->setRange(0.0, 1000.0); - m_stretchValueSpinBox->setDecimals(2); - m_stretchValueSpinBox->setSingleStep(0.1); - QObject::connect(m_stretchValueSpinBox, static_cast(&QDoubleSpinBox::valueChanged), - this, &TileTabElementWidgets::stretchValueChanged); - m_stretchValueSpinBox->setFixedWidth(m_stretchValueSpinBox->sizeHint().width()); - m_stretchValueSpinBox->setToolTip(stretchToolTip); - - m_gridLayoutGroup = new WuQGridLayoutGroup(gridLayout); - const int32_t rowIndex(gridLayout->rowCount()); - int32_t columnIndex(0); - m_gridLayoutGroup->addWidget(m_indexLabel, rowIndex, columnIndex++, Qt::AlignRight); - m_gridLayoutGroup->addWidget(m_constructionToolButton, rowIndex, columnIndex++); - m_gridLayoutGroup->addWidget(m_contentTypeComboBox->getWidget(), rowIndex, columnIndex++); - m_gridLayoutGroup->addWidget(m_stretchTypeComboBox->getWidget(), rowIndex, columnIndex++); - m_gridLayoutGroup->addWidget(m_stretchValueSpinBox, rowIndex, columnIndex++); -} - -/** - * Destructor. - */ -TileTabElementWidgets::~TileTabElementWidgets() -{ - -} - -/** - * @return The construction menu. - * - * @param toolButton - * The parent toolbutton. - */ -QMenu* -TileTabElementWidgets::createConstructionMenu(QToolButton* toolButton) -{ - const AString deleteText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) - ? "Delete this Column" - : "Delete this Row"); - - const AString duplicateAfterText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) - ? "Duplicate this Column to Right" - : "Duplicate this Row Below"); - - const AString duplicateBeforeText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) - ? "Duplicate this Column to Left" - : "Duplicate this Row Above"); - - const AString insertSpacerAfterText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) - ? "Insert Spacer Column to Right" - : "Insert Spacer Row Below"); - const AString insertSpacerBeforeText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) - ? "Insert Spacer Column to Left" - : "Insert Spacer Row Above"); - const AString moveAfterText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) - ? "Move this Column to Right" - : "Move this Row Down"); - - const AString moveBeforeText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) - ? "Move this Column to Left" - : "Move this Row Up"); - - m_menuDeleteAction = new QAction(deleteText); - m_menuDeleteAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DELETE_IT)); - - m_menuDuplicateAfterAction = new QAction(duplicateAfterText); - m_menuDuplicateAfterAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER)); - - m_menuDuplicateBeforeAction = new QAction(duplicateBeforeText); - m_menuDuplicateBeforeAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE)); - - m_insertSpacerAfterAction = new QAction(insertSpacerAfterText); - m_insertSpacerAfterAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER)); - - m_insertSpacerBeforeAction = new QAction(insertSpacerBeforeText); - m_insertSpacerBeforeAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE)); - - m_menuMoveAfterAction = new QAction(moveAfterText); - m_menuMoveAfterAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::MOVE_AFTER)); - - m_menuMoveBeforeAction = new QAction(moveBeforeText); - m_menuMoveBeforeAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::MOVE_BEFORE)); - - QMenu* menu = new QMenu(toolButton); - menu->addAction(m_menuDuplicateBeforeAction); - menu->addAction(m_menuDuplicateAfterAction); - menu->addSeparator(); - menu->addAction(m_insertSpacerBeforeAction); - menu->addAction(m_insertSpacerAfterAction); - menu->addSeparator(); - menu->addAction(m_menuMoveBeforeAction); - menu->addAction(m_menuMoveAfterAction); - menu->addSeparator(); - menu->addAction(m_menuDeleteAction); - - return menu; -} - -/** - * Update with the given row/column element. - */ -void -TileTabElementWidgets::updateContent(TileTabsGridRowColumnElement* element) -{ - m_element = element; - const bool showFlag(m_element != NULL); - - if (showFlag) { - m_contentTypeComboBox->setSelectedItem(element->getContentType()); - m_stretchTypeComboBox->setSelectedItem(element->getStretchType()); - QSignalBlocker valueBlocker(m_stretchValueSpinBox); - switch (m_element->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - m_stretchValueSpinBox->setRange(0.0, 100.0); - m_stretchValueSpinBox->setSingleStep(1.0); - m_stretchValueSpinBox->setValue(m_element->getPercentStretch()); - m_stretchValueSpinBox->setSuffix("%"); - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - m_stretchValueSpinBox->setRange(0.0, 1000.0); - m_stretchValueSpinBox->setSingleStep(0.1); - m_stretchValueSpinBox->setValue(m_element->getWeightStretch()); - m_stretchValueSpinBox->setSuffix(""); - break; - } - } - - m_gridLayoutGroup->setVisible(showFlag); -} - -/** - * Called when an item is selected from the construction menu - * - * @param action - * Action that was selected. + * Load the template layout configurations */ void -TileTabElementWidgets::constructionMenuTriggered(QAction* action) +TileTabsConfigurationDialog::loadTemplateLayoutConfigurations() { - if (action != NULL) { - const EventTileTabsConfigurationModification::Operation operation - = static_cast(action->data().toInt()); - - /* - * This switch is here so that it will cause a compilation error - * if the operations are changed. - */ - switch (operation) { - case EventTileTabsConfigurationModification::Operation::DELETE_IT: - break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: - break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: - break; - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: - break; - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: - break; - case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: - break; - case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: - break; - } - - EventTileTabsConfigurationModification modification(m_tileTabsConfigurationDialog->getCustomTileTabsConfiguration(), - m_index, - m_rowColumnType, - operation); - emit modificationRequested(modification); - } - + { + const QString configXML(R""""(" + "" + "" + "" + ")""""); + loadTemplateLayoutConfigurationFromXML(configXML); + } + + { + const QString configXML(R""""( + + + + )""""); + loadTemplateLayoutConfigurationFromXML(configXML); + } + + { + const QString configXML(R""""( + + + + + )""""); + loadTemplateLayoutConfigurationFromXML(configXML); + } + + { + const QString configXML(R""""( + + + + + )""""); + loadTemplateLayoutConfigurationFromXML(configXML); + } } /** - * Called when construction menu is about to show. - */ -void -TileTabElementWidgets::constructionMenuAboutToShow() -{ - const TileTabsConfiguration* config = m_tileTabsConfigurationDialog->getCustomTileTabsConfiguration(); + * Load a template configuration from the given XML + */ +void +TileTabsConfigurationDialog::loadTemplateLayoutConfigurationFromXML(const QString& xml) +{ + AString errorMessage; + TileTabsLayoutBaseConfiguration* config = TileTabsLayoutBaseConfiguration::decodeFromXML(xml, + errorMessage); if (config != NULL) { - int32_t numItems(-1); - switch (m_rowColumnType) { - case EventTileTabsConfigurationModification::RowColumnType::COLUMN: - numItems = config->getNumberOfColumns(); - break; - case EventTileTabsConfigurationModification::RowColumnType::ROW: - numItems = config->getNumberOfRows(); - break; - } - - m_menuDeleteAction->setEnabled(numItems > 1); - m_menuDuplicateAfterAction->setEnabled(numItems >= 1); - m_menuDuplicateBeforeAction->setEnabled(numItems >= 1); - m_menuMoveAfterAction->setEnabled((numItems > 1) - && (m_index < (numItems - 1))); - m_menuMoveBeforeAction->setEnabled((numItems > 1) - && (m_index > 0)); + std::unique_ptr configPtr(config); + m_templateLayoutConfigurations.push_back(std::move(configPtr)); } -} - - -/** - * Called when content type combo box changed. - */ -void -TileTabElementWidgets::contentTypeActivated() -{ - if (m_element != NULL) { - m_element->setContentType(m_contentTypeComboBox->getSelectedItem()); - emit itemChanged(); + else { + CaretLogWarning("Failed to laod template configuration from XML:\n" + + xml); } } -/** - * Called when stretch type combo box changed. - */ -void -TileTabElementWidgets::stretchTypeActivated() -{ - if (m_element != NULL) { - m_element->setStretchType(m_stretchTypeComboBox->getSelectedItem()); - emit itemChanged(); - } -} -/** - * Called when stretch value changed. - */ -void -TileTabElementWidgets::stretchValueChanged(double) -{ - if (m_element != NULL) { - switch (m_element->getStretchType()) { - case TileTabsGridRowColumnStretchTypeEnum::PERCENT: - m_element->setPercentStretch(m_stretchValueSpinBox->value()); - break; - case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: - m_element->setWeightStretch(m_stretchValueSpinBox->value()); - break; - } - emit itemChanged(); - } -} diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabsConfigurationDialog.h connectome-workbench-1.5.0/src/GuiQt/TileTabsConfigurationDialog.h --- connectome-workbench-1.4.2/src/GuiQt/TileTabsConfigurationDialog.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabsConfigurationDialog.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,7 +22,7 @@ /*LICENSE_END*/ #include "EventListenerInterface.h" -#include "EventTileTabsConfigurationModification.h" +#include "EventTileTabsGridConfigurationModification.h" #include "TileTabsGridRowColumnContentTypeEnum.h" #include "TileTabsGridRowColumnStretchTypeEnum.h" #include "WuQDialogNonModal.h" @@ -36,17 +36,23 @@ class QPushButton; class QRadioButton; class QSpinBox; +class QStackedWidget; +class QTabWidget; class QToolButton; namespace caret { class BrainBrowserWindow; class BrainBrowserWindowComboBox; + class BrainOpenGLViewportContent; class BrowserWindowContent; class CaretPreferences; class EnumComboBoxTemplate; - class TileTabsConfiguration; - class TileTabElementWidgets; + class TileTabsLayoutBaseConfiguration; + class TileTabsLayoutGridConfiguration; + class TileTabsLayoutManualConfiguration; + class TileTabGridRowColumnWidgets; class TileTabsGridRowColumnElement; + class TileTabsManualTabGeometryWidget; class WuQGridLayoutGroup; class WuQListWidget; @@ -77,15 +83,17 @@ private slots: void browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow); - void newUserConfigurationButtonClicked(); - void deleteUserConfigurationButtonClicked(); void renameUserConfigurationButtonClicked(); - void configurationNumberOfRowsOrColumnsChanged(); + void showConfigurationXmlPushButtonClicked(); + + void gridConfigurationNumberOfRowsOrColumnsChanged(); - void configurationStretchFactorWasChanged(); + void gridConfigurationStretchFactorWasChanged(); + + void addUserConfigurationPushButtonClicked(); void replaceUserConfigurationPushButtonClicked(); @@ -93,168 +101,202 @@ void automaticCustomButtonClicked(QAbstractButton*); - void tileTabsModificationRequested(EventTileTabsConfigurationModification& modification); + void tileTabsModificationRequested(EventTileTabsGridConfigurationModification& modification); void centeringCorrectionCheckBoxClicked(bool checked); + + void manualConfigurationGeometryChanged(); + void manualConfigurationSetToolButtonClicked(); + + void manualConfigurationSetMenuColumnsItemTriggered(); + + void manualConfigurationSetMenuFromAutomaticItemTriggered(); + + void manualConfigurationSetMenuFromCustomItemTriggered(); + + void manualConfigurationWindowAnnotationsDepthSpinBoxValueChanged(int value); + + void userConfigurationSelectionListWidgetItemChanged(); + + void templateConfigurationSelectionListWidgetItemChanged(); + + void configurationSourceTabWidgetClicked(int index); + protected: void focusGained(); virtual void helpButtonClicked() override; private: + enum ConfigurationSourceTypeEnum { + TEMPLATE, + USER + }; + // ADD_NEW_MEMBERS_HERE QWidget* createCopyLoadPushButtonsWidget(); QWidget* createWorkbenchWindowWidget(); - void selectTileTabConfigurationByUniqueID(const AString& uniqueID); + TileTabsLayoutGridConfiguration* getCustomTileTabsGridConfiguration(); - TileTabsConfiguration* getAutomaticTileTabsConfiguration(); + AString getSelectedUserTileTabsConfigurationUniqueIdentifier() const; - TileTabsConfiguration* getCustomTileTabsConfiguration(); + QWidget* createUserConfigurationSelectionWidget(); - TileTabsConfiguration* getSelectedUserTileTabsConfiguration(); + QWidget* createConfigurationTypeWidget(); - QWidget* createUserConfigurationSelectionWidget(); + QWidget* createConfigurationSettingsWidget(); - QWidget* createActiveConfigurationWidget(); + QWidget* createGridRowColumnStretchWidget(); - QWidget* createRowColumnStretchWidget(); + QWidget* createGridCustomOptionsWidget(); - QWidget* createCustomOptionsWidget(); + QWidget* createManualGeometryEditingWidget(); - void updateRowColumnStretchWidgets(TileTabsConfiguration* configuration); + void updateRowColumnStretchWidgets(TileTabsLayoutGridConfiguration* configuration); - void addRowColumnStretchWidget(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, + void addRowColumnStretchWidget(const EventTileTabsGridConfigurationModification::RowColumnType rowColumnType, QGridLayout* gridLayout, - std::vector& elementVector); + std::vector& elementVector); + + void updateConfigurationEditingWidget(); - void updateStretchFactors(); + void updateManualGeometryEditorWidget(); + + void addManualGeometryWidget(QGridLayout* gridLayout, + std::vector& widgetsVector); + + QToolButton* createManualConfigurationSetToolButton(); + + void updateGridStretchFactors(); void updateGraphicsWindow(); void updateCustomOptionsWidget(); - void readConfigurationsFromPreferences(); + void readUserConfigurationsFromPreferences(); + + void updateUserConfigurationListWidget(); + + void updateTemplateConfigurationListWidget(); BrainBrowserWindow* getBrowserWindow(); + const BrainBrowserWindow* getBrowserWindow() const; + BrowserWindowContent* getBrowserWindowContent(); + const BrowserWindowContent* getBrowserWindowContent() const; + + AString getNewConfigurationName(QWidget* dialogParent); + + const BrainOpenGLViewportContent* getViewportContentForTab(const int32_t tabIndex) const; + + TileTabsLayoutManualConfiguration* createManualConfigurationFromCurrentTabs() const; + + std::unique_ptr getSelectedUserConfiguration() const; + + std::unique_ptr getSelectedTemplateConfiguration() const; + + ConfigurationSourceTypeEnum getSelectedConfigurationSourceType() const; + + void loadConfigurationPreviewLabel(TileTabsLayoutBaseConfiguration* configuration); + + void updateTemplateUserConfigurationPushButtons(const ConfigurationSourceTypeEnum sourceType); + + void loadTemplateLayoutConfigurations(); + + void loadTemplateLayoutConfigurationFromXML(const QString& xml); + + bool warnIfGridConfigurationTooSmallDialog(const TileTabsLayoutGridConfiguration* gridConfiguration) const; + + bool loadIntoManualConfiguration(const TileTabsLayoutBaseConfiguration* configuration); + BrainBrowserWindowComboBox* m_browserWindowComboBox; - QWidget* m_customConfigurationWidget; + QStackedWidget* m_editConfigurationStackedWidget; - QWidget* m_customOptionsWidget; + QWidget* m_customGridConfigurationWidget; - QRadioButton* m_automaticConfigurationRadioButton; + QRadioButton* m_automaticGridConfigurationRadioButton; - QRadioButton* m_customConfigurationRadioButton; + QRadioButton* m_customGridConfigurationRadioButton; - QPushButton* m_newConfigurationPushButton; + QRadioButton* m_manualConfigurationRadioButton; QPushButton* m_deleteConfigurationPushButton; QPushButton* m_renameConfigurationPushButton; - QPushButton* m_replacePushButton; + QPushButton* m_showConfigurationXmlPushButton; - QPushButton* m_loadPushButton; + QPushButton* m_addConfigurationPushButton; - WuQListWidget* m_userConfigurationSelectionListWidget; + QPushButton* m_replaceConfigurationPushButton; - QSpinBox* m_numberOfRowsSpinBox; + QPushButton* m_loadConfigurationPushButton; - QSpinBox* m_numberOfColumnsSpinBox; + QTabWidget* m_configurationSourceTabWidget; - std::vector m_columnElements; + int32_t m_configurationSourceTemplateTabIndex; - std::vector m_rowElements; + int32_t m_configurationSourceUserTabIndex; - QGridLayout* m_rowElementsGridLayout = NULL; + WuQListWidget* m_userConfigurationSelectionListWidget; - QGridLayout* m_columnElementsGridLayout = NULL; + WuQListWidget* m_templateConfigurationSelectionListWidget; - QCheckBox* m_centeringCorrectionCheckBox; + QSpinBox* m_numberOfGridRowsSpinBox; - /** Blocks reading of preferences since that may invalidate data pointers */ - bool m_blockReadConfigurationsFromPreferences; + QSpinBox* m_numberOfGridColumnsSpinBox; - /** - * Keep a pointer to preferences but DO NOT delete it - * since the preferences are managed by the session - * manager. - */ - CaretPreferences* m_caretPreferences; + std::vector m_gridColumnElements; - friend class TileTabElementWidgets; + std::vector m_gridRowElements; - static const int32_t s_maximumRowsColumns = 50; - }; - - - /** - * Contains widgets for one row or column of stretching. - */ - class TileTabElementWidgets : public QObject { - Q_OBJECT + QGridLayout* m_gridRowElementsGridLayout = NULL; - public: - TileTabElementWidgets(TileTabsConfigurationDialog* tileTabsConfigurationDialog, - const EventTileTabsConfigurationModification::RowColumnType rowColumnType, - const int32_t index, - QGridLayout* gridLayout, - QObject* parent); + QGridLayout* m_gridColumnElementsGridLayout = NULL; - virtual ~TileTabElementWidgets(); - - void updateContent(TileTabsGridRowColumnElement* element); + QCheckBox* m_gridCenteringCorrectionCheckBox; - signals: - void itemChanged(); + QWidget* m_manualGeometryWidget; - void modificationRequested(EventTileTabsConfigurationModification& modification); + QGridLayout* m_manualGeometryGridLayout; - private slots: - void constructionMenuAboutToShow(); + QToolButton* m_manualConfigurationSetButton; - void constructionMenuTriggered(QAction*); + QString m_setManualToAutomaticGridActionText; + QString m_setManualToCustomGridActionText; + QString m_setManualToGridColumnsActionText; - void contentTypeActivated(); + std::vector m_manualGeometryEditorWidgets; - void stretchTypeActivated(); + QSpinBox* m_manualConfigurationWindowAnnotationsDepthSpinBox; - void stretchValueChanged(double); + std::vector> m_templateLayoutConfigurations; - private: - QMenu* createConstructionMenu(QToolButton* toolButton); + /** Blocks reading of preferences since that may invalidate data pointers */ + bool m_blockReadUserConfigurationsFromPreferences; - TileTabsConfigurationDialog* m_tileTabsConfigurationDialog; - const EventTileTabsConfigurationModification::RowColumnType m_rowColumnType; - const int32_t m_index; - TileTabsGridRowColumnElement* m_element; - - QLabel* m_indexLabel; - QAction* m_constructionAction; - QToolButton* m_constructionToolButton; - EnumComboBoxTemplate* m_contentTypeComboBox; - EnumComboBoxTemplate* m_stretchTypeComboBox; - QDoubleSpinBox* m_stretchValueSpinBox; - - QAction* m_menuDeleteAction; - QAction* m_menuDuplicateAfterAction; - QAction* m_menuDuplicateBeforeAction; - QAction* m_insertSpacerAfterAction; - QAction* m_insertSpacerBeforeAction; - QAction* m_menuMoveAfterAction; - QAction* m_menuMoveBeforeAction; + /** + * Keep a pointer to preferences but DO NOT delete it + * since the preferences are managed by the session + * manager. + */ + CaretPreferences* m_caretPreferences; - WuQGridLayoutGroup* m_gridLayoutGroup; + QLabel* m_configurationImagePreviewLabel; + friend class TileTabGridRowColumnWidgets; + + static const int32_t s_maximumRowsColumns = 50; }; + #ifdef __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ // const AString TileTabsConfigurationDialog::s_automaticConfigurationPrefix = "Automatic Configuration"; #endif // __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabsConfigurationModifier.cxx connectome-workbench-1.5.0/src/GuiQt/TileTabsConfigurationModifier.cxx --- connectome-workbench-1.4.2/src/GuiQt/TileTabsConfigurationModifier.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabsConfigurationModifier.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,619 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -#define __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ -#include "TileTabsConfigurationModifier.h" -#undef __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ - -#include "BrainBrowserWindow.h" -#include "BrainOpenGLViewportContent.h" -#include "BrowserTabContent.h" -#include "CaretAssert.h" -#include "EventBrowserTabNewClone.h" -#include "EventBrowserTabDelete.h" -#include "EventBrowserTabNew.h" -#include "EventBrowserWindowTileTabOperation.h" -#include "EventManager.h" -#include "EventTileTabsConfigurationModification.h" -#include "GuiManager.h" -#include "SpacerTabContent.h" -#include "TileTabsConfiguration.h" - -using namespace caret; - -static bool debugFlag = false; - -/** - * \class caret::TileTabsConfigurationModifier - * \brief Modifies a tile tabs configuration. - * \ingroup GuiQt - */ - -/** - * Constructor. - * - * @param existingTabs - * All tabs in the window. - * @param modifyEvent - * Event describing modification. - */ -TileTabsConfigurationModifier::TileTabsConfigurationModifier(const std::vector& existingTabs, - EventTileTabsConfigurationModification* modifyEvent) -: CaretObject(), -m_existingTabs(existingTabs), -m_modifyEvent(modifyEvent) -{ - CaretAssert(modifyEvent); - m_currentTileTabsConfiguration = modifyEvent->getTileTabsConfiguration(); - CaretAssert(m_currentTileTabsConfiguration); -} - -/** - * Destructor. - */ -TileTabsConfigurationModifier::~TileTabsConfigurationModifier() -{ - for (auto rc : m_rowColumns) { - delete rc; - } - m_rowColumns.clear(); -} - -/** - * Get a description of this object's content. - * @return String describing this object's content. - */ -AString -TileTabsConfigurationModifier::toString() const -{ - AString s; - for (const auto rc : m_rowColumns) { - if ( ! s.isEmpty()) { - s.append("\n"); - } - s.append(rc->toString()); - } - return s; -} - -/** - * Run to perform the operation. - * - * @param errorMessageOut - * Contains error information if operation fails. - * @return - * True if successful, else false. - */ -bool -TileTabsConfigurationModifier::run(AString& errorMessageOut) -{ - errorMessageOut.clear(); - - loadRowColumnsFromTileTabsConfiguration(); - if (debugFlag) { - std::cout << "Loaded: " << toString() << std::endl << std::flush; - } - bool validFlag = performModification(errorMessageOut); - if (debugFlag) { - std::cout << "After Modification: " << toString() << std::endl << std::endl << std::flush; - } - - if (validFlag) { - validFlag = loadRowColumnsIntoTileTabsConfiguration(errorMessageOut); - } - - return validFlag; -} - -/** - * Load the rows or columns from the Tile Tabs Configuration - */ -void -TileTabsConfigurationModifier::loadRowColumnsFromTileTabsConfiguration() -{ - const int32_t numRows = m_currentTileTabsConfiguration->getNumberOfRows(); - const int32_t numColumns = m_currentTileTabsConfiguration->getNumberOfColumns(); - - switch (m_modifyEvent->getRowColumnType()) { - case EventTileTabsConfigurationModification::RowColumnType::COLUMN: - for (int32_t jCol = 0; jCol < numColumns; jCol++) { - m_rowColumns.push_back(new RowColumnContent(m_existingTabs, - m_modifyEvent->getTileTabsConfiguration(), - jCol, - false)); - } - break; - case EventTileTabsConfigurationModification::RowColumnType::ROW: - for (int32_t iRow = 0; iRow < numRows; iRow++) { - m_rowColumns.push_back(new RowColumnContent(m_existingTabs, - m_modifyEvent->getTileTabsConfiguration(), - iRow, - true)); - } - break; - } -} - -/** - * Perform the modification. - * - * @param errorMessageOut - * Contains error information if operation fails. - * @return - * True if successful, else false. - */ -bool -TileTabsConfigurationModifier::performModification(AString& errorMessageOut) -{ - - const int32_t rowColumnIndex = m_modifyEvent->getRowColumnIndex(); - - const int32_t numRowColumns = static_cast(m_rowColumns.size()); - switch (m_modifyEvent->getOperation()) { - case EventTileTabsConfigurationModification::Operation::DELETE_IT: - if (numRowColumns <= 1) { - errorMessageOut = "Cannot delete ROWCOL when there is only one ROWCOL"; - } - else if ((rowColumnIndex >= 0) - && (rowColumnIndex < numRowColumns)) { - CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); - RowColumnContent* deleteRowColumn = m_rowColumns[rowColumnIndex]; - CaretAssert(deleteRowColumn); - m_rowColumns.erase(m_rowColumns.begin() + rowColumnIndex); - CaretAssert(std::find(m_rowColumns.begin(), - m_rowColumns.end(), - deleteRowColumn) == m_rowColumns.end()); - - for (auto t : deleteRowColumn->m_tabElements) { - if (t->m_browserTabContent != NULL) { - m_browserTabsToDelete.push_back(t->m_browserTabContent); - } - } - delete deleteRowColumn; - } - else { - errorMessageOut = "Invalid ROWCOL index=RCINDEX when deleting"; - } - break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: - if ((rowColumnIndex >= 0) - && (rowColumnIndex < numRowColumns)) { - RowColumnContent* rowColumnCopy = m_rowColumns[rowColumnIndex]->clone(errorMessageOut); - if (rowColumnCopy != NULL) { - const int32_t insertOffset = (rowColumnIndex + 1); - m_rowColumns.insert(m_rowColumns.begin() + - insertOffset, - rowColumnCopy); - } - } - else { - errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; - } - break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: - if ((rowColumnIndex >= 0) - && (rowColumnIndex < numRowColumns)) { - RowColumnContent* rowColumnCopy = m_rowColumns[rowColumnIndex]->clone(errorMessageOut); - if (rowColumnCopy != NULL) { - const int32_t insertOffset = rowColumnIndex; - m_rowColumns.insert(m_rowColumns.begin() + - insertOffset, - rowColumnCopy); - } - } - else { - errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; - } - break; - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: - if ((rowColumnIndex >= 0) - && (rowColumnIndex < numRowColumns)) { - CaretAssertVectorIndex(m_rowColumns, 0); - const int32_t numberOfElements = m_rowColumns[0]->m_tabElements.size(); - RowColumnContent* rowColumnSpacer = RowColumnContent::newInstanceContainingSpacers(numberOfElements); - if (rowColumnSpacer != NULL) { - int32_t insertOffset = rowColumnIndex; - if (m_modifyEvent->getOperation() == EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER) { - insertOffset++; - } - m_rowColumns.insert(m_rowColumns.begin() + - insertOffset, - rowColumnSpacer); - } - } - else { - errorMessageOut = "Invalid ROWCOL index=RCINDEX when insert spacer"; - } - break; - case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: - if (numRowColumns <= 1) { - errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; - } - else if (rowColumnIndex == (numRowColumns - 1)) { - errorMessageOut = "Cannot move last ROWCOL after itself"; - } - else if ((rowColumnIndex >= 0) - && (rowColumnIndex < (numRowColumns - 1))) { - CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); - CaretAssertVectorIndex(m_rowColumns, rowColumnIndex + 1); - std::swap(m_rowColumns[rowColumnIndex], - m_rowColumns[rowColumnIndex + 1]); - } - else { - errorMessageOut = "Invalid ROWCOL index=RCINDEX when moving"; - } - break; - case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: - if (numRowColumns <= 1) { - errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; - } - else if (rowColumnIndex == 0) { - errorMessageOut = "Cannot move last ROWCOL before itself"; - } - else if ((rowColumnIndex >= 1) - && (rowColumnIndex < numRowColumns)) { - CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); - CaretAssertVectorIndex(m_rowColumns, rowColumnIndex - 1); - std::swap(m_rowColumns[rowColumnIndex], - m_rowColumns[rowColumnIndex - 1]); - } - else { - errorMessageOut = "Invalid ROWCOL index=RCINDEX when moving"; - } - break; - } - - if ( ! errorMessageOut.isEmpty()) { - AString nameText; - switch (m_modifyEvent->getRowColumnType()) { - case EventTileTabsConfigurationModification::RowColumnType::COLUMN: - nameText = " Column "; - break; - case EventTileTabsConfigurationModification::RowColumnType::ROW: - nameText = " Row "; - break; - } - - /* - * "substite names" - */ - errorMessageOut = errorMessageOut.replace("ROWCOL", nameText); - errorMessageOut = errorMessageOut.replace("RCINDEX", AString::number(rowColumnIndex + 1)); - } - - const bool validFlag = errorMessageOut.isEmpty(); - return validFlag; -} - -/** - * Load the modified rows/columns into the tile tabs configuration current in the browser window - * - * @param errorMessageOut - * Contains error information if operation fails. - * @return - * True if successful, else false. - */ -bool -TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut) -{ - TileTabsConfiguration newConfiguration(*m_currentTileTabsConfiguration); - std::vector browserTabs; - - switch (m_modifyEvent->getRowColumnType()) { - case EventTileTabsConfigurationModification::RowColumnType::COLUMN: - { - const int32_t numRows = static_cast(m_rowColumns[0]->m_tabElements.size()); - CaretAssert(numRows == m_currentTileTabsConfiguration->getNumberOfRows()); - const int32_t numColumns = static_cast(m_rowColumns.size()); - - newConfiguration.setNumberOfRows(numRows); - newConfiguration.setNumberOfColumns(numColumns); - - for (int32_t iRow = 0; iRow < numRows; iRow++) { - for (int32_t jCol = 0; jCol < numColumns; jCol++) { - RowColumnContent* columnContent = m_rowColumns[jCol]; - CaretAssertVectorIndex(columnContent->m_tabElements, iRow); - BrowserTabContent* btc = columnContent->m_tabElements[iRow]->m_browserTabContent; - if (btc != NULL) { - browserTabs.push_back(btc); - } - } - } - - for (int32_t jCol = 0; jCol < numColumns; jCol++) { - TileTabsGridRowColumnElement* rce = newConfiguration.getColumn(jCol); - CaretAssertVectorIndex(m_rowColumns, jCol); - CaretAssert(m_rowColumns[jCol]->m_stretching); - *rce = *m_rowColumns[jCol]->m_stretching; - } - } - break; - case EventTileTabsConfigurationModification::RowColumnType::ROW: - { - const int32_t numRows = static_cast(m_rowColumns.size()); - const int32_t numColumns = static_cast(m_rowColumns[0]->m_tabElements.size()); - CaretAssert(numColumns == m_currentTileTabsConfiguration->getNumberOfColumns()); - - newConfiguration.setNumberOfRows(numRows); - newConfiguration.setNumberOfColumns(numColumns); - - for (int32_t iRow = 0; iRow < numRows; iRow++) { - CaretAssertVectorIndex(m_rowColumns, iRow); - RowColumnContent* rowContent = m_rowColumns[iRow]; - for (int32_t jCol = 0; jCol < numColumns; jCol++) { - CaretAssertVectorIndex(rowContent->m_tabElements, jCol); - BrowserTabContent* btc = rowContent->m_tabElements[jCol]->m_browserTabContent; - if (btc != NULL) { - browserTabs.push_back(btc); - } - } - } - - for (int32_t iRow = 0; iRow < numRows; iRow++) { - TileTabsGridRowColumnElement* rce = newConfiguration.getRow(iRow); - CaretAssertVectorIndex(m_rowColumns, iRow); - CaretAssert(m_rowColumns[iRow]->m_stretching); - *rce = *m_rowColumns[iRow]->m_stretching; - } - } - break; - } - - /* - * Copy new tile tabs configuration into the current configuration - */ - *m_currentTileTabsConfiguration = newConfiguration; - - /* - * Update tabs in the window's toolbar - */ - QWidget* parentWindow(GuiManager::get()->getBrowserWindowByWindowIndex(m_modifyEvent->getWindowIndex())); - const int32_t invalidTabIndex(-1); - EventBrowserWindowTileTabOperation updateTabsEvent(EventBrowserWindowTileTabOperation::OPERATION_REPLACE_TABS, - parentWindow, - m_modifyEvent->getWindowIndex(), - invalidTabIndex, - browserTabs); - EventManager::get()->sendEvent(updateTabsEvent.getPointer()); - - if (updateTabsEvent.isError()) { - errorMessageOut.appendWithNewLine(updateTabsEvent.getErrorMessage()); - } - - if ( ! m_browserTabsToDelete.empty()) { - for (auto btc : m_browserTabsToDelete) { - EventBrowserTabDelete deleteEvent(btc, - btc->getTabNumber()); - EventManager::get()->sendEvent(deleteEvent.getPointer()); - if (deleteEvent.isError()) { - errorMessageOut.appendWithNewLine(deleteEvent.getErrorMessage()); - } - } - m_browserTabsToDelete.clear(); - } - - if (errorMessageOut.isEmpty()) { - return true; - } - - return false; -} - -/* - * Constructor for element in the row column matrix. - * - * @param rowIndex - * The row index - * @param columnIndex - * The column index - * @param browserTabContent - * Browser tab content at (rowIndex, columnIndex) in the current - * Tile Tabs Configuration - */ -TileTabsConfigurationModifier::Element::Element(const int32_t rowIndex, - const int32_t columnIndex, - BrowserTabContent* browserTabContent) -: -m_targetRowIndex(rowIndex), -m_targetColumnIndex(columnIndex), -m_sourceRowIndex(rowIndex), -m_sourceColumnIndex(columnIndex), -m_browserTabContent(browserTabContent) -{ - -} - -/** - * @return String representation of object. - */ -AString -TileTabsConfigurationModifier::Element::toString() const -{ - AString s("(row=%1, column=%2)"); - s = s.arg(m_sourceRowIndex).arg(m_sourceColumnIndex); - return s; -} - -/* - * Constructor for content of a row/column element that - * contains the tabs and stretching for one row or one column - * - * @param existingTabs - * Existing browser tabs. - * @param tileTabsConfiguration - * The current tile tabs configuration in the browser window. - * @param rowColumnIndex - * Index of the row or column related to modification. - * @param rowFlag - * True if rows are being operated upon, false if operating on columns - */ -TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const std::vector& existingTabs, - TileTabsConfiguration* tileTabsConfiguration, - const int32_t rowColumnIndex, - const bool rowFlag) -{ - const int32_t numRows = tileTabsConfiguration->getNumberOfRows(); - const int32_t numColumns = tileTabsConfiguration->getNumberOfColumns(); - const int32_t numberOfTabs =static_cast(existingTabs.size()); - - if (rowFlag) { - for (int32_t jCol = 0; jCol < numColumns; jCol++) { - BrowserTabContent* browserTabContent(NULL); - const int32_t tabIndex = (rowColumnIndex * numColumns) + jCol; - if (tabIndex < numberOfTabs) { - CaretAssertVectorIndex(existingTabs, tabIndex); - browserTabContent = existingTabs[tabIndex]->getBrowserTabContent(); - } - - m_tabElements.push_back(new Element(rowColumnIndex, - jCol, - browserTabContent)); - } - - m_stretching = new TileTabsGridRowColumnElement(*tileTabsConfiguration->getRow(rowColumnIndex)); - } - else { - for (int32_t iRow = 0; iRow < numRows; iRow++) { - BrowserTabContent* browserTabContent(NULL); - const int32_t tabIndex = (iRow * numColumns) + rowColumnIndex; - if (tabIndex < numberOfTabs) { - CaretAssertVectorIndex(existingTabs, tabIndex); - browserTabContent = existingTabs[tabIndex]->getBrowserTabContent(); - } - - m_tabElements.push_back(new Element(iRow, - rowColumnIndex, - browserTabContent)); - } - - m_stretching = new TileTabsGridRowColumnElement(*tileTabsConfiguration->getColumn(rowColumnIndex)); - } -} - -/* - * Constructor that creates the given number of elements. - * - * @param numberOfElements - * Number of elements for row/column. - */ -TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const int32_t numberOfElements) -{ - for (int32_t i = 0; i < numberOfElements; i++) { - m_tabElements.push_back(new Element(i, i, NULL)); - } - m_stretching = new TileTabsGridRowColumnElement(); -} - -/** - * @return New instance containing the given number of elements setup as spacers. - * - * @param numberOfElements - * Number of elements for row/column. - */ -TileTabsConfigurationModifier::RowColumnContent* -TileTabsConfigurationModifier::RowColumnContent::newInstanceContainingSpacers(const int32_t numberOfElements) -{ - RowColumnContent* content = new RowColumnContent(numberOfElements); - - content->m_stretching->setContentType(TileTabsGridRowColumnContentTypeEnum::SPACE); - content->m_stretching->setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); - content->m_stretching->setWeightStretch(1.0); - - return content; -} - -/** - * Destructor. - */ -TileTabsConfigurationModifier::RowColumnContent::~RowColumnContent() -{ - for (auto te : m_tabElements) { - delete te; - } - m_tabElements.clear(); - - delete m_stretching; - m_stretching = NULL; -} - -/** - * Copy constructor. - * - * @param obj - * Instance that is copied. - */ -TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const RowColumnContent& obj) -: CaretObject(obj) -{ - for (auto te : obj.m_tabElements) { - m_tabElements.push_back(new Element(*te)); - } - - m_stretching = new TileTabsGridRowColumnElement(*obj.m_stretching); -} - -/** - * @return String representation of object. - */ -AString -TileTabsConfigurationModifier::RowColumnContent::toString() const -{ - AString s; - for (const auto te : m_tabElements) { - s += (" " + te->toString()); - } - return s; -} - -/** - * Clone this Row/column content instance. - * - * @param errorMessageOut - * Output with error message. - * @return - * True if successful, else false - */ -TileTabsConfigurationModifier::RowColumnContent* -TileTabsConfigurationModifier::RowColumnContent::clone(AString& errorMessageOut) const -{ - RowColumnContent* cloned = new RowColumnContent(*this); - CaretAssert(cloned); - - for (auto te : cloned->m_tabElements) { - if (te->m_browserTabContent != NULL) { - EventBrowserTabNewClone cloneTabEvent(te->m_browserTabContent->getTabNumber()); - EventManager::get()->sendEvent(cloneTabEvent.getPointer()); - if (cloneTabEvent.isError()) { - errorMessageOut.appendWithNewLine(cloneTabEvent.getErrorMessage()); - te->m_browserTabContent = NULL; - } - else { - te->m_browserTabContent = cloneTabEvent.getNewBrowserTab(); - } - } - } - - return cloned; -} - diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabsConfigurationModifier.h connectome-workbench-1.5.0/src/GuiQt/TileTabsConfigurationModifier.h --- connectome-workbench-1.4.2/src/GuiQt/TileTabsConfigurationModifier.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabsConfigurationModifier.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ -#ifndef __TILE_TABS_CONFIGURATION_MODIFIER_H__ -#define __TILE_TABS_CONFIGURATION_MODIFIER_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include -#include -#include "CaretObject.h" - - - -namespace caret { - - class BrainOpenGLViewportContent; - class BrowserTabContent; - class EventTileTabsConfigurationModification; - class SpacerTabContent; - class TileTabsConfiguration; - class TileTabsGridRowColumnElement; - - class TileTabsConfigurationModifier : public CaretObject { - - public: - TileTabsConfigurationModifier(const std::vector& existingTabs, - EventTileTabsConfigurationModification* modifyEvent); - - virtual ~TileTabsConfigurationModifier(); - - TileTabsConfigurationModifier(const TileTabsConfigurationModifier&) = delete; - - TileTabsConfigurationModifier& operator=(const TileTabsConfigurationModifier&) = delete; - - bool run(AString& errorMessageOut); - - - // ADD_NEW_METHODS_HERE - - virtual AString toString() const; - - class Element : public CaretObject { - public: - Element(const int32_t rowIndex, - const int32_t columnIndex, - BrowserTabContent* browserTabContent); - - AString toString() const override; - - int32_t m_targetRowIndex; - - int32_t m_targetColumnIndex; - - int32_t m_sourceRowIndex; - - int32_t m_sourceColumnIndex; - - BrowserTabContent* m_browserTabContent; - - }; - - /** - * Contains the tabs and stretching for one row or one column - */ - class RowColumnContent : public CaretObject { - public: - RowColumnContent(const std::vector& existingTabs, - TileTabsConfiguration* tileTabsConfiguration, - const int32_t rowColumnIndex, - const bool rowFlag); - - RowColumnContent(const RowColumnContent& obj); - - static RowColumnContent* newInstanceContainingSpacers(const int32_t numberOfElements); - - ~RowColumnContent(); - - RowColumnContent* clone(AString& errorMessageOut) const; - - AString toString() const override; - - std::vector m_tabElements; - - TileTabsGridRowColumnElement* m_stretching; - - private: - RowColumnContent(const int32_t numberOfElements); - }; - - private: - - void loadRowColumnsFromTileTabsConfiguration(); - - bool loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut); - - bool performModification(AString& errorMessageOut); - - const std::vector& m_existingTabs; - - EventTileTabsConfigurationModification* m_modifyEvent; - - std::vector m_rowColumns; - - std::vector m_browserTabsToDelete; - - /** - * This is the current tile tabs configuration in the window so DO NOT delete it - */ - TileTabsConfiguration* m_currentTileTabsConfiguration = NULL; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ - // -#endif // __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ - -} // namespace -#endif //__TILE_TABS_CONFIGURATION_MODIFIER_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabsGridConfigurationModifier.cxx connectome-workbench-1.5.0/src/GuiQt/TileTabsGridConfigurationModifier.cxx --- connectome-workbench-1.4.2/src/GuiQt/TileTabsGridConfigurationModifier.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabsGridConfigurationModifier.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,630 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __TILE_TABS_GRID_CONFIGURATION_MODIFIER_DECLARE__ +#include "TileTabsGridConfigurationModifier.h" +#undef __TILE_TABS_GRID_CONFIGURATION_MODIFIER_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrainOpenGLViewportContent.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventBrowserTabNewClone.h" +#include "EventBrowserTabDelete.h" +#include "EventBrowserTabNew.h" +#include "EventBrowserWindowTileTabOperation.h" +#include "EventManager.h" +#include "EventTileTabsGridConfigurationModification.h" +#include "GuiManager.h" +#include "SpacerTabContent.h" +#include "TileTabsLayoutGridConfiguration.h" + +using namespace caret; + +static bool debugFlag = false; + +/** + * \class caret::TileTabsGridConfigurationModifier + * \brief Modifies a tile tabs grid configuration. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param existingTabs + * All tabs in the window. + * @param windowIndex + * Index of window requesting this operation + * @param modifyEvent + * Event describing modification. + */ +TileTabsGridConfigurationModifier::TileTabsGridConfigurationModifier(const std::vector& existingTabs, + const int32_t windowIndex, + EventTileTabsGridConfigurationModification* modifyEvent) +: CaretObject(), +m_existingTabs(existingTabs), +m_windowIndex(windowIndex), +m_modifyEvent(modifyEvent) +{ + CaretAssert(modifyEvent); + m_currentTileTabsConfiguration = modifyEvent->getTileTabsConfiguration(); + CaretAssert(m_currentTileTabsConfiguration); +} + +/** + * Destructor. + */ +TileTabsGridConfigurationModifier::~TileTabsGridConfigurationModifier() +{ + for (auto rc : m_rowColumns) { + delete rc; + } + m_rowColumns.clear(); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +TileTabsGridConfigurationModifier::toString() const +{ + AString s; + for (const auto rc : m_rowColumns) { + if ( ! s.isEmpty()) { + s.append("\n"); + } + s.append(rc->toString()); + } + return s; +} + +/** + * Run to perform the operation. + * + * @param errorMessageOut + * Contains error information if operation fails. + * @return + * True if successful, else false. + */ +bool +TileTabsGridConfigurationModifier::run(AString& errorMessageOut) +{ + errorMessageOut.clear(); + + loadRowColumnsFromTileTabsConfiguration(); + if (debugFlag) { + std::cout << "Loaded: " << toString() << std::endl << std::flush; + } + bool validFlag = performModification(errorMessageOut); + if (debugFlag) { + std::cout << "After Modification: " << toString() << std::endl << std::endl << std::flush; + } + + if (validFlag) { + validFlag = loadRowColumnsIntoTileTabsConfiguration(errorMessageOut); + } + + return validFlag; +} + +/** + * Load the rows or columns from the Tile Tabs Configuration + */ +void +TileTabsGridConfigurationModifier::loadRowColumnsFromTileTabsConfiguration() +{ + const int32_t numRows = m_currentTileTabsConfiguration->getNumberOfRows(); + const int32_t numColumns = m_currentTileTabsConfiguration->getNumberOfColumns(); + + switch (m_modifyEvent->getRowColumnType()) { + case EventTileTabsGridConfigurationModification::RowColumnType::COLUMN: + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + m_rowColumns.push_back(new RowColumnContent(m_existingTabs, + m_modifyEvent->getTileTabsConfiguration(), + jCol, + false)); + } + break; + case EventTileTabsGridConfigurationModification::RowColumnType::ROW: + for (int32_t iRow = 0; iRow < numRows; iRow++) { + m_rowColumns.push_back(new RowColumnContent(m_existingTabs, + m_modifyEvent->getTileTabsConfiguration(), + iRow, + true)); + } + break; + } +} + +/** + * Perform the modification. + * + * @param errorMessageOut + * Contains error information if operation fails. + * @return + * True if successful, else false. + */ +bool +TileTabsGridConfigurationModifier::performModification(AString& errorMessageOut) +{ + + const int32_t rowColumnIndex = m_modifyEvent->getRowColumnIndex(); + + const int32_t numRowColumns = static_cast(m_rowColumns.size()); + switch (m_modifyEvent->getOperation()) { + case EventTileTabsGridConfigurationModification::Operation::DELETE_IT: + if (numRowColumns <= 1) { + errorMessageOut = "Cannot delete ROWCOL when there is only one ROWCOL"; + } + else if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRowColumns)) { + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); + RowColumnContent* deleteRowColumn = m_rowColumns[rowColumnIndex]; + CaretAssert(deleteRowColumn); + m_rowColumns.erase(m_rowColumns.begin() + rowColumnIndex); + CaretAssert(std::find(m_rowColumns.begin(), + m_rowColumns.end(), + deleteRowColumn) == m_rowColumns.end()); + + for (auto t : deleteRowColumn->m_tabElements) { + if (t->m_browserTabContent != NULL) { + m_browserTabsToDelete.push_back(t->m_browserTabContent); + } + } + delete deleteRowColumn; + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when deleting"; + } + break; + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_AFTER: + if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRowColumns)) { + RowColumnContent* rowColumnCopy = m_rowColumns[rowColumnIndex]->clone(errorMessageOut); + if (rowColumnCopy != NULL) { + const int32_t insertOffset = (rowColumnIndex + 1); + m_rowColumns.insert(m_rowColumns.begin() + + insertOffset, + rowColumnCopy); + } + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; + } + break; + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_BEFORE: + if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRowColumns)) { + RowColumnContent* rowColumnCopy = m_rowColumns[rowColumnIndex]->clone(errorMessageOut); + if (rowColumnCopy != NULL) { + const int32_t insertOffset = rowColumnIndex; + m_rowColumns.insert(m_rowColumns.begin() + + insertOffset, + rowColumnCopy); + } + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; + } + break; + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_AFTER: + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_BEFORE: + if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRowColumns)) { + CaretAssertVectorIndex(m_rowColumns, 0); + const int32_t numberOfElements = m_rowColumns[0]->m_tabElements.size(); + RowColumnContent* rowColumnSpacer = RowColumnContent::newInstanceContainingSpacers(numberOfElements); + if (rowColumnSpacer != NULL) { + int32_t insertOffset = rowColumnIndex; + if (m_modifyEvent->getOperation() == EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_AFTER) { + insertOffset++; + } + m_rowColumns.insert(m_rowColumns.begin() + + insertOffset, + rowColumnSpacer); + } + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when insert spacer"; + } + break; + case EventTileTabsGridConfigurationModification::Operation::MOVE_AFTER: + if (numRowColumns <= 1) { + errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; + } + else if (rowColumnIndex == (numRowColumns - 1)) { + errorMessageOut = "Cannot move last ROWCOL after itself"; + } + else if ((rowColumnIndex >= 0) + && (rowColumnIndex < (numRowColumns - 1))) { + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex + 1); + std::swap(m_rowColumns[rowColumnIndex], + m_rowColumns[rowColumnIndex + 1]); + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when moving"; + } + break; + case EventTileTabsGridConfigurationModification::Operation::MOVE_BEFORE: + if (numRowColumns <= 1) { + errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; + } + else if (rowColumnIndex == 0) { + errorMessageOut = "Cannot move last ROWCOL before itself"; + } + else if ((rowColumnIndex >= 1) + && (rowColumnIndex < numRowColumns)) { + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex - 1); + std::swap(m_rowColumns[rowColumnIndex], + m_rowColumns[rowColumnIndex - 1]); + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when moving"; + } + break; + } + + if ( ! errorMessageOut.isEmpty()) { + AString nameText; + switch (m_modifyEvent->getRowColumnType()) { + case EventTileTabsGridConfigurationModification::RowColumnType::COLUMN: + nameText = " Column "; + break; + case EventTileTabsGridConfigurationModification::RowColumnType::ROW: + nameText = " Row "; + break; + } + + /* + * "substite names" + */ + errorMessageOut = errorMessageOut.replace("ROWCOL", nameText); + errorMessageOut = errorMessageOut.replace("RCINDEX", AString::number(rowColumnIndex + 1)); + } + + const bool validFlag = errorMessageOut.isEmpty(); + return validFlag; +} + +/** + * Load the modified rows/columns into the tile tabs configuration current in the browser window + * + * @param errorMessageOut + * Contains error information if operation fails. + * @return + * True if successful, else false. + */ +bool +TileTabsGridConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut) +{ + TileTabsLayoutGridConfiguration newConfiguration(*m_currentTileTabsConfiguration); + std::vector browserTabs; + + switch (m_modifyEvent->getRowColumnType()) { + case EventTileTabsGridConfigurationModification::RowColumnType::COLUMN: + { + const int32_t numRows = static_cast(m_rowColumns[0]->m_tabElements.size()); + CaretAssert(numRows == m_currentTileTabsConfiguration->getNumberOfRows()); + const int32_t numColumns = static_cast(m_rowColumns.size()); + + newConfiguration.setNumberOfRows(numRows); + newConfiguration.setNumberOfColumns(numColumns); + + for (int32_t iRow = 0; iRow < numRows; iRow++) { + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + RowColumnContent* columnContent = m_rowColumns[jCol]; + CaretAssertVectorIndex(columnContent->m_tabElements, iRow); + BrowserTabContent* btc = columnContent->m_tabElements[iRow]->m_browserTabContent; + if (btc != NULL) { + browserTabs.push_back(btc); + } + } + } + + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + TileTabsGridRowColumnElement* rce = newConfiguration.getColumn(jCol); + CaretAssertVectorIndex(m_rowColumns, jCol); + CaretAssert(m_rowColumns[jCol]->m_stretching); + *rce = *m_rowColumns[jCol]->m_stretching; + } + } + break; + case EventTileTabsGridConfigurationModification::RowColumnType::ROW: + { + const int32_t numRows = static_cast(m_rowColumns.size()); + const int32_t numColumns = static_cast(m_rowColumns[0]->m_tabElements.size()); + CaretAssert(numColumns == m_currentTileTabsConfiguration->getNumberOfColumns()); + + newConfiguration.setNumberOfRows(numRows); + newConfiguration.setNumberOfColumns(numColumns); + + for (int32_t iRow = 0; iRow < numRows; iRow++) { + CaretAssertVectorIndex(m_rowColumns, iRow); + RowColumnContent* rowContent = m_rowColumns[iRow]; + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + CaretAssertVectorIndex(rowContent->m_tabElements, jCol); + BrowserTabContent* btc = rowContent->m_tabElements[jCol]->m_browserTabContent; + if (btc != NULL) { + browserTabs.push_back(btc); + } + } + } + + for (int32_t iRow = 0; iRow < numRows; iRow++) { + TileTabsGridRowColumnElement* rce = newConfiguration.getRow(iRow); + CaretAssertVectorIndex(m_rowColumns, iRow); + CaretAssert(m_rowColumns[iRow]->m_stretching); + *rce = *m_rowColumns[iRow]->m_stretching; + } + } + break; + } + + /* + * Copy new tile tabs configuration into the current configuration + */ + *m_currentTileTabsConfiguration = newConfiguration; + + /* + * Update tabs in the window's toolbar + */ + QWidget* parentWindow(GuiManager::get()->getBrowserWindowByWindowIndex(m_modifyEvent->getWindowIndex())); + const int32_t invalidTabIndex(-1); + const int32_t dummyWindowViewport[4] { -1, -1, -1, -1 }; + const int32_t dummyMouseX(-1); + const int32_t dummyMouseY(-1); + EventBrowserWindowTileTabOperation updateTabsEvent(EventBrowserWindowTileTabOperation::OPERATION_REPLACE_TABS, + parentWindow, + m_modifyEvent->getWindowIndex(), + invalidTabIndex, + dummyWindowViewport, + dummyMouseX, + dummyMouseY, + browserTabs); + EventManager::get()->sendEvent(updateTabsEvent.getPointer()); + + if (updateTabsEvent.isError()) { + errorMessageOut.appendWithNewLine(updateTabsEvent.getErrorMessage()); + } + + if ( ! m_browserTabsToDelete.empty()) { + for (auto btc : m_browserTabsToDelete) { + EventBrowserTabDelete deleteEvent(btc, + btc->getTabNumber(), + m_windowIndex); + EventManager::get()->sendEvent(deleteEvent.getPointer()); + if (deleteEvent.isError()) { + errorMessageOut.appendWithNewLine(deleteEvent.getErrorMessage()); + } + } + m_browserTabsToDelete.clear(); + } + + if (errorMessageOut.isEmpty()) { + return true; + } + + return false; +} + +/* + * Constructor for element in the row column matrix. + * + * @param rowIndex + * The row index + * @param columnIndex + * The column index + * @param browserTabContent + * Browser tab content at (rowIndex, columnIndex) in the current + * Tile Tabs Configuration + */ +TileTabsGridConfigurationModifier::Element::Element(const int32_t rowIndex, + const int32_t columnIndex, + BrowserTabContent* browserTabContent) +: +m_targetRowIndex(rowIndex), +m_targetColumnIndex(columnIndex), +m_sourceRowIndex(rowIndex), +m_sourceColumnIndex(columnIndex), +m_browserTabContent(browserTabContent) +{ + +} + +/** + * @return String representation of object. + */ +AString +TileTabsGridConfigurationModifier::Element::toString() const +{ + AString s("(row=%1, column=%2)"); + s = s.arg(m_sourceRowIndex).arg(m_sourceColumnIndex); + return s; +} + +/* + * Constructor for content of a row/column element that + * contains the tabs and stretching for one row or one column + * + * @param existingTabs + * Existing browser tabs. + * @param tileTabsConfiguration + * The current tile tabs configuration in the browser window. + * @param rowColumnIndex + * Index of the row or column related to modification. + * @param rowFlag + * True if rows are being operated upon, false if operating on columns + */ +TileTabsGridConfigurationModifier::RowColumnContent::RowColumnContent(const std::vector& existingTabs, + TileTabsLayoutGridConfiguration* tileTabsConfiguration, + const int32_t rowColumnIndex, + const bool rowFlag) +{ + const int32_t numRows = tileTabsConfiguration->getNumberOfRows(); + const int32_t numColumns = tileTabsConfiguration->getNumberOfColumns(); + const int32_t numberOfTabs =static_cast(existingTabs.size()); + + if (rowFlag) { + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + BrowserTabContent* browserTabContent(NULL); + const int32_t tabIndex = (rowColumnIndex * numColumns) + jCol; + if (tabIndex < numberOfTabs) { + CaretAssertVectorIndex(existingTabs, tabIndex); + browserTabContent = existingTabs[tabIndex]->getBrowserTabContent(); + } + + m_tabElements.push_back(new Element(rowColumnIndex, + jCol, + browserTabContent)); + } + + m_stretching = new TileTabsGridRowColumnElement(*tileTabsConfiguration->getRow(rowColumnIndex)); + } + else { + for (int32_t iRow = 0; iRow < numRows; iRow++) { + BrowserTabContent* browserTabContent(NULL); + const int32_t tabIndex = (iRow * numColumns) + rowColumnIndex; + if (tabIndex < numberOfTabs) { + CaretAssertVectorIndex(existingTabs, tabIndex); + browserTabContent = existingTabs[tabIndex]->getBrowserTabContent(); + } + + m_tabElements.push_back(new Element(iRow, + rowColumnIndex, + browserTabContent)); + } + + m_stretching = new TileTabsGridRowColumnElement(*tileTabsConfiguration->getColumn(rowColumnIndex)); + } +} + +/* + * Constructor that creates the given number of elements. + * + * @param numberOfElements + * Number of elements for row/column. + */ +TileTabsGridConfigurationModifier::RowColumnContent::RowColumnContent(const int32_t numberOfElements) +{ + for (int32_t i = 0; i < numberOfElements; i++) { + m_tabElements.push_back(new Element(i, i, NULL)); + } + m_stretching = new TileTabsGridRowColumnElement(); +} + +/** + * @return New instance containing the given number of elements setup as spacers. + * + * @param numberOfElements + * Number of elements for row/column. + */ +TileTabsGridConfigurationModifier::RowColumnContent* +TileTabsGridConfigurationModifier::RowColumnContent::newInstanceContainingSpacers(const int32_t numberOfElements) +{ + RowColumnContent* content = new RowColumnContent(numberOfElements); + + content->m_stretching->setContentType(TileTabsGridRowColumnContentTypeEnum::SPACE); + content->m_stretching->setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); + content->m_stretching->setWeightStretch(1.0); + + return content; +} + +/** + * Destructor. + */ +TileTabsGridConfigurationModifier::RowColumnContent::~RowColumnContent() +{ + for (auto te : m_tabElements) { + delete te; + } + m_tabElements.clear(); + + delete m_stretching; + m_stretching = NULL; +} + +/** + * Copy constructor. + * + * @param obj + * Instance that is copied. + */ +TileTabsGridConfigurationModifier::RowColumnContent::RowColumnContent(const RowColumnContent& obj) +: CaretObject(obj) +{ + for (auto te : obj.m_tabElements) { + m_tabElements.push_back(new Element(*te)); + } + + m_stretching = new TileTabsGridRowColumnElement(*obj.m_stretching); +} + +/** + * @return String representation of object. + */ +AString +TileTabsGridConfigurationModifier::RowColumnContent::toString() const +{ + AString s; + for (const auto te : m_tabElements) { + s += (" " + te->toString()); + } + return s; +} + +/** + * Clone this Row/column content instance. + * + * @param errorMessageOut + * Output with error message. + * @return + * True if successful, else false + */ +TileTabsGridConfigurationModifier::RowColumnContent* +TileTabsGridConfigurationModifier::RowColumnContent::clone(AString& errorMessageOut) const +{ + RowColumnContent* cloned = new RowColumnContent(*this); + CaretAssert(cloned); + + for (auto te : cloned->m_tabElements) { + if (te->m_browserTabContent != NULL) { + EventBrowserTabNewClone cloneTabEvent(te->m_browserTabContent->getTabNumber()); + EventManager::get()->sendEvent(cloneTabEvent.getPointer()); + if (cloneTabEvent.isError()) { + errorMessageOut.appendWithNewLine(cloneTabEvent.getErrorMessage()); + te->m_browserTabContent = NULL; + } + else { + te->m_browserTabContent = cloneTabEvent.getNewBrowserTab(); + } + } + } + + return cloned; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabsGridConfigurationModifier.h connectome-workbench-1.5.0/src/GuiQt/TileTabsGridConfigurationModifier.h --- connectome-workbench-1.4.2/src/GuiQt/TileTabsGridConfigurationModifier.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabsGridConfigurationModifier.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,141 @@ +#ifndef __TILE_TABS_GRID_CONFIGURATION_MODIFIER_H__ +#define __TILE_TABS_GRID_CONFIGURATION_MODIFIER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include +#include "CaretObject.h" + + + +namespace caret { + + class BrainOpenGLViewportContent; + class BrowserTabContent; + class EventTileTabsGridConfigurationModification; + class SpacerTabContent; + class TileTabsLayoutGridConfiguration; + class TileTabsGridRowColumnElement; + + class TileTabsGridConfigurationModifier : public CaretObject { + + public: + TileTabsGridConfigurationModifier(const std::vector& existingTabs, + const int32_t windowIndex, + EventTileTabsGridConfigurationModification* modifyEvent); + + virtual ~TileTabsGridConfigurationModifier(); + + TileTabsGridConfigurationModifier(const TileTabsGridConfigurationModifier&) = delete; + + TileTabsGridConfigurationModifier& operator=(const TileTabsGridConfigurationModifier&) = delete; + + bool run(AString& errorMessageOut); + + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + class Element : public CaretObject { + public: + Element(const int32_t rowIndex, + const int32_t columnIndex, + BrowserTabContent* browserTabContent); + + AString toString() const override; + + int32_t m_targetRowIndex; + + int32_t m_targetColumnIndex; + + int32_t m_sourceRowIndex; + + int32_t m_sourceColumnIndex; + + BrowserTabContent* m_browserTabContent; + + }; + + /** + * Contains the tabs and stretching for one row or one column + */ + class RowColumnContent : public CaretObject { + public: + RowColumnContent(const std::vector& existingTabs, + TileTabsLayoutGridConfiguration* tileTabsConfiguration, + const int32_t rowColumnIndex, + const bool rowFlag); + + RowColumnContent(const RowColumnContent& obj); + + static RowColumnContent* newInstanceContainingSpacers(const int32_t numberOfElements); + + ~RowColumnContent(); + + RowColumnContent* clone(AString& errorMessageOut) const; + + AString toString() const override; + + std::vector m_tabElements; + + TileTabsGridRowColumnElement* m_stretching; + + private: + RowColumnContent(const int32_t numberOfElements); + }; + + private: + + void loadRowColumnsFromTileTabsConfiguration(); + + bool loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut); + + bool performModification(AString& errorMessageOut); + + const std::vector& m_existingTabs; + + const int32_t m_windowIndex; + + EventTileTabsGridConfigurationModification* m_modifyEvent; + + std::vector m_rowColumns; + + std::vector m_browserTabsToDelete; + + /** + * This is the current tile tabs configuration in the window so DO NOT delete it + */ + TileTabsLayoutGridConfiguration* m_currentTileTabsConfiguration = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __TILE_TABS_GRID_CONFIGURATION_MODIFIER_DECLARE__ + // +#endif // __TILE_TABS_GRID_CONFIGURATION_MODIFIER_DECLARE__ + +} // namespace +#endif //__TILE_TABS_GRID_CONFIGURATION_MODIFIER_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabsManualTabGeometryWidget.cxx connectome-workbench-1.5.0/src/GuiQt/TileTabsManualTabGeometryWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/TileTabsManualTabGeometryWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabsManualTabGeometryWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,375 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __TILE_TABS_MANUAL_TAB_GEOMETRY_WIDGET_DECLARE__ +#include "TileTabsManualTabGeometryWidget.h" +#undef __TILE_TABS_MANUAL_TAB_GEOMETRY_WIDGET_DECLARE__ + +#include +#include +#include +#include + +#include "AnnotationBrowserTab.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EnumComboBoxTemplate.h" +#include "TileTabsConfigurationDialog.h" +#include "TileTabsBrowserTabGeometry.h" +#include "WuQGridLayoutGroup.h" +#include "WuQtUtilities.h" + +using namespace caret; + +/** + * \class caret::TileTabsManualTabGeometryWidget + * \brief Contains widgets for adjusting a manual layout tab's geometry + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param tileTabsConfigurationDialog + * The tile tabs configuration dialog. + * @param index + * Index of this item in dialog (NOT the tab index) + * @param gridLayout + * Gridlayout for widgets + * @param parent + * Parent QObject + */ +TileTabsManualTabGeometryWidget::TileTabsManualTabGeometryWidget(TileTabsConfigurationDialog* tileTabsConfigurationDialog, + const int32_t index, + QGridLayout* gridLayout, + QObject* parent) +: QObject(parent), +m_tileTabsConfigurationDialog(tileTabsConfigurationDialog), +m_index(index) +{ + CaretAssert(tileTabsConfigurationDialog); + CaretAssert(gridLayout); + + m_showTabCheckBox = new QCheckBox(""); + QObject::connect(m_showTabCheckBox, &QCheckBox::clicked, + this, &TileTabsManualTabGeometryWidget::showCheckBoxClicked); + + m_tabNumberLabel = new QLabel(" "); + m_tabNumberLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + m_xMinSpinBox = createPercentSpinBox("Left edge of tab", true); + QObject::connect(m_xMinSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &TileTabsManualTabGeometryWidget::xMinSpinBoxValueChanged); + + m_xMaxSpinBox = createPercentSpinBox("Right edge of tab", false); + QObject::connect(m_xMaxSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &TileTabsManualTabGeometryWidget::xMaxSpinBoxValueChanged); + + m_yMinSpinBox = createPercentSpinBox("Bottom edge of tab", true); + QObject::connect(m_yMinSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &TileTabsManualTabGeometryWidget::yMinSpinBoxValueChanged); + + m_yMaxSpinBox = createPercentSpinBox("Top edge of tab", false); + QObject::connect(m_yMaxSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &TileTabsManualTabGeometryWidget::yMaxSpinBoxValueChanged); + + const QString stackOrderToolTipText("Larger numbers are in front of other tabs. This value only needs adjustment " + "if this tab overlaps with another tab"); + m_stackingOrderSpinBox = new QSpinBox(); + m_stackingOrderSpinBox->setMinimum(-100); + m_stackingOrderSpinBox->setMaximum(100); + m_stackingOrderSpinBox->setSingleStep(1); + m_stackingOrderSpinBox->setToolTip(getStackOrderToolTipText()); + QObject::connect(m_stackingOrderSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &TileTabsManualTabGeometryWidget::stackingOrderValueChanged); + + m_TileTabsLayoutBackgroundTypeEnumComboBox = new EnumComboBoxTemplate(this); + m_TileTabsLayoutBackgroundTypeEnumComboBox->setup(); + m_TileTabsLayoutBackgroundTypeEnumComboBox->getComboBox()->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); + QObject::connect(m_TileTabsLayoutBackgroundTypeEnumComboBox, SIGNAL(itemActivated()), + this, SLOT(tileTabsLayoutBackgroundTypeEnumComboBoxItemActivated())); + + m_gridLayoutGroup = new WuQGridLayoutGroup(gridLayout); + const int32_t rowIndex(gridLayout->rowCount()); + int32_t columnIndex(0); + m_gridLayoutGroup->addWidget(m_showTabCheckBox, rowIndex, columnIndex++, Qt::AlignHCenter); + m_gridLayoutGroup->addWidget(m_tabNumberLabel, rowIndex, columnIndex++, Qt::AlignLeft); + m_gridLayoutGroup->addWidget(m_xMinSpinBox, rowIndex, columnIndex++, Qt::AlignHCenter); + m_gridLayoutGroup->addWidget(m_xMaxSpinBox, rowIndex, columnIndex++, Qt::AlignHCenter); + m_gridLayoutGroup->addWidget(m_yMinSpinBox, rowIndex, columnIndex++, Qt::AlignHCenter); + m_gridLayoutGroup->addWidget(m_yMaxSpinBox, rowIndex, columnIndex++, Qt::AlignHCenter); + m_gridLayoutGroup->addWidget(m_TileTabsLayoutBackgroundTypeEnumComboBox->getWidget(), rowIndex, columnIndex++, Qt::AlignHCenter); + m_gridLayoutGroup->addWidget(m_stackingOrderSpinBox, rowIndex, columnIndex++); +} + +/** + * Destructor. + */ +TileTabsManualTabGeometryWidget::~TileTabsManualTabGeometryWidget() +{ +} + +/** + * @return Tooltip text for order spin box + */ +QString +TileTabsManualTabGeometryWidget::getStackOrderToolTipText() +{ + return WuQtUtilities::createWordWrappedToolTipText("Increase to move behind\n" + "Decrease to move in front"); +} + +/** + * Update the content of this item + * + * @param browserTabContent + * Tab content for the widgets + */ +void +TileTabsManualTabGeometryWidget::updateContent(BrowserTabContent* browserTabContent) +{ + m_browserTabContent = browserTabContent; + const bool showFlag(m_browserTabContent != NULL); + + if (showFlag) { + m_tabNumberLabel->setText(browserTabContent->getTabName()); + AnnotationBrowserTab* annotationBrowserTab = m_browserTabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + annotationBrowserTab->getBounds2D(minX, maxX, minY, maxY); + updatePercentSpinBox(m_xMinSpinBox, minX); + updatePercentSpinBox(m_xMaxSpinBox, maxX); + updatePercentSpinBox(m_yMinSpinBox, minY); + updatePercentSpinBox(m_yMaxSpinBox, maxY); + + m_showTabCheckBox->setChecked(annotationBrowserTab->isBrowserTabDisplayed()); + const TileTabsLayoutBackgroundTypeEnum::Enum background = annotationBrowserTab->getBackgroundType(); + m_TileTabsLayoutBackgroundTypeEnumComboBox->setSelectedItem(background); + + QSignalBlocker blocker(m_stackingOrderSpinBox); // setValue() causes signal + m_stackingOrderSpinBox->setValue(annotationBrowserTab->getStackingOrder()); + } + + m_gridLayoutGroup->setVisible(showFlag); + +} + +/** + * Update a percentage spin box value + * + * @param spinBox + * The spin box + * @param value + * New value for spin box + */ +void +TileTabsManualTabGeometryWidget::updatePercentSpinBox(QDoubleSpinBox* spinBox, + const double value) +{ + CaretAssert(spinBox); + + /* + * Must block signal because setValue() trigger's the signal + */ + QSignalBlocker blocker(spinBox); + spinBox->setValue(value); +} + +/** + * Create a double spin box for percentage value + * + * @param toolTip + * Tooltip for the spin box + * @param minValueFlag + * True if spin box is for a minimum value, else a maximum value + * @return + * The spin box + */ +QDoubleSpinBox* +TileTabsManualTabGeometryWidget::createPercentSpinBox(const QString& toolTip, + const bool minValueFlag) +{ + QDoubleSpinBox* dsb = new QDoubleSpinBox(); + if (minValueFlag) { + dsb->setMinimum(-99.0); + dsb->setMaximum(99.0); + } + else { + dsb->setMinimum(1.0); + dsb->setMaximum(199.0); + } + dsb->setDecimals(1); + dsb->setSingleStep(0.1); + dsb->setToolTip(toolTip); + + /* + * If keyboard tracking is on and user types values 4, 0 then two signals are + * emitted 4 and 40. We do not want this since we want to keep minimum values + * less than maximum values + */ + dsb->setKeyboardTracking(false); + + return dsb; +} + +/** + * Called when show checkbox is clicked + * + * @param status + * New checked status + */ +void +TileTabsManualTabGeometryWidget::showCheckBoxClicked(bool status) +{ + CaretAssert(m_browserTabContent); + if (m_browserTabContent != NULL) { + AnnotationBrowserTab* annotationBrowserTab = m_browserTabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + annotationBrowserTab->setBrowserTabDisplayed(status); + emit itemChanged(); + } +} + +/** + * Called when x-min value is changed + * + * @param value + * New value + */ +void +TileTabsManualTabGeometryWidget::xMinSpinBoxValueChanged(double value) +{ + CaretAssert(m_browserTabContent); + if (m_browserTabContent != NULL) { + AnnotationBrowserTab* annotationBrowserTab = m_browserTabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + annotationBrowserTab->getBounds2D(minX, maxX, minY, maxY); + minX = value; + annotationBrowserTab->setBounds2D(minX, maxX, minY, maxY); + updateContent(m_browserTabContent); // 'geometry' ensures min < max + emit itemChanged(); + } +} + +/** + * Called when x-max value is changed + * + * @param value + * New value + */ +void +TileTabsManualTabGeometryWidget::xMaxSpinBoxValueChanged(double value) +{ + CaretAssert(m_browserTabContent); + if (m_browserTabContent != NULL) { + AnnotationBrowserTab* annotationBrowserTab = m_browserTabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + annotationBrowserTab->getBounds2D(minX, maxX, minY, maxY); + maxX = value; + annotationBrowserTab->setBounds2D(minX, maxX, minY, maxY); + updateContent(m_browserTabContent); // 'geometry' ensures min < max + emit itemChanged(); + } +} + +/** + * Called when y-min value is changed + * + * @param value + * New value + */ +void +TileTabsManualTabGeometryWidget::yMinSpinBoxValueChanged(double value) +{ + CaretAssert(m_browserTabContent); + if (m_browserTabContent != NULL) { + AnnotationBrowserTab* annotationBrowserTab = m_browserTabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + annotationBrowserTab->getBounds2D(minX, maxX, minY, maxY); + minY = value; + annotationBrowserTab->setBounds2D(minX, maxX, minY, maxY); + updateContent(m_browserTabContent); // 'geometry' ensures min < max + emit itemChanged(); + } +} + +/** + * Called when x-max value is changed + * + * @param value + * New value + */ +void +TileTabsManualTabGeometryWidget::yMaxSpinBoxValueChanged(double value) +{ + CaretAssert(m_browserTabContent); + if (m_browserTabContent != NULL) { + AnnotationBrowserTab* annotationBrowserTab = m_browserTabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + float minX(0.0), maxX(0.0), minY(0.0), maxY(0.0); + annotationBrowserTab->getBounds2D(minX, maxX, minY, maxY); + maxY = value; + annotationBrowserTab->setBounds2D(minX, maxX, minY, maxY); + updateContent(m_browserTabContent); // 'geometry' ensures min < max + emit itemChanged(); + } +} + +/** + * Called when stacking order value is changed + * + * @param value + * New value + */ +void +TileTabsManualTabGeometryWidget::stackingOrderValueChanged(int value) +{ + CaretAssert(m_browserTabContent); + if (m_browserTabContent != NULL) { + AnnotationBrowserTab* annotationBrowserTab = m_browserTabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + annotationBrowserTab->setStackingOrder(value); + emit itemChanged(); + } +} + +/** + * Called when background value is changed + * + * @param value + * New value + */ +void +TileTabsManualTabGeometryWidget::tileTabsLayoutBackgroundTypeEnumComboBoxItemActivated() +{ + CaretAssert(m_browserTabContent); + if (m_browserTabContent != NULL) { + AnnotationBrowserTab* annotationBrowserTab = m_browserTabContent->getManualLayoutBrowserTabAnnotation(); + CaretAssert(annotationBrowserTab); + + const TileTabsLayoutBackgroundTypeEnum::Enum backgroundType = m_TileTabsLayoutBackgroundTypeEnumComboBox->getSelectedItem(); + annotationBrowserTab->setBackgroundType(backgroundType); + emit itemChanged(); + } +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/TileTabsManualTabGeometryWidget.h connectome-workbench-1.5.0/src/GuiQt/TileTabsManualTabGeometryWidget.h --- connectome-workbench-1.4.2/src/GuiQt/TileTabsManualTabGeometryWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/TileTabsManualTabGeometryWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,124 @@ +#ifndef __TILE_TABS_MANUAL_TAB_GEOMETRY_WIDGET_H__ +#define __TILE_TABS_MANUAL_TAB_GEOMETRY_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QCheckBox; +class QGridLayout; +class QLabel; +class QDoubleSpinBox; +class QSpinBox; + +namespace caret { + + class BrowserTabContent; + class EnumComboBoxTemplate; + class TileTabsConfigurationDialog; + class WuQGridLayoutGroup; + + class TileTabsManualTabGeometryWidget : public QObject { + + Q_OBJECT + + public: + TileTabsManualTabGeometryWidget(TileTabsConfigurationDialog* tileTabsConfigurationDialog, + const int32_t index, + QGridLayout* gridLayout, + QObject* parent); + + virtual ~TileTabsManualTabGeometryWidget(); + + TileTabsManualTabGeometryWidget(const TileTabsManualTabGeometryWidget&) = delete; + + TileTabsManualTabGeometryWidget& operator=(const TileTabsManualTabGeometryWidget&) = delete; + + void updateContent(BrowserTabContent* browserTabContent); + + static QString getStackOrderToolTipText(); + + // ADD_NEW_METHODS_HERE + + signals: + void itemChanged(); + + private slots: + void showCheckBoxClicked(bool status); + + void xMinSpinBoxValueChanged(double value); + + void xMaxSpinBoxValueChanged(double value); + + void yMinSpinBoxValueChanged(double value); + + void yMaxSpinBoxValueChanged(double value); + + void stackingOrderValueChanged(int value); + + void tileTabsLayoutBackgroundTypeEnumComboBoxItemActivated(); + + private: + QDoubleSpinBox* createPercentSpinBox(const QString& toolTip, + const bool minValueFlag); + + void updatePercentSpinBox(QDoubleSpinBox* spinBox, + const double value); + + TileTabsConfigurationDialog* m_tileTabsConfigurationDialog; + + /** index of this item; NOT the tab index */ + const int32_t m_index; + + BrowserTabContent* m_browserTabContent = NULL; + + QCheckBox* m_showTabCheckBox; + + QLabel* m_tabNumberLabel; + + QDoubleSpinBox* m_xMinSpinBox; + + QDoubleSpinBox* m_xMaxSpinBox; + + QDoubleSpinBox* m_yMinSpinBox; + + QDoubleSpinBox* m_yMaxSpinBox; + + QSpinBox* m_stackingOrderSpinBox; + + EnumComboBoxTemplate* m_TileTabsLayoutBackgroundTypeEnumComboBox; + + WuQGridLayoutGroup* m_gridLayoutGroup; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __TILE_TABS_MANUAL_TAB_GEOMETRY_WIDGET_DECLARE__ + // +#endif // __TILE_TABS_MANUAL_TAB_GEOMETRY_WIDGET_DECLARE__ + +} // namespace +#endif //__TILE_TABS_MANUAL_TAB_GEOMETRY_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeAbstract.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeAbstract.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeAbstract.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeAbstract.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -54,6 +54,7 @@ m_widgetForToolBar(NULL), m_mousePositionValid(false) { + std::vector emptyHistoryXY; m_mousePositionEvent.grabNew(new MouseEvent(NULL, NULL, -1, @@ -63,6 +64,7 @@ 0, 0, 0, + emptyHistoryXY, false)); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeAbstract.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeAbstract.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeAbstract.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeAbstract.h 2021-02-16 19:46:47.000000000 +0000 @@ -34,6 +34,7 @@ class BrainOpenGLViewportContent; class BrainOpenGLWidget; + class GestureEvent; class KeyEvent; class MouseEvent; @@ -192,6 +193,14 @@ virtual void mouseMoveWithShift(const MouseEvent& /*mouseEvent*/) { } /** + * Process a gesture event (pinch zoom; or rotate) + * + * @param gestureEvent + * Gesture event information. + */ + virtual void gestureEvent(const GestureEvent& /*gestureEvent*/) { } + + /** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -31,7 +31,7 @@ #include "AnnotationCreateDialog.h" #include "AnnotationFile.h" #include "AnnotationManager.h" -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationText.h" #include "AnnotationTextEditorDialog.h" @@ -92,7 +92,7 @@ const int32_t browserWindexIndex = m_mouseEvent.getBrowserWindowIndex(); std::vector > selectedAnnotations; AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); - annotationManager->getAnnotationsSelectedForEditingIncludingLabels(browserWindexIndex, + annotationManager->getAnnotationsAndFilesSelectedForEditingIncludingLabels(browserWindexIndex, selectedAnnotations); m_annotationFile = NULL; @@ -103,6 +103,9 @@ allSelectedAnnotationsDeletableFlag = false; } + bool cutCopyValidFlag(true); + int32_t tabIndex(-1); + m_tabSpaceFileAndAnnotations.clear(); m_threeDimCoordAnnotations.clear(); for (std::vector >::iterator iter = selectedAnnotations.begin(); iter != selectedAnnotations.end(); @@ -124,6 +127,13 @@ threeDimCoordFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: + if (tabIndex < 0) { + tabIndex = ann->getTabIndex(); + } + if (tabIndex == ann->getTabIndex()) { + m_tabSpaceFileAndAnnotations.push_back(std::make_pair(iter->second, + ann)); + } break; case AnnotationCoordinateSpaceEnum::VIEWPORT: break; @@ -137,11 +147,25 @@ if ( ! ann->testProperty(Annotation::Property::DELETION)) { allSelectedAnnotationsDeletableFlag = false; } + + if ( !ann->testProperty(Annotation::Property::COPY_CUT_PASTE)) { + cutCopyValidFlag = false; + } } const bool haveThreeDimCoordAnnotationsFlag = ( ! m_threeDimCoordAnnotations.empty()); + /* + * For tab space annotations, all selected annotations MUST be in the same tab + */ + if (m_tabSpaceFileAndAnnotations.size() != selectedAnnotations.size()) { + m_tabSpaceFileAndAnnotations.clear(); + } + + bool oneAnnotationSelectedFlag(false); bool oneDeletableAnnotationSelectedFlag = false; if (selectedAnnotations.size() == 1) { + oneAnnotationSelectedFlag = true; + CaretAssertVectorIndex(selectedAnnotations, 0); m_annotationFile = selectedAnnotations[0].second; m_annotation = selectedAnnotations[0].first; @@ -171,14 +195,16 @@ */ QAction* cutAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::CUT), this, SLOT(cutAnnnotation())); - cutAction->setEnabled(oneDeletableAnnotationSelectedFlag); + cutAction->setEnabled(oneDeletableAnnotationSelectedFlag + && cutCopyValidFlag); /* * Copy */ QAction* copyAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::COPY), this, SLOT(copyAnnotationToAnnotationClipboard())); - copyAction->setEnabled(oneDeletableAnnotationSelectedFlag); + copyAction->setEnabled(oneDeletableAnnotationSelectedFlag + && cutCopyValidFlag); /* * Delete @@ -215,8 +241,11 @@ addSeparator(); /* - * Select All annotations + * De/Select All annotations */ + QAction* deselectAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::DESELECT_ALL), + this, SLOT(deselectAllAnnotations())); + deselectAction->setEnabled( ! selectedAnnotations.empty()); addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::SELECT_ALL), this, SLOT(selectAllAnnotations())); @@ -224,6 +253,33 @@ * Separator */ addSeparator(); + + /* + * Order Operations + */ + QAction* bringToFrontAction = addAction("Bring to Front"); + QObject::connect(bringToFrontAction, &QAction::triggered, + this, [=]() { processAnnotationOrderOperation(AnnotationStackingOrderTypeEnum::BRING_TO_FRONT); }); + QAction* bringForwardAction = addAction("Bring Forward"); + QObject::connect(bringForwardAction, &QAction::triggered, + this, [=]() { processAnnotationOrderOperation(AnnotationStackingOrderTypeEnum::BRING_FORWARD); }); + QAction* sendToBackAction = addAction("Send to Back"); + QObject::connect(sendToBackAction, &QAction::triggered, + this, [=]() { processAnnotationOrderOperation(AnnotationStackingOrderTypeEnum::SEND_TO_BACK); }); + QAction* sendBackwardAction = addAction("Send Backward"); + QObject::connect(sendBackwardAction, &QAction::triggered, + this, [=]() { processAnnotationOrderOperation(AnnotationStackingOrderTypeEnum::SEND_BACKWARD); }); + + bringToFrontAction->setEnabled(oneAnnotationSelectedFlag); + bringForwardAction->setEnabled(oneAnnotationSelectedFlag); + sendToBackAction->setEnabled(oneAnnotationSelectedFlag); + sendBackwardAction->setEnabled(oneAnnotationSelectedFlag); + + + /* + * Separator + */ + addSeparator(); /* * Edit Text @@ -349,8 +405,9 @@ AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeDeleteAnnotations(selectedAnnotations); AString errorMessage; - if ( ! annotationManager->applyCommand(undoCommand, - errorMessage)) { + if ( ! annotationManager->applyCommand(m_userInputModeAnnotations->getUserInputMode(), + undoCommand, + errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } @@ -378,6 +435,15 @@ } /** + * Deselect all annotations in the window. + */ +void +UserInputModeAnnotationsContextMenu::deselectAllAnnotations() +{ + m_userInputModeAnnotations->processDeselectAllAnnotations(); +} + +/** * Select all annotations in the window. */ void @@ -386,7 +452,6 @@ m_userInputModeAnnotations->processSelectAllAnnotations(); } - /** * Set the text for an annotation. */ @@ -475,7 +540,7 @@ } if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { - CaretAssert(0); // TAB NOT ALLOWED + CaretAssert(0); /* TAB NOT ALLOWED */ return; } @@ -520,7 +585,7 @@ QMenu* UserInputModeAnnotationsContextMenu::createDuplicateTabSpaceAnnotationMenu() { - if (m_annotation == NULL) { + if (m_tabSpaceFileAndAnnotations.empty()) { return NULL; } @@ -528,29 +593,24 @@ EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); const std::vector allTabs = tabIndicesEvent.getAllBrowserTabs(); - bool menuValidFlag = false; - if (m_annotation->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { - if (allTabs.size() > 1) { - menuValidFlag = true; - } + if (allTabs.size() < 1) { + return NULL; } QMenu* menu = new QMenu("Duplicate to Tab"); QObject::connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(duplicateAnnotationSelected(QAction*))); - if (menuValidFlag) { - for (BrowserTabContent* tabContent : allTabs) { - if (tabContent->getTabNumber() != m_annotation->getTabIndex()) { - QAction* action = menu->addAction(tabContent->getTabName()); - action->setData((int)tabContent->getTabNumber()); - } + CaretAssertVectorIndex(m_tabSpaceFileAndAnnotations, 0); + CaretAssert(m_tabSpaceFileAndAnnotations[0].second->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB); + const int32_t tabIndex = m_tabSpaceFileAndAnnotations[0].second->getTabIndex(); + for (BrowserTabContent* tabContent : allTabs) { + if (tabContent->getTabNumber() != tabIndex) { + QAction* action = menu->addAction(tabContent->getTabName()); + action->setData((int)tabContent->getTabNumber()); } } - else { - menu->setEnabled(false); - } - + return menu; } @@ -564,33 +624,52 @@ UserInputModeAnnotationsContextMenu::duplicateAnnotationSelected(QAction* action) { CaretAssert(action); - CaretAssert(m_annotationFile); - CaretAssert(m_annotation); + CaretAssert(m_tabSpaceFileAndAnnotations.size() > 0); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); const int32_t tabIndex = action->data().toInt(); - Annotation* annCopy = m_annotation->clone(); - annCopy->setTabIndex(tabIndex); + std::vector> fileAnnCopies; + for (const auto& tabAnn : m_tabSpaceFileAndAnnotations) { + Annotation* annCopy = tabAnn.second->clone(); + annCopy->setTabIndex(tabIndex); + + DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); + dpa->updateForNewAnnotation(annCopy); + + fileAnnCopies.push_back(std::make_pair(tabAnn.first, + annCopy)); + } - DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); - dpa->updateForNewAnnotation(annCopy); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); - undoCommand->setModeDuplicateAnnotation(m_annotationFile, - annCopy); + undoCommand->setModeDuplicateAnnotations(fileAnnCopies); AString errorMessage; - if ( ! annotationManager->applyCommand(undoCommand, + if ( ! annotationManager->applyCommand(m_userInputModeAnnotations->getUserInputMode(), + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } - annotationManager->selectAnnotationForEditing(m_mouseEvent.getBrowserWindowIndex(), - AnnotationManager::SELECTION_MODE_SINGLE, - false, - annCopy); - + + bool firstTimeFlag(true); + for (auto& annCopy : fileAnnCopies) { + if (firstTimeFlag) { + firstTimeFlag = false; + annotationManager->selectAnnotationForEditing(m_mouseEvent.getBrowserWindowIndex(), + AnnotationManager::SELECTION_MODE_SINGLE, + false, /* shift key down */ + annCopy.second); + } + else { + annotationManager->selectAnnotationForEditing(m_mouseEvent.getBrowserWindowIndex(), + AnnotationManager::SELECTION_MODE_EXTENDED, + true, /* shift key down */ + annCopy.second); + } + } + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } @@ -634,7 +713,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyGroupingMode(m_mouseEvent.getBrowserWindowIndex(), + if ( ! annMan->applyGroupingMode(m_userInputModeAnnotations->getUserInputMode(), + m_mouseEvent.getBrowserWindowIndex(), grouping, errorMessage)) { WuQMessageBox::errorOk(this, @@ -645,4 +725,39 @@ EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } +/** + * Called to process an annotation order operation + * + * @param orderType + * The ordering type + */ +void +UserInputModeAnnotationsContextMenu::processAnnotationOrderOperation(const AnnotationStackingOrderTypeEnum::Enum orderType) +{ + const int32_t browserWindowIndex = m_mouseEvent.getBrowserWindowIndex(); + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + std::vector selectedAnnotations = annMan->getAnnotationsSelectedForEditing(browserWindowIndex); + if (selectedAnnotations.size() == 1) { + CaretAssertVectorIndex(selectedAnnotations, 0); + Annotation* selectedAnn = selectedAnnotations[0]; + std::vector sameSpaceAnnotations = annMan->getAnnotationsDrawnInSameWindowAndSpace(selectedAnn, + browserWindowIndex); + if ( ! sameSpaceAnnotations.empty()) { + sameSpaceAnnotations.push_back(selectedAnn); + + AString errorMessage; + if ( ! annMan->applyStackingOrder(sameSpaceAnnotations, + selectedAnn, + orderType, + browserWindowIndex, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + } + } +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotationsContextMenu.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotationsContextMenu.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotationsContextMenu.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotationsContextMenu.h 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ #include #include "AnnotationGroupingModeEnum.h" +#include "AnnotationStackingOrderTypeEnum.h" #include "MouseEvent.h" namespace caret { @@ -65,6 +66,8 @@ void pasteSpecialAnnotationFromAnnotationClipboard(); + void deselectAllAnnotations(); + void selectAllAnnotations(); void setAnnotationText(); @@ -84,7 +87,7 @@ void applyGroupingUngroup(); void duplicateAnnotationSelected(QAction*); - + private: UserInputModeAnnotationsContextMenu(const UserInputModeAnnotationsContextMenu&); @@ -96,6 +99,8 @@ QMenu* createDuplicateTabSpaceAnnotationMenu(); + void processAnnotationOrderOperation(const AnnotationStackingOrderTypeEnum::Enum orderType); + UserInputModeAnnotations* m_userInputModeAnnotations; /* @@ -113,6 +118,8 @@ std::vector m_threeDimCoordAnnotations; + std::vector> m_tabSpaceFileAndAnnotations; + Annotation* m_annotation; AnnotationText* m_textAnnotation; diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotations.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotations.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotations.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotations.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -25,6 +25,7 @@ #include +#include "AnnotationBrowserTab.h" #include "AnnotationChangeCoordinateDialog.h" #include "AnnotationCreateDialog.h" #include "AnnotationColorBar.h" @@ -32,14 +33,16 @@ #include "AnnotationCoordinateInformation.h" #include "AnnotationFile.h" #include "AnnotationManager.h" -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationMultiCoordinateShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationPasteDialog.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationSpatialModification.h" #include "AnnotationText.h" #include "AnnotationTextEditorDialog.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationOneCoordinateShape.h" #include "Brain.h" +#include "BrainBrowserWindow.h" #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" @@ -55,6 +58,8 @@ #include "EventIdentificationRequest.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "GestureEvent.h" #include "GuiManager.h" #include "IdentificationManager.h" #include "KeyEvent.h" @@ -82,9 +87,28 @@ /** * Constructor. + * + * @param windowIndex + * Index of window */ UserInputModeAnnotations::UserInputModeAnnotations(const int32_t windowIndex) -: UserInputModeView(UserInputModeEnum::ANNOTATIONS), +: UserInputModeAnnotations(UserInputModeEnum::Enum::ANNOTATIONS, + windowIndex) +{ +} + +/** + * Constructor. + * + * @param userInputMode + * Input + * @param windowIndex + * Index of window + */ +UserInputModeAnnotations::UserInputModeAnnotations(const UserInputModeEnum::Enum userInputMode, + const int32_t windowIndex) +: UserInputModeView(windowIndex, + userInputMode), m_browserWindowIndex(windowIndex), m_annotationUnderMouse(NULL), m_annotationBeingDragged(NULL) @@ -92,6 +116,7 @@ m_allowMultipleSelectionModeFlag = true; m_mode = MODE_SELECT; m_annotationUnderMouseSizeHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; + m_annotationUnderMousePolyLineCoordinateIndex = -1; m_modeNewAnnotationFileSpaceAndType.grabNew(new NewAnnotationFileSpaceAndType(NULL, AnnotationCoordinateSpaceEnum::VIEWPORT, @@ -131,10 +156,42 @@ annotationManager->deselectAllAnnotationsForEditing(m_browserWindowIndex); resetAnnotationUnderMouse(); + const AnnotationTypeEnum::Enum annType(annotationEvent->getAnnotationType()); m_modeNewAnnotationFileSpaceAndType.grabNew(new NewAnnotationFileSpaceAndType(annotationEvent->getAnnotationFile(), annotationEvent->getAnnotationSpace(), - annotationEvent->getAnnotationType())); - setMode(MODE_NEW_WITH_CLICK); + annType)); + + Mode mode(MODE_NEW_WITH_DRAG_START); + switch (annType) { + case AnnotationTypeEnum::BOX: + break; + case AnnotationTypeEnum::BROWSER_TAB: + break; + case AnnotationTypeEnum::COLOR_BAR: + break; + case AnnotationTypeEnum::IMAGE: + break; + case AnnotationTypeEnum::LINE: + break; + case AnnotationTypeEnum::OVAL: + break; + case AnnotationTypeEnum::POLY_LINE: + switch (annotationEvent->getPolyLineDrawingMode()) { + case EventAnnotationCreateNewType::CONTINUOUS: + mode = MODE_NEW_WITH_DRAG_START; + break; + case EventAnnotationCreateNewType::DISCRETE: + mode = MODE_NEW_WITH_CLICK_SERIES_START; + break; + } + break; + case AnnotationTypeEnum::SCALE_BAR: + break; + case AnnotationTypeEnum::TEXT: + break; + } + + setMode(mode); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } @@ -169,6 +226,7 @@ { m_annotationUnderMouse = NULL; m_annotationUnderMouseSizeHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; + m_annotationUnderMousePolyLineCoordinateIndex = -1; m_annotationBeingDragged = NULL; m_annotationBeingDraggedHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; } @@ -211,6 +269,36 @@ { if (m_mode != mode) { m_mode = mode; + + bool drawingModeFlag(false); + switch (m_mode) { + case MODE_NEW_WITH_DRAG_START: + drawingModeFlag = true; + break; + case MODE_NEW_WITH_CLICK_SERIES: + drawingModeFlag = true; + break; + case MODE_NEW_WITH_CLICK_SERIES_START: + drawingModeFlag = true; + break; + case MODE_NEW_WITH_DRAG: + drawingModeFlag = true; + break; + case MODE_PASTE: + break; + case MODE_PASTE_SPECIAL: + break; + case MODE_SELECT: + break; + case MODE_SET_COORDINATE_ONE: + break; + case MODE_SET_COORDINATE_TWO: + break; + } + + if ( ! drawingModeFlag) { + resetAnnotationBeingCreated(); + } } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } @@ -225,7 +313,13 @@ CursorEnum::Enum cursor = CursorEnum::CURSOR_DEFAULT; switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: + cursor = CursorEnum::CURSOR_CROSS; + break; + case MODE_NEW_WITH_CLICK_SERIES: + cursor = CursorEnum::CURSOR_CROSS; + break; + case MODE_NEW_WITH_CLICK_SERIES_START: cursor = CursorEnum::CURSOR_CROSS; break; case MODE_NEW_WITH_DRAG: @@ -278,6 +372,9 @@ case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: cursor = CursorEnum::CURSOR_ROTATION; break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + cursor = CursorEnum::CURSOR_RESIZE_BOTTOM_LEFT_TOP_RIGHT; + break; } } break; @@ -293,7 +390,7 @@ } /** - * Delete all selected annotations. + * Delete all selected annotations except color bars which are turned off for display */ void UserInputModeAnnotations::deleteSelectedAnnotations() @@ -305,7 +402,8 @@ AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeDeleteAnnotations(selectedAnnotations); AString errorMessage; - if ( ! annotationManager->applyCommand(undoCommand, + if ( ! annotationManager->applyCommand(getUserInputMode(), + undoCommand, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); @@ -353,7 +451,11 @@ case Qt::Key_Delete: { switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: + break; + case MODE_NEW_WITH_CLICK_SERIES: + break; + case MODE_NEW_WITH_CLICK_SERIES_START: break; case MODE_NEW_WITH_DRAG: break; @@ -376,7 +478,13 @@ { bool selectModeFlag = false; switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: + break; + case MODE_NEW_WITH_CLICK_SERIES: + selectModeFlag = true; + break; + case MODE_NEW_WITH_CLICK_SERIES_START: + selectModeFlag = true; break; case MODE_NEW_WITH_DRAG: break; @@ -476,8 +584,9 @@ break; } - AnnotationOneDimensionalShape* oneDim = dynamic_cast(selectedAnnotation); - AnnotationTwoDimensionalShape* twoDim = dynamic_cast(selectedAnnotation); + AnnotationTwoCoordinateShape* twoCoordShape = selectedAnnotation->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* oneCoordShape = selectedAnnotation->castToOneCoordinateShape(); + AnnotationMultiCoordinateShape* multiCoordShape = selectedAnnotation->castToMultiCoordinateShape(); { bool surfaceFlag = false; @@ -504,15 +613,15 @@ std::vector annotations; annotations.push_back(selectedAnnotation); - if (oneDim != NULL) { - AnnotationCoordinate startCoord = *oneDim->getStartCoordinate(); + if (twoCoordShape != NULL) { + AnnotationCoordinate startCoord = *twoCoordShape->getStartCoordinate(); float xyzStart[3]; startCoord.getXYZ(xyzStart); xyzStart[0] += dx; xyzStart[1] += dy; startCoord.setXYZ(xyzStart); - AnnotationCoordinate endCoord = *oneDim->getEndCoordinate(); + AnnotationCoordinate endCoord = *twoCoordShape->getEndCoordinate(); float xyzEnd[3]; endCoord.getXYZ(xyzEnd); xyzEnd[0] += dx; @@ -521,8 +630,8 @@ undoCommand->setModeCoordinateOneAndTwo(startCoord, endCoord, annotations); } - else if (twoDim != NULL) { - AnnotationCoordinate coord = *twoDim->getCoordinate(); + else if (oneCoordShape != NULL) { + AnnotationCoordinate coord = *oneCoordShape->getCoordinate(); float xyz[3]; coord.getXYZ(xyz); xyz[0] += dx; @@ -532,6 +641,23 @@ undoCommand->setModeCoordinateOne(coord, annotations); } + else if (multiCoordShape != NULL) { + std::vector> allCoords; + std::vector> constCoords; + multiCoordShape->getCopyOfAllCoordinates(allCoords); + for (const auto& ac : allCoords) { + float xyz[3]; + ac->getXYZ(xyz); + xyz[0] += dx; + xyz[1] += dy; + ac->setXYZ(xyz); + std::unique_ptr acCopy(new AnnotationCoordinate(*ac)); + constCoords.push_back(std::move(acCopy)); + } + + undoCommand->setModeCoordinateMulti(constCoords, + annotations); + } else { CaretAssert(0); } @@ -542,7 +668,8 @@ AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(getUserInputMode(), + undoCommand, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); @@ -563,6 +690,29 @@ } /** + * Initialize user drawing a new annotation. + * + * @param mouseEvent + * Mouse event information. + */ +void +UserInputModeAnnotations::initializeUserDrawingNewAnnotation(const MouseEvent& mouseEvent) +{ + if (m_newAnnotationCreatingWithMouseDrag != NULL) { + m_newAnnotationCreatingWithMouseDrag.grabNew(NULL); + } + + /* + * Note ALWAYS use WINDOW space for the drag anntotion. + * Otherwise it will not get displayed if surface/stereotaxic + */ + m_newAnnotationCreatingWithMouseDrag.grabNew(new NewMouseDragCreateAnnotation(m_modeNewAnnotationFileSpaceAndType->m_annotationFile, + AnnotationCoordinateSpaceEnum::WINDOW, + m_modeNewAnnotationFileSpaceAndType->m_annotationType, + mouseEvent)); +} + +/** * Process a mouse left drag with no keys down event. * * @param mouseEvent @@ -574,24 +724,19 @@ AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: { - if (m_newAnnotationCreatingWithMouseDrag != NULL) { - m_newAnnotationCreatingWithMouseDrag.grabNew(NULL); - } - - /* - * Note ALWAYS use WINDOW space for the drag anntotion. - * Otherwise it will not get displayed if surface/stereotaxic - */ - m_newAnnotationCreatingWithMouseDrag.grabNew(new NewMouseDragCreateAnnotation(m_modeNewAnnotationFileSpaceAndType->m_annotationFile, - AnnotationCoordinateSpaceEnum::WINDOW, - m_modeNewAnnotationFileSpaceAndType->m_annotationType, - mouseEvent)); + initializeUserDrawingNewAnnotation(mouseEvent); m_mode = MODE_NEW_WITH_DRAG; return; } break; + case MODE_NEW_WITH_CLICK_SERIES: + return; + break; + case MODE_NEW_WITH_CLICK_SERIES_START: + return; + break; case MODE_NEW_WITH_DRAG: userDrawingAnnotationFromMouseDrag(mouseEvent); return; @@ -795,6 +940,7 @@ mouseViewportY, dx, dy, + m_annotationUnderMousePolyLineCoordinateIndex, mouseEvent.isFirstDragging()); if (coordInfo.m_surfaceSpaceInfo.m_validFlag) { annSpatialMod.setSurfaceCoordinateAtMouseXY(coordInfo.m_surfaceSpaceInfo.m_structure, @@ -855,7 +1001,8 @@ } AString errorMessage; - if ( ! annotationManager->applyCommand(command, + if ( ! annotationManager->applyCommand(getUserInputMode(), + command, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); @@ -926,9 +1073,22 @@ UserInputModeAnnotations::mouseLeftClick(const MouseEvent& mouseEvent) { switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: processModeNewMouseLeftClick(mouseEvent); break; + case MODE_NEW_WITH_CLICK_SERIES: + userDrawingAnnotationFromMouseDrag(mouseEvent); + break; + case MODE_NEW_WITH_CLICK_SERIES_START: + { + initializeUserDrawingNewAnnotation(mouseEvent); + m_mode = MODE_NEW_WITH_CLICK_SERIES; + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + annotationManager->setAnnotationBeingDrawnInWindow(m_browserWindowIndex, + m_newAnnotationCreatingWithMouseDrag->getAnnotation()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } + break; case MODE_NEW_WITH_DRAG: break; case MODE_PASTE: @@ -958,7 +1118,20 @@ UserInputModeAnnotations::mouseLeftClickWithShift(const MouseEvent& mouseEvent) { switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: + break; + case MODE_NEW_WITH_CLICK_SERIES: + /* + * Insert vertex and finish annotation + */ + userDrawingAnnotationFromMouseDrag(mouseEvent); + createNewAnnotationFromMouseDrag(mouseEvent); + m_mode = MODE_SELECT; + break; + case MODE_NEW_WITH_CLICK_SERIES_START: + WuQMessageBox::errorOk(m_annotationToolsWidget, + "Annotation has not been started. " + "Click mouse WITHOUT SHIFT key down to draw annotation or press ESC key to exit drawing."); break; case MODE_NEW_WITH_DRAG: break; @@ -968,8 +1141,11 @@ break; case MODE_SELECT: if (m_allowMultipleSelectionModeFlag) { + const bool shiftKeyDown(true); + const bool singleSelectionModeFlag(false); processMouseSelectAnnotation(mouseEvent, - true); + shiftKeyDown, + singleSelectionModeFlag); } break; case MODE_SET_COORDINATE_ONE: @@ -989,7 +1165,11 @@ UserInputModeAnnotations::mouseLeftPress(const MouseEvent& mouseEvent) { switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: + break; + case MODE_NEW_WITH_CLICK_SERIES: + break; + case MODE_NEW_WITH_CLICK_SERIES_START: break; case MODE_NEW_WITH_DRAG: break; @@ -998,8 +1178,16 @@ case MODE_PASTE_SPECIAL: break; case MODE_SELECT: + { + /* + * Single click selects clicked annotation and deselects any that are selected + */ + const bool shiftKeyDown(false); + const bool singleSelectionModeFlag(true); processMouseSelectAnnotation(mouseEvent, - false); + shiftKeyDown, + singleSelectionModeFlag); + } break; case MODE_SET_COORDINATE_ONE: break; @@ -1021,7 +1209,6 @@ UserInputModeAnnotations::setAnnotationUnderMouse(const MouseEvent& mouseEvent, SelectionItemAnnotation* annotationIDIn) { - m_annotationUnderMouse = NULL; m_annotationUnderMouseSizeHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; @@ -1031,9 +1218,14 @@ annotationID = openGLWidget->performIdentificationAnnotations(mouseEvent.getX(), mouseEvent.getY()); } + if (annotationID->isValid()) { m_annotationUnderMouse = annotationID->getAnnotation(); m_annotationUnderMouseSizeHandleType = annotationID->getSizingHandle(); + m_annotationUnderMousePolyLineCoordinateIndex = annotationID->getPolyLineCoordinateIndex(); + } + else { + m_annotationUnderMouse = NULL; } openGLWidget->updateCursor(); @@ -1061,7 +1253,8 @@ UserInputModeAnnotations::userDrawingAnnotationFromMouseDrag(const MouseEvent& mouseEvent) { if (m_newAnnotationCreatingWithMouseDrag != NULL) { - m_newAnnotationCreatingWithMouseDrag->update(mouseEvent.getX(), + m_newAnnotationCreatingWithMouseDrag->update(mouseEvent, + mouseEvent.getX(), mouseEvent.getY()); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); @@ -1083,7 +1276,30 @@ { if (m_newAnnotationCreatingWithMouseDrag != NULL) { + switch (m_mode) { + case MODE_NEW_WITH_CLICK_SERIES: + break; + case MODE_NEW_WITH_CLICK_SERIES_START: + break; + case MODE_NEW_WITH_DRAG: + break; + case MODE_NEW_WITH_DRAG_START: + break; + case MODE_PASTE: + break; + case MODE_PASTE_SPECIAL: + break; + case MODE_SELECT: + break; + case MODE_SET_COORDINATE_ONE: + break; + case MODE_SET_COORDINATE_TWO: + break; + } + std::vector coords = m_newAnnotationCreatingWithMouseDrag->getDrawingCoordinates(); + Annotation* ann = AnnotationCreateDialog::newAnnotationFromSpaceTypeAndBounds(mouseEvent, + coords, m_modeNewAnnotationFileSpaceAndType->m_annotationSpace, m_modeNewAnnotationFileSpaceAndType->m_annotationType, m_modeNewAnnotationFileSpaceAndType->m_annotationFile); @@ -1093,15 +1309,22 @@ setMode(MODE_SELECT); - m_newAnnotationCreatingWithMouseDrag.grabNew(NULL); - - AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); - annotationManager->setAnnotationBeingDrawnInWindow(m_browserWindowIndex, - NULL); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } +/** + * Reset the annotation that is being created + */ +void +UserInputModeAnnotations::resetAnnotationBeingCreated() +{ + m_newAnnotationCreatingWithMouseDrag.grabNew(NULL); + + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + annotationManager->setAnnotationBeingDrawnInWindow(m_browserWindowIndex, + NULL); +} /** * Process a mouse left release event. @@ -1113,7 +1336,11 @@ UserInputModeAnnotations::mouseLeftRelease(const MouseEvent& mouseEvent) { switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: + break; + case MODE_NEW_WITH_CLICK_SERIES: + break; + case MODE_NEW_WITH_CLICK_SERIES_START: break; case MODE_NEW_WITH_DRAG: createNewAnnotationFromMouseDrag(mouseEvent); @@ -1200,6 +1427,69 @@ } /** + * Process a gesture event (pinch zoom; or rotate) + * + * @param gestureEvent + * Gesture event information. + */ +void +UserInputModeAnnotations::gestureEvent(const GestureEvent& gestureEvent) +{ + BrainOpenGLViewportContent* viewportContent = gestureEvent.getViewportContent(); + if (viewportContent == NULL) { + return; + } + + BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); + if (browserTabContent == NULL) { + return; + } + + switch (gestureEvent.getType()) { + case GestureEvent::Type::PINCH: + break; + case GestureEvent::Type::ROTATE: + { + float deltaRotateAngle = gestureEvent.getValue(); + + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); + + float rotationAngle(0.0); + std::vector twoDimAnns; + for (auto a : selectedAnnotations) { + AnnotationOneCoordinateShape* a2d = a->castToOneCoordinateShape(); + if (a2d != NULL) { + if (a2d->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { + if (twoDimAnns.empty()) { + rotationAngle = a2d->getRotationAngle() + deltaRotateAngle; + } + twoDimAnns.push_back(a2d); + } + } + } + + if ( ! twoDimAnns.empty()) { + AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); + std::vector modAnns(twoDimAnns.begin(), + twoDimAnns.end()); + command->setModeRotationAngle(rotationAngle, + modAnns); + AString errorMessage; + if ( ! annotationManager->applyCommand(getUserInputMode(), + command, + errorMessage)) { + WuQMessageBox::errorOk(m_annotationToolsWidget, + errorMessage); + } + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); + } + } + } +} + +/** * Process a mouse left click to set a coordinate. * * @param mouseEvent @@ -1220,13 +1510,17 @@ mouseEvent.getY(), coordInfo); - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(selectedAnnotation); - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(selectedAnnotation); + AnnotationTwoCoordinateShape* twoCoordShape = selectedAnnotation->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* oneCoordShape = selectedAnnotation->castToOneCoordinateShape(); AnnotationCoordinate* coordinate = NULL; AnnotationCoordinate* otherCoordinate = NULL; switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: + break; + case MODE_NEW_WITH_CLICK_SERIES: + break; + case MODE_NEW_WITH_CLICK_SERIES_START: break; case MODE_NEW_WITH_DRAG: break; @@ -1237,18 +1531,18 @@ case MODE_SELECT: break; case MODE_SET_COORDINATE_ONE: - if (oneDimAnn != NULL) { - coordinate = oneDimAnn->getStartCoordinate(); - otherCoordinate = oneDimAnn->getEndCoordinate(); + if (twoCoordShape != NULL) { + coordinate = twoCoordShape->getStartCoordinate(); + otherCoordinate = twoCoordShape->getEndCoordinate(); } - else if (twoDimAnn != NULL) { - coordinate = twoDimAnn->getCoordinate(); + else if (oneCoordShape != NULL) { + coordinate = oneCoordShape->getCoordinate(); } break; case MODE_SET_COORDINATE_TWO: - if (oneDimAnn != NULL) { - coordinate = oneDimAnn->getEndCoordinate(); - otherCoordinate = oneDimAnn->getStartCoordinate(); + if (twoCoordShape != NULL) { + coordinate = twoCoordShape->getEndCoordinate(); + otherCoordinate = twoCoordShape->getStartCoordinate(); } break; } @@ -1290,7 +1584,12 @@ { resetAnnotationUnderMouse(); + std::vector coords; + coords.emplace_back(mouseEvent.getPressedX(), + mouseEvent.getPressedY(), + 0.0); Annotation* ann = AnnotationCreateDialog::newAnnotationFromSpaceAndType(mouseEvent, + coords, m_modeNewAnnotationFileSpaceAndType->m_annotationSpace, m_modeNewAnnotationFileSpaceAndType->m_annotationType, m_modeNewAnnotationFileSpaceAndType->m_annotationFile); @@ -1344,10 +1643,13 @@ * Mouse event information. * @param shiftKeyDownFlag * True if shift key is down. + * @param singleSelectionModeFlag + * If true, deselect any other annotations so that only the annotation under mouse is selected */ void UserInputModeAnnotations::processMouseSelectAnnotation(const MouseEvent& mouseEvent, - const bool shiftKeyDownFlag) + const bool shiftKeyDownFlag, + const bool singleSelectionModeFlag) { BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); const int mouseX = mouseEvent.getX(); @@ -1368,6 +1670,13 @@ mouseY); Annotation* selectedAnnotation = annotationID->getAnnotation(); + /* + * If only one annotation may be selected, deselect all other annotations + */ + if (singleSelectionModeFlag) { + GuiManager::get()->getBrain()->getAnnotationManager()->deselectAllAnnotationsForEditing(m_browserWindowIndex); + } + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); AnnotationManager::SelectionMode selectionMode = AnnotationManager::SELECTION_MODE_SINGLE; if (m_allowMultipleSelectionModeFlag) { @@ -1384,6 +1693,7 @@ if (selectedAnnotation != NULL) { m_annotationBeingDragged = selectedAnnotation; m_annotationBeingDraggedHandleType = annotationID->getSizingHandle(); + m_annotationUnderMousePolyLineCoordinateIndex = annotationID->getPolyLineCoordinateIndex(); } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); @@ -1416,8 +1726,11 @@ * There might not be an annotation under the * mouse and that is okay. */ + const bool shiftKeyDown(false); + const bool singleSelectionModeFlag(false); processMouseSelectAnnotation(mouseEvent, - false); + shiftKeyDown, + singleSelectionModeFlag); UserInputModeAnnotationsContextMenu contextMenu(this, mouseEvent, @@ -1446,7 +1759,11 @@ bool editMenuValid = false; switch (m_mode) { - case MODE_NEW_WITH_CLICK: + case MODE_NEW_WITH_DRAG_START: + break; + case MODE_NEW_WITH_CLICK_SERIES: + break; + case MODE_NEW_WITH_CLICK_SERIES_START: break; case MODE_NEW_WITH_DRAG: break; @@ -1477,8 +1794,8 @@ { std::vector > selectedAnnotations; AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); - annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex, - selectedAnnotations); + annotationManager->getAnnotationsAndFilesSelectedForEditing(m_browserWindowIndex, + selectedAnnotations); if (selectedAnnotations.size() == 1) { CaretAssertVectorIndex(selectedAnnotations, 0); @@ -1493,7 +1810,8 @@ AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeCutAnnotations(annotationVector); AString errorMessage; - if ( ! annotationManager->applyCommand(undoCommand, + if ( ! annotationManager->applyCommand(getUserInputMode(), + undoCommand, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); @@ -1525,7 +1843,7 @@ { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); std::vector > selectedAnnotations; - annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex, + annotationManager->getAnnotationsAndFilesSelectedForEditing(m_browserWindowIndex, selectedAnnotations); if (selectedAnnotations.size() == 1) { @@ -1541,6 +1859,9 @@ case BrainBrowserWindowEditMenuItemEnum::DELETER: deleteSelectedAnnotations(); break; + case BrainBrowserWindowEditMenuItemEnum::DESELECT_ALL: + processDeselectAllAnnotations(); + break; case BrainBrowserWindowEditMenuItemEnum::PASTE: { const MouseEvent* mouseEvent = getMousePosition(); @@ -1566,7 +1887,7 @@ case BrainBrowserWindowEditMenuItemEnum::REDO: { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); + CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(getUserInputMode()); AString errorMessage; if ( ! undoStack->redo(errorMessage)) { @@ -1584,7 +1905,7 @@ case BrainBrowserWindowEditMenuItemEnum::UNDO: { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); + CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(getUserInputMode()); AString errorMessage; if ( ! undoStack->undo(errorMessage)) { @@ -1600,21 +1921,101 @@ } /** + * Process the deselection of all annotations. + */ +void +UserInputModeAnnotations::processDeselectAllAnnotations() +{ + std::vector annotationsSelected; + + switch (getUserInputMode()) { + case UserInputModeEnum::Enum::ANNOTATIONS: + { + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + annMan->deselectAllAnnotationsForEditing(m_browserWindowIndex); + } + break; + case UserInputModeEnum::Enum::BORDERS: + case UserInputModeEnum::Enum::FOCI: + case UserInputModeEnum::Enum::IMAGE: + case UserInputModeEnum::Enum::INVALID: + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + { + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + annMan->deselectAllAnnotationsForEditing(m_browserWindowIndex); + } + break; + case UserInputModeEnum::Enum::VIEW: + break; + case UserInputModeEnum::Enum::VOLUME_EDIT: + break; + } + + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** * Process the selection of all annotations. */ void UserInputModeAnnotations::processSelectAllAnnotations() { - AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - annMan->deselectAllAnnotationsForEditing(m_browserWindowIndex); - - EventAnnotationGetDrawnInWindow getDrawnEvent(m_browserWindowIndex); - EventManager::get()->sendEvent(getDrawnEvent.getPointer()); std::vector annotationsSelected; - getDrawnEvent.getAnnotations(annotationsSelected); - annMan->setAnnotationsForEditing(m_browserWindowIndex, - annotationsSelected); + switch (getUserInputMode()) { + case UserInputModeEnum::Enum::ANNOTATIONS: + { + EventAnnotationGetDrawnInWindow getDrawnEvent(m_browserWindowIndex); + EventManager::get()->sendEvent(getDrawnEvent.getPointer()); + getDrawnEvent.getAnnotations(annotationsSelected); + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + annMan->deselectAllAnnotationsForEditing(m_browserWindowIndex); + + annMan->setAnnotationsForEditing(m_browserWindowIndex, + annotationsSelected); + } + break; + case UserInputModeEnum::Enum::BORDERS: + case UserInputModeEnum::Enum::FOCI: + case UserInputModeEnum::Enum::IMAGE: + case UserInputModeEnum::Enum::INVALID: + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + { + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + annMan->deselectAllAnnotationsForEditing(m_browserWindowIndex); + + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); + CaretAssert(bbw); + std::vector allTabContent; + bbw->getAllTabContent(allTabContent); + + std::vector annotations; + for (auto btc : allTabContent) { + AnnotationBrowserTab* bta = btc->getManualLayoutBrowserTabAnnotation(); + if (bta->isBrowserTabDisplayed()) { + annotations.push_back(bta); + } + } + + if ( ! annotations.empty()) { + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + CaretAssert(annMan); + annMan->setAnnotationsForEditing(m_browserWindowIndex, + annotations); + } + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); + } + break; + case UserInputModeEnum::Enum::VIEW: + break; + case UserInputModeEnum::Enum::VOLUME_EDIT: + break; + } + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); @@ -1671,8 +2072,11 @@ if ( ! ann->testProperty(Annotation::Property::COPY_CUT_PASTE)) { allAllowCopyCutPasteFlag = false; } - if ( ! ann->testProperty(Annotation::Property::DELETION)) { - allAllowDeleteFlag = false; + + if (ann->getType() != AnnotationTypeEnum::BROWSER_TAB) { + if ( ! ann->testProperty(Annotation::Property::DELETION)) { + allAllowDeleteFlag = false; + } } } @@ -1694,6 +2098,9 @@ if (allAllowSelectFlag) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::SELECT_ALL); } + if (anySelectedFlag) { + enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::DESELECT_ALL); + } if (annotationManager->isAnnotationOnClipboardValid()) { const Annotation* clipBoardAnn = annotationManager->getAnnotationOnClipboard(); @@ -1706,7 +2113,7 @@ pasteSpecialTextOut); } - CaretUndoStack* undoStack = annotationManager->getCommandRedoUndoStack(); + CaretUndoStack* undoStack = annotationManager->getCommandRedoUndoStack(getUserInputMode()); if (undoStack->canRedo()) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::REDO); @@ -1815,28 +2222,48 @@ m_annotation->setCoordinateSpace(annotationSpace); CaretAssert(m_annotation); - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(m_annotation); - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(m_annotation); - - if (oneDimShape != NULL) { - setCoordinate(oneDimShape->getStartCoordinate(), + AnnotationMultiCoordinateShape* multiCoordShape = m_annotation->castToMultiCoordinateShape(); + AnnotationOneCoordinateShape* oneCoordShape = m_annotation->castToOneCoordinateShape(); + AnnotationTwoCoordinateShape* twoCoordShape = m_annotation->castToTwoCoordinateShape(); + + Vector3D mouseCoord3D(mouseEvent.getPressedX(), + mouseEvent.getPressedY(), + 0.0); + if (twoCoordShape != NULL) { + setCoordinate(twoCoordShape->getStartCoordinate(), m_mousePressWindowX, m_mousePressWindowY); - setCoordinate(oneDimShape->getEndCoordinate(), + m_drawingCoordinates.push_back(mouseCoord3D); + + setCoordinate(twoCoordShape->getEndCoordinate(), m_mousePressWindowX, m_mousePressWindowY); + m_drawingCoordinates.push_back(mouseCoord3D); + + CaretAssert(m_drawingCoordinates.size() == 2); } - else if (twoDimShape != NULL) { - setCoordinate(twoDimShape->getCoordinate(), + else if (oneCoordShape != NULL) { + setCoordinate(oneCoordShape->getCoordinate(), m_mousePressWindowX, m_mousePressWindowY); - twoDimShape->setWidth(1.0); - twoDimShape->setHeight(1.0); + oneCoordShape->setWidth(1.0); + oneCoordShape->setHeight(1.0); + + m_drawingCoordinates.push_back(mouseCoord3D); + CaretAssert(m_drawingCoordinates.size() == 1); + } + else if (multiCoordShape != NULL) { + AnnotationCoordinate* ac = new AnnotationCoordinate(AnnotationAttributesDefaultTypeEnum::USER); + setCoordinate(ac, m_mousePressWindowX, m_mousePressWindowY); + multiCoordShape->addCoordinate(ac); + + m_drawingCoordinates.push_back(mouseCoord3D); } else { CaretAssert(0); } + if ((m_annotation->getLineColor() == CaretColorEnum::NONE) && (m_annotation->getBackgroundColor() == CaretColorEnum::NONE)) { m_annotation->setLineColor(CaretColorEnum::RED); @@ -1854,27 +2281,37 @@ /** * Update with current mouse location. * + * @param mouseEvent + * Mouse event. * @param mouseWindowX * Mouse window X-coordinate * @param mouseWindowY * Mouse window Y-coordinate */ void -UserInputModeAnnotations::NewMouseDragCreateAnnotation::update(const int32_t mouseWindowXIn, +UserInputModeAnnotations::NewMouseDragCreateAnnotation::update(const MouseEvent& mouseEvent, + const int32_t mouseWindowXIn, const int32_t mouseWindowYIn) { int32_t mouseWindowX = mouseWindowXIn - m_windowOriginX; int32_t mouseWindowY = mouseWindowYIn - m_windowOriginY; - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(m_annotation); - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(m_annotation); - - if (oneDimShape != NULL) { - setCoordinate(oneDimShape->getEndCoordinate(), + AnnotationTwoCoordinateShape* twoCoordShape = m_annotation->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* oneCoordShape = m_annotation->castToOneCoordinateShape(); + AnnotationMultiCoordinateShape* multiCoordShape = m_annotation->castToMultiCoordinateShape(); + + Vector3D mouseCoord3D(mouseEvent.getX(), + mouseEvent.getY(), + 0.0); + + if (twoCoordShape != NULL) { + setCoordinate(twoCoordShape->getEndCoordinate(), mouseWindowX, mouseWindowY); + CaretAssertVectorIndex(m_drawingCoordinates, 1); + m_drawingCoordinates[1] = mouseCoord3D; } - else if (twoDimShape != NULL) { + else if (oneCoordShape != NULL) { const float minX = std::min(m_mousePressWindowX, mouseWindowX); const float maxX = std::max(m_mousePressWindowX, @@ -1898,10 +2335,21 @@ ? (height / static_cast(m_windowHeight)) : 0.01); - AnnotationCoordinate* coord = twoDimShape->getCoordinate(); + AnnotationCoordinate* coord = oneCoordShape->getCoordinate(); setCoordinate(coord, x, y); - twoDimShape->setWidth(relativeWidth); - twoDimShape->setHeight(relativeHeight); + oneCoordShape->setWidth(relativeWidth); + oneCoordShape->setHeight(relativeHeight); + + CaretAssertVectorIndex(m_drawingCoordinates, 0); + m_drawingCoordinates[0][0] = ((mouseEvent.getPressedX() + mouseEvent.getX()) / 2.0); + m_drawingCoordinates[0][1] = ((mouseEvent.getPressedY() + mouseEvent.getY()) / 2.0); + } + else if (multiCoordShape != NULL) { + AnnotationCoordinate* ac = new AnnotationCoordinate(AnnotationAttributesDefaultTypeEnum::USER); + setCoordinate(ac, mouseWindowX, mouseWindowY); + multiCoordShape->addCoordinate(ac); + + m_drawingCoordinates.push_back(mouseCoord3D); } else { CaretAssert(0); @@ -1944,3 +2392,13 @@ return m_annotation; } +/** + * @return The drawing coordinates + */ +const std::vector& +UserInputModeAnnotations::NewMouseDragCreateAnnotation::getDrawingCoordinates() const +{ + return m_drawingCoordinates; +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotations.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotations.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotations.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotations.h 2021-02-16 19:46:47.000000000 +0000 @@ -29,6 +29,7 @@ #include "EventListenerInterface.h" #include "StructureEnum.h" #include "UserInputModeView.h" +#include "Vector3D.h" namespace caret { @@ -36,8 +37,8 @@ class AnnotationCoordinate; class AnnotationCoordinateInformation; class AnnotationFile; - class AnnotationOneDimensionalShape; - class AnnotationTwoDimensionalShape; + class AnnotationTwoCoordinateShape; + class AnnotationOneCoordinateShape; class KeyEvent; class MouseEvent; class SelectionItemAnnotation; @@ -50,10 +51,14 @@ * Annotation mode */ enum Mode { - /** Mouse creates an annotation by clicking */ - MODE_NEW_WITH_CLICK, - /** Mouse creates an annotation by draggin from one point to another */ + /** Mouse updates new annotation as mouse is clicked */ + MODE_NEW_WITH_CLICK_SERIES, + /** Mouse starts a new annotaiton with a series of clicks (used with polyline) */ + MODE_NEW_WITH_CLICK_SERIES_START, + /** Mouse updates new annotation as mouse is dragged */ MODE_NEW_WITH_DRAG, + /** Mouse starts new annotation drawn by dragging */ + MODE_NEW_WITH_DRAG_START, /** User selected Paste from Edit Menu, user may need to click mouse to paste the annotation */ MODE_PASTE, /** User selected Paste Special from Edit Menu, user may need to click mouse to paste the annotation */ @@ -106,6 +111,8 @@ virtual void mouseMoveWithShift(const MouseEvent& mouseEvent); + virtual void gestureEvent(const GestureEvent& gestureEvent); + virtual void showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget); @@ -124,6 +131,18 @@ // ADD_NEW_METHODS_HERE + protected: + UserInputModeAnnotations(const UserInputModeEnum::Enum userInputMode, + const int32_t windowIndex); + + virtual void processMouseSelectAnnotation(const MouseEvent& mouseEvent, + const bool shiftKeyDownFlag, + const bool singleSelectionModeFlag); + + void processDeselectAllAnnotations(); + + void processSelectAllAnnotations(); + private: class NewMouseDragCreateAnnotation { public: @@ -134,7 +153,8 @@ ~NewMouseDragCreateAnnotation(); - void update(const int32_t mouseWindowX, + void update(const MouseEvent& mouseEvent, + const int32_t mouseWindowX, const int32_t mouseWindowY); void setCoordinate(AnnotationCoordinate* coordinate, @@ -143,6 +163,8 @@ const Annotation* getAnnotation() const; + const std::vector& getDrawingCoordinates() const; + private: AnnotationFile* m_annotationFile; @@ -159,6 +181,8 @@ float m_windowWidth; float m_windowHeight; + + std::vector m_drawingCoordinates; }; class NewAnnotationFileSpaceAndType { @@ -183,9 +207,6 @@ void processModeNewMouseLeftClick(const MouseEvent& mouseEvent); - void processMouseSelectAnnotation(const MouseEvent& mouseEvent, - const bool shiftKeyDownFlag); - void processModeSetCoordinate(const MouseEvent& mouseEvent); void setAnnotationUnderMouse(const MouseEvent& mouseEvent, @@ -195,15 +216,15 @@ void userDrawingAnnotationFromMouseDrag(const MouseEvent& mouseEvent); + void initializeUserDrawingNewAnnotation(const MouseEvent& mouseEvent); + void selectAnnotation(Annotation* annotation); Annotation* getSingleSelectedAnnotation(); void cutAnnotation(); - void deleteSelectedAnnotations(); - - void processSelectAllAnnotations(); + virtual void deleteSelectedAnnotations(); void resetAnnotationUnderMouse(); @@ -213,9 +234,8 @@ void pasteAnnotationFromAnnotationClipboardAndChangeSpace(const MouseEvent& mouseEvent); -// bool pasteOneDimensionalShape(AnnotationOneDimensionalShape* oneDimShape, -// AnnotationCoordinateInformation& coordInfo); - + void resetAnnotationBeingCreated(); + UserInputModeAnnotationsWidget* m_annotationToolsWidget; Mode m_mode; @@ -226,6 +246,8 @@ AnnotationSizingHandleTypeEnum::Enum m_annotationUnderMouseSizeHandleType; + int32_t m_annotationUnderMousePolyLineCoordinateIndex; + Annotation* m_annotationBeingDragged; AnnotationSizingHandleTypeEnum::Enum m_annotationBeingDraggedHandleType; @@ -236,7 +258,6 @@ bool m_allowMultipleSelectionModeFlag; - //static const AString s_pasteSpecialMenuItemText; // ADD_NEW_MEMBERS_HERE /* @@ -244,6 +265,8 @@ */ friend class UserInputModeAnnotationsContextMenu; friend class UserInputModeAnnotationsWidget; + friend class UserInputModeTileTabsManualLayout; + friend class UserInputModeTileTabsManualLayoutContextMenu; }; #ifdef __USER_INPUT_MODE_ANNOTATIONS_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotationsWidget.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotationsWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotationsWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotationsWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -30,8 +30,12 @@ #include #include +#include "AnnotationBackgroundTypeWidget.h" +#include "AnnotationBoundsWidget.h" +#include "AnnotationBrowserTab.h" +#include "AnnotationCoordinateCenterXYWidget.h" #include "AnnotationCoordinateSpaceWidget.h" -#include "AnnotationCoordinateWidget.h" +#include "AnnotationCoordinatesWidget.h" #include "AnnotationColorWidget.h" #include "AnnotationDeleteWidget.h" #include "AnnotationFontWidget.h" @@ -40,7 +44,8 @@ #include "AnnotationLine.h" #include "AnnotationLineArrowTipsWidget.h" #include "AnnotationManager.h" -#include "AnnotationOneDimensionalShape.h" +#include "AnnotationNameWidget.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationRedoUndoWidget.h" #include "AnnotationRotationWidget.h" #include "AnnotationText.h" @@ -82,101 +87,23 @@ { CaretAssert(inputModeAnnotations); - m_textEditorWidget = new AnnotationTextEditorWidget(m_browserWindowIndex); - - m_lineArrowTipsWidget = new AnnotationLineArrowTipsWidget(m_browserWindowIndex); - - m_fontWidget = new AnnotationFontWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, - m_browserWindowIndex); - - m_colorWidget = new AnnotationColorWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, - m_browserWindowIndex); - - m_textAlignmentWidget = new AnnotationTextAlignmentWidget(m_browserWindowIndex); - - m_textOrientationWidget = new AnnotationTextOrientationWidget(m_browserWindowIndex); - - m_coordinateSpaceWidget = new AnnotationCoordinateSpaceWidget(m_browserWindowIndex); - - m_coordinateOneWidget = new AnnotationCoordinateWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, - AnnotationCoordinateWidget::COORDINATE_ONE, - m_browserWindowIndex); - - m_coordinateTwoWidget = new AnnotationCoordinateWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, - AnnotationCoordinateWidget::COORDINATE_TWO, - m_browserWindowIndex); - - m_widthHeightWidget = new AnnotationWidthHeightWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, - m_browserWindowIndex); - - m_rotationWidget = new AnnotationRotationWidget(m_browserWindowIndex); - - m_formatWidget = new AnnotationFormatWidget(m_browserWindowIndex); - - m_insertDeleteWidget = new AnnotationInsertNewWidget(m_browserWindowIndex); - - m_deleteWidget = new AnnotationDeleteWidget(m_browserWindowIndex); - - m_redoUndoWidget = new AnnotationRedoUndoWidget(m_browserWindowIndex); - - /* - * Connect signals for setting a coordinate with the mouse. - */ - QObject::connect(m_coordinateOneWidget, SIGNAL(signalSelectCoordinateWithMouse()), - this, SLOT(selectCoordinateOneWithMouse())); - QObject::connect(m_coordinateTwoWidget, SIGNAL(signalSelectCoordinateWithMouse()), - this, SLOT(selectCoordinateTwoWithMouse())); - - /* - * Layout top row of widgets - */ - QWidget* topRowWidget = new QWidget(); - QHBoxLayout* topRowLayout = new QHBoxLayout(topRowWidget); - WuQtUtilities::setLayoutSpacingAndMargins(topRowLayout, 2, 2); - topRowLayout->addWidget(m_colorWidget, 0, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_lineArrowTipsWidget, 0, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_textEditorWidget, 100, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_fontWidget, 0, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_textAlignmentWidget, 0, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_textOrientationWidget, 0, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_insertDeleteWidget, 0, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_deleteWidget, 0, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_formatWidget, 0, Qt::AlignTop); - topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - topRowLayout->addWidget(m_redoUndoWidget, 0, Qt::AlignTop); - topRowLayout->addStretch(); - - topRowWidget->setFixedHeight(topRowWidget->sizeHint().height()); - - /* - * Layout bottom row of widgets - */ - QHBoxLayout* bottomRowLayout = new QHBoxLayout(); - WuQtUtilities::setLayoutSpacingAndMargins(bottomRowLayout, 2, 2); - bottomRowLayout->addWidget(m_coordinateSpaceWidget); - bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - bottomRowLayout->addWidget(m_coordinateOneWidget); - bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - bottomRowLayout->addWidget(m_coordinateTwoWidget); - bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - bottomRowLayout->addWidget(m_widthHeightWidget); - bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); - bottomRowLayout->addWidget(m_rotationWidget); - bottomRowLayout->addStretch(); - - QVBoxLayout* layout = new QVBoxLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 2); - layout->addWidget(topRowWidget); - layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); - layout->addLayout(bottomRowLayout); + switch (inputModeAnnotations->getUserInputMode()) { + case UserInputModeEnum::Enum::ANNOTATIONS: + createAnnotationWidget(); + break; + case UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING: + createTileTabsEditingWidget(); + break; + case UserInputModeEnum::Enum::BORDERS: + case UserInputModeEnum::Enum::FOCI: + case UserInputModeEnum::Enum::IMAGE: + case UserInputModeEnum::Enum::INVALID: + case UserInputModeEnum::Enum::VIEW: + case UserInputModeEnum::Enum::VOLUME_EDIT: + CaretAssert(0); + return; + break; + } EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BRAIN_RESET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE); @@ -241,6 +168,180 @@ } /** + * Create annotation mode widget + */ +void +UserInputModeAnnotationsWidget::createTileTabsEditingWidget() +{ + m_nameWidget = new AnnotationNameWidget(m_inputModeAnnotations->getUserInputMode(), + m_browserWindowIndex); + + m_boundsWidget = new AnnotationBoundsWidget(m_inputModeAnnotations->getUserInputMode(), + AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, + m_browserWindowIndex); + + m_coordinateCenterXYWidget = new AnnotationCoordinateCenterXYWidget(m_inputModeAnnotations->getUserInputMode(), + AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, + AnnotationCoordinateCenterXYWidget::COORDINATE_ONE, + m_browserWindowIndex); + + m_widthHeightWidget = new AnnotationWidthHeightWidget(m_inputModeAnnotations->getUserInputMode(), + AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, + m_browserWindowIndex, + Qt::Vertical); + + m_backgroundTypeWidget = new AnnotationBackgroundTypeWidget(m_inputModeAnnotations->getUserInputMode(), + AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, + m_browserWindowIndex); + + m_formatWidget = new AnnotationFormatWidget(m_inputModeAnnotations->getUserInputMode(), + m_browserWindowIndex); + + m_redoUndoWidget = new AnnotationRedoUndoWidget(Qt::Vertical, + m_inputModeAnnotations->getUserInputMode(), + m_browserWindowIndex); + + QVBoxLayout* centerSizeLayout = new QVBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(centerSizeLayout, 2, 0); + centerSizeLayout->addWidget(m_coordinateCenterXYWidget); + centerSizeLayout->addWidget(m_widthHeightWidget); + centerSizeLayout->addStretch(); + + QVBoxLayout* formatRedoLayout = new QVBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(formatRedoLayout, 2, 0); + formatRedoLayout->addWidget(m_formatWidget); + formatRedoLayout->addSpacing(12); + formatRedoLayout->addWidget(m_redoUndoWidget); + formatRedoLayout->addStretch(); + + QHBoxLayout* layout = new QHBoxLayout(this); + layout->setContentsMargins(2, 2, 2, 2); + layout->setSpacing(8); + layout->addWidget(m_nameWidget, 0, Qt::AlignTop); + layout->addWidget(WuQtUtilities::createVerticalLineWidget()); + layout->addWidget(m_boundsWidget, 0, Qt::AlignTop); + layout->addWidget(WuQtUtilities::createVerticalLineWidget()); + layout->addLayout(centerSizeLayout); + layout->addWidget(WuQtUtilities::createVerticalLineWidget()); + layout->addWidget(m_backgroundTypeWidget, 0, Qt::AlignTop); + layout->addWidget(WuQtUtilities::createVerticalLineWidget()); + layout->addLayout(formatRedoLayout); + layout->addStretch(); +} + +/* + * Create tile tabs mode widget + */ +void +UserInputModeAnnotationsWidget::createAnnotationWidget() +{ + m_textEditorWidget = new AnnotationTextEditorWidget(m_browserWindowIndex); + + m_lineArrowTipsWidget = new AnnotationLineArrowTipsWidget(m_browserWindowIndex); + + m_fontWidget = new AnnotationFontWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, + m_browserWindowIndex); + + m_colorWidget = new AnnotationColorWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, + m_browserWindowIndex); + + m_textAlignmentWidget = new AnnotationTextAlignmentWidget(m_browserWindowIndex); + + m_textOrientationWidget = new AnnotationTextOrientationWidget(m_browserWindowIndex); + + QLabel* coordinateSpaceLabel = new QLabel("Space"); + m_coordinateSpaceWidget = new AnnotationCoordinateSpaceWidget(m_browserWindowIndex); + + m_coordinatesWidget = new AnnotationCoordinatesWidget(m_inputModeAnnotations->getUserInputMode(), + AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, + m_browserWindowIndex); + + m_widthHeightWidget = new AnnotationWidthHeightWidget(m_inputModeAnnotations->getUserInputMode(), + AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, + m_browserWindowIndex, + Qt::Horizontal); + + m_rotationWidget = new AnnotationRotationWidget(m_inputModeAnnotations->getUserInputMode(), + m_browserWindowIndex); + + m_formatWidget = new AnnotationFormatWidget(m_inputModeAnnotations->getUserInputMode(), + m_browserWindowIndex); + + m_insertNewWidget = new AnnotationInsertNewWidget(m_browserWindowIndex); + + m_deleteWidget = new AnnotationDeleteWidget(m_browserWindowIndex); + + m_redoUndoWidget = new AnnotationRedoUndoWidget(Qt::Horizontal, + m_inputModeAnnotations->getUserInputMode(), + m_browserWindowIndex); + + /* + * Layout top row of widgets + */ + QWidget* topRowWidget = new QWidget(); + QHBoxLayout* topRowLayout = new QHBoxLayout(topRowWidget); + WuQtUtilities::setLayoutSpacingAndMargins(topRowLayout, 2, 0); + topRowLayout->addWidget(m_colorWidget, 0, Qt::AlignTop); + topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + topRowLayout->addWidget(m_lineArrowTipsWidget, 0, Qt::AlignTop); + topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + topRowLayout->addWidget(m_textEditorWidget, 100, Qt::AlignTop); + topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + topRowLayout->addWidget(m_fontWidget, 0, Qt::AlignTop); + topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + topRowLayout->addWidget(m_textAlignmentWidget, 0, Qt::AlignTop); + topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + topRowLayout->addWidget(m_textOrientationWidget, 0, Qt::AlignTop); + topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + topRowLayout->addWidget(m_insertNewWidget, 0, Qt::AlignTop); + topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); + topRowLayout->addStretch(); + + topRowWidget->setFixedHeight(topRowWidget->sizeHint().height()); + + /* + * Layout bottom row of widgets + */ + QGridLayout* bottomRowLayout = new QGridLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(bottomRowLayout, 2, 0); + int32_t column(0); + bottomRowLayout->addWidget(coordinateSpaceLabel, 0, column); + bottomRowLayout->addWidget(m_coordinateSpaceWidget, 1, column, Qt::AlignHCenter); + column++; + bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, column, 2, 1); + column++; + bottomRowLayout->addWidget(m_coordinatesWidget, 0, column, 2, 1); + column++; + bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, column, 2, 1); + column++; + bottomRowLayout->addWidget(m_widthHeightWidget, 0, column); + bottomRowLayout->addWidget(m_rotationWidget, 1, column); + column++; + bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, column, 2, 1); + column++; + bottomRowLayout->addWidget(m_deleteWidget, 0, column, 2, 1, Qt::AlignTop); + column++; + bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, column, 2, 1); + column++; + bottomRowLayout->addWidget(m_formatWidget, 0, column, 2, 1, Qt::AlignTop); + column++; + bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, column, 2, 1); + column++; + bottomRowLayout->addWidget(m_redoUndoWidget, 0, column, 2, 1, Qt::AlignTop); + column++; + bottomRowLayout->setColumnStretch(column, 100); + + QVBoxLayout* layout = new QVBoxLayout(this); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); + layout->addWidget(topRowWidget); + layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); + + setSizePolicy(QSizePolicy::Fixed, + sizePolicy().verticalPolicy()); + layout->addLayout(bottomRowLayout); +} + +/** * Select coordinate one with the mouse. */ void @@ -269,7 +370,11 @@ * Show the proper widget */ switch (m_inputModeAnnotations->getMode()) { - case UserInputModeAnnotations::MODE_NEW_WITH_CLICK: + case UserInputModeAnnotations::MODE_NEW_WITH_DRAG_START: + break; + case UserInputModeAnnotations::MODE_NEW_WITH_CLICK_SERIES: + break; + case UserInputModeAnnotations::MODE_NEW_WITH_CLICK_SERIES_START: break; case UserInputModeAnnotations::MODE_NEW_WITH_DRAG: break; @@ -290,11 +395,12 @@ std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); + std::vector browserTabAnnotations; std::vector lineAnnotations; std::vector textAnnotations; std::vector fontStyleAnnotations; - std::vector twoDimAnnotations; - std::vector oneDimAnnotations; + std::vector twoDimAnnotations; + std::vector oneDimAnnotations; for (std::vector::iterator iter = selectedAnnotations.begin(); iter != selectedAnnotations.end(); @@ -302,6 +408,13 @@ Annotation* ann = *iter; CaretAssert(ann); + if (ann->getType() == AnnotationTypeEnum::BROWSER_TAB) { + AnnotationBrowserTab* abt = dynamic_cast(ann); + if (abt != NULL) { + browserTabAnnotations.push_back(abt); + } + } + AnnotationText* annText = dynamic_cast(ann); if (annText != NULL) { textAnnotations.push_back(annText); @@ -312,12 +425,12 @@ fontStyleAnnotations.push_back(annFontStyle); } - AnnotationOneDimensionalShape* annOne = dynamic_cast(ann); + AnnotationTwoCoordinateShape* annOne = dynamic_cast(ann); if (annOne != NULL) { oneDimAnnotations.push_back(annOne); } - AnnotationTwoDimensionalShape* annTwo= dynamic_cast(ann); + AnnotationOneCoordinateShape* annTwo= dynamic_cast(ann); if (annTwo != NULL) { twoDimAnnotations.push_back(annTwo); } @@ -328,34 +441,32 @@ } } - m_coordinateSpaceWidget->updateContent(selectedAnnotations); - m_fontWidget->updateContent(fontStyleAnnotations); - m_textEditorWidget->updateContent(textAnnotations); - m_colorWidget->updateContent(selectedAnnotations); - m_lineArrowTipsWidget->updateContent(lineAnnotations); - m_textAlignmentWidget->updateContent(textAnnotations); - m_textOrientationWidget->updateContent(textAnnotations); - m_widthHeightWidget->updateContent(twoDimAnnotations); - m_rotationWidget->updateContent(selectedAnnotations); //twoDimAnnotations); - m_insertDeleteWidget->updateContent(); - m_deleteWidget->updateContent(); + /* + * Note: pointers are initialized to NULL in the header file + */ + if (m_nameWidget != NULL) m_nameWidget->updateContent(selectedAnnotations); + if (m_boundsWidget != NULL) m_boundsWidget->updateContent(browserTabAnnotations); + if (m_coordinateSpaceWidget != NULL) m_coordinateSpaceWidget->updateContent(selectedAnnotations); + if (m_fontWidget != NULL) m_fontWidget->updateContent(fontStyleAnnotations); + if (m_textEditorWidget != NULL) m_textEditorWidget->updateContent(textAnnotations); + if (m_colorWidget != NULL) m_colorWidget->updateContent(selectedAnnotations); + if (m_lineArrowTipsWidget != NULL) m_lineArrowTipsWidget->updateContent(lineAnnotations); + if (m_textAlignmentWidget != NULL) m_textAlignmentWidget->updateContent(textAnnotations); + if (m_textOrientationWidget != NULL) m_textOrientationWidget->updateContent(textAnnotations); + if (m_widthHeightWidget != NULL) m_widthHeightWidget->updateContent(twoDimAnnotations); + if (m_rotationWidget != NULL) m_rotationWidget->updateContent(selectedAnnotations); + if (m_insertNewWidget != NULL) m_insertNewWidget->updateContent(); + if (m_deleteWidget != NULL) m_deleteWidget->updateContent(); Annotation* coordEditAnnotation = NULL; - AnnotationOneDimensionalShape* coordEditOneDimAnnotation = NULL; if (selectedAnnotations.size() == 1) { coordEditAnnotation = selectedAnnotations[0]; - coordEditOneDimAnnotation = dynamic_cast(coordEditAnnotation); } - m_coordinateOneWidget->updateContent(coordEditAnnotation); - if (coordEditOneDimAnnotation != NULL) { - m_coordinateTwoWidget->updateContent(coordEditOneDimAnnotation); - m_coordinateTwoWidget->setVisible(true); - } - else { - m_coordinateTwoWidget->setVisible(false); - } - - m_redoUndoWidget->updateContent(); + if (m_coordinateCenterXYWidget != NULL) m_coordinateCenterXYWidget->updateContent(selectedAnnotations); + if (m_coordinatesWidget != NULL) m_coordinatesWidget->updateContent(coordEditAnnotation); + if (m_backgroundTypeWidget != NULL) m_backgroundTypeWidget->updateContent(browserTabAnnotations); + if (m_formatWidget != NULL) m_formatWidget->updateContent(selectedAnnotations); + if (m_redoUndoWidget != NULL) m_redoUndoWidget->updateContent(selectedAnnotations); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotationsWidget.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotationsWidget.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeAnnotationsWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeAnnotationsWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -32,14 +32,18 @@ namespace caret { class Annotation; + class AnnotationBackgroundTypeWidget; + class AnnotationBoundsWidget; class AnnotationColorWidget; + class AnnotationCoordinateCenterXYWidget; class AnnotationCoordinateSpaceWidget; - class AnnotationCoordinateWidget; + class AnnotationCoordinatesWidget; class AnnotationDeleteWidget; class AnnotationFontWidget; class AnnotationFormatWidget; class AnnotationInsertNewWidget; class AnnotationLineArrowTipsWidget; + class AnnotationNameWidget; class AnnotationRedoUndoWidget; class AnnotationRotationWidget; class AnnotationTextAlignmentWidget; @@ -75,43 +79,53 @@ UserInputModeAnnotationsWidget& operator=(const UserInputModeAnnotationsWidget&); + void createAnnotationWidget(); + + void createTileTabsEditingWidget(); + QWidget* createInsertMenuToolButton(); QWidget* createTextEditorWidget(); - const int32_t m_browserWindowIndex; + const int32_t m_browserWindowIndex = -1; + + UserInputModeAnnotations* m_inputModeAnnotations = NULL; + + AnnotationNameWidget* m_nameWidget = NULL; + + AnnotationBoundsWidget* m_boundsWidget = NULL; - UserInputModeAnnotations* m_inputModeAnnotations; + AnnotationCoordinateSpaceWidget* m_coordinateSpaceWidget = NULL; - AnnotationCoordinateSpaceWidget* m_coordinateSpaceWidget; + AnnotationCoordinateCenterXYWidget* m_coordinateCenterXYWidget = NULL; - AnnotationCoordinateWidget* m_coordinateOneWidget; + AnnotationCoordinatesWidget* m_coordinatesWidget = NULL; - AnnotationCoordinateWidget* m_coordinateTwoWidget; + AnnotationWidthHeightWidget* m_widthHeightWidget = NULL; - AnnotationWidthHeightWidget* m_widthHeightWidget; + AnnotationRotationWidget* m_rotationWidget = NULL; - AnnotationRotationWidget* m_rotationWidget; + AnnotationLineArrowTipsWidget* m_lineArrowTipsWidget = NULL; - AnnotationLineArrowTipsWidget* m_lineArrowTipsWidget; + AnnotationFontWidget* m_fontWidget = NULL; - AnnotationFontWidget* m_fontWidget; + AnnotationColorWidget* m_colorWidget = NULL; - AnnotationColorWidget* m_colorWidget; + AnnotationTextAlignmentWidget* m_textAlignmentWidget = NULL; - AnnotationTextAlignmentWidget* m_textAlignmentWidget; + AnnotationBackgroundTypeWidget* m_backgroundTypeWidget = NULL; - AnnotationFormatWidget* m_formatWidget; + AnnotationFormatWidget* m_formatWidget = NULL; - AnnotationTextEditorWidget* m_textEditorWidget; + AnnotationTextEditorWidget* m_textEditorWidget = NULL; - AnnotationTextOrientationWidget* m_textOrientationWidget; + AnnotationTextOrientationWidget* m_textOrientationWidget = NULL; - AnnotationInsertNewWidget* m_insertDeleteWidget; + AnnotationInsertNewWidget* m_insertNewWidget = NULL; - AnnotationDeleteWidget* m_deleteWidget; + AnnotationDeleteWidget* m_deleteWidget = NULL; - AnnotationRedoUndoWidget* m_redoUndoWidget; + AnnotationRedoUndoWidget* m_redoUndoWidget = NULL; // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeBorders.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeBorders.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeBorders.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeBorders.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -70,11 +70,11 @@ * @param windowIndex * Index of the browser window using this border processing. */ -UserInputModeBorders::UserInputModeBorders(Border* borderBeingDrawnByOpenGL, - const int32_t windowIndex) -: UserInputModeView(UserInputModeEnum::BORDERS) +UserInputModeBorders::UserInputModeBorders(const int32_t windowIndex) +: UserInputModeView(windowIndex, + UserInputModeEnum::Enum::BORDERS) { - this->borderBeingDrawnByOpenGL = borderBeingDrawnByOpenGL; + this->borderBeingDrawn = new Border(); this->windowIndex = windowIndex; this->mode = MODE_DRAW; this->drawOperation = DRAW_OPERATION_CREATE; @@ -88,6 +88,8 @@ */ UserInputModeBorders::~UserInputModeBorders() { + delete this->borderBeingDrawn; + this->borderBeingDrawn = NULL; } /** @@ -106,13 +108,6 @@ SurfaceProjectedItem* spi = new SurfaceProjectedItem(); AString txt; -// if (projectedItem.isStereotaxicXYZValid()) { -// spi->setStereotaxicXYZ(projectedItem.getStereotaxicXYZ()); -// -// txt += ("Projected Position: " -// + AString::fromNumbers(projectedItem.getStereotaxicXYZ(), 3, ",")); -// } - if (projectedItem.getBarycentricProjection()->isValid()) { SurfaceProjectionBarycentric* bp = projectedItem.getBarycentricProjection(); @@ -131,12 +126,12 @@ if (spi->isStereotaxicXYZValid() || spi->getBarycentricProjection()->isValid()) { - if (borderBeingDrawnByOpenGL->getNumberOfPoints() == 0 || borderBeingDrawnByOpenGL->getStructure() == projectedItem.getStructure()) + if (this->borderBeingDrawn->getNumberOfPoints() == 0 || this->borderBeingDrawn->getStructure() == projectedItem.getStructure()) { spi->setStructure(projectedItem.getStructure()); - this->borderBeingDrawnByOpenGL->addPoint(spi); + this->borderBeingDrawn->addPoint(spi); } else { - const AString prevName(StructureEnum::toGuiName(borderBeingDrawnByOpenGL->getStructure())); + const AString prevName(StructureEnum::toGuiName(this->borderBeingDrawn->getStructure())); const AString newName(StructureEnum::toGuiName(projectedItem.getStructure())); WuQMessageBox::errorOk(borderToolsWidget, ("The last point added is on " @@ -230,7 +225,7 @@ { if (this->mode != mode) { this->mode = mode; - this->borderBeingDrawnByOpenGL->clear(); + this->borderBeingDrawn->clear(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); } this->borderToolsWidget->updateWidget(); @@ -278,7 +273,7 @@ void UserInputModeBorders::drawOperationFinish() { - this->borderBeingDrawnByOpenGL->clear(); + this->borderBeingDrawn->clear(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().addBorder().getPointer()); @@ -290,7 +285,7 @@ void UserInputModeBorders::drawOperationUndo() { - this->borderBeingDrawnByOpenGL->removeLastPoint(); + this->borderBeingDrawn->removeLastPoint(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); } @@ -300,7 +295,7 @@ void UserInputModeBorders::drawOperationReset() { - this->borderBeingDrawnByOpenGL->clear(); + this->borderBeingDrawn->clear(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); } @@ -417,7 +412,6 @@ if (idBorder->isValid()) { Brain* brain = idBorder->getBrain(); Surface* surface = idBorder->getSurface(); - //BorderFile* borderFile = idBorder->getBorderFile(); Border* border = idBorder->getBorder(); this->borderToolsWidget->executeRoiInsideSelectedBorderOperation(brain, surface, @@ -522,4 +516,12 @@ openGLWidget); } +/** + * @return The border being drawn + */ +Border* +UserInputModeBorders::getBorderBeingDrawn() const +{ + return this->borderBeingDrawn; +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeBorders.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeBorders.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeBorders.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeBorders.h 2021-02-16 19:46:47.000000000 +0000 @@ -50,8 +50,7 @@ EDIT_OPERATION_PROPERTIES }; - UserInputModeBorders(Border* borderBeingDrawnByOpenGL, - const int32_t windowIndex); + UserInputModeBorders(const int32_t windowIndex); virtual ~UserInputModeBorders(); @@ -81,6 +80,8 @@ bool isHighlightBorderEndPoints() const; + Border* getBorderBeingDrawn() const; + private: /* * Some private methods are accessed by this friend class @@ -121,11 +122,7 @@ EditOperation editOperation; - /** - * Pointer to border drawn by OpenGL. Since owned - * by OpenGL, DO NOT delete this!!! - */ - Border* borderBeingDrawnByOpenGL; + Border* borderBeingDrawn; int32_t windowIndex; }; diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeBordersWidget.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeBordersWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeBordersWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeBordersWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,12 +23,14 @@ #include #include -#include #include -#include +#include #include +#include +#include #include #include +#include #include #include @@ -47,6 +49,7 @@ #include "BorderPropertiesEditorDialog.h" #include "Brain.h" #include "BrainBrowserWindow.h" +#include "BrainBrowserWindowToolBar.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" @@ -104,10 +107,7 @@ this->inputModeBorders = inputModeBorders; - QLabel* nameLabel = new QLabel("Borders "); - this->widgetMode = this->createModeWidget(); - resetLastEditedBorder(); this->widgetDrawOperation = this->createDrawOperationWidget(); @@ -120,13 +120,19 @@ this->operationStackedWidget->addWidget(this->widgetDrawOperation); this->operationStackedWidget->addWidget(this->widgetEditOperation); this->operationStackedWidget->addWidget(this->widgetRoiOperation); - + QHBoxLayout* layout = new QHBoxLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); - layout->addWidget(nameLabel); - layout->addWidget(this->widgetMode); - layout->addSpacing(10); - layout->addWidget(this->operationStackedWidget); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); + layout->addWidget(BrainBrowserWindowToolBar::createToolWidget("Border
Mode", + this->widgetMode, + BrainBrowserWindowToolBar::WIDGET_PLACEMENT_LEFT, + BrainBrowserWindowToolBar::WIDGET_PLACEMENT_TOP, + 0)); + layout->addWidget(BrainBrowserWindowToolBar::createToolWidget("Border
Operations", + this->operationStackedWidget, + BrainBrowserWindowToolBar::WIDGET_PLACEMENT_LEFT, + BrainBrowserWindowToolBar::WIDGET_PLACEMENT_TOP, + 0)); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BRAIN_RESET); @@ -158,6 +164,8 @@ m_borderOptimizeDialog = NULL; } + this->inputModeBorders->borderBeingDrawn->clear(); + brainEvent->setEventProcessed(); } } @@ -174,53 +182,43 @@ switch (this->inputModeBorders->getMode()) { case UserInputModeBorders::MODE_DRAW: this->operationStackedWidget->setCurrentWidget(this->widgetDrawOperation); - this->setActionGroupByActionData(this->drawOperationActionGroup, - inputModeBorders->getDrawOperation()); resetLastEditedBorder(); break; case UserInputModeBorders::MODE_EDIT: this->operationStackedWidget->setCurrentWidget(this->widgetEditOperation); - this->setActionGroupByActionData(this->editOperationActionGroup, - inputModeBorders->getEditOperation()); break; case UserInputModeBorders::MODE_ROI: this->operationStackedWidget->setCurrentWidget(this->widgetRoiOperation); resetLastEditedBorder(); break; } - const int selectedModeInteger = (int)this->inputModeBorders->getMode(); - - const int modeComboBoxIndex = this->modeComboBox->findData(selectedModeInteger); - CaretAssert(modeComboBoxIndex >= 0); - this->modeComboBox->blockSignals(true); - this->modeComboBox->setCurrentIndex(modeComboBoxIndex); - this->modeComboBox->blockSignals(false); -} - -/** - * Set the action with its data value of the given integer - * as the active action. - * @param actionGroup - * Action group for which action is selected. - * @param dataInteger - * Integer value for data attribute. - */ -void -UserInputModeBordersWidget::setActionGroupByActionData(QActionGroup* actionGroup, - const int dataInteger) -{ - actionGroup->blockSignals(true); - const QList actionList = actionGroup->actions(); - QListIterator iter(actionList); - while (iter.hasNext()) { - QAction* action = iter.next(); - const int actionDataInteger = action->data().toInt(); - if (dataInteger == actionDataInteger) { - action->setChecked(true); + + switch (this->inputModeBorders->getDrawOperation()) { + case UserInputModeBorders::DRAW_OPERATION_CREATE: + m_drawNewRadioButton->setChecked(true); + break; + case UserInputModeBorders::DRAW_OPERATION_ERASE: + m_drawEraseRadioButton->setChecked(true); + break; + case UserInputModeBorders::DRAW_OPERATION_EXTEND: + m_drawExtendRadioButton->setChecked(true); + break; + case UserInputModeBorders::DRAW_OPERATION_OPTIMIZE: + m_drawOptimizeRadioButton->setChecked(true); + break; + case UserInputModeBorders::DRAW_OPERATION_REPLACE: + m_drawReplaceRadioButton->setChecked(true); + break; + } + + switch (this->inputModeBorders->getEditOperation()) { + case UserInputModeBorders::EDIT_OPERATION_DELETE: + m_editDeleteRadioButton->setChecked(true); + break; + case UserInputModeBorders::EDIT_OPERATION_PROPERTIES: + m_editPropertiesRadioButton->setChecked(true); break; - } } - actionGroup->blockSignals(false); } /** @@ -229,37 +227,57 @@ QWidget* UserInputModeBordersWidget::createModeWidget() { - this->modeComboBox = new QComboBox(); - this->modeComboBox->addItem("Draw", (int)UserInputModeBorders::MODE_DRAW); - this->modeComboBox->addItem("Edit", (int)UserInputModeBorders::MODE_EDIT); - this->modeComboBox->addItem("ROI", (int)UserInputModeBorders::MODE_ROI); - QObject::connect(this->modeComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(modeComboBoxSelection(int))); - - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(this->modeComboBox); + m_modeDrawRadioButton = new QRadioButton("Draw"); + m_modeDrawRadioButton->setChecked(true); + + m_modeEditRadioButton = new QRadioButton("Edit"); + m_modeRoiRadioButton = new QRadioButton("ROI"); + + QButtonGroup* buttonGroup = new QButtonGroup(this); + buttonGroup->addButton(m_modeDrawRadioButton); + buttonGroup->addButton(m_modeEditRadioButton); + buttonGroup->addButton(m_modeRoiRadioButton); + QObject::connect(buttonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &UserInputModeBordersWidget::modeRadioButtonClicked); + + QWidget* widget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(widget); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); + layout->addWidget(m_modeDrawRadioButton); + layout->addWidget(m_modeEditRadioButton); + layout->addWidget(m_modeRoiRadioButton); + layout->addStretch(); + widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** - * Called when a mode is selected from the mode combo box. - * @param indx - * Index of item selected. + * Called when a mode radio button is selected.. + * @param button + * button selected. */ -void -UserInputModeBordersWidget::modeComboBoxSelection(int indx) +void +UserInputModeBordersWidget::modeRadioButtonClicked(QAbstractButton* button) { - const int modeInteger = this->modeComboBox->itemData(indx).toInt(); - const UserInputModeBorders::Mode mode = (UserInputModeBorders::Mode)modeInteger; - this->inputModeBorders->setMode(mode); + if (button == m_modeDrawRadioButton) { + this->inputModeBorders->setMode(UserInputModeBorders::MODE_DRAW); + } + else if (button == m_modeEditRadioButton) { + this->inputModeBorders->setMode(UserInputModeBorders::MODE_EDIT); + } + else if (button == m_modeRoiRadioButton) { + this->inputModeBorders->setMode(UserInputModeBorders::MODE_ROI); + } + else { + CaretAssert(0); + } resetLastEditedBorder(); } + /** * @return The draw operation widget. */ @@ -277,14 +295,9 @@ "or hold down the Shift key and click the mouse to display a " "dialog for assigning the borders attributes (name and color). " + m_transformToolTipText); - QAction* drawAction = WuQtUtilities::createAction("New", - WuQtUtilities::createWordWrappedToolTipText(drawToolTipText), - this); - drawAction->setCheckable(true); - drawAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_CREATE)); - QToolButton* drawToolButton = new QToolButton(); - drawToolButton->setDefaultAction(drawAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(drawToolButton); + m_drawNewRadioButton = new QRadioButton("New"); + WuQtUtilities::setWordWrappedToolTip(m_drawNewRadioButton, + drawToolTipText); /* * Erase @@ -295,14 +308,9 @@ "Press the Finish button or hold down the Shift key and click the " "mouse remove the border section." + m_transformToolTipText); - QAction* eraseAction = WuQtUtilities::createAction("Erase", - WuQtUtilities::createWordWrappedToolTipText(eraseToolTipText), - this); - eraseAction->setCheckable(true); - eraseAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_ERASE)); - QToolButton* eraseToolButton = new QToolButton(); - eraseToolButton->setDefaultAction(eraseAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(eraseToolButton); + m_drawEraseRadioButton = new QRadioButton("Erase"); + WuQtUtilities::setWordWrappedToolTip(m_drawEraseRadioButton, + eraseToolTipText); /* * Extend @@ -317,14 +325,9 @@ "from that point to the nearest end point in the border and then the extension " "will be added." + m_transformToolTipText); - QAction* extendAction = WuQtUtilities::createAction("Extend", - WuQtUtilities::createWordWrappedToolTipText(extendToolTipText), - this); - extendAction->setCheckable(true); - extendAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_EXTEND)); - QToolButton* extendToolButton = new QToolButton(); - extendToolButton->setDefaultAction(extendAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(extendToolButton); + m_drawExtendRadioButton = new QRadioButton("Extend"); + WuQtUtilities::setWordWrappedToolTip(m_drawExtendRadioButton, + extendToolTipText); /* * Replace @@ -339,14 +342,9 @@ "Both the first point and the last point in the segment must " "overlap points in the border." + m_transformToolTipText); - QAction* replaceAction = WuQtUtilities::createAction("Replace", - WuQtUtilities::createWordWrappedToolTipText(replaceToolTipText), - this); - replaceAction->setCheckable(true); - replaceAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_REPLACE)); - QToolButton* replaceToolButton = new QToolButton(); - replaceToolButton->setDefaultAction(replaceAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(replaceToolButton); + m_drawReplaceRadioButton = new QRadioButton("Replace"); + WuQtUtilities::setWordWrappedToolTip(m_drawReplaceRadioButton, + replaceToolTipText); /* * Optimize @@ -354,15 +352,19 @@ const AString optimizeToolTipText("A new border optimization process automatically repositions a manually drawn " "border segment to follow the most probable path based on spatial gradients of " "a set of user-selected feature maps (useful for cortical parcellation)."); - QAction* optimizeAction = WuQtUtilities::createAction("Optimize", - WuQtUtilities::createWordWrappedToolTipText(optimizeToolTipText), - this); - optimizeAction->setCheckable(true); - optimizeAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_OPTIMIZE)); - QToolButton* optimizeToolButton = new QToolButton(); - optimizeToolButton->setDefaultAction(optimizeAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(optimizeToolButton); - + m_drawOptimizeRadioButton = new QRadioButton("Optimize"); + WuQtUtilities::setWordWrappedToolTip(m_drawOptimizeRadioButton, + optimizeToolTipText); + + QButtonGroup* drawButtonGroup = new QButtonGroup(this); + drawButtonGroup->addButton(m_drawNewRadioButton); + drawButtonGroup->addButton(m_drawEraseRadioButton); + drawButtonGroup->addButton(m_drawExtendRadioButton); + drawButtonGroup->addButton(m_drawOptimizeRadioButton); + drawButtonGroup->addButton(m_drawReplaceRadioButton); + QObject::connect(drawButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &UserInputModeBordersWidget::drawRadioButtonClicked); + /* * Finish */ @@ -381,7 +383,7 @@ /* * Undo */ - QAction* undoAction = WuQtUtilities::createAction("Undo", + QAction* undoAction = WuQtUtilities::createAction("Undo Point", "Remove (undo) the last point in the\n" "drawn border segment. If the button\n" "is held down, it will repeat removal\n" @@ -419,30 +421,31 @@ resetToolButton->setDefaultAction(resetAction); WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); - this->drawOperationActionGroup = new QActionGroup(this); - this->drawOperationActionGroup->addAction(drawAction); - this->drawOperationActionGroup->addAction(eraseAction); - this->drawOperationActionGroup->addAction(extendAction); - this->drawOperationActionGroup->addAction(optimizeAction); - this->drawOperationActionGroup->addAction(replaceAction); - this->drawOperationActionGroup->setExclusive(true); - QObject::connect(this->drawOperationActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(drawOperationActionTriggered(QAction*))); + + QVBoxLayout* modeLayout = new QVBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(modeLayout, 4, 2); + modeLayout->addWidget(m_drawNewRadioButton); + modeLayout->addWidget(m_drawEraseRadioButton); + modeLayout->addWidget(m_drawExtendRadioButton); + modeLayout->addWidget(m_drawOptimizeRadioButton); + modeLayout->addWidget(m_drawReplaceRadioButton); + modeLayout->addStretch(); + + QVBoxLayout* finishUndoLayout = new QVBoxLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(finishUndoLayout, 4, 2); + finishUndoLayout->addWidget(finishToolButton); + finishUndoLayout->addWidget(m_undoFinishToolButton); + finishUndoLayout->addWidget(undoToolButton); + finishUndoLayout->addSpacing(5); + finishUndoLayout->addWidget(resetToolButton); + finishUndoLayout->addStretch(); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(drawToolButton); - layout->addWidget(eraseToolButton); - layout->addWidget(extendToolButton); - layout->addWidget(optimizeToolButton); - layout->addWidget(replaceToolButton); - layout->addSpacing(10); - layout->addWidget(finishToolButton); - layout->addWidget(m_undoFinishToolButton); - layout->addSpacing(10); - layout->addWidget(undoToolButton); - layout->addWidget(resetToolButton); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); + layout->addLayout(modeLayout); + layout->addLayout(finishUndoLayout); + layout->addStretch(); widget->setFixedWidth(widget->sizeHint().width()); return widget; @@ -548,7 +551,7 @@ } const int32_t browserTabIndex = btc->getTabNumber(); - if (this->inputModeBorders->borderBeingDrawnByOpenGL->verifyAllPointsOnSameStructure() == false) { + if (this->inputModeBorders->borderBeingDrawn->verifyAllPointsOnSameStructure() == false) { WuQMessageBox::errorOk(this, "Error: Border points are on more than one structure."); return; } @@ -578,7 +581,7 @@ break; } - if (this->inputModeBorders->borderBeingDrawnByOpenGL->getNumberOfPoints() < minimumNumberOfBorderPoints) { + if (this->inputModeBorders->borderBeingDrawn->getNumberOfPoints() < minimumNumberOfBorderPoints) { WuQMessageBox::errorOk(this, ("There must be at least " + AString::number(minimumNumberOfBorderPoints) @@ -599,12 +602,12 @@ } else if (wholeBrainController != NULL) { brain = wholeBrainController->getBrain(); - const StructureEnum::Enum structure = this->inputModeBorders->borderBeingDrawnByOpenGL->getStructure(); + const StructureEnum::Enum structure = this->inputModeBorders->borderBeingDrawn->getStructure(); surface = wholeBrainController->getSelectedSurface(structure, btc->getTabNumber()); } else if (surfaceMontageController != NULL) { brain = surfaceMontageController->getBrain(); - const StructureEnum::Enum structure = this->inputModeBorders->borderBeingDrawnByOpenGL->getStructure(); + const StructureEnum::Enum structure = this->inputModeBorders->borderBeingDrawn->getStructure(); surface = surfaceMontageController->getSelectedSurface(structure, btc->getTabNumber()); } @@ -632,7 +635,7 @@ case UserInputModeBorders::DRAW_OPERATION_CREATE: { std::unique_ptr finishBorderDialog( - BorderPropertiesEditorDialog::newInstanceFinishBorder(this->inputModeBorders->borderBeingDrawnByOpenGL, + BorderPropertiesEditorDialog::newInstanceFinishBorder(this->inputModeBorders->borderBeingDrawn, surface, this)); if (finishBorderDialog->exec() == BorderPropertiesEditorDialog::Accepted) { @@ -644,7 +647,7 @@ processBorderOptimization(displayGroup, browserTabIndex, surface, - this->inputModeBorders->borderBeingDrawnByOpenGL); + this->inputModeBorders->borderBeingDrawn); break; case UserInputModeBorders::DRAW_OPERATION_ERASE: case UserInputModeBorders::DRAW_OPERATION_EXTEND: @@ -666,7 +669,7 @@ borderFile->findAllBordersWithPointsNearBothSegmentEndPoints(displayGroup, browserTabIndex, surface, - this->inputModeBorders->borderBeingDrawnByOpenGL, + this->inputModeBorders->borderBeingDrawn, nearestTolerance, bordersFoundFromFile); break; @@ -674,15 +677,9 @@ borderFile->findAllBordersWithAnyPointNearSegmentFirstPoint(displayGroup, browserTabIndex, surface, - this->inputModeBorders->borderBeingDrawnByOpenGL, + this->inputModeBorders->borderBeingDrawn, nearestTolerance, bordersFoundFromFile); -// borderFile->findAllBordersWithEndPointNearSegmentFirstPoint(displayGroup, -// browserTabIndex, -// surface, -// this->inputModeBorders->borderBeingDrawnByOpenGL, -// nearestTolerance, -// bordersFoundFromFile); break; case UserInputModeBorders::DRAW_OPERATION_OPTIMIZE: CaretAssert(0); @@ -691,7 +688,7 @@ borderFile->findAllBordersWithPointsNearBothSegmentEndPoints(displayGroup, browserTabIndex, surface, - this->inputModeBorders->borderBeingDrawnByOpenGL, + this->inputModeBorders->borderBeingDrawn, nearestTolerance, bordersFoundFromFile); break; @@ -762,21 +759,19 @@ break; case UserInputModeBorders::DRAW_OPERATION_ERASE: border->reviseEraseFromEnd(surface, - this->inputModeBorders->borderBeingDrawnByOpenGL); + this->inputModeBorders->borderBeingDrawn); break; case UserInputModeBorders::DRAW_OPERATION_EXTEND: border->reviseExtendFromPointIndex(surface, borderPointIndex, - this->inputModeBorders->borderBeingDrawnByOpenGL); - //border->reviseExtendFromEnd(surface, - // this->inputModeBorders->borderBeingDrawnByOpenGL); + this->inputModeBorders->borderBeingDrawn); break; case UserInputModeBorders::DRAW_OPERATION_OPTIMIZE: CaretAssert(0); break; case UserInputModeBorders::DRAW_OPERATION_REPLACE: border->reviseReplaceSegment(surface, - this->inputModeBorders->borderBeingDrawnByOpenGL); + this->inputModeBorders->borderBeingDrawn); break; } @@ -795,7 +790,7 @@ } else { setLastEditedBorder(undoBorders); - this->inputModeBorders->borderBeingDrawnByOpenGL->clear(); + this->inputModeBorders->borderBeingDrawn->clear(); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); @@ -951,7 +946,7 @@ setLastEditedBorder(undoBorders); if ( ! m_borderOptimizeDialog->isKeepBoundaryBorderSelected()) { - this->inputModeBorders->borderBeingDrawnByOpenGL->clear(); + this->inputModeBorders->borderBeingDrawn->clear(); } } @@ -973,17 +968,28 @@ } /** - * Called when a draw mode button is clicked. - * @param action - * Action that was triggered. + * Called when a draw radio button is clicked. + * @param button + * Button that was clicked */ -void -UserInputModeBordersWidget::drawOperationActionTriggered(QAction* action) +void +UserInputModeBordersWidget::drawRadioButtonClicked(QAbstractButton* button) { - const int drawModeInteger = action->data().toInt(); - const UserInputModeBorders::DrawOperation drawOperation = - static_cast(drawModeInteger); - this->inputModeBorders->setDrawOperation(drawOperation); + if (button == m_drawNewRadioButton) { + this->inputModeBorders->setDrawOperation(UserInputModeBorders::DRAW_OPERATION_CREATE); + } + else if (button == m_drawEraseRadioButton) { + this->inputModeBorders->setDrawOperation(UserInputModeBorders::DRAW_OPERATION_ERASE); + } + else if (button == m_drawExtendRadioButton) { + this->inputModeBorders->setDrawOperation(UserInputModeBorders::DRAW_OPERATION_EXTEND); + } + else if (button == m_drawOptimizeRadioButton) { + this->inputModeBorders->setDrawOperation(UserInputModeBorders::DRAW_OPERATION_OPTIMIZE); + } + else if (button == m_drawReplaceRadioButton) { + this->inputModeBorders->setDrawOperation(UserInputModeBorders::DRAW_OPERATION_REPLACE); + } } /** @@ -995,56 +1001,52 @@ const AString deleteToolTipText = ("Delete a border by clicking the mouse over " "any point in the border." + m_transformToolTipText); - QAction* deleteAction = WuQtUtilities::createAction("Delete", - WuQtUtilities::createWordWrappedToolTipText(deleteToolTipText), - this); - deleteAction->setCheckable(true); - deleteAction->setData(static_cast(UserInputModeBorders::EDIT_OPERATION_DELETE)); - QToolButton* deleteToolButton = new QToolButton(); - deleteToolButton->setDefaultAction(deleteAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(deleteToolButton); + m_editDeleteRadioButton = new QRadioButton("Delete"); + WuQtUtilities::setWordWrappedToolTip(m_editDeleteRadioButton, + deleteToolTipText); + const AString propertiesToolTipText = ("A dialog for editing a border's properties is displayed by " "clicking any point in a border." + m_transformToolTipText); - QAction* propertiesAction = WuQtUtilities::createAction("Properties", - WuQtUtilities::createWordWrappedToolTipText(propertiesToolTipText), - this); - propertiesAction->setCheckable(true); - propertiesAction->setData(static_cast(UserInputModeBorders::EDIT_OPERATION_PROPERTIES)); - QToolButton* propertiesToolButton = new QToolButton(); - propertiesToolButton->setDefaultAction(propertiesAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(propertiesToolButton); - - this->editOperationActionGroup = new QActionGroup(this); - this->editOperationActionGroup->addAction(deleteAction); - this->editOperationActionGroup->addAction(propertiesAction); - this->editOperationActionGroup->setExclusive(true); - QObject::connect(this->editOperationActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(editOperationActionTriggered(QAction*))); + m_editPropertiesRadioButton = new QRadioButton("Properties"); + WuQtUtilities::setWordWrappedToolTip(m_editPropertiesRadioButton, + propertiesToolTipText); + + QButtonGroup* editButtonGroup = new QButtonGroup(this); + editButtonGroup->addButton(m_editDeleteRadioButton); + editButtonGroup->addButton(m_editPropertiesRadioButton); + QObject::connect(editButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &UserInputModeBordersWidget::editRadioButtonClicked); QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(deleteToolButton); - layout->addWidget(propertiesToolButton); + QVBoxLayout* layout = new QVBoxLayout(widget); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); + layout->addWidget(m_editDeleteRadioButton); + layout->addWidget(m_editPropertiesRadioButton); + layout->addStretch(); widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** - * Called when a edit button is clicked. - * @param action - * Action that was triggered. + * Called when a edit radio button is clicked. + * @param button + * Button that was clicked */ -void -UserInputModeBordersWidget::editOperationActionTriggered(QAction* action) +void +UserInputModeBordersWidget::editRadioButtonClicked(QAbstractButton* button) { - const int editModeInteger = action->data().toInt(); - const UserInputModeBorders::EditOperation editOperation = - static_cast(editModeInteger); - this->inputModeBorders->setEditOperation(editOperation); + if (button == m_editDeleteRadioButton) { + this->inputModeBorders->setEditOperation(UserInputModeBorders::EDIT_OPERATION_DELETE); + } + else if (button == m_editPropertiesRadioButton) { + this->inputModeBorders->setEditOperation(UserInputModeBorders::EDIT_OPERATION_PROPERTIES); + } + else { + CaretAssert(0); + } } /** diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeBordersWidget.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeBordersWidget.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeBordersWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeBordersWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -27,9 +27,10 @@ #include "DisplayGroupEnum.h" #include "EventListenerInterface.h" +class QAbstractButton; class QAction; -class QActionGroup; class QComboBox; +class QRadioButton; class QStackedWidget; class QToolButton; @@ -64,16 +65,18 @@ private slots: void adjustViewActionTriggered(); - void drawOperationActionTriggered(QAction*); - void editOperationActionTriggered(QAction*); - - void modeComboBoxSelection(int); void drawResetButtonClicked(); void drawUndoButtonClicked(); void drawUndoLastEditButtonClicked(); void drawFinishButtonClicked(); + void modeRadioButtonClicked(QAbstractButton* button); + + void drawRadioButtonClicked(QAbstractButton* button); + + void editRadioButtonClicked(QAbstractButton* button); + private: class BorderFileAndBorderMemento { public: @@ -91,13 +94,6 @@ UserInputModeBordersWidget& operator=(const UserInputModeBordersWidget&); - void setActionGroupByActionData(QActionGroup* actionGroup, - const int dataInteger); - - QActionGroup* drawOperationActionGroup; - - QActionGroup* editOperationActionGroup; - QWidget* createModeWidget(); QWidget* createDrawOperationWidget(); @@ -115,7 +111,25 @@ Surface* surface, Border* borderDrawnByUser); - QComboBox* modeComboBox; + QRadioButton* m_modeDrawRadioButton; + + QRadioButton* m_modeEditRadioButton; + + QRadioButton* m_modeRoiRadioButton; + + QRadioButton* m_drawNewRadioButton; + + QRadioButton* m_drawEraseRadioButton; + + QRadioButton* m_drawExtendRadioButton; + + QRadioButton* m_drawOptimizeRadioButton; + + QRadioButton* m_drawReplaceRadioButton; + + QRadioButton* m_editDeleteRadioButton; + + QRadioButton* m_editPropertiesRadioButton; QWidget* widgetMode; diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeFoci.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeFoci.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeFoci.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeFoci.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -61,13 +61,13 @@ * Constructor. */ UserInputModeFoci::UserInputModeFoci(const int32_t windowIndex) -: UserInputModeView(UserInputModeEnum::FOCI), +: UserInputModeView(windowIndex, + UserInputModeEnum::Enum::FOCI), m_windowIndex(windowIndex) { m_inputModeFociWidget = new UserInputModeFociWidget(this, windowIndex); - m_mode = MODE_CREATE; - m_editOperation = EDIT_OPERATION_PROPERTIES; + m_mode = MODE_CREATE_AT_ID; setWidgetForToolBar(m_inputModeFociWidget); } @@ -104,26 +104,6 @@ } /** - * @return The edit operation. - */ -UserInputModeFoci::EditOperation -UserInputModeFoci::getEditOperation() const -{ - return m_editOperation; -} - -/** - * Set the edit operation. - * @param editOperation - * New edit operation. - */ -void -UserInputModeFoci::setEditOperation(const EditOperation editOperation) -{ - m_editOperation = editOperation; -} - -/** * Called when 'this' user input receiver is set * to receive events. */ @@ -161,21 +141,13 @@ CursorEnum::Enum cursor = CursorEnum::CURSOR_DEFAULT; switch (m_mode) { - case MODE_CREATE: + case MODE_CREATE_AT_ID: break; - case MODE_EDIT: - cursor = CursorEnum::CURSOR_POINTING_HAND; - switch (m_editOperation) { - case EDIT_OPERATION_DELETE: - cursor = CursorEnum::CURSOR_CROSS; - break; - case EDIT_OPERATION_PROPERTIES: - cursor = CursorEnum::CURSOR_WHATS_THIS; - break; - } + case MODE_DELETE: + cursor = CursorEnum::CURSOR_CROSS; break; - case MODE_OPERATIONS: - cursor = CursorEnum::CURSOR_POINTING_HAND; + case MODE_EDIT: + cursor = CursorEnum::CURSOR_WHATS_THIS; break; } @@ -233,7 +205,7 @@ true); switch (m_mode) { - case MODE_CREATE: + case MODE_CREATE_AT_ID: { SelectionItemSurfaceNode* idNode = idManager->getSurfaceNodeIdentification(); SelectionItemVoxel* idVoxel = idManager->getVoxelIdentification(); @@ -285,6 +257,7 @@ m_inputModeFociWidget); } } break; + case MODE_DELETE: case MODE_EDIT: { FociFile* fociFile = NULL; @@ -308,23 +281,26 @@ if ((fociFile != NULL) && (focus != NULL)) { - switch (m_editOperation) { - case EDIT_OPERATION_DELETE: + switch (m_mode) { + case MODE_CREATE_AT_ID: + break; + case MODE_DELETE: + { fociFile->removeFocus(focus); updateAfterFociChanged(); + } break; - case EDIT_OPERATION_PROPERTIES: + case MODE_EDIT: { FociPropertiesEditorDialog::editFocus(fociFile, focus, openGLWidget); } + break; } } } break; - case MODE_OPERATIONS: - break; } } diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeFoci.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeFoci.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeFoci.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeFoci.h 2021-02-16 19:46:47.000000000 +0000 @@ -37,14 +37,9 @@ public: enum Mode { - MODE_CREATE, - MODE_EDIT, - MODE_OPERATIONS - }; - - enum EditOperation { - EDIT_OPERATION_DELETE, - EDIT_OPERATION_PROPERTIES + MODE_CREATE_AT_ID, + MODE_DELETE, + MODE_EDIT }; UserInputModeFoci(const int32_t windowIndex); @@ -81,10 +76,6 @@ void setMode(const Mode mode); - EditOperation getEditOperation() const; - - void setEditOperation(const EditOperation editOperation); - void updateAfterFociChanged(); Surface* getAnatomicalSurfaceForSurface(Surface* surface); @@ -96,8 +87,6 @@ UserInputModeFociWidget* m_inputModeFociWidget; Mode m_mode; - - EditOperation m_editOperation; }; #ifdef __USER_INPUT_MODE_FOCI_DECLARE__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeFociWidget.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeFociWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeFociWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeFociWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -20,11 +20,11 @@ /*LICENSE_END*/ #include -#include -#include +#include +#include #include #include -#include +#include #include #define __USER_INPUT_MODE_FOCI_WIDGET_DECLARE__ @@ -88,27 +88,11 @@ ); m_inputModeFoci = inputModeFoci; - QLabel* nameLabel = new QLabel("Foci "); - QWidget* modeWidget = createModeWidget(); - m_createOperationWidget = createCreateOperationWidget(); - - m_editOperationWidget = createEditOperationWidget(); - - m_taskOperationWidget = createTaskOperationWidget(); - - m_operationStackedWidget = new QStackedWidget(); - m_operationStackedWidget->addWidget(m_createOperationWidget); - m_operationStackedWidget->addWidget(m_editOperationWidget); - //m_operationStackedWidget->addWidget(m_taskOperationWidget); - - QHBoxLayout* layout = new QHBoxLayout(this); + QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); - layout->addWidget(nameLabel); layout->addWidget(modeWidget); - layout->addSpacing(10); - layout->addWidget(m_operationStackedWidget); layout->addStretch(); } @@ -130,53 +114,16 @@ * Show the proper widget */ switch (m_inputModeFoci->getMode()) { - case UserInputModeFoci::MODE_CREATE: - m_operationStackedWidget->setCurrentWidget(m_createOperationWidget); -// setActionGroupByActionData(m_createOperationActionGroup, -// m_inputModeFoci->getCreateOperation()); + case UserInputModeFoci::MODE_CREATE_AT_ID: + m_modeCreateLastIdRadioButton->setChecked(true); break; - case UserInputModeFoci::MODE_EDIT: - m_operationStackedWidget->setCurrentWidget(m_editOperationWidget); - setActionGroupByActionData(m_editOperationActionGroup, - m_inputModeFoci->getEditOperation()); - break; - case UserInputModeFoci::MODE_OPERATIONS: - m_operationStackedWidget->setCurrentWidget(m_taskOperationWidget); + case UserInputModeFoci::MODE_DELETE: + m_modeDeleteRadioButton->setChecked(true); break; - } - const int selectedModeInteger = (int)m_inputModeFoci->getMode(); - - const int modeComboBoxIndex = m_modeComboBox->findData(selectedModeInteger); - CaretAssert(modeComboBoxIndex >= 0); - m_modeComboBox->blockSignals(true); - m_modeComboBox->setCurrentIndex(modeComboBoxIndex); - m_modeComboBox->blockSignals(false); -} - -/** - * Set the action with its data value of the given integer - * as the active action. - * @param actionGroup - * Action group for which action is selected. - * @param dataInteger - * Integer value for data attribute. - */ -void -UserInputModeFociWidget::setActionGroupByActionData(QActionGroup* actionGroup, - const int dataInteger) -{ - actionGroup->blockSignals(true); - const QList actionList = actionGroup->actions(); - QListIterator iter(actionList); - while (iter.hasNext()) { - QAction* action = iter.next(); - const int actionDataInteger = action->data().toInt(); - if (dataInteger == actionDataInteger) { - action->setChecked(true); + case UserInputModeFoci::MODE_EDIT: + m_modePropertiesRadioButton->setChecked(true); break; - } } - actionGroup->blockSignals(false); } /** @@ -185,79 +132,80 @@ QWidget* UserInputModeFociWidget::createModeWidget() { - m_modeComboBox = new QComboBox(); - m_modeComboBox->addItem("Create", (int)UserInputModeFoci::MODE_CREATE); - m_modeComboBox->addItem("Edit", (int)UserInputModeFoci::MODE_EDIT); -// m_modeComboBox->addItem("Tasks", (int)UserInputModeFoci::MODE_OPERATIONS); - QObject::connect(m_modeComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(modeComboBoxSelection(int))); + const AString newToolTipText = ("Click this button to display a dialog for creating a new focus. " + + m_transformToolTipText); + + QToolButton* newFocusToolButton = new QToolButton(); + newFocusToolButton->setText("New Focus..."); + WuQtUtilities::setToolButtonStyleForQt5Mac(newFocusToolButton); + newFocusToolButton->setToolTip(newToolTipText); + QObject::connect(newFocusToolButton, &QToolButton::clicked, + this, &UserInputModeFociWidget::createNewFocusActionTriggered); + + const AString lastIDToolTipText = ("While this button is on, clicking the mouse on a surface " + "will launch the create focus dialog initialized with coordinates " + "of the selected surface vertex. " + + m_transformToolTipText); + m_modeCreateLastIdRadioButton = new QRadioButton("Create Last ID"); + m_modeCreateLastIdRadioButton->setToolTip(lastIDToolTipText); - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(m_modeComboBox); + const AString deleteToolTipText = ("While this button is on, clicking the mouse over a focus " + "will delete the focus. " + + m_transformToolTipText); + m_modeDeleteRadioButton = new QRadioButton("Delete"); + m_modeDeleteRadioButton->setToolTip(deleteToolTipText); + + const AString propertiesToolTipText = ("While this button is on, clicking the mouse over a focus " + "displays a dialog for editing the focus' properties. " + + m_transformToolTipText); + m_modePropertiesRadioButton = new QRadioButton("Edit Properties"); + m_modePropertiesRadioButton->setToolTip(propertiesToolTipText); + QButtonGroup* buttonGroup = new QButtonGroup(this); + buttonGroup->addButton(m_modeCreateLastIdRadioButton); + buttonGroup->addButton(m_modeDeleteRadioButton); + buttonGroup->addButton(m_modePropertiesRadioButton); + QObject::connect(buttonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &UserInputModeFociWidget::modeRadioButtonClicked); + + QLabel* mouseLabel = new QLabel("Mouse Mode:"); + QWidget* widget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(widget); + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); + layout->addWidget(mouseLabel); + layout->addWidget(m_modeCreateLastIdRadioButton); + layout->addWidget(m_modeDeleteRadioButton); + layout->addWidget(m_modePropertiesRadioButton); + layout->addSpacing(8); + layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); + layout->addSpacing(8); + layout->addWidget(newFocusToolButton); + widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** - * Called when a mode is selected from the mode combo box. - * @param indx - * Index of item selected. + * Called when a mode radio button is selected.. + * @param button + * button selected. */ void -UserInputModeFociWidget::modeComboBoxSelection(int indx) -{ - const int modeInteger = m_modeComboBox->itemData(indx).toInt(); - const UserInputModeFoci::Mode mode = (UserInputModeFoci::Mode)modeInteger; - m_inputModeFoci->setMode(mode); -} - -/** - * @return The draw operation widget. - */ -QWidget* -UserInputModeFociWidget::createCreateOperationWidget() +UserInputModeFociWidget::modeRadioButtonClicked(QAbstractButton* button) { - const AString newToolTipText = ("Press this button to display a dialog for creating a new focus. " - "If the mouse is clicked over a model, the dialog for creating a focus is " - "displayed with the focus' coordinates set to the stereotaxic coordinates at " - "the location of the mouse click." - + m_transformToolTipText); - QAction* newFocusAction = WuQtUtilities::createAction("New...", - WuQtUtilities::createWordWrappedToolTipText(newToolTipText), - this, - this, - SLOT(createNewFocusActionTriggered())); - QToolButton* newFocusToolButton = new QToolButton(); - newFocusToolButton->setDefaultAction(newFocusAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(newFocusToolButton); - - const AString lastIDToolTipText = ("Press this button to display a dialog for creating a new focus " - "with the focus' coordinates set to the stereotaxic location of the " - "last identification operation. While in focus mode, an identification " - "is performed by holding down the Shift key and clicking the mouse." - + m_transformToolTipText); - QAction* lastIdFocusAction = WuQtUtilities::createAction("Last ID", - WuQtUtilities::createWordWrappedToolTipText(lastIDToolTipText), - this, - this, - SLOT(createLastIdentificationFocusActionTriggered())); - QToolButton* lastIdFocusToolButton = new QToolButton(); - lastIdFocusToolButton->setDefaultAction(lastIdFocusAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(lastIdFocusToolButton); - - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(newFocusToolButton); - layout->addSpacing(5); - layout->addWidget(lastIdFocusToolButton); - - widget->setFixedWidth(widget->sizeHint().width()); - return widget; + if (button == m_modeCreateLastIdRadioButton) { + m_inputModeFoci->setMode(UserInputModeFoci::MODE_CREATE_AT_ID); + } + else if (button == m_modeDeleteRadioButton) { + m_inputModeFoci->setMode(UserInputModeFoci::MODE_DELETE); + } + else if (button == m_modePropertiesRadioButton) { + m_inputModeFoci->setMode(UserInputModeFoci::MODE_EDIT); + } + else { + CaretAssert(0); + } } /** @@ -378,77 +326,4 @@ } } -/** - * @return The edit widget. - */ -QWidget* -UserInputModeFociWidget::createEditOperationWidget() -{ - const AString deleteToolTipText = ("Delete a focus by clicking the mouse over the focus." - + m_transformToolTipText); - QAction* deleteAction = WuQtUtilities::createAction("Delete", - WuQtUtilities::createWordWrappedToolTipText(deleteToolTipText), - this); - deleteAction->setCheckable(true); - deleteAction->setData(static_cast(UserInputModeFoci::EDIT_OPERATION_DELETE)); - QToolButton* deleteToolButton = new QToolButton(); - deleteToolButton->setDefaultAction(deleteAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(deleteToolButton); - - const AString propertiesToolTipText = ("Click the mouse over a focus to display a dialog " - "for editing the focus' properties." - + m_transformToolTipText); - QAction* propertiesAction = WuQtUtilities::createAction("Properties", - WuQtUtilities::createWordWrappedToolTipText(propertiesToolTipText), - this); - propertiesAction->setCheckable(true); - propertiesAction->setData(static_cast(UserInputModeFoci::EDIT_OPERATION_PROPERTIES)); - QToolButton* propertiesToolButton = new QToolButton(); - propertiesToolButton->setDefaultAction(propertiesAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(propertiesToolButton); - - m_editOperationActionGroup = new QActionGroup(this); - m_editOperationActionGroup->addAction(deleteAction); - m_editOperationActionGroup->addAction(propertiesAction); - m_editOperationActionGroup->setExclusive(true); - QObject::connect(m_editOperationActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(editOperationActionTriggered(QAction*))); - - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(deleteToolButton); - layout->addWidget(propertiesToolButton); - - widget->setFixedWidth(widget->sizeHint().width()); - return widget; -} - -/** - * @return The task operation widget. - */ -QWidget* -UserInputModeFociWidget::createTaskOperationWidget() -{ - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - - widget->setFixedWidth(widget->sizeHint().width()); - return widget; -} - -/** - * Called when an edit operation button is selected. - * @param action - * Action that was selected. - */ -void -UserInputModeFociWidget::editOperationActionTriggered(QAction* action) -{ - const int editModeInteger = action->data().toInt(); - const UserInputModeFoci::EditOperation editOperation = - static_cast(editModeInteger); - m_inputModeFoci->setEditOperation(editOperation); -} diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeFociWidget.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeFociWidget.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeFociWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeFociWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -26,10 +26,9 @@ #include "AString.h" +class QAbstractButton; class QAction; -class QActionGroup; -class QComboBox; -class QStackedWidget; +class QRadioButton; namespace caret { @@ -57,9 +56,7 @@ void createLastIdentificationFocusActionTriggered(); - void editOperationActionTriggered(QAction*); - - void modeComboBoxSelection(int); + void modeRadioButtonClicked(QAbstractButton* button); private: UserInputModeFociWidget(const UserInputModeFociWidget&); @@ -68,32 +65,17 @@ QWidget* createModeWidget(); - QWidget* createCreateOperationWidget(); - - QWidget* createEditOperationWidget(); - - QWidget* createTaskOperationWidget(); - - void setActionGroupByActionData(QActionGroup* actionGroup, - const int dataInteger); - // ADD_NEW_MEMBERS_HERE UserInputModeFoci* m_inputModeFoci; const int32_t m_windowIndex; - QComboBox* m_modeComboBox; - - QActionGroup* m_editOperationActionGroup; - - QWidget* m_createOperationWidget; - - QWidget* m_editOperationWidget; + QRadioButton* m_modeCreateLastIdRadioButton; - QWidget* m_taskOperationWidget; + QRadioButton* m_modeDeleteRadioButton; - QStackedWidget* m_operationStackedWidget; + QRadioButton* m_modePropertiesRadioButton; QString m_transformToolTipText; diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeImage.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeImage.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeImage.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeImage.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -63,7 +63,8 @@ * Constructor. */ UserInputModeImage::UserInputModeImage(const int32_t windowIndex) -: UserInputModeView(UserInputModeEnum::IMAGE), +: UserInputModeView(windowIndex, + UserInputModeEnum::Enum::IMAGE), m_windowIndex(windowIndex) { m_inputModeImageWidget = new UserInputModeImageWidget(this, diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeImageWidget.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeImageWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeImageWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeImageWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -21,9 +21,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -92,118 +94,41 @@ ); m_inputModeImage = inputModeImage; - QLabel* nameLabel = new QLabel("Image Control Points "); - m_editOperationWidget = createEditOperationWidget(); - - - QHBoxLayout* layout = new QHBoxLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); - layout->addWidget(nameLabel); - layout->addSpacing(10); - layout->addWidget(m_editOperationWidget); - layout->addStretch(); -} - -/** - * Destructor. - */ -UserInputModeImageWidget::~UserInputModeImageWidget() -{ - -} - -/** - * Update the contents of the widget. - */ -void -UserInputModeImageWidget::updateWidget() -{ - setActionGroupByActionData(m_editOperationActionGroup, - (int)m_inputModeImage->getEditOperation()); -} - -/** - * Set the action with its data value of the given integer - * as the active action. - * @param actionGroup - * Action group for which action is selected. - * @param dataInteger - * Integer value for data attribute. - */ -void -UserInputModeImageWidget::setActionGroupByActionData(QActionGroup* actionGroup, - const int dataInteger) -{ - actionGroup->blockSignals(true); - const QList actionList = actionGroup->actions(); - QListIterator iter(actionList); - while (iter.hasNext()) { - QAction* action = iter.next(); - const int actionDataInteger = action->data().toInt(); - if (dataInteger == actionDataInteger) { - action->setChecked(true); - break; - } - } - actionGroup->blockSignals(false); -} - - -/** - * @return The edit widget. - */ -QWidget* -UserInputModeImageWidget::createEditOperationWidget() -{ /* * Add button */ const AString addToolTipText = ("Click the mouse over an image and volume slice " "to add a control point." + m_transformToolTipText); - QAction* addAction = WuQtUtilities::createAction("Add", - WuQtUtilities::createWordWrappedToolTipText(addToolTipText), - this); - addAction->setCheckable(true); - addAction->setData(static_cast(UserInputModeImage::EDIT_OPERATION_ADD)); - QToolButton* addToolButton = new QToolButton(); - addToolButton->setDefaultAction(addAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(addToolButton); + + m_addControlPointRadioButton = new QRadioButton("Add"); + m_addControlPointRadioButton->setChecked(true); + m_addControlPointRadioButton->setToolTip(addToolTipText); /* * Delete button */ const AString deleteToolTipText = ("Delete a control point by clicking the mouse over the control point." - + m_transformToolTipText); - QAction* deleteAction = WuQtUtilities::createAction("Delete", - WuQtUtilities::createWordWrappedToolTipText(deleteToolTipText), - this); - deleteAction->setCheckable(true); - deleteAction->setData(static_cast(UserInputModeImage::EDIT_OPERATION_DELETE)); - QToolButton* deleteToolButton = new QToolButton(); - deleteToolButton->setDefaultAction(deleteAction); - WuQtUtilities::setToolButtonStyleForQt5Mac(deleteToolButton); + + m_transformToolTipText); + m_deleteControlPointRadioButton = new QRadioButton("Delete"); + + QButtonGroup* addDeleteButtonGroup = new QButtonGroup(this); + addDeleteButtonGroup->addButton(m_addControlPointRadioButton); + addDeleteButtonGroup->addButton(m_deleteControlPointRadioButton); + QObject::connect(addDeleteButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &UserInputModeImageWidget::addDeleteRadioButtonClicked); - /* - * Action group to make add/delete actions mutually exclusive - */ - m_editOperationActionGroup = new QActionGroup(this); - m_editOperationActionGroup->addAction(deleteAction); - m_editOperationActionGroup->addAction(addAction); - m_editOperationActionGroup->setExclusive(true); - QObject::connect(m_editOperationActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(editOperationActionTriggered(QAction*))); /* * Convert button */ const AString convertToolTipText = ("Convert image to volume"); QAction* convertAction = WuQtUtilities::createAction("Convert...", - WuQtUtilities::createWordWrappedToolTipText(convertToolTipText), - this, - this, - SLOT(convertActionTriggered())); + WuQtUtilities::createWordWrappedToolTipText(convertToolTipText), + this, + this, + SLOT(convertActionTriggered())); convertAction->setCheckable(false); m_convertToolButton = new QToolButton(); m_convertToolButton->setDefaultAction(convertAction); @@ -223,20 +148,42 @@ m_deleteAllToolButton->setDefaultAction(deleteAllAction); WuQtUtilities::setToolButtonStyleForQt5Mac(m_deleteAllToolButton); + QLabel* mouseModeLabel = new QLabel("Control Points:"); - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); + QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(addToolButton); - layout->addWidget(deleteToolButton); - layout->addSpacing(15); + layout->addWidget(mouseModeLabel); + layout->addWidget(m_addControlPointRadioButton); + layout->addWidget(m_deleteControlPointRadioButton); + layout->addSpacing(10); layout->addWidget(m_deleteAllToolButton); - layout->addSpacing(35); + layout->addSpacing(12); layout->addWidget(m_convertToolButton); layout->addStretch(); +} + +/** + * Destructor. + */ +UserInputModeImageWidget::~UserInputModeImageWidget() +{ -// widget->setFixedWidth(widget->sizeHint().width()); - return widget; +} + +/** + * Update the contents of the widget. + */ +void +UserInputModeImageWidget::updateWidget() +{ + switch (m_inputModeImage->getEditOperation()) { + case UserInputModeImage::EDIT_OPERATION_ADD: + m_addControlPointRadioButton->setChecked(true); + break; + case UserInputModeImage::EDIT_OPERATION_DELETE: + m_deleteControlPointRadioButton->setChecked(true); + break; + } } /** @@ -276,17 +223,22 @@ } } - /** - * Called when an edit operation button is selected. - * @param action - * Action that was selected. + * Called when a add/delete radio button is selected.. + * @param button + * button selected. */ void -UserInputModeImageWidget::editOperationActionTriggered(QAction* action) +UserInputModeImageWidget::addDeleteRadioButtonClicked(QAbstractButton* button) { - const int editModeInteger = action->data().toInt(); - const UserInputModeImage::EditOperation editOperation = static_cast(editModeInteger); - m_inputModeImage->setEditOperation(editOperation); + if (button == m_addControlPointRadioButton) { + m_inputModeImage->setEditOperation(UserInputModeImage::EDIT_OPERATION_ADD); + } + else if (button == m_deleteControlPointRadioButton) { + m_inputModeImage->setEditOperation(UserInputModeImage::EDIT_OPERATION_DELETE); + } + else { + CaretAssert(0); + } } diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeImageWidget.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeImageWidget.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeImageWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeImageWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -26,7 +26,9 @@ #include "AString.h" +class QAbstractButton; class QActionGroup; +class QRadioButton; class QToolButton; namespace caret { @@ -53,32 +55,27 @@ void deleteAllActionTriggered(); - void editOperationActionTriggered(QAction*); - + void addDeleteRadioButtonClicked(QAbstractButton* button); + private: UserInputModeImageWidget(const UserInputModeImageWidget&); UserInputModeImageWidget& operator=(const UserInputModeImageWidget&); - QWidget* createEditOperationWidget(); - - void setActionGroupByActionData(QActionGroup* actionGroup, - const int dataInteger); - // ADD_NEW_MEMBERS_HERE UserInputModeImage* m_inputModeImage; const int32_t m_windowIndex; - QActionGroup* m_editOperationActionGroup; + QRadioButton* m_addControlPointRadioButton; + + QRadioButton* m_deleteControlPointRadioButton; QToolButton* m_deleteAllToolButton; QToolButton* m_convertToolButton; - QWidget* m_editOperationWidget; - QString m_transformToolTipText; friend class UserInputModeImage; diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,390 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_CONTEXT_MENU_DECLARE__ +#include "UserInputModeTileTabsManualLayoutContextMenu.h" +#undef __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_CONTEXT_MENU_DECLARE__ + +#include + +#include + +#include "AnnotationBrowserTab.h" +#include "AnnotationManager.h" +#include "AnnotationRedoUndoCommand.h" +#include "Brain.h" +#include "BrainOpenGLWidget.h" +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventBrowserTabGetAll.h" +#include "EventBrowserWindowTileTabOperation.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" +#include "MathFunctions.h" +#include "MouseEvent.h" +#include "UserInputModeTileTabsManualLayout.h" +#include "WuQDataEntryDialog.h" +#include "WuQMessageBox.h" + +using namespace caret; + + + +/** + * \class caret::UserInputModeTileTabsManualLayoutContextMenu + * \brief Context (pop-up) menu for User Input Manual Tile Tabs Layout. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param userInputTileTabsManualLayout + * The manual layout input processor + * @param mouseEvent + * The mouse event that caused display of this menu. + * @param browserTabContent + * Content of browser tab. + * @param parentOpenGLWidget + * Parent OpenGL Widget on which the menu is displayed. + */ +UserInputModeTileTabsManualLayoutContextMenu::UserInputModeTileTabsManualLayoutContextMenu(UserInputModeTileTabsManualLayout* userInputTileTabsManualLayout, + const MouseEvent& mouseEvent, + BrowserTabContent* browserTabContent, + BrainOpenGLWidget* parentOpenGLWidget) +: QMenu(parentOpenGLWidget), +m_userInputTileTabsManualLayout(userInputTileTabsManualLayout), +m_mouseEvent(mouseEvent), +m_browserTabContent(browserTabContent), +m_parentOpenGLWidget(parentOpenGLWidget) +{ + CaretAssert(m_userInputTileTabsManualLayout); + + const int32_t browserWindexIndex = m_mouseEvent.getBrowserWindowIndex(); + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(browserWindexIndex); + for (auto ann : selectedAnnotations) { + AnnotationBrowserTab* abt = dynamic_cast(ann); + if (abt != NULL) { + m_selectedBrowserTabAnnotations.push_back(abt); + } + } + const bool oneTabSelectedFlag((m_selectedBrowserTabAnnotations.size() == 1) + && (m_browserTabContent != NULL)); + + /* + * De/Select All tabs + */ + QAction* deselectAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::DESELECT_ALL), + this, SLOT(deselectAllAnnotations())); + deselectAction->setEnabled( ! m_selectedBrowserTabAnnotations.empty()); + addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::SELECT_ALL), + this, SLOT(selectAllAnnotations())); + + + /* + * Separator + */ + addSeparator(); + + /* + * Order Operations + */ + QAction* bringToFrontAction = addAction("Bring to Front"); + QObject::connect(bringToFrontAction, &QAction::triggered, + this, &UserInputModeTileTabsManualLayoutContextMenu::applyOrderBringToFront); + QAction* bringForwardAction = addAction("Bring Forward"); + QObject::connect(bringForwardAction, &QAction::triggered, + this, &UserInputModeTileTabsManualLayoutContextMenu::applyOrderBringForward); + QAction* sendToBackAction = addAction("Send to Back"); + QObject::connect(sendToBackAction, &QAction::triggered, + this, &UserInputModeTileTabsManualLayoutContextMenu::applyOrderSendToBack); + QAction* sendBackwardAction = addAction("Send Backward"); + QObject::connect(sendBackwardAction, &QAction::triggered, + this, &UserInputModeTileTabsManualLayoutContextMenu::applyOrderSendBackward); + + bringToFrontAction->setEnabled(oneTabSelectedFlag); + bringForwardAction->setEnabled(oneTabSelectedFlag); + sendToBackAction->setEnabled(oneTabSelectedFlag); + sendBackwardAction->setEnabled(oneTabSelectedFlag); + + const bool showGroupingOptionsFlag(false); + if (showGroupingOptionsFlag) { + addSeparator(); + + /* + * Group annotations + */ + QAction* groupAction = addAction(AnnotationGroupingModeEnum::toGuiName(AnnotationGroupingModeEnum::GROUP), + this, SLOT(applyGroupingGroup())); + groupAction->setEnabled(annotationManager->isGroupingModeValid(browserWindexIndex, + AnnotationGroupingModeEnum::GROUP)); + + /* + * Ungroup annotations + */ + QAction* ungroupAction = addAction(AnnotationGroupingModeEnum::toGuiName(AnnotationGroupingModeEnum::UNGROUP), + this, SLOT(applyGroupingUngroup())); + ungroupAction->setEnabled(annotationManager->isGroupingModeValid(browserWindexIndex, + AnnotationGroupingModeEnum::UNGROUP)); + + /* + * Regroup annotations + */ + QAction* regroupAction = addAction(AnnotationGroupingModeEnum::toGuiName(AnnotationGroupingModeEnum::REGROUP), + this, SLOT(applyGroupingRegroup())); + regroupAction->setEnabled(annotationManager->isGroupingModeValid(browserWindexIndex, + AnnotationGroupingModeEnum::REGROUP)); + } + + addSeparator(); + + /* + * Insert new tab option + */ + QAction* insertNewTabAction = addAction("Insert New Tab"); + QObject::connect(insertNewTabAction, &QAction::triggered, + this, &UserInputModeTileTabsManualLayoutContextMenu::processInsertNewTabMenuItem); + + addSeparator(); + + /* + * Expand option + */ + QAction* expendToFillAction = addAction(getShinkAndExpandTabMenuItemText()); + QObject::connect(expendToFillAction, &QAction::triggered, + this, &UserInputModeTileTabsManualLayoutContextMenu::processShrinkAndExpandTabMenuItem); + expendToFillAction->setEnabled(oneTabSelectedFlag); +} + +/** + * Destructor. + */ +UserInputModeTileTabsManualLayoutContextMenu::~UserInputModeTileTabsManualLayoutContextMenu() +{ +} + +/** + * @return Text for "shrink and expand tab" menu item (this function is available from + * this context menu and the Arrange Menu in the toolbar. This function ensures the + * same text in both menus. + */ +AString +UserInputModeTileTabsManualLayoutContextMenu::getShinkAndExpandTabMenuItemText() +{ + return "Move/Resize Tab to Fill Empty Space"; +} + +/** + * Deselect all annotations in the window. + */ +void +UserInputModeTileTabsManualLayoutContextMenu::deselectAllAnnotations() +{ + m_userInputTileTabsManualLayout->processDeselectAllAnnotations(); +} + +/** + * Select all annotations in the window. + */ +void +UserInputModeTileTabsManualLayoutContextMenu::selectAllAnnotations() +{ + m_userInputTileTabsManualLayout->processSelectAllAnnotations(); +} + +/** + * Group annotations. + */ +void +UserInputModeTileTabsManualLayoutContextMenu::applyGroupingGroup() +{ + applyGrouping(AnnotationGroupingModeEnum::GROUP); +} + +/** + * Ungroup annotations. + */ +void +UserInputModeTileTabsManualLayoutContextMenu::applyGroupingRegroup() +{ + applyGrouping(AnnotationGroupingModeEnum::REGROUP); +} + +/** + * Regroup annotations. + */ +void +UserInputModeTileTabsManualLayoutContextMenu::applyGroupingUngroup() +{ + applyGrouping(AnnotationGroupingModeEnum::UNGROUP); +} + +/** + * Apply grouping selection. + * + * @param grouping + * Selected grouping. + */ +void +UserInputModeTileTabsManualLayoutContextMenu::applyGrouping(const AnnotationGroupingModeEnum::Enum grouping) +{ + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + + AString errorMessage; + if ( ! annMan->applyGroupingMode(m_userInputTileTabsManualLayout->getUserInputMode(), + m_mouseEvent.getBrowserWindowIndex(), + grouping, + errorMessage)) { + WuQMessageBox::errorOk(this, + errorMessage); + } + + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Called when expand to fill space menu item is selected + */ +void +UserInputModeTileTabsManualLayoutContextMenu::processShrinkAndExpandTabMenuItem() +{ + const int32_t browserWindowIndex = m_mouseEvent.getBrowserWindowIndex(); + BrainBrowserWindow* window = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); + CaretAssert(window); + std::vector allTabContent; + window->getAllTabContent(allTabContent); + + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + AString errorMessage; + const bool result = annMan->shrinkAndExpandSelectedBrowserTabAnnotation(allTabContent, + browserWindowIndex, + m_userInputTileTabsManualLayout->getUserInputMode(), + errorMessage); + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + + if ( ! result) { + WuQMessageBox::errorOk(this, + errorMessage); + } +} + +/** + * Called when bring to front is selected + */ +void +UserInputModeTileTabsManualLayoutContextMenu::applyOrderBringToFront() +{ + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_TO_FRONT); +} + +/** + * Called when bring forward is selected + */ +void +UserInputModeTileTabsManualLayoutContextMenu::applyOrderBringForward() +{ + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_FORWARD); +} + +/** + * Called when send to back is selected + */ +void +UserInputModeTileTabsManualLayoutContextMenu::applyOrderSendToBack() +{ + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_TO_BACK); +} + +/** + * Called when send backward is selected + */ +void +UserInputModeTileTabsManualLayoutContextMenu::applyOrderSendBackward() +{ + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_BACKWARD); +} + +/** + * Insert a new tab at mouse location + */ +void +UserInputModeTileTabsManualLayoutContextMenu::processInsertNewTabMenuItem() +{ + processWindowTileTabOperation(EventBrowserWindowTileTabOperation::OPERATION_MANUAL_NEW_TAB); +} + + +/** + * Called to process a tile tab operation + * + * @param operation + * The operation + */ +void +UserInputModeTileTabsManualLayoutContextMenu::processWindowTileTabOperation(const EventBrowserWindowTileTabOperation::Operation operation) +{ + switch (operation) { + case EventBrowserWindowTileTabOperation::OPERATION_GRID_NEW_TAB_AFTER: + break; + case EventBrowserWindowTileTabOperation::OPERATION_GRID_NEW_TAB_BEFORE: + break; + case EventBrowserWindowTileTabOperation::OPERATION_MANUAL_NEW_TAB: + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_FORWARD: + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_TO_FRONT: + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_BACKWARD: + break; + case EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_TO_BACK: + break; + case EventBrowserWindowTileTabOperation::OPERATION_REPLACE_TABS: + break; + case EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB: + break; + } + + std::vector emptyBrowserTabs; + + int tabIndex(-1); + if (m_browserTabContent != NULL) { + tabIndex = m_browserTabContent->getTabNumber(); + } + int windowViewport[4] { 0, 0, m_parentOpenGLWidget->width(), m_parentOpenGLWidget->height() }; + EventBrowserWindowTileTabOperation tileTabOperation(operation, + m_parentOpenGLWidget, + m_mouseEvent.getBrowserWindowIndex(), + tabIndex, + windowViewport, + m_mouseEvent.getX(), + m_mouseEvent.getY(), + emptyBrowserTabs); + EventManager::get()->sendEvent(tileTabOperation.getPointer()); +} + + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,108 @@ +#ifndef __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_CONTEXT_MENU_H__ +#define __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_CONTEXT_MENU_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2015 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "AnnotationGroupingModeEnum.h" +#include "EventBrowserWindowTileTabOperation.h" +#include "MouseEvent.h" + +namespace caret { + + class AnnotationBrowserTab; + class BrainOpenGLWidget; + class BrowserTabContent; + class UserInputModeTileTabsManualLayout; + + class UserInputModeTileTabsManualLayoutContextMenu : public QMenu { + + Q_OBJECT + + public: + UserInputModeTileTabsManualLayoutContextMenu(UserInputModeTileTabsManualLayout* userInputTileTabsManualLayout, + const MouseEvent& mouseEvent, + BrowserTabContent* browserTabContent, + BrainOpenGLWidget* parentOpenGLWidget); + + virtual ~UserInputModeTileTabsManualLayoutContextMenu(); + + static AString getShinkAndExpandTabMenuItemText(); + + // ADD_NEW_METHODS_HERE + + private slots: + void deselectAllAnnotations(); + + void selectAllAnnotations(); + + void applyGroupingGroup(); + + void applyGroupingRegroup(); + + void applyGroupingUngroup(); + + void applyOrderBringToFront(); + + void applyOrderBringForward(); + + void applyOrderSendToBack(); + + void applyOrderSendBackward(); + + void processShrinkAndExpandTabMenuItem(); + + void processInsertNewTabMenuItem(); + + private: + UserInputModeTileTabsManualLayoutContextMenu(const UserInputModeTileTabsManualLayoutContextMenu&); + + UserInputModeTileTabsManualLayoutContextMenu& operator=(const UserInputModeTileTabsManualLayoutContextMenu&); + + void applyGrouping(const AnnotationGroupingModeEnum::Enum grouping); + + void processWindowTileTabOperation(const EventBrowserWindowTileTabOperation::Operation operation); + + UserInputModeTileTabsManualLayout* m_userInputTileTabsManualLayout = NULL; + + /* + * NOT a reference. Need to COPY as its source may be deleted. + */ + const MouseEvent m_mouseEvent; + + BrowserTabContent* m_browserTabContent; + + BrainOpenGLWidget* m_parentOpenGLWidget; + + std::vector m_selectedBrowserTabAnnotations; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_CONTEXT_MENU_DECLARE__ + // +#endif // __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_CONTEXT_MENU_DECLARE__ + +} // namespace +#endif //__USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_CONTEXT_MENU_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeTileTabsManualLayout.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeTileTabsManualLayout.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeTileTabsManualLayout.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeTileTabsManualLayout.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,189 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_DECLARE__ +#include "UserInputModeTileTabsManualLayout.h" +#undef __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_DECLARE__ + +#include "AnnotationBrowserTab.h" +#include "AnnotationManager.h" +#include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrainOpenGLViewportContent.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventBrowserTabCloseInToolBar.h" +#include "EventBrowserTabSelectInWindow.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" +#include "UserInputModeTileTabsManualLayoutContextMenu.h" +#include "WuQMessageBox.h" + +using namespace caret; + +/** + * \class caret::UserInputModeTileTabsManualLayout + * \brief User Input mode for editing manual tile tabs layout + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param windowIndex + * Index of window + */ +UserInputModeTileTabsManualLayout::UserInputModeTileTabsManualLayout(const int32_t windowIndex) +: UserInputModeAnnotations(UserInputModeEnum::Enum::TILE_TABS_MANUAL_LAYOUT_EDITING, + windowIndex) +{ + +} + +/** + * Destructor. + */ +UserInputModeTileTabsManualLayout::~UserInputModeTileTabsManualLayout() +{ +} + +/** + * Show a context menu (pop-up menu at mouse location) + * + * @param mouseEvent + * Mouse event information. + * @param menuPosition + * Point at which menu is displayed (passed to QMenu::exec()) + * @param openGLWidget + * OpenGL widget in which context menu is requested + */ +void +UserInputModeTileTabsManualLayout::showContextMenu(const MouseEvent& mouseEvent, + const QPoint& menuPosition, + BrainOpenGLWidget* openGLWidget) +{ + BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); + BrowserTabContent* tabContent = viewportContent->getBrowserTabContent(); + + /* + * Select any annotation that is under the mouse. + * There might not be an annotation under the + * mouse and that is okay. + */ + const bool singleSelectionModeFlag(false); + processMouseSelectAnnotation(mouseEvent, + false, + singleSelectionModeFlag); + + UserInputModeTileTabsManualLayoutContextMenu contextMenu(this, + mouseEvent, + tabContent, + openGLWidget); + if (contextMenu.actions().size() > 0) { + contextMenu.exec(menuPosition); + } +} + +/** + * Delete all selected tabs + */ +void +UserInputModeTileTabsManualLayout::deleteSelectedAnnotations() +{ + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); + if ( ! selectedAnnotations.empty()) { + std::vector tabs; + for (auto ann : selectedAnnotations) { + AnnotationBrowserTab* abt = dynamic_cast(ann); + if (abt != NULL) { + tabs.push_back(abt->getBrowserTabContent()); + } + } + + if ( ! tabs.empty()) { + BrainBrowserWindow* window = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); + CaretAssert(window); + + QString msg("Close the selected tab(s)?"); + if (WuQMessageBox::warningOkCancel(window, + msg)) { + for (auto t : tabs) { + EventBrowserTabCloseInToolBar deleteEvent(t, + t->getTabNumber()); + EventManager::get()->sendEvent(deleteEvent.getPointer()); + } + } + } +/* Will eventually use an undo command when "reopen closed browser tab" is implemented + AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); + undoCommand->setModeDeleteAnnotations(selectedAnnotations); + AString errorMessage; + if ( ! annotationManager->applyCommand(getUserInputMode(), + undoCommand, + errorMessage)) { + WuQMessageBox::errorOk(m_annotationToolsWidget, + errorMessage); + } + */ + } + + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Process a mouse left click for selection mode. + * + * @param mouseEvent + * Mouse event information. + * @param shiftKeyDownFlag + * True if shift key is down. + * @param singleSelectionModeFlag + * If true, deselect any other annotations so that only the annotation under mouse is selected + */ +void +UserInputModeTileTabsManualLayout::processMouseSelectAnnotation(const MouseEvent& mouseEvent, + const bool shiftKeyDownFlag, + const bool singleSelectionModeFlag) +{ + /* + * Do normal selection processing + */ + UserInputModeAnnotations::processMouseSelectAnnotation(mouseEvent, + shiftKeyDownFlag, + singleSelectionModeFlag); + + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); + if (selectedAnnotations.size() == 1) { + CaretAssertVectorIndex(selectedAnnotations, 0); + AnnotationBrowserTab* tabAnn = dynamic_cast(selectedAnnotations[0]); + if (tabAnn != NULL) { + const int32_t browserTabIndex = tabAnn->getTabIndex(); + + EventBrowserTabSelectInWindow selectTabEvent(browserTabIndex); + EventManager::get()->sendEvent(selectTabEvent.getPointer()); + } + } +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeTileTabsManualLayout.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeTileTabsManualLayout.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeTileTabsManualLayout.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeTileTabsManualLayout.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,68 @@ +#ifndef __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_H__ +#define __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "UserInputModeAnnotations.h" + + + +namespace caret { + + class UserInputModeTileTabsManualLayout : public UserInputModeAnnotations { + + public: + UserInputModeTileTabsManualLayout(const int32_t windowIndex); + + virtual ~UserInputModeTileTabsManualLayout(); + + UserInputModeTileTabsManualLayout(const UserInputModeTileTabsManualLayout&) = delete; + + UserInputModeTileTabsManualLayout& operator=(const UserInputModeTileTabsManualLayout&) = delete; + + virtual void showContextMenu(const MouseEvent& mouseEvent, + const QPoint& menuPosition, + BrainOpenGLWidget* openGLWidget) override; + + + // ADD_NEW_METHODS_HERE + + protected: + virtual void processMouseSelectAnnotation(const MouseEvent& mouseEvent, + const bool shiftKeyDownFlag, + const bool singleSelectionModeFlag); + + private: + virtual void deleteSelectedAnnotations() override; + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_DECLARE__ + // +#endif // __USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_DECLARE__ + +} // namespace +#endif //__USER_INPUT_MODE_TILE_TABS_MANUAL_LAYOUT_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeViewContextMenu.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeViewContextMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeViewContextMenu.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeViewContextMenu.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -36,9 +36,11 @@ #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretLogger.h" +#include "CaretPreferences.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "ChartingDataManager.h" #include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlay.h" #include "ChartTwoOverlaySet.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiConnectivityMatrixDataFileManager.h" @@ -49,6 +51,7 @@ #include "EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" +#include "EventImageCapture.h" #include "EventUpdateInformationWindows.h" #include "EventUserInterfaceUpdate.h" #include "FociPropertiesEditorDialog.h" @@ -64,6 +67,7 @@ #include "OverlaySet.h" #include "MetricDynamicConnectivityFile.h" #include "Model.h" +#include "ModelChartTwo.h" #include "ProgressReportingDialog.h" #include "SelectionItemBorderSurface.h" #include "SelectionItemChartTwoLabel.h" @@ -76,7 +80,7 @@ #include "SessionManager.h" #include "Surface.h" #include "UserInputModeFociWidget.h" -#include "UserInputTileTabsContextMenu.h" +#include "UserInputModeViewContextTileTabsSubMenu.h" #include "VolumeDynamicConnectivityFile.h" #include "VolumeFile.h" #include "WuQDataEntryDialog.h" @@ -97,6 +101,8 @@ /** * Constructor. * + * @param mouseEvent + * The mouse event * @param viewportContent * Content of the viewport. * @param selectionManager @@ -104,7 +110,8 @@ * @param parentOpenGLWidget * Parent OpenGL Widget on which the menu is displayed. */ -UserInputModeViewContextMenu::UserInputModeViewContextMenu(BrainOpenGLViewportContent* viewportContent, +UserInputModeViewContextMenu::UserInputModeViewContextMenu(const MouseEvent& mouseEvent, + BrainOpenGLViewportContent* viewportContent, SelectionManager* selectionManager, BrainOpenGLWidget* parentOpenGLWidget) : QMenu(parentOpenGLWidget) @@ -116,11 +123,11 @@ this->browserTabContent = viewportContent->getBrowserTabContent(); CaretAssert(this->browserTabContent); - UserInputTileTabsContextMenu* tabMenu = new UserInputTileTabsContextMenu(this->parentOpenGLWidget, - this->viewportContent); + UserInputModeViewContextTileTabsSubMenu* tabMenu = new UserInputModeViewContextTileTabsSubMenu(mouseEvent, + this->parentOpenGLWidget, + this->viewportContent); if (tabMenu->isValid()) { - addSubMenuToMenu(tabMenu, - true); + addMenu(tabMenu); } else { delete tabMenu; @@ -129,71 +136,51 @@ /* * Add the identification actions. */ - addIdentificationActions(); + QMenu* identifyMenu = createIdentifyMenu(); + if (identifyMenu != NULL) { + addMenu(identifyMenu); + } /* * Add the border options. */ - addBorderRegionOfInterestActions(); + QMenu* borderMenu = createBorderRegionOfInterestMenu(); + if (borderMenu != NULL) { + addMenu(borderMenu); + } /* * Add the chart actions */ - addChartActions(); + QMenu* chartMenu = createChartMenu(); + if (chartMenu != NULL) { + addMenu(chartMenu); + } /* * Add the foci actions. */ - addFociActions(); + QMenu* fociMenu = createFociMenu(); + if (fociMenu != NULL) { + addMenu(fociMenu); + } /* * Show Label ROI operations only for surfaces */ - addLabelRegionOfInterestActions(); - - const SelectionItemSurfaceNodeIdentificationSymbol* idSymbol = selectionManager->getSurfaceNodeIdentificationSymbol(); - - bool showRemoveVertexSymbolsFlag = false; - if (this->browserTabContent != NULL) { - switch (this->browserTabContent->getSelectedModelType()) { - case ModelTypeEnum::MODEL_TYPE_CHART: - break; - case ModelTypeEnum::MODEL_TYPE_CHART_TWO: - break; - case ModelTypeEnum::MODEL_TYPE_INVALID: - break; - case ModelTypeEnum::MODEL_TYPE_SURFACE: - showRemoveVertexSymbolsFlag = true; - break; - case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: - showRemoveVertexSymbolsFlag = true; - break; - case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: - showRemoveVertexSymbolsFlag = true; - break; - case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: - showRemoveVertexSymbolsFlag = true; - break; - } + QMenu* labelMenu = createLabelRegionOfInterestMenu(); + if (labelMenu != NULL) { + addMenu(labelMenu); } - if (showRemoveVertexSymbolsFlag) { + + if (SessionManager::get()->getCaretPreferences()->isDevelopMenuEnabled()) { if (this->actions().count() > 0) { this->addSeparator(); } - this->addAction("Remove All Vertex Identification Symbols", - this, - SLOT(removeAllNodeIdentificationSymbolsSelected())); - } - - if (idSymbol->isValid()) { - const AString text = ("Remove Identification of Vertices " - + AString::number(idSymbol->getNodeNumber())); - - this->addAction(WuQtUtilities::createAction(text, - "", - this, - this, - SLOT(removeNodeIdentificationSymbolSelected()))); + + QAction* rgbaPixelAction = this->addAction("Show Pixel RGBA..."); + QObject::connect(rgbaPixelAction, &QAction::triggered, + this, &UserInputModeViewContextMenu::showFrameBufferPixelRgbaSelected); } } @@ -259,10 +246,10 @@ } /** - * Add the identification actions to the menu. + * @return The identification menu */ -void -UserInputModeViewContextMenu::addIdentificationActions() +QMenu* +UserInputModeViewContextMenu::createIdentifyMenu() { /* * Accumlate identification actions @@ -351,15 +338,70 @@ SLOT(identifyVolumeFocusSelected()))); } - addActionsToMenu(identificationActions, - true); + const SelectionItemSurfaceNodeIdentificationSymbol* idSymbol = this->selectionManager->getSurfaceNodeIdentificationSymbol(); + + bool showRemoveVertexSymbolsFlag = false; + if (this->browserTabContent != NULL) { + switch (this->browserTabContent->getSelectedModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + showRemoveVertexSymbolsFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + showRemoveVertexSymbolsFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + showRemoveVertexSymbolsFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + showRemoveVertexSymbolsFlag = true; + break; + } + } + if (showRemoveVertexSymbolsFlag) { + identificationActions.push_back(WuQtUtilities::createAction("Remove All Vertex Identification Symbols", + "", + this, + this, + SLOT(removeAllNodeIdentificationSymbolsSelected()))); + } + + if (idSymbol->isValid()) { + const AString text = ("Remove Identification of Vertices " + + AString::number(idSymbol->getNodeNumber())); + + identificationActions.push_back(WuQtUtilities::createAction(text, + "", + this, + this, + SLOT(removeNodeIdentificationSymbolSelected()))); + } + + QMenu* menu(NULL); + + if ( ! identificationActions.isEmpty()) { + menu = new QMenu("Identify"); + + for (auto action : identificationActions) { + menu->addAction(action); + } + } + + return menu; } /** - * Add the border region of interest actions to the menu. + * @return the border menu. */ -void -UserInputModeViewContextMenu::addBorderRegionOfInterestActions() +QMenu* +UserInputModeViewContextMenu::createBorderRegionOfInterestMenu() { SelectionItemBorderSurface* borderID = this->selectionManager->getSurfaceBorderIdentification(); @@ -408,8 +450,19 @@ } } + QMenu* menu(NULL); + + if ( ! borderActions.isEmpty()) { + menu = new QMenu("Borders"); + + for (auto action : borderActions) { + menu->addAction(action); + } + } addActionsToMenu(borderActions, true); + + return menu; } /** @@ -458,7 +511,7 @@ if (idNode->isValid() && idVoxel->isValid()) { - std::cout << "Have both surface and volume ID" << std::endl; + /* std::cout << "Have both surface and volume ID" << std::endl; */ } /* @@ -668,10 +721,10 @@ } /** - * Add all label region of interest options to the menu + * @return The label region of interest menu */ -void -UserInputModeViewContextMenu::addLabelRegionOfInterestActions() +QMenu* +UserInputModeViewContextMenu::createLabelRegionOfInterestMenu() { createParcelConnectivities(); @@ -785,7 +838,7 @@ const AString actionName("Show Connectivity for " + sourceLabelName); QAction* action = connectivityActionGroup->addAction(actionName); - action->setData(qVariantFromValue((void*)parcelConnectivity)); + action->setData(QVariant::fromValue((void*)parcelConnectivity)); connectivityActions.push_back(action); } } @@ -794,7 +847,7 @@ const AString fiberTrajActionName("Show Average Fiber Trajectory for " + sourceLabelName); QAction* fiberTrajAction = ciftiFiberTrajectoryActionGroup->addAction(fiberTrajActionName); - fiberTrajAction->setData(qVariantFromValue((void*)parcelConnectivity)); + fiberTrajAction->setData(QVariant::fromValue((void*)parcelConnectivity)); ciftiFiberTrajectoryActions.push_back(fiberTrajAction); } @@ -828,25 +881,36 @@ const AString tsActionName("Show Data/Time Series Graph For " + sourceLabelName); QAction* tsAction = chartableDataActionGroup->addAction(tsActionName); - tsAction->setData(qVariantFromValue((void*)parcelConnectivity)); + tsAction->setData(QVariant::fromValue((void*)parcelConnectivity)); chartableDataActions.push_back(tsAction); } } } - addActionsToMenu(connectivityActions, - true); - addActionsToMenu(ciftiFiberTrajectoryActions, - true); - addActionsToMenu(chartableDataActions, - true); + std::vector allActions; + allActions.insert(allActions.end(), connectivityActions.begin(), connectivityActions.end()); + allActions.insert(allActions.end(), ciftiFiberTrajectoryActions.begin(), ciftiFiberTrajectoryActions.end()); + allActions.insert(allActions.end(), chartableDataActions.begin(), chartableDataActions.end()); + + QMenu* menu(NULL); + + if ( ! allActions.empty()) { + menu = new QMenu("Label ROI"); + + for (auto action : allActions) { + menu->addAction(action); + } + } + + return menu; } /** - * Add chart options to the menu + * Add chart options to the menu. + * @return The Chart menu */ -void -UserInputModeViewContextMenu::addChartActions() +QMenu* +UserInputModeViewContextMenu::createChartMenu() { QList chartActions; @@ -859,9 +923,59 @@ SLOT(editChartLabelSelected()))); } - addActionsToMenu(chartActions, true); + if (this->browserTabContent != NULL) { + const ModelChartTwo* chartTwoModel = this->browserTabContent->getDisplayedChartTwoModel(); + if (chartTwoModel != NULL) { + const int32_t tabIndex = this->browserTabContent->getTabNumber(); + switch (chartTwoModel->getSelectedChartTwoDataType(tabIndex)) { + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_INVALID: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_HISTOGRAM: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_LAYER: + { + QList layerActions = getChartTwoLineLayerMenuActions(); + chartActions.append(layerActions); + } + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_LINE_SERIES: + break; + case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + break; + } + } + } + + QMenu* menu(NULL); + + if ( ! chartActions.isEmpty()) { + menu = new QMenu("Chart"); + + for (auto action : chartActions) { + menu->addAction(action); + } + } + + return menu; +} + +/** + * @return Menu actions for chart two line layer + */ +QList +UserInputModeViewContextMenu::getChartTwoLineLayerMenuActions() +{ + QList actions; + + const ChartTwoOverlaySet* overlaySet = this->browserTabContent->getChartTwoOverlaySet(); + const int32_t numValidOverlays = overlaySet->getNumberOfDisplayedOverlays(); + if (numValidOverlays > 0) { + } + + return actions; } + /** * Called to edit the chart label. */ @@ -894,12 +1008,12 @@ } /** - * Add the foci options to the menu. + * @return The foci menu */ -void -UserInputModeViewContextMenu::addFociActions() +QMenu* +UserInputModeViewContextMenu::createFociMenu() { - QList fociCreateActions; + QList fociActions; const SelectionItemSurfaceNodeIdentificationSymbol* idSymbol = selectionManager->getSurfaceNodeIdentificationSymbol(); SelectionItemFocusSurface* focusID = this->selectionManager->getSurfaceFocusIdentification(); @@ -914,14 +1028,11 @@ if (surfaceID->isValid() && (focusID->isValid() == false)) { const int32_t nodeIndex = surfaceID->getNodeNumber(); - const Surface* surface = surfaceID->getSurface(); const QString text = ("Create Focus at Vertex " + QString::number(nodeIndex) - + " (" - + AString::fromNumbers(surface->getCoordinate(nodeIndex), 3, ",") - + ")..."); + + "..."); - fociCreateActions.push_back(WuQtUtilities::createAction(text, + fociActions.push_back(WuQtUtilities::createAction(text, "", this, this, @@ -930,14 +1041,11 @@ else if (idSymbol->isValid() && (focusID->isValid() == false)) { const int32_t nodeIndex = idSymbol->getNodeNumber(); - const Surface* surface = idSymbol->getSurface(); const QString text = ("Create Focus at Selected Vertex " + QString::number(nodeIndex) - + " (" - + AString::fromNumbers(surface->getCoordinate(nodeIndex), 3, ",") - + ")..."); + + "..."); - fociCreateActions.push_back(WuQtUtilities::createAction(text, + fociActions.push_back(WuQtUtilities::createAction(text, "", this, this, @@ -960,21 +1068,13 @@ + ") XYZ (" + AString::fromNumbers(xyz, 3, ",") + ")..."); - fociCreateActions.push_back(WuQtUtilities::createAction(text, + fociActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(createVolumeFocusSelected()))); } - addActionsToMenu(fociCreateActions, - true); - - /* - * Actions for editing - */ - QList fociEditActions; - /* * Edit Surface Focus */ @@ -982,7 +1082,7 @@ const QString text = ("Edit Surface Focus (" + focusID->getFocus()->getName() + ")"); - fociEditActions.push_back(WuQtUtilities::createAction(text, + fociActions.push_back(WuQtUtilities::createAction(text, "", this, this, @@ -996,15 +1096,24 @@ const QString text = ("Edit Volume Focus (" + focusVolID->getFocus()->getName() + ")"); - fociEditActions.push_back(WuQtUtilities::createAction(text, + fociActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(editVolumeFocusSelected()))); } - - addActionsToMenu(fociEditActions, - true); + + QMenu* menu(NULL); + + if ( ! fociActions.isEmpty()) { + menu = new QMenu("Foci"); + + for (auto action : fociActions) { + menu->addAction(action); + } + } + + return menu; } /** @@ -1454,10 +1563,15 @@ SelectionItemBorderSurface* borderID = this->selectionManager->getSurfaceBorderIdentification(); Brain* brain = borderID->getBrain(); this->selectionManager->clearOtherSelectedItems(borderID); - const AString idMessage = this->selectionManager->getIdentificationText(brain); + int32_t tabIndex = -1; + if (this->browserTabContent != NULL) { + tabIndex = this->browserTabContent->getTabNumber(); + } IdentificationManager* idManager = brain->getIdentificationManager(); - idManager->addIdentifiedItem(new IdentifiedItem(idMessage)); + idManager->addIdentifiedItem(new IdentifiedItem(this->selectionManager->getSimpleIdentificationText(brain), + this->selectionManager->getFormattedIdentificationText(brain, + tabIndex))); EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } @@ -1468,7 +1582,12 @@ UserInputModeViewContextMenu::createSurfaceFocusSelected() { SelectionItemSurfaceNode* surfaceID = this->selectionManager->getSurfaceNodeIdentification(); - const Surface* surface = surfaceID->getSurface(); + const Surface* focusSurface = surfaceID->getSurface(); + const StructureEnum::Enum structure = focusSurface->getStructure(); + const Surface* anatSurface = GuiManager::get()->getBrain()->getPrimaryAnatomicalSurfaceForStructure(structure); + const Surface* surface((anatSurface != NULL) + ? anatSurface + : focusSurface); const int32_t nodeIndex = surfaceID->getNodeNumber(); const float* xyz = surface->getCoordinate(nodeIndex); @@ -1556,10 +1675,15 @@ SelectionItemFocusSurface* focusID = this->selectionManager->getSurfaceFocusIdentification(); Brain* brain = focusID->getBrain(); this->selectionManager->clearOtherSelectedItems(focusID); - const AString idMessage = this->selectionManager->getIdentificationText(brain); + int32_t tabIndex = -1; + if (this->browserTabContent != NULL) { + tabIndex = this->browserTabContent->getTabNumber(); + } IdentificationManager* idManager = brain->getIdentificationManager(); - idManager->addIdentifiedItem(new IdentifiedItem(idMessage)); + idManager->addIdentifiedItem(new IdentifiedItem(this->selectionManager->getSimpleIdentificationText(brain), + this->selectionManager->getFormattedIdentificationText(brain, + tabIndex))); EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } @@ -1572,10 +1696,15 @@ SelectionItemFocusVolume* focusID = this->selectionManager->getVolumeFocusIdentification(); Brain* brain = focusID->getBrain(); this->selectionManager->clearOtherSelectedItems(focusID); - const AString idMessage = this->selectionManager->getIdentificationText(brain); + int32_t tabIndex = -1; + if (this->browserTabContent != NULL) { + tabIndex = this->browserTabContent->getTabNumber(); + } IdentificationManager* idManager = brain->getIdentificationManager(); - idManager->addIdentifiedItem(new IdentifiedItem(idMessage)); + idManager->addIdentifiedItem(new IdentifiedItem(this->selectionManager->getSimpleIdentificationText(brain), + this->selectionManager->getFormattedIdentificationText(brain, + tabIndex))); EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } @@ -1629,10 +1758,15 @@ SelectionItemVoxel* voxelID = this->selectionManager->getVoxelIdentification(); Brain* brain = voxelID->getBrain(); this->selectionManager->clearOtherSelectedItems(voxelID); - const AString idMessage = this->selectionManager->getIdentificationText(brain); + int32_t tabIndex = -1; + if (this->browserTabContent != NULL) { + tabIndex = this->browserTabContent->getTabNumber(); + } IdentificationManager* idManager = brain->getIdentificationManager(); - idManager->addIdentifiedItem(new IdentifiedItem(idMessage)); + idManager->addIdentifiedItem(new IdentifiedItem(this->selectionManager->getSimpleIdentificationText(brain), + this->selectionManager->getFormattedIdentificationText(brain, + tabIndex))); EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } @@ -1690,6 +1824,119 @@ return result; } +/** + * Called to display RGBA for pixel under mouse + */ +void +UserInputModeViewContextMenu::showFrameBufferPixelRgbaSelected() +{ + CaretAssert(this->parentOpenGLWidget); + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + QImage image(this->parentOpenGLWidget->grabFramebuffer()); +#else + QImage image(this->parentOpenGLWidget->grabFrameBuffer()); +#endif + + const QPoint mouseXY(this->parentOpenGLWidget->mapFromGlobal(pos())); + const int32_t x(mouseXY.x()); + const int32_t y(mouseXY.y()); + + if ( ! image.isNull()) { + if ((x >= 0) + && (x < image.width()) + && (y >= 0) + && (y < image.height())) { + QColor color = image.pixelColor(x, y); + double red(color.redF()); + double green(color.greenF()); + double blue(color.blueF()); + double alpha(color.alphaF()); + + int redInt(color.red()); + int greenInt(color.green()); + int blueInt(color.blue()); + int alphaInt(color.alpha()); + + QString formatName(QString::number(image.format())); + switch (image.format()) { + case QImage::Format_ARGB32_Premultiplied: + formatName = "Format_ARGB32_Premultiplied"; + break; + case QImage::Format_ARGB32: + formatName = "Format_ARGB32"; + break; + default: + break; + } + + const int intWidth(5); + const int floatWidth(4); + const int precision(2); + const char format('f'); + AString txt(""); + txt.appendWithNewLine("QImage::Format: " + formatName); + txt.appendWithNewLine(QString("Pixel XY (origin top left): (%1, %2)
").arg(x).arg(y)); + txt.appendWithNewLine(QString("Red: %1 %2
").arg(redInt, intWidth).arg(red, floatWidth, format, precision)); + txt.appendWithNewLine(QString("Green: %1 %2
").arg(greenInt, intWidth).arg(green, floatWidth, format, precision)); + txt.appendWithNewLine(QString("Blue: %1 %2
").arg(blueInt, intWidth).arg(blue, floatWidth, format, precision)); + if (image.hasAlphaChannel()) { + txt.appendWithNewLine(QString("Alpha: %1 %2
").arg(alphaInt, intWidth).arg(alpha, floatWidth, format, precision)); + } + else { + txt.appendWithNewLine("Alpha: no alpha channel in image.
"); + } + + if (image.format() == QImage::Format_ARGB32_Premultiplied) { + red *= alpha; + green *= alpha; + blue *= alpha; + + redInt = static_cast(red * 255.0); + greenInt = static_cast(green * 255.0); + blueInt = static_cast(blue * 255.0); + alphaInt = static_cast(alpha * 255.0); + + txt.appendWithNewLine("

"); + txt.appendWithNewLine("After removal of alpha pre-multiplication:"); + txt.appendWithNewLine(QString("Red: %1 %2
").arg(redInt, intWidth).arg(red, floatWidth, format, precision)); + txt.appendWithNewLine(QString("Green: %1 %2
").arg(greenInt, intWidth).arg(green, floatWidth, format, precision)); + txt.appendWithNewLine(QString("Blue: %1 %2
").arg(blueInt, intWidth).arg(blue, floatWidth, format, precision)); + if (image.hasAlphaChannel()) { + txt.appendWithNewLine(QString("Alpha: %1 %2
").arg(alphaInt, intWidth).arg(alpha, floatWidth, format, precision)); + } + } + + const bool debugFlag(false); + if (debugFlag) { + for (int32_t i = 0; i < 30; i++) { + QImage imageCopy = image.convertToFormat((QImage::Format)i); + QColor color = imageCopy.pixelColor(x, y); + AString txt; + txt.appendWithNewLine("

"); + txt.appendWithNewLine("QImage::Format: " + AString::number(imageCopy.format())); + txt.appendWithNewLine(QString("Pixel XY (origin top left): (%1, %2)
").arg(x).arg(y)); + txt.appendWithNewLine(QString("Red: %1 %2
").arg((int)color.red(), intWidth).arg((double)color.redF(), floatWidth, format, precision)); + txt.appendWithNewLine(QString("Green: %1 %2
").arg((int)color.green(), intWidth).arg((double)color.greenF(), floatWidth, format, precision)); + txt.appendWithNewLine(QString("Blue: %1 %2
").arg((int)color.blue(), intWidth).arg((double)color.blueF(), floatWidth, format, precision)); + if (image.hasAlphaChannel()) { + txt.appendWithNewLine(QString("Alpha: %1 %2
").arg((int)color.alpha(), intWidth).arg((double)color.alphaF(), floatWidth, format, precision)); + } + else { + txt.appendWithNewLine("Alpha: no alpha channel in image.
"); + } + std::cout << txt << std::endl << std::endl; + } + } + + txt.appendWithNewLine(""); + + WuQMessageBox::informationOk(this->parentOpenGLWidget, + txt.replace(" ", " ")); + } + } +} + /* ------------------------------------------------------------------------- */ /** diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeViewContextMenu.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeViewContextMenu.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeViewContextMenu.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeViewContextMenu.h 2021-02-16 19:46:47.000000000 +0000 @@ -42,6 +42,8 @@ class CiftiConnectivityMatrixDataFileManager; class CiftiFiberTrajectoryManager; class ChartingDataManager; + class ModelChartTwo; + class MouseEvent; class SelectionManager; class LabelFile; class Surface; @@ -51,7 +53,8 @@ Q_OBJECT public: - UserInputModeViewContextMenu(BrainOpenGLViewportContent* viewportContent, + UserInputModeViewContextMenu(const MouseEvent& mouseEvent, + BrainOpenGLViewportContent* viewportContent, SelectionManager* selectionManager, BrainOpenGLWidget* parentOpenGLWidget); @@ -94,6 +97,8 @@ void editChartLabelSelected(); + void showFrameBufferPixelRgbaSelected(); + private: enum class ParcelType { PARCEL_TYPE_INVALID, @@ -146,15 +151,17 @@ bool enableDataSeriesGraphsIfNoneEnabled(); - void addIdentificationActions(); + QMenu* createIdentifyMenu(); + + QMenu* createBorderRegionOfInterestMenu(); - void addBorderRegionOfInterestActions(); + QMenu* createChartMenu(); - void addChartActions(); + QMenu* createFociMenu(); - void addFociActions(); + QMenu* createLabelRegionOfInterestMenu(); - void addLabelRegionOfInterestActions(); + QList getChartTwoLineLayerMenuActions(); void addSubMenuToMenu(QMenu* menu, const bool addSeparatorBeforeMenu); diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,190 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __USER_INPUT_MODE_VIEW_CONTEXT_TILE_TABS_SUB_MENU_DECLARE__ +#include "UserInputModeViewContextTileTabsSubMenu.h" +#undef __USER_INPUT_MODE_VIEW_CONTEXT_TILE_TABS_SUB_MENU_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrainOpenGLViewportContent.h" +#include "BrowserTabContent.h" +#include "BrowserWindowContent.h" +#include "CaretAssert.h" +#include "EventBrowserTabGet.h" +#include "EventBrowserWindowTileTabOperation.h" +#include "EventManager.h" +#include "GuiManager.h" +#include "MouseEvent.h" + +using namespace caret; + + + +/** + * \class caret::UserInputModeViewContextTileTabsSubMenu + * \brief Menu for tile tabs operations in a window context menu + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param mouseEvent + * The mouse event that caused menu display + * @param parentWidget + * Parent widget on which error message dialogs are displayed + * @param viewportContent + * Content of the viewport under the mouse. + */ +UserInputModeViewContextTileTabsSubMenu::UserInputModeViewContextTileTabsSubMenu(const MouseEvent& mouseEvent, + QWidget* parentWidget, + BrainOpenGLViewportContent* viewportContent) +: QMenu("Tabs"), +m_parentWidget(parentWidget), +m_windowIndex(viewportContent->getWindowIndex()), +m_tabIndex(viewportContent->getTabIndex()) +{ + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_windowIndex); + CaretAssert(bbw); + CaretAssert(m_windowIndex >= 0); + CaretAssert(m_tabIndex >= 0); + + int32_t tabViewport[4]; + viewportContent->getTabViewportBeforeApplyingMargins(tabViewport); + + viewportContent->getWindowViewport(m_windowViewport); + + m_mouseWindowX = mouseEvent.getX(); + m_mouseWindowY = mouseEvent.getY(); + + EventBrowserTabGet tabContentEvent(m_tabIndex); + EventManager::get()->sendEvent(tabContentEvent.getPointer()); + BrowserTabContent* tabContent = tabContentEvent.getBrowserTab(); + + if (bbw->isTileTabsSelected()) { + switch (bbw->getBrowerWindowContent()->getTileTabsConfigurationMode()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + if (tabContent != NULL) { + addItemToMenu("Insert New Tab Before This Tab", + EventBrowserWindowTileTabOperation::OPERATION_GRID_NEW_TAB_BEFORE); + addItemToMenu("Insert New Tab After This Tab", + EventBrowserWindowTileTabOperation::OPERATION_GRID_NEW_TAB_AFTER); + } + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + const bool showManualTabLayoutOptionsFlag(false); + if (showManualTabLayoutOptionsFlag) { + addItemToMenu("Insert Tab at Mouse", + EventBrowserWindowTileTabOperation::OPERATION_MANUAL_NEW_TAB); + + if (tabContent != NULL) { + if (actions().count() > 0) { + addSeparator(); + } + addItemToMenu("Bring to Front", + EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_TO_FRONT); + addItemToMenu("Bring Forward", + EventBrowserWindowTileTabOperation::OPERATION_ORDER_BRING_FORWARD); + addItemToMenu("Send to Back", + EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_TO_BACK); + addItemToMenu("Send Backward", + EventBrowserWindowTileTabOperation::OPERATION_ORDER_SEND_BACKWARD); + } + } + break; + } + + if (tabContent != NULL) { + if (actions().count() > 0) { + addSeparator(); + } + addItemToMenu("Select This Tab", + EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB); + } + } + + QObject::connect(this, &QMenu::triggered, + this, &UserInputModeViewContextTileTabsSubMenu::actionTriggered); +} + +/** + * Destructor. + */ +UserInputModeViewContextTileTabsSubMenu::~UserInputModeViewContextTileTabsSubMenu() +{ +} + +/** + * @return Is this menu valid? + */ +bool +UserInputModeViewContextTileTabsSubMenu::isValid() const +{ + return (actions().size() > 0); +} + +/** + * Add an operation to the menu with the given text + * + * @param text + * Text for menu + * @param operation + * The operation + */ +void +UserInputModeViewContextTileTabsSubMenu::addItemToMenu(const QString& text, + const EventBrowserWindowTileTabOperation::Operation operation) +{ + QAction* action = addAction(text); + action->setData(static_cast(operation)); +} + + + +/** + * Gets called when an action is selected from the menu. + * + * @param action + * Action selected by user. + */ +void +UserInputModeViewContextTileTabsSubMenu::actionTriggered(QAction* action) +{ + if (action == NULL) { + return; + } + + const int operationInt = action->data().toInt(); + const EventBrowserWindowTileTabOperation::Operation operation = static_cast(operationInt); + std::vector emptyBrowserTabs; + EventBrowserWindowTileTabOperation tileTabOperation(operation, + m_parentWidget, + m_windowIndex, + m_tabIndex, + m_windowViewport, + m_mouseWindowX, + m_mouseWindowY, + emptyBrowserTabs); + EventManager::get()->sendEvent(tileTabOperation.getPointer()); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,102 @@ +#ifndef __USER_INPUT_MODE_VIEW_CONTEXT_TILE_TABS_SUB_MENU_H__ +#define __USER_INPUT_MODE_VIEW_CONTEXT_TILE_TABS_SUB_MENU_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "EventBrowserWindowTileTabOperation.h" + +class QWidget; + +namespace caret { + + class BrainOpenGLViewportContent; + class MouseEvent; + + class UserInputModeViewContextTileTabsSubMenu : public QMenu { + + Q_OBJECT + + public: + UserInputModeViewContextTileTabsSubMenu(const MouseEvent& mouseEvent, + QWidget* parentWidget, + BrainOpenGLViewportContent* viewportContent); + + virtual ~UserInputModeViewContextTileTabsSubMenu(); + + bool isValid() const; + + // ADD_NEW_METHODS_HERE + + private slots: + void actionTriggered(QAction* action); + + private: + UserInputModeViewContextTileTabsSubMenu(const UserInputModeViewContextTileTabsSubMenu&); + + UserInputModeViewContextTileTabsSubMenu& operator=(const UserInputModeViewContextTileTabsSubMenu&); + + void addItemToMenu(const QString& text, + const EventBrowserWindowTileTabOperation::Operation operation); + + QWidget* m_parentWidget; + + const int32_t m_windowIndex; + + const int32_t m_tabIndex; + + int32_t m_windowViewport[4]; + + int32_t m_mouseWindowX = 0; + + int32_t m_mouseWindowY = 0; + + QAction* m_createNewGridTabBeforeAction = nullptr; + + QAction* m_createNewGridTabAfterAction = nullptr; + + QAction* m_createNewManualTabAction = nullptr; + + QAction* m_orderBringToFrontAction = nullptr; + + QAction* m_orderBringForwardAction = nullptr; + + QAction* m_orderSendToBackAction = nullptr; + + QAction* m_orderSendBackwardAction = nullptr; + + QAction* m_selectTabAction = nullptr; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __USER_INPUT_MODE_VIEW_CONTEXT_TILE_TABS_SUB_MENU_DECLARE__ + // +#endif // __USER_INPUT_MODE_VIEW_CONTEXT_TILE_TABS_SUB_MENU_DECLARE__ + +} // namespace +#endif //__USER_INPUT_MODE_VIEW_CONTEXT_TILE_TABS_SUB_MENU_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeView.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeView.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeView.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeView.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -30,16 +30,21 @@ #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" +#include "CaretLogger.h" #include "ChartTwoCartesianAxis.h" +#include "ChartTwoOverlay.h" #include "ChartTwoOverlaySet.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUpdateYokedWindows.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" +#include "GestureEvent.h" #include "GuiManager.h" +#include "KeyEvent.h" #include "MouseEvent.h" #include "SelectionItemChartTwoLabel.h" +#include "SelectionItemChartTwoLineLayerVerticalNearest.h" #include "SelectionManager.h" #include "UserInputModeViewContextMenu.h" #include "WuQDataEntryDialog.h" @@ -59,9 +64,12 @@ /** * Constructor. + * @param windowIndex + * Index of the window */ -UserInputModeView::UserInputModeView() -: UserInputModeAbstract(UserInputModeEnum::VIEW) +UserInputModeView::UserInputModeView(const int32_t windowIndex) +: UserInputModeAbstract(UserInputModeEnum::Enum::VIEW), +m_browserWindowIndex(windowIndex) { } @@ -69,11 +77,15 @@ /** * Constructor for subclasses. * + * @param windowIndex + * Index of the window * @param inputMode * Subclass' input mode. */ -UserInputModeView::UserInputModeView(const UserInputModeEnum::Enum inputMode) -: UserInputModeAbstract(inputMode) +UserInputModeView::UserInputModeView(const int32_t windowIndex, + const UserInputModeEnum::Enum inputMode) +: UserInputModeAbstract(inputMode), +m_browserWindowIndex(windowIndex) { } @@ -239,6 +251,24 @@ mouseEvent.getOpenGLWidget(), mouseEvent.getX(), mouseEvent.getY()); + + SelectionManager* selectionManager = GuiManager::get()->getBrain()->getSelectionManager(); + + SelectionItemChartTwoLineLayerVerticalNearest* layerSelection = selectionManager->getChartTwoLineLayerVerticalNearestIdentification(); + CaretAssert(layerSelection); + + if (layerSelection->isValid()) { + processChartActiveLayerAction(ChartActiveLayerMode::SELECT, + layerSelection->getChartTwoOverlay(), + layerSelection->getLineSegmentIndex()); + } + else if (layerSelection->isOutsideChartBounds()) { + ChartTwoOverlay* invalidChartOverlay(NULL); + int32_t invalidLineSegmentIndex(-1); + processChartActiveLayerAction(ChartActiveLayerMode::DESELECT_ALL, + invalidChartOverlay, + invalidLineSegmentIndex); + } } /** @@ -306,6 +336,24 @@ mouseEvent.getDy()); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR); } + else if (browserTabContent->isChartTwoDisplayed()) { + const int32_t x1(mouseEvent.getPressedX()); + const int32_t y1(mouseEvent.getPressedY()); + const int32_t x2(mouseEvent.getX()); + const int32_t y2(mouseEvent.getY()); + + Matrix4x4 m1, m2; + int32_t chartViewport[4]; + if (viewportContent->getChartDataMatricesAndViewport(m1, + m2, + chartViewport)) { + browserTabContent->applyChartTwoAxesBoundSelection(chartViewport, + x1, y1, x2, y2); + } + else { + CaretLogSevere("Chart viewport is invalid"); + } + } else { browserTabContent->applyMouseRotation(viewportContent, mouseEvent.getPressedX(), @@ -355,7 +403,15 @@ return; } - browserTabContent->applyMouseScaling(mouseEvent.getDx(), mouseEvent.getDy()); + int32_t modelViewport[4]; + viewportContent->getModelViewport(modelViewport); + browserTabContent->applyMouseScaling(viewportContent, + mouseEvent.getPressedX(), + mouseEvent.getPressedY(), + mouseEvent.getPressedX() - modelViewport[0], + mouseEvent.getPressedY() - modelViewport[1], + mouseEvent.getDx(), + mouseEvent.getDy()); updateGraphics(mouseEvent); } @@ -387,6 +443,106 @@ } /** + * Process a mouse left release event. + * + * @param mouseEvent + * Mouse event information. + */ +void +UserInputModeView::mouseLeftRelease(const MouseEvent& mouseEvent) +{ + BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); + if (viewportContent == NULL) { + return; + } + + BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); + if (browserTabContent == NULL) { + return; + } + + if (browserTabContent->isChartTwoDisplayed()) { + const int32_t x1(mouseEvent.getPressedX()); + const int32_t y1(mouseEvent.getPressedY()); + const int32_t x2(mouseEvent.getX()); + const int32_t y2(mouseEvent.getY()); + + Matrix4x4 m1, m2; + int32_t chartViewport[4]; + if (viewportContent->getChartDataMatricesAndViewport(m1, + m2, + chartViewport)) { + browserTabContent->finalizeChartTwoAxesBoundSelection(chartViewport, + x1, y1, x2, y2); + updateGraphics(viewportContent); + } + else { + CaretLogSevere("Chart viewport is invalid"); + } + } +} + +/** + * Process a mouse left press event. + * + * @param mouseEvent + * Mouse event information. + */ +void +UserInputModeView::mouseLeftPress(const MouseEvent& /*mouseEvent*/) +{ +} + +/** + * Process a gesture event (pinch zoom; or rotate) + * + * @param gestureEvent + * Gesture event information. + */ +void +UserInputModeView::gestureEvent(const GestureEvent& gestureEvent) +{ + BrainOpenGLViewportContent* viewportContent = gestureEvent.getViewportContent(); + if (viewportContent == NULL) { + return; + } + + BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); + if (browserTabContent == NULL) { + return; + } + + switch (gestureEvent.getType()) { + case GestureEvent::Type::PINCH: + { + float deltaY(gestureEvent.getValue()); + if (deltaY > 0.0) { + float scaleFactor(0.0); + if (deltaY > 1.0) { + scaleFactor = 2.0; + } + else if (deltaY < 1.0) { + scaleFactor = -2.0; + } + if (scaleFactor != 0.0) { + browserTabContent->applyMouseScaling(viewportContent, + gestureEvent.getStartCenterX(), + gestureEvent.getStartCenterX(), + gestureEvent.getStartCenterX(), + gestureEvent.getStartCenterY(), + 0.0f, + scaleFactor); + updateGraphics(viewportContent); + } + } + } + break; + case GestureEvent::Type::ROTATE: + break; + } +} + +/** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent @@ -414,7 +570,8 @@ mouseY, false); - UserInputModeViewContextMenu contextMenu(viewportContent, + UserInputModeViewContextMenu contextMenu(mouseEvent, + viewportContent, idManager, openGLWidget); contextMenu.exec(menuPosition); @@ -423,6 +580,51 @@ /** * If this windows is yoked, issue an event to update other * windows that are using the same yoking. + * + * @param viewportContent + * Content of the viewport + */ +void +UserInputModeView::updateGraphics(const BrainOpenGLViewportContent* viewportContent) +{ + bool issuedYokeEvent = false; + if (viewportContent != NULL) { + BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); + const int32_t browserWindowIndex = viewportContent->getWindowIndex(); + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(browserWindowIndex).getPointer()); + + YokingGroupEnum::Enum brainYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + YokingGroupEnum::Enum chartYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; + + if (browserTabContent != NULL) { + if (browserTabContent->isBrainModelYoked()) { + brainYokingGroup = browserTabContent->getBrainModelYokingGroup(); + issuedYokeEvent = true; + } + if (browserTabContent->isChartModelYoked()) { + chartYokingGroup = browserTabContent->getChartModelYokingGroup(); + issuedYokeEvent = true; + } + + if (issuedYokeEvent) { + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUpdateYokedWindows(brainYokingGroup, + chartYokingGroup).getPointer()); + } + } + } + + /* + * If not yoked, just need to update graphics. + */ + if ( ! issuedYokeEvent) { + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(viewportContent->getWindowIndex()).getPointer()); + } +} + +/** + * If this windows is yoked, issue an event to update other + * windows that are using the same yoking. */ void UserInputModeView::updateGraphics(const MouseEvent& mouseEvent) @@ -462,3 +664,118 @@ } } +/** + * Process a key press event + * + * @param keyEvent + * Key event information. + * @return + * True if the input process recognized the key event + * and the key event SHOULD NOT be propagated to parent + * widgets + */ +bool +UserInputModeView::keyPressEvent(const KeyEvent& keyEvent) +{ + bool keyWasProcessedFlag(false); + + bool decrementFlag(false); + bool incrementFlag(false); + const int32_t keyCode = keyEvent.getKeyCode(); + switch (keyCode) { + case Qt::Key_Right: + incrementFlag = true; + break; + case Qt::Key_Left: + decrementFlag = true; + break; + case Qt::Key_Up: + incrementFlag = true; + break; + case Qt::Key_Down: + decrementFlag = true; + break; + } + + if (decrementFlag + || incrementFlag) { + std::array mouseXY; + if (keyEvent.getMouseXY(mouseXY)) { + /* + * Increment/decrement selected point in line layer chart. + * Identification will fail if chart is not visible. + */ + SelectionManager* selectionManager = keyEvent.getOpenGLWidget()->performIdentification(mouseXY[0], + mouseXY[1], + false); + + SelectionItemChartTwoLineLayerVerticalNearest* layerSelection = selectionManager->getChartTwoLineLayerVerticalNearestIdentification(); + CaretAssert(layerSelection); + if (layerSelection->isValid()) { + ChartTwoOverlay* invalidChartOverlay(NULL); + int32_t invalidLineSegmentIndex(-1); + if (incrementFlag) { + processChartActiveLayerAction(ChartActiveLayerMode::INCREMENT, + invalidChartOverlay, + invalidLineSegmentIndex); + } + else if (decrementFlag) { + processChartActiveLayerAction(ChartActiveLayerMode::DECREMENT, + invalidChartOverlay, + invalidLineSegmentIndex); + } + else { + CaretAssertMessage(0, "Invalid increment/decrement"); + } + } + } + } + + return keyWasProcessedFlag; +} + +/** + * Process a chart active layer action + * + * @param chartActiveMode + * The mode + * @param chartOverlay + * The given chart overlay + * @param pointIndex + * Index of point selected + */ +void +UserInputModeView::processChartActiveLayerAction(const ChartActiveLayerMode chartActiveMode, + ChartTwoOverlay* chartOverlay, + const int32_t pointIndex) +{ + ChartTwoOverlaySet* chartOverlaySet = NULL; + BrowserTabContent* browserTabContent = + GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); + if (browserTabContent != NULL) { + chartOverlaySet = browserTabContent->getChartTwoOverlaySet(); + } + + if (chartOverlaySet != NULL) { + switch (chartActiveMode) { + case ChartActiveLayerMode::DECREMENT: + chartOverlaySet->incrementOverlayActiveLineChartPoint(-1); + break; + case ChartActiveLayerMode::DESELECT_ALL: + chartOverlaySet->selectOverlayActiveLineChart(NULL, + -1); + break; + case ChartActiveLayerMode::INCREMENT: + chartOverlaySet->incrementOverlayActiveLineChartPoint(1); + break; + case ChartActiveLayerMode::SELECT: + chartOverlaySet->selectOverlayActiveLineChart(chartOverlay, + pointIndex); + break; + } + } + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().addToolBox().getPointer()); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeView.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeView.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeView.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeView.h 2021-02-16 19:46:47.000000000 +0000 @@ -27,10 +27,13 @@ namespace caret { + class BrainOpenGLViewportContent; + class ChartTwoOverlay; + class UserInputModeView : public UserInputModeAbstract { public: - UserInputModeView(); + UserInputModeView(const int32_t windowIndex); virtual ~UserInputModeView(); @@ -42,6 +45,8 @@ virtual CursorEnum::Enum getCursor() const; + virtual bool keyPressEvent(const KeyEvent& /*keyEvent*/) override; + virtual void mouseLeftDoubleClick(const MouseEvent& mouseEvent); virtual void mouseLeftClick(const MouseEvent& mouseEvent); @@ -58,25 +63,47 @@ virtual void mouseLeftDragWithShift(const MouseEvent& mouseEvent); + virtual void mouseLeftPress(const MouseEvent& mouseEvent); + + virtual void mouseLeftRelease(const MouseEvent& mouseEvent); + + virtual void gestureEvent(const GestureEvent& gestureEvent); + virtual void showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget); protected: - UserInputModeView(const UserInputModeEnum::Enum inputMode); + UserInputModeView(const int32_t windowIndex, + const UserInputModeEnum::Enum inputMode); private: + enum class ChartActiveLayerMode { + DECREMENT, + DESELECT_ALL, + INCREMENT, + SELECT + }; + UserInputModeView(const UserInputModeView&); UserInputModeView& operator=(const UserInputModeView&); void updateGraphics(const MouseEvent& mouseEvent); + void updateGraphics(const BrainOpenGLViewportContent* viewportContent); + void processModelViewIdentification(BrainOpenGLViewportContent* viewportContent, BrainOpenGLWidget* openGLWidget, const int32_t mouseClickX, const int32_t mouseClickY); + void processChartActiveLayerAction(const ChartActiveLayerMode chartActiveMode, + ChartTwoOverlay* chartOverlay, + const int32_t pointIndex); + + const int32_t m_browserWindowIndex; + public: virtual AString toString() const; diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeVolumeEdit.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeVolumeEdit.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeVolumeEdit.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeVolumeEdit.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -62,7 +62,8 @@ * Index of window using this volume editor input handler. */ UserInputModeVolumeEdit::UserInputModeVolumeEdit(const int32_t windowIndex) -: UserInputModeView(UserInputModeEnum::VOLUME_EDIT), +: UserInputModeView(windowIndex, + UserInputModeEnum::Enum::VOLUME_EDIT), m_windowIndex(windowIndex) { m_inputModeVolumeEditWidget = new UserInputModeVolumeEditWidget(this, diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeVolumeEditWidget.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputModeVolumeEditWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeVolumeEditWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeVolumeEditWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -24,15 +24,18 @@ #undef __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_DECLARE__ #include +#include #include #include #include #include +#include #include #include #include #include "Brain.h" +#include "BrainBrowserWindowToolBar.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "EventDataFileAdd.h" @@ -82,12 +85,20 @@ { CaretAssert(inputModeVolumeEdit); - QVBoxLayout* layout = new QVBoxLayout(this); + QHBoxLayout* layout = new QHBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(createSelectionToolBar()); - layout->addWidget(createModeToolBar()); - setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); - + layout->addWidget(BrainBrowserWindowToolBar::createToolWidget("Voxel Operation", + createOperationWidget(), + BrainBrowserWindowToolBar::WIDGET_PLACEMENT_LEFT, + BrainBrowserWindowToolBar::WIDGET_PLACEMENT_TOP, + 0)); + layout->addWidget(BrainBrowserWindowToolBar::createToolWidget("Options", + createSelectionToolBar(), + BrainBrowserWindowToolBar::WIDGET_PLACEMENT_LEFT, + BrainBrowserWindowToolBar::WIDGET_PLACEMENT_TOP, + 0)); + layout->addStretch(); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_UPDATE_VOLUME_EDITING_TOOLBAR); } @@ -132,8 +143,8 @@ if (volumeEditInfo.m_volumeFile != NULL) { if (volumeEditInfo.m_volumeFile->isMappedWithLabelTable()) { - m_voxelLabelValueToolButton->setVisible(true); - m_voxelFloatValueSpinBox->setVisible(false); + m_voxelLabelValueToolButton->setEnabled(true); + m_voxelFloatValueSpinBox->setEnabled(false); AString buttonText = m_voxelLabelValueAction->text(); GiftiLabelTable* labelTable = volumeEditInfo.m_volumeFile->getMapLabelTable(volumeEditInfo.m_mapIndex); @@ -179,8 +190,8 @@ isValid = true; } else if (volumeEditInfo.m_volumeFile->isMappedWithPalette()) { - m_voxelLabelValueToolButton->setVisible(false); - m_voxelFloatValueSpinBox->setVisible(true); + m_voxelLabelValueToolButton->setEnabled(false); + m_voxelFloatValueSpinBox->setEnabled(true); isValid = true; } else if (volumeEditInfo.m_volumeFile->isMappedWithRGBA()) { @@ -192,37 +203,46 @@ m_addMapsToolButton->defaultAction()->setEnabled(isValid); + m_modeOnRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON)); + m_modeOffRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF)); + m_modeDilateRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE)); + m_modeErodeRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE)); + m_modeFillTwoDimRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D)); + m_modeFillThreeDimRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D)); + m_modeRemoveTwoDimRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D)); + m_modeRemoveThreeDimRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D)); + m_modeRetainThreeDimRadioButton->setEnabled(isModeButtonEnabled(VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D)); + } + } + + this->setEnabled(isValid); +} + +/** + * @return True if the button for the given mode is valid + */ +bool +UserInputModeVolumeEditWidget::isModeButtonEnabled(const VolumeEditingModeEnum::Enum mode) const +{ + bool modeEnabledFlag(false); + UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; + if (m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo)) { + m_lockAction->setChecked(volumeEditInfo.m_volumeFileEditorDelegate->isLocked(volumeEditInfo.m_mapIndex)); + + if (volumeEditInfo.m_volumeFile != NULL) { const bool orthogonalFlag = (volumeEditInfo.m_sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL); - - QList modeActions = m_volumeEditModeActionGroup->actions(); - const int32_t numModeActions = modeActions.size(); - for (int32_t i = 0; i < numModeActions; i++) { - QAction* action = modeActions.at(i); - - const int modeInt = action->data().toInt(); - - bool validFlag = false; - VolumeEditingModeEnum::Enum mode = VolumeEditingModeEnum::fromIntegerCode(modeInt, - &validFlag); - CaretAssert(validFlag); - - bool modeEnabledFlag = false; - - if (orthogonalFlag) { - modeEnabledFlag = true; - } - else { - modeEnabledFlag = VolumeEditingModeEnum::isObliqueEditingAllowed(mode); - } - - action->setEnabled(modeEnabledFlag); + if (orthogonalFlag) { + modeEnabledFlag = true; + } + else { + modeEnabledFlag = VolumeEditingModeEnum::isObliqueEditingAllowed(mode); } } } - - this->setEnabled(isValid); + return modeEnabledFlag; } + /** * @return Create and return the selection toolbar. @@ -230,7 +250,7 @@ QWidget* UserInputModeVolumeEditWidget::createSelectionToolBar() { - QLabel* volumeLabel = new QLabel("Volume:"); + QLabel* volumeLabel = new QLabel("File"); m_newFileToolButton = new QToolButton(); m_newFileToolButton->setDefaultAction(WuQtUtilities::createAction("New", @@ -257,56 +277,39 @@ WuQtUtilities::setToolButtonStyleForQt5Mac(lockFileToolButton); - QLabel* editLabel = new QLabel("Edit:"); - - QToolButton* undoToolButton = new QToolButton(); - undoToolButton->setDefaultAction(WuQtUtilities::createAction("Undo", - "Undo the last volume edit", - this, - this, SLOT(undoActionTriggered()))); - WuQtUtilities::setToolButtonStyleForQt5Mac(undoToolButton); - - - QToolButton* redoToolButton = new QToolButton(); - redoToolButton->setDefaultAction(WuQtUtilities::createAction("Redo", - "Redo (or is it undo) the last undo", - this, - this, SLOT(redoActionTriggered()))); - WuQtUtilities::setToolButtonStyleForQt5Mac(redoToolButton); - - - QToolButton* resetToolButton = new QToolButton(); - resetToolButton->setDefaultAction(WuQtUtilities::createAction("Reset", - "Reset all edting of the volume", - this, - this, SLOT(resetActionTriggered()))); - WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); - QLabel* brushSizeLabel = new QLabel("Brush Size:"); + QLabel* brushSizeLabel = new QLabel("Brush"); const int MIN_BRUSH_SIZE = 1; - const int MAX_BRUSH_SIZE = 999; + const int MAX_BRUSH_SIZE = 99; + + QLabel* xLabel = new QLabel("P:"); m_xBrushSizeSpinBox = new WuQSpinBoxOddValue(this); m_xBrushSizeSpinBox->setRange(MIN_BRUSH_SIZE, MAX_BRUSH_SIZE); + m_xBrushSizeSpinBox->setSingleStep(2); QObject::connect(m_xBrushSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(xBrushSizeValueChanged(int))); m_xBrushSizeSpinBox->getWidget()->setToolTip("Parasagittal brush size (voxels).\n" "Must be an odd value."); + QLabel* yLabel = new QLabel("C:"); m_yBrushSizeSpinBox = new WuQSpinBoxOddValue(this); m_yBrushSizeSpinBox->setRange(MIN_BRUSH_SIZE, MAX_BRUSH_SIZE); + m_yBrushSizeSpinBox->setSingleStep(2); QObject::connect(m_yBrushSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(yBrushSizeValueChanged(int))); m_yBrushSizeSpinBox->getWidget()->setToolTip("Coronal brush size (voxels).\n" "Must be an odd value."); + QLabel* zLabel = new QLabel("A:"); m_zBrushSizeSpinBox = new WuQSpinBoxOddValue(this); m_zBrushSizeSpinBox->setRange(MIN_BRUSH_SIZE, MAX_BRUSH_SIZE); + m_zBrushSizeSpinBox->setSingleStep(2); QObject::connect(m_zBrushSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(zBrushSizeValueChanged(int))); m_zBrushSizeSpinBox->getWidget()->setToolTip("Axial brush size (voxels).\n" "Must be an odd value."); - m_voxelValueLabel = new QLabel("Value:"); + m_voxelValueLabel = new QLabel("Value"); m_voxelFloatValueSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-1000.0, 1000.0, 1.0, 1, @@ -314,7 +317,7 @@ SLOT(voxelValueChanged(double))); m_voxelFloatValueSpinBox->setValue(1.0); - m_voxelLabelValueAction = WuQtUtilities::createAction("XXX", + m_voxelLabelValueAction = WuQtUtilities::createAction("Label", "Choose Label for Voxels", this, this, SLOT(labelValueActionTriggered())); @@ -322,85 +325,188 @@ m_voxelLabelValueToolButton->setDefaultAction(m_voxelLabelValueAction); WuQtUtilities::setToolButtonStyleForQt5Mac(m_voxelLabelValueToolButton); - const int SPACE = 10; + m_voxelLabelValueToolButton->setFixedWidth(m_voxelLabelValueToolButton->sizeHint().width()); + m_voxelFloatValueSpinBox->setFixedWidth(m_voxelLabelValueToolButton->sizeHint().width()); + + WuQtUtilities::matchWidgetWidths(m_newFileToolButton, m_addMapsToolButton, lockFileToolButton); + const int32_t spinBoxWidth(20 + m_newFileToolButton->sizeHint().width()); + m_xBrushSizeSpinBox->setFixedWidth(spinBoxWidth); + m_yBrushSizeSpinBox->setFixedWidth(spinBoxWidth); + m_zBrushSizeSpinBox->setFixedWidth(spinBoxWidth); + m_voxelFloatValueSpinBox->setFixedWidth(spinBoxWidth); + + QGridLayout* gridLayout = new QGridLayout(); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); + int32_t row(0); + int32_t col(0); + gridLayout->addWidget(volumeLabel, row++, col, Qt::AlignHCenter); + gridLayout->addWidget(m_newFileToolButton, row++, col); + gridLayout->addWidget(m_addMapsToolButton, row++, col); + gridLayout->addWidget(lockFileToolButton, row++, col); + row = 0; + col++; + gridLayout->addWidget(brushSizeLabel, row++, col, 1, 2, Qt::AlignHCenter); + gridLayout->addWidget(xLabel, row++, col); + gridLayout->addWidget(yLabel, row++, col); + gridLayout->addWidget(zLabel, row++, col); + row = 0; + col++; + gridLayout->addWidget(brushSizeLabel, row++, col, Qt::AlignHCenter); + gridLayout->addWidget(m_xBrushSizeSpinBox->getWidget(), row++, col); + gridLayout->addWidget(m_yBrushSizeSpinBox->getWidget(), row++, col); + gridLayout->addWidget(m_zBrushSizeSpinBox->getWidget(), row++, col); + row = 0; + col++; + gridLayout->addWidget(m_voxelValueLabel, row++, col, Qt::AlignHCenter); + gridLayout->addWidget(m_voxelFloatValueSpinBox, row++, col); + gridLayout->addWidget(m_voxelLabelValueToolButton, row++, col); + QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); + QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(volumeLabel); - layout->addWidget(m_newFileToolButton); - layout->addWidget(m_addMapsToolButton); - layout->addWidget(lockFileToolButton); - layout->addSpacing(SPACE); - layout->addWidget(editLabel); - layout->addWidget(undoToolButton); - layout->addWidget(redoToolButton); - layout->addWidget(resetToolButton); - layout->addSpacing(SPACE); - layout->addWidget(brushSizeLabel); - layout->addWidget(m_xBrushSizeSpinBox->getWidget()); - layout->addWidget(m_yBrushSizeSpinBox->getWidget()); - layout->addWidget(m_zBrushSizeSpinBox->getWidget()); - layout->addSpacing(SPACE); - layout->addWidget(m_voxelValueLabel); - layout->addWidget(m_voxelFloatValueSpinBox); - layout->addWidget(m_voxelLabelValueToolButton); + layout->addLayout(gridLayout); layout->addStretch(); + + return widget; +} + +/** + * @return Create and return the edit widget. + */ +QWidget* +UserInputModeVolumeEditWidget::createEditWidget() +{ + QToolButton* undoToolButton = new QToolButton(); + undoToolButton->setDefaultAction(WuQtUtilities::createAction("Undo", + "Undo the last volume edit", + this, + this, SLOT(undoActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(undoToolButton); + + + QToolButton* redoToolButton = new QToolButton(); + redoToolButton->setDefaultAction(WuQtUtilities::createAction("Redo", + "Redo (or is it undo) the last undo", + this, + this, SLOT(redoActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(redoToolButton); + + QToolButton* resetToolButton = new QToolButton(); + resetToolButton->setDefaultAction(WuQtUtilities::createAction("Reset", + "Reset all edting of the volume", + this, + this, SLOT(resetActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); + + QWidget* widget = new QWidget(); + QHBoxLayout* editLayout = new QHBoxLayout(widget); + WuQtUtilities::setLayoutSpacingAndMargins(editLayout, 4, 2); + editLayout->addStretch(); + editLayout->addWidget(undoToolButton); + editLayout->addWidget(redoToolButton); + editLayout->addWidget(resetToolButton); + editLayout->addStretch(); + return widget; } /** + * @return Radio button for selection of the given mode + * @mode The editing mode + */ +QRadioButton* +UserInputModeVolumeEditWidget::createModeRadioButton(const VolumeEditingModeEnum::Enum mode) +{ + QRadioButton* rb = new QRadioButton(VolumeEditingModeEnum::toGuiName(mode)); + WuQtUtilities::setWordWrappedToolTip(rb, VolumeEditingModeEnum::toToolTip(mode)); + + return rb; +} +/** * @return Create and return the mode toolbar. */ QWidget* -UserInputModeVolumeEditWidget::createModeToolBar() +UserInputModeVolumeEditWidget::createOperationWidget() { - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); + m_modeOnRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON); + m_modeOnRadioButton->setChecked(true); - QLabel* modeLabel = new QLabel("Mode:"); - layout->addWidget(modeLabel); + m_modeOffRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF); - m_volumeEditModeActionGroup = new QActionGroup(this); - m_volumeEditModeActionGroup->setExclusive(true); - QObject::connect(m_volumeEditModeActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(editingModeActionTriggered(QAction*))); - - std::vector editModes; - VolumeEditingModeEnum::getAllEnums(editModes); - - bool firstActionFlag = true; - for (std::vector::iterator iter = editModes.begin(); - iter != editModes.end(); - iter++) { - const VolumeEditingModeEnum::Enum mode = *iter; - - const int modeInt = VolumeEditingModeEnum::toIntegerCode(mode); - const AString modeName = VolumeEditingModeEnum::toGuiName(mode); - const AString toolTip = WuQtUtilities::createWordWrappedToolTipText(VolumeEditingModeEnum::toToolTip(mode)); - - QAction* action = new QAction(modeName, - this); - action->setData(modeInt); - action->setToolTip(toolTip); - action->setCheckable(true); - if (firstActionFlag) { - firstActionFlag = false; - action->setChecked(true); - } - - m_volumeEditModeActionGroup->addAction(action); - - QToolButton* toolButton = new QToolButton(); - toolButton->setDefaultAction(action); - WuQtUtilities::setToolButtonStyleForQt5Mac(toolButton); - - layout->addWidget(toolButton); - } + m_modeDilateRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE); - layout->addStretch(); + m_modeErodeRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE); + m_modeFillTwoDimRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D); + + m_modeFillThreeDimRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D); + + m_modeRemoveTwoDimRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D); + + m_modeRemoveThreeDimRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D); + + m_modeRetainThreeDimRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D); + + QButtonGroup* buttonGroup = new QButtonGroup(this); + buttonGroup->addButton(m_modeOnRadioButton); + buttonGroup->addButton(m_modeOffRadioButton); + buttonGroup->addButton(m_modeDilateRadioButton); + buttonGroup->addButton(m_modeErodeRadioButton); + buttonGroup->addButton(m_modeFillTwoDimRadioButton); + buttonGroup->addButton(m_modeFillThreeDimRadioButton); + buttonGroup->addButton(m_modeRemoveTwoDimRadioButton); + buttonGroup->addButton(m_modeRemoveThreeDimRadioButton); + buttonGroup->addButton(m_modeRetainThreeDimRadioButton); + + QGridLayout* gridLayout = new QGridLayout(); +#ifdef CARET_OS_LINUX + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 2); +#else + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); +#endif + int32_t row(0); + gridLayout->addWidget(m_modeOnRadioButton, + row, 0); + row++; + gridLayout->addWidget(m_modeOffRadioButton, + row, 0); + row++; + gridLayout->addWidget(m_modeDilateRadioButton, + row, 0); + row++; + gridLayout->addWidget(m_modeErodeRadioButton, + row, 0); + row++; + + row = 0; + gridLayout->addWidget(m_modeFillTwoDimRadioButton, + row, 1); + row++; + gridLayout->addWidget(m_modeFillThreeDimRadioButton, + row, 1); + row++; + gridLayout->addWidget(m_modeRemoveTwoDimRadioButton, + row, 1); + row++; + gridLayout->addWidget(m_modeRemoveThreeDimRadioButton, + row, 1); + row++; + gridLayout->addWidget(m_modeRetainThreeDimRadioButton, + row, 1); + row++; + + QWidget* widget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(widget); +#ifdef CARET_OS_LINUX + WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); +#else + WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 0); +#endif + layout->addLayout(gridLayout); + layout->addWidget(createEditWidget()); + layout->addStretch(); + return widget; } @@ -437,15 +543,38 @@ VolumeEditingModeEnum::Enum UserInputModeVolumeEditWidget::getEditingMode() const { - QAction* action = m_volumeEditModeActionGroup->checkedAction(); - CaretAssert(action); - - const int modeInt = action->data().toInt(); + VolumeEditingModeEnum::Enum editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON; - bool validFlag = false; - const VolumeEditingModeEnum::Enum editMode = VolumeEditingModeEnum::fromIntegerCode(modeInt, - &validFlag); - CaretAssert(validFlag); + if (m_modeOnRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON; + } + else if (m_modeOffRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF; + } + else if (m_modeDilateRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE; + } + else if (m_modeErodeRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE; + } + else if (m_modeFillTwoDimRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D; + } + else if (m_modeFillThreeDimRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D; + } + else if (m_modeRemoveTwoDimRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D; + } + else if (m_modeRemoveThreeDimRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D; + } + else if (m_modeRetainThreeDimRadioButton->isChecked()) { + editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D; + } + else { + CaretAssert(0); + } return editMode; } diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputModeVolumeEditWidget.h connectome-workbench-1.5.0/src/GuiQt/UserInputModeVolumeEditWidget.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputModeVolumeEditWidget.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputModeVolumeEditWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -28,113 +28,136 @@ #include "UserInputModeVolumeEdit.h" #include "VolumeEditingModeEnum.h" +class QAbstractButton; class QAction; -class QActionGroup; class QDoubleSpinBox; class QLabel; +class QRadioButton; class QSpinBox; class QToolButton; namespace caret { - class UserInputModeVolumeEdit; - class WuQSpinBoxOddValue; - - class UserInputModeVolumeEditWidget : public QWidget, - public EventListenerInterface { - - Q_OBJECT - - public: - UserInputModeVolumeEditWidget(UserInputModeVolumeEdit* inputModeVolumeEdit, - const int32_t windowIndex, - QWidget* parent = 0); - - virtual ~UserInputModeVolumeEditWidget(); - - void receiveEvent(Event* event); - - void updateWidget(); - - void getEditingParameters(VolumeEditingModeEnum::Enum& editingModeOut, - int32_t brushSizesOut[3], - float& floatValueOut, - AString& labelNameOut) const; - - VolumeEditingModeEnum::Enum getEditingMode() const; - - // ADD_NEW_METHODS_HERE +class WuQSpinBoxOddValue; +class UserInputModeVolumeEditWidget : public QWidget, +public EventListenerInterface { + + Q_OBJECT + +public: + UserInputModeVolumeEditWidget(UserInputModeVolumeEdit* inputModeVolumeEdit, + const int32_t windowIndex, + QWidget* parent = 0); + + virtual ~UserInputModeVolumeEditWidget(); + + void receiveEvent(Event* event); + + void updateWidget(); + + void getEditingParameters(VolumeEditingModeEnum::Enum& editingModeOut, + int32_t brushSizesOut[3], + float& floatValueOut, + AString& labelNameOut) const; + + VolumeEditingModeEnum::Enum getEditingMode() const; + + // ADD_NEW_METHODS_HERE + private slots: - void newFileActionTriggered(); - - void lockFileActionTriggered(); - - void undoActionTriggered(); - - void redoActionTriggered(); - - void resetActionTriggered(); - - void xBrushSizeValueChanged(int); - - void yBrushSizeValueChanged(int); - - void zBrushSizeValueChanged(int); - - void voxelValueChanged(double); - - void labelValueActionTriggered(); - - void editingModeActionTriggered(QAction*); - - void addMapsActionTriggered(); - - private: - UserInputModeVolumeEditWidget(const UserInputModeVolumeEditWidget&); - - UserInputModeVolumeEditWidget& operator=(const UserInputModeVolumeEditWidget&); - - QWidget* createSelectionToolBar(); - - QWidget* createModeToolBar(); - - void viewVolumeInNewOverlay(VolumeFile* vf, - const int32_t mapIndex); - - QAction* m_lockAction; - - UserInputModeVolumeEdit* m_inputModeVolumeEdit; - - const int32_t m_windowIndex; - - QActionGroup* m_volumeEditModeActionGroup; - - QToolButton* m_newFileToolButton; - - WuQSpinBoxOddValue* m_xBrushSizeSpinBox; - - WuQSpinBoxOddValue* m_yBrushSizeSpinBox; - - WuQSpinBoxOddValue* m_zBrushSizeSpinBox; - - QLabel* m_voxelValueLabel; - - QAction* m_voxelLabelValueAction; - - QToolButton* m_voxelLabelValueToolButton; - - QDoubleSpinBox* m_voxelFloatValueSpinBox; - - QToolButton* m_addMapsToolButton; - - // ADD_NEW_MEMBERS_HERE - - }; + void newFileActionTriggered(); + + void lockFileActionTriggered(); + + void undoActionTriggered(); + + void redoActionTriggered(); + + void resetActionTriggered(); + + void xBrushSizeValueChanged(int); + + void yBrushSizeValueChanged(int); + + void zBrushSizeValueChanged(int); + + void voxelValueChanged(double); + + void labelValueActionTriggered(); + + void editingModeActionTriggered(QAction*); + + void addMapsActionTriggered(); + +private: + UserInputModeVolumeEditWidget(const UserInputModeVolumeEditWidget&); + + UserInputModeVolumeEditWidget& operator=(const UserInputModeVolumeEditWidget&); + + QWidget* createSelectionToolBar(); + + QWidget* createOperationWidget(); + + QWidget* createEditWidget(); + void viewVolumeInNewOverlay(VolumeFile* vf, + const int32_t mapIndex); + + bool isModeButtonEnabled(const VolumeEditingModeEnum::Enum mode) const; + + QRadioButton* createModeRadioButton(const VolumeEditingModeEnum::Enum mode); + + QAction* m_lockAction; + + UserInputModeVolumeEdit* m_inputModeVolumeEdit; + + const int32_t m_windowIndex; + + QToolButton* m_newFileToolButton; + + WuQSpinBoxOddValue* m_xBrushSizeSpinBox; + + WuQSpinBoxOddValue* m_yBrushSizeSpinBox; + + WuQSpinBoxOddValue* m_zBrushSizeSpinBox; + + QLabel* m_voxelValueLabel; + + QAction* m_voxelLabelValueAction; + + QToolButton* m_voxelLabelValueToolButton; + + QDoubleSpinBox* m_voxelFloatValueSpinBox; + + QToolButton* m_addMapsToolButton; + + QRadioButton* m_modeOnRadioButton; + + QRadioButton* m_modeOffRadioButton; + + QRadioButton* m_modeDilateRadioButton; + + QRadioButton* m_modeErodeRadioButton; + + QRadioButton* m_modeFillTwoDimRadioButton; + + QRadioButton* m_modeFillThreeDimRadioButton; + + QRadioButton* m_modeRemoveTwoDimRadioButton; + + QRadioButton* m_modeRemoveThreeDimRadioButton; + + QRadioButton* m_modeRetainThreeDimRadioButton; + + // ADD_NEW_MEMBERS_HERE + +}; + #ifdef __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_DECLARE__ - // +// #endif // __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_VOLUME_EDIT_WIDGET_H__ + diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputTileTabsContextMenu.cxx connectome-workbench-1.5.0/src/GuiQt/UserInputTileTabsContextMenu.cxx --- connectome-workbench-1.4.2/src/GuiQt/UserInputTileTabsContextMenu.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputTileTabsContextMenu.cxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,137 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2017 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __USER_INPUT_TILE_TABS_CONTEXT_MENU_DECLARE__ -#include "UserInputTileTabsContextMenu.h" -#undef __USER_INPUT_TILE_TABS_CONTEXT_MENU_DECLARE__ - -#include "BrainBrowserWindow.h" -#include "BrainOpenGLViewportContent.h" -#include "BrowserTabContent.h" -#include "CaretAssert.h" -#include "EventBrowserTabGet.h" -#include "EventBrowserWindowTileTabOperation.h" -#include "EventManager.h" -#include "GuiManager.h" - -using namespace caret; - - - -/** - * \class caret::UserInputTileTabsContextMenu - * \brief Menu for tile tabs operations in a window context menu - * \ingroup GuiQt - */ - -/** - * Constructor. - * - * @param parentWidget - * Parent widget on which error message dialogs are displayed - * @param viewportContent - * Content of the viewport under the mouse. - */ -UserInputTileTabsContextMenu::UserInputTileTabsContextMenu(QWidget* parentWidget, - BrainOpenGLViewportContent* viewportContent) -: QMenu("Tabs"), -m_parentWidget(parentWidget), -m_windowIndex(viewportContent->getWindowIndex()), -m_tabIndex(viewportContent->getTabIndex()) -{ - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_windowIndex); - CaretAssert(bbw); - CaretAssert(m_windowIndex >= 0); - CaretAssert(m_tabIndex >= 0); - - EventBrowserTabGet tabContentEvent(m_tabIndex); - EventManager::get()->sendEvent(tabContentEvent.getPointer()); - BrowserTabContent* tabContent = tabContentEvent.getBrowserTab(); - if (tabContent != NULL) { - const AString tabNumberText(AString::number(m_tabIndex + 1)); - - if (bbw->isTileTabsSelected()) { - m_createNewTabBeforeAction = addAction("Create New Tab Before This Tab"); - m_createNewTabAfterAction = addAction("Create New Tab After This Tab"); - m_selectTabAction = addAction("Select This Tab"); - } - - QObject::connect(this, &QMenu::triggered, - this, &UserInputTileTabsContextMenu::actionTriggered); - } -} - -/** - * Destructor. - */ -UserInputTileTabsContextMenu::~UserInputTileTabsContextMenu() -{ -} - -/** - * @return Is this menu valid? - */ -bool -UserInputTileTabsContextMenu::isValid() const -{ - return (actions().size() > 0); -} - - -/** - * Gets called when an action is selected from the menu. - * - * @param action - * Action selected by user. - */ -void -UserInputTileTabsContextMenu::actionTriggered(QAction* action) -{ - EventBrowserWindowTileTabOperation::Operation operation = EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB; - bool validOperationFlag = false; - - if (action == m_createNewTabBeforeAction) { - operation = EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_BEFORE; - validOperationFlag = true; - } - else if (action == m_createNewTabAfterAction) { - operation = EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_AFTER; - validOperationFlag = true; - } - else if (action == m_selectTabAction) { - operation = EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB; - validOperationFlag = true; - } - else if (action != NULL) { - CaretAssertMessage(0, "Invalid menu action. Has a new menu action been added?"); - } - - if (validOperationFlag) { - std::vector emptyBrowserTabs; - EventBrowserWindowTileTabOperation tileTabOperation(operation, - m_parentWidget, - m_windowIndex, - m_tabIndex, - emptyBrowserTabs); - EventManager::get()->sendEvent(tileTabOperation.getPointer()); - } -} - diff -Nru connectome-workbench-1.4.2/src/GuiQt/UserInputTileTabsContextMenu.h connectome-workbench-1.5.0/src/GuiQt/UserInputTileTabsContextMenu.h --- connectome-workbench-1.4.2/src/GuiQt/UserInputTileTabsContextMenu.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/UserInputTileTabsContextMenu.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -#ifndef __USER_INPUT_TILE_TABS_CONTEXT_MENU_H__ -#define __USER_INPUT_TILE_TABS_CONTEXT_MENU_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2017 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - -#include - -class QWidget; - -namespace caret { - - class BrainOpenGLViewportContent; - - class UserInputTileTabsContextMenu : public QMenu { - - Q_OBJECT - - public: - UserInputTileTabsContextMenu(QWidget* parentWidget, - BrainOpenGLViewportContent* viewportContent); - - virtual ~UserInputTileTabsContextMenu(); - - bool isValid() const; - - // ADD_NEW_METHODS_HERE - - private slots: - void actionTriggered(QAction* action); - - private: - UserInputTileTabsContextMenu(const UserInputTileTabsContextMenu&); - - UserInputTileTabsContextMenu& operator=(const UserInputTileTabsContextMenu&); - - QWidget* m_parentWidget; - - const int32_t m_windowIndex; - - const int32_t m_tabIndex; - - QAction* m_createNewTabBeforeAction = nullptr; - - QAction* m_createNewTabAfterAction = nullptr; - - QAction* m_selectTabAction = nullptr; - - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __USER_INPUT_TILE_TABS_CONTEXT_MENU_DECLARE__ - // -#endif // __USER_INPUT_TILE_TABS_CONTEXT_MENU_DECLARE__ - -} // namespace -#endif //__USER_INPUT_TILE_TABS_CONTEXT_MENU_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/VolumePropertiesEditorDialog.cxx connectome-workbench-1.5.0/src/GuiQt/VolumePropertiesEditorDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/VolumePropertiesEditorDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/VolumePropertiesEditorDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -56,7 +56,7 @@ : WuQDialogNonModal("Volume Properties", parent) { - QLabel* opacityLabel = new QLabel("Opacity: "); + QLabel* opacityLabel = new QLabel("'All' Display Slice Opacity: "); m_opacitySpinBox = WuQFactory::newDoubleSpinBox(); m_opacitySpinBox->setRange(0.0, 1.0); m_opacitySpinBox->setSingleStep(0.1); diff -Nru connectome-workbench-1.4.2/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx connectome-workbench-1.5.0/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx --- connectome-workbench-1.4.2/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -82,7 +82,7 @@ } this->modelComboBox->addItem(item->getName(), - qVariantFromValue((void*)item)); + QVariant::fromValue((void*)item)); } if (selectedItemIndex >= 0) { diff -Nru connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx --- connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -295,6 +295,7 @@ case ModelTypeEnum::MODEL_TYPE_CHART_TWO: case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: case ModelTypeEnum::MODEL_TYPE_SURFACE: case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: @@ -539,6 +540,7 @@ case ModelTypeEnum::MODEL_TYPE_CHART_TWO: case ModelTypeEnum::MODEL_TYPE_INVALID: break; + case ModelTypeEnum::MODEL_TYPE_MULTI_MEDIA: case ModelTypeEnum::MODEL_TYPE_SURFACE: case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: diff -Nru connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationManager.cxx connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationManager.cxx --- connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -47,6 +47,7 @@ #include "WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h" #include "WbMacroCustomOperationIncrementRotation.h" #include "WbMacroCustomOperationIncrementVolumeSlice.h" +#include "WbMacroCustomOperationSurfaceDefaultColor.h" #include "WbMacroCustomDataInfo.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" @@ -740,6 +741,9 @@ case WbMacroCustomOperationTypeEnum::INCREMENTAL_VOLUME_SLICE: operationOut = new WbMacroCustomOperationIncrementVolumeSlice(); break; + case WbMacroCustomOperationTypeEnum::SURFACE_DEFAULT_COLOR: + operationOut = new WbMacroCustomOperationSurfaceDefaultColor(); + break; } CaretAssert(operationOut); diff -Nru connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.cxx connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.cxx --- connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,164 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_SURFACE_DEFAULT_COLOR_DECLARE__ +#include "WbMacroCustomOperationSurfaceDefaultColor.h" +#undef __WB_MACRO_CUSTOM_OPERATION_SURFACE_DEFAULT_COLOR_DECLARE__ + +#include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "DisplayPropertiesSurface.h" +#include "GuiManager.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationSurfaceDefaultColor + * \brief Macro custom operation for incremental rotation + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationSurfaceDefaultColor::WbMacroCustomOperationSurfaceDefaultColor() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::SURFACE_DEFAULT_COLOR) +{ + +} + +/** + * Destructor. + */ +WbMacroCustomOperationSurfaceDefaultColor::~WbMacroCustomOperationSurfaceDefaultColor() +{ +} + +/** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationSurfaceDefaultColor::createCommand() +{ + const int32_t versionOne(1); + + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), + versionOne, + "none", + WbMacroCustomOperationTypeEnum::toGuiName(getOperationType()), + "Set the surface default vertex color (RGB)", + 1.0, + errorMessage); + if (command != NULL) { + command->addParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Red (0-255)", + "187"); + command->addParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Green (0-255)", + "187"); + command->addParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Blue (0-255)", + "187"); + } + else { + appendToErrorMessage(errorMessage); + } + + return command; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor + * @param executorOptions + * Options for executor + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationSurfaceDefaultColor::executeCommand(QWidget* /*parent*/, + const WuQMacroExecutorMonitor* /*executorMonitor*/, + const WuQMacroExecutorOptions* /*executorOptions*/, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(macroCommand); + + if ( ! validateCorrectNumberOfParameters(macroCommand, 3)) { + return false; + } + + const int32_t redInt(macroCommand->getParameterAtIndex(0)->getValue().toInt()); + const int32_t greenInt(macroCommand->getParameterAtIndex(1)->getValue().toInt()); + const int32_t blueInt(macroCommand->getParameterAtIndex(2)->getValue().toInt()); + + if ((redInt < 0) + || (redInt > 255)) { + appendToErrorMessage("Red must be in range [0, 255]"); + return false; + } + if ((greenInt < 0) + || (greenInt > 255)) { + appendToErrorMessage("Green must be in range [0, 255]"); + return false; + } + if ((blueInt < 0) + || (blueInt > 255)) { + appendToErrorMessage("Blue must be in range [0, 255]"); + return false; + } + + std::array rgb { + static_cast(redInt), + static_cast(greenInt), + static_cast(blueInt), + }; + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + dsp->setDefaultColorRGB(rgb); + + updateSurfaceColoring(); + updateGraphics(); + updateUserInterface(); + + return true; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.h connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.h --- connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,68 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_SURFACE_DEFAULT_COLOR_H__ +#define __WB_MACRO_CUSTOM_OPERATION_SURFACE_DEFAULT_COLOR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WbMacroCustomOperationBase.h" + + + +namespace caret { + + class BrowserTabContent; + + class WbMacroCustomOperationSurfaceDefaultColor : public WbMacroCustomOperationBase { + + public: + WbMacroCustomOperationSurfaceDefaultColor(); + + virtual ~WbMacroCustomOperationSurfaceDefaultColor(); + + WbMacroCustomOperationSurfaceDefaultColor(const WbMacroCustomOperationSurfaceDefaultColor&) = delete; + + WbMacroCustomOperationSurfaceDefaultColor& operator=(const WbMacroCustomOperationSurfaceDefaultColor&) = delete; + + virtual bool executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, + const WuQMacroCommand* macroCommand) override; + + virtual WuQMacroCommand* createCommand() override; + + + // ADD_NEW_METHODS_HERE + + private: + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_SURFACE_DEFAULT_COLOR_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_SURFACE_DEFAULT_COLOR_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_SURFACE_DEFAULT_COLOR_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx --- connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -138,6 +138,9 @@ "INCREMENTAL_VOLUME_SLICE", "Increment Volume Slice")); + enumData.push_back(WbMacroCustomOperationTypeEnum(SURFACE_DEFAULT_COLOR, + "SURFACE_DEFAULT_COLOR", + "Surface Default Color")); } /** diff -Nru connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationTypeEnum.h connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationTypeEnum.h --- connectome-workbench-1.4.2/src/GuiQt/WbMacroCustomOperationTypeEnum.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WbMacroCustomOperationTypeEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -50,7 +50,9 @@ /** Incremental Rotation */ INCREMENTAL_ROTATION, /** Increment volume slice */ - INCREMENTAL_VOLUME_SLICE + INCREMENTAL_VOLUME_SLICE, + /** Surface default color */ + SURFACE_DEFAULT_COLOR }; diff -Nru connectome-workbench-1.4.2/src/GuiQt/WbMacroHelper.cxx connectome-workbench-1.5.0/src/GuiQt/WbMacroHelper.cxx --- connectome-workbench-1.4.2/src/GuiQt/WbMacroHelper.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WbMacroHelper.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -35,7 +35,6 @@ #include "EventManager.h" #include "EventMovieManualModeRecording.h" #include "EventSceneActive.h" -#include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MovieRecorder.h" diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQCollapsibleWidget.cxx connectome-workbench-1.5.0/src/GuiQt/WuQCollapsibleWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQCollapsibleWidget.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQCollapsibleWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -121,7 +121,7 @@ QLabel* label = new QLabel(text); QAction* showHideAction = this->showHideActionGroup->addAction("-"); - showHideAction->setData(qVariantFromValue((void*)widget)); + showHideAction->setData(QVariant::fromValue((void*)widget)); QToolButton* showHideToolButton = new QToolButton(); showHideToolButton->setDefaultAction(showHideAction); diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQColorEditorWidget.cxx connectome-workbench-1.5.0/src/GuiQt/WuQColorEditorWidget.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQColorEditorWidget.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQColorEditorWidget.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,792 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_COLOR_EDITOR_WIDGET_DECLARE__ +#include "WuQColorEditorWidget.h" +#undef __WU_Q_COLOR_EDITOR_WIDGET_DECLARE__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretColorEnum.h" +#include "MathFunctions.h" +#include "WuQImageLabel.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::WuQColorEditorWidget + * \brief Widget for editing color + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WuQColorEditorWidget::WuQColorEditorWidget(QWidget* parent) +: QWidget(parent) +{ + QWidget* hueSaturationWidget = createHueSaturationColorLabel(); + QWidget* valueWidget = createValueColorLabel(); + + QWidget* controlsWidget = createControlsWidget(); + QWidget* caretColorWidget = createCaretColorNoNamesSelectionButtonsWidget(); + + + QGridLayout* layout = new QGridLayout(this); + layout->addWidget(hueSaturationWidget, 0, 0); + layout->addWidget(valueWidget, 0, 1); + layout->addWidget(caretColorWidget, 0, 2, 1, 1); + layout->addWidget(controlsWidget, 1, 0, 1, 3); + +// m_currentColor.setRgb(255, 0, 0); +// m_originalColor = m_currentColor; +// updateControls(); + + setCurrentColor(QColor()); + setSizePolicy(sizePolicy().horizontalPolicy(), + QSizePolicy::Fixed); +} + +/** + * Destructor. + */ +WuQColorEditorWidget::~WuQColorEditorWidget() +{ +} + +/** + * @return The color in the editor + */ +QColor +WuQColorEditorWidget::getColor() const +{ + return m_currentColor; +} + + +/** + * Set the current color for editing + * @param color + * New color for editing + */ +void +WuQColorEditorWidget::setCurrentColor(const QColor& color) +{ + m_currentColor = color; + m_originalColor = m_currentColor; + + updateControls(); +} + +/** + * @return New instance of the controls widget + */ +QWidget* +WuQColorEditorWidget::createControlsWidget() +{ + QLabel* currentLabel = new QLabel("Current\nColor"); + currentLabel->setAlignment(Qt::AlignHCenter); + m_currentColorSwatchWidget = new QWidget(); + m_currentColorSwatchWidget->setMinimumWidth(50); + m_currentColorSwatchWidget->setMinimumHeight(30); + + QLabel* originalLabel = new QLabel("Original\nColor"); + m_originalColorSwatchWidget = new QWidget(); + m_originalColorSwatchWidget->setMinimumWidth(50); + m_originalColorSwatchWidget->setMinimumHeight(20); + + QToolButton* revertToolButton = new QToolButton(); + revertToolButton->setText("Revert"); + revertToolButton->setToolTip("Revert to Original Color"); + QObject::connect(revertToolButton, &QToolButton::clicked, + this, &WuQColorEditorWidget::revertToOriginalColorToolButtonClicked); + + QVBoxLayout* colorLayout = new QVBoxLayout(); + colorLayout->addWidget(currentLabel); + colorLayout->addWidget(m_currentColorSwatchWidget, 100); + colorLayout->addWidget(originalLabel); + colorLayout->addWidget(m_originalColorSwatchWidget); + colorLayout->addWidget(revertToolButton); + + m_hueSlider = new QSlider(); + m_hueSlider->setOrientation(Qt::Horizontal); + m_hueSlider->setRange(0, 359); + QObject::connect(m_hueSlider, &QSlider::valueChanged, + this, &WuQColorEditorWidget::hueChanged); + + m_hueSpinBox = new QSpinBox(); + m_hueSpinBox->setRange(0, 359); + m_hueSpinBox->setWrapping(true); + QObject::connect(m_hueSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &WuQColorEditorWidget::hueChanged); + + m_saturationSlider = new QSlider(); + m_saturationSlider->setOrientation(Qt::Horizontal); + m_saturationSlider->setRange(0, 255); + QObject::connect(m_saturationSlider, &QSlider::valueChanged, + this, &WuQColorEditorWidget::saturationChanged); + + m_saturationSpinBox = new QSpinBox(); + m_saturationSpinBox->setRange(0, 255); + QObject::connect(m_saturationSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &WuQColorEditorWidget::saturationChanged); + + m_valueSlider = new QSlider(); + m_valueSlider->setOrientation(Qt::Horizontal); + m_valueSlider->setRange(0, 255); + QObject::connect(m_valueSlider, &QSlider::valueChanged, + this, &WuQColorEditorWidget::valueChanged); + + m_valueSpinBox = new QSpinBox(); + m_valueSpinBox->setRange(0, 255); + QObject::connect(m_valueSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &WuQColorEditorWidget::valueChanged); + + m_redSlider = new QSlider(); + m_redSlider->setOrientation(Qt::Horizontal); + m_redSlider->setRange(0, 255); + QObject::connect(m_redSlider, &QSlider::valueChanged, + this, &WuQColorEditorWidget::redChanged); + + m_redSpinBox = new QSpinBox(); + m_redSpinBox->setRange(0, 255); + QObject::connect(m_redSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &WuQColorEditorWidget::redChanged); + + m_greenSlider = new QSlider(); + m_greenSlider->setOrientation(Qt::Horizontal); + m_greenSlider->setRange(0, 255); + QObject::connect(m_greenSlider, &QSlider::valueChanged, + this, &WuQColorEditorWidget::greenChanged); + + m_greenSpinBox = new QSpinBox(); + m_greenSpinBox->setRange(0, 255); + QObject::connect(m_greenSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &WuQColorEditorWidget::greenChanged); + + m_blueSlider = new QSlider(); + m_blueSlider->setOrientation(Qt::Horizontal); + m_blueSlider->setRange(0, 255); + QObject::connect(m_blueSlider, &QSlider::valueChanged, + this, &WuQColorEditorWidget::blueChanged); + + m_blueSpinBox = new QSpinBox(); + m_blueSpinBox->setRange(0, 255); + QObject::connect(m_blueSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &WuQColorEditorWidget::blueChanged); + + const int32_t COL_LABEL(0); + const int32_t COL_SLIDER(COL_LABEL + 1); + const int32_t COL_SPIN_BOX(COL_SLIDER + 1); + + QGridLayout* adjustLayout = new QGridLayout(); + adjustLayout->setColumnStretch(COL_LABEL, 0); + adjustLayout->setColumnStretch(COL_SLIDER, 100); + adjustLayout->setColumnStretch(COL_SPIN_BOX, 0); + + int32_t row(0); + adjustLayout->addWidget(new QLabel("Red:"), + row, COL_LABEL); + adjustLayout->addWidget(m_redSlider, + row, COL_SLIDER); + adjustLayout->addWidget(m_redSpinBox, + row, COL_SPIN_BOX); + row++; + + adjustLayout->addWidget(new QLabel("Green:"), + row, COL_LABEL); + adjustLayout->addWidget(m_greenSlider, + row, COL_SLIDER); + adjustLayout->addWidget(m_greenSpinBox, + row, COL_SPIN_BOX); + row++; + + adjustLayout->addWidget(new QLabel("Blue:"), + row, COL_LABEL); + adjustLayout->addWidget(m_blueSlider, + row, COL_SLIDER); + adjustLayout->addWidget(m_blueSpinBox, + row, COL_SPIN_BOX); + row++; + + adjustLayout->addWidget(new QLabel("Hue:"), + row, COL_LABEL); + adjustLayout->addWidget(m_hueSlider, + row, COL_SLIDER); + adjustLayout->addWidget(m_hueSpinBox, + row, COL_SPIN_BOX); + row++; + + adjustLayout->addWidget(new QLabel("Sat:"), + row, COL_LABEL); + adjustLayout->addWidget(m_saturationSlider, + row, COL_SLIDER); + adjustLayout->addWidget(m_saturationSpinBox, + row, COL_SPIN_BOX); + row++; + + adjustLayout->addWidget(new QLabel("Value:"), + row, COL_LABEL); + adjustLayout->addWidget(m_valueSlider, + row, COL_SLIDER); + adjustLayout->addWidget(m_valueSpinBox, + row, COL_SPIN_BOX); + row++; + + QWidget* widget = new QWidget(); + QHBoxLayout* layout = new QHBoxLayout(widget); +// WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); + layout->addLayout(colorLayout, 0); + layout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0); + layout->addLayout(adjustLayout, 100); + + return widget; +} + +/** + * Called to revert to the original color + */ +void +WuQColorEditorWidget::revertToOriginalColorToolButtonClicked() +{ + m_currentColor = m_originalColor; + updateControls(); + emitColorChangedSignal(); +} + +/** + * Emit the color changed signal when the color changes + */ +void +WuQColorEditorWidget::emitColorChangedSignal() +{ + emit colorChanged(m_currentColor); +} + +/** + * Called when hue changed + * @param hue + * New hue + */ +void +WuQColorEditorWidget::hueChanged(int hue) +{ + m_currentColor.setHsv(hue, + m_currentColor.hsvSaturation(), + m_currentColor.value()); + + updateControls(); + emitColorChangedSignal(); +} + +/** + * Called when saturation changed + * @param saturation + * New saturation + */ +void +WuQColorEditorWidget::saturationChanged(int saturation) +{ + m_currentColor.setHsv(m_currentColor.hsvHue(), + saturation, + m_currentColor.value()); + + updateControls(); + emitColorChangedSignal(); +} + +/** + * Called when changed + * @param value + * New value + */ +void +WuQColorEditorWidget::valueChanged(int value) +{ + m_currentColor.setHsv(m_currentColor.hsvHue(), + m_currentColor.hsvSaturation(), + value); + + updateControls(); + emitColorChangedSignal(); +} + +/** + * Called when red changed + * @param red + * New red + */ +void +WuQColorEditorWidget::redChanged(int red) +{ + m_currentColor.setRed(red); + + updateControls(); + emitColorChangedSignal(); +} + +/** + * Called when green changed + * @param green + * New green + */ +void +WuQColorEditorWidget::greenChanged(int green) +{ + m_currentColor.setGreen(green); + + updateControls(); + emitColorChangedSignal(); +} + +/** + * Called when blue changed + * @param blue + * New blue + */ +void +WuQColorEditorWidget::blueChanged(int blue) +{ + m_currentColor.setBlue(blue); + + updateControls(); + emitColorChangedSignal(); +} + +/** + * Update the controls with the current color + */ +void +WuQColorEditorWidget::updateControls() +{ + updateSliderAndSpinBox(m_hueSlider, + m_hueSpinBox, + m_currentColor.hsvHue()); + + updateSliderAndSpinBox(m_saturationSlider, + m_saturationSpinBox, + m_currentColor.hsvSaturation()); + + updateSliderAndSpinBox(m_valueSlider, + m_valueSpinBox, + m_currentColor.value()); + + updateSliderAndSpinBox(m_redSlider, + m_redSpinBox, + m_currentColor.red()); + + updateSliderAndSpinBox(m_greenSlider, + m_greenSpinBox, + m_currentColor.green()); + + updateSliderAndSpinBox(m_blueSlider, + m_blueSpinBox, + m_currentColor.blue()); + + m_currentColorSwatchWidget->setStyleSheet("background-color: rgb(" + + QString::number(m_currentColor.red()) + + ", " + QString::number(m_currentColor.green()) + + ", " + QString::number(m_currentColor.blue()) + + ");"); + + m_originalColorSwatchWidget->setStyleSheet("background-color: rgb(" + + QString::number(m_originalColor.red()) + + ", " + QString::number(m_originalColor.green()) + + ", " + QString::number(m_originalColor.blue()) + + ");"); + + updateValueColorLabel(); + + updateHueSaturationLabel(); + + setEnabled(m_currentColor.isValid()); +} + +/** + * Update the slider and the spin box with the given value + * @param slider + * Slider that is update + * @param spinBox + * Spin box that is updated + * @param value + * New value for slider and spin box + */ +void +WuQColorEditorWidget::updateSliderAndSpinBox(QSlider* slider, + QSpinBox* spinBox, + const int value) +{ + QSignalBlocker sliderBlocker(slider); + slider->setValue(value); + + QSignalBlocker spinBoxBlocker(spinBox); + spinBox->setValue(value); +} + +QWidget* +WuQColorEditorWidget::createHueSaturationColorLabel() +{ + const int32_t xWidth(360); + const int32_t yHeight(256); + + m_hueSaturationColorLabel = new WuQImageLabel(); + m_hueSaturationColorLabel->setFixedSize(xWidth, yHeight); + m_hueSaturationPixmap = QPixmap(xWidth, + yHeight); + QPainter painter(&m_hueSaturationPixmap); + QPen pen = painter.pen(); + + updateHueSaturationToLabelTransforms(); + for (int32_t x = 0; x < xWidth; x++) { + const float hue = m_hueToLabelLinearTransform->inverseTransformValue(x); + + for (int32_t y = 0; y < yHeight; y++) { + const float sat = m_saturationToLabelLinearTransform->inverseTransformValue(y); + pen.setColor(QColor::fromHsv(hue, sat, 255)); + painter.setPen(pen); + painter.drawPoint(x, y); + } + } + + m_hueSaturationColorLabel->setPixmap(m_hueSaturationPixmap); + + QObject::connect(m_hueSaturationColorLabel, &WuQImageLabel::clickedXY, + this, &WuQColorEditorWidget::hueSaturationLabelClicked); + + return m_hueSaturationColorLabel; +} + +/** + * Update with cursor showing hue and saturation values + */ +void +WuQColorEditorWidget::updateHueSaturationLabel() +{ + const float hue(m_currentColor.hsvHue()); + const float saturation(m_currentColor.hsvSaturation()); + + updateHueSaturationToLabelTransforms(); + const float cursorX = m_hueToLabelLinearTransform->transformValue(hue); + const float cursorY = m_saturationToLabelLinearTransform->transformValue(saturation); + QPixmap pixmap = m_hueSaturationPixmap; + + QPainter painter(&pixmap); + painter.translate(cursorX, cursorY); + QPen pen = painter.pen(); + pen.setColor(QColor(0, 0, 0)); + pen.setWidth(3); + painter.setPen(pen); + + const int32_t cursorSize(10); + painter.drawLine(-cursorSize, 0, cursorSize, 0); + painter.drawLine(0, -cursorSize, 0, cursorSize); + + m_hueSaturationColorLabel->setPixmap(pixmap); +} + +void +WuQColorEditorWidget::hueSaturationLabelClicked(int x, int y) +{ + updateHueSaturationToLabelTransforms(); + const float hue = m_hueToLabelLinearTransform->transformValue(x); + const float saturation = m_saturationToLabelLinearTransform->transformValue(y); + + m_currentColor.setHsv(hue, saturation, m_currentColor.value()); + updateControls(); + emitColorChangedSignal(); +} + + +QWidget* +WuQColorEditorWidget::createValueColorLabel() +{ + m_valueColorLabel = new WuQImageLabel(); + m_valueColorLabel->setFixedSize(25, 256); + + + QObject::connect(m_valueColorLabel, &WuQImageLabel::clickedXY, + this, &WuQColorEditorWidget::valueLabelClicked); + + return m_valueColorLabel; +} + +void +WuQColorEditorWidget::updateValueColorLabel() +{ + const int32_t xWidth = m_valueColorLabel->width(); + const int32_t yHeight = m_valueColorLabel->height(); + + QPixmap pixmap(xWidth, + yHeight); + QPainter painter(&pixmap); + + painter.fillRect(0, 0, xWidth, yHeight, QColor(255, 255, 255)); + + QPen pen = painter.pen(); + const int32_t saturation = m_saturationSpinBox->value(); + const int32_t hue = m_hueSpinBox->value(); + + updateValueToLabelXyTransform(); + + /* + * Max saturation is at top + */ + const int32_t halfWidth(xWidth / 2); + + for (int32_t value = 0; value < 255; value++) { + const float yValue = m_valueToLabelLinearTransform->transformValue(value); + pen.setColor(QColor::fromHsv(hue, saturation, value)); + painter.setPen(pen); + painter.drawRect(0, yValue, halfWidth, 1); + } + + const int32_t quarterWidth(halfWidth / 2); + QPolygon triangle; + triangle.append(QPoint(halfWidth, 0)); + triangle.append(QPoint(xWidth, -quarterWidth)); + triangle.append(QPoint(xWidth, quarterWidth)); + + const int32_t valueY = (yHeight - m_currentColor.value()); + pen.setColor(QColor(0, 0, 0)); + pen.setWidth(3); + painter.setPen(pen); + painter.translate(0, valueY); + QBrush brush = painter.brush(); + brush.setColor(QColor(0, 0, 0)); + brush.setStyle(Qt::SolidPattern); + painter.setBrush(brush); + painter.drawPolygon(triangle); + + m_valueColorLabel->setPixmap(pixmap); +} + +void +WuQColorEditorWidget::valueLabelClicked(int /*x*/, int y) +{ + updateValueToLabelXyTransform(); + + const float newValue = m_valueToLabelLinearTransform->inverseTransformValue(y); + + m_currentColor.setHsv(m_currentColor.hsvHue(), + m_currentColor.hsvSaturation(), + newValue); + updateControls(); + emitColorChangedSignal(); +} + +void +WuQColorEditorWidget::updateValueToLabelXyTransform() +{ + /* + * Only need to update if Label size has changed + * or first time + */ + if (m_valueColorLabel->size() != m_valueLabelSize) { + m_valueToLabelLinearTransform.reset(); + } + + if ( ! m_valueToLabelLinearTransform) { + m_valueLabelSize = m_valueColorLabel->size(); + + /* + * Qt's widgets have (0, 0) in the upper left corner + * The transform maps an HSV's "value" to a widgets Y-coordinate (pixel) + */ + const float labelHeight(m_valueColorLabel->height() - 1); + const float valueMaximum(255); /* ranges [0, 255] */ + + m_valueToLabelLinearTransform.reset(new LinearEquationTransform(0, valueMaximum, + 0, labelHeight, + 0, labelHeight)); + } +} + +void +WuQColorEditorWidget::updateHueSaturationToLabelTransforms() +{ + if (m_hueSaturationColorLabel->size() != m_hueSaturationLabelSize) { + m_hueToLabelLinearTransform.reset(); + m_saturationToLabelLinearTransform.reset(); + } + + if ( ! m_hueToLabelLinearTransform) { + /* + * Qt's widgets have (0, 0) in the upper left corner + * The transform maps an HSV's "hue" to a widgets X-coordinate (pixel) + * Hue=359 is at left, Hue=0 is at right + */ + const float labelWidth(m_hueSaturationColorLabel->width() - 1); + const float hueMaximum(359); /* ranges [0, 359] */ + const float hueMinimum(0); + + m_hueToLabelLinearTransform.reset(new LinearEquationTransform(hueMinimum, hueMaximum, + 0, labelWidth, + hueMinimum, labelWidth)); + + const float labelHeight(m_hueSaturationColorLabel->height() - 1); + const float saturationMaximum(255); + const float saturationMinimum(0); + m_saturationToLabelLinearTransform.reset(new LinearEquationTransform(saturationMinimum, saturationMaximum, + 0, labelHeight, + saturationMinimum, labelHeight)); + } +} + +QWidget* +WuQColorEditorWidget::createCaretColorNoNamesSelectionButtonsWidget() +{ + QWidget* widget = new QWidget; + QGridLayout* layout = new QGridLayout(widget); + layout->setHorizontalSpacing(0); + layout->setVerticalSpacing(0); + + std::vector colorEnums; + CaretColorEnum::getColorEnums(colorEnums); + + const int32_t maxRows(8); + int32_t row(0); + int32_t col(0); + std::vector tbWidgets; + for (auto cc : colorEnums) { + QToolButton* tb = new QToolButton(); + + QSize iconSize(18, 18); + float rgbaFloat[4]; + CaretColorEnum::toRGBAFloat(cc, rgbaFloat); + QPixmap pm(WuQtUtilities::createCaretColorEnumPixmap(tb, iconSize.width(), iconSize.height(), + CaretColorEnum::CUSTOM, rgbaFloat, false)); + + QAction* a = new QAction(CaretColorEnum::toGuiName(cc)); + a->setData(CaretColorEnum::toIntegerCode(cc)); + a->setIcon(pm); + QObject::connect(a, &QAction::triggered, + this, [=]() { caretColorActionClicked(a); } ); + + tb->setDefaultAction(a); + tb->setToolButtonStyle(Qt::ToolButtonIconOnly); + tb->setIconSize(iconSize); + + + layout->addWidget(tb, row, col); + + row++; + if (row >= maxRows) { + row = 0; + col++; + } + + tbWidgets.push_back(tb); + } + + WuQtUtilities::matchWidgetWidths(tbWidgets); + + widget->setFixedSize(widget->sizeHint()); + + return widget; +} + +void +WuQColorEditorWidget::caretColorActionClicked(QAction* action) +{ + CaretAssert(action); + + const int intData = action->data().toInt(); + bool validFlag(false); + const CaretColorEnum::Enum color = CaretColorEnum::fromIntegerCode(intData, + &validFlag); + if (validFlag) { + uint8_t rgba[4]; + CaretColorEnum::toRGBAByte(color, rgba); + m_currentColor.setRgb(rgba[0], rgba[1], rgba[2]); + + updateControls(); + emitColorChangedSignal(); + } +} + + + + + + +/* ======================================================================== */ +LinearEquationTransform::LinearEquationTransform(const float xMin, + const float xMax, + const float yMin, + const float yMax, + const float x0, + const float y0) +{ + const float dy(yMax - yMin); + const float dx(xMax - xMin); + if (dx == 0) { + return; + } + + const float slope(dy / dx); + const float intercept = y0 - (slope * x0); + + m_transform.scale(1.0, -slope); + m_transform.translate(0.0, -intercept); + + if (m_transform.isInvertible()) { + m_inverseTransform = m_transform.inverted(); + } +} + +LinearEquationTransform::~LinearEquationTransform() +{ + +} + +float +LinearEquationTransform::transformValue(const float value) const +{ + QPointF pIn(0, value); + QPointF pOut = m_transform.map(QPointF(0.0, value)); + + const float valueOut = pOut.y(); + return valueOut; +} + +float +LinearEquationTransform::inverseTransformValue(const float value) const +{ + QPointF pIn(0, value); + QPointF pOut = m_inverseTransform.map(QPointF(0.0, value)); + + const float valueOut = pOut.y(); + return valueOut; +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQColorEditorWidget.h connectome-workbench-1.5.0/src/GuiQt/WuQColorEditorWidget.h --- connectome-workbench-1.4.2/src/GuiQt/WuQColorEditorWidget.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQColorEditorWidget.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,188 @@ +#ifndef __WU_Q_COLOR_EDITOR_WIDGET_H__ +#define __WU_Q_COLOR_EDITOR_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include +#include + +class QAction; +class QLabel; +class QSlider; +class QSpinBox; +class QTransform; + +namespace caret { + + class LinearEquationTransform; + class WuQImageLabel; + + class WuQColorEditorWidget : public QWidget { + + Q_OBJECT + + public: + WuQColorEditorWidget(QWidget* parent = 0); + + virtual ~WuQColorEditorWidget(); + + WuQColorEditorWidget(const WuQColorEditorWidget&) = delete; + + WuQColorEditorWidget& operator=(const WuQColorEditorWidget&) = delete; + + QColor getColor() const; + + public slots: + void setCurrentColor(const QColor& color); + // ADD_NEW_METHODS_HERE + + signals: + void colorChanged(const QColor& color); + + private slots: + void hueChanged(int hue); + + void saturationChanged(int saturation); + + void valueChanged(int value); + + void redChanged(int red); + + void greenChanged(int green); + + void blueChanged(int blue); + + void hueSaturationLabelClicked(int x, int y); + + void valueLabelClicked(int x, int y); + + void caretColorActionClicked(QAction* action); + + void revertToOriginalColorToolButtonClicked(); + + private: + void updateControls(); + + void updateSliderAndSpinBox(QSlider* slider, + QSpinBox* spinBox, + const int value); + + QWidget* createControlsWidget(); + + QWidget* createHueSaturationColorLabel(); + + void updateHueSaturationToLabelTransforms(); + + QWidget* createValueColorLabel(); + + void updateValueToLabelXyTransform(); + + void updateValueColorLabel(); + + void updateHueSaturationLabel(); + + QWidget* createCaretColorNoNamesSelectionButtonsWidget(); + + void emitColorChangedSignal(); + + QSpinBox* m_hueSpinBox; + + QSlider* m_hueSlider; + + QSpinBox* m_saturationSpinBox; + + QSlider* m_saturationSlider; + + QSpinBox* m_valueSpinBox; + + QSlider* m_valueSlider; + + QSpinBox* m_redSpinBox; + + QSlider* m_redSlider; + + QSpinBox* m_greenSpinBox; + + QSlider* m_greenSlider; + + QSpinBox* m_blueSpinBox; + + QSlider* m_blueSlider; + + QWidget* m_currentColorSwatchWidget; + + QWidget* m_originalColorSwatchWidget; + + WuQImageLabel* m_hueSaturationColorLabel; + + QPixmap m_hueSaturationPixmap; + + WuQImageLabel* m_valueColorLabel; + + QColor m_currentColor; + + QColor m_originalColor; + + std::unique_ptr m_valueToLabelLinearTransform; + + QSize m_valueLabelSize; + + QSize m_hueSaturationLabelSize; + + std::unique_ptr m_hueToLabelLinearTransform; + + std::unique_ptr m_saturationToLabelLinearTransform; + + // ADD_NEW_MEMBERS_HERE + + }; + + class LinearEquationTransform { + public: + LinearEquationTransform(const float xMin, + const float xMax, + const float yMin, + const float yMax, + const float x0, + const float y0); + + ~LinearEquationTransform(); + + float transformValue(const float value) const; + + float inverseTransformValue(const float value) const; + + private: + QTransform m_transform; + + QTransform m_inverseTransform; + }; + +#ifdef __WU_Q_COLOR_EDITOR_WIDGET_DECLARE__ + // +#endif // __WU_Q_COLOR_EDITOR_WIDGET_DECLARE__ + +} // namespace +#endif //__WU_Q_COLOR_EDITOR_WIDGET_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQDoubleSpinBox.cxx connectome-workbench-1.5.0/src/GuiQt/WuQDoubleSpinBox.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQDoubleSpinBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQDoubleSpinBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -199,6 +199,7 @@ void WuQDoubleSpinBox::setDecimals(int prec) { + QSignalBlocker blocker(m_spinBox); m_decimalsMode = DecimalsMode::FIXED; m_spinBox->setDecimals(prec); } @@ -322,6 +323,7 @@ void WuQDoubleSpinBox::updateDecimalsForAutoMode() { + QSignalBlocker blocker(m_spinBox); static bool decimalsTestFlag = false; if (decimalsTestFlag) { testDigitsRightOfDecimal(); @@ -376,6 +378,7 @@ void WuQDoubleSpinBox::setPrefix(const QString &prefix) { + QSignalBlocker blocker(m_spinBox); m_spinBox->setPrefix(prefix); } @@ -391,6 +394,7 @@ WuQDoubleSpinBox::setRange(double minimum, double maximum) { + QSignalBlocker blocker(m_spinBox); m_minimumValue = minimum; m_maximumValue = maximum; m_rangeMode = RangeMode::INCLUSIVE; @@ -445,6 +449,7 @@ const double spinBoxMinimum, const double spinBoxMaximum) { + QSignalBlocker blocker(m_spinBox); m_minimumValue = dataMinimum; m_maximumValue = dataMaximum; m_rangeMode = RangeMode::EXCEEDABLE; @@ -539,6 +544,7 @@ void WuQDoubleSpinBox::setSingleStep(double value) { + QSignalBlocker blocker(m_spinBox); CaretAssert(value >= 0.0); m_spinBox->setSingleStep(value); @@ -575,6 +581,7 @@ void WuQDoubleSpinBox::updateSingleStepPercentage() { + QSignalBlocker blocker(m_spinBox); switch (m_singleStepMode) { case SingleStepMode::FIXED: break; @@ -605,6 +612,7 @@ void WuQDoubleSpinBox::setSuffix(const QString &suffix) { + QSignalBlocker blocker(m_spinBox); m_spinBox->setSuffix(suffix); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQImageLabel.cxx connectome-workbench-1.5.0/src/GuiQt/WuQImageLabel.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQImageLabel.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQImageLabel.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -20,6 +20,7 @@ /*LICENSE_END*/ #include +#include #define __WU_Q_IMAGE_LABEL_DECLARE__ #include "WuQImageLabel.h" @@ -196,6 +197,28 @@ * The mouse event. */ void +WuQImageLabel::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + const int dx = std::abs(m_mouseMaxX - m_mouseMinX); + const int dy = std::abs(m_mouseMaxY - m_mouseMinY); + + const int tolerance = 5; + + if ((dx < tolerance) + && (dy < tolerance)) { + emit doubleClicked(); + } + } +} + +/** + * Called when the mouse button is pressed. + * + * @param ev + * The mouse event. + */ +void WuQImageLabel::mousePressEvent(QMouseEvent* ev) { if (ev->button() == Qt::LeftButton) { @@ -224,6 +247,7 @@ if ((dx < tolerance) && (dy < tolerance)) { emit clicked(); + emit clickedXY(ev->x(), ev->y()); } } } diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQImageLabel.h connectome-workbench-1.5.0/src/GuiQt/WuQImageLabel.h --- connectome-workbench-1.4.2/src/GuiQt/WuQImageLabel.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQImageLabel.h 2021-02-16 19:46:47.000000000 +0000 @@ -68,6 +68,21 @@ */ void clicked(); + /** + * Emitted if the mouse button is clicked over this widget + * with the X, Y coordinates + */ + void clickedXY(int x, int y); + + /** + * Emitted if mouse is double-clicked over + * this widget (may also get clicked() signal. + */ + void doubleClicked(); + + protected: + void mouseDoubleClickEvent(QMouseEvent *event); + private: WuQImageLabel(const WuQImageLabel&); diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQMacroDialog.cxx connectome-workbench-1.5.0/src/GuiQt/WuQMacroDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQMacroDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQMacroDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -761,7 +761,7 @@ const QString windowText = m_runOptionsWindowComboBox->currentText(); m_runOptionsWindowComboBox->clear(); const std::vector windowIDs = WuQMacroManager::instance()->getMainWindowIdentifiers(); - for (const auto id : windowIDs) { + for (const auto& id : windowIDs) { m_runOptionsWindowComboBox->addItem(id); } m_runOptionsWindowComboBox->setCurrentText(windowText); diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQMacroManager.cxx connectome-workbench-1.5.0/src/GuiQt/WuQMacroManager.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQMacroManager.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQMacroManager.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -237,7 +237,7 @@ auto existingWatcher = m_signalWatchers.find(name); if (existingWatcher != m_signalWatchers.end()) { - CaretLogSevere("Object named \"" + CaretLogWarning("Object named \"" + name + "\" has already been connected for macros\n" + SystemUtilities::getBackTrace() @@ -255,6 +255,10 @@ widgetWatcher->setParent(this); m_signalWatchers.insert(std::make_pair(name, widgetWatcher)); + + QObject::connect(object, &QObject::destroyed, + this, &WuQMacroManager::objectBeingDestroyed); + return true; } else { @@ -309,6 +313,30 @@ } /** + * Called when a macro watch object is being destroyed + * @param object + * Qt object subclass that is being destroyed. + */ +void +WuQMacroManager::objectBeingDestroyed(QObject* object) +{ + if (object != NULL) { + const QString name(object->objectName()); + if (name.isEmpty()) { + CaretLogWarning("PROGRAM ERROR: Attempting to remove macro watcher for object with empty name."); + } + else { + const int32_t erasedCount(m_signalWatchers.erase(name)); + if (erasedCount == 0) { + CaretLogWarning("Failed to remove macro watcher for object named \"" + + name + + "\""); + } + } + } +} + +/** * Adds the given macro comand to the macro that is currently being * recorded. If no macro is being recorded, no action is taken. * diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQMacroManager.h connectome-workbench-1.5.0/src/GuiQt/WuQMacroManager.h --- connectome-workbench-1.4.2/src/GuiQt/WuQMacroManager.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQMacroManager.h 2021-02-16 19:46:47.000000000 +0000 @@ -224,6 +224,9 @@ const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut); + private slots: + void objectBeingDestroyed(QObject* object); + private: WuQMacroManager(const QString& name, QObject* parent = NULL); diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQMessageBox.cxx connectome-workbench-1.5.0/src/GuiQt/WuQMessageBox.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQMessageBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQMessageBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -161,6 +161,8 @@ * Message that is displayed. * @param informativeText * Displayed below 'text' if this is not empty. + * @param defaultButton + * The default button * @return * true if the Ok button was pressed else false * if the cancel button was pressed. @@ -168,7 +170,8 @@ bool WuQMessageBox::warningOkCancel(QWidget* parent, const QString& text, - const QString& informativeText) + const QString& informativeText, + const DefaultButtonOkCancel defaultButton) { QMessageBox msgBox(parent); msgBox.setIcon(QMessageBox::Warning); @@ -179,7 +182,14 @@ } msgBox.addButton(QMessageBox::Ok); msgBox.addButton(QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Ok); + switch (defaultButton) { + case DefaultButtonOkCancel::OK: + msgBox.setDefaultButton(QMessageBox::Ok); + break; + case DefaultButtonOkCancel::CANCEL: + msgBox.setDefaultButton(QMessageBox::Cancel); + break; + } msgBox.setEscapeButton(QMessageBox::Cancel); QMessageBox::StandardButton buttonPressed = @@ -905,3 +915,45 @@ msgBox.exec(); } +/** + * Display an error message box with the + * given text, detailed text, and an OK button. + * + * @param parent + * Parent on which message box is displayed. + * @param text + * Message that is displayed. + * @param detailedText + * Detailed text in a scroll region + */ +void +WuQMessageBox::errorDetailedTextOk(QWidget* parent, + const QString& text, + const QString& detailedText) +{ + QMessageBox msgBox(parent); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setWindowTitle(""); + msgBox.setText(text); + msgBox.setDetailedText(detailedText); + msgBox.addButton(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setEscapeButton(QMessageBox::Ok); + + /* + * Expaned the details region by clicking the "Show Details" button. + * From: https://stackoverflow.com/questions/36083551/qmessagebox-show-details/36084125#36084125 + */ + foreach (QAbstractButton *button, msgBox.buttons()) { + if (msgBox.buttonRole(button) == QMessageBox::ActionRole) { + if (button->text().startsWith("Show Detail")) { + button->click(); + } + break; + } + } + + msgBox.exec(); +} + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQMessageBox.h connectome-workbench-1.5.0/src/GuiQt/WuQMessageBox.h --- connectome-workbench-1.4.2/src/GuiQt/WuQMessageBox.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQMessageBox.h 2021-02-16 19:46:47.000000000 +0000 @@ -48,9 +48,18 @@ NO }; + enum class DefaultButtonOkCancel { + OK, + CANCEL + }; + static void errorOk(QWidget* parent, const QString& text); + static void errorDetailedTextOk(QWidget* parent, + const QString& text, + const QString& detailedText); + static void informationOk(QWidget* parent, const QString& text); @@ -78,7 +87,8 @@ static bool warningOkCancel(QWidget* parent, const QString& text, - const QString& informativeText); + const QString& informativeText, + const DefaultButtonOkCancel defaultButton = DefaultButtonOkCancel::OK); static bool warningYesNoWithDoNotShowAgain(QWidget* parent, const QString& uniqueIdentifier, diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQScrollArea.cxx connectome-workbench-1.5.0/src/GuiQt/WuQScrollArea.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQScrollArea.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQScrollArea.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_SCROLL_AREA_DECLARE__ +#include "WuQScrollArea.h" +#undef __WU_Q_SCROLL_AREA_DECLARE__ + +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WuQScrollArea + * \brief Scroll area that allows a specific fixed width and/or height + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param fixedWidth + * If positive, scroll area will be this widgth + * @param fixedHeight + * If positive, scroll area will be this height + */ +WuQScrollArea::WuQScrollArea(const int32_t fixedWidth, + const int32_t fixedHeight) +: QScrollArea(), +m_fixedWidth(fixedWidth), +m_fixedHeight(fixedHeight) +{ +} + +/** + * Destructor. + */ +WuQScrollArea::~WuQScrollArea() +{ +} + +/** + * @param fixedWidth + * If positive, scroll area will be this widgth + * @param fixedHeight + * If positive, scroll area will be this height + * @return New instance of scroll area + */ +WuQScrollArea* +WuQScrollArea::newInstance(const int32_t fixedWidth, + const int32_t fixedHeight) +{ + WuQScrollArea* sa = new WuQScrollArea(fixedWidth, + fixedHeight); + + /* + * These are virtual functions so cannot be called + * from a constructor + */ + QSizePolicy sp(sa->sizePolicy()); + if (fixedWidth > 0) { + sp.setHorizontalPolicy(QSizePolicy::Fixed); + } + if (fixedHeight > 0) { + sp.setVerticalPolicy(QSizePolicy::Fixed); + } + sa->setSizePolicy(sp); + + return sa; +} + +QSize +WuQScrollArea::sizeHint() const +{ + QSize sz = QScrollArea::sizeHint(); + + if (m_fixedWidth > 0) { + sz.setWidth(m_fixedWidth); + } + if (m_fixedHeight > 0) { + sz.setHeight(m_fixedHeight); + } + + return sz; +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQScrollArea.h connectome-workbench-1.5.0/src/GuiQt/WuQScrollArea.h --- connectome-workbench-1.4.2/src/GuiQt/WuQScrollArea.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQScrollArea.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,69 @@ +#ifndef __WU_Q_SCROLL_AREA_H__ +#define __WU_Q_SCROLL_AREA_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + + + +namespace caret { + + class WuQScrollArea : public QScrollArea { + + Q_OBJECT + + public: + static WuQScrollArea* newInstance(const int32_t fixedWidth, + const int32_t fixedHeight); + + virtual ~WuQScrollArea(); + + WuQScrollArea(const WuQScrollArea&) = delete; + + WuQScrollArea& operator=(const WuQScrollArea&) = delete; + + virtual QSize sizeHint() const override; + + // ADD_NEW_METHODS_HERE + + private: + WuQScrollArea(const int32_t fixedWidth, + const int32_t fixedHeight); + + const int32_t m_fixedWidth; + + const int32_t m_fixedHeight; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_SCROLL_AREA_DECLARE__ + // +#endif // __WU_Q_SCROLL_AREA_DECLARE__ + +} // namespace +#endif //__WU_Q_SCROLL_AREA_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQSpinBox.cxx connectome-workbench-1.5.0/src/GuiQt/WuQSpinBox.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQSpinBox.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQSpinBox.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -53,6 +53,11 @@ { } +/** + * Process key events (override parent class method) + * @param event + * The key event + */ void WuQSpinBox::keyPressEvent(QKeyEvent* event) { @@ -64,3 +69,23 @@ QSpinBox::keyPressEvent(event); } +/** + * Process timer events (override parent class method) + * + * @param event + * The key event + */ +void +WuQSpinBox::timerEvent(QTimerEvent* event) +{ + /* + * If a method called by valueChanged() takes "too long" to execute + * a second valueChanged() signal is emitted by QSpinBox. May be + * related to an "auto repeat timer" as if user held down the button. + * This is QTBUG-14259: https://bugreports.qt.io/browse/QTBUG-14259 + * + * So, ignore timer events + */ + QWidget::timerEvent(event); +} + diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQSpinBox.h connectome-workbench-1.5.0/src/GuiQt/WuQSpinBox.h --- connectome-workbench-1.4.2/src/GuiQt/WuQSpinBox.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQSpinBox.h 2021-02-16 19:46:47.000000000 +0000 @@ -53,6 +53,8 @@ protected: void keyPressEvent(QKeyEvent* event) override; + void timerEvent(QTimerEvent* event) override; + private: // ADD_NEW_MEMBERS_HERE diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQSpinBoxOddValue.cxx connectome-workbench-1.5.0/src/GuiQt/WuQSpinBoxOddValue.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQSpinBoxOddValue.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQSpinBoxOddValue.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -272,7 +272,7 @@ WuQSpinBoxOddValueSpinBox::WuQSpinBoxOddValueSpinBox(QWidget* parent) : QSpinBox(parent) { - lineEdit()->setEnabled(false); + /* Line edit is not disabled, otherwise it appears disabled but arrow buttons still function */ } /** diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQTabBar.cxx connectome-workbench-1.5.0/src/GuiQt/WuQTabBar.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQTabBar.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQTabBar.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -59,7 +59,7 @@ void WuQTabBar::mousePressEvent(QMouseEvent* event) { - emit mousePressedSignal(); + emit mousePressedSignal(event); QTabBar::mousePressEvent(event); } @@ -73,7 +73,7 @@ void WuQTabBar::mouseReleaseEvent(QMouseEvent* event) { - emit mouseReleasedSignal(); + emit mouseReleasedSignal(event); QTabBar::mouseReleaseEvent(event); } diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQTabBar.h connectome-workbench-1.5.0/src/GuiQt/WuQTabBar.h --- connectome-workbench-1.4.2/src/GuiQt/WuQTabBar.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQTabBar.h 2021-02-16 19:46:47.000000000 +0000 @@ -48,9 +48,9 @@ // ADD_NEW_METHODS_HERE signals: - void mousePressedSignal(); + void mousePressedSignal(QMouseEvent* event); - void mouseReleasedSignal(); + void mouseReleasedSignal(QMouseEvent* event); protected: virtual void mousePressEvent(QMouseEvent* event) override; diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQToolTipHelper.cxx connectome-workbench-1.5.0/src/GuiQt/WuQToolTipHelper.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQToolTipHelper.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQToolTipHelper.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,250 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2021 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_TOOL_TIP_HELPER_DECLARE__ +#include "WuQToolTipHelper.h" +#undef __WU_Q_TOOL_TIP_HELPER_DECLARE__ + +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::WuQToolTipHelper + * \brief Assists with tooltip for GUI components + * \ingroup GuiQt + */ + +/** + * @returns A tooltip helper for the given menu. The helper will be a child of the menu + * so that when the menu is destroyed, the helper will also be destoryed. Thus, the + * returned value can be ignored. + * + * @param menu + * Menu that receives a tooltip helper + */ +WuQToolTipHelper* +WuQToolTipHelper::newInstanceForMenu(QMenu* menu) +{ + CaretAssert(menu); + + WuQToolTipHelper* helper = new WuQToolTipHelper(Mode::MENU_TOOLTIPS, + menu); + + return helper; +} + + +/** + * Constructor. + * @param mode + * The mode + * @param parent + * The parent gui component that uses the tool tip helper + */ +WuQToolTipHelper::WuQToolTipHelper(const Mode mode, + QObject* parent) +: QObject(parent), +m_mode(mode) +{ + CaretAssert(parent); + + m_timer = new QTimer(this); + QObject::connect(m_timer, &QTimer::timeout, + this, &WuQToolTipHelper::showToolTipAfterTimeout); + + switch (mode) { + case Mode::MENU_TOOLTIPS: + { + m_menu = qobject_cast(parent); + CaretAssert(m_menu); + if (m_menu == NULL) { + return; + } + + /* + * Show tooltip when menu action is under mouse + */ + QObject::connect(m_menu, &QMenu::hovered, + this, &WuQToolTipHelper::menuActionHovered); + + /* + * ToolTip may remain up after menu is closed. + * So, hide tooltip as menu is closing. + */ + QObject::connect(m_menu, &QMenu::aboutToHide, + this, &WuQToolTipHelper::hideToolTip); + } + break; + } +} + +/** + * Destructor. + */ +WuQToolTipHelper::~WuQToolTipHelper() +{ +} + +/** + * Slot called when the mouse is over a menu item + * @param action + * Action that is under mouse. If action has non-empty tooltip text, a tooltip is displayed near the menu + */ +void +WuQToolTipHelper::menuActionHovered(QAction* action) +{ + CaretAssert(action); + + AString text(action->toolTip()); + + if (m_useWhatsThisFlag) { + if (text.isEmpty()) { + hideToolTip(); + } + else { + /* + * If an action does not have a tooltip, Qt + * return's the action's text as a tooltip. + * In this case, do not show tooltip. + */ + if (action->text() == action->toolTip()) { + hideToolTip(); + return; + } + else { + /* + * If tooltip is not formatted, word wrap the text + */ + if ( ! text.toLower().contains("")) { + text = ("" + text + ""); //WuQtUtilities::createWordWrappedToolTipText(text); + } + + /* + * Show tooltip containing scene description below mouse + * (note: positive Y is down in Qt coords) + */ + const QPoint toolTipXY(QCursor::pos() + + QPoint(0, 15)); + QWhatsThis::showText(toolTipXY, + text); + } + } + } + else { + if (text.isEmpty()) { + hideToolTip(); + } + else { + /* + * When an action has not been given a tooltip, + * calling QAction::toolTip() returns QAction::text(). + * In this case, do not show tooltip since it + * is NOT useful (same as the menu text) + */ + if (action->text() == action->toolTip()) { + hideToolTip(); + return; + } + else { + /* + * If tooltip is not formatted, using HTML word wraps the text + */ + if ( ! text.toLower().contains("")) { + text = ("" + text + ""); + } + + /* + * If text has not changed, tooltip is displayed + * so don't need to display again. + */ + if (text != m_toolTipText) { + hideToolTip(); + + m_toolTipText = text; + + /* + * Set position of tooltip below mouse + * (note: positive Y is down in Qt coords) + */ + const QPoint toolTipPos(QCursor::pos() + + QPoint(0, 15)); + m_toolTipXY[0] = toolTipPos.x(); + m_toolTipXY[1] = toolTipPos.y(); + + /* + * Set delay for display of tooltip + */ + if ( ! m_timer->isActive()) { + const int32_t timeMilliseconds(1000); + m_timer->start(timeMilliseconds); + } + + } + } + } + } +} + +/** + * Called to hide tool tip when menu is closing. + */ +void +WuQToolTipHelper::hideToolTip() +{ + if (m_useWhatsThisFlag) { + QWhatsThis::hideText(); + } + else { + QToolTip::hideText(); + } + + m_toolTipText.clear(); + m_timer->stop(); +} + +/** + * Called when timer times out to show the tooltip + */ +void +WuQToolTipHelper::showToolTipAfterTimeout() +{ + QPoint pos(m_toolTipXY[0], + m_toolTipXY[1]); + QToolTip::showText(pos, + m_toolTipText); +} + +bool +WuQToolTipHelper::event(QEvent* event) +{ + //std::cout << "Event: " << (int)event->type() << std::endl << std::flush; + return QObject::event(event); +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQToolTipHelper.h connectome-workbench-1.5.0/src/GuiQt/WuQToolTipHelper.h --- connectome-workbench-1.4.2/src/GuiQt/WuQToolTipHelper.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQToolTipHelper.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,91 @@ +#ifndef __WU_Q_TOOL_TIP_HELPER_H__ +#define __WU_Q_TOOL_TIP_HELPER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2021 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include + +class QAction; +class QMenu; +class QTimer; + +namespace caret { + + class WuQToolTipHelper : public QObject { + + Q_OBJECT + + public: + static WuQToolTipHelper* newInstanceForMenu(QMenu* menu); + + virtual ~WuQToolTipHelper(); + + WuQToolTipHelper(const WuQToolTipHelper&) = delete; + + WuQToolTipHelper& operator=(const WuQToolTipHelper&) = delete; + + + // ADD_NEW_METHODS_HERE + + private slots: + void menuActionHovered(QAction* action); + + void hideToolTip(); + + void showToolTipAfterTimeout(); + + protected: + virtual bool event(QEvent* event) override; + + private: + enum class Mode { + MENU_TOOLTIPS + }; + + WuQToolTipHelper(const Mode mode, + QObject* parent); + + const Mode m_mode; + + QMenu* m_menu = NULL; + + bool m_useWhatsThisFlag = false; + + QTimer* m_timer = NULL; + + QString m_toolTipText; + + std::array m_toolTipXY; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_TOOL_TIP_HELPER_DECLARE__ + // +#endif // __WU_Q_TOOL_TIP_HELPER_DECLARE__ + +} // namespace +#endif //__WU_Q_TOOL_TIP_HELPER_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQtUtilities.cxx connectome-workbench-1.5.0/src/GuiQt/WuQtUtilities.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQtUtilities.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQtUtilities.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -926,27 +927,67 @@ QWidget* w9, QWidget* w10) { - const int maxHeight = getMaximumWidgetHeight(w1, - w2, - w3, - w4, - w5, - w6, - w7, - w8, - w9, - w10); + std::vector widgets; + + if (w1 != NULL) widgets.push_back(w1); + if (w2 != NULL) widgets.push_back(w2); + if (w3 != NULL) widgets.push_back(w3); + if (w4 != NULL) widgets.push_back(w4); + if (w5 != NULL) widgets.push_back(w5); + if (w6 != NULL) widgets.push_back(w6); + if (w7 != NULL) widgets.push_back(w7); + if (w8 != NULL) widgets.push_back(w8); + if (w9 != NULL) widgets.push_back(w9); + if (w10 != NULL) widgets.push_back(w10); + + matchWidgetHeights(widgets); + +// const int maxHeight = getMaximumWidgetHeight(w1, +// w2, +// w3, +// w4, +// w5, +// w6, +// w7, +// w8, +// w9, +// w10); +// if (maxHeight > 0) { +// w1->setFixedHeight(maxHeight); +// w2->setFixedHeight(maxHeight); +// if (w3 != NULL) w3->setFixedHeight(maxHeight); +// if (w4 != NULL) w4->setFixedHeight(maxHeight); +// if (w5 != NULL) w5->setFixedHeight(maxHeight); +// if (w6 != NULL) w6->setFixedHeight(maxHeight); +// if (w7 != NULL) w7->setFixedHeight(maxHeight); +// if (w8 != NULL) w8->setFixedHeight(maxHeight); +// if (w9 != NULL) w9->setFixedHeight(maxHeight); +// if (w10 != NULL) w10->setFixedHeight(maxHeight); +// } +} + +/** + * Find the widget with the maximum height in its + * size hint. Apply this height to all of the widgets. + + * @param widgets + * Widgets that have height matched + */ +void +WuQtUtilities::matchWidgetHeights(std::vector& widgets) +{ + int maxHeight = 0; + const int num = widgets.size(); + for (auto w : widgets) { + CaretAssert(w); + maxHeight = std::max(maxHeight, + w->sizeHint().height()); + } + if (maxHeight > 0) { - w1->setFixedHeight(maxHeight); - w2->setFixedHeight(maxHeight); - if (w3 != NULL) w3->setFixedHeight(maxHeight); - if (w4 != NULL) w4->setFixedHeight(maxHeight); - if (w5 != NULL) w5->setFixedHeight(maxHeight); - if (w6 != NULL) w6->setFixedHeight(maxHeight); - if (w7 != NULL) w7->setFixedHeight(maxHeight); - if (w8 != NULL) w8->setFixedHeight(maxHeight); - if (w9 != NULL) w9->setFixedHeight(maxHeight); - if (w10 != NULL) w10->setFixedHeight(maxHeight); + for (int i = 0; i < num; i++) { + widgets[i]->setFixedHeight(maxHeight); + } } } @@ -977,7 +1018,7 @@ QWidget* w9, QWidget* w10) { - QVector widgets; + std::vector widgets; if (w1 != NULL) widgets.push_back(w1); if (w2 != NULL) widgets.push_back(w2); @@ -990,13 +1031,25 @@ if (w9 != NULL) widgets.push_back(w9); if (w10 != NULL) widgets.push_back(w10); + matchWidgetWidths(widgets); +} + +/** + * Find the widget with the maximum width in its + * size hint. Apply this width to all of the widgets. + + * @param widgets + * Widgets that have width matched + */ +void +WuQtUtilities::matchWidgetWidths(std::vector& widgets) +{ int maxWidth = 0; const int num = widgets.size(); - for (int i = 0; i < num; i++) { - const int w = widgets[i]->sizeHint().width(); - if (w > maxWidth) { - maxWidth = w; - } + for (auto w : widgets) { + CaretAssert(w); + maxWidth = std::max(maxWidth, + w->sizeHint().width()); } if (maxWidth > 0) { @@ -1007,6 +1060,21 @@ } /** + * Make all of the widgets the same width and height + * using width from widest widget and height from tallest + * widget. + + * @param widgets + * Widgets that have sizes matched + */ +void +WuQtUtilities::matchWidgetSizes(std::vector& widgets) +{ + matchWidgetWidths(widgets); + matchWidgetHeights(widgets); +} + +/** * Set the margins and spacing for a layout. * @param layout * Layout that has margins and spacings set. @@ -1695,5 +1763,26 @@ void WuQtUtilities::setToolButtonStyleForQt5Mac(QToolButton*) { } #endif +/** + * Rename items in combox box by replacing 'before' in the names with 'after' + * @param comboBox + * The combo box + * @param before + * If this text is found in the name it is replaced with 'after' + * @param after + * Text that replaces 'before' + */ +void +WuQtUtilities::replaceComboBoxItemNames(QComboBox* comboBox, + const QString& before, + const QString& after) +{ + CaretAssert(comboBox); + const int32_t numItems = comboBox->count(); + for (int32_t i = 0; i < numItems; i++) { + QString newName = comboBox->itemText(i).replace(before, after); + comboBox->setItemText(i, newName); + } +} diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQtUtilities.h connectome-workbench-1.5.0/src/GuiQt/WuQtUtilities.h --- connectome-workbench-1.4.2/src/GuiQt/WuQtUtilities.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQtUtilities.h 2021-02-16 19:46:47.000000000 +0000 @@ -33,6 +33,7 @@ class QAction; class QBoxLayout; +class QComboBox; class QDialog; class QIcon; class QKeySequence; @@ -186,6 +187,8 @@ QWidget* w9 = 0, QWidget* w10 = 0); + static void matchWidgetHeights(std::vector& widgets); + static void matchWidgetWidths(QWidget* w1, QWidget* w2, QWidget* w3 = 0, @@ -197,6 +200,10 @@ QWidget* w9 = 0, QWidget* w10 = 0); + static void matchWidgetWidths(std::vector& widgets); + + static void matchWidgetSizes(std::vector& widgets); + static void setLayoutSpacingAndMargins(QLayout* layout, const int spacing, const int contentsMargin); @@ -228,6 +235,10 @@ static void setToolButtonStyleForQt5Mac(QToolButton* toolButton); + static void replaceComboBoxItemNames(QComboBox* comboBox, + const QString& before, + const QString& after); + private: static QSharedPointer createPixmapWidgetPainterPrivate(const QWidget* widget, QPixmap& pixmap, diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQValueChangedSignalWatcher.cxx connectome-workbench-1.5.0/src/GuiQt/WuQValueChangedSignalWatcher.cxx --- connectome-workbench-1.4.2/src/GuiQt/WuQValueChangedSignalWatcher.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQValueChangedSignalWatcher.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,136 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_VALUE_CHANGED_SIGNAL_WATCHER_DECLARE__ +#include "WuQValueChangedSignalWatcher.h" +#undef __WU_Q_VALUE_CHANGED_SIGNAL_WATCHER_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +using namespace caret; + +#include +#include +#include +#include + +#include "CaretColorEnumComboBox.h" + +/** + * \class caret::WuQValueChangedSignalWatcher + * \brief Simplifies connecting many value changed signals to one slot + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param parent + * The parent object + */ +WuQValueChangedSignalWatcher::WuQValueChangedSignalWatcher(QObject* parent) +: QObject(parent) +{ + +} + +/** + * Destructor. + */ +WuQValueChangedSignalWatcher::~WuQValueChangedSignalWatcher() +{ +} + +/** + * Watch for value changed signal from the given object. If the object is not + * supported a message is logged. + * + * @param object + * Object whose signal are watched for value changed + */ +void +WuQValueChangedSignalWatcher::addObject(QObject* object) +{ + CaretAssert(object); + + QCheckBox* checkBox = qobject_cast(object); + if (checkBox != NULL) { + QObject::connect(checkBox, &QCheckBox::clicked, + this, [=](bool) { emit valueChanged(); }); + m_widgets.push_back(checkBox); + return; + } + + QDoubleSpinBox* doubleSpinBox = qobject_cast(object); + if (doubleSpinBox != NULL) { + QObject::connect(doubleSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, [=](double) { emit valueChanged(); }); + m_widgets.push_back(doubleSpinBox); + return; + } + + CaretColorEnumComboBox* caretColorComboBox = qobject_cast(object); + if (caretColorComboBox != NULL) { + QObject::connect(caretColorComboBox, &CaretColorEnumComboBox::colorSelected, + this, [=](const CaretColorEnum::Enum) { emit valueChanged(); }); + m_widgets.push_back(caretColorComboBox->getComboBox()); + return; + } + + /* must be after CaretColorEnumComboBox as it is child of QComboBox */ + QComboBox* comboBox = qobject_cast(object); + if (comboBox != NULL) { + QObject::connect(comboBox, QOverload::of(&QComboBox::activated), + this, [=](int) { emit valueChanged(); }); + m_widgets.push_back(comboBox); + return; + } + + QSpinBox* spinBox = qobject_cast(object); + if (spinBox != NULL) { + QObject::connect(spinBox, QOverload::of(&QSpinBox::valueChanged), + this, [=](int) { emit valueChanged(); }); + m_widgets.push_back(spinBox); + return; + } + + const QString msg("Object of class " + + QString(object->metaObject()->className()) + + " not supported by WuQValueChangedSignalWatcher"); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + +} + +/** + * Set any widgets visible status + * @param status + * New visibility status + */ +void +WuQValueChangedSignalWatcher::setWidgetsVisible(const bool status) +{ + for (QWidget* w : m_widgets) { + w->setVisible(status); + } +} + + + diff -Nru connectome-workbench-1.4.2/src/GuiQt/WuQValueChangedSignalWatcher.h connectome-workbench-1.5.0/src/GuiQt/WuQValueChangedSignalWatcher.h --- connectome-workbench-1.4.2/src/GuiQt/WuQValueChangedSignalWatcher.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/WuQValueChangedSignalWatcher.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,76 @@ +#ifndef __WU_Q_VALUE_CHANGED_SIGNAL_WATCHER_H__ +#define __WU_Q_VALUE_CHANGED_SIGNAL_WATCHER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include + +#include + +class QWidget; + +namespace caret { + + class WuQValueChangedSignalWatcher : public QObject { + + Q_OBJECT + + public: + WuQValueChangedSignalWatcher(QObject* parent); + + virtual ~WuQValueChangedSignalWatcher(); + + WuQValueChangedSignalWatcher(const WuQValueChangedSignalWatcher&) = delete; + + WuQValueChangedSignalWatcher& operator=(const WuQValueChangedSignalWatcher&) = delete; + + void addObject(QObject* object); + + void setWidgetsVisible(const bool status); + + signals: + void valueChanged(); + +// private slots: +// void booChanged(bool); +// +// void intChanged(int); +// +// void doubleChanged(double); + + // ADD_NEW_METHODS_HERE + + private: + std::vector m_widgets; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_VALUE_CHANGED_SIGNAL_WATCHER_DECLARE__ + // +#endif // __WU_Q_VALUE_CHANGED_SIGNAL_WATCHER_DECLARE__ + +} // namespace +#endif //__WU_Q_VALUE_CHANGED_SIGNAL_WATCHER_H__ diff -Nru connectome-workbench-1.4.2/src/GuiQt/ZipSceneFileDialog.cxx connectome-workbench-1.5.0/src/GuiQt/ZipSceneFileDialog.cxx --- connectome-workbench-1.4.2/src/GuiQt/ZipSceneFileDialog.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/GuiQt/ZipSceneFileDialog.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -37,11 +37,13 @@ #include "CaretAssert.h" #include "CaretFileDialog.h" #include "CaretLogger.h" +#include "CaretPreferences.h" #include "CursorDisplayScoped.h" #include "DataFileException.h" #include "FileInformation.h" #include "GuiManager.h" #include "SceneBasePathWidget.h" +#include "SessionManager.h" #include "SceneFile.h" #include "SystemUtilities.h" #include "WuQMessageBox.h" @@ -255,7 +257,12 @@ zipFileName, errorMessage); - if ( ! successFlag) { + if (successFlag) { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + CaretAssert(prefs); + prefs->addToRecentFilesAndOrDirectories(m_sceneFile->getFileName()); + } + else { if (errorMessage.isEmpty()) { errorMessage = "Zipping scene file failed with unknown error."; } diff -Nru connectome-workbench-1.4.2/src/Nifti/Matrix4x4.cxx connectome-workbench-1.5.0/src/Nifti/Matrix4x4.cxx --- connectome-workbench-1.4.2/src/Nifti/Matrix4x4.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Nifti/Matrix4x4.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -75,7 +75,8 @@ * */ Matrix4x4::Matrix4x4() - : CaretObject() + : Matrix4x4Interface(), + CaretObject() { this->initializeMembersMatrix4x4(); } @@ -92,7 +93,8 @@ * @param Object that is copied. */ Matrix4x4::Matrix4x4(const Matrix4x4& o) - : CaretObject(o) + : Matrix4x4Interface(o), + CaretObject(o) { this->initializeMembersMatrix4x4(); this->copyHelper(o); diff -Nru connectome-workbench-1.4.2/src/Nifti/Matrix4x4.h connectome-workbench-1.5.0/src/Nifti/Matrix4x4.h --- connectome-workbench-1.4.2/src/Nifti/Matrix4x4.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Nifti/Matrix4x4.h 2021-02-16 19:46:47.000000000 +0000 @@ -23,6 +23,7 @@ #include "CaretObject.h" +#include "Matrix4x4Interface.h" #include #include @@ -35,7 +36,7 @@ /** * A 4x4 homogeneous transformation matrix. */ -class Matrix4x4 : public CaretObject { +class Matrix4x4 : public Matrix4x4Interface, public CaretObject { public: Matrix4x4(); @@ -132,7 +133,7 @@ void multiplyPoint4(float p[4]) const; - void multiplyPoint3(float p[3]) const; + virtual void multiplyPoint3(float p[3]) const override; void multiplyPoint3(double p[3]) const; diff -Nru connectome-workbench-1.4.2/src/Nifti/NiftiHeader.cxx connectome-workbench-1.5.0/src/Nifti/NiftiHeader.cxx --- connectome-workbench-1.4.2/src/Nifti/NiftiHeader.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Nifti/NiftiHeader.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -297,6 +297,13 @@ return ret.getMatrix(); } +VolumeSpace NiftiHeader::getVolumeSpace() const +{ + auto myDims = getDimensions(); + myDims.resize(3, 1);//ensure 3 dimensions + return VolumeSpace(myDims.data(), getSForm()); +} + double NiftiHeader::getTimeStep() const { int timeUnit = XYZT_TO_TIME(m_header.xyzt_units); @@ -810,7 +817,7 @@ { Quirks ret; if (header.sizeof_hdr != sizeof(nifti_2_header)) throw DataFileException(filename, "incorrect sizeof_hdr"); - const char magic[] = "n+2\0\r\n\032\n";//only support single-file nifti + const char magic[] = "n+2\0\r\n\032\n";//only support single-file nifti, magic string detailed at https://www.nitrc.org/forum/forum.php?thread_id=2148&forum_id=1941 for (int i = 0; i < 8; ++i) { if (header.magic[i] != magic[i]) throw DataFileException(filename, "incorrect magic"); diff -Nru connectome-workbench-1.4.2/src/Nifti/NiftiHeader.h connectome-workbench-1.5.0/src/Nifti/NiftiHeader.h --- connectome-workbench-1.4.2/src/Nifti/NiftiHeader.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Nifti/NiftiHeader.h 2021-02-16 19:46:47.000000000 +0000 @@ -22,7 +22,7 @@ /*LICENSE_END*/ #include "CaretBinaryFile.h" -#include "VolumeBase.h" //for AbstractHeader, AbstravtVolumeExtension +#include "VolumeBase.h" //for AbstractHeader, AbstractVolumeExtension #include "nifti1.h" #include "nifti2.h" @@ -55,6 +55,7 @@ std::vector getDimensions() const; std::vector > getSForm() const; + VolumeSpace getVolumeSpace() const;//convenience function double getTimeStep() const;//seconds int64_t getDataOffset() const { return m_header.vox_offset; } int16_t getDataType() const { return m_header.datatype; } diff -Nru connectome-workbench-1.4.2/src/Operations/OperationBorderFileExportToCaret5.cxx connectome-workbench-1.5.0/src/Operations/OperationBorderFileExportToCaret5.cxx --- connectome-workbench-1.4.2/src/Operations/OperationBorderFileExportToCaret5.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationBorderFileExportToCaret5.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -121,7 +121,7 @@ AString outputCaret5FilePrefix = FileInformation(myParams->getString(2)).getAbsoluteFilePath(); std::vector allSurfaces; - const std::vector& surfaceInputs = *(myParams->getRepeatableParameterInstances(3)); + const std::vector& surfaceInputs = myParams->getRepeatableParameterInstances(3); const int32_t numSurfaceInputs = static_cast(surfaceInputs.size()); for (int32_t iSurf = 0; iSurf < numSurfaceInputs; iSurf++) { SurfaceFile* sf = surfaceInputs[iSurf]->getSurface(1); diff -Nru connectome-workbench-1.4.2/src/Operations/OperationBorderMerge.cxx connectome-workbench-1.5.0/src/Operations/OperationBorderMerge.cxx --- connectome-workbench-1.4.2/src/Operations/OperationBorderMerge.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationBorderMerge.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -68,7 +68,7 @@ { LevelProgress myProgress(myProgObj); BorderFile* outFile = myParams->getOutputBorder(1); - const vector& borderInst = *(myParams->getRepeatableParameterInstances(2)); + const vector& borderInst = myParams->getRepeatableParameterInstances(2); int numInputs = (int)borderInst.size(); if (numInputs < 1) throw OperationException("no inputs specified"); for (int i = 0; i < numInputs; ++i) @@ -91,7 +91,7 @@ outFile->getClassColorTable()->append(*(input->getClassColorTable()));//let the append logic deal with conflicts outFile->getNameColorTable()->append(*(input->getNameColorTable()));//we don't need the return values, as the numbers in the label tables are meaningless int numBorderParts = input->getNumberOfBorders(); - const vector& selectOpts = *(borderInst[i]->getRepeatableParameterInstances(2)); + const vector& selectOpts = borderInst[i]->getRepeatableParameterInstances(2); int numSelectOpts = (int)selectOpts.size(); if (numSelectOpts > 0) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx connectome-workbench-1.5.0/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx --- connectome-workbench-1.4.2/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -79,6 +79,9 @@ volOpt->addVolumeParameter(2, "volume-in", "the input volume file"); volOpt->createOptionalParameter(3, "-from-cropped", "the input is cropped to the size of the volume structure"); + OptionalParameter* collideOpt = ret->createOptionalParameter(9, "-label-collision", "how to handle conflicts between label keys"); + collideOpt->addStringParameter(1, "action", "'ERROR', 'SURFACES_FIRST', or 'LEGACY', default 'ERROR', use 'LEGACY' to match v1.4.2 and earlier"); + AString helpText = AString("This command helps you make a new dscalar, dtseries, or dlabel cifti file that matches the brainordinate space used in another cifti file. ") + "The template file must have the desired brainordinate space in the mapping along the column direction (for dtseries, dscalar, dlabel, and symmetric dconn this is always the case). " + "All input cifti files must have a brain models mapping along column and use the same volume space and/or surface vertex count as the template for structures that they contain. " + @@ -113,11 +116,23 @@ VOLUME_ALL }; + enum LabelConflictLogic + { + ERROR, + SURFACES_FIRST, + LEGACY + }; + struct DataSourceInfo { DataSourceType type; int64_t index;//for repeatable options + DataSourceInfo(const DataSourceType myType, const int64_t myIndex) + { + type = myType; + index = myIndex; + }; DataSourceInfo() { type = NONE; @@ -131,6 +146,22 @@ LevelProgress myProgress(myProgObj); const CiftiFile* templateCifti = myParams->getCifti(1); CiftiFile* ciftiOut = myParams->getOutputCifti(2); + LabelConflictLogic conflictLogic = ERROR; + OptionalParameter* collideOpt = myParams->getOptionalParameter(9); + if (collideOpt->m_present) + { + AString collideStr = collideOpt->getString(1); + if (collideStr == "ERROR") + { + conflictLogic = ERROR; + } else if (collideStr == "SURFACES_FIRST") { + conflictLogic = SURFACES_FIRST; + } else if (collideStr == "LEGACY") { + conflictLogic = LEGACY; + } else { + throw OperationException("incorrect string for label collision option"); + } + } const CiftiXML& templateXML = templateCifti->getCiftiXML(); if (templateXML.getNumberOfDimensions() != 2) { @@ -185,7 +216,7 @@ volInfo[i].type = VOLUME_ALL; } } - const vector& ciftiInstances = *(myParams->getRepeatableParameterInstances(5)); + const vector& ciftiInstances = myParams->getRepeatableParameterInstances(5); for (int instance = 0; instance < (int)ciftiInstances.size(); ++instance) { const CiftiFile* thisCifti = ciftiInstances[instance]->getCifti(1); @@ -289,7 +320,7 @@ ciftiNameFile = thisCifti;//cifti trumps everything, if it has scalar or label or parcels } } - const vector& metricInstances = *(myParams->getRepeatableParameterInstances(6)); + const vector& metricInstances = myParams->getRepeatableParameterInstances(6); for (int instance = 0; instance < (int)metricInstances.size(); ++instance) { if (labelMode == 1) throw OperationException("-metric option specified when other inputs are label-type files"); @@ -329,9 +360,9 @@ checkStructureMatch(thisMetric, thisStruct, "metric file '" + thisMetric->getFileName() + "'", "the -metric option specified"); surfInfo[outIndex].type = METRIC; surfInfo[outIndex].index = instance; - if (instance == 0) nameFile = thisMetric;//-metric trumps -volume-all for names, but use the first -metric + if (nameFile == NULL) nameFile = thisMetric; } - const vector& labelInstances = *(myParams->getRepeatableParameterInstances(7)); + const vector& labelInstances = myParams->getRepeatableParameterInstances(7); for (int instance = 0; instance < (int)labelInstances.size(); ++instance) { if (labelMode == 0) throw OperationException("-label option specified when other inputs are real-valued files"); @@ -371,9 +402,9 @@ checkStructureMatch(thisLabel, thisStruct, "label file '" + thisLabel->getFileName() + "'", "the -label option specified"); surfInfo[outIndex].type = LABEL; surfInfo[outIndex].index = instance; - if (instance == 0) nameFile = thisLabel;//-label trumps -volume-all for names, but use the first -label + if (nameFile == NULL) nameFile = thisLabel; } - const vector& volumeInstances = *(myParams->getRepeatableParameterInstances(8)); + const vector& volumeInstances = myParams->getRepeatableParameterInstances(8); for (int instance = 0; instance < (int)volumeInstances.size(); ++instance) { AString structString = volumeInstances[instance]->getString(1); @@ -510,111 +541,129 @@ } } ciftiOut->setCiftiXML(outXML); - for (int whichStruct = 0; whichStruct < (int)surfStructures.size(); ++whichStruct) + vector legacyOrder = surfInfo; + vector legacyIsSurface(surfInfo.size(), true);//need to separately track what the type of model is + vector legacyStructure = surfStructures;//and its structure + if (volStructures.size() > 0 && volInfo[0].type == VOLUME_ALL) + { + legacyOrder.push_back(volInfo[0]);//record order of things to do, not one per every structure + legacyIsSurface.push_back(false); + legacyStructure.push_back(StructureEnum::ALL);//doesn't matter + } else { + legacyOrder.insert(legacyOrder.end(), volInfo.begin(), volInfo.end());//there doesn't appear to be an append function + legacyIsSurface.resize(legacyOrder.size(), false); + legacyStructure.insert(legacyStructure.end(), volStructures.begin(), volStructures.end()); + } + bool errorOnLabelConflict = (conflictLogic == ERROR); + auto useOrder = legacyOrder; + auto useIsSurface = legacyIsSurface; + auto useStructure = legacyStructure; + switch (conflictLogic) + { + case ERROR: + case LEGACY: + break; + case SURFACES_FIRST: + useOrder.assign(legacyOrder.rbegin(), legacyOrder.rend());//basically, legacy order had the right idea, but the label conflict logic reversed it + useIsSurface.assign(legacyIsSurface.rbegin(), legacyIsSurface.rend()); + useStructure.assign(legacyStructure.rbegin(), legacyStructure.rend()); + } + for (int i = 0; i < int(useOrder.size()); ++i) { - switch (surfInfo[whichStruct].type) + switch(useOrder[i].type) { case CIFTI: { - const CiftiFile* toUse = ciftiInstances[surfInfo[whichStruct].index]->getCifti(1); - if (labelMode == 1) + if (useIsSurface[i]) { - LabelFile tempLabel; - AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempLabel); - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempLabel); + const CiftiFile* toUse = ciftiInstances[useOrder[i].index]->getCifti(1); + if (labelMode == 1) + { + LabelFile tempLabel; + AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, useStructure[i], &tempLabel); + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], &tempLabel, false, errorOnLabelConflict); + } else { + MetricFile tempMetric; + AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, useStructure[i], &tempMetric); + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], &tempMetric); + } } else { - MetricFile tempMetric; - AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempMetric); - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempMetric); + const CiftiFile* toUse = ciftiInstances[useOrder[i].index]->getCifti(1); + VolumeFile tempVol; + int64_t dims1[3], dims2[3], off1[3], off2[3];//check if the cropped space matches, if so we can save memory easily by using the crop argument (and this is also the common case) + vector > sform1, sform2; + AlgorithmCiftiSeparate::getCroppedVolSpace(toUse, CiftiXML::ALONG_COLUMN, useStructure[i], dims1, sform1, off1); + AlgorithmCiftiSeparate::getCroppedVolSpace(templateCifti, CiftiXML::ALONG_COLUMN, useStructure[i], dims2, sform2, off2); + VolumeSpace space1(dims1, sform1), space2(dims2, sform2); + if (space1.matches(space2)) + { + AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, useStructure[i], &tempVol, off1, NULL, true); + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], &tempVol, true, false, errorOnLabelConflict); + } else { + AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, useStructure[i], &tempVol, off1, NULL, false); + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], &tempVol, false, false, errorOnLabelConflict); + } } break; } case METRIC: { - const MetricFile* toUse = metricInstances[surfInfo[whichStruct].index]->getMetric(2); - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], toUse); + const MetricFile* toUse = metricInstances[useOrder[i].index]->getMetric(2); + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], toUse); break; } case LABEL: { - const LabelFile* toUse = labelInstances[surfInfo[whichStruct].index]->getLabel(2); - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], toUse); + const LabelFile* toUse = labelInstances[useOrder[i].index]->getLabel(2); + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], toUse, false, errorOnLabelConflict); break; } - case NONE: + case VOLUME: { - int numNodes = templateMap.getSurfaceNumberOfNodes(surfStructures[whichStruct]); - if (labelMode == 1) - { - LabelFile tempLabel; - tempLabel.setNumberOfNodesAndColumns(numNodes, numMaps); - int32_t unlabeledKey = tempLabel.getLabelTable()->getUnassignedLabelKey(); - vector scratchCol(numNodes, unlabeledKey); - for (int64_t i = 0; i < numMaps; ++i) - { - tempLabel.setLabelKeysForColumn(i, scratchCol.data()); - } - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempLabel); - } else { - MetricFile tempMetric; - tempMetric.setNumberOfNodesAndColumns(numNodes, numMaps); - vector scratchCol(numNodes, 0.0f); - for (int64_t i = 0; i < numMaps; ++i) - { - tempMetric.setValuesForColumn(i, scratchCol.data()); - } - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempMetric); - } + const VolumeFile* toUse = volumeInstances[useOrder[i].index]->getVolume(2); + bool fromCropped = volumeInstances[useOrder[i].index]->getOptionalParameter(3)->m_present; + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], toUse, fromCropped, false, errorOnLabelConflict); break; } - default: - CaretAssert(false); - throw OperationException("internal error, invalid source type for surface data, tell the developers what you did"); - } - } - if (volStructures.size() > 0 && volInfo[0].type == VOLUME_ALL)//NOTE: if one structure is VOLUME_ALL, all are, and the cropped space is different than per-structure, so DO NOT enter the structure loop - { - const VolumeFile* toUse = volAllOpt->getVolume(1); - bool fromCropped = volAllOpt->getOptionalParameter(2)->m_present; - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, toUse, fromCropped); - } else { - for (int whichStruct = 0; whichStruct < (int)volStructures.size(); ++whichStruct) - { - switch (volInfo[whichStruct].type) + case VOLUME_ALL: + {//this only exists once in order vectors + const VolumeFile* toUse = volAllOpt->getVolume(1); + bool fromCropped = volAllOpt->getOptionalParameter(2)->m_present; + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, toUse, fromCropped, false, errorOnLabelConflict); + break; + } + case NONE: { - case CIFTI: + if (useIsSurface[i]) { - const CiftiFile* toUse = ciftiInstances[volInfo[whichStruct].index]->getCifti(1); - VolumeFile tempVol; - int64_t dims1[3], dims2[3], off1[3], off2[3];//check if the cropped space matches, if so we can save memory easily by using the crop argument (and this is also the common case) - vector > sform1, sform2; - AlgorithmCiftiSeparate::getCroppedVolSpace(toUse, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], dims1, sform1, off1); - AlgorithmCiftiSeparate::getCroppedVolSpace(templateCifti, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], dims2, sform2, off2); - VolumeSpace space1(dims1, sform1), space2(dims2, sform2); - if (space1.matches(space2)) + int numNodes = templateMap.getSurfaceNumberOfNodes(useStructure[i]); + if (labelMode == 1) { - AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, off1, NULL, true); - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, true); + LabelFile tempLabel; + tempLabel.setNumberOfNodesAndColumns(numNodes, numMaps); + int32_t unlabeledKey = tempLabel.getLabelTable()->getUnassignedLabelKey(); + vector scratchCol(numNodes, unlabeledKey); + for (int64_t i = 0; i < numMaps; ++i) + { + tempLabel.setLabelKeysForColumn(i, scratchCol.data()); + } + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], &tempLabel, false, errorOnLabelConflict); } else { - AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, off1, NULL, false); - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, false); + MetricFile tempMetric; + tempMetric.setNumberOfNodesAndColumns(numNodes, numMaps); + vector scratchCol(numNodes, 0.0f); + for (int64_t i = 0; i < numMaps; ++i) + { + tempMetric.setValuesForColumn(i, scratchCol.data()); + } + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], &tempMetric); } - break; - } - case VOLUME: - { - const VolumeFile* toUse = volumeInstances[volInfo[whichStruct].index]->getVolume(2); - bool fromCropped = volumeInstances[volInfo[whichStruct].index]->getOptionalParameter(3)->m_present; - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], toUse, fromCropped); - break; - } - case NONE: - { + } else { VolumeFile tempVol; int64_t offset[3]; vector dims(3); vector > sform; - AlgorithmCiftiSeparate::getCroppedVolSpace(templateCifti, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], dims.data(), sform, offset); + AlgorithmCiftiSeparate::getCroppedVolSpace(templateCifti, CiftiXML::ALONG_COLUMN, useStructure[i], dims.data(), sform, offset); dims.push_back(numMaps); if (labelMode == 1) { @@ -630,12 +679,9 @@ tempVol.reinitialize(dims, sform); tempVol.setValueAllVoxels(0.0f); } - AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, true); - break; + AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, useStructure[i], &tempVol, true, false, errorOnLabelConflict); } - default: - CaretAssert(false); - throw OperationException("internal error, invalid source type for volume structure data, tell the developers what you did"); + break; } } } diff -Nru connectome-workbench-1.4.2/src/Operations/OperationCiftiCreateParcellatedFromTemplate.cxx connectome-workbench-1.5.0/src/Operations/OperationCiftiCreateParcellatedFromTemplate.cxx --- connectome-workbench-1.4.2/src/Operations/OperationCiftiCreateParcellatedFromTemplate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationCiftiCreateParcellatedFromTemplate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -76,7 +76,7 @@ { fillValue = (float)fillOpt->getDouble(1); } - const vector& inputInstances = *(myParams->getRepeatableParameterInstances(5)); + const vector& inputInstances = myParams->getRepeatableParameterInstances(5); const CiftiXML& templateXML = templateCifti->getCiftiXML(); if (templateXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::PARCELS) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationCiftiEstimateFWHM.cxx connectome-workbench-1.5.0/src/Operations/OperationCiftiEstimateFWHM.cxx --- connectome-workbench-1.4.2/src/Operations/OperationCiftiEstimateFWHM.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationCiftiEstimateFWHM.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -99,7 +99,7 @@ column = columnOpt->getInteger(1) - 1;//compensate for 1-based UI indices if (column < 0 || column >= myCifti->getNumberOfColumns()) throw OperationException("invalid column index"); } - const vector& surfInstances = *(myParams->getRepeatableParameterInstances(4)); + const vector& surfInstances = myParams->getRepeatableParameterInstances(4); int numInstances = (int)surfInstances.size(); for (int i = 0; i < numInstances; ++i) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationCiftiExportDenseMapping.cxx connectome-workbench-1.5.0/src/Operations/OperationCiftiExportDenseMapping.cxx --- connectome-workbench-1.4.2/src/Operations/OperationCiftiExportDenseMapping.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationCiftiExportDenseMapping.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -95,8 +95,8 @@ } else { throw OperationException("incorrect string for direction, use ROW or COLUMN"); } - const vector& surfOpts = *(myParams->getRepeatableParameterInstances(3)); - const vector& volOpts = *(myParams->getRepeatableParameterInstances(4)); + const vector& surfOpts = myParams->getRepeatableParameterInstances(3); + const vector& volOpts = myParams->getRepeatableParameterInstances(4); OptionalParameter* volAllOpt = myParams->getOptionalParameter(5); const CiftiXML& myXML = myCifti->getCiftiXML(); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw OperationException("specified direction in cifti file does not have BRAIN_MODELS mapping"); diff -Nru connectome-workbench-1.4.2/src/Operations/OperationCiftiMath.cxx connectome-workbench-1.5.0/src/Operations/OperationCiftiMath.cxx --- connectome-workbench-1.4.2/src/Operations/OperationCiftiMath.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationCiftiMath.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -85,7 +85,7 @@ cout << "parsed '" + expression + "' as '" + myExpr.toString() + "'" << endl; vector myVarNames = myExpr.getVarNames(); CiftiFile* myCiftiOut = myParams->getOutputCifti(2); - const vector& myVarOpts = *(myParams->getRepeatableParameterInstances(3)); + const vector& myVarOpts = myParams->getRepeatableParameterInstances(3); OptionalParameter* fixNanOpt = myParams->getOptionalParameter(4); bool nanfix = false; float nanfixval = 0; @@ -115,7 +115,7 @@ int thisNumDims = tempXML.getNumberOfDimensions(); vector thisSelectInfo(thisNumDims, -1); vector thisRepeat(thisNumDims, false); - const vector& thisSelectOpts = *(myVarOpts[i]->getRepeatableParameterInstances(3)); + const vector& thisSelectOpts = myVarOpts[i]->getRepeatableParameterInstances(3); for (int j = 0; j < (int)thisSelectOpts.size(); ++j) { int dim = (int)thisSelectOpts[j]->getInteger(1) - 1; diff -Nru connectome-workbench-1.4.2/src/Operations/OperationCiftiMerge.cxx connectome-workbench-1.5.0/src/Operations/OperationCiftiMerge.cxx --- connectome-workbench-1.4.2/src/Operations/OperationCiftiMerge.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationCiftiMerge.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,7 +23,9 @@ #include "CaretAssert.h" #include "CaretPointer.h" +#include "CaretCommandGlobalOptions.h" #include "CiftiFile.h" +#include "FileInformation.h" #include @@ -67,8 +69,10 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); - CiftiFile* ciftiOut = myParams->getOutputCifti(1); - const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); + CiftiFile* ciftiOut = myParams->getOutputCifti(1);//NOTE: doesn't actually get created until the first setRow() call + FileInformation ciftiOutInfo(ciftiOut->getFileName()); + AString ciftiOutCanonicalName = ciftiOutInfo.getCanonicalFilePath(); + const vector& myInputs = myParams->getRepeatableParameterInstances(2); int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); vector ciftiList(numInputs); @@ -93,13 +97,22 @@ { ciftiList[i].openFile(myInputs[i]->getString(1)); } + if (caret_global_command_options.m_ciftiReadMemory) + { + ciftiList[i].convertToInMemory(); + } else {//if you manually read input files, you manually check for filename collision... + if (!ciftiOut->isInMemory() && FileInformation(myInputs[i]->getString(1)).getCanonicalFilePath() == ciftiOutCanonicalName) + { + ciftiOut->convertToInMemory(); + } + } const CiftiFile* ciftiIn = &(ciftiList[i]); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); if (thisXML.getNumberOfDimensions() != 2) throw OperationException("only 2D cifti are supported"); if (!thisXML.getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(baseColMapping)) throw OperationException("file '" + ciftiIn->getFileName() + "' has non-matching mapping along columns"); if (thisXML.getMappingType(CiftiXML::ALONG_ROW) != baseRowMapping.getType()) throw OperationException("file '" + ciftiIn->getFileName() + "' has different mapping type along rows"); - const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& columnOpts = myInputs[i]->getRepeatableParameterInstances(2); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { @@ -155,7 +168,7 @@ const CiftiFile* ciftiIn = &(ciftiList[i]); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); - const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& columnOpts = myInputs[i]->getRepeatableParameterInstances(2); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { @@ -274,7 +287,7 @@ const CiftiFile* ciftiIn = &(ciftiList[i]); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); - const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& columnOpts = myInputs[i]->getRepeatableParameterInstances(2); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationConvertAffine.cxx connectome-workbench-1.5.0/src/Operations/OperationConvertAffine.cxx --- connectome-workbench-1.4.2/src/Operations/OperationConvertAffine.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationConvertAffine.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -105,7 +105,7 @@ { myAffine.writeWorld(toWorld->getString(1), toWorld->getOptionalParameter(2)->m_present); } - const vector& toFlirt = *(myParams->getRepeatableParameterInstances(4));//the return of this is a pointer so that it can return NULL if the key is wrong, after asserting + const vector& toFlirt = myParams->getRepeatableParameterInstances(4);//the return of this is a pointer so that it can return NULL if the key is wrong, after asserting int numFlirt = (int)toFlirt.size();//so, dereference immediately since it should be caught in debug via assert for (int i = 0; i < numFlirt; ++i) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationConvertFiberOrientations.cxx connectome-workbench-1.5.0/src/Operations/OperationConvertFiberOrientations.cxx --- connectome-workbench-1.4.2/src/Operations/OperationConvertFiberOrientations.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationConvertFiberOrientations.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -73,7 +73,7 @@ throw OperationException(" must have a label table, see -volume-label-import"); } CiftiFile* ciftiOut = myParams->getOutputCifti(2); - const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); + const vector& myInstances = myParams->getRepeatableParameterInstances(3); int numFibers = (int)myInstances.size(); if (numFibers < 1) throw OperationException("must specify -fiber at least once"); if (numFibers > 3) throw OperationException("only three fibers are supported at this time"); diff -Nru connectome-workbench-1.4.2/src/Operations/OperationConvertWarpfield.cxx connectome-workbench-1.5.0/src/Operations/OperationConvertWarpfield.cxx --- connectome-workbench-1.4.2/src/Operations/OperationConvertWarpfield.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationConvertWarpfield.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -116,7 +116,7 @@ { myWarp.writeITK(toITK->getString(1)); } - const vector& toFnirt = *(myParams->getRepeatableParameterInstances(4));//the return of this is a pointer so that it can return NULL if the key is wrong, after asserting + const vector& toFnirt = myParams->getRepeatableParameterInstances(4);//the return of this is a pointer so that it can return NULL if the key is wrong, after asserting int numFnirt = (int)toFnirt.size();//so, dereference immediately since it should be caught in debug via assert for (int i = 0; i < numFnirt; ++i) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationFociCreate.cxx connectome-workbench-1.5.0/src/Operations/OperationFociCreate.cxx --- connectome-workbench-1.4.2/src/Operations/OperationFociCreate.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationFociCreate.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -79,7 +79,7 @@ FociFile* outputFociFile = myParams->getOutputFoci(1); - const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); + const vector& myInputs = myParams->getRepeatableParameterInstances(2); int numInputs = (int)myInputs.size(); if (numInputs == 0) { throw OperationException("no inputs specified"); diff -Nru connectome-workbench-1.4.2/src/Operations/OperationLabelMerge.cxx connectome-workbench-1.5.0/src/Operations/OperationLabelMerge.cxx --- connectome-workbench-1.4.2/src/Operations/OperationLabelMerge.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationLabelMerge.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -85,7 +85,7 @@ { LevelProgress myProgress(myProgObj); LabelFile* myLabelOut = myParams->getOutputLabel(1); - const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); + const vector& myInputs = myParams->getRepeatableParameterInstances(2); int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); const LabelFile* firstLabel = myInputs[0]->getLabel(1); @@ -100,7 +100,7 @@ fileRemap[i] = outTable.append(*(inputLabel->getLabelTable()));//NOTE: does (and must) include identity mappings - anything that doesn't match was invalid in the original file if (numNodes != inputLabel->getNumberOfNodes()) throw OperationException("file '" + inputLabel->getFileName() + "' has a different number of nodes than the first"); if (myStruct != inputLabel->getStructure()) throw OperationException("file '" + inputLabel->getFileName() + "' has a different structure than the first"); - const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& columnOpts = myInputs[i]->getRepeatableParameterInstances(2); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { @@ -132,7 +132,7 @@ for (int i = 0; i < numInputs; ++i) { const LabelFile* inputLabel = myInputs[i]->getLabel(1); - const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& columnOpts = myInputs[i]->getRepeatableParameterInstances(2); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationMetricMath.cxx connectome-workbench-1.5.0/src/Operations/OperationMetricMath.cxx --- connectome-workbench-1.4.2/src/Operations/OperationMetricMath.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationMetricMath.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -80,7 +80,7 @@ cout << "parsed '" + expression + "' as '" + myExpr.toString() + "'" << endl; vector myVarNames = myExpr.getVarNames(); MetricFile* myMetricOut = myParams->getOutputMetric(2); - const vector& myVarOpts = *(myParams->getRepeatableParameterInstances(3)); + const vector& myVarOpts = myParams->getRepeatableParameterInstances(3); OptionalParameter* fixNanOpt = myParams->getOptionalParameter(4); bool nanfix = false; float nanfixval = 0; diff -Nru connectome-workbench-1.4.2/src/Operations/OperationMetricMerge.cxx connectome-workbench-1.5.0/src/Operations/OperationMetricMerge.cxx --- connectome-workbench-1.4.2/src/Operations/OperationMetricMerge.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationMetricMerge.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -67,7 +67,7 @@ { LevelProgress myProgress(myProgObj); MetricFile* myMetricOut = myParams->getOutputMetric(1); - const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); + const vector& myInputs = myParams->getRepeatableParameterInstances(2); int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); const MetricFile* firstMetric = myInputs[0]->getMetric(1); @@ -79,7 +79,7 @@ const MetricFile* inputMetric = myInputs[i]->getMetric(1); if (numNodes != inputMetric->getNumberOfNodes()) throw OperationException("file '" + inputMetric->getFileName() + "' has a different number of nodes than the first"); if (myStruct != inputMetric->getStructure()) throw OperationException("file '" + inputMetric->getFileName() + "' has a different structure than the first"); - const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& columnOpts = myInputs[i]->getRepeatableParameterInstances(2); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { @@ -109,7 +109,7 @@ for (int i = 0; i < numInputs; ++i) { const MetricFile* inputMetric = myInputs[i]->getMetric(1); - const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& columnOpts = myInputs[i]->getRepeatableParameterInstances(2); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationSceneFileMerge.cxx connectome-workbench-1.5.0/src/Operations/OperationSceneFileMerge.cxx --- connectome-workbench-1.4.2/src/Operations/OperationSceneFileMerge.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationSceneFileMerge.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -63,7 +63,7 @@ { LevelProgress myProgress(myProgObj); SceneFile outSceneFile; - const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); + const vector& myInputs = myParams->getRepeatableParameterInstances(2); int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); for (int i = 0; i < numInputs; ++i) @@ -71,7 +71,7 @@ ParameterComponent* thisInput = myInputs[i]; SceneFile inputScene; inputScene.readFile(thisInput->getString(1)); - const vector& selectOpts = *(thisInput->getRepeatableParameterInstances(2)); + const vector& selectOpts = thisInput->getRepeatableParameterInstances(2); int numSelect = (int)selectOpts.size(); if (numSelect > 0) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationSetMapNames.cxx connectome-workbench-1.5.0/src/Operations/OperationSetMapNames.cxx --- connectome-workbench-1.4.2/src/Operations/OperationSetMapNames.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationSetMapNames.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -50,14 +50,17 @@ OptionalParameter* fileOpt = ret->createOptionalParameter(2, "-name-file", "use a text file to replace all map names"); fileOpt->addStringParameter(1, "file", "text file containing map names, one per line"); + OptionalParameter* dataFileOpt = ret->createOptionalParameter(4, "-from-data-file", "use the map names from another data file"); + dataFileOpt->addStringParameter(1, "file", "a data file with the same number of maps"); + ParameterComponent* mapOpt = ret->createRepeatableParameter(3, "-map", "specify a map to set the name of"); mapOpt->addIntegerParameter(1, "index", "the map index to change the name of"); mapOpt->addStringParameter(2, "new-name", "the name to set for the map"); ret->setHelpText( AString("Sets the name of one or more maps for metric, shape, label, volume, cifti scalar or cifti label files. ") + - "If the -name-file option is not specified, the -map option must be specified at least once. " + - "The -map option cannot be used when -name-file is specified." + "You must specify either -name-file, or -from-data-file, or at least one -map option. " + + "The three option types are mutually exclusive." ); return ret; } @@ -67,13 +70,15 @@ LevelProgress myProgress(myProgObj); AString fileName = myParams->getString(1); OptionalParameter* fileOpt = myParams->getOptionalParameter(2); - const vector& mapOpts = *(myParams->getRepeatableParameterInstances(3)); + OptionalParameter* dataFileOpt = myParams->getOptionalParameter(4); + const vector& mapOpts = myParams->getRepeatableParameterInstances(3); CaretPointer caretDataFile(CaretDataFileHelper::readAnyCaretDataFile(fileName)); CaretMappableDataFile* mappableFile = dynamic_cast(caretDataFile.getPointer()); if (mappableFile == NULL) throw OperationException("cannot set map names on this file type"); if (fileOpt->m_present) { if (!mapOpts.empty()) throw OperationException("-map may not be specified when using -name-file"); + if (dataFileOpt->m_present) throw OperationException("-from-data-file may not be specified when using -name-file"); AString listfileName = fileOpt->getString(1); ifstream nameListFile(listfileName.toLocal8Bit().constData()); if (!nameListFile.good()) @@ -101,14 +106,27 @@ throw OperationException("name file contains more names than can be used on the file"); } } else { - if (mapOpts.empty()) throw OperationException("you must specify at least one option that sets a map name"); - for (int i = 0; i < (int)mapOpts.size(); ++i) - { - int mapIndex = (int)mapOpts[i]->getInteger(1) - 1; - if (mapIndex < 0) throw OperationException("invalid map index, indices are 1-based"); - if (mapIndex >= mappableFile->getNumberOfMaps()) throw OperationException("invalid map index, file doesn't have enough maps"); - AString newName = mapOpts[i]->getString(2); - mappableFile->setMapName(mapIndex, newName); + if (dataFileOpt->m_present) { + if (!mapOpts.empty()) throw OperationException("-map may not be specified when using -from-data-file"); + CaretPointer nameCaretDataFile(CaretDataFileHelper::readAnyCaretDataFile(dataFileOpt->getString(1))); + CaretMappableDataFile* nameMappableFile = dynamic_cast(nameCaretDataFile.getPointer()); + if (nameMappableFile == NULL) throw OperationException("cannot read map names from this file type"); + int numMaps = mappableFile->getNumberOfMaps(); + if (nameMappableFile->getNumberOfMaps() != numMaps) throw OperationException("-from-data-file specifies a file with a different number of maps than the file to modify"); + for (int i = 0; i < numMaps; ++i) + { + mappableFile->setMapName(i, nameMappableFile->getMapName(i));//NOTE: getMapName can give #1, #2, etc when map names are actually blank + } + } else { + if (mapOpts.empty()) throw OperationException("you must specify at least one option that sets a map name"); + for (int i = 0; i < (int)mapOpts.size(); ++i) + { + int mapIndex = (int)mapOpts[i]->getInteger(1) - 1; + if (mapIndex < 0) throw OperationException("invalid map index, indices are 1-based"); + if (mapIndex >= mappableFile->getNumberOfMaps()) throw OperationException("invalid map index, file doesn't have enough maps"); + AString newName = mapOpts[i]->getString(2); + mappableFile->setMapName(mapIndex, newName); + } } } mappableFile->writeFile(fileName); diff -Nru connectome-workbench-1.4.2/src/Operations/OperationShowScene.cxx connectome-workbench-1.5.0/src/Operations/OperationShowScene.cxx --- connectome-workbench-1.4.2/src/Operations/OperationShowScene.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationShowScene.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -61,7 +61,8 @@ #include "SceneFile.h" #include "ScenePrimitiveArray.h" #include "SessionManager.h" -#include "TileTabsConfiguration.h" +#include "TileTabsLayoutGridConfiguration.h" +#include "TileTabsLayoutManualConfiguration.h" #include "VolumeFile.h" //#include "workbench_png.h" @@ -142,13 +143,14 @@ "The available image formats may vary by operating system.\n" "Image formats available on this system are:\n" ); - std::vector imageFileExtensions; + std::vector readImageFileExtensions, writeImageFileExtensions; AString defaultExtension; - ImageFile::getImageFileExtensions(imageFileExtensions, - defaultExtension); + ImageFile::getWorkbenchSupportedImageFileExtensions(readImageFileExtensions, + writeImageFileExtensions, + defaultExtension); - for (std::vector::iterator iter = imageFileExtensions.begin(); - iter != imageFileExtensions.end(); + for (std::vector::iterator iter = writeImageFileExtensions.begin(); + iter != writeImageFileExtensions.end(); iter++) { const AString ext = *iter; helpText += (" " @@ -416,7 +418,8 @@ } int windowViewport[4] = { 0, 0, imageWidth, imageHeight }; - + const int windowBeforeAspectLockingViewport[4] = { 0, 0, imageWidth, imageHeight }; + const int windowWidth = windowViewport[2]; const int windowHeight = windowViewport[3]; @@ -463,9 +466,22 @@ if (restoreToTabTiles) { CaretPointer brainOpenGL(createBrainOpenGL()); - TileTabsConfiguration* tileTabsConfiguration = bwc->getSelectedTileTabsConfiguration(); - CaretAssert(tileTabsConfiguration); + TileTabsLayoutGridConfiguration* gridConfig = NULL; //tileTabsConfiguration->castToGridConfiguration(); + bool manualFlag(false); + switch (bwc->getTileTabsConfigurationMode()) { + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: + gridConfig = bwc->getCustomGridTileTabsConfiguration(); + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + gridConfig = bwc->getCustomGridTileTabsConfiguration(); + break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + manualFlag = true; + break; + } + if ((gridConfig != NULL) + || manualFlag) { const std::vector tabIndices = bwc->getSceneTabIndices(); if ( ! tabIndices.empty()) { std::vector allTabContent; @@ -489,15 +505,18 @@ if (numTabContent <= 0) { throw OperationException("Failed to find any tab content"); } - std::vector rowHeights; - std::vector columnWidths; - if ( ! tileTabsConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, - windowHeight, - numTabContent, - bwc->getTileTabsConfigurationMode(), - rowHeights, - columnWidths)) { - throw OperationException("Tile Tabs Row/Column sizing failed !!!"); + + if (gridConfig != NULL) { + std::vector rowHeights; + std::vector columnWidths; + if ( ! gridConfig->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, + windowHeight, + numTabContent, + bwc->getTileTabsConfigurationMode(), + rowHeights, + columnWidths)) { + throw OperationException("Tile Tabs Row/Column sizing failed !!!"); + } } const int32_t tabIndexToHighlight = -1; @@ -505,6 +524,7 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabContent, bwc, gapsAndMargins, + windowBeforeAspectLockingViewport, windowViewport, windowIndex, tabIndexToHighlight); @@ -512,7 +532,7 @@ std::vector constViewports(viewports.begin(), viewports.end()); brainOpenGL->drawModels(windowIndex, - UserInputModeEnum::VIEW, + UserInputModeEnum::Enum::VIEW, brain, mesaContext, constViewports); @@ -534,6 +554,10 @@ } viewports.clear(); } + } + else { + throw OperationException("Tile tabs configuration is neither Grid nor Manual"); + } } else { CaretPointer brainOpenGL(createBrainOpenGL()); @@ -557,12 +581,13 @@ tabContent, gapsAndMargins, windowIndex, + windowBeforeAspectLockingViewport, windowViewport)); std::vector viewportContents; viewportContents.push_back(content); brainOpenGL->drawModels(windowIndex, - UserInputModeEnum::VIEW, + UserInputModeEnum::Enum::VIEW, brain, mesaContext, viewportContents); diff -Nru connectome-workbench-1.4.2/src/Operations/OperationTemplate.cxx.txt connectome-workbench-1.5.0/src/Operations/OperationTemplate.cxx.txt --- connectome-workbench-1.4.2/src/Operations/OperationTemplate.cxx.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationTemplate.cxx.txt 2021-02-16 19:46:47.000000000 +0000 @@ -1,6 +1,6 @@ /*LICENSE_START*/ /* - * Copyright (C) 2018 Washington University School of Medicine + * Copyright (C) 2020 Washington University School of Medicine * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff -Nru connectome-workbench-1.4.2/src/Operations/OperationTemplate.h.txt connectome-workbench-1.5.0/src/Operations/OperationTemplate.h.txt --- connectome-workbench-1.4.2/src/Operations/OperationTemplate.h.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationTemplate.h.txt 2021-02-16 19:46:47.000000000 +0000 @@ -3,7 +3,7 @@ /*LICENSE_START*/ /* - * Copyright (C) 2018 Washington University School of Medicine + * Copyright (C) 2020 Washington University School of Medicine * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff -Nru connectome-workbench-1.4.2/src/Operations/OperationVolumeMath.cxx connectome-workbench-1.5.0/src/Operations/OperationVolumeMath.cxx --- connectome-workbench-1.4.2/src/Operations/OperationVolumeMath.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationVolumeMath.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -80,7 +80,7 @@ cout << "parsed '" + expression + "' as '" + myExpr.toString() + "'" << endl; vector myVarNames = myExpr.getVarNames(); VolumeFile* myVolOut = myParams->getOutputVolume(2); - const vector& myVarOpts = *(myParams->getRepeatableParameterInstances(3)); + const vector& myVarOpts = myParams->getRepeatableParameterInstances(3); OptionalParameter* fixNanOpt = myParams->getOptionalParameter(4); bool nanfix = false; float nanfixval = 0; diff -Nru connectome-workbench-1.4.2/src/Operations/OperationVolumeMerge.cxx connectome-workbench-1.5.0/src/Operations/OperationVolumeMerge.cxx --- connectome-workbench-1.4.2/src/Operations/OperationVolumeMerge.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationVolumeMerge.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -67,7 +67,7 @@ { LevelProgress myProgress(myProgObj); VolumeFile* volumeOut = myParams->getOutputVolume(1); - const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); + const vector& myInputs = myParams->getRepeatableParameterInstances(2); int numInputs = (int)myInputs.size(); if (numInputs < 1) throw OperationException("no inputs specified"); int64_t subvolCount = 0; @@ -87,7 +87,7 @@ } vector thisDims = myVol->getDimensions(); if (thisDims[4] != firstDims[4]) throw ("volume file '" + myVol->getFileName() + "' has a different number of components"); - const vector& subvolOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& subvolOpts = myInputs[i]->getRepeatableParameterInstances(2); int numSubvolOpts = (int)subvolOpts.size(); if (numSubvolOpts > 0) { @@ -119,7 +119,7 @@ for (int i = 0; i < numInputs; ++i) { const VolumeFile* myVol = myInputs[i]->getVolume(1); - const vector& subvolOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); + const vector& subvolOpts = myInputs[i]->getRepeatableParameterInstances(2); int numSubvolOpts = (int)subvolOpts.size(); if (numSubvolOpts > 0) { diff -Nru connectome-workbench-1.4.2/src/Operations/OperationWbsparseMergeDense.cxx connectome-workbench-1.5.0/src/Operations/OperationWbsparseMergeDense.cxx --- connectome-workbench-1.4.2/src/Operations/OperationWbsparseMergeDense.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Operations/OperationWbsparseMergeDense.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -66,7 +66,7 @@ throw OperationException("incorrect string for direction, use ROW or COLUMN"); } AString outputName = myParams->getString(2); - const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); + const vector& myInstances = myParams->getRepeatableParameterInstances(3); vector > wbsparseList; int numCifti = (int)myInstances.size(); for (int i = 0; i < numCifti; ++i) diff -Nru connectome-workbench-1.4.2/src/OperationsBase/OperationParameters.cxx connectome-workbench-1.5.0/src/OperationsBase/OperationParameters.cxx --- connectome-workbench-1.4.2/src/OperationsBase/OperationParameters.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/OperationsBase/OperationParameters.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,6 +23,8 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "ProgramParametersException.h" + #include "AnnotationFile.h" #include "BorderFile.h" #include "CiftiFile.h" @@ -97,7 +99,11 @@ OptionalParameter* ParameterComponent::createOptionalParameter(const int32_t key, const AString& optionSwitch, const AString& description) { - CaretAssertMessage(checkUniqueOption(key), "optional parameter created with previously used key"); + if (!checkUniqueOption(key)) + { + CaretAssert(false); + throw ProgramParametersException("optional parameter created with previously used key"); + } if (optionSwitch.isEmpty() || optionSwitch[0] != '-') CaretLogWarning("developer warning: option '" + optionSwitch + "' created, but does not start with dash"); OptionalParameter* ret = new OptionalParameter(key, optionSwitch, description); m_optionList.push_back(ret); @@ -106,7 +112,11 @@ ParameterComponent* ParameterComponent::createRepeatableParameter(const int32_t key, const AString& optionSwitch, const AString& description) { - CaretAssertMessage(checkUniqueRepeatable(key), "repeatable parameter created with previously used key"); + if (!checkUniqueRepeatable(key)) + { + CaretAssert(false); + throw ProgramParametersException("repeatable parameter created with previously used key"); + } if (optionSwitch.isEmpty() || optionSwitch[0] != '-') CaretLogWarning("developer warning: repeatable option '" + optionSwitch + "' created, but does not start with dash"); RepeatableOption* newOpt = new RepeatableOption(key, optionSwitch, description); m_repeatableOptions.push_back(newOpt); @@ -215,8 +225,8 @@ return m_paramList[i]; } } - CaretAssertMessage(false, "Algorithm asked for parameter it didn't specify, or of wrong type"); - return NULL; + CaretAssert(false); + throw ProgramParametersException("Algorithm asked for parameter it didn't specify, or of wrong type"); } OptionalParameter* ParameterComponent::getOptionalParameter(const int32_t key) @@ -229,22 +239,71 @@ return m_optionList[i]; } } - CaretAssertMessage(false, "Algorithm asked for option it didn't specify"); - return NULL; + CaretAssert(false); + throw ProgramParametersException("Algorithm asked for option it didn't specify"); } -const vector* ParameterComponent::getRepeatableParameterInstances(const int32_t key) +const vector& ParameterComponent::getRepeatableParameterInstances(const int32_t key) { for (size_t i = 0; i < m_repeatableOptions.size(); ++i) { if (m_repeatableOptions[i]->m_key == key) { m_repeatableOptions[i]->m_operationUsed = true; - return &(m_repeatableOptions[i]->m_instances); + return m_repeatableOptions[i]->m_instances; + } + } + CaretAssert(false); + throw ProgramParametersException("Algorithm asked for repeatable option it didn't specify"); +} + +const vector& ParameterComponent::getRepeatableParameterPositions(const int32_t key) +{ + for (size_t i = 0; i < m_repeatableOptions.size(); ++i) + { + if (m_repeatableOptions[i]->m_key == key) + { + m_repeatableOptions[i]->m_operationUsed = true; + return m_repeatableOptions[i]->m_positions; + } + } + CaretAssert(false); + throw ProgramParametersException("Algorithm asked for positions of repeatable options it didn't specify"); +} + +namespace +{ + int nextOpt(const vector& opts, const vector& nextIndex) + { + int ret = -1; + int32_t bestPos = -1; + for (size_t i = 0; i < opts.size(); ++i) + { + if (nextIndex[i] < opts[i]->m_positions.size()) + { + if (ret == -1 || opts[i]->m_positions[nextIndex[i]] < bestPos) + { + ret = i; + bestPos = opts[i]->m_positions[nextIndex[i]]; + } + } } + return ret; } - CaretAssertMessage(false, "Algorithm asked for option it didn't specify"); - return NULL; +} + +vector ParameterComponent::getRepeatableOrder() +{ + vector ret; + vector indexList(m_repeatableOptions.size(), 0); + while (true)//the for loop version of this is harder to read + { + int nextUse = nextOpt(m_repeatableOptions, indexList); + if (nextUse == -1) break; + ret.push_back(OrderInfo(m_repeatableOptions[nextUse]->m_key, indexList[nextUse])); + ++indexList[nextUse]; + } + return ret; } AbstractParameter* ParameterComponent::getOutputParameter(const int32_t key, const OperationParametersEnum::Enum type) @@ -257,80 +316,129 @@ return m_outputList[i]; } } - CaretAssertMessage(false, "Algorithm asked for output it didn't specify, or of wrong type"); - return NULL; + CaretAssert(false); + throw ProgramParametersException("Algorithm asked for output it didn't specify, or of wrong type"); } //sadly, lots of boilerplate for convenience functions +//these only get called for all operations by -all-commands-help, so check and throw in release is fine void ParameterComponent::addBooleanParameter(const int32_t key, const caret::AString& name, const caret::AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::BOOL), "input boolean parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::BOOL)) + { + CaretAssert(false); + throw ProgramParametersException("input boolean parameter created with previously used key"); + } m_paramList.push_back(new BooleanParameter(key, name, description)); } void ParameterComponent::addCiftiParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::CIFTI), "input cifti parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::CIFTI)) + { + CaretAssert(false); + throw ProgramParametersException("input cifti parameter created with previously used key"); + } m_paramList.push_back(new CiftiParameter(key, name, description)); } void ParameterComponent::addFociParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::FOCI), "input foci parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::FOCI)) + { + CaretAssert(false); + throw ProgramParametersException("input foci parameter created with previously used key"); + } m_paramList.push_back(new FociParameter(key, name, description)); } void ParameterComponent::addBorderParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::BORDER), "input border parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::BORDER)) + { + CaretAssert(false); + throw ProgramParametersException("input border parameter created with previously used key"); + } m_paramList.push_back(new BorderParameter(key, name, description)); } void ParameterComponent::addDoubleParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::DOUBLE), "input double parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::DOUBLE)) + { + CaretAssert(false); + throw ProgramParametersException("input double parameter created with previously used key"); + } m_paramList.push_back(new DoubleParameter(key, name, description)); } void ParameterComponent::addAnnotationParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::ANNOTATION), "input annotation parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::ANNOTATION)) + { + CaretAssert(false); + throw ProgramParametersException("input annotation parameter created with previously used key"); + } m_paramList.push_back(new AnnotationParameter(key, name, description)); } void ParameterComponent::addMetricParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::METRIC), "input metric parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::METRIC)) + { + CaretAssert(false); + throw ProgramParametersException("input metric parameter created with previously used key"); + } m_paramList.push_back(new MetricParameter(key, name, description)); } void ParameterComponent::addIntegerParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::INT), "input integer parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::INT)) + { + CaretAssert(false); + throw ProgramParametersException("input integer parameter created with previously used key"); + } m_paramList.push_back(new IntegerParameter(key, name, description)); } void ParameterComponent::addLabelParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::LABEL), "input label parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::LABEL)) + { + CaretAssert(false); + throw ProgramParametersException("input label parameter created with previously used key"); + } m_paramList.push_back(new LabelParameter(key, name, description)); } void ParameterComponent::addStringParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::STRING), "input string parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::STRING)) + { + CaretAssert(false); + throw ProgramParametersException("input string parameter created with previously used key"); + } m_paramList.push_back(new StringParameter(key, name, description)); } void ParameterComponent::addSurfaceParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::SURFACE), "input surface parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::SURFACE)) + { + CaretAssert(false); + throw ProgramParametersException("input surface parameter created with previously used key"); + } m_paramList.push_back(new SurfaceParameter(key, name, description)); } void ParameterComponent::addVolumeParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::VOLUME), "input volume parameter created with previously used key"); + if (!checkUniqueInput(key, OperationParametersEnum::VOLUME)) + { + CaretAssert(false); + throw ProgramParametersException("input volume parameter created with previously used key"); + } m_paramList.push_back(new VolumeParameter(key, name, description)); } @@ -341,49 +449,81 @@ void ParameterComponent::addCiftiOutputParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::CIFTI), "output cifti parameter created with previously used key"); + if (!checkUniqueOutput(key, OperationParametersEnum::CIFTI)) + { + CaretAssert(false); + throw ProgramParametersException("output cifti parameter created with previously used key"); + } m_outputList.push_back(new CiftiParameter(key, name, description)); } void ParameterComponent::addFociOutputParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::FOCI), "output foci parameter created with previously used key"); + if (!checkUniqueOutput(key, OperationParametersEnum::FOCI)) + { + CaretAssert(false); + throw ProgramParametersException("output foci parameter created with previously used key"); + } m_outputList.push_back(new FociParameter(key, name, description)); } void ParameterComponent::addBorderOutputParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::BORDER), "output foci parameter created with previously used key"); + if (!checkUniqueOutput(key, OperationParametersEnum::BORDER)) + { + CaretAssert(false); + throw ProgramParametersException("output foci parameter created with previously used key"); + } m_outputList.push_back(new BorderParameter(key, name, description)); } void ParameterComponent::addAnnotationOutputParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::ANNOTATION), "output annotation parameter created with previously used key"); + if (!checkUniqueOutput(key, OperationParametersEnum::ANNOTATION)) + { + CaretAssert(false); + throw ProgramParametersException("output annotation parameter created with previously used key"); + } m_outputList.push_back(new AnnotationParameter(key, name, description)); } void ParameterComponent::addMetricOutputParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::METRIC), "output metric parameter created with previously used key"); + if (!checkUniqueOutput(key, OperationParametersEnum::METRIC)) + { + CaretAssert(false); + throw ProgramParametersException("output metric parameter created with previously used key"); + } m_outputList.push_back(new MetricParameter(key, name, description)); } void ParameterComponent::addLabelOutputParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::LABEL), "output label parameter created with previously used key"); + if (!checkUniqueOutput(key, OperationParametersEnum::LABEL)) + { + CaretAssert(false); + throw ProgramParametersException("output label parameter created with previously used key"); + } m_outputList.push_back(new LabelParameter(key, name, description)); } void ParameterComponent::addSurfaceOutputParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::SURFACE), "output surface parameter created with previously used key"); + if (!checkUniqueOutput(key, OperationParametersEnum::SURFACE)) + { + CaretAssert(false); + throw ProgramParametersException("output surface parameter created with previously used key"); + } m_outputList.push_back(new SurfaceParameter(key, name, description)); } void ParameterComponent::addVolumeOutputParameter(const int32_t key, const AString& name, const AString& description) { - CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::VOLUME), "output volume parameter created with previously used key"); + if (!checkUniqueOutput(key, OperationParametersEnum::VOLUME)) + { + CaretAssert(false); + throw ProgramParametersException("output volume parameter created with previously used key"); + } m_outputList.push_back(new VolumeParameter(key, name, description)); } diff -Nru connectome-workbench-1.4.2/src/OperationsBase/OperationParameters.h connectome-workbench-1.5.0/src/OperationsBase/OperationParameters.h --- connectome-workbench-1.4.2/src/OperationsBase/OperationParameters.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/OperationsBase/OperationParameters.h 2021-02-16 19:46:47.000000000 +0000 @@ -211,8 +211,20 @@ OptionalParameter* getOptionalParameter(const int32_t key); ///return instances of a repeatable option - const std::vector* getRepeatableParameterInstances(const int32_t key); + const std::vector& getRepeatableParameterInstances(const int32_t key); + ///return positions of the instances, only needed for chaining different transform types in volume resample so far + const std::vector& getRepeatableParameterPositions(const int32_t key); + + struct OrderInfo + { + int32_t key; + size_t index; + OrderInfo(int32_t keyIn, size_t indexIn) { key = keyIn; index = indexIn; } + }; + ///return a vector in the order of all repeatable options, with key and index into instances + std::vector getRepeatableOrder(); + ///functions to check for key/type uniqueness - used only in asserts bool checkUniqueInput(const int32_t& key, const OperationParametersEnum::Enum& type); bool checkUniqueOutput(const int32_t& key, const OperationParametersEnum::Enum& type); @@ -257,6 +269,7 @@ ParameterComponent m_template; bool m_operationUsed;//check if the operation called get...() for this parameter std::vector m_instances;//to be filled by parser + std::vector m_positions;//to resolve ambiguities in ordering between different repeatable options RepeatableOption(const RepeatableOption& rhs) : m_key(rhs.m_key), m_optionSwitch(rhs.m_optionSwitch), @@ -287,7 +300,6 @@ ///get the unformatted help text, without command or arguments descriptions, to be formatted by the argument parser AString& getHelpText(); - }; //templates for the common cases diff -Nru connectome-workbench-1.4.2/src/Palette/CMakeLists.txt connectome-workbench-1.5.0/src/Palette/CMakeLists.txt --- connectome-workbench-1.4.2/src/Palette/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -19,12 +19,17 @@ # Create the NIFTI library # ADD_LIBRARY(Palette +EventPaletteGroupsGet.h Palette.h +PaletteNew.h PaletteColorBarValuesModeEnum.h PaletteColorMapping.h PaletteColorMappingSaxReader.h PaletteColorMappingXmlElements.h PaletteEnums.h +PaletteGroup.h +PaletteGroupStandardPalettes.h +PaletteGroupUserCustomPalettes.h PaletteHistogramRangeModeEnum.h PaletteInvertModeEnum.h PaletteModifiedStatusEnum.h @@ -33,11 +38,16 @@ PaletteThresholdOutlineDrawingModeEnum.h PaletteThresholdRangeModeEnum.h +EventPaletteGroupsGet.cxx Palette.cxx +PaletteNew.cxx PaletteColorBarValuesModeEnum.cxx PaletteColorMapping.cxx PaletteColorMappingSaxReader.cxx PaletteEnums.cxx +PaletteGroup.cxx +PaletteGroupStandardPalettes.cxx +PaletteGroupUserCustomPalettes.cxx PaletteHistogramRangeModeEnum.cxx PaletteInvertModeEnum.cxx PaletteModifiedStatusEnum.cxx diff -Nru connectome-workbench-1.4.2/src/Palette/EventPaletteGroupsGet.cxx connectome-workbench-1.5.0/src/Palette/EventPaletteGroupsGet.cxx --- connectome-workbench-1.4.2/src/Palette/EventPaletteGroupsGet.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/EventPaletteGroupsGet.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,75 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_PALETTE_GROUPS_GET_DECLARE__ +#include "EventPaletteGroupsGet.h" +#undef __EVENT_PALETTE_GROUPS_GET_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventPaletteGroupsGet + * \brief Event that gets palette groups + * \ingroup Palette + */ + +/** + * Constructor. + */ +EventPaletteGroupsGet::EventPaletteGroupsGet() +: Event(EventTypeEnum::EVENT_PALETTE_GROUPS_GET) +{ + +} + +/** + * Destructor. + */ +EventPaletteGroupsGet::~EventPaletteGroupsGet() +{ +} + +/** + * Add a palette group + * @param paletteGroup + * Group that is added. + * Note: Pass by values so that a shared_ptr is automatically converted to a weak_ptr + */ +void +EventPaletteGroupsGet::addPaletteGroup(std::weak_ptr paletteGroup) +{ + m_paletteGroups.push_back(paletteGroup); +} + +/** + * @return The palette groups + */ +std::vector> +EventPaletteGroupsGet::getPaletteGroups() const +{ + return m_paletteGroups; +} + diff -Nru connectome-workbench-1.4.2/src/Palette/EventPaletteGroupsGet.h connectome-workbench-1.5.0/src/Palette/EventPaletteGroupsGet.h --- connectome-workbench-1.4.2/src/Palette/EventPaletteGroupsGet.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/EventPaletteGroupsGet.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,65 @@ +#ifndef __EVENT_PALETTE_GROUPS_GET_H__ +#define __EVENT_PALETTE_GROUPS_GET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class PaletteGroup; + + class EventPaletteGroupsGet : public Event { + + public: + EventPaletteGroupsGet(); + + virtual ~EventPaletteGroupsGet(); + + EventPaletteGroupsGet(const EventPaletteGroupsGet&) = delete; + + EventPaletteGroupsGet& operator=(const EventPaletteGroupsGet&) = delete; + + void addPaletteGroup(std::weak_ptr paletteGroup); + + std::vector> getPaletteGroups() const; + + // ADD_NEW_METHODS_HERE + + private: + std::vector> m_paletteGroups; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_PALETTE_GROUPS_GET_DECLARE__ + // +#endif // __EVENT_PALETTE_GROUPS_GET_DECLARE__ + +} // namespace +#endif //__EVENT_PALETTE_GROUPS_GET_H__ diff -Nru connectome-workbench-1.4.2/src/Palette/PaletteGroup.cxx connectome-workbench-1.5.0/src/Palette/PaletteGroup.cxx --- connectome-workbench-1.4.2/src/Palette/PaletteGroup.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/PaletteGroup.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,244 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_GROUP_DECLARE__ +#include "PaletteGroup.h" +#undef __PALETTE_GROUP_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventManager.h" +using namespace caret; + + + +/** + * \class caret::PaletteGroup + * \brief Contains a group of palettes + * \ingroup Palette + */ + +/** + * Constructor creates an empty, editable palette group. + * + * @param groupType + * Type of group containing palettes + */ +PaletteGroup::PaletteGroup(const GroupType groupType) +: CaretObject(), +m_groupType(groupType) +{ + switch (groupType) { + case GroupType::DATA_FILE: + CaretLogSevere("Palettes in data file not supported yet"); + return; + break; + case GroupType::STANDARD: + m_groupName = "Standard"; + break; + case GroupType::USER_CUSTOM: + m_groupName = "User Custom"; + break; + } +} + +/** + * Destructor. + */ +PaletteGroup::~PaletteGroup() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +PaletteGroup::toString() const +{ + return "PaletteGroup"; +} + +/** + * @return The group type + */ +PaletteGroup::GroupType +PaletteGroup::getGroupType() const +{ + return m_groupType; +} + +/** + * @return Name of group + */ +AString +PaletteGroup::getGroupName() const +{ + return m_groupName; +} + +/** + * @return True if this container is editable + */ +bool +PaletteGroup::isEditable() const +{ + return m_editableFlag; +} + +/** + * Set the editable status of this container. Subclasses that are not editable + * use this method to disable the editable status. + * + * @param status + * New editable status + */ +void +PaletteGroup::setEditable(const bool status) +{ + m_editableFlag = status; +} + +/** + * Add a palette. Palette must contain a name not used by an existing palette in this group. + * @param palette + * Palette to add + * @param errorMessageOut + * Output with error message if failure to add palette + * @return True if palette was added, else false. + */ +bool +PaletteGroup::addPalette(const PaletteNew& palette, + AString& errorMessageOut) +{ + if ( ! m_editableFlag) { + errorMessageOut = ("Adding palettes to " + m_groupName + " group not allowed"); + return false; + } + + const AString paletteName = palette.getName(); + if (paletteName.trimmed().isEmpty()) { + errorMessageOut = "Palette for adding must have a name"; + return false; + } + + return addPaletteImplementation(palette, + errorMessageOut); +} + +/** + * Replace a palette. Palette must contain a name that matches name of an existing palette + * @param palette + * Palette used as a replacement + * @param errorMessageOut + * Output with error message if failure to replace palette + * @return True if palette was replaced, else false. + */ +bool +PaletteGroup::replacePalette(const PaletteNew& palette, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + if ( ! m_editableFlag) { + errorMessageOut = ("Replacing palettes in " + m_groupName + " group not allowed"); + return false; + } + + const AString paletteName = palette.getName(); + if (paletteName.trimmed().isEmpty()) { + errorMessageOut = "Palette for replacing must have a name"; + return false; + } + + return replacePaletteImplementation(palette, + errorMessageOut); + + return true; +} + +/** + * Rename a palette. Palette must contain a name that matches name of an existing palette + * @param paletteName + * Name of palette that is renamed + * @param newPaletteName + * New name for palette + * @param errorMessageOut + * Output with error message if failure to rename palette + * @return True if palette was rename, else false. + */ +bool +PaletteGroup::renamePalette(const AString& paletteName, + const AString& newPaletteName, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + if (paletteName.trimmed().isEmpty()) { + errorMessageOut = "Palette for renaming must have a name"; + return false; + } + + if (newPaletteName.trimmed().isEmpty()) { + errorMessageOut = "New palette name is empty"; + return false; + } + + if (hasPaletteWithName(newPaletteName)) { + errorMessageOut = ("Palette with new name " + + newPaletteName + + " exists. Palette names must be unique."); + return false; + } + + return renamePaletteImplementation(paletteName, + newPaletteName, + errorMessageOut); +} + +/** + * Remove a palette. Palette must contain a name that matches name of an existing palette + * @param paletteName + * Name of palette that is removed + * @param errorMessageOut + * Output with error message if failure to remove palette + * @return True if palette was removed, else false. + */ +bool +PaletteGroup::removePalette(const AString& paletteName, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + if ( ! m_editableFlag) { + errorMessageOut = ("Removing palettes in " + m_groupName + " group not allowed"); + return false; + } + + if (paletteName.trimmed().isEmpty()) { + errorMessageOut = "Palette for removing must have a name"; + return false; + } + + return removePaletteImplementation(paletteName, + errorMessageOut); +} + diff -Nru connectome-workbench-1.4.2/src/Palette/PaletteGroup.h connectome-workbench-1.5.0/src/Palette/PaletteGroup.h --- connectome-workbench-1.4.2/src/Palette/PaletteGroup.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/PaletteGroup.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,119 @@ +#ifndef __PALETTE_GROUP_H__ +#define __PALETTE_GROUP_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +#include "CaretObject.h" +#include "PaletteNew.h" + +namespace caret { + + class PaletteGroup : public CaretObject { + + public: + enum class GroupType { + DATA_FILE, + STANDARD, + USER_CUSTOM + }; + + PaletteGroup(const GroupType groupType); + + virtual ~PaletteGroup(); + + PaletteGroup(const PaletteGroup&) = delete; + + PaletteGroup& operator=(const PaletteGroup&) = delete; + + AString getGroupName() const; + + GroupType getGroupType() const; + + bool isEditable() const; + + bool addPalette(const PaletteNew& palette, + AString& errorMessageOut); + + bool replacePalette(const PaletteNew& palette, + AString& errorMessageOut); + + bool removePalette(const AString& paletteName, + AString& errorMessageOut); + + bool renamePalette(const AString& paletteName, + const AString& newPaletteName, + AString& errorMessageOut); + + virtual void getPalettes(std::vector& palettesOut) const = 0; + + virtual std::unique_ptr getPaletteWithName(const AString& paletteName) = 0; + + virtual bool hasPaletteWithName(const AString& paletteName) = 0; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + protected: + void setEditable(const bool status); + + virtual bool addPaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) = 0; + + virtual bool replacePaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) = 0; + + virtual bool removePaletteImplementation(const AString& paletteName, + AString& errorMessageOut) = 0; + + virtual bool renamePaletteImplementation(const AString& paletteName, + const AString& newPaletteName, + AString& errorMessageOut) = 0; + + /* + * Compares palettes by name + */ + struct PaletteNewCompare { + bool operator() (const PaletteNew& lhs, const PaletteNew& rhs) const { + return (lhs.getName() < rhs.getName()); + } + }; + + private: + const GroupType m_groupType; + + AString m_groupName; + + bool m_editableFlag = true; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PALETTE_GROUP_DECLARE__ + // +#endif // __PALETTE_GROUP_DECLARE__ + +} // namespace +#endif //__PALETTE_GROUP_H__ diff -Nru connectome-workbench-1.4.2/src/Palette/PaletteGroupStandardPalettes.cxx connectome-workbench-1.5.0/src/Palette/PaletteGroupStandardPalettes.cxx --- connectome-workbench-1.4.2/src/Palette/PaletteGroupStandardPalettes.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/PaletteGroupStandardPalettes.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,300 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_GROUP_STANDARD_PALETTES_DECLARE__ +#include "PaletteGroupStandardPalettes.h" +#undef __PALETTE_GROUP_STANDARD_PALETTES_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" + +using namespace caret; + +/** + * \class caret::PaletteGroupStandardPalettes + * \brief Palette group that contains the standard palettes (built-in) + * \ingroup Palette + */ + +/** + * Constructor. + */ +PaletteGroupStandardPalettes::PaletteGroupStandardPalettes() +: PaletteGroup(GroupType::STANDARD) +{ +} + +/** + * Destructor. + */ +PaletteGroupStandardPalettes::~PaletteGroupStandardPalettes() +{ +} + +/** + * Call after constructor to load palettes. Uses some virtual methods + * so cannot call from constructor. + */ +void +PaletteGroupStandardPalettes::loadPalettes() +{ + m_palettes.clear(); + + /* + * Some simulated palettes for now + */ + { + std::vector posRange; + posRange.push_back(PaletteNew::ScalarColor(0.0, 0.4, 0.0, 0.0)); + posRange.push_back(PaletteNew::ScalarColor(0.3, 0.7, 0.0, 0.0)); + posRange.push_back(PaletteNew::ScalarColor(1.0, 0.1, 0.0, 0.0)); + + std::vector negRange; + negRange.push_back(PaletteNew::ScalarColor(-1.0, 0.0, 0.4, 0.9)); + negRange.push_back(PaletteNew::ScalarColor(-0.6, 0.7, 0.0, 0.7)); + negRange.push_back(PaletteNew::ScalarColor( 0.0, 0.1, 0.0, 0.3)); + + float zeroRGB[3] = { 0.0, 1.0, 0.0 }; + PaletteNew p(posRange, zeroRGB, negRange); + p.setName("Psycho"); + addPaletteHelper(p); + } + + { + std::vector posRange; + posRange.push_back(PaletteNew::ScalarColor(0.0, 0.4, 0.7, 0.0)); + posRange.push_back(PaletteNew::ScalarColor(0.5, 0.7, 0.7, 0.0)); + posRange.push_back(PaletteNew::ScalarColor(1.0, 0.1, 0.0, 0.0)); + + std::vector negRange; + negRange.push_back(PaletteNew::ScalarColor(-1.0, 0.0, 0.5, 0.9)); + negRange.push_back(PaletteNew::ScalarColor(-0.2, 0.7, 0.0, 0.7)); + negRange.push_back(PaletteNew::ScalarColor( 0.0, 0.1, 0.0, 0.3)); + + float zeroRGB[3] = { 0.5, 1.0, 0.0 }; + PaletteNew p(posRange, zeroRGB, negRange); + p.setName("Roy"); + addPaletteHelper(p); + } +} + +/** + * Add the given palette and log any errors + * @param palette + * Palette to add + */ +void +PaletteGroupStandardPalettes::addPaletteHelper(const PaletteNew& palette) +{ + setEditable(true); + + AString errorMessage; + const bool result = addPalette(palette, + errorMessage); + if ( ! result) { + CaretLogSevere("Creating Standard Palette named \"" + + palette.getName() + + "\": " + + errorMessage); + } + + setEditable(false); +} + +/** + * Add a palette. Palette must contain a name not used by an existing palette in this group. + * @param palette + * Palette to add + * @param errorMessageOut + * Output with error message if failure to add palette + * @return True if palette was added, else false. + */ +bool +PaletteGroupStandardPalettes::addPaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) +{ + /* + * Try to insert palette. + * Second element in pair is true if successful + * and false if a palette with same name is in container. + */ + std::pair result = m_palettes.insert(palette); + if (result.second) { + return true; + } + + errorMessageOut = ("Cannot add palette with name " + + palette.getName() + + ". Palette with name exists."); + return false; +} + +/** + * Replace a palette. Palette must contain a name that matches name of an existing palette + * @param palette + * Palette used as a replacement + * @param errorMessageOut + * Output with error message if failure to replace palette + * @return True if palette was replaced, else false. + */ +bool +PaletteGroupStandardPalettes::replacePaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) +{ + auto findIter = m_palettes.find(palette); + if (findIter != m_palettes.end()) { + /* + * Must remove before inserting + */ + m_palettes.erase(findIter); + + std::pair result = m_palettes.insert(palette); + if ( ! result.second) { + const AString msg("Failed to insert new palette when replacing"); + CaretLogSevere(msg); + CaretAssertMessage(0, msg); + errorMessageOut = msg; + return false; + } + } + + errorMessageOut = ("No palette with name " + + palette.getName() + + "was found for replacing."); + return false; +} + +/** + * Rename a palette. Palette must contain a name that matches name of an existing palette + * @param paletteName + * Name of palette that is renamed + * @param newPaletteName + * New name for palette + * @param errorMessageOut + * Output with error message if failure to rename palette + * @return True if palette was rename, else false. + */ +bool +PaletteGroupStandardPalettes::renamePaletteImplementation(const AString& paletteName, + const AString& newPaletteName, + AString& errorMessageOut) +{ + for (ContainerIterator iter = m_palettes.begin(); + iter != m_palettes.end(); + iter++) { + if (iter->getName() == paletteName) { + PaletteNew palette = *iter; + palette.setName(newPaletteName); + + m_palettes.erase(iter); + + return addPalette(palette, + errorMessageOut); + } + } + + errorMessageOut = ("Palette for renaming with name " + + newPaletteName + + " does not exist."); + return false; +} + +/** + * Remove a palette. Palette must contain a name that matches name of an existing palette + * @param paletteName + * Name of palette that is removed + * @param errorMessageOut + * Output with error message if failure to remove palette + * @return True if palette was removed, else false. + */ +bool +PaletteGroupStandardPalettes::removePaletteImplementation(const AString& paletteName, + AString& errorMessageOut) +{ + for (auto iter = m_palettes.begin(); + iter != m_palettes.end(); + iter++) { + if (iter->getName() == paletteName) { + m_palettes.erase(iter); + return true; + } + } + + errorMessageOut = ("No palette with name \"" + + paletteName + + "\" was found for removing."); + return false; +} + +/** + * Get the palettes + * @param palettesOut + * Output containing all palettes in this group + */ +void +PaletteGroupStandardPalettes::getPalettes(std::vector& palettesOut) const +{ + palettesOut.clear(); + for (auto p : m_palettes) { + palettesOut.push_back(p); + } +} + +/** + * Get pointer to palette with the selected name + * @param paletteName + * Name of palette + * @return Pointer to palette or NULL if no palette with name exits + */ +std::unique_ptr +PaletteGroupStandardPalettes::getPaletteWithName(const AString& paletteName) +{ + std::unique_ptr paletteOut; + + /* Need reference to palettes to a pointer can be obtained */ + for (auto& p : m_palettes) { + if (p.getName() == paletteName) { + paletteOut.reset(new PaletteNew(p)); + break; + } + } + + return paletteOut; +} + +/** + * @return True if a palette with the given name exists, else false + * @param paletteName + * Name of palette + */ +bool +PaletteGroupStandardPalettes::hasPaletteWithName(const AString& paletteName) +{ + /* Need reference to palettes to a pointer can be obtained */ + for (auto& p : m_palettes) { + if (p.getName() == paletteName) { + return true; + break; + } + } + + return false; +} diff -Nru connectome-workbench-1.4.2/src/Palette/PaletteGroupStandardPalettes.h connectome-workbench-1.5.0/src/Palette/PaletteGroupStandardPalettes.h --- connectome-workbench-1.4.2/src/Palette/PaletteGroupStandardPalettes.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/PaletteGroupStandardPalettes.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,87 @@ +#ifndef __PALETTE_GROUP_STANDARD_PALETTES_H__ +#define __PALETTE_GROUP_STANDARD_PALETTES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include + +#include "PaletteGroup.h" + + + +namespace caret { + class PaletteNew; + + class PaletteGroupStandardPalettes : public PaletteGroup { + + public: + PaletteGroupStandardPalettes(); + + virtual ~PaletteGroupStandardPalettes(); + + PaletteGroupStandardPalettes(const PaletteGroupStandardPalettes&) = delete; + + PaletteGroupStandardPalettes& operator=(const PaletteGroupStandardPalettes&) = delete; + + void loadPalettes(); + + virtual void getPalettes(std::vector& palettesOut) const override; + + virtual std::unique_ptr getPaletteWithName(const AString& paletteName) override; + + virtual bool hasPaletteWithName(const AString& paletteName) override; + + // ADD_NEW_METHODS_HERE + + protected: + virtual bool addPaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) override; + + virtual bool replacePaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) override; + + virtual bool removePaletteImplementation(const AString& paletteName, + AString& errorMessageOut) override; + + virtual bool renamePaletteImplementation(const AString& paletteName, + const AString& newPaletteName, + AString& errorMessageOut) override; + + private: + void addPaletteHelper(const PaletteNew& palette); + + /* unordered_set may be a better choice as sorting not important */ + typedef std::set ContainerType; + typedef ContainerType::iterator ContainerIterator; + ContainerType m_palettes; + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PALETTE_GROUP_STANDARD_PALETTES_DECLARE__ + // +#endif // __PALETTE_GROUP_STANDARD_PALETTES_DECLARE__ + +} // namespace +#endif //__PALETTE_GROUP_STANDARD_PALETTES_H__ diff -Nru connectome-workbench-1.4.2/src/Palette/PaletteGroupUserCustomPalettes.cxx connectome-workbench-1.5.0/src/Palette/PaletteGroupUserCustomPalettes.cxx --- connectome-workbench-1.4.2/src/Palette/PaletteGroupUserCustomPalettes.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/PaletteGroupUserCustomPalettes.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,187 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __PALETTE_GROUP_USER_CUSTOM_PALETTES_DECLARE__ +#include "PaletteGroupUserCustomPalettes.h" +#undef __PALETTE_GROUP_USER_CUSTOM_PALETTES_DECLARE__ + +#include "CaretAssert.h" +#include "CaretPreferences.h" + +using namespace caret; + + + +/** + * \class caret::PaletteGroupUserCustomPalettes + * \brief Group for user custom palettes + * \ingroup Palette + */ + +/** + * Constructor. + * @param caretPreferences + * Pointer to caret preferences + */ +PaletteGroupUserCustomPalettes::PaletteGroupUserCustomPalettes(CaretPreferences* caretPreferences) +: PaletteGroup(PaletteGroup::GroupType::USER_CUSTOM) +{ + CaretAssert(caretPreferences); + m_caretPreferences = caretPreferences; +} + +/** + * Destructor. + */ +PaletteGroupUserCustomPalettes::~PaletteGroupUserCustomPalettes() +{ +} + +/** + * Add a palette. Palette must contain a name not used by an existing palette in this group. + * @param palette + * Palette to add + * @param errorMessageOut + * Output with error message if failure to add palette + * @return True if palette was added, else false. + */ +bool +PaletteGroupUserCustomPalettes::addPaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) +{ + AString paletteXML; + return m_caretPreferences->paletteUserCustomAdd(palette.getName(), + paletteXML, + errorMessageOut); +} + +/** + * Replace a palette. Palette must contain a name that matches name of an existing palette + * @param palette + * Palette used as a replacement + * @param errorMessageOut + * Output with error message if failure to replace palette + * @return True if palette was replaced, else false. + */ +bool +PaletteGroupUserCustomPalettes::replacePaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) +{ + AString paletteXML; + return m_caretPreferences->paletteUserCustomReplace(palette.getName(), + paletteXML, + errorMessageOut); +} + +/** + * Rename a palette. Palette must contain a name that matches name of an existing palette + * @param paletteName + * Name of palette that is renamed + * @param newPaletteName + * New name for palette + * @param errorMessageOut + * Output with error message if failure to rename palette + * @return True if palette was rename, else false. + */ +bool +PaletteGroupUserCustomPalettes::renamePaletteImplementation(const AString& paletteName, + const AString& newPaletteName, + AString& errorMessageOut) +{ + std::unique_ptr palette = getPaletteWithName(paletteName); + if (palette) { + AString paletteXML; + return m_caretPreferences->paletteUserCustomRename(paletteName, + newPaletteName, + paletteXML, + errorMessageOut); + } + + errorMessageOut = ("Palette with name \"" + + paletteName + + "\" does not exist for renaming in preferences"); + return false; +} + +/** + * Remove a palette. Palette must contain a name that matches name of an existing palette + * @param paletteName + * Name of palette that is removed + * @param errorMessageOut + * Output with error message if failure to remove palette + * @return True if palette was removed, else false. + */ +bool +PaletteGroupUserCustomPalettes::removePaletteImplementation(const AString& paletteName, + AString& errorMessageOut) +{ + return m_caretPreferences->paletteUserCustomRemove(paletteName, + errorMessageOut); +} + +/** + * Get the palettes + * @param palettesOut + * Output containing all palettes in this group + */ +void +PaletteGroupUserCustomPalettes::getPalettes(std::vector& palettesOut) const +{ + palettesOut.clear(); + + std::vector allPalettesXML; + m_caretPreferences->paletteUserCustomGetAll(allPalettesXML); + + for (auto p : allPalettesXML) { + /* Convert from XML to a palette */ + } +} + +/** + * Get pointer to palette with the selected name + * @param paletteName + * Name of palette + * @return Pointer to palette or NULL if no palette with name exits else false + */ +std::unique_ptr +PaletteGroupUserCustomPalettes::getPaletteWithName(const AString& paletteName) +{ + std::unique_ptr paletteOut; + AString paletteXML; + if (m_caretPreferences->paletteUserCustomGetByName(paletteName, + paletteXML)) { + + } + + return paletteOut; +} + +/** + * @return True if a palette with the given name exists + * @param paletteName + * Name of palette + */ +bool +PaletteGroupUserCustomPalettes::hasPaletteWithName(const AString& paletteName) +{ + return m_caretPreferences->paletteUserCustomExists(paletteName); +} + diff -Nru connectome-workbench-1.4.2/src/Palette/PaletteGroupUserCustomPalettes.h connectome-workbench-1.5.0/src/Palette/PaletteGroupUserCustomPalettes.h --- connectome-workbench-1.4.2/src/Palette/PaletteGroupUserCustomPalettes.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/PaletteGroupUserCustomPalettes.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,82 @@ +#ifndef __PALETTE_GROUP_USER_CUSTOM_PALETTES_H__ +#define __PALETTE_GROUP_USER_CUSTOM_PALETTES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "PaletteGroup.h" + + + +namespace caret { + + class CaretPreferences; + + class PaletteGroupUserCustomPalettes : public PaletteGroup { + + public: + PaletteGroupUserCustomPalettes(CaretPreferences* caretPreferences); + + virtual ~PaletteGroupUserCustomPalettes(); + + PaletteGroupUserCustomPalettes(const PaletteGroupUserCustomPalettes&) = delete; + + PaletteGroupUserCustomPalettes& operator=(const PaletteGroupUserCustomPalettes&) = delete; + + virtual void getPalettes(std::vector& palettesOut) const override; + + virtual std::unique_ptr getPaletteWithName(const AString& paletteName) override; + + virtual bool hasPaletteWithName(const AString& paletteName) override; + + // ADD_NEW_METHODS_HERE + + protected: + virtual bool addPaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) override; + + virtual bool replacePaletteImplementation(const PaletteNew& palette, + AString& errorMessageOut) override; + + virtual bool removePaletteImplementation(const AString& paletteName, + AString& errorMessageOut) override; + + virtual bool renamePaletteImplementation(const AString& paletteName, + const AString& newPaletteName, + AString& errorMessageOut) override; + + private: + /* Note: DO NOT delete preferences */ + CaretPreferences* m_caretPreferences = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __PALETTE_GROUP_USER_CUSTOM_PALETTES_DECLARE__ + // +#endif // __PALETTE_GROUP_USER_CUSTOM_PALETTES_DECLARE__ + +} // namespace +#endif //__PALETTE_GROUP_USER_CUSTOM_PALETTES_H__ diff -Nru connectome-workbench-1.4.2/src/Palette/PaletteNew.cxx connectome-workbench-1.5.0/src/Palette/PaletteNew.cxx --- connectome-workbench-1.4.2/src/Palette/PaletteNew.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/PaletteNew.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,147 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "PaletteNew.h" + +#include "CaretAssert.h" +#include "CaretException.h" +#include "CaretLogger.h" + +#include "ColorFunctions.h" +#include "MathFunctions.h" + +#include + +using namespace std; +using namespace caret; + +namespace +{ + int searchLowSide(const float scalar, const vector& controlPoints, int low, int high) + { + while (low < high - 1) + { + int guess = (high + low) / 2;//simple bisection search + if (scalar < controlPoints[guess].scalar) + { + high = guess; + } else { + low = guess; + } + } + return low; + } + + void copyColor(float to[3], const float from[3]) + { + for (int i = 0; i < 3; ++i) + { + CaretAssert(from[i] >= 0.0f && from[i] <= 1.0f); + to[i] = from[i]; + } + } +} + +PaletteNew::PaletteNew(vector posRange, float zeroColor[3], vector negRange) : m_posRange(posRange), m_negRange(negRange) +{ + CaretAssert(posRange[0].scalar == 0.0f); + CaretAssert(posRange.back().scalar == 1.0f); + CaretAssert(negRange[0].scalar == -1.0f); + CaretAssert(negRange.back().scalar == 0.0f); + copyColor(m_zeroColor, zeroColor); +} + +void PaletteNew::getPositiveColor(const float scalar, float rgbOut[3]) const +{ + CaretAssert(scalar >= 0.0f); + m_posRange.getPaletteColor(scalar, rgbOut); +} + +void PaletteNew::getNegativeColor(const float scalar, float rgbOut[3]) const +{ + CaretAssert(scalar <= 0.0f); + m_negRange.getPaletteColor(scalar, rgbOut); +} + +PaletteNew::PaletteRange::PaletteRange(const vector controlPoints) +{ + CaretAssert(controlPoints.size() > 1); + for (size_t i = 1; i < controlPoints.size(); ++i) + { + if (controlPoints[i].scalar <= controlPoints[i - 1].scalar) + { + CaretAssert(false); + throw CaretException("palette ranges must use unique, ascending-sorted scalars"); + } + } + m_controlPoints = controlPoints; + m_lowPoint = m_controlPoints[0].scalar; + m_highPoint = m_controlPoints.back().scalar; + float diff = m_highPoint - m_lowPoint;//in practice this should always be 1 + m_lookup[0] = 0; + m_lookup[BUCKETS] = m_controlPoints.size() - 2;//do not allow search to ever say that it is past the last control point + for (int i = 1; i < BUCKETS; ++i) + { + m_lookup[i] = searchLowSide(m_lowPoint + (diff * i) / (BUCKETS), m_controlPoints, 0, m_controlPoints.size() - 1);//save the low control point at each bucket wall (can derive high from next bucket) + } +} + +void PaletteNew::PaletteRange::getPaletteColor(const float scalar, float rgbOut[3]) const +{ + CaretAssert(MathFunctions::isNumeric(scalar)); + int bucket = int((scalar - m_lowPoint) / (m_highPoint - m_lowPoint) * BUCKETS);//this implicitly tests for below or above the palette range + if (bucket < 0) + { + copyColor(rgbOut, m_controlPoints[0].color); + return; + } + if (bucket >= BUCKETS) + { + copyColor(rgbOut, m_controlPoints.back().color); + return; + } + int lowSide = searchLowSide(scalar, m_controlPoints, m_lookup[bucket], m_lookup[bucket + 1] + 1);//this is why m_lookup is [BUCKETS + 1] + float interpFactor = (scalar - m_controlPoints[lowSide].scalar) / (m_controlPoints[lowSide + 1].scalar - m_controlPoints[lowSide].scalar); + for (int i = 0; i < 3; ++i) + {//never output outside of [0, 1] + rgbOut[i] = max(0.0f, min(1.0f, m_controlPoints[lowSide].color[i] * (1 - interpFactor) + m_controlPoints[lowSide + 1].color[i] * interpFactor)); + } +} + +vector PaletteNew::PaletteRange::getPerceptualGradient(const int numBuckets) const +{ + CaretAssert(numBuckets >= 3);//one at min, one at max, all equally spaced + vector ret(numBuckets); + ScalarColor lowColor, midColor, highColor; + midColor.scalar = m_lowPoint; + getPaletteColor(midColor.scalar, midColor.color); + lowColor = midColor;//use central difference, with unsymmetric endpoints + for (int i = 1; i < numBuckets - 1; ++i) + { + float interpFactor = float(i) / (numBuckets - 1); + highColor.scalar = (1 - interpFactor) * m_lowPoint + interpFactor * m_highPoint; + getPaletteColor(highColor.scalar, highColor.color); + ret[i - 1] = ColorFunctions::perceptualDistanceSRGB(highColor.color, lowColor.color) / (highColor.scalar - lowColor.scalar); + lowColor = midColor; + midColor = highColor; + } + ret[numBuckets - 1] = ColorFunctions::perceptualDistanceSRGB(highColor.color, midColor.color) / (highColor.scalar - midColor.scalar); + return ret; +} diff -Nru connectome-workbench-1.4.2/src/Palette/PaletteNew.h connectome-workbench-1.5.0/src/Palette/PaletteNew.h --- connectome-workbench-1.4.2/src/Palette/PaletteNew.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Palette/PaletteNew.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,124 @@ +#ifndef __PALETTE_NEW_H__ +#define __PALETTE_NEW_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "AString.h" + +namespace caret { + + class PaletteNew + { + public: + struct ScalarColor + { + float scalar; + float color[3];//RGB, no A + ScalarColor(const float scalar, const float color[3]) + { + this->scalar = scalar; + for (int i = 0; i < 3; ++i) + { + this->color[i] = color[i]; + } + } + ScalarColor(const float scalar, const float red, const float green, const float blue) + { + this->scalar = scalar; + color[0] = red; + color[1] = green; + color[2] = blue; + } + ScalarColor() + { + scalar = 0.0f; + for (int i = 0; i < 3; ++i) + { + color[i] = 0.0f; + } + } + bool operator==(const ScalarColor& rhs) const { + if (scalar != rhs.scalar) { + return false; + } + for (int i = 0; i < 3; i++) { + if (color[i] != rhs.color[i]) { + return false; + } + } + return true; + } + static ScalarColor fromRGB255(const float scalar, const int red, const int green, const int blue) + { + return ScalarColor(scalar, red / 255.0f, green / 255.0f, blue / 255.0f); + } + void toRGB255(int& redOut, int& greenOut, int& blueOut) const + { + redOut = int(color[0] * 255.99f);//dirty hack to avoid needing to test == 256 + greenOut = int(color[1] * 255.99f);//keeps bin widths fair to float, and should round trip + blueOut = int(color[2] * 255.99f); + } + }; + + PaletteNew(std::vector posRange, float zeroColor[3], std::vector negRange); + + void getPositiveColor(const float scalar, float rgbOut[3]) const; + void getNegativeColor(const float scalar, float rgbOut[3]) const; + void getZeroColor(float colorOut[3]) const { for (int i = 0; i < 3; ++i) colorOut[i] = m_zeroColor[i]; } + + AString getName() const { return m_name; } + + void setName(const AString& name) { m_name = name; } + + std::vector getPosRange() const { return m_posRange.getRange(); } + std::vector getNegRange() const { return m_negRange.getRange(); } + + //TODO: simulate color deficiency + std::vector getPosPerceptualGradient(const int numBuckets) const { return m_posRange.getPerceptualGradient(numBuckets); } + std::vector getNegPerceptualGradient(const int numBuckets) const { return m_negRange.getPerceptualGradient(numBuckets); } + + private: + class PaletteRange + { + static constexpr int BUCKETS = 256;//keep this at a 2^n to minimize float rounding during lookup + std::vector m_controlPoints; + int m_lookup[BUCKETS + 1];//this is used only to narrow the binary search range, frequently making it unnecessary to search + float m_lowPoint, m_highPoint;//less indirection for critical values + public: + PaletteRange(const std::vector controlPoints); + + void getPaletteColor(const float scalar, float rgbOut[3]) const; + + std::vector getRange() const { return m_controlPoints; } + + std::vector getPerceptualGradient(const int numBuckets) const; + }; + + PaletteRange m_posRange, m_negRange; + float m_zeroColor[3]; + AString m_name; + }; + +}//namespace + +#endif //__PALETTE_NEW_H__ diff -Nru connectome-workbench-1.4.2/src/Qwt/qwt_null_paintdevice.cpp connectome-workbench-1.5.0/src/Qwt/qwt_null_paintdevice.cpp --- connectome-workbench-1.4.2/src/Qwt/qwt_null_paintdevice.cpp 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Qwt/qwt_null_paintdevice.cpp 2021-02-16 19:46:47.000000000 +0000 @@ -10,6 +10,7 @@ #include "qwt_null_paintdevice.h" #include #include +#include class QwtNullPaintDevice::PrivateData { diff -Nru connectome-workbench-1.4.2/src/Qwt/qwt_painter_command.h connectome-workbench-1.5.0/src/Qwt/qwt_painter_command.h --- connectome-workbench-1.4.2/src/Qwt/qwt_painter_command.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Qwt/qwt_painter_command.h 2021-02-16 19:46:47.000000000 +0000 @@ -15,6 +15,7 @@ #include #include #include +#include class QPainterPath; diff -Nru connectome-workbench-1.4.2/src/Qwt/qwt_painter.cpp connectome-workbench-1.5.0/src/Qwt/qwt_painter.cpp --- connectome-workbench-1.4.2/src/Qwt/qwt_painter.cpp 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Qwt/qwt_painter.cpp 2021-02-16 19:46:47.000000000 +0000 @@ -27,6 +27,7 @@ #include #include #include +#include #if QT_VERSION >= 0x050000 #include diff -Nru connectome-workbench-1.4.2/src/Qwt/qwt_plot_panner.cpp connectome-workbench-1.5.0/src/Qwt/qwt_plot_panner.cpp --- connectome-workbench-1.4.2/src/Qwt/qwt_plot_panner.cpp 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Qwt/qwt_plot_panner.cpp 2021-02-16 19:46:47.000000000 +0000 @@ -14,6 +14,7 @@ #include #include #include +#include static QBitmap qwtBorderMask( const QWidget *canvas, const QSize &size ) { diff -Nru connectome-workbench-1.4.2/src/Qwt/qwt_plot_renderer.cpp connectome-workbench-1.5.0/src/Qwt/qwt_plot_renderer.cpp --- connectome-workbench-1.4.2/src/Qwt/qwt_plot_renderer.cpp 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Qwt/qwt_plot_renderer.cpp 2021-02-16 19:46:47.000000000 +0000 @@ -23,6 +23,7 @@ #if QT_VERSION >= 0x050000 #include #include +#include #else // QT_VERSION #include #include diff -Nru connectome-workbench-1.4.2/src/Qwt/qwt_widget_overlay.cpp connectome-workbench-1.5.0/src/Qwt/qwt_widget_overlay.cpp --- connectome-workbench-1.4.2/src/Qwt/qwt_widget_overlay.cpp 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Qwt/qwt_widget_overlay.cpp 2021-02-16 19:46:47.000000000 +0000 @@ -13,6 +13,7 @@ #include #include #include +#include static QImage::Format qwtMaskImageFormat() { diff -Nru connectome-workbench-1.4.2/src/Resources/Gui/gui_resources.qrc connectome-workbench-1.5.0/src/Resources/Gui/gui_resources.qrc --- connectome-workbench-1.4.2/src/Resources/Gui/gui_resources.qrc 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Gui/gui_resources.qrc 2021-02-16 19:46:47.000000000 +0000 @@ -8,6 +8,16 @@ ./LayersPanel/construction.png ./LayersPanel/wrench.png ./PaletteSettings/chain_link_icon.png +./PaletteEditorDialog/construction.png +./RecentFilesDialog/favorite_filled.png +./RecentFilesDialog/favorite_outline.png +./RecentFilesDialog/forget.png +./RecentFilesDialog/forget_black.png +./RecentFilesDialog/forget_red.png +./RecentFilesDialog/forget_on.png +./RecentFilesDialog/hcp_image.png +./RecentFilesDialog/share.png +./RecentFilesDialog/twitter_image.png ./SceneFileDialog/caution.png ./SpecFileDialog/delete_icon.png ./SpecFileDialog/load_icon.png @@ -17,10 +27,12 @@ ./Splash/startup_image.png ./Splash/twitter.png ./ToolBar/clapboard.png +./ToolBar/clipping.png ./ToolBar/features_toolbox.png ./ToolBar/help.png ./ToolBar/identify.png ./ToolBar/info.png +./ToolBar/lighting.png ./ToolBar/macro.png ./ToolBar/movie.png ./ToolBar/overlay_toolbox.png Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/PaletteEditorDialog/construction.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/PaletteEditorDialog/construction.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/favorite_filled.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/favorite_filled.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/favorite_outline.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/favorite_outline.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/forget_black.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/forget_black.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/forget_on.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/forget_on.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/forget.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/forget.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/forget_red.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/forget_red.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/hcp_image.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/hcp_image.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/share.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/share.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/RecentFilesDialog/twitter_image.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/RecentFilesDialog/twitter_image.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/ToolBar/clipping.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/ToolBar/clipping.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Gui/ToolBar/lighting.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Gui/ToolBar/lighting.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image001.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image001.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image002.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image002.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image003.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image003.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image004.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image004.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image005.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image005.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image006.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image006.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image007.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image007.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image009.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image009.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image010.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image010.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image011.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image011.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image012.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image012.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image013.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image013.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image014.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image014.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image015.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image015.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image016.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image016.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image017.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image017.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image018.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image018.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image019.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image019.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image020.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image020.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image021.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image021.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image022.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image022.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image023.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image023.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image024.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image024.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image025.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image025.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image026.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image026.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image027.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image027.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image028.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image028.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image029.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image029.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image030.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image030.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image031.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image031.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image032.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image032.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image033.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image033.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,2556 +0,0 @@ - - - - - - - - - - - - - -

- -

Guide to WB Annotations

- -

1.3.2

- -

01 November 2018

- -

 

- -

- -
-
- -

 

- -

Table of Contents

- -

Understanding the Need for Locking -the Aspect Ratio. 6

- -

Scenes. 8

- -

Annotation Toolbar and Example -Annotations. 9

- -

Annotation Coordinate Spaces. 10

- -

Chart Data Space (Ch). 10

- -

Stereotaxic Coordinate Space (St). 10

- -

Surface Coordinate Space (Sf). 10

- -

Tab Coordinate Space (T). 11

- -

Window Coordinate Space (W). 11

- -

Types of Annotations. 12

- -

Box. 12

- -

Color Bars. 13

- -

Image. 13

- -

Line. 13

- -

Oval 13

- -

Text. 13

- -

Annotation Properties. 14

- -

Annotation Line Width, Line Color, -and Fill Color. 14

- -

Annotation Line Arrow Tips. 15

- -

Annotation Text Characters. 15

- -

Annotation Text Font Property. 16

- -

Annotation Text Alignment Property. 16

- -

Annotation Text Orientation.. 17

- -

Annotation Coordinate Property. 17

- -

Annotation Width and Height. 18

- -

Annotation Rotation Property. 18

- -

Annotation Files. 18

- -

Disk Annotation File. 18

- -

Scene Annotation File. 19

- -

Selecting Annotations. 19

- -

Editing Annotation Position, Size, -and Rotation. 20

- -

Editing (Redo and Undo). 21

- -

Inserting Annotations. 21

- -

Deleting Annotations. 23

- -

Format 23

- -

Context Sensitive (Pop-up) Menu. 24

- -

Edit Menu. 26

- -

Display Control of Annotations and -Substitutions. 27

- -

Options. 27

- -

Selection of Annotations in Features -Toolbox. 28

- -

Content of Annotations Tab in -Features Toolbox. 28

- -

Substitutions. 29

- -

Annotation Substitution File. 30

- -

Inserting substitutions into -Annotations. 30

- -

Annotation Grouping. 32

- -

Mouse Selection of Annotations. 32

- -

Creating a User Group.. 32

- -

Removing a User Group.. 32

- -

Recreating a User Group.. 33

- -

Color Bar Editing. 33

- -

Location and Positioning. 34

- -

Data Numerics. 35

- -

 

- -
-
- -

- -

Figure 1: Single Tab View................................................................................................... 5

- -

Figure 2: Tile Tabs View..................................................................................................... 5

- -

Figure 3: Annotations on Surface........................................................................................ 7

- -

Figure 4: Width Increased with Unlocked Aspect Ratio...................................................... 7

- -

Figure 5: Width increased with a Locked Aspect Ratio........................................................ 7

- -

Figure 6: Figure from A multimodal parcellation of human -cerebral cortex (Glasser et al).. 8

- -

Figure 7: Window with Annotation ToolBar....................................................................... 9

- -

Figure 8: Surface with Annotations................................................................................... 12

- -

Figure 9: Window Width Increased with Unlocked Aspect Ratio...................................... 12

- -

Figure 10: Window Width Increased After Locking Aspect Ratio..................................... 12

- -

Figure 11: Line Width and Line/Fill Color Controls.......................................................... 15

- -

Figure 12: Line Arrow Tip Controls.................................................................................. 15

- -

Figure 13: Text Characters and Offset (Surface Space only) -Controls............................... 16

- -

Figure 14: Text Font Attributes.......................................................................................... 16

- -

Figure 15: Text Alignment Controls.................................................................................. 16

- -

Figure 16: Text Orientation Controls................................................................................. 17

- -

Figure 17: Coordinate Controls (Chart, Stereotaxic, Tab, -Window).................................. 17

- -

Figure 18: Annotation Coordinate (Chart, Stereotaxic, Tab, or -Window).......................... 17

- -

Figure 19: Surface Coordinate Controls............................................................................. 17

- -

Figure 20: Width and Height Controls............................................................................... 18

- -

Figure 21: Rotation Control............................................................................................... 18

- -

Figure 22: Box in Red that is Selected for Editing............................................................. 20

- -

Figure 23: Editing Redo and Undo Controls...................................................................... 21

- -

Figure 24: Insert New Annotation Controls....................................................................... 21

- -

Figure 25: Annotation File Selection Controls................................................................... 22

- -

Figure 26: Delete Annotation Control................................................................................ 23

- -

Figure 27: Format Menu Items.......................................................................................... 23

- -

Figure 28: Features ToolBox Annotation Controls............................................................ 27

- -

Figure 29: Overlay ToolBox.............................................................................................. 31

- -

Figure 30: Overlay and Map Settings Color Bar Controls................................................. 31

- -


-  

- -

 

- -

- -

Figure 1: -Single Tab View

- -

 

- -

 

- -

- -

Figure 2: Tile Tabs View

- -

Understanding the Need for Locking the Aspect Ratio

- -

 

- -

To create an optimal view of -a brain model, the user may adjust the size of the window and optionally remove -the Toolbar, Overlay Toolbox, and the Features Toolbox.  As the windowÕs -shape changes, the size of displayed brain model is adjusted to best fill the -available space.  For a cerebral cortex model in a default lateral view, -the height of the cortex is set to be slightly smaller than the height of its -available area in the window.  If this causes parts of the cortex to -exceed the width of the available are, the cortex is shrunk so that all of it -is visible. 

- -

 

- -

Allowing an independent width -and height for the window is known as a variable aspect ratio.  An aspect -ratio is the ratio of height to width (height divided by width).  Due to -this variable aspect ratio, annotations in Tab and Window Coordinate Space use -a special two-dimensional coordinate system consisting of percentages.  In -this percentage coordinate system, the range for X-percentage coordinates range -from 0% at the left edge of the graphics region to 100% at the right -edge.  Correspondingly, Y-percentage coordinates range from 0% at the -bottom edge to 100% at the top edge.  Thus, if one places an annotation at -an X-coordinate of 100%, this annotation will always be at the right edge of -the window, regardless of the windowÕs width.

- -

 

- -

While this percentage -coordinate system works well with a variable aspect ratio, it has a major -shortcoming.  If one places an annotation in Tab or Window Space over an -anatomical feature of a brain model and increases the width (or height) of the -window, the annotation may ÒdriftÓ and no longer overlay the anatomical -feature.  This anomaly occurs because the size of the brain model does not -increase in the same proportion as the size of the window.  To correct -this problem, wb_view provides (and strongly recommends) locking of the aspect -ratio.  When aspect locking is enabled, the aspect ratio at the time of -locking is saved and used when drawing brain models.  If the window is -changed to a size that does not result in the same aspect ratio as the locked -aspect ratio, padding is added to the horizontal or vertical sides of the -window to limit the graphics region in a way that matches the locked aspect -ratio.   For example, suppose one creates two annotations in tab -coordinate space, a red oval and a fuchsia box as in Figure 3.  Now suppose one increases the width of the -window while the aspect ratio is unlocked.  Notice that the annotations no -longer overlay the original anatomical regions, Figure 4.  However, if one locks the aspect ratio and -then increases the width of the window, padding is added to the side of the -window to restrict the model to an area with the aspect ratio from the time of -locking, Figure 5.

- -

 

- -

 

- -

- -

Figure 3: -Annotations on Surface

- -

 

- -

- -

Figure 4: -Width Increased with Unlocked Aspect Ratio

- -

 

- -

 

- -

- -

Figure 5: -Width increased with a Locked Aspect Ratio

- -

 

- -

 

- -

 

- -

 

- -

In conclusion, locking the -aspect ratio should always be performed prior to creating annotations.  In -addition, once the aspect ratio is locked, it should never be unlocked.  -Otherwise, it will cause annotations to drift from their desired locations.

- -

 

- -

Scenes

- -

 

- -

Closely related to -annotations are scenes.  Scenes allow one to preserve and reproduce Ôthe -stateÕ of wb_view and are frequently used to produce figures for -publications.    Scenes can be shared with collaborators or with -the neuroscience community through the BALSA Database.  Annotations are -frequently featured in scenes and allow one to highlight items of in the data -and present auxiliary information (Figure 6). - Annotations are typically saved within a scene which eliminates the -management of an external data file (although annotations may be stored in -external data files).

- -

 

- -

- -

Figure 6: -Figure from A multimodal parcellation of human cerebral cortex (Glasser et al)

- -

 

- -

 

- -

 

- -

 

- -

Annotation Toolbar and Example Annotations

- -

 

- -

- -

Figure 7: Window with -Annotation ToolBar

- -

 
-

- -

 

- -

Annotation Coordinate Spaces

- -

 

- -

Several coordinate spaces are -available for annotations.  The coordinate spaces control the movement of -placement of annotations.

- -

 

- -

Chart Data Space (Ch)

- -

 

- -

An annotation in chart data -space is displayed within the char data plot and is invalid outside of the data -plot.  The coordinate range is identical to the X- and Y-axis of the -displayed chart data.  This is the range of data in the chart changes, an -annotation in chart data space will move.

- -

 

- -

Spacer Coordinate Space (Sp)

- -

 

- -

When Tile Tabs is enabled, -rows and/or columns in the Tile Tabs Configuration may contain Spacers or Tabs -(Tabs is the default).  Spacers create an empty row (or column) in a Tile -Tabs Configuration so that one has regions for displaying annotations without -underlying brain models.  Each annotation in Spacer Coordinate Space is -assigned to a specific row and column and is visible only when Tile Tabs is -enabled.  The most common use for annotations in this space is as row and -column titles.

- -

Stereotaxic Coordinate Space (St)

- -

 

- -

An annotation in stereotaxic -space will appear in any display of a surface or a volume.  A particular -annotation in stereotaxic space may appear in more than one location such as -when viewing a surface montage.

- -

 

- -

Stereotaxic space (Talairach -is an example of a specific stereotaxic space) is the three-dimensional -coordinate system in which surface and volume models are displayed.  In -Workbench, the stereotaxic space is in an LPI orientation (negative X is Left, -negative Y is Posterior, and negative Z is Inferior).  Annotations in Stereotaxic -Space will follow the model as it is panned, rotated, or zoomed.  At -times, annotations in Stereotaxic Space may not be visible when they are behind -a model.  Annotations in Stereotaxic Space are always drawn in a plane -parallel to the screen.

- -

 

- -

Surface Coordinate Space (Sf)

- -

 

- -

An annotation in surface -coordinate space will appear in any view of the surface to which the annotation -is attached.  When a surface space is used, the annotation is associated -with a surface by the combination of the surfaceÕs structure (ie: left cerebral -cortex) and a vertex index.  An optional offset vector is used to improve -visibility of the annotation (so not ÔbehindÕ the surface).

- -

 

- -

Tab Coordinate Space (T)

- -

 

- -

An annotation in tab coordinate -space is displayed in a specific tab (uses tab number).  When Tile Tabs is -enabled, all of the windowÕs tabs are displayed in a grid-like pattern.  Tab -Coordinate Space is a two-dimensional space with the X- and Y-coordinates expressed -in percentages ranging from 0% to 100%.  For the X-coordinate, 0% is on -the left and 100% is on the right.  For the Y-coordinate, 0% is at the -bottom and 100% is at the top. 

- -

If Tile Tabs is enabled, the -coordinate range spans only cell allocation to that tab in the graphics -region.  When the tabÕs aspect is locked, the locations of 0% and 100% may -be offset from the graphics regions edges so that annotations remain at a -consistent location over an underlying brain model.

- -

 

- -

Window Coordinate Space (W)

- -

 

- -

The window coordinate functions -nearly identically to the tab coordinate space except that the coordinates -always span the entire graphics region.  Tile Tabs has no effect on window -coordinates. 

- -

 

- -

Aspect Locking for Tab and -Window Coordinate Spaces

- -

 

- -

The graphics region in wb_view -defaults to a variable aspect ratio.  With a variable aspect ratio, the -user may independently adjust the width and height of the window.  Since -the tab and window coordinate spaces use a percentage coordinate system, -annotations in these spaces will move away from the center as the width or -height is increased.  Conversely, the annotation will move towards the -center as the width or height is decreased.  For example, in Figure -8, a red oval and a fuchsia square annotation -are displayed over a left hemisphere.  In one stretches the width of the -window with an unlocked aspect ratio, the two annotations move away from the -center but remain at the same percentage width coordinate, Figure 9.  In contrast, if one locks the aspect ratio and -expands the width of the window, padding is added to the sides so that the -percentage coordinates remain limited to the original aspect ratio, Figure -10.  The result of aspect locking is that -the annotations remain over the same anatomical features as the windowÕs size -changes.

- -

 

- -

 

- -

- -

Figure 8: -Surface with Annotations

- -

   

- -

- -

Figure 9: -Window Width Increased with Unlocked Aspect Ratio

- -

- -

Figure 10: -Window Width Increased After Locking Aspect Ratio

- -

 

- -

Types of Annotations

- -

 

- -

Box

- -

 

- -

A box annotation is -positioned with one coordinate located at the center of the box.  The -boxÕs width and height are ÔnormalizedÕ relative to the size of width and -height of the tab or window containing the box.  Thus, a size of 100% will -fill the tab/window horizontally.  Boxes possess both Fill and Line colors -and one or both may be used.

- -

 

- -

Color Bars

- -

 

- -

Color bars are a special case -of annotations that pictorially describe the mapping of numeric data into a -color palette.  Color bars are not created like other annotation types but -are enabled for display by pressing the color bar button for a row in the -Overlay ToolBoxÕs Layers tab.  Positioning of color bars is performed -automatically unless the user chooses to manually position them.  Color -bars are limited to Tab and Window spaces.  Editing of color bars is -described at the end of this document.

- -

 

- -

Image

- -

 

- -

An image annotation is -positioned with one coordinate located at the center of the image.  The -height of image is specified as a percentage of the region containing the -image.  As the region changes in height, a corresponding change will occur -in the height of the image.  The width of the image will scale with the -height of the image using the aspect ratio of the image when it was associated -with the orientation.

- -

 

- -

Line

- -

 

- -

A line annotation contains -two coordinates with one located at each end point.  Lines are colored -using the Line Color only.  Arrows may be added to each of the end points.

- -

 

- -

Oval

- -

 

- -

With the exception of the its -shape, an ovalÕs functionality is identical to a box.

- -

 

- -

Text

- -

 

- -

Text annotations contain one -coordinate and the text alignment properties control the offset of the text -relative to the coordinate.  Text characters are drawn in the Font Color.  The -Line color, if enabled, draws a box around the text.  The File color, if -enabled, draws a background behind the text.  The height of text is -specified as a percentage of the regionÕs height containing the text.  As -the region changes in height, a corresponding change will occur in the size of -the text.

- -

 

- -

Text annotations may contain -substitutions. 

- -

 

- -

Annotation Properties

- -

 

- -

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Property

-
-

Box

-
-

Color Bar

-
-

Image

-
-

Line

-
-

Oval

-
-

Text

-
-

Arrow Tips

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

 

-
-

 

-
-

Line Width

-
-

Y

-
-

 

-
-

 

-
-

Y

-
-

Y

-
-

Y

-
-

Line Color

-
-

Y

-
-

 

-
-

 

-
-

Y

-
-

Y

-
-

Y

-
-

Fill Color

-
-

Y

-
-

Y

-
-

 

-
-

 

-
-

Y

-
-

Y

-
-

Text Color

-
-

 

-
-

Y

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

Text Characters

-
-

 

-
-

Y

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

Font Name

-
-

 

-
-

Y

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

Font Size

-
-

 

-
-

Y

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

Font Style

-
-

 

-
-

 

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

Horizontal Alignment

-
-

 

-
-

 

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

Vertical Alignment

-
-

 

-
-

 

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

Orientation

-
-

 

-
-

 

-
-

 

-
-

 

-
-

 

-
-

Y

-
-

Width

-
-

Y

-
-

Y

-
-

Y

-
-

 

-
-

Y

-
-

 

-
-

Height

-
-

Y

-
-

Y

-
-

Y

-
-

 

-
-

Y

-
-

 

-
-

Rotation

-
-

Y

-
-

Y

-
-

Y

-
-

Y

-
-

Y

-
-

Y

-
- -

 

- -

 

- -

Annotation Line Width, Line Color, and Fill Color

- -

 

- -

The Line Width Property controls -the line width of Line annotations and the outline width of Box, Oval, and Text -annotations.  Line widths are expressed as a percentage of the height of -the region containing the annotation.  When multiple annotations are -selected with different line widths, the smallest selected line width is -displayed followed by a plus symbol (+).

- -

 

- -

The Line Color Property -controls the outline color of a Box, Oval or Text.  To change the color, -one presses the small arrow to display a menu containing the available colors.  -The two special selections are Custom and None.  Choosing Custom displays -a dialog that allow the user to create a color by adjusting red, green, and -blue color components.  None inhibits the drawing of the associated line.

- -

 

- -

The Fill Color Property -controls the background color for a Box, Oval, or Text annotation and its -functionality is the same as the Line Color property.

- -

 

- -

- -

Figure 11: Line Width and -Line/Fill Color Controls

- -

 

- -

Annotation Line Arrow Tips

- -

 

- -

- -

Figure 12: Line Arrow Tip -Controls

- -

 

- -

The Annotation Line Arrow -Tips section controls the addition of arrows to the end points of a line annotation.  -The top arrow button adds an arrow to the lineÕs start and the bottom arrow -button adds an arrow to the lineÕs end.

- -

Annotation Text Characters

- -

 

- -

The Text section allows the -user to edit the text of the selected Text Annotation (when one and only one -annotation is selected).  Multiple lines of text are separated by a Ò\nÓ -character sequence.  The combo box in the second row allows the user to -connect a text annotation in Surface Space to the surface vertex with a line or -an arrow.  When connecting a text annotation to the surface, the offset in -the surface coordinate often needs adjustment.  In addition, one may -double-click the text annotation to edit the text in a multi-line editor.

- -

 

- -

- -

Figure 13: Text Characters and -Offset (Surface Space only) Controls

- -

 

- -

Annotation Text Font Property

- -

 

- -

Font Properties associated -with Text Annotations include the Font Name, the Font Size, the Text Color and -options for Bold, Italic, and Underline.  When multiple annotations are -selected, the first matching font and smallest font size is selected.  The -Bold, Italic, and Underline buttons are selected only if all selected text -annotations have the attribute enabled.

- -

 

- -

- -

Figure 14: Text Font Attributes

- -

 

- -

Annotation Text Alignment Property

- -

 

- -

Text Alignment Properties provide -options for both horizontal (top row) and vertical alignment (bottom row) of -text.  Horizontal options allow left-aligned text, center-aligned text, or -right-aligned text.  Vertial options allow for top-aligned text, -middle-aligned text, or bottom-aligned text.

- -

 

- -

When there are multiple text -annotations selected with different alignments, none of the alignment buttons appear -in selected state.

- -

 

- -

- -

Figure 15: Text Alignment -Controls

- -

 

- -

Annotation Text Orientation

- -

 

- -

The text orientation property -includes two selections, Horizontal and Stacked.   Text characters -drawn in Horizontal Orientation are drawn in a left-to-right sequence.  -Text characters drawn in Stacked Orientation are drawn in a top-to-bottom -sequence.  If there are multiple text annotations selected with differing -orientations, neither of the orientation buttons is shown as selected.

- -

 

- -

- -

Figure 16: Text Orientation -Controls

- -

 

- -

 

- -

Annotation Coordinate Property

- -

 

- -

With the exception of Surface -Space, the coordinates for annotations are specified with XYZ-coordinates.  If -the annotation is in Surface Space, selections are provided for the Structure, -Vertex Index, Offset Mode (C or N), and Offset Distance.  The Centroid (C) -Offset Mode uses a vector from the centroid of the surface through the -vertex.  The Normal (N) Mode offsets using the vertexÕs normal vector.  Line -annotations contain coordinates for each endpoint and all other annotations use -a one coordinate.

- -

 

- -

- -

Figure 17: Coordinate Controls -(Chart, Stereotaxic, Tab, Window)

- -

Figure 18: Annotation -Coordinate (Chart, Stereotaxic, Tab, or Window)

- -

 

- -

- -

Figure 19: Surface Coordinate -Controls

- -

 

- -

Located on the left side of -the coordinate controls are alphanumeric characters that display an abbreviated -name for the coordinate space of the selected annotation(s).  If all of -the selected annotations are in the same coordinate space, the letter will be Ch -(Chart Data), Sf (Surface), St (Stereotaxic) T (Tab, followed by tab numbers), -and W (Window).  If multiple annotations are selected and they are in -different coordinate spaces, a plus symbol (+) is displayed.  The -coordinate controls are enabled only when a single annotation is selected.

- -

Annotation Width and Height

- -

 

- -

The Annotation Width and Height -Properties control the width and height of Box, Image, and Oval -Annotations.   Both the width and height are normalized values that -range from 0% to 100% where 100% is the full width/height of the tab or window.  -For image annotations, adjusting the width (or height) will also change the -height (or width) of the image so that the imageÕs aspect ratio is preserved to -prevent distortion of the image.

- -

 

- -

- -

Figure 20: Width and Height -Controls

- -

 

- -

Annotation Rotation Property

- -

 

- -

The Annotation Rotation -property controls the rotation annotations.  The rotation value is in -degrees and a positive value rotates clockwise.

- -

 

- -

- -

 

- -

 

- -

 

- -

Annotation Files

- -

 

- -

There two mechanisms for -saving annotations.

- -

Disk Annotation File

- -

 

- -

Disk Annotation Files can be -opened, saved, added to a Spec File just like other Workbench data files.

- -

 

- -

Scene Annotation File

- -

 

- -

The Scene Annotation File is -maintained Òin memoryÓ and when the user creates a Scene, these annotations -become part of the scene.  Thus, these annotations can only be displayed -when the scene is loaded.  A special entry is provided on the Manage/Save -Files Window to remove all active Scene Annotations.

- -

 

- -

Selecting Annotations

- -

 

- -

To select a single annotation -while in Annotations mode, simply move the mouse over the annotation (the -cursor will become a Òfour arrowsÓ symbol) and click the mouse.

- -

 

- -

To select multiple -annotations, hold down the Shift Key while clicking the mouse over an -annotation.  When the mouse is clicked with the Shift Key down, any other -selected annotations remain selected. 

- -


-If an annotation that is a member of a group is selected, all annotations in -the group become selected. 

- -

 

- -

To deselect all annotations, -click the mouse in a region where there is no annotation.  If there are -multiple annotations selected and one wants to deselect an annotation, click -the mouse over the annotation while holding down the Shift Key.

- -

 

- -

When an annotation is in the -selected state, sizing handles are displayed around the annotation.  For a -Line Annotation, sizing handle symbols are drawn at the end points with a -slightly larger square symbol at the first coordinate (head of the -arrow).  For a Box or Oval Annotation, a box is drawn in the foreground -color with sizing handles distributed about the box.  When the mouse is -moved over a sizing handle, the mouse cursor will change to a symbol that -indicates how moving the size handle with the mouse will change the shape of -the annotation.

- -

 

- -

Editing Annotation Position, Size, and Rotation

- -

 

- -

To move an annotation, first -select the annotation and move the mouse over the annotation so that the cursor -is the four arrows symbol.  Hold down the left mouse button and then drag -the mouse to move the annotation to its new location.

- -

 

- -

To resize a line annotation, -first select the line annotation.  Next, move the mouse over a sizing -handle at either end of the line so that the cursor becomes a Òtwo arrowÓ -symbol.  Now, hold down the left mouse button and drag the line end point -to its new location.

- -

 

- -

To resize a box or oval -annotation, first select the annotation.  Next, move the mouse over one of -the sizing symbols so that the cursor becomes a Òtwo arrowÓ symbol.  Now, -hold down the left mouse button and drag to change the width and/or height of -the shape.  Dragging the circular symbol at the corners changes both the -width and height of the shape.  Dragging the square symbol on a side -changes width or height but not both.

- -

 

- -

- -

Figure 22: Box in Red that is -Selected for Editing

- -

 

- -

To rotate a box, image, oval, -or text annotation, first select the annotation.  Next, move the mouse -over the small rotation handle at the top of the shape (small black circle -attached to box around TEXT) at which time the cursor will become a rotation -symbol.  Now, to rotate the annotation, hold down the left mouse button -and drag the mouse away from the annotation to the desired orientation. The -rotation handle will always point towards the mouse pointer so it is important -that the mouse is moved away from the annotation.  If the mouse is over -the annotation, rotation will be erratic and difficult.

- -

 

- -

 

- -

 

- -

 

- -

Annotations may also be -moved, resized, and rotated using the annotation toolbarÕs controls while a -shape is selected.

- -

 

- -

Editing (Redo and Undo)

- -

 

- -

 

- -

Figure 23: Editing Redo and -Undo Controls

- -

 

- -

When editing annotations, the -user may mistakenly modify or delete an annotation.  For these instances, -pressing the Undo button will ÒundoÓ the last change to an annotation.  -Redo and Undo can also be used with grouping operations (described later in -this document).

- -

Inserting Annotations

- -

 

- -

- -

Figure 24: Insert New -Annotation Controls

- -

 

- -

 

- -

- -

Figure 25: Annotation File -Selection Controls

- -

 

- -

 

- -

When creating annotations, -new annotations are placed in either ÒScene AnnotationsÓ or an Annotation File -stored to disk.  Scene Annotations are not saved to an annotation file but -instead are added to a scene when a a new scene is created.  Note that the -user will need to save the scene file.  Disk annotation files are saved -and opened like other data files.

- -

 

- -

To create a new annotation, one -must first choose the file that will contain the newly created annotation (the -default is Scene Annotations).  Once the file is selected, it is not -necessary to select a file unless one wants to change the file.  Second, -the user must choose the Space from the top row of buttons (the selected space -is highlighted).  Third, the user must click one of the type buttons in -the bottom row to choose the type of annotation that is created.  Lastly, -the user moves the mouse into the graphics region at which time the mouse -pointer becomes a small ÔplusÕ symbol.  The user must either click the mouse -in the graphics region to insert a default annotation for the type or drag the -mouse to create a rectangular region that bounds the new annotation.  If -the location of the new annotation is incompatible with the selected annotation -space, a dialog pops up that allows the user to choose a different space or to -cancel creation of the annotation. When entering a text annotation, a dialog -will always pop-up for entry of the text.

- -

 

- -

BEWARE that when drawing -annotations in surface space, the annotation may move a short distance from -where it was drawn.  This is caused by the surface offset attribute of the -surface coordinate.  While the annotation is attached to the surface -vertex closest to the mouse click, the annotation is offset by a vector.  -It is this offset vector that causes the annotation to move.  Without the -offset vector, surface annotations may be partially or even fully obscured by -the surface.

- -

Deleting Annotations

- -

 

- -

- -

Figure 26: Delete Annotation -Control

- -

 

- -

Individual annotations are -deleted while in Annotation Mode by clicking the Trash Can Icon in the -Annotation Toolbar.  One may remove an entire file of Annotations on the -Manage/Save Files window.

- -

 

- -

Format

- -

 

- -

- -

Figure 27: Format Menu Items

- -

 

- -

The arrange menu is used to -arrange and format multiple annotations.  At least two annotations must be -selected for an alignment operation.  At least three annotations must be -selected for a distribute operation.  Alignment and Distribution -operations may only be performed on annotations in Tab or Window space.

- -

 

- -

á      -Align Left Ð The selected -annotations are moved so that the left edge of each annotation is at the same -X-coordinate as the left-most annotation.

- -

á      -Align Center Ð The selected -annotations are moved so that center X-coordinate of each annotation is at the -average X-coordinate of the annotations.

- -

á      -Align Right Ð The selected -annotations are moved so that the right edge of each annotation is at the same -X-coordinate as the right-most annotation.

- -

á      -Align Top - The selected -annotations are moved so that the top edge of each annotation is at the same Y-coordinate -as the top-most annotation.

- -

á      -Align Middle Ð The selected -annotations are moved so that the center Y-coordinate of each annotation is at -the average Y-coordinate of the annotations.

- -

á      -Align Bottom - The selected -annotations are moved so that the bottom edge of each annotation is at the same -Y-coordinate as the bottom-most annotation.

- -

á      -Distribute Horizontally Ð The -left-most and right-most annotations do not move.  The other annotations -are moved so there is equal horizontal spacing between adjacent annotations.

- -

á      -Distribute Vertically - The -top-most and bottom-most annotations do not move.  The other annotations -are moved so there is equal vertical spacing between adjacent annotations.

- -

á      -Group - See the description in -Annotation Grouping.

- -

á      -Regroup - See the description in -Annotation Grouping.

- -

á      -Ungroup - See the description in -Annotation Grouping.

- -

 

- -

Context Sensitive (Pop-up) Menu

- -

 

- -

A pop-up menu is available when -the Mode (mouse) is Annotate.  This menu is displayed when the user places -the mouse over an annotation and right-clicks the mouse (Control key held down -with mouse click on Mac).  Items in the menu are enabled only in certain -conditions.

- -

 

- -

Items in the pop-up menu are:

- -

á      -Cut Ð Removes the annotation under -the mouse and places it onto the annotation clipboard.

- -

á      -Copy Ð Copies the annotation under -the mouse and places it onto the annotation clipboard.

- -

á      -Delete Ð Deletes the annotation -under the mouse.

- -

á      -Duplicate to Tab Ð When this item -is enabled, it displays a menu listing the tabs to which the annotation may be -pasted.

- -

á      -Paste Ð Inserts a copy of the -annotation on the clipboard at the mouse location.  If the mouse location -is incompatible with the annotationÕs space, a dialog is pops up to change the -space or cancel the paste operation.

- -

á      -Paste and Change Space Ð Pops up a -dialog to choose the space and then pastes a copy of the annotation from the -clipboard in the selected space.

- -

á      -Select All Ð Selects all -annotations displayed in the window.

- -

á      -Edit Text Ð Pops up a dialog for -changing the text in a text annotation.

- -

á      -Turn Off Stereotaxic/Surface -Annotation Display in Other Tabs Ð The  display of the selected -annotations is turned off in all tabs except the tab in which this menu items -was selected.

- -

á      -Turn On Stereotaxic/Surface -Annotation Display in All Tabs Ð The display of the selected annotations is -turned on in all tabs.

- -

á      -Turn On Stereotaxic/Surface -Annotation Display in All Groups Ð The display of the selected annotations is -turned on in all Display Groups (A, B, C, D).

- -

á      -Turn On Stereotaxic/Surface -Annotation Display in Only in Group Ð Allows selection of a Display Group and -the selected annotations are set to only display in the selected Display Group.

- -

á      -Group Ð See the description in -Annotation Grouping.

- -

á      -Ungroup Ð See the description in -Annotation Grouping.

- -

á      -Regroup Ð See the description in -Annotation Grouping.

- -

 

- -

Edit Menu

- -

 

- -

The WindowÕs Edit Menu is -available for some operations.

- -

 

- -

á      -Undo Ð Rolls back the last -annotation modification (additional text is added to the Undo item providing -information about the modification).

- -

á      -Redo Ð Rolls back the last ÒundoÓ -(additional text is added to the Redo item providing information about the -modification).

- -

á      -Cut Ð See Cut under Context -Sensitive (Pop-up) Menu.

- -

á      -Copy Ð See Copy under Context -Sensitive (Pop-up) Menu.

- -

á      -Paste Ð See Paste under Context -Sensitive (Pop-up) Menu.

- -

á      -Paste Special Ð See Paste and -Change Space under Context Sensitive (Pop-up) Menu.

- -

á      -Delete Ð See Delete under Context -Sensitive (Pop-up) Menu.

- -

á      -Select All Ð Selects all -annotations displayed in the window.

- -
-
- -

 

- -

Display Control of Annotations and Substitutions

- -

 

- -

- -

Figure 28: Features ToolBox -Annotation Controls

- -

 

- -

Controlling the display of -annotation is performed using the Annot section in the Features Toolbox.

- -

 

- -

Options

- -

 

- -

Display Annotations Ð Enables -and disables the display of all annotation types in all open windows.

- -

 

- -

Display Text Annotations Ð -Enables and disables the display of text annotations in all open windows.  -Note that the Display Annotation checkbox affects all annotations including -text annotations.

- -

 

- -

Show Window Annotations in -Single Tab View Ð When checked, annotations in Window space are always -displayed.  When unchecked, annotations in Window space are only displayed -when Tile Tabs is enabled.

- -

 

- -

Group Ð Group allows the user -to easily switch between several different display selections of -annotations. 

- -

 

- -

The display selections for -annotations in window space function different than other spaces.  Because -the ÔGroupÕ selection is a property of each browser window ÒtabÓ, it is not -available when drawing Window annotations.  Therefore, there is only one -display selection for annotations in window space.

- -

 

- -

Selection of Annotations in Features Toolbox

- -

 

- -

To select an annotation in -the Features Toolbox, the user clicks the mouse over the name of the -annotation.  The annotation name is selected and all other annotations are -deselected.  The selected annotation is highlighted in the graphics -region.  The user may select multiple annotations in the features toolbox -by holding down the CTRL (Apple) key or the Shift key.  If the CTRL key is -held down while the mouse is clicked, the annotation under the mouse is added -to the selected annotations.  If the Shift key is held down while the mouse -is clicked, all annotation from the last selected annotation to the annotations -under the mouse are selected.

- -

 

- -

If the user clicks the name -of either group type, Space or User, all annotations in the group are selected. - If the user selects the name of annotation that is in a User Group, all -annotations in the User Group are selected.

- -

 

- -

 

- -

Content of Annotations Tab in Features Toolbox

- -

 

- -

In the Features Toolbox, -annotations are grouped into a hierarchy of File, Group, and Annotations. - The hierarchy is collapsed and expanded using the small triangles on the -left side of the annotation listing.  There are three checkbox states, -unchecked, partially checked, and checked.  An annotation is either -unchecked (not displayed) or checked (is displayed).  An Annotation Group -(and File) is unchecked when all annotations in the group (file) are unchecked -(not displayed); partially checked when some, but not all annotations in the -group are checked (displayed), and the group (file) is checked when all -annotations in the group are checked (displayed).

- -

 

- -

For each annotation in the -features toolbox, an icon (small image) is displayed between the checkbox and -the annotationÕs name.  This icon may consist of several colors.  If -the annotation's ÔFill ColorÕ is enabled, the background of the icon is the -fill color.  If the annotationÕs line color is enabled, the edges of the -icon are drawn in the line color.  If the annotation is a text annotation, -a small square consisting of the text characterÕs color is in the center of icon. -

- -

 

- -

- -

 

- -

Substitutions

- -

 

- -

á      -Enable Substitutions Ð When checked, substitutions are performed -when drawing annotations.  When unchecked, the substitutions identifiers -($A$, etc) are always drawn and may be useful when editing annotations.

- -

á      -On Ð Selects an annotations substitution file for -activation.  Only one file may be active.

- -

á      -Yoke Ð Allows yoking of the Index (row of CSV file) to Map -Overlays in the Overlay Toolbox.

- -

á      -Index Ð Controls the row of the CSV file that is used for -substitutions.

- -

á      -File Ð Name of the annotation substitutions file.

- -

 

- -

Annotation Substitution File.

- -

 

- -

The Annotation Substitutions are contained in a Comma -Separated Value (CSV) File.  Excel can be used to create a CSV file.  -Each column contains substitution values for a particular substitution and is -identified by sequential letters A, B, C, É  Thus, cell B1 is the first -substitution for ÔBÕ, cell B2 is the second substitution for ÔBÕ, and so -on.  In a way, the column identifiers (A, B, C, É) are analogous to -brainordinates and the rows (1, 2, 3, É) are analogous to maps.  When -saving an Annotation Substitution File, use the extension (.wb_annsub.csv).

- -

 

- -

The content of the file is just the substitutions and -nothing else.  There are no headers of any kind.

- -

 

- -

- -

 

- -

 

- -

 

- -

 

- -

Inserting substitutions into Annotations

- -

 

- -

To insert a substitution into a Annotation, simply insert -text containing the column identifier within Ô$Õ characters.  This $B$ -indicates that substitutions are loaded from column B as shown in an Excel -spreadsheet.  An annotation may contain any number of substitutions -identifiers.

- -

 

- -

- -

 

- -

After creation of the annotation containing two -substitutions, $A$ and $B$, the annotation is drawn with the substitution -value.

- -

 

- -

- -

 

- -

 

- -

If the substitution fails when drawing the annotation, the -substitution identifier is displayed.  The substitution identifier is -always displayed if Enable Substitutions is off in the Features Toolbox.

- -

 

- -

- -

 

- -
-
- -

 

- -

Annotation Grouping

- -

 

- -

Annotations are organized -into a hierarchy consisting of File->Group->Annotation.  There are -two types of groups: Space Groups and User Groups.  By default, an -annotation is assigned to a Space Group.  The user may move annotations -from a Space Group to a User Group by performing a ÔGroupÕ operation. - Annotations may also be moved from a User Group to a Space Group by -performing an ÔUngroupÕ operation. 

- -

 

- -

Both types of groups may only -contain annotations that are in the same coordinate space.  Stereotaxic -annotations are assigned to one group.  Surface annotations are also in -one group.  There is a group for each tab and a group for each window.

- -

 

- -

Mouse Selection of Annotations

- -

 

- -

To select an annotation with -the mouse, the user simply clicks the mouse over an annotation.  The -annotation under the mouse is selected and any other annotations are -deselected.  To select multiple annotations the user clicks the annotation -with the mouse while the Shift key is held down.  To deselect all -annotations, the user clicks the mouse in an empty region (not over an -annotation).

- -

 

- -

If the user selects an -annotation with the mouse and the annotation is in a User Group, all -annotations in the User Group are selected.

- -

 

- -

 

- -

Creating a User Group

- -

 

- -

Creation of a user group is -allowed when two or more annotations are selected and all of the selected -annotations are in the same Space Group.  If these conditions are met, the -user may create a User Group by selected Group from the Annotation ToolbarÕs -Format->Arrange Menu.  The user may also right click the mouse and -select Group from the pop-up menu.

- -

 

- -

Removing a User Group

- -

 

- -

To remove a User Group, the -annotations in the User Group must be selected (recall that clicking any -annotation in a User Group selects all annotations in the User Group). - The annotations are returned to a Space Group by selecting -Ungroup from the Annotation ToolbarÕs Format->Arrange Menu.  The -user may also right click the mouse and select Ungroup from the pop-up menu.

- -

 

- -

 

- -

Recreating a User Group

- -

 

- -

To recreate a User Group, the -user must select at least one annotation that is currently in a Space Group but -was previously in a User Group.  If these conditions are met, the user may -recreate the User Group by selected Regroup from the Annotation ToolbarÕs -Format->Arrange Menu.  The user may also right click the mouse and -select Regroup from the pop-up menu.  All annotations that were previously -in the User Group are moved back to the User Group.

- -

 

- -

 

- -

Color Bar Editing

- -

 

- -

Each overlay contains one -color bar that is available when the selected overlay is mapped using a color -palette (typically for floating point data).  To show a color bar, press -the rainbow button under Settings in Overlay ToolBox->Layers.  To Edit -the color bar, press the wrench icon.

- -

 

- -

 

- -

- -

Figure 29: Overlay ToolBox

- -

 

- -

 

- -

- -

Figure 30: Overlay and Map -Settings Color Bar Controls

- -

 

- -

Location and Positioning

- -

 

- -

This section controls the -coordinate space and location within the space (tab or window)  for the -color bar.

- -

 

- -

Show Color Bar in Sets the allowable space for the color bar.  Tab -restricts the color bar to the region in which the tabÕs data is drawn.  Window -allows drawing of the color bar in an area of the window.

- -


-
-

- -

 

- -

Positioning  When Automatic is selected, the color bar is -positioned in the bottom left corner of the tab/window. If there is more than -one color bar, they are vertically stacked.  Manual  allows -the user to manually position the color bar by selecting the color bar (click -it in Annotations Editing Mode) and dragging it with the mouse or using the -coordinate controls in the annotationÕs editing toolbar.  It is not -necessary to switch the Positioning Mode to Manual to move the color bar.  -Simply select the color bar and drag it with the mouse and the Positioning will -immediately change to Manual.

- -

 

- -

Data Numerics

- -

 

- -

Data Mode Controls format of numeric above color bar.  -When Data is selected, the numeric text above the color bar reflects -numerical values within the data.  With Percentile the values above -the color bar are percentages within the range of data.  When Sign Only -is selected, a plus and/or minus indicate the polarity of the data.

- -

 

- -

Numeric Format Controls the formatting of numeric text above the -color bar.  With Auto, the choice of decimal or scientific notation -and digits right of the decimal point is automatically chosen to best represent -the data values.  Decimal format ensures the data is in a decimal -format.  Scientific displays the data using scientific notation.

- -

 

- -

Dec/Sci Decimals Enabled when Numeric Format is Decimal or Scientific -and controls the number of digits right of the decimal point.

- -

 

- -

Subdivisions By default, the numeric text above the color bar -consists of a negative value, zero, and a positive value.  Subdivisions is -used to add additional values between zero and negative/positive.

- -

 

- -

Show Tick Marks If checked, a small tick mark is displayed below each -numeric text value to indicate its exact position in the color bar.

- -

 

- -

 

- -

References:

- -

 

- -

Glasser MF, Coalson TS, Robinson EC, et al. A -multi-modal parcellation of human cerebral cortex. Nature. -2016;536(7615):171-178. doi:10.1038/nature18933.

- -

 

- -
- - - - Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image001.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image001.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image002.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image002.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image003.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image003.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image004.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image004.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image005.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image005.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image006.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image006.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image007.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image007.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image008.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image008.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image009.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image009.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image010.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image010.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image011.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image011.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image012.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image012.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image013.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image013.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image014.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image014.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image015.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image015.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image016.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image016.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image017.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image017.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image018.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image018.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image019.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image019.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image020.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image020.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image021.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image021.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image022.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image022.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image023.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image023.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image024.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image024.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image025.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image025.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image026.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image026.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image027.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image027.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_Guide.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_Guide.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Annotations/Annotations_Guide.html 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Annotations/Annotations_Guide.html 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,2442 @@ + + + + + + + + + + + +
+ +

Guide to WB Annotations

+ +

 

+ +

January 20, 2021

+ +

 

+ +

+ +
+
+ +

 

+ +

Table of Contents

+ +

Annotation Toolbar and Example +Annotations. 1

+ +

Annotation Coordinate Spaces. 1

+ +

Chart Data Space (Ch). 1

+ +

Stereotaxic Coordinate Space (St). 1

+ +

Surface Coordinate Space (Sf). 1

+ +

Tab Coordinate Space (T). 1

+ +

Window Coordinate Space (W). 1

+ +

Types of Annotations. 1

+ +

Box. 1

+ +

Color Bars. 1

+ +

Image. 1

+ +

Line. 1

+ +

Polyline. 1

+ +

Oval. 1

+ +

Text. 1

+ +

Annotation Properties. 1

+ +

Annotation Line Width, Line Color, and +Fill Color. 1

+ +

Annotation Line Arrow Tips. 1

+ +

Annotation Text Characters. 1

+ +

Annotation Text Font Property. 1

+ +

Annotation Text Alignment Property. 1

+ +

Annotation Text Orientation. 1

+ +

Annotation Coordinate Property. 1

+ +

Annotation Width and Height. 1

+ +

Annotation Rotation Property. 1

+ +

Annotation Files. 1

+ +

Disk Annotation File. 1

+ +

Scene Annotation File. 1

+ +

Selecting Annotations. 1

+ +

Editing Annotation Position, Size, and +Rotation. 1

+ +

Editing (Redo and Undo). 1

+ +

Inserting Annotations. 1

+ +

Deleting Annotations. 1

+ +

Format. 1

+ +

Context Sensitive (Pop-up) Menu. 1

+ +

Edit Menu. 1

+ +

Display Control of Annotations. 1

+ +

Options. 1

+ +

Selection of Annotations in Features +Toolbox. 1

+ +

Content of Annotations Tab in Features +Toolbox. 1

+ +

Annotation Grouping. 1

+ +

Mouse Selection of Annotations. 1

+ +

Creating a User Group. 1

+ +

Removing a User Group.. 1

+ +

Recreating a User Group. 1

+ +

Color Bar Editing. 1

+ +

Location and Positioning. 1

+ +

Data Numerics. 1

+ +

 

+ +
+
+ +

 

+ +

+ +

Figure 1: Single Tab View

+ +

 

+ +

 

+ +

+ +

Figure 2: Tile Tabs View

+ +

Understanding the Need for +Locking the Aspect Ratio

+ +

 

+ +

To create an optimal view of +a brain model, the user may adjust the size of the window and optionally remove +the Toolbar, Overlay Toolbox, and the Features Toolbox.  As the window’s shape changes, +the size of displayed brain model is adjusted to best fill the available +space.  For a cerebral cortex model in a default lateral view, the height of +the cortex is set to be slightly smaller than the height of its available area +in the window.  If this causes parts of the cortex to exceed the width of the +available are, the cortex is shrunk so that all of it is visible. 

+ +

 

+ +

Allowing an independent width +and height for the window is known as a variable aspect ratio.  An aspect ratio +is the ratio of height to width (height divided by width).  Due to this +variable aspect ratio, annotations in Tab and Window Coordinate Space use a +special two-dimensional coordinate system consisting of percentages.  In this +percentage coordinate system, the range for X-percentage coordinates range from +0% at the left edge of the graphics region to 100% at the right edge.  +Correspondingly, Y-percentage coordinates range from 0% at the bottom edge to +100% at the top edge.  Thus, if one places an annotation at an X-coordinate of +100%, this annotation will always be at the right edge of the window, regardless +of the window’s width.

+ +

 

+ +

While this percentage +coordinate system works well with a variable aspect ratio, it has a major +shortcoming.  If one places an annotation in Tab or Window Space over an +anatomical feature of brain model and increases the width (or height) of the +window, the annotation may “drift†and no longer hover above the anatomical +feature.  This anomaly occurs because the size of the brain model does not +increase in the same proportion as the size of the window.  To correct this +problem, wb_view provides (and strongly recommends) locking of the aspect +ratio.  When aspect locking is enabled, the aspect ratio at the time of locking +is saved and used when drawing brain models.  If the window is changed to a +size that does not result in the same aspect ratio as the locked aspect ratio, +padding is added to the horizontal or vertical sides of the window to limit the +graphics region in a way that matches the locked aspect ratio.   For example, +suppose one creates two annotations in tab coordinate space, a red oval and a +fuchsia box as in Figure 1.  Now suppose +one increases the width of the window while the aspect ratio is unlocked.  +Notice that the annotations are no longer hover over the original anatomical +regions, Figure 4.  However, if one locks +the aspect ratio and then increases the width of the window, padding is added +to the side of the window to restrict the model to an area with the aspect +ratio from the time of locking, Figure +5.

+ +

 

+ +

 

+ +

+ +

Figure 3: Annotations on +Surface

+ +

 

+ +

+ +

Figure 4: Width Increased with +Unlocked Aspect Ratio

+ +

 

+ +

 

+ +

+ +

Figure 5: Width increased with +a Locked Aspect Ratio

+ +

 

+ +

 

+ +

 

+ +

 

+ +

 

+ +

 

+ +

Scenes

+ +

 

+ +

Scenes allow one to preserve +and reproduce ‘the state’ of wb_view and are frequently used to produce figures +for publications.    Scenes can be shared with collaborators or with the +neuroscience community through the BALSA Database.  Annotations are frequently +featured in scenes and allow one to highlight items of in the data and present +auxiliary information (Figure +7).  Annotations are typically saved within a +scene which eliminates the management of an external data file (although +annotations may be stored in external data files).

+ +

 

+ +

 

+ +

+ +

Figure 6: Figure from A +multimodal parcellation of human cerebral cortex (Glasser et al)

+ +

 

+ +

 

+ +

 

+ +

 

+ +

Annotation Toolbar and Example Annotations

+ +

 

+ +

+ +

Figure 7: Window with Annotation ToolBar

+ +

 
+

+ +

 

+ +

Annotation Coordinate Spaces

+ +

 

+ +

Several coordinate spaces are +available for annotations.  The coordinate spaces control the movement of +placement of annotations.

+ +

 

+ +

Chart Data Space (Ch)

+ +

 

+ +

An annotation in chart data +space is displayed within the char data plot and is invalid outside of the data +plot.  The coordinate range is identical to the X- and Y-axis of the displayed +chart data.  This is the range of data in the chart changes, an annotation in +chart data space will move.

+ +

 

+ +

Stereotaxic Coordinate Space (St)

+ +

 

+ +

An annotation in stereotaxic +space will appear in any display of a surface or a volume.  A particular +annotation in stereotaxic space may appear in more than one location such as +when viewing a surface montage.

+ +

 

+ +

Stereotaxic space (Talairach +is an example of a specific stereotaxic space) is the three-dimensional +coordinate system in which surface and volume models are displayed.  In +Workbench, the stereotaxic space is in an LPI orientation (negative X is Left, +negative Y is Posterior, and negative Z is Inferior).  Annotations in Stereotaxic +Space will follow the model as it is panned, rotated, or zoomed.  At times, +annotations in Stereotaxic Space may not be visible when they are behind a +model.  Annotations in Stereotaxic Space are always drawn in a plane parallel +to the screen.

+ +

 

+ +

Surface Coordinate Space (Sf)

+ +

 

+ +

An annotation in surface +coordinate space will appear in any view of the surface to which the annotation +is attached.  When a surface space is used, the annotation is associated with a +surface by the combination of the surface’s structure (ie: left cerebral +cortex) and a vertex index.  An optional offset vector is used to improve +visibility of the annotation (so not ‘behind’ the surface).

+ +

 

+ +

Tab Coordinate Space (T)

+ +

 

+ +

An annotation in tab coordinate +space is displayed in a specific tab (uses tab number).  When Tile Tabs is +enabled, all of the window’s tabs are displayed in a grid-like pattern.  Tab +Coordinate Space is a two-dimensional space with the X- and Y-coordinates expressed +in percentages ranging from 0% to 100%.  For the X-coordinate, 0% is on the +left and 100% is on the right.  For the Y-coordinate, 0% is at the bottom and +100% is at the top. 

+ +

If Tile Tabs is enabled, the +coordinate range spans only cell allocation to that tab in the graphics +region.  When the tab’s aspect is locked, the locations of 0% and 100% may be +offset from the graphics regions edges so that annotations remain at a consistent +location over an underlying brain model.

+ +

 

+ +

Window Coordinate Space (W)

+ +

 

+ +

The window coordinate functions +nearly identically to the tab coordinate space except that the coordinates +always span the entire graphics region.  Tile Tabs has no effect on window +coordinates. 

+ +

 

+ +

Aspect Locking for Tab and +Window Coordinate Spaces

+ +

 

+ +

The graphics region in +wb_view defaults to a variable aspect ratio.  With a variable aspect ratio, the +user may independently adjust the width and height of the window.  Since the +tab and window coordinate spaces use a percentage coordinate system, +annotations in these spaces will move away from the center as the width or +height is increased.  Conversely, the annotation will move towards the center +as the width or height is decreased.  For example, in Figure 9, a red oval and a fuchsia square annotation +are displayed over a left hemisphere.  In one stretches the width of the window +with an unlocked aspect ratio, the two annotations move away from the center +but remain at the same percentage width coordinate, Figure 10.  In contrast, if one locks the aspect ratio and +expands the width of the window, padding is added to the sides so that the +percentage coordinates remain limited to the original aspect ratio, Figure 11.  The result of aspect locking is that the +annotations remain over the same anatomical features as the window’s size +changes.

+ +

 

+ +

 

+ +

+ +

Figure 8: Surface with +Annotations

+ +

   

+ +

+ +

Figure 9: Window Width +Increased with Unlocked Aspect Ratio

+ +

+ +

Figure 10: Window Width +Increased After Locking Aspect Ratio

+ +

 

+ +

Types of Annotations

+ +

 

+ +

Box

+ +

 

+ +

A box annotation is +positioned with one coordinate located at the center of the box.  The box’s +width and height are ‘normalized’ relative to the size of width and height of the +tab or window containing the box.  Thus, a size of 100% will fill the tab/window +horizontally.  Boxes possess both Fill and Line colors and one or both may be +used.

+ +

 

+ +

Color Bars

+ +

 

+ +

Color bars are a special case +of annotations that pictorially describe the mapping of numeric data into a +color palette.  Color bars are not created like other annotation types but are +enabled for display by pressing the color bar button for a row in the Overlay +ToolBox’s Layers tab.  Positioning of color bars is performed automatically +unless the user chooses to manually position them.  Color bars are limited to +Tab and Window spaces.  Editing of color bars is described at the end of this +document.

+ +

 

+ +

Image

+ +

 

+ +

An image annotation is +positioned with one coordinate located at the center of the image.  The height +of image is specified as a percentage of the region containing the image.  As +the region changes in height, a corresponding change will occur in the height +of the image.  The width of the image will scale with the height of the image +using the aspect ratio of the image when it was associated with the +orientation.

+ +

 

+ +

Line

+ +

 

+ +

A line annotation contains +two coordinates with one located at each end point.  Lines are colored using +the Line Color only.  Arrows may be added to each of the end points.

+ +

 

+ +

Polyline

+ +

 

+ +

A polyline consists of a +connected sequence of line segments.  Polylines are colored using the Line +Color only.

+ +

Oval

+ +

 

+ +

With the exception of the its +shape, an oval’s functionality is identical to a box.

+ +

 

+ +

Text

+ +

 

+ +

Text annotations contain one +coordinate and the text alignment properties control the offset of the text +relative to the coordinate.  Text characters are drawn in the Font Color.  The +Line color, if enabled, draws a box around the text.  The File color, if +enabled, draws a background behind the text.  The height of text is specified +as a percentage of the region’s height containing the text.  As the region +changes in height, a corresponding change will occur in the size of the text.

+ +

 

+ +

Annotation Properties

+ +

 

+ +

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Property

+
+

Box

+
+

Color Bar

+
+

Image

+
+

Line

+
+

Poly-

+

line

+
+

Oval

+
+

Text

+
+

Arrow Tips

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

 

+
+

 

+
+

 

+
+

Line Width

+
+

Y

+
+

 

+
+

 

+
+

Y

+
+

Y

+
+

Y

+
+

Y

+
+

Line Color

+
+

Y

+
+

 

+
+

 

+
+

Y

+
+

Y

+
+

Y

+
+

Y

+
+

Fill Color

+
+

Y

+
+

Y

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Y

+
+

Text Color

+
+

 

+
+

Y

+
+

 

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Text Characters

+
+

 

+
+

Y

+
+

 

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Font Name

+
+

 

+
+

Y

+
+

 

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Font Size

+
+

 

+
+

Y

+
+

 

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Font Style

+
+

 

+
+

 

+
+

 

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Horizontal Alignment

+
+

 

+
+

 

+
+

 

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Vertical Alignment

+
+

 

+
+

 

+
+

 

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Orientation

+
+

 

+
+

 

+
+

 

+
+

 

+
+

 

+
+

 

+
+

Y

+
+

Width

+
+

Y

+
+

Y

+
+

Y

+
+

 

+
+

 

+
+

Y

+
+

 

+
+

Height

+
+

Y

+
+

Y

+
+

Y

+
+

 

+
+

 

+
+

Y

+
+

 

+
+

Rotation

+
+

Y

+
+

Y

+
+

Y

+
+

Y

+
+

 

+
+

Y

+
+

Y

+
+ +

 

+ +

 

+ +

Annotation Line Width, Line Color, and Fill Color

+ +

 

+ +

The Line Width Property controls +the line width of Line and Polyline annotations and the outline width of Box, Oval, +and Text annotations.  Line widths are expressed as a percentage of the height +of the region containing the annotation.  When multiple annotations are +selected with different line widths, the smallest selected line width is +displayed followed by a plus symbol (+).

+ +

 

+ +

The Line Color Property +controls the outline color of a Box, Oval or Text.  To change the color, one +presses the small arrow to display a menu containing the available colors.  The +two special selections are Custom and None.  Choosing Custom displays a dialog +that allow the user to create a color by adjusting red, green, and blue color +components.  None inhibits the drawing of the associated line.

+ +

 

+ +

The Fill Color Property +controls the background color for a Box, Oval, or Text annotation and its +functionality is the same as the Line Color property.

+ +

 

+ +

+ +

Figure 11: Line Width and Line/Fill Color Controls

+ +

 

+ +

Annotation Line Arrow Tips

+ +

 

+ +

+ +

Figure 12: Line Arrow Tip Controls

+ +

 

+ +

The Annotation Line Arrow +Tips section controls the addition of arrows to the end points of a line +annotation.  The top arrow button adds an arrow to the line’s start and the +bottom arrow button adds an arrow to the line’s end.

+ +

Annotation Text Characters

+ +

 

+ +

The Text section allows the +user to edit the text of the selected Text Annotation (when one and only one +annotation is selected).  Multiple lines of text are separated by a “\n†+character sequence.  The combo box in the second row allows the user to connect +a text annotation in Surface Space to the surface vertex with a line or an +arrow.  When connecting a text annotation to the surface, the offset in the +surface coordinate often needs adjustment.  In addition, one may double-click +the text annotation to edit the text in a multi-line editor.

+ +

 

+ +

+ +

Figure 13: Text Characters and Offset (Surface Space only) +Controls

+ +

 

+ +

Annotation Text Font Property

+ +

 

+ +

Font Properties associated +with Text Annotations include the Font Name, the Font Size, the Text Color and +options for Bold, Italic, and Underline.  When multiple annotations are +selected, the first matching font and smallest font size is selected.  The Bold, +Italic, and Underline buttons are selected only if all selected text +annotations have the attribute enabled.

+ +

 

+ +

+ +

Figure 14: Text Font Attributes

+ +

 

+ +

Annotation Text Alignment Property

+ +

 

+ +

Text Alignment Properties provide +options for both horizontal (top row) and vertical alignment (bottom row) of +text.  Horizontal options allow left-aligned text, center-aligned text, or +right-aligned text.  Vertial options allow for top-aligned text, middle-aligned +text, or bottom-aligned text.

+ +

 

+ +

When there are multiple text +annotations selected with different alignments, none of the alignment buttons appear +in selected state.

+ +

 

+ +

+ +

Figure 15: Text Alignment Controls

+ +

 

+ +

Annotation Text Orientation

+ +

 

+ +

The text orientation property +includes two selections, Horizontal and Stacked.   Text characters drawn in +Horizontal Orientation are drawn in a left-to-right sequence.  Text characters +drawn in Stacked Orientation are drawn in a top-to-bottom sequence.  If there +are multiple text annotations selected with differing orientations, neither of +the orientation buttons is shown as selected.

+ +

 

+ +

+ +

Figure 16: Text Orientation Controls

+ +

 

+ +

 

+ +

Annotation Coordinate Property

+ +

 

+ +

With the exception of Surface +Space, the coordinates for annotations are specified with XYZ-coordinates.  If +the annotation is in Surface Space, selections are provided for the Structure, +Vertex Index, Offset Mode (C or N), and Offset Distance.  The Centroid (C) +Offset Mode uses a vector from the centroid of the surface through the vertex.  +The Normal (N) Mode offsets using the vertex’s normal vector.  Line annotations +contain coordinates for each endpoint and all other annotations use a one +coordinate.

+ +

 

+ +

+ +

Figure 17: Coordinate Controls (Chart, Stereotaxic, Tab, +Window)

+ +

 

+ +

+ +

Figure 19: Surface Coordinate Controls

+ +

 

+ +

Located on the left side of +the coordinate controls are alphanumeric characters that display an abbreviated +name for the coordinate space of the selected annotation(s).  If all of the +selected annotations are in the same coordinate space, the letter will be Ch +(Chart Data), Sf (Surface), St (Stereotaxic) T (Tab, followed by tab numbers), +and W (Window).  If multiple annotations are selected and they are in different +coordinate spaces, a plus symbol (+) is displayed.  The coordinate controls are +enabled only when a single annotation is selected.

+ +

Annotation Width and Height

+ +

 

+ +

The Annotation Width and Height +Properties control the width and height of Box, Image, and Oval Annotations.   Both +the width and height are normalized values that range from 0% to 100% where 100% +is the full width/height of the tab or window.  For image annotations, +adjusting the width (or height) will also change the height (or width) of the +image so that the image’s aspect ratio is preserved to prevent distortion of +the image.

+ +

 

+ +

+ +

Figure 20: Width and Height Controls

+ +

 

+ +

Annotation Rotation Property

+ +

 

+ +

The Annotation Rotation +property controls the rotation annotations.  The rotation value is in degrees +and a positive value rotates clockwise.

+ +

 

+ +height=35 src="Annotations_1.4.2.fld/image018.png" align=left hspace=12>

+ +

 

+ +

 

+ +

 

+ +

Annotation Files

+ +

 

+ +

There two mechanisms for +saving annotations.

+ +

Disk Annotation File

+ +

 

+ +

Disk Annotation Files can be +opened, saved, added to a Spec File just like other Workbench data files.

+ +

 

+ +

Scene Annotation File

+ +

 

+ +

The Scene Annotation File is +maintained “in memory†and when the user creates a Scene, these annotations +become part of the scene.  Thus, these annotations can only be displayed when +the scene is loaded.  A special entry is provided on the Manage/Save Files +Window to remove all active Scene Annotations.

+ +

 

+ +

Selecting Annotations

+ +

 

+ +

To select a single annotation +while in Annotations mode, simply move the mouse over the annotation (the +cursor will become a “four arrows†symbol) and click the mouse.

+ +

 

+ +

To select multiple +annotations, hold down the Shift Key while clicking the mouse over an +annotation.  When the mouse is clicked with the Shift Key down, any other +selected annotations remain selected. 

+ +


+If an annotation that is a member of a group is selected, all annotations in +the group become selected. 

+ +

 

+ +

To deselect all annotations, +click the mouse in a region where there is no annotation.  If there are +multiple annotations selected and one wants to deselect an annotation, click +the mouse over the annotation while holding down the Shift Key.

+ +

 

+ +

When an annotation is in the +selected state, sizing handles are displayed around the annotation.  For a Line +Annotation, sizing handle symbols are drawn at the end points with a slightly +larger square symbol at the first coordinate (head of the arrow).  For a Box or +Oval Annotation, a box is drawn in the foreground color with sizing handles +distributed about the box.  When the mouse is moved over a sizing handle, the +mouse cursor will change to a symbol that indicates how moving the size handle +with the mouse will change the shape of the annotation.

+ +

 

+ +

Editing Annotation Position, Size, and Rotation

+ +

 

+ +

To move an annotation, first +select the annotation and move the mouse over the annotation so that the cursor +is the four arrows symbol.  Hold down the left mouse button and then drag the +mouse to move the annotation to its new location.

+ +

 

+ +

To resize a line annotation, +first select the line annotation.  Next, move the mouse over a sizing handle at +either end of the line so that the cursor becomes a “two arrow†symbol.  Now, +hold down the left mouse button and drag the line end point to its new +location.

+ +

 

+ +

To resize a box or oval +annotation, first select the annotation.  Next, move the mouse over one of the +sizing symbols so that the cursor becomes a “two arrow†symbol.  Now, hold down +the left mouse button and drag to change the width and/or height of the shape.  +Dragging the circular symbol at the corners changes both the width and height +of the shape.  Dragging the square symbol on a side changes width or height but +not both.

+ +

 

+ +

+ +

Figure 22: Box in Red that is Selected for Editing

+ +

 

+ +

To rotate a box, image, oval, +or text annotation, first select the annotation.  Next, move the mouse over the +small rotation handle at the top of the shape (small black circle attached to +box around TEXT) at which time the cursor will become a rotation symbol.  Now, to +rotate the annotation, hold down the left mouse button and drag the mouse away +from the annotation to the desired orientation. The rotation handle will always +point towards the mouse pointer so it is important that the mouse is moved away +from the annotation.  If the mouse is over the annotation, rotation will be +erratic and difficult.

+ +

 

+ +

 

+ +

 

+ +

 

+ +

Annotations may also be +moved, resized, and rotated using the annotation toolbar’s controls while a +shape is selected.

+ +

 

+ +

Editing (Redo and Undo)

+ +

 

+ +

 

+ +

Figure 23: Editing Redo and Undo Controls

+ +

 

+ +

When editing annotations, the +user may mistakenly modify or delete an annotation.  For these instances, +pressing the Undo button will “undo†the last change to an annotation.  Redo +and Undo can also be used with grouping operations (described later in this +document).

+ +

Inserting Annotations

+ +

 

+ +

+ +

Figure 24: Insert New Annotation Controls

+ +

 

+ +

 

+ +

+ +

Figure 25: Annotation File Selection Controls

+ +

 

+ +

 

+ +

When creating annotations, +new annotations are placed in either “Scene Annotations†or an Annotation File +stored to disk.  Scene Annotations are not saved to an annotation file but +instead are added to a scene when a a new scene is created.  Note that the user +will need to save the scene file.  Disk annotation files are saved and opened +like other data files.

+ +

 

+ +

To create a new annotation, one +must first choose the file that will contain the newly created annotation (the default +is Scene Annotations).  Once the file is selected, it is not necessary to +select a file unless one wants to change the file.  Second, the user must +choose the Space from the top row of buttons (the selected space is +highlighted).  Third, the user must click one of the type buttons in the bottom +row to choose the type of annotation that is created.  Lastly, the user moves +the mouse into the graphics region at which time the mouse pointer becomes a +small ‘plus’ symbol.  The user must either click the mouse in the graphics +region to insert a default annotation for the type or drag the mouse to create +a rectangular region that bounds the new annotation.  If the location of the +new annotation is incompatible with the selected annotation space, a dialog pops +up that allows the user to choose a different space or to cancel creation of +the annotation. When entering a text annotation, a dialog will always pop-up +for entry of the text.

+ +

 

+ +

BEWARE that when drawing +annotations in surface space, the annotation may move a short distance from +where it was drawn.  This is caused by the surface offset attribute of the +surface coordinate.  While the annotation is attached to the surface vertex +closest to the mouse click, the annotation is offset by a vector.  It is this offset +vector that causes the annotation to move.  Without the offset vector, surface +annotations may be partially or even fully obscured by the surface.

+ +

Deleting Annotations

+ +

 

+ +

+ +

Figure 26: Delete Annotation Control

+ +

 

+ +

Individual annotations are +deleted while in Annotation Mode by clicking the Trash Can Icon in the +Annotation Toolbar.  One may remove an entire file of Annotations on the +Manage/Save Files window.

+ +

 

+ +

Format

+ +

 

+ +

+ +

Figure 27: Format Menu Items

+ +

 

+ +

The arrange menu is used to +arrange and format multiple annotations.  At least two annotations must be +selected for an alignment operation.  At least three annotations must be +selected for a distribute operation.  Alignment and Distribution operations may +only be performed on annotations in Tab or Window space.

+ +

 

+ +

·      +Align Left – The selected +annotations are moved so that the left edge of each annotation is at the same +X-coordinate as the left-most annotation.

+ +

·      +Align Center – The selected +annotations are moved so that center X-coordinate of each annotation is at the +average X-coordinate of the annotations.

+ +

·      +Align Right – The selected +annotations are moved so that the right edge of each annotation is at the same +X-coordinate as the right-most annotation.

+ +

·      +Align Top - The selected +annotations are moved so that the top edge of each annotation is at the same Y-coordinate +as the top-most annotation.

+ +

·      +Align Middle – The selected +annotations are moved so that the center Y-coordinate of each annotation is at +the average Y-coordinate of the annotations.

+ +

·      +Align Bottom - The selected +annotations are moved so that the bottom edge of each annotation is at the same +Y-coordinate as the bottom-most annotation.

+ +

·      +Distribute Horizontally – The +left-most and right-most annotations do not move.  The other annotations are +moved so there is equal horizontal spacing between adjacent annotations.

+ +

·      +Distribute Vertically - The +top-most and bottom-most annotations do not move.  The other annotations are +moved so there is equal vertical spacing between adjacent annotations.

+ +

·      +Bring to Front – For overlapping +annotations (must be in same space and in Chart, Tab or Window space).  Moves +the selected annotation so that it is in the top layer of any overlapping +annotations.

+ +

·      +Bring Forward – Moves the selected +annotation up one layer in the stack of overlapping annotations.

+ +

·      +Send to Back – Moves the selected +annotation to the bottom layer of any overlapping annotations.

+ +

·      +Send Backward – Moves the object +down one layer in the stack of overlapping annotations.

+ +

·      +Group - See the description in +Annotation Grouping.

+ +

·      +Regroup - See the description in +Annotation Grouping.

+ +

·      +Ungroup - See the description in +Annotation Grouping.

+ +

 

+ +

Context Sensitive (Pop-up) Menu

+ +

 

+ +

A pop-up menu is available when +the Mode (mouse) is Annotate.  This menu is displayed when the user places the +mouse over an annotation and right-clicks the mouse (Control key held down with +mouse click on Mac).  Items in the menu are enabled only in certain conditions.

+ +

 

+ +

Items in the pop-up menu are:

+ +

·      +Cut – Removes the annotation under +the mouse and places it onto the annotation clipboard.

+ +

·      +Copy – Copies the annotation under +the mouse and places it onto the annotation clipboard.

+ +

·      +Delete – Deletes the annotation +under the mouse.

+ +

·      +Duplicate to Tab – When this item +is enabled, it displays a menu listing the tabs to which the annotation may be +pasted.

+ +

·      +Paste – Inserts a copy of the +annotation on the clipboard at the mouse location.  If the mouse location is +incompatible with the annotation’s space, a dialog is pops up to change the +space or cancel the paste operation.

+ +

·      +Paste and Change Space – Pops up a +dialog to choose the space and then pastes a copy of the annotation from the +clipboard in the selected space.

+ +

·      +Select All – Selects all +annotations displayed in the window.

+ +

·      +Edit Text – Pops up a dialog for +changing the text in a text annotation.

+ +

·      +Turn Off Stereotaxic/Surface +Annotation Display in Other Tabs – The  display of the selected annotations is +turned off in all tabs except the tab in which this menu items was selected.

+ +

·      +Turn On Stereotaxic/Surface +Annotation Display in All Tabs – The display of the selected annotations is +turned on in all tabs.

+ +

·      +Turn On Stereotaxic/Surface +Annotation Display in All Groups – The display of the selected annotations is +turned on in all Display Groups (A, B, C, D).

+ +

·      +Turn On Stereotaxic/Surface +Annotation Display in Only in Group – Allows selection of a Display Group and +the selected annotations are set to only display in the selected Display Group.

+ +

·      +Bring to Front – See the +description in Annotation Grouping above.

+ +

·      +Bring Forward – See the +description in Annotation Grouping above.

+ +

·      +Send to Back – See the description +in Annotation Grouping above.

+ +

·      +Send Backward – See the +description in Annotation Grouping above.

+ +

·      +Group – See the description in +Annotation Grouping.

+ +

·      +Ungroup – See the description in +Annotation Grouping.

+ +

·      +Regroup – See the description in +Annotation Grouping.

+ +

 

+ +

Edit Menu

+ +

 

+ +

The Window’s Edit Menu is +available for some operations.

+ +

 

+ +

·      +Undo – Rolls back the last +annotation modification (additional text is added to the Undo item providing +information about the modification).

+ +

·      +Redo – Rolls back the last “undo†+(additional text is added to the Redo item providing information about the +modification).

+ +

·      +Cut – See Cut under Context +Sensitive (Pop-up) Menu.

+ +

·      +Copy – See Copy under Context +Sensitive (Pop-up) Menu.

+ +

·      +Paste – See Paste under Context +Sensitive (Pop-up) Menu.

+ +

·      +Paste Special – See Paste and +Change Space under Context Sensitive (Pop-up) Menu.

+ +

·      +Delete – See Delete under Context +Sensitive (Pop-up) Menu.

+ +

·      +Select All – Selects all +annotations displayed in the window.

+ +
+
+ +

 

+ +

Display Control of Annotations

+ +

 

+ +

+ +

Figure 28: Features ToolBox Annotation Controls

+ +

 

+ +

Controlling the display of +annotation is performed using the Annot section in the Features Toolbox.

+ +

 

+ +

Options

+ +

 

+ +

Display Annotations – Enables +and disables the display of all annotation types in all open windows.

+ +

 

+ +

Display Text Annotations – +Enables and disables the display of text annotations in all open windows.  Note +that the Display Annotation checkbox affects all annotations including text +annotations.

+ +

 

+ +

Show Window Annotations in +Single Tab View – When checked, annotations in Window space are always +displayed.  When unchecked, annotations in Window space are only displayed when +Tile Tabs is enabled.

+ +

 

+ +

Group – Group allows the user +to easily switch between several different display selections of annotations. 

+ +

 

+ +

Selected Items: On, Off – +Turns On (or Off) any annotations that are currently selected.

+ +

 

+ +

The display selections for +annotations in window space function different than other spaces.  Because the +‘Group’ selection is a property of each browser window “tabâ€, it is not +available when drawing Window annotations.  Therefore, there is only one +display selection for annotations in window space.

+ +

 

+ +

Selection of Annotations in Features Toolbox

+ +

 

+ +

To select an annotation in +the Features Toolbox, the user clicks the mouse over the name of the +annotation.  The annotation name is selected and all other annotations are +deselected.  The selected annotation is highlighted in the graphics +region.  The user may select multiple annotations in the features toolbox +by holding down the CTRL (Apple) key or the Shift key.  If the CTRL key is +held down while the mouse is clicked, the annotation under the mouse is added +to the selected annotations.  If the Shift key is held down while the +mouse is clicked, all annotation from the last selected annotation to the +annotations under the mouse are selected.

+ +

 

+ +

If the user clicks the name +of either group type, Space or User, all annotations in the group are selected. + If the user selects the name of annotation that is in a User Group, all +annotations in the User Group are selected.

+ +

 

+ +

 

+ +

Content of Annotations Tab in Features Toolbox

+ +

 

+ +

In the Features Toolbox, +annotations are grouped into a hierarchy of File, Group, and Annotations. + The hierarchy is collapsed and expanded using the small triangles on the +left side of the annotation listing.  There are three checkbox states, +unchecked, partially checked, and checked.  An annotation is either +unchecked (not displayed) or checked (is displayed).  An Annotation Group +(and File) is unchecked when all annotations in the group (file) are unchecked +(not displayed); partially checked when some, but not all annotations in the +group are checked (displayed), and the group (file) is checked when all +annotations in the group are checked (displayed).

+ +

 

+ +

For each annotation in the +features toolbox, an icon (small image) is displayed between the checkbox and +the annotation’s name.  This icon may consist of several colors.  If +the annotation's ‘Fill Color’ is enabled, the background of the icon is the +fill color.  If the annotation’s line color is enabled, the edges of the +icon are drawn in the line color.  If the annotation is a text annotation, +a small square consisting of the text character’s color is in the center of +icon.

+ +

 

+ +

Annotation Grouping

+ +

 

+ +

Annotations are organized +into a hierarchy consisting of File->Group->Annotation.  There are +two types of groups: Space Groups and User Groups.  By default, an +annotation is assigned to a Space Group.  The user may move annotations +from a Space Group to a User Group by performing a ‘Group’ operation. + Annotations may also be moved from a User Group to a Space Group by +performing an ‘Ungroup’ operation. 

+ +

 

+ +

Both types of groups may only +contain annotations that are in the same coordinate space.  Stereotaxic +annotations are assigned to one group.  Surface annotations are also in +one group.  There is a group for each tab and a group for each window.

+ +

 

+ +

Mouse Selection of Annotations

+ +

 

+ +

To select an annotation with +the mouse, the user simply clicks the mouse over an annotation.  The +annotation under the mouse is selected and any other annotations are deselected. + To select multiple annotations the user clicks the annotation with the +mouse while the Shift key is held down.  To deselect all annotations, the +user clicks the mouse in an empty region (not over an annotation).

+ +

 

+ +

If the user selects an +annotation with the mouse and the annotation is in a User Group, all +annotations in the User Group are selected.

+ +

 

+ +

 

+ +

Creating a User Group

+ +

 

+ +

Creation of a user group is +allowed when two or more annotations are selected and all of the selected +annotations are in the same Space Group.  If these conditions are met, the +user may create a User Group by selected Group from the Annotation Toolbar’s +Format->Arrange Menu.  The user may also right click the mouse and +select Group from the pop-up menu.

+ +

 

+ +

Removing a User Group

+ +

 

+ +

To remove a User Group, the +annotations in the User Group must be selected (recall that clicking any +annotation in a User Group selects all annotations in the User Group). + The annotations are returned to a Space Group by selecting +Ungroup from the Annotation Toolbar’s Format->Arrange Menu.  The +user may also right click the mouse and select Ungroup from the pop-up menu.

+ +

 

+ +

 

+ +

Recreating a User Group

+ +

 

+ +

To recreate a User Group, the +user must select at least one annotation that is currently in a Space Group but +was previously in a User Group.  If these conditions are met, the user may +recreate the User Group by selected Regroup from the Annotation Toolbar’s +Format->Arrange Menu.  The user may also right click the mouse and +select Regroup from the pop-up menu.  All annotations that were previously +in the User Group are moved back to the User Group.

+ +

 

+ +

 

+ +

Color Bar Editing

+ +

 

+ +

Each overlay contains one +color bar that is available when the selected overlay is mapped using a color +palette (typically for floating point data).  To show a color bar, press the +rainbow button under Settings in Overlay ToolBox->Layers.  To Edit the color +bar, press the wrench icon.

+ +

 

+ +

 

+ +

+ +

Figure 29: Overlay ToolBox

+ +

 

+ +

 

+ +

+ +

Figure 30: Overlay and Map Settings Color Bar Controls

+ +

 

+ +

Location and Positioning

+ +

 

+ +

This section controls the +coordinate space and location within the space (tab or window)  for the color +bar.

+ +

 

+ +

Show Color Bar in Sets the allowable space for the color bar.  Tab +restricts the color bar to the region in which the tab’s data is drawn.  Window +allows drawing of the color bar in an area of the window.

+ +


+
+

+ +

 

+ +

Positioning  When Automatic is selected, the color bar is +positioned in the bottom left corner of the tab/window. If there is more than +one color bar, they are vertically stacked.  Manual  allows the user to +manually position the color bar by selecting the color bar (click it in +Annotations Editing Mode) and dragging it with the mouse or using the +coordinate controls in the annotation’s editing toolbar.  It is not necessary +to switch the Positioning Mode to Manual to move the color bar.  Simply select +the color bar and drag it with the mouse and the Positioning will immediately +change to Manual.

+ +

 

+ +

Data Numerics

+ +

 

+ +

Data Mode Controls format of numeric above color bar.  When Data +is selected, the numeric text above the color bar reflects numerical values +within the data.  With Percentile the values above the color bar are +percentages within the range of data.  When Sign Only is selected, a +plus and/or minus indicate the polarity of the data.

+ +

 

+ +

Numeric Format Controls the formatting of numeric text above the +color bar.  With Auto, the choice of decimal or scientific notation and +digits right of the decimal point is automatically chosen to best represent the +data values.  Decimal format ensures the data is in a decimal format.  Scientific +displays the data using scientific notation.

+ +

 

+ +

Dec/Sci Decimals Enabled when Numeric Format is Decimal or Scientific +and controls the number of digits right of the decimal point.

+ +

 

+ +

Subdivisions By default, the numeric text above the color bar +consists of a negative value, zero, and a positive value.  Subdivisions is used +to add additional values between zero and negative/positive.

+ +

 

+ +

Show Tick Marks If checked, a small tick mark is displayed below each +numeric text value to indicate its exact position in the color bar.

+ +

 

+ +

 

+ +

References:

+ +

 

+ +

Glasser MF, Coalson TS, Robinson EC, et al. A +multi-modal parcellation of human cerebral cortex. Nature. 2016;536(7615):171-178. doi:10.1038/nature18933.

+ +

 

+ +
+ + + + diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/Charting_Overview.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/Charting_Overview.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/Charting_Overview.html 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/Charting_Overview.html 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,87 @@ + + + + + Charting Overview + + +
+

Charting Overview

+
+ Significant upgrades to charting are included in the release of Workbench 1.5.0. + These upgrades include the new Lines Chart Type and customizable axes. +
+
+

Histogram Charts

+
+ Histogram charts are available for any data file that contain one or + more maps of scalar data and this includes many CIFTI file types, + GIFTI metric files, and volume files.  A histogram shows the + distribution of data within a single map with an option to average + the data from all maps.  This map contain the same data that is + mapped to brainordinates in surface and volume views.  To view + a histogram chart, change Toolbar->Display mode to Chart and select + Histogram in Toolbar->Chart Type.  In Toolbox->Chart + Layers, enable an overlay using an On checkbox and then choose a + file and map for display as a histogram.  Multiple histograms + are displayed using multiple layers.  When displaying more + than one histogram, one may want to change the histogram chart from + Bars to Envelope by clicking the Layer's wrench button, selecting + the Palette tab, and selecting the appropriate check boxes in the + Histogram section.  There are a variety of additional options + for controlling the display of histograms.
+
+
+
+
+
+

Dyn (Dynamic) Lines Charts

+
+ A dynami lines chart shows one + data element from each map for a selected brainordinate (identified surface + vertex or volume voxel).  To display dynamic lines charts from a + data-file, one must enable the layer by checking both the On checkbox and + the Load checkbox.  Note that the Load checkbox is a property of + the file so if the same file is in more than one layer, the Load + checkbox will maintain the same checked status in these + layers.  Once loading is enabled for a file, one must identify + brainordinates by clicking the mouse on a surface vertex or a volume + voxel.  Doing so will load the data for the selected + brainordinate and display the data as a Dyn Lines chart.  A history + of lines loaded from a data file is available on in the Dyn Lines + section of the Overlay and Map Settings dialog (click the Wrench + button in the row containing the layer).  One sets the number + of chart lines displayed along with editing properties that include + the color and thickness of the chart lines.
+
+ +
+ +
+

Line Charts

+
+ Line charts display the content of a file's map or a row from some file types. + In the Overlay ToolBox, the top layer displays all data files supporting lines. + The remaining layers only list files that are compatible with the top layer. + Compatible files are those that use the same data units or contain the same + number of elements in their maps. +
+
+
+

Matrix Charts

+
+ Matrix charts are available for a limited number of files that + contain parcellated connectivity.  and for scalar data series files; + By default the full matrix + is displayed.  If the matrix is square (number of rows in the + data file matches the number of columns) a button in the Layer + controls the display of the matrix as the full matrix or the lower + or upper quadrant.  Coloring the matrix uses a palette and + is identical to that used for display of the file's data in surface + and volume overlays.
+
+
+
+
+ + diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/ChartingOverview.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/ChartingOverview.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/ChartingOverview.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/ChartingOverview.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ - - - - - Charting Overview - - -
-

Charting Overview

-
- With the release of 1.3.0, charting has been completely revised. The - previous version of charting is still available by selecting the - Chart Old in Toolbar->View.  Any scenes created with the - older version of charting should display correctly in Chart - Old.  In addition, wb_view will attempt to convert old versions - of charting to the new charting functionality.
-
- Charting has expanded to include the display of histograms in - addition to the pre-existing support for matrices and line-series - data.  An important feature of Charting is the simultaneous - display of multiple charts for the selected chart type. In the - Toolbar, the Chart Layers tab provides the selection of charts, as - layers, similar to Layers used in the Overlays for surfaces and - volumes.
-
-

Histogram Charts

-
- Histogram charts are available for any data file that contain one or - more maps of scalar data and this includes many CIFTI file types, - GIFTI metric files, and volume files.  A histogram shows the - distribution of data within a single map with an option to average - the data from all maps.  This map contain the same data that is - mapped to brainordinates in surface and volume views.  To view - a histogram chart, change Toolbar->View mode to Chart and select - Histogram in Toolbar->Chart Type.  In Toolbox->Chart - Layers, enable an overlay using an On checkbox and then choose a - file and map for display as a histogram.  Multiple histograms - can be displayed using multiple layers.  When displaying more - than one histogram, one may want to change the histogram chart from - Bars to Envelope by clicking the Layer's wrench button, selecting - the Palette tab, and selecting the appropriate check boxes in the - Histogram section.  There are a variety of additional options - for controlling the display of histograms.
-
-
-
-
-
-

Line-Series Charts

-
- Line-Series charts are available for most data files that contain - multiple maps of scalar data.  A line-series chart shows one - data element from each map for a selected brainordinate (surface - vertex or volume voxel).  To display line-series charts from a - data-file, one must enable the layer by checking the On checkbox and - enable loading of line charts from the data-file by checking the - Load checkbox.  Note that the Load checkbox is a property of - the file so if the same file is in more than one layer, the Load - checkbox will maintain the same checked status in these - layers.  Once loading is enabled for a file, one must identify - brainordinates by clicking the mouse on a surface vertex or a volume - voxel.  Doing so will load the data for the selected - brainordinate and display the data as a line-chart.  A history - of line-charts loaded from a data file is available on in the Lines - section of the Overlay and Map Settings dialog (click the Wrench - button in the row containing the layer).  One sets the number - of chart lines displayed along with editing properties that include - the color and thickness of the chart lines.
-
-
-
-
-
-

Matrix Charts

-
- Matrix charts are available for a limited number of files that - contain parcellated connectivity.  By default the full matrix - is displayed.  If the matrix is square (number of rows in the - data file matches the number of columns) a button in the Layer - controls the display of the matrix as the full matrix or the lower - or upper quadrant.  The palette mapping for coloring the matrix - is identical to that used for display of the file's data in surface - and volume overlays.
-
-
-
-
- - Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/HistogramChart.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/HistogramChart.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/HistogramControl.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/HistogramControl.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/LinesChart.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/LinesChart.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesChart.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesChart.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesHistory.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/LineSeriesHistory.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/ChartingOverview/MatrixChart.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/ChartingOverview/MatrixChart.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Features_Toolbox/Features_Toolbox.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Features_Toolbox/Features_Toolbox.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Features_Toolbox/Features_Toolbox.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Features_Toolbox/Features_Toolbox.html 2021-02-16 19:46:47.000000000 +0000 @@ -17,7 +17,7 @@ on structures.
 
The Features Toolbox is not displayed by default, but is turned on - with the button (or with button (or with View Menu > Features Toolbox) and attached to the right side of the Viewing Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Features_Toolbox/Foci/Foci.html 2021-02-16 19:46:47.000000000 +0000 @@ -37,6 +37,13 @@ foci from their coordinate space to the nearest point directly on the surface displayed.
+
  • Coordinate Type Chooses coordinates + for positioning foci. Projected displays foci at their projected + to surface coordinates that place for at the correct, relative + position on all surface types. Stereotaxic displays foci at + their original stereotaxic coordinates and should only be + used with Anatomical Surface or Volume Slices.
    +
  • Coloring sets the Class, Name or Standard Color as the foci color source.
  • @@ -45,6 +52,12 @@
  • Draw As sets the shape of the foci to Spheres or Squares.
  • +
  • Diameter Type Selects how + diameter of foci is determined. Millimeters uses millimeters but + may not work well when viewing brains of different sizdes (species). + Percentage sizes the symbols as a percentage of the window's + height and works well for inter-species comparisons.
    +
  • Symbol Diameter sets the size of the foci symbols.
  • diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Features_Toolbox/Images/Images.html 2021-02-16 19:46:47.000000000 +0000 @@ -2,12 +2,15 @@ - Borders + Images Images
    +

    + This functionality is deprecated and may be removed in a future release of Connectome Workbench. To use the new image display functionality: (1) Select Media in the Display section of the toolbar; (2) Click the Media tab in the Overlay Toolbox and select image(s) for display. +

    The Images Tab in the Features Toolbox Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Identify_Brainordinate_Window/CiftiFileRow.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Identify_Brainordinate_Window/CiftiFileRow.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Identify_Brainordinate_Window/Identify_Brainordinate_Window.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Identify_Brainordinate_Window/Identify_Brainordinate_Window.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Identify_Brainordinate_Window/Identify_Brainordinate_Window.html 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Identify_Brainordinate_Window/Identify_Brainordinate_Window.html 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,65 @@ + + + + + Select Brainordinate + + + Identify Brainordinate
    + Allows one to identify a brainordinate by its CIFTI file row, + CIFTI parcel membership, Label, or by it Surface vertex. +

      +
    • Surface Vertex
      +
    • +
        +
      • Structure sets the hemisphere + on which the vertex will be identified. 
        +
      • +
      • Vertex Index sets the vertex + number to be identified. 
        +
      • +
      +
    +
      +
    • Cifti File Row is only an active + option when a Dense Connectome, Dense Parcel or Trajectory + file is loaded (only dense CIFTI files activate this + selection).
      +
    • +
        +
      • File selects the file from + which a particular row will be viewed.
        +
      • +
      • Row Index sets the matrix row number to be mapped (also + + + + + + + + + identifies the corresponding vertex, which is not the + same as the row index). The Row index + will be displayed as the Map selection in the Overlay + + + + + + + Toolbox: Layers + tab.
      • +
      +
    +
    +
    +
    + + Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Identify_Brainordinate_Window/Indentify_Brainordinate.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Identify_Brainordinate_Window/Indentify_Brainordinate.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Information_Window/Information_Window.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Information_Window/Information_Window.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Information_Window/Information_Window.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Information_Window/Information_Window.html 2021-02-16 19:46:47.000000000 +0000 @@ -2,10 +2,18 @@ - Information Window + Legacy Information Window - Information Window
    + Legacy Information Window
    +

    This functionality is deprecated and may be removed in a future + release of Connectome Workbench.

    The new Information Window is enabled + in the ID tab of the Preferences Dialog. Look for "Identification Display" + and changes it setting to Overlay Toolbox. The new Information will then appear in right side of the Overlay Toolbox. It may be necessary to drag the "splitter" (small circle) to allocate more space to the Information display. +

    + This window has been replaced with an improved information display that + may be in a window or in the Overlay Toolbox and the choice of which is selected in the ID + tab of Preferences.

    The Information Window provides information on identified + + + + + + + + + + +

    + +

    Macros Overview

    + +

     

    + +

    Two new features have been +added and continue to be updated in wb_view.  The first new feature, macros, +permit the automation of a sequence of commands in wb_view.  These commands +encompass events from user-interface controls and special complex operations.   +Facilities exist for creating macros by recording user interactions with the +user-interface and manual editing of the macros.  In addition, the macro system +is integrated with another new feature, a new and improved movie creation +system.

    + +

     

    + +

    The macro system in wb_view +is similar to the macro systems in other software applications such as +Microsoft Office including PowerPoint’s animation system.

    + +

    In wb_view, the user can +record a macro while interacting that forms a sequence of commands from the +user interacting with the controls in wb_view.  In addition, facilities are +available to edit a macro and to add, delete, or move commands within the +macro.  In addition, there are special complex commands the user may insert +into a macro.  For the most part, these special commands perform an animation-like +operation.

    + +

     

    + +

    In the current +implementation, macros are always associated with the ‘current scene’.  The current scene is either the most recently +created or loaded scene.  In most cases, the current scene defines valid +preconditions for running of an associated macro.  In the future, macros may +also be stored in files or in the user’s preferences in order to simply a +sequence of operations in wb_view.  In addition,  a designated ‘command key +can be assigned to macro to simplify execution of the macro.  For example, a +macro that selects a particular volume slice view and selects slices at a +particular coordinate.

    + +

     

    + +

    As previously mentioned, the +macro system is integrated with a new movie creation system.  To simplify movie +creation, the user may create a macro that generates all commands needed to +create the movie.  One key feature in the macro system allows the user to +specify a delay before the execution of a macro command and a duration for +special commands.  This time duration, along with the movie system’s frame rate +(number of frames per second), results in the related section in the movie +running for the requested amount of time.  For example, the special command for +Surface Interpolation requires the user to enter the starting surface, ending +surface, and a time duration.  Wb_view will determine the number of iterations +in the interpolation operation so that the interpolation runs for the requested +duration in the movie.  Note that a larger duration or higher frame rate +results in a smoother animation operation.

    + +

     

    + +

     

    + +

    Macro Menu

    + +

    ·      +Macros…. – Displays the Macro Dialog for editing and running +macros.  A button in the toolbar also will display this dialog.

    + +

    ·      +Start Recording New Macro…  Displays a dialog for recording a +macro from selections made in the GUI.  Recording of a new macro is also +available by pressing the Record button on the Macro Dialog.

    + +

    ·      +Stop Recording New Macro – Select this item to finish recording +of a macro

    + +

     

    + +

     

    + +

    Record Macro Dialog

    + +

     

    + +

    + +

     

    + +

     

    + +

    ·      +Store macro in – Chooses where the macro is placed.  Choices are +the current scene or into the user’s preferences (disabled at this time)

    + +

    ·      +Macro name – Name for the macro

    + +

    ·      +Short Cut Key – Allows user to launch the macro using a short cut +key instead of running the macro from the macro dialog (primarily aimed at +macros stored in the preferences)

    + +

    ·      +Description – User should enter a description of the macro in +this text box

    + +

     

    + +

    After pressing the OK button, make selections in the GUI.  +When finished, select Stop Recording from the Macro Menu.

    + +

     

    + +

    Macro +Dialog with Macro Selected

    + +

     

    + +

     

    + +

    Top Row Buttons

    + +

    ·      +Macros in – Selects macros displayed in the dialog.  Macros are +available from the current scene or preferences

    + +

    ·      +Reload button (curly arrow) – Reloads the current scene

    + +

    ·      +Import/Export (…) – Allows import or export of the macro group

    + +

     

    + +

    Second Row Buttons

    + +

    ·      +Stop – Stops a macro that is running or  stops recording of a new +macro.

    + +

    ·      +Run – Run the selected macro (this button is only enabled when a +macro is selected (it is disabled if a command is selected)

    + +

    ·      +Pause – Pauses the currently running macro.  Pressing the button +while a macro is paused will resume execution of the macro.

    + +

    ·      +Record – When recording is off, a red outline circle is +displayed.  Pressing the button while it is in this state will display the +Record Button Menu as described below.  When recording is active, a red filled +circle is displayed and pressing the button will top recording.  

    + +

    ·      +Insert (Plus) – Allows insertion of new macros or insertion of +commands into an existing macro.  See Insert Button Menu below

    + +

    ·      +Up Arrow – Move the selected item up one position

    + +

    ·      +Down Arrow – Move the selected item down one position

    + +

    ·      +X – Delete the selected item (macro or macro command)

    + +

     

    + +

    Record Button Menu

    + +

    ·      +Record and Insert New Commands Below – Starts recording new +command from the user interacting with the user-interface.

    + +

    ·      +Record and Insert New Macro Below – Starts recording a new macro +from the user interacting with the user-interface.

    + +

     

    + +

    Insert (plus) button Menu

    + +

    ·      +Insert New Command Below – Displays the Add Macro Command for +inserting a new command. 

    + +

    ·      +Copy and Insert Macro Below – Copies and inserts a macro, +possibly from another scene.

    + +

    ·      +Insert New Macro Below – Inserts a new, empty macro.

    + +

     

    + +

     

    + +

    The list view contains macro and macro commands.  A macro +has an arrow button on its left side that can be toggled to show the commands +within the macro.

    + +

     

    + +

    Macro -

    + +

     

    + +

    ·      +Name – Allows editing of the macro’s name

    + +

    ·      +Short Cut Key – Allows assignment of a short cut key to the macro

    + +

    ·      +Description – Allows editing of the macro’s description

    + +

     

    + +

    Run Macro Options

    + +

    ·      +Window – Selects window in which macro runs

    + +

    ·      +Loop – Macro loops continuously until it is stopped and reloads +the current scene when at the end of the loop

    + +

    ·      +Ignore delays and durations – While a macro is running, all delay +times are ignored and for in iterative command, the iterations are limited to +the first and last iterations.

    + +

    ·      +Move mouse to highlight controls – If the macro was recorded from +the GUI the mouse will move and highlight a control until the control is +processed.  Note: If this is selected, the user will have difficulty using the +mouse while the macro runs. 

    + +

    ·      +Record movie while macro runs – Movie recording is enabled while +the macro runs. 

    + +

    ·      +Create movie after macro finishes – When running of the macro has +completed, a movie is automatically created.  When this checkbox transitions +from unchecked to checked, a dialog for movie file selection is displayed.

    + +
    +
    + +

     

    + +

    Macro Dialog with a Macro Command Selected

    + +

     

    + +

    + +

     

    + +

    Command

    + +

    ·      +Title – Title of the macro command

    + +

    ·      +GUI Name – This name is assigned by the GUI to uniquely identify +items in the user-interface that are affected by the selected command.

    + +

    ·      +GUI Type – The type of item affected by the macro

    + +

    ·      +Delay – Amount to wait before the command starts

    + +

    ·      +Description – Describes the command

    + +

    ·      +Edit Parameters – The content varies and includes parameters for +the command

    + +

     

    + +

     

    + +

     

    + +
    +
    + +

     

    + +

    Add Macro Command Dialog

    + +

     

    + +

    + +

     

    + +

    The Add Macro Command dialog appears when the adds a command +using the Plus button.

    + +

     

    + +

    ·      +Type – Selects the type of command.  Choices are “Custom +Operation†that performs a special operation and Widget for items in the +user-interface.

    + +

    ·      +Commands – Lists commands available

    + +

    ·      +Description – Contains a description of the command

    + +

     

    + +

     

    + +
    +
    + +

     

    + +

    Movie Recording Dialog Main Tab

    + +

     

    + +

    + +

     

    + +

    Source

    + +

    ·      +Record from Window – Selects window that is recorded

    + +

    ·      +Record Region – Choose from Graphics (area in which brain models +are drawn) or Window (entire window including toolbar, toolbox, graphics, etc)

    + +

     

    + +

    Recording Mode

    + +

    ·      +Automatic – Images are recorded anytime there is an update

    + +

    ·      +Manual – Pressing the record button will record an image for the +selected number of seconds

    + +

     

    + +

    Output Movie

    + +

    ·      +Create Movie – Displays a file selection dialog for selection of +the movie file.  If the user clicks the Save button on the file dialog, the +movie is created. Creation of a movie may take some time.  The format of the +movie file is based upon the extension of the movie filename. 

    + +

    ·      +Reset – Erases all recorded images. Typically used if the user +wants to erase a partial recording and start over.   In addition, this button +is also used if the user has disabled “Remove temporary images after movie +creation†on the settings tab.

    + +

     

    + +

     

    + +

     

    + +

    Movie Recording Settings Tab

    + +

     

    + +

    + +

     

    + +

    ·      +Resolution – Selection the resolution (width/height) of the +movie.  This may only be changed when there are no recorded images.

    + +

    ·      +Custom Resolution – Enabled when Resolution is Custom and allows +user to specify width and height.

    + +

    ·      +Frames Per Second – Number of frames per second in the movie. +Some Macro Commands contain a duration (seconds) and will use the Frames Per +Second to ensure proper length of movie.

    + +

    ·      +Remove temporary images after movie creation – After the movie is +created, image that were used to make the movie are removed.  If this is +disabled (usually only for debugging), a new movie may contain recorded images +from a previous movie.  Use the “Reset†button on the Main tab to remove the +temporary images.

    + +
    + + + + diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html 2021-02-16 19:46:47.000000000 +0000 @@ -114,6 +114,11 @@ + O.
      +
    • Open Recent... Displays dialog + for selecting a recently opened Scene or Spec File.
    • +
    +
    • Open Location... to open Workbench-readable files that are located remotely via a URL that the user sets (custom) or standard files through @@ -121,16 +126,8 @@ face="Calibri">The shortcut for Open Location is Command/Ctrl + L.
    -
      -
    • Open Recent Spec File... to open - a previously opened specification file from a dropdown list.
    • -
    -
      -
    • Open Recent Scene File... to open - a previously opened scene file from a dropdown list.
    • -
    + +
    -
      +
      • Capture Image... controls for capturing and saving images from the Workbench window.
      • +
      + +
        +
      • Movie Recording... Displays dialog recording the Workbench graphics region.
      • Close Tab to close the currently + + + + Macro Menu + + + Macro Menu
        +
          +
        • Macros - Displays dialog for managing macros
        • +
        • Start Recording New Macro...
        • +
        • Stop Recording New Macro
        • +
            +

            + See the Macros documentation. + + diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/Menus.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/Menus.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/Menus.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/Menus.html 2021-02-16 19:46:47.000000000 +0000 @@ -15,6 +15,8 @@

          • View
          • Data
          • Surface
          • +
          • Volume
          • +
          • Macro
          • Develop  (Contains developer options; must be enabled through Preferences)
            Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/RightClickPopUpMenu.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/RightClickPopUpMenu.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/TabBar_Tab_Menu.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/TabBar_Tab_Menu.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_AutomaticGrid.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_AutomaticGrid.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html 2021-02-16 19:46:47.000000000 +0000 @@ -17,6 +17,8 @@ + Tabs in the Viewing @@ -25,8 +27,21 @@ - Tabs in the Viewing + Area
            +
            + dialog
            +
            +

            Tile Tabs Configuration in Workbench Window
            +

            + This section contains the tile tabs + configuration for the selected Workbench Window.  If a Scene is saved, the + selected Configuration is saved to the Scene and is restored + when the Scene is displayed.
            +
            +
            +

            Workbench @@ -36,6 +51,15 @@ + Window 

            +
            + Selects the window for control of the window's Tile Tabs + Configuration
            +
            +

            Active Configuration Type
            +

            +
              +
            • Automatic Grid  @@ -46,64 +70,91 @@ + - This is the default selection and Workbench will adjust + the number of rows and columns so that all tabs are + displayed.  The first tab will be in + the top left corner with subsequent tabs appearing to the + right and wrapping to the next row when a row is filled.  - Area. 
              -
              -
              -
              -

              Tile Tabs Configuration in Workbench Window
              -

              - This section contains the tile tabs - configuration for the selected Workbench Window.  If a Scene is saved, the - selected Configuration is saved to the Scene and is restored - when the Scene is displayed.
              -
              -
              Workbench Window  - Selects the window for control of the window's Tile Tabs - Configuration
              -
              -
              -

              Layout of Tabs in a Tile Tabs Configuration

              -  A Tile Tabs Configuration is a grid - layout that displays all Tabs in the Window.  Regardless of - the Configuration Type, the first tab will be in the top left - corner with subsequent tabs appearing to the right and wrapping to - the next row when a row is filled.  Rows and/or columns may - also contain 'Spacers' (typically used for text as row/column - headers) and tabs skip over these spacers.
              -

              -

              Configuration Types
              -

              -
                -
              • Automatic - Configuration - This is the default selection and - Workbench will adjust the number of rows and columns so that - all tabs are displayed.  Use Custom when Automatic does not produce - the desired layout of tabs.
              • -
              • Custom Configuration - - Allows the user to customize the configuration of the Tile - Tabs layout including number of rows, number of columns, - heights of the rows, and widths of the columns.  If the - Custom Configuration contains insufficient rows and columns - for the Window's Tabs, some tabs will not be displayed.  + + Use Custom Grid or Manual when Automatic Grid does not + produce the desired layout of tabs.
              • +
              • Custom Grid - Allows the user to + customize the configuration of the Tile Tabs grid layout + including number of rows, number of columns, heights of the + rows, and widths of the columns.  If the Custom + Configuration contains insufficient rows and columns for the + Window's Tabs, some tabs will not be displayed.  Conversely, there will be empty space at the bottom when the Tile Tabs Configuration contains space for more tabs than are currently in the Window.  The user may also designate - rows or columns that contains 'Spacers'.
                -
              • + rows or columns as 'Spacers' that are typically used for text + annotations row/column headers. +
              • Manual - A better name for + "Manual Configuration" may be "No Configuration" as wb_view + makes no effort to position and size tabs in the window.  + Instead, it requires the user to position and size all tabs, + providing the utmost in layout flexibility.  Tabs may be + placed anywhere in the window including partially outside the + window and a tab may overlap other tabs.  Unlike the + Custom Grid, there is no editing of the configuration to add + or remove tabs.  Adding a new tab (File Menu -> New + Tab), places the new tab in the window and the user will need + to adjust the position and size of the new tab.  When a + tab is removed (File Menu -> Close Tab, or clicking a tab's + Close button in the tab bar), the tab is removed from the + window opening up space for other tabs.   The + position and size of each tab is edited on the Tile Tabs + Configuration Dialog or using the more powerful options in the + Window’s “Tile†mode.  To assist with creating a new + Manual Configuration, options exist for initializing the tab + positions from the Automatic or Custom Grids.  Lastly, in + a Manual Configuration, the display of individual tabs can be + disabled and may be useful when working with and possibly + annotating overlapping tabs.
              • +
              +

              Manual Set Button

              +

              On the right side of the Manual Selection + (Round) Button is a button titled "Set..".  When "Set.." is + clicked, a menu appears that allows the user set initialize the + size and position of the tabs using using a grid.

              +
                +
              • Set Bounds of Tabs from Automatic + Grid - Tab bounds will be identical to the + Automatic Grid.
              • +
              • Set Bounds of Tabs from Custom + Grid - Tab bounds will be identical to the Custom + Grid.  Note that a custom grid may not display all tabs + but all tabs will be in the manual configuration.
              • +
              • Set Bounds of Tabs from Grid + - Using a dialog, the user is prompted to enter the number of + columns in the grid and wb_view will set the number of rows so + that the grid contains all tabs.  Tabs bounds are then + set using the grid
              • +
              +

              Since a user may have tile tab + configurations that are useful with other data sets, options are + available to save the current configuration or initialize the + current configuration with a previously saved User + Configuration.  In addition, Template Configurations are + available to initialize the geometry of a configuration.
              +

              +
              -

              Custom Configuration Rows/Columns
              +

              Active Configuration Settings

              +
              + dialog +

              Custom Grid Rows/Columns Editing

                @@ -111,25 +162,21 @@ column.
              • Construction Menu - Allows the user to duplicate, move, or delete rows and columns.  -
              • Content - The content of the row or column chosen from Space or Tab.  The content of 'Space' is limited to annotations in "Spacer Coordinate Space" or "Window Space" Annotations.  The content of 'Tab' is limited to display of a browser tab along with annotations in - spaces other than "Spacer Coordinate Space."
                -
              • + spaces other than "Spacer Coordinate Space."
              • Type - The type of 'stretching' - chosen from Percent and Weight.
                -
              • + chosen from Percent and Weight.
              • Stretch - The stretching value.  When the Type is Percent this value is a percentage and when the Type is weight it is the weight for - this row/column.
                -
              • + this row/column.
              -

              Explanation of Stretching

              +

              Explanation of Stretching


              The space allocated to a row is determined by a combination of the Stretching Type and the Stretching Value.  (Note that row and @@ -144,8 +191,7 @@ that use Percentage sum to more than 100%, then part or all of the last rows may not be displayed (likewise for columns).  If there are no rows/columns that use Weight, then there may be blank - unused space, depending on the percentages used."
              -
              + unused space, depending on the percentages used."
              Example of Percentage Stretching
              • Row 1: 20%
              • @@ -160,8 +206,6 @@
              • Row 3 Height: 300 pixels (30% of 1000)
              -


              -

              Weighted Stretching Type

              When a row's Type is set to Weight, the Height of the row is affected by Stretching Values of all Rows with the Stretching Type @@ -169,7 +213,6 @@ weights from all rows are summed and the row's weight is divided by the sum.  This result (row's weight divided by sum) becomes the percentage of the window's height allocated to the row.
              -
              Example of Weighted Stretching
              • Row 1: 1.0
              • @@ -181,8 +224,7 @@

                • Row 1 Height: 250 pixels   ((1.0 / 4.0) * 1000 = - 250)
                  -
                • + 250)
                • Row 2 Height: 500 pixels   ((2.0 / 4.0) * 1000 = 500)
                • Row 3 Height: 250 pixels   ((1.0 / 4.0) * 1000 = 250)
                • @@ -192,8 +234,7 @@ When both Percentage and Weighted Stretching are used, rows with Percentage stretching are assigned their requested height percentage and any remaining space is allocated to rows with weighted - Stretching.
                  -
                  + Stretching.
                  Example of Percentage and Weighted Stretching
                  • Row 1: Percentage, 20%
                  • @@ -202,8 +243,7 @@
                  • Row 4: Weighted, 2.0

                  Result for a Window 1000 pixels in height (Note: Sum of - Percentages is 50% and Sum of Weights is 3.0):
                  -

                  + Percentages is 50% and Sum of Weights is 3.0):

                  • Row 1 Height: 200 pixels (20% of 1000)
                  • Row 2 Height: 300 pixels (30% of 1000)
                  • @@ -219,10 +259,9 @@
                  • Row 2 Height: 300 pixels (30% of 1000)
                  • Row 3 Height: 125 pixels ((1.0 / 4.0) * 50% * 1000)
                  • Row 4 Height: 250 pixels ((2.0 / 4.0) * 50% * 1000)
                  • -
                  • Row 5 Height: 125 pixels ((1.0 / 4.0) * 50% * 1000)
                    -
                  • +
                  • Row 5 Height: 125 pixels ((1.0 / 4.0) * 50% * 1000)
                  -

                  Summary of Percentage and Weighted Stretching

                  +
                  Summary of Percentage and Weighted Stretching

                  The advantage of using Percentage stretching is that the row will be allocated the requested percentage of the window's height.  The disadvantage of Percentage Stretching is that if @@ -247,38 +286,332 @@ the same size and the Rows containing the Brain Models will occupy all of the remaining vertical space.

                  -

                  User Configuration

                  - The User Configuration contains Tile Tabs - Configurations that have been created by the user.  These - configurations are saved in the user's preferences and thus are - available in future Workbench sessions.
                  +

                  Manual Editing
                  +

                  +

                  dialog

                  +

                  In a manual configuration, the user is responsible for the + placement and sizing of the tab. 
                  +

                  +
                    +
                  • Show - Checkbox enables or disables the drawing of a + tab in the window
                  • +
                  • Tab Name - Name of the tab
                  • +
                  • Left - Left side of the tab.  Value is a window + percentage ranging 0% (left side of window) to 100% (right side + of window)
                  • +
                  • Right - Right side of the tab
                  • +
                  • Bottom - Bottom of the tab
                  • +
                  • Top - Top of the tab
                  • +
                  • Background - Opaque (hides any tabs behind 'this' tab) + or Transparent (no background drawn so tabs behind 'this' tab + may be visible)
                  • +
                  • Order - A numerical value that sets the back to front + order of the tab (larger numerical values are in back of + smaller numerical values).  When tabs overlay, the Order + value is used to control the overlap of tabs (which tab is in + front or other tab(s)).
                  • +
                  +
                  + More powerful controls for editing a manual layout are available in + Tile Mode (Select "Tile" in Mode section of the Toolbar).
                  +

                  +

                  Configuration Library
                  +

                  + The Configurations Library section  + contains configurations that have been created by the user and + configurations provided by wb_view.    These User + Configurations are saved in the user's preferences and thus are + available in future Workbench sessions for that user.  The + Template Configurations are provided by wb_view.  While the + Template Configurations are not editable by the user, the user may + load a Template Configuration as the Current Configuration, edit + it, and then save it as a User Configuration.  The name of + each configuration is followed by a letter and number in + parenthesis.  The number indicates the number of tabs in the + configuration and the letter indicates the type of configuration, + G=Grid, M=Manual.
                    -
                  • New - Click this button to create - a new User Configuration
                  • Rename - Click this button to - rename the selected User Configuration
                  • + rename the selected User Configuration (Disabled for Template) +
                  • Delete - Click this button to - delete the selected User Configuration
                  • + delete the selected User Configuration (Disabled for Template) +
                  -

                  Replace and Load Push Buttons

                  +

                  Configuration Loading and Saving

                    +
                  • Add - Adds the Active + Configuration to the User Configurations that are saved to + Preferences.  A dialog prompts the user for the name of + the new configuration.
                  • Replace - Replaces (saves) the - selected User Configuration with the content of the Custom + selected User Configuration with the content of the Active Configuration
                  • Load - Copies the selected User - Configuration into the Custom Configuration and is used for - the layout of the window
                    -
                  • + Configuration into the Active Configuration using the Type + from the User Configuration or the Template + Configuration.  If the configuration is a Custom Grid and + there are more tabs viewed than in the Custom Grid, a dialog + is displayed to warn the user.  If the configuration is a + Manual Configuration and the number of tabs in the window is + different that the number of tabs in the configuration, a + dialog warns the user with the option to expand or contract + the number of tabs in the window. +
                  +

                  Configuration Preview

                  +

                  The preview shows an outline of the tabs in + the selected Template or User Configuration so that the user can + see the resulting layout of tabs.
                  +

                  +
                    +
                  +

                  Manual Configuration Editing Toolbar in Main Window

                  + dialog
                  +
                  + The toolbar is visible when a manual configuration is active and + "Tile" mode is selected.
                  +
                  + Located in the Mode section of the Toolbar is new mode named + “Tileâ€.  When Tile Tabs is enabled and the Active Configuration + is a Manual, “Tile†is enabled.  When in Tile Mode, controls + are added at the bottom of the Toolbar for editing the position and + size of the selected tab(s).  All numerical values in the + editing controls are in Window Percentages with zero percent at the + bottom/left and one-hundred percent at the right/top.
                  +
                    +
                  • (Tab Number/Name) - If one tab is selected, its number + and name is displayed.  If more than one tab is displayed, + only the tab numbers are displayed.  The Draw Content + checkbox controls drawing on/off of the tab.
                    +
                  • +
                  • Bounds – Controls for changing the size of the tab by + moving the tab’s sides.  Moving one of the bounds will + change the width or height of the tab.
                  • +
                  • Center – Controls for setting the position of the tab + by moving the center of the tab.  Width and height will not + change.
                  • +
                  • Size – Controls for setting the width or height of the + tab.
                  • +
                  • Background - Controls for changing the background + between Opaque and TransparentOrder + controls the drawing of tabs from back to front.  It is a + numerical value that sets the back to front order of the tab + (larger numerical values are in front of smaller numerical + values).  When tabs overlay, the Order value is used to + control the overlap of tabs (which tab is in front or other + tab(s)).
                  • +
                  • Format – Displays a menu for performing placement + options on the selected tab(s)
                  • +
                  • Edit – Allows one to undo or redo a tab position or + size modification.
                    +
                  • +
                  + If multiple tabs are selected and the tabs have a different values + for one of the numerical values, the numerical value is followed by + a plus (+) sign.  When one of the numerical values is changed, + it is applied to all selected tabs.  This may be useful in some + situations.  Suppose one wants two tabs to have the same left + bound.  Simply select both tabs (click tabs while holding down + the SHIFT key), type the desired value in the Left numerical + control, and press the Return key.
                  +
                  +

                  Tips for Manual Configuration Editing

                  +
                    +
                  • It may be helpful to set the Window background color so that + it is different from all other background colors.  Doing so + will help identify available space in the window and any regions + along the edges that are aspect locked.  Areas displaying + the solid background color are areas where tabs are + visible.  The background color with a pattern identifies + areas where tabs are not displayed due to aspect locking of the + window.
                    +
                  • +
                  • Set to View Mode
                  • +
                  • Adjust the window to the desired size
                  • +
                  • Lock the Window Aspect (Window Menu -> Lock Window Aspect + Ratio should be checked)
                  • +
                  • Unlock Tab Aspect (Window Menu -> Lock Tab Aspect Ratios + should NOT be checked)
                  • +
                  • Click the Tile button to change the Mode for editing of the + manual tab configuration (clicking the Tile button automatically + set the the Configuration Type to Manual)
                  • +
                  • Adjust dimensions of tabs to achieve the desired layout
                  • +
                  • Lock the Tab Aspect Ratios (Check Window Menu -> Lock Tab + Aspect Ratios or click the Lock Aspect button in the Toolbar's + "Tab" section
                  • +
                  • Return to View Mode
                    +
                  • +
                  +

                  Format Menu

                  +
                  + Dialog
                  +
                    +
                  • Align Left –  When two or more tabs are selected, + align the tabs so the left edges are the same as the left-most + tab
                  • +
                  • Align Center – When two or more tabs are selected, + align the tabs so that the center X-coordinates are the same
                  • +
                  • Align Right – When two or more tabs are selected, align + the tabs so the right edges are the same as the right-most tab
                  • +
                  • Align Top – When two or more tabs are selected, align + the tabs so the top edges are the same as the top-most tab
                  • +
                  • Align Middle – When two or more tabs are selected, + align the tabs so that the center Y-coordinates are the same
                  • +
                  • Align Bottom – When two or more tabs are selected, + align the tabs so the bottom edges are the same as the + bottom-most tab
                  • +
                  • Distribute Horizontally – When three or more tabs are + selected, adjust the positions and sizes of the tabs + horizontally so that they do not overlap but fill the horizontal + axis of the window
                  • +
                  • Distribute Vertically - When three or more tabs are + selected, adjust the positions and sizes of the tabs vertically + so that they do not overlap but fill the vertical axis of the + window
                  • +
                  • Bring to Front – When one tab is selected and the tab + overlaps with other tabs, move this tab forward so that this tab + is front of all other tabs
                  • +
                  • Bring Forward – When one tab is selected and the tab + overlaps with other tabs, move this tab forward one position
                  • +
                  • Send to Back - When one tab is selected and the tab + overlaps with other tabs, move this tab backward one position
                  • +
                  • Send Backward – When one tab is selected and the tab + overlaps with other tabs, move this tab backward so that it is + behind all other tabs
                  • +
                  • Expand Tab to Fill Empty Space – When one tab is + selected expand this tab so that it fills available empty space + around itself


                  -
                  -
                    -
                  -
                  -
                  -
                  +

                  Context Sensitive Menu on Tabs in Tab Bar

                  + +
                  + menu
                  +
                  +
                  + This menu is displayed by right-clicking a tab in the tab bar at the + top of the window.  The selections at the top of the menu are + new.
                  +
                  + The Active Tab is the tab that is selected and may be different than + the tab that is under the mouse.
                  +
                    +
                  • Select for Manual Layout Editing – Selects the tab so + that its layout attributes are editable
                  • +
                  • Show (or Hide) Tab Content in Window – Enables/Disables + display of tab in window and may be useful for editing + overlapping tabs or annotating overlapping tabs
                  • +
                  • Create New Tab Before This Tab - Create a new tab and + insert it into the tab bar on the left of the tab under the + mouse
                  • +
                  • Create New Tab After This Tab - Create a new tab and + insert it into the tab bar on the right of the tab under the + mouse
                  • +
                  • Duplicate This Tab at Beginning - Duplicate the tab + under the mouse and insert it at the far left position in the + tab bar
                  • +
                  • Duplicate This Tab Before Active Tab - Duplicate the + tab under the mouse and insert it on the left of the active tab + in the tab bar
                  • +
                  • Duplicate This Tab After Active Tab - Duplicate the tab + under the mouse and insert on the right of the active tab in the + tab bar
                  • +
                  • Duplicate This Tab at End - Duplicate the tab under the + mouse and insert it at the far right position in the tab bar
                  • +
                  • Move This Tab to Beginning - Move the tab under the + mouse to the far left position in the tab bar
                  • +
                  • Move This Tab to Before Active Tab - Move the tab under + the mouse to the left of the active tab
                  • +
                  • Move This Tab to After Active Tab - Move the tab under + the mouse to the right of active tab
                  • +
                  • Move This Tab to End - Move the tab under the mouse to + the far right side of the tab bar
                  • +
                  • Delete This Tab - Delete the tab under the mouse
                  • +
                  +

                  Context Sensitive (right-click) Menu in Window

                  +
                  + Menu
                  +
                  + This menu is displayed by right-clicking in the graphics region (are + where brain models are drawn).
                  +
                  +
                    +
                  • Select All – Selects all displayed tabs for editing
                  • +
                  • Brain to Front – Functionality documented in Arrange + Menu
                  • +
                  • Bring Forward – Functionality documented in Arrange + Menu
                  • +
                  • Send to Back – Functionality documented in Arrange Menu
                  • +
                  • Send Backward – Functionality documented in Arrange + Menu
                  • +
                  • Insert New Tab – Inserts a new tab at the mouse + position.
                  • +
                  • Expand Tab to Fill Empty Space - Functionality + documented in Arrange Menu
                    +
                  • +
                  +
                  +

                  Moving and Resizing Tabs with Mouse

                  +

                  Select a Tab using the Mouse

                  +
                    +
                  • Enable Tile Tabs (View Menu -> Enter Tile Tabs)
                  • +
                  • Click "Tile" in the Mode section of the Toolbar
                  • +
                  • Notice that tabs are enclosed in thin dashed lines delineating + the bounds of the tabs
                  • +
                  • Move the mouse over a tab and notice that the cursor + transforms from an arrow to four arrows indicating that the + mouse is over a tab.
                  • +
                  • Click the mouse over a tab to select the tab.
                  • +
                  • Notice that the tab is now delineated by solid lines with + sizing handles (squares at the corners and sides) indicating + that it is selected.  The sizing handles at the corners + allow changing the width and/or height of the tab while.   + The sizing handles along the sides vertical sides allow changing + only the width and the sizing handles on the horizontal edges + allow changing only the height.
                  • +
                  +
                  +

                  Move a Tab using the Mouse
                  +

                  +
                    +
                  • Select a tab.
                  • +
                  • Drag the mouse (hold down the left mouse button while moving + the mouse) to move the tab.
                  • +
                  +

                  Resize a Tab using the Mouse
                  +

                  +
                    +
                  • Select a tab.
                  • +
                  • Move the mouse over one of the sizing handles.  The mouse + cursor will transform to a two-arrow cursor and the arrows + indicate the direction of mouse movement that will resize the + tab
                  • +
                  • Drag the mouse to resize the tab.
                  • +
                  +


                  +

                  +

                  Moving Multiple Tabs using the Mouse
                  +

                  +
                    +
                  • To select multiple tabs, first select one tab.
                  • +
                  • To select another tab, move the mouse over a tab, hold down + the SHIFT key and click the mouse.
                  • +
                  • Drag the mouse to move the selected tabs.
                  • +
                  • Note that resizing of multiple tabs is allowed at this time + and may be added at a later time.
                  • +
                  +

                  Redo/Undo

                  + The Tile Toolbar contains Redo and Undo button in the Edit + section.  If a mistake is made while editing changing at tab's + geometry, clicking the Undo button will revert the most recent + modification and may be clicked again to further revert + modifications.

                  Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_Manual.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_Manual.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Toolbar_Arrange_Menu.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Toolbar_Arrange_Menu.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Window_Tile_Editing_Toolbar.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Window_Tile_Editing_Toolbar.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html 2021-02-16 19:46:47.000000000 +0000 @@ -154,22 +154,7 @@ Tabs Configuration. -
                • Tile Tabs Configuration Mode - > Displays a menu for selection of Automatic or Custom - modes for tab layout when Tile Tabs is enabled.  - Automatic creates a layout that displays all tabs in the - window.  Custom selects a mode that allows the user to - setup the layout out the tabs in the window in rows and - columns in addition to "stretch factors" for special sizing of - the rows and/or columns.
                  -
                • -
                • Load Custom With User Configuration - > A menu that lists all of the user's Tile Tabs - Configurations.  If one is selected, the Tile Tabs - Configuration mode is set to Custom and selected User - Configuration is loaded into the Custom Configuration.
                  -
                • -
                +

                Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/Volume_Menu/Volume_Menu.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/Volume_Menu/Volume_Menu.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Menus/Volume_Menu/Volume_Menu.html 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Menus/Volume_Menu/Volume_Menu.html 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,13 @@ + + + + + Volume Menu + + + Volume Menu
                +
                  +
                • Properties... - Displays dialog for editing volume display properties
                • +
                + + diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html 2021-02-16 19:46:47.000000000 +0000 @@ -21,7 +21,8 @@ mouse button and move the mouse back and forth and up and down. Note that the rotation function is not active in Volume - View. All other mouse controls are active in + View. In volume view, zooming sequences through + the slices for the selected axis. All other mouse controls are active in all views.
                diff -Nru "/tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html" "/tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html" --- "/tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html" 2020-01-07 23:24:23.000000000 +0000 +++ "/tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html" 2021-02-16 19:46:47.000000000 +0000 @@ -9,12 +9,12 @@

                Chart Layers


                The Chart Layers tab contain controls for the display of - charts.  Some controls are enabled only for a specific chart - type (Histogram, Line-Series, or Matrix).
                + charts.  Some controls are visible only for a specific chart + type (Histogram, Lines, Dyn Lines, or Matrix).
                • On : Enables the layer for display as a chart.
                • -
                • Load : Enables loading of line-series data from the file +
                • Load (DL) : Enables loading of line-series data from the file selected in the overlay.  This is a file property so if the file is in more than one layer, the check boxes will have the same status.
                  @@ -22,17 +22,26 @@
                • Settings Wrench :  Launches the Overlay and Map Settings Dialog.
                • -
                • Settings Color Bar: Displays a color bar in the graphics +
                • Settings Color Bar (H, M): Displays a color bar in the graphics window showing distribution of
                • Settings Construction : Displays a menu for adding, moving, and removing layers.
                • -
                • Settings Matrix : Selects view of matrix to the full matrix, +
                • Setting Color (L): Button with a small color square selects color of a line +
                • +
                • Settings Point Offset (L): Offset of text containing point index, x, y. +
                • +
                • Setting Mu-Sigma (L): Alter mean/deviation for lines +
                • +
                • Settings Matrix (M): Selects view of matrix to the full matrix, full with no diagonal, or an lower/upper triangular view.
                • -
                • Settings Vertical Axis : Selects location of the vertical axis - to the left or right.
                  +
                • Settings Opacity (M): Sets the opacity for the matrix in the layer +
                • +
                • Width (L): Width of lines +
                • +
                • Point (L): Enables display of text for the selected point in a line chart. Point is selected using the index numerical box or by clicking with mouse in the line.
                • File : Selects file for the layer.
                • @@ -53,6 +62,13 @@


                -
                - + Histogram
                +

                + Lines
                +

                + Dyn Lines +

                + Matrix +

                + Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/DynLinesChartingLayers.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/DynLinesChartingLayers.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/HistogramChartingLayers.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/HistogramChartingLayers.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/LinesChartingLayers.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/LinesChartingLayers.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/MatrixChartLayers.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/MatrixChartLayers.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html 2021-02-16 19:46:47.000000000 +0000 @@ -74,7 +74,7 @@
              -
              +
              Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.html 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.html 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,12 @@ +Information + + + +Each control contains a "Tooltip". To view a tooltip, move and then +rest mouse over a control. After a short delay, the tooltip describing +the control's function' is displayed. + + + \ No newline at end of file Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html 2021-02-16 19:46:47.000000000 +0000 @@ -24,19 +24,6 @@ underneath that layer are visible.
              -
            • The File pulldown sets the file - to display on that layer. Only loaded files that can be - displayed in the type of view set for the Active - - - - - Tab will be options. Files can - contain one or more brain Maps that may be selected by - number or name in the pulldowns to the right.
            • -
            -
            • Clicking the button opens the
            +
            • Yoke sets map yoking between layers to a map yoking group denoted by a Roman numeral. Navigation between maps of layers in the same map yoking group diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.html 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.html 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,18 @@ +Media + + + +
                +
              • On: Enables display of image in the layer
              • +
              • Wrench: Unused at this time
              • +
              • Contstruction: Edits layers (add/remove/etc)
              • +
              • Opacity: Disabled at this time but may by used for image transparency
              • +
              • File: Selects media file
              • +
              • Yoke: Unused at this time
              • +
              • Index: Index of frame
              • +
              • Name: Name of frame
              • +
              + + \ No newline at end of file Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html 2021-02-16 19:46:47.000000000 +0000 @@ -7,8 +7,11 @@ Overlay Toolbox
              The Overlay Toolbox contains controls for - setting the data to be displayed on structures and in charts.
              -  
              + setting the data to be displayed on structures and in charts. + It also contains identification information and controls. A small + circle on the left of the identification is dragged to change + the amount of space allocated to the identification section.
              +  
              By default, the Overlay Toolbox is attached to the bottom of the Workbench Window, under the 
              +
            • + Media controls the display of images in the current tab.
              +
            • +
            +
            • Vol/Surf Outline controls display of surface contours on volume slices/planes.
            - Tabs and files available for viewing change +
              +
            • + Information controls the display of information.
              +
            • +
            + Tabs and files available for viewing change depending on whether you are in Chart view, Montage Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/CiftiFileRow.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/CiftiFileRow.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Indentify_Brainordinate.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Indentify_Brainordinate.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ - - - - - Select Brainordinate - - - Identify Brainordinate
            - Allows one to identify a brainordinate by its CIFTI file row, - CIFTI parcel membership, Label, or by it Surface vertex. -
              -
            • Surface Vertex
              -
            • -
                -
              • Structure sets the hemisphere - on which the vertex will be identified. 
                -
              • -
              • Vertex Index sets the vertex - number to be identified. 
                -
              • -
              -
            -
            -
            -
            -
            - - diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ - - - - - Splash Screen - - - - Splash - - - - - - - - - Screen
            - The Splash Screen opens by default upon launching wb_view and - contains: -
              -
            • Workbench version information
            • -
            - -
            -
              - -
            • Shortcut list for - opening Specification - - - - - - - - - - - - - - files (spec files) and scene files in the current - directory or those that have been recently opened.
              -
              - To select and open - a Spec file listed, select its title and click the Open - button, or double click on its title.
              -
              - To turn off the default opening of the - Splash Screen at startup, turn off this option in Preferences - (File menu - or wb_view - - menu [Mac]).
              - -
                -
                -
                -
              -
              -
              -
            • -
            - - Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Splash_Screen/Splash_screen.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Splash_Screen/Splash_screen.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Splash_Screen/Thumbs.db and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Splash_Screen/Thumbs.db differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html 2021-02-16 19:46:47.000000000 +0000 @@ -19,16 +19,13 @@ structures.
            -
          • The Setup button opens the - Clipping Planes settings box that contains options for - panning, rotating, and setting the thickness for the - clipping box. The Show Clipping Box Outline toggle +
          • Show Clipping Box Outline toggle is useful for seeing exactly what will be clipped when clipping planes are turned on.

          -
          +
          Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping_Planes.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping_Planes.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html 2021-02-16 19:46:47.000000000 +0000 @@ -20,8 +20,7 @@
           
          - +
          • Rotate (X, Y, Z) sets the - rotation about the x-, y-, and z-axes.
          • + rotation about the x-, y-, and z-axes. These values are + ignored for flat surfaces.
          • Oblique Rotate (X, Y, Z) @@ -46,10 +46,22 @@ Indices/Coords.
            +
          • Flat Rotation sets rotation for flat + surfaces about the Z-axis (screen)
          • +
          +
          • Zoom sets zoom factor relative to the default of 1.
            +
          • Right Flat Offset Offsets the right + flat surface in the flat surface montage view
          • +
          +
            +
          • Right Flat ZoomSets the zoom factor for + the right flat surface the flat surface montage view
          • +
          +
          • Copy copies the settings from the Transform fields to the selected (highlighted) Custom Orientation file listed on the right.
          • Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html 2021-02-16 19:46:47.000000000 +0000 @@ -14,7 +14,21 @@ href="../../Montage_View/Montage_View.html">Montage
            , All, or Surface - views. + views. +

            + Tooltips on the buttons provide additional usage details. +

              +
            • New - Draw a new border +
            • Erase - Erase a section of an existing border +
            • Extend - Extend an existing border +
            • Optimize +
            • Replace - Replace a section of a border +
            • Finish - Click to finish a border that is being drawn +
            • Undo Finish - Undo the last Erase, Extend, or Replace operation +
            • Undo Point - Undo the last point drawn by user +
            • Reset - Remove all points in a partially drawn border +
            +

            Drawing a border:

            • By default, any loaded border files are automatically displayed on the surface. To turn borders off @@ -60,8 +74,7 @@
            -
            +
              Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing_toolbar.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing_toolbar.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html 2021-02-16 19:46:47.000000000 +0000 @@ -18,7 +18,7 @@ or Surface views.

              -
              +
              • Any Borders to be edited must be displayed. If they are off, toggle Display Borders on in the
                -
                diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html 2021-02-16 19:46:47.000000000 +0000 @@ -12,7 +12,13 @@ the bottom of the
                Toolbar.

                -
                + +
                  +
                • Create Last ID - Create a focus at the location of the mouse click +
                • Delete - Delete a clicked focus +
                • Edit Properties - Edit the properties of a clicked focus +
                +

                • Loaded foci files are NOT automatically displayed. To turn Foci on before creating a new focus, toggle diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html 2021-02-16 19:46:47.000000000 +0000 @@ -12,7 +12,6 @@ the Toolbar.

                  -
                  • By Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html 2021-02-16 19:46:47.000000000 +0000 @@ -24,90 +24,15 @@ Area are still active in Border and Foci modes. Information on mouse and key strokes for using the mode tools are available as Tooltips. -
                    Border - button opens the border drawing, editing, and ROI tools at the - bottom of the Toolbar:
                    -
                    -
                      -
                    • Border tools are only active for surface - structures in Montage, - All, or - Surface - views.
                    • -
                    -
                      -
                    • When Border mode is activated, any loaded - border files are automatically displayed on the surface. To - turn them off before Border drawing, toggle off Display - Borders in the Features - - - - - - - Toolbox Borders tab.
                    • -
                    -
                      -
                    • Draw tools are selected by - default. Draw by holding down the alt/option key and the left - mouse button while dragging your mouse where you want the - border.
                    • -
                    - - Foci button opens - the focus creation and editing tools at the bottom of the Toolbar:
                    - -
                      -
                    • Foci tools are active for surfaces and in - volumes (all views but Chart).
                    • -
                    -
                      -
                    • When Foci mode is activated, foci from - any loaded foci files are NOT automatically displayed. If you - want to turn them on before creating/editing a focus, toggle - on Display Foci in the Features Toolbox Foci - tab.
                    • -
                    - + href="../../Workbench_Window/Tooltip/Tooltip.html">Tooltips.
                    + Modes are: +
                      +
                    • View - Viewing mode, the default mode. +
                    • Tile Layout - Edit manual tile tabs layout. Click Search tab at top left and look for Tile Tabs Configuration +
                    • Annotate - Draw annotations. Click Search tab at top left and look for Anootations Guide +
                    • Edit Borders - Draw borders. Click Search tab at top left and look for Border Drawing +
                    • Edit Foci - Add Foci. Click Search tab at top left and look for Foci Creation and Foci Editing +
                    • Edit Voxels - Edit voxels in a volume file +
                    diff -Nru "/tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.html" "/tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.html" --- "/tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.html" 1970-01-01 00:00:00.000000000 +0000 +++ "/tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.html" 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,10 @@ +Voxel Editing + +

                    Voxel Editing>

                    + +

                    Voxel Operations

                    +This section provides voxel operations for editing the voxels. Tooltips on each of the controls describe their operations. +

                    Options

                    +The options allow creating a new volume file, adding a new map to an existing volume file, setting the brush (number of voxels affected by a mouse click) and the value to which voxels are set. + + \ No newline at end of file Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html 2021-02-16 19:46:47.000000000 +0000 @@ -2,10 +2,10 @@ - Tab Functions + Misc Functions - Tab Functions
                    + Misc Functions
                    The Tab section of the Toolbar contains controls for functions that are active across

                    - +
                      +
                    • Lock Aspect - Locks the aspect ration of all tabs and the main window. +
                    • Light Bulb Icon - Turns lightin on (default) and off +
                    • Scissor Icon - Displays clipping planes controls +
                    • Scale Bar Icon - Displays scale bar icon
                    Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html 2021-02-16 19:46:47.000000000 +0000 @@ -5,863 +5,7 @@ Toolbar - Toolbar
                    - Toolbar is the section at the top of the
                    Workbench - Window that contains viewing settings for the - structure you are displaying in the Active - Tab and access to - Workbench functional modes (border drawing, region of interest - definition, etc.).
                    -
                    -

                    - - - -
                      - -
                        -
                      • The Toolbar can be hidden or shown - using the button at the - top right of the Workbench Window or by clicking on - Toolbar in the View Menu.
                        -
                      • -
                      • Tooltips - (available by hovering mouse over buttons/elements) are - an easy way to see information on the buttons and - functions on the Toolbar (or throughout wb_view).
                        -
                      • -
                      -
                      -
                    -
                    -

                    Montage Toolbar

                    -
                      -
                    • Orientation contains buttons to - set orthogonal and User-defined views of the brain surface - displayed.
                    • -
                    -
                      -
                        -
                      • Orthogonal view buttons are labeled: LM - (lateral-medial), DV (dorsal-ventral), AP - (anterior-posterior). 
                        -
                      • -
                      • Using the Mouse - Controls, in Montage view, rotation is - mirrored between hemisphere lateral/medial sides and panning - moves hemisphere sides together or away from each other.
                        -
                      • -
                      • The Reset button resets the - orientation/zoom to the default.
                        -
                      • -
                      • Custom - - - - - - - - - - Orientation allows one to set and save (or - not) a specific transform (pan, rotate, oblique rotate, - zoom) for a surface or volume.
                      • -
                      -
                    -
                      -
                    • Montage Selection contains - pull-downs and checkboxes to set the surface montage to view.
                    • -
                    -
                      -
                        -
                      • Top Left pull-down sets the montage - structure: Cerebral Cortex, Cerebellar Cortex, or Flat. A - structure is only viewable if the appropriate surface is - loaded.
                        -
                      • -
                      • Top Right pull-down sets the page - orientation of the surfaces displayed. Pull-downs in the - middle set the surface file for each hemisphere from the - list of loaded files. Up to 2 left and 2 right surfaces can - be displayed at once. The checkboxes indicate if the set - surfaces are being displayed.
                        -
                      • -
                      • Left, Lateral, Medial, - and Right checkboxes indicate if these structures - are being displayed.
                      • -
                      -
                    - -
                      -
                    • Clipping - contains settings for cutting down Surface, Volume, or - Features data to be viewed.
                    • -
                    - Tab - contains cross-tab functions for yoking the display of two or more - Viewing Tabs and for locking aspect ratio (used with - annotations).  Shading slightly alters the coloring assigned - to brainordinates to provide a better quality image.  Shading - only affects surfaces.
                    -
                    -
                    -

                    -
                    -
                    -

                    Volume Toolbar

                    -
                      -
                    • Slice - - - - - - - - - - - - Plane contains buttons to set which volume slice planes - are displayed: P (parasagittal), C (coronal), - A (axial), All (all 3 planes). 
                    • -
                    -
                    -
                    -
                    -
                      -
                    • The Reset button resets the - orientation/zoom to the default.
                    • -
                    -
                    -
                    -
                      -
                    • Custom - - - - - - - - - - - Orientation allows one to set and save (or - not) a specific transform (pan, rotate, oblique rotate, - zoom) for a surface or volume.
                    • -
                    • The crosshair button (large '+' symbol) - enables the drawing of crosshairs, representing the selected - slices.
                    • -
                    • The crosshair labels button enables - drawing labels indicating the orientation of the viewed - slice.
                      -
                    • -
                    -
                    -
                      -
                    • Montage contains settings for - viewing multiple volume slices from a single slice plane at - once. Rows/Cols set the number of Rows or - Columns of slices to display. Step sets the index - spacing between slices in the volume slice montage. 
                    • -
                    -

                    -
                      - -
                    • Slice Indices/Coords - contains - settings for the slice index and stereotaxic (Talairach) - coordinate to be viewed for each slice plane.  -
                    • -
                      -
                    - -
                    -
                      -
                    • The vertical Origin button - rests the slice indices to the default (centered at the - AC).
                    • -
                    -
                    -
                    -
                    -
                      - -
                    • The  button (default on) - activates movement of the crosshairs/volume slice to the same - plane as the brainordinate selected in any of the Viewing - Tabs in the same yoking group as the Volume tab.
                    • -
                    -
                    -
                    -
                      -
                    • The pull-down at the bottom toggles - between Orthogonal and Oblique volume - viewing.
                    • -
                    • The button at the left enables the - slices to move to the brainordinate of an identification - operation (mouse click on a volume slice or a surface.
                    • -
                    • The "M" button at the right selects - masking during oblique viewing.  In oblique viewing, - cubic interpolation is used which may cause edge effects and - masking will remove these edge effects.
                      -
                    • -
                    -
                    -
                    - -
                      -
                    • Clipping - contains settings for cutting down Surface, Volume, or - Features data to be viewed.
                    • -
                    -
                      -
                    • Tab - contains cross-tab functions for yoking the display of two or - more Viewing Tabs and for locking aspect ratio (used with - annotations). Shading - slightly alters the coloring assigned to brainordinates to - provide a better quality image.  Shading only affects - surfaces.
                    • -
                    -
                    -
                    -
                    -
                    -

                    All Toolbar

                    -
                    -

                    -
                      -
                    • Orientation contains buttons to - set orthogonal and User-defined views of the brain - surfaces/volume displayed.
                    • -
                    -
                    -
                      -
                    • Orthogonal view buttons are labeled: L - (left), R (right), D (dorsal), V - (ventral), A (anterior), and P - (posterior). 
                    • -
                    -
                    -
                    -
                    -
                      -
                    • The Reset button resets the - orientation/zoom to the default.
                    • -
                    -
                    -
                    - -
                    -
                      -
                    • Surface Viewing contains - pull-downs and check-boxes to set the surfaces to view: L - (left hemisphere), R (right hemisphere), C - (cerebellum).
                    • -
                    -
                    -
                      -
                    • The top pull-down menu changes the type - of surface: Anatomical, Inflated or Very Inflated.
                    • -
                    -
                    -
                    -
                      -
                    • The check-boxes to the left of the L, R - and C turn on/off the display of that structure.
                    • -
                    -
                    -
                    -
                      -
                    • To change the structure's surface file, - click on the L, R or C button and a list of loaded files - will appear for selection.
                    • -
                    -
                    -
                    -
                      -
                    • The scroll boxes - to the right, control the spacing between left and right - hemispheres (relative to anatomical spacing = 0) and the - spacing of the cerebellum away from the cortex.
                    • - -
                    -
                    - -
                      -
                    • Slice Indices/Coords - contains toggles to turn off/on volume planes (P = - parasagittal, C = coronal, A = axial) and - settings for the slice index and stereotaxic (Talairach) - coordinate to be viewed. 
                    • -
                    -
                    -
                      -
                    • The vertical Origin button - rests the slice indices to the default (centered at the - AC).
                    • -
                    -
                    -
                    -
                    -
                      - -
                    • The  button (default on) - activates movement of the crosshairs/volume slice to the same - plane as the brainordinate selected in any of the Viewing - Tabs in the same yoking group as the All tab.
                    • -
                    -
                    -
                    -
                      -
                    • The pull-down at the bottom toggles - between Orthogonal and Oblique volume - viewing.
                    • -
                    -
                    - -
                      -
                    • Clipping - contains settings for cutting down Surface, Volume, or - Features data to be viewed.
                    • -
                    -
                      -
                    • Tab contains - cross-tab functions for yoking the display of two or more - Viewing Tabs - and for locking aspect ratio (used with annotations).  Shading slightly alters the coloring - assigned to brainordinates to provide a better quality - image.  Shading only affects surfaces.
                    • -
                    -
                    -
                    -
                    -
                    -

                    Histogram and Line Series Chart Toolbar

                    - Chart Type selects the type of chart - displayed in the tab.  Both Histogram and Line-Series charts - use the same toolbar.
                    -
                    - Chart Axes contains controls for setting axes' attributes.
                    -
                    -
                    -
                      -
                    • Edit Axis Selection - Selects the - axis (Left, Bottom, Right) for editing
                      -
                    • -
                    • Label from File In selection - A - label is displayed in the axis (eg: counts, data, etc.) and - this label is a property of a file.  Since there may be - more than one layer, use this control to choose the layer and - the label displayed is from the file in the selected layer.
                      -
                    • -
                    • Edit Label... Pops up a dialog for - editing the text in the axis' label.
                      -
                    • -
                    • Axis Checkbox - Turns display of - the entire axis on of off.
                      -
                    • -
                    • Label Checkbox - Turns display of - the label on or off.
                      -
                    • -
                    • Nums Checkbox - Turns display of - the numeric values in the axis on or off.
                      -
                    • -
                    • Rotate Checkbox - When checked, - the label is rotated so that the text is oriented bottom to - top (rotated 90 degrees).
                      -
                    • -
                    • Ticks Checkbox - Turns display of - the axis tick marks on or off.
                      -
                    • -
                    • Label Size - Controls the height - of the label's text characters as a percentage of the tab's - graphics region height.
                      -
                    • -
                    • Scale Size - Controls the width (left - or right axis) or height (bottom axis) axis as a percentage - of the tab's graphics region height.
                    • -
                    • Pad Size - Controls the height of - the label's text characters as a percentage of the tab's - graphics region height.
                    • -
                    • Lines Size- Controls the height of the label's text - characters as a percentage of the tab's graphics region - height.
                      -
                    • -
                    • Range Mode - Chooses the range of - the axis.  Auto extends the range of the axis to beyond - the data's minimum and maximum values so that, in most cases, - whole numbers are displayed.  Data sets the axis range to - the exact range of the data.  User allows the user to set - the minimum and maximum of the data displayed.
                      -
                    • -
                    • Max (Range) Value - The minimum - value in the axis.
                      -
                    • -
                    • Min (Range) Value - The maximum - value in the axis.
                      -
                    • -
                    • Format - Controls format of the - numerical values in the axis.  Decimal displays the - numerical values using a decimal format (eg: -1.8, 2, - etc).  Scientific displays the numerical values using - scientific notation (eg: 1.32e-2; -2.5e+3; etc.)   Auto - chooses a format to best display the values from either - Decimal or Scientific.
                      -
                    • -
                    • Decimals - When the Format is - Decimal or Scientific, this controls the number of digits - right of the decimal point.
                      -
                    • -
                    • Subdivisions - Subdivisions is the - number of numeric values between the minimum and maximum - values.  Auto sets the number number of subdivisions - appropriate for the data.  User allows the user to set - the number of subdivisions using the numerical value below - Auto/User.
                      -
                    • -
                    - Chart Title contains controls for the - display of a title above the chart.
                    -
                    -
                      -
                    • Show Title Checkbox - Turns - display of the title on or off.
                      -
                    • -
                    • Size Value - Sets the size of the - label's text as a percentage of the tab's graphics region - height.
                      -
                    • -
                    • Pad Value - Sets the size of the - space between the edge of the tab's graphics region and the - top of the text.
                      -
                    • -
                    • Edit Title button - Displays a - dialog for editing the title's text.
                      -
                    • -
                    -
                    -
                    Mode - contains buttons for switching between Viewing - - - - - - - - - - - - - - - - Area mouse controls for performing actions - including Border - drawing and Foci - creation or typical View mode.
                    -
                    - Tab
                    contains - cross-tab functions for yoking the display of two or more Viewing - Tabs
                    and for - locking aspect ratio (used with annotation
                    -
                    -
                    -
                    -
                    -

                    Matrix Chart Toolbar

                    -
                    - Chart Type selects - the type of chart displayed in the tab.
                    -
                    - Chart Orientation
                    contains buttons to set - orthogonal and User-defined views of the brain surfaces/volume - displayed.
                    -
                    -
                      -
                    • Reset button resets the view of - the matrix to remove any panning and zooming (shift-drag mouse - to pan matrix, command-drag mouse to zoom).
                    • -
                    • Custom button allows one to create - or use an existing custom orientation.
                    • -
                    - Chart Attributes
                    -
                    -
                      -
                    • Cell Width - Scales the width of - the cells to be larger or smaller.
                    • -
                    • Cell Height - Scales the height of - the cells to be larger or smaller.
                    • -
                    • Highlight Selection - If a - row is selected in the matrix, row is highlighted with an - outline.  The color of the outline is set on the - Preferences' Colors tab.
                    • -
                    • Show Grid Outline - - Displays a grid with lines at the horizontal and vertical cell - boundaries.  The color of the grid lines is set on the - Preferences' Colors tab.
                    • -
                    - Chart Title - contains controls for the display of a title above the chart.
                    -
                    -     Show Title Checkbox - Turns display - of the title on or off.
                    -     Size Value - Sets the size of the - label's text as a percentage of the tab's graphics region - height.
                    -     Pad Value - Sets the size of the - space between the edge of the tab's graphics region and the top - of the text.
                    -     Edit Title button - Displays a dialog - for editing the title's text.
                    -
                    -
                    -

                    -
                    -
                    -
                    -

                    Surface Toolbar

                    -
                    -

                    -
                      -
                    • Orientation contains buttons to - set orthogonal and User-defined views of the brain surface - displayed: L (lateral), M (medial), D (dorsal), - V (ventral), A - (anterior), and P (posterior). 
                    • -
                    -
                      -
                        -
                      • The Reset button resets the - orientation/zoom to the default.
                        -
                      • -
                      • Custom - - - - - - - - Orientation allows one to set and save (or - not) a specific transform (pan, rotate, oblique rotate, - zoom) for a surface or volume.
                      • -
                      -
                    -
                      -
                    • Selection contains pull-downs to - set the brain structure (top) and surface (bottom) to view.
                    • -
                    - -
                      -
                    • Clipping - contains settings for cutting down Surface, Volume, or - Features data to be viewed.
                    • -
                    -           Tab - contains cross-tab functions for yoking the display of two or more - Viewing Tabs and - for locking aspect ratio (used with annotations). 
                    - Shading slightly alters - the coloring assigned to brainordinates to provide a better - quality image.  Shading only affects surfaces.
                    -
                    -
                    -

                    Line Series Old Chart Toolbar

                    -
                    -

                    - Chart Type (Old), - determines which kind of chart is viewed. If one or more - Workbench-chartable files are loaded, one or more Chart Types - will be available for selection. Chartable files for the - selected Chart Type are listed and can be selected for display - in the Overlay Toolbox Charting - tab.
                    -  
                    - Chart Types currently available:
                    - Data/Time - - - - - - - - Series (top image) chartable files are graphed - for a brainordinate identified in a non-Chart view Viewing Tab, - showing a data value (y-axis) for each Map Index/unit of time - (x-axis). The following file types are available for Data/Time - Series charting, as long as they contain more than one - map: -
                    -
                    - * - CIFTI - Data Series (.dtseries.nii)
                    - * - CIFTI Parcel Scalar (.pscalar.nii)
                    - * - CIFTI Parcel Series (.ptseries.nii)
                    - * - CIFTI Scalar (.dscalar.nii)
                    - * - Metric (.func.gii, .shape.gii)
                    - * - Volume (.nii, .nii.gz)
                    -
                    - Matrix - (bottom image) chartable files:
                    -
                    - * - CIFTI Parcel to - - - - - - - parcel connectivity (.pconn.nii) -- matrix - - - - - - - - - - - with parcels as the y-axis rows, parcels as the x-axis - columns, and the colored cells indicate the correlation - (connectivity) values between the parcel pair.
                    - * - CIFTI Parcel Scalar (.pscalar.nii) -- matrix - - - - - - - - - - with parcels as the y-axis rows, map indices as the x-axis - columns, and the colored cells indicate the scalar data value - for a parcel/map pair in a particular parcel - (identified in a non-Chart view Viewing Tab). 
                    -
                    -
                    -
                    -
                    -

                    Matrix Old Chart Toolbar

                    -
                    -
                    -

                    -

                    -
                    + The toolbar is at the top of the Workbench Window. The content of the tooblar is dependent upon the Mode and Display selections.

                    + Each control in the toolbar contains a tooltip describing the control's function. To view the tooltip, move and then rest the mouse over the control. After a short delay, the tooltip will appear. Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Macro_icon.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Macro_icon.png differ Binary files /tmp/tmpl3Ut7_/XkUZv8jxJ2/connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Movie_icon.png and /tmp/tmpl3Ut7_/APhP8mbF0W/connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Movie_icon.png differ diff -Nru connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html --- connectome-workbench-1.4.2/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html 2021-02-16 19:46:47.000000000 +0000 @@ -54,6 +54,12 @@ align="middle" width="26">    Identify Brainordinate button

                    +     Movies Window button
                    +
                    +     Macros Window button
                    +
                        Scenes Window button

                    diff -Nru connectome-workbench-1.4.2/src/Resources/Help/help_resources.qrc connectome-workbench-1.5.0/src/Resources/Help/help_resources.qrc --- connectome-workbench-1.4.2/src/Resources/Help/help_resources.qrc 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/help_resources.qrc 2021-02-16 19:46:47.000000000 +0000 @@ -1,44 +1,39 @@ ./help_resources.qrc -./HelpFiles/Annotations/Annotations_1.3.fld/image001.png -./HelpFiles/Annotations/Annotations_1.3.fld/image002.png -./HelpFiles/Annotations/Annotations_1.3.fld/image003.png -./HelpFiles/Annotations/Annotations_1.3.fld/image004.png -./HelpFiles/Annotations/Annotations_1.3.fld/image005.png -./HelpFiles/Annotations/Annotations_1.3.fld/image006.png -./HelpFiles/Annotations/Annotations_1.3.fld/image007.png -./HelpFiles/Annotations/Annotations_1.3.fld/image008.png -./HelpFiles/Annotations/Annotations_1.3.fld/image009.png -./HelpFiles/Annotations/Annotations_1.3.fld/image010.png -./HelpFiles/Annotations/Annotations_1.3.fld/image011.png -./HelpFiles/Annotations/Annotations_1.3.fld/image012.png -./HelpFiles/Annotations/Annotations_1.3.fld/image013.png -./HelpFiles/Annotations/Annotations_1.3.fld/image014.png -./HelpFiles/Annotations/Annotations_1.3.fld/image015.png -./HelpFiles/Annotations/Annotations_1.3.fld/image016.png -./HelpFiles/Annotations/Annotations_1.3.fld/image017.png -./HelpFiles/Annotations/Annotations_1.3.fld/image018.png -./HelpFiles/Annotations/Annotations_1.3.fld/image019.png -./HelpFiles/Annotations/Annotations_1.3.fld/image020.png -./HelpFiles/Annotations/Annotations_1.3.fld/image021.png -./HelpFiles/Annotations/Annotations_1.3.fld/image022.png -./HelpFiles/Annotations/Annotations_1.3.fld/image023.png -./HelpFiles/Annotations/Annotations_1.3.fld/image024.png -./HelpFiles/Annotations/Annotations_1.3.fld/image025.png -./HelpFiles/Annotations/Annotations_1.3.fld/image026.png -./HelpFiles/Annotations/Annotations_1.3.fld/image027.png -./HelpFiles/Annotations/Annotations_1.3.fld/image028.png -./HelpFiles/Annotations/Annotations_1.3.fld/image029.png -./HelpFiles/Annotations/Annotations_1.3.fld/image030.png -./HelpFiles/Annotations/Annotations_1.3.fld/image031.png -./HelpFiles/Annotations/Annotations_1.3.fld/image032.png -./HelpFiles/Annotations/Annotations_1.3.fld/image033.png -./HelpFiles/Annotations/Annotations_1.3.html +./HelpFiles/Annotations/Annotations_1.4.2.fld/image001.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image002.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image003.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image004.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image005.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image006.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image007.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image008.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image009.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image010.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image011.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image012.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image013.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image014.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image015.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image016.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image017.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image018.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image019.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image020.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image021.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image022.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image023.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image024.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image025.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image026.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image027.png +./HelpFiles/Annotations/Annotations_Guide.html ./HelpFiles/Best_Practices_Guides/Annotation_and_Scenes_Best_Practices.html -./HelpFiles/ChartingOverview/ChartingOverview.html +./HelpFiles/ChartingOverview/Charting_Overview.html ./HelpFiles/ChartingOverview/HistogramChart.png ./HelpFiles/ChartingOverview/HistogramControl.png +./HelpFiles/ChartingOverview/LinesChart.png ./HelpFiles/ChartingOverview/LineSeriesChart.png ./HelpFiles/ChartingOverview/LineSeriesHistory.png ./HelpFiles/ChartingOverview/MatrixChart.png @@ -75,11 +70,22 @@ ./HelpFiles/Glossary/Scene/Scene.html ./HelpFiles/Glossary/Specification_File/Spec_File.png ./HelpFiles/Glossary/Specification_File/Specification_File.html +./HelpFiles/Glossary/Specification_File/Thumbs.db ./HelpFiles/Glossary/Whiteordinate/Whiteordinate.html +./HelpFiles/Identify_Brainordinate_Window/CiftiFileRow.png +./HelpFiles/Identify_Brainordinate_Window/Identify_Brainordinate_Window.html +./HelpFiles/Identify_Brainordinate_Window/Indentify_Brainordinate.png ./HelpFiles/Information_Window/Information_Window.html ./HelpFiles/Information_Window/Information_Window.png ./HelpFiles/Information_Window/Properties/Information_Properties.png ./HelpFiles/Information_Window/Properties/Properties.html +./HelpFiles/Macros/MacrosWorkbench.fld/image001.png +./HelpFiles/Macros/MacrosWorkbench.fld/image002.png +./HelpFiles/Macros/MacrosWorkbench.fld/image003.png +./HelpFiles/Macros/MacrosWorkbench.fld/image004.png +./HelpFiles/Macros/MacrosWorkbench.fld/image005.png +./HelpFiles/Macros/MacrosWorkbench.fld/image006.png +./HelpFiles/Macros/MacrosWorkbench.html ./HelpFiles/Menus/Data_Menu/Data_Menu.html ./HelpFiles/Menus/Data_Menu/Project_Foci_Dialog.png ./HelpFiles/Menus/Data_Menu/Thumbs.db @@ -90,9 +96,11 @@ ./HelpFiles/Menus/Edit_Menu/Edit_Menu.png ./HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control.html ./HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control_dialog.png +./HelpFiles/Menus/File_Menu/Animation_Control/Thumbs.db ./HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image.html ./HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image_dialog.png ./HelpFiles/Menus/File_Menu/Capture_Image/File_menu.png +./HelpFiles/Menus/File_Menu/Capture_Image/Thumbs.db ./HelpFiles/Menus/File_Menu/File_Menu.html ./HelpFiles/Menus/File_Menu/File_menu.png ./HelpFiles/Menus/File_Menu/Preferences/Preferences.html @@ -102,6 +110,7 @@ ./HelpFiles/Menus/File_Menu/Preferences/Preferences_NewTabs.png ./HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png ./HelpFiles/Menus/File_Menu/Preferences/Preferences_Vol.png +./HelpFiles/Menus/File_Menu/Preferences/Thumbs.db ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Gear_symbol.png ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Reload_symbol.png ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Remove_symbol.png @@ -109,6 +118,7 @@ ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files_dialog.png ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Thumbs.db ./HelpFiles/Menus/File_Menu/Save-Manage_Files/Upload_symbol.png +./HelpFiles/Menus/File_Menu/Thumbs.db ./HelpFiles/Menus/Help_Menu/Bug_Report_box.png ./HelpFiles/Menus/Help_Menu/Help_box.png ./HelpFiles/Menus/Help_Menu/Help_button.png @@ -116,6 +126,7 @@ ./HelpFiles/Menus/Help_Menu/Help_Menu.png ./HelpFiles/Menus/Help_Menu/Help_Search.png ./HelpFiles/Menus/Help_Menu/Workbench_Help.png +./HelpFiles/Menus/Macro_Menu/Macro_Menu.html ./HelpFiles/Menus/Menus.html ./HelpFiles/Menus/Surface_Menu/Surface_Menu.html ./HelpFiles/Menus/Surface_Menu/Surface_Properties.png @@ -123,15 +134,24 @@ ./HelpFiles/Menus/Thumbs.db ./HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.html ./HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.png +./HelpFiles/Menus/View_Menu/Thumbs.db +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/RightClickPopUpMenu.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/TabBar_Tab_Menu.png ./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html ./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_AutomaticGrid.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_Manual.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Toolbar_Arrange_Menu.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Window_Tile_Editing_Toolbar.png ./HelpFiles/Menus/View_Menu/View_Menu.html ./HelpFiles/Menus/View_Menu/View_Menu.png +./HelpFiles/Menus/Volume_Menu/Volume_Menu.html ./HelpFiles/Menus/wb_view_Menu/Thumbs.db ./HelpFiles/Menus/wb_view_Menu/wb_view_Menu.html ./HelpFiles/Menus/wb_view_Menu/wb_view_Menu.png ./HelpFiles/Menus/Window_Menu/IdentifyBrainordinateDialog.png ./HelpFiles/Menus/Window_Menu/Rename_tab.png +./HelpFiles/Menus/Window_Menu/Thumbs.db ./HelpFiles/Menus/Window_Menu/Window_Menu.html ./HelpFiles/Menus/Window_Menu/Window_Menu.png ./HelpFiles/Menus/Window_Menu.png @@ -144,12 +164,19 @@ ./HelpFiles/Open_Spec_File_Dialog/Open_Spec_File_Dialog.html ./HelpFiles/Open_Spec_File_Dialog/Thumbs.db ./HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html +./HelpFiles/Overlay_Toolbox/Chart_Layers/DynLinesChartingLayers.png +./HelpFiles/Overlay_Toolbox/Chart_Layers/HistogramChartingLayers.png ./HelpFiles/Overlay_Toolbox/Chart_Layers/LineChartingLayers.png +./HelpFiles/Overlay_Toolbox/Chart_Layers/LinesChartingLayers.png +./HelpFiles/Overlay_Toolbox/Chart_Layers/MatrixChartLayers.png ./HelpFiles/Overlay_Toolbox/Charting/Charting.html ./HelpFiles/Overlay_Toolbox/Charting/Charting_History.png ./HelpFiles/Overlay_Toolbox/Charting/Charting_Loading.png +./HelpFiles/Overlay_Toolbox/Charting/Thumbs.db ./HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html ./HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png +./HelpFiles/Overlay_Toolbox/Information/Information.html +./HelpFiles/Overlay_Toolbox/Information/Information.png ./HelpFiles/Overlay_Toolbox/Layers/Add_Layer.png ./HelpFiles/Overlay_Toolbox/Layers/Colorbar.png ./HelpFiles/Overlay_Toolbox/Layers/Layers.html @@ -159,9 +186,12 @@ ./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Settings_icon.png ./HelpFiles/Overlay_Toolbox/Layers/Overlay_Toolbox_horiz.png ./HelpFiles/Overlay_Toolbox/Layers/Settings_icon.png +./HelpFiles/Overlay_Toolbox/Media/Media.html +./HelpFiles/Overlay_Toolbox/Media/Media.png ./HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html ./HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png ./HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png +./HelpFiles/Overlay_Toolbox/Thumbs.db ./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Color-Vol-Surf_Outline.png ./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Tab-Vol-Surf_Outline.png ./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html @@ -172,12 +202,7 @@ ./HelpFiles/Scenes_Window/Create_New_Scene_File/Thumbs.db ./HelpFiles/Scenes_Window/Scenes_Window.html ./HelpFiles/Scenes_Window/Scenes_Window.png -./HelpFiles/Select_Brainordinate_Window/CiftiFileRow.png -./HelpFiles/Select_Brainordinate_Window/Indentify_Brainordinate.png -./HelpFiles/Select_Brainordinate_Window/Select_Brainordinate_Window.html -./HelpFiles/Splash_Screen/Splash_Screen.html -./HelpFiles/Splash_Screen/Splash_screen.png -./HelpFiles/Splash_Screen/Thumbs.db +./HelpFiles/Scenes_Window/Thumbs.db ./HelpFiles/Toolbar/AllToolbar.png ./HelpFiles/Toolbar/Clipping/Clipping.html ./HelpFiles/Toolbar/Clipping/Clipping_Planes.png @@ -190,6 +215,7 @@ ./HelpFiles/Toolbar/MatrixChartToolbar.png ./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html ./HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png +./HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing_toolbar.png ./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_edit.png ./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html ./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Edit_Border_Properties.png @@ -205,11 +231,15 @@ ./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Edit_Focus_box.png ./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_edit.png ./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html +./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Thumbs.db ./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png +./HelpFiles/Toolbar/Mode/Foci_Creation/Thumbs.db ./HelpFiles/Toolbar/Mode/Foci_Tools.png ./HelpFiles/Toolbar/Mode/Mode.html ./HelpFiles/Toolbar/Mode/Thumbs.db ./HelpFiles/Toolbar/Mode/Volume_Tools.png +./HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.html +./HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.png ./HelpFiles/Toolbar/MontageToolbar.png ./HelpFiles/Toolbar/SurfaceToolbar.png ./HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html @@ -223,9 +253,13 @@ ./HelpFiles/WBwindow_Horiz_Labels.png ./HelpFiles/WindowForScene.png ./HelpFiles/Workbench_Window/Active_Tab/Active_Tab.html +./HelpFiles/Workbench_Window/Active_Tab/Thumbs.db ./HelpFiles/Workbench_Window/Active_Tab/Viewing_Tabs.png +./HelpFiles/Workbench_Window/Thumbs.db +./HelpFiles/Workbench_Window/Tooltip/Thumbs.db ./HelpFiles/Workbench_Window/Tooltip/Tooltip.html ./HelpFiles/Workbench_Window/Tooltip/Tooltip.png +./HelpFiles/Workbench_Window/Viewing_Area/Thumbs.db ./HelpFiles/Workbench_Window/Viewing_Area/Viewing_Area.html ./HelpFiles/Workbench_Window/Viewing_Area/WBwindow_Horiz_Labels.png ./HelpFiles/Workbench_Window/Viewing_Tabs/Thumbs.db @@ -237,13 +271,17 @@ ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_Brainordinate_Icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Information_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Macro_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Movie_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Overlay_TB_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Scenes_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Thumbs.db ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Toolbar_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/ToolTips_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/WindowForScene.tiff ./HelpFiles/Workbench_Window/Workbench_Window.html +./OLD_help_resources.qrc ./update_resources.sh diff -Nru connectome-workbench-1.4.2/src/Resources/Help/OLD_help_resources.qrc connectome-workbench-1.5.0/src/Resources/Help/OLD_help_resources.qrc --- connectome-workbench-1.4.2/src/Resources/Help/OLD_help_resources.qrc 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/OLD_help_resources.qrc 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,283 @@ + + +./help_resources.qrc +./HelpFiles/Annotations/Annotations_1.4.2.fld/image001.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image002.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image003.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image004.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image005.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image006.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image007.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image008.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image009.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image010.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image011.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image012.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image013.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image014.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image015.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image016.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image017.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image018.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image019.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image020.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image021.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image022.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image023.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image024.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image025.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image026.png +./HelpFiles/Annotations/Annotations_1.4.2.fld/image027.png +./HelpFiles/Annotations/Annotations_Guide.html +./HelpFiles/Best_Practices_Guides/Annotation_and_Scenes_Best_Practices.html +./HelpFiles/ChartingOverview/Charting_Overview.html +./HelpFiles/ChartingOverview/HistogramChart.png +./HelpFiles/ChartingOverview/HistogramControl.png +./HelpFiles/ChartingOverview/LinesChart.png +./HelpFiles/ChartingOverview/LineSeriesChart.png +./HelpFiles/ChartingOverview/LineSeriesHistory.png +./HelpFiles/ChartingOverview/MatrixChart.png +./HelpFiles/Features_Toolbox/Annotations/Annotations.fld/filelist.xml +./HelpFiles/Features_Toolbox/Annotations/Annotations.fld/image001.png +./HelpFiles/Features_Toolbox/Annotations/Annotations.fld/themedata.thmx +./HelpFiles/Features_Toolbox/Annotations/Annotations.html +./HelpFiles/Features_Toolbox/Annotations/annotations.png +./HelpFiles/Features_Toolbox/Annotations/substitutions.png +./HelpFiles/Features_Toolbox/Borders/Borders.html +./HelpFiles/Features_Toolbox/Borders/Borders_Attributes.png +./HelpFiles/Features_Toolbox/Borders/Borders_Selection.png +./HelpFiles/Features_Toolbox/Borders_Attributes.png +./HelpFiles/Features_Toolbox/Features_TB_icon.png +./HelpFiles/Features_Toolbox/Features_Toolbox.html +./HelpFiles/Features_Toolbox/Fibers/Fibers.html +./HelpFiles/Features_Toolbox/Foci/Foci.html +./HelpFiles/Features_Toolbox/Foci/Foci_Attributes.png +./HelpFiles/Features_Toolbox/Foci/Foci_Selection.png +./HelpFiles/Features_Toolbox/Images/Images.html +./HelpFiles/Features_Toolbox/Images/Images_Attributes.png +./HelpFiles/Features_Toolbox/Images/Images_Selection.png +./HelpFiles/Features_Toolbox/Labels/Labels.html +./HelpFiles/Features_Toolbox/Labels/Labels_Attributes.png +./HelpFiles/Features_Toolbox/Labels/Labels_Selection.png +./HelpFiles/Glossary/Brainordinate/Brainordinate.html +./HelpFiles/Glossary/CIFTI/CIFTI.html +./HelpFiles/Glossary/ConnectomeDB/ConnectomeDB.html +./HelpFiles/Glossary/ConnectomeDB/ConnectomeDB_login.png +./HelpFiles/Glossary/ConnectomeDB/Thumbs.db +./HelpFiles/Glossary/GIFTI/GIFTI.html +./HelpFiles/Glossary/Grayordinate/Grayordinate.html +./HelpFiles/Glossary/NIFTI/NIFTI.html +./HelpFiles/Glossary/Scene/Scene.html +./HelpFiles/Glossary/Specification_File/Spec_File.png +./HelpFiles/Glossary/Specification_File/Specification_File.html +./HelpFiles/Glossary/Specification_File/Thumbs.db +./HelpFiles/Glossary/Whiteordinate/Whiteordinate.html +./HelpFiles/Identify_Brainordinate_Window/CiftiFileRow.png +./HelpFiles/Identify_Brainordinate_Window/Identify_Brainordinate_Window.html +./HelpFiles/Identify_Brainordinate_Window/Indentify_Brainordinate.png +./HelpFiles/Information_Window/Information_Window.html +./HelpFiles/Information_Window/Information_Window.png +./HelpFiles/Information_Window/Properties/Information_Properties.png +./HelpFiles/Information_Window/Properties/Properties.html +./HelpFiles/Macros/MacrosWorkbench.fld/image001.png +./HelpFiles/Macros/MacrosWorkbench.fld/image002.png +./HelpFiles/Macros/MacrosWorkbench.fld/image003.png +./HelpFiles/Macros/MacrosWorkbench.fld/image004.png +./HelpFiles/Macros/MacrosWorkbench.fld/image005.png +./HelpFiles/Macros/MacrosWorkbench.fld/image006.png +./HelpFiles/Macros/MacrosWorkbench.html +./HelpFiles/Menus/Data_Menu/Data_Menu.html +./HelpFiles/Menus/Data_Menu/Project_Foci_Dialog.png +./HelpFiles/Menus/Data_Menu/Thumbs.db +./HelpFiles/Menus/Develop_Menu/Develop_Menu.html +./HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/filelist.xml +./HelpFiles/Menus/Edit_Menu/Edit_Menu.fld/themedata.thmx +./HelpFiles/Menus/Edit_Menu/Edit_Menu.html +./HelpFiles/Menus/Edit_Menu/Edit_Menu.png +./HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control.html +./HelpFiles/Menus/File_Menu/Animation_Control/Animation_Control_dialog.png +./HelpFiles/Menus/File_Menu/Animation_Control/Thumbs.db +./HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image.html +./HelpFiles/Menus/File_Menu/Capture_Image/Capture_Image_dialog.png +./HelpFiles/Menus/File_Menu/Capture_Image/File_menu.png +./HelpFiles/Menus/File_Menu/Capture_Image/Thumbs.db +./HelpFiles/Menus/File_Menu/File_Menu.html +./HelpFiles/Menus/File_Menu/File_menu.png +./HelpFiles/Menus/File_Menu/Preferences/Preferences.html +./HelpFiles/Menus/File_Menu/Preferences/Preferences_Colors.png +./HelpFiles/Menus/File_Menu/Preferences/Preferences_ID.png +./HelpFiles/Menus/File_Menu/Preferences/Preferences_Misc.png +./HelpFiles/Menus/File_Menu/Preferences/Preferences_NewTabs.png +./HelpFiles/Menus/File_Menu/Preferences/Preferences_OpenGL.png +./HelpFiles/Menus/File_Menu/Preferences/Preferences_Vol.png +./HelpFiles/Menus/File_Menu/Preferences/Thumbs.db +./HelpFiles/Menus/File_Menu/Save-Manage_Files/Gear_symbol.png +./HelpFiles/Menus/File_Menu/Save-Manage_Files/Reload_symbol.png +./HelpFiles/Menus/File_Menu/Save-Manage_Files/Remove_symbol.png +./HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files.html +./HelpFiles/Menus/File_Menu/Save-Manage_Files/Save-Manage_Files_dialog.png +./HelpFiles/Menus/File_Menu/Save-Manage_Files/Thumbs.db +./HelpFiles/Menus/File_Menu/Save-Manage_Files/Upload_symbol.png +./HelpFiles/Menus/File_Menu/Thumbs.db +./HelpFiles/Menus/Help_Menu/Bug_Report_box.png +./HelpFiles/Menus/Help_Menu/Help_box.png +./HelpFiles/Menus/Help_Menu/Help_button.png +./HelpFiles/Menus/Help_Menu/Help_Menu.html +./HelpFiles/Menus/Help_Menu/Help_Menu.png +./HelpFiles/Menus/Help_Menu/Help_Search.png +./HelpFiles/Menus/Help_Menu/Workbench_Help.png +./HelpFiles/Menus/Macro_Menu/Macro_Menu.html +./HelpFiles/Menus/Menus.html +./HelpFiles/Menus/Surface_Menu/Surface_Menu.html +./HelpFiles/Menus/Surface_Menu/Surface_Properties.png +./HelpFiles/Menus/Surface_Menu/Volume_Interaction_Surfaces.png +./HelpFiles/Menus/Thumbs.db +./HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.html +./HelpFiles/Menus/View_Menu/Gaps_And_Margins/Gaps_And_Margins.png +./HelpFiles/Menus/View_Menu/Thumbs.db +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/RightClickPopUpMenu.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/TabBar_Tab_Menu.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_AutomaticGrid.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_Manual.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Toolbar_Arrange_Menu.png +./HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Window_Tile_Editing_Toolbar.png +./HelpFiles/Menus/View_Menu/View_Menu.html +./HelpFiles/Menus/View_Menu/View_Menu.png +./HelpFiles/Menus/Volume_Menu/Volume_Menu.html +./HelpFiles/Menus/wb_view_Menu/Thumbs.db +./HelpFiles/Menus/wb_view_Menu/wb_view_Menu.html +./HelpFiles/Menus/wb_view_Menu/wb_view_Menu.png +./HelpFiles/Menus/Window_Menu/IdentifyBrainordinateDialog.png +./HelpFiles/Menus/Window_Menu/Rename_tab.png +./HelpFiles/Menus/Window_Menu/Thumbs.db +./HelpFiles/Menus/Window_Menu/Window_Menu.html +./HelpFiles/Menus/Window_Menu/Window_Menu.png +./HelpFiles/Menus/Window_Menu.png +./HelpFiles/Mouse_Controls/Mouse_Controls.html +./HelpFiles/Mouse_Controls/RightClick_Surface.png +./HelpFiles/Mouse_Controls/RightClick_Volume.png +./HelpFiles/Mouse_Controls/Thumbs.db +./HelpFiles/Open_Spec_File_Dialog/gear-button.png +./HelpFiles/Open_Spec_File_Dialog/Open_Spec.png +./HelpFiles/Open_Spec_File_Dialog/Open_Spec_File_Dialog.html +./HelpFiles/Open_Spec_File_Dialog/Thumbs.db +./HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html +./HelpFiles/Overlay_Toolbox/Chart_Layers/DynLinesChartingLayers.png +./HelpFiles/Overlay_Toolbox/Chart_Layers/HistogramChartingLayers.png +./HelpFiles/Overlay_Toolbox/Chart_Layers/LineChartingLayers.png +./HelpFiles/Overlay_Toolbox/Chart_Layers/LinesChartingLayers.png +./HelpFiles/Overlay_Toolbox/Chart_Layers/MatrixChartLayers.png +./HelpFiles/Overlay_Toolbox/Charting/Charting.html +./HelpFiles/Overlay_Toolbox/Charting/Charting_History.png +./HelpFiles/Overlay_Toolbox/Charting/Charting_Loading.png +./HelpFiles/Overlay_Toolbox/Charting/Thumbs.db +./HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html +./HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png +./HelpFiles/Overlay_Toolbox/Information/Information.html +./HelpFiles/Overlay_Toolbox/Information/Information.png +./HelpFiles/Overlay_Toolbox/Layers/Add_Layer.png +./HelpFiles/Overlay_Toolbox/Layers/Colorbar.png +./HelpFiles/Overlay_Toolbox/Layers/Layers.html +./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_and_Map_Settings.html +./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Overlay_Toolbox_horiz.png +./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Palette_Settings.png +./HelpFiles/Overlay_Toolbox/Layers/Overlay_and_Map_Settings/Settings_icon.png +./HelpFiles/Overlay_Toolbox/Layers/Overlay_Toolbox_horiz.png +./HelpFiles/Overlay_Toolbox/Layers/Settings_icon.png +./HelpFiles/Overlay_Toolbox/Media/Media.html +./HelpFiles/Overlay_Toolbox/Media/Media.png +./HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html +./HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png +./HelpFiles/Overlay_Toolbox/Overlay_Toolbox_vert.png +./HelpFiles/Overlay_Toolbox/Thumbs.db +./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Color-Vol-Surf_Outline.png +./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Tab-Vol-Surf_Outline.png +./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.html +./HelpFiles/Overlay_Toolbox/Vol-Surf_Outline/Vol-Surf_Outline.png +./HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene.png +./HelpFiles/Scenes_Window/Create_New_Scene_File/Create_New_Scene_File.html +./HelpFiles/Scenes_Window/Create_New_Scene_File/Scenes button.png +./HelpFiles/Scenes_Window/Create_New_Scene_File/Thumbs.db +./HelpFiles/Scenes_Window/Scenes_Window.html +./HelpFiles/Scenes_Window/Scenes_Window.png +./HelpFiles/Scenes_Window/Thumbs.db +./HelpFiles/Toolbar/AllToolbar.png +./HelpFiles/Toolbar/Clipping/Clipping.html +./HelpFiles/Toolbar/Clipping/Clipping_Planes.png +./HelpFiles/Toolbar/Clipping/Thumbs.db +./HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html +./HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png +./HelpFiles/Toolbar/HistogramLineChartToolbar.png +./HelpFiles/Toolbar/LineSeriesChartOldToolbar.png +./HelpFiles/Toolbar/MatrixChartOldToolbar.png +./HelpFiles/Toolbar/MatrixChartToolbar.png +./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html +./HelpFiles/Toolbar/Mode/Border_Drawing/Border_drawing.png +./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_edit.png +./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html +./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Edit_Border_Properties.png +./HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Thumbs.db +./HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Border_ROI.png +./HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_Region_of_Interest.png +./HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Create_ROI.html +./HelpFiles/Toolbar/Mode/Border_Drawing/Create_ROI/Thumbs.db +./HelpFiles/Toolbar/Mode/Border_Drawing/Thumbs.db +./HelpFiles/Toolbar/Mode/Border_Tools.png +./HelpFiles/Toolbar/Mode/Foci_Creation/Create_Focus.png +./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html +./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Edit_Focus_box.png +./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_edit.png +./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html +./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Thumbs.db +./HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png +./HelpFiles/Toolbar/Mode/Foci_Creation/Thumbs.db +./HelpFiles/Toolbar/Mode/Foci_Tools.png +./HelpFiles/Toolbar/Mode/Mode.html +./HelpFiles/Toolbar/Mode/Thumbs.db +./HelpFiles/Toolbar/Mode/Volume_Tools.png +./HelpFiles/Toolbar/MontageToolbar.png +./HelpFiles/Toolbar/SurfaceToolbar.png +./HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html +./HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png +./HelpFiles/Toolbar/Thumbs.db +./HelpFiles/Toolbar/Toolbar.html +./HelpFiles/Toolbar/Toolbar_icon.png +./HelpFiles/Toolbar/Volume_ID_button.png +./HelpFiles/Toolbar/VolumeToolbar.png +./HelpFiles/Toolbar/VolumeView.png +./HelpFiles/WBwindow_Horiz_Labels.png +./HelpFiles/WindowForScene.png +./HelpFiles/Workbench_Window/Active_Tab/Active_Tab.html +./HelpFiles/Workbench_Window/Active_Tab/Thumbs.db +./HelpFiles/Workbench_Window/Active_Tab/Viewing_Tabs.png +./HelpFiles/Workbench_Window/Thumbs.db +./HelpFiles/Workbench_Window/Tooltip/Thumbs.db +./HelpFiles/Workbench_Window/Tooltip/Tooltip.html +./HelpFiles/Workbench_Window/Tooltip/Tooltip.png +./HelpFiles/Workbench_Window/Viewing_Area/Thumbs.db +./HelpFiles/Workbench_Window/Viewing_Area/Viewing_Area.html +./HelpFiles/Workbench_Window/Viewing_Area/WBwindow_Horiz_Labels.png +./HelpFiles/Workbench_Window/Viewing_Tabs/Thumbs.db +./HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.html +./HelpFiles/Workbench_Window/Viewing_Tabs/Viewing_Tabs.png +./HelpFiles/Workbench_Window/WBwindow_Horiz_Labels.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Features_TB_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Help_button.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_Brainordinate_Icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Identify_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Information_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Macro_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Movie_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Overlay_TB_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Scenes_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Thumbs.db +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Toolbar_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/ToolTips_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/WindowForScene.tiff +./HelpFiles/Workbench_Window/Workbench_Window.html +./update_resources.sh + + diff -Nru connectome-workbench-1.4.2/src/Resources/Help/update_resources.sh connectome-workbench-1.5.0/src/Resources/Help/update_resources.sh --- connectome-workbench-1.4.2/src/Resources/Help/update_resources.sh 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Resources/Help/update_resources.sh 2021-02-16 19:46:47.000000000 +0000 @@ -1,11 +1,11 @@ #!/bin/sh -rm -rf OLD_HelpFiles -mv HelpFiles OLD_HelpFiles +#rm -rf OLD_HelpFiles +#mv HelpFiles OLD_HelpFiles -cp -r /mnt/myelin/shared/WB_Tutorial/WB_1.3_Help ./HelpFiles +#cp -r /mnt/myelin/shared/WB_Tutorial/WB_1.3_Help ./HelpFiles -rm -rf OLD_HelpFiles +#rm -rf OLD_HelpFiles rcc -project -o help_resources.qrc diff -Nru connectome-workbench-1.4.2/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx connectome-workbench-1.5.0/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx --- connectome-workbench-1.4.2/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -69,6 +69,11 @@ m_sceneAssistant->addArray("m_colorBackgroundSurface", m_colors.m_colorBackgroundSurface, 3, 0); + m_sceneAssistant->addArray("m_colorForegroundMedia", + m_colors.m_colorForegroundMedia, 3, 255); + m_sceneAssistant->addArray("m_colorBackgroundMedia", + m_colors.m_colorBackgroundMedia, 3, 0); + m_sceneAssistant->addArray("m_colorForegroundVolume", m_colors.m_colorForegroundVolume, 3, 255); m_sceneAssistant->addArray("m_colorBackgroundVolume", diff -Nru connectome-workbench-1.4.2/src/Scenes/CMakeLists.txt connectome-workbench-1.5.0/src/Scenes/CMakeLists.txt --- connectome-workbench-1.4.2/src/Scenes/CMakeLists.txt 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/CMakeLists.txt 2021-02-16 19:46:47.000000000 +0000 @@ -64,6 +64,7 @@ ScenePathNameArray.h ScenePrimitive.h ScenePrimitiveArray.h +SceneRestoreWarningCodesEnum.h SceneSaxReader.h SceneString.h SceneStringArray.h @@ -77,6 +78,7 @@ SceneXmlStreamReader.h SceneXmlStreamWriter.h SceneableInterface.h +TileTabsBrowserTabGeometrySceneHelper.h BackgroundAndForegroundColorsSceneHelper.cxx DisplayGroupAndTabItemHelper.cxx @@ -108,6 +110,7 @@ ScenePathNameArray.cxx ScenePrimitive.cxx ScenePrimitiveArray.cxx +SceneRestoreWarningCodesEnum.cxx SceneSaxReader.cxx SceneString.cxx SceneStringArray.cxx @@ -118,6 +121,7 @@ SceneXmlStreamBase.cxx SceneXmlStreamReader.cxx SceneXmlStreamWriter.cxx +TileTabsBrowserTabGeometrySceneHelper.cxx ) TARGET_LINK_LIBRARIES(Scenes ${CARET_QT5_LINK}) diff -Nru connectome-workbench-1.4.2/src/Scenes/SceneAttributes.cxx connectome-workbench-1.5.0/src/Scenes/SceneAttributes.cxx --- connectome-workbench-1.4.2/src/Scenes/SceneAttributes.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/SceneAttributes.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -23,6 +23,8 @@ #include "SceneAttributes.h" #undef __SCENE_ATTRIBUTES_DECLARE__ +#include "CaretAssert.h" + using namespace caret; @@ -370,4 +372,37 @@ m_useSceneForgroundAndBackgroundColorsFlag = status; } +/** + * Set a scene restore warning code. This may occur when there is an incompatibiity with older scenes that can be detected. + * @param warningCode + * The warning code. + */ +void +SceneAttributes::setSceneRestoreWarningCode(const SceneRestoreWarningCodesEnum::Enum warningCode) const +{ + m_sceneWarningCodes.insert(warningCode); +} + +/** + * @return A message descrbing any warning encountered while loading a scene. Empty if no warnings. + */ +AString +SceneAttributes::getSceneLoadWarningMessage() const +{ + AString loadMessage; + + for (auto swc : m_sceneWarningCodes) { + loadMessage.appendWithNewLine(""); + loadMessage.appendWithNewLine(SceneRestoreWarningCodesEnum::toDescriptiveMessage(swc)); + } + if ( ! loadMessage.isEmpty()) { + loadMessage.appendWithNewLine(""); + loadMessage.appendWithNewLine("Compare the Scene Preview Image to contents of main window to find differences."); + loadMessage.appendWithNewLine(""); + loadMessage.appendWithNewLine("Note: Warnings are produced for all tabs including those not visible (tile tabs off)."); + } + + return loadMessage; +} + diff -Nru connectome-workbench-1.4.2/src/Scenes/SceneAttributes.h connectome-workbench-1.5.0/src/Scenes/SceneAttributes.h --- connectome-workbench-1.4.2/src/Scenes/SceneAttributes.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/SceneAttributes.h 2021-02-16 19:46:47.000000000 +0000 @@ -21,8 +21,10 @@ */ /*LICENSE_END*/ +#include #include "CaretObject.h" +#include "SceneRestoreWarningCodesEnum.h" #include "SceneTypeEnum.h" namespace caret { @@ -94,6 +96,10 @@ void setUseSceneForegroundAndBackgroundColors(const bool status); + void setSceneRestoreWarningCode(const SceneRestoreWarningCodesEnum::Enum warningCode) const; + + AString getSceneLoadWarningMessage() const; + private: SceneAttributes& operator=(const SceneAttributes&); @@ -127,6 +133,8 @@ mutable AString m_errorMessage; + mutable std::set m_sceneWarningCodes; + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/Scenes/Scene.cxx connectome-workbench-1.5.0/src/Scenes/Scene.cxx --- connectome-workbench-1.4.2/src/Scenes/Scene.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/Scene.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -239,11 +239,7 @@ m_hasFilesWithRemotePaths = false; m_sceneInfo = new SceneInfo(); - static int counter = 1; - const AString macroGroupName("SceneFile_" - + AString::number(counter)); - m_macroGroup.reset(new WuQMacroGroup(macroGroupName)); - m_macroGroup->clearModified(); + initializeMacroGroup(); } Scene::Scene(const Scene& rhs) @@ -252,6 +248,12 @@ m_sceneAttributes = new SceneAttributes(*(rhs.m_sceneAttributes)); m_hasFilesWithRemotePaths = rhs.m_hasFilesWithRemotePaths; m_sceneInfo = new SceneInfo(*(rhs.m_sceneInfo)); + + initializeMacroGroup(); + if (rhs.m_macroGroup) { + m_macroGroup->appendMacroGroup(rhs.m_macroGroup.get()); + } + for (std::vector::const_iterator iter = rhs.m_sceneClasses.begin(); iter != rhs.m_sceneClasses.end(); ++iter) { m_sceneClasses.push_back(new SceneClass(**iter)); @@ -275,6 +277,19 @@ } /** + * Initialize the macro group + */ +void +Scene::initializeMacroGroup() +{ + static int counter = 1; + const AString macroGroupName("SceneFile_" + + AString::number(counter)); + m_macroGroup.reset(new WuQMacroGroup(macroGroupName)); + m_macroGroup->clearModified(); +} + +/** * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. */ std::vector diff -Nru connectome-workbench-1.4.2/src/Scenes/Scene.h connectome-workbench-1.5.0/src/Scenes/Scene.h --- connectome-workbench-1.4.2/src/Scenes/Scene.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/Scene.h 2021-02-16 19:46:47.000000000 +0000 @@ -103,6 +103,8 @@ private: + void initializeMacroGroup(); + /** Attributes of the scene*/ SceneAttributes* m_sceneAttributes; diff -Nru connectome-workbench-1.4.2/src/Scenes/SceneRestoreWarningCodesEnum.cxx connectome-workbench-1.5.0/src/Scenes/SceneRestoreWarningCodesEnum.cxx --- connectome-workbench-1.4.2/src/Scenes/SceneRestoreWarningCodesEnum.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/SceneRestoreWarningCodesEnum.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,399 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __SCENE_RESTORE_WARNING_CODES_ENUM_DECLARE__ +#include "SceneRestoreWarningCodesEnum.h" +#undef __SCENE_RESTORE_WARNING_CODES_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::SceneRestoreWarningCodesEnum + * \brief Contains code that identify detectable warnings in scenes + * + * Contains code that identify detectable warnings in scenes. + * An example is old chart two matrices that contains translation/zooming. Changes were made + * that removed use of translation and zooming and replaced it with adjusting X&Y axes. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_sceneRestoreWarningCodesEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void sceneRestoreWarningCodesEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "SceneRestoreWarningCodesEnum.h" + * + * Instatiate: + * m_sceneRestoreWarningCodesEnumComboBox = new EnumComboBoxTemplate(this); + * m_sceneRestoreWarningCodesEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_sceneRestoreWarningCodesEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(sceneRestoreWarningCodesEnumComboBoxItemActivated())); + * + * Update the selection: + * m_sceneRestoreWarningCodesEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const SceneRestoreWarningCodesEnum::Enum VARIABLE = m_sceneRestoreWarningCodesEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +SceneRestoreWarningCodesEnum::SceneRestoreWarningCodesEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +SceneRestoreWarningCodesEnum::~SceneRestoreWarningCodesEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +SceneRestoreWarningCodesEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(SceneRestoreWarningCodesEnum(CHART_TWO_MATRIX_TRANSFORM, + "CHART_TWO_MATRIX_TRANSFORM", + "Chart Two Matrix Transform")); + + enumData.push_back(SceneRestoreWarningCodesEnum(CHART_TWO_MATRIX_CELL_WIDTH_HEIGHT, + "CHART_TWO_MATRIX_CELL_WIDTH_HEIGHT", + "Chart Two Matrix Cell Width Height")); + +} + + +/** + * @return A message descrbing any warning encountered while loading a scene. Empty if no warnings. + */ +AString +SceneRestoreWarningCodesEnum::toDescriptiveMessage(const Enum enumValue) +{ + AString msg; + switch (enumValue) { + case SceneRestoreWarningCodesEnum::CHART_TWO_MATRIX_CELL_WIDTH_HEIGHT: + msg = ("Warning: A Chart Matrix view is incorrect because matrix cell/height scaling is no longer supported. " + "Solution: Adjust the range of the matrix axes to correct the view of the matrix."); + break; + case SceneRestoreWarningCodesEnum::CHART_TWO_MATRIX_TRANSFORM: + msg = ("Warning: A Chart Matrix view is incorrect because panning/zooming is no longer supported for matrices. " + "Solution: Adjust the range of the matrix axes to correct the view of the matrix."); + break; + } + + return msg; +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const SceneRestoreWarningCodesEnum* +SceneRestoreWarningCodesEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const SceneRestoreWarningCodesEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +SceneRestoreWarningCodesEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const SceneRestoreWarningCodesEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +SceneRestoreWarningCodesEnum::Enum +SceneRestoreWarningCodesEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneRestoreWarningCodesEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneRestoreWarningCodesEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type SceneRestoreWarningCodesEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +SceneRestoreWarningCodesEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const SceneRestoreWarningCodesEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +SceneRestoreWarningCodesEnum::Enum +SceneRestoreWarningCodesEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneRestoreWarningCodesEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneRestoreWarningCodesEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type SceneRestoreWarningCodesEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +SceneRestoreWarningCodesEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const SceneRestoreWarningCodesEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +SceneRestoreWarningCodesEnum::Enum +SceneRestoreWarningCodesEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneRestoreWarningCodesEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneRestoreWarningCodesEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type SceneRestoreWarningCodesEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +SceneRestoreWarningCodesEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +SceneRestoreWarningCodesEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(SceneRestoreWarningCodesEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +SceneRestoreWarningCodesEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(SceneRestoreWarningCodesEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff -Nru connectome-workbench-1.4.2/src/Scenes/SceneRestoreWarningCodesEnum.h connectome-workbench-1.5.0/src/Scenes/SceneRestoreWarningCodesEnum.h --- connectome-workbench-1.4.2/src/Scenes/SceneRestoreWarningCodesEnum.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/SceneRestoreWarningCodesEnum.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,106 @@ +#ifndef __SCENE_RESTORE_WARNING_CODES_ENUM_H__ +#define __SCENE_RESTORE_WARNING_CODES_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2020 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class SceneRestoreWarningCodesEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Chart two matrices that contain obsolete translation/zooming */ + CHART_TWO_MATRIX_TRANSFORM, + /** Chart two matrices that contain obsolete cell width/height */ + CHART_TWO_MATRIX_CELL_WIDTH_HEIGHT + }; + + + ~SceneRestoreWarningCodesEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + + static AString toDescriptiveMessage(const Enum enumValue); + +private: + SceneRestoreWarningCodesEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const SceneRestoreWarningCodesEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __SCENE_RESTORE_WARNING_CODES_ENUM_DECLARE__ +std::vector SceneRestoreWarningCodesEnum::enumData; +bool SceneRestoreWarningCodesEnum::initializedFlag = false; +int32_t SceneRestoreWarningCodesEnum::integerCodeCounter = 0; +#endif // __SCENE_RESTORE_WARNING_CODES_ENUM_DECLARE__ + +} // namespace +#endif //__SCENE_RESTORE_WARNING_CODES_ENUM_H__ diff -Nru connectome-workbench-1.4.2/src/Scenes/SceneXmlStreamReader.cxx connectome-workbench-1.5.0/src/Scenes/SceneXmlStreamReader.cxx --- connectome-workbench-1.4.2/src/Scenes/SceneXmlStreamReader.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/SceneXmlStreamReader.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -102,6 +102,7 @@ if (scene == NULL) { return; } + m_scene = scene; m_filename = sceneFileName; @@ -318,6 +319,10 @@ pathName->setValueToAbsolutePath(m_filename, xmlReader.readElementText()); sceneObject = pathName; + + if (DataFile::isFileOnNetwork(pathName->stringValue())) { + m_scene->setHasFilesWithRemotePaths(true); + } } break; case SceneObjectDataTypeEnum::SCENE_STRING: diff -Nru connectome-workbench-1.4.2/src/Scenes/SceneXmlStreamReader.h connectome-workbench-1.5.0/src/Scenes/SceneXmlStreamReader.h --- connectome-workbench-1.4.2/src/Scenes/SceneXmlStreamReader.h 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/SceneXmlStreamReader.h 2021-02-16 19:46:47.000000000 +0000 @@ -67,6 +67,8 @@ AString m_filename; + Scene* m_scene = NULL; + // ADD_NEW_MEMBERS_HERE }; diff -Nru connectome-workbench-1.4.2/src/Scenes/SceneXmlStreamWriter.cxx connectome-workbench-1.5.0/src/Scenes/SceneXmlStreamWriter.cxx --- connectome-workbench-1.4.2/src/Scenes/SceneXmlStreamWriter.cxx 2020-01-07 23:24:23.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/SceneXmlStreamWriter.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -271,7 +271,7 @@ objectMap->getName()); const std::map& sceneMap = objectMap->getMap(); - for (const auto iter : sceneMap) { + for (const auto& iter : sceneMap) { const int32_t key = iter.first; const QString keyString(QString::number(key)); diff -Nru connectome-workbench-1.4.2/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.cxx connectome-workbench-1.5.0/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.cxx --- connectome-workbench-1.4.2/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.cxx 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.cxx 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,157 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __TILE_TABS_BROWSER_TAB_GEOMETRY_SCENE_HELPER_DECLARE__ +#include "TileTabsBrowserTabGeometrySceneHelper.h" +#undef __TILE_TABS_BROWSER_TAB_GEOMETRY_SCENE_HELPER_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" +#include "TileTabsBrowserTabGeometry.h" + +using namespace caret; + + + +/** + * \class caret::TileTabsBrowserTabGeometrySceneHelper + * \brief Helps with saving/restoring tile tabs geometry from scene + * \ingroup Scenes + */ + +/** + * Constructor. + * + * @param geometry + * Tile tabs geometry saved to or restored from scene + */ +TileTabsBrowserTabGeometrySceneHelper::TileTabsBrowserTabGeometrySceneHelper(TileTabsBrowserTabGeometry* geometry) +: CaretObject(), +m_geometry(geometry) +{ + CaretAssert(m_geometry); + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + + m_sceneAssistant->add("m_displayFlag", + &m_geometry->m_displayFlag); + m_sceneAssistant->add("m_tabIndex", + &m_geometry->m_tabIndex); + m_sceneAssistant->add("m_minX", + &m_geometry->m_minX); + m_sceneAssistant->add("m_maxX", + &m_geometry->m_maxX); + m_sceneAssistant->add("m_minY", + &m_geometry->m_minY); + m_sceneAssistant->add("m_maxY", + &m_geometry->m_maxY); + m_sceneAssistant->add("m_stackingOrder", + &m_geometry->m_stackingOrder); + m_sceneAssistant->add("m_backgroundType", + &m_geometry->m_backgroundType); +} + +/** + * Destructor. + */ +TileTabsBrowserTabGeometrySceneHelper::~TileTabsBrowserTabGeometrySceneHelper() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +TileTabsBrowserTabGeometrySceneHelper::toString() const +{ + return "TileTabsBrowserTabGeometrySceneHelper"; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +TileTabsBrowserTabGeometrySceneHelper::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "TileTabsBrowserTabGeometrySceneHelper", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +TileTabsBrowserTabGeometrySceneHelper::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + if ( ! sceneClass->getPrimitive("m_displayFlag")) { + m_geometry->m_displayFlag = true; + } + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + +/** + * @return True if the geometry was restored from the scene, else false. + */ +bool +TileTabsBrowserTabGeometrySceneHelper::wasRestoredFromScene() const +{ + return m_wasRestoredFromSceneFlag; +} + diff -Nru connectome-workbench-1.4.2/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.h connectome-workbench-1.5.0/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.h --- connectome-workbench-1.4.2/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.h 1970-01-01 00:00:00.000000000 +0000 +++ connectome-workbench-1.5.0/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.h 2021-02-16 19:46:47.000000000 +0000 @@ -0,0 +1,92 @@ +#ifndef __TILE_TABS_BROWSER_TAB_GEOMETRY_SCENE_HELPER_H__ +#define __TILE_TABS_BROWSER_TAB_GEOMETRY_SCENE_HELPER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + class TileTabsBrowserTabGeometry; + + class TileTabsBrowserTabGeometrySceneHelper : public CaretObject, public SceneableInterface { + + public: + TileTabsBrowserTabGeometrySceneHelper(TileTabsBrowserTabGeometry* geometry); + + virtual ~TileTabsBrowserTabGeometrySceneHelper(); + + TileTabsBrowserTabGeometrySceneHelper(const TileTabsBrowserTabGeometrySceneHelper&) = delete; + + TileTabsBrowserTabGeometrySceneHelper& operator=(const TileTabsBrowserTabGeometrySceneHelper&) = delete; + + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + bool wasRestoredFromScene() const; + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + TileTabsBrowserTabGeometry* m_geometry; + + std::unique_ptr m_sceneAssistant; + + bool m_wasRestoredFromSceneFlag = false; + + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __TILE_TABS_BROWSER_TAB_GEOMETRY_SCENE_HELPER_DECLARE__ + // +#endif // __TILE_TABS_BROWSER_TAB_GEOMETRY_SCENE_HELPER_DECLARE__ + +} // namespace +#endif //__TILE_TABS_BROWSER_TAB_GEOMETRY_SCENE_HELPER_H__