[PATCH v6 09/11] kconfig: Add xconfig-modifications

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The tool can be called from any configurator. We chose to modify xconfig
for this purpose. These files contain the necessary modifications to
xconfig in order to resolve conflicts.

Co-developed-by: Patrick Franz <deltaone@xxxxxxxxxx>
Signed-off-by: Patrick Franz <deltaone@xxxxxxxxxx>
Co-developed-by: Ibrahim Fayaz <phayax@xxxxxxxxx>
Signed-off-by: Ibrahim Fayaz <phayax@xxxxxxxxx>
Reviewed-by: Luis Chamberlain <mcgrof@xxxxxxxxxx>
Tested-by: Evgeny Groshev <eugene.groshev@xxxxxxxxx>
Suggested-by: Sarah Nadi <nadi@xxxxxxxxxxx>
Suggested-by: Thorsten Berger <thorsten.berger@xxxxxx>
Signed-off-by: Thorsten Berger <thorsten.berger@xxxxxx>
Signed-off-by: Ole Schuerks <ole0811sch@xxxxxxxxx>
---
 scripts/kconfig/qconf.cc | 676 ++++++++++++++++++++++++++++++++++++++-
 scripts/kconfig/qconf.h  | 111 +++++++
 2 files changed, 785 insertions(+), 2 deletions(-)

diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
index 90f139480eda..e899fa573db4 100644
--- a/scripts/kconfig/qconf.cc
+++ b/scripts/kconfig/qconf.cc
@@ -19,21 +19,40 @@
 #include <QRegularExpression>
 #include <QScreen>
 #include <QToolBar>
+#include <QListWidget>
+#include <QComboBox>
+#include <QTableWidget>
+#include <QHBoxLayout>
+#include <QMovie>
+#include <QMessageBox>
+#include <QPlainTextEdit>
+#include <QAbstractItemView>
+#include <QMimeData>
+#include <QBrush>
+#include <QColor>
 
+#include <xalloc.h>
+#include "lkc.h"
+#include <vector>
 #include <stdlib.h>
 
-#include <xalloc.h>
 #include "lkc.h"
 #include "qconf.h"
+#include "configfix.h"
+#include "picosat_functions.h"
 
 #include "images.h"
 
+static QString tristate_value_to_string(tristate val);
+static tristate string_value_to_tristate(QString s);
 
 static QApplication *configApp;
 static ConfigSettings *configSettings;
 
 QAction *ConfigMainWindow::saveAction;
 
+static bool picosat_available;
+
 ConfigSettings::ConfigSettings()
 	: QSettings("kernel.org", "qconf")
 {
@@ -406,7 +425,7 @@ void ConfigList::updateSelection(void)
 	ConfigItem* item = (ConfigItem*)selectedItems().first();
 	if (!item)
 		return;
-
+	emit selectionChanged(selectedItems());
 	menu = item->menu;
 	emit menuChanged(menu);
 	if (!menu)
@@ -540,6 +559,7 @@ void ConfigList::changeValue(ConfigItem* item)
 		}
 		if (oldexpr != newexpr)
 			ConfigList::updateListForAll();
+			emit updateConflictsViewColorization();
 		break;
 	default:
 		break;
@@ -899,6 +919,7 @@ void ConfigList::contextMenuEvent(QContextMenuEvent *e)
 			action, &QAction::setChecked);
 		action->setChecked(showName);
 		headerPopup->addAction(action);
+		headerPopup->addAction(addSymbolFromContextMenu);
 	}
 
 	headerPopup->exec(e->globalPos());
@@ -919,6 +940,7 @@ QList<ConfigList *> ConfigList::allLists;
 QAction *ConfigList::showNormalAction;
 QAction *ConfigList::showAllAction;
 QAction *ConfigList::showPromptAction;
+QAction *ConfigList::addSymbolFromContextMenu;
 
 void ConfigList::setAllOpen(bool open)
 {
@@ -1239,7 +1261,11 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
 		info, &ConfigInfoView::setInfo);
 	connect(list, &ConfigList::menuChanged,
 		parent, &ConfigMainWindow::setMenuLink);
+	connect(list, &ConfigList::menuChanged,
+		parent, &ConfigMainWindow::conflictSelected);
 
+	connect(list, &ConfigList::updateConflictsViewColorization, this,
+		&ConfigSearchWindow::updateConflictsViewColorizationFowarder);
 	layout1->addWidget(split);
 
 	QVariant x, y;
@@ -1262,6 +1288,11 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
 		this, &ConfigSearchWindow::saveSettings);
 }
 
