Browse Source

Conflict Item Widget

isundil 9 months ago
parent
commit
1d5a53f9d3

+ 1 - 0
fakeRaid.vcxproj

@@ -34,6 +34,7 @@
     <QtMoc Include="fakeRaid\conflictModal.h" />
     <ClInclude Include="fakeRaid\exports.h" />
     <ClInclude Include="fakeRaid\fileItem.h" />
+    <ClInclude Include="fakeRaid\htmlFont.hh" />
     <ClInclude Include="fakeRaid\iconProvider.h" />
   </ItemGroup>
   <ItemGroup>

+ 3 - 0
fakeRaid.vcxproj.filters

@@ -83,5 +83,8 @@
     <ClInclude Include="fakeRaid\fileItem.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="fakeRaid\htmlFont.hh">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

+ 51 - 7
fakeRaid/ConflictItemWidget.ui

@@ -6,15 +6,15 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>396</width>
-    <height>98</height>
+    <width>538</width>
+    <height>182</height>
    </rect>
   </property>
-  <property name="minimumSize">
-   <size>
-    <width>0</width>
-    <height>0</height>
-   </size>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
   </property>
   <property name="windowTitle">
    <string>Form</string>
@@ -25,6 +25,9 @@
      <property name="text">
       <string>WarningIcon</string>
      </property>
+     <property name="margin">
+      <number>25</number>
+     </property>
     </widget>
    </item>
    <item>
@@ -33,9 +36,24 @@
       <layout class="QHBoxLayout" name="horizontalLayout_2">
        <item>
         <widget class="QLabel" name="fileIcon">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="baseSize">
+          <size>
+           <width>36</width>
+           <height>36</height>
+          </size>
+         </property>
          <property name="text">
           <string>fileIcon</string>
          </property>
+         <property name="scaledContents">
+          <bool>true</bool>
+         </property>
         </widget>
        </item>
        <item>
@@ -60,6 +78,19 @@
        </item>
       </layout>
      </item>
+     <item>
+      <widget class="QLabel" name="labelIssue">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>TextLabel</string>
+       </property>
+      </widget>
+     </item>
      <item>
       <layout class="QHBoxLayout" name="horizontalLayout">
        <item>
@@ -75,6 +106,19 @@
          </property>
         </spacer>
        </item>
+       <item>
+        <widget class="QComboBox" name="ddUseVersion"/>
+       </item>
+       <item>
+        <widget class="QPushButton" name="buttonUseVersion">
+         <property name="text">
+          <string>Use Version</string>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
        <item>
         <widget class="QPushButton" name="buttonCopy">
          <property name="text">

+ 12 - 0
fakeRaid/ConflictModal.ui

@@ -10,12 +10,24 @@
     <height>376</height>
    </rect>
   </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
   <property name="windowTitle">
    <string>Dialog</string>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QListWidget" name="listWidget">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
      <property name="selectionMode">
       <enum>QAbstractItemView::NoSelection</enum>
      </property>

+ 7 - 1
fakeRaid/browseModal.cpp

@@ -3,7 +3,7 @@
 
 using namespace craftlab::fakeraid;
 
