File indexing completed on 2024-05-12 05:09:49
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "tablefieldwidget.h" 0026 #include "../field.h" 0027 #include "../fieldformat.h" 0028 #include "../utils/string_utils.h" 0029 #include "../tellico_debug.h" 0030 0031 #include <KLocalizedString> 0032 0033 #include <QMenu> 0034 #include <QTableWidget> 0035 #include <QMouseEvent> 0036 #include <QEvent> 0037 #include <QHeaderView> 0038 #include <QIcon> 0039 #include <QInputDialog> 0040 0041 namespace { 0042 static const int MIN_TABLE_ROWS = 5; 0043 static const int MAX_TABLE_COLS = 10; 0044 } 0045 0046 using Tellico::GUI::TableFieldWidget; 0047 0048 TableFieldWidget::TableFieldWidget(Tellico::Data::FieldPtr field_, QWidget* parent_) 0049 : FieldWidget(field_, parent_), m_row(-1), m_col(-1) { 0050 0051 bool ok; 0052 m_columns = Tellico::toUInt(field_->property(QStringLiteral("columns")), &ok); 0053 if(!ok) { 0054 m_columns = 1; 0055 } else { 0056 m_columns = qMin(m_columns, MAX_TABLE_COLS); 0057 } 0058 0059 m_table = new QTableWidget(MIN_TABLE_ROWS, m_columns, this); 0060 labelColumns(field()); 0061 0062 m_table->setDragEnabled(false); 0063 0064 m_table->horizontalHeader()->setSectionResizeMode(m_columns-1, QHeaderView::Interactive); 0065 m_table->resizeColumnToContents(m_columns-1); 0066 m_table->setSelectionMode(QAbstractItemView::NoSelection); 0067 // m_table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0068 0069 // capture all the context menu events 0070 m_table->setContextMenuPolicy(Qt::CustomContextMenu); 0071 m_table->verticalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); 0072 m_table->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); 0073 0074 connect(m_table, &QTableWidget::itemChanged, this, &TableFieldWidget::checkModified); 0075 connect(m_table, &QTableWidget::itemChanged, this, &TableFieldWidget::slotResizeColumn); 0076 connect(m_table, &QTableWidget::currentCellChanged, this, &TableFieldWidget::slotCheckRows); 0077 connect(m_table, &QWidget::customContextMenuRequested, this, &TableFieldWidget::tableContextMenu); 0078 connect(m_table->horizontalHeader(), &QWidget::customContextMenuRequested, this, &TableFieldWidget::horizontalHeaderContextMenu); 0079 connect(m_table->verticalHeader(), &QWidget::customContextMenuRequested, this, &TableFieldWidget::verticalHeaderContextMenu); 0080 0081 registerWidget(); 0082 } 0083 0084 QString TableFieldWidget::text() const { 0085 QString text, str, rstack, cstack, rowStr; 0086 for(int row = 0; row < m_table->rowCount(); ++row) { 0087 rowStr.clear(); 0088 cstack.clear(); 0089 for(int col = 0; col < m_table->columnCount(); ++col) { 0090 QTableWidgetItem* item = m_table->item(row, col); 0091 str = item ? item->text().simplified() : QString(); 0092 if(str.isEmpty()) { 0093 cstack += FieldFormat::columnDelimiterString(); 0094 } else { 0095 rowStr += cstack + str + FieldFormat::columnDelimiterString(); 0096 cstack.clear(); 0097 } 0098 } 0099 if(rowStr.isEmpty()) { 0100 rstack += FieldFormat::rowDelimiterString(); 0101 } else { 0102 rowStr.truncate(rowStr.length()-FieldFormat::columnDelimiterString().length()); // remove last delimiter 0103 text += rstack + rowStr + FieldFormat::rowDelimiterString(); 0104 rstack.clear(); 0105 } 0106 } 0107 if(!text.isEmpty()) { 0108 text.truncate(text.length()-FieldFormat::rowDelimiterString().length()); // remove last delimiter 0109 } 0110 return text; 0111 } 0112 0113 void TableFieldWidget::setTextImpl(const QString& text_) { 0114 const QStringList rows = FieldFormat::splitTable(text_); 0115 if(rows.count() != m_table->rowCount()) { 0116 m_table->setRowCount(qMax(rows.count(), MIN_TABLE_ROWS)); 0117 } 0118 for(int row = 0; row < rows.count(); ++row) { 0119 QStringList columnValues = FieldFormat::splitRow(rows.at(row)); 0120 const int ncols = m_table->columnCount(); 0121 if(ncols < columnValues.count()) { 0122 // need to combine all the last values, from ncols-1 to end 0123 QString lastValue = QStringList(columnValues.mid(ncols-1)).join(FieldFormat::columnDelimiterString()); 0124 columnValues = columnValues.mid(0, ncols); 0125 columnValues.replace(ncols-1, lastValue); 0126 } 0127 for(int col = 0; col < ncols; ++col) { 0128 QString value = col < columnValues.count() ? columnValues.at(col) : QString(); 0129 QTableWidgetItem* item = new QTableWidgetItem(value); 0130 m_table->setItem(row, col, item); 0131 } 0132 } 0133 // adjust all columns 0134 for(int col = 0; col < m_table->columnCount(); ++col) { 0135 m_table->resizeColumnToContents(col); 0136 } 0137 } 0138 0139 void TableFieldWidget::clearImpl() { 0140 m_table->clear(); 0141 m_table->setRowCount(MIN_TABLE_ROWS); 0142 labelColumns(field()); 0143 editMultiple(false); 0144 checkModified(); 0145 } 0146 0147 QWidget* TableFieldWidget::widget() { 0148 return m_table; 0149 } 0150 0151 void TableFieldWidget::slotCheckRows(int row_, int) { 0152 if(row_ == m_table->rowCount()-1 && !emptyRow(row_)) { // if is last row and row above is not empty 0153 m_table->insertRow(m_table->rowCount()); 0154 } 0155 } 0156 0157 void TableFieldWidget::slotResizeColumn(QTableWidgetItem* item_) { 0158 m_table->resizeColumnToContents(item_->column()); 0159 } 0160 0161 void TableFieldWidget::slotRenameColumn() { 0162 if(m_col < 0 || m_col >= m_columns) { 0163 return; 0164 } 0165 QString name = m_table->horizontalHeaderItem(m_col)->text(); 0166 bool ok; 0167 QString newName = QInputDialog::getText(this, i18n("Rename Column"), i18n("New column name:"), 0168 QLineEdit::Normal, name, &ok); 0169 if(ok) { 0170 renameColumn(newName); 0171 } 0172 } 0173 0174 bool TableFieldWidget::emptyRow(int row_) const { 0175 for(int col = 0; col < m_table->columnCount(); ++col) { 0176 QTableWidgetItem* item = m_table->item(row_, col); 0177 if(item && !item->text().isEmpty()) { 0178 return false; 0179 } 0180 } 0181 return true; 0182 } 0183 0184 void TableFieldWidget::labelColumns(Tellico::Data::FieldPtr field_) { 0185 QStringList labels; 0186 for(int col = 0; col < m_columns; ++col) { 0187 QString s = field_->property(QStringLiteral("column%1").arg(col+1)); 0188 if(s.isEmpty()) { 0189 s = i18n("Column %1", col+1); 0190 } 0191 labels += s; 0192 } 0193 m_table->setHorizontalHeaderLabels(labels); 0194 } 0195 0196 void TableFieldWidget::renameColumn(const QString& newName_) { 0197 Q_ASSERT(m_col >= 0); 0198 Q_ASSERT(m_col < m_columns); 0199 Q_ASSERT(!newName_.isEmpty()); 0200 0201 Data::FieldPtr newField(new Data::Field(*field())); 0202 newField->setProperty(QStringLiteral("column%1").arg(m_col+1), newName_); 0203 emit fieldChanged(newField); 0204 setField(newField); 0205 labelColumns(newField); 0206 } 0207 0208 void TableFieldWidget::updateFieldHook(Tellico::Data::FieldPtr, Tellico::Data::FieldPtr newField_) { 0209 bool ok; 0210 m_columns = Tellico::toUInt(newField_->property(QStringLiteral("columns")), &ok); 0211 if(!ok) { 0212 m_columns = 1; 0213 } else { 0214 m_columns = qMin(m_columns, MAX_TABLE_COLS); // max of 5 columns 0215 } 0216 if(m_columns != m_table->columnCount()) { 0217 m_table->setColumnCount(m_columns); 0218 } 0219 labelColumns(newField_); 0220 } 0221 0222 void TableFieldWidget::tableContextMenu(QPoint point_) { 0223 if(point_.isNull()) { 0224 return; 0225 } 0226 m_row = m_table->rowAt(point_.y()); 0227 m_col = m_table->columnAt(point_.x()); 0228 makeRowContextMenu(m_table->mapToGlobal(point_)); 0229 } 0230 0231 void TableFieldWidget::horizontalHeaderContextMenu(QPoint point_) { 0232 int col = m_table->horizontalHeader()->logicalIndexAt(point_.x()); 0233 if(col < 0 || col >= m_columns) { 0234 return; 0235 } 0236 m_row = -1; 0237 m_col = col; 0238 0239 QMenu menu(this); 0240 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("Rename Column..."), 0241 this, &TableFieldWidget::slotRenameColumn); 0242 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-clear")), i18n("Clear Table"), 0243 this, &TableFieldWidget::clearImpl); 0244 menu.exec(m_table->horizontalHeader()->mapToGlobal(point_)); 0245 } 0246 0247 void TableFieldWidget::verticalHeaderContextMenu(QPoint point_) { 0248 int row = m_table->verticalHeader()->logicalIndexAt(point_.y()); 0249 if(row < 0 || row >= m_table->rowCount()) { 0250 return; 0251 } 0252 m_row = row; 0253 m_col = -1; 0254 makeRowContextMenu(m_table->verticalHeader()->mapToGlobal(point_)); 0255 } 0256 0257 void TableFieldWidget::makeRowContextMenu(QPoint point_) { 0258 QMenu menu(this); 0259 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-table-insert-row-below")), i18n("Insert Row"), 0260 this, &TableFieldWidget::slotInsertRow); 0261 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-table-delete-row")), i18n("Remove Row"), 0262 this, &TableFieldWidget::slotRemoveRow); 0263 QAction* act = menu.addAction(QIcon::fromTheme(QStringLiteral("arrow-up")), i18n("Move Row Up"), 0264 this, &TableFieldWidget::slotMoveRowUp); 0265 if(m_row < 1) { 0266 act->setEnabled(false); 0267 } 0268 act = menu.addAction(QIcon::fromTheme(QStringLiteral("arrow-down")), i18n("Move Row Down"), 0269 this, &TableFieldWidget::slotMoveRowDown); 0270 if(m_row < 0 || m_row > m_table->rowCount()-1) { 0271 act->setEnabled(false); 0272 } 0273 menu.addSeparator(); 0274 act = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("Rename Column..."), 0275 this, &TableFieldWidget::slotRenameColumn); 0276 if(m_col < 0 || m_col > m_columns-1) { 0277 act->setEnabled(false); 0278 } 0279 menu.addSeparator(); 0280 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-clear")), i18n("Clear Table"), 0281 this, &TableFieldWidget::slotClear); 0282 0283 menu.exec(point_); 0284 } 0285 0286 void TableFieldWidget::slotInsertRow() { 0287 if(m_row > -1) { 0288 m_table->insertRow(m_row); 0289 checkModified(); 0290 } 0291 } 0292 0293 void TableFieldWidget::slotRemoveRow() { 0294 if(m_row > -1) { 0295 m_table->removeRow(m_row); 0296 checkModified(); 0297 } 0298 } 0299 0300 void TableFieldWidget::slotMoveRowUp() { 0301 if(m_row < 1 || m_row > m_table->rowCount()-1) { 0302 return; 0303 } 0304 m_table->blockSignals(true); 0305 for(int col = 0; col < m_table->columnCount(); ++col) { 0306 QTableWidgetItem* item1 = m_table->takeItem(m_row-1, col); 0307 QTableWidgetItem* item2 = m_table->takeItem(m_row , col); 0308 if(item1) { 0309 m_table->setItem(m_row , col, item1); 0310 } 0311 if(item2) { 0312 m_table->setItem(m_row-1, col, item2); 0313 } 0314 } 0315 m_table->blockSignals(false); 0316 checkModified(); 0317 } 0318 0319 void TableFieldWidget::slotMoveRowDown() { 0320 if(m_row < 0 || m_row > m_table->rowCount()-2) { 0321 return; 0322 } 0323 m_table->blockSignals(true); 0324 for(int col = 0; col < m_table->columnCount(); ++col) { 0325 QTableWidgetItem* item1 = m_table->takeItem(m_row , col); 0326 QTableWidgetItem* item2 = m_table->takeItem(m_row+1, col); 0327 if(item1) { 0328 m_table->setItem(m_row+1, col, item1); 0329 } 0330 if(item2) { 0331 m_table->setItem(m_row , col, item2); 0332 } 0333 } 0334 m_table->blockSignals(false); 0335 checkModified(); 0336 } 0337 0338 void TableFieldWidget::slotClear() { 0339 clearImpl(); 0340 }