Commit e53cc01f authored by Emmanuel Promayon's avatar Emmanuel Promayon
Browse files

FIXED first successful compilation of sdk/modeling/imaging/tutorials

parent 5a737dbe
......@@ -101,7 +101,7 @@ Action::ApplyStatus RegionGrowing::apply() {
// this call works only with a GUI (i.e. if theWidget exists)
if ((input == NULL) || (rgWidget == NULL)) {
CAMITK_INFO("RegionGrowing", "apply()", "This action cannot be called without a GUI (input data are required to be set manually.")
CAMITK_WARNING("RegionGrowing::apply(): this action cannot be called without a GUI (input data are required to be set manually.")
return ABORTED;
}
......
......@@ -66,7 +66,7 @@ DicomComponent::DicomComponent(DicomSerie* dicomSerie) throw(AbortException) : I
ippSorter.SetComputeZSpacing(true);
ippSorter.SetZSpacingTolerance(0.001);
if (!ippSorter.Sort(stdFileNames)) {
CAMITK_ERROR("DicomSerieComponent", "DicomSerieComponent", "IPPSorter sorting failed. Try to adjust Z spacing tolerance.")
CAMITK_ERROR("DicomComponent::DicomSerieComponent(..): IPPSorter sorting failed. Try to adjust Z spacing tolerance.")
files = stdFileNames;
zSpacing = DicomParser::getZSpacing(files);
}
......@@ -145,10 +145,9 @@ DicomComponent::DicomComponent(DicomSerie* dicomSerie) throw(AbortException) : I
updateLUT();
}
else {
CAMITK_WARNING("DicomComponent", "Constructor", "Image's lut is null.");
CAMITK_WARNING("DicomComponent::DicomComponent(..): image LUT is null.");
}
}
// --------------- destructor -------------------
......@@ -184,14 +183,14 @@ camitk::ImageOrientationHelper::PossibleImageOrientations DicomComponent::readDi
gdcm::Tag iopTag = gdcm::Tag(0x0020, 0x0037);
scanner.AddTag(iopTag);
if (!scanner.Scan(fileNames)) { // Definitively, scan should never failed, since DicomParser::parseDirectory() has already filter files.
CAMITK_ERROR("DicomComponent", "readDirectCosinesAngle", "Scan failed looking for tag (0x0020, 0x0037) Image Orientation Patient");
CAMITK_ERROR("DicomComponent::readDirectCosinesAngle(..): scan failed looking for tag (0x0020, 0x0037) Image Orientation Patient");
}
// Check value tag exists
gdcm::Scanner::TagToValue const& ttv = scanner.GetMapping(fileNames[0].c_str());
gdcm::Scanner::TagToValue::const_iterator it = ttv.find(iopTag);
if (!(it != ttv.end())) {
CAMITK_WARNING("DicomComponent", "readDirectCosinesAngle", "No tag (0x0020, 0x0037) Image Orientation Patient found on image " + fileNames[0]);
CAMITK_WARNING("DicomComponent::readDirectCosinesAngle(..): no tag (0x0020, 0x0037) Image Orientation Patient found on image " + QString::fromStdString(fileNames[0]));
return ImageOrientationHelper::UNKNOWN;
}
......@@ -303,7 +302,7 @@ camitk::ImageOrientationHelper::PossibleImageOrientations DicomComponent::readDi
}
// should never return UNKNOW
CAMITK_WARNING("DicomSerieComponent", "readDirectCosinesAngle", "No orientation found for this image (direct cosines).")
CAMITK_WARNING("DicomComponent::readDirectCosinesAngle(..): no orientation found for this image (direct cosines).")
return ImageOrientationHelper::UNKNOWN;
}
......
......@@ -52,7 +52,7 @@ QList<DicomSerie*> DicomParser::parseDirectory(const QString& directory) {
dicomImageScanner.AddTag(tagPixelData);
dicomImageScanner.Scan(fileNames);
CAMITK_DEBUG("DicomParser", "parseDirectory", "Parsing files for DICOM image files");
CAMITK_STATIC_TRACE("DicomParser::parseDirectory(..): parsing files for DICOM image files");
foreach(std::string file, dicomImageScanner.GetFilenames()) {
// 1st check the file is a valid DICOM file
if (dicomImageScanner.IsKey(file.c_str())) {
......@@ -60,15 +60,15 @@ QList<DicomSerie*> DicomParser::parseDirectory(const QString& directory) {
gdcm::Scanner::TagToValue const& ttv = dicomImageScanner.GetMapping(file.c_str());
gdcm::Scanner::TagToValue::const_iterator it = ttv.find( tagPixelData );
if ( it != ttv.end() ) {
CAMITK_DEBUG("DicomParser", "parseDirectory", file + " is a DICOM image file");
CAMITK_STATIC_TRACE("DicomParser::parseDirectory(..):" + QString::fromStdString(file) + " is a DICOM image file");
imageFileNames.push_back(file);
}
else {
CAMITK_DEBUG("DicomParser", "parseDirectory", file + " is a DICOM file WITHOUT PixelData information");
CAMITK_STATIC_TRACE("DicomParser::parseDirectory(..)" + QString::fromStdString(file) + " is a DICOM file WITHOUT PixelData information");
}
}
else {
CAMITK_DEBUG("DicomParser", "parseDirectory", file + " is not a valid DICOM file");
CAMITK_STATIC_TRACE("DicomParser::parseDirectory(..)" + QString::fromStdString(file) + " is not a valid DICOM file");
}
}
......@@ -199,7 +199,7 @@ QString DicomParser::getStudyName(const std::vector<std::string>& serieFileNames
QList<QString> DicomParser::stdListOfStringToQt(const std::vector<std::string>& inputList) {
QList<QString> outputFileNames;
foreach(std::string stdFile, inputList) {
outputFileNames.append(QString(stdFile.c_str()));
outputFileNames.append(QString::fromStdString(stdFile));
}
return outputFileNames;
......@@ -225,7 +225,7 @@ double DicomParser::getZSpacing(const std::vector<std::string>& serieFileNames)
std::string file = serieFileNames.at(0);
const char* value = scanner.GetValue(file.c_str(), zSpacingTag);
if (!value) {
CAMITK_ERROR("DicomParser", "getZSpacing", "No Z spacing found on image: " + serieFileNames.at(0));
CAMITK_STATIC_ERROR("DicomParser::getZSpacing(..): no Z spacing found on image: " + QString::fromStdString(serieFileNames.at(0)));
return 1.0;
}
return atof(value);
......
......@@ -219,16 +219,16 @@ bool DicomItkComponentExtension::findAllDicomSeries(const QString& directoryName
dicomIO->GetValueFromTag(ComponentM_HighBit, highBitTag);
dicomIO->GetValueFromTag(ComponentM_StudyDescription, studyDescriptionTag);
info->modality = QString(modalityTag.c_str());
info->protocolName = QString(protocolNameTag.c_str());
info->seriesDescription = QString(seriesDescriptionTag.c_str());
info->rows = QString(rowsTag.c_str());
info->columns = QString(columnsTag.c_str());
info->modality = QString::fromStdString(modalityTag);
info->protocolName = QString::fromStdString(protocolNameTag);
info->seriesDescription = QString::fromStdString(seriesDescriptionTag);
info->rows = QString::fromStdString(rowsTag);
info->columns = QString::fromStdString(columnsTag);
QTextStream(&info->slices) << filenames.size();
info->numberOfTemporalPositions = QString(numberOfTemporalPositionsTag.c_str());
info->bitsAllocated = QString(bitsAllocatedTag.c_str());
info->highBit = QString(highBitTag.c_str());
info->studyDescription = QString(studyDescriptionTag.c_str());
info->numberOfTemporalPositions = QString::fromStdString(numberOfTemporalPositionsTag);
info->bitsAllocated = QString::fromStdString(bitsAllocatedTag);
info->highBit = QString::fromStdString(highBitTag);
info->studyDescription = QString::fromStdString(studyDescriptionTag);
// Keep the filenames and the info in the map for this series
theSeries[info] = filenames;
......
......@@ -282,7 +282,7 @@ template<class ImageType> bool ItkImageComponentExtension::saveImage(ImageCompon
writer->Update();
}
catch (const itk::ExceptionObject& err ) {
CAMITK_ERROR("ItkImageComponentExtension","saveImage", "Saving Error: problem occurs while saving file:" + filename.toStdString() + ": ITK Exception: " + err.what());
CAMITK_STATIC_ERROR("ItkImageComponentExtension::saveImage(): saving Error: problem occurs while saving file:" + filename + ": ITK Exception: " + err.what());
return false;
}
......
......@@ -78,7 +78,7 @@ Action::ApplyStatus GenerateModel::apply() {
// ask the user
originalFilename = QFileDialog::getSaveFileName( NULL, "Generate Model", "", "ModelingML (*.mml)" );
if ( originalFilename.isNull() ) {
CAMITK_INFO("GenerateModel", "apply()", "A filename is required.")
CAMITK_WARNING("GenerateModel::apply(): filename required.")
return ABORTED;
}
}
......
......@@ -41,7 +41,7 @@ QWidget* SimulationDialog::getWidget() {
myWidget->show();
}
else {
CAMITK_ERROR("SimulationDialog", "getWidget", "MMLComponent not valid: no simulation tab can be shown");
CAMITK_ERROR("SimulationDialog::getWidget: MMLComponent not valid: no simulation tab can be shown");
}
......
......@@ -260,7 +260,7 @@ void CreateSC::updateCorners(PMLComponent* pmlComponent) {
// --------------- apply -------------------
Action::ApplyStatus CreateSC::apply() {
CAMITK_INFO("CreateSC", "apply", "Create Structural Component in " << getTargets().last()->getName().toStdString());
CAMITK_TRACE("CreateSC::apply(): create Structural Component in " + getTargets().last()->getName());
// set waiting cursor and status bar
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
......
......@@ -63,7 +63,7 @@ MMLComponent::MMLComponent(const QString& fileName) throw(AbortException) : Comp
// check if the mml already exist
if (QFileInfo(file).exists()) {
CAMITK_WARNING("MMLComponent", "constructor", "Creating MMLComponent from " + fileName.toStdString() + ": mml file already exists in " + file.toStdString() + " (overwriting it)");
CAMITK_WARNING("MMLComponent::MMLComponent(..): creating MMLComponent from " + fileName + ": mml file already exists in " + file + " (overwriting it)");
}
// create an empty mmlIn file with .scn (in the same directory as the .scn)
......@@ -113,7 +113,7 @@ MMLComponent::MMLComponent(const QString& fileName) throw(AbortException) : Comp
}
}
catch (const xml_schema::exception& e) {
CAMITK_ERROR("MMLComponent", "constructor", "xml_schema exception, attempt to read from " + myFileName.toStdString() << endl << e);
CAMITK_ERROR("MMLComponent::MMLComponent(..): xml_schema exception, attempt to read from " + myFileName + "\n" + QString::fromStdString(e.what()));
throw AbortException("Cannot create MMLComponent from " + myFileName.toStdString() + ": xml_schema exception.\nReason: " + e.what());
}
}
......
......@@ -72,7 +72,8 @@ Action::ApplyStatus CloseAllAction::apply() {
return SUCCESS;
}
else {
CAMITK_INFO("CloseAllAction", "apply()", "Action canceled by user")
// just an information as the user volontarily canceled the action
CAMITK_INFO("CloseAllAction::apply(): action canceled by user")
return ABORTED;
}
}
......
......@@ -91,13 +91,14 @@ Action::ApplyStatus OpenAction::apply() {
}
else {
Application::showStatusBarMessage(tr ("Error opening files: ") + (*fileIterator));
CAMITK_INFO("OpenAction", "apply()", "Some files can not be opened. Action failed.")
CAMITK_ERROR("OpenAction::apply(): some files can not be opened. Action failed.")
return ERROR;
}
}
else {
Application::showStatusBarMessage(tr ("Open aborted."));
CAMITK_INFO("OpenAction", "apply()", "File is required. Action aborted.")
// just info as the user voluntarily did not fill the filename
CAMITK_INFO("OpenAction::apply(): file is required. Action aborted.")
return ABORTED;
}
}
......
......@@ -119,7 +119,7 @@ Action::ApplyStatus OpenFile::apply() {
return SUCCESS;
}
else {
CAMITK_INFO("OpenFile", "apply()", "A filename is required. Action aborted.")
CAMITK_WARNING("OpenFile::apply(): filename is required. Action aborted.")
return ABORTED;
}
}
......
......@@ -198,7 +198,8 @@ Action::ApplyStatus SaveAsAction::apply() {
}
else {
Application::showStatusBarMessage( tr ( "Saving aborted" ), 2000 );
CAMITK_INFO("SaveAsAction", "apply()", "A filename is required. Action aborted.")
// just an information as the user volontarily canceled the action
CAMITK_INFO("SaveAsAction::apply(): filename is required. Action aborted.")
return ABORTED;
}
......
......@@ -45,20 +45,20 @@ using namespace camitk;
// --------------- constructor -------------------
BoxVOI::BoxVOI ( ActionExtension* extension ) : Action ( extension ) {
BoxVOI::BoxVOI(ActionExtension* extension) : Action(extension) {
// Setting name, description and input component
setName ( "Box Volume Of Interest" );
setDescription ( tr("<p>This filter helps you to crop a volume to keep only an interesting subsample. To use it, select respectively the min and the max of each axe (i,j,k), i.e 6 points. It is possible to use only 2 points (the origin of parallelepiped and its opposite diagonal point)." ));
setComponent ( "ImageComponent" );
setName("Box Volume Of Interest");
setDescription(tr("<p>This filter helps you to crop a volume to keep only an interesting subsample. To use it, select respectively the min and the max of each axe (i,j,k), i.e 6 points. It is possible to use only 2 points (the origin of parallelepiped and its opposite diagonal point)."));
setComponent("ImageComponent");
setEmbedded ( true );
setEmbedded(true);
// Setting classification family and tags
this->setFamily ( "Image Processing" );
this->addTag ( tr("Crop Volume") );
this->addTag ( tr("Box" ));
this->addTag ( tr("Volume Of Interest" ));
this->addTag ( tr("Seed Point") );
this->setFamily("Image Processing");
this->addTag(tr("Crop Volume"));
this->addTag(tr("Box"));
this->addTag(tr("Volume Of Interest"));
this->addTag(tr("Seed Point"));
// Setting the widget containing the parameters
theWidget = NULL;
......@@ -73,16 +73,16 @@ BoxVOI::~BoxVOI() {
// --------------- getWidget --------------
QWidget* BoxVOI::getWidget() {
BoxVOIWidget* rgWidget = dynamic_cast<BoxVOIWidget*> ( theWidget );
BoxVOIWidget* rgWidget = dynamic_cast<BoxVOIWidget*>(theWidget);
//-- create the widget if needed
if ( !rgWidget ) {
theWidget = new BoxVOIWidget ( this );
rgWidget = dynamic_cast<BoxVOIWidget*> ( theWidget );
if (!rgWidget) {
theWidget = new BoxVOIWidget(this);
rgWidget = dynamic_cast<BoxVOIWidget*>(theWidget);
}
//-- update the widget with a PickedPixelMap param
rgWidget->updateComponent ( dynamic_cast<ImageComponent*> ( getTargets().last() ) );
rgWidget->updateComponent(dynamic_cast<ImageComponent*>(getTargets().last()));
return theWidget;
......@@ -91,25 +91,25 @@ QWidget* BoxVOI::getWidget() {
// --------------- apply -------------------
Action::ApplyStatus BoxVOI::apply() {
// check the widget
BoxVOIWidget* rgWidget = dynamic_cast<BoxVOIWidget*> ( theWidget );
BoxVOIWidget* rgWidget = dynamic_cast<BoxVOIWidget*>(theWidget);
// Get the image
ImageComponent* input = dynamic_cast<ImageComponent*> ( getTargets().last() );
ImageComponent* input = dynamic_cast<ImageComponent*>(getTargets().last());
// this call works only with a GUI (i.e. if theWidget exists)
if ( ( input == NULL ) || ( rgWidget == NULL ) ) {
CAMITK_INFO("BoxVOI", "apply()", "This action cannot be called without a GUI (input data are required to be set manually).")
if ((input == NULL) || (rgWidget == NULL)) {
CAMITK_WARNING("BoxVOI::apply(): this action cannot be called without a GUI (input data are required to be set manually). Action Aborted.")
return ABORTED;
}
// Get the parameters
seedPoints = rgWidget->getSeedPoints ( input );
seedPoints = rgWidget->getSeedPoints(input);
// check if number of seeds is coherent
if ( seedPoints->count() == 2 || seedPoints->count() == 6 ) {
process ( input );
if (seedPoints->count() == 2 || seedPoints->count() == 6) {
process(input);
}
else {
CAMITK_INFO("BoxVOI", "apply()", "It is required to select 2 or 6 seeds to apply this action. Action Aborted.")
CAMITK_WARNING("BoxVOI::apply(): it is required to select 2 or 6 seeds to apply this action. Action Aborted.")
return ABORTED;
}
return SUCCESS;
......@@ -118,17 +118,17 @@ Action::ApplyStatus BoxVOI::apply() {
// --------------- apply -------------------
Action::ApplyStatus BoxVOI::apply(QList<QVector3D>* seedPoints) {
// Get the image
ImageComponent* input = dynamic_cast<ImageComponent*> ( getTargets().last() );
ImageComponent* input = dynamic_cast<ImageComponent*>(getTargets().last());
// Get the parameters
this->seedPoints = seedPoints;
// check if number of seeds is coherent
if ( seedPoints->count() == 2 || seedPoints->count() == 6 ) {
process ( input );
if (seedPoints->count() == 2 || seedPoints->count() == 6) {
process(input);
}
else {
CAMITK_INFO("BoxVOI", "apply()", "It is required to select 2 or 6 seeds to apply this action. Action Aborted.")
CAMITK_WARNING("BoxVOI::apply(): it is required to select 2 or 6 seeds to apply this action. Action Aborted.")
return ABORTED;
}
......@@ -136,35 +136,35 @@ Action::ApplyStatus BoxVOI::apply(QList<QVector3D>* seedPoints) {
}
// --------------- process -------------------
void BoxVOI::process ( ImageComponent* comp ) {
void BoxVOI::process(ImageComponent* comp) {
vtkSmartPointer<vtkImageData> inputImage = comp->getImageData();
vtkSmartPointer<vtkImageData> result = vtkSmartPointer<vtkImageData>::New();
vtkSmartPointer<vtkExtractVOI> extractVOI = vtkSmartPointer<vtkExtractVOI>::New();
//construction of new img
extractVOI->SetInputData ( inputImage );
extractVOI->SetInputData(inputImage);
//get seeds values
int x1, x2, y1, y2, z1, z2 = 0;
if ( seedPoints->count() == 6 ) {
x1 = ( seedPoints->at ( 0 ).x() );
x2 = ( seedPoints->at ( 1 ).x() );
y1 = ( seedPoints->at ( 2 ).y() );
y2 = ( seedPoints->at ( 3 ).y() );
z1 = ( seedPoints->at ( 4 ).z() );
z2 = ( seedPoints->at ( 5 ).z() );
if (seedPoints->count() == 6) {
x1 = (seedPoints->at(0).x());
x2 = (seedPoints->at(1).x());
y1 = (seedPoints->at(2).y());
y2 = (seedPoints->at(3).y());
z1 = (seedPoints->at(4).z());
z2 = (seedPoints->at(5).z());
}
else {
x1 = ( seedPoints->at ( 0 ).x() );
x2 = ( seedPoints->at ( 1 ).x() );
y1 = ( seedPoints->at ( 0 ).y() );
y2 = ( seedPoints->at ( 1 ).y() );
z1 = ( seedPoints->at ( 0 ).z() );
z2 = ( seedPoints->at ( 1 ).z() );
x1 = (seedPoints->at(0).x());
x2 = (seedPoints->at(1).x());
y1 = (seedPoints->at(0).y());
y2 = (seedPoints->at(1).y());
z1 = (seedPoints->at(0).z());
z2 = (seedPoints->at(1).z());
}
// constructs volume of interest
extractVOI->SetVOI ( std::min ( x1, x2 ), std::max ( x1, x2 ), std::min ( y1, y2 ), std::max ( y1, y2 ), std::min ( z1, z2 ), std::max ( z1, z2 ) );
extractVOI->SetVOI(std::min(x1, x2), std::max(x1, x2), std::min(y1, y2), std::max(y1, y2), std::min(z1, z2), std::max(z1, z2));
extractVOI->Update();
// --------------------- Create and return a copy (the filters will be deleted)--
......@@ -174,9 +174,9 @@ void BoxVOI::process ( ImageComponent* comp ) {
double* inputO = inputImage->GetOrigin();
// value - origin in case of origin of an image is not (0,0,0)
double* spacing = comp->getImageData()->GetSpacing();
resultImage->SetOrigin (std::min ( x1, x2 )*spacing[0] + inputO[0],
std::min ( y1, y2 )*spacing[1] + inputO[1],
std::min ( z1, z2 )*spacing[2] + inputO[2]);
resultImage->SetOrigin(std::min(x1, x2)*spacing[0] + inputO[0],
std::min(y1, y2)*spacing[1] + inputO[1],
std::min(z1, z2)*spacing[2] + inputO[2]);
// adapt extent to the new volume
int* dims = resultImage->GetDimensions();
......@@ -187,18 +187,18 @@ void BoxVOI::process ( ImageComponent* comp ) {
0,
dims[2] - 1
};
resultImage->SetExtent ( extent );
resultImage->SetExtent(extent);
//Update values computed
result->SetExtent ( extent );
result->DeepCopy ( resultImage );
result->SetExtent(extent);
result->DeepCopy(resultImage);
// creation of the image component
QString newName;
QTextStream ( &newName ) << comp->getName() << "_cropped";
QTextStream(&newName) << comp->getName() << "_cropped";
// Keep its parent frame.
ImageComponent* outputComp = new ImageComponent ( result, newName );
ImageComponent* outputComp = new ImageComponent(result, newName);
// consider frame policy on new image created
Action::applyTargetPosition(comp, outputComp);
......
......@@ -29,9 +29,7 @@
// -- Core stuff
#include "Application.h"
// Deprecated. Use Application.h to handle logger instead.
//#include "Log.h"
#include "Log.h"
// -- QT stuff
#include <QGraphicsTextItem>
......
......@@ -268,8 +268,8 @@ Action::ApplyStatus ReorientImage::apply() {
// --------------- process -------------------
Action::ApplyStatus ReorientImage::process( ImageComponent* image) {
if (image == NULL) {
CAMITK_INFO("ReorientImage", "apply()", "ImageComponent is null. Action Aborted.")
if (image == nullptr) {
CAMITK_WARNING("ReorientImage::apply(): ImageComponent is null. Action Aborted.")
return ABORTED;
}
......
......@@ -68,7 +68,8 @@ Action::ApplyStatus ChangeColor::apply() {
return SUCCESS;
}
CAMITK_INFO("ChangeColor", "apply()", "Invalid color. Action Aborted.")
return ABORTED;
else {
CAMITK_WARNING("ChangeColor::apply(): invalid color. Action Aborted.")
return ABORTED;
}
}
......@@ -65,8 +65,8 @@ Action::ApplyStatus ComputeNormals::apply() {
polyNormals->Update();
if (polyNormals->GetOutput()->GetPointData()->GetScalars("Normals") == nullptr) {
CAMITK_INFO("ComputeNormals", "apply", "No normals can be computed for: " +
getTargets().first()->getName().toStdString() + " (probably no 3D surface in this mesh)")
CAMITK_WARNING("ComputeNormals::apply(): no normals can be computed for: "
+ getTargets().first()->getName() + " (probably no 3D surface in this mesh)")
return ABORTED;
}
......
......@@ -64,19 +64,19 @@ Action::ApplyStatus ExtractSelection::apply() {
return ABORTED;
}
CAMITK_INFO("ExtractSelection::apply(): Extract selection of " + getTargets().last()->getName());
CAMITK_TRACE("ExtractSelection::apply(): Extract selection of " + getTargets().last()->getName());
vtkSmartPointer<vtkExtractSelection> extractSelection = vtkSmartPointer<vtkExtractSelection>::New();
if (targetMesh->getPointSet() == nullptr) {
CAMITK_INFO("ExtractSelection", "apply", "mesh " +
getTargets().first()->getName().toStdString() + " does not have any vtkPointSet.")
CAMITK_WARNING("ExtractSelection::apply(): mesh "
+ getTargets().first()->getName() + " does not have any vtkPointSet.")
return ABORTED;
}
if (targetMesh->getActiveSelection()->GetNumberOfNodes() == 0) {
CAMITK_INFO("ExtractSelection", "apply", "mesh " +
getTargets().first()->getName().toStdString() + " does not have anything currently selected.")
CAMITK_WARNING("ExtractSelection::apply(): mesh " +
getTargets().first()->getName() + " does not have anything currently selected.")
return ABORTED;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment