src/dialogs/configdialog.cpp

Thu, 11 Jan 2018 15:09:44 +0200

author
Santeri Piippo
date
Thu, 11 Jan 2018 15:09:44 +0200
changeset 1231
ce0c9f2e6b9c
parent 1224
5a31b6d4bf81
permissions
-rw-r--r--

begin rendering rework

/*
 *  LDForge: LDraw parts authoring CAD
 *  Copyright (C) 2013 - 2018 Teemu Piippo
 *
 *  This program 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QGridLayout>
#include <QFileDialog>
#include <QColorDialog>
#include <QBoxLayout>
#include <QKeyEvent>
#include <QGroupBox>
#include <QDoubleSpinBox>
#include <QLineEdit>
#include <QCheckBox>
#include <QSettings>
#include <QPushButton>
#include "../main.h"
#include "../ldDocument.h"
#include "../miscallenous.h"
#include "../glRenderer.h"
#include "../guiutilities.h"
#include "../documentmanager.h"
#include "colorselector.h"
#include "configdialog.h"
#include "ui_configdialog.h"

const char* g_extProgPathFilter =
#ifdef _WIN32
	"Applications(*.exe)(*.exe);;"
#endif
	"All files(*.*)(*.*)";

ShortcutListItem::ShortcutListItem(QListWidget* view, int type) :
	QListWidgetItem(view, type) {}

QAction* ShortcutListItem::action() const
{
	return m_action;
}

void ShortcutListItem::setAction(QAction* action)
{
	m_action = action;
}

QKeySequence ShortcutListItem::sequence() const
{
	return m_sequence;
}

void ShortcutListItem::setSequence(const QKeySequence& sequence)
{
	m_sequence = sequence;
}

ConfigDialog::ConfigDialog(QWidget* parent, ConfigDialog::Tab defaulttab, Qt::WindowFlags f) :
	QDialog(parent, f),
	HierarchyElement(parent),
	ui(*new Ui_ConfigDialog),
	m_settings(makeSettings(this))
{
	ui.setupUi(this);

	// Set defaults
	applyToWidgetOptions(
	[&](QWidget* widget, QString confname)
	{
		QVariant value = m_settings->value(confname, config->defaultValueByName(confname));
		QLineEdit* le;
		QSpinBox* spinbox;
		QDoubleSpinBox* doublespinbox;
		QSlider* slider;
		QCheckBox* checkbox;
		QPushButton* button;

		if ((le = qobject_cast<QLineEdit*>(widget)))
		{
			le->setText(value.toString());
		}
		else if ((spinbox = qobject_cast<QSpinBox*>(widget)))
		{
			spinbox->setValue(value.toInt());
		}
		else if ((doublespinbox = qobject_cast<QDoubleSpinBox*>(widget)))
		{
			doublespinbox->setValue(value.toDouble());
		}
		else if ((slider = qobject_cast<QSlider*>(widget)))
		{
			slider->setValue(value.toInt());
		}
		else if ((checkbox = qobject_cast<QCheckBox*>(widget)))
		{
			checkbox->setChecked(value.toBool());
		}
		else if ((button = qobject_cast<QPushButton*>(widget)))
		{
			setButtonBackground(button, value.toString());
			connect(button, SIGNAL(clicked()), this, SLOT(setButtonColor()));
		}
		else
		{
			print("Unknown widget of type %1\n", widget->metaObject()->className());
		}
	});

	m_window->applyToActions([&](QAction* act)
	{
		addShortcut(act);
	});

	ui.shortcutsList->setSortingEnabled(true);
	ui.shortcutsList->sortItems();
	quickColors = LoadQuickColorList();
	updateQuickColorList();
	initExtProgs();
	selectPage(defaulttab);
	connect(ui.shortcut_set, SIGNAL(clicked()), this, SLOT(slot_setShortcut()));
	connect(ui.shortcut_reset, SIGNAL(clicked()), this, SLOT(slot_resetShortcut()));
	connect(ui.shortcut_clear, SIGNAL(clicked()), this, SLOT(slot_clearShortcut()));
	connect(ui.quickColor_add, SIGNAL(clicked()), this, SLOT(slot_setColor()));
	connect(ui.quickColor_remove, SIGNAL(clicked()), this, SLOT(slot_delColor()));
	connect(ui.quickColor_edit, SIGNAL(clicked()), this, SLOT(slot_setColor()));
	connect(ui.quickColor_addSep, SIGNAL(clicked()), this, SLOT(slot_addColorSeparator()));
	connect(ui.quickColor_moveUp, SIGNAL(clicked()), this, SLOT(slot_moveColor()));
	connect(ui.quickColor_moveDown, SIGNAL(clicked()), this, SLOT(slot_moveColor()));
	connect(ui.quickColor_clear, SIGNAL(clicked()), this, SLOT(slot_clearColors()));
	connect(ui.findDownloadPath, SIGNAL(clicked(bool)), this, SLOT(slot_findDownloadFolder()));
	connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton*)),
		this, SLOT(buttonClicked(QAbstractButton*)));
	connect(ui.m_pages, SIGNAL(currentChanged(int)), this, SLOT(selectPage(int)));
	connect(ui.m_pagelist, SIGNAL(currentRowChanged(int)), this, SLOT(selectPage(int)));
}

ConfigDialog::~ConfigDialog()
{
	delete &ui;
}

void ConfigDialog::selectPage(int row)
{
	ui.m_pagelist->setCurrentRow(row);
	ui.m_pages->setCurrentIndex(row);
}

//
// Adds a shortcut entry to the list of shortcuts.
//
void ConfigDialog::addShortcut(QAction* act)
{
	ShortcutListItem* item = new ShortcutListItem;
	item->setIcon(act->icon());
	item->setAction(act);
	item->setSequence(act->shortcut());
	setShortcutText(item);

	// If the action doesn't have a valid icon, use an empty one
	// so that the list is kept aligned.
	if (act->icon().isNull())
		item->setIcon(GetIcon("empty"));

	ui.shortcutsList->insertItem(ui.shortcutsList->count(), item);
}

//
// Initializes the stuff in the ext programs tab
//
void ConfigDialog::initExtProgs()
{
	QGridLayout* pathsLayout = new QGridLayout;
	int row = 0;

	for (int i = 0; i < NumExternalPrograms; ++i)
	{
		ExtProgramType program = (ExtProgramType) i;
		ExternalProgramWidgets& widgets = m_externalProgramWidgets[i];
		QString name = m_window->externalPrograms()->externalProgramName(program);
		QLabel* icon = new QLabel;
		QLabel* progLabel = new QLabel(name);
		QLineEdit* input = new QLineEdit;
		QPushButton* setPathButton = new QPushButton;

		icon->setPixmap(GetIcon(name.toLower()));
		input->setText(m_window->externalPrograms()->getPathSetting(program));
		setPathButton->setIcon(GetIcon("folder"));
		widgets.input = input;
		widgets.setPathButton = setPathButton;
		widgets.wineBox = nullptr;
		connect(setPathButton, SIGNAL(clicked()), this, SLOT(slot_setExtProgPath()));
		pathsLayout->addWidget(icon, row, 0);
		pathsLayout->addWidget(progLabel, row, 1);
		pathsLayout->addWidget(input, row, 2);
		pathsLayout->addWidget(setPathButton, row, 3);

#ifdef Q_OS_UNIX
		{
			QCheckBox* wineBox = new QCheckBox("Wine");
			wineBox->setChecked(m_window->externalPrograms()->programUsesWine(program));
			widgets.wineBox = wineBox;
			pathsLayout->addWidget(wineBox, row, 4);
		}
#endif
		++row;
	}
	ui.extProgs->setLayout(pathsLayout);
}

void ConfigDialog::applyToWidgetOptions(std::function<void(QWidget*, QString)> func)
{
	// Apply configuration
	for (QWidget* widget : findChildren<QWidget*>())
	{
		if (not widget->objectName().startsWith("config"))
			continue;

		QString optionname(widget->objectName().mid(strlen("config")));

		if (config->existsEntry(optionname))
			func(widget, optionname);
		else
			print("Couldn't find configuration entry named %1", optionname);
	}
}

//
// Set the settings based on widget data.
//
void ConfigDialog::applySettings()
{
	applyToWidgetOptions([&](QWidget* widget, QString confname)
	{
		QVariant value;
		QLineEdit* le;
		QSpinBox* spinbox;
		QDoubleSpinBox* doublespinbox;
		QSlider* slider;
		QCheckBox* checkbox;
		QPushButton* button;

		if ((le = qobject_cast<QLineEdit*>(widget)))
			value = le->text();
		else if ((spinbox = qobject_cast<QSpinBox*>(widget)))
			value = spinbox->value();
		else if ((doublespinbox = qobject_cast<QDoubleSpinBox*>(widget)))
			value = doublespinbox->value();
		else if ((slider = qobject_cast<QSlider*>(widget)))
			value = slider->value();
		else if ((checkbox = qobject_cast<QCheckBox*>(widget)))
			value = checkbox->isChecked();
		else if ((button = qobject_cast<QPushButton*>(widget)))
			value = m_buttonColors[button];
		else
		{
			print("Unknown widget of type %1\n", widget->metaObject()->className());
			return;
		}

		m_settings->setValue(confname, value);
	});

	// Rebuild the quick color toolbar
	m_window->setQuickColors(quickColors);
	config->setQuickColorToolbar(quickColorString());

	// Ext program settings
	for (int i = 0; i < NumExternalPrograms; ++i)
	{
		ExtProgramType program = (ExtProgramType) i;
		ExtProgramToolset* toolset = m_window->externalPrograms();
		ExternalProgramWidgets& widgets = m_externalProgramWidgets[i];
		toolset->getPathSetting(program) = widgets.input->text();

		if (widgets.wineBox)
			toolset->setWineSetting(program, widgets.wineBox->isChecked());
	}

	// Apply shortcuts
	for (int i = 0; i < ui.shortcutsList->count(); ++i)
	{
		auto item = static_cast<ShortcutListItem*>(ui.shortcutsList->item(i));
		item->action()->setShortcut(item->sequence());
	}

	m_window->syncSettings();
	currentDocument()->reloadAllSubfiles();
	m_documents->loadLogoedStuds();
	m_window->currentRenderer()->setBackground();
	m_window->doFullRefresh();
	m_window->updateDocumentList();
}

//
// A dialog button was clicked
//
void ConfigDialog::buttonClicked(QAbstractButton* button)
{
	QDialogButtonBox* dbb = ui.buttonBox;

	if (button == dbb->button(QDialogButtonBox::Ok))
	{
		applySettings();
		accept();
	}
	else if (button == dbb->button(QDialogButtonBox::Apply))
	{
		applySettings();
	}
	else if (button == dbb->button(QDialogButtonBox::Cancel))
	{
		reject();
	}
}

//
// Update the list of color toolbar items in the quick color tab.
//
void ConfigDialog::updateQuickColorList(ColorToolbarItem* sel)
{
	for (QListWidgetItem * item : quickColorItems)
		delete item;

	quickColorItems.clear();

	// Init table items
	for (ColorToolbarItem& entry : quickColors)
	{
		QListWidgetItem* item = new QListWidgetItem;

		if (entry.isSeparator())
		{
			item->setText("<hr />");
			item->setIcon(GetIcon("empty"));
		}
		else
		{
			LDColor color = entry.color();

			if (color.isValid())
			{
				item->setText(color.name());
				item->setIcon(guiUtilities()->makeColorIcon(color, 16));
			}
			else
			{
				item->setText("[[unknown color]]");
				item->setIcon(GetIcon("error"));
			}
		}

		ui.quickColorList->addItem(item);
		quickColorItems << item;

		if (sel and &entry == sel)
		{
			ui.quickColorList->setCurrentItem(item);
			ui.quickColorList->scrollToItem(item);
		}
	}
}

//
// Quick colors: add or edit button was clicked.
//
void ConfigDialog::slot_setColor()
{
	ColorToolbarItem* entry = nullptr;
	QListWidgetItem* item = nullptr;
	const bool isNew = static_cast<QPushButton*>(sender()) == ui.quickColor_add;

	if (not isNew)
	{
		item = getSelectedQuickColor();

		if (not item)
			return;

		int i = getItemRow(item, quickColorItems);
		entry = &quickColors[i];

		if (entry->isSeparator() == true)
			return; // don't color separators
	}

	LDColor defaultValue = entry ? entry->color() : LDColor::nullColor();
	LDColor value;

	if (not ColorSelector::selectColor(this, value, defaultValue))
		return;

	if (entry)
	{
		entry->setColor(value);
	}
	else
	{
		ColorToolbarItem newentry(value, nullptr);
		item = getSelectedQuickColor();
		int idx = (item) ? getItemRow(item, quickColorItems) + 1 : quickColorItems.size();
		quickColors.insert(idx, newentry);
		entry = &quickColors[idx];
	}

	updateQuickColorList(entry);
}

//
// Remove a quick color
//
void ConfigDialog::slot_delColor()
{
	if (ui.quickColorList->selectedItems().isEmpty())
		return;

	QListWidgetItem* item = ui.quickColorList->selectedItems() [0];
	quickColors.removeAt(getItemRow(item, quickColorItems));
	updateQuickColorList();
}

//
// Move a quick color up/down
//
void ConfigDialog::slot_moveColor()
{
	const bool up = (static_cast<QPushButton*>(sender()) == ui.quickColor_moveUp);

	if (ui.quickColorList->selectedItems().isEmpty())
		return;

	QListWidgetItem* item = ui.quickColorList->selectedItems() [0];
	int idx = getItemRow(item, quickColorItems);
	int dest = up ?(idx - 1) :(idx + 1);

	if (dest < 0 or dest >= quickColorItems.size())
		return; // destination out of bounds

	qSwap(quickColors[dest], quickColors[idx]);
	updateQuickColorList(&quickColors[dest]);
}

//
//
// Add a separator to quick colors
//
void ConfigDialog::slot_addColorSeparator()
{
	quickColors << ColorToolbarItem::makeSeparator();
	updateQuickColorList(&quickColors[quickColors.size() - 1]);
}

//
//
// Clear all quick colors
//
void ConfigDialog::slot_clearColors()
{
	quickColors.clear();
	updateQuickColorList();
}

//
//
void ConfigDialog::setButtonColor()
{
	QPushButton* button = qobject_cast<QPushButton*>(sender());

	if (button == nullptr)
	{
		print("setButtonColor: null sender!\n");
		return;
	}

	QColor color = QColorDialog::getColor(m_buttonColors[button]);

	if (color.isValid())
	{
		QString colorname;
		colorname.sprintf("#%.2X%.2X%.2X", color.red(), color.green(), color.blue());
		setButtonBackground(button, colorname);
	}
}

//
// Sets background color of a given button.
//
void ConfigDialog::setButtonBackground(QPushButton* button, QString value)
{
	button->setIcon(GetIcon("colorselect"));
	button->setAutoFillBackground(true);
	button->setStyleSheet(format("background-color: %1", value));
	m_buttonColors[button] = QColor(value);
}

//
// Finds the given list widget item in the list of widget items given.
//
int ConfigDialog::getItemRow(QListWidgetItem* item, QList<QListWidgetItem*>& haystack)
{
	int i = 0;

	for (QListWidgetItem* it : haystack)
	{
		if (it == item)
			return i;

		++i;
	}

	return -1;
}

//
// Which quick color is currently selected?
//
QListWidgetItem* ConfigDialog::getSelectedQuickColor()
{
	if (ui.quickColorList->selectedItems().isEmpty())
		return nullptr;

	return ui.quickColorList->selectedItems() [0];
}

//
// Get the list of shortcuts selected
//
QList<ShortcutListItem*> ConfigDialog::getShortcutSelection()
{
	QList<ShortcutListItem*> out;

	for (QListWidgetItem* entry : ui.shortcutsList->selectedItems())
		out << static_cast<ShortcutListItem*>(entry);

	return out;
}

//
// Edit the shortcut of a given action.
//
void ConfigDialog::slot_setShortcut()
{
	QList<ShortcutListItem*> sel = getShortcutSelection();

	if (sel.size() < 1)
		return;

	ShortcutListItem* item = sel[0];

	if (KeySequenceDialog::staticDialog(item, this))
		setShortcutText(item);
}

//
// Reset a shortcut to defaults
//
void ConfigDialog::slot_resetShortcut()
{
	QList<ShortcutListItem*> sel = getShortcutSelection();

	for (ShortcutListItem* item : sel)
	{
		item->setSequence(m_window->defaultShortcut(item->action()));
		setShortcutText(item);
	}
}

//
// Remove the shortcut of an action.
//
void ConfigDialog::slot_clearShortcut()
{
	QList<ShortcutListItem*> sel = getShortcutSelection();

	for (ShortcutListItem* item : sel)
	{
		item->setSequence(QKeySequence());
		setShortcutText(item);
	}
}

//
// Set the path of an external program
//
void ConfigDialog::slot_setExtProgPath()
{
	ExtProgramType program = NumExternalPrograms;

	for (int i = 0; i < NumExternalPrograms; ++i)
	{
		if (m_externalProgramWidgets[i].setPathButton == sender())
		{
			program = (ExtProgramType) i;
			break;
		}
	}

	if (program != NumExternalPrograms)
	{
		ExtProgramToolset* toolset = m_window->externalPrograms();
		ExternalProgramWidgets& widgets = m_externalProgramWidgets[program];
		QString filepath = QFileDialog::getOpenFileName(this,
			format("Path to %1", toolset->externalProgramName(program)),
			widgets.input->text(), g_extProgPathFilter);
	
		if (filepath.isEmpty())
			return;
	
		widgets.input->setText(filepath);
	}
}

//
// '...' button pressed for the download path
//
void ConfigDialog::slot_findDownloadFolder()
{
	QString dpath = QFileDialog::getExistingDirectory();

	if (not dpath.isEmpty())
		ui.configDownloadFilePath->setText(dpath);
}

//
//
// Updates the text string for a given shortcut list item
//
void ConfigDialog::setShortcutText(ShortcutListItem* item)
{
	QAction* act = item->action();
	QString label = act->iconText();
	QString keybind = item->sequence().toString();
	item->setText(format("%1(%2)", label, keybind));
}

//
// Gets the configuration string of the quick color toolbar
//
QString ConfigDialog::quickColorString()
{
	QString val;

	for (const ColorToolbarItem& entry : quickColors)
	{
		if (val.length() > 0)
			val += ':';

		if (entry.isSeparator())
			val += '|';
		else
			val += format("%1", entry.color().index());
	}

	return val;
}

//
//
KeySequenceDialog::KeySequenceDialog(QKeySequence seq, QWidget* parent, Qt::WindowFlags f) :
	QDialog(parent, f), seq(seq)
{
	lb_output = new QLabel;

	bbx_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); \
	connect(bbx_buttons, SIGNAL(accepted()), this, SLOT(accept())); \
	connect(bbx_buttons, SIGNAL(rejected()), this, SLOT(reject())); \

	setWhatsThis(tr("Into this dialog you can input a key sequence for use as a "
		"shortcut in LDForge. Use OK to confirm the new shortcut and Cancel to "
		"dismiss."));

	QVBoxLayout* layout = new QVBoxLayout;
	layout->addWidget(lb_output);
	layout->addWidget(bbx_buttons);
	setLayout(layout);

	updateOutput();
}

//
//
bool KeySequenceDialog::staticDialog(ShortcutListItem* item, QWidget* parent)
{
	KeySequenceDialog dlg(item->sequence(), parent);

	if (dlg.exec() == QDialog::Rejected)
		return false;

	item->setSequence(dlg.seq);
	return true;
}

//
//
void KeySequenceDialog::updateOutput()
{
	QString shortcut = seq.toString();

	if (seq == QKeySequence())
		shortcut = "&lt;empty&gt;";

	QString text = format("<center><b>%1</b></center>", shortcut);
	lb_output->setText(text);
}

//
//
void KeySequenceDialog::keyPressEvent(QKeyEvent* ev)
{
	seq = ev->key() + ev->modifiers();
	updateOutput();
}

mercurial