diff --git a/src/Algorithms/AlgorithmAnnotationResample.cxx b/src/Algorithms/AlgorithmAnnotationResample.cxx index 028de5ccbe23bac3c48ef1cc0b85eac2b2e950f3..537f92e3432c607e95ad7ae64bffe19dee10773e 100644 --- a/src/Algorithms/AlgorithmAnnotationResample.cxx +++ b/src/Algorithms/AlgorithmAnnotationResample.cxx @@ -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 @@ AlgorithmAnnotationResample::useParameters(OperationParameters* myParams, 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 @@ AlgorithmAnnotationResample::resampleAnnotation(Annotation* ann) 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 --git a/src/Algorithms/AlgorithmCiftiAverage.cxx b/src/Algorithms/AlgorithmCiftiAverage.cxx index 61e3c9370d9463808d43a82623116ec321b236a0..93dc9483e2c1adabb50e7da9d74063b389097ab7 100644 --- a/src/Algorithms/AlgorithmCiftiAverage.cxx +++ b/src/Algorithms/AlgorithmCiftiAverage.cxx @@ -71,7 +71,7 @@ void AlgorithmCiftiAverage::useParameters(OperationParameters* myParams, Progres 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 --git a/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx b/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx index 2c29d3272b93036010584dc8b8093c56b2090e3c..8611d8ab67622610693b32fef4ae9328063a763d 100644 --- a/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx +++ b/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx @@ -145,7 +145,7 @@ void AlgorithmCiftiAverageDenseROI::useParameters(OperationParameters* myParams, 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 --git a/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx b/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx index a19afed9ea7d590487908dcbe3d579d13de81e2f..71d92cbb32512efb990af21e074e31bdfe2a6c68 100644 --- a/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx +++ b/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx @@ -146,7 +146,7 @@ void AlgorithmCiftiAverageROICorrelation::useParameters(OperationParameters* myP 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 --git a/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx b/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx index 261fc72cdb1543a52a38995ffb4222dabc42de33..f8d79385e9770ebae10d3964854b784695af34cd 100644 --- a/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx +++ b/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx @@ -72,10 +72,12 @@ OperationParameters* AlgorithmCiftiCorrelationGradient::getParameters() 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 @@ void AlgorithmCiftiCorrelationGradient::useParameters(OperationParameters* myPar { 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 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En 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 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En 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 --git a/src/Algorithms/AlgorithmCiftiDilate.cxx b/src/Algorithms/AlgorithmCiftiDilate.cxx index b90be480f96ee20729ddf5137180441f3d703c73..b18e3d0c83f409a0bf417b4ca56e39c6f4fc64f8 100644 --- a/src/Algorithms/AlgorithmCiftiDilate.cxx +++ b/src/Algorithms/AlgorithmCiftiDilate.cxx @@ -76,7 +76,7 @@ OperationParameters* AlgorithmCiftiDilate::getParameters() 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 --git a/src/Algorithms/AlgorithmCiftiExtrema.cxx b/src/Algorithms/AlgorithmCiftiExtrema.cxx index 9a3202bd0120792cb304449061587d46d72d76a1..b97a8f1ecaa159f082e29f59a87b6700f2f74fe7 100644 --- a/src/Algorithms/AlgorithmCiftiExtrema.cxx +++ b/src/Algorithms/AlgorithmCiftiExtrema.cxx @@ -30,6 +30,8 @@ #include "SurfaceFile.h" #include "VolumeFile.h" +#include + using namespace caret; using namespace std; @@ -62,11 +64,13 @@ OperationParameters* AlgorithmCiftiExtrema::getParameters() 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 @@ void AlgorithmCiftiExtrema::useParameters(OperationParameters* myParams, Progres { 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 --git a/src/Algorithms/AlgorithmCiftiGradient.cxx b/src/Algorithms/AlgorithmCiftiGradient.cxx index 2663e4e3881ec83b3f567a5a21843fa592b9856a..80dec7ac157f8b0c488b06e1641a3b9c5511cf9a 100644 --- a/src/Algorithms/AlgorithmCiftiGradient.cxx +++ b/src/Algorithms/AlgorithmCiftiGradient.cxx @@ -29,6 +29,7 @@ #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" +#include #include using namespace caret; @@ -69,10 +70,12 @@ OperationParameters* AlgorithmCiftiGradient::getParameters() 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 @@ void AlgorithmCiftiGradient::useParameters(OperationParameters* myParams, Progre { 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 --git a/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx b/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx index 77f5443f0418b0d56dc72821a97d65c57c685653..2a9dba4aac71f16951a515ed1458e9e193eb72c8 100644 --- a/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx +++ b/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx @@ -79,13 +79,13 @@ void AlgorithmCiftiLabelToBorder::useParameters(OperationParameters* myParams, P 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 --git a/src/Algorithms/AlgorithmCiftiMergeDense.cxx b/src/Algorithms/AlgorithmCiftiMergeDense.cxx index 98a591d5f0884e7c2cceceadeca500ce0ccf02ff..77255af82acc7735e80befd45fac34a24808b345 100644 --- a/src/Algorithms/AlgorithmCiftiMergeDense.cxx +++ b/src/Algorithms/AlgorithmCiftiMergeDense.cxx @@ -50,6 +50,9 @@ OperationParameters* AlgorithmCiftiMergeDense::getParameters() 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 @@ void AlgorithmCiftiMergeDense::useParameters(OperationParameters* myParams, Prog 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 @@ AlgorithmCiftiMergeDense::AlgorithmCiftiMergeDense(ProgressObject* myProgObj, co 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 @@ AlgorithmCiftiMergeDense::AlgorithmCiftiMergeDense(ProgressObject* myProgObj, co } 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 @@ AlgorithmCiftiMergeDense::AlgorithmCiftiMergeDense(ProgressObject* myProgObj, co { 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 --git a/src/Algorithms/AlgorithmCiftiMergeDense.h b/src/Algorithms/AlgorithmCiftiMergeDense.h index 0435a539f23b0c2be24d3a9d69a8553ad7ec6e82..06a461c6c8203bf21e235e554a7da736c8a4c3f0 100644 --- a/src/Algorithms/AlgorithmCiftiMergeDense.h +++ b/src/Algorithms/AlgorithmCiftiMergeDense.h @@ -35,7 +35,14 @@ namespace caret { 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 --git a/src/Algorithms/AlgorithmCiftiMergeParcels.cxx b/src/Algorithms/AlgorithmCiftiMergeParcels.cxx index c460324c6f9cff6dd04f9f7d494f2c5fc8396697..47df00171cde7e9bec8115741befed700e9e1501 100644 --- a/src/Algorithms/AlgorithmCiftiMergeParcels.cxx +++ b/src/Algorithms/AlgorithmCiftiMergeParcels.cxx @@ -63,7 +63,7 @@ void AlgorithmCiftiMergeParcels::useParameters(OperationParameters* myParams, Pr 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 --git a/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx b/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx index 9e9e0801377d4e48af254aa9ba9713f6d3b045f9..fcd60076d6539a3da04aa6472181c8a4f65c4813 100644 --- a/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx +++ b/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx @@ -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 @@ OperationParameters* AlgorithmCiftiReplaceStructure::getParameters() 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 @@ OperationParameters* AlgorithmCiftiReplaceStructure::getParameters() 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 @@ void AlgorithmCiftiReplaceStructure::useParameters(OperationParameters* myParams 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 @@ void AlgorithmCiftiReplaceStructure::useParameters(OperationParameters* myParams { 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 @@ void AlgorithmCiftiReplaceStructure::useParameters(OperationParameters* myParams { 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 @@ void AlgorithmCiftiReplaceStructure::useParameters(OperationParameters* myParams { 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* m } } -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 @@ AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* m 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 @@ AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* m 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 @@ AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* m } } 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 @@ AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* m 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* m } } -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 @@ AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* m 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 @@ AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* m 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* m } } -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 @@ AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* m 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 @@ AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* m 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 --git a/src/Algorithms/AlgorithmCiftiReplaceStructure.h b/src/Algorithms/AlgorithmCiftiReplaceStructure.h index cad639c2c8aa027e0880553c0a5edf7d3d873390..b3efa64536c8f9d187951c2cc0fe19af73efc9ff 100644 --- a/src/Algorithms/AlgorithmCiftiReplaceStructure.h +++ b/src/Algorithms/AlgorithmCiftiReplaceStructure.h @@ -33,14 +33,17 @@ namespace caret { 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 --git a/src/Algorithms/AlgorithmCiftiResample.cxx b/src/Algorithms/AlgorithmCiftiResample.cxx index 04a0322a0e2b99aad8de7e490879ae0052c72645..29302eb1f09c3751fd7ae2d10e693b1d3b5b9759 100644 --- a/src/Algorithms/AlgorithmCiftiResample.cxx +++ b/src/Algorithms/AlgorithmCiftiResample.cxx @@ -1202,7 +1202,7 @@ void AlgorithmCiftiResample::processVolume(const CiftiFile* myCiftiIn, const int } } } - } + } } else {//if we don't dilate, we can use cifti to find the used voxels if (doEdgeAdjust) { diff --git a/src/Algorithms/AlgorithmCiftiSeparate.cxx b/src/Algorithms/AlgorithmCiftiSeparate.cxx index 886c7c2cb72857129b1b76fa9686d61030800a5e..9a64a262e64a85467c570bc849ff91ea857eab6f 100644 --- a/src/Algorithms/AlgorithmCiftiSeparate.cxx +++ b/src/Algorithms/AlgorithmCiftiSeparate.cxx @@ -109,7 +109,7 @@ void AlgorithmCiftiSeparate::useParameters(OperationParameters* myParams, Progre 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 @@ void AlgorithmCiftiSeparate::useParameters(OperationParameters* myParams, Progre } 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 @@ void AlgorithmCiftiSeparate::useParameters(OperationParameters* myParams, Progre } 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 --git a/src/Algorithms/AlgorithmCiftiSmoothing.cxx b/src/Algorithms/AlgorithmCiftiSmoothing.cxx index 7cb38738e84c637ac4a099e65687a29eae0a2e6d..5ebee9e2a9a3118cf48cea11aeb00a606755e566 100644 --- a/src/Algorithms/AlgorithmCiftiSmoothing.cxx +++ b/src/Algorithms/AlgorithmCiftiSmoothing.cxx @@ -29,6 +29,8 @@ #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" +#include + using namespace caret; using namespace std; @@ -46,11 +48,13 @@ OperationParameters* AlgorithmCiftiSmoothing::getParameters() { 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 @@ void AlgorithmCiftiSmoothing::useParameters(OperationParameters* myParams, Progr 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 --git a/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx b/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx index 65b6e62b911f23cf3d413ffe2aa03c9d6375bd1d..1258396697df05ff63389466fb01a125c8ad6cf8 100644 --- a/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx +++ b/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx @@ -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 @@ AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(Progres 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 + { + CaretPointer myDist = mySurf->getSignedDistanceHelper(); +#pragma omp CARET_FOR 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 = 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 (int64_t j = 0; j < myDims[1]; ++j) + for (ijk[1] = 0; ijk[1] < myDims[1]; ++ijk[1]) { - for (int64_t i = 0; i < myDims[0]; ++i) + for (ijk[0] = 0; ijk[0] < myDims[0]; ++ijk[0]) { - Vector3D voxCoord; - myVolOut->indexToSpace(i, j, k, voxCoord); - int32_t ret = mySurf->closestNode(voxCoord, exactLim); - if (ret != -1) + if (volMarked[myVolOut->getIndex(ijk)] != 0) { - volMarked[myVolOut->getIndex(i, j, k)] = 1; + 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]) +#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) { - for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) + 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[0] = imin; ijk[0] < imax; ++ijk[0]) + for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) { - myVolOut->indexToSpace(ijk, tempvec); - tempvec -= nodeCoord; - if (tempvec.length() <= exactLim) + 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 @@ AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(Progres { 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 @@ AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(Progres 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 @@ AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(Progres 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 @@ AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(Progres } 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 @@ AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(Progres }//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 @@ AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(Progres 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 @@ AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(Progres } 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 --git a/src/Algorithms/AlgorithmLabelToBorder.cxx b/src/Algorithms/AlgorithmLabelToBorder.cxx index 23627ab98c5fd72cdc2e56271bff09eb4afcf07a..996d5f45b805c16910e98840d3bcd9f42bab98ff 100644 --- a/src/Algorithms/AlgorithmLabelToBorder.cxx +++ b/src/Algorithms/AlgorithmLabelToBorder.cxx @@ -93,7 +93,7 @@ AlgorithmLabelToBorder::AlgorithmLabelToBorder(ProgressObject* myProgObj, const 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 --git a/src/Algorithms/AlgorithmMetricDilate.cxx b/src/Algorithms/AlgorithmMetricDilate.cxx index 7a9cdd90c2115ef3b89566cc1fb0e28224ec7c2c..77231506b51cf008eb9ed98beba64ccc06fe8bee 100644 --- a/src/Algorithms/AlgorithmMetricDilate.cxx +++ b/src/Algorithms/AlgorithmMetricDilate.cxx @@ -77,7 +77,7 @@ OperationParameters* AlgorithmMetricDilate::getParameters() 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 --git a/src/Algorithms/AlgorithmMetricExtrema.cxx b/src/Algorithms/AlgorithmMetricExtrema.cxx index a6a59fe0394c47047708995976afd9f02540ca5c..349c41533474fa38a3875714bc527932da891a58 100644 --- a/src/Algorithms/AlgorithmMetricExtrema.cxx +++ b/src/Algorithms/AlgorithmMetricExtrema.cxx @@ -27,6 +27,7 @@ #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" + #include #include #include @@ -56,7 +57,8 @@ OperationParameters* AlgorithmMetricExtrema::getParameters() 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 @@ void AlgorithmMetricExtrema::useParameters(OperationParameters* myParams, Progre { 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 --git a/src/Algorithms/AlgorithmMetricGradient.cxx b/src/Algorithms/AlgorithmMetricGradient.cxx index cbe900f2221def29f24fc9880a4d711345d68eac..cc4ba030e79cf77e4865f6edde28b33fc1be904a 100644 --- a/src/Algorithms/AlgorithmMetricGradient.cxx +++ b/src/Algorithms/AlgorithmMetricGradient.cxx @@ -58,7 +58,8 @@ OperationParameters* AlgorithmMetricGradient::getParameters() 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 @@ void AlgorithmMetricGradient::useParameters(OperationParameters* myParams, Progr 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 --git a/src/Algorithms/AlgorithmMetricRegression.cxx b/src/Algorithms/AlgorithmMetricRegression.cxx index 7edf944def84c2b49a1825af3b4b8758affe92fa..28a4aab279a5399128eebf433b498633fa9c7f26 100644 --- a/src/Algorithms/AlgorithmMetricRegression.cxx +++ b/src/Algorithms/AlgorithmMetricRegression.cxx @@ -90,7 +90,7 @@ void AlgorithmMetricRegression::useParameters(OperationParameters* myParams, Pro } } 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 @@ void AlgorithmMetricRegression::useParameters(OperationParameters* myParams, Pro } 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 --git a/src/Algorithms/AlgorithmMetricSmoothing.cxx b/src/Algorithms/AlgorithmMetricSmoothing.cxx index 64539633e786f8a1e7f2f5cfdb50a5f36ad16397..d707535c212d853eab0c6ca7b5b9caa3d33b76aa 100644 --- a/src/Algorithms/AlgorithmMetricSmoothing.cxx +++ b/src/Algorithms/AlgorithmMetricSmoothing.cxx @@ -27,6 +27,7 @@ #include "PaletteColorMapping.h" #include "SurfaceFile.h" #include "TopologyHelper.h" + #include using namespace caret; @@ -49,10 +50,12 @@ OperationParameters* AlgorithmMetricSmoothing::getParameters() 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 @@ void AlgorithmMetricSmoothing::useParameters(OperationParameters* myParams, Prog 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 --git a/src/Algorithms/AlgorithmMetricTFCE.cxx b/src/Algorithms/AlgorithmMetricTFCE.cxx index 9785407091adca4fc5055be6a2975ba7e2121e10..16746654aee26c4786051c6f695d6ae181c8c99d 100644 --- a/src/Algorithms/AlgorithmMetricTFCE.cxx +++ b/src/Algorithms/AlgorithmMetricTFCE.cxx @@ -57,7 +57,8 @@ OperationParameters* AlgorithmMetricTFCE::getParameters() 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 @@ void AlgorithmMetricTFCE::useParameters(OperationParameters* myParams, ProgressO { 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 --git a/src/Algorithms/AlgorithmSurfaceAverage.cxx b/src/Algorithms/AlgorithmSurfaceAverage.cxx index 8a40ba1492a64ed7dcb19e303b014e658e597e7d..2e96428c4b87be214fe07ac3d5b14e513d7cff49 100644 --- a/src/Algorithms/AlgorithmSurfaceAverage.cxx +++ b/src/Algorithms/AlgorithmSurfaceAverage.cxx @@ -70,7 +70,7 @@ void AlgorithmSurfaceAverage::useParameters(OperationParameters* myParams, Progr { 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 --git a/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx b/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx index 66142b1757f6d694be496d2c59ed33ea2f1b133c..ab8f827beb10ce3a50373c967c8cfd2b00ff5589 100644 --- a/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx +++ b/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx @@ -52,7 +52,7 @@ OperationParameters* AlgorithmSurfaceCortexLayer::getParameters() 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 --git a/src/Algorithms/AlgorithmSurfaceDistortion.cxx b/src/Algorithms/AlgorithmSurfaceDistortion.cxx index 6fbc716e230c6946f0d2ac3338a55bebbd68528a..d8a34e95619ef904071dc6e42b4eb23a7f052165 100644 --- a/src/Algorithms/AlgorithmSurfaceDistortion.cxx +++ b/src/Algorithms/AlgorithmSurfaceDistortion.cxx @@ -55,7 +55,8 @@ OperationParameters* AlgorithmSurfaceDistortion::getParameters() 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 @@ void AlgorithmSurfaceDistortion::useParameters(OperationParameters* myParams, Pr { 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 --git a/src/Algorithms/AlgorithmTemplate.cxx.txt b/src/Algorithms/AlgorithmTemplate.cxx.txt index e6d29286a1dc115baead77af871525a5108ce498..fc1d05c389799f781990bf163d7eb00afea2d3d0 100644 --- a/src/Algorithms/AlgorithmTemplate.cxx.txt +++ b/src/Algorithms/AlgorithmTemplate.cxx.txt @@ -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 --git a/src/Algorithms/AlgorithmTemplate.h.txt b/src/Algorithms/AlgorithmTemplate.h.txt index a069992c675366e6a0b48fb7080e334ce27568aa..14cdfb947a908ed9024065af46f31525f8c6f62a 100644 --- a/src/Algorithms/AlgorithmTemplate.h.txt +++ b/src/Algorithms/AlgorithmTemplate.h.txt @@ -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 --git a/src/Algorithms/AlgorithmVolumeAffineResample.cxx b/src/Algorithms/AlgorithmVolumeAffineResample.cxx index 344829ba6e02317aaf7ca884b8f2410d38ca6d85..abd15d63c721155388de361effeccd19d783e163 100644 --- a/src/Algorithms/AlgorithmVolumeAffineResample.cxx +++ b/src/Algorithms/AlgorithmVolumeAffineResample.cxx @@ -36,7 +36,7 @@ AString AlgorithmVolumeAffineResample::getCommandSwitch() AString AlgorithmVolumeAffineResample::getShortDescription() { - return "RESAMPLE VOLUME USING AFFINE TRANSFORM"; + return "DEPRECATED: use -volume-resample"; } OperationParameters* AlgorithmVolumeAffineResample::getParameters() @@ -58,7 +58,8 @@ OperationParameters* AlgorithmVolumeAffineResample::getParameters() 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 --git a/src/Algorithms/AlgorithmVolumeDilate.cxx b/src/Algorithms/AlgorithmVolumeDilate.cxx index 35acbb4ce45e6a242523fe84e93b516d5ca1b5f0..7e2888e27dcc2b61485f7b6526674b74c0fc01f4 100644 --- a/src/Algorithms/AlgorithmVolumeDilate.cxx +++ b/src/Algorithms/AlgorithmVolumeDilate.cxx @@ -70,7 +70,7 @@ OperationParameters* AlgorithmVolumeDilate::getParameters() 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 --git a/src/Algorithms/AlgorithmVolumeExtrema.cxx b/src/Algorithms/AlgorithmVolumeExtrema.cxx index 31002326c4566fc10c48eedebd8098934e4eb4ca..e20602eaee8752509238a61191cda1ae4d042db2 100644 --- a/src/Algorithms/AlgorithmVolumeExtrema.cxx +++ b/src/Algorithms/AlgorithmVolumeExtrema.cxx @@ -25,6 +25,7 @@ #include "CaretLogger.h" #include "CaretOMP.h" #include "VolumeFile.h" + #include #include #include @@ -52,7 +53,8 @@ OperationParameters* AlgorithmVolumeExtrema::getParameters() 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 @@ void AlgorithmVolumeExtrema::useParameters(OperationParameters* myParams, Progre { 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 --git a/src/Algorithms/AlgorithmVolumeGradient.cxx b/src/Algorithms/AlgorithmVolumeGradient.cxx index ac28e06e0b9cfed3822583ed8fd1f7f86446d5ad..72b0cab9e3319deae39cada5857f249a7f0e10f4 100644 --- a/src/Algorithms/AlgorithmVolumeGradient.cxx +++ b/src/Algorithms/AlgorithmVolumeGradient.cxx @@ -51,7 +51,8 @@ OperationParameters* AlgorithmVolumeGradient::getParameters() 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 @@ void AlgorithmVolumeGradient::useParameters(OperationParameters* myParams, Progr 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 --git a/src/Algorithms/AlgorithmVolumeParcelResampling.cxx b/src/Algorithms/AlgorithmVolumeParcelResampling.cxx index d6bb354e02a59e5657ea6d7496332e681aea7b30..5edee68ae2283948af2c79997172e38055a7c2e6 100644 --- a/src/Algorithms/AlgorithmVolumeParcelResampling.cxx +++ b/src/Algorithms/AlgorithmVolumeParcelResampling.cxx @@ -25,6 +25,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "Vector3D.h" + #include #include #include @@ -54,12 +55,14 @@ OperationParameters* AlgorithmVolumeParcelResampling::getParameters() 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 @@ void AlgorithmVolumeParcelResampling::useParameters(OperationParameters* myParam 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 @@ void AlgorithmVolumeParcelResampling::resample(LevelProgress& myProgress, const 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 @@ void AlgorithmVolumeParcelResampling::resampleFixZeros(LevelProgress& myProgress 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 --git a/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx b/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx index 2b6ea2192552d2ff08d731665773aaed38821afa..cb17ee7ff93409902bc87e80804d539d321d3c6e 100644 --- a/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx +++ b/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx @@ -25,6 +25,7 @@ #include "CaretLogger.h" #include "Vector3D.h" #include "CaretOMP.h" + #include #include #include @@ -54,10 +55,12 @@ OperationParameters* AlgorithmVolumeParcelResamplingGeneric::getParameters() 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 @@ void AlgorithmVolumeParcelResamplingGeneric::useParameters(OperationParameters* 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 --git a/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx b/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx index 788924fd39baf5b1936dfd761a8d2b68cf0189c1..942b816471005487a2ce6475a53fcd3b44ef9a2e 100644 --- a/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx +++ b/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx @@ -22,6 +22,7 @@ #include "AlgorithmException.h" #include "VolumeFile.h" #include "AlgorithmVolumeSmoothing.h" + #include #include @@ -45,10 +46,12 @@ OperationParameters* AlgorithmVolumeParcelSmoothing::getParameters() 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 @@ void AlgorithmVolumeParcelSmoothing::useParameters(OperationParameters* myParams 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 --git a/src/Algorithms/AlgorithmVolumeResample.cxx b/src/Algorithms/AlgorithmVolumeResample.cxx new file mode 100644 index 0000000000000000000000000000000000000000..accd67e8471f37ca1135c111118a76a8410b42f9 --- /dev/null +++ b/src/Algorithms/AlgorithmVolumeResample.cxx @@ -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 --git a/src/Algorithms/AlgorithmVolumeResample.h b/src/Algorithms/AlgorithmVolumeResample.h new file mode 100644 index 0000000000000000000000000000000000000000..540b86e01dd8325021bcf5458a10e4482e052726 --- /dev/null +++ b/src/Algorithms/AlgorithmVolumeResample.h @@ -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 --git a/src/Algorithms/AlgorithmVolumeSmoothing.cxx b/src/Algorithms/AlgorithmVolumeSmoothing.cxx index ae9540f17ac9a4fdec0991248420edc73a053944..728f39f930520b5e875bcb5ebaa760c99024ca87 100644 --- a/src/Algorithms/AlgorithmVolumeSmoothing.cxx +++ b/src/Algorithms/AlgorithmVolumeSmoothing.cxx @@ -25,6 +25,7 @@ #include "CaretLogger.h" #include "CaretOMP.h" #include "CaretAssert.h" + #include using namespace caret; @@ -48,10 +49,12 @@ OperationParameters* AlgorithmVolumeSmoothing::getParameters() 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 @@ void AlgorithmVolumeSmoothing::useParameters(OperationParameters* myParams, Prog { 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 --git a/src/Algorithms/AlgorithmVolumeTFCE.cxx b/src/Algorithms/AlgorithmVolumeTFCE.cxx index e598aeefdfb6022b3c31bf7e54152da6d8417239..00e3af334842795ee9ce4095b719e5bd531af365 100644 --- a/src/Algorithms/AlgorithmVolumeTFCE.cxx +++ b/src/Algorithms/AlgorithmVolumeTFCE.cxx @@ -54,7 +54,8 @@ OperationParameters* AlgorithmVolumeTFCE::getParameters() 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 @@ void AlgorithmVolumeTFCE::useParameters(OperationParameters* myParams, ProgressO { 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 --git a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx index 050eb6e4dc22edc01a0a3e2eba7eec2578d0c715..10e6919c18c57e1d5e0275bf26b60668c3eab1ee 100644 --- a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx +++ b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx @@ -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 @@ OperationParameters* AlgorithmVolumeToSurfaceMapping::getParameters() 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 @@ OperationParameters* AlgorithmVolumeToSurfaceMapping::getParameters() 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 @@ OperationParameters* AlgorithmVolumeToSurfaceMapping::getParameters() "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 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam 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 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam 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 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam 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 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam 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 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* //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 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* 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 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* } } +//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 --git a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h index 485147ec7504fae2f52ef8bea3416878dc6a60e3..0be79740565fb15103c69f279c472da03bdce5a3 100644 --- a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h +++ b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h @@ -37,7 +37,7 @@ namespace caret { 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,9 +54,13 @@ namespace caret { 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(); diff --git a/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx b/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx index 9ea0bf0ac83de8a15c8dc5bc25a4f46c862ab872..cd5560153daeca75b3f516df9612f1b54ac4d8e6 100644 --- a/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx +++ b/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx @@ -37,7 +37,7 @@ AString AlgorithmVolumeWarpfieldResample::getCommandSwitch() AString AlgorithmVolumeWarpfieldResample::getShortDescription() { - return "RESAMPLE VOLUME USING WARPFIELD"; + return "DEPRECATED: use -volume-resample"; } OperationParameters* AlgorithmVolumeWarpfieldResample::getParameters() @@ -57,7 +57,8 @@ OperationParameters* AlgorithmVolumeWarpfieldResample::getParameters() 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 --git a/src/Algorithms/CMakeLists.txt b/src/Algorithms/CMakeLists.txt index ea6411e498e97973daafddf9bcbcaf7e35d57c99..dfb4d32aaaef7c0d8f7d3de496011801ca06b2d2 100644 --- a/src/Algorithms/CMakeLists.txt +++ b/src/Algorithms/CMakeLists.txt @@ -134,6 +134,7 @@ AlgorithmVolumeParcelResamplingGeneric.h AlgorithmVolumeParcelSmoothing.h AlgorithmVolumeReduce.h AlgorithmVolumeRemoveIslands.h +AlgorithmVolumeResample.h AlgorithmVolumeROIsFromExtrema.h AlgorithmVolumeSmoothing.h AlgorithmVolumeTFCE.h @@ -255,6 +256,7 @@ AlgorithmVolumeParcelResamplingGeneric.cxx AlgorithmVolumeParcelSmoothing.cxx AlgorithmVolumeReduce.cxx AlgorithmVolumeRemoveIslands.cxx +AlgorithmVolumeResample.cxx AlgorithmVolumeROIsFromExtrema.cxx AlgorithmVolumeSmoothing.cxx AlgorithmVolumeTFCE.cxx diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index dac133aaad434a5b622cc2f0ae8104b6989b84ea..9ccf8f050728fe45bf8cd737003a27049cc72018 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -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 @@ Annotation::copyHelperAnnotation(const Annotation& obj) 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 @@ Annotation::clone() const 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 @@ Annotation::clone() const 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 @@ Annotation::newAnnotationOfType(const AnnotationTypeEnum::Enum annotationType, 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 @@ Annotation::newAnnotationOfType(const AnnotationTypeEnum::Enum annotationType, 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 @@ Annotation::initializeAnnotationMembers() 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 @@ Annotation::initializeAnnotationMembers() 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 @@ Annotation::initializeAnnotationMembers() 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 @@ Annotation::initializeAnnotationMembers() 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 @@ Annotation::initializeAnnotationMembers() 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 @@ Annotation::initializeAnnotationMembers() 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 @@ Annotation::initializeAnnotationMembers() 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 @@ Annotation::getTextForPasteMenuItems(AString& pasteMenuItemText, switch (m_type) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -631,6 +698,10 @@ Annotation::getTextForPasteMenuItems(AString& pasteMenuItemText, break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: { const AnnotationText* textAnn = dynamic_cast(this); @@ -683,6 +754,103 @@ Annotation::setCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordin } } +/** + * @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 @@ Annotation::changeSurfaceSpaceToTangentOffset() { 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 @@ Annotation::getSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum s { 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 @@ Annotation::initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum: return; - AnnotationTwoDimensionalShape* twoDimAnn = castToTwoDimensionalShape(); + AnnotationOneCoordinateShape* twoDimAnn = castToOneCoordinateShape(); if (twoDimAnn != NULL) { if (isInSurfaceSpaceWithTangentOffset()) { const float angle = getSurfaceSpaceWithTangentOffsetRotation(structure, @@ -932,129 +1100,6 @@ Annotation::initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum: } 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 @@ Annotation::initializeProperties() 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 @@ Annotation::initializeProperties() 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 @@ Annotation::initializeProperties() 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 @@ Annotation::initializeProperties() 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 @@ Annotation::initializeProperties() 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 @@ Annotation::textAnnotationResetName() switch (m_type) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -1920,6 +2008,10 @@ Annotation::textAnnotationResetName() 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 @@ Annotation::setDrawnInWindowStatus(const int32_t windowIndex) * 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 @@ Annotation::clearDrawnInWindowStatusForAllWindows() } } +/** + * @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 @@ Annotation::setUserDefaultLineWidthPercentage(const float lineWidthPercentage) 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 --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index 1a3e7a20a5112229895467b07aedc8e21132e41a..437b52bdda423ea523a0601cd63e3ad54c483484 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -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 @@ namespace caret { 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; } + + /** + * @return Cast to one-coordinate (NULL if NOT one-coordinate annotation + */ + virtual AnnotationOneCoordinateShape* castToOneCoordinateShape() { return NULL; } - virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const = 0; + /** + * @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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { void textAnnotationResetName(); - bool isDrawnInWindowStatus(const int32_t windowIndex); + bool isDrawnInWindowStatus(const int32_t windowIndex) const; void clearDrawnInWindowStatusForAllWindows(); @@ -421,8 +480,14 @@ namespace caret { 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 --git a/src/Annotations/AnnotationBox.cxx b/src/Annotations/AnnotationBox.cxx index aabc052a01fcf392ab2a60d9db4cfae61c37a25d..7927731030eef8a98ce5b0d2c9747edcaa013bd2 100644 --- a/src/Annotations/AnnotationBox.cxx +++ b/src/Annotations/AnnotationBox.cxx @@ -44,7 +44,7 @@ using namespace caret; * Type for attribute defaults */ AnnotationBox::AnnotationBox(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::BOX, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::BOX, attributeDefaultType) { initializeMembersAnnotationBox(); @@ -63,7 +63,7 @@ AnnotationBox::~AnnotationBox() * Object that is copied. */ AnnotationBox::AnnotationBox(const AnnotationBox& obj) -: AnnotationTwoDimensionalShape(obj) +: AnnotationOneCoordinateShape(obj) { this->initializeMembersAnnotationBox(); this->copyHelperAnnotationBox(obj); @@ -80,7 +80,7 @@ AnnotationBox& AnnotationBox::operator=(const AnnotationBox& obj) { if (this != &obj) { - AnnotationTwoDimensionalShape::operator=(obj); + AnnotationOneCoordinateShape::operator=(obj); this->copyHelperAnnotationBox(obj); } return *this; @@ -126,7 +126,7 @@ void AnnotationBox::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -148,7 +148,7 @@ void AnnotationBox::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Annotations/AnnotationBox.h b/src/Annotations/AnnotationBox.h index 26c6507a8cf6cd316961b2c7f31fda8cf8785dec..09e0dffcfcdaf8fb403c5d30a52042227a25108b 100644 --- a/src/Annotations/AnnotationBox.h +++ b/src/Annotations/AnnotationBox.h @@ -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 --git a/src/Annotations/AnnotationBrowserTab.cxx b/src/Annotations/AnnotationBrowserTab.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a80ec41013c59a1bb11a1163fb863d6cc598d165 --- /dev/null +++ b/src/Annotations/AnnotationBrowserTab.cxx @@ -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 --git a/src/Annotations/AnnotationBrowserTab.h b/src/Annotations/AnnotationBrowserTab.h new file mode 100644 index 0000000000000000000000000000000000000000..8b5e85d1eb0693db8562157ff0f43854f797f0f8 --- /dev/null +++ b/src/Annotations/AnnotationBrowserTab.h @@ -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 --git a/src/Annotations/AnnotationColorBar.cxx b/src/Annotations/AnnotationColorBar.cxx index 6f6ea1d1d5919883c3224be5ec767fa545ca8214..e48dd783118db01fe624eb0fa169d7bce51b46d7 100644 --- a/src/Annotations/AnnotationColorBar.cxx +++ b/src/Annotations/AnnotationColorBar.cxx @@ -47,7 +47,7 @@ using namespace caret; * Type for attribute defaults */ AnnotationColorBar::AnnotationColorBar(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::COLOR_BAR, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::COLOR_BAR, attributeDefaultType), AnnotationFontAttributesInterface() { @@ -89,7 +89,7 @@ AnnotationColorBar::~AnnotationColorBar() * Object that is copied. */ AnnotationColorBar::AnnotationColorBar(const AnnotationColorBar& obj) -: AnnotationTwoDimensionalShape(obj), +: AnnotationOneCoordinateShape(obj), AnnotationFontAttributesInterface() { this->copyHelperAnnotationColorBar(obj); @@ -106,7 +106,7 @@ AnnotationColorBar& AnnotationColorBar::operator=(const AnnotationColorBar& obj) { if (this != &obj) { - AnnotationTwoDimensionalShape::operator=(obj); + AnnotationOneCoordinateShape::operator=(obj); this->copyHelperAnnotationColorBar(obj); } return *this; @@ -537,7 +537,7 @@ void AnnotationColorBar::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -559,7 +559,7 @@ void AnnotationColorBar::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Annotations/AnnotationColorBar.h b/src/Annotations/AnnotationColorBar.h index 7edec95b3e881c0bc829875c5ef0c96c9e99726f..7e87302ec03e9787cd9236755f759edc6a79ef8c 100644 --- a/src/Annotations/AnnotationColorBar.h +++ b/src/Annotations/AnnotationColorBar.h @@ -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 --git a/src/Annotations/AnnotationGroup.cxx b/src/Annotations/AnnotationGroup.cxx index 005373c05cdeb6177e317bd52c37871a3366ef3b..6944aa05016f907c0257d261d029e3453260011f 100644 --- a/src/Annotations/AnnotationGroup.cxx +++ b/src/Annotations/AnnotationGroup.cxx @@ -1058,6 +1058,67 @@ AnnotationGroup::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGrou status); } +/** + * 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 diff --git a/src/Annotations/AnnotationGroup.h b/src/Annotations/AnnotationGroup.h index 87f8a2312e1797752aa522c8436518c9f5f8b7c3..830e807674bb77ab293007a5f9849a2419913691 100644 --- a/src/Annotations/AnnotationGroup.h +++ b/src/Annotations/AnnotationGroup.h @@ -129,6 +129,18 @@ namespace caret { 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 --git a/src/Annotations/AnnotationImage.cxx b/src/Annotations/AnnotationImage.cxx index 3ff626f3035383001f95f0a546007bfe3e886c82..8ea0f217ab39fdad6ac763b93f7e241aa84cef3a 100644 --- a/src/Annotations/AnnotationImage.cxx +++ b/src/Annotations/AnnotationImage.cxx @@ -46,7 +46,7 @@ using namespace caret; * Type for attribute defaults */ AnnotationImage::AnnotationImage(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::IMAGE, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::IMAGE, attributeDefaultType) { initializeMembersAnnotationImage(); @@ -67,7 +67,7 @@ AnnotationImage::~AnnotationImage() * Object that is copied. */ AnnotationImage::AnnotationImage(const AnnotationImage& obj) -: AnnotationTwoDimensionalShape(obj) +: AnnotationOneCoordinateShape(obj) { initializeMembersAnnotationImage(); this->copyHelperAnnotationImage(obj); @@ -84,7 +84,7 @@ AnnotationImage& AnnotationImage::operator=(const AnnotationImage& obj) { if (this != &obj) { - AnnotationTwoDimensionalShape::operator=(obj); + AnnotationOneCoordinateShape::operator=(obj); this->copyHelperAnnotationImage(obj); } return *this; @@ -234,7 +234,9 @@ AnnotationImage::getGraphicsPrimitive() const 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 @@ AnnotationImage::getGraphicsPrimitive() const 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 @@ AnnotationImage::setVertexBounds(const float bottomLeft[3], 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 @@ void AnnotationImage::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -318,7 +323,7 @@ void AnnotationImage::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Annotations/AnnotationImage.h b/src/Annotations/AnnotationImage.h index 172e616ef9d13d993875dd3d29e1da1264a18456..7bda9af5c8506a247bb5facdcfb0528d12c75bc8 100644 --- a/src/Annotations/AnnotationImage.h +++ b/src/Annotations/AnnotationImage.h @@ -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 --git a/src/Annotations/AnnotationLine.cxx b/src/Annotations/AnnotationLine.cxx index f4557e234fb0ff00d0dcbf53191423c274ba2a24..6af8e998e34d0bd78b5b40383bbceb7c9e4a3b6d 100644 --- a/src/Annotations/AnnotationLine.cxx +++ b/src/Annotations/AnnotationLine.cxx @@ -44,7 +44,7 @@ using namespace caret; * Type for attribute defaults */ AnnotationLine::AnnotationLine(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationOneDimensionalShape(AnnotationTypeEnum::LINE, +: AnnotationTwoCoordinateShape(AnnotationTypeEnum::LINE, attributeDefaultType) { initializeMembersAnnotationLine(); @@ -63,7 +63,7 @@ AnnotationLine::~AnnotationLine() * Object that is copied. */ AnnotationLine::AnnotationLine(const AnnotationLine& obj) -: AnnotationOneDimensionalShape(obj) +: AnnotationTwoCoordinateShape(obj) { this->initializeMembersAnnotationLine(); this->copyHelperAnnotationLine(obj); @@ -80,7 +80,7 @@ AnnotationLine& AnnotationLine::operator=(const AnnotationLine& obj) { if (this != &obj) { - AnnotationOneDimensionalShape::operator=(obj); + AnnotationTwoCoordinateShape::operator=(obj); this->copyHelperAnnotationLine(obj); } return *this; @@ -179,7 +179,7 @@ void AnnotationLine::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationOneDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationTwoCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -201,7 +201,7 @@ void AnnotationLine::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationOneDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationTwoCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Annotations/AnnotationLine.h b/src/Annotations/AnnotationLine.h index e17e68e3b0758b02a2e62cb44f47536a66b6ebfc..f36c1b9a2ed12c1e3fc062301e6209839707dbdc 100644 --- a/src/Annotations/AnnotationLine.h +++ b/src/Annotations/AnnotationLine.h @@ -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 --git a/src/Annotations/AnnotationMultiCoordinateShape.cxx b/src/Annotations/AnnotationMultiCoordinateShape.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b9f4373526b58a1b4f9b11cec7a4b46d624f65a2 --- /dev/null +++ b/src/Annotations/AnnotationMultiCoordinateShape.cxx @@ -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 --git a/src/Annotations/AnnotationMultiCoordinateShape.h b/src/Annotations/AnnotationMultiCoordinateShape.h new file mode 100644 index 0000000000000000000000000000000000000000..ec495a3c67fe4cb0b8b836c3a1338739d6500dc4 --- /dev/null +++ b/src/Annotations/AnnotationMultiCoordinateShape.h @@ -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 --git a/src/Annotations/AnnotationTwoDimensionalShape.cxx b/src/Annotations/AnnotationOneCoordinateShape.cxx similarity index 86% rename from src/Annotations/AnnotationTwoDimensionalShape.cxx rename to src/Annotations/AnnotationOneCoordinateShape.cxx index 53fd316153364646d9ca4483f45c3ef18ec194a1..1738b49a02f5cba757663d38800e9719bd901d87 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.cxx +++ b/src/Annotations/AnnotationOneCoordinateShape.cxx @@ -21,12 +21,13 @@ #include -#define __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ -#include "AnnotationTwoDimensionalShape.h" -#undef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ +#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" @@ -40,7 +41,7 @@ using namespace caret; /** - * \class caret::AnnotationTwoDimensionalShape + * \class caret::AnnotationOneCoordinateShape * \brief Class for annotations that are two dimensional (width and height). * \ingroup Annotations */ @@ -53,18 +54,18 @@ using namespace caret; * @param attributeDefaultType * Type for attribute defaults */ -AnnotationTwoDimensionalShape::AnnotationTwoDimensionalShape(const AnnotationTypeEnum::Enum type, +AnnotationOneCoordinateShape::AnnotationOneCoordinateShape(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : Annotation(type, attributeDefaultType) { - initializeMembersAnnotationTwoDimensionalShape(); + initializeMembersAnnotationOneCoordinateShape(); } /** * Destructor. */ -AnnotationTwoDimensionalShape::~AnnotationTwoDimensionalShape() +AnnotationOneCoordinateShape::~AnnotationOneCoordinateShape() { } @@ -73,11 +74,11 @@ AnnotationTwoDimensionalShape::~AnnotationTwoDimensionalShape() * @param obj * Object that is copied. */ -AnnotationTwoDimensionalShape::AnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj) +AnnotationOneCoordinateShape::AnnotationOneCoordinateShape(const AnnotationOneCoordinateShape& obj) : Annotation(obj) { - initializeMembersAnnotationTwoDimensionalShape(); - this->copyHelperAnnotationTwoDimensionalShape(obj); + initializeMembersAnnotationOneCoordinateShape(); + this->copyHelperAnnotationOneCoordinateShape(obj); } /** @@ -87,12 +88,12 @@ AnnotationTwoDimensionalShape::AnnotationTwoDimensionalShape(const AnnotationTwo * @return * Reference to this object. */ -AnnotationTwoDimensionalShape& -AnnotationTwoDimensionalShape::operator=(const AnnotationTwoDimensionalShape& obj) +AnnotationOneCoordinateShape& +AnnotationOneCoordinateShape::operator=(const AnnotationOneCoordinateShape& obj) { if (this != &obj) { Annotation::operator=(obj); - this->copyHelperAnnotationTwoDimensionalShape(obj); + this->copyHelperAnnotationOneCoordinateShape(obj); } return *this; } @@ -103,7 +104,7 @@ AnnotationTwoDimensionalShape::operator=(const AnnotationTwoDimensionalShape& ob * Object that is copied. */ void -AnnotationTwoDimensionalShape::copyHelperAnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj) +AnnotationOneCoordinateShape::copyHelperAnnotationOneCoordinateShape(const AnnotationOneCoordinateShape& obj) { *m_coordinate = *obj.m_coordinate; m_width = obj.m_width; @@ -115,7 +116,7 @@ AnnotationTwoDimensionalShape::copyHelperAnnotationTwoDimensionalShape(const Ann * Initialize members of this class. */ void -AnnotationTwoDimensionalShape::initializeMembersAnnotationTwoDimensionalShape() +AnnotationOneCoordinateShape::initializeMembersAnnotationOneCoordinateShape() { m_coordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); @@ -151,29 +152,11 @@ AnnotationTwoDimensionalShape::initializeMembersAnnotationTwoDimensionalShape() } } -/** - * @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() +AnnotationOneCoordinateShape* +AnnotationOneCoordinateShape::castToOneCoordinateShape() { return this; } @@ -181,8 +164,8 @@ AnnotationTwoDimensionalShape::castToTwoDimensionalShape() /** * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. */ -const AnnotationTwoDimensionalShape* -AnnotationTwoDimensionalShape::castToTwoDimensionalShape() const +const AnnotationOneCoordinateShape* +AnnotationOneCoordinateShape::castToOneCoordinateShape() const { return this; } @@ -191,7 +174,7 @@ AnnotationTwoDimensionalShape::castToTwoDimensionalShape() const * @return The coordinate for the two dimensional shape. */ AnnotationCoordinate* -AnnotationTwoDimensionalShape::getCoordinate() +AnnotationOneCoordinateShape::getCoordinate() { return m_coordinate; } @@ -200,7 +183,7 @@ AnnotationTwoDimensionalShape::getCoordinate() * @return The start coordinate for the two dimensional shape. */ const AnnotationCoordinate* -AnnotationTwoDimensionalShape::getCoordinate() const +AnnotationOneCoordinateShape::getCoordinate() const { return m_coordinate; } @@ -209,7 +192,7 @@ AnnotationTwoDimensionalShape::getCoordinate() const * @return The surface offset vector type for this annotation. */ AnnotationSurfaceOffsetVectorTypeEnum::Enum -AnnotationTwoDimensionalShape::getSurfaceOffsetVectorType() const +AnnotationOneCoordinateShape::getSurfaceOffsetVectorType() const { CaretAssert(m_coordinate); return m_coordinate->getSurfaceOffsetVectorType(); @@ -219,7 +202,7 @@ AnnotationTwoDimensionalShape::getSurfaceOffsetVectorType() const * @return Height for "two-dimensional" annotations in percentage zero to one-hundred. */ float -AnnotationTwoDimensionalShape::getHeight() const +AnnotationOneCoordinateShape::getHeight() const { return m_height; } @@ -231,7 +214,7 @@ AnnotationTwoDimensionalShape::getHeight() const * New value for height of the annotation. */ void -AnnotationTwoDimensionalShape::setHeight(const float height) +AnnotationOneCoordinateShape::setHeight(const float height) { if (height != m_height) { if (isFixedAspectRatio()) { @@ -249,7 +232,7 @@ AnnotationTwoDimensionalShape::setHeight(const float height) * @return Width for "two-dimensional" annotations in percentage zero to one-hundred. */ float -AnnotationTwoDimensionalShape::getWidth() const +AnnotationOneCoordinateShape::getWidth() const { return m_width; } @@ -261,7 +244,7 @@ AnnotationTwoDimensionalShape::getWidth() const * New value for width of the annotation. */ void -AnnotationTwoDimensionalShape::setWidth(const float width) +AnnotationOneCoordinateShape::setWidth(const float width) { if (width != m_width) { if (isFixedAspectRatio()) { @@ -279,7 +262,7 @@ AnnotationTwoDimensionalShape::setWidth(const float width) * @return The rotation angle, in degrees, clockwise, from vertical at the top (12 o'clock). */ float -AnnotationTwoDimensionalShape::getRotationAngle() const +AnnotationOneCoordinateShape::getRotationAngle() const { return m_rotationAngle; } @@ -291,7 +274,7 @@ AnnotationTwoDimensionalShape::getRotationAngle() const * New value rotation angle. */ void -AnnotationTwoDimensionalShape::setRotationAngle(const float rotationAngle) +AnnotationOneCoordinateShape::setRotationAngle(const float rotationAngle) { if (rotationAngle != m_rotationAngle) { m_rotationAngle = rotationAngle; @@ -304,7 +287,7 @@ AnnotationTwoDimensionalShape::setRotationAngle(const float rotationAngle) * @return true if modified, else false. */ bool -AnnotationTwoDimensionalShape::isModified() const +AnnotationOneCoordinateShape::isModified() const { if (Annotation::isModified()) { return true; @@ -321,7 +304,7 @@ AnnotationTwoDimensionalShape::isModified() const * Set the status to unmodified. */ void -AnnotationTwoDimensionalShape::clearModified() +AnnotationOneCoordinateShape::clearModified() { Annotation::clearModified(); @@ -336,10 +319,10 @@ AnnotationTwoDimensionalShape::clearModified() * The other annotation from which attributes are obtained. */ void -AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) +AnnotationOneCoordinateShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) { CaretAssert(otherAnnotation); - const AnnotationTwoDimensionalShape* otherTwoDim = dynamic_cast(otherAnnotation); + const AnnotationOneCoordinateShape* otherTwoDim = dynamic_cast(otherAnnotation); CaretAssert(otherTwoDim); AnnotationCoordinate* coord = getCoordinate(); @@ -355,12 +338,16 @@ AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const An setWindowIndex(otherAnnotation->getWindowIndex()); /* - * Switch color bar to manual positioning + * 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); + } } /** @@ -372,7 +359,7 @@ AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const An * True if sizing handle valid, else false. */ bool -AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const +AnnotationOneCoordinateShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const { const bool viewportFlag = (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::VIEWPORT); @@ -390,6 +377,11 @@ AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp allowsSideResizingFlag = true; allowsRotationFlag = true; break; + case AnnotationTypeEnum::BROWSER_TAB: + allowsMovingFlag = true; + allowsCornerResizingFlag = true; + allowsSideResizingFlag = true; + break; case AnnotationTypeEnum::COLOR_BAR: allowsMovingFlag = true; allowsCornerResizingFlag = true; @@ -409,6 +401,11 @@ AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp allowsSideResizingFlag = true; allowsRotationFlag = true; break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + allowsMovingFlag = true; + break; case AnnotationTypeEnum::TEXT: allowsMovingFlag = true; allowsRotationFlag = true; @@ -478,6 +475,8 @@ AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp validFlag = true; } break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } } @@ -493,7 +492,7 @@ AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp * True if the annotation was modified, else false. */ bool -AnnotationTwoDimensionalShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) +AnnotationOneCoordinateShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) { bool validFlag = false; @@ -615,6 +614,8 @@ AnnotationTwoDimensionalShape::applySpatialModificationChartSpace(const Annotati validFlag = true; } break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } if (validDxyFlag) { @@ -653,6 +654,8 @@ AnnotationTwoDimensionalShape::applySpatialModificationChartSpace(const Annotati case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } } else { @@ -688,7 +691,7 @@ AnnotationTwoDimensionalShape::applySpatialModificationChartSpace(const Annotati * True if the annotation was modified, else false. */ bool -AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, +AnnotationOneCoordinateShape::applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace) { bool badSpaceFlag = false; @@ -867,6 +870,8 @@ AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace validFlag = true; } break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } if (validDxyFlag) { @@ -905,6 +910,8 @@ AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } } else { @@ -937,7 +944,7 @@ AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace * True if the annotation was modified, else false. */ bool -AnnotationTwoDimensionalShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) +AnnotationOneCoordinateShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) { return applySpatialModificationTabOrWindowSpace(spatialModification); } @@ -952,7 +959,7 @@ AnnotationTwoDimensionalShape::applySpatialModificationSpacerTabSpace(const Anno * True if the annotation was modified, else false. */ bool -AnnotationTwoDimensionalShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) +AnnotationOneCoordinateShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) { float xyz[3]; m_coordinate->getXYZ(xyz); @@ -1212,9 +1219,38 @@ AnnotationTwoDimensionalShape::applySpatialModificationTabOrWindowSpace(const An validRotationFlag = true; } break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } - if (validCoordinatesFlag) { + 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 */ @@ -1276,6 +1312,8 @@ AnnotationTwoDimensionalShape::applySpatialModificationTabOrWindowSpace(const An case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } } else { @@ -1318,7 +1356,7 @@ AnnotationTwoDimensionalShape::applySpatialModificationTabOrWindowSpace(const An * True if rotation is valid, else false. */ bool -AnnotationTwoDimensionalShape::rotationAngleTest(const float previousMouseXYZ[3], +AnnotationOneCoordinateShape::rotationAngleTest(const float previousMouseXYZ[3], const float shapeXYZ[3], const float currentMouseXYZ[3]) const { @@ -1326,7 +1364,7 @@ AnnotationTwoDimensionalShape::rotationAngleTest(const float previousMouseXYZ[3] shapeXYZ, previousMouseXYZ); - static const float MINIMUM_ANGLE = 0.025; //0.01; + static const float MINIMUM_ANGLE = 0.025; if (angle > MINIMUM_ANGLE) { return true; @@ -1344,7 +1382,7 @@ AnnotationTwoDimensionalShape::rotationAngleTest(const float previousMouseXYZ[3] * True if the annotation was modified, else false. */ bool -AnnotationTwoDimensionalShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) +AnnotationOneCoordinateShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) { if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { return false; @@ -1400,7 +1438,7 @@ AnnotationTwoDimensionalShape::applySpatialModification(const AnnotationSpatialM * Suggested change in shape (signed). */ void -AnnotationTwoDimensionalShape::getSideHandleMouseDelta(const AnnotationSizingHandleTypeEnum::Enum sizeHandle, +AnnotationOneCoordinateShape::getSideHandleMouseDelta(const AnnotationSizingHandleTypeEnum::Enum sizeHandle, const float leftToRightShapeVector[3], const float bottomToTopShapeVector[3], const float mouseDX, @@ -1445,6 +1483,8 @@ AnnotationTwoDimensionalShape::getSideHandleMouseDelta(const AnnotationSizingHan break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } float shapeVector[3]; @@ -1492,7 +1532,7 @@ AnnotationTwoDimensionalShape::getSideHandleMouseDelta(const AnnotationSizingHan * Value that is added coordinate's Y. */ void -AnnotationTwoDimensionalShape::addToXYZWithXY(float xyz[3], +AnnotationOneCoordinateShape::addToXYZWithXY(float xyz[3], const float addX, const float addY) { @@ -1519,7 +1559,7 @@ AnnotationTwoDimensionalShape::addToXYZWithXY(float xyz[3], * The top left corner of the annotation absolute bounds. */ bool -AnnotationTwoDimensionalShape::getShapeBounds(const float viewportWidth, +AnnotationOneCoordinateShape::getShapeBounds(const float viewportWidth, const float viewportHeight, const float viewportXYZ[3], float bottomLeftOut[3], @@ -1531,36 +1571,75 @@ AnnotationTwoDimensionalShape::getShapeBounds(const float viewportWidth, * 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); + 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; } @@ -1577,7 +1656,7 @@ AnnotationTwoDimensionalShape::getShapeBounds(const float viewportWidth, * Height of space. */ void -AnnotationTwoDimensionalShape::setWidthAndHeightFromBounds(const float xyzOne[3], +AnnotationOneCoordinateShape::setWidthAndHeightFromBounds(const float xyzOne[3], const float xyzTwo[3], const float spaceWidth, const float spaceHeight) @@ -1615,7 +1694,7 @@ AnnotationTwoDimensionalShape::setWidthAndHeightFromBounds(const float xyzOne[3] * be valid (non-NULL). */ void -AnnotationTwoDimensionalShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +AnnotationOneCoordinateShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { m_sceneAssistant->saveMembers(sceneAttributes, @@ -1635,7 +1714,7 @@ AnnotationTwoDimensionalShape::saveSubClassDataToScene(const SceneAttributes* sc * this interface. Will NEVER be NULL. */ void -AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +AnnotationOneCoordinateShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, @@ -1649,7 +1728,7 @@ AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(const SceneAttribute * Default for newly created text annotations. */ void -AnnotationTwoDimensionalShape::setUserDefaultHeight(const float height) +AnnotationOneCoordinateShape::setUserDefaultHeight(const float height) { s_userDefaultHeight = height; } @@ -1661,7 +1740,7 @@ AnnotationTwoDimensionalShape::setUserDefaultHeight(const float height) * Default for newly created annotations. */ void -AnnotationTwoDimensionalShape::setUserDefaultWidth(const float width) +AnnotationOneCoordinateShape::setUserDefaultWidth(const float width) { s_userDefaultWidth = width; } diff --git a/src/Annotations/AnnotationTwoDimensionalShape.h b/src/Annotations/AnnotationOneCoordinateShape.h similarity index 80% rename from src/Annotations/AnnotationTwoDimensionalShape.h rename to src/Annotations/AnnotationOneCoordinateShape.h index e49c988435f77126adcb820ce9a7c06ebed3abb2..cdc9ddfe76ac529d1a8dad9d44f15218cad5b91d 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.h +++ b/src/Annotations/AnnotationOneCoordinateShape.h @@ -1,5 +1,5 @@ -#ifndef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ -#define __ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ +#ifndef __ANNOTATION_ONE_COORDINATE_SHAPE_H__ +#define __ANNOTATION_ONE_COORDINATE_SHAPE_H__ /*LICENSE_START*/ /* @@ -30,25 +30,21 @@ namespace caret { class AnnotationCoordinate; - class AnnotationTwoDimensionalShape : public Annotation { + class AnnotationOneCoordinateShape : public Annotation { public: - AnnotationTwoDimensionalShape(const AnnotationTypeEnum::Enum type, + AnnotationOneCoordinateShape(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); - virtual ~AnnotationTwoDimensionalShape(); + virtual ~AnnotationOneCoordinateShape(); - AnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj); + AnnotationOneCoordinateShape(const AnnotationOneCoordinateShape& obj); - AnnotationTwoDimensionalShape& operator=(const AnnotationTwoDimensionalShape& obj); + AnnotationOneCoordinateShape& operator=(const AnnotationOneCoordinateShape& obj); - virtual AnnotationOneDimensionalShape* castToOneDimensionalShape() override; + virtual AnnotationOneCoordinateShape* castToOneCoordinateShape() override; - virtual const AnnotationOneDimensionalShape* castToOneDimensionalShape() const override; - - virtual AnnotationTwoDimensionalShape* castToTwoDimensionalShape() override; - - virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const override; + virtual const AnnotationOneCoordinateShape* castToOneCoordinateShape() const override; AnnotationCoordinate* getCoordinate(); @@ -118,9 +114,9 @@ namespace caret { const SceneClass* sceneClass); private: - void copyHelperAnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj); + void copyHelperAnnotationOneCoordinateShape(const AnnotationOneCoordinateShape& obj); - void initializeMembersAnnotationTwoDimensionalShape(); + void initializeMembersAnnotationOneCoordinateShape(); void addToXYZWithXY(float xyz[3], const float addX, @@ -157,11 +153,11 @@ namespace caret { }; -#ifdef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ - float AnnotationTwoDimensionalShape::s_userDefaultWidth = 25.0; +#ifdef __ANNOTATION_ONE_COORDINATE_SHAPE_DECLARE__ + float AnnotationOneCoordinateShape::s_userDefaultWidth = 25.0; - float AnnotationTwoDimensionalShape::s_userDefaultHeight = 25.0; -#endif // __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ + float AnnotationOneCoordinateShape::s_userDefaultHeight = 25.0; +#endif // __ANNOTATION_ONE_COORDINATE_SHAPE_DECLARE__ } // namespace -#endif //__ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ +#endif //__ANNOTATION_ONE_COORDINATE_SHAPE_H__ diff --git a/src/Annotations/AnnotationOval.cxx b/src/Annotations/AnnotationOval.cxx index f8c9212e50767666edd3c3e49cfbae0197b6e4ce..9bb9969e5c4dc753729336db46507fae8bae8fce 100644 --- a/src/Annotations/AnnotationOval.cxx +++ b/src/Annotations/AnnotationOval.cxx @@ -44,7 +44,7 @@ using namespace caret; * Type for attribute defaults */ AnnotationOval::AnnotationOval(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) -: AnnotationTwoDimensionalShape(AnnotationTypeEnum::OVAL, +: AnnotationOneCoordinateShape(AnnotationTypeEnum::OVAL, attributeDefaultType) { initializeMembersAnnotationOval(); @@ -63,7 +63,7 @@ AnnotationOval::~AnnotationOval() * Object that is copied. */ AnnotationOval::AnnotationOval(const AnnotationOval& obj) -: AnnotationTwoDimensionalShape(obj) +: AnnotationOneCoordinateShape(obj) { this->initializeMembersAnnotationOval(); this->copyHelperAnnotationOval(obj); @@ -80,7 +80,7 @@ AnnotationOval& AnnotationOval::operator=(const AnnotationOval& obj) { if (this != &obj) { - AnnotationTwoDimensionalShape::operator=(obj); + AnnotationOneCoordinateShape::operator=(obj); this->copyHelperAnnotationOval(obj); } return *this; @@ -124,7 +124,7 @@ void AnnotationOval::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, + AnnotationOneCoordinateShape::saveSubClassDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -146,7 +146,7 @@ void AnnotationOval::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Annotations/AnnotationOval.h b/src/Annotations/AnnotationOval.h index 283dd2f9dd22513e7740cdb4bcf7e083a411b62e..e5450f636760123d69251ebaa8958c9c90745b1a 100644 --- a/src/Annotations/AnnotationOval.h +++ b/src/Annotations/AnnotationOval.h @@ -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 --git a/src/Annotations/AnnotationPolyLine.cxx b/src/Annotations/AnnotationPolyLine.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4406a66bec1df3642d32969530b18034402af736 --- /dev/null +++ b/src/Annotations/AnnotationPolyLine.cxx @@ -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 --git a/src/Annotations/AnnotationPolyLine.h b/src/Annotations/AnnotationPolyLine.h new file mode 100644 index 0000000000000000000000000000000000000000..de0875cccd7bdfa48514ad1a168068bed61226a8 --- /dev/null +++ b/src/Annotations/AnnotationPolyLine.h @@ -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 --git a/src/Annotations/AnnotationRedoUndoCommand.cxx b/src/Annotations/AnnotationRedoUndoCommand.cxx index f5466cc2fdf3d62765ef04142772be336380a9b8..78c73efc5eec1f9bc314874b13fb6a3514f155f5 100644 --- a/src/Annotations/AnnotationRedoUndoCommand.cxx +++ b/src/Annotations/AnnotationRedoUndoCommand.cxx @@ -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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, } } 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, + 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, } } 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, 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 @@ AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, 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 @@ AnnotationRedoUndoCommand::redo(AString& errorMessageOut) 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 @@ AnnotationRedoUndoCommand::undo(AString& errorMessageOut) 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 @@ AnnotationRedoUndoCommand::mergeWith(const CaretUndoCommand* command) 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 @@ AnnotationRedoUndoCommand::setModeCoordinateOne(const AnnotationCoordinate& coor 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 @@ AnnotationRedoUndoCommand::setModeCoordinateOneAndTwo(const AnnotationCoordinate 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 @@ AnnotationRedoUndoCommand::setModeCoordinateTwo(const AnnotationCoordinate& coor CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(redoAnnotation); AnnotationCoordinate* redoCoordinate = NULL; if (oneDimShape != NULL) { @@ -981,6 +1308,51 @@ AnnotationRedoUndoCommand::setModeCoordinateTwo(const AnnotationCoordinate& coor } } +/** + * 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. * @@ -1381,6 +1753,43 @@ AnnotationRedoUndoCommand::setModeDuplicateAnnotation(AnnotationFile* annotation 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 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, 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 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, 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 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, 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 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, } +/** + * 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 @@ AnnotationRedoUndoCommand::setModeTextCharacters(const AString& text, switch (annotation->getType()) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -1609,6 +2048,10 @@ AnnotationRedoUndoCommand::setModeTextCharacters(const AString& text, 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 @@ AnnotationRedoUndoCommand::setModeTwoDimHeight(const float newHeight, 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 @@ AnnotationRedoUndoCommand::setModeTwoDimWidth(const float newWidth, 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 @@ AnnotationRedoUndoCommand::setModeTwoDimWidth(const float newWidth, } } +/** + * 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 --git a/src/Annotations/AnnotationRedoUndoCommand.h b/src/Annotations/AnnotationRedoUndoCommand.h index c15773d4652b0030f85436c8debb0351298aec8b..f43989c8a54ad55945851c41cefffa989488cd29 100644 --- a/src/Annotations/AnnotationRedoUndoCommand.h +++ b/src/Annotations/AnnotationRedoUndoCommand.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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,9 +179,22 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx b/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx index 8ec48ec93223c199bdc9f0e1a2cb8a7d85ba5735..04681f6da52b193ef94a8f9d6723696978d66062 100644 --- a/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx +++ b/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx @@ -110,6 +110,18 @@ AnnotationRedoUndoCommandModeEnum::initialize() "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 @@ AnnotationRedoUndoCommandModeEnum::initialize() "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 @@ AnnotationRedoUndoCommandModeEnum::initialize() "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 --git a/src/Annotations/AnnotationRedoUndoCommandModeEnum.h b/src/Annotations/AnnotationRedoUndoCommandModeEnum.h index 3b8d2c84f1c7cabc64d4e5a68f37c462098473a4..0145af2e8d0efb0ee3ff725511bd48147f531519 100644 --- a/src/Annotations/AnnotationRedoUndoCommandModeEnum.h +++ b/src/Annotations/AnnotationRedoUndoCommandModeEnum.h @@ -37,6 +37,12 @@ public: 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 @@ public: 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 @@ public: DELETE_ANNOTATIONS, /** Duplicate Annotation */ DUPLICATE_ANNOTATION, + /** Duplicate Annotations */ + DUPLICATE_ANNOTATIONS, /** Group Annotations */ GROUPING_GROUP, /** Regroup Annotations */ @@ -73,6 +83,10 @@ public: 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 --git a/src/Annotations/AnnotationScaleBar.cxx b/src/Annotations/AnnotationScaleBar.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4fa8c2c6f1ed2c6b9c946681bf0fa6177a1234c4 --- /dev/null +++ b/src/Annotations/AnnotationScaleBar.cxx @@ -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 --git a/src/Annotations/AnnotationScaleBar.h b/src/Annotations/AnnotationScaleBar.h new file mode 100644 index 0000000000000000000000000000000000000000..2951acf10cea536055f52dada9af61a53e5229e8 --- /dev/null +++ b/src/Annotations/AnnotationScaleBar.h @@ -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 --git a/src/Annotations/AnnotationScaleBarTextLocationEnum.cxx b/src/Annotations/AnnotationScaleBarTextLocationEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2a2d7d7d25543bfbaac1ed439bb11a931a39d86e --- /dev/null +++ b/src/Annotations/AnnotationScaleBarTextLocationEnum.cxx @@ -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 --git a/src/Common/TileTabsConfigurationLayoutTypeEnum.h b/src/Annotations/AnnotationScaleBarTextLocationEnum.h similarity index 74% rename from src/Common/TileTabsConfigurationLayoutTypeEnum.h rename to src/Annotations/AnnotationScaleBarTextLocationEnum.h index 8a57604bdf62ac5b762b961d3399091f08aa9730..813358a4bca1a4b006f69c01b20aed91633c1adf 100644 --- a/src/Common/TileTabsConfigurationLayoutTypeEnum.h +++ b/src/Annotations/AnnotationScaleBarTextLocationEnum.h @@ -1,5 +1,5 @@ -#ifndef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ -#define __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ +#ifndef __ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_H__ +#define __ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_H__ /*LICENSE_START*/ /* @@ -28,7 +28,7 @@ namespace caret { -class TileTabsConfigurationLayoutTypeEnum { +class AnnotationScaleBarTextLocationEnum { public: /** @@ -36,13 +36,13 @@ public: */ enum Enum { /** */ - GRID, + BOTTOM, /** */ - MANUAL + RIGHT }; - ~TileTabsConfigurationLayoutTypeEnum(); + ~AnnotationScaleBarTextLocationEnum(); static AString toName(Enum enumValue); @@ -63,14 +63,14 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); private: - TileTabsConfigurationLayoutTypeEnum(const Enum enumValue, + AnnotationScaleBarTextLocationEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const TileTabsConfigurationLayoutTypeEnum* findData(const Enum enumValue); + static const AnnotationScaleBarTextLocationEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -94,11 +94,11 @@ private: 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__ +#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 //__TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ +#endif //__ANNOTATION_SCALE_BAR_TEXT_LOCATION_ENUM_H__ diff --git a/src/Annotations/AnnotationScaleBarUnitsTypeEnum.cxx b/src/Annotations/AnnotationScaleBarUnitsTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..740cdbc0c1e84e118e65e4c8af78dc7a981552c5 --- /dev/null +++ b/src/Annotations/AnnotationScaleBarUnitsTypeEnum.cxx @@ -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 --git a/src/Annotations/AnnotationScaleBarUnitsTypeEnum.h b/src/Annotations/AnnotationScaleBarUnitsTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..92ab1ca6af774a23cbffe36084977b1e37cd0c3a --- /dev/null +++ b/src/Annotations/AnnotationScaleBarUnitsTypeEnum.h @@ -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 --git a/src/Annotations/AnnotationSizingHandleTypeEnum.cxx b/src/Annotations/AnnotationSizingHandleTypeEnum.cxx index 23d2d8a3d5a9bf4b22161cb2cd5e641d05c2c6bf..9d8e0855941982ae10fb78e1545ee24e073f28b9 100644 --- a/src/Annotations/AnnotationSizingHandleTypeEnum.cxx +++ b/src/Annotations/AnnotationSizingHandleTypeEnum.cxx @@ -154,6 +154,9 @@ AnnotationSizingHandleTypeEnum::initialize() "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 --git a/src/Annotations/AnnotationSizingHandleTypeEnum.h b/src/Annotations/AnnotationSizingHandleTypeEnum.h index 38f62ba51992390d7495e7601955deddc0779031..8f13924d8e31b34e79c97c53fcf99d6c72256e0a 100644 --- a/src/Annotations/AnnotationSizingHandleTypeEnum.h +++ b/src/Annotations/AnnotationSizingHandleTypeEnum.h @@ -58,7 +58,9 @@ public: /** 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 --git a/src/Annotations/AnnotationSpatialModification.cxx b/src/Annotations/AnnotationSpatialModification.cxx index f78b65882c8bd891c49eb5ac5c1246a654c477d3..b43e85065d29f27b3b0b9782b7737c922f6960d9 100644 --- a/src/Annotations/AnnotationSpatialModification.cxx +++ b/src/Annotations/AnnotationSpatialModification.cxx @@ -55,6 +55,8 @@ using namespace caret; * 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 @@ AnnotationSpatialModification::AnnotationSpatialModification(const AnnotationSiz 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_mouseX(mouseX), m_mouseY(mouseY), m_mouseDX(mouseDX), m_mouseDY(mouseDY), +m_polyLineCoordinateIndex(polyLineCoordinateIndex), m_startOfDraggingFlag(startOfDraggingFlag) { m_chartCoordAtMouseXY.m_chartXYZValid = false; diff --git a/src/Annotations/AnnotationSpatialModification.h b/src/Annotations/AnnotationSpatialModification.h index 4699198f31d25f025600934e6d76eb4cfc491fba..f68a2631eee08e7153db4065e404c7de5dc713fd 100644 --- a/src/Annotations/AnnotationSpatialModification.h +++ b/src/Annotations/AnnotationSpatialModification.h @@ -42,6 +42,7 @@ namespace caret { 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 @@ namespace caret { const float m_mouseDY; + const int32_t m_polyLineCoordinateIndex; + const bool m_startOfDraggingFlag; ChartCoord m_chartCoordAtMouseXY; @@ -150,9 +153,10 @@ namespace caret { // 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 --git a/src/Annotations/AnnotationStackingOrderOperation.cxx b/src/Annotations/AnnotationStackingOrderOperation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e654765b87f1b20bdc3f28306354ee074e0df674 --- /dev/null +++ b/src/Annotations/AnnotationStackingOrderOperation.cxx @@ -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 --git a/src/Annotations/AnnotationStackingOrderOperation.h b/src/Annotations/AnnotationStackingOrderOperation.h new file mode 100644 index 0000000000000000000000000000000000000000..9accec176c6ffd50fdabd17128770828ac02ee03 --- /dev/null +++ b/src/Annotations/AnnotationStackingOrderOperation.h @@ -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 --git a/src/Annotations/AnnotationStackingOrderTypeEnum.cxx b/src/Annotations/AnnotationStackingOrderTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5f052bfd6dc0b3e23e3e18ae9bf06a574038de23 --- /dev/null +++ b/src/Annotations/AnnotationStackingOrderTypeEnum.cxx @@ -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 --git a/src/Annotations/AnnotationStackingOrderTypeEnum.h b/src/Annotations/AnnotationStackingOrderTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..34e4294b53e1120b4e37928677abbaee4b3323c6 --- /dev/null +++ b/src/Annotations/AnnotationStackingOrderTypeEnum.h @@ -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 --git a/src/Annotations/AnnotationText.cxx b/src/Annotations/AnnotationText.cxx index 2d37233e3236dda13a44df7ad684d5d91d895999..52b55ba0477ecd190bc57d977ec067e0c8264017 100644 --- a/src/Annotations/AnnotationText.cxx +++ b/src/Annotations/AnnotationText.cxx @@ -53,7 +53,7 @@ using namespace caret; */ 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 @@ m_fontSizeType(fontSizeType) 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 @@ AnnotationText::~AnnotationText() * Object that is copied. */ AnnotationText::AnnotationText(const AnnotationText& obj) -: AnnotationTwoDimensionalShape(obj), +: AnnotationOneCoordinateShape(obj), AnnotationFontAttributesInterface(), m_fontSizeType(obj.m_fontSizeType) { @@ -914,7 +914,7 @@ AnnotationText::copyHelperAnnotationText(const AnnotationText& obj) 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 @@ AnnotationText::applySpatialModification(const AnnotationSpatialModification& sp 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 @@ void 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 @@ void AnnotationText::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { - AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, + AnnotationOneCoordinateShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { /* diff --git a/src/Annotations/AnnotationText.h b/src/Annotations/AnnotationText.h index 16c78015d69b99b5189f22516c9c0f5edffceeea..8b6cf49e9c7c881b4e2bb994273620ad755a5a16 100644 --- a/src/Annotations/AnnotationText.h +++ b/src/Annotations/AnnotationText.h @@ -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 --git a/src/Annotations/AnnotationOneDimensionalShape.cxx b/src/Annotations/AnnotationTwoCoordinateShape.cxx similarity index 88% rename from src/Annotations/AnnotationOneDimensionalShape.cxx rename to src/Annotations/AnnotationTwoCoordinateShape.cxx index 3a1d3a032781323790d408d00458458e8aaa8668..ad1fbbf648e631488f572fcfae2f02270a3bca7e 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.cxx +++ b/src/Annotations/AnnotationTwoCoordinateShape.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ -#include "AnnotationOneDimensionalShape.h" -#undef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ +#define __ANNOTATION_TWO_COORDINATE_SHAPE_DECLARE__ +#include "AnnotationTwoCoordinateShape.h" +#undef __ANNOTATION_TWO_COORDINATE_SHAPE_DECLARE__ #include @@ -37,7 +37,7 @@ using namespace caret; /** - * \class caret::AnnotationOneDimensionalShape + * \class caret::AnnotationTwoCoordinateShape * \brief Class for annotations that are one-dimensional (lines) * \ingroup Annotations */ @@ -49,18 +49,18 @@ using namespace caret; * @param attributeDefaultType * Type for attribute defaults */ -AnnotationOneDimensionalShape::AnnotationOneDimensionalShape(const AnnotationTypeEnum::Enum type, +AnnotationTwoCoordinateShape::AnnotationTwoCoordinateShape(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : Annotation(type, attributeDefaultType) { - initializeMembersAnnotationOneDimensionalShape(); + initializeMembersAnnotationTwoCoordinateShape(); } /** * Destructor. */ -AnnotationOneDimensionalShape::~AnnotationOneDimensionalShape() +AnnotationTwoCoordinateShape::~AnnotationTwoCoordinateShape() { } @@ -69,12 +69,12 @@ AnnotationOneDimensionalShape::~AnnotationOneDimensionalShape() * @param obj * Object that is copied. */ -AnnotationOneDimensionalShape::AnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj) +AnnotationTwoCoordinateShape::AnnotationTwoCoordinateShape(const AnnotationTwoCoordinateShape& obj) : Annotation(obj) { - initializeMembersAnnotationOneDimensionalShape(); + initializeMembersAnnotationTwoCoordinateShape(); - this->copyHelperAnnotationOneDimensionalShape(obj); + this->copyHelperAnnotationTwoCoordinateShape(obj); } /** @@ -84,12 +84,12 @@ AnnotationOneDimensionalShape::AnnotationOneDimensionalShape(const AnnotationOne * @return * Reference to this object. */ -AnnotationOneDimensionalShape& -AnnotationOneDimensionalShape::operator=(const AnnotationOneDimensionalShape& obj) +AnnotationTwoCoordinateShape& +AnnotationTwoCoordinateShape::operator=(const AnnotationTwoCoordinateShape& obj) { if (this != &obj) { Annotation::operator=(obj); - this->copyHelperAnnotationOneDimensionalShape(obj); + this->copyHelperAnnotationTwoCoordinateShape(obj); } return *this; } @@ -100,7 +100,7 @@ AnnotationOneDimensionalShape::operator=(const AnnotationOneDimensionalShape& ob * Object that is copied. */ void -AnnotationOneDimensionalShape::copyHelperAnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj) +AnnotationTwoCoordinateShape::copyHelperAnnotationTwoCoordinateShape(const AnnotationTwoCoordinateShape& obj) { *m_startCoordinate = *obj.m_startCoordinate; *m_endCoordinate = *obj.m_endCoordinate; @@ -110,7 +110,7 @@ AnnotationOneDimensionalShape::copyHelperAnnotationOneDimensionalShape(const Ann * Initialize members of this class. */ void -AnnotationOneDimensionalShape::initializeMembersAnnotationOneDimensionalShape() +AnnotationTwoCoordinateShape::initializeMembersAnnotationTwoCoordinateShape() { m_startCoordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); m_endCoordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); @@ -129,8 +129,8 @@ AnnotationOneDimensionalShape::initializeMembersAnnotationOneDimensionalShape() /** * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. */ -AnnotationOneDimensionalShape* -AnnotationOneDimensionalShape::castToOneDimensionalShape() +AnnotationTwoCoordinateShape* +AnnotationTwoCoordinateShape::castToTwoCoordinateShape() { return this; } @@ -138,35 +138,17 @@ AnnotationOneDimensionalShape::castToOneDimensionalShape() /** * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. */ -const AnnotationOneDimensionalShape* -AnnotationOneDimensionalShape::castToOneDimensionalShape() const +const AnnotationTwoCoordinateShape* +AnnotationTwoCoordinateShape::castToTwoCoordinateShape() 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() +AnnotationTwoCoordinateShape::getStartCoordinate() { return m_startCoordinate; } @@ -175,7 +157,7 @@ AnnotationOneDimensionalShape::getStartCoordinate() * @return The start coordinate for the one dimensional shape. */ const AnnotationCoordinate* -AnnotationOneDimensionalShape::getStartCoordinate() const +AnnotationTwoCoordinateShape::getStartCoordinate() const { return m_startCoordinate; } @@ -184,7 +166,7 @@ AnnotationOneDimensionalShape::getStartCoordinate() const * @return The end coordinate for the one dimensional shape. */ AnnotationCoordinate* -AnnotationOneDimensionalShape::getEndCoordinate() +AnnotationTwoCoordinateShape::getEndCoordinate() { return m_endCoordinate; } @@ -193,7 +175,7 @@ AnnotationOneDimensionalShape::getEndCoordinate() * @return The end coordinate for the one dimensional shape. */ const AnnotationCoordinate* -AnnotationOneDimensionalShape::getEndCoordinate() const +AnnotationTwoCoordinateShape::getEndCoordinate() const { return m_endCoordinate; } @@ -202,7 +184,7 @@ AnnotationOneDimensionalShape::getEndCoordinate() const * @return The surface offset vector type for this annotation. */ AnnotationSurfaceOffsetVectorTypeEnum::Enum -AnnotationOneDimensionalShape::getSurfaceOffsetVectorType() const +AnnotationTwoCoordinateShape::getSurfaceOffsetVectorType() const { CaretAssert(m_startCoordinate); return m_startCoordinate->getSurfaceOffsetVectorType(); @@ -213,7 +195,7 @@ AnnotationOneDimensionalShape::getSurfaceOffsetVectorType() const * @return true if modified, else false. */ bool -AnnotationOneDimensionalShape::isModified() const +AnnotationTwoCoordinateShape::isModified() const { if (Annotation::isModified()) { return true; @@ -234,7 +216,7 @@ AnnotationOneDimensionalShape::isModified() const * Set the status to unmodified. */ void -AnnotationOneDimensionalShape::clearModified() +AnnotationTwoCoordinateShape::clearModified() { Annotation::clearModified(); @@ -250,10 +232,10 @@ AnnotationOneDimensionalShape::clearModified() * The other annotation from which attributes are obtained. */ void -AnnotationOneDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) +AnnotationTwoCoordinateShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) { CaretAssert(otherAnnotation); - const AnnotationOneDimensionalShape* otherOneDim = dynamic_cast(otherAnnotation); + const AnnotationTwoCoordinateShape* otherOneDim = dynamic_cast(otherAnnotation); CaretAssert(otherOneDim); AnnotationCoordinate* startCoord = getStartCoordinate(); @@ -281,7 +263,7 @@ AnnotationOneDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const An * Rotation angle of the annotation. */ float -AnnotationOneDimensionalShape::getRotationAngle(const float viewportWidth, +AnnotationTwoCoordinateShape::getRotationAngle(const float viewportWidth, const float viewportHeight) const { if ( ! isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { @@ -320,7 +302,7 @@ AnnotationOneDimensionalShape::getRotationAngle(const float viewportWidth, * Rotation angle for the annotation. */ void -AnnotationOneDimensionalShape::setRotationAngle(const float viewportWidth, +AnnotationTwoCoordinateShape::setRotationAngle(const float viewportWidth, const float viewportHeight, const float rotationAngle) { @@ -367,7 +349,7 @@ AnnotationOneDimensionalShape::setRotationAngle(const float viewportWidth, * True if sizing handle valid, else false. */ bool -AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const +AnnotationTwoCoordinateShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const { bool xyPlaneFlag = false; @@ -427,6 +409,8 @@ AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp validFlag = true; } break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } return validFlag; @@ -441,7 +425,7 @@ AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp * True if the annotation was modified, else false. */ bool -AnnotationOneDimensionalShape::applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification) +AnnotationTwoCoordinateShape::applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification) { bool validFlag = false; @@ -506,6 +490,8 @@ AnnotationOneDimensionalShape::applySpatialModificationSurfaceSpace(const Annota break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } @@ -525,7 +511,7 @@ AnnotationOneDimensionalShape::applySpatialModificationSurfaceSpace(const Annota * True if the annotation was modified, else false. */ bool -AnnotationOneDimensionalShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) +AnnotationTwoCoordinateShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) { return applySpatialModificationTabOrWindowSpace(spatialModification); } @@ -540,7 +526,7 @@ AnnotationOneDimensionalShape::applySpatialModificationSpacerTabSpace(const Anno * True if the annotation was modified, else false. */ bool -AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) +AnnotationTwoCoordinateShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) { float xyz1[3]; float xyz2[3]; @@ -629,7 +615,7 @@ AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const An rotationAngle += 360.0; } - CaretPointer shapeCopy(dynamic_cast(this->clone())); + CaretPointer shapeCopy(dynamic_cast(this->clone())); shapeCopy->setRotationAngle(spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, rotationAngle); @@ -643,6 +629,8 @@ AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const An validFlag = true; } break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } if (validFlag) { @@ -678,7 +666,7 @@ AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const An * True if the annotation was modified, else false. */ bool -AnnotationOneDimensionalShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) +AnnotationTwoCoordinateShape::applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification) { bool validFlag = false; @@ -725,6 +713,8 @@ AnnotationOneDimensionalShape::applySpatialModificationChartSpace(const Annotati break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } if (validFlag) { @@ -743,7 +733,7 @@ AnnotationOneDimensionalShape::applySpatialModificationChartSpace(const Annotati * True if the annotation was modified, else false. */ bool -AnnotationOneDimensionalShape::applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification) +AnnotationTwoCoordinateShape::applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification) { bool validFlag = false; @@ -780,6 +770,8 @@ AnnotationOneDimensionalShape::applySpatialModificationStereotaxicSpace(const An break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; + case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_POLY_LINE_COORDINATE: + break; } if (validFlag) { @@ -798,7 +790,7 @@ AnnotationOneDimensionalShape::applySpatialModificationStereotaxicSpace(const An * True if the annotation was modified, else false. */ bool -AnnotationOneDimensionalShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) +AnnotationTwoCoordinateShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) { if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { return false; @@ -843,7 +835,7 @@ AnnotationOneDimensionalShape::applySpatialModification(const AnnotationSpatialM * be valid (non-NULL). */ void -AnnotationOneDimensionalShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +AnnotationTwoCoordinateShape::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { m_sceneAssistant->saveMembers(sceneAttributes, @@ -863,7 +855,7 @@ AnnotationOneDimensionalShape::saveSubClassDataToScene(const SceneAttributes* sc * this interface. Will NEVER be NULL. */ void -AnnotationOneDimensionalShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +AnnotationTwoCoordinateShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, diff --git a/src/Annotations/AnnotationOneDimensionalShape.h b/src/Annotations/AnnotationTwoCoordinateShape.h similarity index 75% rename from src/Annotations/AnnotationOneDimensionalShape.h rename to src/Annotations/AnnotationTwoCoordinateShape.h index 8b40ed6f04531709ad589d3417ba31c33bc72e4a..8759d170eb7c200b80597fc5979cd9927c00318d 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.h +++ b/src/Annotations/AnnotationTwoCoordinateShape.h @@ -1,5 +1,5 @@ -#ifndef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ -#define __ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ +#ifndef __ANNOTATION_TWO_COORDINATE_SHAPE_H__ +#define __ANNOTATION_TWO_COORDINATE_SHAPE_H__ /*LICENSE_START*/ /* @@ -29,25 +29,21 @@ namespace caret { class AnnotationCoordinate; - class AnnotationOneDimensionalShape : public Annotation { + class AnnotationTwoCoordinateShape : public Annotation { public: - AnnotationOneDimensionalShape(const AnnotationTypeEnum::Enum type, + AnnotationTwoCoordinateShape(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); - virtual ~AnnotationOneDimensionalShape(); + virtual ~AnnotationTwoCoordinateShape(); - AnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj); + AnnotationTwoCoordinateShape(const AnnotationTwoCoordinateShape& obj); - AnnotationOneDimensionalShape& operator=(const AnnotationOneDimensionalShape& obj); + AnnotationTwoCoordinateShape& operator=(const AnnotationTwoCoordinateShape& obj); - virtual AnnotationOneDimensionalShape* castToOneDimensionalShape() override; + virtual AnnotationTwoCoordinateShape* castToTwoCoordinateShape() override; - virtual const AnnotationOneDimensionalShape* castToOneDimensionalShape() const override; - - virtual AnnotationTwoDimensionalShape* castToTwoDimensionalShape() override; - - virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const override; + virtual const AnnotationTwoCoordinateShape* castToTwoCoordinateShape() const override; AnnotationCoordinate* getStartCoordinate(); @@ -92,9 +88,9 @@ namespace caret { const SceneClass* sceneClass); private: - void copyHelperAnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj); + void copyHelperAnnotationTwoCoordinateShape(const AnnotationTwoCoordinateShape& obj); - void initializeMembersAnnotationOneDimensionalShape(); + void initializeMembersAnnotationTwoCoordinateShape(); bool applySpatialModificationChartSpace(const AnnotationSpatialModification& spatialModification); @@ -116,9 +112,9 @@ namespace caret { }; -#ifdef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ +#ifdef __ANNOTATION_TWO_COORDINATE_SHAPE_DECLARE__ // -#endif // __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ +#endif // __ANNOTATION_TWO_COORDINATE_SHAPE_DECLARE__ } // namespace -#endif //__ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ +#endif //__ANNOTATION_TWO_COORDINATE_SHAPE_H__ diff --git a/src/Annotations/AnnotationTypeEnum.cxx b/src/Annotations/AnnotationTypeEnum.cxx index 1f316d52155681cb5ac1cf3a99668235ec1c0f87..50ee30358ada81d7e39ed974ce9c833c5870b958 100644 --- a/src/Annotations/AnnotationTypeEnum.cxx +++ b/src/Annotations/AnnotationTypeEnum.cxx @@ -110,6 +110,10 @@ AnnotationTypeEnum::initialize() "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 @@ AnnotationTypeEnum::initialize() "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 --git a/src/Annotations/AnnotationTypeEnum.h b/src/Annotations/AnnotationTypeEnum.h index a0650752a7ee2dc95cf6a763e41c1c53ab007e31..9eba81b6f40ac09b04d5802ba43f26182625de12 100644 --- a/src/Annotations/AnnotationTypeEnum.h +++ b/src/Annotations/AnnotationTypeEnum.h @@ -37,6 +37,8 @@ public: enum Enum { /** Box */ BOX, + /** Browser Tab */ + BROWSER_TAB, /** Colorbar */ COLOR_BAR, /** Image */ @@ -45,6 +47,10 @@ public: LINE, /** Oval */ OVAL, + /** Poly Line */ + POLY_LINE, + /** Scale Bar */ + SCALE_BAR, /** Text */ TEXT }; diff --git a/src/Annotations/CMakeLists.txt b/src/Annotations/CMakeLists.txt index 7ff91220d283b9f7669d51f94ba4061db4f24616..f87babf8b81b535a512e22fdcc418eebdd4c9d9d 100644 --- a/src/Annotations/CMakeLists.txt +++ b/src/Annotations/CMakeLists.txt @@ -27,6 +27,7 @@ Annotation.h AnnotationAlignmentEnum.h AnnotationAttributesDefaultTypeEnum.h AnnotationBox.h +AnnotationBrowserTab.h AnnotationColorBar.h AnnotationColorBarNumericText.h AnnotationColorBarPositionModeEnum.h @@ -34,6 +35,7 @@ AnnotationColorBarSection.h AnnotationCoordinate.h AnnotationCoordinateSpaceEnum.h AnnotationDistributeEnum.h +AnnotationEditingSelectionInformation.h AnnotationFontAttributesInterface.h AnnotationGroup.h AnnotationGroupKey.h @@ -41,15 +43,21 @@ AnnotationGroupTypeEnum.h 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 @@ AnnotationTextFontNameEnum.h 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 @@ AnnotationColorBarSection.cxx 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 @@ AnnotationTextFontNameEnum.cxx 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 --git a/src/Annotations/EventAnnotationTextGetBounds.cxx b/src/Annotations/EventAnnotationTextGetBounds.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f33377f78b70510290fd2cfd05947d6136621859 --- /dev/null +++ b/src/Annotations/EventAnnotationTextGetBounds.cxx @@ -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 --git a/src/Annotations/EventAnnotationTextGetBounds.h b/src/Annotations/EventAnnotationTextGetBounds.h new file mode 100644 index 0000000000000000000000000000000000000000..47d24db71c9ee3a3f03e19be37253f1af778c42d --- /dev/null +++ b/src/Annotations/EventAnnotationTextGetBounds.h @@ -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 --git a/src/Annotations/EventAnnotationValidate.cxx b/src/Annotations/EventAnnotationValidate.cxx new file mode 100644 index 0000000000000000000000000000000000000000..214f97491a448aff2ffe8db0314ba2b44e51b86b --- /dev/null +++ b/src/Annotations/EventAnnotationValidate.cxx @@ -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 --git a/src/Annotations/EventAnnotationValidate.h b/src/Annotations/EventAnnotationValidate.h new file mode 100644 index 0000000000000000000000000000000000000000..b99592d3ac785a3375fab5e1d63dd009fc698c98 --- /dev/null +++ b/src/Annotations/EventAnnotationValidate.h @@ -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 --git a/src/Brain/AnnotationArrangerExecutor.cxx b/src/Brain/AnnotationArrangerExecutor.cxx index aead692c6762b9adbcada38b61b9551498a24762..1775c51ca398face5cabcd1b64602f6dfef03cf5 100644 --- a/src/Brain/AnnotationArrangerExecutor.cxx +++ b/src/Brain/AnnotationArrangerExecutor.cxx @@ -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 @@ using namespace caret; /** * 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 @@ AnnotationArrangerExecutor::~AnnotationArrangerExecutor() /** * 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 @@ AnnotationArrangerExecutor::alignAnnotations(AnnotationManager* annotationManage /** * 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 @@ AnnotationArrangerExecutor::distributeAnnotationsPrivate(const AnnotationArrange afterMoving); undoCommand->setDescription(AnnotationDistributeEnum::toGuiName(m_distribute)); - validFlag = m_annotationManager->applyCommand(undoCommand, + validFlag = m_annotationManager->applyCommand(m_userInputMode, + undoCommand, errorMessage); } @@ -452,7 +458,8 @@ AnnotationArrangerExecutor::alignAnnotationsPrivate(const AnnotationArrangerInpu afterMoving); undoCommand->setDescription(AnnotationAlignmentEnum::toGuiName(m_alignment)); - validFlag = m_annotationManager->applyCommand(undoCommand, + validFlag = m_annotationManager->applyCommand(m_userInputMode, + undoCommand, errorMessage); } @@ -520,8 +527,8 @@ AnnotationArrangerExecutor::initializeForArranging(const AnnotationArrangerInput 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 @@ AnnotationArrangerExecutor::setupAnnotationInfo(const AnnotationArrangerInputs& 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 @@ AnnotationArrangerExecutor::AnnotationInfo::moveAnnotationByXY(Annotation* annot 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 --git a/src/Brain/AnnotationArrangerExecutor.h b/src/Brain/AnnotationArrangerExecutor.h index 2d11250b41899d2800c4beac253d735fe1e7b9e5..2fc4b8fc5f76e0828ea637cc178d23cb25baf7f6 100644 --- a/src/Brain/AnnotationArrangerExecutor.h +++ b/src/Brain/AnnotationArrangerExecutor.h @@ -28,6 +28,7 @@ #include "BoundingBox.h" #include "CaretObject.h" #include "SpacerTabIndex.h" +#include "UserInputModeEnum.h" namespace caret { @@ -38,7 +39,7 @@ namespace caret { class AnnotationArrangerExecutor : public CaretObject { public: - AnnotationArrangerExecutor(); + AnnotationArrangerExecutor(const UserInputModeEnum::Enum userInputMode); virtual ~AnnotationArrangerExecutor(); @@ -138,6 +139,8 @@ namespace caret { void printAnnotationInfo(const QString& title); + const UserInputModeEnum::Enum m_userInputMode; + Mode m_mode; AnnotationManager* m_annotationManager; diff --git a/src/Brain/AnnotationManager.cxx b/src/Brain/AnnotationManager.cxx index d45af9e5a68d4d880f6653e54850749d74b3a9ae..a64832003715897e30dea70a111ea9ca87ce68e2 100644 --- a/src/Brain/AnnotationManager.cxx +++ b/src/Brain/AnnotationManager.cxx @@ -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_brain(brain) 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_brain(brain) } m_sceneAssistant = new SceneClassAssistant(); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_VALIDATE); } /** @@ -94,7 +107,8 @@ AnnotationManager::~AnnotationManager() delete m_selectionInformation[i]; } - m_annotationRedoUndoStack->clear(); + m_annotationsExceptBrowserTabsRedoUndoStack->clear(); + m_browserTabAnnotationsRedoUndoStack->clear(); delete m_sceneAssistant; } @@ -105,7 +119,8 @@ AnnotationManager::~AnnotationManager() void AnnotationManager::reset() { - m_annotationRedoUndoStack->clear(); + m_annotationsExceptBrowserTabsRedoUndoStack->clear(); + m_browserTabAnnotationsRedoUndoStack->clear(); } /** @@ -113,16 +128,22 @@ AnnotationManager::reset() * 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 @@ AnnotationManager::applyCommand(AnnotationRedoUndoCommand* command, * 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 @@ AnnotationManager::applyCommandInWindow(AnnotationRedoUndoCommand* command, /* * "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 @@ AnnotationManager::deselectAllAnnotationsForEditing(const int32_t windowIndex) 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 @@ AnnotationManager::deselectAllAnnotationsForEditing(const int32_t windowIndex) 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 @@ AnnotationManager::deselectAllAnnotationsForEditing(const int32_t windowIndex) 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 @@ AnnotationManager::getAllAnnotations() const } } - 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,9 +494,47 @@ AnnotationManager::getAllAnnotations() const 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. * @@ -467,6 +549,10 @@ AnnotationManager::getAnnotationEditingSelectionInformation(const int32_t window { 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 @@ AnnotationManager::getAnnotationEditingSelectionInformation(const int32_t window 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 @@ AnnotationManager::getAnnotationsSelectedForEditingInSpaces(const int32_t window * 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 @@ AnnotationManager::getAnnotationsSelectedForEditing(const int32_t windowIndex, } } } - } /** @@ -584,10 +682,19 @@ AnnotationManager::getAnnotationsSelectedForEditing(const int32_t windowIndex, * 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 @@ AnnotationManager::getAnnotationsSelectedForEditingIncludingLabels(const int32_t /** * 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 @@ AnnotationManager::getAnnotationsSelectedForEditingIncludingLabels(const int32_t * 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 @@ AnnotationManager::alignAnnotations(const AnnotationArrangerInputs& arrangerInpu /** * 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 @@ AnnotationManager::alignAnnotations(const AnnotationArrangerInputs& arrangerInpu * 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 @@ AnnotationManager::distributeAnnotations(const AnnotationArrangerInputs& arrange /** * 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 @@ AnnotationManager::distributeAnnotations(const AnnotationArrangerInputs& arrange * 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 @@ AnnotationManager::applyGroupingMode(const int32_t windowIndex, AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingGroupAnnotations(annotationGroupKey, annotations); - validFlag = applyCommandInWindow(command, + validFlag = applyCommandInWindow(userInputMode, + command, windowIndex, errorMessageOut); } @@ -719,7 +836,8 @@ AnnotationManager::applyGroupingMode(const int32_t windowIndex, AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingRegroupAnnotations(annotationGroupKey); - validFlag = applyCommandInWindow(command, + validFlag = applyCommandInWindow(userInputMode, + command, windowIndex, errorMessageOut); } @@ -740,7 +858,8 @@ AnnotationManager::applyGroupingMode(const int32_t windowIndex, AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingUngroupAnnotations(annotationGroupKey); - validFlag = applyCommandInWindow(command, + validFlag = applyCommandInWindow(userInputMode, + command, windowIndex, errorMessageOut); } @@ -765,6 +884,215 @@ AnnotationManager::isGroupingModeValid(const int32_t windowIndex, 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 @@ AnnotationManager::isGroupingModeValid(const int32_t windowIndex, * 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 @@ AnnotationManager::getDisplayedAnnotationFiles(EventGetDisplayedDataFiles* displ /** * @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 @@ AnnotationManager::restoreFromScene(const SceneAttributes* sceneAttributes, } +/** + * 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 --git a/src/Brain/AnnotationManager.h b/src/Brain/AnnotationManager.h index 779aeba83d4534b9156b449ff4200c5fc44eb460..ac5dedf5d9896fbc7f0d3a6d73609a1b6548dd62 100644 --- a/src/Brain/AnnotationManager.h +++ b/src/Brain/AnnotationManager.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { CaretPointer m_clipboardAnnotation; - CaretPointer m_annotationRedoUndoStack; + CaretPointer m_annotationsExceptBrowserTabsRedoUndoStack; + CaretPointer m_browserTabAnnotationsRedoUndoStack; // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index e0dd7ae874ade116b352cffce91b7c4c01cee7e0..4fd7bc311cbd8f8fd337a17b39642533e6268c12 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -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" @@ -128,18 +138,40 @@ 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 @@ Brain::Brain(const CaretPreferences* caretPreferences) 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 @@ Brain::Brain(const CaretPreferences* caretPreferences) 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,14 +231,20 @@ Brain::Brain(const CaretPreferences* caretPreferences) 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); @@ -259,6 +298,9 @@ Brain::Brain(const CaretPreferences* caretPreferences) 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 @@ Brain::Brain(const CaretPreferences* caretPreferences) m_brainordinateHighlightRegionOfInterest = new BrainordinateRegionOfInterest(); + m_palettesStandardGroup = std::make_shared(); + m_palettesStandardGroup->loadPalettes(); + + m_palettesUserCustomGroup = std::make_shared(caretPreferences); + updateChartModel(); } @@ -308,6 +355,9 @@ Brain::~Brain() if (m_wholeBrainModel != NULL) { delete m_wholeBrainModel; } + if (m_mediaModel != NULL) { + delete m_mediaModel; + } delete m_selectionManager; delete m_identificationManager; @@ -457,9 +507,6 @@ Brain::getDuplicateFileNameCounterForFileType(const DataFileTypeEnum::Enum dataF m_duplicateFileNameCounter[dataFileType] = counterValue; -// m_duplicateFileNameCounter.insert(std::make_pair(dataFileType, -// counterValue)); - return counterValue; } @@ -502,6 +549,7 @@ Brain::resetBrain(const ResetBrainKeepSceneFiles keepSceneFiles, { m_isSpecFileBeingRead = false; m_activeScene = NULL; + SessionManager::get()->resetSceneWithChartOld(); m_surfaceMatchingToAnatomicalFlag = false; @@ -539,7 +587,6 @@ Brain::resetBrain(const ResetBrainKeepSceneFiles keepSceneFiles, 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 @@ Brain::resetBrainKeepSceneFiles() * 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(); @@ -891,6 +938,57 @@ Brain::copyDisplayProperties(const int32_t sourceTabIndex, } } +/** + * 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. * @@ -3432,14 +3530,6 @@ Brain::addReadOrReloadSceneFile(const FileModeAddReadReload fileMode, 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&) { @@ -5015,6 +5105,40 @@ Brain::createModelChartTwo() } } +/** + * 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. */ @@ -5168,7 +5292,168 @@ Brain::processReloadDataFileEvent(EventDataFileReload* reloadDataFileEvent) 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 @@ Brain::updateAfterFilesAddedOrRemoved() updateVolumeSliceModel(); updateWholeBrainModel(); updateChartModel(); + updateMediaModel(); updateFiberTrajectoryMatchingFiberOrientationFiles(); } @@ -5591,14 +5877,14 @@ Brain::loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataF 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 @@ Brain::loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataF // testingAnnFile->setFileName("Testing." + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::ANNOTATION)); // addDataFile(testingAnnFile); - - - - + sortDataFilesByFileNameNoPath(); + /* * Reset the primary anatomical surfaces since they can get set * incorrectly when loading files @@ -5754,6 +6038,15 @@ Brain::loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataF ""); } +/** + * Some files are sorted by name + */ +void +Brain::sortDataFilesByFileNameNoPath() +{ + sortDataFileTypeByFileNameNoPath(m_imageFiles); +} + /** * Load files from the given spec file. * @param specFileToLoad @@ -5794,6 +6087,13 @@ Brain::loadSpecFileFromScene(const SceneAttributes* sceneAttributes, 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 @@ Brain::loadSpecFileFromScene(const SceneAttributes* sceneAttributes, /* * 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 @@ Brain::loadSpecFileFromScene(const SceneAttributes* sceneAttributes, 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 @@ Brain::receiveEvent(Event* event) addDataFileEvent->setErrorMessage(dfe.whatString()); } addDataFileEvent->setEventProcessed(); + + sortDataFilesByFileNameNoPath(); } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_DELETE) { EventDataFileDelete* deleteDataFileEvent = @@ -6100,6 +6410,8 @@ Brain::receiveEvent(Event* event) if (readDataFileEvent->getLoadIntoBrain() == this) { readDataFileEvent->setEventProcessed(); processReadDataFileEvent(readDataFileEvent); + + sortDataFilesByFileNameNoPath(); } } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_RELOAD) { @@ -6112,6 +6424,15 @@ Brain::receiveEvent(Event* event) 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 @@ Brain::receiveEvent(Event* event) 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 @@ Brain::receiveEvent(Event* event) 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 @@ Brain::receiveEvent(Event* event) } } } + 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); @@ -6308,6 +6649,24 @@ Brain::getChartTwoModel() const return m_modelChartTwo; } +/** + * @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. */ @@ -6496,21 +6855,6 @@ Brain::getAllDataFilesWithDataFileType(const DataFileTypeEnum::Enum dataFileType 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 @@ Brain::getAllDataFiles(std::vector& allDataFilesOut, 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 @@ Brain::getAllDataFiles(std::vector& allDataFilesOut, } } -// 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 @@ Brain::getAllModifiedFiles(const std::vector& excludeThe } } - -///** -// * 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 @@ Brain::writeDataFile(CaretDataFile* caretDataFile) 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 @@ Brain::saveToScene(const SceneAttributes* sceneAttributes, 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 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, bestMatchingSceneClass = const_cast(fileSceneClass); bestMatchingCount = matchCount; } -// caretDataFile->restoreFromScene(sceneAttributes, -// fileSceneClass); } } @@ -7673,6 +7967,11 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, trajFile->finishRestorationOfScene(); } + /* + * Some files are sorted by name + */ + sortDataFilesByFileNameNoPath(); + /* * Restore members */ @@ -7780,6 +8079,10 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, 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")); @@ -7794,6 +8097,77 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, setSurfaceMatchingToAnatomical(m_surfaceMatchingToAnatomicalFlag); } +/** + * @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 @@ Brain::getIdentificationManager() 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 @@ Brain::setSurfaceMatchingToAnatomical(const bool matchStatus) } } +/** + * @return The active scene (NULL if no active scene) + */ +const Scene* +Brain::getActiveScene() const +{ + return m_activeScene; +} + diff --git a/src/Brain/Brain.h b/src/Brain/Brain.h index a136fa8cd5573427576461faa295ed833a19bf6d..c20f278e0ce200e041790c01800d62d2ee955f79 100644 --- a/src/Brain/Brain.h +++ b/src/Brain/Brain.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include #include @@ -49,6 +50,8 @@ namespace caret { class CaretDataFile; class CaretMappableDataFile; class CaretPreferences; + class CaretResult; + class ChartTwoCartesianOrientedAxesYokingManager; class ChartingDataManager; class ChartableLineSeriesBrainordinateInterface; class ChartableMatrixInterface; @@ -80,6 +83,7 @@ namespace caret { class DisplayPropertiesVolume; class EventDataFileRead; class EventDataFileReload; + class EventDataFileReloadAll; class EventSpecFileReadDataFiles; class GapsAndMargins; class IdentificationManager; @@ -89,10 +93,13 @@ namespace caret { 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 @@ namespace caret { class Brain : public CaretObject, public EventListenerInterface, public SceneableInterface { public: - Brain(const CaretPreferences* caretPreferences); + Brain(CaretPreferences* caretPreferences); ~Brain(); @@ -216,6 +223,10 @@ namespace caret { const ChartingDataManager* getChartingDataManager() const; + ModelMedia* getMediaModel(); + + const ModelMedia* getMediaModel() const; + void getAllCiftiMappableDataFiles(std::vector& allCiftiMappableDataFilesOut) const; int32_t getNumberOfConnectivityMatrixDenseFiles() const; @@ -414,8 +425,11 @@ namespace caret { 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 @@ namespace caret { IdentificationManager* getIdentificationManager(); + const IdentificationManager* getIdentificationManager() const; + SelectionManager* getSelectionManager(); BrainordinateRegionOfInterest* getBrainordinateHighlightRegionOfInterest(); @@ -447,6 +463,10 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { void updateSurfaceMontageModel(); + void updateMediaModel(); + void updateBrainStructures(); void updateFiberTrajectoryMatchingFiberOrientationFiles(); @@ -738,6 +764,8 @@ namespace caret { void resetDuplicateFileNameCounter(const bool preserveSceneFileCounter); + std::vector getReloadableDataFiles() const; + std::vector m_brainStructures; std::vector m_annotationFiles; @@ -798,12 +826,16 @@ namespace caret { 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 @@ namespace caret { Scene* m_activeScene = NULL; bool m_surfaceMatchingToAnatomicalFlag = false; + + std::shared_ptr m_palettesStandardGroup; + + std::shared_ptr m_palettesUserCustomGroup; }; } // namespace diff --git a/src/Brain/BrainOpenGL.cxx b/src/Brain/BrainOpenGL.cxx index b51d38094e14629ae767fa175b12a921474c96df..f03412683e774fe654576c59add3d1167a5d6106 100644 --- a/src/Brain/BrainOpenGL.cxx +++ b/src/Brain/BrainOpenGL.cxx @@ -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 @@ BrainOpenGL::BrainOpenGL(BrainOpenGLTextRenderInterface* textRenderer) 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 @@ BrainOpenGL::~BrainOpenGL() 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 @@ BrainOpenGL::initializeOpenGL() * 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 --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 75c5a92c756fb0daf64899a314632173136ef708..9c2b87c12d15e7e50b1173447557e546e2a27f49 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -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 @@ m_volumeSliceThickness(0.0) { 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(c */ if (convertModelToWindowCoordinate(modelXYZ, drawingSpaceXYZ)) { - modelXYZValid = false; drawingSpaceXYZValid = true; } else { @@ -388,7 +392,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::convertModelToWindowCoordinate(const * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationTwoDimShapeBounds(const 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationTwoDimShapeBounds(const 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawModelSpaceAnnotationsOnVolumeSlic m_volumeSpacePlaneValid = true; std::vector emptyColorBars; + std::vector emptyScaleBars; std::vector emptyNotInFileAnnotations; drawAnnotationsInternal(AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, + emptyScaleBars, emptyNotInFileAnnotations, NULL, sliceThickness); @@ -512,6 +525,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawModelSpaceAnnotationsOnVolumeSlic * 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 @@ void 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, drawAnnotationsInternal(drawingCoordinateSpace, colorBars, + scaleBars, notInFileAnnotations, surfaceDisplayed, sliceThickness); @@ -551,6 +568,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat m_volumeSliceThickness = sliceThickness; - setSelectionBoxColor(); - m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, ("At beginning of annotation drawing in space " + AnnotationCoordinateSpaceEnum::toName(drawingCoordinateSpace))); @@ -704,7 +722,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat /* * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat } 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat annotationsFromFile.insert(annotationsFromFile.end(), colorBars.begin(), colorBars.end()); + annotationsFromFile.insert(annotationsFromFile.end(), + scaleBars.begin(), + scaleBars.end()); } } break; @@ -834,9 +901,9 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat } 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat } 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOneDimAnnotationSurfaceTextureOff 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; @@ -1530,6 +1629,88 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOneDimAnnotationSurfaceTextureOff return drawnFlag; } +/** + * 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. * @@ -1590,10 +1771,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil m_selectionInfo.push_back(SelectionInfo(annotationFile, box, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + invalidPolyLineCoordinateIndex, selectionCenterXYZ)); } else { @@ -1651,7 +1835,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil } } if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, box, bottomLeft, bottomRight, @@ -1706,7 +1890,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBoxSurfaceTangentOffset(Annotatio 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBoxSurfaceTangentOffset(Annotatio 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBoxSurfaceTangentOffset(Annotatio 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBoxSurfaceTangentOffset(Annotatio } } if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, box, bottomLeft, bottomRight, @@ -1796,134 +1984,457 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBoxSurfaceTangentOffset(Annotatio 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); + 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; } @@ -1981,14 +2492,17 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotati tickMarksOffsetFromBotom); } if (colorBar->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, colorBar, bottomLeft, bottomRight, @@ -2650,7 +3164,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi glPopMatrix(); if (oval->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, oval, bottomLeft, bottomRight, @@ -2757,7 +3274,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOvalSurfaceTangentOffset(Annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOvalSurfaceTangentOffset(Annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOvalSurfaceTangentOffset(Annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOvalSurfaceTangentOffset(Annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(Annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(Annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(Annotati floatTopLeft[i] = topLeft[i]; floatTopRight[i] = topRight[i]; } - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, text, floatBottomLeft, floatBottomRight, @@ -3306,6 +3826,10 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi } if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, text, bottomLeft, bottomRight, @@ -3491,10 +4019,16 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF } if (image->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, image, bottomLeft, bottomRight, @@ -3635,7 +4172,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImageSurfaceTangentOffset(Annotat 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImageSurfaceTangentOffset(Annotat 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImageSurfaceTangentOffset(Annotat 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImageSurfaceTangentOffset(Annotat } } if (image->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, + drawAnnotationTwoDimShapeSizingHandles(annotationFile, image, bottomLeft, bottomRight, @@ -3912,7 +4453,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi } if (line->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationOneDimSizingHandles(annotationFile, + drawAnnotationLineSizingHandles(annotationFile, line, lineHeadXYZ, lineTailXYZ, @@ -3971,6 +4515,13 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLineSurfaceTextureOffset(Annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLineSurfaceTextureOffset(Annotati 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLineSurfaceTextureOffset(Annotati 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,17 +4656,226 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLineSurfaceTextureOffset(Annotati * 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 @@ -4194,6 +4916,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getLineWidthPercentageInSelectionMode * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi Annotation* annotation, const float xyz[3], const float halfWidthHeight, - const float rotationAngle) + const float rotationAngle, + const int32_t polyLineCoordinateIndex) { glPushMatrix(); @@ -4266,55 +4991,95 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann float cornerSquareSize = 3.0 + lineThickness; if (tangentSurfaceOffsetFlag) { - cornerSquareSize = lineThickness;// * 4.0; + cornerSquareSize = lineThickness; cornerSquareSize = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(cornerSquareSize); } @@ -4379,20 +5144,22 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann annotation, secondPointSymbolXYZ, cornerSquareSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { @@ -4415,7 +5183,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann annotation, midPointXYZ, cornerSquareSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } } @@ -4440,7 +5209,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann * 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann annotation, handleLeft, sizeHandleSize, - rotationAngle); + rotationAngle, + invalidPolyLineCoordinateIndex); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { @@ -4631,16 +5446,89 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann 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 @@ BrainOpenGLAnnotationDrawingFixedPipeline::setSelectionBoxColor() 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 --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 0207f0f6e16696972c8c809fcc762f290be6a12a..da81d531dd035517a45f077a93d97f65a7d8c074 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -39,18 +39,23 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { const SpacerTabIndex m_spacerTabIndex; const WindowDrawingMode m_windowDrawingMode; const bool m_annotationUserInputModeFlag; + const bool m_tileTabsManualLayoutUserInputModeFlag; }; BrainOpenGLAnnotationDrawingFixedPipeline(BrainOpenGLFixedPipeline* brainOpenGLFixedPipeline); @@ -98,6 +106,7 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { AnnotationSizingHandleTypeEnum::Enum m_sizingHandle; + int32_t m_polyLineCoordinateIndex; + double m_windowXYZ[3]; }; @@ -169,6 +182,7 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { AnnotationBox* box, const Surface* surfaceDisplayed); + bool drawBrowserTab(AnnotationFile* annotationFile, + AnnotationBrowserTab* browserTab); + bool drawBoxSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationBox* box, const float surfaceExtentZ, @@ -238,11 +259,23 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { std::vector& lineCoordinatesOut, std::vector& arrowCoordinatesOut) const; - void setSelectionBoxColor(); + void setSelectionBoxColor(const Annotation* annotation); void startOpenGLForDrawing(GLint* savedShadeModelOut, GLboolean* savedLightingEnabledOut); diff --git a/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx index 70bd290f1a842e6bb85e6fd9e679313402da707a..8ffd1b235cb5290b55df506ef8ce1c4d3f949bf9 100644 --- a/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx @@ -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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawChartOverlaySet(Brain* brain, 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawChartOverlaySet(Brain* brain, bool drawHistogramFlag = true; bool drawLineSeriesFlag = true; + bool drawLineLayerFlag = true; bool drawMatrixFlag = true; /* @@ -231,6 +242,11 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawChartOverlaySet(Brain* brain, && (! 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawChartOverlaySet(Brain* brain, 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawChartOverlaySet(Brain* brain, * 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch * 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 ( ! yLeftRightValid) { + yMinLeftRight = 0.0; + yMaxLeftRight = 0.0; } - if ( ! xTopValid) { - xMinTop = 0.0; - xMaxTop = 0.0; - } - if ( ! xBottomValid) { - xMinBottom = 0.0; - xMaxBottom = 0.0; + if ( ! xBottomTopValid) { + xMinBottomTop = 0.0; + xMaxBottomTop = 0.0; } ChartTwoCartesianAxis* leftAxis = NULL; @@ -567,41 +575,45 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch */ 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch * slightly larger than the minimum value. */ const float smallRange = 0.01; - if (xMinBottom >= xMaxBottom) { - xMaxBottom = xMinBottom + smallRange; - } - if (xMinTop >= xMaxTop) { - xMaxTop = xMinBottom + smallRange; - } - if (yMinLeft >= yMaxLeft) { - yMaxLeft = yMinLeft + smallRange; + if (xMinBottomTop >= xMaxBottomTop) { + xMaxBottomTop = xMinBottomTop + smallRange; } - if (yMinRight >= yMaxRight) { - yMaxRight = yMinRight + smallRange; + if (yMinLeftRight >= yMaxLeftRight) { + yMaxLeftRight = yMinLeftRight + smallRange; } glViewport(chartGraphicsDrawingViewport[0], @@ -776,33 +774,14 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch } } } + 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawHistogramOrLineSeriesChart(const Ch } } } -} -/** - * 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); + if (drawLineLayerFlag) { - 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; - } + /* + * Need to draw tooltips last so they are not under other chart lines + */ + std::vector> textToolTips; - CaretMappableDataFile* mapFile = NULL; - ChartTwoOverlay::SelectedIndexType selectedIndexType = ChartTwoOverlay::SelectedIndexType::INVALID; - int32_t selectedIndex = -1; - chartOverlay->getSelectionData(mapFile, - selectedIndexType, - selectedIndex); - if (mapFile == NULL) { - continue; + 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); } - const ChartableTwoFileMatrixChart* matrixChart = mapFile->getChartingDelegate()->getMatrixCharting(); - int32_t overlayRows = 0; - int32_t overlayColumns = 0; - matrixChart->getMatrixDimensions(overlayRows, - overlayColumns); - /* - * 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChart() * 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) + const float opacity, + std::vector& rowColumnHighlightingOut, + const bool blendingEnabled) { - GraphicsPrimitiveV3fC4f* matrixPrimitive = matrixChart->getMatrixChartingGraphicsPrimitive(chartViewingType, - CiftiMappableDataFile::MatrixGridMode::FILLED); + 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChartContent(const ChartableT } 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChartContent(const ChartableT 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChartContent(const ChartableT * 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawMatrixChartContent(const ChartableT } } -// 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::convertPercentageOfViewportToOpenGLLine if (pixelsWidthOrHeight < s_minimumLineWidthOpenGL) { pixelsWidthOrHeight = s_minimumLineWidthOpenGL; } -// if (pixelsWidthOrHeight > s_maximumLineWidthOpenGL) { -// pixelsWidthOrHeight = s_maximumLineWidthOpenGL; -// } return pixelsWidthOrHeight; } @@ -1746,19 +1938,15 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawPrimitivePrivate(GraphicsPrimitive* * 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::HistogramChartDrawingInfo::~HistogramCh * 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::AxisDrawingInfo(BrainO 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_tabViewportHeight(tabViewport[3]) 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 @@ m_tabViewportHeight(tabViewport[3]) * 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 @@ m_tabViewportHeight(tabViewport[3]) 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::initializeNumericText( 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::initializeNumericText( */ 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::initializeNumericText( 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::initializeNumericText( 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::setLabelAndNumericsCoo * 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::drawAxis(BrainOpenGLCh 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 @@ BrainOpenGLChartTwoDrawingFixedPipeline::AxisDrawingInfo::drawAxis(BrainOpenGLCh 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 --git a/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h index 807efd64bb34ad07a2d4a0633ca9b86977f0f117..d46c8ef624da7507da0090ce988abe4e851b46b6 100644 --- a/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { SelectionItemChartTwoHistogram* m_selectionItemHistogram; - SelectionItemChartTwoLineSeries* m_selectionItemLineSeries; + SelectionItemChartTwoLineLayer* m_selectionItemLineLayer; + SelectionItemChartTwoLineLayerVerticalNearest* m_selectionItemLineLayerVerticalNearest; + + SelectionItemChartTwoLineSeries* m_selectionItemLineSeries; + SelectionItemChartTwoMatrix* m_selectionItemMatrix; SelectionItemChartTwoLabel* m_selectionItemChartLabel; diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 8f4002f62214c144ae4008574abf85a5277af9e7..4ef0dd954e6433ced08890e9571218c302b672fb 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -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 @@ BrainOpenGLFixedPipeline::selectModelImplementation(const int32_t windowIndex, 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 @@ BrainOpenGLFixedPipeline::selectModelImplementation(const int32_t windowIndex, m_brain = NULL; m_windowIndex = -1; - m_windowUserInputMode = UserInputModeEnum::INVALID; + m_windowUserInputMode = UserInputModeEnum::Enum::INVALID; } /** @@ -379,7 +389,7 @@ BrainOpenGLFixedPipeline::projectToModelImplementation(const int32_t windowIndex this->modeProjectionData = NULL; m_brain = NULL; m_windowIndex = -1; - m_windowUserInputMode = UserInputModeEnum::INVALID; + m_windowUserInputMode = UserInputModeEnum::Enum::INVALID; } /** @@ -448,6 +458,10 @@ BrainOpenGLFixedPipeline::updateForegroundAndBackgroundColors(const BrainOpenGLV 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 @@ BrainOpenGLFixedPipeline::setTabViewport(const BrainOpenGLViewportContent* vpCon } /** - * 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 @@ BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(const std::vectorgetTabIndex(); + + 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 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, setTabViewport(NULL); + const bool selectionModeFlag(false); m_specialCaseGraphicsAnnotations.clear(); - setAnnotationColorBarsForDrawing(viewportContents); + setAnnotationColorBarsAndBrowserTabsForDrawing(viewportContents, + selectionModeFlag); m_tileTabsActiveFlag = (viewportContents.size() > 1); @@ -605,7 +752,20 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, 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 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, 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 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, */ 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 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, * 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 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, m_brain = NULL; m_windowIndex = -1; - m_windowUserInputMode = UserInputModeEnum::INVALID; + m_windowUserInputMode = UserInputModeEnum::Enum::INVALID; } /** @@ -875,7 +1122,8 @@ BrainOpenGLFixedPipeline::drawChartCoordinateSpaceAnnotations(const BrainOpenGLV * 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 @@ BrainOpenGLFixedPipeline::drawChartCoordinateSpaceAnnotations(const BrainOpenGLV 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 @@ BrainOpenGLFixedPipeline::drawSpacerAnnotations(const BrainOpenGLViewportContent 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 @@ BrainOpenGLFixedPipeline::drawSpacerAnnotations(const BrainOpenGLViewportContent 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 @@ BrainOpenGLFixedPipeline::drawSpacerAnnotations(const BrainOpenGLViewportContent 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 @@ BrainOpenGLFixedPipeline::drawSpacerAnnotations(const BrainOpenGLViewportContent 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 @@ BrainOpenGLFixedPipeline::drawTabAnnotations(const BrainOpenGLViewportContent* t 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 @@ BrainOpenGLFixedPipeline::drawTabAnnotations(const BrainOpenGLViewportContent* t 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 @@ BrainOpenGLFixedPipeline::drawTabAnnotations(const BrainOpenGLViewportContent* t 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 @@ BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) */ 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 @@ BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) 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 @@ BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, 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 @@ BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, 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 @@ BrainOpenGLFixedPipeline::applyClippingPlanes(const ClippingDataType clippingDat CaretAssert(m_clippingPlaneGroup); + if ( ! m_clippingPlaneGroup->isEnabled()) { + return; + } + float rotation[3]; m_clippingPlaneGroup->getRotationAngles(rotation); @@ -1861,9 +2185,6 @@ BrainOpenGLFixedPipeline::disableLineAntiAliasing() if (glIsEnabled(GL_MULTISAMPLE)) { return; } - -// glDisable(GL_LINE_SMOOTH); -// glDisable(GL_BLEND); } /** @@ -1896,6 +2217,8 @@ BrainOpenGLFixedPipeline::drawSurfaceModel(BrowserTabContent* browserTabContent, surface, this->windowTabIndex); + setupScaleBarDrawingInformation(browserTabContent); + this->drawSurface(surface, browserTabContent->getScaling(), nodeColoringRGBA, @@ -1953,7 +2276,13 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, } 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, glDisable(GL_POLYGON_OFFSET_FILL); } - if (blendingEnabled == false) { + if ( ! blendingEnabled) { glDisable(GL_BLEND); } + } break; } @@ -2029,7 +2389,6 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, drawSurfaceNormalVectors(surface); } this->drawSurfaceBorders(surface); - this->drawSurfaceFoci(surface); this->drawSurfaceNodeAttributes(surface); this->drawSurfaceBorderBeingDrawn(surface); @@ -2037,7 +2396,8 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, * 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, + emptyScaleBars, emptyViewportAnnotations, annotationDrawingNullSurface, annotationDrawingUnusedSurfaceScaling); @@ -2073,10 +2437,17 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, */ 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, * 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, 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 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, + emptyScaleBars, emptyViewportAnnotations, annotationDrawingNullSurface, annotationDrawingUnusedSurfaceScaling); @@ -2744,8 +3120,6 @@ BrainOpenGLFixedPipeline::drawSurfaceNodeAttributes(Surface* surface) } } - const float symbolDiameter = nodeID.getSymbolSize(); - uint8_t symbolRGBA[4]; if (isSelect) { @@ -2766,6 +3140,20 @@ BrainOpenGLFixedPipeline::drawSurfaceNodeAttributes(Surface* surface) } 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 @@ BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) 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 @@ BrainOpenGLFixedPipeline::unstretchedBorderLineTest(const float p1[3], /** * 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 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) 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 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) 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 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) 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 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) 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 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) 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 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) glPopAttrib(); } - /** * Draw borders on a surface. * @param surface @@ -3814,12 +4260,6 @@ BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, 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 @@ BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, viewport); } } - - glPopAttrib(); } /** @@ -3881,6 +4319,7 @@ BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vector volumeDrawInfo; + std::vector volumeDrawInfoOutsideFaces; for (std::vector::iterator iter = volumeDrawInfoIn.begin(); iter != volumeDrawInfoIn.end(); iter++) { @@ -3895,10 +4334,35 @@ BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vector= 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 @@ BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vectorgetDisplayPropertiesVolume(); + 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 @@ BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vector 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; + } + } + } + + 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; } - glPopMatrix(); } } + + 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 @@ BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vectorgetFanMultiplier(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 @@ BrainOpenGLFixedPipeline::drawAllFiberOrientations(const FiberOrientationDisplay 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 @@ BrainOpenGLFixedPipeline::drawFiberTrajectories(const Plane* plane, 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 @@ BrainOpenGLFixedPipeline::drawSurfaceMontageModel(BrowserTabContent* browserTabC center, mvp->getProjectionViewType()); + if (ivp == 0) { + setupScaleBarDrawingInformation(browserTabContent); + } + this->drawSurface(mvp->getSurface(), browserTabContent->getScaling(), nodeColoringRGBA, @@ -5241,6 +6397,56 @@ BrainOpenGLFixedPipeline::drawSurfaceMontageModel(BrowserTabContent* browserTabC savedVP[3]); } +/** + * 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 @@ -5337,6 +6543,8 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte center, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL); + setupScaleBarDrawingInformation(browserTabContent); + const SurfaceTypeEnum::Enum surfaceType = wholeBrainModel->getSelectedSurfaceType(tabNumberIndex); /* @@ -5540,6 +6748,13 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte glPopMatrix(); } } + + /* + * Special case to draw foci when surfaces are not displayed + */ + if (numSurfaceToDraw <= 0) { + drawSurfaceFoci(NULL); + } } /** @@ -5848,11 +7063,15 @@ BrainOpenGLFixedPipeline::setOrthographicProjectionWithHeight(const int32_t view 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 @@ BrainOpenGLFixedPipeline::setOrthographicProjectionWithWidth(const int32_t viewp 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 @@ BrainOpenGLFixedPipeline::drawImage(const BrainOpenGLViewportContent* vpContent, 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 @@ BrainOpenGLFixedPipeline::drawTextAtModelCoords(const float modelXYZ[3], } } +/** + * 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 @@ BrainOpenGLFixedPipeline::getStateOfOpenGL() const 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 --git a/src/Brain/BrainOpenGLFixedPipeline.h b/src/Brain/BrainOpenGLFixedPipeline.h index 172935495af2133a9ea55fd3d4e9b9ff3079e30d..b492a32d794a629acf3a614a4670256110738b2f 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.h +++ b/src/Brain/BrainOpenGLFixedPipeline.h @@ -43,6 +43,8 @@ namespace caret { class Annotation; + class AnnotationBrowserTab; + class AnnotationScaleBar; class AnnotationText; class BoundingBox; class Brain; @@ -63,17 +65,18 @@ namespace caret { 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 @@ namespace caret { float fanMultiplier; bool isDrawWithMagnitude; float minimumMagnitude; + float maximumUncertainty; float magnitudeMultiplier; Plane* plane; FiberOrientationSymbolTypeEnum::Enum symbolType; @@ -263,6 +267,10 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { friend class BrainOpenGLAnnotationDrawingFixedPipeline; friend class BrainOpenGLChartDrawingFixedPipeline; friend class BrainOpenGLChartTwoDrawingFixedPipeline; + friend class BrainOpenGLMediaDrawing; friend class BrainOpenGLVolumeObliqueSliceDrawing; friend class BrainOpenGLVolumeSliceDrawing; friend class BrainOpenGLVolumeTextureSliceDrawing; diff --git a/src/Brain/BrainOpenGLMediaDrawing.cxx b/src/Brain/BrainOpenGLMediaDrawing.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d6876cd2272d777e5778b1497e712945b4f34e0b --- /dev/null +++ b/src/Brain/BrainOpenGLMediaDrawing.cxx @@ -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 --git a/src/Brain/BrainOpenGLMediaDrawing.h b/src/Brain/BrainOpenGLMediaDrawing.h new file mode 100644 index 0000000000000000000000000000000000000000..2c185ce0e1d29ab867e5dc1bf9285cdf43a885aa --- /dev/null +++ b/src/Brain/BrainOpenGLMediaDrawing.h @@ -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 --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index 48f3768200803760dd99230aa2ecb4a2d28c21a8..7eb80197e649b75cba90ada8945ec2452397b07b 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -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 @@ using namespace caret; /** * 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 @@ using namespace caret; * @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 @@ BrainOpenGLViewportContent::BrainOpenGLViewportContent(const int windowViewport[ 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_spacerTabContent(spacerTabContent) 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() 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 @@ BrainOpenGLViewportContent::initializeMembersBrainOpenGLViewportContent() 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 @@ BrainOpenGLViewportContent::copyHelperBrainOpenGLViewportContent(const BrainOpen 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 @@ BrainOpenGLViewportContent::getTabViewportBeforeApplyingMargins(int tabViewportO 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 @@ -416,6 +464,20 @@ BrainOpenGLViewportContent::getWindowViewport(int windowViewportOut[4]) const windowViewportOut[3] = m_windowHeight; } +/** + * @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. */ @@ -464,10 +526,11 @@ BrainOpenGLViewportContent::getTabIndex() const 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 @@ BrainOpenGLViewportContent::toString() const 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 @@ BrainOpenGLViewportContent::toString() const * 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 @@ BrainOpenGLViewportContent::createViewportForSingleTab(std::vector eventContent = EventBrowserWindowContent::getWindowContent(windowIndex); EventManager::get()->sendEvent(eventContent->getPointer()); @@ -564,7 +640,9 @@ BrainOpenGLViewportContent::createViewportForSingleTab(std::vector 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 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetTileTabsConfigurationMode()) { + 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 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(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 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetRow(iRowFromTop)->getContentType(); + const TileTabsGridRowColumnContentTypeEnum::Enum rowContentType = gridConfiguration->getRow(iRowFromTop)->getContentType(); switch (rowContentType) { case TileTabsGridRowColumnContentTypeEnum::SPACE: spacerTabFlag = true; @@ -663,7 +814,7 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetColumn(jCol)->getContentType(); + const TileTabsGridRowColumnContentTypeEnum::Enum tabContentType = gridConfiguration->getColumn(jCol)->getContentType(); switch (tabContentType) { case TileTabsGridRowColumnContentTypeEnum::SPACE: spacerTabFlag = true; @@ -740,7 +891,7 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorisCenteringCorrectionEnabled()) { + if (gridConfiguration->isCenteringCorrectionEnabled()) { /* * Kludge that fixes old scenes * NEED TO ADJUST FOR X too ? @@ -856,7 +1007,9 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetWindowIndex(), @@ -877,7 +1030,9 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetWindowIndex(), @@ -891,12 +1046,182 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetTabIndex() == 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 --git a/src/Brain/BrainOpenGLViewportContent.h b/src/Brain/BrainOpenGLViewportContent.h index c8def250850a3bda74e98410004c2baff9593ddc..f25177ca35d46d49d307a9636b8c9447321e34eb 100644 --- a/src/Brain/BrainOpenGLViewportContent.h +++ b/src/Brain/BrainOpenGLViewportContent.h @@ -32,7 +32,8 @@ namespace caret { class BrowserWindowContent; class GapsAndMargins; class SpacerTabContent; - class TileTabsConfiguration; + class TileTabsLayoutGridConfiguration; + class TileTabsLayoutManualConfiguration; class BrainOpenGLViewportContent : public CaretObject { @@ -59,8 +60,12 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { /** 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 @@ namespace caret { 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 --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 084a568352141261731b473309b882d4221a5e7b..ee47cf9a63fae735ab296c7a681ce0d1cef26761 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -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 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewProjection(const BrainO 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 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewProjection(const BrainO 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 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewProjection(const BrainO m_fixedPipelineDrawing->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, sliceThickness); @@ -1211,7 +1227,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceFoci(const Plane& plane) 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 @@ BrainOpenGLVolumeObliqueSliceDrawing::ObliqueSlice::draw(BrainOpenGLFixedPipelin 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 @@ BrainOpenGLVolumeObliqueSliceDrawing::ObliqueSlice::draw(BrainOpenGLFixedPipelin } } 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 --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 93bb46fc20c233bcbd8fa6f31d189e9e8241cd4d..173380d9d804a9618ecebb99b1bd9b0728e4114b 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -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 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM 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 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM 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 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM 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 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM 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 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM 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 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM m_fixedPipelineDrawing->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, sliceThickness); @@ -901,341 +889,6 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM } } -/** - * 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 @@ getAxisInfo(const VolumeMappableInterface* volumeFile, } axisInfoOut.valid = true; - - //axisInfoOut.print(); } /** @@ -1507,14 +1158,11 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnu /* * 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 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnu 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 @@ BrainOpenGLVolumeSliceDrawing::showBrainordinateHighlightRegionOfInterest(const /** * Draw identification symbols on volume slice with the given plane. * + * @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(const VolumeMappableInterface* volume, + const Plane& plane, + const float sliceThickness) { - IdentificationManager* idManager = m_brain->getIdentificationManager(); - - SelectionItemVoxelIdentificationSymbol* symbolID = m_brain->getSelectionManager()->getVoxelIdentificationSymbol(); + 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. + * @param sliceThickess + * Thickness of the slice + */ +void +BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + Brain* brain, + const VolumeMappableInterface* volume, + const Plane& plane, + const float sliceThickness) +{ + 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(); + 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 @@ BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const Plane& plane) return; break; } - + if (idManager->isShowVolumeIdentificationSymbols()) { uint8_t rgba[4]; const int32_t numVoxelIdSymbols = static_cast(voxelIDs.size()); @@ -1998,13 +1688,25 @@ BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const Plane& plane) 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 @@ BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const Plane& plane) glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); - m_fixedPipelineDrawing->drawSphereWithDiameter(rgba, + fixedPipelineDrawing->drawSphereWithDiameter(rgba, symbolDiameter); glPopMatrix(); } @@ -2026,11 +1728,11 @@ BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const Plane& plane) 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 @@ BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const Plane& plane) 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 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV /* * 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 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV /* 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 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV volumeDrawingOpacity); glDisable(GL_POLYGON_OFFSET_FILL); + + if (m_identificationModeFlag) { + processIdentification(true); + } } showBrainordinateHighlightRegionOfInterest(sliceViewPlane, @@ -2862,6 +2561,8 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const VolumeMappableInte 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 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineNotCached(const ModelTypeEnum:: 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 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineNotCached(const ModelTypeEnum:: colorSourceBrowserTabIndex); } - //const float thicknessPercentage = GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(thicknessMillimeters); SurfacePlaneIntersectionToContour contour(surface, plane, outlineColor, @@ -3200,8 +2902,27 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceFoci(const Plane& plane) 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 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceFoci(const Plane& plane) 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 @@ BrainOpenGLVolumeSliceDrawing::addVoxelToIdentification(const int32_t volumeInde /** * 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 @@ BrainOpenGLVolumeSliceDrawing::processIdentification() 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 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceAllView(const VolumeSliceViewP /* * 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 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceAllView(const VolumeSliceViewP << " 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 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceAllView(const VolumeSliceViewP 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 --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.h b/src/Brain/BrainOpenGLVolumeSliceDrawing.h index 73afc88443feef9dc9e5f8c5a3f609a1fe1c2396..a47a00415caba7a4397c2eedfd911426d8dba279 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.h @@ -83,6 +83,12 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { const float sliceCoordinates[3], const float sliceNormalVector[3]); - void processIdentification(); + void processIdentification(const bool doNotReplaceUnderlayFlag); void resetIdentification(); diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index 1a10b7d4ef378b36bcb842df5e2fa6f51fc9c1b4..152b06384da844cd0aecb3fdb302f6043e1c608e 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -894,7 +894,8 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainO } } } - 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 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainO m_fixedPipelineDrawing->windowTabIndex, SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, - annotationModeFlag); + annotationModeFlag, + tileTabsEditModeFlag); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, sliceThickness); @@ -1401,7 +1403,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceFoci(const Plane& plane) 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 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS 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 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS * 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 --git a/src/Brain/BrainOpenGLWindowContent.cxx b/src/Brain/BrainOpenGLWindowContent.cxx index 89eebccab233ecc37b9b000f38fe89e65c81f846..7fb53bfa3ea2540ae497338604d36822531b2da6 100644 --- a/src/Brain/BrainOpenGLWindowContent.cxx +++ b/src/Brain/BrainOpenGLWindowContent.cxx @@ -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 @@ const BrainOpenGLViewportContent* 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 @@ BrainOpenGLWindowContent::getTabViewportWithLockAspectXY(const int32_t x, && (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 --git a/src/Brain/BrainOpenGLWindowContent.h b/src/Brain/BrainOpenGLWindowContent.h index 9109f795d25367fff7c0d2e7b7cd35b0a9ceb94a..5c7f9ab46d6d615537f7cc843c3ed17ee6cb5f2b 100644 --- a/src/Brain/BrainOpenGLWindowContent.h +++ b/src/Brain/BrainOpenGLWindowContent.h @@ -56,6 +56,9 @@ namespace caret { 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 --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index d26357bc89eb8b92f53548677c429860bd25ea23..0e24d14a9da707b3c4da3ef0eac89bd12e0359b6 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -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 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) m_surfaceMontageModel = NULL; m_chartModel = NULL; m_chartTwoModel = NULL; + m_mediaModel = NULL; m_guiName = ""; m_userName = ""; m_volumeSurfaceOutlineSetModel = new VolumeSurfaceOutlineSetModel(); @@ -131,6 +143,10 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) 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 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) 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 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) 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 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) "ViewingTransformations", m_viewingTransformation); + m_sceneClassAssistant->add("m_mediaViewingTransformation", + "ViewingTransformationsMedia", + m_mediaViewingTransformation); + m_sceneClassAssistant->add("m_volumeSliceViewingTransformation", "ViewingTransformations", m_volumeSliceViewingTransformation); @@ -225,8 +250,13 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) &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 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) 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 @@ BrowserTabContent::~BrowserTabContent() 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 @@ BrowserTabContent::cloneBrowserTabContent(BrowserTabContent* tabToClone) *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 @@ BrowserTabContent::cloneBrowserTabContent(BrowserTabContent* tabToClone) 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 @@ BrowserTabContent::getDefaultName() const { 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 @@ BrowserTabContent::getDescriptionOfContent(PlainTextStringBuilder& descriptionOu 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 @@ BrowserTabContent::getDescriptionOfContent(PlainTextStringBuilder& descriptionOu 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 @@ BrowserTabContent::getDescriptionOfContent(PlainTextStringBuilder& descriptionOu model->getDescriptionOfContent(tabIndex, descriptionOut); } + else if (mediaFlag) { + model->getDescriptionOfContent(tabIndex, + descriptionOut); + } if (wholeBrainFlag || volumeFlag) { @@ -555,6 +617,9 @@ BrowserTabContent::getDescriptionOfContent(PlainTextStringBuilder& descriptionOu else if (chartTwoFlag) { getChartTwoOverlaySet()->getDescriptionOfContent(descriptionOut); } + else if (mediaFlag) { + getMediaOverlaySet()->getDescriptionOfContent(descriptionOut); + } else { getOverlaySet()->getDescriptionOfContent(descriptionOut); } @@ -601,6 +666,9 @@ BrowserTabContent::getModelForDisplay() 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 @@ BrowserTabContent::getModelForDisplay() const 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; @@ -717,6 +788,35 @@ BrowserTabContent::getDisplayedChartTwoModel() const return mc; } +/** + * 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. * @@ -850,6 +950,17 @@ BrowserTabContent::isChartTwoDisplayed() const 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? @@ -994,6 +1105,69 @@ BrowserTabContent::getChartTwoOverlaySet() const return m_chartTwoModel->getChartTwoOverlaySet(m_tabNumber); } +/** + * @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. */ @@ -1042,6 +1216,7 @@ BrowserTabContent::update(const std::vector models) 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 @@ BrowserTabContent::update(const std::vector models) 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 @@ BrowserTabContent::update(const std::vector models) 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 @@ BrowserTabContent::update(const std::vector models) 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 @@ BrowserTabContent::update(const std::vector models) 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) { @@ -1205,6 +1392,17 @@ BrowserTabContent::isSurfaceModelValid() const return valid; } +/** + * 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? * @@ -1293,14 +1491,29 @@ BrowserTabContent::setAspectRatio(const float aspectRatio) 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 @@ BrowserTabContent::receiveEvent(Event* event) } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - //keepSliceCoordinateForSelectedAxis = true; break; } switch (m_volumeSliceSettings->getSliceDrawingType()) { @@ -1371,7 +1583,6 @@ BrowserTabContent::receiveEvent(Event* event) } selectSlicesAtCoordinate(volumeSliceXYZ); - //m_volumeSliceSettings->selectSlicesAtCoordinate(volumeSliceXYZ); } } } @@ -1431,6 +1642,8 @@ BrowserTabContent::getAnnotationColorBars(std::vector& colo 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 @@ BrowserTabContent::getAnnotationColorBars(std::vector& colo 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 @@ BrowserTabContent::getDisplayedPaletteMapFiles(std::vectorgetChartTwoDataType() == 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 @@ BrowserTabContent::getFilesAndMapIndicesInOverlays(EventCaretMappableDataFilesAn if (overlayDataFile != NULL) { if (mapIndex >= 0) { fileAndMapsEvent->addBrainordinateFileAndMap(overlayDataFile, - mapIndex); + mapIndex, + m_tabNumber); } } } @@ -1942,6 +2199,27 @@ BrowserTabContent::getFilesDisplayedInTab(std::vector& displayed 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 @@ BrowserTabContent::getViewingTransformation() else if (isChartTwoDisplayed()) { return m_chartTwoMatrixViewingTranformation; } + else if (isMediaDisplayed()) { + return m_mediaViewingTransformation; + } return m_viewingTransformation; } @@ -2251,6 +2532,9 @@ BrowserTabContent::getViewingTransformation() const else if (isChartTwoDisplayed()) { return m_chartTwoMatrixViewingTranformation; } + else if (isMediaDisplayed()) { + return m_mediaViewingTransformation; + } return m_viewingTransformation; } @@ -2376,6 +2660,20 @@ BrowserTabContent::setObliqueVolumeRotationMatrix(const Matrix4x4& obliqueRotati 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten || 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten } } 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten if (isFlat) { /* - * No rotation. + * Special flat map rotation + */ + flatRotate = dy; + + /* + * No 3D rotation. */ dx = 0.0; dy = 0.0; @@ -2972,10 +3355,95 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten } } + 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 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten /** * 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 @@ BrowserTabContent::applyMouseScaling(const int32_t /*mouseDX*/, } } 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 @@ BrowserTabContent::applyMouseTranslation(BrainOpenGLViewportContent* viewportCon } } 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; @@ -3327,6 +3825,60 @@ BrowserTabContent::applyMouseTranslation(BrainOpenGLViewportContent* viewportCon updateYokedModelBrowserTabs(); } +/** + * 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. * @@ -3424,18 +3976,18 @@ BrowserTabContent::getTransformationsForOpenGLDrawing(const ProjectionViewTypeEn } 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 @@ BrowserTabContent::getTransformationsForOpenGLDrawing(const ProjectionViewTypeEn 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 @@ BrowserTabContent::getTransformationsInModelTransform(ModelTransform& modelTrans 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 @@ BrowserTabContent::setTransformationsFromModelTransform(const ModelTransform& mo 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 @@ BrowserTabContent::saveToScene(const SceneAttributes* sceneAttributes, { 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 @@ BrowserTabContent::saveToScene(const SceneAttributes* sceneAttributes, 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 @@ BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, 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 @@ BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, m.getRotation(rotationX, rotationY, rotationZ); - //rotationX = -rotationX; - //rotationY = 180.0 - rotationY; rotationY = 90 + rotationY; rotationZ = -rotationZ; m.identity(); @@ -3760,6 +4340,8 @@ BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, 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 @@ BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, 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,8 +4430,37 @@ BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, } } + + 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 * @@ -3970,6 +4581,7 @@ BrowserTabContent::setClippingPlaneTransformation(const float panning[3], updateYokedModelBrowserTabs(); } + /** * Get the clipping plane group (const method). * @@ -3993,6 +4605,25 @@ BrowserTabContent::resetClippingPlaneTransformation() updateYokedModelBrowserTabs(); } +/** + * @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) */ @@ -4092,12 +4723,72 @@ BrowserTabContent::setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sl } /** - * @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 @@ BrowserTabContent::setChartModelYokingGroup(const YokingGroupEnum::Enum chartMod * 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 @@ BrowserTabContent::setBrainModelYokingGroup(const YokingGroupEnum::Enum brainMod * 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 @@ BrowserTabContent::setBrainModelYokingGroup(const YokingGroupEnum::Enum brainMod *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 @@ BrowserTabContent::updateYokedModelBrowserTabs() 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 @@ BrowserTabContent::updateBrainModelYokedBrowserTabs() /* * 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 @@ BrowserTabContent::updateBrainModelYokedBrowserTabs() 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 @@ BrowserTabContent::updateChartModelYokedBrowserTabs() /* * 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 @@ BrowserTabContent::updateChartModelYokedBrowserTabs() } } +/** + * @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 --git a/src/Brain/BrowserTabContent.h b/src/Brain/BrowserTabContent.h index d9babb5bf90e650d6ccd4e772fd4703d4ff08722..9bca4f1ee29c9bc44393a6221752ffdeeb70ee5a 100644 --- a/src/Brain/BrowserTabContent.h +++ b/src/Brain/BrowserTabContent.h @@ -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 @@ namespace caret { class Surface; class ViewingTransformations; class ViewingTransformationsCerebellum; + class ViewingTransformationsMedia; class ViewingTransformationsVolume; class VolumeMappableInterface; class VolumeSliceSettings; @@ -101,6 +110,13 @@ namespace caret { 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 @@ namespace caret { const ModelChartTwo* getDisplayedChartTwoModel() const; + ModelMedia* getDisplayedMediaModel(); + + const ModelMedia* getDisplayedMediaModel() const; + ModelSurface* getDisplayedSurfaceModel(); const ModelSurface* getDisplayedSurfaceModel() const; @@ -149,6 +169,8 @@ namespace caret { bool isVolumeSlicesDisplayed() const; + bool isMediaDisplayed() const; + bool isWholeBrainDisplayed() const; void getFilesDisplayedInTab(std::vector& displayedDataFilesOut); @@ -169,6 +191,8 @@ namespace caret { bool isSurfaceMontageModelValid() const; + bool isMediaModelValid() const; + void getAnnotationColorBars(std::vector& colorBarsOut); void getDisplayedPaletteMapFiles(std::vector& mapFiles, @@ -215,6 +239,10 @@ namespace caret { void resetClippingPlaneTransformation(); + bool isClippingPlanesEnabled(); + + void setClippingPlanesEnabled(const bool status); + const float* getTranslation() const; void getTranslation(float translationOut[3]) const; @@ -237,6 +265,10 @@ namespace caret { void setObliqueVolumeRotationMatrix(const Matrix4x4& obliqueRotationMatrix); + Matrix4x4 getFlatRotationMatrix() const; + + void setFlatRotationMatrix(const Matrix4x4& flatRotationMatrix); + void getRightCortexFlatMapOffset(float& offsetX, float& offsetY) const; @@ -276,7 +308,12 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { /** 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 @@ namespace caret { /** 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 @@ namespace caret { /** Whole brain surface settings. */ WholeBrainSurfaceSettings* m_wholeBrainSurfaceSettings; + std::unique_ptr m_scaleBar; + /** aspect ratio */ float m_aspectRatio; @@ -596,7 +678,26 @@ namespace caret { */ 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 --git a/src/Brain/BrowserWindowContent.cxx b/src/Brain/BrowserWindowContent.cxx index 8c097401fd7b463cd6c92ad19856772eb39905f8..1d88a05df8f956996e0ad69ac4fb78ec1392d1cc 100644 --- a/src/Brain/BrowserWindowContent.cxx +++ b/src/Brain/BrowserWindowContent.cxx @@ -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 @@ BrowserWindowContent::BrowserWindowContent(const int32_t windowIndex) : 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_windowIndex(windowIndex) 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 @@ BrowserWindowContent::reset() 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 @@ BrowserWindowContent::setTileTabsEnabled(const bool tileTabsEnabled) * @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 @@ BrowserWindowContent::getSelectedTileTabsConfiguration() * @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 @@ BrowserWindowContent::getSelectedTileTabsConfiguration() const } /** - * @return The automatic tile tabs configuration. + * @return The automatic grid tile tabs configuration */ -TileTabsConfiguration* -BrowserWindowContent::getAutomaticTileTabsConfiguration() +TileTabsLayoutGridConfiguration* +BrowserWindowContent::getAutomaticGridTileTabsConfiguration() { - return m_automaticTileTabsConfiguration.get(); + return m_automaticGridTileTabsConfiguration.get(); } /** - * @return The automatic tile tabs configuration (const method) + * @return The automatic grid tile tabs configuration (const method) */ -const TileTabsConfiguration* -BrowserWindowContent::getAutomaticTileTabsConfiguration() const +const TileTabsLayoutGridConfiguration* +BrowserWindowContent::getAutomaticGridTileTabsConfiguration() const { - return m_automaticTileTabsConfiguration.get(); + return m_automaticGridTileTabsConfiguration.get(); } /** - * @return The custom tile tabs configuration + * @return The custom grid tile tabs configuration */ -TileTabsConfiguration* -BrowserWindowContent::getCustomTileTabsConfiguration() +TileTabsLayoutGridConfiguration* +BrowserWindowContent::getCustomGridTileTabsConfiguration() { - return m_customTileTabsConfiguration.get(); + return m_customGridTileTabsConfiguration.get(); } /** - * @return The custom tile tabs configuration (const method) + * @return The custom grid tile tabs configuration (const method) */ -const TileTabsConfiguration* -BrowserWindowContent::getCustomTileTabsConfiguration() const +const TileTabsLayoutGridConfiguration* +BrowserWindowContent::getCustomGridTileTabsConfiguration() const { - return m_customTileTabsConfiguration.get(); + return m_customGridTileTabsConfiguration.get(); +} + +/** + * Set the custom grid tile tabs configuration + * + * @param gridConfiguration + * Grid configuration that is copied to the custom grid + */ +void +BrowserWindowContent::setCustomGridTileTabsConfiguration(const TileTabsLayoutGridConfiguration* gridConfiguration) +{ + 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 @@ BrowserWindowContent::getTileTabsConfigurationMode() const * 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. */ @@ -402,6 +618,26 @@ BrowserWindowContent::setSceneWindowTabIndices(const std::vector& scene m_sceneTabIndices = sceneTabIndices; } +/** + * @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. * @@ -427,14 +663,14 @@ BrowserWindowContent::saveToScene(const SceneAttributes* sceneAttributes, 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 @@ BrowserWindowContent::saveToScene(const SceneAttributes* sceneAttributes, * 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 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, /* 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 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, * 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 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, * 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 @@ BrowserWindowContent::restoreFromOldBrainBrowserWindowScene(const SceneAttribute 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 @@ BrowserWindowContent::restoreFromOldBrainBrowserWindowScene(const SceneAttribute * 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 --git a/src/Brain/BrowserWindowContent.h b/src/Brain/BrowserWindowContent.h index 06567a3a74e73fa3c89652090142acba55871566..77f870b1d883b806392f14ee4ee2385f92bb1176 100644 --- a/src/Brain/BrowserWindowContent.h +++ b/src/Brain/BrowserWindowContent.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { BrowserWindowContent& operator=(const BrowserWindowContent&); + void setCustomGridConfigurationToDefault(); + std::unique_ptr m_sceneAssistant; bool m_validFlag; @@ -142,20 +155,22 @@ namespace caret { 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 --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 9e40d104d9c7fc5bb1e7ed505545195466a2f366..5b985a446ba1c8e111aecf6d4cf76a9a15f2aacf 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -37,6 +37,7 @@ BrainOpenGLChartDrawingFixedPipeline.h BrainOpenGLChartTwoDrawingFixedPipeline.h BrainOpenGLChartTwoDrawingInterface.h BrainOpenGLFixedPipeline.h +BrainOpenGLMediaDrawing.h BrainOpenGLPrimitiveDrawing.h BrainOpenGLShape.h BrainOpenGLShapeCone.h @@ -55,6 +56,7 @@ BrainStructureNodeAttributes.h BrowserTabContent.h BrowserWindowContent.h ChartTwoOverlay.h +ChartTwoOverlayActiveModeEnum.h ChartTwoOverlaySet.h ChartTwoOverlaySetArray.h ChartingDataManager.h @@ -76,19 +78,22 @@ DisplayPropertyDataBoolean.h 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 @@ EventSceneActive.h 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 @@ SelectionItemChartMatrix.h SelectionItemChartTimeSeries.h SelectionItemChartTwoHistogram.h SelectionItemChartTwoLabel.h +SelectionItemChartTwoLineLayer.h +SelectionItemChartTwoLineLayerVerticalNearest.h SelectionItemChartTwoLineSeries.h SelectionItemChartTwoMatrix.h SelectionItemCiftiConnectivityMatrixRowColumn.h @@ -177,6 +196,7 @@ TabContentBase.h UserInputModeEnum.h ViewingTransformations.h ViewingTransformationsCerebellum.h +ViewingTransformationsMedia.h ViewingTransformationsVolume.h VolumeSliceDrawingTypeEnum.h VolumeSliceInterpolationEdgeEffectsMaskingEnum.h @@ -199,6 +219,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline.cxx BrainOpenGLChartDrawingFixedPipeline.cxx BrainOpenGLChartTwoDrawingFixedPipeline.cxx BrainOpenGLFixedPipeline.cxx +BrainOpenGLMediaDrawing.cxx BrainOpenGLPrimitiveDrawing.cxx BrainOpenGLShape.cxx BrainOpenGLShapeCone.cxx @@ -217,6 +238,7 @@ BrainStructureNodeAttributes.cxx BrowserTabContent.cxx BrowserWindowContent.cxx ChartTwoOverlay.cxx +ChartTwoOverlayActiveModeEnum.cxx ChartTwoOverlaySet.cxx ChartTwoOverlaySetArray.cxx ChartingDataManager.cxx @@ -237,19 +259,22 @@ DisplayPropertiesVolume.cxx 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 @@ EventSceneActive.cxx 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 @@ SelectionItemChartMatrix.cxx SelectionItemChartTimeSeries.cxx SelectionItemChartTwoHistogram.cxx SelectionItemChartTwoLabel.cxx +SelectionItemChartTwoLineLayer.cxx +SelectionItemChartTwoLineLayerVerticalNearest.cxx SelectionItemChartTwoLineSeries.cxx SelectionItemChartTwoMatrix.cxx SelectionItemCiftiConnectivityMatrixRowColumn.cxx @@ -337,6 +376,7 @@ TabContentBase.cxx UserInputModeEnum.cxx ViewingTransformations.cxx ViewingTransformationsCerebellum.cxx +ViewingTransformationsMedia.cxx ViewingTransformationsVolume.cxx VolumeSliceDrawingTypeEnum.cxx VolumeSliceInterpolationEdgeEffectsMaskingEnum.cxx diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx index ff59dbf9556e85de3b31f41b8039e4bf64dd4d4f..cb4032c4bc174bc085e1cf24851fa2194a986141 100644 --- a/src/Brain/ChartTwoOverlay.cxx +++ b/src/Brain/ChartTwoOverlay.cxx @@ -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_overlayIndex(overlayIndex) 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_overlayIndex(overlayIndex) 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 @@ ChartTwoOverlay::setWeakPointerToSelf(std::weak_ptr weakPointer 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 @@ ChartTwoOverlay::getChartTwoDataType() const /** * 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 @@ ChartTwoOverlay::setChartTwoCompoundDataType(const ChartTwoCompoundDataType& cha " 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 @@ ChartTwoOverlay::copyData(const ChartTwoOverlay* overlay) *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 @@ ChartTwoOverlay::isHistorySupported() const 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 @@ ChartTwoOverlay::isMapYokingSupportedPrivate(const CaretMappableDataFile* mapFil 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 @@ ChartTwoOverlay::getBounds(BoundingBox& boundingBoxOut) const 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 @@ ChartTwoOverlay::getBounds(BoundingBox& boundingBoxOut) const 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,12 +635,212 @@ ChartTwoOverlay::getBounds(BoundingBox& boundingBoxOut) const } break; case ChartTwoDataTypeEnum::CHART_DATA_TYPE_MATRIX: + { + const ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); + validFlag = matrixChart->getMatrixChartingGraphicsPrimitive(getMatrixTriangularViewingMode(), + CiftiMappableDataFile::MatrixGridMode::FILLED_TEXTURE, + getMatrixOpacity())->getVertexBounds(boundingBoxOut); + } break; } return validFlag; } +/** + * @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) */ @@ -720,11 +982,11 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma 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 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma 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 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma 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 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma 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 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma { 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 @@ ChartTwoOverlay::setSelectionData(CaretMappableDataFile* selectedMapFile, 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 @@ ChartTwoOverlay::isAllMapsSupported() const } } 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 @@ ChartTwoOverlay::isMatrixTriangularViewingModeSupported() const 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 @@ ChartTwoOverlay::isMatrixTriangularViewingModeSupported() const } /** - * @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,78 @@ ChartTwoOverlay::isCartesianVerticalAxisLocationSupported() const 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 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; @@ -1329,6 +1657,363 @@ ChartTwoOverlay::isCartesianVerticalAxisLocationSupported() const 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 @@ ChartTwoOverlay::saveToScene(const SceneAttributes* sceneAttributes, 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 @@ ChartTwoOverlay::restoreFromScene(const SceneAttributes* sceneAttributes, 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 @@ ChartTwoOverlay::restoreFromScene(const SceneAttributes* sceneAttributes, 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 @@ ChartTwoOverlay::restoreFromScene(const SceneAttributes* sceneAttributes, 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 --git a/src/Brain/ChartTwoOverlay.h b/src/Brain/ChartTwoOverlay.h index 8c429f844abe1b4022923a1a6aada4dfe91232b6..5c8ef6d669194b04ba663bdb0e903f00ab2f8543 100644 --- a/src/Brain/ChartTwoOverlay.h +++ b/src/Brain/ChartTwoOverlay.h @@ -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 @@ namespace caret { class AnnotationColorBar; class BoundingBox; class CaretMappableDataFile; + class ChartTwoDataCartesian; class ChartTwoOverlaySet; + class Matrix4x4; class PlainTextStringBuilder; class SceneClassAssistant; @@ -61,7 +68,7 @@ namespace caret { ChartTwoDataTypeEnum::Enum getChartTwoDataType() const; - ChartTwoCompoundDataType getChartTwoCompoundDataType() const; + const ChartTwoCompoundDataType* getChartTwoCompoundDataType() const; void setChartTwoCompoundDataType(const ChartTwoCompoundDataType& chartCompoundDataType); @@ -101,12 +108,20 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { /** 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 --git a/src/Brain/ChartTwoOverlayActiveModeEnum.cxx b/src/Brain/ChartTwoOverlayActiveModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..63d644b3d3b61fb6fc63088cd187a7cde82f9712 --- /dev/null +++ b/src/Brain/ChartTwoOverlayActiveModeEnum.cxx @@ -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 --git a/src/Brain/ChartTwoOverlayActiveModeEnum.h b/src/Brain/ChartTwoOverlayActiveModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..83a73f96128ba4bfb8113e5a159a16a4c3fd07e5 --- /dev/null +++ b/src/Brain/ChartTwoOverlayActiveModeEnum.h @@ -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 --git a/src/Brain/ChartTwoOverlaySet.cxx b/src/Brain/ChartTwoOverlaySet.cxx index 6a44acb528aa20e611cfcf873d58afd7ee50487b..d101bf93883443e70d758c694070d2429fa63b84 100644 --- a/src/Brain/ChartTwoOverlaySet.cxx +++ b/src/Brain/ChartTwoOverlaySet.cxx @@ -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 @@ ChartTwoOverlaySet::ChartTwoOverlaySet(const ChartTwoDataTypeEnum::Enum chartDat 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_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); - m_chartAxisLeft->setEnabledByChart(false); - m_chartAxisRight->setEnabledByChart(false); - m_chartAxisBottom->setEnabledByChart(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 @@ m_tabIndex(tabIndex) 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 @@ ChartTwoOverlaySet::copyOverlaySet(const ChartTwoOverlaySet* overlaySet) 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,12 +223,21 @@ ChartTwoOverlaySet::copyCartesianAxes(const ChartTwoOverlaySet* overlaySet) { 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. */ @@ -403,6 +436,48 @@ ChartTwoOverlaySet::getNumberOfDisplayedOverlays() const return m_numberOfDisplayedOverlays; } +/** + * 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 @@ -417,6 +492,10 @@ ChartTwoOverlaySet::insertOverlayAbove(const int32_t overlayIndex) 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 @@ ChartTwoOverlaySet::insertOverlayBelow(const int32_t overlayIndex) 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 @@ ChartTwoOverlaySet::firstOverlaySelectionChanged() 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 @@ ChartTwoOverlaySet::toString() const 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 @@ ChartTwoOverlaySet::receiveEvent(Event* event) 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 @@ ChartTwoOverlaySet::receiveEvent(Event* event) 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 @@ ChartTwoOverlaySet::getDataRangeForAxis(const ChartAxisLocationEnum::Enum chartA 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()); @@ -868,6 +843,71 @@ ChartTwoOverlaySet::getDataRangeForAxis(const ChartAxisLocationEnum::Enum chartA return false; } +/** + * 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? */ @@ -881,10 +921,14 @@ ChartTwoOverlaySet::isAxesSupportedByChartDataType() const 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 @@ ChartTwoOverlaySet::getDisplayedChartAxes(std::vector& a { 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 @@ ChartTwoOverlaySet::getAxisLabel(const ChartTwoCartesianAxis* axis) const 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 @@ ChartTwoOverlaySet::getAxisLabel(const ChartTwoCartesianAxis* axis) const 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 @@ ChartTwoOverlaySet::getAxisLabel(const ChartTwoCartesianAxis* axis) const 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 @@ ChartTwoOverlaySet::setAxisLabel(const ChartTwoCartesianAxis* axis, 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 @@ ChartTwoOverlaySet::setAxisLabel(const ChartTwoCartesianAxis* axis, 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 @@ ChartTwoOverlaySet::setAxisLabel(const ChartTwoCartesianAxis* axis, 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; } } @@ -1096,6 +1430,69 @@ ChartTwoOverlaySet::setAxisLineThickness(const float axisLineThickness) m_axisLineThickness = axisLineThickness; } +/** + * 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. * @@ -1113,7 +1510,7 @@ ChartTwoOverlaySet::saveToScene(const SceneAttributes* sceneAttributes, { SceneClass* sceneClass = new SceneClass(instanceName, "ChartTwoOverlaySet", - 1); + 2); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -1135,6 +1532,118 @@ ChartTwoOverlaySet::saveToScene(const SceneAttributes* sceneAttributes, return sceneClass; } +/** + * 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. * @@ -1155,24 +1664,31 @@ ChartTwoOverlaySet::restoreFromScene(const SceneAttributes* sceneAttributes, } 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 @@ ChartTwoOverlaySet::restoreFromScene(const SceneAttributes* sceneAttributes, 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 --git a/src/Brain/ChartTwoOverlaySet.h b/src/Brain/ChartTwoOverlaySet.h index c4e8dfec983e62681c72b5b4e6bb4eef93bd63e8..074d541f037c754df25243c3eea3c5a5f20ced21 100644 --- a/src/Brain/ChartTwoOverlaySet.h +++ b/src/Brain/ChartTwoOverlaySet.h @@ -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 @@ namespace caret { void copyCartesianAxes(const ChartTwoOverlaySet* overlaySet); + ChartTwoDataTypeEnum::Enum getChartTwoDataType() const; + ChartTwoOverlay* getPrimaryOverlay(); const ChartTwoOverlay* getPrimaryOverlay() const; @@ -74,6 +79,40 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { /** 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 --git a/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx b/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx index 661619556803b53f62d107a3076dc30c7c48ae19..dc0cc142ec2b118bcd04700e55a85245ab546f94 100644 --- a/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx +++ b/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx @@ -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 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromParcelFile(Brain* bra CiftiConnectivityMatrixParcelFile* parcelFile, const int32_t rowIndex, const int32_t columnIndex, - std::vector& rowColumnInformationOut) + std::vector& rowColumnInformationOut, + HtmlTableBuilder& htmlTableBuilder) { CaretAssert(parcelFile); @@ -194,6 +196,8 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromParcelFile(Brain* bra 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 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromParcelFile(Brain* bra 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 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromConnectivityMatrixFil 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 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromConnectivityMatrixFil 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 @@ CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromConnectivityMatrixFil 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 @@ bool 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 @@ CiftiConnectivityMatrixDataFileManager::loadDataForSurfaceNode(Brain* brain, + 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 @@ CiftiConnectivityMatrixDataFileManager::loadDataForSurfaceNode(Brain* brain, + 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 @@ CiftiConnectivityMatrixDataFileManager::loadAverageDataForSurfaceNodes(Brain* br 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 @@ CiftiConnectivityMatrixDataFileManager::loadDataForVoxelAtCoordinate(Brain* brai + 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 @@ CiftiConnectivityMatrixDataFileManager::loadDataForVoxelAtCoordinate(Brain* brai + AString::fromNumbers(xyz, 3, ",") + ", column index=" + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())); + htmlTableBuilder.addRow(("Column Index: " + AString::number(columnIndex + CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI())), + cmf->getFileNameNoPath()); } } } diff --git a/src/Brain/CiftiConnectivityMatrixDataFileManager.h b/src/Brain/CiftiConnectivityMatrixDataFileManager.h index ed5f5157432abcf5574b9b8e30a2bc4ede5615cf..d73d70efe82ee0e1af7c4765e5141eac5ba1d6f8 100644 --- a/src/Brain/CiftiConnectivityMatrixDataFileManager.h +++ b/src/Brain/CiftiConnectivityMatrixDataFileManager.h @@ -30,6 +30,7 @@ namespace caret { class Brain; class CiftiConnectivityMatrixParcelFile; class CiftiMappableConnectivityMatrixDataFile; + class HtmlTableBuilder; class SurfaceFile; class CiftiConnectivityMatrixDataFileManager @@ -44,7 +45,8 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Brain/CiftiFiberTrajectoryManager.cxx b/src/Brain/CiftiFiberTrajectoryManager.cxx index a11f1dae710c5f2cd6ee1dbce8245d85b74123c2..33fb96048538d83fbf6223bf92515ff2d1329a52 100644 --- a/src/Brain/CiftiFiberTrajectoryManager.cxx +++ b/src/Brain/CiftiFiberTrajectoryManager.cxx @@ -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 @@ CiftiFiberTrajectoryManager::~CiftiFiberTrajectoryManager() /** * 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 @@ bool 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 @@ CiftiFiberTrajectoryManager::loadDataForSurfaceNode(Brain* brain, + " 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 @@ CiftiFiberTrajectoryManager::loadDataForSurfaceNode(Brain* brain, /** * 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 @@ CiftiFiberTrajectoryManager::loadDataAverageForSurfaceNodes(Brain* brain, /** * 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 @@ CiftiFiberTrajectoryManager::loadDataForVoxelAtCoordinate(Brain* brain, + 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 @@ CiftiFiberTrajectoryManager::loadDataForVoxelAtCoordinate(Brain* brain, /** * Load average data for the given voxel indices. * + * @param brain + * The brain * @param volumeDimensionIJK * Dimensions of the volume. * @param voxelIndices diff --git a/src/Brain/CiftiFiberTrajectoryManager.h b/src/Brain/CiftiFiberTrajectoryManager.h index 22581b636691f575c47df54a40aa0c44025c11cc..08b89abb586ee678b1d315b6f2d8ce839ed2deaf 100644 --- a/src/Brain/CiftiFiberTrajectoryManager.h +++ b/src/Brain/CiftiFiberTrajectoryManager.h @@ -28,6 +28,7 @@ namespace caret { class Brain; + class HtmlTableBuilder; class SurfaceFile; class CiftiFiberTrajectoryManager : public CaretObject { @@ -40,14 +41,16 @@ namespace caret { 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 --git a/src/Brain/ClippingPlaneGroup.cxx b/src/Brain/ClippingPlaneGroup.cxx index f0bc41133275b2bd3ea6eccf42a2d59ed142b88d..de472ec8091edad334d4ad19cf9914d0a9ba3136 100644 --- a/src/Brain/ClippingPlaneGroup.cxx +++ b/src/Brain/ClippingPlaneGroup.cxx @@ -50,6 +50,7 @@ ClippingPlaneGroup::ClippingPlaneGroup() 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 @@ ClippingPlaneGroup::copyHelperClippingPlaneGroup(const ClippingPlaneGroup& obj) m_thickness[i] = obj.m_thickness[i]; } + /* m_enabledStatus NOT copied */ + m_rotationMatrix = obj.m_rotationMatrix; m_xAxisSelectionStatus = obj.m_xAxisSelectionStatus; @@ -128,6 +131,26 @@ ClippingPlaneGroup::copyHelperClippingPlaneGroup(const ClippingPlaneGroup& obj) invalidateActiveClippingPlainEquations(); } +/** + * @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. */ @@ -155,8 +178,6 @@ ClippingPlaneGroup::resetToDefaultValues() { resetTransformation(); - m_displayClippingBoxStatus = false; - m_xAxisSelectionStatus = false; m_yAxisSelectionStatus = false; m_zAxisSelectionStatus = false; @@ -550,11 +571,13 @@ ClippingPlaneGroup::isDisplayClippingBoxSelected() const 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 @@ ClippingPlaneGroup::saveToScene(const SceneAttributes* sceneAttributes, { SceneClass* sceneClass = new SceneClass(instanceName, "ClippingPlaneGroup", - 1); + 2); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -844,6 +867,7 @@ void ClippingPlaneGroup::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { + m_enabledStatus = false; resetToDefaultValues(); if (sceneClass == NULL) { @@ -863,6 +887,15 @@ ClippingPlaneGroup::restoreFromScene(const SceneAttributes* sceneAttributes, 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 --git a/src/Brain/ClippingPlaneGroup.h b/src/Brain/ClippingPlaneGroup.h index 422d738e0cabc1aeeaeb889dff75245fb668b4f9..7664d964a847e0c7c93fc18127af899025a2049c 100644 --- a/src/Brain/ClippingPlaneGroup.h +++ b/src/Brain/ClippingPlaneGroup.h @@ -47,6 +47,10 @@ namespace caret { 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 @@ namespace caret { float m_thickness[3]; + bool m_enabledStatus = false; + bool m_xAxisSelectionStatus; bool m_yAxisSelectionStatus; diff --git a/src/Brain/DataToolTipsManager.cxx b/src/Brain/DataToolTipsManager.cxx index 247bcc092710229f57a7d8fa29e6037c528e5de2..447184bf0d069540e68e73047899f616651dc0e4 100644 --- a/src/Brain/DataToolTipsManager.cxx +++ b/src/Brain/DataToolTipsManager.cxx @@ -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 @@ DataToolTipsManager::getToolTip(const Brain* brain, 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 --git a/src/Brain/DisplayPropertiesBorders.cxx b/src/Brain/DisplayPropertiesBorders.cxx index 8b8d840897cc65101949cb0f21f5af4498a67c75..899745e4d6ab06c9f0c8c9c1b730c5e36822b819 100644 --- a/src/Brain/DisplayPropertiesBorders.cxx +++ b/src/Brain/DisplayPropertiesBorders.cxx @@ -208,6 +208,7 @@ DisplayPropertiesBorders::copyDisplayProperties(const int32_t sourceTabIndex, 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 --git a/src/Brain/DisplayPropertiesFiberOrientation.cxx b/src/Brain/DisplayPropertiesFiberOrientation.cxx index f1c29fe8b41cf58d8e43814bedac8865659c91aa..a151de8d818bc6c4f54c38286acad4f8bdd3eacb 100644 --- a/src/Brain/DisplayPropertiesFiberOrientation.cxx +++ b/src/Brain/DisplayPropertiesFiberOrientation.cxx @@ -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 @@ using namespace caret; /** * 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 @@ DisplayPropertiesFiberOrientation::DisplayPropertiesFiberOrientation() 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 @@ DisplayPropertiesFiberOrientation::~DisplayPropertiesFiberOrientation() void DisplayPropertiesFiberOrientation::reset() { + m_maximumUncertainty->setAllValues(s_defaultMaximumUncertainty); } /** @@ -203,6 +217,7 @@ DisplayPropertiesFiberOrientation::copyDisplayProperties(const int32_t sourceTab m_lengthMultiplierInTab[targetTabIndex] = m_lengthMultiplierInTab[sourceTabIndex]; m_fiberColoringTypeInTab[targetTabIndex] = m_fiberColoringTypeInTab[sourceTabIndex]; m_fanMultiplierInTab[targetTabIndex] = m_fanMultiplierInTab[sourceTabIndex]; + m_maximumUncertainty->copyValues(sourceTabIndex, targetTabIndex); } /** @@ -564,6 +579,40 @@ DisplayPropertiesFiberOrientation::setMinimumMagnitude(const DisplayGroupEnum::E } } +/** + * @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 @@ -773,7 +822,9 @@ DisplayPropertiesFiberOrientation::restoreFromScene(const SceneAttributes* scene break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; - } + } + + setMaximumUncertaintyFromFiles(); } /** @@ -827,3 +878,27 @@ DisplayPropertiesFiberOrientation::setFanMultiplier(const DisplayGroupEnum::Enum } } +/** + * 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 --git a/src/Brain/DisplayPropertiesFiberOrientation.h b/src/Brain/DisplayPropertiesFiberOrientation.h index a355090bd929fedd6d7174b30f9afdf236fba83d..48b5d303aff0f31f7b983dae892020eeac3ea23c 100644 --- a/src/Brain/DisplayPropertiesFiberOrientation.h +++ b/src/Brain/DisplayPropertiesFiberOrientation.h @@ -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 @@ namespace caret { virtual void update(); + void setMaximumUncertaintyFromFiles(); + virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); @@ -86,6 +94,13 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Brain/DisplayPropertiesFoci.cxx b/src/Brain/DisplayPropertiesFoci.cxx index e64ae90e28ebd8e5d66fe7eee5fba1b7253b5365..292ec58486a13eb25f3acf90efd867fd8e7a4763 100644 --- a/src/Brain/DisplayPropertiesFoci.cxx +++ b/src/Brain/DisplayPropertiesFoci.cxx @@ -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 @@ DisplayPropertiesFoci::DisplayPropertiesFoci() : 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 @@ DisplayPropertiesFoci::DisplayPropertiesFoci() 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 @@ DisplayPropertiesFoci::DisplayPropertiesFoci() 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 @@ DisplayPropertiesFoci::copyDisplayProperties(const int32_t sourceTabIndex, 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 @@ DisplayPropertiesFoci::copyDisplayProperties(const int32_t sourceTabIndex, 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); } /** @@ -297,13 +318,49 @@ DisplayPropertiesFoci::setDisplayGroupForTab(const int32_t browserTabIndex, m_displayGroup[browserTabIndex] = displayGroup; } +/** + * @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 @@ DisplayPropertiesFoci::getFociSize(const DisplayGroupEnum::Enum displayGroup, * 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) { @@ -344,6 +403,40 @@ DisplayPropertiesFoci::setFociSize(const DisplayGroupEnum::Enum displayGroup, } } +/** + * @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 @@ -485,6 +578,53 @@ DisplayPropertiesFoci::setDrawingType(const DisplayGroupEnum::Enum displayGroup, } } +/** + * @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 @@ -586,6 +726,7 @@ DisplayPropertiesFoci::restoreFromScene(const SceneAttributes* sceneAttributes, if (sceneClass == NULL) { return; } + resetPrivate(); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); @@ -596,6 +737,5 @@ DisplayPropertiesFoci::restoreFromScene(const SceneAttributes* sceneAttributes, case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } - } diff --git a/src/Brain/DisplayPropertiesFoci.h b/src/Brain/DisplayPropertiesFoci.h index 34a930113b8ec234568e7224b5800352c7ec474c..671c956536092090651331e5973bf4b7c023ed2a 100644 --- a/src/Brain/DisplayPropertiesFoci.h +++ b/src/Brain/DisplayPropertiesFoci.h @@ -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 @@ namespace caret { 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 setFociSizeMillimeters(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const float pointSize); + + float getFociSizePercentage(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const; - void setFociSize(const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex, - const float pointSize); + 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 @@ namespace caret { 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 @@ namespace caret { const SceneClass* sceneClass); private: + void resetPrivate(); + DisplayPropertiesFoci(const DisplayPropertiesFoci&); DisplayPropertiesFoci& operator=(const DisplayPropertiesFoci&); @@ -123,6 +152,10 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Brain/DisplayPropertiesSurface.cxx b/src/Brain/DisplayPropertiesSurface.cxx index 9e2c2f97a9982d9f948d8017d0bab0478c30c571..916960216d0d6eec1b118e6207ee75c52847439c 100644 --- a/src/Brain/DisplayPropertiesSurface.cxx +++ b/src/Brain/DisplayPropertiesSurface.cxx @@ -48,6 +48,7 @@ DisplayPropertiesSurface::DisplayPropertiesSurface() 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 @@ DisplayPropertiesSurface::DisplayPropertiesSurface() &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 @@ void DisplayPropertiesSurface::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { + resetDefaultColorRGB(); + if (sceneClass == NULL) { return; } @@ -270,4 +277,53 @@ DisplayPropertiesSurface::setOpacity(const float opacity) 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 --git a/src/Brain/DisplayPropertiesSurface.h b/src/Brain/DisplayPropertiesSurface.h index 2137234175ffb6ce41efe60d823bd05b251e13f0..c74d98d67634a661e80ac99651c70cc72dce85d1 100644 --- a/src/Brain/DisplayPropertiesSurface.h +++ b/src/Brain/DisplayPropertiesSurface.h @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "DisplayProperties.h" #include "SurfaceDrawingTypeEnum.h" @@ -68,6 +70,16 @@ namespace caret { 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 @@ namespace caret { SurfaceDrawingTypeEnum::Enum m_surfaceDrawingType; float m_opacity; + + std::array m_defaultColorRGB; + + bool m_backfaceCullingEnabled = false; }; #ifdef __DISPLAY_PROPERTIES_SURFACE_DECLARE__ diff --git a/src/Brain/DisplayPropertyDataEnum.h b/src/Brain/DisplayPropertyDataEnum.h index 70fb7e4de803f53b12e97dc673b24394fa9b6e86..7f72db818ce1a2ebdf7c005b7f37a3e3ecaddd6d 100644 --- a/src/Brain/DisplayPropertyDataEnum.h +++ b/src/Brain/DisplayPropertyDataEnum.h @@ -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 --git a/src/Brain/EventAnnotationColorBarGet.cxx b/src/Brain/EventAnnotationBarsGet.cxx similarity index 52% rename from src/Brain/EventAnnotationColorBarGet.cxx rename to src/Brain/EventAnnotationBarsGet.cxx index 45a000c1a51fb5efbd600632016b956a532e33af..06ddd054d8cce052a220d7f2a2a0255941faa06b 100644 --- a/src/Brain/EventAnnotationColorBarGet.cxx +++ b/src/Brain/EventAnnotationBarsGet.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ -#include "EventAnnotationColorBarGet.h" -#undef __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ +#define __EVENT_ANNOTATION_BARS_GET_DECLARE__ +#include "EventAnnotationBarsGet.h" +#undef __EVENT_ANNOTATION_BARS_GET_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" @@ -31,43 +31,43 @@ using namespace caret; /** - * \class caret::EventAnnotationColorBarGet + * \class caret::EventAnnotationBarsGet * \brief Event to get annontation color bars for tab(s). * \ingroup Brain */ /** - * Constructor for getting annotations for ALL tabs. + * Constructor for getting annotation bars for ALL tabs. */ -EventAnnotationColorBarGet::EventAnnotationColorBarGet() -: Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), +EventAnnotationBarsGet::EventAnnotationBarsGet() +: Event(EventTypeEnum::EVENT_ANNOTATION_BARS_GET), m_allTabsFlag(true) { } /** - * Constructor for getting annotations for the given tab index. + * Constructor for getting annotation bars for the given tab index. * * @param tabIndex - * Index of tab for which color bars are requested. + * Index of tab for which annotation bars are requested. */ -EventAnnotationColorBarGet::EventAnnotationColorBarGet(const int32_t tabIndex) -: Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), +EventAnnotationBarsGet::EventAnnotationBarsGet(const int32_t tabIndex) +: Event(EventTypeEnum::EVENT_ANNOTATION_BARS_GET), m_allTabsFlag(false) { m_tabIndices.insert(tabIndex); } /** - * Constructor for getting annotations for the given tab indices. + * Constructor for getting annotation bars 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. + * Indices of tabs for which annotation bars are requested. If + * the indices are empty no bars will be gotten. */ -EventAnnotationColorBarGet::EventAnnotationColorBarGet(const std::vector& tabIndices) -: Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), +EventAnnotationBarsGet::EventAnnotationBarsGet(const std::vector& tabIndices) +: Event(EventTypeEnum::EVENT_ANNOTATION_BARS_GET), m_allTabsFlag(false) { m_tabIndices.insert(tabIndices.begin(), @@ -77,18 +77,18 @@ m_allTabsFlag(false) /** * Destructor. */ -EventAnnotationColorBarGet::~EventAnnotationColorBarGet() +EventAnnotationBarsGet::~EventAnnotationBarsGet() { } /** * Add annotation color bars. - * + * * @param annotationColorBars * Annotation color bars that are added. */ void -EventAnnotationColorBarGet::addAnnotationColorBars(const std::vector& annotationColorBars) +EventAnnotationBarsGet::addAnnotationColorBars(const std::vector& annotationColorBars) { m_annotationColorBars.insert(m_annotationColorBars.end(), annotationColorBars.begin(), @@ -96,15 +96,28 @@ EventAnnotationColorBarGet::addAnnotationColorBars(const std::vector -EventAnnotationColorBarGet::getAnnotationColorBars() const +EventAnnotationBarsGet::getAnnotationColorBars() const { return m_annotationColorBars; } +/** + * @return The annotation scale bars after event completes. + */ +std::vector +EventAnnotationBarsGet::getAnnotationScaleBars() const +{ + return m_annotationScaleBars; +} diff --git a/src/Brain/EventAnnotationColorBarGet.h b/src/Brain/EventAnnotationBarsGet.h similarity index 64% rename from src/Brain/EventAnnotationColorBarGet.h rename to src/Brain/EventAnnotationBarsGet.h index 9cec78baf927312cec881b1dd6374d0a59ff6550..ce3d9de6ec32e66dd3da379db64710c44868e714 100644 --- a/src/Brain/EventAnnotationColorBarGet.h +++ b/src/Brain/EventAnnotationBarsGet.h @@ -1,5 +1,5 @@ -#ifndef __EVENT_ANNOTATION_COLOR_BAR_GET_H__ -#define __EVENT_ANNOTATION_COLOR_BAR_GET_H__ +#ifndef __EVENT_ANNOTATION_BARS_GET_H__ +#define __EVENT_ANNOTATION_BARS_GET_H__ /*LICENSE_START*/ /* @@ -29,30 +29,35 @@ namespace caret { class AnnotationColorBar; + class AnnotationScaleBar; - class EventAnnotationColorBarGet : public Event { + class EventAnnotationBarsGet : public Event { public: - EventAnnotationColorBarGet(); + EventAnnotationBarsGet(); - EventAnnotationColorBarGet(const int32_t tabIndex); + EventAnnotationBarsGet(const int32_t tabIndex); - EventAnnotationColorBarGet(const std::vector& tabIndices); + EventAnnotationBarsGet(const std::vector& tabIndices); - virtual ~EventAnnotationColorBarGet(); + 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: - EventAnnotationColorBarGet(const EventAnnotationColorBarGet&); + EventAnnotationBarsGet(const EventAnnotationBarsGet&); - EventAnnotationColorBarGet& operator=(const EventAnnotationColorBarGet&); + EventAnnotationBarsGet& operator=(const EventAnnotationBarsGet&); bool m_allTabsFlag; @@ -60,13 +65,15 @@ namespace caret { std::vector m_annotationColorBars; + std::vector m_annotationScaleBars; + // ADD_NEW_MEMBERS_HERE }; -#ifdef __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ +#ifdef __EVENT_ANNOTATION_BARS_GET_DECLARE__ // -#endif // __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ +#endif // __EVENT_ANNOTATION_BARS_GET_DECLARE__ } // namespace -#endif //__EVENT_ANNOTATION_COLOR_BAR_GET_H__ +#endif //__EVENT_ANNOTATION_BARS_GET_H__ diff --git a/src/Brain/EventBrowserTabReopenAvailable.cxx b/src/Brain/EventBrowserTabReopenAvailable.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f624f5bd3d6bbc1acfa7f388778a85bceb5fecea --- /dev/null +++ b/src/Brain/EventBrowserTabReopenAvailable.cxx @@ -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 --git a/src/Brain/EventBrowserTabReopenAvailable.h b/src/Brain/EventBrowserTabReopenAvailable.h new file mode 100644 index 0000000000000000000000000000000000000000..77e8d54049820757307da4c5926c8dbafa32d14f --- /dev/null +++ b/src/Brain/EventBrowserTabReopenAvailable.h @@ -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 --git a/src/Brain/EventBrowserWindowGetTabs.cxx b/src/Brain/EventBrowserWindowGetTabs.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6691a355e0f30413c1bef0f36bf645cc58b90cd8 --- /dev/null +++ b/src/Brain/EventBrowserWindowGetTabs.cxx @@ -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 --git a/src/Brain/EventBrowserWindowGetTabs.h b/src/Brain/EventBrowserWindowGetTabs.h new file mode 100644 index 0000000000000000000000000000000000000000..deb69bcde4ef8829af5504dce93587e726d1d55d --- /dev/null +++ b/src/Brain/EventBrowserWindowGetTabs.h @@ -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 --git a/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx b/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx index e4ecd5e5755063514865fa5d828f3848a5bf0397..76d42cf64355662c87683bc71d65998df2a72c83 100644 --- a/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx +++ b/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.cxx @@ -54,39 +54,64 @@ EventCaretMappableDataFilesAndMapsInDisplayedOverlays::~EventCaretMappableDataFi } /** - * 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 @@ EventCaretMappableDataFilesAndMapsInDisplayedOverlays::addChartFileAndMap(CaretM * 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 @@ EventCaretMappableDataFilesAndMapsInDisplayedOverlays::addBrainordinateFileAndMa * @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)); + 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_chartMapFilesAndIndices) { - infoOut.push_back(FileInfo(OverlayType::CHART, - 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 + */ +bool +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::satisfiesConstraints(const int32_t tabIndex) +{ + 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 */ -std::map> -EventCaretMappableDataFilesAndMapsInDisplayedOverlays::getMapFilesAndIndices() const +void +EventCaretMappableDataFilesAndMapsInDisplayedOverlays::setTabIndicesConstraint(const std::set& tabIndices) { - return m_mapFilesAndIndices; + m_tabIndices = tabIndices; } /** @@ -170,12 +276,27 @@ EventCaretMappableDataFilesAndMapsInDisplayedOverlays::getMapFilesAndIndices() c * @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 --git a/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h b/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h index b1af03efda58d2f285c853a68139904e70277f31..c9216df17731ed1ef395a218a162d007107b0cdd 100644 --- a/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h +++ b/src/Brain/EventCaretMappableDataFilesAndMapsInDisplayedOverlays.h @@ -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 @@ namespace caret { 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); + void addChartOneFileAndMap(CaretMappableDataFile* mapFile, + const int32_t mapIndex, + const int32_t tabIndex); + + void addChartTwoFileAndMap(CaretMappableDataFile* mapFile, + const int32_t mapIndex, + const int32_t tabIndex); - std::map> getMapFilesAndIndices() const; + 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 --git a/src/Brain/EventChartOverlayValidate.cxx b/src/Brain/EventChartTwoOverlayValidate.cxx similarity index 73% rename from src/Brain/EventChartOverlayValidate.cxx rename to src/Brain/EventChartTwoOverlayValidate.cxx index 61d2b7f9f86ac35abc11692ceb12097c690af900..7261d53c5fef2ebb43424f4296534d66d6684d9f 100644 --- a/src/Brain/EventChartOverlayValidate.cxx +++ b/src/Brain/EventChartTwoOverlayValidate.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ -#include "EventChartOverlayValidate.h" -#undef __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ +#define __EVENT_CHART_TWO_OVERLAY_VALIDATE_DECLARE__ +#include "EventChartTwoOverlayValidate.h" +#undef __EVENT_CHART_TWO_OVERLAY_VALIDATE_DECLARE__ #include "EventTypeEnum.h" @@ -30,7 +30,7 @@ using namespace caret; /** - * \class caret::EventChartOverlayValidate + * \class caret::EventChartTwoOverlayValidate * \brief Test a chart overlay for validity (it exists). * \ingroup Brain */ @@ -41,8 +41,8 @@ using namespace caret; * @param chartOverlay * Chart overlay for verification. */ -EventChartOverlayValidate::EventChartOverlayValidate(const ChartTwoOverlay* chartOverlay) -: Event(EventTypeEnum::EVENT_CHART_OVERLAY_VALIDATE), +EventChartTwoOverlayValidate::EventChartTwoOverlayValidate(const ChartTwoOverlay* chartOverlay) +: Event(EventTypeEnum::EVENT_CHART_TWO_OVERLAY_VALIDATE), m_chartOverlay(chartOverlay) { m_valid = false; @@ -51,7 +51,7 @@ EventChartOverlayValidate::EventChartOverlayValidate(const ChartTwoOverlay* char /** * Destructor. */ -EventChartOverlayValidate::~EventChartOverlayValidate() +EventChartTwoOverlayValidate::~EventChartTwoOverlayValidate() { } @@ -60,7 +60,7 @@ EventChartOverlayValidate::~EventChartOverlayValidate() * @return true if the chart overlay was found to be valid. */ bool -EventChartOverlayValidate::isValidChartOverlay() const +EventChartTwoOverlayValidate::isValidChartOverlay() const { return m_valid; } @@ -73,7 +73,7 @@ EventChartOverlayValidate::isValidChartOverlay() const * Chart overlay tested for match. */ void -EventChartOverlayValidate::testValidChartOverlay(const ChartTwoOverlay* chartOverlay) +EventChartTwoOverlayValidate::testValidChartOverlay(const ChartTwoOverlay* chartOverlay) { if (m_chartOverlay == chartOverlay) { m_valid = true; diff --git a/src/Brain/EventChartOverlayValidate.h b/src/Brain/EventChartTwoOverlayValidate.h similarity index 69% rename from src/Brain/EventChartOverlayValidate.h rename to src/Brain/EventChartTwoOverlayValidate.h index 7f4c8dec939e62d389afb0669fc05fb59288ebf9..4f5f2f1af31f6354e24259e2eccf74da184c2651 100644 --- a/src/Brain/EventChartOverlayValidate.h +++ b/src/Brain/EventChartTwoOverlayValidate.h @@ -1,5 +1,5 @@ -#ifndef __EVENT_CHART_OVERLAY_VALIDATE_H__ -#define __EVENT_CHART_OVERLAY_VALIDATE_H__ +#ifndef __EVENT_CHART_TWO_OVERLAY_VALIDATE_H__ +#define __EVENT_CHART_TWO_OVERLAY_VALIDATE_H__ /*LICENSE_START*/ /* @@ -28,21 +28,21 @@ namespace caret { class ChartTwoOverlay; - class EventChartOverlayValidate : public Event { + class EventChartTwoOverlayValidate : public Event { public: - EventChartOverlayValidate(const ChartTwoOverlay* chartOverlay); + EventChartTwoOverlayValidate(const ChartTwoOverlay* chartOverlay); - virtual ~EventChartOverlayValidate(); + virtual ~EventChartTwoOverlayValidate(); bool isValidChartOverlay() const; void testValidChartOverlay(const ChartTwoOverlay* chartOverlay); private: - EventChartOverlayValidate(const EventChartOverlayValidate&); + EventChartTwoOverlayValidate(const EventChartTwoOverlayValidate&); - EventChartOverlayValidate& operator=(const EventChartOverlayValidate&); + EventChartTwoOverlayValidate& operator=(const EventChartTwoOverlayValidate&); public: @@ -58,9 +58,9 @@ namespace caret { }; -#ifdef __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ +#ifdef __EVENT_CHART_TWO_OVERLAY_VALIDATE_DECLARE__ // -#endif // __EVENT_CHART_OVERLAY_VALIDATE_DECLARE__ +#endif // __EVENT_CHART_TWO_OVERLAY_VALIDATE_DECLARE__ } // namespace -#endif //__EVENT_CHART_OVERLAY_VALIDATE_H__ +#endif //__EVENT_CHART_TWO_OVERLAY_VALIDATE_H__ diff --git a/src/Brain/EventDataFileReloadAll.cxx b/src/Brain/EventDataFileReloadAll.cxx new file mode 100644 index 0000000000000000000000000000000000000000..9a61dcea2a683f0e312dfdedc8fe751c17ebaeac --- /dev/null +++ b/src/Brain/EventDataFileReloadAll.cxx @@ -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 --git a/src/Brain/EventDataFileReloadAll.h b/src/Brain/EventDataFileReloadAll.h new file mode 100644 index 0000000000000000000000000000000000000000..b6c8c2e88342bdc4975f11eb023f162ac0bdfeca --- /dev/null +++ b/src/Brain/EventDataFileReloadAll.h @@ -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 --git a/src/Brain/EventUserInputModeGet.cxx b/src/Brain/EventUserInputModeGet.cxx new file mode 100644 index 0000000000000000000000000000000000000000..013470eba3bb6909fa637ceaf8d2fc84b7c0f3eb --- /dev/null +++ b/src/Brain/EventUserInputModeGet.cxx @@ -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 --git a/src/Brain/EventUserInputModeGet.h b/src/Brain/EventUserInputModeGet.h new file mode 100644 index 0000000000000000000000000000000000000000..aa0f167736b898fb693e7d744db4b8a3bcff40c7 --- /dev/null +++ b/src/Brain/EventUserInputModeGet.h @@ -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 --git a/src/Brain/FociDrawingProjectionTypeEnum.cxx b/src/Brain/FociDrawingProjectionTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..603243bde440ab5680841a85401b7f9156ace9f2 --- /dev/null +++ b/src/Brain/FociDrawingProjectionTypeEnum.cxx @@ -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 --git a/src/Brain/FociDrawingProjectionTypeEnum.h b/src/Brain/FociDrawingProjectionTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..89dbee2a545bb945ec0151576fb9f07fa7ee9776 --- /dev/null +++ b/src/Brain/FociDrawingProjectionTypeEnum.h @@ -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 --git a/src/Brain/GapsAndMargins.cxx b/src/Brain/GapsAndMargins.cxx index f2d9322fe59e57d63d513d94de5f4ac2e013c56f..5dc39e369a343c23cde75aabedf22082f838a1ab 100644 --- a/src/Brain/GapsAndMargins.cxx +++ b/src/Brain/GapsAndMargins.cxx @@ -118,8 +118,10 @@ GapsAndMargins::receiveEvent(Event* event) 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 --git a/src/Brain/IdentificationFilter.cxx b/src/Brain/IdentificationFilter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2445e7ecf81e6ac37f6382da9f4a4f836978dffa --- /dev/null +++ b/src/Brain/IdentificationFilter.cxx @@ -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 --git a/src/Brain/IdentificationFilter.h b/src/Brain/IdentificationFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..b5c80e25f9925c2f631a21fcab681e638fc5bb1a --- /dev/null +++ b/src/Brain/IdentificationFilter.h @@ -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 --git a/src/Brain/IdentificationFilterTabSelectionEnum.cxx b/src/Brain/IdentificationFilterTabSelectionEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2b9c1ae7d241a96528aff0c7d727d85ce06a7ce0 --- /dev/null +++ b/src/Brain/IdentificationFilterTabSelectionEnum.cxx @@ -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 --git a/src/Brain/IdentificationFilterTabSelectionEnum.h b/src/Brain/IdentificationFilterTabSelectionEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..0cdf71e10cbcdf6715f14e4c7aa770a858f4d050 --- /dev/null +++ b/src/Brain/IdentificationFilterTabSelectionEnum.h @@ -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 --git a/src/Brain/IdentificationFormattedTextGenerator.cxx b/src/Brain/IdentificationFormattedTextGenerator.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b0de9086ae721a995986a867322949438da9963e --- /dev/null +++ b/src/Brain/IdentificationFormattedTextGenerator.cxx @@ -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 --git a/src/Brain/IdentificationFormattedTextGenerator.h b/src/Brain/IdentificationFormattedTextGenerator.h new file mode 100644 index 0000000000000000000000000000000000000000..8b343fd130ba53f63243f16542f87768af1c3d18 --- /dev/null +++ b/src/Brain/IdentificationFormattedTextGenerator.h @@ -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 --git a/src/Brain/IdentificationHistoryManager.cxx b/src/Brain/IdentificationHistoryManager.cxx new file mode 100644 index 0000000000000000000000000000000000000000..98b7b20da4cb0f7f8c5d17998b7959e267b130e7 --- /dev/null +++ b/src/Brain/IdentificationHistoryManager.cxx @@ -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 --git a/src/Brain/IdentificationHistoryManager.h b/src/Brain/IdentificationHistoryManager.h new file mode 100644 index 0000000000000000000000000000000000000000..dc535ebd831696fd5081deba3a1a84461a831713 --- /dev/null +++ b/src/Brain/IdentificationHistoryManager.h @@ -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 --git a/src/Brain/IdentificationHistoryRecord.cxx b/src/Brain/IdentificationHistoryRecord.cxx new file mode 100644 index 0000000000000000000000000000000000000000..88830ac9c08d6aefac6e728b4ccfd8090400a1cb --- /dev/null +++ b/src/Brain/IdentificationHistoryRecord.cxx @@ -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 --git a/src/Brain/IdentificationHistoryRecord.h b/src/Brain/IdentificationHistoryRecord.h new file mode 100644 index 0000000000000000000000000000000000000000..6b1914bc797e941bb30f36a9826be0b16f541fd7 --- /dev/null +++ b/src/Brain/IdentificationHistoryRecord.h @@ -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 --git a/src/Brain/IdentificationManager.cxx b/src/Brain/IdentificationManager.cxx index 7c032dad3d8c22c07225ef1a494f0d4dd8f153b3..6f4f2cdcae4d0c07188698d70f47e8f99860f63d 100644 --- a/src/Brain/IdentificationManager.cxx +++ b/src/Brain/IdentificationManager.cxx @@ -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 @@ IdentificationManager::IdentificationManager(const CaretPreferences* caretPrefer 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 @@ IdentificationManager::IdentificationManager(const CaretPreferences* caretPrefer &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 @@ IdentificationManager::addIdentifiedItem(IdentifiedItem* item) } } - addIdentifiedItemPrivate(item); + const bool restoringSceneFlag(false); + addIdentifiedItemPrivate(item, + restoringSceneFlag); } /** @@ -128,14 +160,23 @@ IdentificationManager::addIdentifiedItem(IdentifiedItem* item) * @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 @@ IdentificationManager::getIdentificationText() const if (text.isEmpty() == false) { text += "

"; } - text += item->getText(); + text += item->getSimpleText(); } return text; @@ -227,11 +268,26 @@ IdentificationManager::getNodeIdentifiedItemsForSurface(const StructureEnum::Enu 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 @@ IdentificationManager::getIdentifiedItemsForVolume() const 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 @@ IdentificationManager::removeAllIdentifiedItems() delete item; } + m_identificationHistoryManager->clearHistory(); + m_identifiedItems.clear(); m_mostRecentIdentifiedItem = NULL; @@ -382,7 +448,8 @@ IdentificationManager::removeAllIdentifiedSymbols() m_mostRecentIdentifiedItem = NULL; } - itemToKeep = new IdentifiedItem(item->getText()); + itemToKeep = new IdentifiedItem(item->getSimpleText(), + item->getFormattedText()); delete item; } else { @@ -390,7 +457,8 @@ IdentificationManager::removeAllIdentifiedSymbols() } if (itemToKeep != NULL) { - if (itemToKeep->getText().isEmpty()) { + if (itemToKeep->getSimpleText().isEmpty() + && itemToKeep->getFormattedText().isEmpty()) { delete itemToKeep; itemToKeep = NULL; } @@ -423,6 +491,26 @@ IdentificationManager::setContralateralIdentificationEnabled(const bool enabled) m_contralateralIdentificationEnabled = enabled; } +/** + * @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 */ @@ -463,6 +551,46 @@ IdentificationManager::setMostRecentIdentificationSymbolSize(const float symbolS m_identifcationMostRecentSymbolSize = symbolSize; } +/** + * @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. */ @@ -545,6 +673,46 @@ IdentificationManager::setShowVolumeIdentificationSymbols(const bool showVolumeI m_showVolumeIdentificationSymbols = showVolumeIdentificationSymbols; } +/** + * @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. * @@ -606,11 +774,13 @@ IdentificationManager::restoreFromScene(const SceneAttributes* sceneAttributes, 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 @@ IdentificationManager::restoreFromScene(const SceneAttributes* sceneAttributes, IdentifiedItem* item = new IdentifiedItem(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { - addIdentifiedItemPrivate(item); + addIdentifiedItemPrivate(item, + restoringSceneFlag); } else { delete item; @@ -632,7 +803,8 @@ IdentificationManager::restoreFromScene(const SceneAttributes* sceneAttributes, IdentifiedItemNode* item = new IdentifiedItemNode(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { - addIdentifiedItemPrivate(item); + addIdentifiedItemPrivate(item, + restoringSceneFlag); } else { delete item; @@ -642,7 +814,8 @@ IdentificationManager::restoreFromScene(const SceneAttributes* sceneAttributes, IdentifiedItemVoxel* item = new IdentifiedItemVoxel(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { - addIdentifiedItemPrivate(item); + addIdentifiedItemPrivate(item, + restoringSceneFlag); } else { delete item; @@ -681,3 +854,41 @@ IdentificationManager::restoreFromScene(const SceneAttributes* sceneAttributes, } } } + +/** + * @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 --git a/src/Brain/IdentificationManager.h b/src/Brain/IdentificationManager.h index 9e6b3ea51df4dcd73b9ef72eb4b11b1eb05feabe..e053828dfec67b1c25bd25ec3d8217f4d0881634 100644 --- a/src/Brain/IdentificationManager.h +++ b/src/Brain/IdentificationManager.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { private: - void addIdentifiedItemPrivate(IdentifiedItem* item); + void addIdentifiedItemPrivate(IdentifiedItem* item, + const bool restoringSceneFlag); // ADD_NEW_MEMBERS_HERE @@ -122,14 +155,28 @@ namespace caret { 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 --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationSimpleTextGenerator.cxx similarity index 92% rename from src/Brain/IdentificationTextGenerator.cxx rename to src/Brain/IdentificationSimpleTextGenerator.cxx index fe5ec45c7f2cf6c2a6d21b8e108352530775eea0..8edfded5133492b83e742d21c624b057101ace97 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationSimpleTextGenerator.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ -#include "IdentificationTextGenerator.h" -#undef __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ +#define __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ +#include "IdentificationSimpleTextGenerator.h" +#undef __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ #include "Border.h" #include "Brain.h" @@ -36,6 +36,7 @@ #include "ChartableMatrixInterface.h" #include "ChartableTwoFileDelegate.h" #include "ChartableTwoFileHistogramChart.h" +#include "ChartableTwoFileLineLayerChart.h" #include "ChartableTwoFileLineSeriesChart.h" #include "ChartableTwoFileMatrixChart.h" #include "CiftiMappableConnectivityMatrixDataFile.h" @@ -62,6 +63,7 @@ #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemChartTimeSeries.h" #include "SelectionItemChartTwoHistogram.h" +#include "SelectionItemChartTwoLineLayerVerticalNearest.h" #include "SelectionItemChartTwoLineSeries.h" #include "SelectionItemChartTwoMatrix.h" #include "SelectionItemFocusSurface.h" @@ -85,7 +87,7 @@ using namespace caret; /** - * \class IdentificationTextGenerator + * \class IdentificationSimpleTextGenerator * \brief Creates text describing selected data. * * Examine the selected data and generate descriptive text. @@ -94,7 +96,7 @@ using namespace caret; /** * Constructor. */ -IdentificationTextGenerator::IdentificationTextGenerator() +IdentificationSimpleTextGenerator::IdentificationSimpleTextGenerator() : CaretObject() { @@ -103,7 +105,7 @@ IdentificationTextGenerator::IdentificationTextGenerator() /** * Destructor. */ -IdentificationTextGenerator::~IdentificationTextGenerator() +IdentificationSimpleTextGenerator::~IdentificationSimpleTextGenerator() { } @@ -116,7 +118,7 @@ IdentificationTextGenerator::~IdentificationTextGenerator() * The brain. */ AString -IdentificationTextGenerator::createIdentificationText(const SelectionManager* idManager, +IdentificationSimpleTextGenerator::createIdentificationText(const SelectionManager* idManager, const Brain* brain) const { CaretAssert(idManager); @@ -163,8 +165,11 @@ IdentificationTextGenerator::createIdentificationText(const SelectionManager* id this->generateChartTwoHistogramIdentificationText(idText, idManager->getChartTwoHistogramIdentification()); + this->generateChartTwoLineLayerIdentificationText(idText, + idManager->getChartTwoLineLayerVerticalNearestIdentification()); + this->generateChartTwoLineSeriesIdentificationText(idText, - idManager->getChartTwoLineSeriesIdentification()); + idManager->getChartTwoLineSeriesIdentification()); this->generateChartTwoMatrixIdentificationText(idText, idManager->getChartTwoMatrixIdentification()); @@ -190,7 +195,7 @@ IdentificationTextGenerator::createIdentificationText(const SelectionManager* id * String builder for identification text. */ AString -IdentificationTextGenerator::createToolTipText(const Brain* brain, +IdentificationSimpleTextGenerator::createToolTipText(const Brain* brain, const BrowserTabContent* browserTab, const SelectionManager* selectionManager, const DataToolTipsManager* dataToolTipsManager) const @@ -244,7 +249,7 @@ IdentificationTextGenerator::createToolTipText(const Brain* brain, * Information for volume voxel ID. */ void -IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateVolumeIdentificationText(IdentificationStringBuilder& idText, const Brain* brain, const SelectionItemVoxel* idVolumeVoxel) const { @@ -490,6 +495,7 @@ IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStri int64_t voxelIJK[3]; if (ciftiFile->getVolumeVoxelIdentificationForMaps(mapIndices, xyz, + " ", voxelIJK, textValue)) { AString boldText = (DataFileTypeEnum::toOverlayTypeName(ciftiFile->getDataFileType()) @@ -523,7 +529,7 @@ IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStri * Information for surface node ID. */ void -IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateSurfaceIdentificationText(IdentificationStringBuilder& idText, const Brain* brain, const SelectionItemSurfaceNode* idSurfaceNode) const { @@ -647,6 +653,7 @@ IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStr surface->getStructure(), nodeNumber, surface->getNumberOfNodes(), + " ", textValue); if (valid) { idText.addLine(true, @@ -711,7 +718,7 @@ IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStr * Indices of maps of the file that are used in overlays. */ void -IdentificationTextGenerator::getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, +IdentificationSimpleTextGenerator::getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, std::vector& mapIndicesOut) const { mapIndicesOut.clear(); @@ -756,7 +763,7 @@ IdentificationTextGenerator::getMapIndicesOfFileUsedInOverlays(const CaretMappab * Information for chart id. */ void -IdentificationTextGenerator::generateChartDataSeriesIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateChartDataSeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartDataSeries* idChartDataSeries) const { if (idChartDataSeries->isValid()) { @@ -777,7 +784,7 @@ IdentificationTextGenerator::generateChartDataSeriesIdentificationText(Identific * Information for chart id. */ void -IdentificationTextGenerator::generateChartFrequencySeriesIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateChartFrequencySeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartFrequencySeries* idChartFrequencySeries) const { if (idChartFrequencySeries->isValid()) { @@ -798,7 +805,7 @@ IdentificationTextGenerator::generateChartFrequencySeriesIdentificationText(Iden * Information for matrix chart id. */ void -IdentificationTextGenerator::generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartMatrix* idChartMatrix) const { if (idChartMatrix->isValid()) { @@ -843,7 +850,7 @@ IdentificationTextGenerator::generateChartMatrixIdentificationText(Identificatio * Information for selected chart two histogram. */ void -IdentificationTextGenerator::generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartTwoHistogram* idChartTwoHistogram) const { const int32_t mapIndex = idChartTwoHistogram->getMapIndex(); @@ -888,6 +895,65 @@ IdentificationTextGenerator::generateChartTwoHistogramIdentificationText(Identif } } +/** + * 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. * @@ -897,7 +963,7 @@ IdentificationTextGenerator::generateChartTwoHistogramIdentificationText(Identif * Information for selected chart two line-series. */ void -IdentificationTextGenerator::generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartTwoLineSeries* idChartTwoLineSeries) const { if (idChartTwoLineSeries->isValid()) { @@ -912,7 +978,7 @@ IdentificationTextGenerator::generateChartTwoLineSeriesIdentificationText(Identi int32_t primitiveIndex = idChartTwoLineSeries->getLineSegmentIndex(); - AString boldText("Line Chart"); + AString boldText("Line Series Chart"); idText.addLine(false, boldText, mapFile->getFileNameNoPath()); @@ -958,7 +1024,7 @@ IdentificationTextGenerator::generateChartTwoLineSeriesIdentificationText(Identi * Information for selected chart two matrix. */ void -IdentificationTextGenerator::generateChartTwoMatrixIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateChartTwoMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartTwoMatrix* idChartTwoMatrix) const { if (idChartTwoMatrix->isValid()) { @@ -982,7 +1048,7 @@ IdentificationTextGenerator::generateChartTwoMatrixIdentificationText(Identifica const AString rowName = matrixChart->getRowName(rowIndex); if ( ! rowName.isEmpty()) { idText.addLine(true, - ("Row " + AString::number(rowIndex + 1)), + "", rowName); } } @@ -991,7 +1057,7 @@ IdentificationTextGenerator::generateChartTwoMatrixIdentificationText(Identifica const AString colName = matrixChart->getColumnName(colIndex); if ( ! colName.isEmpty()) { idText.addLine(true, - ("Column " + AString::number(colIndex + 1)), + "", colName); } } @@ -1027,7 +1093,7 @@ IdentificationTextGenerator::generateChartTwoMatrixIdentificationText(Identifica * Information for CIFTI Connectivity Matrix Row/Column. */ void -IdentificationTextGenerator::generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const { if (idCiftiConnMatrix->isValid()) { @@ -1068,7 +1134,7 @@ IdentificationTextGenerator::generateCiftiConnectivityMatrixIdentificationText(I * Source of chart data. */ void -IdentificationTextGenerator::generateChartDataSourceText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateChartDataSourceText(IdentificationStringBuilder& idText, const AString& typeOfChartText, const ChartDataSource* chartDataSource) const { @@ -1147,7 +1213,7 @@ IdentificationTextGenerator::generateChartDataSourceText(IdentificationStringBui * The map file data selector. */ void -IdentificationTextGenerator::generateMapFileSelectorText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateMapFileSelectorText(IdentificationStringBuilder& idText, const MapFileDataSelector* mapFileDataSelector) const { @@ -1249,7 +1315,7 @@ IdentificationTextGenerator::generateMapFileSelectorText(IdentificationStringBui * Information for chart id. */ void -IdentificationTextGenerator::generateChartTimeSeriesIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateChartTimeSeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartTimeSeries* idChartTimeSeries) const { if (idChartTimeSeries->isValid()) { @@ -1272,7 +1338,7 @@ IdentificationTextGenerator::generateChartTimeSeriesIdentificationText(Identific * True if this is for tooltip. */ void -IdentificationTextGenerator::generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemBorderSurface* idSurfaceBorder, const bool toolTipFlag) const { @@ -1325,7 +1391,7 @@ IdentificationTextGenerator::generateSurfaceBorderIdentifcationText(Identificati * True if this is for tooltip. */ void -IdentificationTextGenerator::generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemFocusSurface* idSurfaceFocus, const bool toolTipFlag) const { @@ -1465,7 +1531,7 @@ IdentificationTextGenerator::generateSurfaceFociIdentifcationText(Identification * Information for surface focus ID. */ void -IdentificationTextGenerator::generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemFocusVolume* idVolumeFocus) const { if (idVolumeFocus->isValid()) { @@ -1548,7 +1614,7 @@ IdentificationTextGenerator::generateVolumeFociIdentifcationText(IdentificationS * Information for image ID. */ void -IdentificationTextGenerator::generateImageIdentificationText(IdentificationStringBuilder& idText, +IdentificationSimpleTextGenerator::generateImageIdentificationText(IdentificationStringBuilder& idText, const SelectionItemImage* idImage) const { if (idImage->isValid()) { @@ -1584,7 +1650,7 @@ IdentificationTextGenerator::generateImageIdentificationText(IdentificationStrin * String builder for identification text. */ void -IdentificationTextGenerator::generateSurfaceToolTip(const Brain* brain, +IdentificationSimpleTextGenerator::generateSurfaceToolTip(const Brain* brain, const BrowserTabContent* browserTab, const SelectionManager* selectionManager, const DataToolTipsManager* dataToolTipsManager, @@ -1663,6 +1729,7 @@ IdentificationTextGenerator::generateSurfaceToolTip(const Brain* brain, surfaceStructure, surfaceNodeIndex, surfaceNumberOfNodes, + " ", textValue); if ( ! textValue.isEmpty()) { idText.addLine(indentFlag, @@ -1682,17 +1749,6 @@ IdentificationTextGenerator::generateSurfaceToolTip(const Brain* brain, 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()); -// } -// } } } @@ -1703,18 +1759,6 @@ IdentificationTextGenerator::generateSurfaceToolTip(const Brain* brain, 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()); -// } -// } } } } @@ -1732,7 +1776,7 @@ IdentificationTextGenerator::generateSurfaceToolTip(const Brain* brain, * String builder for identification text. */ void -IdentificationTextGenerator::generateVolumeToolTip(const BrowserTabContent* browserTab, +IdentificationSimpleTextGenerator::generateVolumeToolTip(const BrowserTabContent* browserTab, const SelectionManager* selectionManager, const DataToolTipsManager* dataToolTipsManager, IdentificationStringBuilder& idText) const @@ -1812,6 +1856,7 @@ IdentificationTextGenerator::generateVolumeToolTip(const BrowserTabContent* brow int64_t ijk[3]; mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, xyz, + " ", ijk, textValue); if ( ! textValue.isEmpty()) { @@ -1831,7 +1876,7 @@ IdentificationTextGenerator::generateVolumeToolTip(const BrowserTabContent* brow * Overlay set for overlay. */ Overlay* -IdentificationTextGenerator::getTopEnabledOverlay(OverlaySet* overlaySet) const +IdentificationSimpleTextGenerator::getTopEnabledOverlay(OverlaySet* overlaySet) const { CaretAssert(overlaySet); const int32_t numberOfOverlays = overlaySet->getNumberOfDisplayedOverlays(); @@ -1856,7 +1901,7 @@ IdentificationTextGenerator::getTopEnabledOverlay(OverlaySet* overlaySet) const * String builder for identification text. */ void -IdentificationTextGenerator::generateChartToolTip(const SelectionManager* selectionManager, +IdentificationSimpleTextGenerator::generateChartToolTip(const SelectionManager* selectionManager, const DataToolTipsManager* dataToolTipsManager, IdentificationStringBuilder& idText) const { @@ -1879,7 +1924,7 @@ IdentificationTextGenerator::generateChartToolTip(const SelectionManager* select * @return String describing this object's content. */ AString -IdentificationTextGenerator::toString() const +IdentificationSimpleTextGenerator::toString() const { - return "IdentificationTextGenerator"; + return "IdentificationSimpleTextGenerator"; } diff --git a/src/Brain/IdentificationTextGenerator.h b/src/Brain/IdentificationSimpleTextGenerator.h similarity index 88% rename from src/Brain/IdentificationTextGenerator.h rename to src/Brain/IdentificationSimpleTextGenerator.h index df3320e58d45c9e5a6e5515210fbcc54b134e1f7..e2bb429b060e188a36cc342c5fbaa8ecfbee672e 100644 --- a/src/Brain/IdentificationTextGenerator.h +++ b/src/Brain/IdentificationSimpleTextGenerator.h @@ -1,5 +1,5 @@ -#ifndef __IDENTIFICATION_TEXT_GENERATOR__H_ -#define __IDENTIFICATION_TEXT_GENERATOR__H_ +#ifndef __IDENTIFICATION_SIMPLE_TEXT_GENERATOR__H_ +#define __IDENTIFICATION_SIMPLE_TEXT_GENERATOR__H_ /*LICENSE_START*/ /* @@ -39,6 +39,7 @@ namespace caret { class SelectionItemChartFrequencySeries; class SelectionItemChartMatrix; class SelectionItemChartTwoHistogram; + class SelectionItemChartTwoLineLayerVerticalNearest; class SelectionItemChartTwoLineSeries; class SelectionItemChartTwoMatrix; class SelectionItemCiftiConnectivityMatrixRowColumn; @@ -51,12 +52,12 @@ namespace caret { class SelectionManager; class IdentificationStringBuilder; - class IdentificationTextGenerator : public CaretObject { + class IdentificationSimpleTextGenerator : public CaretObject { public: - IdentificationTextGenerator(); + IdentificationSimpleTextGenerator(); - virtual ~IdentificationTextGenerator(); + virtual ~IdentificationSimpleTextGenerator(); AString createIdentificationText(const SelectionManager* idManager, const Brain* brain) const; @@ -67,9 +68,9 @@ namespace caret { const DataToolTipsManager* dataToolTipsManager) const; private: - IdentificationTextGenerator(const IdentificationTextGenerator&); + IdentificationSimpleTextGenerator(const IdentificationSimpleTextGenerator&); - IdentificationTextGenerator& operator=(const IdentificationTextGenerator&); + IdentificationSimpleTextGenerator& operator=(const IdentificationSimpleTextGenerator&); public: virtual AString toString() const; @@ -124,6 +125,9 @@ namespace caret { void generateChartTwoHistogramIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartTwoHistogram* idChartTwoHistogram) const; + void generateChartTwoLineLayerIdentificationText(IdentificationStringBuilder& idText, + const SelectionItemChartTwoLineLayerVerticalNearest* idChartTwoLineLayer) const; + void generateChartTwoLineSeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartTwoLineSeries* idChartTwoLineSeries) const; @@ -151,9 +155,9 @@ namespace caret { friend class DataToolTipsManager; }; -#ifdef __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ +#ifdef __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ // -#endif // __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ +#endif // __IDENTIFICATION_SIMPLE_TEXT_GENERATOR_DECLARE__ } // namespace -#endif //__IDENTIFICATION_TEXT_GENERATOR__H_ +#endif //__IDENTIFICATION_SIMPLE_TEXT_GENERATOR__H_ diff --git a/src/Brain/IdentificationSymbolSizeTypeEnum.cxx b/src/Brain/IdentificationSymbolSizeTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6b5826fbb7a68d8b58e5790f306bb9f9aadf4f38 --- /dev/null +++ b/src/Brain/IdentificationSymbolSizeTypeEnum.cxx @@ -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 --git a/src/Brain/IdentificationSymbolSizeTypeEnum.h b/src/Brain/IdentificationSymbolSizeTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..2627a267bb749b97c41c4f21129d8142f8b1641d --- /dev/null +++ b/src/Brain/IdentificationSymbolSizeTypeEnum.h @@ -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 --git a/src/Brain/IdentifiedItem.cxx b/src/Brain/IdentifiedItem.cxx index b1e0a59d54285ba72ba4488479f5622acdca84b2..cdaf058dc0f7107be4212e7ca962752e5dffce65 100644 --- a/src/Brain/IdentifiedItem.cxx +++ b/src/Brain/IdentifiedItem.cxx @@ -48,14 +48,18 @@ IdentifiedItem::IdentifiedItem() /** * 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 @@ IdentifiedItem::initializeMembers() m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_text", &m_text); + m_sceneAssistant->add("m_formattedText", &m_formattedText); } @@ -135,11 +140,20 @@ IdentifiedItem::isValid() const /** * 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,14 +166,23 @@ IdentifiedItem::clearText() } /** - * @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. @@ -167,7 +190,8 @@ IdentifiedItem::getText() const AString IdentifiedItem::toString() const { - return ("m_text=" + m_text); + return ("m_text=" + m_text + + "m_formattedText=" + m_formattedText); } /** diff --git a/src/Brain/IdentifiedItem.h b/src/Brain/IdentifiedItem.h index f52f9c7c9000c314fa4247fa8e04e079508dc19d..6a04b244cbac3e26a97722d6113cbf78ed291c34 100644 --- a/src/Brain/IdentifiedItem.h +++ b/src/Brain/IdentifiedItem.h @@ -34,7 +34,8 @@ namespace caret { public: IdentifiedItem(); - IdentifiedItem(const AString& text); + IdentifiedItem(const AString& simpleText, + const AString& formattedText); virtual ~IdentifiedItem(); @@ -47,12 +48,15 @@ namespace caret { 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 @@ namespace caret { AString m_text; + AString m_formattedText; + SceneClassAssistant* m_sceneAssistant; friend class IdentificationManager; diff --git a/src/Brain/IdentifiedItemNode.cxx b/src/Brain/IdentifiedItemNode.cxx index 2e7e54141ff50925c9f3e5469aebf4a2ac9a9b4d..573acd674283bbda2f37d72ab5746847cd94aac7 100644 --- a/src/Brain/IdentifiedItemNode.cxx +++ b/src/Brain/IdentifiedItemNode.cxx @@ -61,8 +61,10 @@ IdentifiedItemNode::IdentifiedItemNode() /** * 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 @@ IdentifiedItemNode::IdentifiedItemNode() * 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 @@ IdentifiedItemNode::copyHelperIdentifiedItemNode(const IdentifiedItemNode& obj) 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 @@ IdentifiedItemNode::initializeMembers() 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 @@ IdentifiedItemNode::initializeMembers() 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 @@ IdentifiedItemNode::setSymbolSize(const float symbolSize) 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 @@ IdentifiedItemNode::restoreFromScene(const SceneAttributes* sceneAttributes, return; } + m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Brain/IdentifiedItemNode.h b/src/Brain/IdentifiedItemNode.h index 9317cb6e9c87d3f06a3cf4f534909d80df36e345..4ef4d2bff4bc2be2e8d7da16f3125b4c4afb078b 100644 --- a/src/Brain/IdentifiedItemNode.h +++ b/src/Brain/IdentifiedItemNode.h @@ -22,6 +22,7 @@ /*LICENSE_END*/ +#include "IdentificationSymbolSizeTypeEnum.h" #include "IdentifiedItem.h" #include "StructureEnum.h" @@ -32,7 +33,8 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { float m_symbolSize; + IdentificationSymbolSizeTypeEnum::Enum m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + SceneClassAssistant* m_sceneAssistant; }; diff --git a/src/Brain/IdentifiedItemVoxel.cxx b/src/Brain/IdentifiedItemVoxel.cxx index 373c71a8d9400dd7aa14f10a562ea0b4f73d811a..3f111e132f55b1e132b656c2a245b9bc7505fd10 100644 --- a/src/Brain/IdentifiedItemVoxel.cxx +++ b/src/Brain/IdentifiedItemVoxel.cxx @@ -49,10 +49,18 @@ IdentifiedItemVoxel::IdentifiedItemVoxel() /** * 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 @@ IdentifiedItemVoxel::initializeMembers() 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 @@ IdentifiedItemVoxel::copyHelperIdentifiedItemVoxel(const IdentifiedItemVoxel& ob m_symbolRGB[2] = obj.m_symbolRGB[2]; m_symbolSize = obj.m_symbolSize; + m_identificationSymbolSizeType = obj.m_identificationSymbolSizeType; } /** @@ -226,6 +238,26 @@ IdentifiedItemVoxel::setSymbolSize(const float symbolSize) m_symbolSize = symbolSize; } +/** + * @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 @@ IdentifiedItemVoxel::restoreFromScene(const SceneAttributes* sceneAttributes, return; } + m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); diff --git a/src/Brain/IdentifiedItemVoxel.h b/src/Brain/IdentifiedItemVoxel.h index 56e34fb676aec2a65bd65f41ea315763a7d4fdd8..ec0a8e6d28b3803bc60d5af27814354ef9343adf 100644 --- a/src/Brain/IdentifiedItemVoxel.h +++ b/src/Brain/IdentifiedItemVoxel.h @@ -22,6 +22,7 @@ /*LICENSE_END*/ +#include "IdentificationSymbolSizeTypeEnum.h" #include "IdentifiedItem.h" @@ -33,7 +34,8 @@ namespace caret { public: IdentifiedItemVoxel(); - IdentifiedItemVoxel(const AString& text, + IdentifiedItemVoxel(const AString& simpleText, + const AString& formattedText, const float xyz[3]); virtual ~IdentifiedItemVoxel(); @@ -56,6 +58,10 @@ namespace caret { void setSymbolSize(const float symbolSize); + IdentificationSymbolSizeTypeEnum::Enum getIdentificationSymbolSizeType() const; + + void setIdentificationSymbolSizeType(const IdentificationSymbolSizeTypeEnum::Enum sizeType); + virtual AString toString() const; @@ -93,6 +99,8 @@ namespace caret { float m_symbolSize; + IdentificationSymbolSizeTypeEnum::Enum m_identificationSymbolSizeType = IdentificationSymbolSizeTypeEnum::MILLIMETERS; + SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE diff --git a/src/Brain/MediaOverlay.cxx b/src/Brain/MediaOverlay.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6392764639a9aee5b24ee62f0ec131075002696f --- /dev/null +++ b/src/Brain/MediaOverlay.cxx @@ -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 --git a/src/Brain/MediaOverlay.h b/src/Brain/MediaOverlay.h new file mode 100644 index 0000000000000000000000000000000000000000..2fcd45aa33f2eb7ef9f7f578862f9fc55ee18bcc --- /dev/null +++ b/src/Brain/MediaOverlay.h @@ -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 --git a/src/Brain/MediaOverlaySet.cxx b/src/Brain/MediaOverlaySet.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1ac0b374856b44e59af764e45a81bde1250a7bcd --- /dev/null +++ b/src/Brain/MediaOverlaySet.cxx @@ -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 --git a/src/Brain/MediaOverlaySet.h b/src/Brain/MediaOverlaySet.h new file mode 100644 index 0000000000000000000000000000000000000000..d5575a6e3ccbd81fac883736ac0d94cd8deb916c --- /dev/null +++ b/src/Brain/MediaOverlaySet.h @@ -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 --git a/src/Brain/MediaOverlaySetArray.cxx b/src/Brain/MediaOverlaySetArray.cxx new file mode 100644 index 0000000000000000000000000000000000000000..271128fe9b7ea531b1ea76561bdfd7e217731209 --- /dev/null +++ b/src/Brain/MediaOverlaySetArray.cxx @@ -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 --git a/src/Brain/MediaOverlaySetArray.h b/src/Brain/MediaOverlaySetArray.h new file mode 100644 index 0000000000000000000000000000000000000000..efc3b88388604397fd3da19132a28d58d994b058 --- /dev/null +++ b/src/Brain/MediaOverlaySetArray.h @@ -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 --git a/src/Brain/Model.cxx b/src/Brain/Model.cxx index 6ec95a26ae0f1d086ad6b136eff5b65e21e4fa01..9b12ea82c586d422eb6dd73b18be666da5ee89f1 100644 --- a/src/Brain/Model.cxx +++ b/src/Brain/Model.cxx @@ -144,6 +144,34 @@ Model::getChartTwoOverlaySet(const int /*tabIndex*/) const return NULL; } +/** + * 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. * diff --git a/src/Brain/Model.h b/src/Brain/Model.h index f17458bdafc7dc75a9234cc83cccf7c2e73494c9..c9ee1733f6ea7be6d1952e7f783ea65167e3760f 100644 --- a/src/Brain/Model.h +++ b/src/Brain/Model.h @@ -29,6 +29,7 @@ namespace caret { class Brain; class ChartTwoOverlaySet; + class MediaOverlaySet; class OverlaySet; class PlainTextStringBuilder; @@ -71,6 +72,10 @@ namespace caret { 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 --git a/src/Brain/ModelChartTwo.cxx b/src/Brain/ModelChartTwo.cxx index d4038cbcff6f1e72bd549f9e08a362c86268cef4..91100440997d9f6b4ea3c7f70df8a21eb14f65aa 100644 --- a/src/Brain/ModelChartTwo.cxx +++ b/src/Brain/ModelChartTwo.cxx @@ -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 @@ ModelChartTwo::ModelChartTwo(Brain* brain) "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 @@ ModelChartTwo::loadChartDataForCiftiMappableFileRow(CiftiMappableDataFile* cifti { CaretAssert(ciftiMapFile); - //std::vector tabIndices; EventBrowserTabIndicesGetAll tabIndicesEvent; EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); @@ -381,62 +382,6 @@ ModelChartTwo::receiveEvent(Event* event) 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 @@ ModelChartTwo::saveModelSpecificInformationToScene(const SceneAttributes* sceneA 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 @@ ModelChartTwo::saveModelSpecificInformationToScene(const SceneAttributes* sceneA 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 @@ ModelChartTwo::saveModelSpecificInformationToScene(const SceneAttributes* sceneA } } + 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 @@ ModelChartTwo::restoreModelSpecificInformationFromScene(const SceneAttributes* s 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 @@ ModelChartTwo::restoreLineSeriesChartFromChartOneModel(ModelChart* modelChartOne * 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 @@ ModelChartTwo::restoreMatrixChartFromChartOneModel(ModelChart* modelChartOne, 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 @@ ModelChartTwo::copyTabContent(const int32_t sourceTabIndex, 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 @@ ModelChartTwo::copyChartTwoCartesianAxes(const int32_t sourceTabIndex, m_histogramChartOverlaySetArray->copyChartOverlaySetCartesianAxes(sourceTabIndex, destinationTabIndex); + m_lineLayerChartOverlaySetArray->copyChartOverlaySetCartesianAxes(sourceTabIndex, + destinationTabIndex); m_lineSeriesChartOverlaySetArray->copyChartOverlaySetCartesianAxes(sourceTabIndex, destinationTabIndex); } @@ -1139,6 +1102,8 @@ ModelChartTwo::updateChartOverlaySets(const int32_t tabIndex) 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 @@ ModelChartTwo::getChartTwoOverlaySet(const int tabIndex) 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 @@ ModelChartTwo::getChartTwoOverlaySet(const int tabIndex) const 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 --git a/src/Brain/ModelChartTwo.h b/src/Brain/ModelChartTwo.h index 63f5824a32c1d1ed7d0ce04db82f57ae5e076cda..ca90d49a3991e20b9189cf9137820c6f5eb69d23 100644 --- a/src/Brain/ModelChartTwo.h +++ b/src/Brain/ModelChartTwo.h @@ -88,6 +88,8 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Brain/ModelMedia.cxx b/src/Brain/ModelMedia.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d9cbe526473f32931abca424b0c0d852cc65d09e --- /dev/null +++ b/src/Brain/ModelMedia.cxx @@ -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 --git a/src/Brain/ModelMedia.h b/src/Brain/ModelMedia.h new file mode 100644 index 0000000000000000000000000000000000000000..587238ad61e218391e3029c485d7b297e8e3399e --- /dev/null +++ b/src/Brain/ModelMedia.h @@ -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 --git a/src/Brain/ModelSurface.cxx b/src/Brain/ModelSurface.cxx index 34a117bf7fc938819da3002d43f0fc01c4c1abaa..c7a897ea1b8ec18e9d8a05aa4fda3f8afc2806e9 100644 --- a/src/Brain/ModelSurface.cxx +++ b/src/Brain/ModelSurface.cxx @@ -293,6 +293,11 @@ ModelSurface::copyTabContent(const int32_t sourceTabIndex, { Model::copyTabContent(sourceTabIndex, destinationTabIndex); + + /* + * Fix WB-879 Overlay not copied when duplicating tab containing surface + */ + getOverlaySet(destinationTabIndex)->copyOverlaySet(getOverlaySet(sourceTabIndex)); } diff --git a/src/Brain/ModelTypeEnum.cxx b/src/Brain/ModelTypeEnum.cxx index be37b3ef85d2623bc0947feeac28d88e4ceed628..9a225ee2500c8195aafdc6767cc3fc6a3995023c 100644 --- a/src/Brain/ModelTypeEnum.cxx +++ b/src/Brain/ModelTypeEnum.cxx @@ -104,6 +104,12 @@ ModelTypeEnum::initialize() 6, "MODEL_TYPE_WHOLE_BRAIN", "Whole Brain")); + + enumData.push_back(ModelTypeEnum(MODEL_TYPE_MULTI_MEDIA, + 7, + "MODEL_TYPE_MULTI_MEDIA", + "Media")); + } /** diff --git a/src/Brain/ModelTypeEnum.h b/src/Brain/ModelTypeEnum.h index 3d52e072ca56edb9bbffb9daa7862ff79f419803..e4f203f5e590a426e456b01310469613abe87eea 100644 --- a/src/Brain/ModelTypeEnum.h +++ b/src/Brain/ModelTypeEnum.h @@ -49,7 +49,9 @@ public: /** 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 --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index bb525685374945a370b9c6c1464b4671eb957a83..ae99d31a58c064a5023bf3c250250548d9a3bfd5 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -620,7 +620,7 @@ MovieRecorder::createMovieWithQProcessPipe(const QString& programName, TextFile textFile; try { - for (const auto name : m_imageFileNames) { + for (const auto& name : m_imageFileNames) { textFile.addLine("file " + name); } diff --git a/src/Brain/Overlay.cxx b/src/Brain/Overlay.cxx index fdf86cc0b04b35f361ce8fda37fdd50b70668d93..1c4d5fa97b4ae51c0fdce8596d965725b4da7da5 100644 --- a/src/Brain/Overlay.cxx +++ b/src/Brain/Overlay.cxx @@ -270,6 +270,8 @@ Overlay::copyData(const Overlay* overlay) m_selectedMapIndex = overlay->m_selectedMapIndex; m_mapYokingGroup = overlay->m_mapYokingGroup; + m_wholeBrainVoxelDrawingMode = overlay->m_wholeBrainVoxelDrawingMode; + *m_colorBar = *overlay->m_colorBar; } diff --git a/src/Brain/OverlaySet.cxx b/src/Brain/OverlaySet.cxx index 30e152cdd4c859296df8f65d9371eac5e36b1094..69c93157242c297764cb22840031f42077fed0ef 100644 --- a/src/Brain/OverlaySet.cxx +++ b/src/Brain/OverlaySet.cxx @@ -1409,9 +1409,36 @@ OverlaySet::receiveEvent(Event* event) 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 --git a/src/Brain/OverlaySet.h b/src/Brain/OverlaySet.h index bd3a57fd3c8da71c3f5871fccaa511f856949df3..54794890f14e90636a40f355123d12d728455797 100644 --- a/src/Brain/OverlaySet.h +++ b/src/Brain/OverlaySet.h @@ -92,6 +92,8 @@ namespace caret { std::vector& labelFilesOut, std::vector& labelMapIndicesOut); + bool hasObliqueOnlyVolumeSelected() const; + void resetOverlayYokingToOff(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, diff --git a/src/Brain/SelectionItem.cxx b/src/Brain/SelectionItem.cxx index 7c29cbfc7499f4138785919046a080593d06cdf5..a121e54311b06c699cde31f62e15535b6508ec1c 100644 --- a/src/Brain/SelectionItem.cxx +++ b/src/Brain/SelectionItem.cxx @@ -289,7 +289,8 @@ SelectionItem::isOtherScreenDepthCloserToViewer(const double otherScreenDepth) c * 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 --git a/src/Brain/SelectionItemAnnotation.cxx b/src/Brain/SelectionItemAnnotation.cxx index 2e8c5a8c816892ef9341c7b86112446872b088b3..a8e49aa8833f5191ba46d24d928cc17cf3dfc494 100644 --- a/src/Brain/SelectionItemAnnotation.cxx +++ b/src/Brain/SelectionItemAnnotation.cxx @@ -65,6 +65,7 @@ SelectionItemAnnotation::reset() m_annotationFile = NULL; m_annotation = NULL; m_sizingHandle = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; + m_polyLineCoordinateIndex = -1; } /** @@ -103,6 +104,15 @@ SelectionItemAnnotation::getSizingHandle() const return m_sizingHandle; } +/** + * @return Index of poly line coordinate + */ +int32_t +SelectionItemAnnotation::getPolyLineCoordinateIndex() const +{ + return m_polyLineCoordinateIndex; +} + /** * Add a annotation to the selected annotations. * @@ -112,17 +122,21 @@ SelectionItemAnnotation::getSizingHandle() const * 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 @@ SelectionItemAnnotation::toString() const { 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 --git a/src/Brain/SelectionItemAnnotation.h b/src/Brain/SelectionItemAnnotation.h index 8bdb693a688cd236f0bf1cfdd3af2727e79ae995..0112d8d98536f92470f8a4a3e96030962d7720b9 100644 --- a/src/Brain/SelectionItemAnnotation.h +++ b/src/Brain/SelectionItemAnnotation.h @@ -49,9 +49,12 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Brain/SelectionItemChartTwoLineLayer.cxx b/src/Brain/SelectionItemChartTwoLineLayer.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1a53f9a0bdcb250cace679acea3ac708e5198c95 --- /dev/null +++ b/src/Brain/SelectionItemChartTwoLineLayer.cxx @@ -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 --git a/src/Brain/SelectionItemChartTwoLineLayer.h b/src/Brain/SelectionItemChartTwoLineLayer.h new file mode 100644 index 0000000000000000000000000000000000000000..35f16ecab2a16b51bf461888443b89190d585603 --- /dev/null +++ b/src/Brain/SelectionItemChartTwoLineLayer.h @@ -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 --git a/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.cxx b/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e696fc79fff09c45b052bf69f5de4bfbf15c1c34 --- /dev/null +++ b/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.cxx @@ -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 --git a/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.h b/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.h new file mode 100644 index 0000000000000000000000000000000000000000..d381ecc0b0a2d3851e5d0c276ae56d43889bf4cb --- /dev/null +++ b/src/Brain/SelectionItemChartTwoLineLayerVerticalNearest.h @@ -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 --git a/src/Brain/SelectionItemDataTypeEnum.cxx b/src/Brain/SelectionItemDataTypeEnum.cxx index 7d1b29855af94f0d8373b053ed20e617ca90bf13..3bc6979aca1f0d2ba7f6bc4a290b08054394f6ef 100644 --- a/src/Brain/SelectionItemDataTypeEnum.cxx +++ b/src/Brain/SelectionItemDataTypeEnum.cxx @@ -103,6 +103,14 @@ SelectionItemDataTypeEnum::initialize() "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 --git a/src/Brain/SelectionItemDataTypeEnum.h b/src/Brain/SelectionItemDataTypeEnum.h index 5ce2bf9d1a4ba26223b8cda9eb802662bb6021be..65bb820ab7e43a51785463ccd4701deaebc94a97 100644 --- a/src/Brain/SelectionItemDataTypeEnum.h +++ b/src/Brain/SelectionItemDataTypeEnum.h @@ -61,6 +61,10 @@ public: 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 --git a/src/Brain/SelectionManager.cxx b/src/Brain/SelectionManager.cxx index 91b24486aba5c474dece21b140b795677d01f020..ecdb9a4d636993caebb85721ba5037c80fec4da8 100644 --- a/src/Brain/SelectionManager.cxx +++ b/src/Brain/SelectionManager.cxx @@ -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 @@ SelectionManager::SelectionManager() 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 @@ SelectionManager::SelectionManager() 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 @@ SelectionManager::SelectionManager() 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 @@ SelectionManager::receiveEvent(Event* /*event*/) 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 @@ SelectionManager::filterSelections(const bool applySelectionBackgroundFiltering) 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 @@ SelectionManager::setAllSelectionsEnabled(const bool status) } /** - * 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, @@ -451,6 +464,24 @@ SelectionManager::getIdentificationText(const Brain* brain) const return text; } +/** + * 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. */ @@ -753,6 +784,42 @@ SelectionManager::getChartTwoHistogramIdentification() const return m_chartTwoHistogramIdentification.get(); } +/** + * @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 */ diff --git a/src/Brain/SelectionManager.h b/src/Brain/SelectionManager.h index 731496ee3ca5df7ea8f4fb5d101a9d74165019a4..62ada45efc12567e14130d54b273d34b2944251f 100644 --- a/src/Brain/SelectionManager.h +++ b/src/Brain/SelectionManager.h @@ -39,6 +39,8 @@ namespace caret { class SelectionItemChartTimeSeries; class SelectionItemChartTwoHistogram; class SelectionItemChartTwoLabel; + class SelectionItemChartTwoLineLayer; + class SelectionItemChartTwoLineLayerVerticalNearest; class SelectionItemChartTwoLineSeries; class SelectionItemChartTwoMatrix; class SelectionItemCiftiConnectivityMatrixRowColumn; @@ -52,7 +54,8 @@ namespace caret { class SelectionItemVoxel; class SelectionItemVoxelEditing; class SelectionItemVoxelIdentificationSymbol; - class IdentificationTextGenerator; + class IdentificationFormattedTextGenerator; + class IdentificationSimpleTextGenerator; class Surface; class SelectionManager : public CaretObject, public EventListenerInterface { @@ -138,6 +141,14 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { SelectionItemSurfaceTriangle* m_surfaceTriangleIdentification; - IdentificationTextGenerator* m_idTextGenerator; + IdentificationSimpleTextGenerator* m_idTextGenerator; + + std::unique_ptr m_idFormattedTextGenerator; SelectionItemVoxel* m_voxelIdentification; diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 7907d2eadd22c547599ce42e95119c4977c864c4..b5742e2255b90b93b60d864f12b1e33086501ab7 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -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 @@ SessionManager::SessionManager() 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 @@ SessionManager::~SessionManager() * 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(); @@ -294,6 +313,25 @@ SessionManager::toString() const return "SessionManager"; } +/** + * 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. * @@ -310,18 +348,11 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::receiveEvent(Event* event) 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 @@ SessionManager::saveToScene(const SceneAttributes* sceneAttributes, * 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 @@ SessionManager::saveToScene(const SceneAttributes* sceneAttributes, 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 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, m_caretPreferences->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); m_caretPreferences->invalidateSceneDataValues(); + m_sceneRestoredWithChartOldFlag = false; + if (sceneClass == NULL) { return; } @@ -736,6 +864,23 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, } + /* + * 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 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, /* * 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 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, 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 @@ SessionManager::getMovieRecorder() const 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 --git a/src/Brain/SessionManager.h b/src/Brain/SessionManager.h index 319985f1a17cb0b8a5543fe1117eac1c3ca3b883..61bee8d317fdea01e3c9fe8a8a6773f16ede1b5a 100644 --- a/src/Brain/SessionManager.h +++ b/src/Brain/SessionManager.h @@ -22,6 +22,7 @@ /*LICENSE_END*/ #include +#include #include #include @@ -38,9 +39,13 @@ namespace caret { 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 @@ namespace caret { virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); + + bool hasSceneWithChartOld() const; + + void resetSceneWithChartOld(); + private: SessionManager(); @@ -116,11 +126,36 @@ namespace caret { 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; + + /** Active browser tabs */ + std::array m_browserTabs; - /** The browser tabs */ - BrowserTabContent* m_browserTabs[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + /** + * 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 @@ namespace caret { std::map m_spacerTabsMap; std::unique_ptr m_movieRecorder; + + std::vector> m_chartingAxisDisplayGroups; + + bool m_sceneRestoredWithChartOldFlag = false; }; #ifdef __SESSION_MANAGER_DECLARE__ diff --git a/src/Brain/SurfaceDrawingTypeEnum.cxx b/src/Brain/SurfaceDrawingTypeEnum.cxx index 3021557e747f80a945d058cdbac4b9c8b1ade2bc..5d8f55fd6722d969ae039c8726db8aaaa4e127fd 100644 --- a/src/Brain/SurfaceDrawingTypeEnum.cxx +++ b/src/Brain/SurfaceDrawingTypeEnum.cxx @@ -82,6 +82,10 @@ SurfaceDrawingTypeEnum::initialize() "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 --git a/src/Brain/SurfaceDrawingTypeEnum.h b/src/Brain/SurfaceDrawingTypeEnum.h index 8ec4e80525c9816a3da8b7164705e643ad56be1d..6e8dfb4f2f3fdd41acd2ff347180dee272d8e233 100644 --- a/src/Brain/SurfaceDrawingTypeEnum.h +++ b/src/Brain/SurfaceDrawingTypeEnum.h @@ -39,6 +39,8 @@ public: 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 --git a/src/Brain/SurfaceNodeColoring.cxx b/src/Brain/SurfaceNodeColoring.cxx index 47ed8f4e740d0b79b99802c8d1019d0f37622573..dd2e749e91df31e4f0545ac0751117f80f595633 100644 --- a/src/Brain/SurfaceNodeColoring.cxx +++ b/src/Brain/SurfaceNodeColoring.cxx @@ -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 @@ SurfaceNodeColoring::colorSurfaceNodes(Model* model, 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 @@ SurfaceNodeColoring::colorSurfaceNodes(Model* model, this->colorSurfaceNodes(displayPropertiesLabels, browserTabIndex, surface, + defaultColor, overlaySet, rgbaColor); @@ -285,8 +293,14 @@ SurfaceNodeColoring::showBrainordinateHighlightRegionOfInterest(const Brain* bra /** * 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 @@ void 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 --git a/src/Brain/SurfaceNodeColoring.h b/src/Brain/SurfaceNodeColoring.h index ab2014dc81c57e4b9cbb46a1c2f656993f1b01c6..03bd4f62ae15e8c6c38a481701a72cdd8a714574 100644 --- a/src/Brain/SurfaceNodeColoring.h +++ b/src/Brain/SurfaceNodeColoring.h @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "CaretColorEnum.h" #include "CaretObject.h" #include "CaretPointer.h" @@ -82,6 +84,7 @@ namespace caret { void colorSurfaceNodes(const DisplayPropertiesLabels* dpl, const int32_t browserTabIndex, const Surface* surface, + const std::array& defaultSurfaceColor, OverlaySet* overlaySet, float* rgbaNodeColors); diff --git a/src/Brain/UserInputModeEnum.cxx b/src/Brain/UserInputModeEnum.cxx index 9a9749feae58e26d5e37c76bae7c72a9d665e00d..8c95898a6e1c4ac4ca27af3b4fe247a4ca8d4535 100644 --- a/src/Brain/UserInputModeEnum.cxx +++ b/src/Brain/UserInputModeEnum.cxx @@ -106,31 +106,35 @@ UserInputModeEnum::initialize() } 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 --git a/src/Brain/UserInputModeEnum.h b/src/Brain/UserInputModeEnum.h index 564e4c488e6a1a730267fd742a96abcf73417545..44418740a723f9249eac518c44770bc597ae35b2 100644 --- a/src/Brain/UserInputModeEnum.h +++ b/src/Brain/UserInputModeEnum.h @@ -34,7 +34,7 @@ public: /** * Enumerated values. */ - enum Enum { + enum class Enum { /** Invalid */ INVALID, /** Annotations */ @@ -45,6 +45,8 @@ public: FOCI, /** Image */ IMAGE, + /** Tile tabs manual layouot editing */ + TILE_TABS_MANUAL_LAYOUT_EDITING, /** View */ VIEW, /** Volume Edit */ diff --git a/src/Brain/ViewingTransformations.cxx b/src/Brain/ViewingTransformations.cxx index 22f39a71cd3ed7a18ebebf16a6915b3ca34bad29..5aa90931e9e3e2522b21a7821ddac979c2867e65 100644 --- a/src/Brain/ViewingTransformations.cxx +++ b/src/Brain/ViewingTransformations.cxx @@ -49,6 +49,8 @@ ViewingTransformations::ViewingTransformations() 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 @@ ViewingTransformations::ViewingTransformations() 0.0); m_sceneAssistant->add("m_scaling", &m_scaling); + m_sceneAssistant->addArray("m_rightCortexFlatMapOffset", m_rightCortexFlatMapOffset, 2, @@ -73,6 +76,7 @@ ViewingTransformations::ViewingTransformations() ViewingTransformations::~ViewingTransformations() { delete m_rotationMatrix; + delete m_flatRotationMatrix; delete m_sceneAssistant; } @@ -117,6 +121,7 @@ ViewingTransformations::copyHelperViewingTransformations(const ViewingTransforma 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; @@ -220,6 +225,26 @@ ViewingTransformations::setRotationMatrix(const Matrix4x4& rotationMatrix) *m_rotationMatrix = rotationMatrix; } +/** + * @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. * @@ -283,6 +308,7 @@ ViewingTransformations::resetView() 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 @@ ViewingTransformations::anteriorView() { 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 @@ ViewingTransformations::posteriorView() { m_rotationMatrix->identity(); m_rotationMatrix->setRotation(-90.0, 0.0, 0.0); -// m_rotationMatrix->rotateX(-90.0); } /** @@ -339,7 +362,6 @@ ViewingTransformations::posteriorView() void ViewingTransformations::dorsalView() { -// m_rotationMatrix->identity(); m_rotationMatrix->setRotation(0.0, 0.0, 90.0); } @@ -349,8 +371,6 @@ ViewingTransformations::dorsalView() void ViewingTransformations::ventralView() { -// m_rotationMatrix->identity(); -// m_rotationMatrix->rotateY(-180.0); m_rotationMatrix->setRotation(0.0, 180.0, 90.0); } @@ -392,6 +412,11 @@ ViewingTransformations::saveToScene(const SceneAttributes* sceneAttributes, 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 @@ ViewingTransformations::restoreFromScene(const SceneAttributes* sceneAttributes, 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 --git a/src/Brain/ViewingTransformations.h b/src/Brain/ViewingTransformations.h index 839aa9ddd5aa78ddadb5efac3e374edafd40ba8b..b387dfb52683aba7fbcf9223bd6b542380c654fc 100644 --- a/src/Brain/ViewingTransformations.h +++ b/src/Brain/ViewingTransformations.h @@ -64,6 +64,10 @@ namespace caret { 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 @@ namespace caret { /** Scaling. */ float m_scaling; + /** Rotation matrix for flat surface */ + Matrix4x4* m_flatRotationMatrix; + /** Offset for right cortex flat map */ float m_rightCortexFlatMapOffset[2]; diff --git a/src/Brain/ViewingTransformationsMedia.cxx b/src/Brain/ViewingTransformationsMedia.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1e3bb88b8941841707251e8fb2975223b7ac0a82 --- /dev/null +++ b/src/Brain/ViewingTransformationsMedia.cxx @@ -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 --git a/src/Brain/ViewingTransformationsMedia.h b/src/Brain/ViewingTransformationsMedia.h new file mode 100644 index 0000000000000000000000000000000000000000..f0917bd9be225f4e11c64366e82471fc62541151 --- /dev/null +++ b/src/Brain/ViewingTransformationsMedia.h @@ -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 --git a/src/Brain/VolumeSliceSettings.cxx b/src/Brain/VolumeSliceSettings.cxx index e43e307c1fdebb9a3d5b3d646d940fbd1437715a..eb2b60ab3e30cb6dffc9630ffe0ea9aa58173e03 100644 --- a/src/Brain/VolumeSliceSettings.cxx +++ b/src/Brain/VolumeSliceSettings.cxx @@ -192,6 +192,8 @@ VolumeSliceSettings::getDescriptionOfContent(const ModelTypeEnum::Enum modelType 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 --git a/src/Brain/VolumeSurfaceOutlineModel.cxx b/src/Brain/VolumeSurfaceOutlineModel.cxx index b5956312c3208d9c81829b9ca96c72951b2b798b..fef839cee9d5f21dec1ed338a439ae3359f65251 100644 --- a/src/Brain/VolumeSurfaceOutlineModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineModel.cxx @@ -131,7 +131,16 @@ VolumeSurfaceOutlineModel::receiveEvent(Event* event) 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 --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cd998fa00eb6d98595606e5a0a85cd018e4b3aee..642afa4f05c50e19c1cad1b7308be32ae5315339 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 --git a/src/Charting/2 b/src/Charting/2 new file mode 100644 index 0000000000000000000000000000000000000000..2348d3b173f422579c7b04f69fc2fae49518950a --- /dev/null +++ b/src/Charting/2 @@ -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 --git a/src/Charting/CMakeLists.txt b/src/Charting/CMakeLists.txt index 590b4d2b2af6d6a6884cd7f9693c343a59170cf6..1916da7a6fe4e1e0cd26fd3d0061bc83270b8fa3 100644 --- a/src/Charting/CMakeLists.txt +++ b/src/Charting/CMakeLists.txt @@ -38,12 +38,19 @@ ChartSelectionModeEnum.h 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 @@ ChartTwoMatrixLoadingDimensionEnum.h 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 @@ ChartSelectionModeEnum.cxx 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 @@ ChartTwoMatrixLoadingDimensionEnum.cxx ChartTwoMatrixTriangularViewingModeEnum.cxx ChartTwoNumericSubdivisionsModeEnum.cxx ChartTwoLineSeriesHistory.cxx +ChartTwoOverlaySetInterface.cxx ChartTwoTitle.cxx -EventChartTwoAttributesChanged.cxx -EventChartTwoAxisGetDataRange.cxx +EventChartTwoCartesianAxisDisplayGroup.cxx +EventChartTwoCartesianOrientedAxesYoking.cxx EventChartTwoLoadLineSeriesData.cxx MapFileDataSelector.cxx ) diff --git a/src/Charting/ChartTwoAxisOrientationTypeEnum.cxx b/src/Charting/ChartTwoAxisOrientationTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2ca999d0596132993b25b31ce6601c2847b5f14c --- /dev/null +++ b/src/Charting/ChartTwoAxisOrientationTypeEnum.cxx @@ -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 --git a/src/Charting/ChartTwoAxisOrientationTypeEnum.h b/src/Charting/ChartTwoAxisOrientationTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..8f02fbc0b765da9eea2f596932a197595567e7a4 --- /dev/null +++ b/src/Charting/ChartTwoAxisOrientationTypeEnum.h @@ -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 --git a/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx b/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx index bcd52ab519abf433759b53e15e66e3f69d151e77..0663aa53f2086b6a10c237ef06264a0c74ed9201 100644 --- a/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx +++ b/src/Charting/ChartTwoAxisScaleRangeModeEnum.cxx @@ -110,6 +110,7 @@ ChartTwoAxisScaleRangeModeEnum::initialize() } initializedFlag = true; + const AString invalidOldName(""); enumData.push_back(ChartTwoAxisScaleRangeModeEnum(AUTO, "AUTO", "Auto", @@ -125,6 +126,26 @@ ChartTwoAxisScaleRangeModeEnum::initialize() "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 @@ ChartTwoAxisScaleRangeModeEnum::getAllGuiNames(std::vector& allGuiNames } } +/** + * @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 --git a/src/Charting/ChartTwoAxisScaleRangeModeEnum.h b/src/Charting/ChartTwoAxisScaleRangeModeEnum.h index 7aa20fcab8189848b08e49afc48d1276a8686007..32a17778298db171e313ee08567e88216ab4d3ac 100644 --- a/src/Charting/ChartTwoAxisScaleRangeModeEnum.h +++ b/src/Charting/ChartTwoAxisScaleRangeModeEnum.h @@ -40,8 +40,15 @@ public: /** 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 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + static bool isYokingRangeMode(const Enum enumValue); + private: ChartTwoAxisScaleRangeModeEnum(const Enum enumValue, const AString& name, diff --git a/src/Charting/ChartTwoCartesianAxis.cxx b/src/Charting/ChartTwoCartesianAxis.cxx index 9b1af91d7752068ffa8234c560bf9e766ad61c16..86db71e90b870d8d4a4ff16b8a5ac757f28c6178 100644 --- a/src/Charting/ChartTwoCartesianAxis.cxx +++ b/src/Charting/ChartTwoCartesianAxis.cxx @@ -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 @@ using namespace caret; /** * 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_axisLocation(axisLocation) &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_axisLocation(axisLocation) &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 @@ ChartTwoCartesianAxis::operator=(const ChartTwoCartesianAxis& obj) 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 @@ ChartTwoCartesianAxis::setDisplayedByUser(const bool displayedByUser) { 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 @@ void 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 @@ ChartTwoCartesianAxis::getUserDigitsRightOfDecimal() const void ChartTwoCartesianAxis::setUserDigitsRightOfDecimal(const int32_t digitsRightOfDecimal) { - m_userDigitsRightOfDecimal = digitsRightOfDecimal; + getDisplayGroupOrThisAxisForSet()->m_userDigitsRightOfDecimal = digitsRightOfDecimal; } @@ -277,7 +325,7 @@ ChartTwoCartesianAxis::setUserDigitsRightOfDecimal(const int32_t digitsRightOfDe int32_t ChartTwoCartesianAxis::getUserNumberOfSubdivisions() const { - return m_userNumberOfSubdivisions; + return getDisplayGroupOrThisAxisForGet()->m_userNumberOfSubdivisions; } /** @@ -288,7 +336,7 @@ ChartTwoCartesianAxis::getUserNumberOfSubdivisions() const void ChartTwoCartesianAxis::setUserNumberOfSubdivisions(const int32_t numberOfSubdivisions) { - m_userNumberOfSubdivisions = numberOfSubdivisions; + getDisplayGroupOrThisAxisForSet()->m_userNumberOfSubdivisions = numberOfSubdivisions; } /** @@ -297,7 +345,7 @@ ChartTwoCartesianAxis::setUserNumberOfSubdivisions(const int32_t numberOfSubdivi ChartTwoNumericSubdivisionsModeEnum::Enum ChartTwoCartesianAxis::getNumericSubdivsionsMode() const { - return m_numericSubdivsionsMode; + return getDisplayGroupOrThisAxisForGet()->m_numericSubdivsionsMode; } /** @@ -309,70 +357,63 @@ ChartTwoCartesianAxis::getNumericSubdivsionsMode() const 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 @@ ChartTwoCartesianAxis::setEnabledByChart(const bool enabled) bool ChartTwoCartesianAxis::isShowTickmarks() const { - return m_showTickmarks; + return getDisplayGroupOrThisAxisForGet()->m_showTickmarks; } /** @@ -392,7 +433,7 @@ ChartTwoCartesianAxis::isShowTickmarks() const void ChartTwoCartesianAxis::setShowTickmarks(const bool showTickmarks) { - m_showTickmarks = showTickmarks; + getDisplayGroupOrThisAxisForSet()->m_showTickmarks = showTickmarks; } /** @@ -401,7 +442,7 @@ ChartTwoCartesianAxis::setShowTickmarks(const bool showTickmarks) bool ChartTwoCartesianAxis::isShowLabel() const { - return m_showLabel; + return getDisplayGroupOrThisAxisForGet()->m_showLabel; } /** @@ -412,30 +453,18 @@ ChartTwoCartesianAxis::isShowLabel() const 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 */ @@ -463,7 +492,7 @@ ChartTwoCartesianAxis::setUnits(const CaretUnitsTypeEnum::Enum units) NumericFormatModeEnum::Enum ChartTwoCartesianAxis::getUserNumericFormat() const { - return m_userNumericFormat; + return getDisplayGroupOrThisAxisForGet()->m_userNumericFormat; } /** @@ -475,7 +504,7 @@ ChartTwoCartesianAxis::getUserNumericFormat() const void ChartTwoCartesianAxis::setUserNumericFormat(const NumericFormatModeEnum::Enum numericFormat) { - m_userNumericFormat = numericFormat; + getDisplayGroupOrThisAxisForSet()->m_userNumericFormat = numericFormat; } /** @@ -525,7 +554,7 @@ ChartTwoCartesianAxis::setLabelOverlayIndex(const int32_t labelOverlayIndex) float ChartTwoCartesianAxis::getLabelTextSize() const { - return m_labelTextSize; + return getDisplayGroupOrThisAxisForGet()->m_labelTextSize; } /** @@ -537,7 +566,7 @@ ChartTwoCartesianAxis::getLabelTextSize() const void ChartTwoCartesianAxis::setLabelTextSize(const float labelTextSize) { - m_labelTextSize = labelTextSize; + getDisplayGroupOrThisAxisForSet()->m_labelTextSize = labelTextSize; } /** @@ -546,7 +575,7 @@ ChartTwoCartesianAxis::setLabelTextSize(const float labelTextSize) float ChartTwoCartesianAxis::getNumericsTextSize() const { - return m_numericsTextSize; + return getDisplayGroupOrThisAxisForGet()->m_numericsTextSize; } /** @@ -558,7 +587,7 @@ ChartTwoCartesianAxis::getNumericsTextSize() const void ChartTwoCartesianAxis::setNumericsTextSize(const float numericsTextSize) { - m_numericsTextSize = numericsTextSize; + getDisplayGroupOrThisAxisForSet()->m_numericsTextSize = numericsTextSize; } /** @@ -567,7 +596,7 @@ ChartTwoCartesianAxis::setNumericsTextSize(const float numericsTextSize) bool ChartTwoCartesianAxis::isNumericsTextDisplayed() const { - return m_numericsTextDisplayed; + return getDisplayGroupOrThisAxisForGet()->m_numericsTextDisplayed; } /** @@ -579,7 +608,7 @@ ChartTwoCartesianAxis::isNumericsTextDisplayed() const void ChartTwoCartesianAxis::setNumericsTextDisplayed(const bool numericsTextDisplayed) { - m_numericsTextDisplayed = numericsTextDisplayed; + getDisplayGroupOrThisAxisForSet()->m_numericsTextDisplayed = numericsTextDisplayed; } /** @@ -588,7 +617,7 @@ ChartTwoCartesianAxis::setNumericsTextDisplayed(const bool numericsTextDisplayed bool ChartTwoCartesianAxis::isNumericsTextRotated() const { - return m_numericsTextRotated; + return getDisplayGroupOrThisAxisForGet()->m_numericsTextRotated; } /** @@ -600,7 +629,7 @@ ChartTwoCartesianAxis::isNumericsTextRotated() const void ChartTwoCartesianAxis::setNumericsTextRotated(const bool numericsTextRotated) { - m_numericsTextRotated = numericsTextRotated; + getDisplayGroupOrThisAxisForSet()->m_numericsTextRotated = numericsTextRotated; } /** @@ -609,7 +638,7 @@ ChartTwoCartesianAxis::setNumericsTextRotated(const bool numericsTextRotated) float ChartTwoCartesianAxis::getPaddingSize() const { - return m_paddingSize; + return getDisplayGroupOrThisAxisForGet()->m_paddingSize; } /** @@ -621,7 +650,7 @@ ChartTwoCartesianAxis::getPaddingSize() const void ChartTwoCartesianAxis::setPaddingSize(const float paddingSize) { - m_paddingSize = paddingSize; + getDisplayGroupOrThisAxisForSet()->m_paddingSize = paddingSize; } /** @@ -671,347 +700,91 @@ ChartTwoCartesianAxis::restoreFromScene(const SceneAttributes* sceneAttributes, 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; +ChartTwoCartesianAxis* +ChartTwoCartesianAxis::getDisplayGroupOrThisAxisForSet() +{ + ChartTwoCartesianAxis* axis(this); - 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; + /* + * 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 --git a/src/Charting/ChartTwoCartesianAxis.h b/src/Charting/ChartTwoCartesianAxis.h index 265749db1eb7e24c986c6ce263fd65a4b0ee3055..2d0d2146e129d456515a9184cf11aedda68daa2f 100644 --- a/src/Charting/ChartTwoCartesianAxis.h +++ b/src/Charting/ChartTwoCartesianAxis.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { /** 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 --git a/src/Charting/ChartTwoCartesianCustomSubdivisions.cxx b/src/Charting/ChartTwoCartesianCustomSubdivisions.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6f6d42533b427dcf4589c9aa768c1dee53b5138f --- /dev/null +++ b/src/Charting/ChartTwoCartesianCustomSubdivisions.cxx @@ -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 --git a/src/Charting/ChartTwoCartesianCustomSubdivisions.h b/src/Charting/ChartTwoCartesianCustomSubdivisions.h new file mode 100644 index 0000000000000000000000000000000000000000..2eeaf2bc139e0ce6e788d12fbeeac85873cbe17a --- /dev/null +++ b/src/Charting/ChartTwoCartesianCustomSubdivisions.h @@ -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 --git a/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.cxx b/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.cxx new file mode 100644 index 0000000000000000000000000000000000000000..882a3ec791382fba58ec8f640b3a5b7e8f9d724f --- /dev/null +++ b/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.cxx @@ -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 --git a/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.h b/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.h new file mode 100644 index 0000000000000000000000000000000000000000..c0cfd520b5b61520b85b49e57793fc924e4bb0e4 --- /dev/null +++ b/src/Charting/ChartTwoCartesianCustomSubdivisionsLabel.h @@ -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 --git a/src/Charting/ChartTwoCartesianOrientedAxes.cxx b/src/Charting/ChartTwoCartesianOrientedAxes.cxx new file mode 100644 index 0000000000000000000000000000000000000000..affb6585d2af90c88c9c731dbb29ba2784002c99 --- /dev/null +++ b/src/Charting/ChartTwoCartesianOrientedAxes.cxx @@ -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 --git a/src/Charting/ChartTwoCartesianOrientedAxes.h b/src/Charting/ChartTwoCartesianOrientedAxes.h new file mode 100644 index 0000000000000000000000000000000000000000..d0ac4554bc3f989e21e007f6d759661c1378c208 --- /dev/null +++ b/src/Charting/ChartTwoCartesianOrientedAxes.h @@ -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 --git a/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.cxx b/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e2eb8e56d6b402f359c9afed8767d51c58efab62 --- /dev/null +++ b/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.cxx @@ -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 --git a/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.h b/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.h new file mode 100644 index 0000000000000000000000000000000000000000..0badd2ba682711b334be1f64b3a70e079ba2c049 --- /dev/null +++ b/src/Charting/ChartTwoCartesianOrientedAxesYokingManager.h @@ -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 --git a/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.cxx b/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8f8ae149cc89bb49461f9e01c23af243f2e0ed98 --- /dev/null +++ b/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.cxx @@ -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 --git a/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.h b/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..1950d45a907551cd1c2408c496cae312eec0a2f3 --- /dev/null +++ b/src/Charting/ChartTwoCartesianSubdivisionsModeEnum.h @@ -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 --git a/src/Charting/ChartTwoCompoundDataType.cxx b/src/Charting/ChartTwoCompoundDataType.cxx index 4326d466372fdfe3daf3c9b7dacc7497f97fef28..3b1e0d016c6ef0ab6ba7c6ac8b73a5f4c9206983 100644 --- a/src/Charting/ChartTwoCompoundDataType.cxx +++ b/src/Charting/ChartTwoCompoundDataType.cxx @@ -49,6 +49,8 @@ using namespace caret; * 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 @@ using namespace caret; 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 @@ ChartTwoCompoundDataType::ChartTwoCompoundDataType(const ChartTwoDataTypeEnum::E 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 @@ ChartTwoCompoundDataType::newInstanceForHistogram(const int32_t histogramNumberO 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 @@ ChartTwoCompoundDataType::newInstanceForLineSeries(const CaretUnitsTypeEnum::Enu /** * @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 @@ ChartTwoCompoundDataType::initializeChartTwoCompoundDataType() { 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 @@ ChartTwoCompoundDataType::initializeChartTwoCompoundDataType() &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 @@ ChartTwoCompoundDataType::initializeChartTwoCompoundDataType() * 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 @@ ChartTwoCompoundDataType::operator==(const ChartTwoCompoundDataType& obj) const 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) { @@ -295,6 +348,15 @@ ChartTwoCompoundDataType::getLineChartUnitsAxisX() const return m_lineChartUnitsAxisX; } +/** + * @return Line chart Y-axis units. + */ +CaretUnitsTypeEnum::Enum +ChartTwoCompoundDataType::getLineChartUnitsAxisY() const +{ + return m_lineChartUnitsAxisY; +} + /** * @return Line chart number of element in the X-axis. */ @@ -337,19 +399,36 @@ ChartTwoCompoundDataType::toString() const 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)); diff --git a/src/Charting/ChartTwoCompoundDataType.h b/src/Charting/ChartTwoCompoundDataType.h index 5463afa7d2da445a2e1f7e73b2c8d80b87910703..7aa70104de5415f60a8290eb34ff2ad8a8154c6a 100644 --- a/src/Charting/ChartTwoCompoundDataType.h +++ b/src/Charting/ChartTwoCompoundDataType.h @@ -39,17 +39,25 @@ namespace caret { 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 @@ namespace caret { CaretUnitsTypeEnum::Enum getLineChartUnitsAxisX() const; + CaretUnitsTypeEnum::Enum getLineChartUnitsAxisY() const; + int32_t getLineChartNumberOfElementsAxisX() const; int32_t getMatrixNumberOfRows() const; @@ -110,6 +120,8 @@ namespace caret { CaretUnitsTypeEnum::Enum m_lineChartUnitsAxisX; + CaretUnitsTypeEnum::Enum m_lineChartUnitsAxisY; + int32_t m_lineChartNumberOfElementsAxisX; int32_t m_matrixNumberOfRows; diff --git a/src/Charting/ChartTwoDataCartesian.cxx b/src/Charting/ChartTwoDataCartesian.cxx index 65fdd1f1032eb20f25ac89b4b85eaf8d0a22af3f..b481467a848c03c423d4d5024227479976440d37 100644 --- a/src/Charting/ChartTwoDataCartesian.cxx +++ b/src/Charting/ChartTwoDataCartesian.cxx @@ -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 @@ m_graphicsPrimitiveType(graphicsPrimitiveType) 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 @@ ChartTwoDataCartesian::initializeMembersChartTwoDataCartesian() 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 @@ ChartTwoDataCartesian::initializeMembersChartTwoDataCartesian() 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 @@ ChartTwoDataCartesian::initializeMembersChartTwoDataCartesian() /* 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 @@ ChartTwoDataCartesian::initializeMembersChartTwoDataCartesian() &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 @@ ChartTwoDataCartesian::initializeMembersChartTwoDataCartesian() 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 @@ ChartTwoDataCartesian::initializeMembersChartTwoDataCartesian() 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())); } /** @@ -204,6 +210,26 @@ ChartTwoDataCartesian::clone() const return cloneCopy; } +/** + * 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 @@ -252,10 +278,13 @@ ChartTwoDataCartesian::copyHelperChartTwoDataCartesian(const ChartTwoDataCartesi 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 @@ void 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,11 +330,35 @@ ChartTwoDataCartesian::setSelected(const bool selectionStatus) */ 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. * @@ -395,12 +438,21 @@ ChartTwoDataCartesian::getDataAxisUnitsY() } /** - * @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,17 +462,29 @@ ChartTwoDataCartesian::getColor() const * 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. */ @@ -444,6 +508,157 @@ ChartTwoDataCartesian::setLineWidth(const float lineWidth) m_lineWidth); } +/** + * @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. * @@ -464,9 +679,12 @@ ChartTwoDataCartesian::saveToScene(const SceneAttributes* sceneAttributes, 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 @@ ChartTwoDataCartesian::restoreFromScene(const SceneAttributes* sceneAttributes, 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 --git a/src/Charting/ChartTwoDataCartesian.h b/src/Charting/ChartTwoDataCartesian.h index fe93f530a76d17e90db853e93e01033291cb9a66..7bf1da951844c56fdd08169b194022e11488885c 100644 --- a/src/Charting/ChartTwoDataCartesian.h +++ b/src/Charting/ChartTwoDataCartesian.h @@ -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 @@ namespace caret { class ChartPoint; class GraphicsPrimitiveV3f; class MapFileDataSelector; + class Matrix4x4Interface; class SceneClassAssistant; class ChartTwoDataCartesian : public CaretObjectTracksModification, public SceneableInterface { @@ -48,6 +49,8 @@ namespace caret { virtual ChartTwoDataCartesian* clone() const; + virtual ChartTwoDataCartesian* clone(const Matrix4x4Interface& matrix) const; + bool isSelected() const; void setSelected(const bool selectionStatus); @@ -55,6 +58,9 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { */ 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 @@ namespace caret { 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 @@ namespace caret { bool m_selectionStatus; - CaretColorEnum::Enum m_color; + CaretColor m_caretColor; float m_lineWidth; @@ -135,6 +147,10 @@ namespace caret { float m_timeStepInSecondsAxisX; + mutable int32_t m_selectedPointIndex = 0; + + bool m_selectedPointDisplayed = false; + static int32_t caretColorIndex; SceneClassAssistant* m_sceneAssistant; diff --git a/src/Charting/ChartTwoDataTypeEnum.cxx b/src/Charting/ChartTwoDataTypeEnum.cxx index 9bac88d38061f5765a7c83fa64b08bf309feb2cc..bbc7549cae7998d489451cd0a971a0b71b251b58 100644 --- a/src/Charting/ChartTwoDataTypeEnum.cxx +++ b/src/Charting/ChartTwoDataTypeEnum.cxx @@ -77,15 +77,19 @@ using namespace caret; * * @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 @@ ChartTwoDataTypeEnum::initialize() 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?"); @@ -256,6 +271,21 @@ ChartTwoDataTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) return enumValue; } +/** + * 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. * diff --git a/src/Charting/ChartTwoDataTypeEnum.h b/src/Charting/ChartTwoDataTypeEnum.h index af3b482a5849c2cd57c6a3742d15eec016eb1c9b..7f555593fc0fdab7da24449d4b1acb493e56746b 100644 --- a/src/Charting/ChartTwoDataTypeEnum.h +++ b/src/Charting/ChartTwoDataTypeEnum.h @@ -39,6 +39,8 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: /** A user-friendly name that is displayed in the GUI */ AString guiName; + + AString toolTipText; }; #ifdef __CHART_TWO_DATA_TYPE_ENUM_DECLARE__ diff --git a/src/Charting/ChartTwoLineLayerContentTypeEnum.cxx b/src/Charting/ChartTwoLineLayerContentTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c7cbd6b34603842fff40922cc625271781b29072 --- /dev/null +++ b/src/Charting/ChartTwoLineLayerContentTypeEnum.cxx @@ -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 --git a/src/Charting/ChartTwoLineLayerContentTypeEnum.h b/src/Charting/ChartTwoLineLayerContentTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..bece25952b52722ce03a1e5a8efbddea5c7068ab --- /dev/null +++ b/src/Charting/ChartTwoLineLayerContentTypeEnum.h @@ -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 --git a/src/Charting/ChartTwoLineSeriesHistory.cxx b/src/Charting/ChartTwoLineSeriesHistory.cxx index 6cb8dacdf45cf1d4e2cca13cf1bc273014b2d649..53be2c528a25e0f329414e8c645b10a614d523c8 100644 --- a/src/Charting/ChartTwoLineSeriesHistory.cxx +++ b/src/Charting/ChartTwoLineSeriesHistory.cxx @@ -386,7 +386,7 @@ ChartTwoLineSeriesHistory::addHistoryItem(ChartTwoDataCartesian* historyItem) } } - historyItem->setColor(m_defaultColor); + historyItem->setColorEnum(m_defaultColor); historyItem->setLineWidth(m_defaultLineWidth); addHistoryItemNoDefaults(historyItem); updateDisplayedHistoryItems(); diff --git a/src/Charting/ChartTwoMatrixDisplayProperties.cxx b/src/Charting/ChartTwoMatrixDisplayProperties.cxx index dc896c863f04c75d81adac78b67ec15c9089e307..175e51666a308e8260f2dbb2d104e992243f5469 100644 --- a/src/Charting/ChartTwoMatrixDisplayProperties.cxx +++ b/src/Charting/ChartTwoMatrixDisplayProperties.cxx @@ -25,6 +25,7 @@ #include "CaretAssert.h" #include "EventManager.h" +#include "MathFunctions.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -66,10 +67,6 @@ ChartTwoMatrixDisplayProperties::initializeInstance() { 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 @@ ChartTwoMatrixDisplayProperties::operator=(const ChartTwoMatrixDisplayProperties 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 @@ ChartTwoMatrixDisplayProperties::receiveEvent(Event* /*event*/) void ChartTwoMatrixDisplayProperties::resetPropertiesToDefault() { - m_cellPercentageZoomWidth = 100.0; - m_cellPercentageZoomHeight = 100.0; m_highlightSelectedRowColumnFlag = true; m_displayGridLinesFlag = false; } @@ -200,47 +193,6 @@ ChartTwoMatrixDisplayProperties::setSelectedRowColumnHighlighted(const bool high m_highlightSelectedRowColumnFlag = highlightStatus; } -/** - * @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. * @@ -258,7 +210,7 @@ ChartTwoMatrixDisplayProperties::saveToScene(const SceneAttributes* sceneAttribu { SceneClass* sceneClass = new SceneClass(instanceName, "ChartTwoMatrixDisplayProperties", - 1); + 2); /* Version 2 removes cell zoom width/height */ m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -291,6 +243,17 @@ ChartTwoMatrixDisplayProperties::restoreFromScene(const SceneAttributes* sceneAt 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 --git a/src/Charting/ChartTwoMatrixDisplayProperties.h b/src/Charting/ChartTwoMatrixDisplayProperties.h index 19a9c75e5e31f57e9dbd106ae3fb370aca7bbbbf..38b311e420cd5919e3a6ab275829c7f473847d17 100644 --- a/src/Charting/ChartTwoMatrixDisplayProperties.h +++ b/src/Charting/ChartTwoMatrixDisplayProperties.h @@ -52,14 +52,6 @@ namespace caret { 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 @@ namespace caret { SceneClassAssistant* m_sceneAssistant; - float m_cellPercentageZoomHeight; - - float m_cellPercentageZoomWidth; - bool m_highlightSelectedRowColumnFlag; bool m_displayGridLinesFlag; diff --git a/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx b/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx index cdffdd5a46a7fe59cb3fa146db49c1cf024927fa..7ef17ebc88ee17688340c84576cd9b17ad125193 100644 --- a/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx +++ b/src/Charting/ChartTwoNumericSubdivisionsModeEnum.cxx @@ -106,13 +106,17 @@ ChartTwoNumericSubdivisionsModeEnum::initialize() } 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 --git a/src/Charting/ChartTwoOverlaySetInterface.cxx b/src/Charting/ChartTwoOverlaySetInterface.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8954e00c30ffedf9d3f0795a4488bd2fd042d307 --- /dev/null +++ b/src/Charting/ChartTwoOverlaySetInterface.cxx @@ -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 --git a/src/Charting/ChartTwoOverlaySetInterface.h b/src/Charting/ChartTwoOverlaySetInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..3f0cbbd61d85fc4d9fd4cb5468fe31ccdb2dea98 --- /dev/null +++ b/src/Charting/ChartTwoOverlaySetInterface.h @@ -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 --git a/src/Charting/EventChartTwoAttributesChanged.cxx b/src/Charting/EventChartTwoAttributesChanged.cxx deleted file mode 100644 index 639e1c09792dba4c0b6eb2fa874a1a9c5d6db06d..0000000000000000000000000000000000000000 --- a/src/Charting/EventChartTwoAttributesChanged.cxx +++ /dev/null @@ -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 --git a/src/Charting/EventChartTwoAttributesChanged.h b/src/Charting/EventChartTwoAttributesChanged.h deleted file mode 100644 index 993094619b56fbb6bdc3e10afb02996a33c0e803..0000000000000000000000000000000000000000 --- a/src/Charting/EventChartTwoAttributesChanged.h +++ /dev/null @@ -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 --git a/src/Charting/EventChartTwoAxisGetDataRange.cxx b/src/Charting/EventChartTwoAxisGetDataRange.cxx deleted file mode 100644 index f34aff3a6cc8cbff86aef1913794c5aa7240864f..0000000000000000000000000000000000000000 --- a/src/Charting/EventChartTwoAxisGetDataRange.cxx +++ /dev/null @@ -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 --git a/src/Charting/EventChartTwoAxisGetDataRange.h b/src/Charting/EventChartTwoAxisGetDataRange.h deleted file mode 100644 index 207c13fc3594059d731010bb2da82f72e7330283..0000000000000000000000000000000000000000 --- a/src/Charting/EventChartTwoAxisGetDataRange.h +++ /dev/null @@ -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 --git a/src/Charting/EventChartTwoCartesianAxisDisplayGroup.cxx b/src/Charting/EventChartTwoCartesianAxisDisplayGroup.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0f396950f48c2054f44f3b408a9cdda4f6f5db84 --- /dev/null +++ b/src/Charting/EventChartTwoCartesianAxisDisplayGroup.cxx @@ -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 --git a/src/Charting/EventChartTwoCartesianAxisDisplayGroup.h b/src/Charting/EventChartTwoCartesianAxisDisplayGroup.h new file mode 100644 index 0000000000000000000000000000000000000000..5c2a254c887e10011707b7f3c2bf39f56daa36c5 --- /dev/null +++ b/src/Charting/EventChartTwoCartesianAxisDisplayGroup.h @@ -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 --git a/src/Charting/EventChartTwoCartesianOrientedAxesYoking.cxx b/src/Charting/EventChartTwoCartesianOrientedAxesYoking.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8bcc241814b354e791484f6dc05b08786c17154e --- /dev/null +++ b/src/Charting/EventChartTwoCartesianOrientedAxesYoking.cxx @@ -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 --git a/src/Charting/EventChartTwoCartesianOrientedAxesYoking.h b/src/Charting/EventChartTwoCartesianOrientedAxesYoking.h new file mode 100644 index 0000000000000000000000000000000000000000..4eab5e32622d863b70d6004773847ccf45ec40bd --- /dev/null +++ b/src/Charting/EventChartTwoCartesianOrientedAxesYoking.h @@ -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 --git a/src/Cifti/CiftiFile.cxx b/src/Cifti/CiftiFile.cxx index 13663ed9b2580dd48072b411d1a53be898853608..512ec9050c379feb2d5ae4ee83b9dfcaa0e8ca6d 100644 --- a/src/Cifti/CiftiFile.cxx +++ b/src/Cifti/CiftiFile.cxx @@ -551,6 +551,7 @@ namespace 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 @@ namespace 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 --git a/src/CommandLine/CMakeLists.txt b/src/CommandLine/CMakeLists.txt index dfd865211f838f848c7a8572dbdde2a5d6382736..36933f57c2ba66a24cd476c8ba1f84676b745f71 100644 --- a/src/CommandLine/CMakeLists.txt +++ b/src/CommandLine/CMakeLists.txt @@ -147,3 +147,6 @@ ${CMAKE_SOURCE_DIR}/Common ${QUAZIP_INCLUDE_DIRS} ) +ENABLE_TESTING() + +ADD_TEST(command_help ${EXE_NAME} -all-commands-help) diff --git a/src/Commands/CommandClassCreateEnum.cxx b/src/Commands/CommandClassCreateEnum.cxx index ecd2383a1a8ff4263df9e14535bd625e0ce1d586..cdc209e75734c0a90981d6366e9335111790f966 100644 --- a/src/Commands/CommandClassCreateEnum.cxx +++ b/src/Commands/CommandClassCreateEnum.cxx @@ -524,7 +524,7 @@ CommandClassCreateEnum::createImplementationFile(const AString& outputFileName, 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 @@ CommandClassCreateEnum::createImplementationFile(const AString& outputFileName, 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 @@ CommandClassCreateEnum::createImplementationFile(const AString& outputFileName, 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 --git a/src/Commands/CommandOperation.cxx b/src/Commands/CommandOperation.cxx index 9943c1c1df031798140d156fd77c07a494b4ad80..e60bdd7f5b8f89881758ea57ce4ee9423437a823 100644 --- a/src/Commands/CommandOperation.cxx +++ b/src/Commands/CommandOperation.cxx @@ -82,6 +82,14 @@ void CommandOperation::setCiftiOutputDTypeNoScale(const int16_t&) { } +void CommandOperation::setVolumeOutputDTypeAndScale(const int16_t&, const double&, const double&) +{ +} + +void CommandOperation::setVolumeOutputDTypeNoScale(const int16_t&) +{ +} + AString CommandOperation::doCompletion(ProgramParameters&, const bool&) { return ""; diff --git a/src/Commands/CommandOperation.h b/src/Commands/CommandOperation.h index 0d54ef43509d2f66c0dbb820ce16dee9f14276ef..e376138c2386b6bc4561746fbfdb086dc2a42be8 100644 --- a/src/Commands/CommandOperation.h +++ b/src/Commands/CommandOperation.h @@ -42,11 +42,15 @@ namespace caret { 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 --git a/src/Commands/CommandOperationManager.cxx b/src/Commands/CommandOperationManager.cxx index 96a126792b887957d83537a16710e1b000033343..060eb01c1e6cc3b6a0a1bfa5be07f2264e125f22 100644 --- a/src/Commands/CommandOperationManager.cxx +++ b/src/Commands/CommandOperationManager.cxx @@ -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 @@ CommandOperationManager::CommandOperationManager() 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 @@ CommandOperationManager::CommandOperationManager() 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 @@ CommandOperationManager::CommandOperationManager() 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 @@ namespace 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 @@ CommandOperationManager::runCommand(ProgramParameters& parameters) 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 @@ CommandOperationManager::runCommand(ProgramParameters& parameters) 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 @@ CommandOperationManager::runCommand(ProgramParameters& parameters) { 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 @@ AString CommandOperationManager::doCompletion(ProgramParameters& parameters, con 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 @@ void CommandOperationManager::printGlobalOptions() 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 @@ void CommandOperationManager::printGlobalOptions() 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 @@ void CommandOperationManager::printGlobalOptions() 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 --git a/src/Commands/CommandParser.cxx b/src/Commands/CommandParser.cxx index 1486779ba58285a283a0e545bc8e2775e2ddbebd..a6d7e233596b2f08655919fc03c626d81d03efd1 100644 --- a/src/Commands/CommandParser.cxx +++ b/src/Commands/CommandParser.cxx @@ -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 @@ CommandParser::CommandParser(AutoOperationInterface* myAutoOper) : 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 @@ void CommandParser::setCiftiOutputDTypeNoScale(const int16_t& dtype) 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 @@ void CommandParser::parseComponent(ParameterComponent* myComponent, ProgramParam 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 @@ bool CommandParser::parseOption(const AString& mySwitch, ParameterComponent* myC 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 @@ void CommandParser::makeOnDiskOutputs(const vector& outAssociation) { 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 @@ void CommandParser::writeOutput(const vector& outAssociation) 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 --git a/src/Commands/CommandParser.h b/src/Commands/CommandParser.h index 9924a9c54a36d3e5c1415311d93b1d9887e7d623..46b59ce2dd807ab3bb37792687e81fc44592feaf 100644 --- a/src/Commands/CommandParser.h +++ b/src/Commands/CommandParser.h @@ -37,11 +37,11 @@ namespace caret { { 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 @@ namespace caret { 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 --git a/src/Common/AString.cxx b/src/Common/AString.cxx index b892ddba1df83357884e26a3cb226eb5fd69f636..a84fc2eb83e269f4478e5d91ff9db6ebaebed9a2 100644 --- a/src/Common/AString.cxx +++ b/src/Common/AString.cxx @@ -977,7 +977,7 @@ AString::findLongestCommonPrefix(const std::vector& stringVector) */ 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 @@ AString::findLongestCommonPrefix(const std::vector& stringVector) 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 @@ AString::findLongestCommonPrefix(const std::vector& stringVector) 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 --git a/src/Common/AString.h b/src/Common/AString.h index f7fc09733d9c775c338703e3aa8f20847613c3a5..5f11e33068ea48f3a81e1452890a600b55e753cf 100644 --- a/src/Common/AString.h +++ b/src/Common/AString.h @@ -22,6 +22,7 @@ #define ASTRING_H #include +#include #include #include @@ -40,6 +41,7 @@ namespace caret { 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 @@ namespace caret { //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 --git a/src/Common/BackgroundAndForegroundColors.cxx b/src/Common/BackgroundAndForegroundColors.cxx index 9df91a4ba07ccc6787c740a09ecbf1dcb7a9992b..7972aaf2ea4cd2a53efc08fc4d695d497b9a7ba1 100644 --- a/src/Common/BackgroundAndForegroundColors.cxx +++ b/src/Common/BackgroundAndForegroundColors.cxx @@ -140,6 +140,14 @@ BackgroundAndForegroundColors::operator==(const BackgroundAndForegroundColors& o 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 @@ BackgroundAndForegroundColors::copyHelperBackgroundAndForegroundColors(const Bac 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 @@ BackgroundAndForegroundColors::reset() 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 @@ BackgroundAndForegroundColors::setColorChartHistogramThreshold(const uint8_t col } } +/** + * 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 --git a/src/Common/BackgroundAndForegroundColors.h b/src/Common/BackgroundAndForegroundColors.h index eb123170b1a17e158c9ba6be38ee4166ef343af5..0688129e4fc330a211bb08fc27d574a67f047908 100644 --- a/src/Common/BackgroundAndForegroundColors.h +++ b/src/Common/BackgroundAndForegroundColors.h @@ -96,6 +96,14 @@ namespace caret { 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 @@ namespace caret { uint8_t m_colorChartHistogramThreshold[3]; + uint8_t m_colorForegroundMedia[3]; + + uint8_t m_colorBackgroundMedia[3]; + // ADD_NEW_MEMBERS_HERE friend class BackgroundAndForegroundColorsSceneHelper; diff --git a/src/Common/BoundingBox.cxx b/src/Common/BoundingBox.cxx index 37b328ee6a544d677d43561daff3211bb0cc481b..6eeee990f522869d7c111eccc2e0923168bdd8a2 100644 --- a/src/Common/BoundingBox.cxx +++ b/src/Common/BoundingBox.cxx @@ -22,6 +22,7 @@ #include #include #include "BoundingBox.h" +#include "MathFunctions.h" using namespace caret; @@ -108,6 +109,19 @@ BoundingBox::copyHelper(const BoundingBox& bo) } } +/** + * @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. @@ -203,6 +217,30 @@ BoundingBox::set(const float minMaxXYZ[6]) this->boundingBox[5] = minMaxXYZ[5]; } +/** + * 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 @@ -249,6 +287,63 @@ BoundingBox::update(const float x, if (z > this->boundingBox[5]) this->boundingBox[5] = z; } +/** + * 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. @@ -309,6 +404,18 @@ BoundingBox::getDifferenceZ() const return (this->boundingBox[5] - this->boundingBox[4]); } +/** + * @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 @@ BoundingBox::getMaxZ() const } /** - * 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 @@ BoundingBox::getMinXYZ() const } /** - * 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 @@ BoundingBox::toString() const 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 --git a/src/Common/BoundingBox.h b/src/Common/BoundingBox.h index 836931630d278e9d259b351a5b5f798b542dd2e7..e0566ff96102a4da82022c89849e0b317dcbfff4 100644 --- a/src/Common/BoundingBox.h +++ b/src/Common/BoundingBox.h @@ -21,7 +21,7 @@ */ /*LICENSE_END*/ - +#include #include #include @@ -47,6 +47,8 @@ public: virtual ~BoundingBox(); public: + bool isValid2D() const; + void resetZeros(); void resetWithMaximumExtent(); @@ -64,12 +66,23 @@ public: 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 @@ public: float getDifferenceZ() const; + float getMaximumDifferenceOfXYZ() const; + float getMinX() const; float getMaxX() const; @@ -92,9 +107,9 @@ public: 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 @@ public: void limitCoordinateToBoundingBox(double xyz[3]) const; + bool intersectsXY(const BoundingBox& bb) const; + AString toString() const; private: diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index f8d6d2db5679d10130570189ad50ea8dd0833d6b..55c5d4485d6e3e78432299d7f0e65ddd9c1de9dd 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -58,11 +58,14 @@ BoundingBox.h 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 @@ CaretPointer.h 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 @@ DisplayGroupEnum.h 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 @@ LogManager.h LogRecord.h MathFunctionEnum.h MathFunctions.h +Matrix4x4Interface.h MatrixFunctions.h ModelTransform.h MultiDimArray.h @@ -143,6 +158,13 @@ ProgramParameters.h 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 @@ StereotaxicSpaceEnum.h 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 @@ BoundingBox.cxx 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 @@ CaretObjectTracksModification.cxx 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 @@ DisplayGroupEnum.cxx 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 @@ Plane.cxx 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 @@ StereotaxicSpaceEnum.cxx 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 --git a/src/Common/CardinalDirectionEnum.cxx b/src/Common/CardinalDirectionEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..cc756fef017ee13806e124945065eb1ff27f464b --- /dev/null +++ b/src/Common/CardinalDirectionEnum.cxx @@ -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 --git a/src/Common/CardinalDirectionEnum.h b/src/Common/CardinalDirectionEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..a7faa86c4d661fff3e5dbaa293c82a2b59ef2eb3 --- /dev/null +++ b/src/Common/CardinalDirectionEnum.h @@ -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 --git a/src/Common/CaretAssert.h b/src/Common/CaretAssert.h index 6ad9c58cfbb2755bbbdfea48ec2942bab4b0d9a8..d4e42267a6d27cb6555dbb04ddba5bb50d9f02aa 100644 --- a/src/Common/CaretAssert.h +++ b/src/Common/CaretAssert.h @@ -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 --git a/src/Common/CaretAssertion.cxx b/src/Common/CaretAssertion.cxx index 9603ad0141fc6c9e43a757b4473e30439365d1b5..53e6b71831d01dde89d042ebd17d31b95995e6ee 100644 --- a/src/Common/CaretAssertion.cxx +++ b/src/Common/CaretAssertion.cxx @@ -144,6 +144,36 @@ CaretAssertion::assertToDoFatal(const char* filename, } } +/** + * 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 + * 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: diff --git a/src/Common/CaretAssertion.h b/src/Common/CaretAssertion.h index 6cc278b12222c9b5ce55ff75daab1d50bf17591f..f6848bf7eb2f8366d2be15b92fe120c465920a7d 100644 --- a/src/Common/CaretAssertion.h +++ b/src/Common/CaretAssertion.h @@ -27,6 +27,8 @@ namespace caret { + class AString; + /** * Contains static methods for assertion processing. * @@ -54,6 +56,11 @@ public: 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 --git a/src/Common/CaretBinaryFile.cxx b/src/Common/CaretBinaryFile.cxx index a0d263d24d602de9f5b66e7faf10dc5262a7f026..d78259346ef1acf374f8e14facf7d0e8c850d4c2 100644 --- a/src/Common/CaretBinaryFile.cxx +++ b/src/Common/CaretBinaryFile.cxx @@ -348,6 +348,7 @@ void QFileImpl::read(void* dataOut, const int64_t& count, int64_t* numRead) 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 --git a/src/Common/CaretColor.cxx b/src/Common/CaretColor.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a33cabf4247f67a64dfa48a7948d881590c44f8e --- /dev/null +++ b/src/Common/CaretColor.cxx @@ -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 --git a/src/Common/CaretColor.h b/src/Common/CaretColor.h new file mode 100644 index 0000000000000000000000000000000000000000..33a56242caf16df664334b6260c73848f46768ce --- /dev/null +++ b/src/Common/CaretColor.h @@ -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 --git a/src/Common/CaretCommandGlobalOptions.cxx b/src/Common/CaretCommandGlobalOptions.cxx new file mode 100644 index 0000000000000000000000000000000000000000..440b3fff1fbf6c3c3ae16092e32f7b17e5420ce8 --- /dev/null +++ b/src/Common/CaretCommandGlobalOptions.cxx @@ -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 --git a/src/Common/CaretCommandGlobalOptions.h b/src/Common/CaretCommandGlobalOptions.h new file mode 100644 index 0000000000000000000000000000000000000000..301c6e1eb25af2c3672ba852c3a80ee5d14a0ae9 --- /dev/null +++ b/src/Common/CaretCommandGlobalOptions.h @@ -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 --git a/src/Common/CaretLogger.h b/src/Common/CaretLogger.h index 8289af1db323b30b16b5abd7a9901b5aa74c9043..8501c1490fd137b8cb82b3ba5fc90a083376ccae 100644 --- a/src/Common/CaretLogger.h +++ b/src/Common/CaretLogger.h @@ -53,6 +53,11 @@ namespace caret { */ 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 --git a/src/Common/CaretMathExpression.cxx b/src/Common/CaretMathExpression.cxx index 7e822f9c7fcff40213c3f01836ebe72c374441df..060deacbb3e5c9d45f0272cc7d5f80285db67567 100644 --- a/src/Common/CaretMathExpression.cxx +++ b/src/Common/CaretMathExpression.cxx @@ -36,7 +36,7 @@ CaretMathExpression::CaretMathExpression(const AString& expression) 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 @@ double CaretMathExpression::MathNode::eval(const vector& values) const 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 @@ AString CaretMathExpression::MathNode::toString(const std::vector& varN 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 @@ AString CaretMathExpression::MathNode::toString(const std::vector& varN 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 @@ AString CaretMathExpression::MathNode::toString(const std::vector& varN { 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 @@ AString CaretMathExpression::MathNode::toString(const std::vector& varN { 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 @@ AString CaretMathExpression::MathNode::toString(const std::vector& varN { 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 @@ AString CaretMathExpression::MathNode::toString(const std::vector& varN { 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 @@ AString CaretMathExpression::MathNode::toString(const std::vector& varN 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 @@ AString CaretMathExpression::MathNode::toString(const std::vector& varN 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 @@ bool CaretMathExpression::accept(const char& c) 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 @@ CaretPointer CaretMathExpression::powExpr() 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 @@ CaretPointer CaretMathExpression::funcExpr() 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 CaretMathExpression::terminal() { 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 @@ CaretPointer CaretMathExpression::terminal() 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::terminal() 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 --git a/src/Common/CaretMathExpression.h b/src/Common/CaretMathExpression.h index 533c9e7cd6a8c713cf11e8f486dd31b36fadc2ab..29d39b6980250a50a9d4bfacc7d4a4c16e5d539c 100644 --- a/src/Common/CaretMathExpression.h +++ b/src/Common/CaretMathExpression.h @@ -61,7 +61,7 @@ class CaretMathExpression 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 @@ class CaretMathExpression 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 --git a/src/Common/CaretPreferenceDataValue.h b/src/Common/CaretPreferenceDataValue.h index 390c6ee31800a5b1fe7977a0d0dafe14edaf4d5c..4c6cbc1703f43c5029875f3f8235dd9fa18b806c 100644 --- a/src/Common/CaretPreferenceDataValue.h +++ b/src/Common/CaretPreferenceDataValue.h @@ -39,6 +39,7 @@ namespace caret { public: enum class DataType { + BOOLEAN, FLOAT, INTEGER, STRING diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 791e93d85d20bbf5ac9ea950fd29750ce1745d7f..15ba42fadb170b01ece073caaf88bc8da8dbcbcb 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -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 @@ CaretPreferences::CaretPreferences() 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 @@ CaretPreferences::CaretPreferences() 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 @@ CaretPreferences::~CaretPreferences() * 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 @@ CaretPreferences::~CaretPreferences() void CaretPreferences::invalidateSceneDataValues() { - for (auto pdv : m_preferenceDataValues) { + for (auto pdv : m_preferenceStoredInSceneDataValues) { pdv->setSceneValueValid(false); } } @@ -114,7 +166,7 @@ CaretPreferences::invalidateSceneDataValues() std::vector CaretPreferences::getPreferenceSceneDataValues() { - return m_preferenceDataValues; + return m_preferenceStoredInSceneDataValues; } /** @@ -446,10 +498,8 @@ CaretPreferences::writeMacros() 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 @@ CaretPreferences::removeAllTileTabsConfigurations() * 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 @@ CaretPreferences::writeTileTabsConfigurations() * 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 @@ CaretPreferences::readTileTabsConfigurations(const bool performSync) 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. + * @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 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. + * @param includeManualConfigurationsFlag + * If true, include manual configurations; if false exclude manual configurations */ -TileTabsConfiguration* -CaretPreferences::getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) +std::vector> +CaretPreferences::getTileTabsUserConfigurationsNamesAndUniqueIdentifiers(const bool includeManualConfigurationsFlag) const { - for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); - iter != this->tileTabsConfigurations.end(); - iter++) { - TileTabsConfiguration* ttc = *iter; - if (ttc->getUniqueIdentifier() == uniqueIdentifier) { - return ttc; + 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 @@ CaretPreferences::getTileTabsConfigurationByUniqueIdentifier(const AString& uniq * 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 @@ CaretPreferences::getTileTabsConfigurationByName(const AString& name) const } /** - * 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 @@ CaretPreferences::setUserBackgroundAndForegroundColors(const BackgroundAndForegr 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 @@ CaretPreferences::setBackgroundAndForegroundColorsMode(const BackgroundAndForegr 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 @@ CaretPreferences::setBalsaUserName(const AString& userName) } /** - * @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 @@ CaretPreferences::readPreferences() 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 @@ CaretPreferences::readPreferences() 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 @@ CaretPreferences::byteRgbToFloatRgb(const uint8_t byteRGB[3], } } +/** + * 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 --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index b839d160be73cd521c5ad0fb3c0d43329102a20e..ef20479f88acf344b6bb2e2f52e4952d2dda51e7 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -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 @@ namespace caret { class CaretPreferenceDataValue; class ModelTransform; - class TileTabsConfiguration; + class RecentFileItemsContainer; + class TileTabsLayoutBaseConfiguration; class WuQMacroGroup; class CaretPreferences : public CaretObject { @@ -61,24 +67,47 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { void setDynamicConnectivityDefaultedOn(const bool defaultedOn); + bool isGuiGesturesEnabled() const; + + void setGuiGesturesEnabled(const bool status); + WuQMacroGroup* getMacros(); const WuQMacroGroup* getMacros() const; @@ -218,6 +271,29 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { std::vector previousSceneFiles; - std::vector previousOpenFileDirectories; - LogLevelEnum::Enum loggingLevel; ImageCaptureMethodEnum::Enum imageCaptureMethod; @@ -287,7 +374,7 @@ namespace caret { std::vector customViews; - std::vector tileTabsConfigurations; + std::vector tileTabsConfigurations; bool displayVolumeAxesCrosshairs; @@ -303,7 +390,21 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Common/CaretResult.cxx b/src/Common/CaretResult.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8f9aa24fd98e62fc73f2decae95bbef356ea86a6 --- /dev/null +++ b/src/Common/CaretResult.cxx @@ -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 --git a/src/Common/CaretResult.h b/src/Common/CaretResult.h new file mode 100644 index 0000000000000000000000000000000000000000..61dd1cfe6b11818c2cd69dd1f412f5b2c8385538 --- /dev/null +++ b/src/Common/CaretResult.h @@ -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 --git a/src/Common/CaretRgb.cxx b/src/Common/CaretRgb.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4e65d5c24eed1d916ca3852c8fb77b80c561f853 --- /dev/null +++ b/src/Common/CaretRgb.cxx @@ -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 --git a/src/Common/CaretRgb.h b/src/Common/CaretRgb.h new file mode 100644 index 0000000000000000000000000000000000000000..08829c5ee54c89b0f9f06bf8752d406b5904952a --- /dev/null +++ b/src/Common/CaretRgb.h @@ -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 --git a/src/Common/ColorFunctions.cxx b/src/Common/ColorFunctions.cxx new file mode 100644 index 0000000000000000000000000000000000000000..cccc0cc51388266b421aea1579fbefe82233a4a7 --- /dev/null +++ b/src/Common/ColorFunctions.cxx @@ -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 --git a/src/Common/ColorFunctions.h b/src/Common/ColorFunctions.h new file mode 100644 index 0000000000000000000000000000000000000000..687491c7279c595e625b4433401721f8f5c2590c --- /dev/null +++ b/src/Common/ColorFunctions.h @@ -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 --git a/src/Common/CubicSpline.cxx b/src/Common/CubicSpline.cxx index f08cc2c599e4d996e0963c509ff11b8f539df8d9..8ffb11b54114bf2f99cdaaff726df67bf2ed049b 100644 --- a/src/Common/CubicSpline.cxx +++ b/src/Common/CubicSpline.cxx @@ -29,7 +29,7 @@ CubicSpline::CubicSpline() 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::hermite(float frac, bool lowEdge, bool highEdge) 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 --git a/src/Common/DataFileTypeEnum.cxx b/src/Common/DataFileTypeEnum.cxx index 94d297af423b8d2e8e450a4c7f8391b726b386a3..892ae145cd33cfab879eab0826586e6f68349bc9 100644 --- a/src/Common/DataFileTypeEnum.cxx +++ b/src/Common/DataFileTypeEnum.cxx @@ -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 @@ DataFileTypeEnum::DataFileTypeEnum(const Enum enumValue, 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); + 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; } - filterText += ")"; - this->qFileDialogNameFilter = filterText; + this->qReadFileDialogNameFilter = createQFileDialogNameFilter(this->guiName, + this->readFileExtensions); + this->qWriteFileDialogNameFilter = createQFileDialogNameFilter(this->guiName, + this->writeFileExtensions); } /** @@ -251,10 +275,7 @@ DataFileTypeEnum::initialize() "Image", "IMAGE", false, - "jpg", - "jpeg", - "png", - "ppm")); + "XXX")); /* Extensions for IMAGE set in constructor */ enumData.push_back(DataFileTypeEnum(LABEL, "LABEL", @@ -564,7 +585,8 @@ DataFileTypeEnum::fromQFileDialogFilter(const AString& qFileDialogNameFilter, 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 @@ DataFileTypeEnum::fromQFileDialogFilter(const AString& qFileDialogNameFilter, } /** - * 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 @@ DataFileTypeEnum::fromQFileDialogFilter(const AString& qFileDialogNameFilter, * 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->qFileDialogNameFilter; + 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->qWriteFileDialogNameFilter; } /** @@ -610,6 +648,60 @@ DataFileTypeEnum::isFileUsedWithOneStructure(const Enum enumValue) return enumInstance->oneStructureFlag; } +/** + * @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 @@ -620,7 +712,7 @@ DataFileTypeEnum::getAllFileExtensions(const Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); - return enumInstance->fileExtensions; + return enumInstance->readAndWriteFileExtensions; } /** @@ -709,8 +801,8 @@ DataFileTypeEnum::getFilesExtensionsForEveryFile(const bool includeNonWritableFi if (validFlag) { allExtensions.insert(allExtensions.end(), - enumIter->fileExtensions.begin(), - enumIter->fileExtensions.end()); + enumIter->readAndWriteFileExtensions.begin(), + enumIter->readAndWriteFileExtensions.end()); } } @@ -742,8 +834,8 @@ DataFileTypeEnum::addFileExtensionIfMissing(const AString& filenameIn, * 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 @@ DataFileTypeEnum::toFileExtension(const Enum enumValue) 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 @@ DataFileTypeEnum::isValidFileExtension(const AString& filename, * 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 @@ DataFileTypeEnum::fromFileExtension(const AString& filename, bool* isValidOut) 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 @@ DataFileTypeEnum::getAllEnums(std::vector& allEnums, 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 @@ DataFileTypeEnum::getAllConnectivityEnums(std::vector& connectivityEnumsOu } +/** + * 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 --git a/src/Common/DataFileTypeEnum.h b/src/Common/DataFileTypeEnum.h index 0143dd8dbe75862135ac90f8023aaf7044a4116a..5c45c878223be936cab8ef23f90e53e33c877f77 100644 --- a/src/Common/DataFileTypeEnum.h +++ b/src/Common/DataFileTypeEnum.h @@ -137,7 +137,9 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: /** 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 --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index e884cbc09b0944f26414089d0f6ee71e9349e53c..6cfaa5b853c84d4586e6579392d95f7a9920dfc7 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -135,11 +135,30 @@ DeveloperFlagsEnum::initialize() 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 --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index b8ac7412bdcafa8ec40c2663ec4c81346c5a6f4e..61338839dc6984a01b49950c21302c3f5e07762b 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -39,7 +39,10 @@ public: 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 --git a/src/Common/EventBrowserTabClose.cxx b/src/Common/EventBrowserTabClose.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3ef6af4576897c2bc1c118d3f649aff7905e6028 --- /dev/null +++ b/src/Common/EventBrowserTabClose.cxx @@ -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 --git a/src/Common/EventBrowserTabClose.h b/src/Common/EventBrowserTabClose.h new file mode 100644 index 0000000000000000000000000000000000000000..d5bd658c4253026413cb66d52d7fecb1488ac781 --- /dev/null +++ b/src/Common/EventBrowserTabClose.h @@ -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 --git a/src/Common/EventBrowserTabDelete.cxx b/src/Common/EventBrowserTabDelete.cxx index c341e5f4d26236ed96ef289e1086718e47c38300..89e95b2c482541a9f5dbf7127e18d0ab6b70fc77 100644 --- a/src/Common/EventBrowserTabDelete.cxx +++ b/src/Common/EventBrowserTabDelete.cxx @@ -25,12 +25,21 @@ using namespace caret; /** * 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 @@ EventBrowserTabDelete::getBrowserTabIndex() const return m_browserTabIndex; } +/** + * @return Index of browser window that is deleting tab + */ +int32_t +EventBrowserTabDelete::getWindowIndex() const +{ + return m_windowIndex; +} + diff --git a/src/Common/EventBrowserTabDelete.h b/src/Common/EventBrowserTabDelete.h index d2f717f28149dbdd5cae12336a1151cba1a67d82..15216fdd4df26bad8fba1a7618a8586909da718d 100644 --- a/src/Common/EventBrowserTabDelete.h +++ b/src/Common/EventBrowserTabDelete.h @@ -34,7 +34,8 @@ namespace caret { public: EventBrowserTabDelete(BrowserTabContent* browserTab, - const int32_t browserTabIndex); + const int32_t browserTabIndex, + const int32_t windowIndex); virtual ~EventBrowserTabDelete(); @@ -42,6 +43,8 @@ namespace caret { int32_t getBrowserTabIndex() const; + int32_t getWindowIndex() const; + private: EventBrowserTabDelete(const EventBrowserTabDelete&); @@ -50,6 +53,8 @@ namespace caret { BrowserTabContent* m_browserTab; const int32_t m_browserTabIndex; + + const int32_t m_windowIndex; }; } // namespace diff --git a/src/Common/EventBrowserTabIndicesGetAllViewed.cxx b/src/Common/EventBrowserTabIndicesGetAllViewed.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e9cd4bedccabc8f8f2f0c9b1b17a2b311b2f8de1 --- /dev/null +++ b/src/Common/EventBrowserTabIndicesGetAllViewed.cxx @@ -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 --git a/src/Common/EventBrowserTabIndicesGetAllViewed.h b/src/Common/EventBrowserTabIndicesGetAllViewed.h new file mode 100644 index 0000000000000000000000000000000000000000..429075b6503a5d6ab53fbab85fa5e2ed3520541a --- /dev/null +++ b/src/Common/EventBrowserTabIndicesGetAllViewed.h @@ -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 --git a/src/Common/EventBrowserTabReopenClosed.cxx b/src/Common/EventBrowserTabReopenClosed.cxx new file mode 100644 index 0000000000000000000000000000000000000000..19c456db63a7a3928029429cc70aff9662c17675 --- /dev/null +++ b/src/Common/EventBrowserTabReopenClosed.cxx @@ -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 --git a/src/Common/EventBrowserTabReopenClosed.h b/src/Common/EventBrowserTabReopenClosed.h new file mode 100644 index 0000000000000000000000000000000000000000..3aa55468bc4afb2c0b9ce2d99a7d1e418dd1aa83 --- /dev/null +++ b/src/Common/EventBrowserTabReopenClosed.h @@ -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 --git a/src/Common/EventBrowserTabSelectInWindow.cxx b/src/Common/EventBrowserTabSelectInWindow.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f1ccebcbcb1ee66c7b02a3efe8bfdba3427328ac --- /dev/null +++ b/src/Common/EventBrowserTabSelectInWindow.cxx @@ -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 --git a/src/Common/EventBrowserTabSelectInWindow.h b/src/Common/EventBrowserTabSelectInWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..0458ab0475c3af881ed253a203dec0338b1946e6 --- /dev/null +++ b/src/Common/EventBrowserTabSelectInWindow.h @@ -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 --git a/src/Common/EventGetViewportSize.h b/src/Common/EventGetViewportSize.h index de6b19f5125f32839f56b6c1fcfce038cf0e8d56..f7115d701322d3e110e1e6d9b9b519d2fdad236b 100644 --- a/src/Common/EventGetViewportSize.h +++ b/src/Common/EventGetViewportSize.h @@ -37,6 +37,8 @@ namespace caret { 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 --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index 17ce49d8c5340884af380b0e3ded43ddc2f538d6..22b60c861fdd80a5971c02823b2c901cb863ff4f 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -367,9 +367,13 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) { 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 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) 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 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) 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 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) 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 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) 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 --git a/src/Common/EventRecentFilesSystemAccessMode.cxx b/src/Common/EventRecentFilesSystemAccessMode.cxx new file mode 100644 index 0000000000000000000000000000000000000000..12c6ce103ce8730d0f84f21f36243f3a7bf5659f --- /dev/null +++ b/src/Common/EventRecentFilesSystemAccessMode.cxx @@ -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 --git a/src/Common/EventRecentFilesSystemAccessMode.h b/src/Common/EventRecentFilesSystemAccessMode.h new file mode 100644 index 0000000000000000000000000000000000000000..e3b745a359a8a8261d5f14ff70485f06097a573e --- /dev/null +++ b/src/Common/EventRecentFilesSystemAccessMode.h @@ -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 --git a/src/Common/EventTileTabsConfigurationModification.cxx b/src/Common/EventTileTabsGridConfigurationModification.cxx similarity index 67% rename from src/Common/EventTileTabsConfigurationModification.cxx rename to src/Common/EventTileTabsGridConfigurationModification.cxx index 4f4a12d682c0d7573a834b572f1802a689b30c38..74c3a4ff8df0c9aef7da9c886a9112b6102c5061 100644 --- a/src/Common/EventTileTabsConfigurationModification.cxx +++ b/src/Common/EventTileTabsGridConfigurationModification.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ -#include "EventTileTabsConfigurationModification.h" -#undef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ +#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" @@ -31,8 +31,8 @@ using namespace caret; /** - * \class caret::EventTileTabsConfigurationModification - * \brief Event for modifying a tile tabs configuration. + * \class caret::EventTileTabsGridConfigurationModification + * \brief Event for modifying a grid tile tabs configuration. * \ingroup Common */ @@ -48,7 +48,7 @@ using namespace caret; * @param operation * Enumerated type with type of modification. */ -EventTileTabsConfigurationModification::EventTileTabsConfigurationModification(TileTabsConfiguration* tileTabsConfiguration, +EventTileTabsGridConfigurationModification::EventTileTabsGridConfigurationModification(TileTabsLayoutGridConfiguration* tileTabsConfiguration, const int32_t rowColumnIndex, const RowColumnType rowColumnType, const Operation operation) @@ -59,21 +59,21 @@ m_rowColumnType(rowColumnType), m_operation(operation), m_windowIndex(-1) { - + CaretAssert(m_tileTabsConfiguration); } /** * Destructor. */ -EventTileTabsConfigurationModification::~EventTileTabsConfigurationModification() +EventTileTabsGridConfigurationModification::~EventTileTabsGridConfigurationModification() { } /** * @return Tile tabs configuration that is modified. */ -TileTabsConfiguration* -EventTileTabsConfigurationModification::getTileTabsConfiguration() +TileTabsLayoutGridConfiguration* +EventTileTabsGridConfigurationModification::getTileTabsConfiguration() { return m_tileTabsConfiguration; } @@ -83,7 +83,7 @@ EventTileTabsConfigurationModification::getTileTabsConfiguration() * @return Index of the window. */ int32_t -EventTileTabsConfigurationModification::getWindowIndex() const +EventTileTabsGridConfigurationModification::getWindowIndex() const { return m_windowIndex; } @@ -95,7 +95,7 @@ EventTileTabsConfigurationModification::getWindowIndex() const * Index of the window. */ void -EventTileTabsConfigurationModification::setWindowIndex(const int32_t windowIndex) +EventTileTabsGridConfigurationModification::setWindowIndex(const int32_t windowIndex) { m_windowIndex = windowIndex; } @@ -104,7 +104,7 @@ EventTileTabsConfigurationModification::setWindowIndex(const int32_t windowIndex * @return The index of the row or column. */ int32_t -EventTileTabsConfigurationModification::getRowColumnIndex() const +EventTileTabsGridConfigurationModification::getRowColumnIndex() const { return m_rowColumnIndex; } @@ -112,8 +112,8 @@ EventTileTabsConfigurationModification::getRowColumnIndex() const /** * @return The type ROW or COLUMN */ -EventTileTabsConfigurationModification::RowColumnType -EventTileTabsConfigurationModification::getRowColumnType() const +EventTileTabsGridConfigurationModification::RowColumnType +EventTileTabsGridConfigurationModification::getRowColumnType() const { return m_rowColumnType; } @@ -121,8 +121,8 @@ EventTileTabsConfigurationModification::getRowColumnType() const /** * @return The operation. */ -EventTileTabsConfigurationModification::Operation -EventTileTabsConfigurationModification::getOperation() const +EventTileTabsGridConfigurationModification::Operation +EventTileTabsGridConfigurationModification::getOperation() const { return m_operation; } diff --git a/src/Common/EventTileTabsConfigurationModification.h b/src/Common/EventTileTabsGridConfigurationModification.h similarity index 70% rename from src/Common/EventTileTabsConfigurationModification.h rename to src/Common/EventTileTabsGridConfigurationModification.h index 202fa67c998e3d954b36136e084450eab35146b6..3cf97dbf8799e584721b153a0d3b316b8b0d99f8 100644 --- a/src/Common/EventTileTabsConfigurationModification.h +++ b/src/Common/EventTileTabsGridConfigurationModification.h @@ -1,5 +1,5 @@ -#ifndef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ -#define __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ +#ifndef __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_H__ +#define __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_H__ /*LICENSE_START*/ /* @@ -31,9 +31,9 @@ namespace caret { - class TileTabsConfiguration; + class TileTabsLayoutGridConfiguration; - class EventTileTabsConfigurationModification : public Event { + class EventTileTabsGridConfigurationModification : public Event { public: /** @@ -57,18 +57,18 @@ namespace caret { MOVE_BEFORE }; - EventTileTabsConfigurationModification(TileTabsConfiguration* tileTabsConfiguration, + EventTileTabsGridConfigurationModification(TileTabsLayoutGridConfiguration* tileTabsConfiguration, const int32_t rowColumnIndex, const RowColumnType rowColumnType, const Operation operation); - virtual ~EventTileTabsConfigurationModification(); + virtual ~EventTileTabsGridConfigurationModification(); - EventTileTabsConfigurationModification(const EventTileTabsConfigurationModification&) = delete; + EventTileTabsGridConfigurationModification(const EventTileTabsGridConfigurationModification&) = delete; - EventTileTabsConfigurationModification& operator=(const EventTileTabsConfigurationModification&) = delete; + EventTileTabsGridConfigurationModification& operator=(const EventTileTabsGridConfigurationModification&) = delete; - TileTabsConfiguration* getTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* getTileTabsConfiguration(); int32_t getRowColumnIndex() const; @@ -83,7 +83,7 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - TileTabsConfiguration* m_tileTabsConfiguration; + TileTabsLayoutGridConfiguration* m_tileTabsConfiguration; const int32_t m_rowColumnIndex; @@ -97,9 +97,9 @@ namespace caret { }; -#ifdef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ +#ifdef __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_DECLARE__ // -#endif // __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ +#endif // __EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_DECLARE__ } // namespace -#endif //__EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ +#endif //__EVENT_TILE_TABS_GRID_CONFIGURATION_MODIFICATION_H__ diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index 8e3619a83fca36b59cd0258a418dbf8c079979e7..b0310a6515e43f3cf45d3027670e27c2deab13e2 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -78,9 +78,9 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 @@ EventTypeEnum::initialize() "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 --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index ade6bdd81f7dbcb237595dd7a7da236e2147a9be..3d0978b29d1db943266c1317ef6e88e366e269cc 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -44,8 +44,8 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 --git a/src/Common/FileInformation.cxx b/src/Common/FileInformation.cxx index b67393f2299d15074c14e283a1c405aa07386f8d..db6eb4e0140306b26331100bb9b87237388bfbc0 100644 --- a/src/Common/FileInformation.cxx +++ b/src/Common/FileInformation.cxx @@ -750,6 +750,15 @@ AString FileInformation::getLastDirectory() const return myList[myList.size() - 1]; } +/** + * @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 --git a/src/Common/FileInformation.h b/src/Common/FileInformation.h index 67e212c6b33775b8446e832c3e498aa727a4de56..28b12c278c18f2ea4615357dd945326036c3c818 100644 --- a/src/Common/FileInformation.h +++ b/src/Common/FileInformation.h @@ -88,6 +88,8 @@ namespace caret { AString getLastDirectory() const; + QDateTime getLastModified() const; + void getFileComponents(AString& absolutePathOut, AString& fileNameWithoutExtensionOut, AString& extensionWithoutDotOut) const; diff --git a/src/Common/FileOpenFromOpSysTypeEnum.cxx b/src/Common/FileOpenFromOpSysTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d27ec6931ce33713d18a6951f3f6e911514d3f2b --- /dev/null +++ b/src/Common/FileOpenFromOpSysTypeEnum.cxx @@ -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 --git a/src/Common/FileOpenFromOpSysTypeEnum.h b/src/Common/FileOpenFromOpSysTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..84d371ed5d68a41781374542634c16ef049ed8e7 --- /dev/null +++ b/src/Common/FileOpenFromOpSysTypeEnum.h @@ -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 --git a/src/Common/FloatMatrix.cxx b/src/Common/FloatMatrix.cxx index 9a7764433472a81b9bb3e42a507e9fade90f7d9b..3aa404d823dfcf35a8c02cb2f3751516cd21b409 100644 --- a/src/Common/FloatMatrix.cxx +++ b/src/Common/FloatMatrix.cxx @@ -291,6 +291,19 @@ void FloatMatrix::getAffineVectors(Vector3D& xvec, Vector3D& yvec, Vector3D& zve 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 --git a/src/Common/FloatMatrix.h b/src/Common/FloatMatrix.h index cafbbcb7e10ad02d26d4d7eac7b6a0474edb5430..48704d20d0861f36cf1202dcb5965980a1f45f83 100644 --- a/src/Common/FloatMatrix.h +++ b/src/Common/FloatMatrix.h @@ -101,6 +101,8 @@ namespace caret { 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 --git a/src/Common/HtmlTableBuilder.cxx b/src/Common/HtmlTableBuilder.cxx new file mode 100644 index 0000000000000000000000000000000000000000..fece0cbb3a87bcb8c68e684ebfbf5e6ee8254917 --- /dev/null +++ b/src/Common/HtmlTableBuilder.cxx @@ -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 --git a/src/Common/HtmlTableBuilder.h b/src/Common/HtmlTableBuilder.h new file mode 100644 index 0000000000000000000000000000000000000000..93b186e4856e475b3f6e39226e6e179b4e7b4f00 --- /dev/null +++ b/src/Common/HtmlTableBuilder.h @@ -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 --git a/src/Common/IdentificationDisplayModeEnum.cxx b/src/Common/IdentificationDisplayModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..51f774a9ac7409c0b5496958fe482b778ca1293c --- /dev/null +++ b/src/Common/IdentificationDisplayModeEnum.cxx @@ -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 --git a/src/Common/IdentificationDisplayModeEnum.h b/src/Common/IdentificationDisplayModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..cf2bf244859bfc9125491fc2e48e67a3939139e5 --- /dev/null +++ b/src/Common/IdentificationDisplayModeEnum.h @@ -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 --git a/src/Common/MathFunctionEnum.cxx b/src/Common/MathFunctionEnum.cxx index 5bd8d899b6da932cac2125265efaefd3747c518f..1ecd03c4c560256aa9a458ad45e5021ebc8c1396 100644 --- a/src/Common/MathFunctionEnum.cxx +++ b/src/Common/MathFunctionEnum.cxx @@ -76,10 +76,10 @@ MathFunctionEnum::initialize() 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 --git a/src/Common/MathFunctionEnum.h b/src/Common/MathFunctionEnum.h index fe744fd8597c337617bf13f42faf2d2812d996cb..e9ba2c32e9ca96ac443d191c0bec113d67ede8fa 100644 --- a/src/Common/MathFunctionEnum.h +++ b/src/Common/MathFunctionEnum.h @@ -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 --git a/src/Common/MathFunctions.cxx b/src/Common/MathFunctions.cxx index a2bdc652ad4dd2185632a1524784eb71d96b5711..2084da9ff06d14d704fdb08ea1d2b9b454b23e0b 100644 --- a/src/Common/MathFunctions.cxx +++ b/src/Common/MathFunctions.cxx @@ -2705,3 +2705,33 @@ MathFunctions::expandBoxPercentage3D(float bottomLeft[3], 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 --git a/src/Common/MathFunctions.h b/src/Common/MathFunctions.h index d2300b39f211460bb7dca76a1bda50ca8ce5ce91..13020a95c565f090bb6190180bd059937a1634c2 100644 --- a/src/Common/MathFunctions.h +++ b/src/Common/MathFunctions.h @@ -357,6 +357,11 @@ public: 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 --git a/src/Common/Matrix4x4Interface.h b/src/Common/Matrix4x4Interface.h new file mode 100644 index 0000000000000000000000000000000000000000..9774cdc652ad353634557fdf0fd2dd914903a732 --- /dev/null +++ b/src/Common/Matrix4x4Interface.h @@ -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 --git a/src/Common/ModelTransform.cxx b/src/Common/ModelTransform.cxx index b42557ee3366e901fbe4c74c153a0dc5231830bd..e58e7a8adba89862f90fe7991a3b54df4de90b67 100644 --- a/src/Common/ModelTransform.cxx +++ b/src/Common/ModelTransform.cxx @@ -70,12 +70,14 @@ ModelTransform::setToIdentity() 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; } } } @@ -211,6 +213,21 @@ ModelTransform::getObliqueRotation(float obliqueRotation[4][4]) const } } +/** + * 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. */ @@ -323,6 +340,22 @@ ModelTransform::setObliqueRotation(const float obliqueRotation[4][4]) } } +/** + * 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. * @@ -364,9 +397,10 @@ ModelTransform::setScaling(const float scaling) /** * 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 @@ ModelTransform::getAsString() const } } + 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 @@ ModelTransform::getAsString() const /** * 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 @@ ModelTransform::setFromString(const AString& s) 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 @@ ModelTransform::setFromString(const AString& s) 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 @@ ModelTransform::setFromString(const AString& s) } } + 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 @@ ModelTransform::copyHelper(const ModelTransform& modelTransform) 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 @@ ModelTransform::copyHelper(const ModelTransform& modelTransform) * 4x4 rotation matrix. * @param obliqueRotationMatrix * 4x4 oblique rotation matrix. + * @param floatRotationMatrixArray + * The flat rotation matrix * @param zoom * Zooming. * @param rightCortexFlatMapOffsetX @@ -565,6 +638,7 @@ ModelTransform::setPanningRotationMatrixAndZoom(const float panX, 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 @@ ModelTransform::setPanningRotationMatrixAndZoom(const float panX, setObliqueRotation(obliqueRotationMatrix); + this->setFlatRotation(floatRotationMatrixArray); + this->setScaling(zoom); this->setRightCortexFlatMapOffset(rightCortexFlatMapOffsetX, @@ -595,6 +671,8 @@ ModelTransform::setPanningRotationMatrixAndZoom(const float panX, * 4x4 rotation matrix. * @param obliqueRotationMatrix * 4x4 oblique rotation matrix. + * @param floatRotationMatrixArray + * The flat rotation matrix * @param zoom * Zooming. * @param rightCortexFlatMapOffsetX @@ -610,6 +688,7 @@ ModelTransform::getPanningRotationMatrixAndZoom(float& panX, float& panZ, float rotationMatrix[4][4], float obliqueRotationMatrix[4][4], + float floatRotationMatrixArray[4][4], float& zoom, float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY, @@ -623,6 +702,8 @@ ModelTransform::getPanningRotationMatrixAndZoom(float& panX, getObliqueRotation(obliqueRotationMatrix); + getFlatRotation(floatRotationMatrixArray); + zoom = getScaling(); getRightCortexFlatMapOffset(rightCortexFlatMapOffsetX, diff --git a/src/Common/ModelTransform.h b/src/Common/ModelTransform.h index 7fae0c771eee92f7cf7875ae92662fbded1a2bd2..84f5dbeeb6c39c37617bd7eacb5be25b4b6813cb 100644 --- a/src/Common/ModelTransform.h +++ b/src/Common/ModelTransform.h @@ -45,6 +45,8 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { float& panZ, float rotationMatrix[4][4], float obliqueRotationMatrix[4][4], + float flatRotationMatrix[4][4], float& zoom, float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY, @@ -121,6 +127,8 @@ namespace caret { float obliqueRotation[4][4]; + float flatRotation[4][4]; + float scaling; float rightCortexFlatMapOffsetXY[2]; diff --git a/src/Common/RecentFileItem.cxx b/src/Common/RecentFileItem.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ec4cf6f87748790424ba4ee26e3fee4a7b206561 --- /dev/null +++ b/src/Common/RecentFileItem.cxx @@ -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 --git a/src/Common/RecentFileItem.h b/src/Common/RecentFileItem.h new file mode 100644 index 0000000000000000000000000000000000000000..9ed0276b4ec8d0e539958efe231882ecfb38c28c --- /dev/null +++ b/src/Common/RecentFileItem.h @@ -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 --git a/src/Common/RecentFileItemSortingKeyEnum.cxx b/src/Common/RecentFileItemSortingKeyEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..71efd0e84b4805b1b033907ef5b4a1600e778bb1 --- /dev/null +++ b/src/Common/RecentFileItemSortingKeyEnum.cxx @@ -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 --git a/src/Common/RecentFileItemSortingKeyEnum.h b/src/Common/RecentFileItemSortingKeyEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..eb3e172bd18bc5f77d6080e44854b8a3c0054477 --- /dev/null +++ b/src/Common/RecentFileItemSortingKeyEnum.h @@ -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 --git a/src/Common/RecentFileItemTypeEnum.cxx b/src/Common/RecentFileItemTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f52330916df58264d9cc0506ad67a6a041398170 --- /dev/null +++ b/src/Common/RecentFileItemTypeEnum.cxx @@ -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 --git a/src/Common/RecentFileItemTypeEnum.h b/src/Common/RecentFileItemTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..deee77c27e8ec636313ec24fccaa9f54aa8c7716 --- /dev/null +++ b/src/Common/RecentFileItemTypeEnum.h @@ -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 --git a/src/Common/RecentFileItemsContainer.cxx b/src/Common/RecentFileItemsContainer.cxx new file mode 100644 index 0000000000000000000000000000000000000000..78c0a1e3065ee0e0fc5c4bd4ed75d55148a73d3e --- /dev/null +++ b/src/Common/RecentFileItemsContainer.cxx @@ -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 --git a/src/Common/RecentFileItemsContainer.h b/src/Common/RecentFileItemsContainer.h new file mode 100644 index 0000000000000000000000000000000000000000..eb369c41bdcc476b7166c4f55804fce1ee1d75bf --- /dev/null +++ b/src/Common/RecentFileItemsContainer.h @@ -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 --git a/src/Common/RecentFileItemsContainerModeEnum.cxx b/src/Common/RecentFileItemsContainerModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d6d6d8ae1c605b6f04bacca9d16860125ce56992 --- /dev/null +++ b/src/Common/RecentFileItemsContainerModeEnum.cxx @@ -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 --git a/src/Common/RecentFileItemsContainerModeEnum.h b/src/Common/RecentFileItemsContainerModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..24398dd62efcbb3cf3227646c0416c6f2c009a8e --- /dev/null +++ b/src/Common/RecentFileItemsContainerModeEnum.h @@ -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 --git a/src/Common/RecentFileItemsFilter.cxx b/src/Common/RecentFileItemsFilter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0876ceabae4f365e93418e6e6481c88251f5d88d --- /dev/null +++ b/src/Common/RecentFileItemsFilter.cxx @@ -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 --git a/src/Common/RecentFileItemsFilter.h b/src/Common/RecentFileItemsFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..310ae677b784531f4c3ec68d58465249eb5c2db1 --- /dev/null +++ b/src/Common/RecentFileItemsFilter.h @@ -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 --git a/src/Common/RecentFilesSystemAccessModeEnum.cxx b/src/Common/RecentFilesSystemAccessModeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0e383e84c1cc7fd61ea4084551c22d2a5787e23b --- /dev/null +++ b/src/Common/RecentFilesSystemAccessModeEnum.cxx @@ -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 --git a/src/Common/RecentFilesSystemAccessModeEnum.h b/src/Common/RecentFilesSystemAccessModeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..ab7ffc161a1b726add5b33046dba951f56fd0075 --- /dev/null +++ b/src/Common/RecentFilesSystemAccessModeEnum.h @@ -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 --git a/src/Common/StructureEnum.cxx b/src/Common/StructureEnum.cxx index de9800e8e1a7a530b1e39dfff2a4ef476392652f..175dd408a9b7980bf1fa9163122f170b26783bed 100644 --- a/src/Common/StructureEnum.cxx +++ b/src/Common/StructureEnum.cxx @@ -287,7 +287,7 @@ StructureEnum::fromName(const AString& name, bool* isValidOut) *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 @@ StructureEnum::fromGuiName(const AString& guiName, bool* isValidOut) *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 @@ StructureEnum::fromCiftiName(const AString& ciftiName, bool* isValidOut) *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 @@ StructureEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) *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 --git a/src/Common/TileTabsBaseConfiguration.cxx b/src/Common/TileTabsBaseConfiguration.cxx deleted file mode 100644 index e22e23a4bd8c34a5b8bd9e79d2f2c166f72091f3..0000000000000000000000000000000000000000 --- a/src/Common/TileTabsBaseConfiguration.cxx +++ /dev/null @@ -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 --git a/src/Common/TileTabsBaseConfiguration.h b/src/Common/TileTabsBaseConfiguration.h deleted file mode 100644 index 8e5ed2571775634f405e29d1230526ab301e7c91..0000000000000000000000000000000000000000 --- a/src/Common/TileTabsBaseConfiguration.h +++ /dev/null @@ -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 --git a/src/Common/TileTabsBrowserTabGeometry.cxx b/src/Common/TileTabsBrowserTabGeometry.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e91d4a8b77ecd59b7eeeabd85130a6e3832a6738 --- /dev/null +++ b/src/Common/TileTabsBrowserTabGeometry.cxx @@ -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 --git a/src/Common/TileTabsBrowserTabGeometry.h b/src/Common/TileTabsBrowserTabGeometry.h new file mode 100644 index 0000000000000000000000000000000000000000..0a394adedd4d1c845610e82bd2603aba0dd64453 --- /dev/null +++ b/src/Common/TileTabsBrowserTabGeometry.h @@ -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 --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx deleted file mode 100644 index 01db9f5cb3313d9e5aaea64e575b00aa93930832..0000000000000000000000000000000000000000 --- a/src/Common/TileTabsConfiguration.cxx +++ /dev/null @@ -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 --git a/src/Common/TileTabsConfiguration.h b/src/Common/TileTabsConfiguration.h deleted file mode 100644 index 87661333ff9234d0b12ed542b0277cc1a00148fb..0000000000000000000000000000000000000000 --- a/src/Common/TileTabsConfiguration.h +++ /dev/null @@ -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 --git a/src/Common/TileTabsLayoutBackgroundTypeEnum.cxx b/src/Common/TileTabsLayoutBackgroundTypeEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a6036bf3feda612a4f6a75ef98cfcf2135c9752d --- /dev/null +++ b/src/Common/TileTabsLayoutBackgroundTypeEnum.cxx @@ -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 --git a/src/Common/TileTabsLayoutBackgroundTypeEnum.h b/src/Common/TileTabsLayoutBackgroundTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..7cd99ac64f4f7a9a7a9ea2eae358a5a32d574192 --- /dev/null +++ b/src/Common/TileTabsLayoutBackgroundTypeEnum.h @@ -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 --git a/src/Common/TileTabsLayoutBaseConfiguration.cxx b/src/Common/TileTabsLayoutBaseConfiguration.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a69e98ff36c0deeed8a0bed43ba53a1102015de1 --- /dev/null +++ b/src/Common/TileTabsLayoutBaseConfiguration.cxx @@ -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 --git a/src/Common/TileTabsLayoutBaseConfiguration.h b/src/Common/TileTabsLayoutBaseConfiguration.h new file mode 100644 index 0000000000000000000000000000000000000000..475e42afa276773a1b7fc20e247d81382e5eecd4 --- /dev/null +++ b/src/Common/TileTabsLayoutBaseConfiguration.h @@ -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 --git a/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx b/src/Common/TileTabsLayoutConfigurationTypeEnum.cxx similarity index 62% rename from src/Common/TileTabsConfigurationLayoutTypeEnum.cxx rename to src/Common/TileTabsLayoutConfigurationTypeEnum.cxx index fe45e41cb3c6517a8fb38f0e1389a191332ad7a5..017a23d913fe9b3f2e11b3fffec7ea162629532f 100644 --- a/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx +++ b/src/Common/TileTabsLayoutConfigurationTypeEnum.cxx @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ -#include "TileTabsConfigurationLayoutTypeEnum.h" -#undef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ +#define __TILE_TABS_LAYOUT_CONFIGURATION_TYPE_ENUM_DECLARE__ +#include "TileTabsLayoutConfigurationTypeEnum.h" +#undef __TILE_TABS_LAYOUT_CONFIGURATION_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,7 +30,7 @@ using namespace caret; /** - * \class caret::TileTabsConfigurationLayoutTypeEnum + * \class caret::TileTabsLayoutConfigurationTypeEnum * \brief Types of tile tabs layouts * * Using this enumerated type in the GUI with an EnumComboBoxTemplate @@ -40,30 +40,30 @@ using namespace caret; * class EnumComboBoxTemplate; * * Declare the member: - * EnumComboBoxTemplate* m_tileTabsConfigurationLayoutTypeEnumComboBox; + * EnumComboBoxTemplate* m_TileTabsLayoutConfigurationTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: - * void tileTabsConfigurationLayoutTypeEnumComboBoxItemActivated(); + * void TileTabsLayoutConfigurationTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "TileTabsConfigurationLayoutTypeEnum.h" + * #include "TileTabsLayoutConfigurationTypeEnum.h" * * Instatiate: - * m_tileTabsConfigurationLayoutTypeEnumComboBox = new EnumComboBoxTemplate(this); - * m_tileTabsConfigurationLayoutTypeEnumComboBox->setup(); + * m_TileTabsLayoutConfigurationTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_TileTabsLayoutConfigurationTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: - * QObject::connect(m_tileTabsConfigurationLayoutTypeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(tileTabsConfigurationLayoutTypeEnumComboBoxItemActivated())); + * QObject::connect(m_TileTabsLayoutConfigurationTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(TileTabsLayoutConfigurationTypeEnumComboBoxItemActivated())); * * Update the selection: - * m_tileTabsConfigurationLayoutTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_TileTabsLayoutConfigurationTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const TileTabsConfigurationLayoutTypeEnum::Enum VARIABLE = m_tileTabsConfigurationLayoutTypeEnumComboBox->getSelectedItem(); + * const TileTabsLayoutConfigurationTypeEnum::Enum VARIABLE = m_TileTabsLayoutConfigurationTypeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +78,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -TileTabsConfigurationLayoutTypeEnum::TileTabsConfigurationLayoutTypeEnum(const Enum enumValue, +TileTabsLayoutConfigurationTypeEnum::TileTabsLayoutConfigurationTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +91,7 @@ TileTabsConfigurationLayoutTypeEnum::TileTabsConfigurationLayoutTypeEnum(const E /** * Destructor. */ -TileTabsConfigurationLayoutTypeEnum::~TileTabsConfigurationLayoutTypeEnum() +TileTabsLayoutConfigurationTypeEnum::~TileTabsLayoutConfigurationTypeEnum() { } @@ -99,21 +99,25 @@ TileTabsConfigurationLayoutTypeEnum::~TileTabsConfigurationLayoutTypeEnum() * Initialize the enumerated metadata. */ void -TileTabsConfigurationLayoutTypeEnum::initialize() +TileTabsLayoutConfigurationTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(TileTabsConfigurationLayoutTypeEnum(GRID, - "GRID", - "Grid")); + enumData.push_back(TileTabsLayoutConfigurationTypeEnum(AUTOMATIC_GRID, + "AUTOMATIC_GRID", + "Automatic Grid")); - enumData.push_back(TileTabsConfigurationLayoutTypeEnum(MANUAL, - "MANUAL", - "Manual")); + enumData.push_back(TileTabsLayoutConfigurationTypeEnum(CUSTOM_GRID, + "CUSTOM_GRID", + "Custom Grid")); + enumData.push_back(TileTabsLayoutConfigurationTypeEnum(MANUAL, + "MANUAL", + "Manual")); + } /** @@ -123,14 +127,14 @@ TileTabsConfigurationLayoutTypeEnum::initialize() * @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) +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 TileTabsConfigurationLayoutTypeEnum* d = &enumData[i]; + const TileTabsLayoutConfigurationTypeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -147,10 +151,10 @@ TileTabsConfigurationLayoutTypeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -TileTabsConfigurationLayoutTypeEnum::toName(Enum enumValue) { +TileTabsLayoutConfigurationTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); + const TileTabsLayoutConfigurationTypeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -164,18 +168,29 @@ TileTabsConfigurationLayoutTypeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsConfigurationLayoutTypeEnum::Enum -TileTabsConfigurationLayoutTypeEnum::fromName(const AString& name, bool* isValidOut) +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 = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsLayoutConfigurationTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsConfigurationLayoutTypeEnum& d = *iter; + const TileTabsLayoutConfigurationTypeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -187,7 +202,7 @@ TileTabsConfigurationLayoutTypeEnum::fromName(const AString& name, bool* isValid *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsLayoutConfigurationTypeEnum")); } return enumValue; } @@ -200,10 +215,11 @@ TileTabsConfigurationLayoutTypeEnum::fromName(const AString& name, bool* isValid * String representing enumerated value. */ AString -TileTabsConfigurationLayoutTypeEnum::toGuiName(Enum enumValue) { +TileTabsLayoutConfigurationTypeEnum::toGuiName(Enum enumValue) +{ if (initializedFlag == false) initialize(); - const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); + const TileTabsLayoutConfigurationTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -217,18 +233,18 @@ TileTabsConfigurationLayoutTypeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsConfigurationLayoutTypeEnum::Enum -TileTabsConfigurationLayoutTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +TileTabsLayoutConfigurationTypeEnum::Enum +TileTabsLayoutConfigurationTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsLayoutConfigurationTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsConfigurationLayoutTypeEnum& d = *iter; + const TileTabsLayoutConfigurationTypeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -240,7 +256,7 @@ TileTabsConfigurationLayoutTypeEnum::fromGuiName(const AString& guiName, bool* i *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsLayoutConfigurationTypeEnum")); } return enumValue; } @@ -252,10 +268,10 @@ TileTabsConfigurationLayoutTypeEnum::fromGuiName(const AString& guiName, bool* i * Integer code for data type. */ int32_t -TileTabsConfigurationLayoutTypeEnum::toIntegerCode(Enum enumValue) +TileTabsLayoutConfigurationTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); + const TileTabsLayoutConfigurationTypeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -270,18 +286,18 @@ TileTabsConfigurationLayoutTypeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -TileTabsConfigurationLayoutTypeEnum::Enum -TileTabsConfigurationLayoutTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +TileTabsLayoutConfigurationTypeEnum::Enum +TileTabsLayoutConfigurationTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsLayoutConfigurationTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsConfigurationLayoutTypeEnum& enumInstance = *iter; + const TileTabsLayoutConfigurationTypeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -293,7 +309,7 @@ TileTabsConfigurationLayoutTypeEnum::fromIntegerCode(const int32_t integerCode, *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsLayoutConfigurationTypeEnum")); } return enumValue; } @@ -306,13 +322,13 @@ TileTabsConfigurationLayoutTypeEnum::fromIntegerCode(const int32_t integerCode, * A vector that is OUTPUT containing all of the enumerated values. */ void -TileTabsConfigurationLayoutTypeEnum::getAllEnums(std::vector& allEnums) +TileTabsLayoutConfigurationTypeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -328,16 +344,16 @@ TileTabsConfigurationLayoutTypeEnum::getAllEnums(std::vector& allNames, const bool isSorted) +TileTabsLayoutConfigurationTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(TileTabsConfigurationLayoutTypeEnum::toName(iter->enumValue)); + allNames.push_back(TileTabsLayoutConfigurationTypeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -354,16 +370,16 @@ TileTabsConfigurationLayoutTypeEnum::getAllNames(std::vector& allNames, * If true, the names are sorted in alphabetical order. */ void -TileTabsConfigurationLayoutTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +TileTabsLayoutConfigurationTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(TileTabsConfigurationLayoutTypeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(TileTabsLayoutConfigurationTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { diff --git a/src/Common/TileTabsLayoutConfigurationTypeEnum.h b/src/Common/TileTabsLayoutConfigurationTypeEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..318ea3d511be699cbf0be5828be6af44989052db --- /dev/null +++ b/src/Common/TileTabsLayoutConfigurationTypeEnum.h @@ -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 --git a/src/Common/TileTabsGridLayoutConfiguration.cxx b/src/Common/TileTabsLayoutGridConfiguration.cxx similarity index 74% rename from src/Common/TileTabsGridLayoutConfiguration.cxx rename to src/Common/TileTabsLayoutGridConfiguration.cxx index dbe571bf669530c64eb2da88a10aa080ecb4bc8f..fcd72788fff55bab132493211dd7abbf6d8fb38b 100644 --- a/src/Common/TileTabsGridLayoutConfiguration.cxx +++ b/src/Common/TileTabsLayoutGridConfiguration.cxx @@ -22,9 +22,9 @@ #include #include -#define __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ -#include "TileTabsGridLayoutConfiguration.h" -#undef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ +#define __TILE_TABS_LAYOUT_GRID_CONFIGURATION_DECLARE__ +#include "TileTabsLayoutGridConfiguration.h" +#undef __TILE_TABS_LAYOUT_GRID_CONFIGURATION_DECLARE__ #include #include @@ -38,24 +38,54 @@ using namespace caret; /** - * \class caret::TileTabsGridLayoutConfiguration + * \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. */ -TileTabsGridLayoutConfiguration::TileTabsGridLayoutConfiguration() -: TileTabsBaseConfiguration(TileTabsConfigurationLayoutTypeEnum::GRID) +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. */ -TileTabsGridLayoutConfiguration::~TileTabsGridLayoutConfiguration() +TileTabsLayoutGridConfiguration::~TileTabsLayoutGridConfiguration() { } @@ -67,11 +97,11 @@ TileTabsGridLayoutConfiguration::~TileTabsGridLayoutConfiguration() * @param obj * Object that is copied. */ -TileTabsGridLayoutConfiguration::TileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj) -: TileTabsBaseConfiguration(obj) +TileTabsLayoutGridConfiguration::TileTabsLayoutGridConfiguration(const TileTabsLayoutGridConfiguration& obj) +: TileTabsLayoutBaseConfiguration(obj) { initialize(); - this->copyHelperTileTabsGridLayoutConfiguration(obj); + this->copyHelperTileTabsLayoutGridConfiguration(obj); } /** @@ -84,12 +114,12 @@ TileTabsGridLayoutConfiguration::TileTabsGridLayoutConfiguration(const TileTabsG * @return * Reference to this object. */ -TileTabsGridLayoutConfiguration& -TileTabsGridLayoutConfiguration::operator=(const TileTabsGridLayoutConfiguration& obj) +TileTabsLayoutGridConfiguration& +TileTabsLayoutGridConfiguration::operator=(const TileTabsLayoutGridConfiguration& obj) { if (this != &obj) { - TileTabsBaseConfiguration::operator=(obj); - this->copyHelperTileTabsGridLayoutConfiguration(obj); + TileTabsLayoutBaseConfiguration::operator=(obj); + this->copyHelperTileTabsLayoutGridConfiguration(obj); } return *this; } @@ -100,10 +130,10 @@ TileTabsGridLayoutConfiguration::operator=(const TileTabsGridLayoutConfiguration * * @return The new Copy. */ -TileTabsGridLayoutConfiguration* -TileTabsGridLayoutConfiguration::newCopyWithNewUniqueIdentifier() const +TileTabsLayoutBaseConfiguration* +TileTabsLayoutGridConfiguration::newCopyWithNewUniqueIdentifier() const { - TileTabsGridLayoutConfiguration* newCopy = new TileTabsGridLayoutConfiguration(*this); + TileTabsLayoutGridConfiguration* newCopy = new TileTabsLayoutGridConfiguration(*this); CaretAssert(newCopy); return newCopy; } @@ -113,12 +143,13 @@ TileTabsGridLayoutConfiguration::newCopyWithNewUniqueIdentifier() const * Initialize an instance of a tile tabs configuration. */ void -TileTabsGridLayoutConfiguration::initialize() +TileTabsLayoutGridConfiguration::initialize() { setNumberOfRows(2); setNumberOfColumns(2); m_centeringCorrectionEnabled = false; + m_customDefaultFlag = false; } /** @@ -127,28 +158,78 @@ TileTabsGridLayoutConfiguration::initialize() * Object that is copied. */ void -TileTabsGridLayoutConfiguration::copyHelperTileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj) +TileTabsLayoutGridConfiguration::copyHelperTileTabsLayoutGridConfiguration(const TileTabsLayoutGridConfiguration& obj) { - if (this == &obj) { - return; - } +// if (this == &obj) { +// return; +// } - copyHelperTileTabsBaseConfiguration(obj); + copyHelperTileTabsLayoutBaseConfiguration(obj); m_columns = obj.m_columns; m_rows = obj.m_rows; m_centeringCorrectionEnabled = obj.m_centeringCorrectionEnabled; + m_customDefaultFlag = false; } /** - * Copies the tile tabs configuration rows, columns, and - * stretch factors. Name is NOT copied. + * 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 -TileTabsGridLayoutConfiguration::copy(const TileTabsGridLayoutConfiguration& rhs) +TileTabsLayoutGridConfiguration::copy(const TileTabsLayoutBaseConfiguration& rhs) { - AString savedName = getName(); - copyHelperTileTabsGridLayoutConfiguration(rhs); - setName(savedName); + 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; } /** @@ -157,7 +238,7 @@ TileTabsGridLayoutConfiguration::copy(const TileTabsGridLayoutConfiguration& rhs * @param Information for element. */ TileTabsGridRowColumnElement* -TileTabsGridLayoutConfiguration::getColumn(const int32_t columnIndex) +TileTabsLayoutGridConfiguration::getColumn(const int32_t columnIndex) { CaretAssertVectorIndex(m_columns, columnIndex); return &m_columns[columnIndex]; @@ -169,7 +250,7 @@ TileTabsGridLayoutConfiguration::getColumn(const int32_t columnIndex) * @param Information for element. */ const TileTabsGridRowColumnElement* -TileTabsGridLayoutConfiguration::getColumn(const int32_t columnIndex) const +TileTabsLayoutGridConfiguration::getColumn(const int32_t columnIndex) const { CaretAssertVectorIndex(m_columns, columnIndex); return &m_columns[columnIndex]; @@ -181,7 +262,7 @@ TileTabsGridLayoutConfiguration::getColumn(const int32_t columnIndex) const * @param Information for element. */ TileTabsGridRowColumnElement* -TileTabsGridLayoutConfiguration::getRow(const int32_t rowIndex) +TileTabsLayoutGridConfiguration::getRow(const int32_t rowIndex) { CaretAssertVectorIndex(m_rows, rowIndex); return &m_rows[rowIndex]; @@ -193,7 +274,7 @@ TileTabsGridLayoutConfiguration::getRow(const int32_t rowIndex) * @param Information for element. */ const TileTabsGridRowColumnElement* -TileTabsGridLayoutConfiguration::getRow(const int32_t rowIndex) const +TileTabsLayoutGridConfiguration::getRow(const int32_t rowIndex) const { CaretAssertVectorIndex(m_rows, rowIndex); return &m_rows[rowIndex]; @@ -219,10 +300,10 @@ TileTabsGridLayoutConfiguration::getRow(const int32_t rowIndex) const * True if the ouput is valid, else false. */ bool -TileTabsGridLayoutConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, +TileTabsLayoutGridConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, const int32_t windowHeight, const int32_t numberOfModelsToDraw, - const TileTabsGridModeEnum::Enum configurationMode, + const TileTabsLayoutConfigurationTypeEnum::Enum configurationMode, std::vector& rowHeightsOut, std::vector& columnWidthsOut) { @@ -241,7 +322,7 @@ TileTabsGridLayoutConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const columnWidthsOut.clear(); switch (configurationMode) { - case TileTabsGridModeEnum::AUTOMATIC: + case TileTabsLayoutConfigurationTypeEnum::AUTOMATIC_GRID: { /* * Update number of rows/columns in the default configuration @@ -258,8 +339,9 @@ TileTabsGridLayoutConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const for (int32_t i = 0; i < numCols; i++) { columnWidthsOut.push_back(windowWidth / numCols); } - } break; - case TileTabsGridModeEnum::CUSTOM: + } + break; + case TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: { /* * Rows/columns from user configuration @@ -355,38 +437,42 @@ TileTabsGridLayoutConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const columnWidthsOut.push_back(w); } } break; + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + CaretAssert(0); + 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, ", ")); + 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; } @@ -404,10 +490,10 @@ TileTabsGridLayoutConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const } /** - * @return Number of rows. + * @return Number of rows (all rows including tabs and spacers) */ int32_t -TileTabsGridLayoutConfiguration::getNumberOfRows() const +TileTabsLayoutGridConfiguration::getNumberOfRows() const { return m_rows.size(); } @@ -419,16 +505,16 @@ TileTabsGridLayoutConfiguration::getNumberOfRows() const * New number of rows. */ void -TileTabsGridLayoutConfiguration::setNumberOfRows(const int32_t numberOfRows) +TileTabsLayoutGridConfiguration::setNumberOfRows(const int32_t numberOfRows) { m_rows.resize(numberOfRows); } /** - * @return Number of columns. + * @return Number of columns (All columns including tabs and spacers) */ int32_t -TileTabsGridLayoutConfiguration::getNumberOfColumns() const +TileTabsLayoutGridConfiguration::getNumberOfColumns() const { return m_columns.size(); } @@ -440,11 +526,47 @@ TileTabsGridLayoutConfiguration::getNumberOfColumns() const * New number of rows. */ void -TileTabsGridLayoutConfiguration::setNumberOfColumns(const int32_t numberOfColumns) +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. @@ -456,7 +578,7 @@ TileTabsGridLayoutConfiguration::setNumberOfColumns(const int32_t numberOfColumn * Output with number of columns. */ void -TileTabsGridLayoutConfiguration::getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, +TileTabsLayoutGridConfiguration::getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) { @@ -477,7 +599,7 @@ TileTabsGridLayoutConfiguration::getRowsAndColumnsForNumberOfTabs(const int32_t * @return True if the centering correction is enabled. */ bool -TileTabsGridLayoutConfiguration::isCenteringCorrectionEnabled() const +TileTabsLayoutGridConfiguration::isCenteringCorrectionEnabled() const { return m_centeringCorrectionEnabled; } @@ -489,7 +611,7 @@ TileTabsGridLayoutConfiguration::isCenteringCorrectionEnabled() const * New status for enabling the centering correction */ void -TileTabsGridLayoutConfiguration::setCenteringCorrectionEnabled(const bool status) +TileTabsLayoutGridConfiguration::setCenteringCorrectionEnabled(const bool status) { m_centeringCorrectionEnabled = status; } @@ -503,13 +625,12 @@ TileTabsGridLayoutConfiguration::setCenteringCorrectionEnabled(const bool status * columns is always greater than the number of rows. */ void -TileTabsGridLayoutConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs) +TileTabsLayoutGridConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs) { int32_t numRows(0), numCols(0); getRowsAndColumnsForNumberOfTabs(numberOfTabs, numRows, numCols); - setNumberOfRows(numRows); setNumberOfColumns(numCols); } @@ -521,17 +642,17 @@ TileTabsGridLayoutConfiguration::updateAutomaticConfigurationRowsAndColumns(cons * Contains XML representation of configuration. */ void -TileTabsGridLayoutConfiguration::encodeInXML(AString& xmlTextOut) const +TileTabsLayoutGridConfiguration::encodeInXMLString(AString& xmlTextOut) const { xmlTextOut = encodeVersionInXML(2); } /** * @return Encoded tile tabs configuration in XML - * using the give XML version of TileTabsGridLayoutConfiguration. + * using the give XML version of TileTabsLayoutGridConfiguration. */ AString -TileTabsGridLayoutConfiguration::encodeVersionInXML(const int32_t versionNumber) const +TileTabsLayoutGridConfiguration::encodeVersionInXML(const int32_t versionNumber) const { AString s; @@ -555,7 +676,7 @@ TileTabsGridLayoutConfiguration::encodeVersionInXML(const int32_t versionNumber) * @return Encoded tile tabs configuration in XML with Stream Writer */ AString -TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionOne() const +TileTabsLayoutGridConfiguration::encodeInXMLWithStreamWriterVersionOne() const { AString xmlString; QXmlStreamWriter writer(&xmlString); @@ -575,7 +696,7 @@ TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionOne() const 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) { + for (const auto& e : m_rows) { rowStretchFactors.push_back(e.getWeightStretch()); } writer.writeCharacters(AString::fromNumbers(rowStretchFactors, " ")); @@ -586,7 +707,7 @@ TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionOne() const 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) { + for (const auto& e : m_columns) { columnStretchFactors.push_back(e.getWeightStretch()); } writer.writeCharacters(AString::fromNumbers(columnStretchFactors, " ")); @@ -601,7 +722,7 @@ TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionOne() const * @return Encoded tile tabs configuration in XML with Stream Writer */ AString -TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionTwo() const +TileTabsLayoutGridConfiguration::encodeInXMLWithStreamWriterVersionTwo() const { AString xmlString; QXmlStreamWriter writer(&xmlString); @@ -633,13 +754,13 @@ TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionTwo() const * Vector of elements added to XML. */ void -TileTabsGridLayoutConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer, +TileTabsLayoutGridConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer, const AString tagName, const std::vector& elements) const { writer.writeStartElement(tagName); - for (const auto e : elements) { + 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())); @@ -662,8 +783,8 @@ TileTabsGridLayoutConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer * The root element. */ void -TileTabsGridLayoutConfiguration::decodeFromXML(QXmlStreamReader& xml, - const QString& rootElementText) +TileTabsLayoutGridConfiguration::decodeFromXMLString(QXmlStreamReader& xml, + const AString& rootElementText) { CaretAssert( ! rootElementText.isEmpty()); @@ -689,38 +810,18 @@ TileTabsGridLayoutConfiguration::decodeFromXML(QXmlStreamReader& xml, decodeFromXMLWithStreamReaderVersionTwo(xml); } else { - xml.raiseError("TileTabsGridLayoutConfiguration invalid version=" + xml.raiseError("TileTabsLayoutGridConfiguration invalid version=" + versionNumberText); } } else { - xml.raiseError("TileTabsGridLayoutConfiguration first element is " + xml.raiseError("TileTabsLayoutGridConfiguration 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; + } } /** @@ -730,7 +831,7 @@ TileTabsGridLayoutConfiguration::decodeFromXML(QXmlStreamReader& xml, * Stream XML parser. */ void -TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml) +TileTabsLayoutGridConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml) { std::set invalidElements; @@ -815,7 +916,7 @@ TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStr * If invalid elements were encountered, don't throw */ AString msg("Invalid element(s) ignored: "); - for (const auto s : invalidElements) { + for (const auto& s : invalidElements) { msg.append(s + " "); } CaretLogWarning(msg); @@ -860,7 +961,7 @@ TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStr * Stream XML parser. */ void -TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml) +TileTabsLayoutGridConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml) { m_rows.clear(); m_columns.clear(); @@ -967,7 +1068,7 @@ TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStr * If invalid elements were encountered, don't throw */ AString msg("Invalid element(s) ignored: "); - for (const auto s : invalidElements) { + for (const auto& s : invalidElements) { msg.append(s + " "); } CaretLogWarning(msg); @@ -1004,7 +1105,7 @@ TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStr * True if read successfully, else false. */ bool -TileTabsGridLayoutConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, +TileTabsLayoutGridConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, TileTabsGridRowColumnElement& element, AString& errorMessageOut) { @@ -1072,17 +1173,22 @@ TileTabsGridLayoutConfiguration::decodeRowColumnElement(QXmlStreamReader& reader * @return String version of an instance. */ AString -TileTabsGridLayoutConfiguration::toString() const +TileTabsLayoutGridConfiguration::toString() const { - AString s = TileTabsBaseConfiguration::toString(); + 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) { + for (const auto& item : m_columns) { s.append(" Column " + AString::number(indx) + ": " + item.toString() + "\n"); indx++; } indx = 0; - for (const auto item : m_rows) { + for (const auto& item : m_rows) { s.append(" Row " + AString::number(indx) + ": " + item.toString() + "\n"); indx++; } @@ -1090,3 +1196,45 @@ TileTabsGridLayoutConfiguration::toString() const 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 --git a/src/Common/TileTabsGridLayoutConfiguration.h b/src/Common/TileTabsLayoutGridConfiguration.h similarity index 62% rename from src/Common/TileTabsGridLayoutConfiguration.h rename to src/Common/TileTabsLayoutGridConfiguration.h index 35eeb543b8478aefbf303fa6061a7156529dfe80..6af8da36d48c7e5144834bf885d1db874b9359eb 100644 --- a/src/Common/TileTabsGridLayoutConfiguration.h +++ b/src/Common/TileTabsLayoutGridConfiguration.h @@ -1,5 +1,5 @@ -#ifndef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_H__ -#define __TILE_TABS_GRID_LAYOUT_CONFIGURATION_H__ +#ifndef __TILE_TABS_LAYOUT_GRID_CONFIGURATION_H__ +#define __TILE_TABS_LAYOUT_GRID_CONFIGURATION_H__ /*LICENSE_START*/ /* @@ -22,8 +22,8 @@ /*LICENSE_END*/ #include "CaretException.h" -#include "TileTabsBaseConfiguration.h" -#include "TileTabsGridModeEnum.h" +#include "TileTabsLayoutBaseConfiguration.h" +#include "TileTabsLayoutConfigurationTypeEnum.h" #include "TileTabsGridRowColumnElement.h" class QXmlStreamReader; @@ -31,25 +31,34 @@ class QXmlStreamWriter; namespace caret { - class TileTabsGridLayoutConfiguration : public TileTabsBaseConfiguration { + class TileTabsLayoutGridConfiguration : public TileTabsLayoutBaseConfiguration { public: - TileTabsGridLayoutConfiguration(); + static TileTabsLayoutGridConfiguration* newInstanceAutomaticGrid(); - virtual ~TileTabsGridLayoutConfiguration(); + static TileTabsLayoutGridConfiguration* newInstanceCustomGrid(); - TileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj); + TileTabsLayoutGridConfiguration(const TileTabsLayoutConfigurationTypeEnum::Enum gridConfigType); + + virtual ~TileTabsLayoutGridConfiguration(); + + TileTabsLayoutGridConfiguration(const TileTabsLayoutGridConfiguration& obj); - TileTabsGridLayoutConfiguration& operator=(const TileTabsGridLayoutConfiguration& obj); + TileTabsLayoutGridConfiguration& operator=(const TileTabsLayoutGridConfiguration& obj); - void copy(const TileTabsGridLayoutConfiguration& rhs); + virtual void copy(const TileTabsLayoutBaseConfiguration& rhs) override; - virtual TileTabsGridLayoutConfiguration* newCopyWithNewUniqueIdentifier() const 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 TileTabsGridModeEnum::Enum configurationMode, + const TileTabsLayoutConfigurationTypeEnum::Enum configurationMode, std::vector& rowHeightsOut, std::vector& columnWidthsOut); @@ -80,24 +89,31 @@ namespace caret { 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: - public: - virtual void decodeFromXML(QXmlStreamReader& xml, - const QString& rootElement) override; + virtual void decodeFromXMLString(QXmlStreamReader& xml, + const AString& rootElement) override; - virtual void encodeInXML(AString& xmlTextOut) const override; + virtual void encodeInXMLString(AString& xmlTextOut) const override; private: - void copyHelperTileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj); + void copyHelperTileTabsLayoutGridConfiguration(const TileTabsLayoutGridConfiguration& obj); void decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml); void decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml); - AString encodeVersionInXML(const int32_t versionNumber) const; - AString encodeInXMLWithStreamWriterVersionOne() const; AString encodeInXMLWithStreamWriterVersionTwo() const; @@ -118,6 +134,9 @@ namespace caret { 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; @@ -145,34 +164,34 @@ namespace caret { static const AString s_v2_centeringCorrectionName; - friend class TileTabsBaseConfiguration; + friend class TileTabsLayoutBaseConfiguration; }; -#ifdef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ - const AString TileTabsGridLayoutConfiguration::s_nameTagName = "Name"; - const AString TileTabsGridLayoutConfiguration::s_uniqueIdentifierTagName = "UniqueIdentifier"; +#ifdef __TILE_TABS_LAYOUT_GRID_CONFIGURATION_DECLARE__ + const AString TileTabsLayoutGridConfiguration::s_nameTagName = "Name"; + const AString TileTabsLayoutGridConfiguration::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 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 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__ + 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_GRID_LAYOUT_CONFIGURATION_H__ +#endif //__TILE_TABS_LAYOUT_GRID_CONFIGURATION_H__ diff --git a/src/Common/TileTabsLayoutManualConfiguration.cxx b/src/Common/TileTabsLayoutManualConfiguration.cxx new file mode 100644 index 0000000000000000000000000000000000000000..bfb3947d43eb0d1f046c6961cdc405349117d63a --- /dev/null +++ b/src/Common/TileTabsLayoutManualConfiguration.cxx @@ -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 --git a/src/Common/TileTabsLayoutManualConfiguration.h b/src/Common/TileTabsLayoutManualConfiguration.h new file mode 100644 index 0000000000000000000000000000000000000000..be2ef669a8f4b158403a22d2bcf6572eb9a96f65 --- /dev/null +++ b/src/Common/TileTabsLayoutManualConfiguration.h @@ -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 --git a/src/Common/TileTabsGridModeEnum.cxx b/src/Common/ToolBarWidthModeEnum.cxx similarity index 67% rename from src/Common/TileTabsGridModeEnum.cxx rename to src/Common/ToolBarWidthModeEnum.cxx index 15547e3dff88bf2f002e790afb1ecb0937642393..efdf7e592610492aaa140cde896f31f21bfc31c8 100644 --- a/src/Common/TileTabsGridModeEnum.cxx +++ b/src/Common/ToolBarWidthModeEnum.cxx @@ -1,7 +1,7 @@ /*LICENSE_START*/ /* - * Copyright (C) 2018 Washington University School of Medicine + * 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 @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __TILE_TABS_GRID_MODE_ENUM_DECLARE__ -#include "TileTabsGridModeEnum.h" -#undef __TILE_TABS_GRID_MODE_ENUM_DECLARE__ +#define __TOOL_BAR_WIDTH_MODE_ENUM_DECLARE__ +#include "ToolBarWidthModeEnum.h" +#undef __TOOL_BAR_WIDTH_MODE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,8 +30,10 @@ using namespace caret; /** - * \class caret::TileTabsGridModeEnum - * \brief Enumerated type for Grid Tile Tab Configuration Mode (auto, custom) + * \class caret::ToolBarWidthModeEnum + * \brief Enumerated type for tool bar width mode + * + * * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -40,30 +42,30 @@ using namespace caret; * class EnumComboBoxTemplate; * * Declare the member: - * EnumComboBoxTemplate* m_TileTabsGridModeEnumComboBox; + * EnumComboBoxTemplate* m_toolBarWidthModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: - * void TileTabsGridModeEnumComboBoxItemActivated(); + * void toolBarWidthModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "TileTabsGridModeEnum.h" + * #include "ToolBarWidthModeEnum.h" * * Instatiate: - * m_TileTabsGridModeEnumComboBox = new EnumComboBoxTemplate(this); - * m_TileTabsGridModeEnumComboBox->setup(); + * m_toolBarWidthModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_toolBarWidthModeEnumComboBox->setup(); * * Get notified when the user changes the selection: - * QObject::connect(m_TileTabsGridModeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(TileTabsGridModeEnumComboBoxItemActivated())); + * QObject::connect(m_toolBarWidthModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(toolBarWidthModeEnumComboBoxItemActivated())); * * Update the selection: - * m_TileTabsGridModeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_toolBarWidthModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const TileTabsGridModeEnum::Enum VARIABLE = m_TileTabsGridModeEnumComboBox->getSelectedItem(); + * const ToolBarWidthModeEnum::Enum VARIABLE = m_toolBarWidthModeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +80,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -TileTabsGridModeEnum::TileTabsGridModeEnum(const Enum enumValue, +ToolBarWidthModeEnum::ToolBarWidthModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +93,7 @@ TileTabsGridModeEnum::TileTabsGridModeEnum(const Enum enumValue, /** * Destructor. */ -TileTabsGridModeEnum::~TileTabsGridModeEnum() +ToolBarWidthModeEnum::~ToolBarWidthModeEnum() { } @@ -99,20 +101,20 @@ TileTabsGridModeEnum::~TileTabsGridModeEnum() * Initialize the enumerated metadata. */ void -TileTabsGridModeEnum::initialize() +ToolBarWidthModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(TileTabsGridModeEnum(AUTOMATIC, - "AUTOMATIC", - "Automatic")); + enumData.push_back(ToolBarWidthModeEnum(STANDARD, + "STANDARD", + "Standard")); - enumData.push_back(TileTabsGridModeEnum(CUSTOM, - "CUSTOM", - "Custom")); + enumData.push_back(ToolBarWidthModeEnum(WIDE, + "WIDE", + "Wide")); } @@ -123,14 +125,14 @@ TileTabsGridModeEnum::initialize() * @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) +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 TileTabsGridModeEnum* d = &enumData[i]; + const ToolBarWidthModeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -147,10 +149,10 @@ TileTabsGridModeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -TileTabsGridModeEnum::toName(Enum enumValue) { +ToolBarWidthModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsGridModeEnum* enumInstance = findData(enumValue); + const ToolBarWidthModeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -164,18 +166,18 @@ TileTabsGridModeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsGridModeEnum::Enum -TileTabsGridModeEnum::fromName(const AString& name, bool* isValidOut) +ToolBarWidthModeEnum::Enum +ToolBarWidthModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; + Enum enumValue = ToolBarWidthModeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsGridModeEnum& d = *iter; + const ToolBarWidthModeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -187,7 +189,7 @@ TileTabsGridModeEnum::fromName(const AString& name, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsGridModeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type ToolBarWidthModeEnum")); } return enumValue; } @@ -200,10 +202,10 @@ TileTabsGridModeEnum::fromName(const AString& name, bool* isValidOut) * String representing enumerated value. */ AString -TileTabsGridModeEnum::toGuiName(Enum enumValue) { +ToolBarWidthModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsGridModeEnum* enumInstance = findData(enumValue); + const ToolBarWidthModeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -217,18 +219,18 @@ TileTabsGridModeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsGridModeEnum::Enum -TileTabsGridModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +ToolBarWidthModeEnum::Enum +ToolBarWidthModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; + Enum enumValue = ToolBarWidthModeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsGridModeEnum& d = *iter; + const ToolBarWidthModeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -240,7 +242,7 @@ TileTabsGridModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsGridModeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type ToolBarWidthModeEnum")); } return enumValue; } @@ -252,10 +254,10 @@ TileTabsGridModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) * Integer code for data type. */ int32_t -TileTabsGridModeEnum::toIntegerCode(Enum enumValue) +ToolBarWidthModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsGridModeEnum* enumInstance = findData(enumValue); + const ToolBarWidthModeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -270,18 +272,18 @@ TileTabsGridModeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -TileTabsGridModeEnum::Enum -TileTabsGridModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +ToolBarWidthModeEnum::Enum +ToolBarWidthModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; + Enum enumValue = ToolBarWidthModeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsGridModeEnum& enumInstance = *iter; + const ToolBarWidthModeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -293,7 +295,7 @@ TileTabsGridModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOu *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsGridModeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type ToolBarWidthModeEnum")); } return enumValue; } @@ -306,13 +308,13 @@ TileTabsGridModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOu * A vector that is OUTPUT containing all of the enumerated values. */ void -TileTabsGridModeEnum::getAllEnums(std::vector& allEnums) +ToolBarWidthModeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -328,16 +330,16 @@ TileTabsGridModeEnum::getAllEnums(std::vector& allEn * If true, the names are sorted in alphabetical order. */ void -TileTabsGridModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +ToolBarWidthModeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(TileTabsGridModeEnum::toName(iter->enumValue)); + allNames.push_back(ToolBarWidthModeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -354,16 +356,16 @@ TileTabsGridModeEnum::getAllNames(std::vector& allNames, const bool isS * If true, the names are sorted in alphabetical order. */ void -TileTabsGridModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +ToolBarWidthModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(TileTabsGridModeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(ToolBarWidthModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { diff --git a/src/Common/TileTabsGridModeEnum.h b/src/Common/ToolBarWidthModeEnum.h similarity index 75% rename from src/Common/TileTabsGridModeEnum.h rename to src/Common/ToolBarWidthModeEnum.h index 4478b4fb3034e7d157381f1f9c0f5e1dd03f3756..d58a5eee7cb70a482d68c3c25ebb80b49bd9c4ab 100644 --- a/src/Common/TileTabsGridModeEnum.h +++ b/src/Common/ToolBarWidthModeEnum.h @@ -1,9 +1,9 @@ -#ifndef __TILE_TABS_GRID_MODE_ENUM_H__ -#define __TILE_TABS_GRID_MODE_ENUM_H__ +#ifndef __TOOL_BAR_WIDTH_MODE_ENUM_H__ +#define __TOOL_BAR_WIDTH_MODE_ENUM_H__ /*LICENSE_START*/ /* - * Copyright (C) 2018 Washington University School of Medicine + * 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 @@ -28,21 +28,21 @@ namespace caret { -class TileTabsGridModeEnum { +class ToolBarWidthModeEnum { public: /** * Enumerated values. */ enum Enum { - /** Workbench creates tab layout */ - AUTOMATIC, - /** User customizes tab layout */ - CUSTOM + /** Standard width */ + STANDARD, + /** Expanded for wide displays */ + WIDE }; - ~TileTabsGridModeEnum(); + ~ToolBarWidthModeEnum(); static AString toName(Enum enumValue); @@ -63,14 +63,14 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); private: - TileTabsGridModeEnum(const Enum enumValue, + ToolBarWidthModeEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const TileTabsGridModeEnum* findData(const Enum enumValue); + static const ToolBarWidthModeEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -94,11 +94,11 @@ private: 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__ +#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 //__TILE_TABS_GRID_MODE_ENUM_H__ +#endif //__TOOL_BAR_WIDTH_MODE_ENUM_H__ diff --git a/src/Common/Vector3D.h b/src/Common/Vector3D.h index 5dc23b9730335b4cb9b343d6bd5d7d203d4a1f1a..71f3d8e29679e8e5499ac86c477eb208f880070e 100644 --- a/src/Common/Vector3D.h +++ b/src/Common/Vector3D.h @@ -59,6 +59,7 @@ namespace caret { 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 --git a/src/Common/WorkbenchQtMessageHandler.cxx b/src/Common/WorkbenchQtMessageHandler.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ba6af2793f4d9976436f4eeaef38d7ac5809a7ba --- /dev/null +++ b/src/Common/WorkbenchQtMessageHandler.cxx @@ -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 --git a/src/Common/WorkbenchQtMessageHandler.h b/src/Common/WorkbenchQtMessageHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..fffe61dfdd66bb916dea45b58cf1757a862b7357 --- /dev/null +++ b/src/Common/WorkbenchQtMessageHandler.h @@ -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 --git a/src/Desktop/desktop.cxx b/src/Desktop/desktop.cxx index b6c7a923efdb719cdc68bd44ae02d3c0d3858442..6228038df7e477c0f43ad57a57408dd82cc2e15a 100644 --- a/src/Desktop/desktop.cxx +++ b/src/Desktop/desktop.cxx @@ -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 @@ struct ProgramState 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 @@ main(int argc, char* argv[]) * Create the session manager. */ SessionManager::createSessionManager(ApplicationTypeEnum::APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE); - caretLoggerIsValid = true; /* * Parameters for the program. @@ -399,16 +215,25 @@ main(int argc, char* argv[]) 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 @@ main(int argc, char* argv[]) * 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 @@ main(int argc, char* argv[]) 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 @@ void printHelp(const AString& progName) 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 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog 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 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog 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\" option" << std::endl; + 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 " << thisParam << " option" << std::endl; hasFatalError = true; } } else if (thisParam == "-spec-load-all") { @@ -969,6 +879,9 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog 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 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog ProgramState::ProgramState() { sceneFileName = ""; + sceneFileNameNoDialog = ""; sceneNameOrNumber = ""; windowSizeXY[0] = -1; windowSizeXY[1] = -1; @@ -988,4 +902,5 @@ ProgramState::ProgramState() graphicsSizeXY[0] = -1; graphicsSizeXY[1] = -1; showSplash = true; + macMenuFlag = false; } diff --git a/src/Files/AffineFile.cxx b/src/Files/AffineFile.cxx index 386c3f09b4674a22d251de4dbadb9c313ab9c361..984212717f460534a06388be4aeccd52e20bb939 100644 --- a/src/Files/AffineFile.cxx +++ b/src/Files/AffineFile.cxx @@ -25,6 +25,8 @@ #include "FileInformation.h" #include "NiftiIO.h" +#include + #include #include #include @@ -52,7 +54,7 @@ FloatMatrix AffineFile::read34(const AString& filename) { 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 @@ FloatMatrix AffineFile::read34(const AString& filename) 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::write44(const FloatMatrix& out, const AString& filename) } } -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 --git a/src/Files/AffineFile.h b/src/Files/AffineFile.h index e26201d5c695f30c7fefa7093c704f5ded904768..57c668aa575607bd0d7320cf1c664951a4d1cdb5 100644 --- a/src/Files/AffineFile.h +++ b/src/Files/AffineFile.h @@ -34,8 +34,8 @@ namespace caret { 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 --git a/src/Files/AffineSeriesFile.cxx b/src/Files/AffineSeriesFile.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7912dbdc54bf64dd8236e4b9adf0abb2ea184d2f --- /dev/null +++ b/src/Files/AffineSeriesFile.cxx @@ -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 --git a/src/Files/AffineSeriesFile.h b/src/Files/AffineSeriesFile.h new file mode 100644 index 0000000000000000000000000000000000000000..a01e70776442383ba49624f8635a413d3e85baf4 --- /dev/null +++ b/src/Files/AffineSeriesFile.h @@ -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 --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index 5962690d52713d5cbda901d39503f30743dec3a2..e9ae9dc7c5df47479fa77b31ac0ad0d9be148df3 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -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 @@ AnnotationFile::clear() } } break; + case ANNOTATION_FILE_DUMMY_FOR_DRAWING: + break; } } @@ -226,8 +230,10 @@ AnnotationFile::initializeAnnotationFile() /* 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 @@ AnnotationFile::copyHelperAnnotationFile(const AnnotationFile& obj) 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 @@ AnnotationFile::receiveEvent(Event* event) } } } + 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 @@ AnnotationFile::receiveEvent(Event* event) 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 @@ AnnotationFile::receiveEvent(Event* event) 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 @@ bool 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 @@ AnnotationFile::cloneAnnotationsFromTabToTab(const int32_t fromTabIndex, * 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 @@ AnnotationFile::cloneAnnotationsFromTabToTab(const int32_t fromTabIndex, 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]); } - - return ( ! groupsToRemoveIterators.empty()); + + 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); + + 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 @@ AnnotationFile::getAllAnnotationGroups(std::vector& annotation 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 @@ AnnotationFile::processGroupingAnnotations(EventAnnotationGrouping* groupingEven 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 @@ AnnotationFile::processRegroupingAnnotations(EventAnnotationGrouping* groupingEv * 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 @@ AnnotationFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, } } break; + case ANNOTATION_FILE_DUMMY_FOR_DRAWING: + break; } /* @@ -1760,6 +1845,7 @@ AnnotationFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, 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 @@ AnnotationFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, sceneAttributes->addToErrorMessage(dfe.whatString()); } } + } + break; + case ANNOTATION_FILE_DUMMY_FOR_DRAWING: break; } @@ -2130,9 +2219,6 @@ AnnotationFile::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup 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 @@ AnnotationFile::isItemSelectedForEditingInWindow(const int32_t /*windowIndex*/) * 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 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile 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 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile 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 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile } } 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 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile } } 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 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile } } 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 --git a/src/Files/AnnotationFile.h b/src/Files/AnnotationFile.h index be344a6002db973ea96991fa3873ca258f2e58a5..4f1577b39b5161007e3d26d78d0c55cd82c67a14 100644 --- a/src/Files/AnnotationFile.h +++ b/src/Files/AnnotationFile.h @@ -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 @@ namespace caret { * "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 @@ namespace caret { 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 @@ namespace caret { AnnotationGroup* getSpaceAnnotationGroup(const Annotation* annotation); - void updateSpacerAnnotationsAfterTileTabsModification(const EventTileTabsConfigurationModification* modEvent); + void updateSpacerAnnotationsAfterTileTabsModification(const EventTileTabsGridConfigurationModification* modEvent); const AnnotationFileSubType m_fileSubType; @@ -230,7 +245,9 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Files/AnnotationFileXmlFormatBase.h b/src/Files/AnnotationFileXmlFormatBase.h index 947e71b2ed9b67a588c1b1d0b412ca938fcf0145..b10a14e1bcc2997761b0f74dddfb8fef88fc297b 100644 --- a/src/Files/AnnotationFileXmlFormatBase.h +++ b/src/Files/AnnotationFileXmlFormatBase.h @@ -52,6 +52,8 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Files/AnnotationFileXmlReader.cxx b/src/Files/AnnotationFileXmlReader.cxx index 4788b9cb42f45d46adfed7ba3c7865b716b61b6d..5db8a70c34dbce11b59f9e9c3b88d3259326f2b3 100644 --- a/src/Files/AnnotationFileXmlReader.cxx +++ b/src/Files/AnnotationFileXmlReader.cxx @@ -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 @@ AnnotationFileXmlReader::readVersionOne(AnnotationFile* annotationFile) } 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 @@ AnnotationFileXmlReader::readAnnotationAttributes(Annotation* annotation, } /** - * Read a one dimensional annotation. + * Read a two coordinate annotation. * * @param annotationElementName * Name of one-dimensional attribute. @@ -606,8 +607,8 @@ AnnotationFileXmlReader::readAnnotationAttributes(Annotation* annotation, * 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); @@ -635,6 +636,54 @@ AnnotationFileXmlReader::readOneDimensionalAnnotation(const QString& annotationE annotation->getEndCoordinate()); } +/** + * 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. * @@ -713,55 +762,68 @@ AnnotationFileXmlReader::readGroup(AnnotationFile* annotationFile) 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 @@ AnnotationFileXmlReader::readGroup(AnnotationFile* annotationFile) * 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 --git a/src/Files/AnnotationFileXmlReader.h b/src/Files/AnnotationFileXmlReader.h index 0abb283ca5d2c2aeec77ebfddf3506cfa93ccfb0..d1d671cc567acff9994b87fb010a6cb3b61b4d4d 100644 --- a/src/Files/AnnotationFileXmlReader.h +++ b/src/Files/AnnotationFileXmlReader.h @@ -35,9 +35,10 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Files/AnnotationFileXmlWriter.cxx b/src/Files/AnnotationFileXmlWriter.cxx index e3c2812a57ecc70f15cdbe93cba18b0cfe5e7668..296573ac197e07de257e43351f65c3ea7be2ed59 100644 --- a/src/Files/AnnotationFileXmlWriter.cxx +++ b/src/Files/AnnotationFileXmlWriter.cxx @@ -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 @@ AnnotationFileXmlWriter::writeFileContentToXmlStreamWriter(const AnnotationFile* 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 @@ AnnotationFileXmlWriter::writeGroup(const AnnotationGroup* group) 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 @@ AnnotationFileXmlWriter::writeGroup(const AnnotationGroup* group) 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 @@ AnnotationFileXmlWriter::writeBox(const AnnotationBox* box) { CaretAssert(box); - writeTwoDimensionalAnnotation(box, + writeOneCoordinateShapeAnnotation(box, ELEMENT_BOX); } @@ -371,10 +348,25 @@ AnnotationFileXmlWriter::writeLine(const AnnotationLine* line) { 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. * @@ -386,7 +378,7 @@ AnnotationFileXmlWriter::writeOval(const AnnotationOval* oval) { CaretAssert(oval); - writeTwoDimensionalAnnotation(oval, + writeOneCoordinateShapeAnnotation(oval, ELEMENT_OVAL); } @@ -530,7 +522,7 @@ AnnotationFileXmlWriter::getAnnotationPropertiesAsAttributes(const Annotation* a * 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 @@ AnnotationFileXmlWriter::getTwoDimAnnotationPropertiesAsAttributes(const Annotat } /** - * 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 @@ AnnotationFileXmlWriter::writeTwoDimensionalAnnotation(const AnnotationTwoDimens /** - * 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); @@ -616,6 +608,42 @@ AnnotationFileXmlWriter::writeOneDimensionalAnnotation(const AnnotationOneDimens 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 --git a/src/Files/AnnotationFileXmlWriter.h b/src/Files/AnnotationFileXmlWriter.h index 5fb5c99bce29d34bf9383525d7bc946c3a01fb4f..9f1fd51704855e37687ced05e27955320128b99f 100644 --- a/src/Files/AnnotationFileXmlWriter.h +++ b/src/Files/AnnotationFileXmlWriter.h @@ -37,10 +37,12 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { CaretPointer m_stream; CaretPointer m_streamHelper; - + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Files/AnnotationTextSubstitutionFile.cxx b/src/Files/AnnotationTextSubstitutionFile.cxx index 7f980a9b52fc7f7d26e8e111637295a59af6d11b..e8728ebb19fddedba816f6b941a9dfca09a832dc 100644 --- a/src/Files/AnnotationTextSubstitutionFile.cxx +++ b/src/Files/AnnotationTextSubstitutionFile.cxx @@ -330,7 +330,7 @@ void 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 --git a/src/Files/CMakeLists.txt b/src/Files/CMakeLists.txt index 0520e6a59d5b33e2ec6ab17d5ee72cbc04abbc11..b66631ce2b25ac02ad8ae36e29ea06b100b0e892 100755 --- a/src/Files/CMakeLists.txt +++ b/src/Files/CMakeLists.txt @@ -22,6 +22,7 @@ ENDIF () # ADD_LIBRARY(Files AffineFile.h +AffineSeriesFile.h AnnotationFile.h AnnotationFileXmlFormatBase.h AnnotationFileXmlReader.h @@ -50,6 +51,7 @@ ChartableMatrixSeriesInterface.h ChartableTwoFileDelegate.h ChartableTwoFileBaseChart.h ChartableTwoFileHistogramChart.h +ChartableTwoFileLineLayerChart.h ChartableTwoFileLineSeriesChart.h ChartableTwoFileMatrixChart.h CiftiBrainordinateDataSeriesFile.h @@ -80,6 +82,7 @@ EventChartMatrixParcelYokingValidation.h EventGetDisplayedDataFiles.h EventMapYokingSelectMap.h EventMapYokingValidation.h +EventMediaFilesGet.h EventSurfaceColoringInvalidate.h EventSurfaceStructuresValidGet.h Fiber.h @@ -89,6 +92,8 @@ FiberOrientationTrajectory.h FiberTrajectoryColorModel.h FiberTrajectoryMapProperties.h FiberTrajectoryDisplayModeEnum.h +FileIdentificationAttributes.h +FileIdentificationMapSelectionEnum.h FilePathNamePrefixCompactor.h FociFile.h FociFileSaxReader.h @@ -109,6 +114,7 @@ LabelDrawingProperties.h LabelDrawingTypeEnum.h LabelFile.h MapYokingGroupEnum.h +MediaFile.h MetricDynamicConnectivityFile.h MetricFile.h MetricSmoothingObject.h @@ -162,6 +168,7 @@ XmlStreamReaderHelper.h XmlStreamWriterHelper.h AffineFile.cxx +AffineSeriesFile.cxx AnnotationFile.cxx AnnotationFileXmlFormatBase.cxx AnnotationFileXmlReader.cxx @@ -185,6 +192,7 @@ ChartableMatrixInterface.cxx ChartableTwoFileDelegate.cxx ChartableTwoFileBaseChart.cxx ChartableTwoFileHistogramChart.cxx +ChartableTwoFileLineLayerChart.cxx ChartableTwoFileLineSeriesChart.cxx ChartableTwoFileMatrixChart.cxx CiftiBrainordinateDataSeriesFile.cxx @@ -215,6 +223,7 @@ EventChartMatrixParcelYokingValidation.cxx EventGetDisplayedDataFiles.cxx EventMapYokingSelectMap.cxx EventMapYokingValidation.cxx +EventMediaFilesGet.cxx EventSurfaceColoringInvalidate.cxx EventSurfaceStructuresValidGet.cxx Fiber.cxx @@ -224,6 +233,8 @@ FiberOrientationTrajectory.cxx FiberTrajectoryColorModel.cxx FiberTrajectoryDisplayModeEnum.cxx FiberTrajectoryMapProperties.cxx +FileIdentificationAttributes.cxx +FileIdentificationMapSelectionEnum.cxx FilePathNamePrefixCompactor.cxx FociFile.cxx FociFileSaxReader.cxx @@ -244,6 +255,7 @@ LabelDrawingProperties.cxx LabelDrawingTypeEnum.cxx LabelFile.cxx MapYokingGroupEnum.cxx +MediaFile.cxx MetricDynamicConnectivityFile.cxx MetricFile.cxx MetricSmoothingObject.cxx diff --git a/src/Files/CaretDataFile.cxx b/src/Files/CaretDataFile.cxx index 4865f0e77caa73d99d91af3345924b186c9b7131..9e2fce5d67ff327f22ae66fb2b567ac0111ac961 100644 --- a/src/Files/CaretDataFile.cxx +++ b/src/Files/CaretDataFile.cxx @@ -455,6 +455,45 @@ CaretDataFile::getFileNameNoExtension() const 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 --git a/src/Files/CaretDataFile.h b/src/Files/CaretDataFile.h index 3bfb1c7cd16bafa7aedc99f42f58a54688263d70..08fc160de74834b8ecb2895027c62888de0f25c1 100644 --- a/src/Files/CaretDataFile.h +++ b/src/Files/CaretDataFile.h @@ -29,6 +29,8 @@ namespace caret { + class ImageFile; + class MediaFile; class GiftiMetaData; class CaretDataFile : public DataFile, public SceneableInterface { @@ -85,6 +87,12 @@ namespace caret { 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 --git a/src/Files/CaretMappableDataFile.cxx b/src/Files/CaretMappableDataFile.cxx index 30fcb3e6be5a8f939e2065efce7af9a852448bb2..2519671670342f92a4ac2c888f299029cf568b87 100644 --- a/src/Files/CaretMappableDataFile.cxx +++ b/src/Files/CaretMappableDataFile.cxx @@ -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 @@ void 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 @@ CaretMappableDataFile::saveFileDataToScene(const SceneAttributes* sceneAttribute 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 @@ CaretMappableDataFile::saveFileDataToScene(const SceneAttributes* sceneAttribute 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 @@ CaretMappableDataFile::restoreFileDataFromScene(const SceneAttributes* sceneAttr 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 @@ const CiftiXML CaretMappableDataFile::getCiftiXML() const * 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 @@ CaretMappableDataFile::getSurfaceNodeIdentificationForMaps(const std::vector& /*mapIndices*/, const float* /*xyz[3]*/, + const AString& /*dataValueSeparator*/, int64_t* /*ijkOut[3]*/, AString& textOut) const { @@ -1520,4 +1550,22 @@ CaretMappableDataFile::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 @@ namespace caret { 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 --git a/src/Files/ChartableTwoFileBaseChart.cxx b/src/Files/ChartableTwoFileBaseChart.cxx index c4363a5742dee1dc66c64a39724ed462dd67da09..5c97cf1f50cb39a885b93bf72fe540198a92511f 100644 --- a/src/Files/ChartableTwoFileBaseChart.cxx +++ b/src/Files/ChartableTwoFileBaseChart.cxx @@ -76,8 +76,8 @@ m_parentCaretMappableDataFile(parentCaretMappableDataFile) 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 @@ ChartableTwoFileBaseChart::~ChartableTwoFileBaseChart() 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 @@ ChartableTwoFileBaseChart::setDefaultAxisTitle(const ChartAxisLocationEnum::Enum 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 @@ ChartableTwoFileBaseChart::setDefaultAxisTitle(const ChartAxisLocationEnum::Enum } 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 @@ ChartableTwoFileBaseChart::setDefaultAxisTitle(const ChartAxisLocationEnum::Enum 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 @@ ChartableTwoFileBaseChart::getChartTwoDataType() const /** * @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 @@ ChartableTwoFileBaseChart::updateChartTwoCompoundDataTypeAfterFileChanges(const } /** - * @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 @@ ChartableTwoFileBaseChart::restoreFromScene(const SceneAttributes* sceneAttribut 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 --git a/src/Files/ChartableTwoFileBaseChart.h b/src/Files/ChartableTwoFileBaseChart.h index 010fc8bd8635ef9009f9ed17756b21fc6b2028f2..1c83d879d7d7aca7f660e44efa5bbbee6c44d4c2 100644 --- a/src/Files/ChartableTwoFileBaseChart.h +++ b/src/Files/ChartableTwoFileBaseChart.h @@ -64,11 +64,11 @@ namespace caret { 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 @@ namespace caret { ChartTwoCompoundDataType m_compoundChartDataType; - AString m_bottomAxisTitleText; + AString m_bottomTopAxisTitleText; AString m_leftRightAxisTitleText; diff --git a/src/Files/ChartableTwoFileDelegate.cxx b/src/Files/ChartableTwoFileDelegate.cxx index 03faab3301f78751ec9ba1cd435bb1c7db86a461..b01280a9f561a4d65f61604ade603073e404ddfe 100644 --- a/src/Files/ChartableTwoFileDelegate.cxx +++ b/src/Files/ChartableTwoFileDelegate.cxx @@ -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 @@ ChartableTwoFileDelegate::updateAfterFileChanged() 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 @@ ChartableTwoFileDelegate::updateAfterFileChanged() 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 @@ ChartableTwoFileDelegate::updateAfterFileChanged() 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 @@ ChartableTwoFileDelegate::updateAfterFileChanged() 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 @@ ChartableTwoFileDelegate::updateAfterFileChanged() 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 @@ ChartableTwoFileDelegate::updateAfterFileChanged() 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 @@ ChartableTwoFileDelegate::clear() 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 @@ void ChartableTwoFileDelegate::clearModified() { m_histogramCharting->clearModified(); + m_lineLayerCharting->clearModified(); m_lineSeriesCharting->clearModified(); m_matrixCharting->clearModified(); } @@ -277,6 +300,9 @@ ChartableTwoFileDelegate::isModified() const if (m_matrixCharting->isModified()) { return true; } + if (m_lineLayerCharting->isModified()) { + return true; + } if (m_lineSeriesCharting->isModified()) { return true; } @@ -322,6 +348,24 @@ ChartableTwoFileDelegate::getHistogramCharting() const return m_histogramCharting.get(); } +/** + * @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. */ @@ -404,11 +448,11 @@ ChartableTwoFileDelegate::isChartingSupportedForChartTwoDataType(const ChartTwoD 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 @@ ChartableTwoFileDelegate::getSupportedChartTwoDataTypes(std::vectorgetChartTwoDataType()); } + if (m_lineLayerCharting != NULL) { + chartDataTypesOut.push_back(m_lineLayerCharting->getChartTwoDataType()); + } if (m_lineSeriesCharting!= NULL) { chartDataTypesOut.push_back(m_lineSeriesCharting->getChartTwoDataType()); } @@ -445,13 +492,16 @@ ChartableTwoFileDelegate::getSupportedChartTwoDataTypes(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 @@ bool 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 @@ ChartableTwoFileDelegate::saveToScene(const SceneAttributes* sceneAttributes, 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 @@ ChartableTwoFileDelegate::restoreFromScene(const SceneAttributes* sceneAttribute 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 --git a/src/Files/ChartableTwoFileDelegate.h b/src/Files/ChartableTwoFileDelegate.h index ab0947b8aaf5df80be85d96c4c46684c1e703c83..4b9836d8cdb77e5d0572391bc352a4e01cae2bbe 100644 --- a/src/Files/ChartableTwoFileDelegate.h +++ b/src/Files/ChartableTwoFileDelegate.h @@ -32,6 +32,7 @@ namespace caret { class CaretMappableDataFile; class ChartableTwoFileHistogramChart; + class ChartableTwoFileLineLayerChart; class ChartableTwoFileLineSeriesChart; class ChartableTwoFileMatrixChart; @@ -59,6 +60,10 @@ namespace caret { const ChartableTwoFileHistogramChart* getHistogramCharting() const; + ChartableTwoFileLineLayerChart* getLineLayerCharting(); + + const ChartableTwoFileLineLayerChart* getLineLayerCharting() const; + ChartableTwoFileLineSeriesChart* getLineSeriesCharting(); const ChartableTwoFileLineSeriesChart* getLineSeriesCharting() const; @@ -75,7 +80,7 @@ namespace caret { 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 @@ namespace caret { std::unique_ptr m_histogramCharting; + std::unique_ptr m_lineLayerCharting; + std::unique_ptr m_lineSeriesCharting; std::unique_ptr m_matrixCharting; diff --git a/src/Files/ChartableTwoFileLineLayerChart.cxx b/src/Files/ChartableTwoFileLineLayerChart.cxx new file mode 100644 index 0000000000000000000000000000000000000000..455696734eba9fa23cc3bc8981acbd4495bef268 --- /dev/null +++ b/src/Files/ChartableTwoFileLineLayerChart.cxx @@ -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 --git a/src/Files/ChartableTwoFileLineLayerChart.h b/src/Files/ChartableTwoFileLineLayerChart.h new file mode 100644 index 0000000000000000000000000000000000000000..02fffce3c2b715cc6afd177519959c2cfd93d601 --- /dev/null +++ b/src/Files/ChartableTwoFileLineLayerChart.h @@ -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 --git a/src/Files/ChartableTwoFileLineSeriesChart.cxx b/src/Files/ChartableTwoFileLineSeriesChart.cxx index 7aec0abaa8537ba1ac8d47eeed887e0cc0447e51..fda40768fadc5abf3836f237dccfcd02fcec4215 100644 --- a/src/Files/ChartableTwoFileLineSeriesChart.cxx +++ b/src/Files/ChartableTwoFileLineSeriesChart.cxx @@ -142,8 +142,28 @@ m_lineSeriesContentType(lineSeriesContentType) 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 @@ ChartableTwoFileLineSeriesChart::loadLineCharts(const EventChartTwoLoadLineSerie 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 @@ ChartableTwoFileLineSeriesChart::loadLineCharts(const EventChartTwoLoadLineSerie 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 --git a/src/Files/ChartableTwoFileMatrixChart.cxx b/src/Files/ChartableTwoFileMatrixChart.cxx index af1b89252e25c29923f22c031eab79d5803e1586..2c4691077fbbdc7f49295c4c73d9ed86b8c99289 100644 --- a/src/Files/ChartableTwoFileMatrixChart.cxx +++ b/src/Files/ChartableTwoFileMatrixChart.cxx @@ -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 @@ m_validRowColumnSelectionDimensions(validRowColumnSelectionDimensions) 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_validRowColumnSelectionDimensions(validRowColumnSelectionDimensions) m_scalarDataSeriesFile = dynamic_cast(ciftiMapFile); CaretAssert(m_scalarDataSeriesFile); m_hasRowSelectionFlag = true; + hasRowScalarsFlag = true; break; } @@ -227,6 +230,31 @@ m_validRowColumnSelectionDimensions(validRowColumnSelectionDimensions) 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_validRowColumnSelectionDimensions(validRowColumnSelectionDimensions) 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_validRowColumnSelectionDimensions(validRowColumnSelectionDimensions) 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 @@ ChartableTwoFileMatrixChart::getMatrixDataRGBA(int32_t& numberOfRowsOut, * 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 @@ ChartableTwoFileMatrixChart::getSelectedRowColumnIndices(const int32_t tabIndex, { 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 @@ ChartableTwoFileMatrixChart::setSelectedRowColumnIndex(const int32_t tabIndex, 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 --git a/src/Files/ChartableTwoFileMatrixChart.h b/src/Files/ChartableTwoFileMatrixChart.h index b8da84f810bc4d3d030b1369a675534a6671a4c4..d00bcf24d7c6674109c60b7c8a6870c559ae8e1a 100644 --- a/src/Files/ChartableTwoFileMatrixChart.h +++ b/src/Files/ChartableTwoFileMatrixChart.h @@ -35,7 +35,7 @@ namespace caret { class CiftiParcelScalarFile; class CiftiParcelSeriesFile; class CiftiScalarDataSeriesFile; - class GraphicsPrimitiveV3fC4f; + class GraphicsPrimitive; class ChartableTwoFileMatrixChart : public ChartableTwoFileBaseChart { @@ -59,8 +59,9 @@ namespace caret { 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 --git a/src/Files/CiftiFiberOrientationFile.cxx b/src/Files/CiftiFiberOrientationFile.cxx index 6f4394b7493bb33f91ec625fd7449e521a978957..d7670df338d6638fd43ef7573d79da405d279d58 100644 --- a/src/Files/CiftiFiberOrientationFile.cxx +++ b/src/Files/CiftiFiberOrientationFile.cxx @@ -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 @@ CiftiFiberOrientationFile::supportsWriting() const 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 --git a/src/Files/CiftiFiberOrientationFile.h b/src/Files/CiftiFiberOrientationFile.h index fdfd25a961045ddc4584ca66d4418f020add4b9b..8c1d7bcd9b8f1971abc0c60cd05e8d6d3623baf1 100644 --- a/src/Files/CiftiFiberOrientationFile.h +++ b/src/Files/CiftiFiberOrientationFile.h @@ -79,6 +79,8 @@ namespace caret { void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); + float getMaximumVariance() const; + // ADD_NEW_METHODS_HERE private: @@ -103,7 +105,7 @@ namespace caret { bool m_displayStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_volumeSpacing[3]; - + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 3c317700b14d0bb6e1ebaba512246257ecfe02c1..3f433fd008a1673d094e962000f44f1743e7f80a 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -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 @@ CiftiMappableDataFile::receiveEvent(Event* event) 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 @@ CiftiMappableDataFile::readFile(const AString& ciftiMapFileName) 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 @@ CiftiMappableDataFile::invalidateColoringInAllMaps() * 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 @@ CiftiMappableDataFile::getMatrixRGBA(std::vector &rgba) * * @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 @@ CiftiMappableDataFile::getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTr 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 @@ CiftiMappableDataFile::getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTr */ 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 @@ CiftiMappableDataFile::getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTr * 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 @@ CiftiMappableDataFile::getMatrixChartingGraphicsPrimitive(const ChartTwoMatrixTr } } + 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; } @@ -2425,6 +2629,37 @@ CiftiMappableDataFile::getCiftiParcelsMapForDirection(const int direction) const return NULL; } +/** + * 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 @@ CiftiMappableDataFile::updateScalarColoringForMap(const int32_t mapIndex) */ invalidateHistogramChartColoring(); - m_matrixGraphicsPrimitive.reset(); + m_matrixGraphicsTrianglesPrimitive.reset(); + m_matrixGraphicsTexturePrimitive.reset(); m_matrixGraphicsOutlinePrimitive.reset(); + m_previousMatrixOpacity = -1.0; } /** @@ -4029,6 +4266,157 @@ CiftiMappableDataFile::getVoxelColorInMap(const int64_t indexIn1, } } +/** + * 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. * @@ -4457,6 +4845,8 @@ CiftiMappableDataFile::getMapSurfaceNodeValue(const int32_t mapIndex, * 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn structure); if ((mappingDataParcelIndex >= 0) && (mappingDataParcelIndex < static_cast(mappingParcels.size()))) { + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } textValueOut = mappingParcels[mappingDataParcelIndex].m_name; } } @@ -4613,6 +5019,9 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn structure); if ((parcelIndex >= 0) && (parcelIndex < static_cast(parcels.size()))) { + if ( ! textValueOut.isEmpty()) { + textValueOut.append(dataValueSeparator); + } textValueOut = parcels[parcelIndex].m_name; } } @@ -4687,6 +5099,9 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn 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 @@ CiftiMappableDataFile::getParcelLabelMapSurfaceNodeValue(const int32_t mapIndex, * 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 @@ CiftiMappableDataFile::getSurfaceNodeIdentificationForMaps(const std::vector(value); const GiftiLabel* gl = glt->getLabel(labelKey); + if ( ! textOut.isEmpty()) { + textOut.append(dataValueSeparator); + } if (gl != NULL) { textOut += gl->getName(); } @@ -4974,17 +5396,21 @@ CiftiMappableDataFile::getSurfaceNodeIdentificationForMaps(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 @@ CiftiMappableDataFile::getMapVolumeVoxelValues(const std::vector mapInd 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 @@ CiftiMappableDataFile::getMapVolumeVoxelValues(const std::vector mapInd 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 @@ CiftiMappableDataFile::getMapVolumeVoxelValues(const std::vector mapInd 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 @@ CiftiMappableDataFile::getMapDataOffsetForVoxelAtCoordinate(const float coordina * 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 @@ CiftiMappableDataFile::getMapDataOffsetForVoxelAtCoordinate(const float coordina 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 @@ CiftiMappableDataFile::getVolumeVoxelIdentificationForMaps(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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { public: enum class MatrixGridMode { - FILLED, + FILLED_TRIANGLES, + FILLED_TEXTURE, OUTLINE }; @@ -513,8 +539,9 @@ namespace caret { 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 @@ namespace caret { /** 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 --git a/src/Files/EventCaretMappableDataFilesGet.cxx b/src/Files/EventCaretMappableDataFilesGet.cxx index 718698658e5f7d8afabc0154d53ebc3c97568505..fb8665f58722736a6419d125be923629f9f555fd 100644 --- a/src/Files/EventCaretMappableDataFilesGet.cxx +++ b/src/Files/EventCaretMappableDataFilesGet.cxx @@ -18,6 +18,7 @@ */ /*LICENSE_END*/ +#include #include #include "CaretAssert.h" @@ -109,4 +110,24 @@ EventCaretMappableDataFilesGet::getAllFiles(std::vector& 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 --git a/src/Files/EventCaretMappableDataFilesGet.h b/src/Files/EventCaretMappableDataFilesGet.h index c85953b12f94a15a6c9daf26d3b0f35ed2bac35f..bb05670e50bef9c920609f3b45c3a76a6081a50e 100644 --- a/src/Files/EventCaretMappableDataFilesGet.h +++ b/src/Files/EventCaretMappableDataFilesGet.h @@ -41,6 +41,8 @@ namespace caret { void getAllFiles(std::vector& allFilesOut) const; + void getAllFilesSortedByName(std::vector& allFilesOut) const; + private: enum Mode { MODE_ANY_DATA_FILE_TYPE, diff --git a/src/Files/EventGetDisplayedDataFiles.cxx b/src/Files/EventGetDisplayedDataFiles.cxx index 1ec9441fc460089fa13832d6b44589c2720f9bb8..f6f731b345c1ab73545c859a044d48a93297b626 100644 --- a/src/Files/EventGetDisplayedDataFiles.cxx +++ b/src/Files/EventGetDisplayedDataFiles.cxx @@ -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 @@ using namespace caret; */ /** - * 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 @@ using namespace caret; */ 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()); @@ -64,6 +92,15 @@ EventGetDisplayedDataFiles::~EventGetDisplayedDataFiles() { } +/** + * @return the mode + */ +EventGetDisplayedDataFiles::Mode +EventGetDisplayedDataFiles::getMode() const +{ + return m_mode; +} + /** * Is the tab index one for determining displayed data files. * @@ -75,6 +112,16 @@ EventGetDisplayedDataFiles::~EventGetDisplayedDataFiles() 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 @@ EventGetDisplayedDataFiles::isTestForDisplayedDataFileInTabIndex(const int32_t t 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; } @@ -161,48 +197,6 @@ EventGetDisplayedDataFiles::getDisplayedDataFiles() const return m_displayedDataFiles; } -/** - * @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. */ diff --git a/src/Files/EventGetDisplayedDataFiles.h b/src/Files/EventGetDisplayedDataFiles.h index ac31fcc640d489c2ec53249d2f2d42d6ad046ede..a0c5245db620f93c14228f03d57fe7d98fc24ee6 100644 --- a/src/Files/EventGetDisplayedDataFiles.h +++ b/src/Files/EventGetDisplayedDataFiles.h @@ -24,8 +24,6 @@ #include #include "Event.h" -#include "StructureEnum.h" - namespace caret { @@ -34,17 +32,25 @@ namespace caret { 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 @@ namespace caret { std::vector getWindowIndices() const; - std::vector getDisplayedSurfaceStructures() const; - private: EventGetDisplayedDataFiles(const EventGetDisplayedDataFiles&); @@ -67,7 +71,7 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - void setupSurfaceStrucutures() const; + const Mode m_mode; // ADD_NEW_MEMBERS_HERE @@ -76,10 +80,6 @@ namespace caret { 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 --git a/src/Files/EventMediaFilesGet.cxx b/src/Files/EventMediaFilesGet.cxx new file mode 100644 index 0000000000000000000000000000000000000000..bb0f8c2618e7c132eb04dd98899e7b3aff41ae58 --- /dev/null +++ b/src/Files/EventMediaFilesGet.cxx @@ -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 --git a/src/Files/EventMediaFilesGet.h b/src/Files/EventMediaFilesGet.h new file mode 100644 index 0000000000000000000000000000000000000000..ada3a62cb9f0da3c822a9274a1635fa2b0f8fb70 --- /dev/null +++ b/src/Files/EventMediaFilesGet.h @@ -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 --git a/src/Files/FileIdentificationAttributes.cxx b/src/Files/FileIdentificationAttributes.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d135bd9bae0320d33ee476b94ea3179bbc6f1d4d --- /dev/null +++ b/src/Files/FileIdentificationAttributes.cxx @@ -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 --git a/src/Files/FileIdentificationAttributes.h b/src/Files/FileIdentificationAttributes.h new file mode 100644 index 0000000000000000000000000000000000000000..16126fa81439e272e03e84aa287b442116d88178 --- /dev/null +++ b/src/Files/FileIdentificationAttributes.h @@ -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 --git a/src/Files/FileIdentificationMapSelectionEnum.cxx b/src/Files/FileIdentificationMapSelectionEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6cdcbdc2b0adb456bc46d67bb625641715452246 --- /dev/null +++ b/src/Files/FileIdentificationMapSelectionEnum.cxx @@ -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 --git a/src/Files/FileIdentificationMapSelectionEnum.h b/src/Files/FileIdentificationMapSelectionEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..807bafb0b7e167348d34c8ebd5202d01240c50c6 --- /dev/null +++ b/src/Files/FileIdentificationMapSelectionEnum.h @@ -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 --git a/src/Files/GiftiTypeFile.cxx b/src/Files/GiftiTypeFile.cxx index 6de85ed84a8b49f8924f0b3cb80c4313f388dbe4..278f98c3f71a265c204748ca9b87b474c63055bd 100644 --- a/src/Files/GiftiTypeFile.cxx +++ b/src/Files/GiftiTypeFile.cxx @@ -1313,6 +1313,8 @@ GiftiTypeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile * 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 @@ GiftiTypeFile::getSurfaceNodeIdentificationForMaps(const std::vector& m const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, + const AString& dataValueSeparator, AString& textOut) const { textOut.clear(); @@ -1377,9 +1380,12 @@ GiftiTypeFile::getSurfaceNodeIdentificationForMaps(const std::vector& m 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 @@ GiftiTypeFile::getSurfaceNodeIdentificationForMaps(const std::vector& m const float value = mf->getValue(nodeIndex, mapIndex); if ( ! valuesText.isEmpty()) { - valuesText.append(", "); + valuesText.append(dataValueSeparator); } valuesText.append(AString::number(value, 'f', 3)); } diff --git a/src/Files/GiftiTypeFile.h b/src/Files/GiftiTypeFile.h index b534902e4ce30c000684f946d3ef7ea359b0340d..b2caa2c796cf1736701beb0ff7a167c87a641deb 100644 --- a/src/Files/GiftiTypeFile.h +++ b/src/Files/GiftiTypeFile.h @@ -174,7 +174,8 @@ namespace caret { 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 --git a/src/Files/GroupAndNameHierarchyItem.cxx b/src/Files/GroupAndNameHierarchyItem.cxx index ac5154b70048de1886733ff84483dca6144143ce..bac8b2d6984a993a6164557083c8428b1472ea59 100644 --- a/src/Files/GroupAndNameHierarchyItem.cxx +++ b/src/Files/GroupAndNameHierarchyItem.cxx @@ -722,6 +722,19 @@ GroupAndNameHierarchyItem::copySelections(const int32_t sourceTabIndex, 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 --git a/src/Files/ImageFile.cxx b/src/Files/ImageFile.cxx index 3e4932082fb6c088f028709c84eadcb67e5cfd58..efe26a55c96f95f012f244d3b76d55491d9a1904 100644 --- a/src/Files/ImageFile.cxx +++ b/src/Files/ImageFile.cxx @@ -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 @@ const float ImageFile::s_defaultWindowDepthPercentage = 990; * Constructor. */ ImageFile::ImageFile() -: CaretDataFile(DataFileTypeEnum::IMAGE) +: MediaFile(DataFileTypeEnum::IMAGE) { m_controlPointFile.grabNew(new ControlPointFile()); @@ -63,7 +67,7 @@ ImageFile::ImageFile() * 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 @@ ImageFile::ImageFile(const unsigned char* imageDataRGBA, 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 @@ ImageFile::clear() } /** - * @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 @@ ImageFile::isEmpty() const 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 @@ ImageFile::writeFile(const AString& filename) } /** - * 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 @@ ImageFile::getImageFileExtensions(std::vector& imageFileExtensions, * 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); } /** @@ -1707,6 +1598,100 @@ ImageFile::getControlPointFile() const return m_controlPointFile; } +/** + * @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 @@ -1724,6 +1709,9 @@ void 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 @@ void 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 --git a/src/Files/ImageFile.h b/src/Files/ImageFile.h index 0a833d541b682b792fc927e5373ddd43c5b95b5a..9d1154b7098aae11efc0fab222481a06d1c95e2b 100644 --- a/src/Files/ImageFile.h +++ b/src/Files/ImageFile.h @@ -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 @@ namespace caret { class VolumeFile; /// File for images -class ImageFile : public CaretDataFile { +class ImageFile : public MediaFile { public: // class ControlPoint { // public: @@ -99,34 +100,25 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: CaretPointer m_controlPointFile; + mutable std::unique_ptr m_graphicsPrimitiveForMediaDrawing; + static const float s_defaultWindowDepthPercentage; }; diff --git a/src/Files/MediaFile.cxx b/src/Files/MediaFile.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f7af4172b0a92ced984cd649381830af4fefe70d --- /dev/null +++ b/src/Files/MediaFile.cxx @@ -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 --git a/src/Files/MediaFile.h b/src/Files/MediaFile.h new file mode 100644 index 0000000000000000000000000000000000000000..f7f122a0ffca254ad039912d5b7ef499baeb5dfe --- /dev/null +++ b/src/Files/MediaFile.h @@ -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 --git a/src/Files/PaletteFile.cxx b/src/Files/PaletteFile.cxx index 06f12de848e6139ee23642651ff80e73dc56bd66..99c0944739f78b54968d0b68266de90592599ac6 100644 --- a/src/Files/PaletteFile.cxx +++ b/src/Files/PaletteFile.cxx @@ -94,6 +94,77 @@ PaletteFile::clear() this->addDefaultPalettes(); } +/** + * 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. * @@ -805,6 +876,94 @@ PaletteFile::addDefaultPalettes() 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 @@ PaletteFile::addDefaultPalettes() // 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 @@ PaletteFile::addDefaultPalettes() // 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 @@ PaletteFile::addDefaultPalettes() // // 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 @@ PaletteFile::addDefaultPalettes() // 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 --git a/src/Files/PaletteFile.h b/src/Files/PaletteFile.h index 505ad5fb391d432b198caf96433a344ff95b8738..0ab1c71f0c06e7423ab6db287904fbdb5a0b1135 100644 --- a/src/Files/PaletteFile.h +++ b/src/Files/PaletteFile.h @@ -75,6 +75,18 @@ namespace caret { 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 --git a/src/Files/RibbonMappingHelper.cxx b/src/Files/RibbonMappingHelper.cxx index 316b4a478f01455aac9b37c65c9eb91ff9bb5718..d871f756aadf2cb8740471fda94f18b204f63f39 100644 --- a/src/Files/RibbonMappingHelper.cxx +++ b/src/Files/RibbonMappingHelper.cxx @@ -263,6 +263,120 @@ namespace 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 --git a/src/Files/RibbonMappingHelper.h b/src/Files/RibbonMappingHelper.h index 5ca5fd247c1ef4520bd5cef7deae942e6413a340..ef0f1b7edf0062adfdc1fdad483bab94aa6f0150 100644 --- a/src/Files/RibbonMappingHelper.h +++ b/src/Files/RibbonMappingHelper.h @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include "Vector3D.h" + #include "stdint.h" #include #include @@ -45,6 +47,14 @@ namespace caret } }; + 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 @@ namespace caret 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 --git a/src/Files/SceneDataFileInfo.cxx b/src/Files/SceneDataFileInfo.cxx index e1618a783c632bc3157690417d811ee8413365dd..8ad7a4ec58989caf29efa5524a175b6ce894b48e 100644 --- a/src/Files/SceneDataFileInfo.cxx +++ b/src/Files/SceneDataFileInfo.cxx @@ -307,7 +307,7 @@ SceneDataFileInfo::sort(std::vector& sceneDataFileInfo, void SceneDataFileInfo::print(const std::vector& sceneDataFileInfo) { - for (const auto sdfi : sceneDataFileInfo) { + for (const auto& sdfi : sceneDataFileInfo) { std::cout << sdfi.getAbsolutePathAndFileName() << std::endl; } } diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 2d0bf5ea6df201ac5b67ec9dd2c6ce63b4f7b064..cb3348bd6f23395bff47d511575c5bff6bd8cb16 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -575,6 +575,53 @@ void SceneFile::setBasePathType(const SceneFileBasePathTypeEnum::Enum basePathTy } } +/** + * 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. * @@ -1096,6 +1143,37 @@ bool SceneFile::findBaseDirectoryForDataFiles(AString& baseDirectoryOut, 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(); @@ -1104,11 +1182,12 @@ SceneFile::findBaseDirectoryForDataFiles(AString& baseDirectoryOut, 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 @@ SceneFile::findBaseDirectoryForDataFiles(AString& baseDirectoryOut, } } - 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 @@ SceneFile::findBaseDirectoryForDataFiles(AString& baseDirectoryOut, 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 @@ SceneFile::findBaseDirectoryForDataFiles(AString& baseDirectoryOut, */ 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 @@ SceneFile::findBaseDirectoryForDataFiles(AString& baseDirectoryOut, } } - baseDirectoryOut = sceneFilePath; if ( ! longestPathMatch.empty()) { /* * Assemble the path components into a directory. */ baseDirectoryOut = AString::join(longestPathMatch, - directorySeparator); + directorySeparator); } /* @@ -1421,7 +1507,7 @@ SceneFile::getAllDataFileInfoFromAllScenes() const std::vector fileInfoOut; - for (const auto nameAndIndices : allNamesAndIndices) { + for (const auto& nameAndIndices : allNamesAndIndices) { fileInfoOut.emplace_back(nameAndIndices.m_dataFileName, basePath, getFileName(), diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index 84411b25fc486843bb9a9a074c8c656e99562a71..d7c3ebe600a76dea04561b04ad0260338956b368 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -45,7 +45,31 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Files/SceneFileXmlStreamBase.h b/src/Files/SceneFileXmlStreamBase.h index 9b75437596fada2ba3c910bc0e2d88bd649e3afe..13c3c4c77eead5b358f27eb703dbd518f0d5ef37 100644 --- a/src/Files/SceneFileXmlStreamBase.h +++ b/src/Files/SceneFileXmlStreamBase.h @@ -54,6 +54,8 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Files/SceneFileXmlStreamReader.cxx b/src/Files/SceneFileXmlStreamReader.cxx index 853eeda5f7f01991a111518d0ce8a701a2cc0a8b..3f83c3c593efd221db159b2368e3cd1ce77d3333 100644 --- a/src/Files/SceneFileXmlStreamReader.cxx +++ b/src/Files/SceneFileXmlStreamReader.cxx @@ -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 @@ void 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 @@ SceneFileXmlStreamReader::readSceneInfoDirectory(QXmlStreamReader& xmlReader, 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 --git a/src/Files/SceneFileXmlStreamWriter.cxx b/src/Files/SceneFileXmlStreamWriter.cxx index 5fae42fac70380c4a5aef311d0e914821c4bc25a..5030ac74542013e6784f50a94234a1827b7b53c4 100644 --- a/src/Files/SceneFileXmlStreamWriter.cxx +++ b/src/Files/SceneFileXmlStreamWriter.cxx @@ -182,7 +182,9 @@ SceneFileXmlStreamWriter::writeSceneInfoDirectory(QXmlStreamWriter& xmlWriter, { /* * 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 --git a/src/Files/SignedDistanceHelper.cxx b/src/Files/SignedDistanceHelper.cxx index f7c557b507a0e2030f040c2e70946c43e50fe984..68f581999176983be01c878b726f7ef4ede5fe55 100644 --- a/src/Files/SignedDistanceHelper.cxx +++ b/src/Files/SignedDistanceHelper.cxx @@ -113,6 +113,69 @@ float SignedDistanceHelper::dist(const float coord[3], WindingLogic myWinding) 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 --git a/src/Files/SignedDistanceHelper.h b/src/Files/SignedDistanceHelper.h index b898b57155a9db2f48a1e6011a423936d7231b7c..c7786a1766550928756a31c9d62518b707b1c8c5 100644 --- a/src/Files/SignedDistanceHelper.h +++ b/src/Files/SignedDistanceHelper.h @@ -111,6 +111,7 @@ namespace caret { ///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 --git a/src/Files/SurfaceProjectedItem.cxx b/src/Files/SurfaceProjectedItem.cxx index 1191d78d4b376352052778ee6ba3cc2f895c69c1..c30785b649d1dfb43d8346e39485bce15b91600d 100644 --- a/src/Files/SurfaceProjectedItem.cxx +++ b/src/Files/SurfaceProjectedItem.cxx @@ -194,6 +194,30 @@ SurfaceProjectedItem::unprojectToVolumeXYZ(const SurfaceFile& sf, } } +/** + * 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, @@ -209,31 +233,33 @@ SurfaceProjectedItem::unprojectToVolumeXYZ(const SurfaceFile& sf, * */ 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 == false) { - if (this->vanEssenProjection->isValid()) { - valid = this->vanEssenProjection->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 ( ! valid) { if (this->stereotaxicXYZValid) { this->getStereotaxicXYZ(xyzOut); valid = true; @@ -372,9 +398,7 @@ SurfaceProjectedItem::getVolumeXYZ() const /* * 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 @@ SurfaceProjectedItem::getVolumeXYZ(float xyzOut[3]) const /* * 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 --git a/src/Files/SurfaceProjectedItem.h b/src/Files/SurfaceProjectedItem.h index 33d747e1a100c1661b3cc1d4d58624a9a1b1f94e..2b470808dc1408b08d4f80780b0c26c1899d2226 100644 --- a/src/Files/SurfaceProjectedItem.h +++ b/src/Files/SurfaceProjectedItem.h @@ -63,6 +63,10 @@ namespace caret { 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 --git a/src/Files/VolumeDynamicConnectivityFile.cxx b/src/Files/VolumeDynamicConnectivityFile.cxx index de12c37272cc39a19934af4d32770931f4163276..ad2c4f0a74cd227bc2a667b661434d4888e23b8d 100644 --- a/src/Files/VolumeDynamicConnectivityFile.cxx +++ b/src/Files/VolumeDynamicConnectivityFile.cxx @@ -489,7 +489,7 @@ VolumeDynamicConnectivityFile::getConnectivityForVoxelIndex(const int64_t ijk[3] 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 --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index 360abbe5617ea34fc38a37676ec3f90226a7bbc0..fe13aeb26b24bbabf5348afa2ee2ff6a98897d1d 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -18,6 +18,7 @@ */ /*LICENSE_END*/ +#include #include #include #include @@ -89,6 +90,10 @@ VolumeFile::VolumeFile(const DataFileTypeEnum::Enum dataFileType) 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 @@ VolumeFile::VolumeFile() 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 @@ VolumeFile::VolumeFile(const vector& dimensionsIn, const vectorclone()); + 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 @@ VolumeFile::clear() 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 @@ VolumeFile::writeFile(const AString& filename) 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 @@ VolumeFile::writeFile(const AString& filename) 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 VolumeFile::interpolateValue(const float coordIn1, const float coordIn2, c { 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 VolumeFile::interpolateValue(const float coordIn1, const float coordIn2, c { 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 @@ VolumeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) c * 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 @@ VolumeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) c 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 @@ VolumeFile::getVolumeVoxelIdentificationForMaps(const std::vector& mapI 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 @@ VolumeFile::getVolumeVoxelIdentificationForMaps(const std::vector& mapI else { valuesText.append("?"); } + valuesText.append(" (" + + getMapName(mapIndex) + + ")"); } else { valuesText.append(AString::number(value, 'f', 3)); diff --git a/src/Files/VolumeFile.h b/src/Files/VolumeFile.h index 3a8e488c64cc636e7cfe4b5cd2f9e5bf1c8b37bd..91a64967ad91df16571755e39626932fe812dd64 100644 --- a/src/Files/VolumeFile.h +++ b/src/Files/VolumeFile.h @@ -127,6 +127,12 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { virtual bool getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], + const AString& dataValueSeparator, int64_t ijkOut[3], AString& textOut) const; diff --git a/src/Files/VolumeSpline.cxx b/src/Files/VolumeSpline.cxx index a91a37d43c07a25f22aa6cc220209fd66343609c..c9337cff19fb999c13ce03d8e837b1ef0e11d34f 100644 --- a/src/Files/VolumeSpline.cxx +++ b/src/Files/VolumeSpline.cxx @@ -67,7 +67,7 @@ VolumeSpline::VolumeSpline(const float* frame, const int64_t framedims[3]) ++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 @@ VolumeSpline::VolumeSpline(const float* frame, const int64_t framedims[3]) ++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 @@ VolumeSpline::VolumeSpline(const float* frame, const int64_t framedims[3]) ++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 @@ VolumeSpline::VolumeSpline(const float* frame, const int64_t framedims[3]) } } +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 @@ float VolumeSpline::sample(const float& ifloat, const float& jfloat, const float 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 --git a/src/Files/WarpfieldFile.cxx b/src/Files/WarpfieldFile.cxx index 5a2692401b920716962617ed8dccad3c9a1839e7..01c52cf7bfcdc321101a019d2e9a963a8347af65 100644 --- a/src/Files/WarpfieldFile.cxx +++ b/src/Files/WarpfieldFile.cxx @@ -174,10 +174,14 @@ void WarpfieldFile::writeITK(const AString& warpname) const 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 --git a/src/Files/XmlStreamReaderHelper.cxx b/src/Files/XmlStreamReaderHelper.cxx index 34712bf96233ce02f875868c734c92e763250f6f..143d94146fa721329156d99f06c020256d8fc544 100644 --- a/src/Files/XmlStreamReaderHelper.cxx +++ b/src/Files/XmlStreamReaderHelper.cxx @@ -165,9 +165,20 @@ XmlStreamReaderHelper::getRequiredAttributeStringValue(const QXmlStreamAttribute } } 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 --git a/src/FilesBase/GiftiLabelTable.cxx b/src/FilesBase/GiftiLabelTable.cxx index 0bf1ac78bc9a96880f1e61d22c7038e435558f86..6d2d5727a2c308368c17936079385815af53108b 100644 --- a/src/FilesBase/GiftiLabelTable.cxx +++ b/src/FilesBase/GiftiLabelTable.cxx @@ -148,7 +148,7 @@ GiftiLabelTable::clear() * */ std::map -GiftiLabelTable::append(const GiftiLabelTable& glt) +GiftiLabelTable::append(const GiftiLabelTable& glt, const bool errorOnLabelConflict) { std::map keyConverterMap; @@ -156,8 +156,8 @@ GiftiLabelTable::append(const GiftiLabelTable& glt) 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,31 +183,10 @@ GiftiLabelTable::addLabel( 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); } -/** - * 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. @@ -228,31 +207,10 @@ GiftiLabelTable::addLabel( 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 @@ -263,12 +221,16 @@ GiftiLabelTable::addLabel( * */ 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 @@ GiftiLabelTable::addLabel(const GiftiLabel* glIn) /* * 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 --git a/src/FilesBase/GiftiLabelTable.h b/src/FilesBase/GiftiLabelTable.h index ed408a617f0d304cce43cf4f7d6da608ebf8233c..8f5e225d68a42477419f7eb11821c82201753d88 100644 --- a/src/FilesBase/GiftiLabelTable.h +++ b/src/FilesBase/GiftiLabelTable.h @@ -70,35 +70,23 @@ private: 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 --git a/src/FilesBase/VolumeMappableInterface.cxx b/src/FilesBase/VolumeMappableInterface.cxx index 37f22ae87e79041e9b446675955c53329456fe90..6d9706cbef5f0d0772d307c1198d768007a020f5 100644 --- a/src/FilesBase/VolumeMappableInterface.cxx +++ b/src/FilesBase/VolumeMappableInterface.cxx @@ -43,13 +43,12 @@ VolumeMappableInterface::getVoxelSpacing(float& spacingOut1, 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 --git a/src/FilesBase/VolumeSpace.cxx b/src/FilesBase/VolumeSpace.cxx index e2e16ba3e48562c0ccab715d9c3fc068e508c528..ae12425e0933936ccbe548f25010ce834fa751dd 100644 --- a/src/FilesBase/VolumeSpace.cxx +++ b/src/FilesBase/VolumeSpace.cxx @@ -55,7 +55,7 @@ VolumeSpace::VolumeSpace(const int64_t dims[3], const float sform[12]) 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 --git a/src/FtglFont/FTGL/ftgl.h b/src/FtglFont/FTGL/ftgl.h index af31ab4295f05a4bd8ce6ff9b2cbe5110d2594e0..df355a86010fe3e58f1770109d7964b6ac28c79f 100644 --- a/src/FtglFont/FTGL/ftgl.h +++ b/src/FtglFont/FTGL/ftgl.h @@ -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 --git a/src/Gifti/GiftiDataArray.cxx b/src/Gifti/GiftiDataArray.cxx index 74f15186e54f50f240a463a2e2a353daace018e6..006fed0e70b964dbf3d9b5266eefe4ea29018dc7 100644 --- a/src/Gifti/GiftiDataArray.cxx +++ b/src/Gifti/GiftiDataArray.cxx @@ -680,12 +680,12 @@ GiftiDataArray::readFromText(const AString text, // // 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 @@ GiftiDataArray::readFromText(const AString text, // DataCompressZLib compressor; const uint64_t uncompressedDataLength = - compressor.uncompressData(dataBuffer, + compressor.uncompressData(dataBuffer.data(), numDecoded, (unsigned char*)&data[0], data.size()); @@ -712,11 +712,6 @@ GiftiDataArray::readFromText(const AString text, throw GiftiException(AString::fromStdString(str.str())); } - // - // Free memory - // - delete[] dataBuffer; - // // Is byte swapping needed ? // @@ -1205,36 +1200,30 @@ GiftiDataArray::writeAsXML(std::ostream& stream, // 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 --git a/src/Graphics/CMakeLists.txt b/src/Graphics/CMakeLists.txt index 53cac7b7a50f2a7cb4a4d983989f90b997676136..e59de039257701001c992298972805871503e33c 100644 --- a/src/Graphics/CMakeLists.txt +++ b/src/Graphics/CMakeLists.txt @@ -25,6 +25,7 @@ GraphicsPrimitiveV3f.h GraphicsPrimitiveV3fC4f.h GraphicsPrimitiveV3fC4ub.h GraphicsPrimitiveV3fN3f.h +GraphicsPrimitiveV3fN3fC4ub.h GraphicsPrimitiveV3fT3f.h GraphicsShape.h GraphicsUtilitiesOpenGL.h @@ -46,6 +47,7 @@ GraphicsPrimitiveV3f.cxx GraphicsPrimitiveV3fC4f.cxx GraphicsPrimitiveV3fC4ub.cxx GraphicsPrimitiveV3fN3f.cxx +GraphicsPrimitiveV3fN3fC4ub.cxx GraphicsPrimitiveV3fT3f.cxx GraphicsShape.cxx GraphicsUtilitiesOpenGL.cxx diff --git a/src/Graphics/CaretOpenGLInclude.h b/src/Graphics/CaretOpenGLInclude.h index 16536c203e3e3b6f049c95a93c22c85586b9bbec..acfd77531c354bc5ed2a48f8862bb0459c7cd713 100644 --- a/src/Graphics/CaretOpenGLInclude.h +++ b/src/Graphics/CaretOpenGLInclude.h @@ -27,6 +27,16 @@ * \ingroup Common */ +/* + * 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 --git a/src/Graphics/GraphicsEngineDataOpenGL.cxx b/src/Graphics/GraphicsEngineDataOpenGL.cxx index 2fc7e3aa17e728d622417e48a95f10f872ca09cc..f2dcc85e91c401e42ff0f1df113a1e372f016201 100644 --- a/src/Graphics/GraphicsEngineDataOpenGL.cxx +++ b/src/Graphics/GraphicsEngineDataOpenGL.cxx @@ -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 @@ GraphicsEngineDataOpenGL::loadCoordinateBuffer(GraphicsPrimitive* primitive) 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 @@ GraphicsEngineDataOpenGL::loadTextureImageDataBuffer(GraphicsPrimitive* primitiv 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 @@ GraphicsEngineDataOpenGL::loadTextureImageDataBuffer(GraphicsPrimitive* primitiv } 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 @@ GraphicsEngineDataOpenGL::loadTextureImageDataBuffer(GraphicsPrimitive* primitiv 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 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) { 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 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) 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 @@ GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, { CaretAssert(primitive); + bool modelSpaceLineFlag = false; + bool windowSpaceLineFlag = false; switch (primitive->m_primitiveType) { case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: break; @@ -868,14 +973,14 @@ GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, 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 @@ GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, 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 @@ GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, 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 @@ GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, 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 @@ GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, /* * 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 @@ GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, 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 @@ GraphicsEngineDataOpenGL::drawPrivate(const PrivateDrawMode drawMode, 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 @@ GraphicsEngineDataOpenGL::drawPrivate(const PrivateDrawMode drawMode, 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 --git a/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx b/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx index 50ce01c6591ef180b30ce65c47ee493c8a1ea04e..7e1d850eba8195ea118474de4387603046343511 100644 --- a/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx +++ b/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx @@ -54,6 +54,8 @@ using namespace caret; /** * Constructor. * + * @param mode + * The conversion mode * @param xyz * The XYZ vertices. * @param floatRGBA @@ -118,7 +120,35 @@ GraphicsOpenGLPolylineTriangles::~GraphicsOpenGLPolylineTriangles() */ 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 @@ GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(const joinType); GraphicsPrimitive* primitiveOut = lineConversion.convertLinesToPolygons(errorMessageOut); - + + if (triangleVertexIndicesToLineVertexIndicesOut != NULL) { + *triangleVertexIndicesToLineVertexIndicesOut = lineConversion.m_triangleVertexIndicesToLineVertexIndices; + } return primitiveOut; } @@ -1258,7 +1291,7 @@ GraphicsOpenGLPolylineTriangles::getAverageColorRGBA(const int32_t primitiveVert 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 @@ GraphicsOpenGLPolylineTriangles::convertLineSegmentsToTriangles() } 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 --git a/src/Graphics/GraphicsOpenGLPolylineTriangles.h b/src/Graphics/GraphicsOpenGLPolylineTriangles.h index cd67d8c9d7be9cd43cff167c704691be45f1326e..8de5cc96b684d9b56b78a47e22ab6d6dcc1ebf9a 100644 --- a/src/Graphics/GraphicsOpenGLPolylineTriangles.h +++ b/src/Graphics/GraphicsOpenGLPolylineTriangles.h @@ -43,6 +43,10 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Graphics/GraphicsPrimitive.cxx b/src/Graphics/GraphicsPrimitive.cxx index c2796737766faa313d9a539826cdc7570824dd9d..ba2e2820664cf10f1fd3de5046057b4cade39e4d 100644 --- a/src/Graphics/GraphicsPrimitive.cxx +++ b/src/Graphics/GraphicsPrimitive.cxx @@ -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 @@ using namespace caret; * 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 @@ GraphicsPrimitive::GraphicsPrimitive(const VertexDataType vertexDataType, const ColorDataType colorDataType, const VertexColorType vertexColorType, const TextureDataType textureDataType, + const TextureWrappingType textureWrappingType, + const TextureFilteringType textureFilteringType, const PrimitiveType primitiveType) : CaretObject(), EventListenerInterface(), @@ -74,10 +88,12 @@ GraphicsPrimitive::GraphicsPrimitive(const VertexDataType vertexDataType, 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 @@ GraphicsPrimitive::GraphicsPrimitive(const GraphicsPrimitive& obj) 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 @@ GraphicsPrimitive::copyHelperGraphicsPrimitive(const GraphicsPrimitive& obj) m_textureImageBytesRGBA = obj.m_textureImageBytesRGBA; m_textureImageWidth = obj.m_textureImageWidth; m_textureImageHeight = obj.m_textureImageHeight; + invalidateVertexMeasurements(); + m_graphicsEngineDataForOpenGL.reset(); } @@ -180,8 +201,8 @@ GraphicsPrimitive::reserveForNumberOfVertices(const int32_t numberOfVertices) m_floatTextureSTR.reserve(numberOfVertices * 3); break; } - - m_boundingBoxValid = false; + + invalidateVertexMeasurements(); } /** @@ -293,6 +314,19 @@ GraphicsPrimitive::setUsageTypeTextureCoordinates(const UsageType usageType) 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 @@ GraphicsPrimitive::addVertexProtected(const float xyz[3], 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 @@ GraphicsPrimitive::getVertexFloatXYZ(const int32_t vertexIndex, 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 @@ GraphicsPrimitive::replaceFloatXYZ(const std::vector& xyz) 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 @@ void 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]; @@ -934,6 +1053,47 @@ GraphicsPrimitive::replaceVertexFloatXYZ(const int32_t vertexIndex, } } +/** + * 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. * @@ -977,6 +1137,23 @@ void 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 @@ void 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 @@ GraphicsPrimitive::replaceVertexByteRGBA(const int32_t vertexIndex, 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 @@ GraphicsPrimitive::replaceAllVertexSolidByteRGBA(const uint8_t rgba[4]) 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 @@ GraphicsPrimitive::getVertexBounds(BoundingBox& boundingBoxOut) const 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 @@ GraphicsPrimitive::copyVertex(const int32_t copyFromIndex, } } - m_boundingBoxValid = false; + invalidateVertexMeasurements(); } /** @@ -1673,6 +1901,315 @@ GraphicsPrimitive::simplfyLines(const int32_t skipVertexCount) m_floatTextureSTR = textureSTR; } +/** + * 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. * @@ -1748,6 +2285,20 @@ GraphicsPrimitive::newPrimitiveV3fN3f(const GraphicsPrimitive::PrimitiveType pri return primitive; } +/** + * @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 @@ GraphicsPrimitive::newPrimitiveV3fC4ub(const GraphicsPrimitive::PrimitiveType pr 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 --git a/src/Graphics/GraphicsPrimitive.h b/src/Graphics/GraphicsPrimitive.h index bfc30a56e7d921a85bd46c1f79573c6840faf813..d16cc9da7ce43320ccb752a4655166de82cc3199 100644 --- a/src/Graphics/GraphicsPrimitive.h +++ b/src/Graphics/GraphicsPrimitive.h @@ -29,7 +29,6 @@ #include "CaretObject.h" #include "EventListenerInterface.h" - namespace caret { class BoundingBox; @@ -38,7 +37,9 @@ namespace caret { class GraphicsPrimitiveV3fC4f; class GraphicsPrimitiveV3fC4ub; class GraphicsPrimitiveV3fN3f; + class GraphicsPrimitiveV3fN3fC4ub; class GraphicsPrimitiveV3fT3f; + class Matrix4x4Interface; class GraphicsPrimitive : public CaretObject, public EventListenerInterface { @@ -95,6 +96,26 @@ namespace caret { FLOAT_STR }; + /** + * 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 @@ -254,6 +275,26 @@ namespace caret { MILLIMETERS }; + /** + * 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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(); @@ -359,11 +406,33 @@ namespace caret { */ 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. */ @@ -374,6 +443,10 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { AString getVertexColorTypeAsText(const VertexColorType vertexColorType) const; + void invalidateVertexMeasurements(); + const VertexDataType m_vertexDataType; const NormalVectorDataType m_normalVectorDataType; @@ -468,8 +560,14 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx b/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx index ca3397ccd7988f19e338b3bb1f76c4ae553671dc..175d0cb1017918db55fef4232278e55bf57c38cf 100644 --- a/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx +++ b/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx @@ -125,13 +125,14 @@ GraphicsPrimitiveSelectionHelper::setupSelectionBeforeDrawing() 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 --git a/src/Graphics/GraphicsPrimitiveV3f.cxx b/src/Graphics/GraphicsPrimitiveV3f.cxx index 06797069d88c5bf752cbdc18ef33c85a415f265b..c9bbc1ccdd69151c3702f9e280e9bd0ab6d07e75 100644 --- a/src/Graphics/GraphicsPrimitiveV3f.cxx +++ b/src/Graphics/GraphicsPrimitiveV3f.cxx @@ -55,6 +55,8 @@ GraphicsPrimitiveV3f::GraphicsPrimitiveV3f(const PrimitiveType primitiveType, ColorDataType::FLOAT_RGBA, VertexColorType::SOLID_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { m_floatSolidRGBA[0] = rgba[0]; @@ -78,6 +80,8 @@ GraphicsPrimitiveV3f::GraphicsPrimitiveV3f(const PrimitiveType primitiveType, ColorDataType::UNSIGNED_BYTE_RGBA, VertexColorType::SOLID_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { m_unsignedByteSolidRGBA[0] = rgba[0]; diff --git a/src/Graphics/GraphicsPrimitiveV3fC4f.cxx b/src/Graphics/GraphicsPrimitiveV3fC4f.cxx index 7a731e077f6a7648db948ef179b1721d01a69d1f..a7f2c113c11c8580eafb0de0927de3c7100dc4e1 100644 --- a/src/Graphics/GraphicsPrimitiveV3fC4f.cxx +++ b/src/Graphics/GraphicsPrimitiveV3fC4f.cxx @@ -46,6 +46,8 @@ GraphicsPrimitiveV3fC4f::GraphicsPrimitiveV3fC4f(const PrimitiveType primitiveTy ColorDataType::FLOAT_RGBA, VertexColorType::PER_VERTEX_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { diff --git a/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx b/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx index 44a872d832bfe756ac7d5e97c8c4f633f467fb74..7404bc53985933da481bc40bf3d93cbf29bf7061 100644 --- a/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx +++ b/src/Graphics/GraphicsPrimitiveV3fC4ub.cxx @@ -46,6 +46,8 @@ GraphicsPrimitiveV3fC4ub::GraphicsPrimitiveV3fC4ub(const PrimitiveType primitive ColorDataType::UNSIGNED_BYTE_RGBA, VertexColorType::PER_VERTEX_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { diff --git a/src/Graphics/GraphicsPrimitiveV3fN3f.cxx b/src/Graphics/GraphicsPrimitiveV3fN3f.cxx index e30f31957ea44b03d4ec975a4b44fde3131125d4..745a8b178be06cdf577c03b48c0b484f7e079415 100644 --- a/src/Graphics/GraphicsPrimitiveV3fN3f.cxx +++ b/src/Graphics/GraphicsPrimitiveV3fN3f.cxx @@ -55,6 +55,8 @@ GraphicsPrimitiveV3fN3f::GraphicsPrimitiveV3fN3f(const PrimitiveType primitiveTy ColorDataType::FLOAT_RGBA, VertexColorType::SOLID_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { m_floatSolidRGBA[0] = rgba[0]; @@ -78,6 +80,8 @@ GraphicsPrimitiveV3fN3f::GraphicsPrimitiveV3fN3f(const PrimitiveType primitiveTy ColorDataType::UNSIGNED_BYTE_RGBA, VertexColorType::SOLID_RGBA, TextureDataType::NONE, + TextureWrappingType::CLAMP, + TextureFilteringType::LINEAR, primitiveType) { m_unsignedByteSolidRGBA[0] = rgba[0]; diff --git a/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.cxx b/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1c6991e68d6c328049cc8a9cf51c8ae399a25486 --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.cxx @@ -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 --git a/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.h b/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.h new file mode 100644 index 0000000000000000000000000000000000000000..a532257d086064ac0cfb61e3f6a085c36ed62dcf --- /dev/null +++ b/src/Graphics/GraphicsPrimitiveV3fN3fC4ub.h @@ -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 --git a/src/Graphics/GraphicsPrimitiveV3fT3f.cxx b/src/Graphics/GraphicsPrimitiveV3fT3f.cxx index c6de7875263cdcd488ea9d070b90a649a575749a..401c8b31694e85b7613459762a2b0b849071c605 100644 --- a/src/Graphics/GraphicsPrimitiveV3fT3f.cxx +++ b/src/Graphics/GraphicsPrimitiveV3fT3f.cxx @@ -45,16 +45,24 @@ using namespace caret; * 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 --git a/src/Graphics/GraphicsPrimitiveV3fT3f.h b/src/Graphics/GraphicsPrimitiveV3fT3f.h index a0845280bcaddd718553862ce75044f3e67508c6..2109235d3d334ff372c0d4ff63135e8265e5aac8 100644 --- a/src/Graphics/GraphicsPrimitiveV3fT3f.h +++ b/src/Graphics/GraphicsPrimitiveV3fT3f.h @@ -37,7 +37,9 @@ namespace caret { 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 --git a/src/Graphics/GraphicsShape.cxx b/src/Graphics/GraphicsShape.cxx index 687f8dd8be84d191459dd7624da87a9ea7899a18..03091ec5784b434356a8772f3ed2c538b42fa429 100644 --- a/src/Graphics/GraphicsShape.cxx +++ b/src/Graphics/GraphicsShape.cxx @@ -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 @@ GraphicsShape::drawSphereByteColor(const float xyz[3], 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 @@ GraphicsShape::drawSpheresByteColor(const float xyz[], 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 @@ GraphicsShape::drawCircleFilled(const float xyz[3], 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; @@ -630,6 +596,189 @@ GraphicsShape::drawCircleFilled(const float xyz[3], glPopMatrix(); } +/** + * 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 * @@ -784,7 +933,7 @@ GraphicsShape::drawRing(const float xyz[3], 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 @@ GraphicsShape::createCirclePrimitive(const int32_t numberOfDivisions, /* * 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 --git a/src/Graphics/GraphicsShape.h b/src/Graphics/GraphicsShape.h index d77d6b6dde9d835cc7ab6fe459bb0bd33f720f08..c947bc0ddd088d8baaeae80c72d8aca9259d8698 100644 --- a/src/Graphics/GraphicsShape.h +++ b/src/Graphics/GraphicsShape.h @@ -21,7 +21,7 @@ */ /*LICENSE_END*/ - +#include #include #include @@ -111,10 +111,20 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { virtual AString toString() const; private: + enum class Shape { + CIRCLE_FILLED, + RING, + SQUARE, + }; + class RingKey { public: RingKey(const int32_t numberOfDivisions, @@ -244,6 +266,13 @@ namespace caret { 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 --git a/src/Graphics/GraphicsUtilitiesOpenGL.cxx b/src/Graphics/GraphicsUtilitiesOpenGL.cxx index 93e99ce8e8c932e46017edb53cfb5328a5fe0a71..07f582638813d7b629e8c6ce0b2967d8ff914ae3 100644 --- a/src/Graphics/GraphicsUtilitiesOpenGL.cxx +++ b/src/Graphics/GraphicsUtilitiesOpenGL.cxx @@ -129,6 +129,28 @@ GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToMillimeters(const fl return millimeters ; } +/** + * 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 @@ GraphicsUtilitiesOpenGL::getOpenGLError(const AString& message) 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 --git a/src/Graphics/GraphicsUtilitiesOpenGL.h b/src/Graphics/GraphicsUtilitiesOpenGL.h index 9bc38f405a07bb9fd51f830b433a680340feb523..6757842d46d2e6f30d677cd787441fd8988f9bf2 100644 --- a/src/Graphics/GraphicsUtilitiesOpenGL.h +++ b/src/Graphics/GraphicsUtilitiesOpenGL.h @@ -39,6 +39,8 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/AboutWorkbenchDialog.cxx b/src/GuiQt/AboutWorkbenchDialog.cxx index 0070f86d3c3390d953c633804e1c5e64e508bd63..2524405a42bf9dec85b5bfdaf01666bcebd9cc54 100644 --- a/src/GuiQt/AboutWorkbenchDialog.cxx +++ b/src/GuiQt/AboutWorkbenchDialog.cxx @@ -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 @@ AboutWorkbenchDialog::AboutWorkbenchDialog(BrainOpenGLWidget* openGLParentWidget 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 @@ AboutWorkbenchDialog::displayMoreInformation() } 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 --git a/src/GuiQt/AnnotationBackgroundTypeWidget.cxx b/src/GuiQt/AnnotationBackgroundTypeWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..cd8f5b03c05dacaacb3137cbfaf788274d7a1d84 --- /dev/null +++ b/src/GuiQt/AnnotationBackgroundTypeWidget.cxx @@ -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 --git a/src/GuiQt/AnnotationBackgroundTypeWidget.h b/src/GuiQt/AnnotationBackgroundTypeWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..02c021067e843086a735f4c866ccaa8b74c4edd6 --- /dev/null +++ b/src/GuiQt/AnnotationBackgroundTypeWidget.h @@ -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 --git a/src/GuiQt/AnnotationBoundsWidget.cxx b/src/GuiQt/AnnotationBoundsWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7c70a3b965d98cb14573249cb607ce0ac5a7b75c --- /dev/null +++ b/src/GuiQt/AnnotationBoundsWidget.cxx @@ -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 --git a/src/GuiQt/AnnotationBoundsWidget.h b/src/GuiQt/AnnotationBoundsWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..9aa4f4fa6acf4ef434bc0b10fb03e3b497cfd081 --- /dev/null +++ b/src/GuiQt/AnnotationBoundsWidget.h @@ -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 --git a/src/GuiQt/AnnotationChangeCoordinateDialog.cxx b/src/GuiQt/AnnotationChangeCoordinateDialog.cxx index b54eee7c7bedcfa0952755c056a8d20715e03f9d..ed10b02f90dd91a333c53525f3d6c4300a84f695 100644 --- a/src/GuiQt/AnnotationChangeCoordinateDialog.cxx +++ b/src/GuiQt/AnnotationChangeCoordinateDialog.cxx @@ -30,6 +30,7 @@ #include "Annotation.h" #include "AnnotationCoordinate.h" +#include "AnnotationCoordinateInformation.h" #include "AnnotationCoordinateSelectionWidget.h" #include "CaretAssert.h" #include "WuQMessageBox.h" diff --git a/src/GuiQt/AnnotationChangeCoordinateDialog.h b/src/GuiQt/AnnotationChangeCoordinateDialog.h index bc89f738a2f5075b2b5a114e61bf71551027bf77..daa940e07d43a54e24e87bacd5f8c9a65b6af02d 100644 --- a/src/GuiQt/AnnotationChangeCoordinateDialog.h +++ b/src/GuiQt/AnnotationChangeCoordinateDialog.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include "UserInputModeAnnotations.h" #include "WuQDialogModal.h" diff --git a/src/GuiQt/AnnotationColorWidget.cxx b/src/GuiQt/AnnotationColorWidget.cxx index fc51bbf823c87e4dda163994c5e0624f10f84df1..f88faaed308746c309db0c99e581def2d18b67a0 100644 --- a/src/GuiQt/AnnotationColorWidget.cxx +++ b/src/GuiQt/AnnotationColorWidget.cxx @@ -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 @@ AnnotationColorWidget::backgroundColorSelected(const CaretColorEnum::Enum caretC 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 @@ AnnotationColorWidget::isBothColorsSetToNoneAllowed(QWidget* widget, case AnnotationTypeEnum::BOX: allowBothColorsNoneFlag = false; break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -517,6 +520,11 @@ AnnotationColorWidget::isBothColorsSetToNoneAllowed(QWidget* widget, 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 @@ AnnotationColorWidget::lineColorSelected(const CaretColorEnum::Enum caretColor) 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 @@ AnnotationColorWidget::lineThicknessSpinBoxValueChanged(double value) 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 --git a/src/GuiQt/AnnotationCoordinateCenterXYWidget.cxx b/src/GuiQt/AnnotationCoordinateCenterXYWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..56b27318739ddd4fc6b3dd21ca69bc000a03b88a --- /dev/null +++ b/src/GuiQt/AnnotationCoordinateCenterXYWidget.cxx @@ -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 --git a/src/GuiQt/AnnotationCoordinateWidget.h b/src/GuiQt/AnnotationCoordinateCenterXYWidget.h similarity index 57% rename from src/GuiQt/AnnotationCoordinateWidget.h rename to src/GuiQt/AnnotationCoordinateCenterXYWidget.h index e5822eba3b27f5478e575aba5666fe4663c33170..6c0ca69450cae99b3a04c32f8ff0ee06f2b9beb4 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.h +++ b/src/GuiQt/AnnotationCoordinateCenterXYWidget.h @@ -1,5 +1,5 @@ -#ifndef __ANNOTATION_COORDINATE_WIDGET_H__ -#define __ANNOTATION_COORDINATE_WIDGET_H__ +#ifndef __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_H__ +#define __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_H__ /*LICENSE_START*/ /* @@ -26,18 +26,16 @@ #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationWidgetParentEnum.h" +#include "UserInputModeEnum.h" class QDoubleSpinBox; -class QSpinBox; namespace caret { class Annotation; class AnnotationCoordinate; - class EnumComboBoxTemplate; - class StructureEnumComboBox; - class AnnotationCoordinateWidget : public QWidget { + class AnnotationCoordinateCenterXYWidget: public QWidget { Q_OBJECT @@ -47,72 +45,55 @@ namespace caret { COORDINATE_TWO }; - AnnotationCoordinateWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, + AnnotationCoordinateCenterXYWidget(const UserInputModeEnum::Enum userInputMode, + const AnnotationWidgetParentEnum::Enum parentWidgetType, const WhichCoordinate whichCoordinate, const int32_t browserWindowIndex, QWidget* parent = 0); - virtual ~AnnotationCoordinateWidget(); + virtual ~AnnotationCoordinateCenterXYWidget(); // ADD_NEW_METHODS_HERE - void updateContent(Annotation* annotation); - - signals: - void signalSelectCoordinateWithMouse(); + void updateContent(std::vector& selectedAnnotations); private slots: - void valueChanged(); - - void setCoordinateActionTriggered(); + void xValueChanged(double value); - void surfaceOffsetLengthValueChanged(double); - - void surfaceOffsetVectorTypeChanged(); + void yValueChanged(double value); private: - AnnotationCoordinateWidget(const AnnotationCoordinateWidget&); + AnnotationCoordinateCenterXYWidget(const AnnotationCoordinateCenterXYWidget&); - AnnotationCoordinateWidget& operator=(const AnnotationCoordinateWidget&); + AnnotationCoordinateCenterXYWidget& operator=(const AnnotationCoordinateCenterXYWidget&); + + void processValueChanged(QDoubleSpinBox* spinBox, + const double value); - AnnotationCoordinate* getCoordinate(); + 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; - 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; + std::vector m_annotations; }; -#ifdef __ANNOTATION_COORDINATE_WIDGET_DECLARE__ +#ifdef __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_DECLARE__ // -#endif // __ANNOTATION_COORDINATE_WIDGET_DECLARE__ +#endif // __ANNOTATION_COORDINATE_CENTER_XY_WIDGET_DECLARE__ } // namespace -#endif //__ANNOTATION_COORDINATE_WIDGET_H__ +#endif //__ANNOTATION_COORDINATE_CENTER_XY_WIDGET_H__ diff --git a/src/GuiQt/AnnotationCoordinateInformation.cxx b/src/GuiQt/AnnotationCoordinateInformation.cxx index 83a4a03d67e0439298c5af6ecc6f85b18b5edb6d..8fd47a8a076a3af961fff08e92ca2bfb920b8103 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.cxx +++ b/src/GuiQt/AnnotationCoordinateInformation.cxx @@ -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 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi } } +/** + * 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 @@ AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWi 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 @@ AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWi 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 @@ bool 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 @@ AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(Annotation* an * 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 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati * 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 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati 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 --git a/src/GuiQt/AnnotationCoordinateInformation.h b/src/GuiQt/AnnotationCoordinateInformation.h index 96daac16995d9543278556ddc01698daa429c6ce..de2c5b98355142eea2b97e33b24328f683fb1827 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.h +++ b/src/GuiQt/AnnotationCoordinateInformation.h @@ -31,8 +31,9 @@ class QLabel; namespace caret { class Annotation; - class AnnotationOneDimensionalShape; - class AnnotationTwoDimensionalShape; + class AnnotationTwoCoordinateShape; + class AnnotationOneCoordinateShape; + class AnnotationMultiCoordinateShape; class BrainOpenGLWidget; class BrainOpenGLViewportContent; class MouseEvent; @@ -48,10 +49,12 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx b/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx index 58546544871cb1788eb3c0583e6b0082698450d0..0fa721b6bd410de3d57e8d08a4943d71414f7ba5 100644 --- a/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx @@ -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 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; + case AnnotationTypeEnum::BROWSER_TAB: + enableTabSpaceFlag = false; + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -112,6 +116,13 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) 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 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno 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 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno 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 @@ AnnotationCoordinateSelectionWidget::setCoordinateForNewAnnotation(Annotation* a 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 @@ AnnotationCoordinateSelectionWidget::setCoordinateForNewAnnotation(Annotation* a } if ( ! validCoordsFlag) { - errorMessageOut = "Failed to set coordinates for annotatin."; + errorMessageOut = "Failed to set coordinates for annotation."; } updateAnnotationDisplayProperties(annotation); diff --git a/src/GuiQt/AnnotationCoordinateSelectionWidget.h b/src/GuiQt/AnnotationCoordinateSelectionWidget.h index 8b9b64b1a662bc17552018cfad642a91c0aedb7b..9b80f80fb480274323a1c65afca74f6f7e27c32f 100644 --- a/src/GuiQt/AnnotationCoordinateSelectionWidget.h +++ b/src/GuiQt/AnnotationCoordinateSelectionWidget.h @@ -21,9 +21,10 @@ */ /*LICENSE_END*/ - +#include #include +//#include "AnnotationCoordinateInformation.h" #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationTypeEnum.h" #include "UserInputModeAnnotations.h" @@ -36,8 +37,6 @@ namespace caret { class Annotation; class AnnotationCoordinate; class AnnotationImage; - class AnnotationOneDimensionalShape; - class AnnotationTwoDimensionalShape; class AnnotationCoordinateSelectionWidget : public QWidget { @@ -74,10 +73,6 @@ namespace caret { 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 @@ namespace caret { const AnnotationCoordinateInformation* m_optionalSecondCoordInfo; + std::vector> m_optionalMultiCoordInfo; + static const QString s_SPACE_PROPERTY_NAME; diff --git a/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx b/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx index 88890fb5ab8c3b9afa96732879eae858baaab7ef..30c416ad881803f12c9a3c566c933729df8362c3 100644 --- a/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx @@ -121,7 +121,7 @@ AnnotationCoordinateSpaceWidget::updateContent(std::vector annotati } AString indicesString; - for (const auto s : tabIndices) { + for (const auto& s : tabIndices) { if (indicesString.isEmpty()) { indicesString.append(":"); } diff --git a/src/GuiQt/AnnotationCoordinateWidget.cxx b/src/GuiQt/AnnotationCoordinatesWidget.cxx similarity index 50% rename from src/GuiQt/AnnotationCoordinateWidget.cxx rename to src/GuiQt/AnnotationCoordinatesWidget.cxx index 4595898a13b4c1f6b7ba51e9ea116673b63100c3..b4cdd486cd1470125dbf6b10442516a560ab9215 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.cxx +++ b/src/GuiQt/AnnotationCoordinatesWidget.cxx @@ -19,27 +19,30 @@ */ /*LICENSE_END*/ -#define __ANNOTATION_COORDINATE_WIDGET_DECLARE__ -#include "AnnotationCoordinateWidget.h" -#undef __ANNOTATION_COORDINATE_WIDGET_DECLARE__ +#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 "AnnotationOneDimensionalShape.h" +#include "AnnotationTwoCoordinateShape.h" #include "AnnotationRedoUndoCommand.h" -#include "AnnotationTwoDimensionalShape.h" +#include "AnnotationScaleBar.h" +#include "AnnotationOneCoordinateShape.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" @@ -64,7 +67,7 @@ using namespace caret; /** - * \class caret::AnnotationCoordinateWidget + * \class caret::AnnotationCoordinatesWidget * \brief Widget for editing annotation coordinate * \ingroup GuiQt */ @@ -72,26 +75,27 @@ using namespace caret; /** * Constructor. * - * @param - * @param whichCoordinate - * Which coordinate, one (or only), or two + * @param userInputMode + * The user input mode + * @param parentWidgetType + * Type of parent widget * @param browserWindowIndex * Index of browser window * @param parent * Parent widget */ -AnnotationCoordinateWidget::AnnotationCoordinateWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, - const WhichCoordinate whichCoordinate, +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_whichCoordinate(whichCoordinate), m_browserWindowIndex(browserWindowIndex) { m_annotation = NULL; - + QString colonString; switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: @@ -101,160 +105,151 @@ m_browserWindowIndex(browserWindowIndex) 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"); + m_zCoordLabel = new QLabel(" Z" + colonString); + QLabel* surfaceVertexLabel = new QLabel("Vertex:"); + + createCoordinateWidgets(0); + createCoordinateWidgets(1); - 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"); + 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); - const float spinBoxMaximumWidth = 80.0f; - m_xCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); - m_yCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); - m_zCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); + 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_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"); + 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 COORDINATE_TWO: + case 1: + QObject::connect(m_surfaceNodeIndexSpinBox[coordinateIndex], SIGNAL(valueChanged(int)), + this, SLOT(valueChangedCoordinateTwo())); + break; + default: + CaretAssert(0); 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(); */ + 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 AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: + 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); - 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_yCoordSpinBox[coordinateIndex] = createCoordinateSpinBox(coordinateIndex, "Y", 1); - 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); + 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); - QHBoxLayout* layout = new QHBoxLayout(this); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); - layout->addWidget(m_surfaceWidget); - layout->addWidget(m_coordinateWidget); - if (setCoordinateToolButton != NULL) { - layout->addWidget(setCoordinateToolButton); + 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"); } - - setSizePolicy(QSizePolicy::Fixed, - QSizePolicy::Fixed); } /** - * Destructor. + * @return Coordinate for given indx + * @param const int32_t coordinateIndex + * Index of coordinate */ -AnnotationCoordinateWidget::~AnnotationCoordinateWidget() -{ -} - AnnotationCoordinate* -AnnotationCoordinateWidget::getCoordinate() +AnnotationCoordinatesWidget::getCoordinate(const int32_t coordinateIndex) { AnnotationCoordinate* ac = NULL; if (m_annotation != NULL) { - AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(m_annotation); - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(m_annotation); + AnnotationTwoCoordinateShape* oneDimShape = dynamic_cast(m_annotation); + AnnotationOneCoordinateShape* twoDimShape = dynamic_cast(m_annotation); - switch (m_whichCoordinate) { - case COORDINATE_ONE: + switch (coordinateIndex) { + case 0: if (oneDimShape != NULL) { ac = oneDimShape->getStartCoordinate(); } @@ -262,11 +257,14 @@ AnnotationCoordinateWidget::getCoordinate() ac = twoDimShape->getCoordinate(); } break; - case COORDINATE_TWO: + case 1: if (oneDimShape != NULL) { ac = oneDimShape->getEndCoordinate(); } break; + default: + CaretAssert(0); + break; } } @@ -280,7 +278,7 @@ AnnotationCoordinateWidget::getCoordinate() * @param coordinate. */ void -AnnotationCoordinateWidget::updateContent(Annotation* annotation) +AnnotationCoordinatesWidget::updateContent(Annotation* annotation) { m_annotation = NULL; if (annotation != NULL) { @@ -291,34 +289,102 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) m_surfaceStructureComboBox->listOnlyValidStructures(); - const AnnotationCoordinate* coordinate = getCoordinate(); + 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); + } - bool surfaceFlag = 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) { - float xyz[3]; + xyzFlag = true; 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 zDepthMinimum = -1000.0; + const double zDepthMaximum = 1000.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: { @@ -329,10 +395,10 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) zMin = coordinateMinimum; zMax = coordinateMaximum; digitsRightOfDecimalX = 3; - digitsRightOfDecimalX = 3; + digitsRightOfDecimalY = 3; xStep = 1.0; yStep = 1.0; - + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); CaretAssert(bbw); BrowserTabContent* browserTabContent = bbw->getBrowserTabContent(); @@ -347,6 +413,9 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) 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; @@ -408,7 +477,9 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) yMax = percentageMaximum; zMin = zDepthMinimum; zMax = zDepthMaximum; - suffix = "%"; + xSuffix = "%"; + ySuffix = "%"; + zSuffix = "%"; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: xMin = coordinateMinimum; @@ -422,15 +493,20 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; + xyzFlag = false; break; case AnnotationCoordinateSpaceEnum::TAB: xMin = percentageMinimum; xMax = percentageMaximum; yMin = percentageMinimum; yMax = percentageMaximum; - zMin = zDepthMinimum; + zMin = 0.0; zMax = zDepthMaximum; - suffix = "%"; + xSuffix = "%"; + ySuffix = "%"; + digitsRightOfDecimalZ = 0; + zStep = 1.0; + zLabelText = " O:"; break; case AnnotationCoordinateSpaceEnum::VIEWPORT: /* @@ -442,128 +518,156 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) 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; + zMin = 0.0; zMax = zDepthMaximum; - suffix = "%"; + xSuffix = "%"; + ySuffix = "%"; + digitsRightOfDecimalZ = 0; + zStep = 1.0; + zLabelText = " O:"; 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); + 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); } - else { - setEnabled(true); + } + + 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); } - else { - setEnabled(false); + if (coordinateIndex == 0) { + m_surfaceStructureComboBox->getWidget()->setEnabled(surfaceFlag); + m_surfaceOffsetVectorTypeComboBox->getWidget()->setEnabled(surfaceFlag); } - - m_surfaceWidget->setVisible(surfaceFlag); - m_coordinateWidget->setVisible( ! 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 value changed. - * + * Called when surface offset Two value changed. + * * @param value * New value. */ void -AnnotationCoordinateWidget::surfaceOffsetLengthValueChanged(double value) +AnnotationCoordinatesWidget::surfaceOffsetLengthValueTwoChanged(double value) { - const AnnotationCoordinate* coordinate = getCoordinate(); - if ((m_annotation != NULL) - && (coordinate != NULL)) { - AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(value); - valueChanged(); - } + AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(value); + valueChangedCoordinateTwo(); } /** * Called when surface offset vector type is changed. */ void -AnnotationCoordinateWidget::surfaceOffsetVectorTypeChanged() +AnnotationCoordinatesWidget::surfaceOffsetVectorTypeChanged() { - const AnnotationCoordinate* coordinate = getCoordinate(); - if ((m_annotation != NULL) - && (coordinate != NULL)) { - CaretAssert(m_surfaceOffsetVectorTypeComboBox); - AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(m_surfaceOffsetVectorTypeComboBox->getSelectedItem()); - valueChanged(); - } + CaretAssert(m_surfaceOffsetVectorTypeComboBox); + AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(m_surfaceOffsetVectorTypeComboBox->getSelectedItem()); + valueChangedCoordinateOne(); + valueChangedCoordinateTwo(); } /** * Gets called when a coordinate value is changed. */ void -AnnotationCoordinateWidget::valueChanged() +AnnotationCoordinatesWidget::valueChangedCoordinateOne() +{ + valueChangedCoordinate(0); +} +/** + * Gets called when a coordinate value is changed. + */ +void +AnnotationCoordinatesWidget::valueChangedCoordinateTwo() { - const AnnotationCoordinate* coordinate = getCoordinate(); + 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; @@ -602,12 +706,9 @@ AnnotationCoordinateWidget::valueChanged() surfaceOffsetLength, surfaceOffsetVector); structure = m_surfaceStructureComboBox->getSelectedStructure(); - surfaceNodeIndex = m_surfaceNodeIndexSpinBox->value(); - surfaceOffsetLength = m_surfaceOffsetLengthSpinBox->value(); - - if (m_surfaceOffsetVectorTypeComboBox != NULL) { - surfaceOffsetVector = m_surfaceOffsetVectorTypeComboBox->getSelectedItem(); - } + surfaceOffsetVector = m_surfaceOffsetVectorTypeComboBox->getSelectedItem(); + surfaceNodeIndex = m_surfaceNodeIndexSpinBox[coordinateIndex]->value(); + surfaceOffsetLength = m_surfaceOffsetLengthSpinBox[coordinateIndex]->value(); coordinateCopy.setSurfaceSpace(structure, surfaceNumberOfNodes, @@ -617,9 +718,9 @@ AnnotationCoordinateWidget::valueChanged() } else { float xyz[3] = { - (float)m_xCoordSpinBox->value(), - (float)m_yCoordSpinBox->value(), - (float)m_zCoordSpinBox->value() + (float)m_xCoordSpinBox[coordinateIndex]->value(), + (float)m_yCoordSpinBox[coordinateIndex]->value(), + (float)m_zCoordSpinBox[coordinateIndex]->value() }; coordinateCopy.setXYZ(xyz); } @@ -638,23 +739,34 @@ AnnotationCoordinateWidget::valueChanged() 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 (m_whichCoordinate) { - case COORDINATE_ONE: + switch (coordinateIndex) { + case 0: undoCommand->setModeCoordinateOne(coordinateCopy, selectedAnnotations); break; - case COORDINATE_TWO: + case 1: undoCommand->setModeCoordinateTwo(coordinateCopy, selectedAnnotations); break; + default: + CaretAssert(0); + break; } + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -680,16 +792,68 @@ AnnotationCoordinateWidget::valueChanged() } /** - * Called when the set coordinate button is clicked. + * @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) */ -void -AnnotationCoordinateWidget::setCoordinateActionTriggered() +QDoubleSpinBox* +AnnotationCoordinatesWidget::createCoordinateSpinBox(const int32_t coordinateIndex, + const QString& axisCharacter, + const int32_t xyzIndex) { - QPoint middlePos(width() / 2, - height() / 2); - QToolTip::showText(mapToGlobal(middlePos), - m_plusButtonToolTipText, - this); + const int digitsRightOfDecimal = 1; - signalSelectCoordinateWithMouse(); + 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 --git a/src/GuiQt/AnnotationCoordinatesWidget.h b/src/GuiQt/AnnotationCoordinatesWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..d678e5ee00a0af9844066a363a92fc003fc5feef --- /dev/null +++ b/src/GuiQt/AnnotationCoordinatesWidget.h @@ -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 --git a/src/GuiQt/AnnotationCreateDialog.cxx b/src/GuiQt/AnnotationCreateDialog.cxx index 042c7e75cab3dad306fd35044e275848bbfd3525..8c56507009840608e493cf8a4d76a7c93489f2b7 100644 --- a/src/GuiQt/AnnotationCreateDialog.cxx +++ b/src/GuiQt/AnnotationCreateDialog.cxx @@ -77,6 +77,8 @@ using namespace caret; * * @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 @@ using namespace caret; */ 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 @@ AnnotationCreateDialog::newAnnotationFromSpaceAndType(const MouseEvent& mouseEve * * @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 @@ AnnotationCreateDialog::newAnnotationFromSpaceAndType(const MouseEvent& mouseEve */ 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 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndBounds(const MouseEvent& mo * 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 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndBounds(const MouseEvent& mo 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 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, } NewAnnotationInfo newInfo(mouseEvent, + drawingCoordinates, annotationSpaceIn, annotationType, useBothFlag, @@ -185,13 +197,15 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, 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 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, 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 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, } 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 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, 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 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: break; } @@ -378,21 +403,48 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, 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 @@ AnnotationCreateDialog::selectImageButtonClicked() /* * 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 @@ AnnotationCreateDialog::finishAnnotationCreation(AnnotationFile* annotationFile, 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 @@ AnnotationCreateDialog::finishAnnotationCreation(AnnotationFile* annotationFile, * * @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 @@ AnnotationCreateDialog::finishAnnotationCreation(AnnotationFile* annotationFile, * 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_annotationFile(annotationFile) 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 @@ m_annotationFile(annotationFile) switch (annotationType) { case AnnotationTypeEnum::BOX: break; + case AnnotationTypeEnum::BROWSER_TAB: + break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: @@ -889,6 +1026,10 @@ m_annotationFile(annotationFile) break; case AnnotationTypeEnum::OVAL: break; + case AnnotationTypeEnum::POLY_LINE: + break; + case AnnotationTypeEnum::SCALE_BAR: + break; case AnnotationTypeEnum::TEXT: break; } @@ -942,6 +1083,9 @@ AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() case AnnotationTypeEnum::BOX: useAverageFlag = true; break; + case AnnotationTypeEnum::BROWSER_TAB: + useAverageFlag = true; + break; case AnnotationTypeEnum::COLOR_BAR: useAverageFlag = true; break; @@ -954,6 +1098,12 @@ AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() 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 --git a/src/GuiQt/AnnotationCreateDialog.h b/src/GuiQt/AnnotationCreateDialog.h index 2aad84ba9dc18355045e7ea591bdcfdf0fd48c3e..deb1b41b8688cd06b2441e63b21616db1007dbc1 100644 --- a/src/GuiQt/AnnotationCreateDialog.h +++ b/src/GuiQt/AnnotationCreateDialog.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { AnnotationCoordinateInformation m_coordTwoInfo; + std::vector> m_coordMultiInfo; + bool m_coordTwoInfoValid; float m_percentageWidth; @@ -113,6 +121,7 @@ namespace caret { 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 @@ namespace caret { Annotation* m_annotationThatWasCreated; -// float m_annotationFromBoundsWidth; -// -// float m_annotationFromBoundsHeight; - QButtonGroup* m_annotationSpaceButtonGroup; QTextEdit* m_textEdit; @@ -166,10 +171,6 @@ namespace caret { 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 --git a/src/GuiQt/AnnotationDeleteWidget.cxx b/src/GuiQt/AnnotationDeleteWidget.cxx index a6dd38cd018df0f7e3d56e2ab44a891b78ce3eb2..21850218f6c7d5b41c0e9470228a6a6618446d24 100644 --- a/src/GuiQt/AnnotationDeleteWidget.cxx +++ b/src/GuiQt/AnnotationDeleteWidget.cxx @@ -152,6 +152,28 @@ AnnotationDeleteWidget::deleteActionTriggered() 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 @@ AnnotationDeleteWidget::deleteActionTriggered() undoCommand->setModeDeleteAnnotations(deleteAnnotations); AString errorMessage; - if ( ! annotationManager->applyCommand(undoCommand, - errorMessage)) { + if ( ! annotationManager->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, + errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } diff --git a/src/GuiQt/AnnotationFontWidget.cxx b/src/GuiQt/AnnotationFontWidget.cxx index 69265393f5540b347fd3044461c02678dd5bffaf..00779c85b0bf2fd6330c2b767db3b6f18be056a7 100644 --- a/src/GuiQt/AnnotationFontWidget.cxx +++ b/src/GuiQt/AnnotationFontWidget.cxx @@ -606,7 +606,8 @@ AnnotationFontWidget::textColorSelected(const CaretColorEnum::Enum caretColor) 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 @@ AnnotationFontWidget::fontBoldChanged() 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 @@ AnnotationFontWidget::fontItalicChanged() 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 @@ AnnotationFontWidget::fontNameChanged() 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 @@ AnnotationFontWidget::fontSizeChanged() 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 @@ AnnotationFontWidget::fontUnderlineChanged() 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 --git a/src/GuiQt/AnnotationFormatWidget.cxx b/src/GuiQt/AnnotationFormatWidget.cxx index 908a5cd06bacf7763879738ecff163e551bdffc3..89bf3a30b0a0a0cfe2debc6acdcd55e5b53fb4f9 100644 --- a/src/GuiQt/AnnotationFormatWidget.cxx +++ b/src/GuiQt/AnnotationFormatWidget.cxx @@ -42,14 +42,18 @@ using namespace caret; /** * 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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ AnnotationFormatWidget::~AnnotationFormatWidget() /** * 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 @@ AnnotationFormatWidget::updateContent(Annotation* /*annotation*/) QWidget* AnnotationFormatWidget::createArrangeMenuToolButton() { - AnnotationMenuArrange* arrangeMenu = new AnnotationMenuArrange(m_browserWindowIndex); + AnnotationMenuArrange* arrangeMenu = new AnnotationMenuArrange(m_userInputMode, + m_browserWindowIndex); QAction* arrangeAction = new QAction("Arrange", this); diff --git a/src/GuiQt/AnnotationFormatWidget.h b/src/GuiQt/AnnotationFormatWidget.h index 52b08cbcf94dca03a5c324c44344cec787072e7d..fa15e98dd8ec67634f59ca601394b2348cce90ee 100644 --- a/src/GuiQt/AnnotationFormatWidget.h +++ b/src/GuiQt/AnnotationFormatWidget.h @@ -25,6 +25,8 @@ #include +#include "UserInputModeEnum.h" + namespace caret { class Annotation; @@ -34,7 +36,8 @@ namespace caret { 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 @@ namespace caret { // ADD_NEW_METHODS_HERE - void updateContent(Annotation* annotation); + void updateContent(const std::vector& annotations); private: AnnotationFormatWidget(const AnnotationFormatWidget&); @@ -51,6 +54,8 @@ namespace caret { QWidget* createArrangeMenuToolButton(); + const UserInputModeEnum::Enum m_userInputMode; + const int32_t m_browserWindowIndex; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/AnnotationInsertNewWidget.cxx b/src/GuiQt/AnnotationInsertNewWidget.cxx index b768fa26ba37858c04e0c136702277ef93047bbe..d1bee377b4dd7f76ca769b9f3e4ef9f9d824c7ea 100644 --- a/src/GuiQt/AnnotationInsertNewWidget.cxx +++ b/src/GuiQt/AnnotationInsertNewWidget.cxx @@ -80,16 +80,19 @@ m_browserWindowIndex(browserWindowIndex) * 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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ AnnotationInsertNewWidget::enableDisableSpaceActions() 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 @@ AnnotationInsertNewWidget::createShapeToolButton(const AnnotationTypeEnum::Enum 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 @@ AnnotationInsertNewWidget::spaceOrShapeActionTriggered() &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 @@ AnnotationInsertNewWidget::createShapePixmap(const QWidget* widget, 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 @@ AnnotationInsertNewWidget::createShapePixmap(const QWidget* widget, 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 --git a/src/GuiQt/AnnotationInsertNewWidget.h b/src/GuiQt/AnnotationInsertNewWidget.h index eb6f03e4c1015d4e428d858bb2008cebe09b94b6..1dd6c47bb1976f9751b6a3e9df6fc89f44f8354e 100644 --- a/src/GuiQt/AnnotationInsertNewWidget.h +++ b/src/GuiQt/AnnotationInsertNewWidget.h @@ -84,6 +84,12 @@ namespace caret { AnnotationMenuFileSelection* m_fileSelectionMenu; + QToolButton* m_polyLineToolButton = NULL; + + AString m_polyLineDrawClicksToolTipText; + + AString m_polyLineDrawDragToolTipText; + static AString s_previousImageFileDirectory; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/AnnotationLineArrowTipsWidget.cxx b/src/GuiQt/AnnotationLineArrowTipsWidget.cxx index 8a9a4a2522923e1e6d9676bfd6c0fcca63d4e343..6ee02f7bb43d56d17f3161d461ef0d21a5def0b3 100644 --- a/src/GuiQt/AnnotationLineArrowTipsWidget.cxx +++ b/src/GuiQt/AnnotationLineArrowTipsWidget.cxx @@ -163,7 +163,8 @@ AnnotationLineArrowTipsWidget::startArrowTipActionToggled() 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 @@ AnnotationLineArrowTipsWidget::endArrowTipActionToggled() 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 --git a/src/GuiQt/AnnotationMenuArrange.cxx b/src/GuiQt/AnnotationMenuArrange.cxx index 33583be3f2bc017d7797dbcf22cc8ec920f129bd..5f9087d239041b8633ef226086a6f742b1be89ea 100644 --- a/src/GuiQt/AnnotationMenuArrange.cxx +++ b/src/GuiQt/AnnotationMenuArrange.cxx @@ -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 @@ using namespace caret; /** * 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 @@ AnnotationMenuArrange::~AnnotationMenuArrange() void AnnotationMenuArrange::addAlignmentSelections() { + if ( ! actions().isEmpty()) { + addSeparator(); + } + std::vector alignments; AnnotationAlignmentEnum::getAllEnums(alignments); @@ -116,6 +151,10 @@ AnnotationMenuArrange::addAlignmentSelections() void AnnotationMenuArrange::addDistributeSelections() { + if ( ! actions().isEmpty()) { + addSeparator(); + } + std::vector distributes; AnnotationDistributeEnum::getAllEnums(distributes); @@ -140,6 +179,21 @@ AnnotationMenuArrange::addDistributeSelections() 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 @@ AnnotationMenuArrange::addGroupingSelections() 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,14 +282,92 @@ AnnotationMenuArrange::menuAboutToShow() { 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 @@ AnnotationMenuArrange::menuActionTriggered(QAction* action) 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 @@ AnnotationMenuArrange::menuActionTriggered(QAction* action) 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 @@ AnnotationMenuArrange::applyAlignment(const AnnotationAlignmentEnum::Enum alignm 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 @@ AnnotationMenuArrange::applyDistribute(const AnnotationDistributeEnum::Enum dist 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 @@ AnnotationMenuArrange::applyGrouping(const AnnotationGroupingModeEnum::Enum grou 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 @@ AnnotationMenuArrange::applyGrouping(const AnnotationGroupingModeEnum::Enum grou EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } + /** * Create an alignment pixmap. * diff --git a/src/GuiQt/AnnotationMenuArrange.h b/src/GuiQt/AnnotationMenuArrange.h index 98adf6c55d89772fc06c22a1f34036fc2ebe52af..fb396d4d726063ec6e6e255a3ce84ce9ec1989c8 100644 --- a/src/GuiQt/AnnotationMenuArrange.h +++ b/src/GuiQt/AnnotationMenuArrange.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/AnnotationMenuFileSelection.cxx b/src/GuiQt/AnnotationMenuFileSelection.cxx index bc3d91f47af22f01dedf511fbe0ba7493d093505..c7b6c516528a298475f1e9d78624f421bad14c11 100644 --- a/src/GuiQt/AnnotationMenuFileSelection.cxx +++ b/src/GuiQt/AnnotationMenuFileSelection.cxx @@ -113,9 +113,10 @@ AnnotationMenuFileSelection::chooseDiskFile() /* * 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 --git a/src/GuiQt/AnnotationNameWidget.cxx b/src/GuiQt/AnnotationNameWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4f533e49ae0ee924a840d2804eea07926a62eb4a --- /dev/null +++ b/src/GuiQt/AnnotationNameWidget.cxx @@ -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 --git a/src/GuiQt/AnnotationNameWidget.h b/src/GuiQt/AnnotationNameWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..18af642a6d5e9a0288c6e96316981e3348343cbf --- /dev/null +++ b/src/GuiQt/AnnotationNameWidget.h @@ -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 --git a/src/GuiQt/AnnotationPasteDialog.cxx b/src/GuiQt/AnnotationPasteDialog.cxx index e6d7e2db232bfa1697a919ae0d4f4df310de9999..21e3b25d8437524255c9cbe47d9835cc80716b4a 100644 --- a/src/GuiQt/AnnotationPasteDialog.cxx +++ b/src/GuiQt/AnnotationPasteDialog.cxx @@ -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 @@ AnnotationPasteDialog::pasteAnnotationOnClipboard(const MouseEvent& mouseEvent, 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 @@ AnnotationPasteDialog::pasteAnnotationOnClipboard(const MouseEvent& mouseEvent, 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 @@ AnnotationPasteDialog::pasteAnnotationOnClipboard(const MouseEvent& mouseEvent, 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 @@ AnnotationPasteDialog::getAnnotationThatWasCreated() * True if the shape's coordinate was updated for pasting, else false. */ bool -AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationOneDimensionalShape* oneDimShape, +AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationTwoCoordinateShape* oneDimShape, AnnotationCoordinateInformation& coordInfo) { @@ -398,6 +434,120 @@ AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationOneDimensionalShape* o return validCoordsFlag; } +/** + * 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. */ @@ -438,8 +588,30 @@ AnnotationPasteDialog::okButtonClicked() 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 --git a/src/GuiQt/AnnotationPasteDialog.h b/src/GuiQt/AnnotationPasteDialog.h index 57ef9c28b3b8649d3b3dd8647ebbf0809a3010f2..42ae5916856d92e30ed411728f55a08ab3f78f06 100644 --- a/src/GuiQt/AnnotationPasteDialog.h +++ b/src/GuiQt/AnnotationPasteDialog.h @@ -32,7 +32,8 @@ namespace caret { class AnnotationCoordinateInformation; class AnnotationCoordinateSelectionWidget; class AnnotationFile; - class AnnotationOneDimensionalShape; + class AnnotationMultiCoordinateShape; + class AnnotationTwoCoordinateShape; class MouseEvent; class AnnotationPasteDialog : public WuQDialogModal { @@ -62,9 +63,12 @@ namespace caret { 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 --git a/src/GuiQt/AnnotationRedoUndoWidget.cxx b/src/GuiQt/AnnotationRedoUndoWidget.cxx index 3b7519b67b67f8c8665294336c5da38fec268ba7..09ba881985c8a2a4d06611a95a76c272cb1174a3 100644 --- a/src/GuiQt/AnnotationRedoUndoWidget.cxx +++ b/src/GuiQt/AnnotationRedoUndoWidget.cxx @@ -50,10 +50,20 @@ using namespace caret; /** * 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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ AnnotationRedoUndoWidget::~AnnotationRedoUndoWidget() } /** - * 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 @@ void 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 @@ void 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 --git a/src/GuiQt/AnnotationRedoUndoWidget.h b/src/GuiQt/AnnotationRedoUndoWidget.h index 58af270ddff35b0fef98caed2372599aba667266..15523df308a60ce6c3a54aee70a38b07f228c089 100644 --- a/src/GuiQt/AnnotationRedoUndoWidget.h +++ b/src/GuiQt/AnnotationRedoUndoWidget.h @@ -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 @@ namespace caret { AnnotationRedoUndoWidget& operator=(const AnnotationRedoUndoWidget&); + const UserInputModeEnum::Enum m_userInputMode; + const int32_t m_browserWindowIndex; QAction* m_redoAction; diff --git a/src/GuiQt/AnnotationRotationWidget.cxx b/src/GuiQt/AnnotationRotationWidget.cxx index 0e2d17bd498f3267c8fff15ace1f9d20823f2110..15554f03079fd9d9a5112d23909c025750676199 100644 --- a/src/GuiQt/AnnotationRotationWidget.cxx +++ b/src/GuiQt/AnnotationRotationWidget.cxx @@ -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 @@ using namespace caret; * @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 @@ AnnotationRotationWidget::~AnnotationRotationWidget() * 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 @@ AnnotationRotationWidget::updateContent(std::vector& annotations) 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 @@ AnnotationRotationWidget::rotationValueChanged(double value) 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 --git a/src/GuiQt/AnnotationRotationWidget.h b/src/GuiQt/AnnotationRotationWidget.h index 6fb7419a6af1c8df9607af551352a9b477518ad8..bd86b35e0baebbd738fc4d4161688ab2464c1091 100644 --- a/src/GuiQt/AnnotationRotationWidget.h +++ b/src/GuiQt/AnnotationRotationWidget.h @@ -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 @@ namespace caret { 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 --git a/src/GuiQt/AnnotationSelectionViewController.cxx b/src/GuiQt/AnnotationSelectionViewController.cxx index 3fc754f3bdfb18bffd98afd3153fffea8458382a..3f1792c7ba0ff0fcfb266b53122b9c8e705d150d 100644 --- a/src/GuiQt/AnnotationSelectionViewController.cxx +++ b/src/GuiQt/AnnotationSelectionViewController.cxx @@ -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 --git a/src/GuiQt/AnnotationTextAlignmentWidget.cxx b/src/GuiQt/AnnotationTextAlignmentWidget.cxx index 8e5398dfcb69b907509197a42227f193832d723c..cda7308b3adbb908f25f1cd33ef6944d2836e3f3 100644 --- a/src/GuiQt/AnnotationTextAlignmentWidget.cxx +++ b/src/GuiQt/AnnotationTextAlignmentWidget.cxx @@ -305,7 +305,8 @@ AnnotationTextAlignmentWidget::horizontalAlignmentActionSelected(QAction* action 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 @@ AnnotationTextAlignmentWidget::verticalAlignmentActionSelected(QAction* action) 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 --git a/src/GuiQt/AnnotationTextEditorDialog.cxx b/src/GuiQt/AnnotationTextEditorDialog.cxx index a1c28fb270886e21cea04f3efd2bf83de6cd0298..fa12fee4e78ac23620a4a2cbd64b527231181a5a 100644 --- a/src/GuiQt/AnnotationTextEditorDialog.cxx +++ b/src/GuiQt/AnnotationTextEditorDialog.cxx @@ -133,7 +133,8 @@ AnnotationTextEditorDialog::textWasEdited() undoCommand->setModeTextCharacters(text, annotationVector); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(UserInputModeEnum::Enum::ANNOTATIONS, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); diff --git a/src/GuiQt/AnnotationTextEditorWidget.cxx b/src/GuiQt/AnnotationTextEditorWidget.cxx index 22e774eb10ec3e7c5e818fbd3c80b7ffde23d594..895d81f3414d12d15d286b08055c9b7ecb668920 100644 --- a/src/GuiQt/AnnotationTextEditorWidget.cxx +++ b/src/GuiQt/AnnotationTextEditorWidget.cxx @@ -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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ m_browserWindowIndex(browserWindowIndex) setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); - setMaximumWidth(200); } /** @@ -196,7 +203,8 @@ AnnotationTextEditorWidget::annotationTextChanged() 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 @@ AnnotationTextEditorWidget::annotationTextConnectTypeEnumComboBoxItemActivated() 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 --git a/src/GuiQt/AnnotationTextOrientationWidget.cxx b/src/GuiQt/AnnotationTextOrientationWidget.cxx index 3dbe7064a90a1771c7703ea9b75942dcab2e93c1..1bca87b27dbfc5db9c8670cd28fbbd812f9d086d 100644 --- a/src/GuiQt/AnnotationTextOrientationWidget.cxx +++ b/src/GuiQt/AnnotationTextOrientationWidget.cxx @@ -189,7 +189,8 @@ AnnotationTextOrientationWidget::orientationActionSelected(QAction* action) 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 --git a/src/GuiQt/AnnotationTextSubstitutionViewController.cxx b/src/GuiQt/AnnotationTextSubstitutionViewController.cxx index 0a3c71c311308d4b6187f713d06aaa731919cbfc..c77bcd2330df37738cb85b874b416dde999c2a8c 100644 --- a/src/GuiQt/AnnotationTextSubstitutionViewController.cxx +++ b/src/GuiQt/AnnotationTextSubstitutionViewController.cxx @@ -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 @@ AnnotationTextSubstitutionViewController::updateSelections() 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 --git a/src/GuiQt/AnnotationWidthHeightWidget.cxx b/src/GuiQt/AnnotationWidthHeightWidget.cxx index 0896ca317f66da268a18c3fcd48fc993d7fb36d8..36d04e468ba8df016a60c1d5123155365d8740ae 100644 --- a/src/GuiQt/AnnotationWidthHeightWidget.cxx +++ b/src/GuiQt/AnnotationWidthHeightWidget.cxx @@ -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 @@ using namespace caret; * @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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ AnnotationWidthHeightWidget::~AnnotationWidthHeightWidget() * 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 @@ AnnotationWidthHeightWidget::updateContent(std::vectorsetSuffix("%"); } + m_widthSpinBox->setEnabled(true); m_widthSpinBox->blockSignals(false); m_heightSpinBox->blockSignals(true); @@ -187,8 +242,8 @@ AnnotationWidthHeightWidget::updateContent(std::vectorgetBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(m_userInputMode, + undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); @@ -233,7 +289,7 @@ AnnotationWidthHeightWidget::heightValueChanged(double value) 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 @@ AnnotationWidthHeightWidget::widthValueChanged(double value) { 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 @@ AnnotationWidthHeightWidget::widthValueChanged(double value) 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 --git a/src/GuiQt/AnnotationWidthHeightWidget.h b/src/GuiQt/AnnotationWidthHeightWidget.h index 37c73b01b94b1f76f27db524cf13838ec3a74c36..08f2fc0bab4f834c39a2b3d1abd292a9fdde632f 100644 --- a/src/GuiQt/AnnotationWidthHeightWidget.h +++ b/src/GuiQt/AnnotationWidthHeightWidget.h @@ -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 @@ namespace caret { // ADD_NEW_METHODS_HERE - void updateContent(std::vector& annotations2D); + void updateContent(std::vector& annotations2D); private slots: void widthValueChanged(double value); @@ -62,6 +65,8 @@ namespace caret { // ADD_NEW_MEMBERS_HERE + const UserInputModeEnum::Enum m_userInputMode; + const AnnotationWidgetParentEnum::Enum m_parentWidgetType; const int32_t m_browserWindowIndex; @@ -70,7 +75,7 @@ namespace caret { QDoubleSpinBox* m_heightSpinBox; - std::vector m_annotations2D; + std::vector m_annotations2D; }; diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index cbb74f7685d1910eadb8b6ab7110117188d3c41f..d17528940d03d2e283a3c7958b7477024640b6a5 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -28,6 +28,7 @@ #include +#include "ApplicationInformation.h" #include "BalsaUserRoles.h" #include "CaretAssert.h" #include "CaretHttpManager.h" @@ -340,7 +341,7 @@ BalsaDatabaseManager::verifyUploadFileResponse(const std::map& 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 @@ BalsaDatabaseManager::isStudyIDValid(const AString& studyID) if (getAllStudyInformation(studyInformation, errorMessage)) { - for (const auto info : studyInformation) { + for (const auto& info : studyInformation) { if (info.getStudyID() == studyID) { return true; } @@ -1551,6 +1552,8 @@ BalsaDatabaseManager::uploadZippedSceneFile(SceneFile* sceneFile, return false; } + CaretLogFine("Uploading to BALSA with Version " + ApplicationInformation().getVersion()); + enum ProgressEnum { PROGRESS_NONE, PROGRESS_LOGIN, diff --git a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx index 4241e07583f1fe56425402d3a47979a91bd53c30..690406050d13551a53b53f040bdc299182d5033b 100644 --- a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx +++ b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx @@ -704,7 +704,7 @@ BalsaDatabaseUploadSceneFileDialog::loginButtonClicked() 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 @@ BalsaDatabaseUploadSceneFileDialog::selectStudyTitleButtonClicked() if ( ! bsi.isEmpty()) { m_balsaStudyIDLineEdit->setText(bsi.getStudyID()); m_balsaStudyTitleLineEdit->setText(bsi.getStudyTitle()); + m_extractDirectoryNameLineEdit->setText(bsi.getStudyExtractionDirectoryName()); } validateUploadData(); } diff --git a/src/GuiQt/BalsaStudyInformation.cxx b/src/GuiQt/BalsaStudyInformation.cxx index cebf904dcaea1f7ff797bbe302f08c31b46d6ee1..05fb51607367489e131cb2d4d25278ab26cfc84f 100644 --- a/src/GuiQt/BalsaStudyInformation.cxx +++ b/src/GuiQt/BalsaStudyInformation.cxx @@ -52,12 +52,16 @@ BalsaStudyInformation::BalsaStudyInformation() * 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 @@ BalsaStudyInformation::BalsaStudyInformation(const QJsonObject& jsonObject) && (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 @@ BalsaStudyInformation::copyHelperBalsaStudyInformation(const BalsaStudyInformati { m_studyID = obj.m_studyID; m_studyTitle = obj.m_studyTitle; + m_studyExtractionDirectoryName = obj.m_studyExtractionDirectoryName; m_editableStatus = obj.m_editableStatus; } @@ -201,6 +211,15 @@ BalsaStudyInformation::setStudyTitle(const AString& studyTitle) m_studyTitle = studyTitle; } +/** + * @return extraction directory name + */ +AString +BalsaStudyInformation::getStudyExtractionDirectoryName() const +{ + return m_studyExtractionDirectoryName; +} + /** * @return True is the study is editable. */ diff --git a/src/GuiQt/BalsaStudyInformation.h b/src/GuiQt/BalsaStudyInformation.h index 3305a54e07929f67a1ed284026cd227847b1411a..3b231dfe697804482154418157b1f99d7b7d349d 100644 --- a/src/GuiQt/BalsaStudyInformation.h +++ b/src/GuiQt/BalsaStudyInformation.h @@ -36,7 +36,8 @@ namespace caret { BalsaStudyInformation(); BalsaStudyInformation(const AString& studyID, - const AString& studyTitle); + const AString& studyTitle, + const AString& studyExtractionDirectoryName); virtual ~BalsaStudyInformation(); @@ -56,6 +57,8 @@ namespace caret { void setStudyTitle(const AString& studyTitle); + AString getStudyExtractionDirectoryName() const; + bool isEditable() const; // ADD_NEW_METHODS_HERE @@ -71,6 +74,8 @@ namespace caret { /** study title*/ AString m_studyTitle; + AString m_studyExtractionDirectoryName; + /** editable status (true is YES) */ bool m_editableStatus = false; diff --git a/src/GuiQt/BalsaStudySelectionDialog.cxx b/src/GuiQt/BalsaStudySelectionDialog.cxx index a2ea71b308a41a7e26dc70c0d5be8647694e51b8..19e4fff90c4242640a3647ceccb9d3c67a500249 100644 --- a/src/GuiQt/BalsaStudySelectionDialog.cxx +++ b/src/GuiQt/BalsaStudySelectionDialog.cxx @@ -242,8 +242,10 @@ BalsaStudySelectionDialog::newStudyButtonClicked() return; } } + QString dummyExtractionDirectoryName; m_studyInformation.emplace_back(studyID, - title); + title, + dummyExtractionDirectoryName); loadStudyTitleTableWidget(title); } else { diff --git a/src/GuiQt/BorderPropertiesEditorDialog.cxx b/src/GuiQt/BorderPropertiesEditorDialog.cxx index ad510d1062b0128a8efc52e9064477878dc3bc39..40bfa47530f8f9561bc6dfcb08c3261f2f27f509 100644 --- a/src/GuiQt/BorderPropertiesEditorDialog.cxx +++ b/src/GuiQt/BorderPropertiesEditorDialog.cxx @@ -339,7 +339,7 @@ BorderPropertiesEditorDialog::loadBorderFileComboBox() 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 --git a/src/GuiQt/BorderSelectionViewController.cxx b/src/GuiQt/BorderSelectionViewController.cxx index 862614eed860a541ce6c7133482eee4f2c9132c4..532964dd2799b7fee56d0ff43f5f9a26c06278be 100644 --- a/src/GuiQt/BorderSelectionViewController.cxx +++ b/src/GuiQt/BorderSelectionViewController.cxx @@ -207,13 +207,12 @@ BorderSelectionViewController::createAttributesWidget() "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 --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 2544151a811ccda0ccf419f9ad18ff7e3a3ef33b..b7d6e9ce5ccb9664dc46ec5c21c573bf31b68f19 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -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 @@ m_browserWindowIndex(browserWindowIndex) createMenus(); - if (s_enableMacDuplicateMenuBarFlag) { - s_enableMacDuplicateMenuBarFlag = false; - - m_toolbar->insertDuplicateMenuBar(this); - } - m_toolbar->updateToolBar(); processShowOverlayToolBox(m_overlayToolBoxAction->isChecked()); @@ -270,6 +267,8 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ BrainBrowserWindow::receiveEvent(Event* event) 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 @@ BrainBrowserWindow::receiveEvent(Event* event) } } 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 @@ BrainBrowserWindow::receiveEvent(Event* event) 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 @@ BrainBrowserWindow::createActions() 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 @@ BrainBrowserWindow::createActions() 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 @@ BrainBrowserWindow::createActions() Qt::CTRL + Qt::Key_W, this, m_toolbar, - SLOT(closeSelectedTab())); + SLOT(closeSelectedTabFromFileMenu())); m_closeWithoutConfirmationFlag = false; m_closeWindowActionConfirmTitle = "Close Window..."; @@ -1446,7 +1517,11 @@ BrainBrowserWindow::createActions() 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 @@ BrainBrowserWindow::createActions() 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 @@ BrainBrowserWindow::createActions() 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 @@ BrainBrowserWindow::createActions() 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 @@ BrainBrowserWindow::createMenus() * Create the menu bar and add menus to it. */ QMenuBar* menubar = menuBar(); + menubar->addMenu(createMenuFile()); menubar->addMenu(createMenuEdit()); @@ -1679,6 +1778,7 @@ BrainBrowserWindow::createMenuDevelop() m_developerExportVtkFileAction->setVisible(false); menu->addAction(m_developerGraphicsTimingAction); + menu->addAction(m_developerGraphicsTimingDurationAction); std::vector developerFlags; DeveloperFlagsEnum::getAllEnums(developerFlags); @@ -1714,6 +1814,8 @@ BrainBrowserWindow::createMenuDevelop() } } + menu->addAction(m_dataPaletteEditorDialogAction); + return menu; } @@ -1787,6 +1889,7 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) CaretAssert(balsaDialog); balsaDialog->show(); } +#endif /* * Update graphics and GUI @@ -1794,12 +1897,6 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) 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 @@ BrainBrowserWindow::createMenuFile() 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 @@ BrainBrowserWindow::createMenuEdit() 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 @@ BrainBrowserWindow::processEditMenuAboutToShow() 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 @@ BrainBrowserWindow::processFileMenuAboutToShow() { 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 @@ BrainBrowserWindow::isMacOptionKeyDown() const 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 @@ BrainBrowserWindow::processViewMenuAboutToShow() 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 @@ BrainBrowserWindow::processViewMenuAboutToShow() * 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 @@ BrainBrowserWindow::getTileTabsConfigurationLabelText(const TileTabsGridModeEnum 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 @@ BrainBrowserWindow::getTileTabsConfigurationLabelText(const TileTabsGridModeEnum * Event with modification information. */ void -BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsConfigurationModification* modEvent) +BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsGridConfigurationModification* modEvent) { CaretAssert(modEvent); @@ -2554,8 +2439,9 @@ BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsConfigurationModifi vpContent = m_openGLWidget->getViewportContent(); } - TileTabsConfigurationModifier modifier(vpContent, - modEvent); + TileTabsGridConfigurationModifier modifier(vpContent, + m_browserWindowIndex, + modEvent); AString errorMessage; if (! modifier.run(errorMessage)) { @@ -2573,27 +2459,6 @@ BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsConfigurationModifi modEvent->setEventProcessed(); } -/** - * 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. * @@ -2605,10 +2470,13 @@ BrainBrowserWindow::processViewTileTabsAutomaticCustomTriggered(QAction* action) { 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); @@ -2618,49 +2486,6 @@ BrainBrowserWindow::processViewTileTabsAutomaticCustomTriggered(QAction* action) EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); } -/** - * 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 @@ BrainBrowserWindow::createMenuView() 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 @@ BrainBrowserWindow::createMenuView() 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 @@ BrainBrowserWindow::createMenuSurface() { QMenu* menu = new QMenu("Surface", this); - menu->addAction("Information...", - this, - SLOT(processSurfaceMenuInformation())); + + 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."); - menu->addAction("Properties...", - this, - SLOT(processShowSurfacePropertiesDialog())); + QAction* propertiesAction = menu->addAction("Properties...", + this, + SLOT(processShowSurfacePropertiesDialog())); + propertiesAction->setToolTip("Edit surface properties including opacity and default color."); - menu->addAction("Primary Anatomical...", - this, - SLOT(processSurfaceMenuPrimaryAnatomical())); + 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,9 +2800,21 @@ BrainBrowserWindow::createMenuWindow() 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 @@ BrainBrowserWindow::createMenuHelp() 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 @@ BrainBrowserWindow::processDevelopGraphicsTiming() 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 @@ BrainBrowserWindow::processDevelopGraphicsTiming() 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 @@ BrainBrowserWindow::processDevelopExportVtkFile() 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 @@ BrainBrowserWindow::processDataFileOpen() { if (s_previousOpenFileNameFilter.isEmpty()) { s_previousOpenFileNameFilter = - DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION); + DataFileTypeEnum::toQFileDialogFilterForReading(DataFileTypeEnum::SPECIFICATION); } /* @@ -3298,26 +3179,38 @@ BrainBrowserWindow::processDataFileOpen() 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 @@ BrainBrowserWindow::processDataFileOpen() 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 @@ BrainBrowserWindow::loadFilesFromCommandLine(const std::vector& filenam loadSpecFileMode, userName, password); + + for (auto name : filenames) { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->addToRecentFilesAndOrDirectories(name); + } + } /** @@ -3430,10 +3390,13 @@ BrainBrowserWindow::loadFilesFromCommandLine(const std::vector& filenam * @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 @@ BrainBrowserWindow::loadSceneFromCommandLine(const AString& sceneFileName, } 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 @@ BrainBrowserWindow::loadSceneFromCommandLine(const AString& sceneFileName, /* 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 @@ BrainBrowserWindow::loadFiles(QWidget* parentForDialogs, specFileName = fileInfo.getAbsoluteFilePath(); } if (fileInfo.exists()) { - SessionManager::get()->getCaretPreferences()->addToPreviousSpecFiles(specFileName); + SessionManager::get()->getCaretPreferences()->addToRecentFilesAndOrDirectories(specFileName); } specFile.readFile(specFileName); @@ -3841,8 +3820,6 @@ BrainBrowserWindow::loadFiles(QWidget* parentForDialogs, GuiManager::get()->processShowSceneDialog(this); } - EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); - showDataFileReadWarningsDialog(); return successFlag; @@ -4057,6 +4034,17 @@ BrainBrowserWindow::processDuplicateTab() m_toolbar->addNewDuplicatedTab(previousTabContent); } +/** + * 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. */ @@ -4103,7 +4091,7 @@ BrainBrowserWindow::processMoveSelectedTabToWindowMenuAboutToBeDisplayed() 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 @@ BrainBrowserWindow::processMoveSelectedTabToWindowMenuAboutToBeDisplayed() 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); } } @@ -4426,6 +4414,20 @@ BrainBrowserWindow::getBrowerWindowContent() const return m_browserWindowContent; } +/** + * 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 @@ -4467,6 +4469,16 @@ BrainBrowserWindow::processHcpWebsiteInBrowser() QDesktopServices::openUrl(url); } +/** + * 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. */ @@ -4974,17 +4986,3 @@ BrainBrowserWindow::showDataFileReadWarningsDialog() 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 --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index 67dfd4ece040fa0646b97ebdfc733b4f1ab8245c..6b3f1ff95015aba33f9244c3eebc72d654bb03d4 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -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 @@ namespace caret { 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 @@ namespace caret { void removeAndReturnAllTabs(std::vector& allTabContent); + void getAllBrainOpenGLViewportContent(std::vector& viewportContentOut) const; + int32_t getBrowserWindowIndex() const; bool isTileTabsSelected() const; @@ -99,11 +103,24 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { void processDuplicateTab(); void processDataFileLocationOpen(); void processDataFileOpen(); + void processOpenRecent(); void processManageSaveLoadedFiles(); void processCaptureImage(); void processMovieRecording(); @@ -185,8 +199,6 @@ namespace caret { void processShowIdentifyBrainordinateDialog(); void processGapsAndMargins(); - void processViewTileTabsLoadUserConfigurationMenuAboutToShow(); - void processViewTileTabsLoadUserConfigurationMenuItemTriggered(QAction* action); void processViewTileTabsAutomaticCustomTriggered(QAction* action); @@ -202,12 +214,6 @@ namespace caret { 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 @@ namespace caret { void processFileMenuAboutToShow(); void processDataMenuAboutToShow(); void processViewMenuAboutToShow(); + void processWindowMenuAboutToShow(); void processSurfaceMenuInformation(); void processSurfaceMenuPrimaryAnatomical(); @@ -224,6 +231,7 @@ namespace caret { void processConnectToConnectomeDataBase(); void processHcpWebsiteInBrowser(); + void processHcpUsersGroup(); void processHcpFeatureRequestWebsiteInBrowser(); void processReportWorkbenchBug(); @@ -231,7 +239,8 @@ namespace caret { void processShowVolumePropertiesDialog(); void processDevelopGraphicsTiming(); - + void processDevelopGraphicsTimingDuration(); + void processDevelopExportVtkFile(); void developerMenuAboutToShow(); void developerMenuFlagTriggered(QAction*); @@ -297,7 +306,6 @@ namespace caret { QMenu* createMenuView(); QMenu* createMenuViewMoveOverlayToolBox(); QMenu* createMenuViewMoveFeaturesToolBox(); - QMenu* createMenuViewTileTabsLoadUserConfiguration(); QMenu* createMenuConnect(); QMenu* createMenuData(); QMenu* createMenuSurface(); @@ -335,7 +343,7 @@ namespace caret { void saveBrowserWindowContentForScene(); - void modifyTileTabsConfiguration(EventTileTabsConfigurationModification* modEvent); + void modifyTileTabsConfiguration(EventTileTabsGridConfigurationModification* modEvent); /** Index of this window */ const int32_t m_browserWindowIndex; @@ -354,10 +362,14 @@ namespace caret { 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 @@ namespace caret { QMenu* m_viewMoveFeaturesToolBoxMenu; QMenu* m_viewMoveOverlayToolBoxMenu; - QMenu* m_viewTileTabsLoadUserConfigurationMenu; QAction* m_viewFullScreenAction; QAction* m_viewTileTabsAction; @@ -391,7 +402,8 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/BrainBrowserWindowComboBox.cxx b/src/GuiQt/BrainBrowserWindowComboBox.cxx index 8b26251ceba9f194d029c79f3f493b9c064cc1f5..11fec0481b64d1e905b90bc915a1417211b2a901 100644 --- a/src/GuiQt/BrainBrowserWindowComboBox.cxx +++ b/src/GuiQt/BrainBrowserWindowComboBox.cxx @@ -108,7 +108,7 @@ BrainBrowserWindowComboBox::updateComboBox() break; } m_comboBox->setItemData(i, - qVariantFromValue((void*)bbw)); + QVariant::fromValue((void*)bbw)); if (bbw == selectedWindow) { defaultIndex = i; diff --git a/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx b/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx index bb7f994dc9f6ecc25e562a00428c04baedf13930..a49629a905a490cfd46896c05503c369eb1400b6 100644 --- a/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx +++ b/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx @@ -126,6 +126,11 @@ BrainBrowserWindowEditMenuItemEnum::initialize() "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 --git a/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h b/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h index 264c7c3abbba018a8b9191b35325f68076174845..402c2fd56aa09058ea1f3241baedd3298c444c7a 100644 --- a/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h +++ b/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h @@ -42,6 +42,8 @@ public: CUT, /** Delete (note: 'DELETE' is reserved word on Windows) */ DELETER, + /** Deselect All */ + DESELECT_ALL, /** Paste */ PASTE, /** Paste Special */ diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index f7ee1e2c7bd3cd5aa6b47b077a77ab40b28a3ad2..69cd678fd91d51c85d10929c6a9b6d28bc44ba91 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -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 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 m_fociSelectionViewController = NULL; m_imageSelectionViewController = NULL; m_labelSelectionViewController = NULL; + m_mediaSelectionViewController = NULL; m_overlaySetViewController = NULL; m_volumeSurfaceOutlineSetViewController = NULL; @@ -183,6 +189,7 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 m_fociTabIndex = -1; m_imageTabIndex = -1; m_labelTabIndex = -1; + m_mediaTabIndex = -1; m_overlayTabIndex = -1; m_volumeSurfaceOutlineTabIndex = -1; @@ -276,6 +283,15 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 "Labels"); } + 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, @@ -284,8 +300,26 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 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 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 } else { if (isOverlayToolBox) { - setMinimumWidth(300); + setMinimumWidth(100); setMaximumWidth(800); } else { @@ -316,6 +350,59 @@ BrainBrowserWindowOrientedToolBox::~BrainBrowserWindowOrientedToolBox() EventManager::get()->removeAllEventsFromListener(this); } +/** + * @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 @@ -490,6 +577,41 @@ BrainBrowserWindowOrientedToolBox::saveToScene(const SceneAttributes* sceneAttri 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 @@ BrainBrowserWindowOrientedToolBox::restoreFromScene(const SceneAttributes* scene } #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 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) break; case DataFileTypeEnum::VOLUME_DYNAMIC: haveConnFiles = true; -// haveVolumes = true; -// const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); -// if (volDynConnFile != NULL) { -// haveConnFiles = true; -// } break; } } @@ -778,6 +907,7 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) */ int defaultTabIndex = -1; bool enableLayers = true; + bool enableMedia = false; bool enableVolumeSurfaceOutline = false; bool enableChartOne = false; bool enableChartTwo = false; @@ -788,6 +918,16 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) 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 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) 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 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) */ 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 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) 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 --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.h b/src/GuiQt/BrainBrowserWindowOrientedToolBox.h index b051b2b389c35c62327d5060a23312eddfcdaaf0..b99f199d26f041270feacaef2d127e9ae660621c 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.h +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.h @@ -28,6 +28,7 @@ #include "EventListenerInterface.h" #include "SceneableInterface.h" +class QSplitter; class QTabWidget; namespace caret { @@ -39,8 +40,10 @@ namespace caret { class CiftiConnectivityMatrixViewController; class FiberOrientationSelectionViewController; class FociSelectionViewController; + class IdentificationDisplayWidget; class ImageSelectionViewController; class LabelSelectionViewController; + class MediaOverlaySetViewController; class OverlaySetViewController; class VolumeSurfaceOutlineSetViewController; class WuQTabWidgetWithSizeHint; @@ -89,6 +92,8 @@ namespace caret { int addToTabWidget(QWidget* page, const QString& label); + QWidget* createSplitterAndIdentificationWidget(const Qt::Orientation orientation); + OverlaySetViewController* m_overlaySetViewController; AnnotationSelectionViewController* m_annotationViewController; @@ -111,6 +116,8 @@ namespace caret { LabelSelectionViewController* m_labelSelectionViewController; + MediaOverlaySetViewController* m_mediaSelectionViewController; + VolumeSurfaceOutlineSetViewController* m_volumeSurfaceOutlineSetViewController; WuQTabWidgetWithSizeHint* m_tabWidget; @@ -141,8 +148,14 @@ namespace caret { 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 --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index f1fffa402c594d459c66ae7dc4ebc93493c231de..daa059dc52a312bfdef3b9d1a5c8927172e6e2d0 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -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 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, const QString& objectNamePrefix, BrainBrowserWindow* parentBrainBrowserWindow) -: QToolBar(parentBrainBrowserWindow) +: QToolBar(parentBrainBrowserWindow), +m_parentBrainBrowserWindow(parentBrainBrowserWindow) { this->browserWindowIndex = browserWindowIndex; m_tabIndexForTileTabsHighlighting = -1; @@ -170,17 +196,6 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow 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 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow 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,33 +526,26 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow 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. */ @@ -510,7 +554,6 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow 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 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow } } + /* + * 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 @@ BrainBrowserWindowToolBar::~BrainBrowserWindowToolBar() { 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; } /** @@ -750,6 +742,42 @@ BrainBrowserWindowToolBar::addNewTabWithContent(BrowserTabContent* tabContent) -1); } +/** + * 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 @@ BrainBrowserWindowToolBar::replaceBrowserTabs(const std::vectortabBar->count(); for (int32_t iTab = 0; iTab < beforeNumTabs; iTab++) { this->tabBar->setTabData(iTab, - qVariantFromValue((void*)NULL)); + QVariant::fromValue((void*)NULL)); } /* @@ -806,7 +834,7 @@ BrainBrowserWindowToolBar::replaceBrowserTabs(const std::vectortabBar->setTabData(iTab, - qVariantFromValue((void*)btc)); + QVariant::fromValue((void*)btc)); this->updateTabName(iTab); } @@ -825,11 +853,14 @@ BrainBrowserWindowToolBar::replaceBrowserTabs(const std::vector= 0){ @@ -877,6 +910,8 @@ BrainBrowserWindowToolBar::insertNewTabAtTabBarIndex(const int32_t tabBarIndex) 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 @@ BrainBrowserWindowToolBar::insertTabContentPrivate(const InsertTabMode insertTab } 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 @@ BrainBrowserWindowToolBar::allowAddingNewTab() 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 @@ BrainBrowserWindowToolBar::allowAddingNewTab() } /* - * 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 @@ BrainBrowserWindowToolBar::allowAddingNewTab() 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 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() ModelChart* chartModel = NULL; ModelChartTwo* chartTwoModel = NULL; + ModelMedia* mediaModel(NULL); ModelSurfaceMontage* surfaceMontageModel = NULL; ModelVolume* volumeModel = NULL; ModelWholeBrain* wholeBrainModel = NULL; @@ -1134,14 +1173,17 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() 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 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() 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 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() rightSurfaceModel); tabIndex = loadIntoTab(tabIndex, cerebellumSurfaceModel); + tabIndex = loadIntoTab(tabIndex, + mediaModel); const int numTabs = this->tabBar->count(); if (numTabs > 0) { @@ -1247,6 +1294,8 @@ BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() 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 @@ BrainBrowserWindowToolBar::moveTabsToNewWindows() } 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 @@ BrainBrowserWindowToolBar::removeAndReturnAllTabs(std::vectortabBar->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 @@ BrainBrowserWindowToolBar::removeTabWithContent(BrowserTabContent* browserTabCon 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 @@ BrainBrowserWindowToolBar::updateTabName(const int32_t tabIndex) * called by the BrowswerWindow's File Menu. */ void -BrainBrowserWindowToolBar::closeSelectedTab() +BrainBrowserWindowToolBar::closeSelectedTabFromFileMenu() { tabCloseSelected(this->tabBar->currentIndex()); } @@ -1611,9 +1663,8 @@ BrainBrowserWindowToolBar::selectedTabChanged(int indx) 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 @@ BrainBrowserWindowToolBar::resetTabIndexForTileTabsHighlighting() * 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 @@ BrainBrowserWindowToolBar::tabCloseSelected(int tabIndex) { if ((tabIndex >= 0) && (tabIndex < this->tabBar->count())) { - tabClosed(tabIndex); + tabClosed(tabIndex, + RemoveTabMode::CLOSE_TAB_CONTENT_FOR_REOPENING); } this->updateGraphicsWindow(); } @@ -1715,12 +1776,16 @@ BrainBrowserWindowToolBar::tabCloseSelected(int tabIndex) * * @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(); @@ -1729,6 +1794,16 @@ BrainBrowserWindowToolBar::tabClosed(int tabIndex) } } +/** + * Emit the viewedModelChanged() signal + * This method is used by friend classes + */ +void +BrainBrowserWindowToolBar::emitViewModelChangedSignal() +{ + emit viewedModelChanged(); +} + /** * Called when a tab has been moved. * @@ -1746,9 +1821,13 @@ BrainBrowserWindowToolBar::tabMoved(int /*from*/, int /*to*/) /** * 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 @@ BrainBrowserWindowToolBar::removeTab(int tabIndex) 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 @@ BrainBrowserWindowToolBar::updateToolBar() 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 @@ BrainBrowserWindowToolBar::updateToolBar() 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 @@ BrainBrowserWindowToolBar::updateToolBar() 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 @@ BrainBrowserWindowToolBar::updateToolBar() 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 @@ BrainBrowserWindowToolBar::updateToolBar() 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 @@ BrainBrowserWindowToolBar::updateToolBar() } } - /* - * 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 @@ BrainBrowserWindowToolBar::updateToolBarComponents(BrowserTabContent* browserTab 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 @@ BrainBrowserWindowToolBar::updateToolBarComponents(BrowserTabContent* browserTab 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 @@ BrainBrowserWindowToolBar::updateViewWidget(BrowserTabContent* browserTabContent 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 @@ BrainBrowserWindowToolBar::updateViewWidget(BrowserTabContent* browserTabContent 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,149 +2302,7 @@ BrainBrowserWindowToolBar::updateOrientationWidget(BrowserTabContent* browserTab 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); + m_orientationToolBarComponent->updateContent(browserTabContent); } /** @@ -2601,217 +2313,11 @@ BrainBrowserWindowToolBar::updateOrientationWidget(BrowserTabContent* browserTab 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_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 @@ BrainBrowserWindowToolBar::updateWholeBrainSurfaceOptionsWidget(BrowserTabConten 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 @@ BrainBrowserWindowToolBar::createModeWidget() /* * 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 @@ BrainBrowserWindowToolBar::createModeWidget() "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); - - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeAnnotationsAction, + 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->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 @@ BrainBrowserWindowToolBar::createModeWidget() * 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(); + const UserInputModeEnum::Enum currentMode = this->selectedUserInputProcessor->getUserInputMode(); - UserInputModeEnum::Enum inputMode = UserInputModeEnum::INVALID; + UserInputModeEnum::Enum inputMode = UserInputModeEnum::Enum::INVALID; - if (action == this->modeInputModeAnnotationsAction) { - if (currentMode != UserInputModeEnum::ANNOTATIONS) { - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); - CaretAssert(bbw); - if ( ! bbw->changeInputModeToAnnotationsWarningDialog()) { + 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 @@ BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) 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 @@ BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) 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 ((button == this->modeInputModeImageRadioButton) + && (this->modeInputModeImageRadioButton != NULL)) { + inputMode = UserInputModeEnum::Enum::IMAGE; } - else if ((action == this->modeInputModeImageAction) - && (this->modeInputModeImageAction != NULL)) { - inputMode = UserInputModeEnum::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 @@ BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) 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 @@ BrainBrowserWindowToolBar::updateModeWidget(BrowserTabContent* /*browserTabConte 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::VIEW: - this->modeInputModeViewAction->setChecked(true); + case UserInputModeEnum::Enum::VOLUME_EDIT: + this->modeInputVolumeEditRadioButton->setChecked(true); + break; + case UserInputModeEnum::Enum::VIEW: + this->modeInputModeViewRadioButton->setChecked(true); break; } - + this->modeWidgetGroup->blockAllSignals(false); this->updateDisplayedModeUserInputWidget(); @@ -3226,65 +2663,22 @@ BrainBrowserWindowToolBar::updateModeWidget(BrowserTabContent* /*browserTabConte 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 @@ BrainBrowserWindowToolBar::createTabOptionsWidget(QToolButton* toolBarLockWindow 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 @@ BrainBrowserWindowToolBar::createTabOptionsWidget(QToolButton* toolBarLockWindow void BrainBrowserWindowToolBar::updateTabOptionsWidget(BrowserTabContent* browserTabContent) { - if (this->windowWidget->isHidden()) { + if (this->tabMiscWidget->isHidden()) { return; } @@ -3365,42 +2759,6 @@ BrainBrowserWindowToolBar::updateChartTypeWidget(BrowserTabContent* browserTabCo m_chartTypeToolBarComponent->updateContent(browserTabContent); } -/** - * 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. * @@ -3579,18 +2937,18 @@ BrainBrowserWindowToolBar::updateChartTwoAttributesWidget(BrowserTabContent* bro } /** - * 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 @@ BrainBrowserWindowToolBar::createChartTwoAxesWidget() } /** - * 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 @@ BrainBrowserWindowToolBar::updateChartTwoAxesWidget(BrowserTabContent* browserTa 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 @@ BrainBrowserWindowToolBar::updateSingleSurfaceOptionsWidget(BrowserTabContent* b return; } - this->singleSurfaceSelectionWidgetGroup->blockAllSignals(true); - - this->surfaceSurfaceSelectionControl->updateControl(browserTabContent->getSurfaceModelSelector()); - - this->singleSurfaceSelectionWidgetGroup->blockAllSignals(false); + m_surfaceToolBarComponent->updateContent(browserTabContent); } /** @@ -3712,40 +3038,6 @@ BrainBrowserWindowToolBar::updateSurfaceMontageOptionsWidget(BrowserTabContent* m_surfaceMontageToolBarComponent->updateContent(browserTabContent); } -/** - * @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. * @@ -3819,77 +3111,134 @@ BrainBrowserWindowToolBar::updateVolumePlaneWidget(BrowserTabContent* browserTab } /** - * 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()); + } - QWidget* w = new QWidget(); - QGridLayout* layout = new QGridLayout(w); - layout->setColumnStretch(0, 100); - layout->setColumnStretch(1, 100); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); + 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); + + 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; + } + CaretAssert(rowWidget >= 0); + CaretAssert(rowCount >= 1); + + QWidget* w = new QWidget(); + QGridLayout* layout = new QGridLayout(w); + layout->setContentsMargins(0, 0, 0, 0); + if (columnVerticalBar >= 0) { + layout->setHorizontalSpacing(2); } - layout->addWidget(nameLabel, 2, 0, 1, 2, Qt::AlignHCenter); + else { + layout->setHorizontalSpacing(0); + } + layout->setVerticalSpacing(0); - const bool addVerticalBarOnLeftSide = (verticalBarPlacement == WIDGET_PLACEMENT_LEFT); - const bool addVerticalBarOnRightSide = (verticalBarPlacement == WIDGET_PLACEMENT_RIGHT); + 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); + } - 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; + 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,179 +3270,25 @@ BrainBrowserWindowToolBar::updateToolBox() 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; - } - - 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 { - btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_INVALID); - } - - this->updateToolBar(); - this->updateTabName(-1); - this->updateToolBox(); - emit viewedModelChanged(); - this->updateGraphicsWindow(); -} - -/** - * 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. - */ -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() -{ - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->readCustomViews(); - const std::vector > customViewNameAndComments = prefs->getCustomViewNamesAndComments(); + 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(); QMenu menu; @@ -4113,8 +3308,8 @@ BrainBrowserWindowToolBar::customViewActionTriggered() QAction* selectedAction = menu.exec(QCursor::pos()); if (selectedAction != NULL) { if (selectedAction == editAction) { - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); - GuiManager::get()->processShowCustomViewDialog(bbw); + CaretAssert(m_parentBrainBrowserWindow); + GuiManager::get()->processShowCustomViewDialog(m_parentBrainBrowserWindow); } else { const AString customViewName = selectedAction->text(); @@ -4130,362 +3325,76 @@ BrainBrowserWindowToolBar::customViewActionTriggered() } /** - * Called when the whole brain surface type combo box is changed. + * Receive events from the event manager. + * + * @param event + * Event sent by event manager. */ void -BrainBrowserWindowToolBar::wholeBrainSurfaceTypeComboBoxIndexChanged(int /*indx*/) +BrainBrowserWindowToolBar::receiveEvent(Event* event) { - 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(); + 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 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(); -} - -/** - * 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); -} + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE_IN_TOOL_BAR) { + EventBrowserTabDeleteInToolBar* deleteTabInGuiEvent = dynamic_cast(event); + CaretAssert(deleteTabInGuiEvent); -/** - * 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 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) 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 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) 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 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) 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,11 +3561,163 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) 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. * @@ -4829,7 +3924,8 @@ BrainBrowserWindowToolBar::restoreFromScene(const SceneAttributes* sceneAttribut */ 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 --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 99d3516dfea1fca203b25d0152bfe5e45580a31e..2093791bb03c06b01533661ca3001ded240de6db 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -26,6 +26,7 @@ #include #include +#include "BrainBrowserWindowToolBarOrientation.h" #include "EnumComboBoxTemplate.h" #include "EventListenerInterface.h" #include "ModelTypeEnum.h" @@ -51,22 +52,25 @@ class QVBoxLayout; 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 @@ namespace caret { 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 @@ namespace caret { ~BrainBrowserWindowToolBar(); - void addNewTab(); + BrowserTabContent* addNewTab(); + + void reopenLastClosedTab(EventBrowserTabReopenClosed& reopenTabEvent); void addNewDuplicatedTab(BrowserTabContent* browserTabContentToBeCloned); @@ -105,18 +119,12 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/BrainBrowserWindowToolBarAllSurface.cxx b/src/GuiQt/BrainBrowserWindowToolBarAllSurface.cxx new file mode 100644 index 0000000000000000000000000000000000000000..253c3df670e2db8180721106996290e641f641d9 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarAllSurface.cxx @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarAllSurface.h b/src/GuiQt/BrainBrowserWindowToolBarAllSurface.h new file mode 100644 index 0000000000000000000000000000000000000000..47299cd329750fdc0c65bcaeda89afa687410761 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarAllSurface.h @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx index b31006b28efac0ba8a8498ad0c7f84d036f1a5fb..9b3ca1364ed61ea8e7922be6f3a429fca9c36145 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx @@ -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 @@ BrainBrowserWindowToolBarChartTwoAttributes::updateContent(BrowserTabContent* br 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 @@ MatrixChartTwoAttributesWidget::MatrixChartTwoAttributesWidget(BrainBrowserWindo 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 @@ EventListenerInterface() 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 @@ MatrixChartTwoAttributesWidget::updateContent() { 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 @@ MatrixChartTwoAttributesWidget::valueChanged() { 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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h index f1f446f02760f920253e199fed07b00bb5062f4f..b34bd6e092ce87467482bca7699bbb8e4bf9a6ea 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h @@ -121,16 +121,9 @@ namespace caret { 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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx deleted file mode 100644 index b7818b1080576a7c2ea224841e4635eb8dd1e686..0000000000000000000000000000000000000000 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx +++ /dev/null @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5333551e27ea470fe692f3a0d827609f5f5c8717 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.cxx @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.h new file mode 100644 index 0000000000000000000000000000000000000000..4b770a28478419b05534c582d1efc43cd7a8c04b --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientedAxes.h @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx deleted file mode 100644 index 88e04dba1a5aaa9cde431cf76633f1c64867f9e6..0000000000000000000000000000000000000000 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx +++ /dev/null @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx index a19c7b36861ca0ae5f80a19df6c05dae51787df8..f12074d2ae679239505af0a11f906807a13396d3 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx @@ -73,8 +73,7 @@ m_parentToolBar(parentToolBar) 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 --git a/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx b/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx deleted file mode 100644 index fa3535807318f828b22f0fac185a57996c6ba0ed..0000000000000000000000000000000000000000 --- a/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx +++ /dev/null @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarClipping.h b/src/GuiQt/BrainBrowserWindowToolBarClipping.h deleted file mode 100644 index a76376fca5c7107b4ab218bae7b65f8259191397..0000000000000000000000000000000000000000 --- a/src/GuiQt/BrainBrowserWindowToolBarClipping.h +++ /dev/null @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx b/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx index 3ac368a5211caaa1a13c2ff4128f0cccb4567700..c588d7cadc91e15987d41b5484150a3adaefa8bd 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx @@ -155,4 +155,12 @@ BrainBrowserWindowToolBarComponent::updateUserInterface() m_parentToolBar->updateUserInterface(); } +/** + * @return Parent window toolbar + */ +BrainBrowserWindowToolBar* +BrainBrowserWindowToolBarComponent::getParentToolBar() +{ + return m_parentToolBar; +} diff --git a/src/GuiQt/BrainBrowserWindowToolBarComponent.h b/src/GuiQt/BrainBrowserWindowToolBarComponent.h index 7c7763257cb0f4f5bfd06ac9f4ca001d6bd7ba78..6abbcfedde2530a96587cd53f5862e026f8d0c73 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarComponent.h +++ b/src/GuiQt/BrainBrowserWindowToolBarComponent.h @@ -65,6 +65,8 @@ namespace caret { void blockAllSignals(const bool status); + BrainBrowserWindowToolBar* getParentToolBar(); + public: // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/BrainBrowserWindowToolBarOrientation.cxx b/src/GuiQt/BrainBrowserWindowToolBarOrientation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..06bc3c456bd8474d440d403ab11db362ed8df32d --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarOrientation.cxx @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarOrientation.h b/src/GuiQt/BrainBrowserWindowToolBarOrientation.h new file mode 100644 index 0000000000000000000000000000000000000000..81cd9857f6de58dc5ad04feb03ea293ec447c29b --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarOrientation.h @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index cf9eb4754c9e68be77adb4c15f069fd4c1ee4e34..2753ba602652baae6d3ff84e81fc20d3eb83dee1 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -360,37 +360,13 @@ BrainBrowserWindowToolBarSliceSelection::updateContent(BrowserTabContent* browse } 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 --git a/src/GuiQt/BrainBrowserWindowToolBarSurface.cxx b/src/GuiQt/BrainBrowserWindowToolBarSurface.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2c5f5e26bd8795f0a772da1ac159389166f41c83 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarSurface.cxx @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarSurface.h b/src/GuiQt/BrainBrowserWindowToolBarSurface.h new file mode 100644 index 0000000000000000000000000000000000000000..b73a5a8e65b32da0e885ee05b2d59ff479972be8 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarSurface.h @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx b/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx index c04f57c2dbdcae1539b1d794bc775cd329a04808..03537f217944687caa29c9d6289f10ef5712e472 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx index 9f31eec1b7796a3805b4c32c7a563da8a5857fc7..62ba0b877565a02f46d6146defd4c66859e36dea 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx @@ -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_browserWindowIndex(browserWindowIndex), 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 @@ m_lockWindowAndAllTabAspectButton(toolBarLockWindowAndAllTabAspectRatioButton) "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 @@ BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent blockAllSignals(true); bool chartFlag = false; + bool mediaFlag(false); switch (browserTabContent->getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_CHART: chartFlag = true; @@ -150,6 +239,9 @@ BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent 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 @@ BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent } 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 @@ BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent break; } + m_clippingPlanesAction->setChecked(browserTabContent->isClippingPlanesEnabled()); + m_scaleBarAction->setChecked(browserTabContent->getScaleBar()->isDisplayed()); + blockAllSignals(false); } @@ -225,6 +326,9 @@ BrainBrowserWindowToolBarTab::yokeToGroupComboBoxIndexChanged() 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 @@ BrainBrowserWindowToolBarTab::yokeToGroupComboBoxIndexChanged() 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 --git a/src/GuiQt/BrainBrowserWindowToolBarTab.h b/src/GuiQt/BrainBrowserWindowToolBarTab.h index 9d5c2f37c7e8b124b5e86fc587437ba7ae26fa5a..70d20242ce614e35469352a4e6ff643cebb55ecf 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.h +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx index f2df57ec92072e6774a2d5c9d7d6904b61ed69ea..863601d75c0bc39b06e9fc4172a46bca33d1ae62 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx @@ -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_tabIndexUnderMouse(tabIndexUnderMouse) 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 @@ BrainBrowserWindowToolBarTabPopUpMenu::~BrainBrowserWindowToolBarTabPopUpMenu() * 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 @@ BrainBrowserWindowToolBarTabPopUpMenu::addItem(const MenuItem menuItem) 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 @@ BrainBrowserWindowToolBarTabPopUpMenu::addItem(const MenuItem menuItem) break; } + if ( ! overrideMenuItemText.isEmpty()) { + text = overrideMenuItemText; + } + QAction* action = addAction(text); action->setData(static_cast(menuItem)); action->setEnabled(isEnabled(menuItem)); @@ -168,6 +226,9 @@ void 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 @@ BrainBrowserWindowToolBarTabPopUpMenu::menuItemSelected(QAction* action) 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 @@ BrainBrowserWindowToolBarTabPopUpMenu::menuItemSelected(QAction* action) 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 @@ BrainBrowserWindowToolBarTabPopUpMenu::isEnabled(const MenuItem menuItem) const 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 --git a/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h index 60ed6a911b23264eabc1f0e433e037e69edbd6c1..a4efbb6bc4200cb90c3d96baa4d80aad8fe2208d 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h +++ b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.h @@ -29,6 +29,7 @@ namespace caret { + class AnnotationBrowserTab; class BrainBrowserWindowToolBar; class BrainBrowserWindowToolBarTabPopUpMenu : public QMenu { @@ -50,6 +51,8 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { int32_t m_activeTabIndex; + int32_t m_browserTabContentIndex = -1; + + int32_t m_browserWindowIndex = -1; + + AnnotationBrowserTab* m_selectedBrowserTabAnnotation = NULL; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/BrainBrowserWindowToolBarView.cxx b/src/GuiQt/BrainBrowserWindowToolBarView.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e458fb68d08ce531a421fbff6cd9934875801ad6 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarView.cxx @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarView.h b/src/GuiQt/BrainBrowserWindowToolBarView.h new file mode 100644 index 0000000000000000000000000000000000000000..ff7836be028e233464e6b1291d45ea09434ab657 --- /dev/null +++ b/src/GuiQt/BrainBrowserWindowToolBarView.h @@ -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 --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 71c19edcea107062c63b51db507fd3e70c96b3d4..640614215304395a86488b1b2841454233b2267a 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -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 @@ windowIndex(windowIndex) + AString::number(windowIndex + 1) + ":OpenGLWidget"); - this->borderBeingDrawn = new Border(); - m_mousePositionValid = false; m_mousePositionEvent.grabNew(new MouseEvent(NULL, NULL, @@ -144,17 +149,9 @@ windowIndex(windowIndex) 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 @@ windowIndex(windowIndex) 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 @@ windowIndex(windowIndex) #endif s_brainOpenGLWidgets.insert(this); + + /* + * Get pinch gesture event + */ + grabGesture(Qt::PinchGesture); } /** @@ -226,16 +228,6 @@ BrainOpenGLWidget::~BrainOpenGLWidget() 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); @@ -423,15 +415,6 @@ BrainOpenGLWidget::resizeGL(int w, int h) this->windowHeight[this->windowIndex] = h; } -/** - * @return Pointer to the border that is being drawn. - */ -Border* -BrainOpenGLWidget::getBorderBeingDrawn() -{ - return this->borderBeingDrawn; -} - /** * Clear the contents for drawing into the viewports. */ @@ -450,7 +433,7 @@ BrainOpenGLWidget::updateCursor() /* * 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 @@ BrainOpenGLWidget::performOffScreenImageCapture(const int32_t imageWidth, windowContent); s_singletonOpenGL->drawModels(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + getSelectedInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, windowContent.getAllTabViewports()); @@ -537,6 +520,13 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], 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 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], } } + 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 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], 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 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], getModelEvent.getSelectedBrowserTabContent(), gapsAndMargins, this->windowIndex, + windowViewportBeforeAspectLocking, windowViewport); windowContent.addTabViewport(vc); } @@ -627,6 +672,7 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], NULL, gapsAndMargins, windowIndex, + windowViewportBeforeAspectLocking, windowViewport); windowContent.setWindowViewport(windowViewportContent); } @@ -658,14 +704,18 @@ BrainOpenGLWidget::paintGL() 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 @@ BrainOpenGLWidget::paintGL() 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 @@ BrainOpenGLWidget::event(QEvent* event) 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 @@ BrainOpenGLWidget::event(QEvent* event) #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 @@ BrainOpenGLWidget::contextMenuEvent(QContextMenuEvent* contextMenuEvent) const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { + std::vector emptyHistoryXY; MouseEvent mouseEvent(viewportContent, this, this->windowIndex, @@ -761,11 +926,14 @@ BrainOpenGLWidget::contextMenuEvent(QContextMenuEvent* contextMenuEvent) 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 @@ BrainOpenGLWidget::wheelEvent(QWheelEvent* we) const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, wheelY); if (viewportContent != NULL) { + std::vector emptyHistoryXY; MouseEvent mouseEvent(viewportContent, this, this->windowIndex, @@ -864,10 +1033,14 @@ BrainOpenGLWidget::wheelEvent(QWheelEvent* we) 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 @@ BrainOpenGLWidget::keyPressEvent(QKeyEvent* e) 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 @@ BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) else { this->mousePressX = -10000; this->mousePressY = -10000; + m_mouseHistoryXY.clear(); } me->accept(); @@ -1082,6 +1285,8 @@ BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) 0, this->mousePressX, this->mousePressY, + m_mouseHistoryXY, this->mouseNewDraggingStartedFlag); - this->selectedUserInputProcessor->mouseLeftRelease(mouseEvent); + inputProcessor->mouseLeftRelease(mouseEvent); } } @@ -1123,17 +1329,18 @@ BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) this->mousePressX = -10000; this->mousePressY = -10000; + m_mouseHistoryXY.clear(); this->isMousePressedNearToolBox = false; me->accept(); @@ -1181,6 +1389,7 @@ BrainOpenGLWidget::mouseDoubleClickEvent(QMouseEvent* me) const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { + std::vector emptyHistoryXY; MouseEvent mouseEvent(viewportContent, this, this->windowIndex, @@ -1190,9 +1399,10 @@ BrainOpenGLWidget::mouseDoubleClickEvent(QMouseEvent* me) 0, this->mousePressX, this->mousePressY, + emptyHistoryXY, this->mouseNewDraggingStartedFlag); - this->selectedUserInputProcessor->mouseLeftDoubleClick(mouseEvent); + getSelectedInputProcessor()->mouseLeftDoubleClick(mouseEvent); } } } @@ -1213,7 +1423,7 @@ BrainOpenGLWidget::leaveEvent(QEvent* /*e*/) { m_mousePositionValid = false; - this->selectedUserInputProcessor->setMousePosition(m_mousePositionEvent, + getSelectedInputProcessor()->setMousePosition(m_mousePositionEvent, m_mousePositionValid); } @@ -1242,6 +1452,30 @@ BrainOpenGLWidget::getViewportContentAtXY(const int x, return m_windowContent.getWindowViewport(); } +/** + * 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 @@ -1290,7 +1524,7 @@ BrainOpenGLWidget::performIdentification(const int x, if (idViewport != NULL) { s_singletonOpenGL->selectModel(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + getSelectedInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, idViewport, @@ -1335,8 +1569,27 @@ SelectionItemAnnotation* 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 @@ BrainOpenGLWidget::performIdentificationAnnotations(const int x, 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 @@ BrainOpenGLWidget::performIdentificationVoxelEditing(VolumeFile* editingVolumeFi 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 @@ BrainOpenGLWidget::performProjection(const int x, if (projectionViewport != NULL) { s_singletonOpenGL->projectToModel(this->windowIndex, - this->selectedUserInputProcessor->getUserInputMode(), + getSelectedInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, projectionViewport, @@ -1526,6 +1779,8 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) 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 @@ BrainOpenGLWidget::receiveEvent(Event* event) 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 @@ BrainOpenGLWidget::receiveEvent(Event* event) if (updateOneEvent->getWindowIndex() == this->windowIndex) { updateOneEvent->setEventProcessed(); - doUpdateGraphicsFlag = true; + if (updateOneEvent->isRepaint()) { + doRepaintGraphicsFlag = true; + } + else { + doUpdateGraphicsFlag = true; + } } else { /* @@ -1704,56 +1968,48 @@ BrainOpenGLWidget::receiveEvent(Event* event) } } } - else if (event->getEventType() == EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { - EventGetOrSetUserInputModeProcessor* inputModeEvent = - dynamic_cast(event); - CaretAssert(inputModeEvent); + else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_WINDOW_SHOW_TOOL_TIP) { + EventGraphicsWindowShowToolTip* tipEvent = dynamic_cast(event); + CaretAssert(tipEvent); - 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(); - } - } + 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 @@ BrainOpenGLWidget::receiveEvent(Event* event) CaretAssert(guiUpdateEvent); guiUpdateEvent->setEventProcessed(); - this->selectedUserInputProcessor->update(); + getSelectedInputProcessor()->update(); } else { @@ -1994,6 +2250,18 @@ BrainOpenGLWidget::receiveEvent(Event* event) } } +/** + * 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 */ @@ -2253,4 +2521,26 @@ BrainOpenGLWidget::processMouseEventFromMacro(QMouseEvent* me) 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 --git a/src/GuiQt/BrainOpenGLWidget.h b/src/GuiQt/BrainOpenGLWidget.h index 5f5942eee25ec9f4a9ac9bc9e53673df319f380e..2dfed983cfa53a8e50f2830f0fac11f052a43b04 100644 --- a/src/GuiQt/BrainOpenGLWidget.h +++ b/src/GuiQt/BrainOpenGLWidget.h @@ -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 @@ namespace caret { 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 @@ namespace caret { const int y, SurfaceProjectedItem& projectionOut); - Border* getBorderBeingDrawn(); - static void initializeDefaultGLFormat(); QString getOpenGLInformation(); @@ -148,6 +141,9 @@ namespace caret { virtual void leaveEvent(QEvent* e); + private slots: + void showSelectedChartPointToolTip(); + private: std::vector getDrawingViewportContent(const int32_t windowViewportIn[4]) const; @@ -160,6 +156,9 @@ namespace caret { 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 @@ namespace caret { void repaintGraphics(); + bool processGestureEvent(QGestureEvent* gestureEvent); + + UserInputModeAbstract* getSelectedInputProcessor() const; + + UserInputModeEnum::Enum getSelectedInputMode() const; + const int32_t windowIndex; BrainOpenGLWindowContent m_windowContent; @@ -193,17 +198,9 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index a1fbd80338119540d6d3854538e5ecf082e7b1ac..338d008c957f94d6a819364d1677951d733465e7 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -44,11 +44,14 @@ ELSE() 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 @@ ELSE() 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 @@ ELSE() 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 @@ ELSE() 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 @@ ELSE() GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h + IdentificationDisplayDialog.h + IdentificationDisplayWidget.h + IdentificationFileFilteringRow.h + IdentificationFileFilteringTableWidget.h IdentifyBrainordinateDialog.h ImageCaptureDialog.h ImageFileConvertToVolumeFileDialog.h @@ -144,7 +162,6 @@ ELSE() LockAspectWarningDialog.h MacApplication.h MacDockMenu.h - MacDuplicateMenuBar.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -154,6 +171,8 @@ ELSE() MapSettingsPaletteColorMappingWidget.h MapSettingsParcelsWidget.h MapYokingGroupComboBox.h + MediaOverlayViewController.h + MediaOverlaySetViewController.h MetaDataEditorDialog.h MetaDataEditorWidget.h MovieDialog.h @@ -163,13 +182,22 @@ ELSE() 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 @@ ELSE() 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 @@ ELSE() WbMacroHelper.h WbMacroWidgetActionsManager.h WuQCollapsibleWidget.h + WuQColorEditorWidget.h WuQDataEntryDialog.h WuQDialog.h WuQDialogModal.h @@ -230,6 +261,7 @@ ELSE() WuQMacroWidgetAction.h WuQMessageBox.h WuQwtPlot.h + WuQScrollArea.h WuQSpecialIncrementDoubleSpinBox.h WuQSpinBox.h WuQSpinBoxGroup.h @@ -239,8 +271,10 @@ ELSE() WuQTabWidgetWithSizeHint.h WuQTextEditorDialog.h WuQTimedMessageDisplay.h + WuQToolTipHelper.h WuQTreeWidget.h WuQTrueFalseComboBox.h + WuQValueChangedSignalWatcher.h #WuQWebView.h WuQWidget.h WuQWidgetObjectGroup.h @@ -262,12 +296,15 @@ ENDIF() # 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 @@ AnnotationInsertNewWidget.h AnnotationLineArrowTipsWidget.h AnnotationMenuArrange.h AnnotationMenuFileSelection.h +AnnotationNameWidget.h AnnotationPasteDialog.h AnnotationRedoUndoWidget.h AnnotationRotationWidget.h @@ -303,43 +341,53 @@ BrainBrowserWindowComboBox.h 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 @@ DataFileContentCopyMoveDialog.h 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 @@ EventGetOrSetUserInputModeProcessor.h 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 @@ FociProjectionDialog.h FociPropertiesEditorDialog.h FociSelectionViewController.h GapsAndMarginsDialog.h +GestureEvent.h GiftiLabelTableEditor.h GiftiLabelTableSelectionComboBox.h GroupAndNameHierarchyTreeWidgetItem.h @@ -388,6 +441,10 @@ GroupAndNameHierarchyViewController.h GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h +IdentificationDisplayDialog.h +IdentificationDisplayWidget.h +IdentificationFileFilteringRow.h +IdentificationFileFilteringTableWidget.h IdentifyBrainordinateDialog.h ImageCaptureDialog.h ImageFileConvertToVolumeFileDialog.h @@ -399,7 +456,6 @@ LabelSelectionViewController.h LockAspectWarningDialog.h MacApplication.h MacDockMenu.h -MacDuplicateMenuBar.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -409,6 +465,8 @@ MapSettingsLayerWidget.h MapSettingsPaletteColorMappingWidget.h MapSettingsParcelsWidget.h MapYokingGroupComboBox.h +MediaOverlayViewController.h +MediaOverlaySetViewController.h MetaDataEditorDialog.h MetaDataEditorWidget.h MouseEvent.h @@ -419,14 +477,24 @@ OverlaySetViewController.h 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 @@ SceneReplaceAllDialog.h 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 @@ UserInputModeFociWidget.h 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 @@ WbMacroCustomOperationDelay.h 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 @@ WuQMacroSignalWatcher.h WuQMacroWidgetAction.h WuQMessageBox.h WuQwtPlot.h +WuQScrollArea.h WuQSpecialIncrementDoubleSpinBox.h WuQSpinBox.h WuQSpinBoxGroup.h @@ -525,8 +599,10 @@ WuQTabWidget.h WuQTabWidgetWithSizeHint.h WuQTextEditorDialog.h WuQTimedMessageDisplay.h +WuQToolTipHelper.h WuQTreeWidget.h WuQTrueFalseComboBox.h +WuQValueChangedSignalWatcher.h #WuQWebView.h WuQWidget.h WuQWidgetDisabler.h @@ -535,12 +611,15 @@ WuQtUtilities.h 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 @@ AnnotationInsertNewWidget.cxx AnnotationLineArrowTipsWidget.cxx AnnotationMenuArrange.cxx AnnotationMenuFileSelection.cxx +AnnotationNameWidget.cxx AnnotationPasteDialog.cxx AnnotationRedoUndoWidget.cxx AnnotationRotationWidget.cxx @@ -576,43 +656,53 @@ BrainBrowserWindowComboBox.cxx 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 @@ DataFileContentCopyMoveDialog.cxx DisplayGroupAndTabItemTreeWidgetItem.cxx DisplayGroupAndTabItemViewController.cxx DisplayGroupEnumComboBox.cxx +DynConnViewController.cxx +EventBrowserTabCloseInToolBar.cxx +EventBrowserTabDeleteInToolBar.cxx +EventBrowserTabNewInGUI.cxx EventAnnotationCreateNewType.cxx EventAnnotationGetDrawnInWindow.cxx EventBrowserWindowDrawingContent.cxx @@ -634,10 +728,10 @@ EventGetOrSetUserInputModeProcessor.cxx 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 @@ FociProjectionDialog.cxx FociPropertiesEditorDialog.cxx FociSelectionViewController.cxx GapsAndMarginsDialog.cxx +GestureEvent.cxx GiftiLabelTableEditor.cxx GiftiLabelTableSelectionComboBox.cxx GroupAndNameHierarchyTreeWidgetItem.cxx @@ -660,6 +755,10 @@ GroupAndNameHierarchyViewController.cxx GuiManager.cxx HelpViewerDialog.cxx HyperLinkTextBrowser.cxx +IdentificationDisplayDialog.cxx +IdentificationDisplayWidget.cxx +IdentificationFileFilteringRow.cxx +IdentificationFileFilteringTableWidget.cxx IdentifyBrainordinateDialog.cxx ImageCaptureDialog.cxx ImageFileConvertToVolumeFileDialog.cxx @@ -671,7 +770,6 @@ LabelSelectionViewController.cxx LockAspectWarningDialog.cxx MacApplication.cxx MacDockMenu.cxx -MacDuplicateMenuBar.cxx MapSettingsChartTwoLineHistoryWidget.cxx MapSettingsColorBarPaletteOptionsWidget.cxx MapSettingsColorBarWidget.cxx @@ -681,6 +779,8 @@ MapSettingsLayerWidget.cxx MapSettingsPaletteColorMappingWidget.cxx MapSettingsParcelsWidget.cxx MapYokingGroupComboBox.cxx +MediaOverlayViewController.cxx +MediaOverlaySetViewController.cxx MetaDataEditorDialog.cxx MetaDataEditorWidget.cxx MouseEvent.cxx @@ -691,14 +791,24 @@ OverlaySetViewController.cxx 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 @@ SceneReplaceAllDialog.cxx 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 @@ UserInputModeFoci.cxx 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 @@ WbMacroCustomOperationDelay.cxx 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 @@ WuQMacroSignalWatcher.cxx WuQMacroWidgetAction.cxx WuQMessageBox.cxx WuQwtPlot.cxx +WuQScrollArea.cxx WuQSpecialIncrementDoubleSpinBox.cxx WuQSpinBox.cxx WuQSpinBoxGroup.cxx @@ -793,8 +909,10 @@ WuQTabWidget.cxx WuQTabWidgetWithSizeHint.cxx WuQTextEditorDialog.cxx WuQTimedMessageDisplay.cxx +WuQToolTipHelper.cxx WuQTreeWidget.cxx WuQTrueFalseComboBox.cxx +WuQValueChangedSignalWatcher.cxx #WuQWebView.cxx WuQWidget.cxx WuQWidgetDisabler.cxx diff --git a/src/GuiQt/CardinalDirectionEnumMenu.cxx b/src/GuiQt/CardinalDirectionEnumMenu.cxx new file mode 100644 index 0000000000000000000000000000000000000000..320a74eaff64d20472054d013d5f6720ddfc94df --- /dev/null +++ b/src/GuiQt/CardinalDirectionEnumMenu.cxx @@ -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 --git a/src/GuiQt/CardinalDirectionEnumMenu.h b/src/GuiQt/CardinalDirectionEnumMenu.h new file mode 100644 index 0000000000000000000000000000000000000000..caf87a90729f1e2aa5699fcec5446b85ff41ef83 --- /dev/null +++ b/src/GuiQt/CardinalDirectionEnumMenu.h @@ -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 --git a/src/GuiQt/CaretColorEnumComboBox.cxx b/src/GuiQt/CaretColorEnumComboBox.cxx index 0a0559949ecad4f3817889b1d23f69924de305ef..289f5780c1fddf3ce40fe73e3bfb998aab5193b7 100644 --- a/src/GuiQt/CaretColorEnumComboBox.cxx +++ b/src/GuiQt/CaretColorEnumComboBox.cxx @@ -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 @@ using namespace caret; * 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 @@ CaretColorEnumComboBox::initializeCaretColorComboBox(const AString& customColorS 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 @@ CaretColorEnumComboBox::initializeCaretColorComboBox(const AString& customColorS /* * 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); } } @@ -207,6 +147,74 @@ CaretColorEnumComboBox::initializeCaretColorComboBox(const AString& customColorS this, SLOT(colorComboBoxIndexChanged(int))); } +/** + * 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. */ @@ -258,6 +266,61 @@ CaretColorEnumComboBox::setSelectedColor(const CaretColorEnum::Enum color) } } +/** + * 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 @@ -268,6 +331,35 @@ CaretColorEnumComboBox::colorComboBoxIndexChanged(int indx) { 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 --git a/src/GuiQt/CaretColorEnumComboBox.h b/src/GuiQt/CaretColorEnumComboBox.h index 5882fab420294d9efaf8a0c228d28f2e92926edc..cfbabf71b73bacc654de782411006d2956c2874b 100644 --- a/src/GuiQt/CaretColorEnumComboBox.h +++ b/src/GuiQt/CaretColorEnumComboBox.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include "CaretColorEnum.h" #include "WuQWidget.h" @@ -34,27 +35,62 @@ namespace caret { Q_OBJECT public: - CaretColorEnumComboBox(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 + }; - CaretColorEnumComboBox(const AString& customColorSelectionName, - QObject* parent); + /** + * 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 @@ namespace caret { 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 --git a/src/GuiQt/CaretColorEnumMenu.cxx b/src/GuiQt/CaretColorEnumMenu.cxx index b4364871449491e1fc8279c5f113c37871057c6a..671d9c3e8964b473c8c0518c7c550c91dc70f7fb 100644 --- a/src/GuiQt/CaretColorEnumMenu.cxx +++ b/src/GuiQt/CaretColorEnumMenu.cxx @@ -116,10 +116,13 @@ CaretColorEnumMenu::initializeCaretColorEnumMenu(const int64_t caretColorOptions 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 --git a/src/GuiQt/CaretColorToolButton.cxx b/src/GuiQt/CaretColorToolButton.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4485d5535187f7107039fc8a93c9f54ef34368d9 --- /dev/null +++ b/src/GuiQt/CaretColorToolButton.cxx @@ -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 --git a/src/GuiQt/CaretColorToolButton.h b/src/GuiQt/CaretColorToolButton.h new file mode 100644 index 0000000000000000000000000000000000000000..5a8b352b938caaed3bc7467f17081bf0ecc43ef7 --- /dev/null +++ b/src/GuiQt/CaretColorToolButton.h @@ -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 --git a/src/GuiQt/CaretDataFileSelectionComboBox.cxx b/src/GuiQt/CaretDataFileSelectionComboBox.cxx index ac6f2633510541614f1a17fb7646d2a1914fb891..a74b655953c3f87e252f93c00c2902b806d566f6 100644 --- a/src/GuiQt/CaretDataFileSelectionComboBox.cxx +++ b/src/GuiQt/CaretDataFileSelectionComboBox.cxx @@ -131,7 +131,7 @@ CaretDataFileSelectionComboBox::updateComboBox(CaretDataFileSelectionModel* sele CaretAssertVectorIndex(displayNames, iFile); m_comboBox->addItem(displayNames[iFile], - qVariantFromValue((void*)cdf)); + QVariant::fromValue((void*)cdf)); if (cdf == selectedFile) { defaultIndex = m_comboBox->count() - 1; diff --git a/src/GuiQt/CaretFileDialog.cxx b/src/GuiQt/CaretFileDialog.cxx index 68709f39f1ff5a516c993085cbf6d223a7ff5e8b..bf8ef5c33dd706d05b87ab2f00f8650de5a8953a 100644 --- a/src/GuiQt/CaretFileDialog.cxx +++ b/src/GuiQt/CaretFileDialog.cxx @@ -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; @@ -97,6 +103,19 @@ FilterFilesProxyModel::setDataFileTypeForFiltering(const DataFileTypeEnum::Enum m_dataFileType = dataFileType; } +/** + * 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. @@ -104,27 +123,47 @@ FilterFilesProxyModel::setDataFileTypeForFiltering(const DataFileTypeEnum::Enum /** * 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 @@ CaretFileDialog::initializeCaretFileDialog() 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 @@ CaretFileDialog::getOpenFileNameDialog(QWidget *parent, QString *selectedFilter, Options options ) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_OPEN, + parent, caption, dir, filter); @@ -291,11 +350,12 @@ CaretFileDialog::getOpenFileNameDialog(const DataFileTypeEnum::Enum dataFileType 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 @@ CaretFileDialog::getSaveFileNameDialog(QWidget *parent, QString *selectedFilter, Options options) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_SAVE, + parent, caption, dir, filter); @@ -389,11 +450,12 @@ CaretFileDialog::getSaveFileNameDialog(const DataFileTypeEnum::Enum dataFileType 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 @@ CaretFileDialog::getChooseFileNameDialog(const DataFileTypeEnum::Enum dataFileTy 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 @@ CaretFileDialog::getExistingDirectoryDialog(QWidget *parent, const QString &dir, Options options) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_SAVE, + parent, caption, dir, ""); @@ -549,7 +613,8 @@ CaretFileDialog::getOpenFileNamesDialog(QWidget *parent, QString *selectedFilter, Options options) { - CaretFileDialog cfd(parent, + CaretFileDialog cfd(Mode::MODE_OPEN, + parent, caption, dir, filter); diff --git a/src/GuiQt/CaretFileDialog.h b/src/GuiQt/CaretFileDialog.h index 5a435f20e1487c12d55f17cda2ddf2da564e8d49..467a2ba40518fc665689fba5143afccab7ea05be 100644 --- a/src/GuiQt/CaretFileDialog.h +++ b/src/GuiQt/CaretFileDialog.h @@ -34,14 +34,21 @@ namespace caret { 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 @@ namespace caret { void initializeCaretFileDialog(); + const Mode m_mode; + FilterFilesProxyModel* m_filterFilesProxyModel; class PreviousDialogSettings { @@ -145,8 +154,9 @@ namespace caret { 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 --git a/src/GuiQt/CaretFileDialogExtendable.cxx b/src/GuiQt/CaretFileDialogExtendable.cxx index 79cb9fb6318a6e152e193d5277e892781a1ac0a4..b5f0b2500501064d30040f96bbbd71eabc068f46 100644 --- a/src/GuiQt/CaretFileDialogExtendable.cxx +++ b/src/GuiQt/CaretFileDialogExtendable.cxx @@ -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 --git a/src/GuiQt/CaretFileRemoteDialog.cxx b/src/GuiQt/CaretFileRemoteDialog.cxx index ace31cd8e26f50f5dff47775858ec02aa3a99b43..47c76d145194f8d39eb2743117ca45ec15c173c2 100644 --- a/src/GuiQt/CaretFileRemoteDialog.cxx +++ b/src/GuiQt/CaretFileRemoteDialog.cxx @@ -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 @@ CaretFileRemoteDialog::createAndLoadStandardData() 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 --git a/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx b/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx index e3b6faa8d3303c154c4af007a8ed528de7473707..bd412e1002bbd4471df87d4630f061485c7b233c 100644 --- a/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx +++ b/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx @@ -281,7 +281,7 @@ CaretMappableDataFileAndMapSelector::loadMapFileComboBox(const int32_t selectedF iter++) { CaretMappableDataFile* cmdf = *iter; this->mapFileComboBox->addItem(cmdf->getFileNameNoPath(), - qVariantFromValue((void*)cmdf)); + QVariant::fromValue((void*)cmdf)); } if ((selectedFileIndex >= 0) @@ -974,7 +974,7 @@ CaretMappableDataFileAndMapSelector::loadLastSelectionsForFileType(const DataFil } 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 --git a/src/GuiQt/CaretResultDialog.cxx b/src/GuiQt/CaretResultDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6d57d2ac4f8768aa12eee8d305968703e0213fd9 --- /dev/null +++ b/src/GuiQt/CaretResultDialog.cxx @@ -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 --git a/src/GuiQt/EventMacDockMenuUpdate.h b/src/GuiQt/CaretResultDialog.h similarity index 58% rename from src/GuiQt/EventMacDockMenuUpdate.h rename to src/GuiQt/CaretResultDialog.h index f8c95d0aa96556274207820676dad2c74b59e295..8235564e2d773fe2fde833a6df674e3d935e6d99 100644 --- a/src/GuiQt/EventMacDockMenuUpdate.h +++ b/src/GuiQt/CaretResultDialog.h @@ -1,9 +1,9 @@ -#ifndef __EVENT_MAC_DOCK_MENU_UPDATE_H__ -#define __EVENT_MAC_DOCK_MENU_UPDATE_H__ +#ifndef __CARET_RESULT_DIALOG_H__ +#define __CARET_RESULT_DIALOG_H__ /*LICENSE_START*/ /* - * Copyright (C) 2015 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 @@ -22,34 +22,38 @@ /*LICENSE_END*/ -#include "Event.h" +#include +#include "CaretResult.h" + +class QWidget; namespace caret { - class EventMacDockMenuUpdate : public Event { + class CaretResultDialog { public: - EventMacDockMenuUpdate(); + static bool isError(const std::unique_ptr& caretResult, + QWidget* parent); - virtual ~EventMacDockMenuUpdate(); + static bool isSuccess(const std::unique_ptr& caretResult, + QWidget* parent); // ADD_NEW_METHODS_HERE private: - EventMacDockMenuUpdate(const EventMacDockMenuUpdate&); + static bool run(const std::unique_ptr& caretResult, + QWidget* parent); - EventMacDockMenuUpdate& operator=(const EventMacDockMenuUpdate&); - // ADD_NEW_MEMBERS_HERE }; -#ifdef __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ +#ifdef __CARET_RESULT_DIALOG_DECLARE__ // -#endif // __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ +#endif // __CARET_RESULT_DIALOG_DECLARE__ } // namespace -#endif //__EVENT_MAC_DOCK_MENU_UPDATE_H__ +#endif //__CARET_RESULT_DIALOG_H__ diff --git a/src/GuiQt/ChartHistoryViewController.cxx b/src/GuiQt/ChartHistoryViewController.cxx index 997293d8e46b06e53a436ee996b7e7e2702b6999..1a0c5ceba36357e29cdc0e595f643c241845fd6b 100644 --- a/src/GuiQt/ChartHistoryViewController.cxx +++ b/src/GuiQt/ChartHistoryViewController.cxx @@ -595,7 +595,7 @@ ChartHistoryViewController::chartDataConstructionToolButtonSignalMapped(int indx else if (selectedAction == removeAction) { chartModel->removeChartAtIndex(indx); } - else { + else if (selectedAction != NULL) { CaretAssertMessage(0, "Has a new action been added but not processed?"); } diff --git a/src/GuiQt/ChartLinesSelectionViewController.cxx b/src/GuiQt/ChartLinesSelectionViewController.cxx index 748fc6545ba8fa910efe2a4b7816f2c9c388733c..0ae3b567345b508d6a9495959594bca1e2c3798a 100644 --- a/src/GuiQt/ChartLinesSelectionViewController.cxx +++ b/src/GuiQt/ChartLinesSelectionViewController.cxx @@ -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 @@ ChartLinesSelectionViewController::updateSelectionViewController() const bool checkBoxStatus = chartBrainFile->isLineSeriesChartingEnabled(browserTabIndex); - QVariant brainordinateFilePointerVariant = qVariantFromValue((void*)chartBrainFile); + QVariant brainordinateFilePointerVariant = QVariant::fromValue((void*)chartBrainFile); CaretMappableDataFile* caretMappableDataFile = chartBrainFile->getLineSeriesChartCaretMappableDataFile(); diff --git a/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx b/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx index 856f89e905353668641a9617ec4505f471f26af7..b757cff5a04fd7a55992440365802a977c89cc1b 100644 --- a/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx +++ b/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx @@ -762,8 +762,6 @@ ChartMatrixParcelSelectionViewController::createParcelRemappingWidget(const Qt:: parcelMapFileLayout->addWidget(mapNameComboBox, 3, 1); } break; - default: - CaretAssert(0); } return groupBox; diff --git a/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx b/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx index a67c888f04e8d8b093866d92a214a5da47c7e786..0a451edb38e9496e0f25dd0e16a60bbb83fbebb0 100644 --- a/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx +++ b/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx @@ -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 --git a/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.cxx b/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..061232bfbbe69e715ca45281c00e0cb5ebdd305e --- /dev/null +++ b/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.cxx @@ -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 --git a/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.h b/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..bc38ef088dea5382d6def0e85fb3fb100de0d04e --- /dev/null +++ b/src/GuiQt/ChartTwoAxisPropertiesEditorDialog.h @@ -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 --git a/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.cxx b/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0fa7a115bcbac552f037827f9428ca6be9b9e77b --- /dev/null +++ b/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.cxx @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h b/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.h similarity index 55% rename from src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h rename to src/GuiQt/ChartTwoAxisPropertiesEditorWidget.h index 544348b0c2bea5cf5481352c51bc08a382163e96..0429be7d9c1265fcaddb6d0c501b481696a58073 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h +++ b/src/GuiQt/ChartTwoAxisPropertiesEditorWidget.h @@ -1,5 +1,5 @@ -#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ -#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ +#ifndef __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_H__ +#define __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_H__ /*LICENSE_START*/ /* @@ -21,15 +21,17 @@ */ /*LICENSE_END*/ - -#include "BrainBrowserWindowToolBarComponent.h" +#include +#include #include "ChartAxisLocationEnum.h" +#include "DisplayGroupEnum.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QLabel; -class QSpinBox; +class QStackedWidget; +class QTabBar; class QToolButton; namespace caret { @@ -37,76 +39,60 @@ namespace caret { class AnnotationPercentSizeText; class ChartTwoCartesianAxis; class ChartTwoCartesianAxisWidget; + class ChartTwoCartesianCustomSubdivisionsEditorWidget; class ChartTwoOverlaySet; + class DisplayGroupEnumComboBox; class EnumComboBoxTemplate; class WuQDoubleSpinBox; + class WuQSpinBox; class WuQWidgetObjectGroup; - class BrainBrowserWindowToolBarChartTwoAxes : public BrainBrowserWindowToolBarComponent { + class ChartTwoAxisPropertiesEditorWidget : public QWidget { Q_OBJECT public: - BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar, - const QString& parentObjectName); - - virtual ~BrainBrowserWindowToolBarChartTwoAxes(); + ChartTwoAxisPropertiesEditorWidget(const ChartAxisLocationEnum::Enum axisLocation, + const QString& parentObjectName, + QWidget* parent = 0); - virtual void updateContent(BrowserTabContent* browserTabContent); + virtual ~ChartTwoAxisPropertiesEditorWidget(); - virtual void receiveEvent(Event* event); + void updateControls(ChartTwoOverlaySet* chartOverlaySet, + ChartTwoCartesianAxis* chartAxis); // 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 chartSubdivisionsModeEnumComboBoxItemActivated(); - void getSelectionData(BrowserTabContent* browserTabContent, - ChartTwoOverlaySet* &chartOverlaySetOut, - std::vector& validAxesLocationsOut, - ChartTwoCartesianAxis* &selectedAxisOut) const; + void displayGroupSelected(const DisplayGroupEnum::Enum); - void updateControls(BrowserTabContent* browserTabContent); + private: + ChartTwoAxisPropertiesEditorWidget(const ChartTwoAxisPropertiesEditorWidget&); + + ChartTwoAxisPropertiesEditorWidget& operator=(const ChartTwoAxisPropertiesEditorWidget&); void updateGraphics(); // ADD_NEW_MEMBERS_HERE - QCheckBox* m_axisDisplayedByUserCheckBox; - - EnumComboBoxTemplate* m_axisComboBox; + ChartTwoOverlaySet* m_chartOverlaySet; - QToolButton* m_axisLabelToolButton; + ChartTwoCartesianAxis* m_chartAxis; - QComboBox* m_axisLabelFromOverlayComboBox; + const ChartAxisLocationEnum::Enum m_axisLocation; - EnumComboBoxTemplate* m_autoUserRangeComboBox; + EnumComboBoxTemplate* m_chartSubdivisionsModeEnumComboBox; - WuQDoubleSpinBox* m_userMinimumValueSpinBox; + QToolButton* m_axisLabelToolButton; - WuQDoubleSpinBox* m_userMaximumValueSpinBox; + QComboBox* m_axisLabelFromOverlayComboBox; QCheckBox* m_showTickMarksCheckBox; @@ -118,11 +104,11 @@ namespace caret { EnumComboBoxTemplate* m_userNumericFormatComboBox; - QSpinBox* m_userDigitsRightOfDecimalSpinBox; + WuQSpinBox* m_userDigitsRightOfDecimalSpinBox; EnumComboBoxTemplate* m_numericSubdivisionsModeComboBox; - QSpinBox* m_userSubdivisionsSpinBox; + WuQSpinBox* m_userSubdivisionsSpinBox; WuQDoubleSpinBox* m_labelSizeSpinBox; @@ -132,17 +118,23 @@ namespace caret { WuQDoubleSpinBox* m_paddingSizeSpinBox; - ChartTwoOverlaySet* m_chartOverlaySet; + QStackedWidget* m_numericsStackedWidget; - ChartTwoCartesianAxis* m_chartAxis; + int32_t m_numericsStackedWidgetStandardSubdivsionsIndex = -1; + + int32_t m_numericsStackedWidgetCustomSubdivsionsIndex = -1; + + DisplayGroupEnumComboBox* m_displayGroupComboBox; + + ChartTwoCartesianCustomSubdivisionsEditorWidget* m_customSubdivisionsEditorWidget; WuQWidgetObjectGroup* m_widgetGroup; }; -#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ +#ifdef __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_DECLARE__ // -#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_DECLARE__ +#endif // __CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_DECLARE__ } // namespace -#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_AXES_H__ +#endif //__CHART_TWO_AXIS_PROPERTIES_EDITOR_WIDGET_H__ diff --git a/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.cxx b/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f840efbd832aff1b18328c5acefab34c2eb49ab9 --- /dev/null +++ b/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.cxx @@ -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 --git a/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.h b/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..25ea1c87b502cbced6a712fb530b048ea63de906 --- /dev/null +++ b/src/GuiQt/ChartTwoCartesianCustomSubdivisionsEditorWidget.h @@ -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 --git a/src/GuiQt/ChartTwoLineLayerNormalizationWidget.cxx b/src/GuiQt/ChartTwoLineLayerNormalizationWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5bdf498367a9bd493fb1b69e6a726a08aec44ace --- /dev/null +++ b/src/GuiQt/ChartTwoLineLayerNormalizationWidget.cxx @@ -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 --git a/src/GuiQt/ChartTwoLineLayerNormalizationWidget.h b/src/GuiQt/ChartTwoLineLayerNormalizationWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..4eb8d5035d73f0185be0fd66513965cf6ce77fe0 --- /dev/null +++ b/src/GuiQt/ChartTwoLineLayerNormalizationWidget.h @@ -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 --git a/src/GuiQt/ChartTwoOverlaySetViewController.cxx b/src/GuiQt/ChartTwoOverlaySetViewController.cxx index 5db1dbc90fc469c50fc2a4a0dd25029b3cff8bf1..ae16e8bd19d41130e8709f7e807e231b96a8e9ba 100644 --- a/src/GuiQt/ChartTwoOverlaySetViewController.cxx +++ b/src/GuiQt/ChartTwoOverlaySetViewController.cxx @@ -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 @@ m_browserWindowIndex(browserWindowIndex) } 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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ m_browserWindowIndex(browserWindowIndex) } } 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 @@ m_browserWindowIndex(browserWindowIndex) 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 @@ ChartTwoOverlaySetViewController::updateViewController() 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 @@ ChartTwoOverlaySetViewController::processAddOverlayAbove(const int32_t overlayIn if (chartOverlaySet != NULL) { chartOverlaySet->insertOverlayAbove(overlayIndex); this->updateColoringAndGraphics(); + this->updateViewController(); } } @@ -372,6 +449,7 @@ ChartTwoOverlaySetViewController::processAddOverlayBelow(const int32_t overlayIn if (chartOverlaySet != NULL) { chartOverlaySet->insertOverlayBelow(overlayIndex); this->updateColoringAndGraphics(); + updateViewController(); } } diff --git a/src/GuiQt/ChartTwoOverlaySetViewController.h b/src/GuiQt/ChartTwoOverlaySetViewController.h index de18b713f5196719f36a6214e7312128b0a4bafa..fb0641f232d4b90d944aa74a5a450bc6cd2ada9d 100644 --- a/src/GuiQt/ChartTwoOverlaySetViewController.h +++ b/src/GuiQt/ChartTwoOverlaySetViewController.h @@ -79,6 +79,16 @@ namespace caret { 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 --git a/src/GuiQt/ChartTwoOverlayViewController.cxx b/src/GuiQt/ChartTwoOverlayViewController.cxx index f75c243be4319209854535f226410866f2894cb6..74fd56832ca8d2718d19281b8579fe78166a9a30 100644 --- a/src/GuiQt/ChartTwoOverlayViewController.cxx +++ b/src/GuiQt/ChartTwoOverlayViewController.cxx @@ -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 @@ ChartTwoOverlayViewController::ChartTwoOverlayViewController(const Qt::Orientati : 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 @@ m_chartOverlay(NULL) /* * 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 @@ m_chartOverlay(NULL) /* * 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 @@ m_chartOverlay(NULL) 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_chartOverlay(NULL) m_matrixTriangularViewModeAction->setMenu(matrixTriangularViewModeMenu); m_matrixTriangularViewModeToolButton->setDefaultAction(m_matrixTriangularViewModeAction); m_matrixTriangularViewModeToolButton->setPopupMode(QToolButton::InstantPopup); + + /* + * Matrix opacity spin box + */ + 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); /* - * Axis location button - * Note: macro support is on each action in menu in createMatrixTriangularViewModeMenu + * Line layer normalization action */ - 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); + 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 @@ m_chartOverlay(NULL) 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 @@ ChartTwoOverlayViewController::fileComboBoxSelected(int indx) 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 @@ ChartTwoOverlayViewController::fileComboBoxSelected(int indx) 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 @@ ChartTwoOverlayViewController::enabledCheckBoxClicked(bool checked) 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 @@ ChartTwoOverlayViewController::updateViewController(ChartTwoOverlay* chartOverla 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 @@ ChartTwoOverlayViewController::updateViewController(ChartTwoOverlay* chartOverla 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 @@ ChartTwoOverlayViewController::updateViewController(ChartTwoOverlay* chartOverla 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: @@ -850,11 +978,6 @@ ChartTwoOverlayViewController::updateViewController(ChartTwoOverlay* chartOverla } m_colorBarAction->blockSignals(false); - /* - * Update construction button - */ - m_constructionAction->setEnabled(true); - /* * Update matrix triangular view mode */ @@ -874,23 +997,98 @@ ChartTwoOverlayViewController::updateViewController(ChartTwoOverlay* chartOverla 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); } /** @@ -920,34 +1118,6 @@ ChartTwoOverlayViewController::updateMatrixTriangularViewModeAction(const ChartT m_matrixTriangularViewModeAction->blockSignals(false); } -/** - * 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 */ @@ -1058,70 +1228,158 @@ ChartTwoOverlayViewController::menuMatrixTriangularViewModeTriggered(QAction* ac } /** - * 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 @@ -1223,10 +1481,28 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, 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. */ @@ -1406,63 +1682,6 @@ ChartTwoOverlayViewController::menuReloadFileTriggered() } } -/** - * 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. * @@ -1543,3 +1762,193 @@ ChartTwoOverlayViewController::createMatrixTriangularViewModePixmap(QWidget* wid 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 --git a/src/GuiQt/ChartTwoOverlayViewController.h b/src/GuiQt/ChartTwoOverlayViewController.h index 039273bd2ebcf0f02cefca4c777815b3a51730fb..253500f7ef5fbd5571a411689a25082b55ca3c93 100644 --- a/src/GuiQt/ChartTwoOverlayViewController.h +++ b/src/GuiQt/ChartTwoOverlayViewController.h @@ -25,7 +25,6 @@ #include #include -#include "ChartAxisLocationEnum.h" #include "ChartTwoMatrixTriangularViewingModeEnum.h" class QAction; @@ -37,8 +36,13 @@ class QSpinBox; 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 @@ namespace caret { 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 @@ namespace caret { const QString& parentObjectName, const QString& descriptivePrefix); - QMenu* createAxisLocationMenu(QWidget* widget, - const QString& parentObjectName, - const QString& descriptivePrefix); - void validateYokingSelection(); void updateOverlaySettingsEditor(); @@ -139,12 +159,13 @@ namespace caret { 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 @@ namespace caret { ChartTwoOverlay* m_chartOverlay; + const AString m_parentObjectName; + QCheckBox* m_enabledCheckBox; QCheckBox* m_lineSeriesLoadingEnabledCheckBox; @@ -164,20 +187,26 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/ChartTwoTitleEditorWidget.cxx b/src/GuiQt/ChartTwoTitleEditorWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2dc55a760fcd06cb11a91d8e0346912804332139 --- /dev/null +++ b/src/GuiQt/ChartTwoTitleEditorWidget.cxx @@ -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 --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h b/src/GuiQt/ChartTwoTitleEditorWidget.h similarity index 54% rename from src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h rename to src/GuiQt/ChartTwoTitleEditorWidget.h index aa27d189529a9125b9ac03ae7ac18004602b85ef..eb5ad7a1a4b3aa13780d61492d5fefe9d0134ac3 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h +++ b/src/GuiQt/ChartTwoTitleEditorWidget.h @@ -1,5 +1,5 @@ -#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ -#define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ +#ifndef __CHART_TWO_TITLE_EDITOR_WIDGET_H__ +#define __CHART_TWO_TITLE_EDITOR_WIDGET_H__ /*LICENSE_START*/ /* @@ -22,48 +22,46 @@ /*LICENSE_END*/ -#include "BrainBrowserWindowToolBarComponent.h" +#include -class QCheckBox; +class QLineEdit; namespace caret { + class BrowserTabContent; class ChartTwoOverlaySet; class WuQDoubleSpinBox; - class BrainBrowserWindowToolBarChartTwoTitle : public BrainBrowserWindowToolBarComponent { + class ChartTwoTitleEditorWidget : public QWidget { Q_OBJECT public: - BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar, - const QString& parentObjectName); + ChartTwoTitleEditorWidget(QWidget* parent, + const QString& parentObjectName); - virtual ~BrainBrowserWindowToolBarChartTwoTitle(); - - virtual void updateContent(BrowserTabContent* browserTabContent); + virtual ~ChartTwoTitleEditorWidget(); + + virtual void updateControls(ChartTwoOverlaySet* chartTwoOverlaySet); // ADD_NEW_METHODS_HERE private slots: - void editTitleActionTriggered(); - - void showTitleCheckBoxClicked(bool); + void textLineEditChanged(const QString& text); void sizeSpinBoxValueChanged(double); private: - BrainBrowserWindowToolBarChartTwoTitle(const BrainBrowserWindowToolBarChartTwoTitle&); + ChartTwoTitleEditorWidget(const ChartTwoTitleEditorWidget&); - BrainBrowserWindowToolBarChartTwoTitle& operator=(const BrainBrowserWindowToolBarChartTwoTitle&); + ChartTwoTitleEditorWidget& operator=(const ChartTwoTitleEditorWidget&); - void performUpdating(); + void updateGraphics(); - QCheckBox* m_showTitleCheckBox; + QLineEdit* m_textLineEdit; WuQDoubleSpinBox* m_titleSizeSpinBox; WuQDoubleSpinBox* m_paddingSizeSpinBox; - ChartTwoOverlaySet* m_chartOverlaySet; @@ -71,9 +69,9 @@ namespace caret { }; -#ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ +#ifdef __CHART_TWO_TITLE_EDITOR_WIDGET_DECLARE__ // -#endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_DECLARE__ +#endif // __CHART_TWO_TITLE_EDITOR_WIDGET_DECLARE__ } // namespace -#endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_TWO_TITLE_H__ +#endif //__CHART_TWO_TITLE_EDITOR_WIDGET_H__ diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx index 4ef6a8ec32fb43d37a12c8174f00ace34a138e76..f86dff02d631a82608213c1a23dad5498ac02170 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx @@ -289,7 +289,7 @@ CiftiConnectivityMatrixViewController::updateViewController() 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 @@ CiftiConnectivityMatrixViewController::updateFiberOrientationComboBoxes() } comboBox->addItem(orientFile->getFileNameNoPath(), - qVariantFromValue((void*)orientFile)); + QVariant::fromValue((void*)orientFile)); } } diff --git a/src/GuiQt/ClippingPlanesDialog.cxx b/src/GuiQt/ClippingPlanesWidget.cxx similarity index 50% rename from src/GuiQt/ClippingPlanesDialog.cxx rename to src/GuiQt/ClippingPlanesWidget.cxx index c45140daa76eb6df6606d318f55d49e48c99dca2..d10d396697af05a032cc445a84965d3481fb93ef 100644 --- a/src/GuiQt/ClippingPlanesDialog.cxx +++ b/src/GuiQt/ClippingPlanesWidget.cxx @@ -19,24 +19,29 @@ */ /*LICENSE_END*/ -#define __CLIPPING_PLANES_DIALOG_DECLARE__ -#include "ClippingPlanesDialog.h" -#undef __CLIPPING_PLANES_DIALOG_DECLARE__ +#define __CLIPPING_PLANES_WIDGET_DECLARE__ +#include "ClippingPlanesWidget.h" +#undef __CLIPPING_PLANES_WIDGET_DECLARE__ #include #include #include +#include +#include +#include #include +#include #include -#include "BrainBrowserWindow.h" -#include "BrainBrowserWindowComboBox.h" #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" @@ -45,7 +50,7 @@ using namespace caret; /** - * \class caret::ClippingPlanesDialog + * \class caret::ClippingPlanesWidget * \brief Dialog for adjusting clipping planes transformation. * \ingroup GuiQt */ @@ -53,30 +58,230 @@ using namespace caret; /** * Constructor. */ -ClippingPlanesDialog::ClippingPlanesDialog(QWidget* parent) -: WuQDialogNonModal("Clipping Planes", - parent) +ClippingPlanesWidget::ClippingPlanesWidget(const QString& objectNamePrefix, + QWidget* parent) +: QWidget(parent) { - m_blockDialogUpdate = true; - + 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); + /* - * Window number + * Finish up */ - 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(); + 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 @@ -184,25 +389,12 @@ ClippingPlanesDialog::ClippingPlanesDialog(QWidget* parent) 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())); + + QToolButton* resetToolButton = new QToolButton(); + WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); + resetToolButton->setText("Reset"); + QObject::connect(resetToolButton, &QToolButton::clicked, + this, &ClippingPlanesWidget::resetButtonClicked); /*------------------------------------------------------------------------*/ /* @@ -217,8 +409,8 @@ ClippingPlanesDialog::ClippingPlanesDialog(QWidget* parent) const int COLUMN_Y = column++; const int COLUMN_Z = column++; - QWidget* gridWidget = new QWidget(); - QGridLayout* gridLayout = new QGridLayout(gridWidget); + QGroupBox* groupBox = new QGroupBox("Clipping Box"); + QGridLayout* gridLayout = new QGridLayout(groupBox); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 4); int row = 0; gridLayout->addWidget(xColumnLabel, @@ -275,236 +467,104 @@ ClippingPlanesDialog::ClippingPlanesDialog(QWidget* parent) 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); -} + row++; + gridLayout->addWidget(m_displayClippingBoxCheckBox, + row, 0, 1, 2, Qt::AlignLeft); + gridLayout->addWidget(resetToolButton, + row, 2, 1, 2, Qt::AlignCenter); + row++; -/** - * 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; + return groupBox; } /** - * Called when window number combo box value changed. + * @return Instance of the clipping axes widget */ -void -ClippingPlanesDialog::browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow) +QWidget* +ClippingPlanesWidget::createClippingAxesWidget() { - int32_t windowIndex = -1; - if (browserWindow != NULL) { - windowIndex = browserWindow->getBrowserWindowIndex(); - } + WuQMacroManager* macroManager = WuQMacroManager::instance(); - 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() - }; + 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"); - const float rotation[3] = { - (float)m_xRotateDoubleSpinBox->value(), - (float)m_yRotateDoubleSpinBox->value(), - (float)m_zRotateDoubleSpinBox->value() - }; + 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"); - const float thickness[3] ={ - (float)m_xThicknessDoubleSpinBox->value(), - (float)m_yThicknessDoubleSpinBox->value(), - (float)m_zThicknessDoubleSpinBox->value() - }; + 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"); - /* - * 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(); -} + 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); -/** - * Update the dialog. - */ -void -ClippingPlanesDialog::updateDialog() -{ - m_browserWindowComboBox->updateComboBox(); - updateContent(m_browserWindowComboBox->getSelectedBrowserWindowIndex()); + return groupBox; } - /** - * Update the content in the dialog - * @param browserWindowIndexIn - * Index of the browser window. + * @return Instance of the clipping data type widget */ -void -ClippingPlanesDialog::updateContent(const int32_t browserWindowIndexIn) +QWidget* +ClippingPlanesWidget::createClippingDataTypeWidget() { + WuQMacroManager* macroManager = WuQMacroManager::instance(); - /* - * May get updates when graphics are redrawn by this dialog - * and not doing this could result in infinite loop - */ - if (m_blockDialogUpdate) { - return; - } + 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"); - /* - * 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); - } - } + 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; } -/** - * 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 --git a/src/GuiQt/ClippingPlanesDialog.h b/src/GuiQt/ClippingPlanesWidget.h similarity index 60% rename from src/GuiQt/ClippingPlanesDialog.h rename to src/GuiQt/ClippingPlanesWidget.h index 344c4d21f5747649c57daff70d8020cb9c09eb20..76af54896f2c092fc42e7bf58eacf3ba79ccfc30 100644 --- a/src/GuiQt/ClippingPlanesDialog.h +++ b/src/GuiQt/ClippingPlanesWidget.h @@ -1,5 +1,5 @@ -#ifndef __CLIPPING_PLANES_DIALOG_H__ -#define __CLIPPING_PLANES_DIALOG_H__ +#ifndef __CLIPPING_PLANES_WIDGET_H__ +#define __CLIPPING_PLANES_WIDGET_H__ /*LICENSE_START*/ /* @@ -22,59 +22,51 @@ /*LICENSE_END*/ -#include "WuQDialogNonModal.h" +#include #include "EventListenerInterface.h" class QCheckBox; class QDoubleSpinBox; -class QPushButton; namespace caret { - class BrainBrowserWindow; - class BrainBrowserWindowComboBox; + class BrowserTabContent; class WuQWidgetObjectGroup; - class ClippingPlanesDialog : public WuQDialogNonModal, public EventListenerInterface { + class ClippingPlanesWidget : public QWidget { Q_OBJECT public: - ClippingPlanesDialog(QWidget* parent); + ClippingPlanesWidget(const QString& objectNamePrefix, + QWidget* parent = 0); - virtual ~ClippingPlanesDialog(); + virtual ~ClippingPlanesWidget(); - - void updateDialog(); - - void updateContent(const int32_t browserWindowIndex); + void updateContent(const int32_t tabIndex); // 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); + void resetButtonClicked(); private: void updateGraphicsWindow(); - ClippingPlanesDialog(const ClippingPlanesDialog&); + ClippingPlanesWidget(const ClippingPlanesWidget&); - ClippingPlanesDialog& operator=(const ClippingPlanesDialog&); + ClippingPlanesWidget& operator=(const ClippingPlanesWidget&); + + QWidget* createClippingBoxWidget(); - QPushButton* m_resetPushButton; + QWidget* createClippingAxesWidget(); - BrainBrowserWindowComboBox* m_browserWindowComboBox; + QWidget* createClippingDataTypeWidget(); - WuQWidgetObjectGroup* m_clippingWidgetGroup; + BrowserTabContent* getBrowserTabContent(); + + int32_t m_tabIndex = -1; QDoubleSpinBox* m_xPanDoubleSpinBox; @@ -96,15 +88,27 @@ namespace caret { QCheckBox* m_displayClippingBoxCheckBox; - bool m_blockDialogUpdate; + 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_DIALOG_DECLARE__ +#ifdef __CLIPPING_PLANES_WIDGET_DECLARE__ // -#endif // __CLIPPING_PLANES_DIALOG_DECLARE__ +#endif // __CLIPPING_PLANES_WIDGET_DECLARE__ } // namespace -#endif //__CLIPPING_PLANES_DIALOG_H__ +#endif //__CLIPPING_PLANES_WIDGET_H__ diff --git a/src/GuiQt/CustomViewDialog.cxx b/src/GuiQt/CustomViewDialog.cxx index 08480e01fff7ac472ed7545be253da9a490232fb..0887e107621f4496d9c71e4c3ab059f59d2fdfe7 100644 --- a/src/GuiQt/CustomViewDialog.cxx +++ b/src/GuiQt/CustomViewDialog.cxx @@ -317,7 +317,7 @@ CustomViewDialog::copyToCustomViewPushButtonClicked() 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 @@ CustomViewDialog::moveTransformToCustomView(ModelTransform& modelTransform) obRotX, obRotY, obRotZ, + flatRotate, zoom, rightFlatX, rightFlatY, @@ -344,12 +345,16 @@ CustomViewDialog::moveTransformToCustomView(ModelTransform& modelTransform) 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 @@ CustomViewDialog::copyToTransformPushButtonClicked() 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 @@ CustomViewDialog::copyToTransformPushButtonClicked() 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(); @@ -519,6 +530,20 @@ CustomViewDialog::createTransformsWidget() QObject::connect(m_zObliqueRotateDoubleSpinBox, SIGNAL(valueChanged(double)), 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 */ @@ -575,6 +600,7 @@ CustomViewDialog::createTransformsWidget() 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 @@ CustomViewDialog::createTransformsWidget() 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 @@ CustomViewDialog::browserWindowComboBoxValueChanged(BrainBrowserWindow* browserW 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 @@ CustomViewDialog::transformValueChanged() obRotX, obRotY, obRotZ, + flatRotate, zoom, rightFlatX, rightFlatY, @@ -740,9 +775,14 @@ CustomViewDialog::transformValueChanged() 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 @@ CustomViewDialog::updateContent(const int32_t browserWindowIndexIn) 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 @@ CustomViewDialog::updateContent(const int32_t browserWindowIndexIn) obRotX, obRotY, obRotZ, + flatRotZ, zooming, rightFlatX, rightFlatY, @@ -867,6 +912,14 @@ CustomViewDialog::updateContent(const int32_t browserWindowIndexIn) * 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 @@ CustomViewDialog::getTransformationControlValues(double& panX, double& obRotX, double& obRotY, double& obRotZ, + double& flatRotation, double& zoom, double& rightFlatX, double& rightFlatY, @@ -903,6 +957,8 @@ CustomViewDialog::getTransformationControlValues(double& panX, obRotY = m_yObliqueRotateDoubleSpinBox->value(); obRotZ = m_zObliqueRotateDoubleSpinBox->value(); + flatRotation = m_flatRotationDoubleSpinBox->value(); + zoom = m_zoomDoubleSpinBox->value(); rightFlatX = m_xRightFlatMapSpinBox->value(); @@ -923,6 +979,14 @@ CustomViewDialog::getTransformationControlValues(double& panX, * 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 @@ CustomViewDialog::setTransformationControlValues(const double panX, 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 @@ CustomViewDialog::setTransformationControlValues(const double panX, m_yObliqueRotateDoubleSpinBox->setValue(obRotY); m_zObliqueRotateDoubleSpinBox->setValue(obRotZ); + m_flatRotationDoubleSpinBox->setValue(flatRotation); + m_zoomDoubleSpinBox->setValue(zoom); m_xRightFlatMapSpinBox->setValue(rightFlatX); diff --git a/src/GuiQt/CustomViewDialog.h b/src/GuiQt/CustomViewDialog.h index f3c9a66fbe6fe9b7f9768a6788ce36e3cf0f2be1..7c2c3e8611a114a37d2127871df498c54359f382 100644 --- a/src/GuiQt/CustomViewDialog.h +++ b/src/GuiQt/CustomViewDialog.h @@ -96,6 +96,7 @@ namespace caret { double& obRotX, double& obRotY, double& obRotZ, + double& flatRotate, double& zoom, double& rightFlatX, double& rightFlatY, @@ -110,6 +111,7 @@ namespace caret { 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 @@ namespace caret { QDoubleSpinBox* m_zObliqueRotateDoubleSpinBox; + QDoubleSpinBox* m_flatRotationDoubleSpinBox; + QDoubleSpinBox* m_zoomDoubleSpinBox; QDoubleSpinBox* m_xRightFlatMapSpinBox; diff --git a/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx b/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx index 503913d90147c8ff6e250a849f7c445490b18266..78016ac73842c27887b00c9a01fc32585f688bc6 100644 --- a/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx +++ b/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx @@ -181,7 +181,7 @@ DisplayGroupAndTabItemTreeWidgetItem::setDisplayGroupAndTabItem(DisplayGroupAndT { setData(NAME_COLUMN, Qt::UserRole, - qVariantFromValue(displayGroupAndTabItem)); + QVariant::fromValue(displayGroupAndTabItem)); } diff --git a/src/GuiQt/DynConnViewController.cxx b/src/GuiQt/DynConnViewController.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5ee1c968c88c162a9f8e332d0c67ad0841f87fcc --- /dev/null +++ b/src/GuiQt/DynConnViewController.cxx @@ -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 --git a/src/GuiQt/DynConnViewController.h b/src/GuiQt/DynConnViewController.h new file mode 100644 index 0000000000000000000000000000000000000000..769b1984f1d45a9a653ae66059f861c6a8327372 --- /dev/null +++ b/src/GuiQt/DynConnViewController.h @@ -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 --git a/src/GuiQt/EventAnnotationCreateNewType.cxx b/src/GuiQt/EventAnnotationCreateNewType.cxx index cf1eb30e8a7bd2b4004d8951732aa564fc19342c..838dbe9afdb609ab9429ac69e4426d17953c261f 100644 --- a/src/GuiQt/EventAnnotationCreateNewType.cxx +++ b/src/GuiQt/EventAnnotationCreateNewType.cxx @@ -39,19 +39,25 @@ using namespace caret; /** * 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 @@ EventAnnotationCreateNewType::getAnnotationType() const return m_annotationType; } +/** + * @return Mode for polyline drawing + */ +EventAnnotationCreateNewType::PolyLineDrawingMode +EventAnnotationCreateNewType::getPolyLineDrawingMode() const +{ + return m_polyLineDrawingMode; +} + diff --git a/src/GuiQt/EventAnnotationCreateNewType.h b/src/GuiQt/EventAnnotationCreateNewType.h index e4301e1477f17885d25f72f0cb442ede09ee5877..23c8d3b55888809d39174aeaf130cafd353c624b 100644 --- a/src/GuiQt/EventAnnotationCreateNewType.h +++ b/src/GuiQt/EventAnnotationCreateNewType.h @@ -34,9 +34,15 @@ namespace caret { 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 @@ namespace caret { AnnotationTypeEnum::Enum getAnnotationType() const; + PolyLineDrawingMode getPolyLineDrawingMode() const; + // ADD_NEW_METHODS_HERE private: @@ -59,6 +67,8 @@ namespace caret { const AnnotationTypeEnum::Enum m_annotationType; + const PolyLineDrawingMode m_polyLineDrawingMode; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/EventBrowserTabCloseInToolBar.cxx b/src/GuiQt/EventBrowserTabCloseInToolBar.cxx new file mode 100644 index 0000000000000000000000000000000000000000..92b1728204bfdb17279dbd9f43a14bab4a8439a0 --- /dev/null +++ b/src/GuiQt/EventBrowserTabCloseInToolBar.cxx @@ -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 --git a/src/GuiQt/EventBrowserTabCloseInToolBar.h b/src/GuiQt/EventBrowserTabCloseInToolBar.h new file mode 100644 index 0000000000000000000000000000000000000000..2347fe01531753896a98b2cfdcd0ab2e7242ff8b --- /dev/null +++ b/src/GuiQt/EventBrowserTabCloseInToolBar.h @@ -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 --git a/src/GuiQt/EventBrowserTabDeleteInToolBar.cxx b/src/GuiQt/EventBrowserTabDeleteInToolBar.cxx new file mode 100644 index 0000000000000000000000000000000000000000..52f1decf051bf73af593c3197e6608841babea41 --- /dev/null +++ b/src/GuiQt/EventBrowserTabDeleteInToolBar.cxx @@ -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 --git a/src/GuiQt/EventBrowserTabDeleteInToolBar.h b/src/GuiQt/EventBrowserTabDeleteInToolBar.h new file mode 100644 index 0000000000000000000000000000000000000000..d9e9883f19477bf5675b0d0b3c067b07001c899c --- /dev/null +++ b/src/GuiQt/EventBrowserTabDeleteInToolBar.h @@ -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 --git a/src/GuiQt/EventBrowserTabNewInGUI.cxx b/src/GuiQt/EventBrowserTabNewInGUI.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2243f8cdad428d842f0de0d516c3a3bac4b334b4 --- /dev/null +++ b/src/GuiQt/EventBrowserTabNewInGUI.cxx @@ -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 --git a/src/GuiQt/EventBrowserTabNewInGUI.h b/src/GuiQt/EventBrowserTabNewInGUI.h new file mode 100644 index 0000000000000000000000000000000000000000..269ca55b971840f4e7d309c142da09c0a5204453 --- /dev/null +++ b/src/GuiQt/EventBrowserTabNewInGUI.h @@ -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 --git a/src/GuiQt/EventBrowserWindowTileTabOperation.cxx b/src/GuiQt/EventBrowserWindowTileTabOperation.cxx index 9cd4f3788f39b03557d9e7670dfea823ac6f4c19..d6cb6d889962fd6983c5b01d28874dc2bccf8af4 100644 --- a/src/GuiQt/EventBrowserWindowTileTabOperation.cxx +++ b/src/GuiQt/EventBrowserWindowTileTabOperation.cxx @@ -50,26 +50,59 @@ using namespace caret; * 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 @@ EventBrowserWindowTileTabOperation::selectTabInWindow(QWidget* parentWidget, 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 @@ EventBrowserWindowTileTabOperation::getBrowserTabsForReplaceOperation() const 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 --git a/src/GuiQt/EventBrowserWindowTileTabOperation.h b/src/GuiQt/EventBrowserWindowTileTabOperation.h index 87a6aa0de847057a93ff47514d1ff15d2a7e25af..7a707d7e0ac34005d9998af006fa3467bf775f3d 100644 --- a/src/GuiQt/EventBrowserWindowTileTabOperation.h +++ b/src/GuiQt/EventBrowserWindowTileTabOperation.h @@ -41,8 +41,13 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx b/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx index 3ca428eccf4c8a6173488ed1648d09354300670b..52ed6ac8b121e4316a5916ba4260bf24d7a4aba6 100644 --- a/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx +++ b/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx @@ -55,7 +55,7 @@ EventGetOrSetUserInputModeProcessor::EventGetOrSetUserInputModeProcessor(const i : 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 --git a/src/GuiQt/EventGraphicsUpdateOneWindow.cxx b/src/GuiQt/EventGraphicsUpdateOneWindow.cxx index 42de9a8b352b1ef7562180a228e311f52646e84a..62b49b8467553a376ea4e76fcc180d1b9fce887c 100644 --- a/src/GuiQt/EventGraphicsUpdateOneWindow.cxx +++ b/src/GuiQt/EventGraphicsUpdateOneWindow.cxx @@ -30,11 +30,18 @@ using namespace caret; /** * 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 @@ EventGraphicsUpdateOneWindow::~EventGraphicsUpdateOneWindow() } +/** + * @return Indicates a repaint (instead of updates) is + * to be performed. + */ +bool +EventGraphicsUpdateOneWindow::isRepaint() const +{ + return m_doRepaintFlag; +} + diff --git a/src/GuiQt/EventGraphicsUpdateOneWindow.h b/src/GuiQt/EventGraphicsUpdateOneWindow.h index ca67473abe5a1d4982e2bbc2775e6d7f26e10a19..09cc076b5e749fe5af25edc77efb3f0204ad287c 100644 --- a/src/GuiQt/EventGraphicsUpdateOneWindow.h +++ b/src/GuiQt/EventGraphicsUpdateOneWindow.h @@ -30,13 +30,16 @@ namespace caret { 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 @@ namespace caret { /** index of window for update */ int32_t windowIndex; + + bool m_doRepaintFlag = false; }; } // namespace diff --git a/src/GuiQt/EventGraphicsWindowShowToolTip.cxx b/src/GuiQt/EventGraphicsWindowShowToolTip.cxx new file mode 100644 index 0000000000000000000000000000000000000000..bb0c01ccaa39a280c6d64c2ff88e2f9a0eacc391 --- /dev/null +++ b/src/GuiQt/EventGraphicsWindowShowToolTip.cxx @@ -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 --git a/src/GuiQt/EventGraphicsWindowShowToolTip.h b/src/GuiQt/EventGraphicsWindowShowToolTip.h new file mode 100644 index 0000000000000000000000000000000000000000..bf835700815f76d5aed907c1c6d8311a09c1384f --- /dev/null +++ b/src/GuiQt/EventGraphicsWindowShowToolTip.h @@ -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 --git a/src/GuiQt/EventUpdateInformationWindows.cxx b/src/GuiQt/EventUpdateInformationWindows.cxx index b41b09bfccf78af815e474e150952480f729858a..369ae29e7843210188b51a8fc4c2485a80de4ed7 100644 --- a/src/GuiQt/EventUpdateInformationWindows.cxx +++ b/src/GuiQt/EventUpdateInformationWindows.cxx @@ -46,7 +46,8 @@ EventUpdateInformationWindows::EventUpdateInformationWindows(const AString& info 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 --git a/src/GuiQt/FiberOrientationSelectionViewController.cxx b/src/GuiQt/FiberOrientationSelectionViewController.cxx index 07c650e6ac4db2f6cacd20f7bc3c951874c88e9b..5262634650a554e64fa8ca04a04635a1a62d48ab 100644 --- a/src/GuiQt/FiberOrientationSelectionViewController.cxx +++ b/src/GuiQt/FiberOrientationSelectionViewController.cxx @@ -172,6 +172,15 @@ FiberOrientationSelectionViewController::createAttributesWidget() 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 @@ FiberOrientationSelectionViewController::createAttributesWidget() 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 @@ FiberOrientationSelectionViewController::processAttributesChanges() browserTabIndex, m_minimumMagnitudeSpinBox->value()); + dpfo->setMaximumUncertainty(displayGroup, + browserTabIndex, + m_maximumUncertaintySpinBox->value()); + dpfo->setLengthMultiplier(displayGroup, browserTabIndex, m_lengthMultiplierSpinBox->value()); @@ -428,6 +444,8 @@ FiberOrientationSelectionViewController::updateViewController() 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 --git a/src/GuiQt/FiberOrientationSelectionViewController.h b/src/GuiQt/FiberOrientationSelectionViewController.h index d1915e15ccf2155ce10d775beacb93dad2345938..8431711528d85a86dd044a16e3e444e277864bf5 100644 --- a/src/GuiQt/FiberOrientationSelectionViewController.h +++ b/src/GuiQt/FiberOrientationSelectionViewController.h @@ -96,6 +96,8 @@ namespace caret { QDoubleSpinBox* m_minimumMagnitudeSpinBox; + QDoubleSpinBox* m_maximumUncertaintySpinBox; + QDoubleSpinBox* m_lengthMultiplierSpinBox; QDoubleSpinBox* m_fanMultiplierSpinBox; diff --git a/src/GuiQt/FociPropertiesEditorDialog.cxx b/src/GuiQt/FociPropertiesEditorDialog.cxx index f3d71332e1a4f21045ceb1f8f8963fa3713a191d..7174c0297eb639d5720c0705d2ce87fea7a63085 100644 --- a/src/GuiQt/FociPropertiesEditorDialog.cxx +++ b/src/GuiQt/FociPropertiesEditorDialog.cxx @@ -409,7 +409,7 @@ FociPropertiesEditorDialog::loadFociFileComboBox(const FociFile* selectedFociFil 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 @@ FociPropertiesEditorDialog::okButtonClicked() 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 --git a/src/GuiQt/FociSelectionViewController.cxx b/src/GuiQt/FociSelectionViewController.cxx index e5888c340c241003b1f626e16c727ef077f2a7c8..4d3177ade4d46d746500ea2bd05e40c58c709ce0 100644 --- a/src/GuiQt/FociSelectionViewController.cxx +++ b/src/GuiQt/FociSelectionViewController.cxx @@ -186,6 +186,18 @@ FociSelectionViewController::createAttributesWidget() 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 @@ FociSelectionViewController::createAttributesWidget() 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 @@ FociSelectionViewController::createAttributesWidget() 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 @@ FociSelectionViewController::createAttributesWidget() 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 @@ FociSelectionViewController::processAttributesChanges() 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 @@ FociSelectionViewController::processAttributesChanges() 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); @@ -365,6 +397,38 @@ FociSelectionViewController::fociDisplayGroupSelected(const DisplayGroupEnum::En processFociSelectionChanges(); } +/** + * 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. */ @@ -418,6 +482,8 @@ FociSelectionViewController::updateFociViewController() 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 @@ FociSelectionViewController::updateFociViewController() } 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 --git a/src/GuiQt/FociSelectionViewController.h b/src/GuiQt/FociSelectionViewController.h index 974be517767943200b81086d063625ec1742fcd8..3effaa50344f4c0c4b85d09d53d05f91ef39ab7e 100644 --- a/src/GuiQt/FociSelectionViewController.h +++ b/src/GuiQt/FociSelectionViewController.h @@ -70,6 +70,8 @@ namespace caret { void processAttributesChanges(); + void symbolSizeTypeComboBoxActivated(); + private: FociSelectionViewController(const FociSelectionViewController&); @@ -97,12 +99,16 @@ namespace caret { 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 --git a/src/GuiQt/GestureEvent.cxx b/src/GuiQt/GestureEvent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5156ea42f2f5041c03f27643ae54c52254f1b4c4 --- /dev/null +++ b/src/GuiQt/GestureEvent.cxx @@ -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 --git a/src/GuiQt/GestureEvent.h b/src/GuiQt/GestureEvent.h new file mode 100644 index 0000000000000000000000000000000000000000..84e073b64f8d23c7812add15aaf92b543edd6475 --- /dev/null +++ b/src/GuiQt/GestureEvent.h @@ -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 --git a/src/GuiQt/GiftiLabelTableEditor.cxx b/src/GuiQt/GiftiLabelTableEditor.cxx index 9c8656d90a711cb292371b0bf51d987bb5712b8f..5fc07d1a1676086b1cdbd635d1581dd9d107adde 100644 --- a/src/GuiQt/GiftiLabelTableEditor.cxx +++ b/src/GuiQt/GiftiLabelTableEditor.cxx @@ -642,7 +642,7 @@ GiftiLabelTableEditor::loadLabels(const AString& selectedNameIn, setWidgetItemIconColor(colorItem, rgba); colorItem->setData(Qt::UserRole, - qVariantFromValue((void*)gl)); + QVariant::fromValue((void*)gl)); m_labelSelectionListWidget->addItem(colorItem); if (selectedName == gl->getName()) { diff --git a/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx b/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx index 19824199b7c426d55e1e8e51e0ef68fecafd52b7..0d2203b0c667fe8b751a1e92147fc9059475d422 100644 --- a/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx +++ b/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx @@ -161,7 +161,7 @@ GiftiLabelTableSelectionComboBox::rowsWereInserted(const QModelIndex& /*parent*/ 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 @@ GiftiLabelTableSelectionComboBox::updateContent(GiftiLabelTable* giftiLabelTable 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 @@ void 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 --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index c6396783baa633e444362144c11802b1ea5f9d29..798bbb52f2883604cfec642a00f6ba5fecbb1291 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -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 @@ GuiManager::initializeGuiManager() 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 @@ GuiManager::initializeGuiManager() m_surfacePropertiesEditorDialog = NULL; m_volumePropertiesEditorDialog = NULL; m_tileTabsConfigurationDialog = NULL; + m_paletteEditorDialog = NULL; this->cursorManager = new CursorManager(); @@ -207,6 +212,8 @@ GuiManager::initializeGuiManager() "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 @@ GuiManager::initializeGuiManager() 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))); @@ -304,6 +313,24 @@ GuiManager::initializeGuiManager() WuQMacroManager::instance()->addMacroSupportToObject(m_sceneDialogDisplayAction, "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 */ @@ -342,7 +369,6 @@ GuiManager::initializeGuiManager() 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 @@ GuiManager::~GuiManager() 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 @@ GuiManager::allowBrainBrowserWindowToClose(BrainBrowserWindow* brainBrowserWindo this->reparentNonModalDialogs(brainBrowserWindow); } } - - EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); } return isBrowserWindowAllowedToClose; @@ -674,7 +706,7 @@ GuiManager::newBrainBrowserWindow(QWidget* parent, 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 @@ GuiManager::newBrainBrowserWindow(QWidget* parent, 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 @@ GuiManager::receiveEvent(Event* event) /* * 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 @@ GuiManager::receiveEvent(Event* event) 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 @@ GuiManager::receiveEvent(Event* event) 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 @@ GuiManager::receiveEvent(Event* event) overlayEditorIter != m_overlaySettingsEditors.end(); overlayEditorIter++) { OverlaySettingsEditorDialog* med = *overlayEditorIter; - if (med->isDoNotReplaceSelected() == false) { + if ( ! med->isDoNotReplaceSelected()) { overlayEditor = med; break; } @@ -1538,26 +1573,8 @@ GuiManager::receiveEvent(Event* event) 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 @@ GuiManager::receiveEvent(Event* event) if (m_movieRecordingDialog != NULL) { m_movieRecordingDialog->updateDialog(); } + updateInformationDisplayDialogAction(); } } @@ -1820,6 +1838,97 @@ GuiManager::processShowVolumePropertiesEditorDialog(BrainBrowserWindow* browserW } } +/** + * 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. */ @@ -1946,6 +2055,7 @@ GuiManager::processShowSceneDialog(BrainBrowserWindow* browserWindowIn) * @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 @@ GuiManager::processShowSceneDialogAndScene(BrainBrowserWindow* browserWindow, 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 @@ GuiManager::processShowInformationWindow() 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 @@ GuiManager::showHideInfoWindowSelected(bool status) 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 @@ GuiManager::showHideIdentfyBrainordinateDialog(const bool status, } 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 @@ GuiManager::addParentLessNonModalDialog(QWidget* dialog) 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 @@ -2598,6 +2757,24 @@ GuiManager::processShowConnectomeDataBaseWebView(BrainBrowserWindow* /*browserWi // this->connectomeDatabaseWebView->show(); } +/** + * 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 */ @@ -2675,9 +2852,31 @@ GuiManager::saveToScene(const SceneAttributes* sceneAttributes, /* * 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 @@ GuiManager::restoreFromScene(const SceneAttributes* sceneAttributes, */ 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 @@ GuiManager::restoreFromScene(const SceneAttributes* sceneAttributes, 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) { @@ -2907,6 +3108,39 @@ GuiManager::restoreFromScene(const SceneAttributes* sceneAttributes, } } + /* + * 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 */ @@ -2917,7 +3151,7 @@ GuiManager::restoreFromScene(const SceneAttributes* sceneAttributes, 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 @@ GuiManager::processIdentification(const int32_t tabIndex, 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 @@ GuiManager::processIdentification(const int32_t tabIndex, * 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 @@ GuiManager::processIdentification(const int32_t tabIndex, /* * 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 @@ GuiManager::processIdentification(const int32_t tabIndex, ciftiConnectivityManager->loadDataForSurfaceNode(brain, surface, nodeIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); ciftiFiberTrajectoryManager->loadDataForSurfaceNode(brain, surface, nodeIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); chartingDataManager->loadChartForSurfaceNode(surface, nodeIndex); @@ -3162,10 +3401,12 @@ GuiManager::processIdentification(const int32_t tabIndex, 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 @@ GuiManager::processIdentification(const int32_t tabIndex, ciftiParcelFile, rowIndex, columnIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); } catch (const DataFileException& e) { @@ -3265,7 +3507,8 @@ GuiManager::processIdentification(const int32_t tabIndex, ciftiConnectivityManager->loadRowOrColumnFromConnectivityMatrixFile(matrixFile, rowIndex, columnIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); updateGraphicsFlag = true; } @@ -3349,7 +3592,8 @@ GuiManager::processIdentification(const int32_t tabIndex, ciftiParcelFile, rowIndex, colIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); } catch (const DataFileException& e) { @@ -3369,7 +3613,8 @@ GuiManager::processIdentification(const int32_t tabIndex, ciftiConnectivityManager->loadRowOrColumnFromConnectivityMatrixFile(matrixFile, rowIndex, colIndex, - ciftiLoadingInfo); + ciftiLoadingInfo, + ciftiLoadingInfoTableBuilder); updateGraphicsFlag = true; } @@ -3435,10 +3680,23 @@ GuiManager::processIdentification(const int32_t tabIndex, } } + 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 @@ GuiManager::processIdentification(const int32_t tabIndex, } identifiedItem = new IdentifiedItemNode(identificationMessage, + formattedIdentificationMessage, surface->getStructure(), surface->getNumberOfNodes(), nodeIndex); @@ -3477,8 +3736,8 @@ GuiManager::processIdentification(const int32_t tabIndex, * 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 @@ GuiManager::processIdentification(const int32_t tabIndex, 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 @@ GuiManager::processIdentification(const int32_t tabIndex, } 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 @@ GuiManager::processIdentification(const int32_t tabIndex, 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 @@ GuiManager::processIdentification(const int32_t tabIndex, } } +/** + * 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 --git a/src/GuiQt/GuiManager.h b/src/GuiQt/GuiManager.h index 080280235b09133e280e696409ac0694b30bfbdb..c607af3b94b293f36aa4ceec225dfe7a1fa92e61 100644 --- a/src/GuiQt/GuiManager.h +++ b/src/GuiQt/GuiManager.h @@ -46,19 +46,22 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { void updateAnimationStartTime(double value); + void processShowPaletteEditorDialog(BrainBrowserWindow* browserWindow); + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -182,6 +190,9 @@ namespace caret { SelectionManager* selectionManager, QWidget* parentWidget); + void processReopenLastClosedTab(BrainBrowserWindow* parentWindow); + + /* * Mode used when testing for modified files */ @@ -211,6 +222,12 @@ namespace caret { void sceneDialogDisplayActionTriggered(bool); + void sceneDialogDisplayMenuHovered(QAction* action); + + void sceneDialogDisplayMenuTriggered(QAction* action); + + void sceneDialogDisplayMenuAboutToShow(); + void showHelpDialogActionToggled(bool); private slots: @@ -252,6 +269,10 @@ namespace caret { QPixmap createDataToolTipsIcon(const QWidget* widget); + void processOpenDataFileEvent(EventOperatingSystemRequestOpenDataFile* openDataFileEvent); + + void updateInformationDisplayDialogAction(); + /** One instance of the GuiManager */ static GuiManager* singletonGuiManager; @@ -267,12 +288,12 @@ namespace caret { /** 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/IdentificationDisplayDialog.cxx b/src/GuiQt/IdentificationDisplayDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..34870fb120d467fee669780f58c53315175e3060 --- /dev/null +++ b/src/GuiQt/IdentificationDisplayDialog.cxx @@ -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 --git a/src/GuiQt/IdentificationDisplayDialog.h b/src/GuiQt/IdentificationDisplayDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..00430ae364c813907e3f0243e3303234f09b1502 --- /dev/null +++ b/src/GuiQt/IdentificationDisplayDialog.h @@ -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 --git a/src/GuiQt/IdentificationDisplayWidget.cxx b/src/GuiQt/IdentificationDisplayWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6a79a54b60921b7d6032f55d4cb5e0356eca3b92 --- /dev/null +++ b/src/GuiQt/IdentificationDisplayWidget.cxx @@ -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 --git a/src/GuiQt/IdentificationDisplayWidget.h b/src/GuiQt/IdentificationDisplayWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..bc835ffdd9f58697eb672e80092f06d5757357ba --- /dev/null +++ b/src/GuiQt/IdentificationDisplayWidget.h @@ -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 --git a/src/GuiQt/IdentificationFileFilteringRow.cxx b/src/GuiQt/IdentificationFileFilteringRow.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0226ad413728af23f50f37d85c1ea1f8be0e6e81 --- /dev/null +++ b/src/GuiQt/IdentificationFileFilteringRow.cxx @@ -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 --git a/src/GuiQt/IdentificationFileFilteringRow.h b/src/GuiQt/IdentificationFileFilteringRow.h new file mode 100644 index 0000000000000000000000000000000000000000..60b54078a25ca2edf7525dbbdb3b1309fe2db830 --- /dev/null +++ b/src/GuiQt/IdentificationFileFilteringRow.h @@ -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 --git a/src/GuiQt/IdentificationFileFilteringTableWidget.cxx b/src/GuiQt/IdentificationFileFilteringTableWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0f6acfd67ad359a6a509dfe7218b7a97b3beaa31 --- /dev/null +++ b/src/GuiQt/IdentificationFileFilteringTableWidget.cxx @@ -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 --git a/src/GuiQt/IdentificationFileFilteringTableWidget.h b/src/GuiQt/IdentificationFileFilteringTableWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..1cf0cf357acd9636aa26f65e0476a9b5d501a8de --- /dev/null +++ b/src/GuiQt/IdentificationFileFilteringTableWidget.h @@ -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 --git a/src/GuiQt/ImageCaptureDialog.cxx b/src/GuiQt/ImageCaptureDialog.cxx index c1460f2d865b7c3eef35d8813a69a2138f6e827f..43ccc1617bcc4b79b0780a39f3b0909de3067c3a 100644 --- a/src/GuiQt/ImageCaptureDialog.cxx +++ b/src/GuiQt/ImageCaptureDialog.cxx @@ -64,8 +64,6 @@ using namespace caret; - - /** * \class caret::ImageCaptureDialog * \brief Dialog for capturing images. @@ -973,8 +971,8 @@ ImageCaptureDialog::selectImagePushButtonPressed() 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 @@ ImageCaptureDialog::selectImagePushButtonPressed() 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 @@ ImageCaptureDialog::applyButtonClicked() } 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 @@ ImageCaptureDialog::applyButtonClicked() } 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 --git a/src/GuiQt/ImageSelectionViewController.cxx b/src/GuiQt/ImageSelectionViewController.cxx index 53661617cda2beab26652a1262ca1b4cc9ccc528..2c9ec7e7408f41960d61a997a6b26c5cffc07550 100644 --- a/src/GuiQt/ImageSelectionViewController.cxx +++ b/src/GuiQt/ImageSelectionViewController.cxx @@ -442,6 +442,7 @@ ImageSelectionViewController::updateImageViewController() 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 @@ ImageSelectionViewController::updateImageViewController() 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 --git a/src/GuiQt/KeyEvent.cxx b/src/GuiQt/KeyEvent.cxx index 292b8b3f5e8dc300919a1ce74305bcc6d0874f41..62fe7d60b70a12dea95e8230ada7e505f85c3599 100644 --- a/src/GuiQt/KeyEvent.cxx +++ b/src/GuiQt/KeyEvent.cxx @@ -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 @@ using namespace caret; /** * 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 @@ m_shiftKeyDownFlag(shiftKeyDownFlag) */ KeyEvent::~KeyEvent() { + if (m_viewportContent != NULL) { + delete m_viewportContent; + } } /** @@ -73,6 +117,15 @@ KeyEvent::initializeMembersKeyEvent() { } +/** + * @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. */ @@ -137,4 +190,18 @@ KeyEvent::isShiftKeyDownFlag() const 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 --git a/src/GuiQt/KeyEvent.h b/src/GuiQt/KeyEvent.h index 474074cbd9cf670bb1f83903593a26e24d9b8727..69f77ecdc2a00a6cdb7c1763c851df002224f223 100644 --- a/src/GuiQt/KeyEvent.h +++ b/src/GuiQt/KeyEvent.h @@ -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 @@ namespace caret { 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 @@ namespace caret { public: AString toString() const; + BrainOpenGLViewportContent* getViewportContent() const; + BrainOpenGLWidget* getOpenGLWidget() const; int32_t getBrowserWindowIndex() const; @@ -65,16 +73,27 @@ namespace caret { 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 --git a/src/GuiQt/MacApplication.cxx b/src/GuiQt/MacApplication.cxx index 1b761fff4f39ee2669b43e60bb18c7a578b671e0..43e9041147a5ad96c7541c4bca6b68a0dee2a8c8 100644 --- a/src/GuiQt/MacApplication.cxx +++ b/src/GuiQt/MacApplication.cxx @@ -31,6 +31,7 @@ #include "CaretAssert.h" #include "EventManager.h" #include "EventOperatingSystemRequestOpenDataFile.h" +#include "GuiManager.h" using namespace caret; @@ -85,6 +86,17 @@ MacApplication::event(QEvent *event) EventManager::get()->sendEvent(EventOperatingSystemRequestOpenDataFile(filename).getPointer()); + 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; diff --git a/src/GuiQt/MacDockMenu.cxx b/src/GuiQt/MacDockMenu.cxx index 152ddecfcf20aeff1a53f376af683e0257464e3b..135afd60000dd800e3fcf374814449a45d6a0882 100644 --- a/src/GuiQt/MacDockMenu.cxx +++ b/src/GuiQt/MacDockMenu.cxx @@ -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 @@ extern void qt_mac_set_dock_menu(QMenu *menu); /** * 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(QWidget* parent) */ 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() { - 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); + QStringList noParameters; + QString noWorkingDirectory; + QWidget* parentWidget = GuiManager::get()->getActiveBrowserWindow(); + GuiManager::startNewWbViewInstance(noParameters, + noWorkingDirectory, + parentWidget); +} + +/** + * Add actions for selecting browser windows + */ +void +MacDockMenu::addBrowserWindowActions() +{ /* - * 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 --git a/src/GuiQt/MacDockMenu.h b/src/GuiQt/MacDockMenu.h index 64a7a32db02d406e09bdd1f04332077244217928..69dd6821fe706af464a1ea50bc88c4d7578c029f 100644 --- a/src/GuiQt/MacDockMenu.h +++ b/src/GuiQt/MacDockMenu.h @@ -33,40 +33,31 @@ namespace caret { 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 --git a/src/GuiQt/MacDuplicateMenuBar.cxx b/src/GuiQt/MacDuplicateMenuBar.cxx deleted file mode 100644 index d0f0b832a4123d87694555f7cedff3c50a2fc613..0000000000000000000000000000000000000000 --- a/src/GuiQt/MacDuplicateMenuBar.cxx +++ /dev/null @@ -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 --git a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx index 02fff7927a3c82021767ce187ce51c3b43bccecb..51616e8776655569900ea69c03566248edb89312 100644 --- a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx +++ b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx @@ -403,7 +403,7 @@ MapSettingsChartTwoLineHistoryWidget::loadHistoryIntoTableWidget(ChartTwoLineSer : 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 @@ MapSettingsChartTwoLineHistoryWidget::colorItemSelected(int rowIndex) 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 --git a/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx b/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx index d66cf0d84b3c7df8745afc52ed2296271c4af2f0..21ad1c524e8ee7ae74ec77fc8fb2c6c559df24b2 100644 --- a/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx +++ b/src/GuiQt/MapSettingsColorBarPaletteOptionsWidget.cxx @@ -57,12 +57,18 @@ MapSettingsColorBarPaletteOptionsWidget::MapSettingsColorBarPaletteOptionsWidget : 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 --git a/src/GuiQt/MapSettingsColorBarWidget.cxx b/src/GuiQt/MapSettingsColorBarWidget.cxx index 5eed2fe4d9a6034d74aeaab227a9fac37854c48e..a722bff26d0d08b3b51afda2a64cd7a1cd2601c7 100644 --- a/src/GuiQt/MapSettingsColorBarWidget.cxx +++ b/src/GuiQt/MapSettingsColorBarWidget.cxx @@ -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 @@ MapSettingsColorBarWidget::updateContentPrivate() 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 @@ MapSettingsColorBarWidget::annotationColorBarPositionModeEnumComboBoxItemActivat 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 @@ MapSettingsColorBarWidget::createLocationPositionSection() " 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 --git a/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx b/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx index 6d730e458bbc3267ce8018954239fc13f1b339e4..66da268183700401e375e18b983b9e443e4940a6 100644 --- a/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx +++ b/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx @@ -355,7 +355,7 @@ MapSettingsFiberTrajectoryWidget::updateEditor(CiftiFiberTrajectoryFile* fiberTr defaultIndex = i; } m_colorSelectionComboBox->addItem(item->getName(), - qVariantFromValue((void*)item)); + QVariant::fromValue((void*)item)); } if ((defaultIndex >= 0) && (defaultIndex < m_colorSelectionComboBox->count())) { diff --git a/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx b/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx index 2bc51f4ab8acf19310c8ca8c7496cf065a9853f5..d8b7a1643994f1d143f64968d8286fc2ba58504d 100644 --- a/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx +++ b/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx @@ -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 @@ MapSettingsPaletteColorMappingWidget::MapSettingsPaletteColorMappingWidget(QWidg layout->addWidget(leftWidget, 0); layout->addWidget(rightWidget, 100); + updatePaletteNameComboBox(); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } @@ -848,14 +851,12 @@ MapSettingsPaletteColorMappingWidget::createHistogramControlSection() 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 @@ MapSettingsPaletteColorMappingWidget::createHistogramControlSection() 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 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() /* * 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 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() 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 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() 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 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-BIG_NUMBER, 0.0, 1.0, - 2, + fixedDigitsRightOfDecimal, this, SLOT(scaleFixedNegativeMaximumValueChanged(double))); @@ -1490,7 +1492,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-BIG_NUMBER, 0.0, 1.0, - 2, + fixedDigitsRightOfDecimal, this, SLOT(scaleFixedNegativeMinimumValueChanged(double))); @@ -1503,7 +1505,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, BIG_NUMBER, 1.0, - 2, + fixedDigitsRightOfDecimal, this, SLOT(scaleFixedPositiveMinimumValueChanged(double))); @@ -1516,7 +1518,7 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, BIG_NUMBER, 1.0, - 2, + fixedDigitsRightOfDecimal, this, SLOT(scaleFixedPositiveMaximumValueChanged(double))); @@ -1540,9 +1542,9 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() 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 @@ MapSettingsPaletteColorMappingWidget::createPaletteSection() 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 @@ MapSettingsPaletteColorMappingWidget::updatePaletteMappedToDataValueLabels() } } - 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 @@ MapSettingsPaletteColorMappingWidget::updateEditorInternal(CaretMappableDataFile + 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 --git a/src/GuiQt/MapSettingsPaletteColorMappingWidget.h b/src/GuiQt/MapSettingsPaletteColorMappingWidget.h index 487392fc631e6edc61afd147b72582353a355725..d921978267c22129e0538a526a7c8407f67b3caf 100644 --- a/src/GuiQt/MapSettingsPaletteColorMappingWidget.h +++ b/src/GuiQt/MapSettingsPaletteColorMappingWidget.h @@ -132,6 +132,8 @@ namespace caret { const Histogram* getHistogram(const FastStatistics* statisticsForAll) const; + void updatePaletteNameComboBox(); + PaletteColorMapping* paletteColorMapping; QComboBox* paletteNameComboBox; diff --git a/src/GuiQt/MediaOverlaySetViewController.cxx b/src/GuiQt/MediaOverlaySetViewController.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1b84b98b1b4d3fd49f40cb405883b976b1541cf4 --- /dev/null +++ b/src/GuiQt/MediaOverlaySetViewController.cxx @@ -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 --git a/src/GuiQt/MediaOverlaySetViewController.h b/src/GuiQt/MediaOverlaySetViewController.h new file mode 100644 index 0000000000000000000000000000000000000000..aac60300df933eb303e3f9eb50515e2ce2f0c8ff --- /dev/null +++ b/src/GuiQt/MediaOverlaySetViewController.h @@ -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 --git a/src/GuiQt/MediaOverlayViewController.cxx b/src/GuiQt/MediaOverlayViewController.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d8bb5fd88969fef018f27bec08ff043bf3254f04 --- /dev/null +++ b/src/GuiQt/MediaOverlayViewController.cxx @@ -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 --git a/src/GuiQt/MediaOverlayViewController.h b/src/GuiQt/MediaOverlayViewController.h new file mode 100644 index 0000000000000000000000000000000000000000..619164b31a3b3477b76440a6e123c25c7010fbb1 --- /dev/null +++ b/src/GuiQt/MediaOverlayViewController.h @@ -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 --git a/src/GuiQt/MouseEvent.cxx b/src/GuiQt/MouseEvent.cxx index 5a7c0b04678512d0a76bec6628a1e168e7baf84c..5c058ca72a6698bef0039338b91c67f863d08c58 100644 --- a/src/GuiQt/MouseEvent.cxx +++ b/src/GuiQt/MouseEvent.cxx @@ -54,6 +54,8 @@ using namespace caret; * 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 @@ MouseEvent::MouseEvent(const BrainOpenGLViewportContent* viewportContent, const int32_t dy, const int32_t mousePressX, const int32_t mousePressY, + const std::vector& mouseHistoryXY, const bool firstDraggingFlag) : CaretObject() { @@ -87,6 +90,7 @@ MouseEvent::MouseEvent(const BrainOpenGLViewportContent* viewportContent, m_dy = dy; m_pressX = mousePressX; m_pressY = mousePressY; + m_xyHistory = mouseHistoryXY; m_firstDraggingFlag = firstDraggingFlag; } @@ -295,6 +299,27 @@ MouseEvent::getPressedY() const return m_pressY; } +/** + * @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 --git a/src/GuiQt/MouseEvent.h b/src/GuiQt/MouseEvent.h index 984794c7ea2cfbfb8d645894424e13379cf09b9a..29d8e5229c8c5b384392e10b56775de4b1bc69ef 100644 --- a/src/GuiQt/MouseEvent.h +++ b/src/GuiQt/MouseEvent.h @@ -38,6 +38,20 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { int32_t m_pressY; + std::vector m_xyHistory; + int32_t m_wheelRotation; bool m_firstDraggingFlag; diff --git a/src/GuiQt/MovieDialog.cxx b/src/GuiQt/MovieDialog.cxx index cd96c99067cc549c26b3ed6d7a0685a10ee2f8f2..5810d7550d21fc4b2fdddde547a7944e5b4348d3 100644 --- a/src/GuiQt/MovieDialog.cxx +++ b/src/GuiQt/MovieDialog.cxx @@ -229,7 +229,7 @@ void MovieDialog::on_recordButton_toggled(bool checked) // 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 @@ void MovieDialog::on_recordButton_toggled(bool checked) 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 @@ void MovieDialog::captureFrame(AString filename) } if (validExtension == false) { - if (defaultFileExtension.isEmpty() == false) { - filename += ("." + defaultFileExtension); + if (defaultImageExtension.isEmpty() == false) { + filename += ("." + defaultImageExtension); } } diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 192405dc7839188136f9a7a7d060a4f13d664109..1ada4bba0e48e9e283a93aa4055fbcaa48b0c03e 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -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 @@ MovieRecordingDialog::createMoviePrivate(QWidget* parent, AString errorMessage; const bool successFlag = movieRecorder->createMovie(filename, errorMessage); - if ( ! successFlag) { + if (successFlag) { + SessionManager::get()->getCaretPreferences()->addToRecentFilesAndOrDirectories(filename); + } + else { WuQMessageBox::errorOk(parent, errorMessage); } diff --git a/src/GuiQt/OverlaySettingsEditorDialog.cxx b/src/GuiQt/OverlaySettingsEditorDialog.cxx index 675ccfe0fcad2c8391d095035d30b58bc6cf00b1..3a26aa2fedcc7cf7710d2f0ebee10c455ccdbebf 100644 --- a/src/GuiQt/OverlaySettingsEditorDialog.cxx +++ b/src/GuiQt/OverlaySettingsEditorDialog.cxx @@ -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 @@ OverlaySettingsEditorDialog::OverlaySettingsEditorDialog(QWidget* parent) "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 @@ OverlaySettingsEditorDialog::OverlaySettingsEditorDialog(QWidget* parent) this->setLayoutSpacingAndMargins(layout); layout->addWidget(mapNameWidget); layout->addWidget(m_tabWidget); - //layout->addWidget(windowOptionsWidget); this->setCentralWidget(w, WuQDialog::SCROLL_AREA_NEVER); @@ -310,7 +308,7 @@ OverlaySettingsEditorDialog::updateDialogContentPrivate(Overlay* brainordinateOv bool isParcelsValid = false; bool isFiberTrajectoryValid = false; bool isVolumeLayer = false; - bool isLinesValid = false; + bool isLineHistoryValid = false; QString mapFileName = ""; QString mapName = ""; @@ -407,14 +405,18 @@ OverlaySettingsEditorDialog::updateDialogContentPrivate(Overlay* brainordinateOv 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 @@ OverlaySettingsEditorDialog::updateDialogContentPrivate(Overlay* brainordinateOv } } 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 @@ OverlaySettingsEditorDialog::updateDialogContentPrivate(Overlay* brainordinateOv 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 @@ OverlaySettingsEditorDialog::updateDialog() } 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 @@ void 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 --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 2e006084e62b4518395dc62c0f060c59bd55cd36..429d1ef26f616704cc476901277e29e41d90cb59 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -174,8 +174,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, + "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 @@ OverlayViewController::updateViewController(Overlay* overlay) 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 --git a/src/GuiQt/PaletteCreateNewDialog.cxx b/src/GuiQt/PaletteCreateNewDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e8527820bc361273d268fb4b7358ec5fd6d01802 --- /dev/null +++ b/src/GuiQt/PaletteCreateNewDialog.cxx @@ -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 --git a/src/GuiQt/PaletteCreateNewDialog.h b/src/GuiQt/PaletteCreateNewDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..5ddb1d678742f3a5dad8b434b650e177b3c2eb28 --- /dev/null +++ b/src/GuiQt/PaletteCreateNewDialog.h @@ -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 --git a/src/GuiQt/PaletteEditorDialog.cxx b/src/GuiQt/PaletteEditorDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4528a70cc6658a0e6cdc2f61fbdeac172b08b888 --- /dev/null +++ b/src/GuiQt/PaletteEditorDialog.cxx @@ -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 --git a/src/GuiQt/PaletteEditorDialog.h b/src/GuiQt/PaletteEditorDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..2c503afbd4994d2328296f86ebc95fa7e5db8ff5 --- /dev/null +++ b/src/GuiQt/PaletteEditorDialog.h @@ -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 --git a/src/GuiQt/PaletteEditorRangeRow.cxx b/src/GuiQt/PaletteEditorRangeRow.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a203b69adbaa2c323d5ada6fa9e84bf51c0a1729 --- /dev/null +++ b/src/GuiQt/PaletteEditorRangeRow.cxx @@ -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 --git a/src/GuiQt/PaletteEditorRangeRow.h b/src/GuiQt/PaletteEditorRangeRow.h new file mode 100644 index 0000000000000000000000000000000000000000..4ad838dcce1442b25a1f8cfcbe82796c4599493a --- /dev/null +++ b/src/GuiQt/PaletteEditorRangeRow.h @@ -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 --git a/src/GuiQt/PaletteEditorRangeWidget.cxx b/src/GuiQt/PaletteEditorRangeWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..65ccf3eafb14320f2392abf54d8aa46232fbd226 --- /dev/null +++ b/src/GuiQt/PaletteEditorRangeWidget.cxx @@ -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 --git a/src/GuiQt/PaletteEditorRangeWidget.h b/src/GuiQt/PaletteEditorRangeWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..de887a8f357001ff48d18acca1ee86e24f8a043f --- /dev/null +++ b/src/GuiQt/PaletteEditorRangeWidget.h @@ -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 --git a/src/GuiQt/PalettePixmapPainter.cxx b/src/GuiQt/PalettePixmapPainter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..127481b147323d65a2fcbe04bc22dde52f4cde14 --- /dev/null +++ b/src/GuiQt/PalettePixmapPainter.cxx @@ -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 --git a/src/GuiQt/PalettePixmapPainter.h b/src/GuiQt/PalettePixmapPainter.h new file mode 100644 index 0000000000000000000000000000000000000000..620f8e7927deb32055863d7eca44f3eccb91bf7e --- /dev/null +++ b/src/GuiQt/PalettePixmapPainter.h @@ -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 --git a/src/GuiQt/PaletteSelectionWidget.cxx b/src/GuiQt/PaletteSelectionWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2c680844679de032fb81cc76aa7c13a4ec4add82 --- /dev/null +++ b/src/GuiQt/PaletteSelectionWidget.cxx @@ -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 --git a/src/GuiQt/PaletteSelectionWidget.h b/src/GuiQt/PaletteSelectionWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..f5bb198d07b6652b7399fa5e04a3d206ba57cc8c --- /dev/null +++ b/src/GuiQt/PaletteSelectionWidget.h @@ -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 --git a/src/GuiQt/PreferencesDialog.cxx b/src/GuiQt/PreferencesDialog.cxx index c7084ce82d6cc8fe1b4c7765306a78c3dfaaa550..749b387dc4ac986373bc449e152e7fef3b5fb84e 100644 --- a/src/GuiQt/PreferencesDialog.cxx +++ b/src/GuiQt/PreferencesDialog.cxx @@ -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 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) */ 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 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) "New Tabs"); tabWidget->addTab(createOpenGLWidget(), "OpenGL"); + tabWidget->addTab(m_recentFilesWidget, + "Recent Files"); + setCentralWidget(tabWidget, WuQDialog::SCROLL_AREA_NEVER); @@ -133,57 +142,80 @@ PreferencesDialog::addColorButtonAndSwatch(QGridLayout* gridLayout, 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 @@ PreferencesDialog::addColorButtonAndSwatch(QGridLayout* gridLayout, 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 @@ PreferencesDialog::createColorsWidget() 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 @@ PreferencesDialog::updateColorWidget(CaretPreferences* prefs) 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; @@ -384,16 +430,6 @@ PreferencesDialog::createMiscellaneousWidget() 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 */ @@ -404,6 +440,19 @@ PreferencesDialog::createMiscellaneousWidget() this, SLOT(miscDevelopMenuEnabledComboBoxChanged(bool))); 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 */ @@ -413,6 +462,26 @@ PreferencesDialog::createMiscellaneousWidget() 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 @@ PreferencesDialog::createMiscellaneousWidget() "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 @@ PreferencesDialog::updateMiscellaneousWidget(CaretPreferences* prefs) 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 @@ PreferencesDialog::createIdentificationSymbolWidget() 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 @@ PreferencesDialog::createIdentificationSymbolWidget() 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 @@ PreferencesDialog::updateIdentificationWidget(CaretPreferences* prefs) m_surfaceIdentificationSymbolComboBox->setStatus(prefs->isShowSurfaceIdentificationSymbols()); m_volumeIdentificationSymbolComboBox->setStatus(prefs->isShowVolumeIdentificationSymbols()); m_dataToolTipsComboBox->setStatus(prefs->isShowDataToolTipsEnabled()); + m_identificationModeComboBox->setSelectedItem(prefs->getIdentificationDisplayMode()); } /** @@ -532,6 +625,18 @@ PreferencesDialog::identificationSymbolToggled() prefs->setShowDataToolTipsEnabled(m_dataToolTipsComboBox->isTrue()); } +/** + * 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. */ @@ -664,7 +769,7 @@ PreferencesDialog::createTabDefaltsWidget() 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()); @@ -776,6 +881,15 @@ PreferencesDialog::addWidgetsToLayout(QGridLayout* gridLayout, } } +/** + * Called when changes are made to recent files + */ +void +PreferencesDialog::recentFilesChanged() +{ + updateDialog(); +} + /** * May be called to update the dialog's content. */ @@ -790,6 +904,7 @@ PreferencesDialog::updateDialog() updateIdentificationWidget(prefs); updateOpenGLWidget(prefs); updateVolumeWidget(prefs); + m_recentFilesWidget->updateContent(prefs); m_allWidgets->blockAllSignals(false); } @@ -807,7 +922,7 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) 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 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) 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 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) 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 @@ PreferencesDialog::volumeAxesMontageCoordinatesComboBoxToggled(bool value) * 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(); @@ -1071,17 +1200,6 @@ PreferencesDialog::yokingComboBoxToggled(bool value) prefs->setYokingDefaultedOn(value); } -/** - * 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 @@ -1107,6 +1225,42 @@ PreferencesDialog::miscDevelopMenuEnabledComboBoxChanged(bool value) EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE); } +/** + * 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. */ diff --git a/src/GuiQt/PreferencesDialog.h b/src/GuiQt/PreferencesDialog.h index 1ff954fe9cea17a4ff8f1a2583566bb0fa228dc6..b20e5af5c8737142a5e12e4e1d5c8a4cc604e43e 100644 --- a/src/GuiQt/PreferencesDialog.h +++ b/src/GuiQt/PreferencesDialog.h @@ -36,6 +36,7 @@ namespace caret { class CaretPreferences; class EnumComboBoxTemplate; + class PreferencesRecentFilesWidget; class WuQTrueFalseComboBox; class WuQWidgetObjectGroup; @@ -57,11 +58,11 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { WuQTrueFalseComboBox* m_surfaceIdentificationSymbolComboBox; WuQTrueFalseComboBox* m_volumeIdentificationSymbolComboBox; WuQTrueFalseComboBox* m_dataToolTipsComboBox; + EnumComboBoxTemplate* m_identificationModeComboBox; WuQWidgetObjectGroup* m_allWidgets; + + PreferencesRecentFilesWidget* m_recentFilesWidget; }; #ifdef __PREFERENCES_DIALOG__H__DECLARE__ diff --git a/src/GuiQt/PreferencesRecentFilesWidget.cxx b/src/GuiQt/PreferencesRecentFilesWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2a78b3458b0204733693fa54b39ecddd040e2d73 --- /dev/null +++ b/src/GuiQt/PreferencesRecentFilesWidget.cxx @@ -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 --git a/src/GuiQt/PreferencesRecentFilesWidget.h b/src/GuiQt/PreferencesRecentFilesWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..e37d23c6a802e4421298056c484be80e8fdda24a --- /dev/null +++ b/src/GuiQt/PreferencesRecentFilesWidget.h @@ -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 --git a/src/GuiQt/EventMacDockMenuUpdate.cxx b/src/GuiQt/RecentFileNameWidget.cxx similarity index 70% rename from src/GuiQt/EventMacDockMenuUpdate.cxx rename to src/GuiQt/RecentFileNameWidget.cxx index e53c40cf6c8ab3cb567e507b9ea589679b52a8f0..4ed65dcf324266510747ff19a3e8c1787826c095 100644 --- a/src/GuiQt/EventMacDockMenuUpdate.cxx +++ b/src/GuiQt/RecentFileNameWidget.cxx @@ -1,7 +1,7 @@ /*LICENSE_START*/ /* - * Copyright (C) 2015 Washington University School of Medicine + * 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 @@ -19,19 +19,17 @@ */ /*LICENSE_END*/ -#define __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ -#include "EventMacDockMenuUpdate.h" -#undef __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ +#define __RECENT_FILE_NAME_WIDGET_DECLARE__ +#include "RecentFileNameWidget.h" +#undef __RECENT_FILE_NAME_WIDGET_DECLARE__ #include "CaretAssert.h" -#include "EventTypeEnum.h" - using namespace caret; /** - * \class caret::EventMacDockMenuUpdate + * \class caret::RecentFileNameWidget * \brief * \ingroup GuiQt * @@ -41,8 +39,8 @@ using namespace caret; /** * Constructor. */ -EventMacDockMenuUpdate::EventMacDockMenuUpdate() -: Event(EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE) +RecentFileNameWidget::RecentFileNameWidget() +: QWidget() { } @@ -50,7 +48,7 @@ EventMacDockMenuUpdate::EventMacDockMenuUpdate() /** * Destructor. */ -EventMacDockMenuUpdate::~EventMacDockMenuUpdate() +RecentFileNameWidget::~RecentFileNameWidget() { } diff --git a/src/GuiQt/MacDuplicateMenuBar.h b/src/GuiQt/RecentFileNameWidget.h similarity index 63% rename from src/GuiQt/MacDuplicateMenuBar.h rename to src/GuiQt/RecentFileNameWidget.h index 6ca9bb0e95cddb835804ceb8ad6e72cac1e4d697..3899cbe529f92c4b8b99c61a2d68e8ea949843d4 100644 --- a/src/GuiQt/MacDuplicateMenuBar.h +++ b/src/GuiQt/RecentFileNameWidget.h @@ -1,5 +1,5 @@ -#ifndef __MAC_DUPLICATE_MENU_BAR_H__ -#define __MAC_DUPLICATE_MENU_BAR_H__ +#ifndef __RECENT_FILE_NAME_WIDGET_H__ +#define __RECENT_FILE_NAME_WIDGET_H__ /*LICENSE_START*/ /* @@ -27,39 +27,34 @@ #include -class QMainWindow; -class QMenu; + + namespace caret { - class MacDuplicateMenuBar : public QWidget { + class RecentFileNameWidget : public QWidget { Q_OBJECT public: - MacDuplicateMenuBar(QMainWindow* mainWindow, - QWidget* parent = 0); + RecentFileNameWidget(); - virtual ~MacDuplicateMenuBar(); + virtual ~RecentFileNameWidget(); - MacDuplicateMenuBar(const MacDuplicateMenuBar&) = delete; + RecentFileNameWidget(const RecentFileNameWidget&) = delete; - MacDuplicateMenuBar& operator=(const MacDuplicateMenuBar&) = delete; + RecentFileNameWidget& operator=(const RecentFileNameWidget&) = delete; // ADD_NEW_METHODS_HERE private: - QMenu* duplicateMenu(QMenu* menu); - - QString m_indentText; - // ADD_NEW_MEMBERS_HERE }; -#ifdef __MAC_DUPLICATE_MENU_BAR_DECLARE__ +#ifdef __RECENT_FILE_NAME_WIDGET_DECLARE__ // -#endif // __MAC_DUPLICATE_MENU_BAR_DECLARE__ +#endif // __RECENT_FILE_NAME_WIDGET_DECLARE__ } // namespace -#endif //__MAC_DUPLICATE_MENU_BAR_H__ +#endif //__RECENT_FILE_NAME_WIDGET_H__ diff --git a/src/GuiQt/RecentFilesDialog.cxx b/src/GuiQt/RecentFilesDialog.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b4c9a634606c6b0dab4e0c6fbf275142cb9a8807 --- /dev/null +++ b/src/GuiQt/RecentFilesDialog.cxx @@ -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 --git a/src/GuiQt/RecentFilesDialog.h b/src/GuiQt/RecentFilesDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..9a6a43e55e0b84a20b1349d515d469ce4847842f --- /dev/null +++ b/src/GuiQt/RecentFilesDialog.h @@ -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 --git a/src/GuiQt/RecentFilesTableWidget.cxx b/src/GuiQt/RecentFilesTableWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..13ea194357db522e86f1780dde7d5c1b88a8d4a8 --- /dev/null +++ b/src/GuiQt/RecentFilesTableWidget.cxx @@ -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 --git a/src/GuiQt/RecentFilesTableWidget.h b/src/GuiQt/RecentFilesTableWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..de3cb1a9d79e705ea0d26b3aa3d870a7160ff444 --- /dev/null +++ b/src/GuiQt/RecentFilesTableWidget.h @@ -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 --git a/src/GuiQt/ScaleBarWidget.cxx b/src/GuiQt/ScaleBarWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d548fc6842dfdf75640714b2d8e10d5e61fb6621 --- /dev/null +++ b/src/GuiQt/ScaleBarWidget.cxx @@ -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 --git a/src/GuiQt/ScaleBarWidget.h b/src/GuiQt/ScaleBarWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..2d7a27a2c0e7be2fbf70afc48cac07ff0430bc49 --- /dev/null +++ b/src/GuiQt/ScaleBarWidget.h @@ -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 --git a/src/GuiQt/SceneCreateReplaceDialog.cxx b/src/GuiQt/SceneCreateReplaceDialog.cxx index e38522e60d362382620dd5cdd506a0875b3cafdb..989dbe256598f82613bdbcafbf3ca597434ae9a5 100644 --- a/src/GuiQt/SceneCreateReplaceDialog.cxx +++ b/src/GuiQt/SceneCreateReplaceDialog.cxx @@ -233,6 +233,7 @@ SceneCreateReplaceDialog::SceneCreateReplaceDialog(const AString& dialogTitle, break; } + m_nameLineEdit->setFocus(); m_nameLineEdit->selectAll(); diff --git a/src/GuiQt/SceneDataFileTreeItemModel.cxx b/src/GuiQt/SceneDataFileTreeItemModel.cxx index 4c7ec0e6cb7153c8219cc592e1e868e9521ba9b5..3482b41c6d003f7c15e113b63a3181356fef645e 100644 --- a/src/GuiQt/SceneDataFileTreeItemModel.cxx +++ b/src/GuiQt/SceneDataFileTreeItemModel.cxx @@ -82,7 +82,7 @@ SceneDataFileTreeItemModel::SceneDataFileTreeItemModel(const AString& sceneFileP /* * Add the data files */ - for (const auto dataFileInfo : sceneDataFileInfo) { + for (const auto& dataFileInfo : sceneDataFileInfo) { AString pathName; AString pathAndFileName; switch (sortMode) { @@ -174,7 +174,7 @@ SceneDataFileTreeItemModel::addFindDirectoryPath(const AString& absoluteDirName) 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 --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index cb2a893097ddf444690963858991c34ddc837b6a..e4261b9ac87b605700a67fce3e65bb2932d3011c 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -385,11 +385,11 @@ SceneDialog::loadSceneFileComboBox(SceneFile* selectedSceneFileIn) && 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 @@ SceneDialog::replaceAllScenesPushButtonClicked() 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 @@ SceneDialog::replaceAllScenesPushButtonClicked() * Display the scene */ AString errorMessage; + AString warningMessage; SceneDialog::displayScenePrivateWithErrorMessage(sceneFile, origScene, false, - errorMessage); + errorMessage, + warningMessage); sceneNames.push_back(origScene->getName()); /* @@ -1451,6 +1454,7 @@ SceneDialog::replaceAllScenesPushButtonClicked() newImages.push_back(QImage()); } sceneErrors.push_back(errorMessage); + sceneWarnings.push_back(warningMessage); } } @@ -1460,6 +1464,7 @@ SceneDialog::replaceAllScenesPushButtonClicked() 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 @@ SceneDialog::replaceAllScenesPushButtonClicked() 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 @@ SceneDialog::testScenesPushButtonClicked() std::vector sceneNames; std::vector sceneErrors; + std::vector sceneWarnings; std::vector sceneImages; std::vector newImages; @@ -1657,10 +1670,12 @@ SceneDialog::testScenesPushButtonClicked() } AString errorMessage; + AString warningMessage; SceneDialog::displayScenePrivateWithErrorMessage(sceneFile, origScene, false, - errorMessage); + errorMessage, + warningMessage); /* * Set the active scene */ @@ -1705,6 +1720,7 @@ SceneDialog::testScenesPushButtonClicked() } sceneErrors.push_back(errorMessage); + sceneWarnings.push_back(warningMessage); } } @@ -1714,6 +1730,7 @@ SceneDialog::testScenesPushButtonClicked() 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 @@ SceneDialog::testScenesPushButtonClicked() 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 @@ SceneDialog::createSceneFileWidget() */ 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 @@ SceneDialog::createSceneFileWidget() * 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 @@ SceneDialog::deleteSceneButtonClicked() 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 @@ SceneDialog::showImagePreviewButtonClicked() 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 @@ SceneDialog::showImagePreviewButtonClicked() 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 @@ SceneDialog::displayScenePrivateWithErrorMessageDialog(QWidget* dialogParent, 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 @@ SceneDialog::displayScenePrivateWithErrorMessageDialog(QWidget* dialogParent, * 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 @@ bool 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 @@ SceneDialog::displayScenePrivateWithErrorMessage(SceneFile* sceneFile, SceneClass::setDebugLoggingEnabled(false); + warningMessageOut = sceneAttributes->getSceneLoadWarningMessage(); + cursor.restoreCursor(); /* @@ -2789,7 +2843,7 @@ SceneDialog::displayScenePrivateWithErrorMessage(SceneFile* sceneFile, */ if ( ! sceneFile->isModified()) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSceneFiles(sceneFile->getFileName()); + prefs->addToRecentFilesAndOrDirectories(sceneFile->getFileName()); } } else { @@ -2990,8 +3044,6 @@ SceneDialog::helpButtonClicked() EventManager::get()->sendEvent(helpViewerEvent.getPointer()); } - - /* ======================================================================== */ /** * \class caret::SceneClassWidget @@ -3017,7 +3069,7 @@ SceneClassInfoWidget::SceneClassInfoWidget() m_descriptionLabel = new QLabel(); m_descriptionLabel->setWordWrap(true); - + m_sceneIdLabel = new QLabel(); m_previewImageLabel = new QLabel(); @@ -3098,13 +3150,14 @@ SceneClassInfoWidget::updateContent(Scene* scene, 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 @@ SceneClassInfoWidget::updateContent(Scene* scene, } m_nameLabel->setText(nameText); m_sceneIdLabel->setText(sceneIdText); - m_descriptionLabel->setText(descriptionText); + m_descriptionLabel->setText(fullDescriptionText); QByteArray imageByteArray; AString imageBytesFormat; @@ -3197,19 +3250,18 @@ SceneClassInfoWidget::limitToNumberOfLines(AString& textLines, * 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 @@ SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(const SceneInfo + 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 @@ SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(const SceneInfo * 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 @@ SceneClassInfoWidget::isValid() const return false; } - diff --git a/src/GuiQt/SceneDialog.h b/src/GuiQt/SceneDialog.h index 0b4e4f5c292d1404c910d6d2336764343b51e78a..c36357e1f5ff5f1f8888b74198251827e5621b5e 100644 --- a/src/GuiQt/SceneDialog.h +++ b/src/GuiQt/SceneDialog.h @@ -35,6 +35,7 @@ class QLabel; class QLineEdit; class QPushButton; class QScrollArea; +class QTextEdit; class QVBoxLayout; namespace caret { @@ -63,6 +64,8 @@ namespace caret { void createDefaultSceneFile(); + SceneFile* getSelectedSceneFile(); + static bool isInformUserAboutScenesOnExit(); static bool checkForModifiedFiles(const GuiManager::TestModifiedMode testMode, @@ -136,8 +139,6 @@ namespace caret { virtual void helpButtonClicked() override; private: - SceneFile* getSelectedSceneFile(); - Scene* getSelectedScene(); void loadSceneFileComboBox(SceneFile* selectedSceneFileIn); @@ -164,10 +165,11 @@ namespace caret { 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 @@ namespace caret { const int32_t sceneIndex, AString& nameTextOut, AString& sceneIdTextOut, - AString& descriptionTextOut, - const int32_t maximumLinesInDescription); + AString& abbreviatedDescriptionTextOut, + AString& fullDescriptionTextOut); signals: /** @@ -347,7 +349,6 @@ namespace caret { bool SceneDialog::s_informUserAboutScenesOnExitFlag = true; bool SceneDialog::s_warnUserWhenCreatingSceneFlag = true; bool SceneDialog::s_useSceneForegroundBackgroundColorsFlag = true; - #endif // __SCENE_DIALOG_DECLARE__ } // namespace diff --git a/src/GuiQt/SceneFileInformationDialog.cxx b/src/GuiQt/SceneFileInformationDialog.cxx index 94b3f938348072bc51c573db60931b0259547241..f8b46bc687b00f3952daec8ccaf9d1245c478053 100644 --- a/src/GuiQt/SceneFileInformationDialog.cxx +++ b/src/GuiQt/SceneFileInformationDialog.cxx @@ -66,34 +66,18 @@ m_sceneFile(sceneFile) 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 @@ m_sceneFile(sceneFile) 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 @@ m_sceneFile(sceneFile) setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); - displayFilesHierarchy(); - displayFilesList(); - setSizeOfDialogWhenDisplayed(QSize(600, 800)); + + loadContentIntoDialog(); } /** @@ -135,103 +122,43 @@ SceneFileInformationDialog::~SceneFileInformationDialog() { } -///** -// * 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 @@ SceneFileInformationDialog::displayFilesList() 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 --git a/src/GuiQt/SceneFileInformationDialog.h b/src/GuiQt/SceneFileInformationDialog.h index 18234bb577f471fcf1392bc3e1bdc2932fb8a881..a863b1968d3eff73667d774cda63488a7a94f4b5 100644 --- a/src/GuiQt/SceneFileInformationDialog.h +++ b/src/GuiQt/SceneFileInformationDialog.h @@ -25,6 +25,7 @@ #include +#include "SceneFileBasePathTypeEnum.h" #include "WuQDialogNonModal.h" class QLineEdit; @@ -54,7 +55,10 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - void displayFilesList(); + void loadContentIntoDialog(); + + void displayFilesList(const SceneFileBasePathTypeEnum::Enum basePathType, + const AString& basePathName); void displayFilesHierarchy(); diff --git a/src/GuiQt/ScenePreviewDialog.cxx b/src/GuiQt/ScenePreviewDialog.cxx index 7d04eb6f853af504eaa141335b26e2ff32ed8d6e..5b5b74b2607c55d6fa268343afee424af954639f 100644 --- a/src/GuiQt/ScenePreviewDialog.cxx +++ b/src/GuiQt/ScenePreviewDialog.cxx @@ -95,21 +95,21 @@ ScenePreviewDialog::ScenePreviewDialog(const Scene* scene, 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 --git a/src/GuiQt/SpecFileManagementDialog.cxx b/src/GuiQt/SpecFileManagementDialog.cxx index 28e8ee78dc551a0b62d051b63b6cc2a9091442f0..a0d8e531baa2f65b28514703d0f07630b962b325 100644 --- a/src/GuiQt/SpecFileManagementDialog.cxx +++ b/src/GuiQt/SpecFileManagementDialog.cxx @@ -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 @@ m_specFile(specFile) 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 @@ m_specFile(specFile) 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 @@ m_specFile(specFile) /* * No scrollbars in dialog since the table widget will have scroll bars */ -// const bool enableScrollBars = false; - setTopBottomAndCentralWidgets(toolbarWidget, centralWidget, NULL, @@ -448,32 +455,6 @@ m_specFile(specFile) 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 @@ SpecFileManagementDialog::updateTableDimensionsToFitFiles() 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 @@ SpecFileManagementDialog::setFilterSelections(const SpecFileDialogViewFilesTypeE } } - -///** -// * 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 @@ SpecFileManagementDialog::userButtonPressed(QPushButton* userPushButton) 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 @@ SpecFileManagementDialog::okButtonClickedManageAndSaveFiles() 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 @@ SpecFileManagementDialog::okButtonClickedManageAndSaveFiles() } else { m_specFile->removeAnyFileInformationIfNotInSpecAndNoCaretDataFile(); - AString specFileErrorMessage = writeSpecFile(false); + AString specFileErrorMessage = writeSpecFile(true); if (specFileErrorMessage.isEmpty() == false) { errorMessages.appendWithNewLine(specFileErrorMessage); } @@ -1976,7 +1995,7 @@ SpecFileManagementDialog::writeSpecFile(const bool writeOnlyIfModified) 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 @@ SpecFileManagementDialog::fileReloadOrOpenFileActionSelected(int rowIndex) 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 @@ SpecFileManagementDialog::fileReloadOrOpenFileActionSelected(int rowIndex) 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 @@ SpecFileManagementDialog::changeFileName(QWidget* parent, 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 @@ SpecFileManagementDialog::changeFileName(QWidget* parent, */ updateGraphicWindowsAndUserInterface(); } -// } -// } } /** @@ -2567,89 +2583,6 @@ SpecFileManagementDialog::showFileInformation(CaretDataFile* caretDataFile) 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 @@ SpecFileManagementDialog::createFilesTypesToolBar(QLabel* &labelOut) 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 @@ SpecFileManagementDialog::createFilesTypesToolBar(QLabel* &labelOut) 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 @@ SpecFileManagementDialog::createFilesSelectionToolBar(QLabel* &labelOut) 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 @@ SpecFileManagementDialog::createManageFilesLoadedNotLoadedToolBar(QLabel* &label 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 @@ SpecFileManagementDialog::createStructureToolBar(QLabel* &labelOut) 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 @@ SpecFileManagementDialog::createToolBarWithActionGroup(const QString& text, 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 @@ SpecFileManagementDialog::toolBarFileTypeActionTriggered(QAction* /*action*/) 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 @@ SpecFileManagementDialog::toolBarManageFilesLoadedNotLoadedActionTriggered(QActi void SpecFileManagementDialog::toolBarSelectFilesActionTriggered(QAction* action) { -// m_filesTableWidget->blockSignals(true); - if (action != NULL) { const int dataValue = action->data().toInt(); @@ -2980,8 +2897,6 @@ SpecFileManagementDialog::toolBarSelectFilesActionTriggered(QAction* action) } } } - -// m_filesTableWidget->blockSignals(false); } /* ======================================================================= */ @@ -3038,13 +2953,6 @@ SpecFileManagementDialogRowContent::setSortingKey(const Sorting sorting) const DataFileTypeEnum::Enum dataFileType = m_specFileDataFile->getDataFileType(); QString typeName = SpecFileManagementDialog::getEditedDataFileTypeName(dataFileType); - /* - * Push surface files to the top??? - */ -// if (dataFileType == DataFileTypeEnum::SURFACE) { -// typeName = "AAAAAA"; -// } - /* * Push non-specific structures to the bottom */ diff --git a/src/GuiQt/SpecFileManagementDialog.h b/src/GuiQt/SpecFileManagementDialog.h index 780a53d8ac6605d865bcb4c19e19bbcce22de15a..89ba98451b2bc702128e7e410b594876c3221a18 100644 --- a/src/GuiQt/SpecFileManagementDialog.h +++ b/src/GuiQt/SpecFileManagementDialog.h @@ -111,10 +111,6 @@ namespace caret { void toolBarManageFilesLoadedNotLoadedActionTriggered(QAction* action); -// void chooseSpecFileNameActionTriggered(); - -// void specFileOptionsActionTriggered(); - void fileReloadOrOpenFileActionSelected(int indx); void fileRemoveActionSelected(int indx); @@ -239,7 +235,9 @@ namespace caret { SpecFile* m_specFile; - QPushButton* m_loadScenesPushButton; + QPushButton* m_reloadAllDataFilesPushButton = NULL; + + QPushButton* m_loadScenesPushButton = NULL; QActionGroup* m_fileTypesActionGroup; diff --git a/src/GuiQt/SplashScreen.cxx b/src/GuiQt/SplashScreen.cxx index 5a2e11dd6e8bb5b5982221ff281c654dd0f99d00..01c605fd908eac8987635b1fccb8463857d5ce91 100644 --- a/src/GuiQt/SplashScreen.cxx +++ b/src/GuiQt/SplashScreen.cxx @@ -586,8 +586,9 @@ SplashScreen::createTopLevelItem(const AString& titleOne, 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 --git a/src/GuiQt/StructureSurfaceSelectionControl.cxx b/src/GuiQt/StructureSurfaceSelectionControl.cxx index 2c8f2700d169f69b85b57ec03ea8c6b552ff3311..7173310d701fdd333260895611aa55b4df3ad123 100644 --- a/src/GuiQt/StructureSurfaceSelectionControl.cxx +++ b/src/GuiQt/StructureSurfaceSelectionControl.cxx @@ -265,7 +265,7 @@ StructureSurfaceSelectionControl::updateControlAfterSelection() 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 --git a/src/GuiQt/SurfacePropertiesEditorDialog.cxx b/src/GuiQt/SurfacePropertiesEditorDialog.cxx index c3116b3629a547ca9a8b260f66ff5e74f50a8f0b..8c0321ea66ae4ccfe9ca56f4b7b5ab5c0b70f8ce 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.cxx +++ b/src/GuiQt/SurfacePropertiesEditorDialog.cxx @@ -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 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) { 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 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) 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 @@ SurfacePropertiesEditorDialog::updateDialog() { m_updateInProgress = true; + updateDefaultSurfaceColorWidget(); + WuQMacroManager::instance()->updateValueInWidgetFromMacroWidgetAction(m_surfaceDrawingTypeComboBox, m_linkSizeSpinBox, m_nodeSizeSpinBox, @@ -184,6 +226,10 @@ SurfacePropertiesEditorDialog::updateDialog() m_displayNormalVectorsCheckBox); m_updateInProgress = false; + + DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + m_backfaceCullingCheckBox->setChecked(dps->isBackfaceCullingEnabled()); + } /** @@ -202,6 +248,86 @@ SurfacePropertiesEditorDialog::receiveEvent(Event* event) } } +/** + * 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. * @@ -258,3 +384,14 @@ SurfacePropertiesEditorDialog::restoreFromScene(const SceneAttributes* sceneAttr 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 --git a/src/GuiQt/SurfacePropertiesEditorDialog.h b/src/GuiQt/SurfacePropertiesEditorDialog.h index 1f56343d482c95af77a9ee3598cc56c7e12f3d99..9bafe67b8c5d6f11b25eef0f77eb2c23591f52fb 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.h +++ b/src/GuiQt/SurfacePropertiesEditorDialog.h @@ -21,6 +21,8 @@ */ /*LICENSE_END*/ +#include + #include "EventListenerInterface.h" #include "SceneableInterface.h" #include "WuQDialogNonModal.h" @@ -51,13 +53,24 @@ namespace caret { 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 @@ namespace caret { QDoubleSpinBox* m_opacitySpinBox; + QWidget* m_surfaceColorWidget; + bool m_updateInProgress; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/SurfaceSelectionViewController.cxx b/src/GuiQt/SurfaceSelectionViewController.cxx index e80935c33082562e4cdeb04d2c99853a7c45c6c6..70ae37f5947c0018acd365daca8343163317a7a3 100644 --- a/src/GuiQt/SurfaceSelectionViewController.cxx +++ b/src/GuiQt/SurfaceSelectionViewController.cxx @@ -220,7 +220,7 @@ SurfaceSelectionViewController::updateControl(SurfaceSelectionModel* selectionMo 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 --git a/src/GuiQt/TileTabGridRowColumnWidgets.cxx b/src/GuiQt/TileTabGridRowColumnWidgets.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4a2e97a28459d452f8e8b784583b906ee7ec192f --- /dev/null +++ b/src/GuiQt/TileTabGridRowColumnWidgets.cxx @@ -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 --git a/src/GuiQt/TileTabGridRowColumnWidgets.h b/src/GuiQt/TileTabGridRowColumnWidgets.h new file mode 100644 index 0000000000000000000000000000000000000000..7dda2d9b4008c310f23c1dfd0b46cf2027c3c4c3 --- /dev/null +++ b/src/GuiQt/TileTabGridRowColumnWidgets.h @@ -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 --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index cc2bc54ee1016f7034c9d9ea0e8f628c30331371..6eef2fe08259e6c69b6382a47313d3bad9243f72 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -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 @@ TileTabsConfigurationDialog::TileTabsConfigurationDialog(BrainBrowserWindow* par : 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 @@ TileTabsConfigurationDialog::receiveEvent(Event* event) updateDialog(); } } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE) { + updateTemplateUserConfigurationPushButtons(getSelectedConfigurationSourceType()); + } } /** @@ -160,71 +188,214 @@ TileTabsConfigurationDialog::focusGained() 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 @@ TileTabsConfigurationDialog::replaceUserConfigurationPushButtonClicked() 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; + } - updateStretchFactors(); + /* + * Display status of tabs must be set after + * configuration is copied from User Configurations + */ + std::set tabsWithDisplayStatusOn; + std::set tabsWithDisplayStatusOff; + + 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 @@ TileTabsConfigurationDialog::getBrowserWindow() return bbw; } - - /** * @return The browser window content for the selected window index. * May be NULL when no tabs are open. @@ -287,7 +858,21 @@ TileTabsConfigurationDialog::getBrowserWindowContent() 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 @@ TileTabsConfigurationDialog::createUserConfigurationSelectionWidget() { m_userConfigurationSelectionListWidget = new WuQListWidget(); m_userConfigurationSelectionListWidget->setSelectionMode(QListWidget::SingleSelection); - - QHBoxLayout* selectionLayout = new QHBoxLayout(); - WuQtUtilities::setLayoutMargins(selectionLayout, - 0); - selectionLayout->addWidget(m_userConfigurationSelectionListWidget, 100); - + QObject::connect(m_userConfigurationSelectionListWidget, &QListWidget::itemSelectionChanged, + this, &TileTabsConfigurationDialog::userConfigurationSelectionListWidgetItemChanged); + + loadTemplateLayoutConfigurations(); + + 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,23 +914,246 @@ TileTabsConfigurationDialog::createUserConfigurationSelectionWidget() 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); - - QGroupBox* configurationWidget = new QGroupBox("User Configurations"); + 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("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); return configurationWidget; } +/** + * 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. */ @@ -368,7 +1184,7 @@ TileTabsConfigurationDialog::createWorkbenchWindowWidget() * @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 @@ TileTabsConfigurationDialog::createCustomOptionsWidget() "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 @@ TileTabsConfigurationDialog::createCustomOptionsWidget() void TileTabsConfigurationDialog::centeringCorrectionCheckBoxClicked(bool checked) { - TileTabsConfiguration* config = getCustomTileTabsConfiguration(); + TileTabsLayoutGridConfiguration* config = getCustomTileTabsGridConfiguration(); if (config != NULL) { config->setCenteringCorrectionEnabled(checked); updateGraphicsWindow(); @@ -411,9 +1227,9 @@ TileTabsConfigurationDialog::centeringCorrectionCheckBoxClicked(bool checked) 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 @@ TileTabsConfigurationDialog::updateCustomOptionsWidget() * @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 @@ TileTabsConfigurationDialog::createRowColumnStretchWidget() * 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 @@ TileTabsConfigurationDialog::updateRowColumnStretchWidgets(TileTabsConfiguration */ { 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 @@ TileTabsConfigurationDialog::updateRowColumnStretchWidgets(TileTabsConfiguration * 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 @@ TileTabsConfigurationDialog::addRowColumnStretchWidget(const EventTileTabsConfig } } - 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(); +} /** - * @return The active configuration widget. + * 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()); + } +} + +/** + * 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"); + 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); - 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_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); - - widget->setFixedWidth(widget->sizeHint().width()); + QGroupBox* layoutSettingsGroupBox = new QGroupBox("Active Configuration Settings"); + QVBoxLayout* settingsGroupBoxLayout = new QVBoxLayout(layoutSettingsGroupBox); + settingsGroupBoxLayout->addWidget(stretchFactorScrollArea); + + 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 @@ void 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_customGridConfigurationRadioButton) { + const TileTabsLayoutGridConfiguration* gridConfig = browserWindowContent->getCustomGridTileTabsConfiguration(); + if (gridConfig != NULL) { + if ( ! warnIfGridConfigurationTooSmallDialog(gridConfig)) { + return; + } + } + browserWindowContent->setTileTabsConfigurationMode(TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID); } - else if (button == m_customConfigurationRadioButton) { - browserWindowContent->setTileTabsConfigurationMode(TileTabsGridModeEnum::CUSTOM); + 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 @@ TileTabsConfigurationDialog::updateDialogWithSelectedTileTabsFromWindow(BrainBro } /** - * 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 TileTabsLayoutConfigurationTypeEnum::CUSTOM_GRID: + m_customGridConfigurationRadioButton->setChecked(true); + m_editConfigurationStackedWidget->setCurrentWidget(m_customGridConfigurationWidget); + editingEnabledFlag = true; break; - case TileTabsGridModeEnum::CUSTOM: - m_customConfigurationRadioButton->setChecked(true); + case TileTabsLayoutConfigurationTypeEnum::MANUAL: + m_manualConfigurationRadioButton->setChecked(true); + m_editConfigurationStackedWidget->setCurrentWidget(m_manualGeometryWidget); + editingEnabledFlag = true; break; } - readConfigurationsFromPreferences(); + m_editConfigurationStackedWidget->setEnabled(editingEnabledFlag); +} + + +/** + * Update the content of the dialog. + */ +void +TileTabsConfigurationDialog::updateDialog() +{ + BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); + if (browserWindowContent == NULL) { + return; + } + + 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 @@ TileTabsConfigurationDialog::updateDialog() 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(); - const bool editableFlag = ( ! m_automaticConfigurationRadioButton->isChecked()); + QSignalBlocker blocker(m_templateConfigurationSelectionListWidget); + m_templateConfigurationSelectionListWidget->clear(); - // This does not re-enable the construction menu - //m_customConfigurationWidget->setEnabled(editableFlag); + 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(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) -{ - 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; - } +TileTabsConfigurationDialog::updateGridStretchFactors() +{ + 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 @@ TileTabsConfigurationDialog::newUserConfigurationButtonClicked() 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 @@ TileTabsConfigurationDialog::deleteUserConfigurationButtonClicked() 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 @@ TileTabsConfigurationDialog::configurationNumberOfRowsOrColumnsChanged() * 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 @@ TileTabsConfigurationDialog::configurationStretchFactorWasChanged() * 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 @@ TileTabsConfigurationDialog::helpButtonClicked() /** - * 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. + * Load a template configuration from the given XML */ void -TileTabElementWidgets::constructionMenuAboutToShow() +TileTabsConfigurationDialog::loadTemplateLayoutConfigurationFromXML(const QString& xml) { - const TileTabsConfiguration* config = m_tileTabsConfigurationDialog->getCustomTileTabsConfiguration(); + 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 --git a/src/GuiQt/TileTabsConfigurationDialog.h b/src/GuiQt/TileTabsConfigurationDialog.h index 29e31cff26ed966f0ba6cff5356d6608f96ed44f..bcaad3e7a725e4a1a7414b2576eef86234dfefe1 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.h +++ b/src/GuiQt/TileTabsConfigurationDialog.h @@ -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 QListWidgetItem; 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 @@ namespace caret { private slots: void browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow); - void newUserConfigurationButtonClicked(); - void deleteUserConfigurationButtonClicked(); void renameUserConfigurationButtonClicked(); - void configurationNumberOfRowsOrColumnsChanged(); + void showConfigurationXmlPushButtonClicked(); + + void gridConfigurationNumberOfRowsOrColumnsChanged(); + + void gridConfigurationStretchFactorWasChanged(); - void configurationStretchFactorWasChanged(); + void addUserConfigurationPushButtonClicked(); void replaceUserConfigurationPushButtonClicked(); @@ -93,168 +101,202 @@ namespace caret { 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 updateManualGeometryEditorWidget(); - void updateStretchFactors(); + 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); - - 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; - - WuQGridLayoutGroup* m_gridLayoutGroup; + /** Blocks reading of preferences since that may invalidate data pointers */ + bool m_blockReadUserConfigurationsFromPreferences; + + /** + * Keep a pointer to preferences but DO NOT delete it + * since the preferences are managed by the session + * manager. + */ + CaretPreferences* m_caretPreferences; + 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 --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsGridConfigurationModifier.cxx similarity index 83% rename from src/GuiQt/TileTabsConfigurationModifier.cxx rename to src/GuiQt/TileTabsGridConfigurationModifier.cxx index 3b04e14c40805eb9be3b682135329cb2e1936429..f4a16cc45951719a1032113420276cadec96dcef 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsGridConfigurationModifier.cxx @@ -21,9 +21,9 @@ #include -#define __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ -#include "TileTabsConfigurationModifier.h" -#undef __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ +#define __TILE_TABS_GRID_CONFIGURATION_MODIFIER_DECLARE__ +#include "TileTabsGridConfigurationModifier.h" +#undef __TILE_TABS_GRID_CONFIGURATION_MODIFIER_DECLARE__ #include "BrainBrowserWindow.h" #include "BrainOpenGLViewportContent.h" @@ -34,18 +34,18 @@ #include "EventBrowserTabNew.h" #include "EventBrowserWindowTileTabOperation.h" #include "EventManager.h" -#include "EventTileTabsConfigurationModification.h" +#include "EventTileTabsGridConfigurationModification.h" #include "GuiManager.h" #include "SpacerTabContent.h" -#include "TileTabsConfiguration.h" +#include "TileTabsLayoutGridConfiguration.h" using namespace caret; static bool debugFlag = false; /** - * \class caret::TileTabsConfigurationModifier - * \brief Modifies a tile tabs configuration. + * \class caret::TileTabsGridConfigurationModifier + * \brief Modifies a tile tabs grid configuration. * \ingroup GuiQt */ @@ -54,13 +54,17 @@ static bool debugFlag = false; * * @param existingTabs * All tabs in the window. + * @param windowIndex + * Index of window requesting this operation * @param modifyEvent * Event describing modification. */ -TileTabsConfigurationModifier::TileTabsConfigurationModifier(const std::vector& existingTabs, - EventTileTabsConfigurationModification* modifyEvent) +TileTabsGridConfigurationModifier::TileTabsGridConfigurationModifier(const std::vector& existingTabs, + const int32_t windowIndex, + EventTileTabsGridConfigurationModification* modifyEvent) : CaretObject(), m_existingTabs(existingTabs), +m_windowIndex(windowIndex), m_modifyEvent(modifyEvent) { CaretAssert(modifyEvent); @@ -71,7 +75,7 @@ m_modifyEvent(modifyEvent) /** * Destructor. */ -TileTabsConfigurationModifier::~TileTabsConfigurationModifier() +TileTabsGridConfigurationModifier::~TileTabsGridConfigurationModifier() { for (auto rc : m_rowColumns) { delete rc; @@ -84,7 +88,7 @@ TileTabsConfigurationModifier::~TileTabsConfigurationModifier() * @return String describing this object's content. */ AString -TileTabsConfigurationModifier::toString() const +TileTabsGridConfigurationModifier::toString() const { AString s; for (const auto rc : m_rowColumns) { @@ -105,7 +109,7 @@ TileTabsConfigurationModifier::toString() const * True if successful, else false. */ bool -TileTabsConfigurationModifier::run(AString& errorMessageOut) +TileTabsGridConfigurationModifier::run(AString& errorMessageOut) { errorMessageOut.clear(); @@ -129,13 +133,13 @@ TileTabsConfigurationModifier::run(AString& errorMessageOut) * Load the rows or columns from the Tile Tabs Configuration */ void -TileTabsConfigurationModifier::loadRowColumnsFromTileTabsConfiguration() +TileTabsGridConfigurationModifier::loadRowColumnsFromTileTabsConfiguration() { const int32_t numRows = m_currentTileTabsConfiguration->getNumberOfRows(); const int32_t numColumns = m_currentTileTabsConfiguration->getNumberOfColumns(); switch (m_modifyEvent->getRowColumnType()) { - case EventTileTabsConfigurationModification::RowColumnType::COLUMN: + case EventTileTabsGridConfigurationModification::RowColumnType::COLUMN: for (int32_t jCol = 0; jCol < numColumns; jCol++) { m_rowColumns.push_back(new RowColumnContent(m_existingTabs, m_modifyEvent->getTileTabsConfiguration(), @@ -143,7 +147,7 @@ TileTabsConfigurationModifier::loadRowColumnsFromTileTabsConfiguration() false)); } break; - case EventTileTabsConfigurationModification::RowColumnType::ROW: + case EventTileTabsGridConfigurationModification::RowColumnType::ROW: for (int32_t iRow = 0; iRow < numRows; iRow++) { m_rowColumns.push_back(new RowColumnContent(m_existingTabs, m_modifyEvent->getTileTabsConfiguration(), @@ -163,14 +167,14 @@ TileTabsConfigurationModifier::loadRowColumnsFromTileTabsConfiguration() * True if successful, else false. */ bool -TileTabsConfigurationModifier::performModification(AString& errorMessageOut) +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 EventTileTabsConfigurationModification::Operation::DELETE_IT: + case EventTileTabsGridConfigurationModification::Operation::DELETE_IT: if (numRowColumns <= 1) { errorMessageOut = "Cannot delete ROWCOL when there is only one ROWCOL"; } @@ -195,7 +199,7 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) errorMessageOut = "Invalid ROWCOL index=RCINDEX when deleting"; } break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_AFTER: if ((rowColumnIndex >= 0) && (rowColumnIndex < numRowColumns)) { RowColumnContent* rowColumnCopy = m_rowColumns[rowColumnIndex]->clone(errorMessageOut); @@ -210,7 +214,7 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; } break; - case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::DUPLICATE_BEFORE: if ((rowColumnIndex >= 0) && (rowColumnIndex < numRowColumns)) { RowColumnContent* rowColumnCopy = m_rowColumns[rowColumnIndex]->clone(errorMessageOut); @@ -225,8 +229,8 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; } break; - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: - case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_AFTER: + case EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_BEFORE: if ((rowColumnIndex >= 0) && (rowColumnIndex < numRowColumns)) { CaretAssertVectorIndex(m_rowColumns, 0); @@ -234,7 +238,7 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) RowColumnContent* rowColumnSpacer = RowColumnContent::newInstanceContainingSpacers(numberOfElements); if (rowColumnSpacer != NULL) { int32_t insertOffset = rowColumnIndex; - if (m_modifyEvent->getOperation() == EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER) { + if (m_modifyEvent->getOperation() == EventTileTabsGridConfigurationModification::Operation::INSERT_SPACER_AFTER) { insertOffset++; } m_rowColumns.insert(m_rowColumns.begin() + @@ -246,7 +250,7 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) errorMessageOut = "Invalid ROWCOL index=RCINDEX when insert spacer"; } break; - case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: + case EventTileTabsGridConfigurationModification::Operation::MOVE_AFTER: if (numRowColumns <= 1) { errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; } @@ -264,7 +268,7 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) errorMessageOut = "Invalid ROWCOL index=RCINDEX when moving"; } break; - case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: + case EventTileTabsGridConfigurationModification::Operation::MOVE_BEFORE: if (numRowColumns <= 1) { errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; } @@ -287,10 +291,10 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) if ( ! errorMessageOut.isEmpty()) { AString nameText; switch (m_modifyEvent->getRowColumnType()) { - case EventTileTabsConfigurationModification::RowColumnType::COLUMN: + case EventTileTabsGridConfigurationModification::RowColumnType::COLUMN: nameText = " Column "; break; - case EventTileTabsConfigurationModification::RowColumnType::ROW: + case EventTileTabsGridConfigurationModification::RowColumnType::ROW: nameText = " Row "; break; } @@ -315,13 +319,13 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) * True if successful, else false. */ bool -TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut) +TileTabsGridConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut) { - TileTabsConfiguration newConfiguration(*m_currentTileTabsConfiguration); + TileTabsLayoutGridConfiguration newConfiguration(*m_currentTileTabsConfiguration); std::vector browserTabs; switch (m_modifyEvent->getRowColumnType()) { - case EventTileTabsConfigurationModification::RowColumnType::COLUMN: + case EventTileTabsGridConfigurationModification::RowColumnType::COLUMN: { const int32_t numRows = static_cast(m_rowColumns[0]->m_tabElements.size()); CaretAssert(numRows == m_currentTileTabsConfiguration->getNumberOfRows()); @@ -349,7 +353,7 @@ TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& } } break; - case EventTileTabsConfigurationModification::RowColumnType::ROW: + 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()); @@ -390,10 +394,16 @@ TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& */ 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()); @@ -404,7 +414,8 @@ TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& if ( ! m_browserTabsToDelete.empty()) { for (auto btc : m_browserTabsToDelete) { EventBrowserTabDelete deleteEvent(btc, - btc->getTabNumber()); + btc->getTabNumber(), + m_windowIndex); EventManager::get()->sendEvent(deleteEvent.getPointer()); if (deleteEvent.isError()) { errorMessageOut.appendWithNewLine(deleteEvent.getErrorMessage()); @@ -431,7 +442,7 @@ TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& * Browser tab content at (rowIndex, columnIndex) in the current * Tile Tabs Configuration */ -TileTabsConfigurationModifier::Element::Element(const int32_t rowIndex, +TileTabsGridConfigurationModifier::Element::Element(const int32_t rowIndex, const int32_t columnIndex, BrowserTabContent* browserTabContent) : @@ -448,7 +459,7 @@ m_browserTabContent(browserTabContent) * @return String representation of object. */ AString -TileTabsConfigurationModifier::Element::toString() const +TileTabsGridConfigurationModifier::Element::toString() const { AString s("(row=%1, column=%2)"); s = s.arg(m_sourceRowIndex).arg(m_sourceColumnIndex); @@ -468,8 +479,8 @@ TileTabsConfigurationModifier::Element::toString() const * @param rowFlag * True if rows are being operated upon, false if operating on columns */ -TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const std::vector& existingTabs, - TileTabsConfiguration* tileTabsConfiguration, +TileTabsGridConfigurationModifier::RowColumnContent::RowColumnContent(const std::vector& existingTabs, + TileTabsLayoutGridConfiguration* tileTabsConfiguration, const int32_t rowColumnIndex, const bool rowFlag) { @@ -517,7 +528,7 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const std::vec * @param numberOfElements * Number of elements for row/column. */ -TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const int32_t numberOfElements) +TileTabsGridConfigurationModifier::RowColumnContent::RowColumnContent(const int32_t numberOfElements) { for (int32_t i = 0; i < numberOfElements; i++) { m_tabElements.push_back(new Element(i, i, NULL)); @@ -531,8 +542,8 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const int32_t * @param numberOfElements * Number of elements for row/column. */ -TileTabsConfigurationModifier::RowColumnContent* -TileTabsConfigurationModifier::RowColumnContent::newInstanceContainingSpacers(const int32_t numberOfElements) +TileTabsGridConfigurationModifier::RowColumnContent* +TileTabsGridConfigurationModifier::RowColumnContent::newInstanceContainingSpacers(const int32_t numberOfElements) { RowColumnContent* content = new RowColumnContent(numberOfElements); @@ -546,7 +557,7 @@ TileTabsConfigurationModifier::RowColumnContent::newInstanceContainingSpacers(co /** * Destructor. */ -TileTabsConfigurationModifier::RowColumnContent::~RowColumnContent() +TileTabsGridConfigurationModifier::RowColumnContent::~RowColumnContent() { for (auto te : m_tabElements) { delete te; @@ -563,7 +574,7 @@ TileTabsConfigurationModifier::RowColumnContent::~RowColumnContent() * @param obj * Instance that is copied. */ -TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const RowColumnContent& obj) +TileTabsGridConfigurationModifier::RowColumnContent::RowColumnContent(const RowColumnContent& obj) : CaretObject(obj) { for (auto te : obj.m_tabElements) { @@ -577,7 +588,7 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const RowColum * @return String representation of object. */ AString -TileTabsConfigurationModifier::RowColumnContent::toString() const +TileTabsGridConfigurationModifier::RowColumnContent::toString() const { AString s; for (const auto te : m_tabElements) { @@ -594,8 +605,8 @@ TileTabsConfigurationModifier::RowColumnContent::toString() const * @return * True if successful, else false */ -TileTabsConfigurationModifier::RowColumnContent* -TileTabsConfigurationModifier::RowColumnContent::clone(AString& errorMessageOut) const +TileTabsGridConfigurationModifier::RowColumnContent* +TileTabsGridConfigurationModifier::RowColumnContent::clone(AString& errorMessageOut) const { RowColumnContent* cloned = new RowColumnContent(*this); CaretAssert(cloned); diff --git a/src/GuiQt/TileTabsConfigurationModifier.h b/src/GuiQt/TileTabsGridConfigurationModifier.h similarity index 73% rename from src/GuiQt/TileTabsConfigurationModifier.h rename to src/GuiQt/TileTabsGridConfigurationModifier.h index c0b57e106f9a082d171ca3da329dc8fbaebe2011..f709ffa59220fe63be3243f937ed0eee2b7357ae 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.h +++ b/src/GuiQt/TileTabsGridConfigurationModifier.h @@ -1,5 +1,5 @@ -#ifndef __TILE_TABS_CONFIGURATION_MODIFIER_H__ -#define __TILE_TABS_CONFIGURATION_MODIFIER_H__ +#ifndef __TILE_TABS_GRID_CONFIGURATION_MODIFIER_H__ +#define __TILE_TABS_GRID_CONFIGURATION_MODIFIER_H__ /*LICENSE_START*/ /* @@ -33,22 +33,23 @@ namespace caret { class BrainOpenGLViewportContent; class BrowserTabContent; - class EventTileTabsConfigurationModification; + class EventTileTabsGridConfigurationModification; class SpacerTabContent; - class TileTabsConfiguration; + class TileTabsLayoutGridConfiguration; class TileTabsGridRowColumnElement; - class TileTabsConfigurationModifier : public CaretObject { + class TileTabsGridConfigurationModifier : public CaretObject { public: - TileTabsConfigurationModifier(const std::vector& existingTabs, - EventTileTabsConfigurationModification* modifyEvent); + TileTabsGridConfigurationModifier(const std::vector& existingTabs, + const int32_t windowIndex, + EventTileTabsGridConfigurationModification* modifyEvent); - virtual ~TileTabsConfigurationModifier(); + virtual ~TileTabsGridConfigurationModifier(); - TileTabsConfigurationModifier(const TileTabsConfigurationModifier&) = delete; + TileTabsGridConfigurationModifier(const TileTabsGridConfigurationModifier&) = delete; - TileTabsConfigurationModifier& operator=(const TileTabsConfigurationModifier&) = delete; + TileTabsGridConfigurationModifier& operator=(const TileTabsGridConfigurationModifier&) = delete; bool run(AString& errorMessageOut); @@ -83,7 +84,7 @@ namespace caret { class RowColumnContent : public CaretObject { public: RowColumnContent(const std::vector& existingTabs, - TileTabsConfiguration* tileTabsConfiguration, + TileTabsLayoutGridConfiguration* tileTabsConfiguration, const int32_t rowColumnIndex, const bool rowFlag); @@ -115,7 +116,9 @@ namespace caret { const std::vector& m_existingTabs; - EventTileTabsConfigurationModification* m_modifyEvent; + const int32_t m_windowIndex; + + EventTileTabsGridConfigurationModification* m_modifyEvent; std::vector m_rowColumns; @@ -124,15 +127,15 @@ namespace caret { /** * This is the current tile tabs configuration in the window so DO NOT delete it */ - TileTabsConfiguration* m_currentTileTabsConfiguration = NULL; + TileTabsLayoutGridConfiguration* m_currentTileTabsConfiguration = NULL; // ADD_NEW_MEMBERS_HERE }; -#ifdef __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ +#ifdef __TILE_TABS_GRID_CONFIGURATION_MODIFIER_DECLARE__ // -#endif // __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ +#endif // __TILE_TABS_GRID_CONFIGURATION_MODIFIER_DECLARE__ } // namespace -#endif //__TILE_TABS_CONFIGURATION_MODIFIER_H__ +#endif //__TILE_TABS_GRID_CONFIGURATION_MODIFIER_H__ diff --git a/src/GuiQt/TileTabsManualTabGeometryWidget.cxx b/src/GuiQt/TileTabsManualTabGeometryWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..61206ab8fed47398a5646fac74d26da56361f3e1 --- /dev/null +++ b/src/GuiQt/TileTabsManualTabGeometryWidget.cxx @@ -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 --git a/src/GuiQt/TileTabsManualTabGeometryWidget.h b/src/GuiQt/TileTabsManualTabGeometryWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..571c414d2134d07c1ca1cf15c38b18942d220206 --- /dev/null +++ b/src/GuiQt/TileTabsManualTabGeometryWidget.h @@ -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 --git a/src/GuiQt/UserInputModeAbstract.cxx b/src/GuiQt/UserInputModeAbstract.cxx index 453b2067492b0071ec07680282e367b991771c82..87a0ca7812ce7c66895573e45ce33db628bb9010 100644 --- a/src/GuiQt/UserInputModeAbstract.cxx +++ b/src/GuiQt/UserInputModeAbstract.cxx @@ -54,6 +54,7 @@ m_userInputMode(inputMode), m_widgetForToolBar(NULL), m_mousePositionValid(false) { + std::vector emptyHistoryXY; m_mousePositionEvent.grabNew(new MouseEvent(NULL, NULL, -1, @@ -63,6 +64,7 @@ m_mousePositionValid(false) 0, 0, 0, + emptyHistoryXY, false)); } diff --git a/src/GuiQt/UserInputModeAbstract.h b/src/GuiQt/UserInputModeAbstract.h index 7ff4cc8be0c48256ecbcc26e6319e7b1dd521963..29132bb5d61d8558e598664d026e40c5b6ade539 100644 --- a/src/GuiQt/UserInputModeAbstract.h +++ b/src/GuiQt/UserInputModeAbstract.h @@ -34,6 +34,7 @@ namespace caret { class BrainOpenGLViewportContent; class BrainOpenGLWidget; + class GestureEvent; class KeyEvent; class MouseEvent; @@ -191,6 +192,14 @@ namespace caret { */ 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) * diff --git a/src/GuiQt/UserInputModeAnnotations.cxx b/src/GuiQt/UserInputModeAnnotations.cxx index 91e649bdb1e7030eead8da2915d5722310627bc4..89fee9e65e1732cc50be7f141078964b48a92efb 100644 --- a/src/GuiQt/UserInputModeAnnotations.cxx +++ b/src/GuiQt/UserInputModeAnnotations.cxx @@ -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 @@ using namespace caret; /** * 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_annotationBeingDragged(NULL) 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 @@ UserInputModeAnnotations::receiveEvent(Event* event) 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 @@ UserInputModeAnnotations::resetAnnotationUnderMouse() { 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 @@ UserInputModeAnnotations::setMode(const Mode mode) { 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 @@ UserInputModeAnnotations::getCursor() const 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 @@ UserInputModeAnnotations::getCursor() const 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 @@ UserInputModeAnnotations::getCursor() const } /** - * Delete all selected annotations. + * Delete all selected annotations except color bars which are turned off for display */ void UserInputModeAnnotations::deleteSelectedAnnotations() @@ -305,7 +402,8 @@ UserInputModeAnnotations::deleteSelectedAnnotations() 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 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) 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 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) { 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 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) 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 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) 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 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) 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 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) 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 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; - if ( ! annMan->applyCommand(undoCommand, + if ( ! annMan->applyCommand(getUserInputMode(), + undoCommand, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); @@ -562,6 +689,29 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) return keyWasProcessedFlag; } +/** + * 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. * @@ -574,24 +724,19 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) 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 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) mouseViewportY, dx, dy, + m_annotationUnderMousePolyLineCoordinateIndex, mouseEvent.isFirstDragging()); if (coordInfo.m_surfaceSpaceInfo.m_validFlag) { annSpatialMod.setSurfaceCoordinateAtMouseXY(coordInfo.m_surfaceSpaceInfo.m_structure, @@ -855,7 +1001,8 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) } AString errorMessage; - if ( ! annotationManager->applyCommand(command, + if ( ! annotationManager->applyCommand(getUserInputMode(), + command, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); @@ -926,9 +1073,22 @@ void 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 @@ void 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 @@ UserInputModeAnnotations::mouseLeftClickWithShift(const MouseEvent& mouseEvent) 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 @@ void 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 @@ UserInputModeAnnotations::mouseLeftPress(const MouseEvent& mouseEvent) 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 @@ void UserInputModeAnnotations::setAnnotationUnderMouse(const MouseEvent& mouseEvent, SelectionItemAnnotation* annotationIDIn) { - m_annotationUnderMouse = NULL; m_annotationUnderMouseSizeHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; @@ -1031,9 +1218,14 @@ UserInputModeAnnotations::setAnnotationUnderMouse(const MouseEvent& mouseEvent, 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 @@ void 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 @@ UserInputModeAnnotations::createNewAnnotationFromMouseDrag(const MouseEvent& mou { 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 @@ UserInputModeAnnotations::createNewAnnotationFromMouseDrag(const MouseEvent& mou 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 @@ void 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); @@ -1199,6 +1426,69 @@ UserInputModeAnnotations::mouseMoveWithShift(const MouseEvent& mouseEvent) NULL); } +/** + * 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. * @@ -1220,13 +1510,17 @@ UserInputModeAnnotations::processModeSetCoordinate(const MouseEvent& mouseEvent) 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 @@ UserInputModeAnnotations::processModeSetCoordinate(const MouseEvent& mouseEvent) 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 @@ UserInputModeAnnotations::processModeNewMouseLeftClick(const MouseEvent& mouseEv { 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 @@ UserInputModeAnnotations::selectAnnotation(Annotation* annotation) * 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 @@ UserInputModeAnnotations::processMouseSelectAnnotation(const MouseEvent& mouseEv 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 @@ UserInputModeAnnotations::processMouseSelectAnnotation(const MouseEvent& mouseEv if (selectedAnnotation != NULL) { m_annotationBeingDragged = selectedAnnotation; m_annotationBeingDraggedHandleType = annotationID->getSizingHandle(); + m_annotationUnderMousePolyLineCoordinateIndex = annotationID->getPolyLineCoordinateIndex(); } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); @@ -1416,8 +1726,11 @@ UserInputModeAnnotations::showContextMenu(const MouseEvent& mouseEvent, * 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 @@ UserInputModeAnnotations::isEditMenuValid() const 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 @@ UserInputModeAnnotations::cutAnnotation() { 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 @@ UserInputModeAnnotations::cutAnnotation() 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 @@ UserInputModeAnnotations::processEditMenuItemSelection(const BrainBrowserWindowE { 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 @@ UserInputModeAnnotations::processEditMenuItemSelection(const BrainBrowserWindowE case BrainBrowserWindowEditMenuItemEnum::DELETER: deleteSelectedAnnotations(); break; + case BrainBrowserWindowEditMenuItemEnum::DESELECT_ALL: + processDeselectAllAnnotations(); + break; case BrainBrowserWindowEditMenuItemEnum::PASTE: { const MouseEvent* mouseEvent = getMousePosition(); @@ -1566,7 +1887,7 @@ UserInputModeAnnotations::processEditMenuItemSelection(const BrainBrowserWindowE 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 @@ UserInputModeAnnotations::processEditMenuItemSelection(const BrainBrowserWindowE case BrainBrowserWindowEditMenuItemEnum::UNDO: { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); - CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); + CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(getUserInputMode()); AString errorMessage; if ( ! undoStack->undo(errorMessage)) { @@ -1599,22 +1920,102 @@ UserInputModeAnnotations::processEditMenuItemSelection(const BrainBrowserWindowE } } +/** + * 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 @@ UserInputModeAnnotations::getEnabledEditMenuItems(std::vectortestProperty(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 @@ UserInputModeAnnotations::getEnabledEditMenuItems(std::vectorisAnnotationOnClipboardValid()) { const Annotation* clipBoardAnn = annotationManager->getAnnotationOnClipboard(); @@ -1706,7 +2113,7 @@ UserInputModeAnnotations::getEnabledEditMenuItems(std::vectorgetCommandRedoUndoStack(); + CaretUndoStack* undoStack = annotationManager->getCommandRedoUndoStack(getUserInputMode()); if (undoStack->canRedo()) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::REDO); @@ -1815,28 +2222,48 @@ UserInputModeAnnotations::NewMouseDragCreateAnnotation::NewMouseDragCreateAnnota 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 @@ UserInputModeAnnotations::NewMouseDragCreateAnnotation::~NewMouseDragCreateAnnot /** * 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); + AnnotationTwoCoordinateShape* twoCoordShape = m_annotation->castToTwoCoordinateShape(); + AnnotationOneCoordinateShape* oneCoordShape = m_annotation->castToOneCoordinateShape(); + AnnotationMultiCoordinateShape* multiCoordShape = m_annotation->castToMultiCoordinateShape(); - if (oneDimShape != NULL) { - setCoordinate(oneDimShape->getEndCoordinate(), + 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 @@ UserInputModeAnnotations::NewMouseDragCreateAnnotation::update(const int32_t mou ? (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 @@ UserInputModeAnnotations::NewMouseDragCreateAnnotation::getAnnotation() const return m_annotation; } +/** + * @return The drawing coordinates + */ +const std::vector& +UserInputModeAnnotations::NewMouseDragCreateAnnotation::getDrawingCoordinates() const +{ + return m_drawingCoordinates; +} + + diff --git a/src/GuiQt/UserInputModeAnnotations.h b/src/GuiQt/UserInputModeAnnotations.h index 2aa73142a3eca16f5291323397cc63747f8a3c71..b62ff8fa07fb884039976fad36d90d34087d5e63 100644 --- a/src/GuiQt/UserInputModeAnnotations.h +++ b/src/GuiQt/UserInputModeAnnotations.h @@ -29,6 +29,7 @@ #include "EventListenerInterface.h" #include "StructureEnum.h" #include "UserInputModeView.h" +#include "Vector3D.h" namespace caret { @@ -36,8 +37,8 @@ namespace caret { class AnnotationCoordinate; class AnnotationCoordinateInformation; class AnnotationFile; - class AnnotationOneDimensionalShape; - class AnnotationTwoDimensionalShape; + class AnnotationTwoCoordinateShape; + class AnnotationOneCoordinateShape; class KeyEvent; class MouseEvent; class SelectionItemAnnotation; @@ -50,10 +51,14 @@ namespace caret { * 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 @@ namespace caret { 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 @@ namespace caret { // 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 @@ namespace caret { ~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 @@ namespace caret { const Annotation* getAnnotation() const; + const std::vector& getDrawingCoordinates() const; + private: AnnotationFile* m_annotationFile; @@ -159,6 +181,8 @@ namespace caret { float m_windowWidth; float m_windowHeight; + + std::vector m_drawingCoordinates; }; class NewAnnotationFileSpaceAndType { @@ -183,9 +207,6 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { void pasteAnnotationFromAnnotationClipboardAndChangeSpace(const MouseEvent& mouseEvent); -// bool pasteOneDimensionalShape(AnnotationOneDimensionalShape* oneDimShape, -// AnnotationCoordinateInformation& coordInfo); - + void resetAnnotationBeingCreated(); + UserInputModeAnnotationsWidget* m_annotationToolsWidget; Mode m_mode; @@ -226,6 +246,8 @@ namespace caret { AnnotationSizingHandleTypeEnum::Enum m_annotationUnderMouseSizeHandleType; + int32_t m_annotationUnderMousePolyLineCoordinateIndex; + Annotation* m_annotationBeingDragged; AnnotationSizingHandleTypeEnum::Enum m_annotationBeingDraggedHandleType; @@ -236,7 +258,6 @@ namespace caret { bool m_allowMultipleSelectionModeFlag; - //static const AString s_pasteSpecialMenuItemText; // ADD_NEW_MEMBERS_HERE /* @@ -244,6 +265,8 @@ namespace caret { */ friend class UserInputModeAnnotationsContextMenu; friend class UserInputModeAnnotationsWidget; + friend class UserInputModeTileTabsManualLayout; + friend class UserInputModeTileTabsManualLayoutContextMenu; }; #ifdef __USER_INPUT_MODE_ANNOTATIONS_DECLARE__ diff --git a/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx b/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx index 8a6d89e61da8f75b45b7bf26aef5bde2c45b19f2..38216e4aa50131d1b93150e2d9032658822e9672 100644 --- a/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx +++ b/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx @@ -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 @@ m_newAnnotationCreatedByContextMenu(NULL) 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 @@ m_newAnnotationCreatedByContextMenu(NULL) 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 @@ m_newAnnotationCreatedByContextMenu(NULL) 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 @@ m_newAnnotationCreatedByContextMenu(NULL) 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 @@ m_newAnnotationCreatedByContextMenu(NULL) */ 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,11 +241,41 @@ m_newAnnotationCreatedByContextMenu(NULL) 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())); + /* + * 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 */ @@ -349,8 +405,9 @@ UserInputModeAnnotationsContextMenu::deleteAnnotations() 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); } @@ -377,6 +434,15 @@ UserInputModeAnnotationsContextMenu::pasteSpecialAnnotationFromAnnotationClipboa m_userInputModeAnnotations->pasteAnnotationFromAnnotationClipboardAndChangeSpace(m_mouseEvent); } +/** + * Deselect all annotations in the window. + */ +void +UserInputModeAnnotationsContextMenu::deselectAllAnnotations() +{ + m_userInputModeAnnotations->processDeselectAllAnnotations(); +} + /** * Select all annotations in the window. */ @@ -386,7 +452,6 @@ UserInputModeAnnotationsContextMenu::selectAllAnnotations() m_userInputModeAnnotations->processSelectAllAnnotations(); } - /** * Set the text for an annotation. */ @@ -475,7 +540,7 @@ UserInputModeAnnotationsContextMenu::turnOnDisplayInGroup(QAction* action) } if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { - CaretAssert(0); // TAB NOT ALLOWED + CaretAssert(0); /* TAB NOT ALLOWED */ return; } @@ -520,7 +585,7 @@ UserInputModeAnnotationsContextMenu::createTurnOnInDisplayGroupMenu() QMenu* UserInputModeAnnotationsContextMenu::createDuplicateTabSpaceAnnotationMenu() { - if (m_annotation == NULL) { + if (m_tabSpaceFileAndAnnotations.empty()) { return NULL; } @@ -528,29 +593,24 @@ UserInputModeAnnotationsContextMenu::createDuplicateTabSpaceAnnotationMenu() 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 @@ void 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 @@ UserInputModeAnnotationsContextMenu::applyGrouping(const AnnotationGroupingModeE 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 @@ UserInputModeAnnotationsContextMenu::applyGrouping(const AnnotationGroupingModeE 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 --git a/src/GuiQt/UserInputModeAnnotationsContextMenu.h b/src/GuiQt/UserInputModeAnnotationsContextMenu.h index 61d582c55cc7d73b21c1ef5c141f98672cc84c2d..35a7beb6162aecfec0e545f9fbd15668552112a9 100644 --- a/src/GuiQt/UserInputModeAnnotationsContextMenu.h +++ b/src/GuiQt/UserInputModeAnnotationsContextMenu.h @@ -25,6 +25,7 @@ #include #include "AnnotationGroupingModeEnum.h" +#include "AnnotationStackingOrderTypeEnum.h" #include "MouseEvent.h" namespace caret { @@ -65,6 +66,8 @@ namespace caret { void pasteSpecialAnnotationFromAnnotationClipboard(); + void deselectAllAnnotations(); + void selectAllAnnotations(); void setAnnotationText(); @@ -84,7 +87,7 @@ namespace caret { void applyGroupingUngroup(); void duplicateAnnotationSelected(QAction*); - + private: UserInputModeAnnotationsContextMenu(const UserInputModeAnnotationsContextMenu&); @@ -96,6 +99,8 @@ namespace caret { QMenu* createDuplicateTabSpaceAnnotationMenu(); + void processAnnotationOrderOperation(const AnnotationStackingOrderTypeEnum::Enum orderType); + UserInputModeAnnotations* m_userInputModeAnnotations; /* @@ -113,6 +118,8 @@ namespace caret { std::vector m_threeDimCoordAnnotations; + std::vector> m_tabSpaceFileAndAnnotations; + Annotation* m_annotation; AnnotationText* m_textAnnotation; diff --git a/src/GuiQt/UserInputModeAnnotationsWidget.cxx b/src/GuiQt/UserInputModeAnnotationsWidget.cxx index 62ab2a206cbe940e45b9db302a176a18ac55658f..df26d2e2639ed9d4aed5e1b3674a877a33d727b0 100644 --- a/src/GuiQt/UserInputModeAnnotationsWidget.cxx +++ b/src/GuiQt/UserInputModeAnnotationsWidget.cxx @@ -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 @@ m_inputModeAnnotations(inputModeAnnotations) { 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); @@ -240,6 +167,180 @@ UserInputModeAnnotationsWidget::receiveEvent(Event* event) } +/** + * 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. */ @@ -269,7 +370,11 @@ UserInputModeAnnotationsWidget::updateWidget() * 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 @@ UserInputModeAnnotationsWidget::updateWidget() 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 @@ UserInputModeAnnotationsWidget::updateWidget() 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 @@ UserInputModeAnnotationsWidget::updateWidget() 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 @@ UserInputModeAnnotationsWidget::updateWidget() } } - 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 --git a/src/GuiQt/UserInputModeAnnotationsWidget.h b/src/GuiQt/UserInputModeAnnotationsWidget.h index 581c55184b5ba0e118c1246a79703798e93c8ba7..800235adfb1db70daf7a6d6b62b15b505eb2c413 100644 --- a/src/GuiQt/UserInputModeAnnotationsWidget.h +++ b/src/GuiQt/UserInputModeAnnotationsWidget.h @@ -32,14 +32,18 @@ class QComboBox; 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 @@ namespace caret { 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 --git a/src/GuiQt/UserInputModeBorders.cxx b/src/GuiQt/UserInputModeBorders.cxx index b069d2cbd5fd5f13b91206c489b124d5d3f13f6b..6d453fd4ee3397ae1f21f2a894715bcbc339be7e 100644 --- a/src/GuiQt/UserInputModeBorders.cxx +++ b/src/GuiQt/UserInputModeBorders.cxx @@ -70,11 +70,11 @@ using namespace caret; * @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(Border* borderBeingDrawnByOpenGL, */ UserInputModeBorders::~UserInputModeBorders() { + delete this->borderBeingDrawn; + this->borderBeingDrawn = NULL; } /** @@ -106,13 +108,6 @@ UserInputModeBorders::drawPointAtMouseXY(BrainOpenGLWidget* openGLWidget, 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 @@ UserInputModeBorders::drawPointAtMouseXY(BrainOpenGLWidget* openGLWidget, 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 @@ UserInputModeBorders::setMode(const Mode mode) { 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 @@ UserInputModeBorders::isHighlightBorderEndPoints() const void UserInputModeBorders::drawOperationFinish() { - this->borderBeingDrawnByOpenGL->clear(); + this->borderBeingDrawn->clear(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().addBorder().getPointer()); @@ -290,7 +285,7 @@ UserInputModeBorders::drawOperationFinish() void UserInputModeBorders::drawOperationUndo() { - this->borderBeingDrawnByOpenGL->removeLastPoint(); + this->borderBeingDrawn->removeLastPoint(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); } @@ -300,7 +295,7 @@ UserInputModeBorders::drawOperationUndo() void UserInputModeBorders::drawOperationReset() { - this->borderBeingDrawnByOpenGL->clear(); + this->borderBeingDrawn->clear(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); } @@ -417,7 +412,6 @@ UserInputModeBorders::mouseLeftClick(const MouseEvent& mouseEvent) 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 @@ UserInputModeBorders::showContextMenu(const MouseEvent& mouseEvent, openGLWidget); } +/** + * @return The border being drawn + */ +Border* +UserInputModeBorders::getBorderBeingDrawn() const +{ + return this->borderBeingDrawn; +} diff --git a/src/GuiQt/UserInputModeBorders.h b/src/GuiQt/UserInputModeBorders.h index 8f7a7ad330b6ed8627238db6ae29645c2a1d4c07..adad0e0edc45cc05222dabaffc4889e4af85b243 100644 --- a/src/GuiQt/UserInputModeBorders.h +++ b/src/GuiQt/UserInputModeBorders.h @@ -50,8 +50,7 @@ namespace caret { EDIT_OPERATION_PROPERTIES }; - UserInputModeBorders(Border* borderBeingDrawnByOpenGL, - const int32_t windowIndex); + UserInputModeBorders(const int32_t windowIndex); virtual ~UserInputModeBorders(); @@ -81,6 +80,8 @@ namespace caret { bool isHighlightBorderEndPoints() const; + Border* getBorderBeingDrawn() const; + private: /* * Some private methods are accessed by this friend class @@ -121,11 +122,7 @@ namespace caret { EditOperation editOperation; - /** - * Pointer to border drawn by OpenGL. Since owned - * by OpenGL, DO NOT delete this!!! - */ - Border* borderBeingDrawnByOpenGL; + Border* borderBeingDrawn; int32_t windowIndex; }; diff --git a/src/GuiQt/UserInputModeBordersWidget.cxx b/src/GuiQt/UserInputModeBordersWidget.cxx index 920389652dcf5b58d2b6965ea7bde096c37b2854..d7dc4c37e02ea8420710838b83dbb2a9fb63dd79 100644 --- a/src/GuiQt/UserInputModeBordersWidget.cxx +++ b/src/GuiQt/UserInputModeBordersWidget.cxx @@ -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 @@ UserInputModeBordersWidget::UserInputModeBordersWidget(UserInputModeBorders* inp this->inputModeBorders = inputModeBorders; - QLabel* nameLabel = new QLabel("Borders "); - this->widgetMode = this->createModeWidget(); - resetLastEditedBorder(); this->widgetDrawOperation = this->createDrawOperationWidget(); @@ -120,13 +120,19 @@ UserInputModeBordersWidget::UserInputModeBordersWidget(UserInputModeBorders* inp 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 @@ UserInputModeBordersWidget::receiveEvent(Event* event) m_borderOptimizeDialog = NULL; } + this->inputModeBorders->borderBeingDrawn->clear(); + brainEvent->setEventProcessed(); } } @@ -174,53 +182,43 @@ UserInputModeBordersWidget::updateWidget() 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 @@ UserInputModeBordersWidget::setActionGroupByActionData(QActionGroup* actionGroup 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 @@ UserInputModeBordersWidget::createDrawOperationWidget() "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 @@ UserInputModeBordersWidget::createDrawOperationWidget() "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 @@ UserInputModeBordersWidget::createDrawOperationWidget() "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 @@ UserInputModeBordersWidget::createDrawOperationWidget() "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 @@ UserInputModeBordersWidget::createDrawOperationWidget() 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 @@ UserInputModeBordersWidget::createDrawOperationWidget() /* * 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 @@ UserInputModeBordersWidget::createDrawOperationWidget() 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 @@ UserInputModeBordersWidget::drawFinishButtonClicked() } 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 @@ UserInputModeBordersWidget::drawFinishButtonClicked() 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 @@ UserInputModeBordersWidget::drawFinishButtonClicked() } 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 @@ UserInputModeBordersWidget::drawFinishButtonClicked() 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 @@ UserInputModeBordersWidget::drawFinishButtonClicked() 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 @@ UserInputModeBordersWidget::drawFinishButtonClicked() borderFile->findAllBordersWithPointsNearBothSegmentEndPoints(displayGroup, browserTabIndex, surface, - this->inputModeBorders->borderBeingDrawnByOpenGL, + this->inputModeBorders->borderBeingDrawn, nearestTolerance, bordersFoundFromFile); break; @@ -674,15 +677,9 @@ UserInputModeBordersWidget::drawFinishButtonClicked() 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 @@ UserInputModeBordersWidget::drawFinishButtonClicked() borderFile->findAllBordersWithPointsNearBothSegmentEndPoints(displayGroup, browserTabIndex, surface, - this->inputModeBorders->borderBeingDrawnByOpenGL, + this->inputModeBorders->borderBeingDrawn, nearestTolerance, bordersFoundFromFile); break; @@ -762,21 +759,19 @@ UserInputModeBordersWidget::drawFinishButtonClicked() 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 @@ UserInputModeBordersWidget::drawFinishButtonClicked() } else { setLastEditedBorder(undoBorders); - this->inputModeBorders->borderBeingDrawnByOpenGL->clear(); + this->inputModeBorders->borderBeingDrawn->clear(); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); @@ -951,7 +946,7 @@ UserInputModeBordersWidget::processBorderOptimization(const DisplayGroupEnum::En setLastEditedBorder(undoBorders); if ( ! m_borderOptimizeDialog->isKeepBoundaryBorderSelected()) { - this->inputModeBorders->borderBeingDrawnByOpenGL->clear(); + this->inputModeBorders->borderBeingDrawn->clear(); } } @@ -973,17 +968,28 @@ UserInputModeBordersWidget::adjustViewActionTriggered() } /** - * 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 @@ UserInputModeBordersWidget::createEditOperationWidget() 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); + m_editPropertiesRadioButton = new QRadioButton("Properties"); + WuQtUtilities::setWordWrappedToolTip(m_editPropertiesRadioButton, + propertiesToolTipText); - 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*))); + 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 --git a/src/GuiQt/UserInputModeBordersWidget.h b/src/GuiQt/UserInputModeBordersWidget.h index e76acdde58a5ec6b0db8148747845639aecf760b..940b9370b61c3fdf936dcdf8881986a0221b9631 100644 --- a/src/GuiQt/UserInputModeBordersWidget.h +++ b/src/GuiQt/UserInputModeBordersWidget.h @@ -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 @@ namespace caret { 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 @@ namespace caret { UserInputModeBordersWidget& operator=(const UserInputModeBordersWidget&); - void setActionGroupByActionData(QActionGroup* actionGroup, - const int dataInteger); - - QActionGroup* drawOperationActionGroup; - - QActionGroup* editOperationActionGroup; - QWidget* createModeWidget(); QWidget* createDrawOperationWidget(); @@ -115,7 +111,25 @@ namespace caret { 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 --git a/src/GuiQt/UserInputModeFoci.cxx b/src/GuiQt/UserInputModeFoci.cxx index 7205b558acec6a21c710aef6c664f8d391f4fc1a..4e50bf45ad8cab4842e68c592bddca4c2e1bc8eb 100644 --- a/src/GuiQt/UserInputModeFoci.cxx +++ b/src/GuiQt/UserInputModeFoci.cxx @@ -61,13 +61,13 @@ using namespace caret; * 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); } @@ -103,26 +103,6 @@ UserInputModeFoci::setMode(const Mode mode) this->m_inputModeFociWidget->updateWidget(); } -/** - * @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 @@ UserInputModeFoci::getCursor() const 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 @@ UserInputModeFoci::mouseLeftClick(const MouseEvent& mouseEvent) true); switch (m_mode) { - case MODE_CREATE: + case MODE_CREATE_AT_ID: { SelectionItemSurfaceNode* idNode = idManager->getSurfaceNodeIdentification(); SelectionItemVoxel* idVoxel = idManager->getVoxelIdentification(); @@ -285,6 +257,7 @@ UserInputModeFoci::mouseLeftClick(const MouseEvent& mouseEvent) m_inputModeFociWidget); } } break; + case MODE_DELETE: case MODE_EDIT: { FociFile* fociFile = NULL; @@ -308,23 +281,26 @@ UserInputModeFoci::mouseLeftClick(const MouseEvent& mouseEvent) 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 --git a/src/GuiQt/UserInputModeFoci.h b/src/GuiQt/UserInputModeFoci.h index debcc471783143cf49ac5e40180befe512d15b23..c8ae102735e4fe4c938215be632fed2cddc6fcbf 100644 --- a/src/GuiQt/UserInputModeFoci.h +++ b/src/GuiQt/UserInputModeFoci.h @@ -37,14 +37,9 @@ namespace caret { 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 @@ namespace caret { void setMode(const Mode mode); - EditOperation getEditOperation() const; - - void setEditOperation(const EditOperation editOperation); - void updateAfterFociChanged(); Surface* getAnatomicalSurfaceForSurface(Surface* surface); @@ -96,8 +87,6 @@ namespace caret { UserInputModeFociWidget* m_inputModeFociWidget; Mode m_mode; - - EditOperation m_editOperation; }; #ifdef __USER_INPUT_MODE_FOCI_DECLARE__ diff --git a/src/GuiQt/UserInputModeFociWidget.cxx b/src/GuiQt/UserInputModeFociWidget.cxx index 82da5b06ad35d544bdc3ac17cf3fbbdfeb4dc20d..53750cb7dce2032c1acfaa45443e9f2ab8d3999d 100644 --- a/src/GuiQt/UserInputModeFociWidget.cxx +++ b/src/GuiQt/UserInputModeFociWidget.cxx @@ -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 @@ UserInputModeFociWidget::UserInputModeFociWidget(UserInputModeFoci* inputModeFoc ); 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 @@ UserInputModeFociWidget::updateWidget() * 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()); + case UserInputModeFoci::MODE_DELETE: + m_modeDeleteRadioButton->setChecked(true); break; - case UserInputModeFoci::MODE_OPERATIONS: - m_operationStackedWidget->setCurrentWidget(m_taskOperationWidget); - 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 @@ UserInputModeFociWidget::setActionGroupByActionData(QActionGroup* actionGroup, 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); - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); - layout->addWidget(m_modeComboBox); + 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); + + 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 @@ UserInputModeFociWidget::createLastIdentificationFocusActionTriggered() } } -/** - * @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 --git a/src/GuiQt/UserInputModeFociWidget.h b/src/GuiQt/UserInputModeFociWidget.h index e574c5ceec110c81ebe5ea6ba88eabb7c9bee297..65a646852ebf0e2060616e1dfb1c7b17d9fadf1e 100644 --- a/src/GuiQt/UserInputModeFociWidget.h +++ b/src/GuiQt/UserInputModeFociWidget.h @@ -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 @@ namespace caret { void createLastIdentificationFocusActionTriggered(); - void editOperationActionTriggered(QAction*); - - void modeComboBoxSelection(int); + void modeRadioButtonClicked(QAbstractButton* button); private: UserInputModeFociWidget(const UserInputModeFociWidget&); @@ -68,32 +65,17 @@ namespace caret { 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 --git a/src/GuiQt/UserInputModeImage.cxx b/src/GuiQt/UserInputModeImage.cxx index 4404dd03e7d197a059cdef2f58b1b297645b121a..890d28c87b8e6e38d2d0aca4fd8c416b8b0e8777 100644 --- a/src/GuiQt/UserInputModeImage.cxx +++ b/src/GuiQt/UserInputModeImage.cxx @@ -63,7 +63,8 @@ using namespace caret; * Constructor. */ UserInputModeImage::UserInputModeImage(const int32_t windowIndex) -: UserInputModeView(UserInputModeEnum::IMAGE), +: UserInputModeView(windowIndex, + UserInputModeEnum::Enum::IMAGE), m_windowIndex(windowIndex) { m_inputModeImageWidget = new UserInputModeImageWidget(this, diff --git a/src/GuiQt/UserInputModeImageWidget.cxx b/src/GuiQt/UserInputModeImageWidget.cxx index 7a1690aa5fa9b6e1676a232d32f91db80bc72191..9b5515e536789065713af1069bbdcc75b59c37d2 100644 --- a/src/GuiQt/UserInputModeImageWidget.cxx +++ b/src/GuiQt/UserInputModeImageWidget.cxx @@ -21,9 +21,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -92,118 +94,41 @@ UserInputModeImageWidget::UserInputModeImageWidget(UserInputModeImage* inputMode ); 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 @@ UserInputModeImageWidget::createEditOperationWidget() 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 @@ UserInputModeImageWidget::deleteAllActionTriggered() } } - /** - * 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 --git a/src/GuiQt/UserInputModeImageWidget.h b/src/GuiQt/UserInputModeImageWidget.h index 3748a74f8a264876cfe734d9ea7b13598eda1e24..655eb53fc11c7d2b25090745ed72b8e419e2cea9 100644 --- a/src/GuiQt/UserInputModeImageWidget.h +++ b/src/GuiQt/UserInputModeImageWidget.h @@ -26,7 +26,9 @@ #include "AString.h" +class QAbstractButton; class QActionGroup; +class QRadioButton; class QToolButton; namespace caret { @@ -53,32 +55,27 @@ namespace caret { 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 --git a/src/GuiQt/UserInputModeTileTabsManualLayout.cxx b/src/GuiQt/UserInputModeTileTabsManualLayout.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d3ec246567fb12c48b6d3ab15f279d67e3913173 --- /dev/null +++ b/src/GuiQt/UserInputModeTileTabsManualLayout.cxx @@ -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 --git a/src/GuiQt/UserInputModeTileTabsManualLayout.h b/src/GuiQt/UserInputModeTileTabsManualLayout.h new file mode 100644 index 0000000000000000000000000000000000000000..179f530677a7c88072fa7415a47a5587546bd51a --- /dev/null +++ b/src/GuiQt/UserInputModeTileTabsManualLayout.h @@ -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 --git a/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.cxx b/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a9e0dc76eba3de8308fd7c2df2b7d5d58cf0c95b --- /dev/null +++ b/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.cxx @@ -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 --git a/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.h b/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.h new file mode 100644 index 0000000000000000000000000000000000000000..e7789c95389debfc601dd925c101db56843360e2 --- /dev/null +++ b/src/GuiQt/UserInputModeTileTabsManualLayoutContextMenu.h @@ -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 --git a/src/GuiQt/UserInputModeView.cxx b/src/GuiQt/UserInputModeView.cxx index c92d5b611c88c882864650f441d8de5655f915b9..9699d097b08ee08fa299cb59c7320c65ed6318f3 100644 --- a/src/GuiQt/UserInputModeView.cxx +++ b/src/GuiQt/UserInputModeView.cxx @@ -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 @@ using namespace caret; /** * 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 @@ UserInputModeView::UserInputModeView() /** * 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 @@ UserInputModeView::mouseLeftClick(const MouseEvent& mouseEvent) 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 @@ UserInputModeView::mouseLeftDrag(const MouseEvent& mouseEvent) 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 @@ UserInputModeView::mouseLeftDragWithCtrl(const MouseEvent& mouseEvent) 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); } @@ -386,6 +442,106 @@ UserInputModeView::mouseLeftDragWithShift(const MouseEvent& mouseEvent) updateGraphics(mouseEvent); } +/** + * 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) * @@ -414,12 +570,58 @@ UserInputModeView::showContextMenu(const MouseEvent& mouseEvent, mouseY, false); - UserInputModeViewContextMenu contextMenu(viewportContent, + UserInputModeViewContextMenu contextMenu(mouseEvent, + viewportContent, idManager, openGLWidget); contextMenu.exec(menuPosition); } +/** + * 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. @@ -462,3 +664,118 @@ UserInputModeView::updateGraphics(const MouseEvent& mouseEvent) } } +/** + * 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 --git a/src/GuiQt/UserInputModeView.h b/src/GuiQt/UserInputModeView.h index 6f8e7b86b672f7e5f49e62b984aba70e8d1e0070..29401989deb6b76bef02a2cf0957febe56ebf48f 100644 --- a/src/GuiQt/UserInputModeView.h +++ b/src/GuiQt/UserInputModeView.h @@ -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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/UserInputModeViewContextMenu.cxx b/src/GuiQt/UserInputModeViewContextMenu.cxx index 141684efbc7187a34c8d0574d787766f663e9c29..5fb25567c62f8298a49beff257923f51bc256105 100644 --- a/src/GuiQt/UserInputModeViewContextMenu.cxx +++ b/src/GuiQt/UserInputModeViewContextMenu.cxx @@ -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 @@ using namespace caret; /** * Constructor. * + * @param mouseEvent + * The mouse event * @param viewportContent * Content of the viewport. * @param selectionManager @@ -104,7 +110,8 @@ using namespace caret; * @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 @@ UserInputModeViewContextMenu::UserInputModeViewContextMenu(BrainOpenGLViewportCo 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 @@ UserInputModeViewContextMenu::UserInputModeViewContextMenu(BrainOpenGLViewportCo /* * 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 @@ UserInputModeViewContextMenu::addSubMenuToMenu(QMenu* menu, } /** - * Add the identification actions to the menu. + * @return The identification menu */ -void -UserInputModeViewContextMenu::addIdentificationActions() +QMenu* +UserInputModeViewContextMenu::createIdentifyMenu() { /* * Accumlate identification actions @@ -351,15 +338,70 @@ UserInputModeViewContextMenu::addIdentificationActions() 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 @@ UserInputModeViewContextMenu::addBorderRegionOfInterestActions() } } + 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 @@ UserInputModeViewContextMenu::createParcelConnectivities() 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 @@ UserInputModeViewContextMenu::createParcelConnectivities() } /** - * 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 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() 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 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() 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 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() 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 @@ UserInputModeViewContextMenu::addChartActions() 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 @@ UserInputModeViewContextMenu::editChartLabelSelected() } /** - * 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 @@ UserInputModeViewContextMenu::addFociActions() 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 @@ UserInputModeViewContextMenu::addFociActions() 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 @@ UserInputModeViewContextMenu::addFociActions() + ") 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 @@ UserInputModeViewContextMenu::addFociActions() 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 @@ UserInputModeViewContextMenu::addFociActions() 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 @@ UserInputModeViewContextMenu::identifySurfaceBorderSelected() 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 @@ void 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 @@ UserInputModeViewContextMenu::identifySurfaceFocusSelected() 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 @@ UserInputModeViewContextMenu::identifyVolumeFocusSelected() 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 @@ UserInputModeViewContextMenu::identifyVoxelSelected() 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 @@ UserInputModeViewContextMenu::warnIfNetworkBrainordinateCountIsLarge(const int64 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 --git a/src/GuiQt/UserInputModeViewContextMenu.h b/src/GuiQt/UserInputModeViewContextMenu.h index 1f2021948c4c396aaa8f4dfa70432d87107fc989..8149c85d95a2e4b027712dc8b1c616d7ac74f6d9 100644 --- a/src/GuiQt/UserInputModeViewContextMenu.h +++ b/src/GuiQt/UserInputModeViewContextMenu.h @@ -42,6 +42,8 @@ namespace caret { class CiftiConnectivityMatrixDataFileManager; class CiftiFiberTrajectoryManager; class ChartingDataManager; + class ModelChartTwo; + class MouseEvent; class SelectionManager; class LabelFile; class Surface; @@ -51,7 +53,8 @@ namespace caret { Q_OBJECT public: - UserInputModeViewContextMenu(BrainOpenGLViewportContent* viewportContent, + UserInputModeViewContextMenu(const MouseEvent& mouseEvent, + BrainOpenGLViewportContent* viewportContent, SelectionManager* selectionManager, BrainOpenGLWidget* parentOpenGLWidget); @@ -94,6 +97,8 @@ namespace caret { void editChartLabelSelected(); + void showFrameBufferPixelRgbaSelected(); + private: enum class ParcelType { PARCEL_TYPE_INVALID, @@ -146,15 +151,17 @@ namespace caret { 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 --git a/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.cxx b/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.cxx new file mode 100644 index 0000000000000000000000000000000000000000..00d09732bc808c4232e9184f7c5440e6760662b8 --- /dev/null +++ b/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.cxx @@ -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 --git a/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.h b/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.h new file mode 100644 index 0000000000000000000000000000000000000000..1d66ceea1e454411ec41ab3702c264d2b2f5b523 --- /dev/null +++ b/src/GuiQt/UserInputModeViewContextTileTabsSubMenu.h @@ -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 --git a/src/GuiQt/UserInputModeVolumeEdit.cxx b/src/GuiQt/UserInputModeVolumeEdit.cxx index c5900750c2109c720740df6a926ed6d09cfc1ac1..31b48f3793af1808e593a65e3039ddbe633a1c96 100644 --- a/src/GuiQt/UserInputModeVolumeEdit.cxx +++ b/src/GuiQt/UserInputModeVolumeEdit.cxx @@ -62,7 +62,8 @@ using namespace caret; * 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 --git a/src/GuiQt/UserInputModeVolumeEditWidget.cxx b/src/GuiQt/UserInputModeVolumeEditWidget.cxx index 32b1d10c9e4337c70d56a157fd089f1e0da98b73..c727fbf828b7b1ee8da1571260b0003d7ce92bd9 100644 --- a/src/GuiQt/UserInputModeVolumeEditWidget.cxx +++ b/src/GuiQt/UserInputModeVolumeEditWidget.cxx @@ -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 @@ m_windowIndex(windowIndex) { 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 @@ UserInputModeVolumeEditWidget::updateWidget() 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 @@ UserInputModeVolumeEditWidget::updateWidget() 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 @@ UserInputModeVolumeEditWidget::updateWidget() 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 @@ UserInputModeVolumeEditWidget::updateWidget() 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 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() 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 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() 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 @@ UserInputModeVolumeEditWidget::createSelectionToolBar() 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 mode toolbar. + * @return Create and return the edit widget. */ QWidget* -UserInputModeVolumeEditWidget::createModeToolBar() +UserInputModeVolumeEditWidget::createEditWidget() { - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); + QToolButton* undoToolButton = new QToolButton(); + undoToolButton->setDefaultAction(WuQtUtilities::createAction("Undo", + "Undo the last volume edit", + this, + this, SLOT(undoActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(undoToolButton); - QLabel* modeLabel = new QLabel("Mode:"); - layout->addWidget(modeLabel); - m_volumeEditModeActionGroup = new QActionGroup(this); - m_volumeEditModeActionGroup->setExclusive(true); - QObject::connect(m_volumeEditModeActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(editingModeActionTriggered(QAction*))); + QToolButton* redoToolButton = new QToolButton(); + redoToolButton->setDefaultAction(WuQtUtilities::createAction("Redo", + "Redo (or is it undo) the last undo", + this, + this, SLOT(redoActionTriggered()))); + WuQtUtilities::setToolButtonStyleForQt5Mac(redoToolButton); - 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); + 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(); - layout->addWidget(toolButton); - } + 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)); - layout->addStretch(); + return rb; +} +/** + * @return Create and return the mode toolbar. + */ +QWidget* +UserInputModeVolumeEditWidget::createOperationWidget() +{ + m_modeOnRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON); + m_modeOnRadioButton->setChecked(true); + + m_modeOffRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF); + + m_modeDilateRadioButton = createModeRadioButton(VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE); + + 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 @@ UserInputModeVolumeEditWidget::getEditingParameters(VolumeEditingModeEnum::Enum& 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 --git a/src/GuiQt/UserInputModeVolumeEditWidget.h b/src/GuiQt/UserInputModeVolumeEditWidget.h index 2eb1c720c2585027fc3b2e681a9e748345e1d713..220eff3698defbc5f1daaac180ed912f9ea5834c 100644 --- a/src/GuiQt/UserInputModeVolumeEditWidget.h +++ b/src/GuiQt/UserInputModeVolumeEditWidget.h @@ -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 --git a/src/GuiQt/UserInputTileTabsContextMenu.cxx b/src/GuiQt/UserInputTileTabsContextMenu.cxx deleted file mode 100644 index 2efb41af3b71931e73c5f420e2b790cf88785009..0000000000000000000000000000000000000000 --- a/src/GuiQt/UserInputTileTabsContextMenu.cxx +++ /dev/null @@ -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 --git a/src/GuiQt/UserInputTileTabsContextMenu.h b/src/GuiQt/UserInputTileTabsContextMenu.h deleted file mode 100644 index bcb533891a9064d5019ba20d988aca88b6db1d23..0000000000000000000000000000000000000000 --- a/src/GuiQt/UserInputTileTabsContextMenu.h +++ /dev/null @@ -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 --git a/src/GuiQt/VolumePropertiesEditorDialog.cxx b/src/GuiQt/VolumePropertiesEditorDialog.cxx index 118fd0f8984e0c0aa14ce628082fae48934424ff..8e30492cefd7db27fedf67466170eb98290d574d 100644 --- a/src/GuiQt/VolumePropertiesEditorDialog.cxx +++ b/src/GuiQt/VolumePropertiesEditorDialog.cxx @@ -56,7 +56,7 @@ VolumePropertiesEditorDialog::VolumePropertiesEditorDialog(QWidget* parent) : 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 --git a/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx b/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx index 9525788ea1a917f3d44af44a7bdc94b80f8695d0..6ff46cbb69adb8cf04f28f0c1a4790c7c627ccc0 100644 --- a/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx +++ b/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx @@ -82,7 +82,7 @@ VolumeSurfaceOutlineColorOrTabViewController::updateViewController(VolumeSurface } this->modelComboBox->addItem(item->getName(), - qVariantFromValue((void*)item)); + QVariant::fromValue((void*)item)); } if (selectedItemIndex >= 0) { diff --git a/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx index 3ab2c97ebfd2d3d9a3f7b743f5d2e3f24036ac7f..ae17a5287fe837f06dcb7b8a25eac1c58a0f204a 100644 --- a/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx @@ -295,6 +295,7 @@ WbMacroCustomOperationAnimateOverlayCrossFade::executeCommandVersionTwo(QWidget* 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 @@ WbMacroCustomOperationAnimateOverlayCrossFade::executeCommandVersionOne(QWidget* 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 --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index f92123655e50fdf3b6dbcbbf22fe33a97dae5a57..6580fa43f999def65faab51cf564383eb80a08f5 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -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 @@ WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnu case WbMacroCustomOperationTypeEnum::INCREMENTAL_VOLUME_SLICE: operationOut = new WbMacroCustomOperationIncrementVolumeSlice(); break; + case WbMacroCustomOperationTypeEnum::SURFACE_DEFAULT_COLOR: + operationOut = new WbMacroCustomOperationSurfaceDefaultColor(); + break; } CaretAssert(operationOut); diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f7872242d73cd5cc23f929bab46072dcacd030de --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.cxx @@ -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 --git a/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.h b/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.h new file mode 100644 index 0000000000000000000000000000000000000000..4fc67e93d4f8ad8ca0cefe9a5c27ff87b7e5c882 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationSurfaceDefaultColor.h @@ -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 --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx index c5d08aa69613496f8c7e8834dd8b59796f1a0d7a..0f1332b38c811ca0ff7c299db026a6d51ebeff0b 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -138,6 +138,9 @@ WbMacroCustomOperationTypeEnum::initialize() "INCREMENTAL_VOLUME_SLICE", "Increment Volume Slice")); + enumData.push_back(WbMacroCustomOperationTypeEnum(SURFACE_DEFAULT_COLOR, + "SURFACE_DEFAULT_COLOR", + "Surface Default Color")); } /** diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h index dc70417027e613ca6f78e84674971ce76e70902a..d698e2fe510c3ebf6b6a3e8efc4c8f79f8a62a18 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.h +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -50,7 +50,9 @@ public: /** Incremental Rotation */ INCREMENTAL_ROTATION, /** Increment volume slice */ - INCREMENTAL_VOLUME_SLICE + INCREMENTAL_VOLUME_SLICE, + /** Surface default color */ + SURFACE_DEFAULT_COLOR }; diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 7c84c77c2919dc444be337733b277f0cfc24857d..81967e3293eacea822bab922b2a8ed80abe49f2a 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -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 --git a/src/GuiQt/WuQCollapsibleWidget.cxx b/src/GuiQt/WuQCollapsibleWidget.cxx index 17b8bbdc588d690f92f60e0037c9a2dd636372c3..fda6b0ed03903afbdb17d3f916a89d1446f11fd9 100644 --- a/src/GuiQt/WuQCollapsibleWidget.cxx +++ b/src/GuiQt/WuQCollapsibleWidget.cxx @@ -121,7 +121,7 @@ WuQCollapsibleWidget::addItem(QWidget* widget, 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 --git a/src/GuiQt/WuQColorEditorWidget.cxx b/src/GuiQt/WuQColorEditorWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2e9d9eccfc637efa4c359b0682ed22c50f25cd83 --- /dev/null +++ b/src/GuiQt/WuQColorEditorWidget.cxx @@ -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 --git a/src/GuiQt/WuQColorEditorWidget.h b/src/GuiQt/WuQColorEditorWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..d69cc2e7ac2330fde7b48ce91d985dc2c37e0a01 --- /dev/null +++ b/src/GuiQt/WuQColorEditorWidget.h @@ -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 --git a/src/GuiQt/WuQDoubleSpinBox.cxx b/src/GuiQt/WuQDoubleSpinBox.cxx index 8b8f390222160ecd0c0146c0a363eda16884ac65..d251feaa9757d16e033aeac120c7b52c3169fbc9 100644 --- a/src/GuiQt/WuQDoubleSpinBox.cxx +++ b/src/GuiQt/WuQDoubleSpinBox.cxx @@ -199,6 +199,7 @@ WuQDoubleSpinBox::decimals() const void WuQDoubleSpinBox::setDecimals(int prec) { + QSignalBlocker blocker(m_spinBox); m_decimalsMode = DecimalsMode::FIXED; m_spinBox->setDecimals(prec); } @@ -322,6 +323,7 @@ WuQDoubleSpinBox::testDigitsRightOfDecimal() void WuQDoubleSpinBox::updateDecimalsForAutoMode() { + QSignalBlocker blocker(m_spinBox); static bool decimalsTestFlag = false; if (decimalsTestFlag) { testDigitsRightOfDecimal(); @@ -376,6 +378,7 @@ WuQDoubleSpinBox::prefix() const void WuQDoubleSpinBox::setPrefix(const QString &prefix) { + QSignalBlocker blocker(m_spinBox); m_spinBox->setPrefix(prefix); } @@ -391,6 +394,7 @@ void WuQDoubleSpinBox::setRange(double minimum, double maximum) { + QSignalBlocker blocker(m_spinBox); m_minimumValue = minimum; m_maximumValue = maximum; m_rangeMode = RangeMode::INCLUSIVE; @@ -445,6 +449,7 @@ WuQDoubleSpinBox::setRangeExceedable(const double dataMinimum, const double spinBoxMinimum, const double spinBoxMaximum) { + QSignalBlocker blocker(m_spinBox); m_minimumValue = dataMinimum; m_maximumValue = dataMaximum; m_rangeMode = RangeMode::EXCEEDABLE; @@ -539,6 +544,7 @@ WuQDoubleSpinBox::setRangePercentage(const double minimumPercentage, void WuQDoubleSpinBox::setSingleStep(double value) { + QSignalBlocker blocker(m_spinBox); CaretAssert(value >= 0.0); m_spinBox->setSingleStep(value); @@ -575,6 +581,7 @@ WuQDoubleSpinBox::setSingleStepPercentage(double percentage) void WuQDoubleSpinBox::updateSingleStepPercentage() { + QSignalBlocker blocker(m_spinBox); switch (m_singleStepMode) { case SingleStepMode::FIXED: break; @@ -605,6 +612,7 @@ WuQDoubleSpinBox::updateSingleStepPercentage() void WuQDoubleSpinBox::setSuffix(const QString &suffix) { + QSignalBlocker blocker(m_spinBox); m_spinBox->setSuffix(suffix); } diff --git a/src/GuiQt/WuQImageLabel.cxx b/src/GuiQt/WuQImageLabel.cxx index 6be5eb87c8f1eae5cb33670cf5315dccd751b992..bc12fdba70b06e7e0661fcddb30c0d1bc278773a 100644 --- a/src/GuiQt/WuQImageLabel.cxx +++ b/src/GuiQt/WuQImageLabel.cxx @@ -20,6 +20,7 @@ /*LICENSE_END*/ #include +#include #define __WU_Q_IMAGE_LABEL_DECLARE__ #include "WuQImageLabel.h" @@ -189,6 +190,28 @@ WuQImageLabel::mouseMoveEvent(QMouseEvent* ev) } } +/** + * Called when the mouse button is pressed. + * + * @param ev + * 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. * @@ -224,6 +247,7 @@ WuQImageLabel::mouseReleaseEvent(QMouseEvent* ev) if ((dx < tolerance) && (dy < tolerance)) { emit clicked(); + emit clickedXY(ev->x(), ev->y()); } } } diff --git a/src/GuiQt/WuQImageLabel.h b/src/GuiQt/WuQImageLabel.h index e2ca1a24549d90986edab5e2ef32f095764f1995..e13d2d5cba01c8e82296b4a86718d4ef0974d97e 100644 --- a/src/GuiQt/WuQImageLabel.h +++ b/src/GuiQt/WuQImageLabel.h @@ -68,6 +68,21 @@ namespace caret { */ 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 --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 802f7aeba4da7de2c66deed1a8e32582638f0c83..72f25be9a5392e97b7be78154d6f28b5da99a46f 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -761,7 +761,7 @@ WuQMacroDialog::updateDialogContents() 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 --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 19c4c906a8881cc98d2d1c3beb81fee26186d533..609a3ae21ab5daf0140511202d59c6d220eb34c4 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -237,7 +237,7 @@ WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, 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 @@ WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, widgetWatcher->setParent(this); m_signalWatchers.insert(std::make_pair(name, widgetWatcher)); + + QObject::connect(object, &QObject::destroyed, + this, &WuQMacroManager::objectBeingDestroyed); + return true; } else { @@ -308,6 +312,30 @@ WuQMacroManager::addMacroSupportToObject(QObject* object, return resultFlag; } +/** + * 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 --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index a1fa8df223e228830b601350d62b6a3983004bcf..7e332193b0bc64fa95398fbbe4cd7e6e01b33225 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -224,6 +224,9 @@ namespace caret { const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut); + private slots: + void objectBeingDestroyed(QObject* object); + private: WuQMacroManager(const QString& name, QObject* parent = NULL); diff --git a/src/GuiQt/WuQMessageBox.cxx b/src/GuiQt/WuQMessageBox.cxx index abf5fe77d6a4500ee0050ca9a599f4fa0e22b093..58a90e35c5a94b57dc098ba730e95014769e9097 100644 --- a/src/GuiQt/WuQMessageBox.cxx +++ b/src/GuiQt/WuQMessageBox.cxx @@ -161,6 +161,8 @@ WuQMessageBox::warningCloseCancel(QWidget* parent, * 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 @@ WuQMessageBox::warningCloseCancel(QWidget* parent, 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 @@ WuQMessageBox::warningOkCancel(QWidget* parent, } 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 @@ WuQMessageBox::errorOk(QWidget* parent, 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 --git a/src/GuiQt/WuQMessageBox.h b/src/GuiQt/WuQMessageBox.h index f0847a8b0e8b22ff78d0f11e6c1a7cce06f1c354..23608379dfb27e05631b2ad2e82d76cdf4687ac3 100644 --- a/src/GuiQt/WuQMessageBox.h +++ b/src/GuiQt/WuQMessageBox.h @@ -48,9 +48,18 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/WuQScrollArea.cxx b/src/GuiQt/WuQScrollArea.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c90f9a049ed234a4b91c64c9fe5ea2ff7ce58b54 --- /dev/null +++ b/src/GuiQt/WuQScrollArea.cxx @@ -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 --git a/src/GuiQt/WuQScrollArea.h b/src/GuiQt/WuQScrollArea.h new file mode 100644 index 0000000000000000000000000000000000000000..7be68910fcbe793818aa17b58c77555b5f49df3c --- /dev/null +++ b/src/GuiQt/WuQScrollArea.h @@ -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 --git a/src/GuiQt/WuQSpinBox.cxx b/src/GuiQt/WuQSpinBox.cxx index 80fc7365804b2c1761509ac3cbea38d29804a0f7..5ddcbe7a5605d6d5a2107e0f3c6624a9f80f20e4 100644 --- a/src/GuiQt/WuQSpinBox.cxx +++ b/src/GuiQt/WuQSpinBox.cxx @@ -53,6 +53,11 @@ WuQSpinBox::~WuQSpinBox() { } +/** + * Process key events (override parent class method) + * @param event + * The key event + */ void WuQSpinBox::keyPressEvent(QKeyEvent* event) { @@ -64,3 +69,23 @@ WuQSpinBox::keyPressEvent(QKeyEvent* event) 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 --git a/src/GuiQt/WuQSpinBox.h b/src/GuiQt/WuQSpinBox.h index d11858300f84f9616fd411f896f619ceb31eeca5..ff7d5bcfe234f2275e343c48ce228417504ac74b 100644 --- a/src/GuiQt/WuQSpinBox.h +++ b/src/GuiQt/WuQSpinBox.h @@ -53,6 +53,8 @@ namespace caret { protected: void keyPressEvent(QKeyEvent* event) override; + void timerEvent(QTimerEvent* event) override; + private: // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQSpinBoxOddValue.cxx b/src/GuiQt/WuQSpinBoxOddValue.cxx index a1c00931e8ba4f80b2678709a052746ce4fd5ffb..4f9cd5c5c2444d9a35b25b8900cd2e81e251b82c 100644 --- a/src/GuiQt/WuQSpinBoxOddValue.cxx +++ b/src/GuiQt/WuQSpinBoxOddValue.cxx @@ -272,7 +272,7 @@ WuQSpinBoxOddValue::setValue(int val) WuQSpinBoxOddValueSpinBox::WuQSpinBoxOddValueSpinBox(QWidget* parent) : QSpinBox(parent) { - lineEdit()->setEnabled(false); + /* Line edit is not disabled, otherwise it appears disabled but arrow buttons still function */ } /** diff --git a/src/GuiQt/WuQTabBar.cxx b/src/GuiQt/WuQTabBar.cxx index 6b1a2934d8f1e4ce82806c91ec059a5ab21f333a..e74525a4774135ad6b43b9ba9e89e4341fa134ea 100644 --- a/src/GuiQt/WuQTabBar.cxx +++ b/src/GuiQt/WuQTabBar.cxx @@ -59,7 +59,7 @@ WuQTabBar::~WuQTabBar() void WuQTabBar::mousePressEvent(QMouseEvent* event) { - emit mousePressedSignal(); + emit mousePressedSignal(event); QTabBar::mousePressEvent(event); } @@ -73,7 +73,7 @@ WuQTabBar::mousePressEvent(QMouseEvent* event) void WuQTabBar::mouseReleaseEvent(QMouseEvent* event) { - emit mouseReleasedSignal(); + emit mouseReleasedSignal(event); QTabBar::mouseReleaseEvent(event); } diff --git a/src/GuiQt/WuQTabBar.h b/src/GuiQt/WuQTabBar.h index 84d294e2e70e021fe36194c0fcf613f3ce1be0f8..cc6bfd1393bb5bc5ebee6a656a3f320552dec398 100644 --- a/src/GuiQt/WuQTabBar.h +++ b/src/GuiQt/WuQTabBar.h @@ -48,9 +48,9 @@ namespace caret { // 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 --git a/src/GuiQt/WuQToolTipHelper.cxx b/src/GuiQt/WuQToolTipHelper.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8b2049fcba5850d881fb80a28b760214b1d52624 --- /dev/null +++ b/src/GuiQt/WuQToolTipHelper.cxx @@ -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 --git a/src/GuiQt/WuQToolTipHelper.h b/src/GuiQt/WuQToolTipHelper.h new file mode 100644 index 0000000000000000000000000000000000000000..7e7f8d7107b2063dd9b4013c05e15dcc20687b87 --- /dev/null +++ b/src/GuiQt/WuQToolTipHelper.h @@ -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 --git a/src/GuiQt/WuQValueChangedSignalWatcher.cxx b/src/GuiQt/WuQValueChangedSignalWatcher.cxx new file mode 100644 index 0000000000000000000000000000000000000000..793236c73e83e2b650607b12cb66bcafe58c602e --- /dev/null +++ b/src/GuiQt/WuQValueChangedSignalWatcher.cxx @@ -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 --git a/src/GuiQt/WuQValueChangedSignalWatcher.h b/src/GuiQt/WuQValueChangedSignalWatcher.h new file mode 100644 index 0000000000000000000000000000000000000000..255c6f35639cf4287923150de16fbce0da1e52d5 --- /dev/null +++ b/src/GuiQt/WuQValueChangedSignalWatcher.h @@ -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 --git a/src/GuiQt/WuQtUtilities.cxx b/src/GuiQt/WuQtUtilities.cxx index d653978ce630a808942a00d7aaa0621c8957ba11..43f364386d9f08528f29c9e78b8560346da7fe9f 100644 --- a/src/GuiQt/WuQtUtilities.cxx +++ b/src/GuiQt/WuQtUtilities.cxx @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -926,27 +927,67 @@ WuQtUtilities::matchWidgetHeights(QWidget* w1, 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 @@ WuQtUtilities::matchWidgetWidths(QWidget* w1, 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 @@ WuQtUtilities::matchWidgetWidths(QWidget* w1, 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) { @@ -1006,6 +1059,21 @@ WuQtUtilities::matchWidgetWidths(QWidget* w1, } } +/** + * 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 @@ -1695,5 +1763,26 @@ WuQtUtilities::setToolButtonStyleForQt5Mac(QToolButton* toolButton) 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 --git a/src/GuiQt/WuQtUtilities.h b/src/GuiQt/WuQtUtilities.h index 13cdeb7a900260b926d2dc0c7eab5ca0108496d0..71981649fa8b32bf6514a73df4506cbe9972408a 100644 --- a/src/GuiQt/WuQtUtilities.h +++ b/src/GuiQt/WuQtUtilities.h @@ -33,6 +33,7 @@ class QAction; class QBoxLayout; +class QComboBox; class QDialog; class QIcon; class QKeySequence; @@ -186,6 +187,8 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { 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 --git a/src/GuiQt/ZipSceneFileDialog.cxx b/src/GuiQt/ZipSceneFileDialog.cxx index c135161be162f3cb318391b9870e28135dd85787..b77e3300b90142b82af7298ec7db45aad9903c6c 100644 --- a/src/GuiQt/ZipSceneFileDialog.cxx +++ b/src/GuiQt/ZipSceneFileDialog.cxx @@ -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 @@ ZipSceneFileDialog::okButtonClicked() 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 --git a/src/Nifti/Matrix4x4.cxx b/src/Nifti/Matrix4x4.cxx index a4c4f86b4e768e670dcf4e0c188e2630ccf89569..c043f95dfc40eca6714948b7c890f23ca6601f70 100644 --- a/src/Nifti/Matrix4x4.cxx +++ b/src/Nifti/Matrix4x4.cxx @@ -75,7 +75,8 @@ static const double SMALL_NEGATIVE_NUMBER = -0.000001; * */ Matrix4x4::Matrix4x4() - : CaretObject() + : Matrix4x4Interface(), + CaretObject() { this->initializeMembersMatrix4x4(); } @@ -92,7 +93,8 @@ Matrix4x4::~Matrix4x4() * @param Object that is copied. */ Matrix4x4::Matrix4x4(const Matrix4x4& o) - : CaretObject(o) + : Matrix4x4Interface(o), + CaretObject(o) { this->initializeMembersMatrix4x4(); this->copyHelper(o); diff --git a/src/Nifti/Matrix4x4.h b/src/Nifti/Matrix4x4.h index d19c61b9e6f1528b357a0bfb0f6dc6445890bdaf..bb3fe7dbe9a5d0d3c9d998810f212416e137121e 100644 --- a/src/Nifti/Matrix4x4.h +++ b/src/Nifti/Matrix4x4.h @@ -23,6 +23,7 @@ #include "CaretObject.h" +#include "Matrix4x4Interface.h" #include #include @@ -35,7 +36,7 @@ namespace caret { /** * A 4x4 homogeneous transformation matrix. */ -class Matrix4x4 : public CaretObject { +class Matrix4x4 : public Matrix4x4Interface, public CaretObject { public: Matrix4x4(); @@ -132,7 +133,7 @@ public: 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 --git a/src/Nifti/NiftiHeader.cxx b/src/Nifti/NiftiHeader.cxx index 57f1506a15b52b48b8398d10618e112c5a391f47..7f4311a66fda8e483cec303f3e951a2ed8335e3e 100644 --- a/src/Nifti/NiftiHeader.cxx +++ b/src/Nifti/NiftiHeader.cxx @@ -297,6 +297,13 @@ vector > NiftiHeader::getSForm() const 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 @@ NiftiHeader::Quirks NiftiHeader::setupFrom(const nifti_2_header& header, const A { 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 --git a/src/Nifti/NiftiHeader.h b/src/Nifti/NiftiHeader.h index 53cb8dfa6f7d2d9377b4c0b1419d9c13ef6e054b..15fd0401b3adddb5b8db9eae93b01faa85504f5b 100644 --- a/src/Nifti/NiftiHeader.h +++ b/src/Nifti/NiftiHeader.h @@ -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 @@ namespace caret 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 --git a/src/Operations/OperationBorderFileExportToCaret5.cxx b/src/Operations/OperationBorderFileExportToCaret5.cxx index 7ef02cfcd3b480a61b9c1c091ac0fdef8895b117..c8e342760630390d45dc04b0538a606785cde7d4 100644 --- a/src/Operations/OperationBorderFileExportToCaret5.cxx +++ b/src/Operations/OperationBorderFileExportToCaret5.cxx @@ -121,7 +121,7 @@ OperationBorderFileExportToCaret5::useParameters(OperationParameters* myParams, 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 --git a/src/Operations/OperationBorderMerge.cxx b/src/Operations/OperationBorderMerge.cxx index 3d57000b42745c8ce554f2de30a28c3b12b04c6c..40835b17da4a2c4991472ae0195c22f603f87b0b 100644 --- a/src/Operations/OperationBorderMerge.cxx +++ b/src/Operations/OperationBorderMerge.cxx @@ -68,7 +68,7 @@ void OperationBorderMerge::useParameters(OperationParameters* myParams, Progress { 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 @@ void OperationBorderMerge::useParameters(OperationParameters* myParams, Progress 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 --git a/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx b/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx index f1b275990b915fcc90517d7ba0aee5f118ba17ee..21298dd5dee23575d899c804f6116c491b953a38 100644 --- a/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx +++ b/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx @@ -79,6 +79,9 @@ OperationParameters* OperationCiftiCreateDenseFromTemplate::getParameters() 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 @@ namespace 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 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m 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 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m 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 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m 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 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m 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 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m 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 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m } } 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) { - switch (surfInfo[whichStruct].type) + 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(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 @@ void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* m 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 --git a/src/Operations/OperationCiftiCreateParcellatedFromTemplate.cxx b/src/Operations/OperationCiftiCreateParcellatedFromTemplate.cxx index 8e81c1a3f38a01b3aea722622e0d5cc896a42896..9439b4d8a18facd148eed547542e79d6c6813e2f 100644 --- a/src/Operations/OperationCiftiCreateParcellatedFromTemplate.cxx +++ b/src/Operations/OperationCiftiCreateParcellatedFromTemplate.cxx @@ -76,7 +76,7 @@ void OperationCiftiCreateParcellatedFromTemplate::useParameters(OperationParamet { 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 --git a/src/Operations/OperationCiftiEstimateFWHM.cxx b/src/Operations/OperationCiftiEstimateFWHM.cxx index 078654d6b0787bbeb0887a4a8d5daa2c3cb87a50..51ce9af6edb87bd2885c7cb5e66d0674fcfefd7e 100644 --- a/src/Operations/OperationCiftiEstimateFWHM.cxx +++ b/src/Operations/OperationCiftiEstimateFWHM.cxx @@ -99,7 +99,7 @@ void OperationCiftiEstimateFWHM::useParameters(OperationParameters* myParams, Pr 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 --git a/src/Operations/OperationCiftiExportDenseMapping.cxx b/src/Operations/OperationCiftiExportDenseMapping.cxx index b14ad9de6182ba52031550b6710499bce8913555..ade9680a71618230fd0005a1bb7dfda5b7a22eb2 100644 --- a/src/Operations/OperationCiftiExportDenseMapping.cxx +++ b/src/Operations/OperationCiftiExportDenseMapping.cxx @@ -95,8 +95,8 @@ void OperationCiftiExportDenseMapping::useParameters(OperationParameters* myPara } 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 --git a/src/Operations/OperationCiftiMath.cxx b/src/Operations/OperationCiftiMath.cxx index b502511a608ff05835e3d60ac79517c87de37f95..49c638cdc3b5e7398d376c7ff33842e339516fd4 100644 --- a/src/Operations/OperationCiftiMath.cxx +++ b/src/Operations/OperationCiftiMath.cxx @@ -85,7 +85,7 @@ void OperationCiftiMath::useParameters(OperationParameters* myParams, ProgressOb 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 @@ void OperationCiftiMath::useParameters(OperationParameters* myParams, ProgressOb 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 --git a/src/Operations/OperationCiftiMerge.cxx b/src/Operations/OperationCiftiMerge.cxx index ad85df072861e8f4172f14666221b27481480a8c..ae24db6656cda547e906f3f3a49d08714a12bc87 100644 --- a/src/Operations/OperationCiftiMerge.cxx +++ b/src/Operations/OperationCiftiMerge.cxx @@ -23,7 +23,9 @@ #include "CaretAssert.h" #include "CaretPointer.h" +#include "CaretCommandGlobalOptions.h" #include "CiftiFile.h" +#include "FileInformation.h" #include @@ -67,8 +69,10 @@ OperationParameters* OperationCiftiMerge::getParameters() 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 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressO { 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 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressO 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 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressO 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 --git a/src/Operations/OperationConvertAffine.cxx b/src/Operations/OperationConvertAffine.cxx index 7055b857b4f0de90507de20dab886336c3aabb60..a5d96bfd6cf57b762d50ee14279d8dac6bacb65f 100644 --- a/src/Operations/OperationConvertAffine.cxx +++ b/src/Operations/OperationConvertAffine.cxx @@ -105,7 +105,7 @@ void OperationConvertAffine::useParameters(OperationParameters* myParams, Progre { 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 --git a/src/Operations/OperationConvertFiberOrientations.cxx b/src/Operations/OperationConvertFiberOrientations.cxx index 093cca5e8b85eff87ea480e5ed46426ab5630eef..dbf83cd57f17de322e94d99af13de0ed2da893a5 100644 --- a/src/Operations/OperationConvertFiberOrientations.cxx +++ b/src/Operations/OperationConvertFiberOrientations.cxx @@ -73,7 +73,7 @@ void OperationConvertFiberOrientations::useParameters(OperationParameters* myPar 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 --git a/src/Operations/OperationConvertWarpfield.cxx b/src/Operations/OperationConvertWarpfield.cxx index a606cf5ae7fb42d7a605ea2e98e198fe8e412e5e..2042c9f8c3286a1a5313af41fb06ff9431482e5b 100644 --- a/src/Operations/OperationConvertWarpfield.cxx +++ b/src/Operations/OperationConvertWarpfield.cxx @@ -116,7 +116,7 @@ void OperationConvertWarpfield::useParameters(OperationParameters* myParams, Pro { 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 --git a/src/Operations/OperationFociCreate.cxx b/src/Operations/OperationFociCreate.cxx index c5fd0b16905492b8b704fb18db00c59b74092095..4614b0cc783f0aa8690dbc82c4a4f1ff518af773 100644 --- a/src/Operations/OperationFociCreate.cxx +++ b/src/Operations/OperationFociCreate.cxx @@ -79,7 +79,7 @@ void OperationFociCreate::useParameters(OperationParameters* myParams, ProgressO 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 --git a/src/Operations/OperationLabelMerge.cxx b/src/Operations/OperationLabelMerge.cxx index e3d6ddc055e72166c50d784ba1ff39d7c1866137..e751f96e0a8e8dd790870039656224ec7f91a947 100644 --- a/src/Operations/OperationLabelMerge.cxx +++ b/src/Operations/OperationLabelMerge.cxx @@ -85,7 +85,7 @@ void OperationLabelMerge::useParameters(OperationParameters* myParams, ProgressO { 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 @@ void OperationLabelMerge::useParameters(OperationParameters* myParams, ProgressO 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 @@ void OperationLabelMerge::useParameters(OperationParameters* myParams, ProgressO 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 --git a/src/Operations/OperationMetricMath.cxx b/src/Operations/OperationMetricMath.cxx index 6b0aac59df6f4b53594399cea2c95f169696e8e7..9cb0c49281a82f66da199bd1482850fa2cbbc8b3 100644 --- a/src/Operations/OperationMetricMath.cxx +++ b/src/Operations/OperationMetricMath.cxx @@ -80,7 +80,7 @@ void OperationMetricMath::useParameters(OperationParameters* myParams, ProgressO 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 --git a/src/Operations/OperationMetricMerge.cxx b/src/Operations/OperationMetricMerge.cxx index 921166d8ee3677c8482703815594cb4776a5dbe1..cade22a54f5b883d6613b783db46d1f21a077919 100644 --- a/src/Operations/OperationMetricMerge.cxx +++ b/src/Operations/OperationMetricMerge.cxx @@ -67,7 +67,7 @@ void OperationMetricMerge::useParameters(OperationParameters* myParams, Progress { 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 @@ void OperationMetricMerge::useParameters(OperationParameters* myParams, Progress 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 @@ void OperationMetricMerge::useParameters(OperationParameters* myParams, Progress 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 --git a/src/Operations/OperationSceneFileMerge.cxx b/src/Operations/OperationSceneFileMerge.cxx index 0462bfe0e5abe7a2b29943b7d0d3f7c19a47521b..314710ba491a06c9a75d09c0db51cf0955e79c69 100644 --- a/src/Operations/OperationSceneFileMerge.cxx +++ b/src/Operations/OperationSceneFileMerge.cxx @@ -63,7 +63,7 @@ void OperationSceneFileMerge::useParameters(OperationParameters* myParams, Progr { 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 @@ void OperationSceneFileMerge::useParameters(OperationParameters* myParams, Progr 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 --git a/src/Operations/OperationSetMapNames.cxx b/src/Operations/OperationSetMapNames.cxx index 7d926b91fc02a6bf3bea387180986df5f7943ea0..5283f97c1acc06c656ad3ee6d4546a983ee0f94b 100644 --- a/src/Operations/OperationSetMapNames.cxx +++ b/src/Operations/OperationSetMapNames.cxx @@ -50,14 +50,17 @@ OperationParameters* OperationSetMapNames::getParameters() 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 @@ void OperationSetMapNames::useParameters(OperationParameters* myParams, Progress 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 @@ void OperationSetMapNames::useParameters(OperationParameters* myParams, Progress 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 --git a/src/Operations/OperationShowScene.cxx b/src/Operations/OperationShowScene.cxx index 19b9e499097276bc4d422d19e3b61c9b832d4255..61ec8366f01f6aaf18f4e926b3e847142667af49 100644 --- a/src/Operations/OperationShowScene.cxx +++ b/src/Operations/OperationShowScene.cxx @@ -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 @@ OperationShowScene::getParameters() "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 @@ OperationShowScene::useParameters(OperationParameters* myParams, } 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 @@ OperationShowScene::useParameters(OperationParameters* myParams, 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 @@ OperationShowScene::useParameters(OperationParameters* myParams, 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 @@ OperationShowScene::useParameters(OperationParameters* myParams, BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabContent, bwc, gapsAndMargins, + windowBeforeAspectLockingViewport, windowViewport, windowIndex, tabIndexToHighlight); @@ -512,7 +532,7 @@ OperationShowScene::useParameters(OperationParameters* myParams, std::vector constViewports(viewports.begin(), viewports.end()); brainOpenGL->drawModels(windowIndex, - UserInputModeEnum::VIEW, + UserInputModeEnum::Enum::VIEW, brain, mesaContext, constViewports); @@ -534,6 +554,10 @@ OperationShowScene::useParameters(OperationParameters* myParams, } viewports.clear(); } + } + else { + throw OperationException("Tile tabs configuration is neither Grid nor Manual"); + } } else { CaretPointer brainOpenGL(createBrainOpenGL()); @@ -557,12 +581,13 @@ OperationShowScene::useParameters(OperationParameters* myParams, tabContent, gapsAndMargins, windowIndex, + windowBeforeAspectLockingViewport, windowViewport)); std::vector viewportContents; viewportContents.push_back(content); brainOpenGL->drawModels(windowIndex, - UserInputModeEnum::VIEW, + UserInputModeEnum::Enum::VIEW, brain, mesaContext, viewportContents); diff --git a/src/Operations/OperationTemplate.cxx.txt b/src/Operations/OperationTemplate.cxx.txt index cd20ea8f0d1323cd7259d719029c0bc9df1d3e5b..d77ceae6b077e59fad0f81767dc6b4f58924c137 100644 --- a/src/Operations/OperationTemplate.cxx.txt +++ b/src/Operations/OperationTemplate.cxx.txt @@ -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 --git a/src/Operations/OperationTemplate.h.txt b/src/Operations/OperationTemplate.h.txt index b32913abb4930529cb155bea883f4c462f6640a8..7573f0c564305547d098b38273b3c580fb7270c8 100644 --- a/src/Operations/OperationTemplate.h.txt +++ b/src/Operations/OperationTemplate.h.txt @@ -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 --git a/src/Operations/OperationVolumeMath.cxx b/src/Operations/OperationVolumeMath.cxx index 002b14911621e6d52551c30e0f2bfafc3a1a2686..7ec812b3ba87a7e00196ae3aefd0a1fa4c96ccf8 100644 --- a/src/Operations/OperationVolumeMath.cxx +++ b/src/Operations/OperationVolumeMath.cxx @@ -80,7 +80,7 @@ void OperationVolumeMath::useParameters(OperationParameters* myParams, ProgressO 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 --git a/src/Operations/OperationVolumeMerge.cxx b/src/Operations/OperationVolumeMerge.cxx index cda6f3e70dce3595f098c4bf26ee3be4ed01b5fa..82f918266d6de7a38457c816f39bdad1f8d3b863 100644 --- a/src/Operations/OperationVolumeMerge.cxx +++ b/src/Operations/OperationVolumeMerge.cxx @@ -67,7 +67,7 @@ void OperationVolumeMerge::useParameters(OperationParameters* myParams, Progress { 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 @@ void OperationVolumeMerge::useParameters(OperationParameters* myParams, Progress } 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 @@ void OperationVolumeMerge::useParameters(OperationParameters* myParams, Progress 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 --git a/src/Operations/OperationWbsparseMergeDense.cxx b/src/Operations/OperationWbsparseMergeDense.cxx index 9a0df03ede17a492a2ca4496fd5155cabed172ac..3a2cf20f1b5531b3d1845c119704ffe5e46be8fe 100644 --- a/src/Operations/OperationWbsparseMergeDense.cxx +++ b/src/Operations/OperationWbsparseMergeDense.cxx @@ -66,7 +66,7 @@ void OperationWbsparseMergeDense::useParameters(OperationParameters* myParams, P 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 --git a/src/OperationsBase/OperationParameters.cxx b/src/OperationsBase/OperationParameters.cxx index 6f3a3f27b202aac44426d6bd9ca29f775a3f2081..88a4816da45f2efde5388f9c4ec56764f7604aa3 100644 --- a/src/OperationsBase/OperationParameters.cxx +++ b/src/OperationsBase/OperationParameters.cxx @@ -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 @@ ParameterComponent::ParameterComponent(const ParameterComponent& rhs) 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 @@ OptionalParameter* ParameterComponent::createOptionalParameter(const int32_t key 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 @@ AbstractParameter* ParameterComponent::getInputParameter(const int32_t key, cons 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 @@ OptionalParameter* ParameterComponent::getOptionalParameter(const int32_t key) 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 @@ AbstractParameter* ParameterComponent::getOutputParameter(const int32_t key, con 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 OperationParameters::setHelpText(const AString& textIn) 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 --git a/src/OperationsBase/OperationParameters.h b/src/OperationsBase/OperationParameters.h index 49371127685f5212f099bdaf4d8721f41cf8c536..d386824174061e880c8a1b8c4490d4e4333851a4 100644 --- a/src/OperationsBase/OperationParameters.h +++ b/src/OperationsBase/OperationParameters.h @@ -211,8 +211,20 @@ namespace caret { 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 @@ namespace caret { 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 @@ namespace caret { ///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 --git a/src/Palette/CMakeLists.txt b/src/Palette/CMakeLists.txt index 3fe11d04773a399b47b870fe2a869609bac775ec..dbe237c462540c7f568cf28e8ccc3e1907bdf02d 100644 --- a/src/Palette/CMakeLists.txt +++ b/src/Palette/CMakeLists.txt @@ -19,12 +19,17 @@ ENDIF () # 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 @@ PaletteScalarAndColor.h 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 --git a/src/Palette/EventPaletteGroupsGet.cxx b/src/Palette/EventPaletteGroupsGet.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c3cf0a8888902de958a10c2b4c9e076a2746aff2 --- /dev/null +++ b/src/Palette/EventPaletteGroupsGet.cxx @@ -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 --git a/src/Palette/EventPaletteGroupsGet.h b/src/Palette/EventPaletteGroupsGet.h new file mode 100644 index 0000000000000000000000000000000000000000..f6b2474cee9dc7e8123d3f352ff3bf8664c05baa --- /dev/null +++ b/src/Palette/EventPaletteGroupsGet.h @@ -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 --git a/src/Palette/PaletteGroup.cxx b/src/Palette/PaletteGroup.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0663320f9e5feedbed7a4f9482bad4a8aca74270 --- /dev/null +++ b/src/Palette/PaletteGroup.cxx @@ -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 --git a/src/Palette/PaletteGroup.h b/src/Palette/PaletteGroup.h new file mode 100644 index 0000000000000000000000000000000000000000..7de51ec92f6e0a03b85632453363661b23379694 --- /dev/null +++ b/src/Palette/PaletteGroup.h @@ -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 --git a/src/Palette/PaletteGroupStandardPalettes.cxx b/src/Palette/PaletteGroupStandardPalettes.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6967560ac3ef4d139e60ee5b64db946ed9d7eb68 --- /dev/null +++ b/src/Palette/PaletteGroupStandardPalettes.cxx @@ -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 --git a/src/Palette/PaletteGroupStandardPalettes.h b/src/Palette/PaletteGroupStandardPalettes.h new file mode 100644 index 0000000000000000000000000000000000000000..4bbd5eb781ee6388f248a3cea1f2e929bda21d55 --- /dev/null +++ b/src/Palette/PaletteGroupStandardPalettes.h @@ -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 --git a/src/Palette/PaletteGroupUserCustomPalettes.cxx b/src/Palette/PaletteGroupUserCustomPalettes.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e618ae926251e7e25d844b8fe82f5091b982250c --- /dev/null +++ b/src/Palette/PaletteGroupUserCustomPalettes.cxx @@ -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 --git a/src/Palette/PaletteGroupUserCustomPalettes.h b/src/Palette/PaletteGroupUserCustomPalettes.h new file mode 100644 index 0000000000000000000000000000000000000000..18958c0bbb957dfda921b6737f43e4bb02bb6c5d --- /dev/null +++ b/src/Palette/PaletteGroupUserCustomPalettes.h @@ -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 --git a/src/Palette/PaletteNew.cxx b/src/Palette/PaletteNew.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3aa8d3782e40bf362eb8ab01447344306c1229c6 --- /dev/null +++ b/src/Palette/PaletteNew.cxx @@ -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 --git a/src/Palette/PaletteNew.h b/src/Palette/PaletteNew.h new file mode 100644 index 0000000000000000000000000000000000000000..bd866250d5337037f10ef8f8091dfa9b40d35f8d --- /dev/null +++ b/src/Palette/PaletteNew.h @@ -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 --git a/src/Qwt/qwt_null_paintdevice.cpp b/src/Qwt/qwt_null_paintdevice.cpp index db1611da25ca9e872157720710aa21c59421118b..cefbc51dfd82b0627db75221ebb91e9aec383936 100644 --- 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 { diff --git a/src/Qwt/qwt_painter.cpp b/src/Qwt/qwt_painter.cpp index e3761afc062e068175a2882371023739c0c5d5d5..cc44f07e9943ec70a90e721bf4583cc675672fc3 100644 --- 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 diff --git a/src/Qwt/qwt_painter_command.h b/src/Qwt/qwt_painter_command.h index 2da597a7fa79dfe04c7f23e2cc5d8851cf04a0f1..ced765a1b95817b1d3e1527abf93669c6eafe716 100644 --- a/src/Qwt/qwt_painter_command.h +++ b/src/Qwt/qwt_painter_command.h @@ -15,6 +15,7 @@ #include #include #include +#include class QPainterPath; diff --git a/src/Qwt/qwt_plot_panner.cpp b/src/Qwt/qwt_plot_panner.cpp index 62a75a2fb3569fcd03c43e7c4b6878fb558b4997..7cc43e8ec3a7062f24b18a4cd8b9ab2ed0fae349 100644 --- 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 ) { diff --git a/src/Qwt/qwt_plot_renderer.cpp b/src/Qwt/qwt_plot_renderer.cpp index b7bfb01cb65c54afc59093113999d42ff253b1c9..39b4fef715a188b9b3b2ac22975f2384b1c89ed8 100644 --- 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 diff --git a/src/Qwt/qwt_widget_overlay.cpp b/src/Qwt/qwt_widget_overlay.cpp index d2b4220c4dd2ecac66a18d70243f680d63b601c1..cdacee3c20913d55f921c545b0821ecedf86d0d0 100644 --- 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 --git a/src/Resources/Gui/PaletteEditorDialog/construction.png b/src/Resources/Gui/PaletteEditorDialog/construction.png new file mode 100644 index 0000000000000000000000000000000000000000..8f4cc3d8ec4b1d358e21ba9a697841175db55d9e Binary files /dev/null and b/src/Resources/Gui/PaletteEditorDialog/construction.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/favorite_filled.png b/src/Resources/Gui/RecentFilesDialog/favorite_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..93790e0424607484cc185bc8c2c2f4c917f83d4d Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/favorite_filled.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/favorite_outline.png b/src/Resources/Gui/RecentFilesDialog/favorite_outline.png new file mode 100644 index 0000000000000000000000000000000000000000..c8397fca94960cd1f45a69674cecaac81e50ef45 Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/favorite_outline.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/forget.png b/src/Resources/Gui/RecentFilesDialog/forget.png new file mode 100644 index 0000000000000000000000000000000000000000..b23719386b1d99a78db599b3d9c264541fbc5347 Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/forget.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/forget_black.png b/src/Resources/Gui/RecentFilesDialog/forget_black.png new file mode 100644 index 0000000000000000000000000000000000000000..73cdbeed88c445b422ed0af9549b321632d36e9b Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/forget_black.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/forget_on.png b/src/Resources/Gui/RecentFilesDialog/forget_on.png new file mode 100644 index 0000000000000000000000000000000000000000..a62b54b2cf6d670c9fb917bce6c56f2ac3319a5c Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/forget_on.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/forget_red.png b/src/Resources/Gui/RecentFilesDialog/forget_red.png new file mode 100644 index 0000000000000000000000000000000000000000..33fa5d47d348ca6a1e39a08649f2eedc41e67558 Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/forget_red.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/hcp_image.png b/src/Resources/Gui/RecentFilesDialog/hcp_image.png new file mode 100644 index 0000000000000000000000000000000000000000..eed64e268ffb3febab391611e823cc57012773f4 Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/hcp_image.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/share.png b/src/Resources/Gui/RecentFilesDialog/share.png new file mode 100644 index 0000000000000000000000000000000000000000..d83912e1b4c701c6f6cb61d5f3b9cb7c223015d5 Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/share.png differ diff --git a/src/Resources/Gui/RecentFilesDialog/twitter_image.png b/src/Resources/Gui/RecentFilesDialog/twitter_image.png new file mode 100644 index 0000000000000000000000000000000000000000..ac7c7814abbac7111d046bde92f685084ec8a49a Binary files /dev/null and b/src/Resources/Gui/RecentFilesDialog/twitter_image.png differ diff --git a/src/Resources/Gui/ToolBar/clipping.png b/src/Resources/Gui/ToolBar/clipping.png new file mode 100644 index 0000000000000000000000000000000000000000..37cd8076414b92b2d58b411af991e9a50934b6d3 Binary files /dev/null and b/src/Resources/Gui/ToolBar/clipping.png differ diff --git a/src/Resources/Gui/ToolBar/lighting.png b/src/Resources/Gui/ToolBar/lighting.png new file mode 100644 index 0000000000000000000000000000000000000000..7889cbe043a6e2d9162fb513dbffd86f2e63f40b Binary files /dev/null and b/src/Resources/Gui/ToolBar/lighting.png differ diff --git a/src/Resources/Gui/gui_resources.qrc b/src/Resources/Gui/gui_resources.qrc index 92f718a5f42836d496c5c22518d39a05d27b68e6..35da8cc6e937f1cc8a9dadae80bc3e81ee93dfbd 100644 --- a/src/Resources/Gui/gui_resources.qrc +++ b/src/Resources/Gui/gui_resources.qrc @@ -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 diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image001.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image001.png deleted file mode 100644 index 43aa24b1f6fefc940b0500290baa10cfb213a269..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image001.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image002.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image002.png deleted file mode 100644 index 77bb61cee0c5439cc44d53cd873e356e94fed3bc..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image002.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image003.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image003.png deleted file mode 100644 index 2ab80839ff47532695d645e957d06c7a989effe3..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image003.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image004.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image004.png deleted file mode 100644 index 7de71cf7cbe305773b83ee7dd8e16bdb053f3bc3..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image004.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image005.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image005.png deleted file mode 100644 index a13630c5ffe9b7c993fd55e83cc65f4811ddbca9..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image005.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image006.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image006.png deleted file mode 100644 index c3b93f4e887f9ea0a5867b7805647b945a8689c5..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image006.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image007.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image007.png deleted file mode 100644 index 6516015af3c88733354a4b3fa5e832e384c5844b..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image007.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png deleted file mode 100644 index e13dbb33f62df8f54fb5e203bce765c3c16e6779..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image018.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image018.png deleted file mode 100644 index f13b27cde7fb141dd8ebc983f36983b1a41b5efc..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image018.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image021.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image021.png deleted file mode 100644 index 9e9521756afda23f675426c53022465a3d0586bf..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image021.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image022.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image022.png deleted file mode 100644 index 71f118ecc5796e1f2bdc366f52f596510d348422..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image022.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image023.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image023.png deleted file mode 100644 index 3f65675a010d4814151ba82d36aa25b897e864f9..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image023.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image025.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image025.png deleted file mode 100644 index 92655d09e68b3c2d775cea77a36bfa77e7e3c1f6..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image025.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image026.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image026.png deleted file mode 100644 index 2221970ed47466e5ac7169afef51cd6f83aeee36..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image026.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image027.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image027.png deleted file mode 100644 index 0db49e281eb6e1de6aa94255f3497bf33d7f9dc4..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image027.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image028.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image028.png deleted file mode 100644 index c3e8baad51a415e9dbfc55ac2f9d320c121f6a3c..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image028.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image029.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image029.png deleted file mode 100644 index 9218a2d51ff741cc825d10db23e99600e60086e7..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image029.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image030.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image030.png deleted file mode 100644 index b6c8066706c2e80ebf4ce948d565f40f2cd594c5..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image030.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image031.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image031.png deleted file mode 100644 index 401395b926bd020b89dbaaa76dc3a3617fc31f54..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image031.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image032.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image032.png deleted file mode 100644 index 9f10b114486ea08517b9481da87692ff52e3f855..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image032.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image033.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image033.png deleted file mode 100644 index 8b1cc291abaa918c605d813dd5c6c9e05bbae4b6..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image033.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image001.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image001.png new file mode 100644 index 0000000000000000000000000000000000000000..579cba013a1fd15da057b6e029ba10fdb3717109 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image001.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image002.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image002.png new file mode 100644 index 0000000000000000000000000000000000000000..38c7eced4ff975cd8677150317db61fc88caecab Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image002.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image003.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image003.png new file mode 100644 index 0000000000000000000000000000000000000000..f40e5da7d3c400a72bc5934bd0dce20e5ed3de1c Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image003.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image004.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image004.png new file mode 100644 index 0000000000000000000000000000000000000000..7a277db74e987ccdfaf6f1c8a65cd5e72d4d3d03 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image004.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image005.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image005.png new file mode 100644 index 0000000000000000000000000000000000000000..263eda07ba528146fabc0514633f1932c87a8803 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image005.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image006.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image006.png new file mode 100644 index 0000000000000000000000000000000000000000..be3889a5914fd8758ba313a8e1700759b7820414 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image006.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image007.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image007.png new file mode 100644 index 0000000000000000000000000000000000000000..e828658acde98c01faf7f0e226e1753fd0e301e7 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image007.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image008.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image008.png new file mode 100644 index 0000000000000000000000000000000000000000..2f7280b669b5ddddf20eb22079db4c7c8b32bf4e Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image008.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image009.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image009.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image009.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image009.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image010.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image010.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image010.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image010.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image011.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image011.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image011.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image011.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image012.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image012.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image012.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image012.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image013.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image013.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image013.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image013.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image014.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image014.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image014.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image014.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image015.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image015.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image015.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image015.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image016.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image016.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image016.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image016.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image017.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image017.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image017.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image017.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image019.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image018.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image019.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image018.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image020.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image019.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image020.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image019.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image020.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image020.png new file mode 100644 index 0000000000000000000000000000000000000000..1265662f55e5ca783b647a28e76611843fb6b762 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image020.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image021.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image021.png new file mode 100644 index 0000000000000000000000000000000000000000..fe713db9da9c5320f7dfa385c45eb96cc9a28624 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image021.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image022.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image022.png new file mode 100644 index 0000000000000000000000000000000000000000..e33bdbdc37ba5117cbda407465141a0a9bf2fa38 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image022.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image024.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image023.png similarity index 100% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image024.png rename to src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image023.png diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image024.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image024.png new file mode 100644 index 0000000000000000000000000000000000000000..01fbeeb2622b87fc8057cbf39ce218ac9f1d5666 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image024.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image025.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image025.png new file mode 100644 index 0000000000000000000000000000000000000000..73475ef28dadee9df771aa3895e5fa72e02881e4 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image025.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image026.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image026.png new file mode 100644 index 0000000000000000000000000000000000000000..9665df1d0b7b865a45e08344264a09055ca83c1c Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image026.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image027.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image027.png new file mode 100644 index 0000000000000000000000000000000000000000..237c3638a9e3abd9a0d9e9ed671851b2cab950bb Binary files /dev/null and b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.4.2.fld/image027.png differ diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html b/src/Resources/Help/HelpFiles/Annotations/Annotations_Guide.html similarity index 50% rename from src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html rename to src/Resources/Help/HelpFiles/Annotations/Annotations_Guide.html index b25375eaa219b993cc3cd7aafca801a14ce69ebe..5551713464cc55447ed101bb94c42c6ca850123e 100644 --- a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.html +++ b/src/Resources/Help/HelpFiles/Annotations/Annotations_Guide.html @@ -1,62 +1,63 @@ - - - + + + + + + +

+ +

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 --git a/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html b/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html index f34e25892d9d8dabb8a1f872950295a7fb654b8e..0468182931e39929b7ce3a0b7e67145dd8ba1c3f 100644 --- a/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html +++ b/src/Resources/Help/HelpFiles/Menus/File_Menu/File_Menu.html @@ -113,6 +113,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 @@ -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 --git a/src/Resources/Help/HelpFiles/Menus/Menus.html b/src/Resources/Help/HelpFiles/Menus/Menus.html index 3a3171881ed2b65eee491bb4c8939733e9f0c8f9..d18b39232f1f74e8f12267cd5845244ddec16d24 100644 --- a/src/Resources/Help/HelpFiles/Menus/Menus.html +++ b/src/Resources/Help/HelpFiles/Menus/Menus.html @@ -15,6 +15,8 @@

        • View
        • Data
        • Surface
        • +
        • Volume
        • +
        • Macro
        • Develop  (Contains developer options; must be enabled through Preferences)
          diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/RightClickPopUpMenu.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/RightClickPopUpMenu.png new file mode 100644 index 0000000000000000000000000000000000000000..651038c934c9b1e9ddb3cb96e6fde5d3b9f539d1 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/RightClickPopUpMenu.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/TabBar_Tab_Menu.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/TabBar_Tab_Menu.png new file mode 100644 index 0000000000000000000000000000000000000000..88708b605a134c47ec325e82661fc307665c3115 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/TabBar_Tab_Menu.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db deleted file mode 100755 index e3935e7f00203ca23c7e6a52de3b83e068b2fd44..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Thumbs.db and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html index fe88ce268fc5001c815896268db065875b5d1d81..0c8324af3ffc0133fe57c2379525530f895810f6 100644 --- a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html +++ b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html @@ -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.

                diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png index 600f460c9ce2d9ee91d811d8d69f9c9d75ce6540..e8c7ce8628e44267578fde37b5ae2ef23945ca3f 100644 Binary files a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png and b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_AutomaticGrid.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_AutomaticGrid.png new file mode 100644 index 0000000000000000000000000000000000000000..dd96aacfa8c5d2caf53fe148adfb60bf685a5257 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_AutomaticGrid.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_Manual.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_Manual.png new file mode 100644 index 0000000000000000000000000000000000000000..47df8c37bd38858bdf85dc4f939d60c9929f5ad1 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration_Manual.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Toolbar_Arrange_Menu.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Toolbar_Arrange_Menu.png new file mode 100644 index 0000000000000000000000000000000000000000..54761e41960da64cda3cc1eafb85785917092c8d Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Toolbar_Arrange_Menu.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Window_Tile_Editing_Toolbar.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Window_Tile_Editing_Toolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..f08490c0ae59e70acb2627bc7588d380daf6df91 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Window_Tile_Editing_Toolbar.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html b/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html index 33f52ed368c4aee9431c544ed9241cb4868c0f45..3a87884d892554f9992194a41cd1c6f7f6955c71 100644 --- a/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html +++ b/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.html @@ -154,22 +154,7 @@ href="file:///Volumes/myelin1/shared/WB_Tutorial/WB_1.3_Help/Workbench_Window/Vi 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.
                -
              • -
              +

              diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.png b/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.png index 055a6291b952273679c6e34c12905e776c98da37..fca4e41ef2e2b781bf4e8ffff7f70408349c673c 100644 Binary files a/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.png and b/src/Resources/Help/HelpFiles/Menus/View_Menu/View_Menu.png differ diff --git a/src/Resources/Help/HelpFiles/Menus/Volume_Menu/Volume_Menu.html b/src/Resources/Help/HelpFiles/Menus/Volume_Menu/Volume_Menu.html new file mode 100644 index 0000000000000000000000000000000000000000..4da25a64365d0b1ec4fe82ec5fd7867594238cbb --- /dev/null +++ b/src/Resources/Help/HelpFiles/Menus/Volume_Menu/Volume_Menu.html @@ -0,0 +1,13 @@ + + + + + Volume Menu + + + Volume Menu
              +
                +
              • Properties... - Displays dialog for editing volume display properties
              • +
              + + diff --git a/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html b/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html index 2b740c67f4ec69f9d9f4cee6248c13f46a1b8064..aa37a47ebd614738651512f3f4b2bb6a23f5b542 100644 --- a/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html +++ b/src/Resources/Help/HelpFiles/Mouse_Controls/Mouse_Controls.html @@ -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 --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html index e777ae7883cdd5a5580a663fac131a2401ba92bc..6434a37c2484e631b720831871fbd361dfb8a99a 100644 --- a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/Chart Layers.html @@ -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 +

              + diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/DynLinesChartingLayers.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/DynLinesChartingLayers.png new file mode 100644 index 0000000000000000000000000000000000000000..06e7b6635cb7854c199d57693d200a6399d11c84 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/DynLinesChartingLayers.png differ diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/HistogramChartingLayers.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/HistogramChartingLayers.png new file mode 100644 index 0000000000000000000000000000000000000000..7fd9e67f7bfab406275e0fb133a1aa826d68e824 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/HistogramChartingLayers.png differ diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/LinesChartingLayers.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/LinesChartingLayers.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf339ff6f7ba949a535bdeed3da9aba85ad256b Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/LinesChartingLayers.png differ diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/MatrixChartLayers.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/MatrixChartLayers.png new file mode 100644 index 0000000000000000000000000000000000000000..d9bc0b88fb4a5e0e98ddcdc34bfeb01bc3230e00 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Chart_Layers/MatrixChartLayers.png differ diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html index 41ff6e180f035f5c087c84b2c0668d836bd884da..627f6b67fcbdc283000484d8853d7bf42fa7954b 100644 --- a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.html @@ -74,7 +74,7 @@
            -
            +
            diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png index 86fe45ed71ca6d8377bd27ef668eab8da59d1052..348b16245ed934bee62c844d95ed8c85aa93955d 100644 Binary files a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Connectivity/Connectivity.png differ diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.html new file mode 100644 index 0000000000000000000000000000000000000000..d0e80b1d7b90cfad870399e8a4afa70d65830b34 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.html @@ -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 diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.png new file mode 100644 index 0000000000000000000000000000000000000000..37cf2b67ff44176cb6ae4fd5492aa93df54280ff Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Information/Information.png differ diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html index fd05fc28c68706453756c4a58639dce002755400..baaa825145bf79d808c4df24deeeeadc72b21a9e 100644 --- a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Layers/Layers.html @@ -23,19 +23,6 @@ displayed. When a top layer is unchecked, the layers 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 @@ -70,6 +57,19 @@ value, layers under the selected layer will show through.
          +
            +
          • 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.
          • +
          • Yoke sets map yoking between layers to a map yoking group denoted by a Roman numeral. diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.html new file mode 100644 index 0000000000000000000000000000000000000000..f665859e3a2dfb67fbd982a979d5df2e85a0b482 --- /dev/null +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.html @@ -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 diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.png new file mode 100644 index 0000000000000000000000000000000000000000..0b182b7af0364b93fea73c0dd1b283bff7504f45 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Media/Media.png differ diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html index a07d3dd5d52e180942ece45cb0b5bd33ce60ea72..f0234420c70b4a612cb08cc4ea0f9400969535e4 100644 --- a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html +++ b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox.html @@ -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 diff --git a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png index 4038fa4c934e2923eb76fedd958d04955af48f37..9581f7b305fe1409ba5ddf1af2febdc566111539 100644 Binary files a/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png and b/src/Resources/Help/HelpFiles/Overlay_Toolbox/Overlay_Toolbox_horiz.png differ diff --git a/src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html b/src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html deleted file mode 100644 index 52bd623575de0bcc346b95f2ea3e14059c07fcf3..0000000000000000000000000000000000000000 --- a/src/Resources/Help/HelpFiles/Splash_Screen/Splash_Screen.html +++ /dev/null @@ -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]).
            - -
              -
              -
              -
            -
            -
            -
          • -
          - - diff --git a/src/Resources/Help/HelpFiles/Splash_Screen/Splash_screen.png b/src/Resources/Help/HelpFiles/Splash_Screen/Splash_screen.png deleted file mode 100644 index 561f0b7ee40d2199725259d8725dfdb96b030633..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Splash_Screen/Splash_screen.png and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Splash_Screen/Thumbs.db b/src/Resources/Help/HelpFiles/Splash_Screen/Thumbs.db deleted file mode 100755 index faa8bb888cf97e961faada06e28f27fca18e6644..0000000000000000000000000000000000000000 Binary files a/src/Resources/Help/HelpFiles/Splash_Screen/Thumbs.db and /dev/null differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html b/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html index 22dee811949959a2eeada33924ff55ac726591e6..de4992d0277cc812680e4f9abfb3b1fb5a6a3cb3 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping.html @@ -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.

        -
        +
        diff --git a/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping_Planes.png b/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping_Planes.png index b3d418a18a30c09d8efbde71c83c116b3d19f63d..de944eb4e1db4681fbe89b7a6e7b1af3004ba48e 100644 Binary files a/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping_Planes.png and b/src/Resources/Help/HelpFiles/Toolbar/Clipping/Clipping_Planes.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html b/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html index 4324e146f99a87d7d151655af40b8e70da6e78f0..a050f39a63ec59f3ef1a37f3d5af908e0d9526f4 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.html @@ -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) @@ -45,10 +45,22 @@ views-- only active when Oblique is selected in Slice 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 diff --git a/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png b/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png index 1d103fb7e2ae3f24ab5db12b97b83a233a880d26..c2ab6d40d51f25528cfc0daf8bc3bb855875bf5a 100644 Binary files a/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png and b/src/Resources/Help/HelpFiles/Toolbar/Custom_Orientation/Custom_Orientation.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html index f1bde4609cf685f232ab348312a2caad1fabe7b0..6767fc380e62ddb4dc17521e6d1a0ae9b75a9b8d 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Drawing.html @@ -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 @@
          -
          +
            diff --git a/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html index 137766bcfd81ddfa6d974c87d3e338cc30a03755..b4a8ea016aab35f1d088aa8e3aadc64d1899311f 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Mode/Border_Drawing/Border_Editing/Border_Editing.html @@ -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 --git a/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html index fc2fcf474c467b9cbb8b435f776ad0ab44659c73..b436e9806637c0d212fbdd044861b1a87ff08d59 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Creation.html @@ -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 --git a/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html index 76cd2c472f68a0f62205be93fedf8cc2b3b7f8dc..47227716afa493783c0e2bb7eab1541f04e242f1 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Editing/Foci_Editing.html @@ -12,7 +12,6 @@ the Toolbar.

                -
                • By diff --git a/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png old mode 100755 new mode 100644 index 7596c9e8b51e4ce76647c561cd857bbffa88b9c7..87a72014f573ff4c3c668705d4a9ae80b9478270 Binary files a/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png and b/src/Resources/Help/HelpFiles/Toolbar/Mode/Foci_Creation/Foci_Tools.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html index 275b175ecfa9bd53f0705fd363c8daa24d03e889..4ee0164e9bfb6ede190a6295bda17d3b4a513f9a 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Mode/Mode.html @@ -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 --git a/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.html b/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.html new file mode 100644 index 0000000000000000000000000000000000000000..2b3abe022df918506e5aaf97403014292a50bc8c --- /dev/null +++ b/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.html @@ -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 diff --git a/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.png b/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.png new file mode 100644 index 0000000000000000000000000000000000000000..401777a16b1b066790444d23705028a70962f91c Binary files /dev/null and b/src/Resources/Help/HelpFiles/Toolbar/Mode/Voxel Editing/VoxelEditing.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html b/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html index 61b9e84ada99cea4848802061cc83bbeae89b868..78ca44886aea81378a85b7790c72cca3c4221714 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.html @@ -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
                  diff --git a/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png b/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png index 1e9912540e2c38d5ea24789311cce8f4813e9a0f..d3df2b80a37fe70f91cef2be948ec9b879a54d61 100644 Binary files a/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png and b/src/Resources/Help/HelpFiles/Toolbar/Tab_Functions/Tab_Functions.png differ diff --git a/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html b/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html index 7ffb9ae0fab9ab39384a0d8146bad86ac21f20ca..4b634b581b2d2573fbaa9728bd45352b92c64c0d 100644 --- a/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html +++ b/src/Resources/Help/HelpFiles/Toolbar/Toolbar.html @@ -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. diff --git a/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Macro_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Macro_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e81a0458d8582cc042cafb87ac39286bee9dc286 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Macro_icon.png differ diff --git a/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Movie_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Movie_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..11cb860463629d32336fd26d0adf51edc51fcd71 Binary files /dev/null and b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Movie_icon.png differ diff --git a/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html index 837eafb50728107793c4a5c4e451223fad0eaca2..6a26a32c7045ae0c3000337658dc3d01fa8cabd5 100644 --- a/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html +++ b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html @@ -54,6 +54,12 @@ align="middle" width="26">    Identify Brainordinate button

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

                  diff --git a/src/Resources/Help/OLD_help_resources.qrc b/src/Resources/Help/OLD_help_resources.qrc new file mode 100644 index 0000000000000000000000000000000000000000..a3acc48a21d1939b572c1a6927c5919c97c1ea7d --- /dev/null +++ b/src/Resources/Help/OLD_help_resources.qrc @@ -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 --git a/src/Resources/Help/help_resources.qrc b/src/Resources/Help/help_resources.qrc index 10eebe98a0417863e3f702a77868628141f0772e..817f27aa977ea407b7508bda36d2dd11f5675679 100644 --- a/src/Resources/Help/help_resources.qrc +++ b/src/Resources/Help/help_resources.qrc @@ -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 --git a/src/Resources/Help/update_resources.sh b/src/Resources/Help/update_resources.sh index 4bb80c07f7a9b7481d2865c0cf0d9f7bca217e79..31fe6a69048670591f6883d7c9087748b93943fb 100755 --- a/src/Resources/Help/update_resources.sh +++ b/src/Resources/Help/update_resources.sh @@ -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 --git a/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx b/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx index 089d948ac7ba437e79c8bdbc708f3f0c47102e28..294f55b3fedc43eaea84b31333c8a444ffd52108 100644 --- a/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx +++ b/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx @@ -69,6 +69,11 @@ m_wasRestoredFromSceneFlag(false) 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 --git a/src/Scenes/CMakeLists.txt b/src/Scenes/CMakeLists.txt index abc7defabeb0d923a867f7257fce7d17a0414d51..5f89476cefc781fdb3fc23e7366fed1c468edcb8 100644 --- a/src/Scenes/CMakeLists.txt +++ b/src/Scenes/CMakeLists.txt @@ -64,6 +64,7 @@ ScenePathName.h ScenePathNameArray.h ScenePrimitive.h ScenePrimitiveArray.h +SceneRestoreWarningCodesEnum.h SceneSaxReader.h SceneString.h SceneStringArray.h @@ -77,6 +78,7 @@ SceneXmlStreamBase.h SceneXmlStreamReader.h SceneXmlStreamWriter.h SceneableInterface.h +TileTabsBrowserTabGeometrySceneHelper.h BackgroundAndForegroundColorsSceneHelper.cxx DisplayGroupAndTabItemHelper.cxx @@ -108,6 +110,7 @@ ScenePathName.cxx ScenePathNameArray.cxx ScenePrimitive.cxx ScenePrimitiveArray.cxx +SceneRestoreWarningCodesEnum.cxx SceneSaxReader.cxx SceneString.cxx SceneStringArray.cxx @@ -118,6 +121,7 @@ SceneWriterXml.cxx SceneXmlStreamBase.cxx SceneXmlStreamReader.cxx SceneXmlStreamWriter.cxx +TileTabsBrowserTabGeometrySceneHelper.cxx ) TARGET_LINK_LIBRARIES(Scenes ${CARET_QT5_LINK}) diff --git a/src/Scenes/Scene.cxx b/src/Scenes/Scene.cxx index 58e1a76e7f251f15ab736078c07b0cf442a2733c..bc248ce2fe163c955dd45c9a44046e06493f2dfd 100644 --- a/src/Scenes/Scene.cxx +++ b/src/Scenes/Scene.cxx @@ -239,11 +239,7 @@ Scene::Scene(const SceneTypeEnum::Enum sceneType) 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 @@ Scene::Scene(const Scene& rhs) 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)); @@ -274,6 +276,19 @@ Scene::~Scene() delete m_sceneInfo; } +/** + * 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. */ diff --git a/src/Scenes/Scene.h b/src/Scenes/Scene.h index 9f3cb430c19b599378d867db86e8dfc199b93485..267d750f6181eaff9a57543cd15764f4c9d9c976 100644 --- a/src/Scenes/Scene.h +++ b/src/Scenes/Scene.h @@ -103,6 +103,8 @@ namespace caret { private: + void initializeMacroGroup(); + /** Attributes of the scene*/ SceneAttributes* m_sceneAttributes; diff --git a/src/Scenes/SceneAttributes.cxx b/src/Scenes/SceneAttributes.cxx index 3199c7f65cf4d3cb411ed9217a0ea7f81fe01dfb..1bcb5a46f9baaa765ec7b2f7084f41149408b23c 100644 --- a/src/Scenes/SceneAttributes.cxx +++ b/src/Scenes/SceneAttributes.cxx @@ -23,6 +23,8 @@ #include "SceneAttributes.h" #undef __SCENE_ATTRIBUTES_DECLARE__ +#include "CaretAssert.h" + using namespace caret; @@ -370,4 +372,37 @@ SceneAttributes::setUseSceneForegroundAndBackgroundColors(const bool status) 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 --git a/src/Scenes/SceneAttributes.h b/src/Scenes/SceneAttributes.h index 473954ed5ef02bc9feb5f16acbfc961d39779173..5810f833c5baeff2851eba3345753bbe9e5dd1f2 100644 --- a/src/Scenes/SceneAttributes.h +++ b/src/Scenes/SceneAttributes.h @@ -21,8 +21,10 @@ */ /*LICENSE_END*/ +#include #include "CaretObject.h" +#include "SceneRestoreWarningCodesEnum.h" #include "SceneTypeEnum.h" namespace caret { @@ -94,6 +96,10 @@ namespace caret { void setUseSceneForegroundAndBackgroundColors(const bool status); + void setSceneRestoreWarningCode(const SceneRestoreWarningCodesEnum::Enum warningCode) const; + + AString getSceneLoadWarningMessage() const; + private: SceneAttributes& operator=(const SceneAttributes&); @@ -127,6 +133,8 @@ namespace caret { mutable AString m_errorMessage; + mutable std::set m_sceneWarningCodes; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Scenes/SceneRestoreWarningCodesEnum.cxx b/src/Scenes/SceneRestoreWarningCodesEnum.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b289156d4ae9b67023bc145ff209191ad8381e69 --- /dev/null +++ b/src/Scenes/SceneRestoreWarningCodesEnum.cxx @@ -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 --git a/src/Scenes/SceneRestoreWarningCodesEnum.h b/src/Scenes/SceneRestoreWarningCodesEnum.h new file mode 100644 index 0000000000000000000000000000000000000000..30bc955cb14d9ac94f2f254b3b6730d8515e8970 --- /dev/null +++ b/src/Scenes/SceneRestoreWarningCodesEnum.h @@ -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 --git a/src/Scenes/SceneXmlStreamReader.cxx b/src/Scenes/SceneXmlStreamReader.cxx index 33e3bcb938dcb86144d96d61aa4547afffd2b26a..754360919331ce8fc0fa1e9279c608f9df5c493f 100644 --- a/src/Scenes/SceneXmlStreamReader.cxx +++ b/src/Scenes/SceneXmlStreamReader.cxx @@ -102,6 +102,7 @@ SceneXmlStreamReader::readScene(QXmlStreamReader& xmlReader, if (scene == NULL) { return; } + m_scene = scene; m_filename = sceneFileName; @@ -318,6 +319,10 @@ SceneXmlStreamReader::readSceneObjectSingle(QXmlStreamReader& xmlReader) pathName->setValueToAbsolutePath(m_filename, xmlReader.readElementText()); sceneObject = pathName; + + if (DataFile::isFileOnNetwork(pathName->stringValue())) { + m_scene->setHasFilesWithRemotePaths(true); + } } break; case SceneObjectDataTypeEnum::SCENE_STRING: diff --git a/src/Scenes/SceneXmlStreamReader.h b/src/Scenes/SceneXmlStreamReader.h index 78fb4ac1753086cfda0ffff61569fc77b7db0990..021183fa894d8cc9f1dcec92054849d0846f9780 100644 --- a/src/Scenes/SceneXmlStreamReader.h +++ b/src/Scenes/SceneXmlStreamReader.h @@ -67,6 +67,8 @@ namespace caret { AString m_filename; + Scene* m_scene = NULL; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Scenes/SceneXmlStreamWriter.cxx b/src/Scenes/SceneXmlStreamWriter.cxx index ef5d2b77e608350364aff1ba310ddc4adb794be6..8743700e5c5b22faa06906575b454579fb352d8e 100644 --- a/src/Scenes/SceneXmlStreamWriter.cxx +++ b/src/Scenes/SceneXmlStreamWriter.cxx @@ -271,7 +271,7 @@ SceneXmlStreamWriter::writeMapObject(const SceneObjectMapIntegerKey* objectMap) 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 --git a/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.cxx b/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.cxx new file mode 100644 index 0000000000000000000000000000000000000000..9610688b12a2510b317a5d369575dd8e3e38505b --- /dev/null +++ b/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.cxx @@ -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 --git a/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.h b/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.h new file mode 100644 index 0000000000000000000000000000000000000000..8ba3c5ebd54e1a0e47923b726591a2f9d615ed68 --- /dev/null +++ b/src/Scenes/TileTabsBrowserTabGeometrySceneHelper.h @@ -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__