File indexing completed on 2025-02-23 04:34:24
0001 /** 0002 * \file frametable.cpp 0003 * Table to edit frames. 0004 * 0005 * \b Project: Kid3 0006 * \author Urs Fleisch 0007 * \date 05 Sep 2007 0008 * 0009 * Copyright (C) 2007-2024 Urs Fleisch 0010 * 0011 * This file is part of Kid3. 0012 * 0013 * Kid3 is free software; you can redistribute it and/or modify 0014 * it under the terms of the GNU General Public License as published by 0015 * the Free Software Foundation; either version 2 of the License, or 0016 * (at your option) any later version. 0017 * 0018 * Kid3 is distributed in the hope that it will be useful, 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0021 * GNU General Public License for more details. 0022 * 0023 * You should have received a copy of the GNU General Public License 0024 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0025 */ 0026 0027 #include "frametable.h" 0028 #include <QAction> 0029 #include <QPoint> 0030 #include <QHeaderView> 0031 #include <QMenu> 0032 #include <QChildEvent> 0033 #include <QLineEdit> 0034 #include "frametablemodel.h" 0035 #include "frameitemdelegate.h" 0036 0037 /** 0038 * Constructor. 0039 * 0040 * @param model frame table model 0041 * @param genreModel genre model 0042 * @param parent parent widget 0043 */ 0044 FrameTable::FrameTable(FrameTableModel* model, GenreModel* genreModel, 0045 QWidget* parent) 0046 : QTableView(parent), m_currentEditor(nullptr) 0047 { 0048 setObjectName(QLatin1String("FrameTable")); 0049 setModel(model); 0050 setSelectionMode(SingleSelection); 0051 horizontalHeader()->setSectionResizeMode(FrameTableModel::CI_Value, QHeaderView::Stretch); 0052 // Set a small height instead of hiding the header, so that the column 0053 // widths can still be resized by the user. 0054 if (model->headersEmpty()) { 0055 horizontalHeader()->setFixedHeight(2); 0056 } 0057 verticalHeader()->hide(); 0058 if (model->isId3v1()) { 0059 bool insertTemporaryRow = model->rowCount() < 1; 0060 if (insertTemporaryRow) 0061 model->insertRow(0); 0062 setMinimumHeight((Frame::FT_LastV1Frame + 1) * (rowHeight(0) + 1)); 0063 if (insertTemporaryRow) 0064 model->removeRow(0); 0065 } 0066 // Set width of first column 0067 #if QT_VERSION >= 0x050b00 0068 int width = fontMetrics().horizontalAdvance(tr("WWW Audio Source") + QLatin1String("WW")); 0069 #else 0070 int width = fontMetrics().width(tr("WWW Audio Source") + QLatin1String("WW")); 0071 #endif 0072 QStyleOptionButton option; 0073 option.initFrom(this); 0074 width += style()->subElementRect( 0075 QStyle::SE_ItemViewItemCheckIndicator, &option, this).width(); 0076 setColumnWidth(FrameTableModel::CI_Enable, width); 0077 0078 horizontalHeader()->setSectionResizeMode(FrameTableModel::CI_Value, QHeaderView::Stretch); 0079 setItemDelegate(new FrameItemDelegate(genreModel, this)); 0080 setEditTriggers(AllEditTriggers); 0081 viewport()->installEventFilter(this); // keep track of editors 0082 setContextMenuPolicy(Qt::CustomContextMenu); 0083 connect(this, &QWidget::customContextMenuRequested, 0084 this, &FrameTable::customContextMenu); 0085 } 0086 0087 /** 0088 * Filters events if this object has been installed as an event filter 0089 * for the watched object. 0090 * This method is reimplemented to keep track of the current open editor. 0091 * It has to be installed on the viewport of the table. 0092 * @param watched watched object 0093 * @param event event 0094 * @return true to filter event out. 0095 */ 0096 bool FrameTable::eventFilter(QObject* watched, QEvent* event) 0097 { 0098 if (event) { 0099 if (QEvent::Type type = event->type(); type == QEvent::ChildAdded) { 0100 if (QObject* obj = static_cast<QChildEvent*>(event)->child(); 0101 obj && obj->isWidgetType()) { 0102 m_currentEditor = static_cast<QWidget*>(obj); 0103 } 0104 } else if (type == QEvent::ChildRemoved) { 0105 if (m_currentEditor == static_cast<QChildEvent*>(event)->child()) { 0106 m_currentEditor = nullptr; 0107 } 0108 } else if (type == QEvent::WindowDeactivate) { 0109 // this is done to avoid losing focus when the window is deactivated 0110 // while editing a cell (i.e. the cell is not closed by pressing Enter) 0111 if ((state() == QAbstractItemView::EditingState) && m_currentEditor) { 0112 commitData(m_currentEditor); 0113 closeEditor(m_currentEditor, QAbstractItemDelegate::EditPreviousItem); 0114 } 0115 } 0116 } 0117 return QTableView::eventFilter(watched, event); 0118 } 0119 0120 /** 0121 * Commit data from the current editor. 0122 * This is used to avoid losing the changes in open editors e.g. when 0123 * the file is changed using Alt-Up or Alt-Down. 0124 * 0125 * @return true if data was committed. 0126 */ 0127 bool FrameTable::acceptEdit() 0128 { 0129 if ((state() == QAbstractItemView::EditingState) && m_currentEditor) { 0130 commitData(m_currentEditor); 0131 // close editor to avoid being stuck in QAbstractItemView::NoState 0132 closeEditor(m_currentEditor, QAbstractItemDelegate::NoHint); 0133 return true; 0134 } 0135 return false; 0136 } 0137 0138 /** 0139 * Get current editor widget if the table is currently in edit state. 0140 * @return current editor widget, 0 if not in edit state. 0141 */ 0142 const QWidget* FrameTable::getCurrentEditor() const 0143 { 0144 return state() == EditingState ? m_currentEditor : nullptr; 0145 } 0146 0147 /** 0148 * Display context menu. 0149 * 0150 * @param row row at which context menu is displayed 0151 * @param col column at which context menu is displayed 0152 * @param pos position where context menu is drawn on screen 0153 */ 0154 void FrameTable::contextMenu(int row, int col, const QPoint& pos) 0155 { 0156 if (const auto ftModel = 0157 qobject_cast<const FrameTableModel*>(model()); 0158 ftModel && col == 0 && row >= 0) { 0159 QMenu menu(this); 0160 QAction* action = menu.addAction(tr("&Select all")); 0161 connect(action, &QAction::triggered, ftModel, &FrameTableModel::selectAllFrames); 0162 action = menu.addAction(tr("&Deselect all")); 0163 connect(action, &QAction::triggered, ftModel, &FrameTableModel::deselectAllFrames); 0164 menu.setMouseTracking(true); 0165 menu.exec(pos); 0166 } 0167 } 0168 0169 /** 0170 * Display custom context menu. 0171 * 0172 * @param pos position where context menu is drawn on screen 0173 */ 0174 void FrameTable::customContextMenu(const QPoint& pos) 0175 { 0176 if (QModelIndex index = indexAt(pos); index.isValid()) { 0177 contextMenu(index.row(), index.column(), mapToGlobal(pos)); 0178 } 0179 } 0180 0181 /** 0182 * Select in the editor of a value row. 0183 * @param row row number 0184 * @param start start position 0185 * @param length number of characters to select 0186 */ 0187 void FrameTable::setValueSelection(int row, int start, int length) 0188 { 0189 if (const auto ftModel = 0190 qobject_cast<const FrameTableModel*>(model())) { 0191 if (QModelIndex idx = ftModel->index(row, FrameTableModel::CI_Value); 0192 idx.isValid()) { 0193 scrollTo(idx); 0194 setCurrentIndex(idx); 0195 edit(idx); 0196 if (auto le = qobject_cast<QLineEdit*>(indexWidget(idx))) { 0197 le->setSelection(start, length); 0198 } 0199 } 0200 } 0201 }