+void ConfigSearchWindow::updateConflictsViewColorizationFowarder(void)
+{
+	emit updateConflictsViewColorization();
+}
+
 void ConfigSearchWindow::saveSettings(void)
 {
 	if (!objectName().isEmpty()) {
@@ -1349,6 +1380,24 @@ ConfigMainWindow::ConfigMainWindow(void)
 	helpText = new ConfigInfoView(split2, "help");
 	setTabOrder(configList, helpText);
 
+	split3 = new QSplitter(split2);
+	split3->setOrientation(Qt::Vertical);
+	conflictsView = new ConflictsView(split3, "help");
+	/*
+	 * conflictsSelected signal in conflictsview triggers when a conflict is
+	 * selected in the view. this line connects that event to conflictselected
+	 * event in mainwindow which updates the selection to match (in the
+	 * configlist) the symbol that was selected.
+	 */
+	connect(conflictsView, &ConflictsView::conflictSelected, this,
+		&ConfigMainWindow::conflictSelected);
+	connect(conflictsView, &ConflictsView::refreshMenu, this,
+		&ConfigMainWindow::refreshMenu);
+	connect(menuList, &ConfigList::updateConflictsViewColorization,
+		conflictsView, &ConflictsView::updateConflictsViewColorization);
+	connect(configList, &ConfigList::updateConflictsViewColorization,
+		conflictsView, &ConflictsView::updateConflictsViewColorization);
+
 	configList->setFocus();
 
 	backAction = new QAction(QPixmap(xpm_back), "Back", this);
@@ -1415,6 +1464,10 @@ ConfigMainWindow::ConfigMainWindow(void)
 	ConfigList::showAllAction->setCheckable(true);
 	ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup);
 	ConfigList::showPromptAction->setCheckable(true);
+	ConfigList::addSymbolFromContextMenu =
+		new QAction("Add symbol from context menu");
+	connect(ConfigList::addSymbolFromContextMenu, &QAction::triggered,
+		conflictsView, &ConflictsView::addSymbol);
 
 	QAction *showDebugAction = new QAction("Show Debug Info", this);
 	  showDebugAction->setCheckable(true);
@@ -1470,6 +1523,8 @@ ConfigMainWindow::ConfigMainWindow(void)
 
 	connect(configList, &ConfigList::menuChanged,
 		helpText, &ConfigInfoView::setInfo);
+	connect(configList, &ConfigList::menuChanged,
+		conflictsView, &ConflictsView::menuChanged);
 	connect(configList, &ConfigList::menuSelected,
 		this, &ConfigMainWindow::changeMenu);
 	connect(configList, &ConfigList::itemSelected,
@@ -1478,6 +1533,8 @@ ConfigMainWindow::ConfigMainWindow(void)
 		this, &ConfigMainWindow::goBack);
 	connect(menuList, &ConfigList::menuChanged,
 		helpText, &ConfigInfoView::setInfo);
+	connect(menuList, &ConfigList::menuChanged,
+		conflictsView, &ConflictsView::menuChanged);
 	connect(menuList, &ConfigList::menuSelected,
 		this, &ConfigMainWindow::changeMenu);
 
@@ -1687,6 +1744,13 @@ void ConfigMainWindow::showSplitView(void)
 	menuList->setFocus();
 }
 
+void ConfigMainWindow::conflictSelected(struct menu *men)
+{
+	configList->clearSelection();
+	menuList->clearSelection();
+	emit(setMenuLink(men));
+}
+
 void ConfigMainWindow::showFullView(void)
 {
 	singleViewAction->setEnabled(true);
@@ -1822,6 +1886,536 @@ void ConfigMainWindow::conf_changed(bool dirty)
 		saveAction->setEnabled(dirty);
 }
 
