Moving to proper bindiff

This commit is contained in:
wargio 2023-12-29 16:18:55 +08:00
parent 39532d312c
commit 439b268927
11 changed files with 506 additions and 92 deletions

View File

@ -7,6 +7,7 @@ set(SOURCES
core/CutterJson.cpp
core/RizinCpp.cpp
core/Basefind.cpp
core/BinDiff.cpp
dialogs/EditStringDialog.cpp
dialogs/WriteCommandsDialogs.cpp
widgets/DisassemblerGraphView.cpp
@ -58,7 +59,6 @@ set(SOURCES
widgets/DecompilerWidget.cpp
widgets/VisualNavbar.cpp
widgets/GraphView.cpp
dialogs/bindiff/BinDiffDialog.cpp
dialogs/preferences/PreferencesDialog.cpp
dialogs/preferences/AppearanceOptionsWidget.cpp
dialogs/preferences/GraphOptionsWidget.cpp
@ -156,6 +156,7 @@ set(SOURCES
tools/basefind/BaseFindDialog.cpp
tools/basefind/BaseFindSearchDialog.cpp
tools/basefind/BaseFindResultsDialog.cpp
tools/bindiff/DiffLoadDialog.cpp
)
set(HEADER_FILES
core/Cutter.h
@ -164,6 +165,7 @@ set(HEADER_FILES
core/CutterJson.h
core/RizinCpp.h
core/Basefind.h
core/BinDiff.h
dialogs/EditStringDialog.h
dialogs/WriteCommandsDialogs.h
widgets/DisassemblerGraphView.h
@ -214,7 +216,6 @@ set(HEADER_FILES
widgets/DecompilerWidget.h
widgets/VisualNavbar.h
widgets/GraphView.h
dialogs/bindiff/BinDiffDialog.h
dialogs/preferences/PreferencesDialog.h
dialogs/preferences/AppearanceOptionsWidget.h
dialogs/preferences/PreferenceCategory.h
@ -321,6 +322,7 @@ set(HEADER_FILES
tools/basefind/BaseFindDialog.h
tools/basefind/BaseFindSearchDialog.h
tools/basefind/BaseFindResultsDialog.h
tools/bindiff/DiffLoadDialog.h
)
set(UI_FILES
dialogs/AboutDialog.ui
@ -347,7 +349,6 @@ set(UI_FILES
widgets/GlobalsWidget.ui
widgets/StringsWidget.ui
widgets/HexdumpWidget.ui
dialogs/bindiff/BinDiffDialog.ui
dialogs/preferences/PreferencesDialog.ui
dialogs/preferences/AppearanceOptionsWidget.ui
dialogs/preferences/GraphOptionsWidget.ui
@ -396,6 +397,7 @@ set(UI_FILES
tools/basefind/BaseFindDialog.ui
tools/basefind/BaseFindSearchDialog.ui
tools/basefind/BaseFindResultsDialog.ui
tools/bindiff/DiffLoadDialog.ui
)
set(QRC_FILES
resources.qrc

120
src/core/BinDiff.cpp Normal file
View File

@ -0,0 +1,120 @@
#include "BinDiff.h"
bool BinDiff::threadCallback(const size_t nLeft, const size_t nMatch, void *user)
{
auto bdiff = reinterpret_cast<BinDiff *>(user);
return bdiff->updateProgress(nLeft, nMatch);
}
BinDiff::BinDiff(CutterCore *core)
: core(core),
result(nullptr),
continue_run(true),
maxTotal(1)
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
,
mutex(QMutex::Recursive)
#endif
{
}
BinDiff::~BinDiff()
{
cancel();
wait();
rz_analysis_match_result_free(result);
}
void BinDiff::run(QString fileName)
{
mutex.lock();
rz_analysis_match_result_free(result);
result = nullptr;
continue_run = true;
maxTotal = 1; // maxTotal must be at least 1.
mutex.unlock();
core->coreMutex.lock();
result = core->diffNewFile(fileName, threadCallback, this);
core->coreMutex.unlock();
emit complete();
}
void BinDiff::cancel()
{
mutex.lock();
continue_run = false;
mutex.unlock();
}
static void setFunctionDescription(FunctionDescription *desc, const RzAnalysisFunction *func)
{
desc->offset = func->addr;
desc->linearSize = rz_analysis_function_linear_size((RzAnalysisFunction *)func);
desc->nargs = rz_analysis_arg_count((RzAnalysisFunction *)func);
desc->nlocals = rz_analysis_var_local_count((RzAnalysisFunction *)func);
desc->nbbs = rz_list_length(func->bbs);
desc->calltype = func->cc ? QString::fromUtf8(func->cc) : QString();
desc->name = func->name ? QString::fromUtf8(func->name) : QString();
desc->edges = rz_analysis_function_count_edges(func, nullptr);
desc->stackframe = func->maxstack;
}
QList<BinDiffMatchDescription> BinDiff::matches()
{
QList<BinDiffMatchDescription> pairs;
RzAnalysisMatchPair *pair = nullptr;
RzListIter *it = nullptr;
const RzAnalysisFunction *fcn_a = nullptr;
const RzAnalysisFunction *fcn_b = nullptr;
CutterRzListForeach (result->matches, it, RzAnalysisMatchPair, pair) {
BinDiffMatchDescription desc;
fcn_a = static_cast<const RzAnalysisFunction *>(pair->pair_a);
fcn_b = static_cast<const RzAnalysisFunction *>(pair->pair_b);
setFunctionDescription(&desc.original, fcn_a);
setFunctionDescription(&desc.modified, fcn_b);
desc.simtype = RZ_ANALYSIS_SIMILARITY_TYPE_STR(pair->similarity);
desc.similarity = pair->similarity;
pairs << desc;
}
return pairs;
}
QList<FunctionDescription> BinDiff::mismatch(bool fileA)
{
QList<FunctionDescription> list;
RzAnalysisFunction *func = nullptr;
RzList *unmatch = fileA ? result->unmatch_a : result->unmatch_b;
RzListIter *it = nullptr;
CutterRzListForeach (unmatch, it, RzAnalysisFunction, func) {
FunctionDescription desc;
setFunctionDescription(&desc, func);
list << desc;
}
return list;
}
bool BinDiff::updateProgress(const size_t nLeft, const size_t nMatch)
{
mutex.lock();
if (maxTotal < nMatch) {
maxTotal = nMatch;
}
if (maxTotal < nLeft) {
maxTotal = nLeft;
}
emit progress(maxTotal, nLeft, nMatch);
bool ret = continue_run;
mutex.unlock();
return ret;
}

47
src/core/BinDiff.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef CUTTER_BINDIFF_CORE_H
#define CUTTER_BINDIFF_CORE_H
#include <QThread>
#include <QMutex>
#include "Cutter.h"
#include "CutterDescriptions.h"
#include <rz_analysis.h>
class CutterCore;
class BinDiff : public QThread
{
Q_OBJECT
public:
explicit BinDiff(CutterCore *core);
virtual ~BinDiff();
void run(QString fileName);
QList<BinDiffMatchDescription> matches();
QList<FunctionDescription> mismatch(bool fileA);
public slots:
void cancel();
signals:
void progress(const size_t total, const size_t nLeft, const size_t nMatch);
void complete();
private:
CutterCore *const core;
RzAnalysisMatchResult *result;
bool continue_run;
size_t maxTotal;
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QMutex mutex;
#else
QRecursiveMutex mutex;
#endif
bool updateProgress(const size_t nLeft, const size_t nMatch);
static bool threadCallback(const size_t nLeft, const size_t nMatch, void *user);
};
#endif // CUTTER_BINDIFF_CORE_H

View File

@ -396,7 +396,7 @@ QString CutterCore::getFunctionExecOut(const std::function<bool(RzCore *)> &fcn,
o = rz_cons_get_buffer();
rz_cons_pop();
rz_cons_echo(NULL);
rz_cons_echo(nullptr);
clean_return:
if (offset != core->offset) {
@ -487,7 +487,7 @@ QString CutterCore::cmdRaw(const char *cmd)
// cleaning up
rz_cons_pop();
rz_cons_echo(NULL);
rz_cons_echo(nullptr);
return res;
}
@ -603,14 +603,14 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int
// load RzBin information
// XXX only for sub-bins
rz_core_bin_load(core, path.toUtf8(), baddr);
rz_bin_select_idx(core->bin, NULL, idx);
rz_bin_select_idx(core->bin, nullptr, idx);
}
#endif
} else {
// Not loading RzBin info coz va = false
}
auto iod = core->io ? core->io->desc : NULL;
auto iod = core->io ? core->io->desc : nullptr;
auto debug =
core->file && iod && (core->file->fd == iod->fd) && iod->plugin && iod->plugin->isdbg;
@ -856,7 +856,7 @@ QString CutterCore::getString(RVA addr, uint64_t len, RzStrEnc encoding, bool es
opt.encoding = encoding;
opt.escape_nl = escape_nl;
auto seek = seekTemp(addr);
return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, NULL));
return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, nullptr));
}
QString CutterCore::getMetaString(RVA addr)
@ -1093,13 +1093,13 @@ void CutterCore::createSignature(const QString &filepath)
ut64 CutterCore::math(const QString &expr)
{
CORE_LOCK();
return rz_num_math(core ? core->num : NULL, expr.toUtf8().constData());
return rz_num_math(core ? core->num : nullptr, expr.toUtf8().constData());
}
ut64 CutterCore::num(const QString &expr)
{
CORE_LOCK();
return rz_num_get(core ? core->num : NULL, expr.toUtf8().constData());
return rz_num_get(core ? core->num : nullptr, expr.toUtf8().constData());
}
QString CutterCore::itoa(ut64 num, int rdx)
@ -1902,7 +1902,7 @@ void CutterCore::setCurrentDebugThread(int tid)
if (!asyncTask(
[=](RzCore *core) {
rz_debug_select(core->dbg, core->dbg->pid, tid);
return (void *)NULL;
return (void *)nullptr;
},
debugTask)) {
return;
@ -1929,7 +1929,7 @@ void CutterCore::setCurrentDebugProcess(int pid)
[=](RzCore *core) {
rz_debug_select(core->dbg, pid, core->dbg->tid);
core->dbg->main_pid = pid;
return (void *)NULL;
return (void *)nullptr;
},
debugTask)) {
return;
@ -2004,7 +2004,7 @@ void CutterCore::startEmulation()
asyncTask(
[&](RzCore *core) {
rz_core_analysis_esil_reinit(core);
rz_core_analysis_esil_init_mem(core, NULL, UT64_MAX, UT32_MAX);
rz_core_analysis_esil_init_mem(core, nullptr, UT64_MAX, UT32_MAX);
rz_core_analysis_esil_init_regs(core);
return nullptr;
},
@ -2180,7 +2180,7 @@ void CutterCore::stopDebug()
CORE_LOCK();
if (currentlyEmulating) {
rz_core_analysis_esil_init_mem_del(core, NULL, UT64_MAX, UT32_MAX);
rz_core_analysis_esil_init_mem_del(core, nullptr, UT64_MAX, UT32_MAX);
rz_core_analysis_esil_deinit(core);
resetWriteCache();
rz_core_debug_clear_register_flags(core);
@ -2218,7 +2218,7 @@ void CutterCore::continueDebug()
if (currentlyEmulating) {
if (!asyncTask(
[](RzCore *core) {
rz_core_esil_step(core, UT64_MAX, "0", NULL, false);
rz_core_esil_step(core, UT64_MAX, "0", nullptr, false);
rz_core_reg_update_flags(core);
return nullptr;
},
@ -2294,7 +2294,7 @@ void CutterCore::continueUntilDebug(ut64 offset)
if (currentlyEmulating) {
if (!asyncTask(
[=](RzCore *core) {
rz_core_esil_step(core, offset, NULL, NULL, false);
rz_core_esil_step(core, offset, nullptr, nullptr, false);
rz_core_reg_update_flags(core);
return nullptr;
},
@ -2380,7 +2380,7 @@ void CutterCore::continueUntilSyscall()
[](void *x) { rz_debug_stop(reinterpret_cast<RzDebug *>(x)); },
core->dbg);
rz_reg_arena_swap(core->dbg->reg, true);
rz_debug_continue_syscalls(core->dbg, NULL, 0);
rz_debug_continue_syscalls(core->dbg, nullptr, 0);
rz_cons_break_pop();
rz_core_dbg_follow_seek_register(core);
return nullptr;
@ -2410,7 +2410,7 @@ void CutterCore::stepDebug()
if (currentlyEmulating) {
if (!asyncTask(
[](RzCore *core) {
rz_core_esil_step(core, UT64_MAX, NULL, NULL, false);
rz_core_esil_step(core, UT64_MAX, nullptr, nullptr, false);
rz_core_reg_update_flags(core);
return nullptr;
},
@ -2635,7 +2635,7 @@ void CutterCore::stopTraceSession()
if (!asyncTask(
[](RzCore *core) {
rz_debug_session_free(core->dbg->session);
core->dbg->session = NULL;
core->dbg->session = nullptr;
return nullptr;
},
debugTask)) {
@ -3255,8 +3255,8 @@ QList<FlirtDescription> CutterCore::getSignaturesDB()
{
CORE_LOCK();
void *ptr = NULL;
RzListIter *iter = NULL;
void *ptr = nullptr;
RzListIter *iter = nullptr;
QList<FlirtDescription> sigdb;
RzList *list = rz_core_analysis_sigdb_list(core, true);
@ -3451,7 +3451,7 @@ QList<SectionDescription> CutterCore::getAllSections()
if (!digests) {
continue;
}
const char *entropy = (const char *)ht_pp_find(digests, "entropy", NULL);
const char *entropy = (const char *)ht_pp_find(digests, "entropy", nullptr);
section.entropy = rz_str_get(entropy);
ht_pp_free(digests);
}
@ -4055,7 +4055,7 @@ void CutterCore::addGlobalVariable(RVA offset, QString name, QString typ)
{
name = sanitizeStringForCommand(name);
CORE_LOCK();
char *errmsg = NULL;
char *errmsg = nullptr;
RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser,
typ.toStdString().c_str(), &errmsg);
if (errmsg) {
@ -4084,7 +4084,7 @@ void CutterCore::modifyGlobalVariable(RVA offset, QString name, QString typ)
if (name.compare(glob->name)) {
rz_analysis_var_global_rename(core->analysis, glob->name, name.toStdString().c_str());
}
char *errmsg = NULL;
char *errmsg = nullptr;
RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser,
typ.toStdString().c_str(), &errmsg);
if (errmsg) {
@ -4260,7 +4260,7 @@ QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
rz_cons_singleton()->is_html = false;
rz_cons_singleton()->was_html = true;
}
rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, NULL, &options);
rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, nullptr, &options);
}
QList<DisassemblyLine> r;
@ -4387,7 +4387,7 @@ QString CutterCore::getVersionInformation()
{ "rz_syscall", &rz_syscall_version },
{ "rz_util", &rz_util_version },
/* ... */
{ NULL, NULL }
{ nullptr, nullptr }
};
versionInfo.append(getRizinVersionReadable());
versionInfo.append("\n");
@ -4564,9 +4564,12 @@ bool CutterCore::isWriteModeEnabled()
return false;
}
RzAnalysisMatchResult *CutterCore::matchFunctionsFromNewFile(const QString &filePath) {
RzList *fcns_a = NULL, *fcns_b = NULL;
RzAnalysisMatchResult *result = NULL;
RzAnalysisMatchResult *CutterCore::diffNewFile(const QString &filePath,
RzAnalysisMatchThreadInfoCb callback, void *user)
{
RzList *fcns_a = nullptr, *fcns_b = nullptr;
RzAnalysisMatchResult *result = nullptr;
RzAnalysisMatchOpt opts;
RzListIter *iter;
RzConfigNode *node;
void *ptr;
@ -4590,7 +4593,7 @@ RzAnalysisMatchResult *CutterCore::matchFunctionsFromNewFile(const QString &file
goto fail;
}
if (!rz_core_bin_load(core_b, NULL, UT64_MAX)) {
if (!rz_core_bin_load(core_b, nullptr, UT64_MAX)) {
qWarning() << "cannot load bin " << filePath;
goto fail;
}
@ -4600,18 +4603,18 @@ RzAnalysisMatchResult *CutterCore::matchFunctionsFromNewFile(const QString &file
goto fail;
}
rz_list_foreach (core_a->config->nodes, iter, ptr) {
rz_list_foreach(core_a->config->nodes, iter, ptr)
{
node = reinterpret_cast<RzConfigNode *>(ptr);
if (!strcmp(node->name, "scr.color") ||
!strcmp(node->name, "scr.interactive") ||
!strcmp(node->name, "cfg.debug")) {
if (!strcmp(node->name, "scr.color") || !strcmp(node->name, "scr.interactive")
|| !strcmp(node->name, "cfg.debug")) {
rz_config_set(core_b->config, node->name, "0");
continue;
}
rz_config_set(core_b->config, node->name, node->value);
}
if (!rz_core_analysis_everything(core_b, false, NULL)) {
if (!rz_core_analysis_everything(core_b, false, nullptr)) {
qWarning() << "cannot analyze binary " << filePath;
goto fail;
}
@ -4631,10 +4634,15 @@ RzAnalysisMatchResult *CutterCore::matchFunctionsFromNewFile(const QString &file
rz_list_sort(fcns_a, core_a->analysis->columnSort);
rz_list_sort(fcns_b, core_b->analysis->columnSort);
opts.analysis_a = core_a->analysis;
opts.analysis_b = core_b->analysis;
opts.callback = callback;
opts.user = user;
// calculate all the matches between the functions of the 2 different core files.
result = rz_analysis_match_functions_2(core_a->analysis, fcns_a, core_b->analysis, fcns_b);
result = rz_analysis_match_functions(fcns_a, fcns_b, &opts);
if (!result) {
qWarning() << "failed to perform the function matching operation";
qWarning() << "failed to perform the function matching operation or job was cancelled.";
goto fail;
}
@ -4647,10 +4655,9 @@ fail:
rz_list_free(fcns_b);
rz_core_free(this->core_b);
this->core_b = nullptr;
return NULL;
return nullptr;
}
/**
* @brief get a compact disassembly preview for tooltips
* @param address - the address from which to print the disassembly

View File

@ -5,6 +5,7 @@
#include "core/CutterDescriptions.h"
#include "core/CutterJson.h"
#include "core/Basefind.h"
#include "core/BinDiff.h"
#include "common/BasicInstructionHighlighter.h"
#include <QMap>
@ -71,6 +72,7 @@ class CUTTER_EXPORT CutterCore : public QObject
friend class RzCoreLocked;
friend class RizinTask;
friend class Basefind;
friend class BinDiff;
public:
explicit CutterCore(QObject *parent = nullptr);
@ -755,7 +757,8 @@ public:
void writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type, RVA address);
/* Diffing/Matching */
RzAnalysisMatchResult *matchFunctionsFromNewFile(const QString &filePath);
RzAnalysisMatchResult *diffNewFile(const QString &filePath,
RzAnalysisMatchThreadInfoCb callback, void *user);
signals:
void refreshAll();

View File

@ -405,6 +405,14 @@ struct BasefindResultDescription
ut32 score;
};
struct BinDiffMatchDescription
{
FunctionDescription original;
FunctionDescription modified;
QString simtype;
double similarity;
};
Q_DECLARE_METATYPE(FunctionDescription)
Q_DECLARE_METATYPE(ImportDescription)
Q_DECLARE_METATYPE(ExportDescription)
@ -446,5 +454,6 @@ Q_DECLARE_METATYPE(RefDescription)
Q_DECLARE_METATYPE(VariableDescription)
Q_DECLARE_METATYPE(BasefindCoreStatusDescription)
Q_DECLARE_METATYPE(BasefindResultDescription)
Q_DECLARE_METATYPE(BinDiffMatchDescription)
#endif // DESCRIPTIONS_H

View File

@ -119,6 +119,7 @@
// Tools
#include "tools/basefind/BaseFindDialog.h"
#include "tools/bindiff/DiffLoadDialog.h"
#define PROJECT_FILE_FILTER tr("Rizin Project (*.rzdb)")
@ -1616,60 +1617,14 @@ void MainWindow::on_actionBackward_triggered()
void MainWindow::on_actionDiff_triggered()
{
QFileDialog dialog(this);
dialog.setWindowTitle(tr("Select a file to use for diffing"));
dialog.setNameFilters({ tr("All files (*)") });
if (!dialog.exec()) {
return;
}
const QString &fileName = QDir::toNativeSeparators(dialog.selectedFiles().first());
if (fileName.isEmpty()) {
return;
}
auto compareTask = new RizinFunctionTask([=](RzCore *) {
return Core()->matchFunctionsFromNewFile(fileName);
}, false);
task = QSharedPointer<RizinTask>(compareTask);
taskDialog = new RizinTaskDialog(task);
taskDialog->setAttribute(Qt::WA_DeleteOnClose);
taskDialog->setDesc(tr("Performing function comparison..."));
taskDialog->show();
connect(task.data(), &RizinTask::finished, this, [this, compareTask]() {
RzAnalysisMatchResult *result = static_cast<RzAnalysisMatchResult *>(compareTask->getResult());
openBinDiffDialog(result);
delete taskDialog;
taskDialog = nullptr;
});
compareTask->startTask();
auto dialog = new DiffLoadDialog(this);
dialog->show();
}
void MainWindow::on_actionForward_triggered()
{
core->seekNext();
}
void MainWindow::openBinDiffDialog(RzAnalysisMatchResult *result)
{
if (!result) {
messageBoxWarning(tr("Error"), tr("Failed to perform the function matching."));
return;
}
if (binDiffDialog) {
delete binDiffDialog;
}
binDiffDialog = new BinDiffDialog(result, this);
binDiffDialog->show();
}
void MainWindow::on_actionDisasAdd_comment_triggered()
{
CommentsDialog c(this);

View File

@ -152,10 +152,10 @@ public slots:
void openNewFileFailed();
void openBinDiffDialog(RzAnalysisMatchResult *result);
void toggleOverview(bool visibility, GraphWidget *targetGraph);
private slots:
void on_actionDiff_triggered();
void on_actionBaseFind_triggered();
void on_actionAbout_triggered();
void on_actionIssue_triggered();
@ -179,8 +179,6 @@ private slots:
void on_actionBackward_triggered();
void on_actionForward_triggered();
void on_actionDiff_triggered();
void on_actionMap_triggered();
void on_actionTabs_on_Top_triggered();

View File

@ -0,0 +1,94 @@
#include "DiffLoadDialog.h"
#include "ui_DiffLoadDialog.h"
#include <core/Cutter.h>
#include <rz_th.h>
#include <QFileDialog>
#include <QMessageBox>
DiffLoadDialog::DiffLoadDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DiffLoadDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
ui->lineEditFileName->setReadOnly(true);
ui->lineEditFileName->setText("");
ui->lineEditDiffFile->setReadOnly(true);
ui->lineEditDiffFile->setText("");
}
DiffLoadDialog::~DiffLoadDialog() {}
QString DiffLoadDialog::getFileToOpen() const
{
return ui->lineEditFileName->text();
}
QString DiffLoadDialog::getPreviousDiffFile() const
{
return ui->lineEditDiffFile->text();
}
void DiffLoadDialog::on_buttonDiffOpen_clicked()
{
QFileDialog dialog(this);
dialog.setWindowTitle(tr("Select the previous diff to load"));
dialog.setNameFilters({ tr("JSON array (*.json)") });
if (!dialog.exec()) {
return;
}
const QString &fileName = QDir::toNativeSeparators(dialog.selectedFiles().first());
if (fileName.isEmpty()) {
return;
}
ui->lineEditDiffFile->setText(fileName);
}
void DiffLoadDialog::on_buttonFileOpen_clicked()
{
QFileDialog dialog(this);
dialog.setWindowTitle(tr("Select a file to use for diffing"));
dialog.setNameFilters({ tr("All files (*)") });
if (!dialog.exec()) {
return;
}
const QString &fileName = QDir::toNativeSeparators(dialog.selectedFiles().first());
if (fileName.isEmpty()) {
return;
}
ui->lineEditFileName->setText(fileName);
}
void DiffLoadDialog::openErrorBox(QString errorMessage)
{
QMessageBox mb(this);
mb.setIcon(QMessageBox::Warning);
mb.setStandardButtons(QMessageBox::Ok);
mb.setWindowTitle(tr("Error"));
mb.setText(errorMessage);
mb.exec();
}
void DiffLoadDialog::on_buttonBox_accepted()
{
QString fileName = getFileToOpen();
if (fileName.isEmpty()) {
openErrorBox(tr("The compare file was not selected."));
return;
}
}
void DiffLoadDialog::on_buttonBox_rejected()
{
// cancel
}

View File

@ -0,0 +1,37 @@
#ifndef DIFF_LOAD_DIALOG_H
#define DIFF_LOAD_DIALOG_H
#include <QDialog>
#include <QListWidgetItem>
#include <memory>
#include <core/Cutter.h>
namespace Ui {
class DiffLoadDialog;
}
class DiffLoadDialog : public QDialog
{
Q_OBJECT
public:
explicit DiffLoadDialog(QWidget *parent = nullptr);
~DiffLoadDialog();
QString getFileToOpen() const;
QString getPreviousDiffFile() const;
private slots:
void on_buttonDiffOpen_clicked();
void on_buttonFileOpen_clicked();
void on_buttonBox_accepted();
void on_buttonBox_rejected();
private:
std::unique_ptr<Ui::DiffLoadDialog> ui;
void openErrorBox(QString errorMessage);
};
#endif // DIFF_LOAD_DIALOG_H

View File

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DiffLoadDialog</class>
<widget class="QDialog" name="DiffLoadDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>636</width>
<height>142</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Select which file to compare.</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditDiffFile"/>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEditFileName"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="fileToCompareLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;File to compare to.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>File to compare (required)</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="buttonDiffOpen">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="buttonFileOpen">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="previousDiffLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;You can load a previous saved diff instead of performing a new diff scan.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Previous diff file (optional)</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DiffLoadDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>234</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DiffLoadDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>254</y>
</hint>
</hints>
</connection>
</connections>
</ui>