diff --git a/CMakeLists.txt b/CMakeLists.txt
index 13bf0b3e..d73ed0ef 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -99,8 +99,8 @@ SET(SNAP_VERSION_PATCH 2)
SET(SNAP_VERSION_QUALIFIER "-beta")
# These fields should also be modified each time
-SET(SNAP_VERSION_RELEASE_DATE "20240926")
-SET(SNAP_VERSION_RELEASE_DATE_FORMATTED "September 26, 2024")
+SET(SNAP_VERSION_RELEASE_DATE "20241111")
+SET(SNAP_VERSION_RELEASE_DATE_FORMATTED "November 11, 2024")
# This field should only change when the format of the settings files changes
# in a non backwards-compatible way
diff --git a/Common/ITKBinaryWeightedAverage/itkBWAfilter.hxx b/Common/ITKBinaryWeightedAverage/itkBWAfilter.hxx
index efa51fab..8b849ef5 100644
--- a/Common/ITKBinaryWeightedAverage/itkBWAfilter.hxx
+++ b/Common/ITKBinaryWeightedAverage/itkBWAfilter.hxx
@@ -179,7 +179,7 @@ void BinaryWeightedAveragingFilter< TLabelImage, TMainImage >
throw itk::ExceptionObject(
"Error: consecutive segmentations must be intersecting for "
"Binary Weighted Averaging. Manually specifying the axis "
- "of interploation may resolve this error.");
+ "of interpolation may resolve this error.");
}
//Compute the Signed Distance Map of the intersecting portion
diff --git a/GUI/Qt/Components/PaintbrushToolPanel.ui b/GUI/Qt/Components/PaintbrushToolPanel.ui
index d6701623..4b0a0bd5 100644
--- a/GUI/Qt/Components/PaintbrushToolPanel.ui
+++ b/GUI/Qt/Components/PaintbrushToolPanel.ui
@@ -206,7 +206,7 @@ font-size:11px;
- <html><head/><body><p><html><head/><body><p><span style=" font-weight:600;">Brush Size (+, - keys)</span></p><p>Adjust the radius of the paintbrush.</p></body></html></p></body></html>
+ <html><head/><body><p><span style=" font-weight:600;">Brush Size (+, - keys)</span></p><p>Adjust the radius of the paintbrush.</p></body></html>
diff --git a/GUI/Qt/Windows/MainImageWindow.cxx b/GUI/Qt/Windows/MainImageWindow.cxx
index 83eb1df7..3acebe9a 100644
--- a/GUI/Qt/Windows/MainImageWindow.cxx
+++ b/GUI/Qt/Windows/MainImageWindow.cxx
@@ -1260,43 +1260,52 @@ void MainImageWindow::dragEnterEvent(QDragEnterEvent *event)
void MainImageWindow::LoadDroppedFile(QString file)
{
- std::string filename = to_utf8(file);
- // Check if the dropped file is a project
- if(m_Model->GetDriver()->IsProjectFile(filename.c_str()))
- {
- // For the time being, the feature of opening the workspace in a new
- // window is not implemented. Instead, we just prompt the user for
- // unsaved changes.
- if(!SaveModifiedLayersDialog::PromptForUnsavedChanges(m_Model))
- return;
-
- // Load the project
- LoadProject(file);
- }
-
- else
+ try
{
- if(m_Model->GetDriver()->IsMainImageLoaded())
+ std::string filename = to_utf8(file);
+ // Check if the dropped file is a project
+ if(m_Model->GetDriver()->IsProjectFile(filename.c_str()))
{
- // check if it's a label description file
- if (m_Model->GetDriver()->GetColorLabelTable()->ValidateFile(filename.c_str()))
- {
- m_Model->GetDriver()->LoadLabelDescriptions(filename.c_str());
+ // For the time being, the feature of opening the workspace in a new
+ // window is not implemented. Instead, we just prompt the user for
+ // unsaved changes.
+ if(!SaveModifiedLayersDialog::PromptForUnsavedChanges(m_Model))
return;
- }
-
- // If an image is already loaded, we show the dialog
- m_DropDialog->SetDroppedFilename(file);
- m_DropDialog->setModal(true);
- RaiseDialog(m_DropDialog);
+ // Load the project
+ LoadProject(file);
}
+
else
{
- // Otherwise, load the main image directly
- m_DropDialog->InitialLoad(file);
+ if(m_Model->GetDriver()->IsMainImageLoaded())
+ {
+ // check if it's a label description file
+ if (m_Model->GetDriver()->GetColorLabelTable()->ValidateFile(filename.c_str()))
+ {
+ m_Model->GetDriver()->LoadLabelDescriptions(filename.c_str());
+ return;
+ }
+
+ // If an image is already loaded, we show the dialog
+ m_DropDialog->SetDroppedFilename(file);
+ m_DropDialog->setModal(true);
+
+ RaiseDialog(m_DropDialog);
+ }
+ else
+ {
+ // Otherwise, load the main image directly
+ m_DropDialog->InitialLoad(file);
+ }
}
}
+ catch (exception &exc) // for minor exceptions, no need to crash the entire program
+ {
+ ReportNonLethalException(this, exc, "File Dropping Error",
+ QString("Failed to load file %1").arg(file));
+ }
+
}
#ifdef __APPLE__
diff --git a/GUI/Qt/main.cxx b/GUI/Qt/main.cxx
index 59604960..fc392ed8 100644
--- a/GUI/Qt/main.cxx
+++ b/GUI/Qt/main.cxx
@@ -143,10 +143,10 @@ void usage(const char *progname)
cout << "Image Options:" << endl;
cout << " -g FILE : Load the main image from FILE" << endl;
cout << " -s FILE [FILE+] : Load the segmentation image from FILE" << endl;
- cout << " : (multiple files may be provided)" << endl;
+ cout << " : (multiple space separated files may be provided)" << endl;
cout << " -l FILE : Load label descriptions from FILE" << endl;
cout << " -o FILE [FILE+] : Load additional images from FILE" << endl;
- cout << " : (multiple files may be provided)" << endl;
+ cout << " : (multiple space separated files may be provided)" << endl;
cout << " -w FILE : Load workspace from FILE" << endl;
cout << " : (-w cannot be mixed with -g,-s,-l,-o options)" << endl;
cout << "Additional Options:" << endl;
diff --git a/Logic/Mesh/GuidedMeshIO.cxx b/Logic/Mesh/GuidedMeshIO.cxx
index d365cebc..02413090 100644
--- a/Logic/Mesh/GuidedMeshIO.cxx
+++ b/Logic/Mesh/GuidedMeshIO.cxx
@@ -51,6 +51,9 @@ GuidedMeshIO::FileFormat
GuidedMeshIO::
GetFormatByExtension(std::string extension)
{
+ if (extension.empty()) // possible when reading dicom series
+ return FileFormat::FORMAT_COUNT;
+
// All format string in the map include '.' prefix
if (extension.at(0) != '.')
extension.insert(0, 1, '.');
@@ -187,9 +190,18 @@ bool
GuidedMeshIO
::IsFilePolyData(const char *filename)
{
- auto reader = vtkNew();
- reader->SetFileName(filename);
- return reader->IsFilePolyData();
+ auto fmt = GetFormatByFilename(filename);
+ if (fmt == FORMAT_COUNT)
+ return false;
+
+ AbstractMeshIODelegate *ioDelegate = AbstractMeshIODelegate::GetDelegate(fmt);
+
+ if (!ioDelegate)
+ return false;
+
+ bool ret = ioDelegate->IsFilePolyData(filename);
+ delete ioDelegate;
+ return ret;
}
GuidedMeshIO::FileFormat
diff --git a/Logic/Mesh/MeshIODelegates.cxx b/Logic/Mesh/MeshIODelegates.cxx
index 4d495845..339166e1 100644
--- a/Logic/Mesh/MeshIODelegates.cxx
+++ b/Logic/Mesh/MeshIODelegates.cxx
@@ -24,11 +24,28 @@ VTKMeshIODelegate::ReadPolyData(const char *filename)
return polyData;
}
+bool
+VTKMeshIODelegate::IsFilePolyData(const char *filename)
+{
+ // VTK file format support 5 different types of data
+ // So we need to use the reader to check whether the underlying data is polydata or not
+ vtkNew reader;
+ reader->SetFileName(filename);
+ return reader->IsFilePolyData();
+}
+
+
VTPMeshIODelegate::VTPMeshIODelegate()
{
}
+bool
+VTPMeshIODelegate::IsFilePolyData(const char *)
+{
+ return true; // VTP file format is always polydata
+}
+
vtkSmartPointer
VTPMeshIODelegate::ReadPolyData(const char *filename)
{
diff --git a/Logic/Mesh/MeshIODelegates.h b/Logic/Mesh/MeshIODelegates.h
index b7a6f380..c4625291 100644
--- a/Logic/Mesh/MeshIODelegates.h
+++ b/Logic/Mesh/MeshIODelegates.h
@@ -18,6 +18,8 @@ class AbstractMeshIODelegate
static void GetDelegate(GuidedMeshIO::FileFormat fmt, AbstractMeshIODelegate *delegate);
+ virtual bool IsFilePolyData(const char *filename) = 0;
+
};
@@ -28,6 +30,8 @@ class VTKMeshIODelegate : public AbstractMeshIODelegate
~VTKMeshIODelegate() = default;
vtkSmartPointer ReadPolyData(const char *filename) override;
+
+ bool IsFilePolyData(const char *filename) override;
};
class VTPMeshIODelegate : public AbstractMeshIODelegate
@@ -37,6 +41,8 @@ class VTPMeshIODelegate : public AbstractMeshIODelegate
virtual ~VTPMeshIODelegate() = default;
vtkSmartPointer ReadPolyData(const char *filename) override;
+
+ bool IsFilePolyData(const char *filename) override;
};
#endif // MESHIODELEGATES_H