Pith - whittle
#include "core/ConfigStore.h"
#include "ui/SchemeDialogs.h"
#include <QAbstractButton>
#include <QDialogButtonBox>
#include <QFrame>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>

namespace whittle {
	namespace {

		constexpr char kHeadStyle[] = "color: #999999; font-size: 8pt;";
		constexpr char kRuleStyle[] = "color: #cccccc; background-color: #cccccc;";

		constexpr char kSheet[] =
		"QDialog { background: #F4F4F4; }"
		"QPushButton { border: 1px solid #c1c1c1; border-radius: 4px;"
		" padding: 0 12px; min-height: 24px; max-height: 24px;"
		" background: #F9F9F9; color: #000000; }"
		"QPushButton:hover { background: #F1F1F1; }"
		"QPushButton:pressed { background: #F4F4F4; }"
		"QLineEdit { border: 1px solid #c1c1c1; border-radius: 4px;"
		" padding: 6px; background: #FAFAFA; color: #222222; }"
		"QListWidget { border: 1px solid #c1c1c1; border-radius: 4px;"
		" background: #FAFAFA; color: #222222; outline: none; }"
		"QListWidget::item { padding: 3px 6px; border: none; }"
		"QListWidget::item:selected { padding: 3px 6px; border: none;"
		" background: #E5E5E5; color: #222222; }";

		constexpr int kHeadGap   = 4;
		constexpr int kBlockGap  = 8;
		constexpr int kActionGap = 6;

		QWidget* sectionHeader(const QString& title) {
			auto* w = new QWidget;
			auto* h = new QHBoxLayout(w);
			h->setContentsMargins(0, 0, 0, 0);
			h->setSpacing(8);

			auto* label = new QLabel(title);
			label->setStyleSheet(QLatin1String(kHeadStyle));
			label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
			h->addWidget(label);

			auto* hr = new QFrame;
			hr->setFrameShape(QFrame::HLine);
			hr->setFrameShadow(QFrame::Plain);
			hr->setStyleSheet(QLatin1String(kRuleStyle));
			hr->setFixedHeight(1);
			hr->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
			h->addWidget(hr, 1);
			return w;
		}

		QMessageBox::StandardButton askYesNo(QWidget* parent, const QString& title,
			const QString& text) {
			QMessageBox box(QMessageBox::Question, title, text,
				QMessageBox::Yes | QMessageBox::No, parent);
			box.setDefaultButton(QMessageBox::No);
			for (QAbstractButton* b : box.buttons()) b->setIcon(QIcon());
				box.exec();
			return box.standardButton(box.clickedButton());
		}

	}

	SaveSchemeDialog::SaveSchemeDialog(const QStringList& existing, bool allowSave,
		QWidget* parent)
	: QDialog(parent), m_existing(existing), m_allowSave(allowSave) {
		setWindowTitle(tr("Schemes"));
		setModal(true);
		resize(333, 280);
		setStyleSheet(QLatin1String(kSheet));

		auto* v = new QVBoxLayout(this);
		v->setContentsMargins(16, 14, 16, 14);
		v->setSpacing(0);

		m_edit = new QLineEdit;
		m_edit->setEnabled(allowSave);
		m_edit->setPlaceholderText(allowSave ? tr("Moniker")
			: tr("Nothing to save!"));
		v->addWidget(m_edit);
		v->addSpacing(kBlockGap);

		m_list = new QListWidget;
		m_list->addItems(existing);
		v->addWidget(m_list, 1);
		v->addSpacing(kActionGap);

		m_del = new QPushButton(tr("Delete"));
		m_del->setEnabled(false);

		auto* buttons = new QDialogButtonBox(
			QDialogButtonBox::Save | QDialogButtonBox::Cancel);
		for (QAbstractButton* b : buttons->buttons()) b->setIcon(QIcon());
			m_ok = buttons->button(QDialogButtonBox::Save);
		m_ok->setEnabled(false);

		int btnW = 0;
		for (QAbstractButton* b : buttons->buttons()) {
			btnW = qMax(btnW, b->sizeHint().width());
			if (auto* p = qobject_cast<QPushButton*>(b)) p->setAutoDefault(false);
		}
		m_del->setMinimumWidth(btnW);

		auto* buttonRow = new QHBoxLayout;
		buttonRow->setContentsMargins(0, 0, 0, 0);
		buttonRow->addWidget(m_del);
		buttonRow->addStretch();
		buttonRow->addWidget(buttons);
		v->addLayout(buttonRow);

		connect(m_edit, &QLineEdit::textChanged, this, &SaveSchemeDialog::validate);
		connect(m_list, &QListWidget::itemClicked, this, [this](QListWidgetItem* i) {
			if (m_allowSave && i) m_edit->setText(i->text());
		});
		connect(m_list, &QListWidget::currentRowChanged,
			this, [this](int row) { m_del->setEnabled(row >= 0); });
		connect(m_del, &QPushButton::clicked, this, &SaveSchemeDialog::onDelete);
		connect(buttons, &QDialogButtonBox::accepted, this, &SaveSchemeDialog::tryAccept);
		connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
	}

	void SaveSchemeDialog::validate() {
		m_ok->setEnabled(m_allowSave && !m_edit->text().trimmed().isEmpty());
	}

	void SaveSchemeDialog::onDelete() {
		auto* item = m_list->currentItem();
		if (!item) return;
		const QString name = item->text();

		const auto btn = askYesNo(this, tr("Delete"), tr("Delete \"%1\"?").arg(name));
		if (btn != QMessageBox::Yes) return;

		if (!ConfigStore::deleteScheme(name)) {
			QMessageBox::warning(this, tr("Delete"),
				tr("Could not delete \"%1\".").arg(name));
			return;
		}

		delete m_list->takeItem(m_list->row(item));
		m_existing.removeAll(name);
		m_listChanged = true;
		m_del->setEnabled(m_list->currentRow() >= 0);
	}

	void SaveSchemeDialog::tryAccept() {
		const QString name = schemeName();
		if (name.isEmpty()) return;
		if (m_existing.contains(name)) {
			const auto btn = askYesNo(this, tr("Over-Write?"),
				tr("\"%1\" already exists.").arg(name));
			if (btn != QMessageBox::Yes) return;
		}
		accept();
	}

	QString SaveSchemeDialog::schemeName() const {
		return m_edit->text().trimmed();
	}

}