File indexing completed on 2024-05-12 17:22:01
0001 /* 0002 SPDX-FileCopyrightText: 2002 Shie Erlich <erlich@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2002 Rafi Yanai <yanai@users.sourceforge.net> 0004 SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "krinterdetailedview.h" 0010 0011 // QtCore 0012 #include <QDebug> 0013 #include <QDir> 0014 #include <QHashIterator> 0015 // QtWidgets 0016 #include <QApplication> 0017 #include <QDirModel> 0018 #include <QHeaderView> 0019 #include <QMenu> 0020 #include <QToolTip> 0021 0022 #include <KConfigCore/KSharedConfig> 0023 #include <KI18n/KLocalizedString> 0024 #include <KIOWidgets/KDirLister> 0025 0026 #include "../FileSystem/krpermhandler.h" 0027 #include "../GUI/krstyleproxy.h" 0028 #include "../compat.h" 0029 #include "../defaults.h" 0030 #include "../krcolorcache.h" 0031 #include "../krglobal.h" 0032 #include "krmousehandler.h" 0033 #include "krviewfactory.h" 0034 #include "krviewitem.h" 0035 #include "krviewitemdelegate.h" 0036 #include "listmodel.h" 0037 0038 KrInterDetailedView::KrInterDetailedView(QWidget *parent, KrViewInstance &instance, KConfig *cfg) 0039 : QTreeView(parent) 0040 , KrInterView(instance, cfg, this) 0041 , _autoResizeColumns(true) 0042 { 0043 connect(_mouseHandler, &KrMouseHandler::renameCurrentItem, this, &KrInterDetailedView::renameCurrentItem); 0044 setWidget(this); 0045 0046 KConfigGroup grpSvr(_config, "Look&Feel"); 0047 _viewFont = grpSvr.readEntry("Filelist Font", _FilelistFont); 0048 0049 setModel(_model); 0050 setRootIsDecorated(false); 0051 setItemsExpandable(false); 0052 setAllColumnsShowFocus(true); 0053 setUniformRowHeights(true); 0054 0055 setMouseTracking(true); 0056 setAcceptDrops(true); 0057 setDropIndicatorShown(true); 0058 0059 setSelectionMode(QAbstractItemView::NoSelection); 0060 setSelectionModel(new DummySelectionModel(_model, this)); 0061 0062 header()->installEventFilter(this); 0063 header()->setSectionResizeMode(QHeaderView::Interactive); 0064 header()->setStretchLastSection(false); 0065 0066 auto *style = new KrStyleProxy(); 0067 style->setParent(this); 0068 setStyle(style); 0069 viewport()->setStyle(style); // for custom tooltip delay 0070 0071 setItemDelegate(new KrViewItemDelegate(this)); 0072 0073 connect(header(), &QHeaderView::sectionResized, this, &KrInterDetailedView::sectionResized); 0074 connect(header(), &QHeaderView::sectionMoved, this, &KrInterDetailedView::sectionMoved); 0075 } 0076 0077 KrInterDetailedView::~KrInterDetailedView() 0078 { 0079 delete _properties; 0080 _properties = nullptr; 0081 delete _operator; 0082 _operator = nullptr; 0083 } 0084 0085 void KrInterDetailedView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) 0086 { 0087 if (_model->ready()) { 0088 KrViewItem *item = getKrViewItem(currentIndex()); 0089 op()->emitCurrentChanged(item); 0090 } 0091 QTreeView::currentChanged(current, previous); 0092 } 0093 0094 void KrInterDetailedView::doRestoreSettings(KConfigGroup grp) 0095 { 0096 auto headerView = header(); 0097 0098 _autoResizeColumns = grp.readEntry("AutoResizeColumns", true); 0099 0100 QByteArray savedState = grp.readEntry("Saved State", QByteArray()); 0101 if (savedState.isEmpty()) { 0102 hideColumn(KrViewProperties::Type); 0103 hideColumn(KrViewProperties::Permissions); 0104 hideColumn(KrViewProperties::Owner); 0105 hideColumn(KrViewProperties::Group); 0106 hideColumn(KrViewProperties::Changed); 0107 hideColumn(KrViewProperties::Accessed); 0108 headerView->resizeSection(KrViewProperties::Ext, QFontMetrics(_viewFont).horizontalAdvance("tar.bz2 ")); 0109 headerView->resizeSection(KrViewProperties::KrPermissions, QFontMetrics(_viewFont).horizontalAdvance("rwx ")); 0110 headerView->resizeSection(KrViewProperties::Size, QFontMetrics(_viewFont).horizontalAdvance("9") * 10); 0111 0112 QDateTime tmp(QDate(2099, 12, 29), QTime(23, 59)); 0113 QString desc = QLocale().toString(tmp, QLocale::ShortFormat) + " "; 0114 0115 headerView->resizeSection(KrViewProperties::Modified, QFontMetrics(_viewFont).horizontalAdvance(desc)); 0116 } else { 0117 headerView->restoreState(savedState); 0118 0119 // do not show new columns by default; restoreState() shows columns not saved 0120 if (KrGlobal::sCurrentConfigVersion < KrGlobal::sConfigVersion) { 0121 hideColumn(KrViewProperties::Changed); 0122 hideColumn(KrViewProperties::Accessed); 0123 } 0124 0125 _model->setExtensionEnabled(!isColumnHidden(KrViewProperties::Ext)); 0126 } 0127 0128 // In case a column is assigned zero size for some reason, it's impossible to fix this from interface. 0129 // We correct this problem by enforcing the minimum width of the column. 0130 auto minSize = headerView->minimumSectionSize(); 0131 for (int i = KrViewProperties::Ext; i < KrViewProperties::MAX_COLUMNS; i++) { 0132 if (!headerView->isSectionHidden(i) && headerView->sectionSize(i) < minSize) { 0133 headerView->resizeSection(i, minSize); 0134 } 0135 } 0136 0137 KrInterView::doRestoreSettings(grp); 0138 } 0139 0140 void KrInterDetailedView::saveSettings(KConfigGroup grp, KrViewProperties::PropertyType properties) 0141 { 0142 KrInterView::saveSettings(grp, properties); 0143 0144 grp.writeEntry("AutoResizeColumns", _autoResizeColumns); 0145 0146 if (properties & KrViewProperties::PropColumns) { 0147 QByteArray state = header()->saveState(); 0148 grp.writeEntry("Saved State", state); 0149 } 0150 } 0151 0152 int KrInterDetailedView::itemsPerPage() 0153 { 0154 QRect rect = visualRect(currentIndex()); 0155 if (!rect.isValid()) { 0156 for (int i = 0; i != _model->rowCount(); i++) { 0157 rect = visualRect(_model->index(i, 0)); 0158 if (rect.isValid()) 0159 break; 0160 } 0161 } 0162 if (!rect.isValid()) 0163 return 0; 0164 int size = (height() - header()->height()) / rect.height(); 0165 if (size < 0) 0166 size = 0; 0167 return size; 0168 } 0169 0170 void KrInterDetailedView::updateView() 0171 { 0172 } 0173 0174 void KrInterDetailedView::setup() 0175 { 0176 setSortMode(_properties->sortColumn, (_properties->sortOptions & KrViewProperties::Descending)); 0177 setSortingEnabled(true); 0178 } 0179 0180 void KrInterDetailedView::keyPressEvent(QKeyEvent *e) 0181 { 0182 if (!e || !_model->ready()) 0183 return; // subclass bug 0184 if (handleKeyEvent(e)) // did the view class handled the event? 0185 return; 0186 QTreeView::keyPressEvent(e); 0187 } 0188 0189 void KrInterDetailedView::mousePressEvent(QMouseEvent *ev) 0190 { 0191 if (!_mouseHandler->mousePressEvent(ev)) 0192 QTreeView::mousePressEvent(ev); 0193 } 0194 0195 void KrInterDetailedView::mouseReleaseEvent(QMouseEvent *ev) 0196 { 0197 if (!_mouseHandler->mouseReleaseEvent(ev)) 0198 QTreeView::mouseReleaseEvent(ev); 0199 } 0200 0201 void KrInterDetailedView::mouseDoubleClickEvent(QMouseEvent *ev) 0202 { 0203 if (!_mouseHandler->mouseDoubleClickEvent(ev)) 0204 QTreeView::mouseDoubleClickEvent(ev); 0205 } 0206 0207 void KrInterDetailedView::mouseMoveEvent(QMouseEvent *ev) 0208 { 0209 if (!_mouseHandler->mouseMoveEvent(ev)) 0210 QTreeView::mouseMoveEvent(ev); 0211 } 0212 0213 void KrInterDetailedView::wheelEvent(QWheelEvent *ev) 0214 { 0215 if (!_mouseHandler->wheelEvent(ev)) 0216 QTreeView::wheelEvent(ev); 0217 } 0218 0219 void KrInterDetailedView::dragEnterEvent(QDragEnterEvent *ev) 0220 { 0221 if (!_mouseHandler->dragEnterEvent(ev)) 0222 QTreeView::dragEnterEvent(ev); 0223 } 0224 0225 void KrInterDetailedView::dragMoveEvent(QDragMoveEvent *ev) 0226 { 0227 QTreeView::dragMoveEvent(ev); 0228 _mouseHandler->dragMoveEvent(ev); 0229 } 0230 0231 void KrInterDetailedView::dragLeaveEvent(QDragLeaveEvent *ev) 0232 { 0233 if (!_mouseHandler->dragLeaveEvent(ev)) 0234 QTreeView::dragLeaveEvent(ev); 0235 } 0236 0237 void KrInterDetailedView::dropEvent(QDropEvent *ev) 0238 { 0239 if (!_mouseHandler->dropEvent(ev)) 0240 QTreeView::dropEvent(ev); 0241 } 0242 0243 bool KrInterDetailedView::event(QEvent *e) 0244 { 0245 _mouseHandler->otherEvent(e); 0246 return QTreeView::event(e); 0247 } 0248 0249 void KrInterDetailedView::renameCurrentItem() 0250 { 0251 QModelIndex nameIndex = _model->index(currentIndex().row(), KrViewProperties::Name); 0252 0253 // cycle through various text selections if we are in the editing mode already 0254 if (state() == QAbstractItemView::EditingState) { 0255 auto delegate = dynamic_cast<KrViewItemDelegate *>(itemDelegate(nameIndex)); 0256 if (!delegate) { 0257 qWarning() << "KrInterView item delegate is not KrViewItemDelegate, selection is not updated"; 0258 return; 0259 } 0260 0261 delegate->cycleEditorSelection(); 0262 return; 0263 } 0264 0265 // create and show file name editor 0266 edit(nameIndex); 0267 updateEditorData(); 0268 update(nameIndex); 0269 } 0270 0271 bool KrInterDetailedView::eventFilter(QObject *object, QEvent *event) 0272 { 0273 if (object == header()) { 0274 if (event->type() == QEvent::ContextMenu) { 0275 auto *me = dynamic_cast<QContextMenuEvent *>(event); 0276 showContextMenu(me->globalPos()); 0277 return true; 0278 } else if (event->type() == QEvent::Resize) { 0279 recalculateColumnSizes(); 0280 return false; 0281 } 0282 } 0283 return false; 0284 } 0285 0286 void KrInterDetailedView::showContextMenu(const QPoint &p) 0287 { 0288 QMenu popup(this); 0289 popup.setTitle(i18n("Columns")); 0290 0291 QVector<QAction *> actions; 0292 0293 for (int i = KrViewProperties::Ext; i < KrViewProperties::MAX_COLUMNS; i++) { 0294 QString text = (_model->headerData(i, Qt::Horizontal)).toString(); 0295 QAction *act = popup.addAction(text); 0296 act->setCheckable(true); 0297 act->setChecked(!header()->isSectionHidden(i)); 0298 act->setData(i); 0299 actions.append(act); 0300 } 0301 0302 popup.addSeparator(); 0303 QAction *actAutoResize = popup.addAction(i18n("Automatically Resize Columns")); 0304 actAutoResize->setCheckable(true); 0305 actAutoResize->setChecked(_autoResizeColumns); 0306 0307 QAction *res = popup.exec(p); 0308 if (res == nullptr) 0309 return; 0310 0311 if (res == actAutoResize) { 0312 _autoResizeColumns = actAutoResize->isChecked(); 0313 recalculateColumnSizes(); 0314 } else { 0315 int column = res->data().toInt(); 0316 0317 if (header()->isSectionHidden(column)) 0318 header()->showSection(column); 0319 else 0320 header()->hideSection(column); 0321 0322 if (KrViewProperties::Ext == column) 0323 _model->setExtensionEnabled(!header()->isSectionHidden(KrViewProperties::Ext)); 0324 } 0325 op()->settingsChanged(KrViewProperties::PropColumns); 0326 } 0327 0328 void KrInterDetailedView::sectionResized(int /*column*/, int oldSize, int newSize) 0329 { 0330 // *** taken from dolphin *** 0331 // If the user changes the size of the headers, the autoresize feature should be 0332 // turned off. As there is no dedicated interface to find out whether the header 0333 // section has been resized by the user or by a resize event, another approach is used. 0334 // Attention: Take care when changing the if-condition to verify that there is no 0335 // regression in combination with bug 178630 (see fix in comment #8). 0336 if ((QApplication::mouseButtons() & Qt::LeftButton) && header()->underMouse()) { 0337 _autoResizeColumns = false; 0338 op()->settingsChanged(KrViewProperties::PropColumns); 0339 } 0340 0341 if (oldSize == newSize || !_model->ready()) 0342 return; 0343 0344 recalculateColumnSizes(); 0345 } 0346 0347 void KrInterDetailedView::sectionMoved(int /*logicalIndex*/, int /*oldVisualIndex*/, int /*newVisualIndex*/) 0348 { 0349 op()->settingsChanged(KrViewProperties::PropColumns); 0350 } 0351 0352 void KrInterDetailedView::recalculateColumnSizes() 0353 { 0354 if (!_autoResizeColumns) 0355 return; 0356 int sum = 0; 0357 for (int i = 0; i != _model->columnCount(); i++) { 0358 if (!isColumnHidden(i)) 0359 sum += header()->sectionSize(i); 0360 } 0361 0362 if (sum != header()->width()) { 0363 int delta = sum - header()->width(); 0364 int nameSize = header()->sectionSize(KrViewProperties::Name); 0365 if (nameSize - delta > 20) 0366 header()->resizeSection(KrViewProperties::Name, nameSize - delta); 0367 } 0368 } 0369 0370 bool KrInterDetailedView::viewportEvent(QEvent *event) 0371 { 0372 if (event->type() == QEvent::ToolTip) { 0373 // only show tooltip if column is not wide enough to show all text. In this case the column 0374 // data text is abbreviated and the full text is shown as tooltip, see ListModel::data(). 0375 0376 auto *he = dynamic_cast<QHelpEvent *>(event); 0377 const QModelIndex index = indexAt(he->pos()); 0378 // name column has a detailed tooltip 0379 if (index.isValid() && index.column() != KrViewProperties::Name) { 0380 int width = header()->sectionSize(index.column()); 0381 QString text = index.data(Qt::DisplayRole).toString(); 0382 0383 int textWidth = QFontMetrics(_viewFont).horizontalAdvance(text); 0384 0385 const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; 0386 textWidth += 2 * textMargin; 0387 0388 QVariant decor = index.data(Qt::DecorationRole); 0389 if (decor.isValid() && decor.type() == QVariant::Pixmap) { 0390 QPixmap p = decor.value<QPixmap>(); 0391 textWidth += p.width() + 2 * textMargin; 0392 } 0393 0394 if (textWidth <= width) { 0395 QToolTip::hideText(); 0396 event->accept(); 0397 return true; 0398 } 0399 } 0400 } 0401 return QTreeView::viewportEvent(event); 0402 } 0403 0404 void KrInterDetailedView::drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const 0405 { 0406 QTreeView::drawRow(painter, options, index); 0407 // (may) draw dashed line border around current item row. This is done internally in 0408 // QTreeView::drawRow() only when panel is focused, we have to repeat it here. 0409 if (index == currentIndex() && drawCurrent()) { 0410 QStyleOptionFocusRect o; 0411 o.backgroundColor = options.palette.color(QPalette::Normal, QPalette::Background); 0412 0413 const QRect focusRect(0, options.rect.y(), header()->length(), options.rect.height()); 0414 o.rect = style()->visualRect(layoutDirection(), viewport()->rect(), focusRect); 0415 0416 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter); 0417 } 0418 } 0419 0420 void KrInterDetailedView::setSortMode(KrViewProperties::ColumnType sortColumn, bool descending) 0421 { 0422 Qt::SortOrder sortDir = descending ? Qt::DescendingOrder : Qt::AscendingOrder; 0423 sortByColumn(sortColumn, sortDir); 0424 } 0425 0426 void KrInterDetailedView::setFileIconSize(int size) 0427 { 0428 KrView::setFileIconSize(size); 0429 setIconSize(QSize(fileIconSize(), fileIconSize())); 0430 } 0431 0432 QRect KrInterDetailedView::itemRect(const FileItem *item) 0433 { 0434 QRect r = visualRect(_model->fileItemIndex(item)); 0435 r.setLeft(0); 0436 r.setWidth(header()->length()); 0437 return r; 0438 } 0439 0440 void KrInterDetailedView::copySettingsFrom(KrView *other) 0441 { 0442 if (other->instance() == instance()) { // the other view is of the same type 0443 auto *v = dynamic_cast<KrInterDetailedView *>(other); 0444 _autoResizeColumns = v->_autoResizeColumns; 0445 header()->restoreState(v->header()->saveState()); 0446 _model->setExtensionEnabled(!isColumnHidden(KrViewProperties::Ext)); 0447 recalculateColumnSizes(); 0448 setFileIconSize(v->fileIconSize()); 0449 } 0450 }