macOS file operations improvements (#321)

* Implemented macOS open with functionality and drag of files to the icon

* fixed bug when dropping a different binary

* fixed Uppercases for application names

* refactor to avoid code in header for CutterApplication

* removed unused includes

* more consistent includes and main.cpp cleanup
This commit is contained in:
Marco Grassi 2018-02-11 02:04:31 +08:00 committed by xarkes
parent bc22302d7d
commit 0fb1086aca
7 changed files with 241 additions and 82 deletions

108
src/CutterApplication.cpp Normal file
View File

@ -0,0 +1,108 @@
#include "CutterApplication.h"
#include <QApplication>
#include <QFileOpenEvent>
#include <QEvent>
#include <QMessageBox>
#include <QCommandLineParser>
#include <QTextCodec>
#include <QStringList>
#include <QProcess>
CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv){
setOrganizationName("Cutter");
setApplicationName("Cutter");
setApplicationVersion(APP_VERSION);
setWindowIcon(QIcon(":/img/cutter.svg"));
// Set QString codec to UTF-8
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
#endif
QCommandLineParser cmd_parser;
cmd_parser.setApplicationDescription(QObject::tr("A Qt and C++ GUI for radare2 reverse engineering framework"));
cmd_parser.addHelpOption();
cmd_parser.addVersionOption();
cmd_parser.addPositionalArgument("filename", QObject::tr("Filename to open."));
QCommandLineOption analOption({"A", "anal"},
QObject::tr("Automatically open file and optionally start analysis. Needs filename to be specified. May be a value between 0 and 2: 0 = no analysis, 1 = aaa, 2 = aaaa (experimental)"),
QObject::tr("level"));
cmd_parser.addOption(analOption);
cmd_parser.process(*this);
QStringList args = cmd_parser.positionalArguments();
// Check r2 version
QString r2version = r_core_version();
QString localVersion = "" R2_GITTAP;
if (r2version != localVersion)
{
QMessageBox msg;
msg.setIcon(QMessageBox::Critical);
msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msg.setWindowTitle(QObject::tr("Version mismatch!"));
msg.setText(QString(QObject::tr("The version used to compile Cutter (%1) does not match the binary version of radare2 (%2). This could result in unexpected behaviour. Are you sure you want to continue?")).arg(localVersion, r2version));
if (msg.exec() == QMessageBox::No)
exit(1);
}
bool analLevelSpecified = false;
int analLevel= 0;
if (cmd_parser.isSet(analOption))
{
analLevel = cmd_parser.value(analOption).toInt(&analLevelSpecified);
if (!analLevelSpecified || analLevel < 0 || analLevel > 2)
{
printf("%s\n", QObject::tr("Invalid Analysis Level. May be a value between 0 and 2.").toLocal8Bit().constData());
exit(1);
}
}
MainWindow *main = new MainWindow();
setMainWindow(main);
if (args.empty())
{
if (analLevelSpecified)
{
printf("%s\n", QObject::tr("Filename must be specified to start analysis automatically.").toLocal8Bit().constData());
exit(1);
}
main->displayNewFileDialog();
}
else // filename specified as positional argument
{
main->openNewFile(args[0], analLevelSpecified ? analLevel : -1);
}
}
bool CutterApplication::event(QEvent *e){
if (e->type() == QEvent::FileOpen) {
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(e);
if (openEvent) {
if (m_FileAlreadyDropped) {
// we already dropped a file in macOS, let's spawn another instance
// (Like the File -> Open)
QString fileName = openEvent->file();
QProcess process(this);
process.setEnvironment(QProcess::systemEnvironment());
QStringList args = QStringList(fileName);
process.startDetached(qApp->applicationFilePath(), args);
} else {
QString fileName = openEvent->file();
m_FileAlreadyDropped = true;
m_MainWindow->closeNewFileDialog();
m_MainWindow->openNewFile(fileName, -1);
}
}
}
return QApplication::event(e);
}

34
src/CutterApplication.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef CUTTERAPPLICATION_H
#define CUTTERAPPLICATION_H
#include <QEvent>
#include <QApplication>
#include "MainWindow.h"
class CutterApplication : public QApplication
{
Q_OBJECT
Q_PROPERTY(MainWindow* mainWindow READ mainWindow WRITE setMainWindow)
public:
CutterApplication(int &argc, char **argv);
MainWindow * mainWindow() {
return m_MainWindow;
}
void setMainWindow(MainWindow * mw) {
m_MainWindow = mw;
}
protected:
bool event(QEvent *e);
private:
bool m_FileAlreadyDropped;
MainWindow *m_MainWindow;
};
#endif // CUTTERAPPLICATION_H

View File

