|
|
|
/*
|
|
|
|
* Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "filepropsdialog.h"
|
|
|
|
#include "ui_file-props.h"
|
|
|
|
#include "icontheme.h"
|
|
|
|
#include "utilities.h"
|
|
|
|
#include "fileoperation.h"
|
|
|
|
#include <QStringBuilder>
|
|
|
|
#include <QStringListModel>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QDateTime>
|
|
|
|
#include <QStandardPaths>
|
|
|
|
#include <QFileDialog>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include "core/totalsizejob.h"
|
|
|
|
#include "core/folder.h"
|
|
|
|
|
|
|
|
#define DIFFERENT_UIDS ((uid)-1)
|
|
|
|
#define DIFFERENT_GIDS ((gid)-1)
|
|
|
|
#define DIFFERENT_PERMS ((mode_t)-1)
|
|
|
|
|
|
|
|
namespace Fm {
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ACCESS_NO_CHANGE = 0,
|
|
|
|
ACCESS_READ_ONLY,
|
|
|
|
ACCESS_READ_WRITE,
|
|
|
|
ACCESS_FORBID
|
|
|
|
};
|
|
|
|
|
|
|
|
FilePropsDialog::FilePropsDialog(Fm::FileInfoList files, QWidget* parent, Qt::WindowFlags f):
|
|
|
|
QDialog(parent, f),
|
|
|
|
fileInfos_{std::move(files)},
|
|
|
|
fileInfo{fileInfos_.front()},
|
|
|
|
singleType(fileInfos_.isSameType()),
|
|
|
|
singleFile(fileInfos_.size() == 1 ? true : false) {
|
|
|
|
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
|
|
|
|
ui = new Ui::FilePropsDialog();
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
|
|
|
if(singleType) {
|
|
|
|
mimeType = fileInfo->mimeType();
|
|
|
|
}
|
|
|
|
|
|
|
|
totalSizeJob = new Fm::TotalSizeJob(fileInfos_.paths(), Fm::TotalSizeJob::DEFAULT);
|
|
|
|
|
|
|
|
initGeneralPage();
|
|
|
|
initPermissionsPage();
|
|
|
|
}
|
|
|
|
|
|
|
|
FilePropsDialog::~FilePropsDialog() {
|
|
|
|
// Stop the timer if it's still running
|
|
|
|
if(fileSizeTimer) {
|
|
|
|
fileSizeTimer->stop();
|
|
|
|
delete fileSizeTimer;
|
|
|
|
fileSizeTimer = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cancel the indexing job if it hasn't finished
|
|
|
|
if(totalSizeJob) {
|
|
|
|
totalSizeJob->cancel();
|
|
|
|
totalSizeJob = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// And finally delete the dialog's UI
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FilePropsDialog::initApplications() {
|
|
|
|
if(singleType && mimeType && !fileInfo->isDir()) {
|
|
|
|
ui->openWith->setMimeType(mimeType);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ui->openWith->hide();
|
|
|
|
ui->openWithLabel->hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FilePropsDialog::initPermissionsPage() {
|
|
|
|
// ownership handling
|
|
|
|
// get owner/group and mode of the first file in the list
|
|
|
|
uid = fileInfo->uid();
|
|
|
|
gid = fileInfo->gid();
|
|
|
|
mode_t mode = fileInfo->mode();
|
|
|
|
ownerPerm = (mode & (S_IRUSR | S_IWUSR | S_IXUSR));
|
|
|
|
groupPerm = (mode & (S_IRGRP | S_IWGRP | S_IXGRP));
|
|
|
|
otherPerm = (mode & (S_IROTH | S_IWOTH | S_IXOTH));
|
|
|
|
execPerm = (mode & (S_IXUSR | S_IXGRP | S_IXOTH));
|
|
|
|
allNative = fileInfo->isNative();
|
|
|
|
hasDir = S_ISDIR(mode);
|
|
|
|
|
|
|
|
// check if all selected files belongs to the same owner/group or have the same mode
|
|
|
|
// at the same time, check if all files are on native unix filesystems
|
|
|
|
for(auto& fi: fileInfos_) {
|
|
|
|
if(allNative && !fi->isNative()) {
|
|
|
|
allNative = false; // not all of the files are native
|
|
|
|
}
|
|
|
|
|
|
|
|
mode_t fi_mode = fi->mode();
|
|
|
|
if(S_ISDIR(fi_mode)) {
|
|
|
|
hasDir = true; // the files list contains dir(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
if(uid != DIFFERENT_UIDS && static_cast<uid_t>(uid) != fi->uid()) {
|
|
|
|
uid = DIFFERENT_UIDS; // not all files have the same owner
|
|
|
|
}
|
|
|
|
if(gid != DIFFERENT_GIDS && static_cast<gid_t>(gid) != fi->gid()) {
|
|
|
|
gid = DIFFERENT_GIDS; // not all files have the same owner group
|
|
|
|
}
|
|
|
|
|
|
|
|
if(ownerPerm != DIFFERENT_PERMS && ownerPerm != (fi_mode & (S_IRUSR | S_IWUSR | S_IXUSR))) {
|
|
|
|
ownerPerm = DIFFERENT_PERMS; // not all files have the same permission for owner
|
|
|
|
}
|
|
|
|
if(groupPerm != DIFFERENT_PERMS && groupPerm != (fi_mode & (S_IRGRP | S_IWGRP | S_IXGRP))) {
|
|
|
|
groupPerm = DIFFERENT_PERMS; // not all files have the same permission for grop
|
|
|
|
}
|
|
|
|
if(otherPerm != DIFFERENT_PERMS && otherPerm != (fi_mode & (S_IROTH | S_IWOTH | S_IXOTH))) {
|
|
|
|
otherPerm = DIFFERENT_PERMS; // not all files have the same permission for other
|
|
|
|
}
|
|
|
|
if(execPerm != DIFFERENT_PERMS && execPerm != (fi_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
|
|
|
|
execPerm = DIFFERENT_PERMS; // not all files have the same executable permission
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// init owner/group
|
|
|
|
initOwner();
|
|
|
|
|
|
|
|
// if all files are of the same type, and some of them are dirs => all of the items are dirs
|
|
|
|
// rwx values have different meanings for dirs
|
|
|
|
// Let's make it clear for the users
|
|
|
|
// init combo boxes for file permissions here
|
|
|
|
QStringList comboItems;
|
|
|
|
comboItems.append("---"); // no change
|
|
|
|
if(singleType && hasDir) { // all files are dirs
|
|
|
|
comboItems.append(tr("View folder content"));
|
|
|
|
comboItems.append(tr("View and modify folder content"));
|
|
|
|
ui->executable->hide();
|
|
|
|
}
|
|
|
|
else { //not all of the files are dirs
|
|
|
|
comboItems.append(tr("Read"));
|
|
|
|
comboItems.append(tr("Read and write"));
|
|
|
|
}
|
|
|
|
comboItems.append(tr("Forbidden"));
|
|
|
|
QStringListModel* comboModel = new QStringListModel(comboItems, this);
|
|
|
|
ui->ownerPerm->setModel(comboModel);
|
|
|
|
ui->groupPerm->setModel(comboModel);
|
|
|
|
ui->otherPerm->setModel(comboModel);
|
|
|
|
|
|
|
|
// owner
|
|
|
|
ownerPermSel = ACCESS_NO_CHANGE;
|
|
|
|
if(ownerPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
|
|
|
|
if(ownerPerm & S_IRUSR) { // can read
|
|
|
|
if(ownerPerm & S_IWUSR) { // can write
|
|
|
|
ownerPermSel = ACCESS_READ_WRITE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ownerPermSel = ACCESS_READ_ONLY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if((ownerPerm & S_IWUSR) == 0) { // cannot read or write
|
|
|
|
ownerPermSel = ACCESS_FORBID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ui->ownerPerm->setCurrentIndex(ownerPermSel);
|
|
|
|
|
|
|
|
// owner and group
|
|
|
|
groupPermSel = ACCESS_NO_CHANGE;
|
|
|
|
if(groupPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
|
|
|
|
if(groupPerm & S_IRGRP) { // can read
|
|
|
|
if(groupPerm & S_IWGRP) { // can write
|
|
|
|
groupPermSel = ACCESS_READ_WRITE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
groupPermSel = ACCESS_READ_ONLY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if((groupPerm & S_IWGRP) == 0) { // cannot read or write
|
|
|
|
groupPermSel = ACCESS_FORBID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ui->groupPerm->setCurrentIndex(groupPermSel);
|
|
|
|
|
|
|
|
// other
|
|
|
|
otherPermSel = ACCESS_NO_CHANGE;
|
|
|
|
if(otherPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
|
|
|
|
if(otherPerm & S_IROTH) { // can read
|
|
|
|
if(otherPerm & S_IWOTH) { // can write
|
|
|
|
otherPermSel = ACCESS_READ_WRITE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
otherPermSel = ACCESS_READ_ONLY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if((otherPerm & S_IWOTH) == 0) { // cannot read or write
|
|
|
|
otherPermSel = ACCESS_FORBID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
ui->otherPerm->setCurrentIndex(otherPermSel);
|
|
|
|
|
|
|
|
// set the checkbox to partially checked state
|
|
|
|
// when owner, group, and other have different executable flags set.
|
|
|
|
// some of them have exec, and others do not have.
|
|
|
|
execCheckState = Qt::PartiallyChecked;
|
|
|
|
if(execPerm != DIFFERENT_PERMS) { // if all files have the same executable permission
|
|
|
|
// check if the files are all executable
|
|
|
|
if((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == (S_IXUSR | S_IXGRP | S_IXOTH)) {
|
|
|
|
// owner, group, and other all have exec permission.
|
|
|
|
ui->executable->setTristate(false);
|
|
|
|
execCheckState = Qt::Checked;
|
|
|
|
}
|
|
|
|
else if((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
|
|
|
|
// owner, group, and other all have no exec permission
|
|
|
|
ui->executable->setTristate(false);
|
|
|
|
execCheckState = Qt::Unchecked;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ui->executable->setCheckState(execCheckState);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FilePropsDialog::initGeneralPage() {
|
|
|
|
// update UI
|
|
|
|
if(singleType) { // all files are of the same mime-type
|
|
|
|
std::shared_ptr<const Fm::IconInfo> icon;
|
|
|
|
// FIXME: handle custom icons for some files
|
|
|
|
// FIXME: display special property pages for special files or
|
|
|
|
// some specified mime-types.
|
|
|
|
if(singleFile) { // only one file is selected.
|
|
|
|
icon = fileInfo->icon();
|
|
|
|
}
|
|
|
|
if(mimeType) {
|
|
|
|
if(!icon) { // get an icon from mime type if needed
|
|
|
|
icon = mimeType->icon();
|
|
|
|
}
|
|
|
|
ui->fileType->setText(mimeType->desc());
|
|
|
|
ui->mimeType->setText(mimeType->name());
|
|
|
|
}
|
|
|
|
if(icon) {
|
|
|
|
ui->iconButton->setIcon(icon->qicon());
|
|
|
|
}
|
|
|
|
|
|
|
|
if(singleFile && fileInfo->isSymlink()) {
|
|
|
|
ui->target->setText(QString::fromStdString(fileInfo->target()));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ui->target->hide();
|
|
|
|
ui->targetLabel->hide();
|
|
|
|
}
|
|
|
|
if(fileInfo->isDir() && fileInfo->isNative()) { // all files are native dirs
|
|
|
|
connect(ui->iconButton, &QAbstractButton::clicked, this, &FilePropsDialog::onIconButtonclicked);
|
|
|
|
}
|
|
|
|
} // end if(singleType)
|
|
|
|
else { // not singleType, multiple files are selected at the same time
|
|
|
|
ui->fileType->setText(tr("Files of different types"));
|
|
|
|
ui->target->hide();
|
|
|
|
ui->targetLabel->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: check if all files has the same parent dir, mtime, or atime
|
|
|
|
if(singleFile) { // only one file is selected
|
|
|
|
auto parent_path = fileInfo->path().parent();
|
|
|
|
auto parent_str = parent_path ? parent_path.displayName(): nullptr;
|
|
|
|
|
|
|
|
ui->fileName->setText(fileInfo->displayName());
|
|
|
|
if(parent_str) {
|
|
|
|
ui->location->setText(parent_str.get());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ui->location->clear();
|
|
|
|
}
|
|
|
|
auto mtime = QDateTime::fromMSecsSinceEpoch(fileInfo->mtime() * 1000);
|
|
|
|
ui->lastModified->setText(mtime.toString(Qt::SystemLocaleShortDate));
|
|
|
|
auto atime = QDateTime::fromMSecsSinceEpoch(fileInfo->atime() * 1000);
|
|
|
|
ui->lastAccessed->setText(atime.toString(Qt::SystemLocaleShortDate));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ui->fileName->setText(tr("Multiple Files"));
|
|
|
|
ui->fileName->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
initApplications(); // init applications combo box
|
|
|
|
|
|
|
|
// calculate total file sizes
|
|
|
|
fileSizeTimer = new QTimer(this);
|
|
|
|
connect(fileSizeTimer, &QTimer::timeout, this, &FilePropsDialog::onFileSizeTimerTimeout);
|
|
|
|
fileSizeTimer->start(600);
|
|
|
|
|
|
|
|
connect(totalSizeJob, &Fm::TotalSizeJob::finished, this, &FilePropsDialog::onDeepCountJobFinished, Qt::BlockingQueuedConnection);
|
|
|
|
totalSizeJob->setAutoDelete(true);
|
|
|
|
totalSizeJob->runAsync();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FilePropsDialog::onDeepCountJobFinished() {
|
|
|
|
onFileSizeTimerTimeout(); // update file size display
|
|
|
|
|
|
|
|
totalSizeJob = nullptr;
|
|
|
|
|
|
|
|
// stop the timer
|
|
|
|
if(fileSizeTimer) {
|
|
|
|
fileSizeTimer->stop();
|
|
|
|
delete fileSizeTimer;
|
|
|
|
fileSizeTimer = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FilePropsDialog::onFileSizeTimerTimeout() {
|
|
|
|
if(totalSizeJob && !totalSizeJob->isCancelled()) {
|
|
|
|
// FIXME:
|
|
|
|
// OMG! It's really unbelievable that Qt developers only implement
|
|
|
|
// QObject::tr(... int n). GNU gettext developers are smarter and
|
|
|
|
// they use unsigned long instead of int.
|
|
|
|
// We cannot use Qt here to handle plural forms. So sad. :-(
|
|
|
|
QString str = Fm::formatFileSize(totalSizeJob->totalSize(), fm_config->si_unit) %
|
|
|
|
QString(" (%1 B)").arg(totalSizeJob->totalSize());
|
|
|
|
// tr(" (%n) byte(s)", "", deepCountJob->total_size);
|
|
|
|
ui->fileSize->setText(str);
|
|
|
|
|
|
|
|
str = Fm::formatFileSize(totalSizeJob->totalOnDiskSize(), fm_config->si_unit) %
|
|
|
|
QString(" (%1 B)").arg(totalSizeJob->totalOnDiskSize());
|
|
|
|
// tr(" (%n) byte(s)", "", deepCountJob->total_ondisk_size);
|
|
|
|
ui->onDiskSize->setText(str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FilePropsDialog::onIconButtonclicked() {
|
|
|
|
QString iconDir;
|
|
|
|
QString iconThemeName = QIcon::themeName();
|
|
|
|
QStringList icons = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
|
|
|
|
"icons",
|
|
|
|
QStandardPaths::LocateDirectory);
|
|
|
|
for (QStringList::ConstIterator it = icons.constBegin(); it != icons.constEnd(); ++it) {
|
|
|
|
QString iconThemeFolder = *it + '/' + iconThemeName;
|
|
|
|
if (QDir(iconThemeFolder).exists() && QFileInfo(iconThemeFolder).permission(QFileDevice::ReadUser)) {
|
|
|
|
// give priority to the "places" folder
|
|
|
|
const QString places = iconThemeFolder + QLatin1String("/places");
|
|
|
|
if (QDir(places).exists() && QFileInfo(places).permission(QFileDevice::ReadUser)) {
|
|
|
|
iconDir = places;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
iconDir = iconThemeFolder;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(iconDir.isEmpty()) {
|
|
|
|
iconDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
|
|
|
|
"icons",
|
|
|
|
QStandardPaths::LocateDirectory);
|
|
|
|
if(iconDir.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const QString iconPath = QFileDialog::getOpenFileName(this, tr("Select an icon"),
|
|
|
|
iconDir,
|
|
|
|
tr("Images (*.png *.xpm *.svg *.svgz )"));
|
|
|
|
if(!iconPath.isEmpty()) {
|
|
|
|
QStringList parts = iconPath.split("/", QString::SkipEmptyParts);
|
|
|
|
if(!parts.isEmpty()) {
|
|
|
|
QString iconName = parts.at(parts.count() - 1);
|
|
|
|
int ln = iconName.lastIndexOf(".");
|
|
|
|
if(ln > -1) {
|
|
|
|
iconName.remove(ln, iconName.length() - ln);
|
|
|
|
customIcon = QIcon::fromTheme(iconName);
|
|
|
|
ui->iconButton->setIcon(customIcon);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FilePropsDialog::accept() {
|
|
|
|
|
|
|
|
// applications
|
|
|
|
if(mimeType && ui->openWith->isChanged()) {
|
|
|
|
auto currentApp = ui->openWith->selectedApp();
|
|
|
|
g_app_info_set_as_default_for_type(currentApp.get(), mimeType->name(), nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if chown or chmod is needed
|
|
|
|
gint32 newUid = uidFromName(ui->owner->text());
|
|
|
|
gint32 newGid = gidFromName(ui->ownerGroup->text());
|
|
|
|
bool needChown = (newUid != -1 && newUid != uid) || (newGid != -1 && newGid != gid);
|
|
|
|
|
|
|
|
int newOwnerPermSel = ui->ownerPerm->currentIndex();
|
|
|
|
int newGroupPermSel = ui->groupPerm->currentIndex();
|
|
|
|
int newOtherPermSel = ui->otherPerm->currentIndex();
|
|
|
|
Qt::CheckState newExecCheckState = ui->executable->checkState();
|
|
|
|
bool needChmod = ((newOwnerPermSel != ownerPermSel) ||
|
|
|
|
(newGroupPermSel != groupPermSel) ||
|
|
|
|
(newOtherPermSel != otherPermSel) ||
|
|
|
|
(newExecCheckState != execCheckState));
|
|
|
|
|
|
|
|
if(needChmod || needChown) {
|
|
|
|
FileOperation* op = new FileOperation(FileOperation::ChangeAttr, fileInfos_.paths());
|
|
|
|
if(needChown) {
|
|
|
|
// don't do chown if new uid/gid and the original ones are actually the same.
|
|
|
|
if(newUid == uid) {
|
|
|
|
newUid = -1;
|
|
|
|
}
|
|
|
|
if(newGid == gid) {
|
|
|
|
newGid = -1;
|
|
|
|
}
|
|
|
|
op->setChown(newUid, newGid);
|
|
|
|
}
|
|
|
|
if(needChmod) {
|
|
|
|
mode_t newMode = 0;
|
|
|
|
mode_t newModeMask = 0;
|
|
|
|
// FIXME: we need to make sure that folders with "r" permission also have "x"
|
|
|
|
// at the same time. Otherwise, it's not able to browse the folder later.
|
|
|
|
if(newOwnerPermSel != ownerPermSel && newOwnerPermSel != ACCESS_NO_CHANGE) {
|
|
|
|
// owner permission changed
|
|
|
|
newModeMask |= (S_IRUSR | S_IWUSR); // affect user bits
|
|
|
|
if(newOwnerPermSel == ACCESS_READ_ONLY) {
|
|
|
|
newMode |= S_IRUSR;
|
|
|
|
}
|
|
|
|
else if(newOwnerPermSel == ACCESS_READ_WRITE) {
|
|
|
|
newMode |= (S_IRUSR | S_IWUSR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(newGroupPermSel != groupPermSel && newGroupPermSel != ACCESS_NO_CHANGE) {
|
|
|
|
qDebug("newGroupPermSel: %d", newGroupPermSel);
|
|
|
|
// group permission changed
|
|
|
|
newModeMask |= (S_IRGRP | S_IWGRP); // affect group bits
|
|
|
|
if(newGroupPermSel == ACCESS_READ_ONLY) {
|
|
|
|
newMode |= S_IRGRP;
|
|
|
|
}
|
|
|
|
else if(newGroupPermSel == ACCESS_READ_WRITE) {
|
|
|
|
newMode |= (S_IRGRP | S_IWGRP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(newOtherPermSel != otherPermSel && newOtherPermSel != ACCESS_NO_CHANGE) {
|
|
|
|
// other permission changed
|
|
|
|
newModeMask |= (S_IROTH | S_IWOTH); // affect other bits
|
|
|
|
if(newOtherPermSel == ACCESS_READ_ONLY) {
|
|
|
|
newMode |= S_IROTH;
|
|
|
|
}
|
|
|
|
else if(newOtherPermSel == ACCESS_READ_WRITE) {
|
|
|
|
newMode |= (S_IROTH | S_IWOTH);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(newExecCheckState != execCheckState && newExecCheckState != Qt::PartiallyChecked) {
|
|
|
|
// executable state changed
|
|
|
|
newModeMask |= (S_IXUSR | S_IXGRP | S_IXOTH);
|
|
|
|
if(newExecCheckState == Qt::Checked) {
|
|
|
|
newMode |= (S_IXUSR | S_IXGRP | S_IXOTH);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
op->setChmod(newMode, newModeMask);
|
|
|
|
|
|
|
|
if(hasDir) { // if there are some dirs in our selected files
|
|
|
|
QMessageBox::StandardButton r = QMessageBox::question(this,
|
|
|
|
tr("Apply changes"),
|
|
|
|
tr("Do you want to recursively apply these changes to all files and sub-folders?"),
|
|
|
|
QMessageBox::Yes | QMessageBox::No);
|
|
|
|
if(r == QMessageBox::Yes) {
|
|
|
|
op->setRecursiveChattr(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
op->setAutoDestroy(true);
|
|
|
|
op->run();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Renaming
|
|
|
|
if(singleFile) {
|
|
|
|
QString new_name = ui->fileName->text();
|
|
|
|
if(fileInfo->displayName() != new_name) {
|
|
|
|
auto path = fileInfo->path();
|
|
|
|
auto parent_path = path.parent();
|
|
|
|
auto dest = parent_path.child(new_name.toLocal8Bit().constData());
|
|
|
|
Fm::GErrorPtr err;
|
|
|
|
if(!g_file_move(path.gfile().get(), dest.gfile().get(),
|
|
|
|
GFileCopyFlags(G_FILE_COPY_ALL_METADATA |
|
|
|
|
G_FILE_COPY_NO_FALLBACK_FOR_MOVE |
|
|
|
|
G_FILE_COPY_NOFOLLOW_SYMLINKS),
|
|
|
|
nullptr, nullptr, nullptr, &err)) {
|
|
|
|
QMessageBox::critical(this, QObject::tr("Error"), err.message());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Custom (folder) icon
|
|
|
|
if(!customIcon.isNull()) {
|
|
|
|
bool reloadNeeded(false);
|
|
|
|
QString iconNamne = customIcon.name();
|
|
|
|
for(auto& fi: fileInfos_) {
|
|
|
|
std::shared_ptr<const Fm::IconInfo> icon = fi->icon();
|
|
|
|
if (!fi->icon() || fi->icon()->qicon().name() != iconNamne) {
|
|
|
|
auto dot_dir = CStrPtr{g_build_filename(fi->path().localPath().get(), ".directory", nullptr)};
|
|
|
|
GKeyFile* kf = g_key_file_new();
|
|
|
|
g_key_file_set_string(kf, "Desktop Entry", "Icon", iconNamne.toLocal8Bit().constData());
|
|
|
|
Fm::GErrorPtr err;
|
|
|
|
if (!g_key_file_save_to_file(kf, dot_dir.get(), &err)) {
|
|
|
|
QMessageBox::critical(this, QObject::tr("Custom Icon Error"), err.message());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
reloadNeeded = true;
|
|
|
|
}
|
|
|
|
g_key_file_free(kf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(reloadNeeded) {
|
|
|
|
// since there can be only one parent dir, only one reload is needed
|
|
|
|
auto parent = fileInfo->path().parent();
|
|
|
|
if(parent.isValid()) {
|
|
|
|
auto folder = Fm::Folder::fromPath(parent);
|
|
|
|
if(folder->isLoaded()) {
|
|
|
|
folder->reload();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QDialog::accept();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FilePropsDialog::initOwner() {
|
|
|
|
if(allNative) {
|
|
|
|
if(uid != DIFFERENT_UIDS) {
|
|
|
|
ui->owner->setText(uidToName(uid));
|
|
|
|
}
|
|
|
|
if(gid != DIFFERENT_GIDS) {
|
|
|
|
ui->ownerGroup->setText(gidToName(gid));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(geteuid() != 0) { // on local filesystems, only root can do chown.
|
|
|
|
ui->owner->setEnabled(false);
|
|
|
|
ui->ownerGroup->setEnabled(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace Fm
|