+void ConfigMainWindow::refreshMenu(void)
+{
+	configList->updateListAll();
+}
+
+void QTableWidget::dropEvent(QDropEvent *event)
+{
+}
+
+void ConflictsView::addPicoSatNote(QToolBar &toolbar)
+{
+	QLabel &label = *new QLabel;
+	auto &iconLabel = *new QLabel();
+	iconLabel.setPixmap(
+		style()->standardIcon(
+			       QStyle::StandardPixmap::SP_MessageBoxInformation)
+			.pixmap(20, 20));
+	label.setText(
+		"The conflict resolver requires that PicoSAT is available as a library.");
+	QAction &showDialog = *new QAction();
+	showDialog.setIconText("Install PicoSAT...");
+	toolbar.addWidget(&iconLabel);
+	toolbar.addWidget(&label);
+	toolbar.addAction(&showDialog);
+	connect(&showDialog, &QAction::triggered,
+		[this]() { (new PicoSATInstallInfoWindow(this))->show(); });
+}
+
+ConflictsView::ConflictsView(QWidget *parent, const char *name)
+	: Parent(parent)
+{
+	/*
+	 * 	- topLevelLayout
+	 * 		- picoSatContainer
+	 *  		- picoSatLayout
+	 *  			- ...
+	 *		- conflictsViewContainer
+	 *			- horizontalLayout
+	 *				- verticalLayout
+	 *				- solutionLayout
+	 */
+	currentSelectedMenu = nullptr;
+	setObjectName(name);
+	QVBoxLayout *topLevelLayout = new QVBoxLayout(this);
+	QWidget *conflictsViewContainer = new QWidget;
+	if (!picosat_available) {
+		conflictsViewContainer->setDisabled(true);
+		QWidget *picoSatContainer = new QWidget;
+		topLevelLayout->addWidget(picoSatContainer);
+		QHBoxLayout *picoSatLayout = new QHBoxLayout(picoSatContainer);
+		QToolBar *picoToolbar = new QToolBar(picoSatContainer);
+		picoSatLayout->addWidget(picoToolbar);
+		picoSatLayout->addStretch();
+		addPicoSatNote(*picoToolbar);
+	}
+	topLevelLayout->addWidget(conflictsViewContainer);
+
+	QHBoxLayout *horizontalLayout = new QHBoxLayout(conflictsViewContainer);
+	QVBoxLayout *verticalLayout = new QVBoxLayout;
+	verticalLayout->setContentsMargins(0, 0, 0, 0);
+	conflictsToolBar =
+		new QToolBar("ConflictTools", conflictsViewContainer);
+	// toolbar buttons [n] [m] [y] [calculate fixes] [remove]
+	QAction *addSymbol = new QAction("Add Symbol");
+	QAction *setConfigSymbolAsNo = new QAction("N");
+	QAction *setConfigSymbolAsModule = new QAction("M");
+	QAction *setConfigSymbolAsYes = new QAction("Y");
+	fixConflictsAction_ = new QAction("Calculate Fixes");
+	QAction *removeSymbol = new QAction("Remove Symbol");
+	QMovie *loadingGif = new QMovie("scripts/kconfig/loader.gif");
+	auto loadingLabel = new QLabel;
+
+	if (loadingGif->isValid()) {
+		loadingGif->setScaledSize(loadingGif->scaledSize().scaled(
+			20, 20, Qt::KeepAspectRatioByExpanding));
+		loadingGif->start();
+		loadingLabel->setMovie(loadingGif);
+	} else {
+		loadingLabel->setText("Calculating...");
+	}
+
+	//if you change the order of buttons here, change the code where
+	//module button was disabled if symbol is boolean, selecting module button
+	//depends on a specific index in list of action
+	fixConflictsAction_->setCheckable(false);
+	conflictsToolBar->addAction(addSymbol);
+	conflictsToolBar->addAction(setConfigSymbolAsNo);
+	conflictsToolBar->addAction(setConfigSymbolAsModule);
+	conflictsToolBar->addAction(setConfigSymbolAsYes);
+	conflictsToolBar->addAction(fixConflictsAction_);
+	conflictsToolBar->addAction(removeSymbol);
+	// loadingLabel->setMargin(5);
+	loadingLabel->setContentsMargins(5, 5, 5, 5);
+	loadingAction = conflictsToolBar->addWidget(loadingLabel);
+	loadingAction->setVisible(false);
+
+	verticalLayout->addWidget(conflictsToolBar);
+
+	connect(addSymbol, &QAction::triggered, this,
+		&ConflictsView::addSymbol);
+	connect(setConfigSymbolAsNo, &QAction::triggered, this,
+		&ConflictsView::changeToNo);
+	connect(setConfigSymbolAsModule, &QAction::triggered, this,
+		&ConflictsView::changeToModule);
+	connect(setConfigSymbolAsYes, &QAction::triggered, this,
+		&ConflictsView::changeToYes);
+	connect(removeSymbol, &QAction::triggered, this,
+		&ConflictsView::removeSymbol);
+	connect(this, &ConflictsView::resultsReady, this,
+		&ConflictsView::updateResults);
+	//connect clicking 'calculate fixes' to 'change all symbol values to fix all conflicts'
+	// no longer used anymore for now.
+	connect(fixConflictsAction_, &QAction::triggered, this,
+		&ConflictsView::calculateFixes);
+
+	conflictsTable = (QTableWidget *)new dropAbleView(this);
+	conflictsTable->setRowCount(0);
+	conflictsTable->setColumnCount(3);
+	conflictsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+	conflictsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+	conflictsTable->setHorizontalHeaderLabels(
+		QStringList() << "Name" << "Wanted value" << "Current value");
+	verticalLayout->addWidget(conflictsTable);
+
+	conflictsTable->setDragDropMode(QAbstractItemView::DropOnly);
+	setAcceptDrops(true);
+
+	connect(conflictsTable, &QTableWidget::cellClicked, this,
+		&ConflictsView::cellClicked);
+	horizontalLayout->addLayout(verticalLayout);
+
+	// populate the solution view on the right hand side:
+	QVBoxLayout *solutionLayout = new QVBoxLayout();
+	solutionLayout->setContentsMargins(0, 0, 0, 0);
+	solutionSelector = new QComboBox();
+	connect(solutionSelector,
+		QOverload<int>::of(&QComboBox::currentIndexChanged),
+		[=](int index) { changeSolutionTable(index); });
+	solutionTable = new QTableWidget();
+	solutionTable->setRowCount(0);
+	solutionTable->setColumnCount(2);
+	solutionTable->setHorizontalHeaderLabels(QStringList()
+						 << "Name" << "New Value");
+	solutionTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+	applyFixButton = new QPushButton("Apply Selected solution");
+	connect(applyFixButton, &QPushButton::clicked, this,
+		&ConflictsView::applyFixButtonClick);
+
+	numSolutionLabel = new QLabel("Solutions:");
+	solutionLayout->addWidget(numSolutionLabel);
+	solutionLayout->addWidget(solutionSelector);
+	solutionLayout->addWidget(solutionTable);
+	solutionLayout->addWidget(applyFixButton);
+
+	horizontalLayout->addLayout(solutionLayout);
+}
+
+void ConflictsView::applyFixButtonClick()
+{
+	signed int solution_number = solutionSelector->currentIndex();
+
+	if (solution_number == -1 || solution_output == NULL) {
+		return;
+	}
+
+	apply_fix(solution_output[solution_number]);
+
+	ConfigList::updateListForAll();
+	for (int i = 0; i < conflictsTable->rowCount(); i++) {
+		conflictsTable->item(i, 2)->setText(
+			conflictsTable->item(i, 1)->text());
+	}
+	updateConflictsViewColorization();
+	QMessageBox msgBox;
+	msgBox.setText("The solution has been applied.");
+	msgBox.exec();
+}
+
+void ConflictsView::changeToYes()
+{
+	QItemSelectionModel *select = conflictsTable->selectionModel();
+	if (select->hasSelection()) {
+		QModelIndexList rows = select->selectedRows();
+		for (int i = 0; i < rows.count(); i++) {
+			conflictsTable->item(rows[i].row(), 1)
+				->setText(tristate_value_to_string(yes));
+		}
+	}
+}
+
+void ConflictsView::changeToModule()
+{
+	QItemSelectionModel *select = conflictsTable->selectionModel();
+	if (select->hasSelection()) {
+		QModelIndexList rows = select->selectedRows();
+		for (int i = 0; i < rows.count(); i++) {
+			conflictsTable->item(rows[i].row(), 1)
+				->setText(tristate_value_to_string(mod));
+		}
+	}
+}
+
+void ConflictsView::changeToNo()
+{
+	QItemSelectionModel *select = conflictsTable->selectionModel();
+	if (select->hasSelection()) {
+		QModelIndexList rows = select->selectedRows();
+		for (int i = 0; i < rows.count(); i++) {
+			conflictsTable->item(rows[i].row(), 1)
+				->setText(tristate_value_to_string(no));
+		}
+	}
+}
+
+void ConflictsView::menuChanged(struct menu *m)
+{
+	currentSelectedMenu = m;
+}
+
+void ConflictsView::addSymbol()
+{
+	addSymbolFromMenu(currentSelectedMenu);
+}
+
+void ConflictsView::selectionChanged(QList<QTreeWidgetItem *> selection)
+{
+	currentSelection = selection;
+}
+
+void ConflictsView::addSymbolFromMenu(struct menu *m)
+{
+	// adds a symbol to the conflict resolver list
+	if (m != nullptr) {
+		if (m->sym != nullptr) {
+			struct symbol *sym = m->sym;
+			tristate currentval = sym_get_tristate_value(sym);
+			//if symbol is not added yet:
+			QAbstractItemModel *tableModel =
+				conflictsTable->model();
+			QModelIndexList matches = tableModel->match(
+				tableModel->index(0, 0), Qt::DisplayRole,
+				QString(sym->name));
+			if (matches.isEmpty()) {
+				conflictsTable->insertRow(
+					conflictsTable->rowCount());
+				conflictsTable->setItem(
+					conflictsTable->rowCount() - 1, 0,
+					new QTableWidgetItem(sym->name));
+				conflictsTable->setItem(
+					conflictsTable->rowCount() - 1, 1,
+					new QTableWidgetItem(
+						tristate_value_to_string(
+							currentval)));
+				conflictsTable->setItem(
+					conflictsTable->rowCount() - 1, 2,
+					new QTableWidgetItem(
+						tristate_value_to_string(
+							currentval)));
+			} else {
+				conflictsTable->item(matches[0].row(), 2)
+					->setText(tristate_value_to_string(
+						currentval));
+			}
+		}
+	}
+}
+
+void ConflictsView::addSymbolFromContextMenu()
+{
+	struct menu *menu;
+
+	if (currentSelection.count() < 0) {
+		return;
+	}
+	for (auto el : currentSelection) {
+		ConfigItem *item = (ConfigItem *)el;
+		if (!item) {
+			continue;
+		}
+		menu = item->menu;
+		addSymbolFromMenu(menu);
+	}
+}
+
+void ConflictsView::removeSymbol()
+{
+	QItemSelectionModel *select = conflictsTable->selectionModel();
+	QAbstractItemModel *itemModel = select->model();
+	if (select->hasSelection()) {
+		QModelIndexList rows = select->selectedRows();
+		itemModel->removeRows(rows[0].row(), rows.size());
+	}
+}
+
+void ConflictsView::cellClicked(int row, int column)
+{
+	auto itemText = conflictsTable->item(row, 0)->text().toUtf8().data();
+	struct property *prop;
+	struct menu *men;
+	struct symbol *sym = sym_find(itemText);
+
+	if (sym == NULL)
+		return;
+	prop = sym->prop;
+	men = prop->menu;
+	// uncommenting following like somehow disables click signal of 'apply selected solution'
+	if (sym->type == symbol_type::S_BOOLEAN) {
+		//disable module button
+		conflictsToolBar->actions()[2]->setDisabled(true);
+	} else {
+		//enable module button
+		conflictsToolBar->actions()[2]->setDisabled(false);
+	}
+	if (column == 1) {
+		// cycle to new value
+		tristate old_val = string_value_to_tristate(
+			conflictsTable->item(row, 1)->text());
+		tristate new_val = old_val;
+		switch (old_val) {
+		case no:
+			new_val = mod;
+			break;
+		case mod:
+			new_val = yes;
+			break;
+		case yes:
+			new_val = no;
+			break;
+		}
+		if (sym->type == S_BOOLEAN && new_val == mod)
+			new_val = yes;
+		conflictsTable->item(row, 1)->setText(
+			tristate_value_to_string(new_val));
+	}
+	emit(conflictSelected(men));
+}
+
+void ConflictsView::changeSolutionTable(int solution_number)
+{
+	size_t i;
+
+	if (solution_output == nullptr || solution_number < 0) {
+		return;
+	}
+	struct sfix_list *selected_solution = solution_output[solution_number];
+	current_solution_number = solution_number;
+	solutionTable->setRowCount(0);
+	i = 0;
+	for (struct list_head *curr = selected_solution->list.next;
+	     curr != &selected_solution->list; curr = curr->next, ++i) {
+		solutionTable->insertRow(solutionTable->rowCount());
+		struct symbol_fix *cur_symbol =
+			select_symbol(selected_solution, i);
+
+		QTableWidgetItem *symbol_name =
+			new QTableWidgetItem(cur_symbol->sym->name);
+
+		solutionTable->setItem(solutionTable->rowCount() - 1, 0,
+				       symbol_name);
+
+		if (cur_symbol->type == symbolfix_type::SF_BOOLEAN) {
+			QTableWidgetItem *symbol_value = new QTableWidgetItem(
+				tristate_value_to_string(cur_symbol->tri));
+			solutionTable->setItem(solutionTable->rowCount() - 1, 1,
+					       symbol_value);
+		} else if (cur_symbol->type == symbolfix_type::SF_NONBOOLEAN) {
+			QTableWidgetItem *symbol_value =
+				new QTableWidgetItem(cur_symbol->nb_val.s);
+			solutionTable->setItem(solutionTable->rowCount() - 1, 1,
+					       symbol_value);
+		} else {
+			QTableWidgetItem *symbol_value =
+				new QTableWidgetItem(cur_symbol->disallowed.s);
+			solutionTable->setItem(solutionTable->rowCount() - 1, 1,
+					       symbol_value);
+		}
+	}
+	updateConflictsViewColorization();
+}
+
+void ConflictsView::updateConflictsViewColorization(void)
+{
+	auto green = QColor(0, 170, 0);
+	auto red = QColor(255, 0, 0);
+	auto grey = QColor(180, 180, 180);
+
+	if (solutionTable->rowCount() == 0 || current_solution_number < 0)
+		return;
+
+	for (int i = 0; i < solutionTable->rowCount(); i++) {
+		QTableWidgetItem *symbol = solutionTable->item(i, 0);
+		//symbol from solution list
+		struct symbol_fix *cur_symbol = select_symbol(
+			solution_output[current_solution_number], i);
+
+		// if symbol is editable but the value is not the target value from solution we got, the color is red
+		// if symbol is editable but the value is the target value from solution we got, the color is green
+		// if symbol is not editable , the value is not the target value, the color is grey
+		// if symbol is not editable , the value is the target value, the color is green
+		auto editable = sym_string_within_range(
+			cur_symbol->sym,
+			tristate_value_to_string(cur_symbol->tri)
+				.toStdString()
+				.c_str());
+		auto _symbol =
+			solutionTable->item(i, 0)->text().toUtf8().data();
+		struct symbol *sym_ = sym_find(_symbol);
+
+		tristate current_value_of_symbol = sym_get_tristate_value(sym_);
+		tristate target_value_of_symbol = string_value_to_tristate(
+			solutionTable->item(i, 1)->text());
+		bool symbol_value_same_as_target = current_value_of_symbol ==
+						   target_value_of_symbol;
+
+		if (editable && !symbol_value_same_as_target) {
+			symbol->setForeground(red);
+		} else if (editable && symbol_value_same_as_target) {
+			symbol->setForeground(green);
+		} else if (!editable && !symbol_value_same_as_target) {
+			symbol->setForeground(grey);
+		} else if (!editable && symbol_value_same_as_target) {
+			symbol->setForeground(green);
+		}
+	}
+}
+
+void ConflictsView::runSatConfAsync()
+{
+	//loop through the rows in conflicts table adding each row into the array:
+	struct symbol_dvalue *p = nullptr;
+	std::vector<struct symbol_dvalue *> wanted_symbols;
+
+	p = static_cast<struct symbol_dvalue *>(calloc(
+		conflictsTable->rowCount(), sizeof(struct symbol_dvalue)));
+	if (!p) {
+		printf("memory allocation error\n");
+		return;
+	}
+
+	for (int i = 0; i < conflictsTable->rowCount(); i++) {
+		struct symbol_dvalue *tmp = (p + i);
+		auto _symbol =
+			conflictsTable->item(i, 0)->text().toUtf8().data();
+		struct symbol *sym = sym_find(_symbol);
+
+		tmp->sym = sym;
+		tmp->type = static_cast<symboldv_type>(
+			sym->type == symbol_type::S_BOOLEAN ? 0 : 1);
+		tmp->tri = string_value_to_tristate(
+			conflictsTable->item(i, 1)->text());
+		wanted_symbols.push_back(tmp);
+	}
+	fixConflictsAction_->setText("Cancel");
+	loadingAction->setVisible(true);
+
+	solution_output = run_satconf(wanted_symbols.data(),
+				      wanted_symbols.size(), &num_solutions);
+
+	free(p);
+	emit resultsReady();
+	{
+		std::lock_guard<std::mutex> lk{ satconf_mutex };
+		satconf_cancelled = true;
+	}
+	satconf_cancellation_cv.notify_one();
+}
+
+void ConflictsView::updateResults(void)
+{
+	fixConflictsAction_->setText("Calculate Fixes");
+	loadingAction->setVisible(false);
+	if (!(solution_output == nullptr || num_solutions == 0)) {
+		solutionSelector->clear();
+		for (unsigned int i = 0; i < num_solutions; i++)
+			solutionSelector->addItem(QString::number(i + 1));
+		// populate the solution table from the first solution gotten
+		numSolutionLabel->setText(
+			QString("Solutions: (%1) found").arg(num_solutions));
+		changeSolutionTable(0);
+	} else {
+		QMessageBox msgBox;
+		msgBox.setText("All symbols are already within range.");
+		msgBox.exec();
+	}
+	if (runSatConfAsyncThread->joinable()) {
+		runSatConfAsyncThread->join();
+		delete runSatConfAsyncThread;
+		runSatConfAsyncThread = nullptr;
+	}
+}
+
+void ConflictsView::calculateFixes()
+{
+	if (conflictsTable->rowCount() == 0) {
+		printd("table is empty\n");
+		return;
+	}
+
+	if (runSatConfAsyncThread == nullptr) {
+		// fire away asynchronous call
+		std::unique_lock<std::mutex> lk{ satconf_mutex };
+
+		numSolutionLabel->setText(QString("Solutions: "));
+		solutionSelector->clear();
+		solutionTable->setRowCount(0);
+		satconf_cancelled = false;
+		runSatConfAsyncThread =
+			new std::thread(&ConflictsView::runSatConfAsync, this);
+	} else {
+		printd("Interrupting rangefix\n");
+		interrupt_rangefix();
+		std::unique_lock<std::mutex> lk{ satconf_mutex };
+		satconf_cancellation_cv.wait(lk, [this] {
+			return satconf_cancelled == true;
+		});
+	}
+}
+
+void ConflictsView::changeAll(void)
+{
+	// not implemented for now
+	return;
+}
+
+ConflictsView::~ConflictsView(void)
+{
+}
+
 void fixup_rootmenu(struct menu *menu)
 {
 	struct menu *child;
@@ -1871,6 +2465,8 @@ int main(int ac, char** av)
 	fixup_rootmenu(&rootmenu);
 	//zconfdump(stdout);
 
+	picosat_available = load_picosat();
+
 	configApp = new QApplication(ac, av);
 
 	configSettings = new ConfigSettings();
@@ -1891,3 +2487,79 @@ int main(int ac, char** av)
 
 	return 0;
 }
