netradiant-custom/radiant/dialog.cpp
2024-01-24 17:09:04 +06:00

528 lines
18 KiB
C++

/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//
// Base dialog class, provides a way to run modal dialogs and
// set/get the widget values in member variables.
//
// Leonardo Zide (leo@lokigames.com)
//
#include "dialog.h"
#include "debugging/debugging.h"
#include "mainframe.h"
#include <cstdlib>
#include "stream/stringstream.h"
#include "gtkutil/dialog.h"
#include "gtkutil/entry.h"
#include "gtkutil/image.h"
#include "gtkutil/spinbox.h"
#include "gtkmisc.h"
#include <QCheckBox>
#include "gtkutil/combobox.h"
#include <QSlider>
#include <QRadioButton>
#include <QButtonGroup>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QString>
struct DialogSliderRow
{
QSlider *m_slider;
QHBoxLayout *m_layout;
DialogSliderRow( int lower, int upper, int step_increment, int page_increment ){
m_slider = new QSlider( Qt::Orientation::Horizontal );
m_slider->setRange( lower, upper );
m_slider->setSingleStep( step_increment );
m_slider->setPageStep( page_increment );
auto label = new QLabel;
label->setMinimumWidth( label->fontMetrics().horizontalAdvance( QString::number( upper ) ) );
QObject::connect( m_slider, &QSlider::valueChanged,
[label]( int value ){
label->setText( QString::number( value ) );
} );
m_layout = new QHBoxLayout;
m_layout->addWidget( label );
m_layout->addWidget( m_slider );
}
DialogSliderRow( double lower, double upper, double step_increment, double page_increment ){
m_slider = new QSlider( Qt::Orientation::Horizontal );
m_slider->setRange( lower * 10, upper * 10 );
m_slider->setSingleStep( step_increment * 10 );
m_slider->setPageStep( page_increment * 10 );
auto label = new QLabel;
label->setMinimumWidth( label->fontMetrics().horizontalAdvance( QString::number( upper, 'f', 1 ) ) );
QObject::connect( m_slider, &QSlider::valueChanged,
[label]( int value ){
label->setText( QString::number( value / 10.0, 'f', 1 ) );
} );
m_layout = new QHBoxLayout;
m_layout->addWidget( label );
m_layout->addWidget( m_slider );
}
};
template<
typename Type_,
typename Other_,
void( *Import ) ( Type_&, Other_ ),
void( *Export ) ( Type_&, const Callback1<Other_>& )
>
class ImportExport
{
public:
typedef Type_ Type;
typedef Other_ Other;
typedef ReferenceCaller1<Type, Other, Import> ImportCaller;
typedef ReferenceCaller1<Type, const Callback1<Other>&, Export> ExportCaller;
};
typedef ImportExport<bool, bool, BoolImport, BoolExport> BoolImportExport;
typedef ImportExport<int, int, IntImport, IntExport> IntImportExport;
typedef ImportExport<std::size_t, std::size_t, SizeImport, SizeExport> SizeImportExport;
typedef ImportExport<float, float, FloatImport, FloatExport> FloatImportExport;
typedef ImportExport<CopiedString, const char*, StringImport, StringExport> StringImportExport;
void BoolToggleImport( QCheckBox& widget, bool value ){
widget.setChecked( value );
}
void BoolToggleExport( QCheckBox& widget, const BoolImportCallback& importCallback ){
importCallback( widget.isChecked() );
}
typedef ImportExport<QCheckBox, bool, BoolToggleImport, BoolToggleExport> BoolToggleImportExport;
void IntRadioImport( QButtonGroup& widget, int index ){
widget.button( index )->setChecked( true );
}
void IntRadioExport( QButtonGroup& widget, const IntImportCallback& importCallback ){
importCallback( widget.checkedId() );
}
typedef ImportExport<QButtonGroup, int, IntRadioImport, IntRadioExport> IntRadioImportExport;
void TextEntryImport( QLineEdit& widget, const char* text ){
widget.setText( text );
}
void TextEntryExport( QLineEdit& widget, const StringImportCallback& importCallback ){
importCallback( widget.text().toLatin1().constData() );
}
typedef ImportExport<QLineEdit, const char*, TextEntryImport, TextEntryExport> TextEntryImportExport;
void FloatSpinnerImport( QDoubleSpinBox& widget, float value ){
widget.setValue( value );
}
void FloatSpinnerExport( QDoubleSpinBox& widget, const FloatImportCallback& importCallback ){
importCallback( widget.value() );
}
typedef ImportExport<QDoubleSpinBox, float, FloatSpinnerImport, FloatSpinnerExport> FloatSpinnerImportExport;
void IntSpinnerImport( QSpinBox& widget, int value ){
widget.setValue( value );
}
void IntSpinnerExport( QSpinBox& widget, const IntImportCallback& importCallback ){
importCallback( widget.value() );
}
typedef ImportExport<QSpinBox, int, IntSpinnerImport, IntSpinnerExport> IntSpinnerImportExport;
void IntSliderImport( QSlider& widget, int value ){
widget.setValue( value );
}
void IntSliderExport( QSlider& widget, const IntImportCallback& importCallback ){
importCallback( widget.value() );
}
typedef ImportExport<QSlider, int, IntSliderImport, IntSliderExport> IntSliderImportExport;
// QSlider operates on int values only, so using 10x range for floats
void FloatSliderImport( QSlider& widget, float value ){
widget.setValue( value * 10.0 );
}
void FloatSliderExport( QSlider& widget, const FloatImportCallback& importCallback ){
importCallback( widget.value() / 10.0 );
}
typedef ImportExport<QSlider, float, FloatSliderImport, FloatSliderExport> FloatSliderImportExport;
void IntComboImport( QComboBox& widget, int value ){
widget.setCurrentIndex( value );
}
void IntComboExport( QComboBox& widget, const IntImportCallback& importCallback ){
importCallback( widget.currentIndex() );
}
typedef ImportExport<QComboBox, int, IntComboImport, IntComboExport> IntComboImportExport;
template<typename FirstArgument>
class CallbackDialogData final : public DLG_DATA
{
public:
typedef Callback1<FirstArgument> ImportCallback;
typedef Callback1<const ImportCallback&> ExportCallback;
private:
ImportCallback m_importWidget;
ExportCallback m_exportWidget;
ImportCallback m_importViewer;
ExportCallback m_exportViewer;
public:
CallbackDialogData( const ImportCallback& importWidget, const ExportCallback& exportWidget, const ImportCallback& importViewer, const ExportCallback& exportViewer )
: m_importWidget( importWidget ), m_exportWidget( exportWidget ), m_importViewer( importViewer ), m_exportViewer( exportViewer ){
}
void release(){
delete this;
}
void importData() const {
m_exportViewer( m_importWidget );
}
void exportData() const {
m_exportWidget( m_importViewer );
}
};
template<typename Widget, typename Viewer>
class AddData
{
DialogDataList& m_data;
public:
AddData( DialogDataList& data ) : m_data( data ){
}
void apply( typename Widget::Type& widget, typename Viewer::Type& viewer ) const {
m_data.push_back(
new CallbackDialogData<typename Widget::Other>(
typename Widget::ImportCaller( widget ),
typename Widget::ExportCaller( widget ),
typename Viewer::ImportCaller( viewer ),
typename Viewer::ExportCaller( viewer )
)
);
}
};
template<typename Widget>
class AddCustomData
{
DialogDataList& m_data;
public:
AddCustomData( DialogDataList& data ) : m_data( data ){
}
void apply(
typename Widget::Type& widget,
const Callback1<typename Widget::Other>& importViewer,
const Callback1<const Callback1<typename Widget::Other>&>& exportViewer
) const {
m_data.push_back(
new CallbackDialogData<typename Widget::Other>(
typename Widget::ImportCaller( widget ),
typename Widget::ExportCaller( widget ),
importViewer,
exportViewer
)
);
}
};
// =============================================================================
// Dialog class
Dialog::~Dialog(){
for ( DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i )
{
( *i )->release();
}
ASSERT_MESSAGE( m_window == 0, "dialog window not destroyed" );
}
void Dialog::ShowDlg(){
ASSERT_MESSAGE( m_window != 0, "dialog was not constructed" );
importData();
m_window->show();
m_window->raise();
m_window->activateWindow();
}
void Dialog::HideDlg(){
ASSERT_MESSAGE( m_window != 0, "dialog was not constructed" );
exportData();
m_window->hide();
}
void Dialog::Create( QWidget *parent ){
ASSERT_MESSAGE( m_window == 0, "dialog cannot be constructed" );
m_window = new QDialog( parent, Qt::Dialog | Qt::WindowCloseButtonHint );
BuildDialog();
}
void Dialog::Destroy(){
ASSERT_MESSAGE( m_window != 0, "dialog cannot be destroyed" );
delete m_window;
m_window = 0;
}
void Dialog::AddBoolToggleData( QCheckBox& widget, const BoolImportCallback& importViewer, const BoolExportCallback& exportViewer ){
AddCustomData<BoolToggleImportExport>( m_data ).apply( widget, importViewer, exportViewer );
}
void Dialog::AddIntRadioData( QButtonGroup& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
AddCustomData<IntRadioImportExport>( m_data ).apply( widget, importViewer, exportViewer );
}
void Dialog::AddTextEntryData( QLineEdit& widget, const StringImportCallback& importViewer, const StringExportCallback& exportViewer ){
AddCustomData<TextEntryImportExport>( m_data ).apply( widget, importViewer, exportViewer );
}
void Dialog::AddFloatSpinnerData( QDoubleSpinBox& widget, const FloatImportCallback& importViewer, const FloatExportCallback& exportViewer ){
AddCustomData<FloatSpinnerImportExport>( m_data ).apply( widget, importViewer, exportViewer );
}
void Dialog::AddIntSpinnerData( QSpinBox& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
AddCustomData<IntSpinnerImportExport>( m_data ).apply( widget, importViewer, exportViewer );
}
void Dialog::AddIntSliderData( QSlider& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
AddCustomData<IntSliderImportExport>( m_data ).apply( widget, importViewer, exportViewer );
}
void Dialog::AddFloatSliderData( QSlider& widget, const FloatImportCallback& importViewer, const FloatExportCallback& exportViewer ){
AddCustomData<FloatSliderImportExport>( m_data ).apply( widget, importViewer, exportViewer );
}
void Dialog::AddIntComboData( QComboBox& widget, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
AddCustomData<IntComboImportExport>( m_data ).apply( widget, importViewer, exportViewer );
}
void Dialog::AddDialogData( QCheckBox& widget, bool& data ){
AddData<BoolToggleImportExport, BoolImportExport>( m_data ).apply( widget, data );
}
void Dialog::AddDialogData( QButtonGroup& widget, int& data ){
AddData<IntRadioImportExport, IntImportExport>( m_data ).apply( widget, data );
}
void Dialog::AddDialogData( QLineEdit& widget, CopiedString& data ){
AddData<TextEntryImportExport, StringImportExport>( m_data ).apply( widget, data );
}
void Dialog::AddDialogData( QDoubleSpinBox& widget, float& data ){
AddData<FloatSpinnerImportExport, FloatImportExport>( m_data ).apply( widget, data );
}
void Dialog::AddDialogData( QSpinBox& widget, int& data ){
AddData<IntSpinnerImportExport, IntImportExport>( m_data ).apply( widget, data );
}
void Dialog::AddDialogData( QSlider& widget, int& data ){
AddData<IntSliderImportExport, IntImportExport>( m_data ).apply( widget, data );
}
void Dialog::AddDialogData( QSlider& widget, float& data ){
AddData<FloatSliderImportExport, FloatImportExport>( m_data ).apply( widget, data );
}
void Dialog::AddDialogData( QComboBox& widget, int& data ){
AddData<IntComboImportExport, IntImportExport>( m_data ).apply( widget, data );
}
void Dialog::exportData(){
for ( DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i )
{
( *i )->exportData();
}
}
void Dialog::importData(){
for ( DialogDataList::iterator i = m_data.begin(); i != m_data.end(); ++i )
{
( *i )->importData();
}
}
void Dialog::EndModal( QDialog::DialogCode code ){
m_window->done( code );
}
QDialog::DialogCode Dialog::DoModal(){
importData();
PreModal();
ASSERT_NOTNULL( m_window );
const QDialog::DialogCode ret = static_cast<QDialog::DialogCode>( m_window->exec() );
if ( ret == QDialog::DialogCode::Accepted ) {
exportData();
}
m_window->hide();
PostModal( ret );
return ret;
}
QCheckBox* Dialog::addCheckBox( QGridLayout* grid, const char* name, const char* flag, const BoolImportCallback& importViewer, const BoolExportCallback& exportViewer ){
auto check = new QCheckBox( flag );
AddBoolToggleData( *check, importViewer, exportViewer );
DialogGrid_packRow( grid, check, name );
return check;
}
QCheckBox* Dialog::addCheckBox( QGridLayout* grid, const char* name, const char* flag, bool& data ){
return addCheckBox( grid, name, flag, BoolImportCaller( data ), BoolExportCaller( data ) );
}
QComboBox* Dialog::addCombo( QGridLayout* grid, const char* name, StringArrayRange values, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
auto combo = new ComboBox;
for ( const char *value : values )
combo->addItem( value );
AddIntComboData( *combo, importViewer, exportViewer );
DialogGrid_packRow( grid, combo, name );
return combo;
}
QComboBox* Dialog::addCombo( QGridLayout* grid, const char* name, int& data, StringArrayRange values ){
return addCombo( grid, name, values, IntImportCaller( data ), IntExportCaller( data ) );
}
void Dialog::addSlider( QGridLayout* grid, const char* name, int& data, int lower, int upper, int step_increment, int page_increment ){
DialogSliderRow row( lower, upper, step_increment, page_increment );
AddIntSliderData( *row.m_slider, IntImportCaller( data ), IntExportCaller( data ) );
DialogGrid_packRow( grid, row.m_layout, name );
}
void Dialog::addSlider( QGridLayout* grid, const char* name, float& data, double lower, double upper, double step_increment, double page_increment ){
DialogSliderRow row( lower, upper, step_increment, page_increment );
AddFloatSliderData( *row.m_slider, FloatImportCaller( data ), FloatExportCaller( data ) );
DialogGrid_packRow( grid, row.m_layout, name );
}
void Dialog::addRadio( QGridLayout* grid, const char* name, StringArrayRange names, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
RadioHBox radioBox = RadioHBox_new( names );
AddIntRadioData( *radioBox.m_radio, importViewer, exportViewer );
DialogGrid_packRow( grid, radioBox.m_hbox, name );
}
void Dialog::addRadio( QGridLayout* grid, const char* name, int& data, StringArrayRange names ){
addRadio( grid, name, names, IntImportCaller( data ), IntExportCaller( data ) );
}
void Dialog::addRadioIcons( QGridLayout* grid, const char* name, StringArrayRange icons, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
auto subgrid = new QGridLayout;
auto buttons = new QButtonGroup( subgrid );
for ( size_t i = 0; i < icons.size(); ++i )
{
auto label = new QLabel;
label->setPixmap( new_local_image( icons[i] ) );
subgrid->addWidget( label, 0, i, Qt::AlignmentFlag::AlignHCenter );
auto button = new QRadioButton;
buttons->addButton( button, i ); // set ids 0+, default ones are negative
subgrid->addWidget( button, 1, i, Qt::AlignmentFlag::AlignHCenter );
}
AddIntRadioData( *buttons, importViewer, exportViewer );
DialogGrid_packRow( grid, subgrid, name );
}
void Dialog::addRadioIcons( QGridLayout* grid, const char* name, int& data, StringArrayRange icons ){
addRadioIcons( grid, name, icons, IntImportCaller( data ), IntExportCaller( data ) );
}
QWidget* Dialog::addTextEntry( QGridLayout* grid, const char* name, const StringImportCallback& importViewer, const StringExportCallback& exportViewer ){
auto entry = new QLineEdit;
AddTextEntryData( *entry, importViewer, exportViewer );
DialogGrid_packRow( grid, entry, name );
return entry;
}
void Dialog::addPathEntry( QGridLayout* grid, const char* name, bool browse_directory, const StringImportCallback& importViewer, const StringExportCallback& exportViewer ){
PathEntry pathEntry = PathEntry_new();
if( browse_directory )
QObject::connect( pathEntry.m_button, &QAction::triggered, [entry = pathEntry.m_entry](){ button_clicked_entry_browse_directory( entry ); } );
else
QObject::connect( pathEntry.m_button, &QAction::triggered, [entry = pathEntry.m_entry](){ button_clicked_entry_browse_file( entry ); } );
AddTextEntryData( *pathEntry.m_entry, importViewer, exportViewer );
DialogGrid_packRow( grid, pathEntry.m_entry, name );
}
void Dialog::addPathEntry( QGridLayout* grid, const char* name, CopiedString& data, bool browse_directory ){
addPathEntry( grid, name, browse_directory, StringImportCallback( StringImportCaller( data ) ), StringExportCallback( StringExportCaller( data ) ) );
}
QWidget* Dialog::addSpinner( QGridLayout* grid, const char* name, int lower, int upper, const IntImportCallback& importViewer, const IntExportCallback& exportViewer ){
auto spin = new SpinBox( lower, upper );
spin->setStepType( QAbstractSpinBox::StepType::AdaptiveDecimalStepType );
AddIntSpinnerData( *spin, importViewer, exportViewer );
DialogGrid_packRow( grid, spin, new SpinBoxLabel( name, spin ) );
return spin;
}
QWidget* Dialog::addSpinner( QGridLayout* grid, const char* name, int& data, int lower, int upper ){
return addSpinner( grid, name, lower, upper, IntImportCallback( IntImportCaller( data ) ), IntExportCallback( IntExportCaller( data ) ) );
}
QWidget* Dialog::addSpinner( QGridLayout* grid, const char* name, double lower, double upper, const FloatImportCallback& importViewer, const FloatExportCallback& exportViewer, int decimals ){
auto spin = new DoubleSpinBox( lower, upper, 0, decimals );
spin->setStepType( QAbstractSpinBox::StepType::AdaptiveDecimalStepType );
AddFloatSpinnerData( *spin, importViewer, exportViewer );
DialogGrid_packRow( grid, spin, new SpinBoxLabel( name, spin ) );
return spin;
}
QWidget* Dialog::addSpinner( QGridLayout* grid, const char* name, float& data, double lower, double upper, int decimals ){
return addSpinner( grid, name, lower, upper, FloatImportCallback( FloatImportCaller( data ) ), FloatExportCallback( FloatExportCaller( data ) ), decimals );
}