-BrowseModal::BrowseModal(bool cancellable, const std::vector<std::string>& previous)
+BrowseModal::BrowseModal(bool _cancellable, const std::vector<std::string>& previous): cancellable(_cancellable)
 {
 	ui.setupUi(this);
 	if (!cancellable)
@@ -28,6 +28,12 @@ BrowseModal::BrowseModal(bool cancellable, const std::vector<std::string>& previ
 BrowseModal::~BrowseModal()
 {}
 
+void BrowseModal::reject()
+{
+	if (cancellable)
+		QDialog::reject();
+}
+
 void BrowseModal::BrowseForInput(QLineEdit& input)
 {
 	QString path = QFileDialog::getExistingDirectory(this, "FakeRaid", input.text());

+ 2 - 0
fakeRaid/browseModal.h

@@ -11,6 +11,7 @@ namespace craftlab::fakeraid
 		~BrowseModal();
 
 		static std::vector<std::string> Display(bool cancellable, const std::vector<std::string>& previous);
+		void reject() override;
 
 	private:
 		BrowseModal() =delete;
@@ -19,6 +20,7 @@ namespace craftlab::fakeraid
 		void BrowseForInput(QLineEdit& input);
 
 		bool rejected = false;
+		bool cancellable;
 		Ui_BrowseModal ui;
 	};
 }

+ 101 - 3
fakeRaid/conflictItemWidget.cpp

@@ -1,5 +1,7 @@
+#include <sstream>
 #include "conflictItemWidget.h"
 #include "iconProvider.h"
+#include "htmlFont.hh"
 
 using namespace craftlab::fakeraid;
 
@@ -8,9 +10,11 @@ ConflictItemWidget::ConflictItemWidget(QListWidget* parent, const QFile& file, c
 	std::filesystem::path p(file.fileName().toStdString());
 	ui.setupUi(this);
 	ui.warningIcon->setPixmap(IconProvider::WarningIcon(*this));
-	ui.fileIcon->setPixmap(IconProvider::FromFile(file));
+	ui.fileIcon->resize(ui.fileIcon->baseSize());
+	ui.fileIcon->setPixmap(IconProvider::FromFile(file, ui.fileIcon->baseSize()));
 	ui.filename->setText(p.filename().string().c_str());
 
+	connect(ui.buttonUseVersion, &QPushButton::clicked, this, [this]() { SetAction(Action::UseVersion); });
 	connect(ui.buttonCopy, &QPushButton::clicked, this, [this]() { SetAction(Action::Copy); });
 	connect(ui.buttonRemove, &QPushButton::clicked, this, [this]() { SetAction(Action::Remove); });
 	connect(ui.buttonIgnore, &QPushButton::clicked, this, [this]() { SetAction(Action::Ignore); });
@@ -20,6 +24,7 @@ void ConflictItemWidget::SetAction(Action&& action)
 {
 	if (currentAction != action)
 	{
+		ui.buttonUseVersion->setChecked(action == Action::UseVersion);
 		ui.buttonCopy->setChecked(action == Action::Copy);
 		ui.buttonRemove->setChecked(action == Action::Remove);
 		ui.buttonIgnore->setChecked(action == Action::Ignore);
@@ -36,21 +41,114 @@ ConflictItemWidget::Action ConflictItemWidget::GetAction() const
 	return currentAction;
 }
 
+std::vector<int> ConflictItemWidget::versionExistsToVersionNumber(const std::vector<bool> exists)
+{
+	std::vector<int> versionNo;
+	std::transform(exists.begin(), exists.end(), std::back_inserter(versionNo), [](bool b) { return b ? 1 : 0; });
+	return versionNo;
+}
+
+std::map<int, std::vector<int>> ConflictItemWidget::groupFileByVersion(const std::vector<int>& versions)
+{
+	std::map<int, std::vector<int>> result;
+	int repoIndex = 0;
+	for (int i : versions)
+		result[i].push_back(repoIndex++);
+	return result;
+}
+
+const char* FILE_STYLE = "style=\"color:green\"";
+const char* REPO_STYLE = "style=\"color:blue\"";
+const char* VERSION_STYLE = "style=\"color:red\"";
+
+void ConflictItemWidget::listRepositories(const std::vector<int>& repo, std::stringstream& stream)
+{
+	if (repo.size() == 1)
+	{
+		stream << "repository " << HTMLFont(repo.front(), REPO_STYLE);
+		return;
+	}
+	stream << "repositories ";
+	bool written = false;
+	for (int i : repo)
+	{
+		if (written)
+			stream << ", ";
+		stream << HTMLFont(i, REPO_STYLE);
+		written = true;
+	}
+}
+
+void ConflictItemWidget::populateListStream(const std::map<int, std::vector<int>>& repoByVersion, std::stringstream& stream)
+{
+	bool missing = repoByVersion.find(0) != repoByVersion.end();
+
+	for (const std::pair<int, std::vector<int>>& versionAndRepoId : repoByVersion)
+	{
+		if (versionAndRepoId.first == 0)
+		{
+			stream << "<li>Does not exists in ";
+			listRepositories(versionAndRepoId.second, stream);
+			stream << "</li>";
+		}
+		else if (repoByVersion.size() == 2 && missing)
+		{
+			stream << "<li>Do exists in ";
+			listRepositories(versionAndRepoId.second, stream);
+			stream << "</li>";
+		}
+		else
+		{
+			stream << "<li>Have version " << HTMLFont(versionAndRepoId.first, VERSION_STYLE) << " in ";
+			listRepositories(versionAndRepoId.second, stream);
+			stream << "</li>";
+		}
+	}
+}
+
 ConflictItemWidget* ConflictItemWidget::FromMissingFile(QListWidget* parent, const std::vector<std::string>& rootPaths, const std::string& filename, const std::vector<bool>& version)
 {
 	const std::string fullPathFirstFound = rootPaths[std::distance(version.begin(), std::find(version.begin(), version.end(), true))] + "/" + filename;
 	ConflictItemWidget* result = new ConflictItemWidget(parent, QFile(fullPathFirstFound.c_str()), filename);
+	result->ui.buttonUseVersion->setVisible(false);
+	result->ui.ddUseVersion->setVisible(false);
+	std::stringstream ss;
+	ss << "File " << HTMLFont(filename, FILE_STYLE) << " does not exists in every repositories: <ul>";
+	populateListStream(groupFileByVersion(versionExistsToVersionNumber(version)), ss);
+	ss << "</ul>";
+	result->ui.labelIssue->setText(ss.str().c_str());
 	return result;
 }
 
 ConflictItemWidget* ConflictItemWidget::FromMissingDir(QListWidget* parent, const std::vector<std::string>& rootPaths, const std::string& filename, const std::vector<bool>& version)
 {
-	return FromMissingFile(parent, rootPaths, filename, version);
+	const std::string fullPathFirstFound = rootPaths[std::distance(version.begin(), std::find(version.begin(), version.end(), true))] + "/" + filename;
+	ConflictItemWidget* result = new ConflictItemWidget(parent, QFile(fullPathFirstFound.c_str()), filename);
+	result->ui.buttonUseVersion->setVisible(false);
+	result->ui.ddUseVersion->setVisible(false);
+	std::stringstream ss;
+	ss << "Folder " << HTMLFont(filename, FILE_STYLE) << " does not exists in every repositories: <ul>";
+	populateListStream(groupFileByVersion(versionExistsToVersionNumber(version)), ss);
+	ss << "</ul>";
+	result->ui.labelIssue->setText(ss.str().c_str());
+	return result;
 }
 
 ConflictItemWidget* ConflictItemWidget::FromConflict(QListWidget* parent, const std::vector<std::string>& rootPaths, const std::string& filename, const std::vector<int>& version)
 {
-	const std::string fullPathFirstFound = rootPaths[std::distance(version.begin(), std::find(version.begin(), version.end(), true))] + "/" + filename;
+	const std::string fullPathFirstFound = rootPaths[std::distance(version.begin(), std::find_if(version.begin(), version.end(), [](const int& val) { return val != 0; }))] + "/" + filename;
 	ConflictItemWidget* result = new ConflictItemWidget(parent, QFile(fullPathFirstFound.c_str()), filename);
+	const auto fileByVersion = groupFileByVersion(version);
+	for (const auto& i : fileByVersion)
+		if (i.first)
+			result->ui.ddUseVersion->addItem("Use version " +QString::number(i.first), i.first);
+	result->ui.ddUseVersion->setCurrentIndex(0);
+	result->ui.buttonCopy->setVisible(false);
+
+	std::stringstream ss;
+	ss << "Found different versions for file " << HTMLFont(filename, FILE_STYLE) << ": <ul>";
+	populateListStream(fileByVersion, ss);
+	ss << "</ul>";
+	result->ui.labelIssue->setText(ss.str().c_str());
 	return result;
 }

+ 7 - 1
fakeRaid/conflictItemWidget.h

@@ -15,9 +15,10 @@ namespace craftlab::fakeraid
 		enum class Action
 		{
 			Undefined,
+			UseVersion,
 			Copy,
 			Remove,
-			Ignore
+			Ignore,
 		};
 
 		static ConflictItemWidget* FromMissingFile(QListWidget* parent, const std::vector<std::string>& rootPaths, const std::string& filename, const std::vector<bool>& version);
@@ -34,6 +35,11 @@ namespace craftlab::fakeraid
 	private:
 		ConflictItemWidget(QListWidget* parent, const QFile& fullPath, const std::string& filename);
 
+		static void listRepositories(const std::vector<int>& repo, std::stringstream& stream);
+		static std::map<int, std::vector<int>> groupFileByVersion(const std::vector<int>& versions);
+		static std::vector<int> versionExistsToVersionNumber(const std::vector<bool> exists);
+		static void populateListStream(const std::map<int, std::vector<int>>& repoByVersion, std::stringstream& stream);
+
 		const std::string filename;
 		Action currentAction = Action::Undefined;
 		Ui_ConflictItemWidget ui;

+ 21 - 4
fakeRaid/conflictModal.cpp

@@ -1,5 +1,5 @@
 #include <QListWidgetItem>
-#include <QFileDialog>
+#include <QMessageBox>
 #include "conflictModal.h"
 #include "ConflictItemWidget.h"
 #include "fileDiff.h"
@@ -9,6 +9,7 @@ using namespace craftlab::fakeraid;
 ConflictModal::ConflictModal(const std::vector<std::string>& rootPaths, const DiffResult& _diffResult): diffResult(std::make_unique<DiffResult>(_diffResult))
 {
 	ui.setupUi(this);
+
 	for (const std::pair<std::string, std::vector<bool>>& i : diffResult->missingFiles)
 		AddConflictingFile(ConflictItemWidget::FromMissingFile(ui.listWidget, rootPaths, i.first, i.second));
 	for (const std::pair<std::string, std::vector<bool>>& i : diffResult->missingDirs)
@@ -17,16 +18,24 @@ ConflictModal::ConflictModal(const std::vector<std::string>& rootPaths, const Di
 		AddConflictingFile(ConflictItemWidget::FromConflict(ui.listWidget, rootPaths, i.first, i.second));
 	setModal(true);
 	ui.buttonProcess->setEnabled(false);
-	connect(ui.buttonIgnoreAll, &QPushButton::clicked, this, [this]() { for (ConflictItemWidget* item: conflicts) item->SetAction(ConflictItemWidget::Action::Ignore); Process(); });
+	connect(ui.buttonIgnoreAll, &QPushButton::clicked, this, &ConflictModal::IgnoreAllAndProcess);
 	connect(ui.buttonProcess, &QPushButton::clicked, this, &ConflictModal::Process);
 }
 
 ConflictModal::~ConflictModal()
 {}
 
+void ConflictModal::IgnoreAllAndProcess()
+{
+	for (ConflictItemWidget* item : conflicts)
+		item->SetAction(ConflictItemWidget::Action::Ignore);
+	Process();
+}
+
 void ConflictModal::Process()
 {
-	close();
+	processed = true;
+	accept();
 }
 
 void ConflictModal::AddConflictingFile(ConflictItemWidget* widget)
@@ -57,7 +66,7 @@ DiffResult ConflictModal::ComputeOutcome() const
 	for (const ConflictItemWidget* i : conflicts)
 	{
 		FileAndSum originalFile = diffResult->FileList.at(i->GetFileName());
-		if (i->GetAction() == ConflictItemWidget::Action::Copy)
+		if (i->GetAction() == ConflictItemWidget::Action::Copy || i->GetAction() == ConflictItemWidget::Action::UseVersion)
 			result.correctFiles.push_back(originalFile);
 		else if (i->GetAction() == ConflictItemWidget::Action::Remove)
 			;
@@ -75,6 +84,14 @@ DiffResult ConflictModal::ComputeOutcome() const
 	return result;
 }
 
+void ConflictModal::reject()
+{
+	if (processed)
+		return QDialog::reject();
+	if (QMessageBox::question(this, "FakeRaid", "Ignore all error ?", QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No) == QMessageBox::StandardButton::Yes)
+		return IgnoreAllAndProcess();
+}
+
 DiffResult ConflictModal::Display(const IEngine& engine, const DiffResult& diffResult)
 {
 	ConflictModal dialog(engine.GetRootPaths(), diffResult);

+ 6 - 0
fakeRaid/conflictModal.h

@@ -18,10 +18,15 @@ namespace craftlab::fakeraid
 		static DiffResult Display(const IEngine& engine, const DiffResult& diffResult);
 		DiffResult ComputeOutcome() const;
 
+		void reject() override;
+
 	private slots:
 		void UpdateProcessButtonStatus();
+		void IgnoreAllAndProcess();
 		void Process();
 
+	protected:
+
 	private:
 		ConflictModal() =delete;
 		ConflictModal(const std::vector<std::string>& rootPaths, const DiffResult& diffResult);
@@ -31,5 +36,6 @@ namespace craftlab::fakeraid
 		std::vector<ConflictItemWidget*> conflicts;
 		std::unique_ptr<DiffResult> diffResult;
 		Ui_ConflictModal ui;
+		bool processed = false;
 	};
 }

+ 25 - 0
fakeRaid/htmlFont.hh

@@ -0,0 +1,25 @@
+#pragma once
+
+#include <ostream>
+
+namespace craftlab::fakeraid
+{
+	template<typename T>
+	class HTMLFont
+	{
+	public:
+		HTMLFont(const T& _val, const std::string& _extraStyle ="") : val(_val), extraStyle(_extraStyle)
+		{
+		}
+
+		friend std::ostream& operator<<(std::ostream& ss, const HTMLFont& self)
+		{
+			ss << "<span " << self.extraStyle << ">" << self.val << "</span>";
+			return ss;
+		}
+
+	private:
+		const T val;
+		const std::string extraStyle;
+	};
+}