@ -283,10 +283,19 @@ void MainWindow::openNewFile(const QString &fn, int analLevel, QList<QString> ad
void MainWindow::displayNewFileDialog()
{
NewFileDialog *n = new NewFileDialog();
newFileDialog = n;
n->setAttribute(Qt::WA_DeleteOnClose);
n->show();
}
void MainWindow::closeNewFileDialog()
{
if (newFileDialog) {
newFileDialog->close();
}
newFileDialog = nullptr;
}
void MainWindow::displayAnalysisOptionsDialog(int analLevel, QList<QString> advancedOptions)
{
OptionsDialog *o = new OptionsDialog(this);

View File

@ -8,6 +8,7 @@
#include "widgets/SidebarWidget.h"
#include "widgets/HexdumpWidget.h"
#include "widgets/PseudocodeWidget.h"
#include "dialogs/NewFileDialog.h"
#include "utils/Configuration.h"
#include <QMainWindow>
@ -59,6 +60,7 @@ public:
void openNewFile(const QString &fn, int analLevel = -1, QList<QString> advancedOptions = QList<QString>());
void displayNewFileDialog();
void closeNewFileDialog();
void displayAnalysisOptionsDialog(int analLevel, QList<QString> advancedOptions);
void openProject(const QString &project_name);
@ -187,6 +189,7 @@ private:
DisassemblerGraphView *graphView = nullptr;
QDockWidget *asmDock = nullptr;
QDockWidget *calcDock = nullptr;
NewFileDialog *newFileDialog = nullptr;
void toggleDockWidget(QDockWidget *dock_widget, bool show);

80
src/apple/Info.plist Normal file
View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>@EXECUTABLE@</string>
<key>CFBundleGetInfoString</key>
<string>Created by Qt/QMake</string>
<key>CFBundleIconFile</key>
<string>@ICON@</string>
<key>CFBundleIdentifier</key>
<string>@BUNDLEIDENTIFIER@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>@TYPEINFO@</string>
<key>NOTE</key>
<string>This file was generated by Qt/QMake.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Data</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>public.data</string>
</array>
<key>NSDocumentClass</key>
<string>FileDataDocument</string>
</dict>
<dict>
<key>CFBundleTypeName</key>
<string>Other</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.executable</string>
<string>public.text</string>
<string>public.archive</string>
<string>public.disk-image</string>
<string>public.image</string>
<string>public.audio</string>
<string>public.movie</string>
<string>com.adobe.pdf</string>
</array>
<key>NSDocumentClass</key>
<string>FileDataDocument</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>*</string>
</array>
<key>CFBundleTypeName</key>
<string>AllTypes</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>****</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>NSDocumentClass</key>
<string>FileDataDocument</string>
</dict>
</array>
</dict>
</plist>

View File

@ -24,6 +24,7 @@ win32 {
macx {
QMAKE_CXXFLAGS = -mmacosx-version-min=10.7 -std=gnu0x -stdlib=libc++
QMAKE_INFO_PLIST = apple/Info.plist
}
@ -87,7 +88,8 @@ SOURCES += \
dialogs/preferences/GraphOptionsWidget.cpp \
widgets/QuickFilterView.cpp \
widgets/ClassesWidget.cpp \
widgets/ResourcesWidget.cpp
widgets/ResourcesWidget.cpp \
CutterApplication.cpp
HEADERS += \
cutter.h \
@ -145,7 +147,8 @@ HEADERS += \
dialogs/preferences/GraphOptionsWidget.h \
widgets/QuickFilterView.h \
widgets/ClassesWidget.h \
widgets/ResourcesWidget.h
widgets/ResourcesWidget.h \
CutterApplication.h
FORMS += \
dialogs/AboutDialog.ui \

View File

@ -1,11 +1,5 @@
#include <QApplication>
#include <QCommandLineParser>
#include <QTextCodec>
#include <QMessageBox>
#include "CutterApplication.h"
#include "MainWindow.h"
#include "dialogs/NewFileDialog.h"
#include "dialogs/OptionsDialog.h"
#ifdef APPIMAGE
#define PREFIX "/tmp/.cutter_usr"
@ -33,79 +27,7 @@ void set_appimage_symlink()
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setOrganizationName("cutter");
a.setApplicationName("cutter");
a.setApplicationVersion(APP_VERSION);
a.setWindowIcon(QIcon(":/img/cutter.svg"));
// Set QString codec to UTF-8
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
#endif
QCommandLineParser cmd_parser;
cmd_parser.setApplicationDescription(QObject::tr("A Qt and C++ GUI for radare2 reverse engineering framework"));
cmd_parser.addHelpOption();
cmd_parser.addVersionOption();
cmd_parser.addPositionalArgument("filename", QObject::tr("Filename to open."));
QCommandLineOption analOption({"A", "anal"},
QObject::tr("Automatically open file and optionally start analysis. Needs filename to be specified. May be a value between 0 and 2: 0 = no analysis, 1 = aaa, 2 = aaaa (experimental)"),
QObject::tr("level"));
cmd_parser.addOption(analOption);
cmd_parser.process(a);
QStringList args = cmd_parser.positionalArguments();
// Check r2 version
QString r2version = r_core_version();
QString localVersion = "" R2_GITTAP;
if (r2version != localVersion)
{
QMessageBox msg;
msg.setIcon(QMessageBox::Critical);
msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msg.setWindowTitle(QObject::tr("Version mismatch!"));
msg.setText(QString(QObject::tr("The version used to compile cutter (%1) does not match the binary version of radare2 (%2). This could result in unexpected behaviour. Are you sure you want to continue?")).arg(localVersion, r2version));
if (msg.exec() == QMessageBox::No)
return 1;
}
bool analLevelSpecified = false;
int analLevel= 0;
if (cmd_parser.isSet(analOption))
{
analLevel = cmd_parser.value(analOption).toInt(&analLevelSpecified);
if (!analLevelSpecified || analLevel < 0 || analLevel > 2)
{
printf("%s\n", QObject::tr("Invalid Analysis Level. May be a value between 0 and 2.").toLocal8Bit().constData());
return 1;
}
}
if (args.empty())
{
if (analLevelSpecified)
{
printf("%s\n", QObject::tr("Filename must be specified to start analysis automatically.").toLocal8Bit().constData());
return 1;
}
MainWindow *main = new MainWindow();
main->displayNewFileDialog();
}
else // filename specified as positional argument
{
MainWindow *main = new MainWindow();
main->openNewFile(args[0], analLevelSpecified ? analLevel : -1);
}
CutterApplication a(argc, argv);
// Hack to make it work with AppImage
#ifdef APPIMAGE