Files
leocad/common/lc_blenderpreferences.h
T
Leonardo Zide 2125868dc6 Validate localized numeric input and unify image limits
Parse dialog numbers using their validator locale so localized decimals and grouped input round trip correctly, while displaying values without digit grouping. Keep invalid input open with a validation message instead of silently saving or clamping it, and serialize Blender settings in their canonical format.

Use a 1–32768 pixel range for image dimensions across Save Image, HTML export, command-line output, POV-Ray, and Blender. Bound export step edits by the model step count, grid spacing to 10000 studs, and area-light grids to 64 samples per axis. Preserve existing show/hide step bounds and avoid a total-pixel cap.

Validation: macOS Qt debug build and numeric dialog checks across ten locales, including localized input, validation messages, image dimension boundaries, and settings round trips.
2026-10-23 12:54:55 -07:00

451 lines
19 KiB
C++

#pragma once
#ifndef LC_DISABLE_BLENDER
#define LC_UNIX_SHELL "/bin/sh"
#define LC_WINDOWS_SHELL "cmd.exe"
#define LC_BLENDER_ADDON_BLEND_FILE "leocad.blend"
#define LC_BLENDER_ADDON_IMPORT_MODULE "TN"
#define LC_BLENDER_ADDON_RENDER_FOLDER "io_scene_render_ldraw"
class lcHttpReply;
class lcBlenderPreferences;
class lcBlenderPreferencesDialog : public QDialog
{
Q_OBJECT
public:
explicit lcBlenderPreferencesDialog(int Width, int Height, double Scale, QWidget* Parent = nullptr);
virtual ~lcBlenderPreferencesDialog();
static bool GetBlenderPreferences(int& Width, int& Height, double& Scale, QWidget* Parent = nullptr);
public slots:
void ShowPathsGroup();
void ResetSettings();
void EnableButton(bool);
void accept() override;
void reject() override;
private:
QPushButton* mApplyButton;
QPushButton* mResetButton;
QPushButton* mPathsButton;
lcBlenderPreferences* mPreferences;
};
class lcBlenderPreferences : public QWidget
{
Q_OBJECT
public:
explicit lcBlenderPreferences(int Width, int Height, double Scale, QWidget* Parent = nullptr);
~lcBlenderPreferences();
static void LoadSettings();
static void SaveSettings();
static bool ExportParameterFile();
static bool SettingsModified(bool Update = true, QString const& Module = QString());
static int ShowMessage(QWidget* Parent, QString const& Header, QString const& Title, QString const& Body = QString(), QString const& Detail = QString(), const int Buttons = MBB_OK, const int Icon = QMessageBox::Warning);
void Apply(const int Response = QDialog::Accepted);
int mImageWidth;
int mImageHeight;
qreal mScale;
protected:
static void SetupControlHelp(QLabel* Label, QWidget* Control);
static int NumPaths(bool DefaultSettings = false);
static int NumSettings(bool DefaultSettings = false);
static int NumSettingsMM(bool DefaultSettings = false);
int GetBlenderAddon(const QString& BlenderDir);
bool ExtractBlenderAddon(const QString& BlenderDir);
static void LoadDefaultParameters(QByteArray& Buffer, int Which);
static bool OverwriteFile(const QString& File);
QString ReadStdErr(bool& Error) const;
void ClearGroupBox(QGroupBox* GroupBox);
void InitPathsAndSettings();
void InitPathsAndSettingsMM();
bool ExtractAddon(const QString FileName, QString &Result);
signals:
void SettingChangedSig(bool);
public slots:
void ResetSettings();
void ShowPathsGroup();
bool PromptCancel();
private slots:
void BrowseBlender(bool);
void ConfigureBlenderAddon(bool = false, bool = false, bool = false);
void UpdateBlenderAddon();
void EnableImportModule();
void SizeChanged(const QString&);
void SetDefaultColor(int);
void ColorButtonClicked(bool);
void SetModelSize(bool = false);
void ValidateColourScheme(int);
void SettingIndexChanged(int = -1);
void SettingTextChanged(const QString&);
void PathChanged();
bool PromptAccept();
void Update();
void ReadStdOut();
void ReadStdOut(const QString&, QString&);
void WriteStdOut();
void GetStandardOutput();
void ShowResult();
void StatusUpdate(bool, bool = true, const QString& = QString());
void DownloadFinished(lcHttpReply*);
private:
static void SetNumericText(QLineEdit* Edit, const QString& Value);
static double GetNumericValue(const QLineEdit* Edit, bool& Ok);
enum BlenderPathType
{
PATH_BLENDER, // 0 QLineEdit/QPushButton
PATH_BLENDFILE, // 1 QLineEdit/QPushButton
PATH_ENVIRONMENT, // 2 QLineEdit/QPushButton
PATH_LDCONFIG, // 3 QLineEdit/QPushButton
PATH_LDRAW, // 4 QLineEdit/QPushButton
PATH_LSYNTH, // 5 QLineEdit/QPushButton
PATH_STUD_LOGO, // 6 QLineEdit/QPushButton
PATH_STUDIO_LDRAW, // 7 QLineEdit/QPushButton
PATH_STUDIO_CUSTOM_PARTS, // 8 QLineEdit/QPushButton
NUM_PATHS
};
enum BlenderLabelType
{
LBL_ADD_ENVIRONMENT, // 0 QCheckBox
DEFAULT_SETTINGS, // 1
LBL_ADD_GAPS = DEFAULT_SETTINGS, // 1 QCheckBox
LBL_BEVEL_EDGES, // 2 QCheckBox
LBL_BLENDFILE_TRUSTED, // 3 QCheckBox
LBL_CROP_IMAGE, // 4 QCheckBox
LBL_CURVED_WALLS, // 5 QCheckBox
LBL_FLATTEN_HIERARCHY, // 6 QCheckBox
NUM_COMBO_ITEMS, // 7
LBL_IMPORT_CAMERAS = NUM_COMBO_ITEMS, // 7 QCheckBox
LBL_IMPORT_LIGHTS, // 8 QCheckBox
LBL_INSTANCE_STUDS, // 9 QCheckBox
LBL_KEEP_ASPECT_RATIO, // 10 QCheckBox
LBL_LINK_PARTS, // 11 QCheckBox
LBL_MINIFIG_HIERARCHY, // 12 QCheckBox
LBL_NUMBER_NODES, // 13 QCheckBox
LBL_OVERWRITE_IMAGE, // 14 QCheckBox
LBL_OVERWRITE_MATERIALS, // 15 QCheckBox
LBL_OVERWRITE_MESHES, // 16 QCheckBox
LBL_POSITION_CAMERA, // 17 QCheckBox
LBL_REMOVE_DOUBLES, // 18 QCheckBox
LBL_RENDER_WINDOW, // 19 QCheckBox
LBL_USE_ARCHIVE_LIBRARY, // 20 QChekcBox
LBL_SEARCH_ADDL_PATHS, // 21 QCheckBox
LBL_SMOOTH_SHADING, // 22 QCheckBox
LBL_TRANSPARENT_BACKGROUND, // 23 QCheckBox
LBL_UNOFFICIAL_PARTS, // 24 QCheckBox
LBL_USE_LOGO_STUDS, // 25 QCheckBox
LBL_VERBOSE, // 26 QCheckBox
LBL_BEVEL_WIDTH, // 27/0 QLineEdit
LBL_CAMERA_BORDER_PERCENT, // 28/1 QLineEdit
LBL_DEFAULT_COLOUR, // 29/2 QLineEdit
LBL_GAPS_SIZE, // 30/3 QLineEdit
LBL_IMAGE_WIDTH, // 31/4 QLineEdit
LBL_IMAGE_HEIGHT, // 32/5 QLineEdit
LBL_IMAGE_SCALE, // 33/6 QLineEdit
LBL_RENDER_PERCENTAGE, // 34/7 QLineEdit
LBL_COLOUR_SCHEME, // 35/0 QComboBox
LBL_FLEX_PARTS_SOURCE, // 36/1 QComboBox
LBL_LOGO_STUD_VERSION, // 27/2 QComboBox
LBL_LOOK, // 38/3 QComboBox
LBL_POSITION_OBJECT, // 39/4 QComboBox
LBL_RESOLUTION, // 40/5 QComboBox
LBL_RESOLVE_NORMALS, // 41/6 QComboBox
NUM_SETTINGS
};
enum BlenderControlType
{
CTL_BLENDER_PATH_EDIT, // 0
CTL_ADD_ENVIRONMENT_BOX = CTL_BLENDER_PATH_EDIT, // 0
CTL_BEVEL_WIDTH_EDIT = CTL_BLENDER_PATH_EDIT, // 0
CTL_COLOUR_SCHEME_COMBO = CTL_BLENDER_PATH_EDIT, // 0
CTL_ADD_GAPS_BOX, // 1
CTL_CAMERA_BORDER_PERCENT_EDIT = CTL_ADD_GAPS_BOX, // 1
CTL_FLEX_PARTS_SOURCE_COMBO = CTL_ADD_GAPS_BOX, // 1
CTL_BEVEL_EDGES_BOX, // 2
CTL_DEFAULT_COLOUR_EDIT = CTL_BEVEL_EDGES_BOX, // 2
CTL_LOGO_STUD_VERSION_COMBO = CTL_BEVEL_EDGES_BOX, // 2
CTL_BLENDFILE_TRUSTED_BOX, // 3
CTL_GAPS_SIZE_EDIT = CTL_BLENDFILE_TRUSTED_BOX, // 3
CTL_LOOK_COMBO = CTL_BLENDFILE_TRUSTED_BOX, // 3
CTL_CROP_IMAGE_BOX, // 4
CTL_IMAGE_WIDTH_EDIT = CTL_CROP_IMAGE_BOX, // 4
CTL_POSITION_OBJECT_COMBO = CTL_CROP_IMAGE_BOX, // 4
CTL_CURVED_WALLS_BOX, // 5
CTL_IMAGE_HEIGHT_EDIT = CTL_CURVED_WALLS_BOX, // 5
CTL_RESOLUTION_COMBO = CTL_CURVED_WALLS_BOX, // 5
CTL_FLATTEN_HIERARCHY_BOX, // 6
CTL_IMAGE_SCALE_EDIT = CTL_FLATTEN_HIERARCHY_BOX, // 6
CTL_RESOLVE_NORMALS_COMBO = CTL_FLATTEN_HIERARCHY_BOX, // 6
CTL_IMPORT_CAMERAS_BOX, // 7
CTL_RENDER_PERCENTAGE_EDIT = CTL_IMPORT_CAMERAS_BOX, // 7
CTL_IMPORT_LIGHTS_BOX, // 8
CTL_INSTANCE_STUDS_BOX, // 9
CTL_KEEP_ASPECT_RATIO_BOX, // 10
CTL_LINK_PARTS_BOX, // 11
CTL_MINIFIG_HIERARCHY_BOX, // 12
CTL_NUMBER_NODES_BOX, // 13
CTL_OVERWRITE_IMAGE_BOX, // 14
CTL_OVERWRITE_MATERIALS_BOX, // 15
CTL_OVERWRITE_MESHES_BOX, // 16
CTL_POSITION_CAMERA_BOX, // 17
CTL_REMOVE_DOUBLES_BOX, // 18
CTL_RENDER_WINDOW_BOX, // 19
CTL_USE_ARCHIVE_LIBS_BOX, // 20
CTL_SEARCH_ADDL_PATHS_BOX, // 21
CTL_SMOOTH_SHADING_BOX, // 22
CTL_TRANSPARENT_BACKGROUND_BOX, // 23
CTL_UNOFFICIAL_PARTS_BOX, // 24
CTL_USE_LOGO_STUDS_BOX, // 25
CTL_VERBOSE_BOX, // 26
};
enum BlenderLabelTypeMM
{
LBL_ADD_ENVIRONMENT_MM, // 0 QCheckBox
LBL_BEVEL_EDGES_MM, // 1 QCheckBox
LBL_BLEND_FILE_TRUSTED_MM, // 2 QCheckBox
LBL_CASE_SENSITIVE_FILESYSTEM, // 3 QCheckBox
LBL_CROP_IMAGE_MM, // 4 QCheckBox
NUM_COMBO_ITEMS_MM, // 5
LBL_DISPLAY_LOGO = NUM_COMBO_ITEMS_MM, // 5 QCheckBox
LBL_IMPORT_CAMERAS_MM, // 6 QCheckBox
LBL_IMPORT_EDGES, // 7 QCheckBox
LBL_IMPORT_LIGHTS_MM, // 8 QCheckBox
LBL_KEEP_ASPECT_RATIO_MM, // 9 QCheckBox
LBL_MAKE_GAPS, // 10 QCheckBox
LBL_META_BFC, // 11 QCheckBox
LBL_META_CLEAR, // 12 QCheckBox
LBL_META_GROUP, // 13 QCheckBox
LBL_META_PAUSE, // 14 QCheckBox
LBL_META_PRINT_WRITE, // 15 QCheckBox
LBL_META_SAVE, // 16 QCheckBox
LBL_META_STEP, // 17 QCheckBox
LBL_META_STEP_GROUPS, // 18 QCheckBox
LBL_META_TEXMAP, // 19 QCheckBox
LBL_NO_STUDS, // 20 QCheckBox
LBL_OVERWRITE_IMAGE_MM, // 21 QCheckBox
LBL_POSITION_CAMERA_MM, // 22 QCheckBox
LBL_PARENT_TO_EMPTY, // 23 QCheckBox
LBL_PREFER_STUDIO, // 24 QCheckBox
LBL_PREFER_UNOFFICIAL, // 25 QCheckBox
LBL_PROFILE, // 26 QCheckBox
LBL_RECALCULATE_NORMALS, // 27 QCheckBox
LBL_REMOVE_DOUBLES_MM, // 28 QCheckBox
LBL_RENDER_WINDOW_MM, // 29 QCheckBox
LBL_SEARCH_ADDL_PATHS_MM, // 30 QCheckBox
LBL_SETEND_FRAME, // 31 QCheckBox
LBL_SET_TIMELINE_MARKERS, // 32 QCheckBox
LBL_SHADE_SMOOTH, // 33 QCheckBox
LBL_TRANSPARENT_BACKGROUND_MM, // 34 QCheckBox
LBL_TREAT_SHORTCUT_AS_MODEL, // 35 QCheckBox
LBL_TRIANGULATE, // 36 QCheckBox
LBL_USE_ARCHIVE_LIBRARY_MM, // 37 QCheckBox
LBL_USE_FREESTYLE_EDGES, // 38 QCheckBox
LBL_VERBOSE_MM, // 39 QCheckBox
LBL_BEVEL_SEGMENTS, // 40/ 0 QLineEdit
LBL_BEVEL_WEIGHT, // 41/ 1 QLineEdit
LBL_BEVEL_WIDTH_MM, // 42/ 2 QLineEdit
LBL_CAMERA_BORDER_PERCENT_MM, // 43/ 3 QLineEdit
LBL_FRAMES_PER_STEP, // 44/ 4 QLineEdit
LBL_GAP_SCALE, // 45/ 5 QLineEdit
LBL_IMPORT_SCALE, // 46/ 6 QLineEdit
LBL_MERGE_DISTANCE, // 47/ 7 QLineEdit
LBL_RENDER_PERCENTAGE_MM, // 48/ 8 QLineEdit
LBL_RESOLUTION_WIDTH, // 49/ 9 QLineEdit
LBL_RESOLUTION_HEIGHT, // 50/10 QLineEdit
LBL_STARTING_STEP_FRAME, // 51/11 QLineEdit
LBL_CHOSEN_LOGO, // 52/ 0 QComboBox
LBL_COLOUR_SCHEME_MM, // 53/ 1 QComboBox
LBL_RESOLUTION_MM, // 54/ 2 QComboBox
LBL_SCALE_STRATEGY, // 55/ 3 QComboBox
LBL_SMOOTH_TYPE, // 56/ 4 QComboBox
NUM_SETTINGS_MM
};
enum BlenderControlTypeMM
{
CTL_BLENDER_PATH_EDIT_MM, // 0
CTL_ADD_ENVIRONMENT_BOX_MM = CTL_BLENDER_PATH_EDIT_MM, // 0
CTL_BEVEL_SEGMENTS_EDIT = CTL_BLENDER_PATH_EDIT_MM, // 0
CTL_CHOSEN_LOGO_COMBO = CTL_BLENDER_PATH_EDIT_MM, // 0
CTL_BEVEL_EDGES_BOX_MM, // 1
CTL_BEVEL_WEIGHT_EDIT = CTL_BEVEL_EDGES_BOX_MM, // 1
CTL_COLOUR_SCHEME_COMBO_MM = CTL_BEVEL_EDGES_BOX_MM, // 1
CTL_BLEND_FILE_TRUSTED_BOX_MM, // 2
CTL_BEVEL_WIDTH_EDIT_MM = CTL_BLEND_FILE_TRUSTED_BOX_MM,// 2
CTL_CASE_SENSITIVE_FILESYSTEM_BOX, // 3
CTL_CAMERA_BORDER_PERCENT_EDIT_MM = CTL_CASE_SENSITIVE_FILESYSTEM_BOX,// 3
CTL_RESOLUTION_COMBO_MM = CTL_CASE_SENSITIVE_FILESYSTEM_BOX,// 3
CTL_SCALE_STRATEGY_COMBO = CTL_CASE_SENSITIVE_FILESYSTEM_BOX,// 3
CTL_CROP_IMAGE_BOX_MM, // 4
CTL_FRAMES_PER_STEP_EDIT = CTL_CROP_IMAGE_BOX_MM, // 4
CTL_SMOOTH_TYPE_COMBO = CTL_CROP_IMAGE_BOX_MM, // 4
CTL_DISPLAY_LOGO_BOX, // 5
CTL_GAP_SCALE_EDIT = CTL_DISPLAY_LOGO_BOX, // 5
CTL_IMPORT_CAMERAS_BOX_MM, // 6
CTL_IMPORT_SCALE_EDIT = CTL_IMPORT_CAMERAS_BOX_MM, // 6
CTL_IMPORT_EDGES_BOX, // 7
CTL_MERGE_DISTANCE_EDIT = CTL_IMPORT_EDGES_BOX, // 7
CTL_IMPORT_LIGHTS_BOX_MM, // 8
CTL_RENDER_PERCENTAGE_EDIT_MM = CTL_IMPORT_LIGHTS_BOX_MM, // 8
CTL_KEEP_ASPECT_RATIO_BOX_MM, // 9
CTL_RESOLUTION_WIDTH_EDIT = CTL_KEEP_ASPECT_RATIO_BOX_MM, // 9
CTL_MAKE_GAPS_BOX, // 10
CTL_RESOLUTION_HEIGHT_EDIT = CTL_MAKE_GAPS_BOX, // 10
CTL_META_BFC_BOX, // 11
CTL_STARTING_STEP_FRAME_EDIT = CTL_META_BFC_BOX, // 11
CTL_META_CLEAR_BOX, // 12
CTL_META_GROUP_BOX, // 13
CTL_META_PAUSE_BOX, // 14
CTL_META_PRINT_WRITE_BOX, // 15
CTL_META_SAVE_BOX, // 16
CTL_META_STEP_BOX, // 17
CTL_META_STEP_GROUPS_BOX, // 18
CTL_META_TEXMAP_BOX, // 19
CTL_NO_STUDS_BOX, // 20
CTL_POSITION_CAMERA_BOX_MM, // 21
CTL_OVERWRITE_IMAGE_BOX_MM, // 22
CTL_PARENT_TO_EMPTY_BOX, // 23
CTL_PREFER_STUDIO_BOX, // 24
CTL_PREFER_UNOFFICIAL_BOX, // 25
CTL_PROFILE_BOX, // 26
CTL_RECALCULATE_NORMALS_BOX, // 27
CTL_REMOVE_DOUBLES_BOX_MM, // 28
CTL_RENDER_WINDOW_BOX_MM, // 29
CTL_SEARCH_ADDL_PATHS_MM_BOX, // 20
CTL_SETEND_FRAME_BOX, // 31
CTL_SET_TIMELINE_MARKERS_BOX, // 32
CTL_SHADE_SMOOTH_BOX, // 33
CTL_TRANSPARENT_BACKGROUND_BOX_MM, // 34
CTL_TREAT_SHORTCUT_AS_MODEL_BOX, // 35
CTL_TRIANGULATE_BOX, // 36
CTL_USE_ARCHIVE_LIBRARY_BOX_MM, // 37
CTL_USE_FREESTYLE_EDGES_BOX, // 38
CTL_VERBOSE_BOX_MM // 39
};
enum BlenderBrickType
{
PARAMS_CUSTOM_COLOURS,
PARAMS_SLOPED_BRICKS,
PARAMS_LIGHTED_BRICKS
};
enum MessageBoxButtonsType
{
MBB_OK,
MBB_YES,
MBB_YES_NO
};
struct BlenderSettings
{
QString key;
QString value;
QString label;
QString tooltip;
};
struct BlenderPaths
{
QString key;
QString key_mm;
QString value;
QString label;
QString tooltip;
};
struct ComboItems
{
QString dataList;
QString itemList;
};
static QString TranslateDefaultText(const QString& Source);
static BlenderPaths mBlenderPaths[];
static BlenderPaths mDefaultPaths[];
static BlenderSettings mBlenderSettings[];
static BlenderSettings mBlenderSettingsMM[];
static BlenderSettings mDefaultSettings[];
static BlenderSettings mDefaultSettingsMM[];
static ComboItems mComboItems[];
static ComboItems mComboItemsMM[];
QList<QLineEdit*> mPathLineEditList;
QList<QPushButton*> mPathBrowseButtonList;
QList<QLabel*> mSettingLabelList;
QList<QCheckBox*> mCheckBoxList;
QList<QLineEdit*> mLineEditList;
QList<QComboBox*> mComboBoxList;
QWidget* mContent = nullptr;
QFormLayout* mForm = nullptr;
QGridLayout* mExeGridLayout = nullptr;
QGridLayout* mAddonGridLayout = nullptr;
QGridLayout* mPathsGridLayout = nullptr;
QFormLayout* mSettingsSubform = nullptr;
QLineEdit* mBlenderVersionEdit = nullptr;
QLabel* mBlenderVersionLabel = nullptr;
QLineEdit* mAddonVersionEdit = nullptr;
QLabel* mAddonVersionLabel = nullptr;
QGroupBox* mModulesBox = nullptr;
QCheckBox* mImportActBox = nullptr;
QCheckBox* mImportMMActBox = nullptr;
QCheckBox* mRenderActBox = nullptr;
QGroupBox* mSettingsBox = nullptr;
QGroupBox* mPathsBox = nullptr;
QAction* mDefaultColourEditAction = nullptr;
QPushButton* mAddonUpdateButton = nullptr;
QPushButton* mAddonStdOutButton = nullptr;
QPushButton* mPathsGroupButton = nullptr;
QProgressBar* mProgressBar = nullptr;
QProcess* mProcess = nullptr;
lcHttpReply *mHttpReply = nullptr;
QByteArray mData;
QTimer mUpdateTimer;
QStringList mStdOutList;
QString mDataDir;
QString mBlenderVersion;
QString mAddonVersion;
bool mConfigured;
bool mDialogCancelled;
bool mBlenderVersionFound;
};
extern class lcBlenderPreferences* gAddonPreferences;
#endif // !LC_DISABLE_BLENDER