/**
* @file SetScaleDialog.cpp
* Implements the SetScaleDialog class.
* @ingroup meshtex-ui
*/
/*
* Copyright 2012 Joel Baxter
*
* This file is part of MeshTex.
*
* MeshTex is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* MeshTex is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MeshTex. If not, see .
*/
#include "GenericPluginUI.h"
#include "SetScaleDialog.h"
#include "PluginUIMessages.h"
#include "iundo.h"
/**
* Size of buffer for the text conversion of float values written to various
* widgets.
*/
#define ENTRY_BUFFER_SIZE 128
/**
* Constructor.
*
* @param rowArgs The row (S axis) arguments; NULL if none.
* @param colArgs The column (T axis) arguments; NULL if none.
*/
SetScaleDialog::SetScaleVisitor::SetScaleVisitor(
const SliceArgs *rowArgs,
const SliceArgs *colArgs) :
_rowArgs(rowArgs),
_colArgs(colArgs)
{
}
/**
* Visitor action; invoke MeshEntity::SetScale on a mesh.
*
* @param [in,out] meshEntity The mesh entity.
*
* @return true.
*/
bool
SetScaleDialog::SetScaleVisitor::Execute(MeshEntity& meshEntity) const
{
if (_rowArgs != NULL)
{
meshEntity.SetScale(MeshEntity::ROW_SLICE_TYPE,
_rowArgs->alignSlice, _rowArgs->refSlice,
_rowArgs->naturalScale, _rowArgs->scaleOrTiles);
}
if (_colArgs != NULL)
{
meshEntity.SetScale(MeshEntity::COL_SLICE_TYPE,
_colArgs->alignSlice, _colArgs->refSlice,
_colArgs->naturalScale, _colArgs->scaleOrTiles);
}
return true;
}
/**
* Constructor. Configure the dialog window and create all the contained
* widgets. Connect widgets to callbacks as necessary.
*
* @param key The unique key identifying this dialog.
*/
SetScaleDialog::SetScaleDialog(const std::string& key) :
GenericDialog(key),
_nullVisitor(new MeshVisitor())
{
// Configure the dialog window.
_dialog->setWindowTitle(DIALOG_SET_SCALE_TITLE);
// Create the contained widgets.
{
auto dialog_grid = new QGridLayout( _dialog );
dialog_grid->setSizeConstraint( QLayout::SizeConstraint::SetFixedSize );
{
// Checkbox for the "S" grouping of widgets. All the widgets in that
// grouping will have a dependence registered on this checkbox; i.e. they
// will only be active when it is checked.
auto group = s_apply = new QGroupBox( DIALOG_SET_SCALE_S_ACTIVE_OPT_LABEL );
dialog_grid->addWidget( group, 0, 0 );
group->setCheckable( true );
group->setChecked( true );
{
auto vbox = new QVBoxLayout( group );
{
// Widgets for specifying S scaling.
auto group = new QGroupBox( DIALOG_SET_SCALE_METHOD_FRAME_TITLE );
vbox->addWidget( group );
auto grid = new QGridLayout( group );
{
auto radio = new QRadioButton( DIALOG_SET_SCALE_NATURAL_OPT_LABEL );
grid->addWidget( radio, 0, 0 );
auto spin = s_scale = new DoubleSpinBox( -999, 999, 1, 3 );
grid->addWidget( spin, 0, 1 );
UIInstance().RegisterWidgetDependence( radio, spin );
radio->setChecked( true );
}
{
auto radio = new QRadioButton( DIALOG_SET_SCALE_TILES_OPT_LABEL );
grid->addWidget( radio, 1, 0 );
auto spin = s_tiles = new DoubleSpinBox( -999, 999, 1, 3 );
grid->addWidget( spin, 1, 1 );
UIInstance().RegisterWidgetDependence( radio, spin );
spin->setEnabled( false );
}
}
{
// Widgets for specifying the alignment column.
auto group = new QGroupBox( DIALOG_SET_SCALE_S_ALIGN_FRAME_TITLE );
vbox->addWidget( group );
auto grid = new QGridLayout( group );
grid->setColumnStretch( 2, 1 );
{
auto radio = new QRadioButton;
grid->addWidget( radio, 0, 0 );
auto spin = col_num_align = new SpinBox( 0, 30 );
grid->addWidget( spin, 0, 1 );
UIInstance().RegisterWidgetDependence( radio, spin );
radio->setChecked( true );
}
{
auto radio = new QRadioButton( DIALOG_SET_SCALE_MAX_OPT_LABEL );
grid->addWidget( radio, 0, 2, Qt::AlignmentFlag::AlignRight );
}
}
{
// Widgets for specifying the reference row & usage.
auto group = row_ref = new QGroupBox( DIALOG_SET_SCALE_S_REF_ROW_OPT_LABEL );
group->setCheckable( true );
group->setChecked( true );
vbox->addWidget( group );
auto grid = new QGridLayout( group );
grid->setColumnStretch( 2, 1 );
{
auto radio = new QRadioButton;
grid->addWidget( radio, 0, 0 );
auto spin = row_num_ref = new SpinBox( 0, 30 );
grid->addWidget( spin, 0, 1 );
UIInstance().RegisterWidgetDependence( radio, spin );
radio->setChecked( true );
}
{
auto radio = new QRadioButton( DIALOG_SET_SCALE_MAX_OPT_LABEL );
grid->addWidget( radio, 0, 2, Qt::AlignmentFlag::AlignRight );
}
{
auto check = row_ref_total = new QCheckBox( DIALOG_SET_SCALE_REF_TOTAL_OPT_LABEL );
grid->addWidget( check, 1, 0, 1, 3 );
check->setChecked( true );
}
}
}
}
{
// Checkbox for the "T" grouping of widgets. All the widgets in that
// grouping will have a dependence registered on this checkbox; i.e. they
// will only be active when it is checked.
auto group = t_apply = new QGroupBox( DIALOG_SET_SCALE_T_ACTIVE_OPT_LABEL );
dialog_grid->addWidget( group, 0, 1 );
group->setCheckable( true );
group->setChecked( true );
{
auto vbox = new QVBoxLayout( group );
{
// Widgets for specifying T scaling.
auto group = new QGroupBox( DIALOG_SET_SCALE_METHOD_FRAME_TITLE );
vbox->addWidget( group );
auto grid = new QGridLayout( group );
{
auto radio = new QRadioButton( DIALOG_SET_SCALE_NATURAL_OPT_LABEL );
grid->addWidget( radio, 0, 0 );
auto spin = t_scale = new DoubleSpinBox( -999, 999, 1, 3 );
grid->addWidget( spin, 0, 1 );
UIInstance().RegisterWidgetDependence( radio, spin );
radio->setChecked( true );
}
{
auto radio = new QRadioButton( DIALOG_SET_SCALE_TILES_OPT_LABEL );
grid->addWidget( radio, 1, 0 );
auto spin = t_tiles = new DoubleSpinBox( -999, 999, 1, 3 );
grid->addWidget( spin, 1, 1 );
UIInstance().RegisterWidgetDependence( radio, spin );
spin->setEnabled( false );
}
}
{
// Widgets for specifying the alignment row.
auto group = new QGroupBox( DIALOG_SET_SCALE_T_ALIGN_FRAME_TITLE );
vbox->addWidget( group );
auto grid = new QGridLayout( group );
grid->setColumnStretch( 2, 1 );
{
auto radio = new QRadioButton;
grid->addWidget( radio, 0, 0 );
auto spin = row_num_align = new SpinBox( 0, 30 );
grid->addWidget( spin, 0, 1 );
UIInstance().RegisterWidgetDependence( radio, spin );
radio->setChecked( true );
}
{
auto radio = new QRadioButton( DIALOG_SET_SCALE_MAX_OPT_LABEL );
grid->addWidget( radio, 0, 2, Qt::AlignmentFlag::AlignRight );
}
}
{
// Widgets for specifying the reference column & usage.
auto group = col_ref = new QGroupBox( DIALOG_SET_SCALE_T_REF_COL_OPT_LABEL );
group->setCheckable( true );
group->setChecked( true );
vbox->addWidget( group );
auto grid = new QGridLayout( group );
grid->setColumnStretch( 2, 1 );
{
auto radio = new QRadioButton;
grid->addWidget( radio, 0, 0 );
auto spin = col_num_ref = new SpinBox( 0, 30 );
grid->addWidget( spin, 0, 1 );
UIInstance().RegisterWidgetDependence( radio, spin );
radio->setChecked( true );
}
{
auto radio = new QRadioButton( DIALOG_SET_SCALE_MAX_OPT_LABEL );
grid->addWidget( radio, 0, 2, Qt::AlignmentFlag::AlignRight );
}
{
auto check = col_ref_total = new QCheckBox( DIALOG_SET_SCALE_REF_TOTAL_OPT_LABEL );
grid->addWidget( check, 1, 0, 1, 3 );
check->setChecked( true );
}
}
}
}
{
auto buttons = new QDialogButtonBox;
dialog_grid->addWidget( buttons, 1, 0, 1, 2 );
CreateOkButtonCallback( buttons->addButton( QDialogButtonBox::StandardButton::Ok ) );
CreateApplyButtonCallback( buttons->addButton( QDialogButtonBox::StandardButton::Apply ) );
CreateCancelButtonCallback( buttons->addButton( QDialogButtonBox::StandardButton::Cancel ) );
}
}
}
/**
* Destructor.
*/
SetScaleDialog::~SetScaleDialog()
{
}
/**
* Handler for the Apply logic for this dialog. Apply the specified scaling to
* the selected mesh entities.
*
* @return true if any meshes are selected, false otherwise.
*/
bool
SetScaleDialog::Apply()
{
// Before doing anything, check to see if there are some meshes selected.
_nullVisitor->ResetVisitedCount();
GlobalSelectionSystem().foreachSelected(*_nullVisitor);
if (_nullVisitor->GetVisitedCount() == 0)
{
// Nope. Warn and bail out.
GenericPluginUI::WarningReportDialog(DIALOG_WARNING_TITLE,
DIALOG_NOMESHES_MSG);
return false;
}
// See if we're going to be affecting the S and/or T texture axis.
const bool sApply = s_apply->isChecked();
const bool tApply = t_apply->isChecked();
if (!sApply && !tApply)
{
// Not affecting either, so bail out.
return true;
}
// OK read the remaining info from the widgets.
MeshEntity::SliceDesignation alignCol, alignRow;
MeshEntity::RefSliceDescriptor refRow, refCol;
SetScaleVisitor::SliceArgs row, col;
SetScaleVisitor::SliceArgs *rowArgs = NULL;
SetScaleVisitor::SliceArgs *colArgs = NULL;
if (sApply)
{
// S axis is affected, so read the S info.
row.naturalScale = s_scale->isEnabled();
if (row.naturalScale)
{
row.scaleOrTiles = s_scale->value();
}
else
{
row.scaleOrTiles = s_tiles->value();
}
alignCol.maxSlice = !col_num_align->isEnabled();
alignCol.index = col_num_align->value();
row.alignSlice = &alignCol;
row.refSlice = NULL;
if ( row_ref->isChecked() )
{
// Reference row is specified, so get that info.
refRow.designation.maxSlice = !row_num_ref->isEnabled();
refRow.designation.index = row_num_ref->value();
refRow.totalLengthOnly = row_ref_total->isChecked();
row.refSlice = &refRow;
}
rowArgs = &row;
}
if (tApply)
{
// T axis is affected, so read the T info.
col.naturalScale = t_scale->isEnabled();
if (col.naturalScale)
{
col.scaleOrTiles = t_scale->value();
}
else
{
col.scaleOrTiles = t_tiles->value();
}
alignRow.maxSlice = !row_num_align->isEnabled();
alignRow.index = row_num_align->value();
col.alignSlice = &alignRow;
col.refSlice = NULL;
if ( col_ref->isChecked() )
{
// Reference column is specified, so get that info.
refCol.designation.maxSlice = !col_num_ref->isEnabled();
refCol.designation.index = col_num_ref->value();
refCol.totalLengthOnly = col_ref_total->isChecked();
col.refSlice = &refCol;
}
colArgs = &col;
}
// Let Radiant know the name of the operation responsible for the changes
// that are about to happen.
UndoableCommand undo(_triggerCommand.c_str());
// Apply the specified scaling to every selected mesh.
SmartPointer scaleVisitor(new SetScaleVisitor(rowArgs, colArgs));
GlobalSelectionSystem().foreachSelected(*scaleVisitor);
// Done!
return true;
}
/**
* Allow an external caller to set some of the S-axis entries.
*
* @param scale Texture scaling.
* @param tiles Texture tiles.
*/
void
SetScaleDialog::PopulateSWidgets(float scale,
float tiles)
{
// Use the texture info to populate some of our widgets.
s_scale->setValue( scale);
s_tiles->setValue( tiles);
}
/**
* Allow an external caller to set some of the T-axis entries.
*
* @param scale Texture scaling.
* @param tiles Texture tiles.
*/
void
SetScaleDialog::PopulateTWidgets(float scale,
float tiles)
{
// Use the texture info to populate some of our widgets.
t_scale->setValue( scale);
t_tiles->setValue( tiles);
}