diff options
Diffstat (limited to '')
-rw-r--r-- | src/gpg/GpgConstants.cpp | 47 | ||||
-rw-r--r-- | src/gpg/function/BasicOperator.cpp | 20 | ||||
-rw-r--r-- | src/gpg/function/BasicOperator.h | 3 | ||||
-rw-r--r-- | src/gpg/function/GpgFileOpera.cpp | 6 | ||||
-rw-r--r-- | src/main.cpp | 5 | ||||
-rw-r--r-- | src/ui/MainWindow.cpp | 2 | ||||
-rw-r--r-- | src/ui/main_window/MainWindowFileSlotFunction.cpp | 40 | ||||
-rw-r--r-- | src/ui/main_window/MainWindowSlotFunction.cpp | 76 | ||||
-rw-r--r-- | src/ui/widgets/FilePage.cpp | 256 | ||||
-rw-r--r-- | src/ui/widgets/FilePage.h | 16 | ||||
-rw-r--r-- | src/ui/widgets/InfoBoardWidget.cpp | 81 | ||||
-rw-r--r-- | src/ui/widgets/InfoBoardWidget.h | 25 | ||||
-rw-r--r-- | src/ui/widgets/TextEdit.cpp | 6 | ||||
-rw-r--r-- | ui/FilePage.ui | 243 | ||||
-rw-r--r-- | ui/InfoBoard.ui | 100 |
15 files changed, 566 insertions, 360 deletions
diff --git a/src/gpg/GpgConstants.cpp b/src/gpg/GpgConstants.cpp index e3de1d06..4a34ca34 100644 --- a/src/gpg/GpgConstants.cpp +++ b/src/gpg/GpgConstants.cpp @@ -28,6 +28,10 @@ #include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem.hpp> +#include <codecvt> +#include <iostream> +#include <locale> +#include <string> const char* GpgFrontend::GpgConstants::PGP_CRYPT_BEGIN = "-----BEGIN PGP MESSAGE-----"; @@ -112,14 +116,19 @@ static inline std::string trim(std::string& s) { std::string GpgFrontend::read_all_data_in_file(const std::string& path) { using namespace boost::filesystem; +#ifdef WINDOWS + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter; + std::wstring w_path = converter.from_bytes(path); + class path file_info(w_path.c_str()); +#else class path file_info(path.c_str()); +#endif - if (!exists(file_info) || !is_regular_file(path)) - throw std::runtime_error("no permission"); + if (!exists(file_info) || !is_regular_file(path)) return {}; std::ifstream in_file; in_file.open(path, std::ios::in); - if (!in_file.good()) throw std::runtime_error("cannot open file"); + if (!in_file.good()) return {}; std::istreambuf_iterator<char> begin(in_file); std::istreambuf_iterator<char> end; std::string in_buffer(begin, end); @@ -129,7 +138,16 @@ std::string GpgFrontend::read_all_data_in_file(const std::string& path) { bool GpgFrontend::write_buffer_to_file(const std::string& path, const std::string& out_buffer) { - std::ofstream out_file(boost::filesystem::path(path).string(), std::ios::out); +#ifdef WINDOWS + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter; + std::wstring w_path = converter.from_bytes(path); + std::ofstream out_file(boost::filesystem::path(w_path).string(), + std::ios::out); +#else + std::ofstream out_file(boost::filesystem::path(path).string(), + std::ios::out | std::ios::trunc); +#endif + if (!out_file.good()) return false; out_file.write(out_buffer.c_str(), out_buffer.size()); out_file.close(); @@ -137,8 +155,16 @@ bool GpgFrontend::write_buffer_to_file(const std::string& path, } std::string GpgFrontend::get_file_extension(const std::string& path) { - // Create a Path object from given string +#ifdef WINDOWS + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter; + std::wstring w_path = converter.from_bytes(path); + // Create a path object from given string + boost::filesystem::path path_obj(w_path); +#else + // Create a path object from given string boost::filesystem::path path_obj(path); +#endif + // Check if file name in the path object has extension if (path_obj.has_extension()) { // Fetch the extension from path object and return @@ -149,15 +175,22 @@ std::string GpgFrontend::get_file_extension(const std::string& path) { } std::string GpgFrontend::get_only_file_name_with_path(const std::string& path) { - // Create a Path object from given string +#ifdef WINDOWS + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter; + std::wstring w_path = converter.from_bytes(path); + // Create a path object from given string + boost::filesystem::path path_obj(w_path); +#else + // Create a path object from given string boost::filesystem::path path_obj(path); +#endif // Check if file name in the path object has extension if (path_obj.has_filename()) { // Fetch the extension from path object and return return (path_obj.parent_path() / path_obj.stem()).string(); } // In case of no extension return empty string - throw std::runtime_error("invalid file path"); + return {}; } /* diff --git a/src/gpg/function/BasicOperator.cpp b/src/gpg/function/BasicOperator.cpp index 912119e2..5f6ffb85 100644 --- a/src/gpg/function/BasicOperator.cpp +++ b/src/gpg/function/BasicOperator.cpp @@ -76,13 +76,15 @@ GpgFrontend::GpgError GpgFrontend::BasicOperator::Verify( GpgVerifyResult& result) const { gpgme_error_t err; + LOG(INFO) << "in buffer size" << in_buffer.size(); GpgData data_in(in_buffer.data(), in_buffer.size()); + GpgData data_out; if (sig_buffer != nullptr) { GpgData sig_data(sig_buffer->data(), sig_buffer->size()); err = check_gpg_error(gpgme_op_verify(ctx, sig_data, data_in, nullptr)); } else - err = check_gpg_error(gpgme_op_verify(ctx, data_in, nullptr, data_in)); + err = check_gpg_error(gpgme_op_verify(ctx, data_in, nullptr, data_out)); auto temp_result = GpgVerifyResult(gpgme_op_verify_result(ctx)); std::swap(result, temp_result); @@ -204,3 +206,19 @@ GpgFrontend::BasicOperator::GetSigners() { } return signers; } +gpg_error_t GpgFrontend::BasicOperator::EncryptSymmetric( + GpgFrontend::ByteArray& in_buffer, GpgFrontend::ByteArrayPtr& out_buffer, + GpgFrontend::GpgEncrResult& result) { + GpgData data_in(in_buffer.data(), in_buffer.size()), data_out; + + gpgme_error_t err = check_gpg_error(gpgme_op_encrypt( + ctx, nullptr, GPGME_ENCRYPT_SYMMETRIC, data_in, data_out)); + + auto temp_data_out = data_out.Read2Buffer(); + std::swap(temp_data_out, out_buffer); + + auto temp_result = GpgEncrResult(gpgme_op_encrypt_result(ctx)); + std::swap(result, temp_result); + + return err; +} diff --git a/src/gpg/function/BasicOperator.h b/src/gpg/function/BasicOperator.h index 39f93668..4ea70eea 100644 --- a/src/gpg/function/BasicOperator.h +++ b/src/gpg/function/BasicOperator.h @@ -37,6 +37,9 @@ class BasicOperator : public SingletonFunctionObject<BasicOperator> { gpg_error_t Encrypt(KeyListPtr keys, BypeArrayRef in_buffer, ByteArrayPtr& out_buffer, GpgEncrResult& result); + gpg_error_t EncryptSymmetric(BypeArrayRef in_buffer, ByteArrayPtr& out_buffer, + GpgEncrResult& result); + gpgme_error_t EncryptSign(KeyListPtr keys, KeyListPtr signers, BypeArrayRef in_buffer, ByteArrayPtr& out_buffer, GpgEncrResult& encr_result, diff --git a/src/gpg/function/GpgFileOpera.cpp b/src/gpg/function/GpgFileOpera.cpp index c3f75cf8..42b37c71 100644 --- a/src/gpg/function/GpgFileOpera.cpp +++ b/src/gpg/function/GpgFileOpera.cpp @@ -41,7 +41,7 @@ GpgFrontend::GpgError GpgFrontend::GpgFileOpera::EncryptFile( if (!write_buffer_to_file(path + ".asc", *out_buffer)) { throw std::runtime_error("write_buffer_to_file error"); }; - + return err; } @@ -94,7 +94,6 @@ gpgme_error_t GpgFrontend::GpgFileOpera::VerifyFile(const std::string& path, if (get_file_extension(path) == ".gpg") { auto err = BasicOperator::GetInstance().Verify(in_buffer, sign_buffer, result); - assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); return err; } else { sign_buffer = @@ -150,8 +149,7 @@ gpg_error_t GpgFrontend::GpgFileOpera::DecryptVerifyFile( if (!(file_extension == ".asc" || file_extension == ".gpg")) out_file_name = path + ".out"; - LOG(INFO) << "GpgFrontend::GpgFileOpera::DecryptVerifyFile out_file_name" - << out_file_name; + LOG(INFO) << "out_file_name" << out_file_name; if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) if (!write_buffer_to_file(out_file_name, *out_buffer)) { diff --git a/src/main.cpp b/src/main.cpp index 1dbf1d5e..3bd9e8a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,7 +40,10 @@ int main(int argc, char* argv[]) { // Qt App QApplication app(argc, argv); + +#ifndef MACOS QApplication::setWindowIcon(QIcon(":gpgfrontend.png")); +#endif #ifdef MACOS // support retina screen @@ -60,7 +63,7 @@ int main(int argc, char* argv[]) { // unicode in source QTextCodec::setCodecForLocale(QTextCodec::codecForName("utf-8")); -#if !defined(RELEASE) +#if !defined(RELEASE) && defined(WINDOWS) // css QFile file(RESOURCE_DIR(qApp->applicationDirPath()) + "/css/default.qss"); file.open(QFile::ReadOnly); diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 9aceed96..f9266297 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -64,7 +64,7 @@ void MainWindow::init() noexcept { mKeyList->slotRefresh(); - infoBoard = new InfoBoardWidget(this, mKeyList); + infoBoard = new InfoBoardWidget(this); /* List of binary Attachments */ attachmentDockCreated = false; diff --git a/src/ui/main_window/MainWindowFileSlotFunction.cpp b/src/ui/main_window/MainWindowFileSlotFunction.cpp index 19be9769..133a7820 100644 --- a/src/ui/main_window/MainWindowFileSlotFunction.cpp +++ b/src/ui/main_window/MainWindowFileSlotFunction.cpp @@ -245,14 +245,16 @@ void MainWindow::slotFileVerify() { signFilePath = path + ".sig"; } - bool ok; - QString text = - QInputDialog::getText(this, _("Origin file to verify"), _("Filepath"), - QLineEdit::Normal, dataFilePath, &ok); - if (ok && !text.isEmpty()) { - dataFilePath = text; - } else { - return; + if (fileInfo.suffix() != "gpg") { + bool ok; + QString text = + QInputDialog::getText(this, _("Origin file to verify"), _("Filepath"), + QLineEdit::Normal, dataFilePath, &ok); + if (ok && !text.isEmpty()) { + dataFilePath = text; + } else { + return; + } } QFileInfo dataFileInfo(dataFilePath), signFileInfo(signFilePath); @@ -395,13 +397,23 @@ void MainWindow::slotFileDecryptVerify() { if (!file_pre_check(this, path)) return; - QString outFileName, fileExtension = QFileInfo(path).suffix(); - - if (fileExtension == "asc" || fileExtension == "gpg") { - int pos = path.lastIndexOf(QChar('.')); - outFileName = path.left(pos); + boost::filesystem::path out_path(path.toStdString()); + if (out_path.extension() == ".asc" || out_path.extension() == ".gpg") { + out_path = out_path.parent_path() / out_path.filename(); } else { - outFileName = path + ".out"; + out_path = out_path.replace_extension(".out").string(); + } + LOG(INFO) << "out path" << out_path; + + if (QFile::exists(out_path.string().c_str())) { + auto ret = + QMessageBox::warning(this, _("Warning"), + QString(_("The output file %1 already exists, do " + "you need to overwrite it?")) + .arg(out_path.filename().string().c_str()), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) return; } GpgDecrResult d_result = nullptr; diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index 4ad33ac3..41549182 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -51,40 +51,56 @@ void MainWindow::slotEncrypt() { auto key_ids = mKeyList->getChecked(); - if (key_ids->empty()) { - QMessageBox::critical(nullptr, _("No Key Selected"), _("No Key Selected")); - return; - } - - auto key_getter = GpgFrontend::GpgKeyGetter::GetInstance(); - auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); - for (const auto& key : *keys) { - if (!key.CanEncrActual()) { - QMessageBox::information( - nullptr, _("Invalid Operation"), - QString( - _("The selected key contains a key that does not actually have a " - "encrypt usage.")) + - "<br/><br/>" + _("For example the Following Key:") + " <br/>" + - QString::fromStdString(key.uids()->front().uid())); - return; - } - } - - auto tmp = std::make_unique<ByteArray>(); - GpgEncrResult result = nullptr; GpgError error; bool if_error = false; - process_operation(this, _("Encrypting"), [&]() { - try { - auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); - error = GpgFrontend::BasicOperator::GetInstance().Encrypt( - std::move(keys), buffer, tmp, result); - } catch (const std::runtime_error& e) { - if_error = true; + auto tmp = std::make_unique<ByteArray>(); + + if (key_ids->empty()) { + // Symmetric Encrypt + auto ret = + QMessageBox::warning(this, _("Warning"), + _("No Key Selected. Do you want to encrypt with a " + "symmetric cipher using a passphrase?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) return; + + process_operation(this, _("Symmetrically Encrypting"), [&]() { + try { + auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); + error = GpgFrontend::BasicOperator::GetInstance().EncryptSymmetric( + buffer, tmp, result); + } catch (const std::runtime_error& e) { + if_error = true; + } + }); + } else { + auto key_getter = GpgFrontend::GpgKeyGetter::GetInstance(); + auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); + for (const auto& key : *keys) { + if (!key.CanEncrActual()) { + QMessageBox::information( + nullptr, _("Invalid Operation"), + QString(_( + "The selected key contains a key that does not actually have a " + "encrypt usage.")) + + "<br/><br/>" + _("For example the Following Key:") + " <br/>" + + QString::fromStdString(key.uids()->front().uid())); + return; + } } - }); + + process_operation(this, _("Encrypting"), [&]() { + try { + auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); + error = GpgFrontend::BasicOperator::GetInstance().Encrypt( + std::move(keys), buffer, tmp, result); + } catch (const std::runtime_error& e) { + if_error = true; + } + }); + } if (!if_error) { auto resultAnalyse = EncryptResultAnalyse(error, std::move(result)); diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp index c51e0c76..f73d9104 100644 --- a/src/ui/widgets/FilePage.cpp +++ b/src/ui/widgets/FilePage.cpp @@ -25,58 +25,36 @@ #include "ui/widgets/FilePage.h" #include <boost/filesystem.hpp> +#include <codecvt> +#include <iostream> +#include <locale> +#include <string> #include "ui/MainWindow.h" #include "ui/SignalStation.h" namespace GpgFrontend::UI { -FilePage::FilePage(QWidget* parent) : QWidget(parent) { +FilePage::FilePage(QWidget* parent) : QWidget(parent), Ui_FilePage() { + setupUi(this); + firstParent = parent; dirModel = new QFileSystemModel(); dirModel->setRootPath(QDir::currentPath()); + dirModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); - dirTreeView = new QTreeView(); - dirTreeView->setModel(dirModel); - dirTreeView->setAnimated(true); - dirTreeView->setIndentation(20); - dirTreeView->setContextMenuPolicy(Qt::CustomContextMenu); - dirTreeView->setColumnWidth(0, 320); - dirTreeView->setRootIndex(dirModel->index(QDir::currentPath())); + fileTreeView->setModel(dirModel); + fileTreeView->setColumnWidth(0, 320); + fileTreeView->sortByColumn(0, Qt::AscendingOrder); mPath = boost::filesystem::path(dirModel->rootPath().toStdString()); createPopupMenu(); - upLevelButton = new QPushButton(); - connect(upLevelButton, SIGNAL(clicked(bool)), this, SLOT(slotUpLevel())); - - QString buttonStyle = - "QPushButton{border:none;background-color:rgba(255, 255, 255, 0);}"; - - auto upPixmap = QPixmap(":up.png"); - upPixmap = - upPixmap.scaled(18, 18, Qt::KeepAspectRatio, Qt::SmoothTransformation); - QIcon upButtonIcon(upPixmap); - upLevelButton->setIcon(upButtonIcon); - upLevelButton->setIconSize(upPixmap.rect().size()); - upLevelButton->setStyleSheet(buttonStyle); - - refreshButton = new QPushButton(_("Refresh")); - connect(refreshButton, SIGNAL(clicked(bool)), this, SLOT(slotGoPath())); - - goPathButton = new QPushButton(); - connect(goPathButton, SIGNAL(clicked(bool)), this, SLOT(slotGoPath())); + connect(upPathButton, &QPushButton::clicked, this, &FilePage::slotUpLevel); + connect(refreshButton, &QPushButton::clicked, this, &FilePage::slotGoPath); + optionsButton->setMenu(optionPopUpMenu); - auto updatePixmap = QPixmap(":refresh.png"); - updatePixmap = updatePixmap.scaled(18, 18, Qt::KeepAspectRatio, - Qt::SmoothTransformation); - QIcon updateButtonIcon(updatePixmap); - goPathButton->setIcon(updateButtonIcon); - goPathButton->setIconSize(updatePixmap.rect().size()); - goPathButton->setStyleSheet(buttonStyle); - - pathEdit = new QLineEdit(); pathEdit->setText(dirModel->rootPath()); pathEditCompleter = new QCompleter(this); @@ -86,29 +64,11 @@ FilePage::FilePage(QWidget* parent) : QWidget(parent) { pathEditCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion); pathEdit->setCompleter(pathEditCompleter); - auto* menuLayout = new QHBoxLayout(); - menuLayout->addWidget(upLevelButton); - menuLayout->setStretchFactor(upLevelButton, 1); - menuLayout->addWidget(pathEdit); - menuLayout->setStretchFactor(pathEdit, 10); - menuLayout->addWidget(goPathButton); - menuLayout->setStretchFactor(goPathButton, 1); - - auto* layout = new QVBoxLayout(); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addLayout(menuLayout); - layout->setStretchFactor(menuLayout, 1); - layout->addWidget(dirTreeView); - layout->setStretchFactor(dirTreeView, 8); - - this->setLayout(layout); - - connect(dirTreeView, &QTreeView::clicked, this, + connect(fileTreeView, &QTreeView::clicked, this, &FilePage::fileTreeViewItemClicked); - connect(dirTreeView, &QTreeView::doubleClicked, this, + connect(fileTreeView, &QTreeView::doubleClicked, this, &FilePage::fileTreeViewItemDoubleClicked); - connect(dirTreeView, &QTreeView::customContextMenuRequested, this, + connect(fileTreeView, &QTreeView::customContextMenuRequested, this, &FilePage::onCustomContextMenu); connect(pathEdit, &QLineEdit::textChanged, [=]() { @@ -139,22 +99,37 @@ void FilePage::fileTreeViewItemClicked(const QModelIndex& index) { } void FilePage::slotUpLevel() { - QModelIndex currentRoot = dirTreeView->rootIndex(); + QModelIndex currentRoot = fileTreeView->rootIndex(); + + auto utf8_path = + dirModel->fileInfo(currentRoot).absoluteFilePath().toStdString(); - mPath = boost::filesystem::path( - dirModel->fileInfo(currentRoot).absoluteFilePath().toStdString()); +#ifdef WINDOWS + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter; + std::wstring w_path = converter.from_bytes(utf8_path); + boost::filesystem::path path_obj(w_path); +#else + boost::filesystem::path path_obj(utf8_path); +#endif + + mPath = path_obj; LOG(INFO) << "get path" << mPath; - if (mPath.has_parent_path()) { + if (mPath.has_parent_path() && !mPath.parent_path().empty()) { mPath = mPath.parent_path(); LOG(INFO) << "parent path" << mPath; pathEdit->setText(mPath.string().c_str()); - slotGoPath(); + this->slotGoPath(); } } void FilePage::fileTreeViewItemDoubleClicked(const QModelIndex& index) { - pathEdit->setText(dirModel->fileInfo(index).absoluteFilePath()); - slotGoPath(); + QFileInfo file_info(dirModel->fileInfo(index).absoluteFilePath()); + if (file_info.isFile()) { + slotOpenItem(); + } else { + pathEdit->setText(file_info.filePath()); + slotGoPath(); + } } QString FilePage::getSelected() const { @@ -163,18 +138,26 @@ QString FilePage::getSelected() const { void FilePage::slotGoPath() { const auto path_edit = pathEdit->text().toStdString(); - LOG(INFO) << "get path edit" << path_edit; - if (mPath.string() != path_edit) mPath = path_edit; + +#ifdef WINDOWS + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter; + std::wstring w_path = converter.from_bytes(path_edit); + boost::filesystem::path path_obj(w_path); +#else + boost::filesystem::path path_obj(path_edit); +#endif + + if (mPath.string() != path_edit) mPath = path_obj; auto fileInfo = QFileInfo(mPath.string().c_str()); if (fileInfo.isDir() && fileInfo.isReadable() && fileInfo.isExecutable()) { mPath = boost::filesystem::path(fileInfo.filePath().toStdString()); LOG(INFO) << "set path" << mPath; - dirTreeView->setRootIndex(dirModel->index(fileInfo.filePath())); + fileTreeView->setRootIndex(dirModel->index(fileInfo.filePath())); + dirModel->setRootPath(fileInfo.filePath()); for (int i = 1; i < dirModel->columnCount(); ++i) { - dirTreeView->resizeColumnToContents(i); + fileTreeView->resizeColumnToContents(i); } pathEdit->setText(mPath.generic_path().string().c_str()); - } else { QMessageBox::critical( this, _("Error"), @@ -186,26 +169,35 @@ void FilePage::slotGoPath() { void FilePage::createPopupMenu() { popUpMenu = new QMenu(); - auto openItemAct = new QAction(_("Open"), this); - connect(openItemAct, SIGNAL(triggered()), this, SLOT(slotOpenItem())); - auto renameItemAct = new QAction(_("Rename"), this); - connect(renameItemAct, SIGNAL(triggered()), this, SLOT(slotRenameItem())); - auto deleteItemAct = new QAction(_("Delete"), this); - connect(deleteItemAct, SIGNAL(triggered()), this, SLOT(slotDeleteItem())); + openItemAct = new QAction(_("Open"), this); + connect(openItemAct, &QAction::triggered, this, &FilePage::slotOpenItem); + renameItemAct = new QAction(_("Rename"), this); + connect(renameItemAct, &QAction::triggered, this, &FilePage::slotRenameItem); + deleteItemAct = new QAction(_("Delete"), this); + connect(deleteItemAct, &QAction::triggered, this, &FilePage::slotDeleteItem); encryptItemAct = new QAction(_("Encrypt Sign"), this); - connect(encryptItemAct, SIGNAL(triggered()), this, SLOT(slotEncryptItem())); + connect(encryptItemAct, &QAction::triggered, this, + &FilePage::slotEncryptItem); decryptItemAct = new QAction(QString(_("Decrypt Verify")) + " " + _("(.gpg .asc)"), this); - connect(decryptItemAct, SIGNAL(triggered()), this, SLOT(slotDecryptItem())); + connect(decryptItemAct, &QAction::triggered, this, + &FilePage::slotDecryptItem); signItemAct = new QAction(_("Sign"), this); - connect(signItemAct, SIGNAL(triggered()), this, SLOT(slotSignItem())); + connect(signItemAct, &QAction::triggered, this, &FilePage::slotSignItem); verifyItemAct = new QAction(QString(_("Verify")) + " " + _("(.sig .gpg .asc)"), this); - connect(verifyItemAct, SIGNAL(triggered()), this, SLOT(slotVerifyItem())); + connect(verifyItemAct, &QAction::triggered, this, &FilePage::slotVerifyItem); hashCalculateAct = new QAction(_("Calculate Hash"), this); - connect(hashCalculateAct, SIGNAL(triggered()), this, - SLOT(slotCalculateHash())); + connect(hashCalculateAct, &QAction::triggered, this, + &FilePage::slotCalculateHash); + + mkdirAct = new QAction(_("Make New Directory"), this); + connect(mkdirAct, &QAction::triggered, this, &FilePage::slotMkdir); + + createEmptyFileAct = new QAction(_("Create Empty File"), this); + connect(createEmptyFileAct, &QAction::triggered, this, + &FilePage::slotCreateEmptyFile); popUpMenu->addAction(openItemAct); popUpMenu->addAction(renameItemAct); @@ -216,15 +208,47 @@ void FilePage::createPopupMenu() { popUpMenu->addAction(signItemAct); popUpMenu->addAction(verifyItemAct); popUpMenu->addSeparator(); + popUpMenu->addAction(mkdirAct); + popUpMenu->addAction(createEmptyFileAct); popUpMenu->addAction(hashCalculateAct); + + optionPopUpMenu = new QMenu(); + + auto showHiddenAct = new QAction(_("Show Hidden File"), this); + showHiddenAct->setCheckable(true); + connect(showHiddenAct, &QAction::triggered, this, [&](bool checked) { + LOG(INFO) << "Set Hidden" << checked; + if (checked) + dirModel->setFilter(dirModel->filter() | QDir::Hidden); + else + dirModel->setFilter(dirModel->filter() & ~QDir::Hidden); + dirModel->setRootPath(mPath.string().c_str()); + }); + optionPopUpMenu->addAction(showHiddenAct); + + auto showSystemAct = new QAction(_("Show System File"), this); + showSystemAct->setCheckable(true); + connect(showSystemAct, &QAction::triggered, this, [&](bool checked) { + LOG(INFO) << "Set Hidden" << checked; + if (checked) + dirModel->setFilter(dirModel->filter() | QDir::System); + else + dirModel->setFilter(dirModel->filter() & ~QDir::System); + dirModel->setRootPath(mPath.string().c_str()); + }); + optionPopUpMenu->addAction(showSystemAct); } void FilePage::onCustomContextMenu(const QPoint& point) { - QModelIndex index = dirTreeView->indexAt(point); + QModelIndex index = fileTreeView->indexAt(point); selectedPath = boost::filesystem::path( dirModel->fileInfo(index).absoluteFilePath().toStdString()); LOG(INFO) << "right click" << selectedPath; if (index.isValid()) { + openItemAct->setEnabled(true); + renameItemAct->setEnabled(true); + deleteItemAct->setEnabled(true); + QFileInfo info(QString::fromStdString(selectedPath.string())); encryptItemAct->setEnabled( info.isFile() && (info.suffix() != "gpg" && info.suffix() != "sig")); @@ -236,9 +260,19 @@ void FilePage::onCustomContextMenu(const QPoint& point) { verifyItemAct->setEnabled( info.isFile() && (info.suffix() == "sig" || info.suffix() == "gpg")); hashCalculateAct->setEnabled(info.isFile() && info.isReadable()); - - popUpMenu->exec(dirTreeView->viewport()->mapToGlobal(point)); + } else { + openItemAct->setEnabled(false); + renameItemAct->setEnabled(false); + deleteItemAct->setEnabled(false); + + encryptItemAct->setEnabled(false); + encryptItemAct->setEnabled(false); + decryptItemAct->setEnabled(false); + signItemAct->setEnabled(false); + verifyItemAct->setEnabled(false); + hashCalculateAct->setEnabled(false); } + popUpMenu->exec(fileTreeView->viewport()->mapToGlobal(point)); } void FilePage::slotOpenItem() { @@ -291,8 +325,8 @@ void FilePage::slotRenameItem() { } void FilePage::slotDeleteItem() { - QModelIndex index = dirTreeView->currentIndex(); - QVariant data = dirTreeView->model()->data(index); + QModelIndex index = fileTreeView->currentIndex(); + QVariant data = fileTreeView->model()->data(index); auto ret = QMessageBox::warning(this, _("Warning"), _("Are you sure you want to delete it?"), @@ -300,7 +334,7 @@ void FilePage::slotDeleteItem() { if (ret == QMessageBox::Cancel) return; - qDebug() << "Delete Item" << data.toString(); + LOG(INFO) << "Delete Item" << data.toString().toStdString(); if (!dirModel->remove(index)) { QMessageBox::critical(this, _("Error"), @@ -377,10 +411,54 @@ void FilePage::slotCalculateHash() { } } +void FilePage::slotMkdir() { + auto index = fileTreeView->rootIndex(); + + QString new_dir_name; + bool ok; + new_dir_name = + QInputDialog::getText(this, _("Make New Directory"), _("Directory Name"), + QLineEdit::Normal, new_dir_name, &ok); + if (ok && !new_dir_name.isEmpty()) { + dirModel->mkdir(index, new_dir_name); + } +} + +void FilePage::slotCreateEmptyFile() { + auto root_path_str = dirModel->rootPath().toStdString(); +#ifdef WINDOWS + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter; + std::wstring w_path = converter.from_bytes(root_path_str); + boost::filesystem::path root_path(w_path); +#else + boost::filesystem::path root_path(root_path_str); +#endif + QString new_file_name; + bool ok; + new_file_name = QInputDialog::getText(this, _("Create Empty File"), + _("Filename (you can given extension)"), + QLineEdit::Normal, new_file_name, &ok); + if (ok && !new_file_name.isEmpty()) { + auto file_path = root_path / new_file_name.toStdString(); + QFile new_file(file_path.string().c_str()); + if (!new_file.open(QIODevice::WriteOnly | QIODevice::NewOnly)) { + QMessageBox::critical(this, _("Error"), _("Unable to create the file.")); + } + new_file.close(); + } +} + void FilePage::keyPressEvent(QKeyEvent* event) { - qDebug() << "Key Press" << event->key(); - if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + LOG(INFO) << "Key Press" << event->key(); + if (pathEdit->hasFocus() && + (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)) { slotGoPath(); + } else if (fileTreeView->currentIndex().isValid()) { + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + slotOpenItem(); + else if (event->key() == Qt::Key_Delete || + event->key() == Qt::Key_Backspace) + slotDeleteItem(); } } diff --git a/src/ui/widgets/FilePage.h b/src/ui/widgets/FilePage.h index 247b5a08..d9492f6e 100644 --- a/src/ui/widgets/FilePage.h +++ b/src/ui/widgets/FilePage.h @@ -33,7 +33,7 @@ namespace GpgFrontend::UI { -class FilePage : public QWidget { +class FilePage : public QWidget, private Ui_FilePage { Q_OBJECT public: explicit FilePage(QWidget* parent = nullptr); @@ -64,6 +64,8 @@ class FilePage : public QWidget { void slotSignItem(); void slotVerifyItem(); void slotCalculateHash(); + void slotMkdir(); + void slotCreateEmptyFile(); void onCustomContextMenu(const QPoint& point); @@ -74,8 +76,6 @@ class FilePage : public QWidget { void createPopupMenu(); QFileSystemModel* dirModel; - QTreeView* dirTreeView; - QLineEdit* pathEdit; QCompleter* pathEditCompleter; QStringListModel* pathCompleteModel; @@ -83,16 +83,18 @@ class FilePage : public QWidget { boost::filesystem::path mPath; boost::filesystem::path selectedPath; - QPushButton* upLevelButton; - QPushButton* goPathButton; - QPushButton* refreshButton; - QMenu* popUpMenu{}; + QMenu* optionPopUpMenu{}; QAction* encryptItemAct{}; QAction* decryptItemAct{}; QAction* signItemAct{}; QAction* verifyItemAct{}; QAction* hashCalculateAct{}; + QAction* mkdirAct{}; + QAction* openItemAct{}; + QAction* renameItemAct{}; + QAction* deleteItemAct{}; + QAction* createEmptyFileAct{}; QWidget* firstParent; }; diff --git a/src/ui/widgets/InfoBoardWidget.cpp b/src/ui/widgets/InfoBoardWidget.cpp index e0de75f9..7bb47bb6 100644 --- a/src/ui/widgets/InfoBoardWidget.cpp +++ b/src/ui/widgets/InfoBoardWidget.cpp @@ -26,81 +26,22 @@ #include "ui/SignalStation.h" #include "ui/settings/GlobalSettingStation.h" +#include "ui_InfoBoard.h" namespace GpgFrontend::UI { -InfoBoardWidget::InfoBoardWidget(QWidget* parent, KeyList* keyList) - : QWidget(parent), mKeyList(keyList) { - infoBoard = new QTextEdit(this); - infoBoard->setReadOnly(true); - infoBoard->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - infoBoard->setMinimumWidth(480); - infoBoard->setContentsMargins(0, 0, 0, 0); - - importFromKeyserverAct = - new QAction(_("Import missing key from Keyserver"), this); - connect(importFromKeyserverAct, SIGNAL(triggered()), this, - SLOT(slotImportFromKeyserver())); - - detailMenu = new QMenu(this); - detailMenu->addAction(importFromKeyserverAct); - importFromKeyserverAct->setVisible(false); - - auto* action_button_menu = new QWidget(); - action_button_menu->setContentsMargins(0, 0, 0, 0); - action_button_menu->setSizePolicy(QSizePolicy::Preferred, - QSizePolicy::Minimum); - action_button_menu->setFixedHeight(40); - - actionButtonLayout = new QHBoxLayout(); - actionButtonLayout->setContentsMargins(0, 0, 0, 0); - actionButtonLayout->setSpacing(0); - - auto* label = new QLabel(_("Actions")); - label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - label->setContentsMargins(0, 0, 0, 0); - mButtonGroup = new QButtonGroup(this); - - auto* bottom_layout = new QHBoxLayout(this); - bottom_layout->addWidget(label); +InfoBoardWidget::InfoBoardWidget(QWidget* parent) + : QWidget(parent), Ui_InfoBoard() { + setupUi(this); + actionButtonLayout->addStretch(); - bottom_layout->addLayout(actionButtonLayout); - action_button_menu->setLayout(bottom_layout); - - QFrame* line; - line = new QFrame(this); - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - line->setContentsMargins(0, 0, 0, 0); - - auto* notificationWidgetLayout = new QVBoxLayout(this); - notificationWidgetLayout->setContentsMargins(0, 0, 0, 0); - notificationWidgetLayout->setSpacing(0); - - notificationWidgetLayout->addWidget(infoBoard); - notificationWidgetLayout->setStretchFactor(infoBoard, 10); - notificationWidgetLayout->addWidget(action_button_menu); - notificationWidgetLayout->setStretchFactor(action_button_menu, 1); - notificationWidgetLayout->addWidget(line); - notificationWidgetLayout->setStretchFactor(line, 1); - notificationWidgetLayout->addStretch(0); - this->setLayout(notificationWidgetLayout); + actionLabel->setText(_("Actions Menu")); + copyButton->setText(_("Copy")); + saveButton->setText(_("Save")); + clearButton->setText(_("Clear")); connect(SignalStation::GetInstance(), &SignalStation::signalRefreshInfoBoard, this, &InfoBoardWidget::slotRefresh); - - // set default size - infoBoard->resize(480, 120); - resize(480, 120); -} - -void InfoBoardWidget::slotImportFromKeyserver() { - auto* importDialog = new KeyServerImportDialog(false, this); - auto key_ids = std::make_unique<KeyIdArgsList>(); - for (const auto& key_id : *keysNotInList) { - key_ids->push_back(key_id.toStdString()); - } - importDialog->slotImport(key_ids); } void InfoBoardWidget::setInfoBoard(const QString& text, @@ -156,9 +97,6 @@ void InfoBoardWidget::associateTextEdit(QTextEdit* edit) { void InfoBoardWidget::associateTabWidget(QTabWidget* tab) { if (mTextPage != nullptr) disconnect(mTextPage, SIGNAL(textChanged()), this, SLOT(slotReset())); - // if (mFileTreeView != nullptr) - // disconnect(mFileTreeView, &FilePage::pathChanged, this, - // &InfoBoardWidget::slotReset); if (mTabWidget != nullptr) { disconnect(mTabWidget, SIGNAL(tabBarClicked(int)), this, SLOT(slotReset())); connect(mTabWidget, SIGNAL(tabCloseRequested(int)), this, @@ -173,6 +111,7 @@ void InfoBoardWidget::associateTabWidget(QTabWidget* tab) { void InfoBoardWidget::addOptionalAction(const QString& name, const std::function<void()>& action) { + LOG(INFO) << "add option" << name.toStdString(); auto actionButton = new QPushButton(name); auto layout = new QHBoxLayout(); layout->setContentsMargins(5, 0, 5, 0); diff --git a/src/ui/widgets/InfoBoardWidget.h b/src/ui/widgets/InfoBoardWidget.h index b7239adb..05a3b345 100644 --- a/src/ui/widgets/InfoBoardWidget.h +++ b/src/ui/widgets/InfoBoardWidget.h @@ -28,6 +28,7 @@ #include "EditorPage.h" #include "gpg/result_analyse/VerifyResultAnalyse.h" #include "ui/details/VerifyDetailsDialog.h" +#include "ui_InfoBoard.h" namespace GpgFrontend::UI { @@ -44,7 +45,7 @@ typedef enum { /** * @brief Class for handling the verifylabel shown at buttom of a textedit-page */ -class InfoBoardWidget : public QWidget { +class InfoBoardWidget : public QWidget, private Ui_InfoBoard { Q_OBJECT public: /** @@ -53,7 +54,7 @@ class InfoBoardWidget : public QWidget { * @param ctx The GPGme-Context * @param parent The parent widget */ - explicit InfoBoardWidget(QWidget* parent, KeyList* keyList); + explicit InfoBoardWidget(QWidget* parent); void associateTextEdit(QTextEdit* edit); @@ -72,17 +73,8 @@ class InfoBoardWidget : public QWidget { */ void setInfoBoard(const QString& text, InfoBoardStatus verifyLabelStatus); - QStringList* keysNotInList; /** List with keys, which are in signature but not - in keylist */ - public slots: - /** - * @details Import the keys contained in keysNotInList from keyserver - * - */ - void slotImportFromKeyserver(); - void slotReset(); /** @@ -91,18 +83,9 @@ class InfoBoardWidget : public QWidget { void slotRefresh(const QString& text, InfoBoardStatus status); private: - QMenu* detailMenu; /** Menu for te Button in verfiyNotification */ - QAction* importFromKeyserverAct; /** Action for importing keys from keyserver - which are notin keylist */ - QTextEdit* infoBoard; - KeyList* mKeyList; /** Table holding the keys */ QTextEdit* mTextPage{nullptr}; /** TextEdit associated to the notification */ - QTabWidget* mTabWidget{ - nullptr}; /** TreeView associated to the notification */ - - QHBoxLayout* actionButtonLayout; - QButtonGroup* mButtonGroup; + QTabWidget* mTabWidget{nullptr}; void deleteWidgetsInLayout(QLayout* layout, int start_index = 0); }; diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index ec6c6c7a..036c69d4 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -77,12 +77,13 @@ void TextEdit::slotNewFileTab() const { void TextEdit::slotOpenFile(QString& path) { QFile file(path); - LOG(INFO) << " path" << path.toStdString(); + LOG(INFO) << "path" << path.toStdString(); auto result = file.open(QIODevice::ReadOnly | QIODevice::Text); if (result) { auto* page = new EditorPage(path); QApplication::setOverrideCursor(Qt::WaitCursor); - tabWidget->addTab(page, strippedName(path)); + auto index = tabWidget->addTab(page, strippedName(path)); + tabWidget->setTabIcon(index, QIcon(":file.png")); tabWidget->setCurrentIndex(tabWidget->count() - 1); QApplication::restoreOverrideCursor(); page->getTextPage()->setFocus(); @@ -96,7 +97,6 @@ void TextEdit::slotOpenFile(QString& path) { } file.close(); - LOG(INFO) << "done"; } void TextEdit::slotOpen() { diff --git a/ui/FilePage.ui b/ui/FilePage.ui index 4e7e8993..6d7f7d7a 100644 --- a/ui/FilePage.ui +++ b/ui/FilePage.ui @@ -6,122 +6,143 @@ <rect> <x>0</x> <y>0</y> - <width>523</width> - <height>370</height> + <width>1041</width> + <height>619</height> </rect> </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>1041</width> + <height>619</height> + </size> + </property> <property name="windowTitle"> <string>Form</string> </property> - <widget class="QWidget" name="verticalLayoutWidget"> - <property name="geometry"> - <rect> - <x>10</x> - <y>20</y> - <width>471</width> - <height>301</height> - </rect> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0,0"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> - <item> - <widget class="QLineEdit" name="pathEdit"> - <property name="tabletTracking"> - <bool>false</bool> - </property> - <property name="text"> - <string/> - </property> - <property name="clearButtonEnabled"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="refreshButton"> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset resource="../gpgfrontend.qrc"> - <normaloff>:/refresh.png</normaloff>:/refresh.png</iconset> - </property> - <property name="flat"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="upPathButton"> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset resource="../gpgfrontend.qrc"> - <normaloff>:/up.png</normaloff>:/up.png</iconset> - </property> - <property name="default"> - <bool>false</bool> - </property> - <property name="flat"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="optionsButton"> - <property name="text"> - <string>...</string> - </property> - <property name="icon"> - <iconset resource="../gpgfrontend.qrc"> - <normaloff>:/configure.png</normaloff>:/configure.png</iconset> - </property> - <property name="popupMode"> - <enum>QToolButton::MenuButtonPopup</enum> - </property> - <property name="toolButtonStyle"> - <enum>Qt::ToolButtonIconOnly</enum> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QTreeView" name="fileTreeView"> - <property name="acceptDrops"> - <bool>true</bool> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="uniformRowHeights"> - <bool>true</bool> - </property> - <property name="sortingEnabled"> - <bool>true</bool> - </property> - <property name="animated"> - <bool>true</bool> - </property> - <attribute name="headerCascadingSectionResizes"> - <bool>true</bool> - </attribute> - <attribute name="headerShowSortIndicator" stdset="0"> - <bool>true</bool> - </attribute> - </widget> - </item> - </layout> - </item> - </layout> - </widget> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetMaximumSize</enum> + </property> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="sizeConstraint"> + <enum>QLayout::SetMaximumSize</enum> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0,0"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <widget class="QLineEdit" name="pathEdit"> + <property name="tabletTracking"> + <bool>false</bool> + </property> + <property name="text"> + <string/> + </property> + <property name="clearButtonEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="refreshButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../gpgfrontend.qrc"> + <normaloff>:/refresh.png</normaloff>:/refresh.png</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="upPathButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../gpgfrontend.qrc"> + <normaloff>:/up.png</normaloff>:/up.png</iconset> + </property> + <property name="default"> + <bool>false</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="optionsButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../gpgfrontend.qrc"> + <normaloff>:/configure.png</normaloff>:/configure.png</iconset> + </property> + <property name="popupMode"> + <enum>QToolButton::MenuButtonPopup</enum> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonIconOnly</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="fileTreeView"> + <property name="contextMenuPolicy"> + <enum>Qt::CustomContextMenu</enum> + </property> + <property name="acceptDrops"> + <bool>true</bool> + </property> + <property name="autoFillBackground"> + <bool>false</bool> + </property> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <property name="animated"> + <bool>true</bool> + </property> + <attribute name="headerCascadingSectionResizes"> + <bool>true</bool> + </attribute> + <attribute name="headerShowSortIndicator" stdset="0"> + <bool>true</bool> + </attribute> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> </widget> <resources> <include location="../gpgfrontend.qrc"/> diff --git a/ui/InfoBoard.ui b/ui/InfoBoard.ui new file mode 100644 index 00000000..dcfe9e4e --- /dev/null +++ b/ui/InfoBoard.ui @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>InfoBoard</class> + <widget class="QWidget" name="InfoBoard"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>788</width> + <height>604</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QTextEdit" name="infoBoard"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item alignment="Qt::AlignLeft"> + <widget class="QWidget" name="horizontalWidget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item alignment="Qt::AlignLeft"> + <widget class="QLabel" name="actionLabel"> + <property name="text"> + <string>Actions</string> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + </widget> + </item> + <item alignment="Qt::AlignRight"> + <widget class="QPushButton" name="copyButton"> + <property name="text"> + <string>Copy</string> + </property> + </widget> + </item> + <item alignment="Qt::AlignRight"> + <widget class="QPushButton" name="saveButton"> + <property name="text"> + <string>Save</string> + </property> + </widget> + </item> + <item alignment="Qt::AlignRight|Qt::AlignBottom"> + <widget class="QPushButton" name="clearButton"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="actionButtonLayout"/> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> |