src/configDialog.cpp

Sat, 03 Aug 2013 04:44:30 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Sat, 03 Aug 2013 04:44:30 +0300
changeset 434
b7a73ef3d4de
parent 431
ec1e2059319b
child 452
47cc663e4ff4
permissions
-rw-r--r--

now with changelog entry

/*
 *  LDForge: LDraw parts authoring CAD
 *  Copyright (C) 2013 Santeri 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 "common.h"
#include "configDialog.h"
#include "file.h"
#include "config.h"
#include "misc.h"
#include "colors.h"
#include "colorSelectDialog.h"
#include "gldraw.h"
#include "ui_config.h"

extern_cfg (str, gl_bgcolor);
extern_cfg (str, gl_maincolor);
extern_cfg (bool, lv_colorize);
extern_cfg (bool, gl_colorbfc);
extern_cfg (float, gl_maincolor_alpha);
extern_cfg (int, gl_linethickness);
extern_cfg (str, gui_colortoolbar);
extern_cfg (bool, edit_schemanticinline);
extern_cfg (bool, gl_blackedges);
extern_cfg (bool, gui_implicitfiles);
extern_cfg (str, net_downloadpath);
extern_cfg (bool, net_guesspaths);
extern_cfg (bool, net_autoclose);

extern_cfg (str, prog_ytruder);
extern_cfg (str, prog_rectifier);
extern_cfg (str, prog_intersector);
extern_cfg (str, prog_coverer);
extern_cfg (str, prog_isecalc);
extern_cfg (str, prog_edger2);
extern_cfg (bool, prog_ytruder_wine);
extern_cfg (bool, prog_rectifier_wine);
extern_cfg (bool, prog_intersector_wine);
extern_cfg (bool, prog_coverer_wine);
extern_cfg (bool, prog_isecalc_wine);
extern_cfg (bool, prog_edger2_wine);

#define act(N) extern_cfg (keyseq, key_##N);
#include "actions.h"

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
ConfigDialog::ConfigDialog (ForgeWindow* parent) : QDialog (parent) {
	ui = new Ui_ConfigUI;
	ui->setupUi (this);
	
	initMainTab();
	initShortcutsTab();
	initQuickColorTab();
	initGridTab();
	initExtProgTab();
	
	ui->downloadPath->setText (net_downloadpath);
	ui->guessNetPaths->setChecked (net_guesspaths);
	ui->autoCloseNetPrompt->setChecked (net_autoclose);
	connect (ui->findDownloadPath, SIGNAL (clicked(bool)), this, SLOT (slot_findDownloadFolder()));
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
ConfigDialog::~ConfigDialog() {
	delete ui;
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::initMainTab() {
	// Init color stuff
	setButtonBackground (ui->backgroundColorButton, gl_bgcolor);
	connect (ui->backgroundColorButton, SIGNAL (clicked()),
		this, SLOT (slot_setGLBackground()));
	
	setButtonBackground (ui->mainColorButton, gl_maincolor.value);
	connect (ui->mainColorButton, SIGNAL (clicked()),
		this, SLOT (slot_setGLForeground()));
	
	// Sliders
	ui->mainColorAlpha->setValue (gl_maincolor_alpha * 10.0f);
	ui->lineThickness->setValue (gl_linethickness);
	
	// Checkboxes
	ui->colorizeObjects->setChecked (lv_colorize);
	ui->colorBFC->setChecked (gl_colorbfc);
	ui->blackEdges->setChecked (gl_blackedges);
	// ui->scemanticInlining->setChecked (edit_schemanticinline);
	ui->implicitFiles->setChecked (gui_implicitfiles);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::initShortcutsTab() {
	ulong i = 0;
	
#define act(N) addShortcut (key_##N, ACTION(N), i);
#include "actions.h"
	
	ui->shortcutsList->setSortingEnabled (true);
	ui->shortcutsList->sortItems();
	
	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()));
}

void ConfigDialog::addShortcut (keyseqconfig& cfg, QAction* act, ulong& i) {
	ShortcutListItem* item = new ShortcutListItem;
	item->setIcon (act->icon());
	item->setKeyConfig (&cfg);
	item->setAction (act);
	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 (i++, item);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::initQuickColorTab() {
	quickColors = quickColorsFromConfig();
	updateQuickColorList();
	
	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()));
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::initGridTab() {
	QGridLayout* gridlayout = new QGridLayout;
	QLabel* xlabel = new QLabel ("X"),
		*ylabel = new QLabel ("Y"),
		*zlabel = new QLabel ("Z"),
		*anglabel = new QLabel ("Angle");
	short i = 1;
	
	for (QLabel* label : initlist<QLabel*> ({ xlabel, ylabel, zlabel, anglabel })) {
		label->setAlignment (Qt::AlignCenter);
		gridlayout->addWidget (label, 0, i++);
	}
	
	for (int i = 0; i < g_NumGrids; ++i) {
		// Icon
		lb_gridIcons[i] = new QLabel;
		lb_gridIcons[i]->setPixmap (getIcon (fmt ("grid-%1", str (g_GridInfo[i].name).toLower())));
		
		// Text label
		lb_gridLabels[i] = new QLabel (fmt ("%1:", g_GridInfo[i].name));
		
		QHBoxLayout* labellayout = new QHBoxLayout;
		labellayout->addWidget (lb_gridIcons[i]);
		labellayout->addWidget (lb_gridLabels[i]);
		gridlayout->addLayout (labellayout, i + 1, 0);
		
		// Add the widgets
		for (int j = 0; j < 4; ++j) {
			dsb_gridData[i][j] = new QDoubleSpinBox;
			dsb_gridData[i][j]->setValue (g_GridInfo[i].confs[j]->value);
			gridlayout->addWidget (dsb_gridData[i][j], i + 1, j + 1);
		}
	}

	ui->grids->setLayout (gridlayout);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
static const struct extProgInfo {
	const str name, iconname;
	strconfig* const path;
	mutable QLineEdit* input;
	mutable QPushButton* setPathButton;
#ifndef _WIN32
	boolconfig* const wine;
	mutable QCheckBox* wineBox;
#endif // _WIN32
} g_extProgInfo[] = {
#ifndef _WIN32
# define EXTPROG(NAME, LOWNAME) { #NAME, #LOWNAME, &prog_##LOWNAME, null, null, &prog_##LOWNAME##_wine, null },
#else
# define EXTPROG(NAME, LOWNAME) { #NAME, #LOWNAME, &prog_##LOWNAME, null, null },
#endif
	EXTPROG (Ytruder, ytruder)
	EXTPROG (Rectifier, rectifier)
	EXTPROG (Intersector, intersector)
	EXTPROG (Isecalc, isecalc)
	EXTPROG (Coverer, coverer)
	EXTPROG (Edger2, edger2)
#undef EXTPROG
};

void ConfigDialog::initExtProgTab() {
	QGridLayout* pathsLayout = new QGridLayout;
	ulong row = 0;
	
	for (const extProgInfo & info : g_extProgInfo) {
		QLabel* icon = new QLabel,
		*progLabel = new QLabel (info.name);
		QLineEdit* input = new QLineEdit;
		QPushButton* setPathButton = new QPushButton;
		
		icon->setPixmap (getIcon (info.iconname));
		input->setText (info.path->value);
		setPathButton->setIcon (getIcon ("folder"));
		info.input = input;
		info.setPathButton = setPathButton;
		
		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);
		
#ifndef _WIN32
		QCheckBox* wineBox = new QCheckBox ("Wine");
		wineBox->setChecked (*info.wine);
		info.wineBox = wineBox;
		pathsLayout->addWidget (wineBox, row, 4);
#endif
		
		++row;
	}

	ui->extProgs->setLayout (pathsLayout);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::updateQuickColorList (LDQuickColor* sel) {
	for (QListWidgetItem* item : quickColorItems)
		delete item;
	
	quickColorItems.clear();
	
	// Init table items
	for (LDQuickColor& entry : quickColors) {
		QListWidgetItem* item = new QListWidgetItem;
		
		if (entry.isSeparator) {
			item->setText ("--------");
			item->setIcon (getIcon ("empty"));
		} else {
			LDColor* col = entry.col;
			
			if (col == null) {
				item->setText ("[[unknown color]]");
				item->setIcon (getIcon ("error"));
			} else {
				item->setText (col->name);
				item->setIcon (makeColorIcon (col, 16));
			}
		}
		
		ui->quickColorList->addItem (item);
		quickColorItems << item;
		
		if (sel && &entry == sel) {
			ui->quickColorList->setCurrentItem (item);
			ui->quickColorList->scrollToItem (item);
		}
	}
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::slot_setColor() {
	LDQuickColor* entry = null;
	QListWidgetItem* item = null;
	const bool isNew = static_cast<QPushButton*> (sender()) == ui->quickColor_add;
	
	if (isNew == false) {
		item = getSelectedQuickColor();
		
		if (!item)
			return;
		
		ulong i = getItemRow (item, quickColorItems);
		entry = &quickColors[i];
		
		if (entry->isSeparator == true)
			return; // don't color separators
	}
	
	short defval = entry ? entry->col->index : -1;
	short val;
	
	if (ColorSelector::getColor (val, defval, this) == false)
		return;
	
	if (entry)
		entry->col = getColor (val);
	else {
		LDQuickColor entry = {getColor (val), null, false};
		
		item = getSelectedQuickColor();
		ulong idx;
		
		if (item)
			idx = getItemRow (item, quickColorItems) + 1;
		else
			idx = quickColorItems.size();
		
		quickColors.insert (idx, entry);
		entry = quickColors[idx];
	}

	updateQuickColorList (entry);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::slot_delColor() {
	if (ui->quickColorList->selectedItems().size() == 0)
		return;
	
	QListWidgetItem* item = ui->quickColorList->selectedItems() [0];
	quickColors.erase (getItemRow (item, quickColorItems));
	updateQuickColorList();
}

// =============================================================================
void ConfigDialog::slot_moveColor() {
	const bool up = (static_cast<QPushButton*> (sender()) == ui->quickColor_moveUp);
	
	if (ui->quickColorList->selectedItems().size() == 0)
		return;
	
	QListWidgetItem* item = ui->quickColorList->selectedItems() [0];
	int idx = getItemRow (item, quickColorItems);
	int dest = up ? (idx - 1) : (idx + 1);
	
	if (dest < 0 || (ulong) dest >= quickColorItems.size())
		return; // destination out of bounds
	
	LDQuickColor tmp = quickColors[dest];
	quickColors[dest] = quickColors[idx];
	quickColors[idx] = tmp;
	
	updateQuickColorList (&quickColors[dest]);
}

// =============================================================================
void ConfigDialog::slot_addColorSeparator() {
	quickColors << LDQuickColor ({null, null, true});
	updateQuickColorList (&quickColors[quickColors.size() - 1]);
}

// =============================================================================
void ConfigDialog::slot_clearColors() {
	quickColors.clear();
	updateQuickColorList();
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::pickColor (strconfig& conf, QPushButton* button) {
	QColor col = QColorDialog::getColor (QColor (conf));
	
	if (col.isValid()) {
		uchar r = col.red(),
			  g = col.green(),
			  b = col.blue();
		conf.value.sprintf ("#%.2X%.2X%.2X", r, g, b);
		setButtonBackground (button, conf.value);
	}
}

void ConfigDialog::slot_setGLBackground() {
	pickColor (gl_bgcolor, ui->backgroundColorButton);
}

void ConfigDialog::slot_setGLForeground() {
	pickColor (gl_maincolor, ui->mainColorButton);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::setButtonBackground (QPushButton* button, str value) {
	button->setIcon (getIcon ("colorselect"));
	button->setAutoFillBackground (true);
	button->setStyleSheet (fmt ("background-color: %1", value));
}

// =============================================================================
int ConfigDialog::getItemRow (QListWidgetItem* item, List<QListWidgetItem*>& haystack) {
	int i = 0;
	
	for (QListWidgetItem* it : haystack) {
		if (it == item)
			return i;
		
		++i;
	}
	return -1;
}

// =============================================================================
QListWidgetItem* ConfigDialog::getSelectedQuickColor() {
	if (ui->quickColorList->selectedItems().size() == 0)
		return null;
	
	return ui->quickColorList->selectedItems() [0];
}

// =============================================================================
QList<ShortcutListItem*> ConfigDialog::getShortcutSelection()
{
	QList<ShortcutListItem*> out;
	
	for (QListWidgetItem* entry : ui->shortcutsList->selectedItems())
		out << static_cast<ShortcutListItem*> (entry);
	
	return out;
}

// =============================================================================
void ConfigDialog::slot_setShortcut()
{
	QList<ShortcutListItem*> sel = getShortcutSelection();
	
	if (sel.size() < 1)
		return;
	
	ShortcutListItem* item = sel[0];
	
	if (KeySequenceDialog::staticDialog (item->keyConfig(), this))
		setShortcutText (item);
}

// =============================================================================
void ConfigDialog::slot_resetShortcut()
{
	QList<ShortcutListItem*> sel = getShortcutSelection();
	
	for (ShortcutListItem* item : sel) {
		item->keyConfig()->reset();
		setShortcutText (item);
	}
}

// =============================================================================
void ConfigDialog::slot_clearShortcut() {
	QList<ShortcutListItem*> sel = getShortcutSelection();
	
	for (ShortcutListItem* item : sel) {
		item->keyConfig()->value = QKeySequence();
		setShortcutText (item);
	}
}

// =============================================================================
void ConfigDialog::slot_setExtProgPath() {
	const extProgInfo* info = null;
	
	for (const extProgInfo& it : g_extProgInfo) {
		if (it.setPathButton == sender()) {
			info = &it;
			break;
		}
	}
	
	assert (info != null);
	
	str filter;
#ifdef _WIN32
	filter = "Applications (*.exe)(*.exe);;All files (*.*)(*.*)";
#endif // WIN32
	
	str fpath = QFileDialog::getOpenFileName (this, fmt ("Path to %1", info->name), *info->path, filter);

	if (fpath.length() == 0)
		return;

	info->input->setText (fpath);
}

// =============================================================================
// -----------------------------------------------------------------------------
void ConfigDialog::slot_findDownloadFolder() {
	str dpath = QFileDialog::getExistingDirectory();
	ui->downloadPath->setText (dpath);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::setShortcutText (ShortcutListItem* item) {
	QAction* act = item->action();
	str label = act->iconText();
	str keybind = item->keyConfig()->value.toString();
	item->setText (fmt ("%1 (%2)", label, keybind));
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
str ConfigDialog::quickColorString() {
	str val;
	
	for (LDQuickColor entry : quickColors) {
		if (val.length() > 0)
			val += ':';
		
		if (entry.isSeparator)
			val += '|';
		else
			val += fmt ("%1", entry.col->index);
	}
	
	return val;
}

const Ui_ConfigUI* ConfigDialog::getUI() const {
	return ui;
}

float ConfigDialog::getGridValue (int i, int j) const {
	return dsb_gridData[i][j]->value();
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ConfigDialog::staticDialog() {
	ConfigDialog dlg (g_win);
	
	if (dlg.exec()) {
		// Apply configuration
		lv_colorize = dlg.getUI()->colorizeObjects->isChecked();
		gl_colorbfc = dlg.getUI()->colorBFC->isChecked();
		// edit_schemanticinline = dlg.getUI()->scemanticInlining->isChecked();
		gl_blackedges = dlg.getUI()->blackEdges->isChecked();
		gl_maincolor_alpha = ((double) dlg.getUI()->mainColorAlpha->value()) / 10.0f;
		gl_linethickness = dlg.getUI()->lineThickness->value();
		gui_implicitfiles = dlg.getUI()->implicitFiles->isChecked();
		net_downloadpath = dlg.getUI()->downloadPath->text();
		net_guesspaths = dlg.getUI()->guessNetPaths->isChecked();
		net_autoclose = dlg.getUI()->autoCloseNetPrompt->isChecked();
		
		if (net_downloadpath.value.right (1) != DIRSLASH)
			net_downloadpath += DIRSLASH;
		
		// Rebuild the quick color toolbar
		g_win->setQuickColors (dlg.quickColors);
		gui_colortoolbar = dlg.quickColorString();
		
		// Set the grid settings
		for (int i = 0; i < g_NumGrids; ++i)
			for (int j = 0; j < 4; ++j)
				g_GridInfo[i].confs[j]->value = dlg.getGridValue (i, j);
		
		// Apply key shortcuts
#define act(N) ACTION(N)->setShortcut (key_##N);
#include "actions.h"
		
		// Ext program settings
		for (const extProgInfo & info : g_extProgInfo) {
			*info.path = info.input->text();
			
#ifndef _WIN32
			*info.wine = info.wineBox->isChecked();
#endif // _WIN32
		}
		
		config::save();
		reloadAllSubfiles();
		g_win->R()->setBackground();
		g_win->fullRefresh();
		g_win->updateToolBars();
		g_win->updateFileList();
	}
}

// =========================================================================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =========================================================================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =========================================================================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =========================================================================================================================
KeySequenceDialog::KeySequenceDialog (QKeySequence seq, QWidget* parent, Qt::WindowFlags f) :
	QDialog (parent, f), seq (seq) {
	lb_output = new QLabel;
	IMPLEMENT_DIALOG_BUTTONS
	
	setWhatsThis ("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 (keyseqconfig* cfg, QWidget* parent) {
	KeySequenceDialog dlg (cfg->value, parent);
	
	if (dlg.exec() == false)
		return false;
	
	cfg->value = dlg.seq;
	return true;
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void KeySequenceDialog::updateOutput() {
	str shortcut = seq.toString();
	
	if (seq == QKeySequence())
		shortcut = "&lt;empty&gt;";
	
	str text = fmt ("<center><b>%1</b></center>", shortcut);
	lb_output->setText (text);
}

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

#include "build/moc_configDialog.cpp"

mercurial