+
+dropAbleView::dropAbleView(QWidget *parent)
+	: QTableWidget(parent)
+{
+}
+
+dropAbleView::~dropAbleView()
+{
+}
+void dropAbleView::dropEvent(QDropEvent *event)
+{
+	event->acceptProposedAction();
+}
+
+static QString tristate_value_to_string(tristate val)
+{
+	switch (val) {
+	case yes:
+		return QString::fromStdString("Y");
+	case mod:
+		return QString::fromStdString("M");
+	case no:
+		return QString::fromStdString("N");
+	default:
+		return QString::fromStdString("");
+	}
+}
+
+static tristate string_value_to_tristate(QString s)
+{
+	if (s == "Y")
+		return tristate::yes;
+	else if (s == "M")
+		return tristate::mod;
+	else if (s == "N")
+		return tristate::no;
+	else
+		return tristate::no;
+}
+
+PicoSATInstallInfoWindow::PicoSATInstallInfoWindow(QWidget *parent)
+	: QDialog(parent)
+{
+	QVBoxLayout &layout = *new QVBoxLayout(this);
+	QLabel &text = *new QLabel();
+	text.setWordWrap(true);
+	layout.addWidget(&text);
+	text.setTextFormat(Qt::MarkdownText);
+	text.setTextInteractionFlags(Qt::TextSelectableByMouse);
+	text.setTextInteractionFlags(Qt::TextBrowserInteraction);
+	text.setOpenExternalLinks(true);
+	text.setText(R""""(
+Install the picosat package or build the library yourself:
+
+## Debian-based distributions
+
+```sh
+sudo apt install picosat
+```
+
+## Fedora
+
+```sh
+sudo dnf install picosat
+```
+
+## Other
+
+You can also build PicoSAT yourself from the
+[sources](https://fmv.jku.at/picosat/picosat-965.tar.gz). You need to compile
+PicoSAT with tracing enabled as a shared library under the name of
+"libpicosat-trace.so", "libpicosat-trace.so.0" or "libpicosat-trace.so.1".
+Tracing can be enabled using the `configure.sh` script with the `--trace`
+option.
+			  )"""");
+}
diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h
index aab25ece95c6..807b7a47544c 100644
--- a/scripts/kconfig/qconf.h
+++ b/scripts/kconfig/qconf.h
@@ -14,8 +14,17 @@
 #include <QStyledItemDelegate>
 #include <QTextBrowser>
 #include <QTreeWidget>
+#include <QTableWidget>
+#include <QList>
+#include <QComboBox>
+#include <QVBoxLayout>
+#include <QLabel>
+#include <thread>
+#include <condition_variable>
 
 #include "expr.h"
+#include "cf_defs.h"
+
 
 class ConfigList;
 class ConfigItem;
@@ -80,6 +89,8 @@ public slots:
 	void parentSelected(void);
 	void gotFocus(struct menu *);
 	void showNameChanged(bool on);
+	void selectionChanged(QList<QTreeWidgetItem *> selection);
+	void updateConflictsViewColorization();
 
 public:
 	void updateListAll(void)
@@ -111,6 +122,82 @@ public slots:
 	static void updateListAllForAll();
 
 	static QAction *showNormalAction, *showAllAction, *showPromptAction;
+	static QAction *addSymbolFromContextMenu;
+
+};
+
+class ConflictsView : public QWidget {
+	Q_OBJECT
+	typedef class QWidget Parent;
+private:
+	QAction *loadingAction;
+public:
+	ConflictsView(QWidget *parent, const char *name = 0);
+	~ConflictsView(void);
+	void addSymbolFromMenu(struct menu *m);
+	int current_solution_number = -1;
+
+public slots:
+	void cellClicked(int, int);
+	void changeAll();
+	// triggered by Qactions on the tool bar that adds/remove symbol
+	void addSymbol();
+	// triggered from config list right click -> add symbols
+	void addSymbolFromContextMenu();
+	void removeSymbol();
+	void menuChanged(struct menu *);
+	void changeToNo();
+	void changeToYes();
+	void changeToModule();
+	void selectionChanged(QList<QTreeWidgetItem *> selection);
+
+	void applyFixButtonClick();
+	void updateConflictsViewColorization();
+	void updateResults();
+
+	// switches the solution table with selected solution index from  solution_output
+	void changeSolutionTable(int solution_number);
+
+	// calls satconfig to solve to get wanted value to current value
+	void calculateFixes();
+signals:
+	void showNameChanged(bool);
+	void showRangeChanged(bool);
+	void showDataChanged(bool);
+	void conflictSelected(struct menu *);
+	void refreshMenu();
+	void resultsReady();
+public:
+	QTableWidget *conflictsTable;
+
+	// the comobox on the right hand side. used to select a solution after
+	// getting solution from satconfig
+	QComboBox *solutionSelector{nullptr};
+
+	// the table which shows the selected solution showing variable = New value changes
+	QTableWidget *solutionTable{nullptr};
+
+	// Apply fixes button on the solution view
+	QPushButton *applyFixButton{nullptr};
+
+	struct sfix_list **solution_output{nullptr};
+	size_t num_solutions;
+
+	QToolBar *conflictsToolBar;
+	struct menu *currentSelectedMenu;
+	QLabel *numSolutionLabel{nullptr};
+	// currently selected config items in configlist.
+	QList<QTreeWidgetItem *> currentSelection;
+	QAction *fixConflictsAction_{nullptr};
+	void runSatConfAsync();
+	std::thread *runSatConfAsyncThread{nullptr};
+
+	std::mutex satconf_mutex;
+	std::condition_variable satconf_cancellation_cv;
+	bool satconf_cancelled{false};
+
+private:
+	void addPicoSatNote(QToolBar &layout);
 };
 
 class ConfigItem : public QTreeWidgetItem {
@@ -214,6 +301,12 @@ public slots:
 	bool _showDebug;
 };
 
