├── .gitignore ├── .travis.yml ├── LICENSE ├── QtFileExplorer.pro ├── README.md ├── deletetask.cpp ├── deletetask.h ├── example2.png ├── fileinfo.cpp ├── fileinfo.h ├── imagepreview.cpp ├── imagepreview.h ├── keypresseater.cpp ├── keypresseater.h ├── main.cpp ├── mywindows.cpp ├── mywindows.h ├── q.ico ├── refreshtask.cpp ├── refreshtask.h ├── ressources.qrc └── test.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.pro.* 2 | *.o 3 | TEST 4 | Makefile 5 | *.stash 6 | moc_* 7 | qrc_* 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | dist: bionic 3 | compiler: 4 | - gcc 5 | before_install: 6 | - sudo apt-get install -y qt5-default qt5-qmake clang-format 7 | install: 8 | - ls 9 | script: 10 | - qmake 11 | - make -j 2 12 | after_success: 13 | - git config --global user.email "travis@travis-ci.org" 14 | - git config --global user.name "Travis CI" 15 | - git checkout master 16 | - clang-format -i -style=LLVM *.cpp *.h 17 | - git add *.cpp *.h 18 | - git commit --message "[ci skip] Travis AUTO FORMAT" 19 | - git push https://${GH_TOKEN}@github.com/Lightjohn/QtFileExplorer.git --quiet > /dev/null 2>&1 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jonathan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QtFileExplorer.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2014-08-12T15:28:35 4 | # 5 | #------------------------------------------------- 6 | config += static 7 | QT += core gui 8 | QT += widgets 9 | 10 | TARGET = QtFileExplorer 11 | TEMPLATE = app 12 | 13 | RC_ICONS = q.ico 14 | 15 | SOURCES += main.cpp\ 16 | deletetask.cpp \ 17 | mywindows.cpp \ 18 | fileinfo.cpp \ 19 | imagepreview.cpp \ 20 | keypresseater.cpp \ 21 | refreshtask.cpp 22 | 23 | HEADERS += \ 24 | deletetask.h \ 25 | mywindows.h \ 26 | fileinfo.h \ 27 | imagepreview.h \ 28 | keypresseater.h \ 29 | refreshtask.h 30 | 31 | FORMS += 32 | 33 | OTHER_FILES += 34 | 35 | RESOURCES += \ 36 | ressources.qrc 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/Lightjohn/QtFileExplorer.svg?branch=master)](https://travis-ci.org/Lightjohn/QtFileExplorer) 2 | 3 | Build 4 | ===== 5 | 6 | **Note**: Will now need **Qt5.15** because of move to bin feature 7 | 8 | you will need `qt5-default` and `qt5-qmake` on Ubuntu 9 | 10 | `qmake` then `make -j 4` 11 | 12 | 13 | 14 | QtFileExplorer 15 | ============== 16 | 17 | Qt quick image visualisation. 18 | 19 | Use Miller Columns to explore files and folder. 20 | 21 | It support only images for now but can be improve. 22 | 23 | Functionalities 24 | ================ 25 | 26 | *SPACE* : Launch preview mode (images only). 27 | 28 | *ENTER* : Open the current file with os default application (every files). 29 | 30 | *Double Clic* : If there is a double click on preview mode then the fullscreen mode is activated. 31 | 32 | *Suppr* : Delete the folder/file selected, also work with shift selected folders/files. 33 | 34 | **WARNING** Suppr does not move to trash if compiled against *deleteList) { 5 | this->status = status; 6 | this->deleteList = deleteList; 7 | this->todelete = something; 8 | } 9 | 10 | void deletetask::deleteFileRm(QString filepath) { 11 | QFileInfo tmp(filepath); 12 | if (tmp.isFile()) { 13 | QFile file(filepath); 14 | if (!file.remove()) { 15 | qDebug() << "File not deleted: " << file.fileName(); 16 | } 17 | } else { 18 | QDir folder(filepath); 19 | if (!folder.removeRecursively()) { 20 | qDebug() << "Not all was deleted: " << folder.absolutePath(); 21 | } 22 | } 23 | } 24 | 25 | void deletetask::run() { 26 | status->setText("DELETING " + todelete.at(0)); 27 | 28 | for (int i = 0; i < todelete.length(); ++i) { 29 | // qDebug() << "DELETE" << shiftList.at(i); 30 | // Old delete = RM -r 31 | QString toDelete = todelete.at(i); 32 | #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 33 | deleteFileRm(toDelete); 34 | #else 35 | // New delete = Move to Trash 36 | if (!QFile::moveToTrash(toDelete)) { 37 | qDebug() << "Not deleted: " << toDelete; 38 | qDebug() << "Trying to rm "; 39 | deleteFileRm(toDelete); 40 | } 41 | #endif 42 | } 43 | status->setText(""); 44 | } 45 | -------------------------------------------------------------------------------- /deletetask.h: -------------------------------------------------------------------------------- 1 | #ifndef DELETETASK_H 2 | #define DELETETASK_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class deletetask : public QRunnable { 11 | public: 12 | deletetask(QStringList, QLabel *, QList *); 13 | void run() override; 14 | void deleteFileRm(QString filepath); 15 | 16 | private: 17 | QLabel *status; 18 | QList *deleteList; 19 | QStringList todelete; 20 | }; 21 | 22 | #endif // DELETETASK_H 23 | -------------------------------------------------------------------------------- /example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lightjohn/QtFileExplorer/fd5ca9f6eb206502d23c5375ba9623dac450e0b6/example2.png -------------------------------------------------------------------------------- /fileinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "fileinfo.h" 2 | 3 | /* Informations displayed 4 | * 5 | * NAME : filename, size limited to xx char see mywindows.cpp 6 | * 7 | * WIDTH : int or 0 if not a image 8 | * 9 | * HEIGHT : int or 0 if not a image 10 | * 11 | * TYPE : extension of the file or "Not a standard file" 12 | * 13 | * SIZE : size in B, KB, GB.. 14 | * 15 | **/ 16 | 17 | fileInfo::fileInfo(QWidget *parent) : QWidget(parent) { 18 | layout = new QVBoxLayout; 19 | 20 | commonName = QString("Name: "); 21 | commonSize = QString("Size: "); 22 | commonSizeEnd = QString(""); 23 | fontSize = QString("font: 14pt;"); 24 | width = QString("Width: Unknown"); 25 | height = QString("Height: Unknown"); 26 | fileType = QString("Type: Unknown"); 27 | 28 | name = new QLabel(commonName + "No file chosen"); 29 | size = new QLabel(commonSize + "0"); 30 | labelWidth = new QLabel(width); 31 | labelHeight = new QLabel(height); 32 | type = new QLabel(fileType); 33 | 34 | name->setStyleSheet(fontSize); 35 | size->setStyleSheet(fontSize); 36 | labelWidth->setStyleSheet(fontSize); 37 | labelHeight->setStyleSheet(fontSize); 38 | type->setStyleSheet(fontSize); 39 | 40 | layout->addWidget(name); 41 | layout->addWidget(labelWidth); 42 | layout->addWidget(labelHeight); 43 | layout->addWidget(type); 44 | layout->addWidget(size); 45 | 46 | this->setLayout(layout); 47 | } 48 | 49 | void fileInfo::setType(QString ext) { type->setText("Type: " + ext); } 50 | 51 | void fileInfo::setName(QString nameIn) { name->setText(commonName + nameIn); } 52 | 53 | void fileInfo::setResolution(int width, int height) { 54 | labelWidth->setText("Width: " + QString::number(width)); 55 | labelHeight->setText("Height: " + QString::number(height)); 56 | } 57 | 58 | void fileInfo::setSize(int sizeIn) { 59 | commonSizeEnd = QString(" B"); 60 | if (sizeIn > 1000) { 61 | sizeIn = sizeIn / 1000; 62 | commonSizeEnd = QString(" kB"); 63 | if (sizeIn > 1000) { 64 | sizeIn = sizeIn / 1000; 65 | commonSizeEnd = QString(" mB"); 66 | if (sizeIn > 1000) { 67 | sizeIn = sizeIn / 1000; 68 | commonSizeEnd = QString(" gB"); 69 | } 70 | } 71 | } 72 | size->setText(commonSize + QString::number(sizeIn) + commonSizeEnd); 73 | } 74 | 75 | fileInfo::~fileInfo() {} 76 | -------------------------------------------------------------------------------- /fileinfo.h: -------------------------------------------------------------------------------- 1 | #ifndef FILEINFO_H 2 | #define FILEINFO_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class fileInfo : public QWidget { 10 | Q_OBJECT 11 | public: 12 | explicit fileInfo(QWidget *parent = 0); 13 | void setName(QString name); 14 | void setSize(int size); 15 | void setType(QString type); 16 | void setResolution(int width, int height); 17 | ~fileInfo(); 18 | 19 | signals: 20 | 21 | public slots: 22 | 23 | private: 24 | QString commonName; 25 | QString commonSize; 26 | QString commonSizeEnd; 27 | QString fontSize; 28 | QString width; 29 | QString height; 30 | QString fileType; 31 | 32 | QLabel *labelWidth; 33 | QLabel *labelHeight; 34 | QLabel *name; 35 | QLabel *type; 36 | QLabel *size; 37 | QVBoxLayout *layout; 38 | }; 39 | 40 | #endif // FILEINFO_H 41 | -------------------------------------------------------------------------------- /imagepreview.cpp: -------------------------------------------------------------------------------- 1 | #include "imagepreview.h" 2 | 3 | imagePreview::imagePreview(QWidget *parent) : QWidget(parent, Qt::Window) { 4 | layout = new QVBoxLayout(this); 5 | image = new QLabel("Wrong image chosen"); 6 | image->setStyleSheet("QLabel { background-color : black; }"); 7 | image->setAlignment(Qt::AlignCenter); 8 | image->setContentsMargins(0, 0, 0, 0); 9 | layout->addWidget(image); 10 | showing = false; 11 | isFullScreen = false; 12 | this->setGeometry(50, 50, image->width(), image->height()); 13 | } 14 | 15 | void imagePreview::showImage(QString path) { 16 | updateImage(path); 17 | showing = true; 18 | this->show(); 19 | } 20 | 21 | void imagePreview::updateImage(QPixmap im) { 22 | int height = (static_cast(this->parent()))->screenH; 23 | int width = (static_cast(this->parent()))->screenW; 24 | 25 | QPixmap tmp; 26 | // Replace all of that by some computation on size and do one resize only 27 | if (im.height() > height) { 28 | tmp = im.scaledToHeight(height - 20, Qt::SmoothTransformation); 29 | } else { 30 | tmp = im; 31 | } 32 | if (tmp.width() > width) { 33 | image->setPixmap(tmp.scaledToWidth(width - 50, Qt::SmoothTransformation)); 34 | } else { 35 | image->setPixmap(tmp); 36 | } 37 | if (!isFullScreen) { 38 | this->adjustSize(); 39 | } 40 | } 41 | 42 | // This function is called when we want to have the opposite effect as the 43 | // actual one Hided -> Shown Shown -> Hided 44 | void imagePreview::hidePreview() { 45 | if (isFullScreen) { 46 | fullScreen(); 47 | } 48 | this->hide(); 49 | showing = false; 50 | } 51 | 52 | // Same idea that hidePreview: we got from fullScreen to normal or vice versa 53 | // according to actual state 54 | void imagePreview::fullScreen() { 55 | if (isFullScreen) { 56 | this->showNormal(); 57 | } else { 58 | this->showFullScreen(); 59 | } 60 | isFullScreen = !isFullScreen; 61 | } 62 | 63 | // If the user close manually the preview windows 64 | void imagePreview::closeEvent(QCloseEvent *) { showing = false; } 65 | -------------------------------------------------------------------------------- /imagepreview.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGEPREVIEW_H 2 | #define IMAGEPREVIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "mywindows.h" 13 | 14 | class imagePreview : public QWidget { 15 | Q_OBJECT 16 | public: 17 | explicit imagePreview(QWidget *parent = nullptr); 18 | void closeEvent(QCloseEvent *event); 19 | void showImage(QString path); 20 | void updateImage(QPixmap im); 21 | void hidePreview(); 22 | void fullScreen(); 23 | 24 | bool showing; 25 | bool isFullScreen; 26 | 27 | signals: 28 | 29 | public slots: 30 | 31 | private: 32 | QVBoxLayout *layout; 33 | QLabel *image; 34 | }; 35 | 36 | #endif // IMAGEPREVIEW_H 37 | -------------------------------------------------------------------------------- /keypresseater.cpp: -------------------------------------------------------------------------------- 1 | #include "keypresseater.h" 2 | 3 | KeyPressEater::KeyPressEater(myWindows *my) { parent = my; } 4 | 5 | // Here we intercept all keyboard events like 6 | // Escape -> To disable fullscreen 7 | // Right-Left became Up-Down because in fullscreen mode we stay in the same 8 | // folder 9 | bool KeyPressEater::eventFilter(QObject *obj, QEvent *event) { 10 | if (event->type() == QEvent::KeyPress) { 11 | QKeyEvent *key = (static_cast(event)); 12 | if (parent->preview->isFullScreen && 13 | (key->key() == Qt::Key_Escape || key->key() == Qt::Key_Right || 14 | key->key() == Qt::Key_Left)) { 15 | if (key->key() == Qt::Key_Escape) { 16 | parent->preview->fullScreen(); 17 | } else { 18 | QWidget *focus = parent->columnView->focusWidget(); 19 | QKeyEvent *newKey; 20 | if (key->key() == Qt::Key_Right) { 21 | newKey = new QKeyEvent(key->type(), Qt::Key_Down, Qt::NoModifier); 22 | } else if (key->key() == Qt::Key_Left) { 23 | newKey = new QKeyEvent(key->type(), Qt::Key_Up, Qt::NoModifier); 24 | } else { 25 | newKey = nullptr; 26 | qDebug() << "Should never be printed, else see keypresseater"; 27 | } 28 | QApplication::sendEvent(focus, newKey); 29 | } 30 | } else { 31 | // There is a little problem: i need to manually send some events to the 32 | // good receiver 33 | QWidget *focus = parent->columnView->focusWidget(); 34 | QApplication::sendEvent(focus, event); // for up down 35 | QApplication::sendEvent(parent->columnView, event); // for right left 36 | } 37 | } 38 | // The double click mouse on preview mode launch fullscreen 39 | if (event->type() == QEvent::MouseButtonDblClick) { 40 | QMouseEvent *mouseEvent = static_cast(event); 41 | if (mouseEvent->button() == Qt::LeftButton) { 42 | parent->preview->fullScreen(); 43 | } 44 | } 45 | return QObject::eventFilter(obj, event); 46 | } 47 | 48 | KeyPressEater::~KeyPressEater() {} 49 | -------------------------------------------------------------------------------- /keypresseater.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYPRESSEATER_H 2 | #define KEYPRESSEATER_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "mywindows.h" 10 | 11 | class myWindows; 12 | 13 | class KeyPressEater : public QObject { 14 | Q_OBJECT 15 | 16 | protected: 17 | bool eventFilter(QObject *obj, QEvent *event); 18 | 19 | public: 20 | KeyPressEater(myWindows *my); 21 | ~KeyPressEater(); 22 | 23 | private: 24 | myWindows *parent; 25 | }; 26 | 27 | #endif // KEYPRESSEATER_H 28 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "mywindows.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | QApplication a(argc, argv); 8 | 9 | myWindows wid; 10 | 11 | a.setObjectName("TEST"); 12 | return a.exec(); 13 | } 14 | -------------------------------------------------------------------------------- /mywindows.cpp: -------------------------------------------------------------------------------- 1 | #include "mywindows.h" 2 | 3 | #include 4 | 5 | myWindows::myWindows(QWidget *parent) : QWidget(parent) { 6 | // Getting size of the screen 7 | QList screenObj = QGuiApplication::screens(); 8 | screen = screenObj.at(0); 9 | int sizeCol; 10 | screenH = screen->geometry().height(); 11 | screenW = screen->geometry().width(); 12 | sizeCol = 150; 13 | 14 | sizePreviewH = 512; 15 | sizePreviewW = 512; 16 | 17 | int minPrev = screenH - sizeCol; 18 | if (minPrev < sizePreviewH) { 19 | sizePreviewH = minPrev; 20 | sizePreviewW = minPrev; 21 | } 22 | 23 | isShiftOn = false; 24 | // Globale layout 25 | // _____Vlayout______ 26 | // | | 27 | // | ___HLayout___ | 28 | // | | | | 29 | // | | preview | | 30 | // | | | | 31 | // | | file info| | 32 | // | |____________| | 33 | // | | 34 | // | column view | 35 | // |________________| 36 | // 37 | layoutGlobal = new QVBoxLayout; 38 | layoutGlobal->setAlignment(Qt::AlignCenter); 39 | 40 | // Gloval preview 41 | 42 | preview = new imagePreview(this); 43 | 44 | // Preview file part 45 | 46 | layoutPreview = new QHBoxLayout; 47 | 48 | lab = new QLabel("image ici"); 49 | lab->setMaximumHeight(sizePreviewH - 110); 50 | imDef = QPixmap(":/images/test.png"); 51 | lab->setPixmap(imDef); 52 | 53 | info = new fileInfo; 54 | 55 | // Might prevent freeze 56 | QFileIconProvider *fileIconProvider = new QFileIconProvider(); 57 | fileIconProvider->setOptions(QFileIconProvider::DontUseCustomDirectoryIcons); 58 | 59 | // Column view part 60 | model = new QFileSystemModel(this); 61 | model->setRootPath(QDir::rootPath()); 62 | model->setResolveSymlinks(true); 63 | model->setReadOnly(false); 64 | model->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs | 65 | QDir::System); 66 | model->setIconProvider(fileIconProvider); 67 | 68 | // Loading preferences 69 | loadSettings(); 70 | 71 | columnView = new QColumnView(this); 72 | columnView->setMinimumHeight(sizeCol); 73 | columnView->setModel(model); 74 | // tree->setRootIndex(model->index(QDir::currentPath())); 75 | columnView->setCurrentIndex(model->index(lastPath)); 76 | // columnView->setRootIndex()); 77 | QItemSelectionModel *itSel = columnView->selectionModel(); 78 | 79 | // Adding rename 80 | 81 | QPushButton *rename = new QPushButton("Rename"); 82 | 83 | // All the thread deletion part 84 | 85 | QThreadPool::globalInstance()->setMaxThreadCount(1); 86 | toDelete = new QList; 87 | QHBoxLayout *spin_and_delete = new QHBoxLayout; 88 | spin_and_delete->setAlignment(Qt::AlignLeft); 89 | deleteStatus = new QLabel(""); 90 | spinBox = new QSpinBox(); 91 | spinBox->setMaximum(5); 92 | spinBox->setMinimum(0); 93 | spinBox->setValue(MAX_DEPTH); 94 | 95 | // Keyboard 96 | 97 | // global space shortcut 98 | shortcutSpace = new QShortcut(QKeySequence(Qt::Key_Space), this); 99 | shortcutSpace->setContext(Qt::ApplicationShortcut); 100 | 101 | // global enter shortcut 102 | shortcutEnter = new QShortcut(QKeySequence(Qt::Key_Return), this); 103 | // shortcutEnter->setContext(Qt::ApplicationShortcut); 104 | 105 | // Global Supr Shortcut 106 | shortcutDel = new QShortcut(QKeySequence(Qt::Key_Delete), this); 107 | shortcutDel->setContext(Qt::ApplicationShortcut); 108 | 109 | // Qconnect 110 | QObject::connect(shortcutSpace, SIGNAL(activated()), this, 111 | SLOT(keyboardEvent())); 112 | QObject::connect(shortcutEnter, SIGNAL(activated()), this, 113 | SLOT(keyboardEnter())); 114 | QObject::connect(shortcutDel, SIGNAL(activated()), this, SLOT(keyboardDel())); 115 | // Listen to qColumnView click 116 | // Selection of a file 117 | QObject::connect(itSel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), 118 | this, SLOT(clickedNew(QModelIndex, QModelIndex))); 119 | QObject::connect(rename, SIGNAL(clicked()), this, SLOT(rename())); 120 | QObject::connect(spinBox, SIGNAL(valueChanged(int)), this, 121 | SLOT(depthChanged(int))); 122 | 123 | // 124 | spin_and_delete->addWidget(new QLabel("Preview depth")); 125 | spin_and_delete->addWidget(spinBox); 126 | spin_and_delete->addWidget(deleteStatus); 127 | 128 | // Adding 129 | layoutPreview->addWidget(lab); 130 | layoutPreview->addWidget(info); 131 | layoutGlobal->addLayout(layoutPreview); 132 | layoutGlobal->addWidget(columnView); 133 | layoutGlobal->addWidget(rename); 134 | layoutGlobal->addLayout(spin_and_delete); 135 | 136 | // Get event even if not in front 137 | eater = new KeyPressEater(this); 138 | preview->installEventFilter(eater); 139 | 140 | this->setLayout(layoutGlobal); 141 | this->resize(1024, 900); 142 | this->show(); 143 | } 144 | 145 | // Update variable last*Path AND if shift is on remember all selected files 146 | void myWindows::updatePath(QModelIndex index) { 147 | lastFilePath = model->filePath(index); 148 | QFileInfo infoFile(lastFilePath); 149 | lastPath = infoFile.canonicalPath(); 150 | if (isShiftOn) { 151 | shiftList.append(lastFilePath); 152 | } else { 153 | shiftList.clear(); 154 | shiftList.append(lastFilePath); 155 | } 156 | } 157 | 158 | // The actionb called by the column view when the user do something 159 | void myWindows::clickedNew(QModelIndex index, QModelIndex) { 160 | updatePath(index); 161 | QString fileName = model->fileName(index); 162 | QFileInfo infoFile(lastFilePath); 163 | QString ext = fileName.split(".") 164 | .back(); // We could use here QFileInfo::completeSuffix() 165 | int SIZE_NAME_MAX = 50; 166 | if (fileName.length() > SIZE_NAME_MAX) { 167 | info->setName(fileName.mid(0, SIZE_NAME_MAX)); 168 | } else { 169 | info->setName(fileName); 170 | } 171 | if (ext.length() == 1 || ext.length() >= 5) { 172 | info->setType("Not a standard file"); 173 | } else { 174 | info->setType(ext.toLower()); 175 | } 176 | info->setSize(int(model->size(index))); 177 | info->setResolution(0, 0); 178 | // If it's an image we update the previews and the informations 179 | QString lowExt = ext.toLower(); 180 | if (infoFile.isFile() && isImage(lowExt)) { 181 | updateImage(); 182 | } else if (infoFile.isDir()) { 183 | // If there is an image inside we try to show it 184 | bool found = parseFolderAndUpdate(lastFilePath, MAX_DEPTH); 185 | // else we show the default image if no file is an image 186 | if (!found) { 187 | lab->setPixmap(imDef); 188 | } 189 | } else { 190 | // else we show the default image 191 | lab->setPixmap(imDef); 192 | } 193 | } 194 | 195 | bool myWindows::parseFolderAndUpdate(QString path, int depth) { 196 | if (depth < 0) { 197 | return false; 198 | } 199 | QDir dir = QDir(path); 200 | dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); 201 | QString lowExt; 202 | QFileInfoList list = dir.entryInfoList(); 203 | // qDebug() << depth << path << list.size(); 204 | bool found = false; 205 | for (int i = 0; i < list.size(); ++i) { 206 | QFileInfo fileInfo = list.at(i); 207 | lowExt = fileInfo.suffix().toLower(); 208 | if (fileInfo.isFile() && isImage(lowExt)) { 209 | updateImage(fileInfo.absoluteFilePath()); 210 | found = true; 211 | } else if (fileInfo.isDir() && depth > 0) { 212 | // qDebug() << "Deeper" << fileInfo.absoluteFilePath(); 213 | found = parseFolderAndUpdate(fileInfo.absoluteFilePath(), depth - 1); 214 | } 215 | if (found) { 216 | break; 217 | } 218 | } 219 | return found; 220 | } 221 | 222 | bool myWindows::isImage(QString suffix) { 223 | QString lowSuffix = suffix.toLower(); 224 | return (lowSuffix == "jpg" || lowSuffix == "jpeg" || lowSuffix == "png" || lowSuffix == "webp"); 225 | } 226 | 227 | void myWindows::updateImage() { updateImage(lastFilePath); } 228 | 229 | void myWindows::updateImage(QString image) { 230 | lastImagePath = QString(image); // For later in case of fullscreen 231 | QPixmap imtmp(image); 232 | if (imtmp.isNull()) { // in the case someone give a bad extension (png instead 233 | // of jpg)... 234 | imtmp = imDef; 235 | } 236 | QPixmap imtmp2 = imtmp.scaledToHeight(sizePreviewH, Qt::SmoothTransformation); 237 | 238 | if (imtmp2.width() > sizePreviewW) { 239 | lab->setPixmap(imtmp2.copy(0, 0, sizePreviewW, sizePreviewH)); 240 | } else { 241 | lab->setPixmap(imtmp2); 242 | } 243 | info->setResolution(imtmp.width(), imtmp.height()); 244 | preview->updateImage(imtmp); 245 | } 246 | 247 | // Function to watch the global shortcut SPACE that is for showing preview 248 | void myWindows::keyboardEvent() { 249 | // qDebug() << "SPACE "; 250 | if (preview->showing) { 251 | preview->hidePreview(); 252 | } else { 253 | if (lastImagePath != nullptr) { 254 | preview->showImage(lastImagePath); 255 | preview->activateWindow(); 256 | } 257 | } 258 | } 259 | 260 | // Function to watch the global shortcut SPACE that is for opening the file with 261 | // default app 262 | void myWindows::keyboardEnter() { 263 | // qDebug() << "ENTER "; 264 | QDesktopServices::openUrl(QUrl::fromLocalFile(lastFilePath)); 265 | } 266 | 267 | // Debug funtion to show all keyboard event 268 | void myWindows::keyPressEvent(QKeyEvent *event) { 269 | if (event->key() == Qt::Key_Shift) { 270 | // qDebug() << "Key Shift"; 271 | isShiftOn = true; 272 | } 273 | } 274 | 275 | void myWindows::rename() { 276 | bool ok; 277 | QString text = QInputDialog::getText(this, tr("Renamming"), tr("base name:"), 278 | QLineEdit::Normal, "", &ok); 279 | int num = 0; 280 | if (ok) { 281 | for (int var = 0; var < shiftList.length(); ++var) { 282 | _rename(shiftList.at(var), text, &num); 283 | } 284 | } 285 | } 286 | 287 | void myWindows::depthChanged(int newValue) { MAX_DEPTH = newValue; } 288 | 289 | void myWindows::_rename(QString path, QString newName, int *num) { 290 | QFileInfo tmp(path); 291 | if (tmp.isFile()) { 292 | QFile file(path); 293 | QString newConstructedName = 294 | tmp.canonicalPath() + QDir::separator() + newName; 295 | // If the name if something XXX-01 else 01 296 | if (newName != "") { 297 | newConstructedName += "-"; 298 | } 299 | if (*num >= 10) { 300 | if (*num < 100) { 301 | newConstructedName += QString("0"); 302 | } 303 | } else { 304 | newConstructedName += QString("00"); 305 | } 306 | newConstructedName += QString::number(*num); 307 | // if the file had an extension we keep it else nothing 308 | // prev.jpg -> XXX-01.jpg 309 | if (tmp.completeSuffix() != "") { 310 | newConstructedName += "." + tmp.completeSuffix(); 311 | } 312 | file.rename(newConstructedName); 313 | *num = *num + 1; 314 | } else if (tmp.isDir()) { 315 | // If we have a dir we get folders and files inside and try to rename them 316 | QDir fold(path); 317 | QStringList elmts = 318 | fold.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files); 319 | for (int var = 0; var < elmts.length(); ++var) { 320 | _rename(path + QDir::separator() + elmts.at(var), newName, num); 321 | } 322 | } 323 | } 324 | 325 | void myWindows::keyReleaseEvent(QKeyEvent *event) { 326 | if (event->key() == Qt::Key_Shift) { 327 | // qDebug() <<"You Release Key " <text(); 328 | isShiftOn = false; 329 | } 330 | } 331 | 332 | void myWindows::loadSettings() { 333 | QSettings settings("IntCorpLightAssociation", "FileViewer"); 334 | lastPath = settings.value("lastPath").toString(); 335 | MAX_DEPTH = settings.value("depthMax").toInt(); 336 | } 337 | void myWindows::saveSettings() { 338 | QSettings settings("IntCorpLightAssociation", "FileViewer"); 339 | settings.setValue("lastPath", lastPath); 340 | settings.setValue("depthMax", MAX_DEPTH); 341 | } 342 | 343 | int myWindows::canDelete() { 344 | QMessageBox box; 345 | box.setText("Selected files/folders will be eternally deleted !!"); 346 | box.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); 347 | box.setWindowFlags(Qt::WindowStaysOnTopHint); 348 | box.setDefaultButton(QMessageBox::Ok); 349 | return box.exec(); 350 | } 351 | 352 | void myWindows::keyboardDel() { 353 | // If oldDelete i.e rm -r, ask for confirmation else move to bin 354 | #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 355 | int ret = canDelete(); 356 | #else 357 | // New way, we can "delete" all the time 358 | int ret = QMessageBox::Ok; 359 | #endif 360 | if (ret == QMessageBox::Ok) { 361 | deletetask *task = new deletetask(shiftList, deleteStatus, toDelete); 362 | QThreadPool::globalInstance()->start(task); 363 | } 364 | } 365 | 366 | // To add coloration to folders/files 367 | // http://stackoverflow.com/questions/1397484/custom-text-color-for-certain-indexes-in-qtreeview 368 | 369 | // When the app is closed we saved what is necessary to save 370 | void myWindows::closeEvent(QCloseEvent *) { saveSettings(); } 371 | 372 | myWindows::~myWindows() { delete eater; } 373 | -------------------------------------------------------------------------------- /mywindows.h: -------------------------------------------------------------------------------- 1 | #ifndef MYWINDOWS_H 2 | #define MYWINDOWS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "deletetask.h" 42 | #include "fileinfo.h" 43 | #include "imagepreview.h" 44 | #include "keypresseater.h" 45 | 46 | class imagePreview; 47 | class KeyPressEater; 48 | 49 | class myWindows : public QWidget { 50 | Q_OBJECT 51 | public: 52 | explicit myWindows(QWidget *parent = nullptr); 53 | void keyPressEvent(QKeyEvent *event); 54 | ~myWindows(); 55 | QShortcut *shortcutSpace; 56 | QShortcut *shortcutEnter; 57 | QShortcut *shortcutDel; 58 | QColumnView *columnView; 59 | QFileSystemModel *model; 60 | imagePreview *preview; 61 | int screenH; 62 | int screenW; 63 | void closeEvent(QCloseEvent *); 64 | 65 | int canDelete(); 66 | public slots: 67 | void clickedNew(QModelIndex index, QModelIndex index2); 68 | void keyboardEvent(); 69 | void keyboardEnter(); 70 | void keyboardDel(); 71 | void rename(); 72 | void keyReleaseEvent(QKeyEvent *event); 73 | void depthChanged(int); 74 | 75 | private: 76 | void _rename(QString path, QString newName, int *num); 77 | void loadSettings(); 78 | void saveSettings(); 79 | void updatePath(QModelIndex index); 80 | void updateImage(); 81 | void updateImage(QString image); 82 | bool parseFolderAndUpdate(QString path, int depth); 83 | bool isImage(QString suffix); 84 | int sizePreviewW; 85 | int sizePreviewH; 86 | 87 | int MAX_DEPTH = 2; 88 | 89 | bool isShiftOn; 90 | 91 | QList *toDelete; 92 | 93 | QPixmap imDef; 94 | QScreen *screen; 95 | QVBoxLayout *layoutGlobal; 96 | QHBoxLayout *layoutPreview; 97 | QLabel *lab; 98 | QLabel *deleteStatus; 99 | fileInfo *info; 100 | QString lastImagePath; 101 | QString lastFilePath; 102 | QString lastPath; 103 | KeyPressEater *eater; 104 | QStringList shiftList; 105 | QSpinBox *spinBox; 106 | }; 107 | 108 | #endif // MYWINDOWS_H 109 | -------------------------------------------------------------------------------- /q.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lightjohn/QtFileExplorer/fd5ca9f6eb206502d23c5375ba9623dac450e0b6/q.ico -------------------------------------------------------------------------------- /refreshtask.cpp: -------------------------------------------------------------------------------- 1 | #include "refreshtask.h" 2 | 3 | refreshtask::refreshtask(QLabel *preview, QString path, int depth) { 4 | shouldrun = false; 5 | maxdepth = depth; 6 | currentPath = path; 7 | this->preview = preview; 8 | } 9 | 10 | void refreshtask::run() {} 11 | 12 | void refreshtask::stop() { shouldrun = true; } 13 | -------------------------------------------------------------------------------- /refreshtask.h: -------------------------------------------------------------------------------- 1 | #ifndef REFRESHTASK_H 2 | #define REFRESHTASK_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class refreshtask : public QRunnable { 10 | public: 11 | refreshtask(QLabel *preview, QString currentPath, int depth); 12 | void run() override; 13 | void stop(); 14 | 15 | private: 16 | bool shouldrun; 17 | QLabel *preview; 18 | QString currentPath; 19 | int maxdepth; 20 | }; 21 | 22 | #endif // REFRESHTASK_H 23 | -------------------------------------------------------------------------------- /ressources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | test.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lightjohn/QtFileExplorer/fd5ca9f6eb206502d23c5375ba9623dac450e0b6/test.png --------------------------------------------------------------------------------