From: hama Date: Fri, 1 May 2015 23:10:01 +0000 (+0200) Subject: Context menu, win32 support X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=27b1c0bc91cf539906181cfd3c5921c04bea1167;p=reqt Context menu, win32 support --- diff --git a/appl/refind/filefinder.cpp b/appl/refind/filefinder.cpp index 0024ac9..bc21de7 100644 --- a/appl/refind/filefinder.cpp +++ b/appl/refind/filefinder.cpp @@ -111,7 +111,8 @@ void FileFinder::fillTable(const QString& path, int depth, QTableWidget* table, else entries = dir.entryInfoList(m_patterns, filters, QDir::NoSort); QList ::iterator it; - QString relativePath = path.mid(1 + m_baseDir.length()); + QString relativePath = ReQStringUtil::nativePath( + path.mid(1 + m_baseDir.length())); QString node, ext; for (it = entries.begin(); it != entries.end(); ++it){ diff --git a/appl/refind/mainwindow.cpp b/appl/refind/mainwindow.cpp index 7c6b745..ccf3ecf 100644 --- a/appl/refind/mainwindow.cpp +++ b/appl/refind/mainwindow.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "base/rebase.hpp" #include "gui/regui.hpp" #include "textfinder.hpp" @@ -42,7 +43,9 @@ MainWindow::MainWindow(const QString& startDir, const QString& homeDir, m_horizontalHeader(NULL), m_lastOrder(Qt::DescendingOrder), m_homeDir(), - m_storageFile(){ + m_storageFile(), + m_fileHandler(), + m_dirHandler(){ ui->setupUi(this); initializeHome(); m_statusMessage = new QLabel(tr("Willkommen bei refind")); @@ -90,7 +93,37 @@ MainWindow::MainWindow(const QString& startDir, const QString& homeDir, ui->tableWidget->setColumnWidth(TC_SIZE, 125); ui->tableWidget->setColumnWidth(TC_MODIFIED, 175); ui->tableWidget->setColumnWidth(TC_TYPE, 75); - ui->tableWidget->horizontalHeader()->setStretchLastSection(true); + addContextMenu(); +} + +/** + * @brief Destructor. + */ +MainWindow::~MainWindow(){ + delete ui; +} + +/** + * Prepares the context menu of the result table. + * + */ +void MainWindow::addContextMenu(){ + ui->tableWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->tableWidget, + SIGNAL(customContextMenuRequested(const QPoint&)), + SLOT(handleTableContextMenu(const QPoint&))); + ContextHandler* handler = new ContextHandler(); + handler->m_text = "kedit"; + handler->m_program = "kedit"; + handler->m_arguments = "${full}"; + handler->m_changeToParentDirectory = ContextHandler::DM_UNDEF; + m_fileHandler.append(handler); + handler = new ContextHandler(); + handler->m_text = "bash"; + handler->m_program = "konsole"; + handler->m_arguments = "${full}"; + handler->m_changeToParentDirectory = ContextHandler::DM_TO_FILE; + m_dirHandler.append(handler); } void MainWindow::mousePressEvent(QMouseEvent* event) @@ -154,13 +187,6 @@ void MainWindow::preview(){ exportToStream(stream, 1); } -/** - * @brief Destructor. - */ -MainWindow::~MainWindow(){ - delete ui; -} - /** * Starts the about dialog. */ @@ -324,54 +350,37 @@ void MainWindow::exportFiles(){ * @param maxRow -1 or the maximum row to export */ void MainWindow::exportToStream(QTextStream& stream, int maxRow){ + QHash placeholders; + buildGlobalPlaceholders(placeholders); if (!ui->comboBoxHeader->currentText().isEmpty()){ - stream << replaceGlobalPlaceholder(ui->comboBoxHeader->currentText()) + stream << replaceGlobalPlaceholders(ui->comboBoxHeader, placeholders) << endl; } int count = ui->tableWidget->rowCount(); if (count > 0 && maxRow > 0) count = maxRow; + QString error; for (int ii = 0; ii < count; ii++){ QString line = ui->comboBoxTemplate->currentText(); - int start = 0; - QString replacement; - QString name; - while (start >= 0){ - start = line.indexOf("${", start); - if (start < 0) - break; - int end = line.indexOf('}', start + 1); - if (end < 0) - break; - name = line.mid(start + 2, end - start - 2); - if (name == "full"){ - QString path = ui->tableWidget->item(ii, TC_PATH)->text(); - if (path.isEmpty()) - path = ui->tableWidget->item(ii, TC_NODE)->text(); - else - path += ui->tableWidget->item(ii, TC_NODE)->text(); - replacement = m_lastBaseDir.absoluteFilePath(path); - }else if (name == "path") - replacement = ui->tableWidget->item(ii, TC_PATH)->text(); - else if (name == "ext") - replacement = ui->tableWidget->item(ii, TC_EXT)->text(); - else if (name == "node") - replacement = ui->tableWidget->item(ii, TC_NODE)->text(); - else if (name == "modified") - replacement = ui->tableWidget->item(ii, TC_MODIFIED)->text(); - else if (name == "size") - replacement = ui->tableWidget->item(ii, TC_SIZE)->text(); - else{ - guiError(ui->comboBoxTemplate, tr("unknown placeholder: ") + name); - break; - } - line = line.replace("${" + name + "}", replacement); - start += replacement.length(); + QHash placeholders; + QString path = m_lastBaseDir.absoluteFilePath( + ReQStringUtil::pathAppend( + ui->tableWidget->item(ii, TC_PATH)->text(), + ui->tableWidget->item(ii, TC_NODE)->text())); + placeholders.insert("full", ReQStringUtil::nativePath(path)); + placeholders.insert("path", ui->tableWidget->item(ii, TC_PATH)->text()); + placeholders.insert("ext", ui->tableWidget->item(ii, TC_EXT)->text()); + placeholders.insert("node", ui->tableWidget->item(ii, TC_NODE)->text()); + placeholders.insert("modified", ui->tableWidget->item(ii, TC_MODIFIED)->text()); + placeholders.insert("size", ui->tableWidget->item(ii, TC_SIZE)->text()); + if (! ReQStringUtil::replacePlaceholders(line, placeholders, &error)){ + guiError(ui->comboBoxTemplate, error); + break; } stream << replaceEscSequences(line) << endl; } if (!ui->comboBoxFooter->currentText().isEmpty()){ - stream << replaceGlobalPlaceholder(ui->comboBoxFooter->currentText()) + stream << replaceGlobalPlaceholders(ui->comboBoxFooter, placeholders) << endl; } } @@ -420,6 +429,73 @@ void MainWindow::handlePlaceholder(QComboBox* target){ target->setCurrentText(target->currentText() + dialog.var()); } +/** + * Handles the request of a context menu of the result table. + * + * @param position the position of a mouse click + */ +void MainWindow::handleTableContextMenu(const QPoint& position){ + int row = ui->tableWidget->rowAt(position.y()); + if (row >= 0){ + QMenu menu; + + QString node = ui->tableWidget->item(row, TC_NODE)->text(); + QString parent = buildAbsPath(row); + QString full = ReQStringUtil::pathAppend(parent, node); + QFileInfo file(full); + QList& list = file.isDir() ? m_dirHandler : m_fileHandler; + QList::const_iterator it; + QHash actions; + ContextHandler* handler; + for (it = list.begin(); it != list.end(); ++it){ + handler = *it; + QString text = handler->m_text + " " + node; + actions.insert(menu.addAction(text), handler); + } + QAction* selectedItem = menu.exec(ui->tableWidget->viewport()->mapToGlobal(position)); + if (selectedItem != NULL) + { + handler = *actions.find(selectedItem); + QString arguments = handler->m_arguments; + QString dir; + switch(handler->m_changeToParentDirectory){ + case ContextHandler::DM_TO_PARENT: + dir = parent; + break; + case ContextHandler::DM_TO_FILE: + dir = full; + break; + default: + dir = ui->comboBoxDirectory->currentText(); + break; + } + QMap placeholders; + placeholders.insert("full", full); + placeholders.insert("node", node); + placeholders.insert("path", parent); + placeholders.insert("ext", ReQStringUtil::extensionOf(node)); + QString error; + QStringList args = arguments.split(' '); + bool hasErrors = false; + for (int ix = 0; ix < args.size(); ix++){ + QString arg = args.at(ix); + if (! ReQStringUtil::replacePlaceholders(arg, placeholders, &error)){ + guiError(NULL, error); + hasErrors = true; + break; + } + args.replace(ix, arg); + } + if (! hasErrors){ + QProcess::startDetached(handler->m_program, args, dir, NULL); + setStatusMessage(false, "call of " + handler->m_program + " " + + arguments); + } + } + + } +} + /** * Handles the push of "select placeholder for the header". */ @@ -440,52 +516,31 @@ void MainWindow::prepareTextFind(){ } } +void MainWindow::buildGlobalPlaceholders(QHash& hash){ + + hash.insert("filepatterns", ui->comboBoxFilePatterns->currentText()); + hash.insert("base", m_lastBaseDir.absolutePath()); + hash.insert("textpattern", ui->comboBoxTextPattern->currentText()); + hash.insert("dirs", QString::number(m_statistics.m_dirs)); + hash.insert("files", QString::number(m_statistics.m_files)); + hash.insert("runtime", QString::number(m_statistics.m_runtimeSeconds, 'g', 3)); + hash.insert("bytes", QString::number(m_statistics.m_bytes)); + hash.insert("megabytes", QString::number((double) m_statistics.m_bytes / 1000000)); + hash.insert("datetime", QDateTime::currentDateTime().toLocalTime().toString( + "yyyy.MM.dd/hh:mm:ss")); +} /** * Replaces placeholders valid in header and footer. * * @param text the text to convert * @return text with the esc sequences replaced */ -QString MainWindow::replaceGlobalPlaceholder(const QString& text){ - int start = 0; - QString replacement; - QString name; - QString rc = text; - while (start >= 0){ - start = rc.indexOf("${", start); - if (start < 0) - break; - int end = rc.indexOf('}', start + 1); - if (end < 0) - break; - name = rc.mid(start + 2, end - start - 2); - if (name == "filepatterns") - replacement = ui->comboBoxFilePatterns->currentText(); - else if (name == "base") - replacement = m_lastBaseDir.absolutePath(); - else if (name == "textpattern") - replacement = ui->comboBoxTextPattern->currentText(); - else if (name == "dirs") - replacement = QString::number(m_statistics.m_dirs); - else if (name == "files") - replacement = QString::number(m_statistics.m_files); - else if (name == "runtime") - replacement = QString::number(m_statistics.m_runtimeSeconds, 'g', 3); - else if (name == "bytes") - replacement = QString::number(m_statistics.m_bytes); - else if (name == "megabytes") - replacement = QString::number((double) m_statistics.m_bytes / 1000000); - else if (name == "datetime") - replacement = QDateTime::currentDateTime().toLocalTime().toString( - "yyyy.MM.dd/hh:mm:ss"); - else{ - QString msg = tr("unknown placeholder: ") + name; - guiError(ui->comboBoxFooter, msg); - break; - } - rc = rc.replace("${" + name + "}", replacement); - start += replacement.length(); - } +QString MainWindow::replaceGlobalPlaceholders(QComboBox* combo, + QHash& placeholders){ + QString rc = combo->currentText(); + QString error; + if (! ReQStringUtil::replacePlaceholders(rc, placeholders, &error)) + guiError(combo, error); return replaceEscSequences(rc); } diff --git a/appl/refind/mainwindow.hpp b/appl/refind/mainwindow.hpp index 8992d86..55cd987 100644 --- a/appl/refind/mainwindow.hpp +++ b/appl/refind/mainwindow.hpp @@ -47,6 +47,21 @@ struct Statistics { double m_runtimeSeconds; }; +class ContextHandler { +public: + enum DirMode { + DM_UNDEF, + DM_TO_PARENT, + DM_TO_FILE + }; + +public: + QString m_text; + QString m_program; + QString m_arguments; + DirMode m_changeToParentDirectory; +}; + class MainWindow: public QMainWindow, public ReGuiValidator { Q_OBJECT @@ -65,6 +80,7 @@ private slots: void filePlaceholder(); void footerPlaceholder(); void fullNameToClipboard(); + void handleTableContextMenu(const QPoint& position); void headerClicked(int col); void headerPlaceholder(); void preview(); @@ -76,15 +92,18 @@ private slots: void up(); private: + void addContextMenu(); QString buildAbsPath(int row); QDir::Filters buildFileTypes(); + void buildGlobalPlaceholders(QHash& hash); QString cellAsText(int row, int col); void exportToStream(QTextStream& stream, int maxRow = -1); void handlePlaceholder(QComboBox* target); void initializeHome(); void mousePressEvent(QMouseEvent* event); void prepareTextFind(); - QString replaceGlobalPlaceholder(const QString& text); + QString replaceGlobalPlaceholders(QComboBox* combo, + QHash& placeholders); void restoreState(); virtual void setStatusMessage(bool error, const QString& message); private: @@ -99,6 +118,10 @@ private: Statistics m_statistics; QString m_homeDir; QString m_storageFile; + QAction* m_actionEditor; + QAction* m_actionStartShell; + QList m_fileHandler; + QList m_dirHandler; }; #endif // MAINWINDOW_HPP diff --git a/appl/refind/mainwindow.ui b/appl/refind/mainwindow.ui index 72fe81f..6752c61 100644 --- a/appl/refind/mainwindow.ui +++ b/appl/refind/mainwindow.ui @@ -872,6 +872,18 @@ + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SelectRows + + + true + Filename @@ -887,7 +899,7 @@ Size (MByte) - AlignRight|AlignVCenter + AlignRight|AlignBottom diff --git a/appl/refind/refind.pro b/appl/refind/refind.pro index 6c1fb75..709b269 100644 --- a/appl/refind/refind.pro +++ b/appl/refind/refind.pro @@ -24,7 +24,8 @@ SOURCES += main.cpp\ ../../gui/ReStateStorage.cpp \ ../../gui/ReGuiValidator.cpp \ dialogglobalplaceholder.cpp \ - dialogfileplaceholder.cpp + dialogfileplaceholder.cpp \ + utils.cpp HEADERS += mainwindow.hpp \ @@ -37,7 +38,8 @@ HEADERS += mainwindow.hpp \ ../../gui/ReGuiValidator.hpp \ ../../gui/regui.hpp \ dialogglobalplaceholder.hpp \ - dialogfileplaceholder.hpp + dialogfileplaceholder.hpp \ + utils.hpp FORMS += mainwindow.ui \ diff --git a/base/ReQStringUtil.cpp b/base/ReQStringUtil.cpp index edd3ddd..af4a960 100644 --- a/base/ReQStringUtil.cpp +++ b/base/ReQStringUtil.cpp @@ -35,6 +35,31 @@ ReString ReQStringUtil::chomp(const ReString& text) return last == text.length() - 1 ? text : text.mid(0, last + 1); } +/** + * Extracts the extension of a filename. + * + * @param filename the filename (with or without path) + * @return "": no extension found
+ * otherwise: the extension of filename + */ +ReString ReQStringUtil::extensionOf(const ReString& filename) +{ + QString rc; + + int index = filename.lastIndexOf('.'); + if (index > 0){ + int index2 = filename.lastIndexOf('/'); + if (index2 < index){ +#if defined __WIN32__ + index2 = filename.lastIndexOf('\\'); + if (index2 < index) + rc = filename.mid(index); +#endif + } + } + return rc; +} + /** * @brief Determines the length and vlaue of an integer. @@ -108,29 +133,6 @@ int ReQStringUtil::lengthOfUInt(const ReString& text, int start, int radix, return rc; } -/** - * Skips a character in a text at a given position if it has an expected value. - * - * @param text text to inspect - * @param expected the character which is expected - * @param index IN/OUT: the position of the expected character. - * Will be incremented if the expected character is found - * @param length IN/OUT: IN: 0: do nothing
- * OUT: 0: the expected character was not found. - * otherwise: the length is incremented - */ -void ReQStringUtil::skipExpected(const ReString& text, QChar expected, - int& index, int& length){ - if (length == 0){ - // error state, do nothing - }else if (index >= text.length() || text[index] != expected){ - length = 0; - }else{ - index++; - length++; - } -} - /** * Returns the length of a date in a string. * @@ -356,6 +358,89 @@ int ReQStringUtil::lengthOfReal(const ReString& text, int start, qreal* pValue){ return found ? ix - start : 0; } +/** + * Appends a relative path to base directory name (absolute or relative). + * + * @param base the base directory, relative or absolute + * @param toAdd a relative path (relative to base) + * @return the combined path + */ +QString ReQStringUtil::pathAppend(const QString& base, const QString& path) +{ + QString rc; + if (!base.isEmpty()) + rc = QDir::cleanPath(base + QDir::separator() + path); + else { + rc = path; + rc.replace("\\", "/"); + if (path.startsWith("/")) + rc.remove(0, 1); + } + return rc; +} + +/** + * Replaces placeholders by their values. + * + * Example for a placeholder: ${path} + * + * @param text IN/OUT: the text to inspect/change + * @param placeholders a hashmap with (name, value) pairs, e.g. ("path", "/") + * @param error OUT: NULL or the error message (unknown name) + * @return true: success
+ * false: unknown name found (not replaced) + */ +bool ReQStringUtil::replacePlaceholders(QString& text, + const QHash& placeholders, QString* error){ + int start = 0; + bool rc = true; + QString name; + QHash::const_iterator it; + while (start >= 0){ + start = text.indexOf("${", start); + if (start < 0) + break; + int end = text.indexOf('}', start + 1); + if (end < 0) + break; + name = text.mid(start + 2, end - start - 2); + it = placeholders.find(name); + if (it == placeholders.end()){ + rc = false; + if (error != NULL) { + *error = QObject::tr("unknown placeholder: ") + name; + } + }else{ + text = text.replace("${" + name + "}", *it); + } + start += (*it).length(); + } + return rc; +} + +/** + * Skips a character in a text at a given position if it has an expected value. + * + * @param text text to inspect + * @param expected the character which is expected + * @param index IN/OUT: the position of the expected character. + * Will be incremented if the expected character is found + * @param length IN/OUT: IN: 0: do nothing
+ * OUT: 0: the expected character was not found. + * otherwise: the length is incremented + */ +void ReQStringUtil::skipExpected(const ReString& text, QChar expected, + int& index, int& length){ + if (length == 0){ + // error state, do nothing + }else if (index >= text.length() || text[index] != expected){ + length = 0; + }else{ + index++; + length++; + } +} + /** * @brief Converts a ReString into an utf-8 string * diff --git a/base/ReQStringUtil.hpp b/base/ReQStringUtil.hpp index e968926..55a0888 100644 --- a/base/ReQStringUtil.hpp +++ b/base/ReQStringUtil.hpp @@ -15,6 +15,7 @@ class ReQStringUtil { public: static ReString chomp(const ReString& text); + static ReString extensionOf(const ReString& filename); static int lengthOfDate(const ReString& text, int start = 0, QDate* value = NULL); static int lengthOfDateTime(const ReString& text, int start = 0, @@ -28,7 +29,24 @@ public: int radix = 10, uint64_t* value = NULL); static int lengthOfUInt(const ReString& text, int start, int radix, uint* pValue); - static void skipExpected(const ReString& text, QChar expected, int& index, int& length); + /** + * Returns the path with native path separators. + * + * @param path the path to change + * @return the path with native path separators + */ + inline static QString nativePath(const QString& path){ +#if defined __WIN32__ + QString rc(path); return rc.replace("/", "\\"); +#else + return path; +#endif + } + static QString pathAppend(const QString& base, const QString& path); + static bool replacePlaceholders(QString& text, const QHash& placeholders, QString* error); + static void skipExpected(const ReString& text, QChar expected, int& index, + int& length); /** * @brief Returns the value of a hexadecimal digit. * diff --git a/gui/ReGuiValidator.cpp b/gui/ReGuiValidator.cpp index d7a145f..6665aef 100644 --- a/gui/ReGuiValidator.cpp +++ b/gui/ReGuiValidator.cpp @@ -116,7 +116,8 @@ QString ReGuiValidator::comboText(QComboBox* combo){ * @param message the error message */ void ReGuiValidator::guiError(QWidget* widget, const QString& message){ - widget->setFocus(Qt::OtherFocusReason); + if (widget != NULL) + widget->setFocus(Qt::OtherFocusReason); setStatusMessage(true, message); m_errors++; }