+class PicoSATInstallInfoWindow : public QDialog {
+	Q_OBJECT
+public:
+	PicoSATInstallInfoWindow(QWidget *parent);
+};
+
 class ConfigSearchWindow : public QDialog {
 	Q_OBJECT
 	typedef class QDialog Parent;
@@ -223,6 +316,9 @@ class ConfigSearchWindow : public QDialog {
 public slots:
 	void saveSettings(void);
 	void search(void);
+	void updateConflictsViewColorizationFowarder();
+signals:
+	void updateConflictsViewColorization();
 
 protected:
 	QLineEdit* editField;
@@ -258,6 +354,8 @@ public slots:
 	void showIntro(void);
 	void showAbout(void);
 	void saveSettings(void);
+	void conflictSelected(struct menu *);
+	void refreshMenu();
 
 protected:
 	void closeEvent(QCloseEvent *e);
@@ -266,10 +364,23 @@ public slots:
 	ConfigList *menuList;
 	ConfigList *configList;
 	ConfigInfoView *helpText;
+	ConflictsView *conflictsView;
+	QToolBar *conflictsToolBar;
 	QAction *backAction;
 	QAction *singleViewAction;
 	QAction *splitViewAction;
 	QAction *fullViewAction;
 	QSplitter *split1;
 	QSplitter *split2;
+	QSplitter *split3;
+};
+
+class dropAbleView : public QTableWidget
+{
+public:
+	dropAbleView(QWidget *parent = nullptr);
+	~dropAbleView();
+
+protected:
+	void dropEvent(QDropEvent *event);
 };
-- 
2.39.5





[Index of Archives]     [Linux&nblp;USB Development]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite Secrets]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux