[education/rkward] rkward: Cut down on unreadable radiobutton boilerplate code

Thomas Friedrichsmeier null at kde.org
Sat Jul 27 22:35:43 BST 2024


Git commit 57ed22de7fe913bf318189ccd188d7fdeca394d0 by Thomas Friedrichsmeier.
Committed on 27/07/2024 at 21:35.
Pushed by tfry into branch 'master'.

Cut down on unreadable radiobutton boilerplate code

M  +14   -35   rkward/dialogs/rkimportdialog.cpp
M  +1    -0    rkward/misc/CMakeLists.txt
M  +16   -39   rkward/misc/editformatdialog.cpp
M  +0    -2    rkward/misc/editformatdialog.h
A  +76   -0    rkward/misc/rkradiogroup.cpp     [License: GPL(v2.0+)]
A  +41   -0    rkward/misc/rkradiogroup.h     [License: GPL(v2.0+)]
M  +41   -83   rkward/misc/rkspecialactions.cpp
M  +10   -15   rkward/plugin/rkradio.cpp
M  +3    -5    rkward/plugin/rkradio.h
M  +8    -19   rkward/settings/rksettingsmodulegeneral.cpp
M  +19   -43   rkward/settings/rksettingsmodulegraphics.cpp
M  +7    -19   rkward/settings/rksettingsmoduleplugins.cpp
M  +0    -3    rkward/settings/rksettingsmoduler.cpp

https://invent.kde.org/education/rkward/-/commit/57ed22de7fe913bf318189ccd188d7fdeca394d0

diff --git a/rkward/dialogs/rkimportdialog.cpp b/rkward/dialogs/rkimportdialog.cpp
index ec2e40381..905d2f246 100644
--- a/rkward/dialogs/rkimportdialog.cpp
+++ b/rkward/dialogs/rkimportdialog.cpp
@@ -1,6 +1,6 @@
 /*
 rkimportdialog - This file is part of RKWard (https://rkward.kde.org). Created: Tue Jan 30 2007
-SPDX-FileCopyrightText: 2007-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileCopyrightText: 2007-2024 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
 SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
 SPDX-License-Identifier: GPL-2.0-or-later
 */
@@ -9,9 +9,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
 #include <QLabel>
 #include <QVBoxLayout>
-#include <QGroupBox>
-#include <QRadioButton>
-#include <QButtonGroup>
 
 #include <KLocalizedString>
 #include <KMessageWidget>
@@ -20,6 +17,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include "../plugin/rkcomponentcontext.h"
 #include "../misc/rkcommonfunctions.h"
 #include "../misc/rkspecialactions.h"
+#include "../misc/rkradiogroup.h"
 
 #include "../debug.h"
 
@@ -58,19 +56,13 @@ RKImportDialog::RKImportDialog(const QString &context_id, QWidget *parent) : KAs
 		w->setWordWrap(true);
 		layout->addWidget(w);
 	}
-	QGroupBox *box = new QGroupBox();
+	auto box = new RKRadioGroup();
 	layout->addWidget(box);
-	select_format_group = new QButtonGroup(this);
-	QVBoxLayout *sublayout = new QVBoxLayout(box);
+	select_format_group = box->group();
 	for (int i = 0; i < filters.size(); ++i) {
-		auto *button = new QRadioButton(filters[i]);
-		sublayout->addWidget(button);
-		select_format_group->addButton(button, i);
+		box->addButton(filters[i], i);
 	}
-	auto *button = new QRadioButton(i18n("None of the above / try another method"));
-	sublayout->addWidget(button);
-	select_format_group->addButton(button);
-	button->setChecked(true);
+	box->addButton(i18n("None of the above / try another method"))->setChecked(true);
 	connect(select_format_group, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled), this, &RKImportDialog::updateState);
 	layout->addStretch();
 	select_format = addPage(page, i18n("Select format"));
@@ -84,18 +76,11 @@ RKImportDialog::RKImportDialog(const QString &context_id, QWidget *parent) : KAs
 		w->setWordWrap(true);
 		layout->addWidget(w);
 	}
-	box = new QGroupBox();
+	box = new RKRadioGroup();
 	layout->addWidget(box);
-	select_rio_group = new QButtonGroup(this);
-	sublayout = new QVBoxLayout(box);
-	button = new QRadioButton(i18n("Use 'rio'-based importer"));
-	button->setEnabled(rio_handle != nullptr);
-	sublayout->addWidget(button);
-	select_rio_group->addButton(button, 0);
-	button = new QRadioButton(i18n("Try another method"));
-	sublayout->addWidget(button);
-	select_rio_group->addButton(button);
-	button->setChecked(true);
+	select_rio_group = box->group();
+	box->addButton(i18n("Use 'rio'-based importer"), 0)->setEnabled(rio_handle != nullptr);
+	box->addButton(i18n("Try another method"))->setChecked(true);
 	connect(select_rio_group, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled), this, &RKImportDialog::updateState);
 	layout->addStretch();
 	select_rio = addPage(page, i18n("Generic 'rio'-based importer"));
@@ -103,17 +88,11 @@ RKImportDialog::RKImportDialog(const QString &context_id, QWidget *parent) : KAs
 	page = new QWidget();
 	layout = new QVBoxLayout(page);
 	layout->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("If your data is moderate in size, and you can open/view it in another application on your system, importing it from clipboard (copy and paste) may be viable.")));
-	box = new QGroupBox();
+	box = new RKRadioGroup();
 	layout->addWidget(box);
-	select_clipboard_group = new QButtonGroup(this);
-	sublayout = new QVBoxLayout(box);
-	button = new QRadioButton(i18n("Import from clipboard"));
-	sublayout->addWidget(button);
-	select_clipboard_group->addButton(button, 0);
-	button = new QRadioButton(i18n("Try another method"));
-	sublayout->addWidget(button);
-	select_clipboard_group->addButton(button);
-	button->setChecked(true);
+	select_clipboard_group = box->group();
+	box->addButton(i18n("Import from clipboard"), 0);
+	box->addButton(i18n("Try another method"))->setChecked(true);
 	connect(select_clipboard_group, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled), this, &RKImportDialog::updateState);
 	layout->addStretch();
 	select_clipboard = addPage(page, i18n("Import from clipboard"));
diff --git a/rkward/misc/CMakeLists.txt b/rkward/misc/CMakeLists.txt
index a3913c188..9287af026 100644
--- a/rkward/misc/CMakeLists.txt
+++ b/rkward/misc/CMakeLists.txt
@@ -34,6 +34,7 @@ SET(misc_STAT_SRCS
    rkoutputdirectory.cpp
    rkstyle.cpp
    rkparsedversion.cpp
+   rkradiogroup.cpp
    )
 
 ADD_LIBRARY(misc STATIC ${misc_STAT_SRCS})
diff --git a/rkward/misc/editformatdialog.cpp b/rkward/misc/editformatdialog.cpp
index 961992b67..c79f70206 100644
--- a/rkward/misc/editformatdialog.cpp
+++ b/rkward/misc/editformatdialog.cpp
@@ -6,9 +6,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
 */
 #include "editformatdialog.h"
 
-#include <QButtonGroup>
-#include <QGroupBox>
-#include <qradiobutton.h>
 #include <qspinbox.h>
 #include <qstringlist.h>
 #include <QVBoxLayout>
@@ -20,6 +17,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "../core/rkvariable.h"
 #include "../misc/rkdialogbuttonbox.h"
+#include "../misc/rkradiogroup.h"
 
 #include "../debug.h"
 
@@ -28,35 +26,21 @@ EditFormatDialog::EditFormatDialog (QWidget *parent) : QDialog (parent) {
 
 	QVBoxLayout *layout = new QVBoxLayout (this);
 
-	alignment_group = new QButtonGroup (this);
-	QGroupBox* alignment_box = new QGroupBox (i18n ("Alignment"), this);
-	layout->addWidget (alignment_box);
-	QVBoxLayout* group_layout = new QVBoxLayout (alignment_box);
-	group_layout->setContentsMargins (0, 0, 0, 0);
-	QRadioButton* button;
-	alignment_group->addButton (button = new QRadioButton (i18n ("Default"), alignment_box), (int) RKVariable::FormattingOptions::AlignDefault);
-	group_layout->addWidget (button);
-	alignment_group->addButton (button = new QRadioButton (i18n ("Left"), alignment_box), (int) RKVariable::FormattingOptions::AlignLeft);
-	group_layout->addWidget (button);
-	alignment_group->addButton (button = new QRadioButton (i18n ("Right"), alignment_box), (int) RKVariable::FormattingOptions::AlignRight);
-	group_layout->addWidget (button);
-	alignment_group->button ((int) RKVariable::FormattingOptions::AlignDefault)->setChecked (true);
-
-	precision_group = new QButtonGroup (this);
-	QGroupBox* precision_box = new QGroupBox (i18n ("Decimal Places"), this);
-	layout->addWidget (precision_box);
-	group_layout = new QVBoxLayout (precision_box);
-	precision_group->addButton (button = new QRadioButton (i18n ("Default setting"), precision_box), (int) RKVariable::FormattingOptions::PrecisionDefault);
-	group_layout->addWidget (button);
-	precision_group->addButton (button = new QRadioButton (i18n ("As required"), precision_box), (int) RKVariable::FormattingOptions::PrecisionRequired);
-	group_layout->addWidget (button);
-	precision_group->addButton (button = new QRadioButton (i18n ("Fixed precision:"), precision_box), (int) RKVariable::FormattingOptions::PrecisionFixed);
-	group_layout->addWidget (button);
-	precision_field = new QSpinBox (precision_box);
-	precision_field->setRange (0, 10);
-	connect (precision_field, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &EditFormatDialog::precisionFieldChanged);
-	group_layout->addWidget (precision_field);
-	precision_group->button ((int) RKVariable::FormattingOptions::PrecisionDefault)->setChecked (true);
+	auto alignment_box = new RKRadioGroup(i18n ("Alignment"));
+	alignment_group = alignment_box->group();
+	layout->addWidget(alignment_box);
+	alignment_box->addButton(i18n("Default"), (int) RKVariable::FormattingOptions::AlignDefault)->setChecked(true);
+	alignment_box->addButton(i18n("Left"), (int) RKVariable::FormattingOptions::AlignLeft);
+	alignment_box->addButton(i18n("Right"), (int) RKVariable::FormattingOptions::AlignRight);
+
+	auto precision_box = new RKRadioGroup(i18n("Decimal Places"));
+	precision_group = precision_box->group();
+	layout->addWidget(precision_box);
+	precision_box->addButton(i18n("Default setting"), (int) RKVariable::FormattingOptions::PrecisionDefault);
+	precision_box->addButton(i18n("As required"), (int) RKVariable::FormattingOptions::PrecisionRequired);
+	precision_field = new QSpinBox();
+	precision_field->setRange(0, 10);
+	precision_box->addButton(i18n("Fixed precision:"), (int) RKVariable::FormattingOptions::PrecisionFixed, precision_field);
 
 	RKDialogButtonBox *buttons = new RKDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
 	layout->addWidget (buttons);
@@ -93,13 +77,6 @@ void EditFormatDialog::accept () {
 	QDialog::accept ();
 }
 
-void EditFormatDialog::precisionFieldChanged (int) {
-	RK_TRACE (EDITOR);
-
-	precision_group->button ((int) RKVariable::FormattingOptions::PrecisionFixed)->setChecked (true);
-}
-
-
 ///////////// EditFormatDialogProxy ////////////////////
 
 EditFormatDialogProxy::EditFormatDialogProxy (QWidget* parent) : QWidget (parent) {
diff --git a/rkward/misc/editformatdialog.h b/rkward/misc/editformatdialog.h
index 99aa5bcef..0c955d71e 100644
--- a/rkward/misc/editformatdialog.h
+++ b/rkward/misc/editformatdialog.h
@@ -22,8 +22,6 @@ Allows editing of format-attributes for an RKVariable
 */
 class EditFormatDialog : public QDialog {
 	Q_OBJECT
-public Q_SLOTS:
-	void precisionFieldChanged (int);
 protected:
 /** reimplemented to make the newly selected options available */
 	void accept () override;
diff --git a/rkward/misc/rkradiogroup.cpp b/rkward/misc/rkradiogroup.cpp
new file mode 100644
index 000000000..0d088b208
--- /dev/null
+++ b/rkward/misc/rkradiogroup.cpp
@@ -0,0 +1,76 @@
+/*
+rkradiogroup - This file is part of the RKWard project. Created: Mon Jul 24 2024
+SPDX-FileCopyrightText: 2024 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
+SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include "rkradiogroup.h"
+
+#include <QSinglePointEvent>
+
+#include "../debug.h"
+
+RKRadioGroup::RKRadioGroup(QWidget *parent) : RKRadioGroup(QString(), parent) {
+}
+
+RKRadioGroup::RKRadioGroup(const QString &label, QWidget *parent) : QGroupBox(label, parent), _group(new QButtonGroup(this)), _layout(new QVBoxLayout(this)) {
+	RK_TRACE(MISC);
+}
+
+RKRadioGroup::~RKRadioGroup() {
+	RK_TRACE(MISC);
+}
+
+QRadioButton* RKRadioGroup::addButton(const QString &label, int id) {
+	RK_TRACE(MISC);
+	auto button = new QRadioButton(label);
+	_group->addButton(button, id);
+	_layout->addWidget(button);
+	return button;
+}
+
+QRadioButton* RKRadioGroup::addButton(const QString &label, int id, QWidget* controlled, QBoxLayout::Direction dir) {
+	RK_TRACE(MISC);
+	QRadioButton *button;
+	auto old_layout = _layout;
+	if (dir != _layout->direction()) {
+		_layout = new QBoxLayout(dir);
+		old_layout->addLayout(_layout);
+	}
+	button = addButton(label, id);
+	_layout->addWidget(controlled);
+	controlled->installEventFilter(this);
+	controlled->setProperty(property, QVariant::fromValue(button));
+	_layout = old_layout;
+	controlled->setEnabled(false);
+	// TODO (see EditFormatDialog: it may also make sense the other way around: if the associated widget is clicked, set this button as active)
+	connect(button, &QAbstractButton::toggled, controlled, &QWidget::setEnabled);
+	return button;
+}
+
+bool RKRadioGroup::setButtonChecked(int id, bool checked) {
+	RK_TRACE(MISC);
+	auto button = _group->button(id);
+	if (button) {
+		button->setChecked(checked);
+		return true;
+	}
+	return false;
+}
+
+bool RKRadioGroup::eventFilter(QObject *obj, QEvent *ev) {
+	if (ev->isSinglePointEvent()) {
+		// When clicking in the widget controlled by a button, automatically check the button (enabling the controlled widget).
+		// In effect, the appearance is that the widget "belongs" to the button.
+		// NOTE: This does not wait for a full click to occur, only a mouse press. The reason is that some widgets will not react on the click,
+		//       if they have been disabled during the press.
+		auto e = static_cast<QSinglePointEvent *>(ev);
+		auto w = static_cast<QWidget *>(obj);
+		if (!w->isEnabled() && e->isBeginEvent()) {
+			auto button = w->property(property).value<QRadioButton*>();
+			if (button && button->isEnabled()) button->setChecked(true);
+		}
+	}
+	return false;
+}
diff --git a/rkward/misc/rkradiogroup.h b/rkward/misc/rkradiogroup.h
new file mode 100644
index 000000000..bb36d7cb1
--- /dev/null
+++ b/rkward/misc/rkradiogroup.h
@@ -0,0 +1,41 @@
+/*
+rkradiogroup - This file is part of the RKWard project. Created: Mon Jul 24 2024
+SPDX-FileCopyrightText: 2024 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
+SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#ifndef RKRADIOGROUP_H
+#define RKRADIOGROUP_H
+
+#include <QGroupBox>
+#include <QButtonGroup>
+#include <QRadioButton>
+#include <QVBoxLayout>
+
+/** This class makes it easier to create a group of radio buttons in a standard layout */
+class RKRadioGroup : public QGroupBox {
+	Q_OBJECT
+public:
+	explicit RKRadioGroup(QWidget *parent=nullptr);
+	explicit RKRadioGroup(const QString &label, QWidget *parent=nullptr);
+	~RKRadioGroup() override;
+
+	QRadioButton* addButton(const QString &label, int id = -1);
+	QRadioButton* addButton(const QString &label, int id, QWidget* controlled, QBoxLayout::Direction dir=QBoxLayout::TopToBottom);
+	QRadioButton* addButton(const QString &label, QWidget* controlled, QBoxLayout::Direction dir=QBoxLayout::TopToBottom) {
+		return addButton(label, -1, controlled, dir);
+	}
+	/** Find the button with the given id, and set it as checked.
+	 *  @return false, if there is no such button, true otherwise. */
+	bool setButtonChecked(int id, bool checked);
+	QButtonGroup* group() const { return _group; };
+protected:
+	bool eventFilter(QObject *obj, QEvent *ev) override;
+private:
+	QButtonGroup *_group;
+	QBoxLayout *_layout;
+	static constexpr const char property[] = "_rkradiogroup_";
+};
+
+#endif
diff --git a/rkward/misc/rkspecialactions.cpp b/rkward/misc/rkspecialactions.cpp
index 419e0b350..7503f5763 100644
--- a/rkward/misc/rkspecialactions.cpp
+++ b/rkward/misc/rkspecialactions.cpp
@@ -45,9 +45,6 @@ void RKPasteSpecialAction::doSpecialPaste() {
 }
 
 #include <QCheckBox>
-#include <QRadioButton>
-#include <QButtonGroup>
-#include <QGroupBox>
 #include <QVBoxLayout>
 #include <QHBoxLayout>
 #include <QLineEdit>
@@ -62,6 +59,7 @@ void RKPasteSpecialAction::doSpecialPaste() {
 #include "rksaveobjectchooser.h"
 #include "../rbackend/rkrinterface.h"
 #include "../misc/rkprogresscontrol.h"
+#include "../misc/rkradiogroup.h"
 
 RKPasteSpecialDialog::RKPasteSpecialDialog(QWidget* parent, bool standalone) : QDialog(parent) {
 	RK_TRACE (MISC);
@@ -77,103 +75,64 @@ RKPasteSpecialDialog::RKPasteSpecialDialog(QWidget* parent, bool standalone) : Q
 	QHBoxLayout *rowlayout = new QHBoxLayout ();
 	pagelayout->addLayout (rowlayout);
 
-	QGroupBox* box;
-	QVBoxLayout* group_layout;
-	QHBoxLayout* h_layout;
-	QRadioButton* rbutton;
-
 	// Mode box
-	box = new QGroupBox (i18n ("Paste Mode"), this);
-	group_layout = new QVBoxLayout (box);
-	dimensionality_group = new QButtonGroup (box);
-	rbutton = new QRadioButton (i18n ("Single string"), box);
-	dimensionality_group->addButton (rbutton, DimSingleString);
-	group_layout->addWidget (rbutton);
-	rbutton = new QRadioButton (i18n ("Vector"), box);
-	dimensionality_group->addButton (rbutton, DimVector);
-	group_layout->addWidget (rbutton);
-	rbutton = new QRadioButton (i18n ("Matrix"), box);
-	dimensionality_group->addButton (rbutton, DimMatrix);
-	group_layout->addWidget (rbutton);
-	rbutton = new QRadioButton (i18n ("data.frame"), box);
-	dimensionality_group->addButton (rbutton, DimDataFrame);
-	rbutton->setChecked (true);
-	group_layout->addWidget (rbutton);
+	auto box = new RKRadioGroup(i18n("Paste Mode"));
+	dimensionality_group = box->group();
+	box->addButton(i18n("Single string"), DimSingleString);
+	box->addButton(i18n("Vector"), DimVector);
+	box->addButton(i18n("Matrix"), DimMatrix);
+	box->addButton(i18n("data.frame"), DimDataFrame)->setChecked(true);;
 	connect (dimensionality_group, &QButtonGroup::idClicked, this, &RKPasteSpecialDialog::updateState);
 	rowlayout->addWidget (box);
 
 	const QMimeData* clipdata = QApplication::clipboard ()->mimeData ();
 
 	// Separator box
-	box = new QGroupBox (i18n ("Field Separator"), this);
-	group_layout = new QVBoxLayout (box);
-	separator_group = new QButtonGroup (box);
-	rbutton = new QRadioButton (i18n ("Tab"), box);
-	separator_group->addButton (rbutton, SepTab);
-	group_layout->addWidget (rbutton);
-	rbutton->setChecked (true);		// tab-separated is a reasonable fallback guess
-	rbutton = new QRadioButton (i18n ("Comma"), box);
-	separator_group->addButton (rbutton, SepComma);
-	group_layout->addWidget (rbutton);
-	if (clipdata->hasFormat ("text/comma-separated-values")) rbutton->setChecked (true);
-	rbutton = new QRadioButton (i18n ("Single space"), box);
-	separator_group->addButton (rbutton, SepSpace);
-	group_layout->addWidget (rbutton);
-	rbutton = new QRadioButton (i18n ("Any whitespace"), box);
-	separator_group->addButton (rbutton, SepWhitespace);
-	group_layout->addWidget (rbutton);
-	h_layout = new QHBoxLayout ();
-	rbutton = new QRadioButton (i18n ("Other:"), box);
-	separator_group->addButton (rbutton, SepCustom);
-	h_layout->addWidget (rbutton);
-	separator_freefield = new QLineEdit (";", box);
-	h_layout->addWidget (separator_freefield);
-	group_layout->addLayout (h_layout);
-	connect (separator_group, &QButtonGroup::idClicked, this, &RKPasteSpecialDialog::updateState);
-	rowlayout->addWidget (box);
+	box = new RKRadioGroup(i18n("Field Separator"));
+	separator_group = box->group();
+	box->addButton(i18n("Tab"), SepTab)->setChecked(true); // tab-separated is a reasonable fallback guess
+	box->addButton(i18n("Comma"), SepComma)->setChecked(clipdata->hasFormat ("text/comma-separated-values"));
+	box->addButton(i18n("Single space"), SepSpace);
+	box->addButton(i18n("Any whitespace"), SepWhitespace);
+	separator_freefield = new QLineEdit(";", box);
+	box->addButton(i18n("Other:"), SepCustom, separator_freefield, QBoxLayout::LeftToRight);
+	connect(separator_group, &QButtonGroup::idClicked, this, &RKPasteSpecialDialog::updateState);
+	rowlayout->addWidget(box);
 
 	rowlayout = new QHBoxLayout;
 	pagelayout->addLayout (rowlayout);
 
 	// Quoting box
-	box = new QGroupBox (i18n ("Quoting"), this);
-	group_layout = new QVBoxLayout (box);
-	quoting_group = new QButtonGroup (box);
-	rbutton = new QRadioButton (i18n ("Do not quote values"), box);
-	quoting_group->addButton (rbutton, QuoteNone);
-	group_layout->addWidget (rbutton);
-	rbutton = new QRadioButton (i18n ("Automatic"), box);
-	rbutton->setChecked (true);
-	quoting_group->addButton (rbutton, QuoteAuto);
-	group_layout->addWidget (rbutton);
-	rbutton = new QRadioButton (i18n ("Quote all values"), box);
-	quoting_group->addButton (rbutton, QuoteAll);
-	group_layout->addWidget (rbutton);
-	connect (quoting_group, &QButtonGroup::idClicked, this, &RKPasteSpecialDialog::updateState);
-	rowlayout->addWidget (box);
+	box = new RKRadioGroup(i18n("Quoting"));
+	quoting_group = box->group();
+	box->addButton(i18n("Do not quote values"), QuoteNone);
+	box->addButton(i18n("Automatic"), QuoteAuto)->setChecked(true);
+	box->addButton(i18n("Quote all values"), QuoteAll);
+	connect(quoting_group, &QButtonGroup::idClicked, this, &RKPasteSpecialDialog::updateState);
+	rowlayout->addWidget(box);
 
 	// Labels
-	box = new QGroupBox(i18n("Labels"), this);
-	group_layout = new QVBoxLayout (box);
-	names_box = new QCheckBox(i18n("First row contains labels"), box);
+	auto gbox = new QGroupBox(i18n("Labels"));
+	auto group_layout = new QVBoxLayout(gbox);
+	names_box = new QCheckBox(i18n("First row contains labels"));
 	group_layout->addWidget(names_box);
-	rownames_box = new QCheckBox(i18n("First column contains labels"), box);
+	rownames_box = new QCheckBox(i18n("First column contains labels"));
 	group_layout->addWidget(rownames_box);
-	rowlayout->addWidget(box);
+	rowlayout->addWidget(gbox);
 
 	// further controls
-	box = new QGroupBox (i18n ("Transformations"), this);
-	group_layout = new QVBoxLayout (box);
-	reverse_h_box = new QCheckBox (i18n ("Reverse horizontally"), box);
-	group_layout->addWidget (reverse_h_box);
-	reverse_v_box = new QCheckBox (i18n ("Reverse vertically"), box);
-	group_layout->addWidget (reverse_v_box);
-	transpose_box = new QCheckBox (i18n ("Flip rows/columns"), box);
-	group_layout->addWidget (transpose_box);
-	insert_nas_box = new QCheckBox (i18n ("Insert NAs where needed"), box);
-	insert_nas_box->setChecked (true);
-	group_layout->addWidget (insert_nas_box);
-	rowlayout->addWidget (box);
+	gbox = new QGroupBox(i18n("Transformations"));
+	group_layout = new QVBoxLayout(gbox);
+	reverse_h_box = new QCheckBox(i18n("Reverse horizontally"));
+	group_layout->addWidget(reverse_h_box);
+	reverse_v_box = new QCheckBox(i18n("Reverse vertically"));
+	group_layout->addWidget(reverse_v_box);
+	transpose_box = new QCheckBox(i18n("Flip rows/columns"));
+	group_layout->addWidget(transpose_box);
+	insert_nas_box = new QCheckBox(i18n("Insert NAs where needed"));
+	insert_nas_box->setChecked(true);
+	group_layout->addWidget(insert_nas_box);
+	rowlayout->addWidget(box);
 
 	QDialogButtonBox *buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
 	ok_button = buttons->button(QDialogButtonBox::Ok);
@@ -201,7 +160,6 @@ void RKPasteSpecialDialog::updateState () {
 	names_box->setEnabled(dimensionality == DimDataFrame);
 	rownames_box->setEnabled(dimensionality == DimDataFrame);
 
-	separator_freefield->setEnabled ((dimensionality != DimSingleString) && (separator_group->checkedId () == SepCustom));
 	ok_button->setEnabled((objectname == nullptr) || objectname->isOk());
 }
 
diff --git a/rkward/plugin/rkradio.cpp b/rkward/plugin/rkradio.cpp
index a49783283..5d0c1a963 100644
--- a/rkward/plugin/rkradio.cpp
+++ b/rkward/plugin/rkradio.cpp
@@ -1,6 +1,6 @@
 /*
 rkradio.cpp - This file is part of RKWard (https://rkward.kde.org). Created: Thu Nov 7 2002
-SPDX-FileCopyrightText: 2002-2014 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileCopyrightText: 2002-2024 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
 SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
 SPDX-License-Identifier: GPL-2.0-or-later
 */
@@ -9,14 +9,13 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
 #include <qdom.h>
 #include <qlabel.h>
-#include <QButtonGroup>
-#include <QGroupBox>
-#include <qradiobutton.h>
 #include <QVBoxLayout>
 
 #include <KLocalizedString>
 
 #include "../misc/xmlhelper.h"
+#include "../misc/rkradiogroup.h"
+
 #include "../debug.h"
 
 RKRadio::RKRadio (const QDomElement &element, RKComponent *parent_component, QWidget *parent_widget) : RKAbstractOptionSelector (parent_component, parent_widget) {
@@ -30,14 +29,12 @@ RKRadio::RKRadio (const QDomElement &element, RKComponent *parent_component, QWi
 	vbox->setContentsMargins (0, 0, 0, 0);
 
 	// create ButtonGroup
-	group = new QButtonGroup (this);
-	group_box = new QGroupBox (xml->i18nStringAttribute (element, "label", i18n ("Select one:"), DL_INFO), this);
-	new QVBoxLayout (group_box);
+	group_box = new RKRadioGroup(xml->i18nStringAttribute(element, "label", i18n("Select one:"), DL_INFO));
 
 	addOptionsAndInit (element);
 
 	vbox->addWidget (group_box);
-	connect (group, &QButtonGroup::idClicked, this, &RKRadio::itemSelected);
+	connect(group_box->group(), &QButtonGroup::idClicked, this, &RKRadio::itemSelected);
 }
 
 RKRadio::~RKRadio(){
@@ -47,22 +44,20 @@ RKRadio::~RKRadio(){
 void RKRadio::setItemInGUI (int id) {
 	RK_TRACE (PLUGIN);
 
-	QAbstractButton *button = group->button (id);
+	QAbstractButton *button = group_box->group()->button(id);
 	if (button) button->setChecked (true);
 }
 
 void RKRadio::addOptionToGUI (const QString &label, int id) {
 	RK_TRACE (PLUGIN);
 
-	QRadioButton *button = new QRadioButton (label, group_box);
-	group->addButton (button, id);
-	group_box->layout ()->addWidget (button);
+	group_box->addButton(label, id);
 }
 
 void RKRadio::setItemEnabledInGUI (int id, bool enabled) {
 	RK_TRACE (PLUGIN);
 
-	QAbstractButton *button = group->button (id);
+	QAbstractButton *button = group_box->group()->button(id);
 	RK_ASSERT (button);
 	button->setEnabled (enabled);
 }
@@ -70,8 +65,8 @@ void RKRadio::setItemEnabledInGUI (int id, bool enabled) {
 QStringList RKRadio::getUiLabelPair () const {
 	RK_TRACE (PLUGIN);
 
-	QStringList ret (stripAccelerators (group_box->title ()));
-	ret.append (stripAccelerators (group->checkedButton ()->text ()));
+	QStringList ret(stripAccelerators(group_box->title()));
+	ret.append(stripAccelerators(group_box->group()->checkedButton()->text()));
 	return ret;
 }
 
diff --git a/rkward/plugin/rkradio.h b/rkward/plugin/rkradio.h
index 379e48be1..d9a2817b2 100644
--- a/rkward/plugin/rkradio.h
+++ b/rkward/plugin/rkradio.h
@@ -1,6 +1,6 @@
 /*
 rkradio.h - This file is part of the RKWard project. Created: Thu Nov 7 2002
-SPDX-FileCopyrightText: 2002-2014 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileCopyrightText: 2002-2024 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
 SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
 SPDX-License-Identifier: GPL-2.0-or-later
 */
@@ -10,8 +10,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "rkabstractoptionselector.h"
 
-class QButtonGroup;
-class QGroupBox;
+class RKRadioGroup;
 
 /** This RKPluginWidget provides a group of radio-buttons for use in plugins.
 @author Thomas Friedrichsmeier
@@ -28,8 +27,7 @@ protected:
 	void setItemEnabledInGUI (int id, bool enabled) override;
 	QStringList getUiLabelPair () const override;
 private:
-	QButtonGroup* group;
-	QGroupBox* group_box;
+	RKRadioGroup* group_box;
 };
 
 #endif
diff --git a/rkward/settings/rksettingsmodulegeneral.cpp b/rkward/settings/rksettingsmodulegeneral.cpp
index 8db0eda97..ecf517993 100644
--- a/rkward/settings/rksettingsmodulegeneral.cpp
+++ b/rkward/settings/rksettingsmodulegeneral.cpp
@@ -14,9 +14,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include <qdir.h>
 #include <qcombobox.h>
 #include <qcheckbox.h>
-#include <qbuttongroup.h>
-#include <qgroupbox.h>
-#include <qradiobutton.h>
 #include <QVBoxLayout>
 
 #include "../misc/getfilenamewidget.h"
@@ -25,6 +22,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include "../misc/rkcommandlineargs.h"
 #include "../misc/rkstandardicons.h"
 #include "../misc/rkstyle.h"
+#include "../misc/rkradiogroup.h"
 #include "rksettings.h"
 
 #include "../version.h"
@@ -104,23 +102,14 @@ public:
 
 		main_vbox->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("The workplace layout (i.e. which script-, data-, help-windows are open) may be saved (and loaded) per R workspace, or independent of the R workspace. Which do you prefer?")));
 
-		workplace_save_chooser = new QButtonGroup(this);
-		group_box = new QGroupBox(this);
-		QVBoxLayout *group_layout = new QVBoxLayout(group_box);
-
-		QAbstractButton* button;
-		button = new QRadioButton(i18n("Save/restore with R workspace, when saving/loading R workspace"), group_box);
-		group_layout->addWidget(button);
-		workplace_save_chooser->addButton(button, RKSettingsModuleGeneral::SaveWorkplaceWithWorkspace);
-		button = new QRadioButton(i18n("Save/restore independent of R workspace (save at end of RKWard session, restore at next start)"), group_box);
-		group_layout->addWidget(button);
-		workplace_save_chooser->addButton(button, RKSettingsModuleGeneral::SaveWorkplaceWithSession);
-		button = new QRadioButton(i18n("Do not save/restore workplace layout"), group_box);
-		group_layout->addWidget(button);
-		workplace_save_chooser->addButton(button, RKSettingsModuleGeneral::DontSaveWorkplace);	
-		if ((button = workplace_save_chooser->button(RKSettingsModuleGeneral::workplace_save_mode))) button->setChecked(true);
+		auto radio_box = new RKRadioGroup();
+		workplace_save_chooser = radio_box->group();
+		radio_box->addButton(i18n("Save/restore with R workspace, when saving/loading R workspace"), RKSettingsModuleGeneral::SaveWorkplaceWithWorkspace);
+		radio_box->addButton(i18n("Save/restore independent of R workspace (save at end of RKWard session, restore at next start)"), RKSettingsModuleGeneral::SaveWorkplaceWithSession);
+		radio_box->addButton(i18n("Do not save/restore workplace layout"), RKSettingsModuleGeneral::DontSaveWorkplace);
+		radio_box->setButtonChecked(RKSettingsModuleGeneral::workplace_save_mode, true);
 		connect(workplace_save_chooser, &QButtonGroup::idClicked, this, &RKSettingsPageGeneral::change);
-		main_vbox->addWidget(group_box);
+		main_vbox->addWidget(radio_box);
 
 		main_vbox->addSpacing(2*RKStyle::spacingHint());
 
diff --git a/rkward/settings/rksettingsmodulegraphics.cpp b/rkward/settings/rksettingsmodulegraphics.cpp
index a9027c920..99ab66645 100644
--- a/rkward/settings/rksettingsmodulegraphics.cpp
+++ b/rkward/settings/rksettingsmodulegraphics.cpp
@@ -12,12 +12,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
 #include <qlayout.h>
 #include <qlabel.h>
-#include <QGroupBox>
 #include <qcheckbox.h>
 #include <QVBoxLayout>
-#include <QButtonGroup>
 #include <QLineEdit>
-#include <QRadioButton>
 #include <QSpinBox>
 
 #include "../misc/rkstyle.h"
@@ -25,6 +22,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include "../misc/rkspinbox.h"
 #include "../misc/rkcommonfunctions.h"
 #include "../misc/rkstandardicons.h"
+#include "../misc/rkradiogroup.h"
 #include "../core/robject.h"
 #include "rksettings.h"
 #include "../debug.h"
@@ -53,52 +51,30 @@ public:
 
 		QHBoxLayout *h_layout1 = new QHBoxLayout();
 		main_vbox->addLayout(h_layout1);
-		QGroupBox *group = new QGroupBox(i18n("Default graphics device"), this);
-		default_device_group = new QButtonGroup(this);
-		QVBoxLayout* group_layout = new QVBoxLayout(group);
-		QRadioButton *button = new QRadioButton(i18n("RKWard native device"), group);
-		default_device_group->addButton(button, (int) RKSettingsModuleGraphics::RKDevice);
-		group_layout->addWidget(button);
-		button = new QRadioButton(i18n("Platform default device"), group);
-		default_device_group->addButton(button, (int) RKSettingsModuleGraphics::PlatformDevice);
-		group_layout->addWidget(button);
-		auto other_device_button = new QRadioButton(i18n("Other device:"), group);
-		default_device_group->addButton(other_device_button, (int) RKSettingsModuleGraphics::OtherDevice);
-		QHBoxLayout *h_layout = new QHBoxLayout();
-		group_layout->addLayout(h_layout);
-		h_layout->addWidget(other_device_button);
+		auto group = new RKRadioGroup(i18n("Default graphics device"));
+		default_device_group =group->group();
+		group->addButton(i18n("RKWard native device"), (int) RKSettingsModuleGraphics::RKDevice);
+		group->addButton(i18n("Platform default device"), (int) RKSettingsModuleGraphics::PlatformDevice);
 		auto default_device_other_edit = RKSettingsModuleGraphics::default_device_other.makeLineEdit(this);
-		h_layout->addWidget(default_device_other_edit);
 		RKCommonFunctions::setTips(i18n("<p>The default device to be used for plotting, i.e. when new plot is created, while no graphics device is active (see <i>options(\"device\")</i>).</p>"
 		                                "<p>The RKWard native device is the recommended choice for most users. This corresponds to the R command <i>RK()</i>.</p>"
 		                                "<p>The 'Platform default device' corresponds to one of <i>X11()</i>, <i>windows()</i>, or <i>quartz()</i>, depending on the platform.</p>"
 		                                "<p>You can also specify the name of a function such as <i>Cairo</i>.</p>"), group);
+		group->addButton(i18n("Other device:"), (int) RKSettingsModuleGraphics::OtherDevice, default_device_other_edit, QBoxLayout::LeftToRight);
 		connect(default_device_group, &QButtonGroup::idClicked, this, &RKSettingsPageGraphics::boxChanged);
-		connect(default_device_group, &QButtonGroup::idToggled, default_device_other_edit, [default_device_other_edit](int id) {
-			default_device_other_edit->setEnabled(id == (int) RKSettingsModuleGraphics::OtherDevice);
-		});
-		default_device_other_edit->setEnabled(false);
-		auto dbutton = default_device_group->button((int) RKSettingsModuleGraphics::default_device);
-		if (dbutton) dbutton->setChecked(true);
+		group->setButtonChecked((int) RKSettingsModuleGraphics::default_device, true);
 		h_layout1->addWidget(group);
 
-		group = new QGroupBox(i18n("Integration of R standard devices"), this);
-		replace_standard_devices_group = new QButtonGroup(this);
-		group_layout = new QVBoxLayout(group);
-		button = new QRadioButton(i18n("Replace with RKWard device"), group);
-		replace_standard_devices_group->addButton(button, (int) RKSettingsModuleGraphics::ReplaceDevice);
-		group_layout->addWidget(button);
-		button = new QRadioButton(i18n("Embed original device"), group);
-		replace_standard_devices_group->addButton(button, (int) RKSettingsModuleGraphics::EmbedDevice);
-		group_layout->addWidget(button);
+		group = new RKRadioGroup(i18n("Integration of R standard devices"));
+		replace_standard_devices_group = group->group();
+		group->addButton(i18n("Replace with RKWard device"), (int) RKSettingsModuleGraphics::ReplaceDevice);
 #ifdef Q_OS_MACOS
-		button->setEnabled(false);
+		group->addButton(i18n("Embed original device"), (int) RKSettingsModuleGraphics::EmbedDevice)->setEnabled(false);
+#else
+		group->addButton(i18n("Embed original device"), (int) RKSettingsModuleGraphics::EmbedDevice);
 #endif
-		button = new QRadioButton(i18n("No device integration"), group);
-		replace_standard_devices_group->addButton(button, (int) RKSettingsModuleGraphics::LeaveDevice);
-		group_layout->addWidget(button);
-		button = static_cast<QRadioButton*>(replace_standard_devices_group->button((int) RKSettingsModuleGraphics::replace_standard_devices));
-		if (button) button->setChecked(true);
+		group->addButton(i18n("No device integration"), (int) RKSettingsModuleGraphics::LeaveDevice);
+		group->setButtonChecked((int) RKSettingsModuleGraphics::replace_standard_devices, true);
 		RKCommonFunctions::setTips(i18n("<p>Many scripts use calls to platform specific standard devices (<i>X11()</i>, <i>windows()</i>, <i>quartz()</i>), although any on-screen device "
 		                                "could be used at these places. RKWard provides overloads for these standard device functions, which can change their behavior when used in "
 		                                "user code:</p>"
@@ -111,20 +87,20 @@ public:
 		connect(replace_standard_devices_group, &QButtonGroup::idClicked, this, &RKSettingsPageGraphics::boxChanged);
 		h_layout1->addWidget(group);
 
-		group = new QGroupBox(i18n("Default window size (for RK(), or embedded device windows)"));
-		group_layout = new QVBoxLayout(group);
+		auto sgroup = new QGroupBox(i18n("Default window size (for RK(), or embedded device windows)"));
+		auto group_layout = new QVBoxLayout(sgroup);
 		group_layout->addWidget(new QLabel(i18n("Default width (inches):")));
 		group_layout->addWidget(RKSettingsModuleGraphics::graphics_width.makeSpinBox(1, 100.0, this));
 		group_layout->addSpacing(2*RKStyle::spacingHint());
 		group_layout->addWidget(new QLabel(i18n("Default height (inches)")));
 		group_layout->addWidget(RKSettingsModuleGraphics::graphics_height.makeSpinBox(1, 100.0, this));
-		main_vbox->addWidget(group);
+		main_vbox->addWidget(sgroup);
 
 		main_vbox->addWidget(RKSettingsModuleGraphics::options_kde_printing.makeCheckbox(i18n("Use KDE printer dialog for printing devices (if available)"), this));
 
 		auto graphics_hist_box = RKSettingsModuleGraphics::graphics_hist_enable.makeCheckableGroupBox(i18n("Screen device history"), this);
 		group_layout = new QVBoxLayout(graphics_hist_box);
-		h_layout = new QHBoxLayout();
+		auto h_layout = new QHBoxLayout();
 		group_layout->addLayout(h_layout);
 		h_layout->addWidget(new QLabel(i18n("Maximum number of recorded plots:")));
 		h_layout->addWidget(RKSettingsModuleGraphics::graphics_hist_max_length.makeSpinBox(1, 200, this));
diff --git a/rkward/settings/rksettingsmoduleplugins.cpp b/rkward/settings/rksettingsmoduleplugins.cpp
index eb3d9e05d..5c3022fd7 100644
--- a/rkward/settings/rksettingsmoduleplugins.cpp
+++ b/rkward/settings/rksettingsmoduleplugins.cpp
@@ -13,9 +13,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
 #include <qlayout.h>
 #include <qlabel.h>
-#include <qbuttongroup.h>
-#include <qradiobutton.h>
-#include <qgroupbox.h>
 #include <qcheckbox.h>
 #include <QVBoxLayout>
 #include <QPushButton>
@@ -28,6 +25,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include "../misc/rkcommonfunctions.h"
 #include "../misc/rkspinbox.h"
 #include "../misc/xmlhelper.h"
+#include "../misc/rkradiogroup.h"
 #include "../windows/rkworkplace.h"
 #include "../plugin/rkcomponentmap.h"
 #include "../dialogs/rkloadlibsdialog.h"
@@ -58,22 +56,12 @@ public:
 		main_vbox->addSpacing(2*RKStyle::spacingHint());
 		main_vbox->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("Some plugins are available with both, a wizard-like interface and a traditional dialog interface. If both are available, which mode of presentation do you prefer?")));
 
-		QGroupBox* button_box = new QGroupBox(this);
-		QVBoxLayout* group_layout = new QVBoxLayout(button_box);
-		button_group = new QButtonGroup(button_box);
-
-		QAbstractButton* button;
-		button = new QRadioButton(i18n("Always prefer dialogs"), button_box);
-		group_layout->addWidget(button);
-		button_group->addButton(button, RKSettingsModulePlugins::PreferDialog);
-		button = new QRadioButton(i18n("Prefer recommended interface"), button_box);
-		group_layout->addWidget(button);
-		button_group->addButton(button, RKSettingsModulePlugins::PreferRecommended);
-		button = new QRadioButton(i18n("Always prefer wizards"), button_box);
-		group_layout->addWidget(button);
-		button_group->addButton(button, RKSettingsModulePlugins::PreferWizard);
-		if ((button = button_group->button(RKSettingsModulePlugins::interface_pref))) button->setChecked(true);
-
+		auto button_box = new RKRadioGroup();
+		button_group = button_box->group();
+		button_box->addButton(i18n("Always prefer dialogs"), RKSettingsModulePlugins::PreferDialog);
+		button_box->addButton(i18n("Prefer recommended interface"), RKSettingsModulePlugins::PreferRecommended);
+		button_box->addButton(i18n("Always prefer wizards"), RKSettingsModulePlugins::PreferWizard);
+		button_box->setButtonChecked(RKSettingsModulePlugins::interface_pref, true);
 		connect(button_group, &QButtonGroup::idClicked, this, &RKSettingsPagePlugins::change);
 		main_vbox->addWidget(button_box);
 
diff --git a/rkward/settings/rksettingsmoduler.cpp b/rkward/settings/rksettingsmoduler.cpp
index 2d8d7c60b..783190a6f 100755
--- a/rkward/settings/rksettingsmoduler.cpp
+++ b/rkward/settings/rksettingsmoduler.cpp
@@ -560,9 +560,6 @@ void RKSettingsModuleRPackages::syncConfig(KConfig *config, RKConfigBase::Config
 	}
 }
 
-#include <QGroupBox>
-#include <QRadioButton>
-
 void RKSettingsModuleRPackages::validateSettingsInteractive (QList<RKSetupWizardItem*>* items) {
 	RK_TRACE (SETTINGS);
 


More information about the rkward-tracker mailing list