File indexing completed on 2024-04-28 05:08:17
0001 /*************************************************************************** 0002 Copyright (C) 2001-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 "detailedlistview.h" 0026 #include "collection.h" 0027 #include "collectionfactory.h" 0028 #include "controller.h" 0029 #include "field.h" 0030 #include "entry.h" 0031 #include "tellico_debug.h" 0032 #include "tellico_kernel.h" 0033 #include "config/tellico_config.h" 0034 #include "models/entrymodel.h" 0035 #include "models/entrysortmodel.h" 0036 #include "models/modelmanager.h" 0037 #include "gui/detailedentryitemdelegate.h" 0038 #include "gui/ratingdelegate.h" 0039 #include "utils/string_utils.h" 0040 0041 #include <KLocalizedString> 0042 #include <KSharedConfig> 0043 0044 #include <QMenu> 0045 #include <QMouseEvent> 0046 #include <QHeaderView> 0047 #include <QContextMenuEvent> 0048 0049 using namespace Tellico; 0050 using Tellico::DetailedListView; 0051 0052 DetailedListView::DetailedListView(QWidget* parent_) : GUI::TreeView(parent_) 0053 , m_loadingCollection(false), m_currentContextColumn(-1) { 0054 setHeaderHidden(false); 0055 setSelectionMode(QAbstractItemView::ExtendedSelection); 0056 setAlternatingRowColors(true); 0057 setRootIsDecorated(false); 0058 setUniformRowHeights(true); 0059 0060 connect(this, &QAbstractItemView::doubleClicked, this, &DetailedListView::slotDoubleClicked); 0061 0062 // header menu 0063 header()->installEventFilter(this); 0064 header()->setMinimumSectionSize(20); 0065 0066 m_headerMenu = new QMenu(this); 0067 m_columnMenu = new QMenu(this); 0068 connect(m_columnMenu, &QMenu::triggered, 0069 this, &DetailedListView::slotColumnMenuActivated); 0070 0071 EntryModel* entryModel = new EntryModel(this); 0072 EntrySortModel* sortModel = new EntrySortModel(this); 0073 sortModel->setSortRole(EntryPtrRole); 0074 sortModel->setSourceModel(entryModel); 0075 setModel(sortModel); 0076 setItemDelegate(new DetailedEntryItemDelegate(this)); 0077 0078 ModelManager::self()->setEntryModel(sortModel); 0079 0080 connect(model(), &QAbstractItemModel::headerDataChanged, this, &DetailedListView::updateHeaderMenu); 0081 connect(model(), &QAbstractItemModel::headerDataChanged, this, &DetailedListView::updateColumnDelegates); 0082 connect(model(), &QAbstractItemModel::columnsInserted, this, &DetailedListView::hideNewColumn); 0083 connect(header(), &QHeaderView::sectionCountChanged, this, &DetailedListView::updateHeaderMenu); 0084 } 0085 0086 DetailedListView::~DetailedListView() { 0087 } 0088 0089 Tellico::EntryModel* DetailedListView::sourceModel() const { 0090 return static_cast<EntryModel*>(sortModel()->sourceModel()); 0091 } 0092 0093 void DetailedListView::addCollection(Tellico::Data::CollPtr coll_) { 0094 if(!coll_) { 0095 return; 0096 } 0097 0098 const QString configGroup = QStringLiteral("Options - %1").arg(CollectionFactory::typeName(coll_)); 0099 KConfigGroup config(KSharedConfig::openConfig(), configGroup); 0100 0101 QString configN; 0102 if(coll_->type() == Data::Collection::Base) { 0103 QUrl url = Kernel::self()->URL(); 0104 for(int i = 0; i < Config::maxCustomURLSettings(); ++i) { 0105 QUrl u = config.readEntry(QStringLiteral("URL_%1").arg(i), QUrl()); 0106 if(u == url) { 0107 configN = QStringLiteral("_%1").arg(i); 0108 break; 0109 } 0110 } 0111 } 0112 0113 // we don't want to immediately hide all these columns when adding fields 0114 disconnect(model(), &QAbstractItemModel::columnsInserted, 0115 this, &DetailedListView::hideNewColumn); 0116 sourceModel()->setImagesAreAvailable(false); 0117 sourceModel()->setFields(coll_->fields()); 0118 connect(model(), &QAbstractItemModel::columnsInserted, 0119 this, &DetailedListView::hideNewColumn); 0120 0121 // we're not using saveState() and restoreState() since our columns are variable 0122 QStringList columnNames = config.readEntry(QLatin1String("ColumnNames") + configN, QStringList()); 0123 QList<int> columnWidths = config.readEntry(QLatin1String("ColumnWidths") + configN, QList<int>()); 0124 QList<int> columnOrder = config.readEntry(QLatin1String("ColumnOrder") + configN, QList<int>()); 0125 0126 // just a broken-world check 0127 while(columnWidths.size() < columnNames.size()) { 0128 columnWidths << 0; 0129 } 0130 while(columnOrder.size() < columnNames.size()) { 0131 columnOrder << columnOrder.size(); 0132 } 0133 QList<int> currentColumnOrder; 0134 // now restore widths and order 0135 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0136 int idx = columnNames.indexOf(columnFieldName(ncol)); 0137 // column width of 0 means hidden 0138 if(idx < 0 || columnWidths.at(idx) <= 0) { 0139 hideColumn(ncol); 0140 if(idx > -1) { 0141 currentColumnOrder << ncol; 0142 } 0143 } else { 0144 setColumnWidth(ncol, columnWidths.at(idx)); 0145 currentColumnOrder << ncol; 0146 } 0147 } 0148 const int maxCount = qMin(currentColumnOrder.size(), columnOrder.size()); 0149 for(int i = 0; i < maxCount; ++i) { 0150 header()->moveSection(header()->visualIndex(currentColumnOrder.at(i)), columnOrder.at(i)); 0151 } 0152 0153 // always hide tables and paragraphs 0154 for(int ncol = 0; ncol < coll_->fields().count(); ++ncol) { 0155 Data::FieldPtr field = model()->headerData(ncol, Qt::Horizontal, FieldPtrRole).value<Data::FieldPtr>(); 0156 if(field) { 0157 if(field->type() == Data::Field::Table || field->type() == Data::Field::Para) { 0158 hideColumn(ncol); 0159 } 0160 } else { 0161 myDebug() << "no field for col" << ncol; 0162 } 0163 } 0164 0165 // because some of the fields got hidden... 0166 updateColumnDelegates(); 0167 updateHeaderMenu(); 0168 checkHeader(); 0169 0170 sortModel()->setSortColumn(config.readEntry(QLatin1String("SortColumn") + configN, -1)); 0171 sortModel()->setSecondarySortColumn(config.readEntry(QLatin1String("PrevSortColumn") + configN, -1)); 0172 sortModel()->setTertiarySortColumn(config.readEntry(QLatin1String("Prev2SortColumn") + configN, -1)); 0173 const int order = config.readEntry(QLatin1String("SortOrder") + configN, static_cast<int>(Qt::AscendingOrder)); 0174 sortModel()->setSortOrder(static_cast<Qt::SortOrder>(order)); 0175 0176 setUpdatesEnabled(false); 0177 m_loadingCollection = true; 0178 addEntries(coll_->entries()); 0179 m_loadingCollection = false; 0180 setUpdatesEnabled(true); 0181 0182 header()->setSortIndicator(sortModel()->sortColumn(), sortModel()->sortOrder()); 0183 } 0184 0185 void DetailedListView::slotReset() { 0186 // clear() does not remove columns 0187 sourceModel()->clear(); 0188 } 0189 0190 void DetailedListView::addEntries(Tellico::Data::EntryList entries_) { 0191 if(entries_.isEmpty()) { 0192 return; 0193 } 0194 sourceModel()->addEntries(entries_); 0195 if(!m_loadingCollection) { 0196 setState(entries_, NewState); 0197 // TODO: this only scrolls to new entries in the list view, not the other widgets 0198 // who use this selection model 0199 setEntriesSelected(entries_); 0200 } 0201 } 0202 0203 void DetailedListView::modifyEntries(Tellico::Data::EntryList entries_) { 0204 if(entries_.isEmpty()) { 0205 return; 0206 } 0207 sourceModel()->modifyEntries(entries_); 0208 setState(entries_, ModifiedState); 0209 } 0210 0211 void DetailedListView::removeEntries(Tellico::Data::EntryList entries_) { 0212 if(entries_.isEmpty()) { 0213 return; 0214 } 0215 sourceModel()->removeEntries(entries_); 0216 } 0217 0218 void DetailedListView::setState(Tellico::Data::EntryList entries_, int state) { 0219 foreach(Data::EntryPtr entry, entries_) { 0220 QModelIndex index = sourceModel()->indexFromEntry(entry); 0221 if(index.isValid()) { 0222 sourceModel()->setData(index, state, SaveStateRole); 0223 } else { 0224 myWarning() << "no index found for" << entry->id() << entry->title(); 0225 } 0226 } 0227 } 0228 0229 void DetailedListView::removeCollection(Tellico::Data::CollPtr coll_) { 0230 if(!coll_) { 0231 myWarning() << "null coll pointer!"; 0232 return; 0233 } 0234 0235 sourceModel()->clear(); 0236 } 0237 0238 void DetailedListView::contextMenuEvent(QContextMenuEvent* event_) { 0239 QModelIndex index = indexAt(event_->pos()); 0240 if(!index.isValid()) { 0241 return; 0242 } 0243 0244 QMenu menu(this); 0245 Controller::self()->plugEntryActions(&menu); 0246 menu.exec(event_->globalPos()); 0247 } 0248 0249 // don't shadow QListView::setSelected 0250 void DetailedListView::setEntriesSelected(Data::EntryList entries_) { 0251 if(entries_.isEmpty()) { 0252 // don't move this one outside the block since it calls setCurrentItem(0) 0253 clearSelection(); 0254 return; 0255 } 0256 0257 clearSelection(); 0258 EntrySortModel* proxyModel = static_cast<EntrySortModel*>(model()); 0259 foreach(Data::EntryPtr entry, entries_) { 0260 QModelIndex index = sourceModel()->indexFromEntry(entry); 0261 if(!proxyModel->mapFromSource(index).isValid()) { 0262 // clear the filter if we're trying to select an entry that is currently filtered out 0263 Controller::self()->clearFilter(); 0264 break; 0265 } 0266 } 0267 blockSignals(true); 0268 QItemSelection itemSel; 0269 foreach(Data::EntryPtr entry, entries_) { 0270 QModelIndex index = proxyModel->mapFromSource(sourceModel()->indexFromEntry(entry)); 0271 itemSel.select(index, index); 0272 } 0273 selectionModel()->select(itemSel, QItemSelectionModel::Select | QItemSelectionModel::Rows); 0274 blockSignals(false); 0275 0276 QModelIndex index = sourceModel()->indexFromEntry(entries_.first()); 0277 scrollTo(proxyModel->mapFromSource(index)); 0278 } 0279 0280 bool DetailedListView::eventFilter(QObject* obj_, QEvent* event_) { 0281 if(event_->type() == QEvent::ContextMenu 0282 && obj_ == header()) { 0283 m_currentContextColumn = header()->logicalIndexAt(static_cast<QContextMenuEvent*>(event_)->pos()); 0284 m_headerMenu->exec(static_cast<QContextMenuEvent*>(event_)->globalPos()); 0285 return true; 0286 } 0287 return GUI::TreeView::eventFilter(obj_, event_); 0288 } 0289 0290 void DetailedListView::slotDoubleClicked(const QModelIndex& index_) { 0291 Data::EntryPtr entry = index_.data(EntryPtrRole).value<Data::EntryPtr>(); 0292 if(entry) { 0293 Controller::self()->editEntry(entry); 0294 } 0295 } 0296 0297 void DetailedListView::slotColumnMenuActivated(QAction* action_) { 0298 const int col = action_->data().toInt(); 0299 if(col > -1) { // only column actions have data 0300 const bool isChecked = action_->isChecked(); 0301 setColumnHidden(col, !isChecked); 0302 // if we're showing a column, resize all sections 0303 if(isChecked) { 0304 resizeColumnToContents(col); 0305 adjustColumnWidths(); 0306 } 0307 } 0308 checkHeader(); 0309 } 0310 0311 void DetailedListView::showAllColumns() { 0312 foreach(QAction* action, m_columnMenu->actions()) { 0313 if(action->isCheckable() && !action->isChecked()) { 0314 action->trigger(); 0315 } 0316 } 0317 } 0318 0319 void DetailedListView::hideAllColumns() { 0320 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0321 hideColumn(ncol); 0322 } 0323 foreach(QAction* action, m_columnMenu->actions()) { 0324 if(action->isCheckable()) { 0325 action->setChecked(false); 0326 } 0327 } 0328 checkHeader(); 0329 } 0330 0331 void DetailedListView::hideCurrentColumn() { 0332 setColumnHidden(m_currentContextColumn, true); 0333 checkHeader(); 0334 } 0335 0336 void DetailedListView::slotRefresh() { 0337 sortModel()->invalidate(); 0338 } 0339 0340 void DetailedListView::setFilter(Tellico::FilterPtr filter_) { 0341 static_cast<EntrySortModel*>(sortModel())->setFilter(filter_); 0342 } 0343 0344 Tellico::FilterPtr DetailedListView::filter() const { 0345 return static_cast<EntrySortModel*>(sortModel())->filter(); 0346 } 0347 0348 void DetailedListView::addField(Tellico::Data::CollPtr, Tellico::Data::FieldPtr field) { 0349 sourceModel()->addFields(Data::FieldList() << field); 0350 } 0351 0352 void DetailedListView::modifyField(Tellico::Data::CollPtr, Tellico::Data::FieldPtr oldField_, Tellico::Data::FieldPtr newField_) { 0353 Q_UNUSED(oldField_) 0354 sourceModel()->modifyField(oldField_, newField_); 0355 } 0356 0357 void DetailedListView::removeField(Tellico::Data::CollPtr, Tellico::Data::FieldPtr field_) { 0358 sourceModel()->removeFields(Data::FieldList() << field_); 0359 } 0360 0361 void DetailedListView::reorderFields(const Tellico::Data::FieldList& fields_) { 0362 QStringList columnNames; 0363 QList<int> columnWidths, columnOrder; 0364 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0365 // ignore hidden columns 0366 if(!isColumnHidden(ncol)) { 0367 columnNames << columnFieldName(ncol); 0368 columnWidths << columnWidth(ncol); 0369 columnOrder << header()->visualIndex(ncol); 0370 } 0371 } 0372 0373 sourceModel()->reorderFields(fields_); 0374 0375 QList<int> currentColumnOrder; 0376 // now restore widths and order 0377 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0378 int idx = columnNames.indexOf(columnFieldName(ncol)); 0379 // column width of 0 means hidden 0380 if(idx < 0 || columnWidths.at(idx) <= 0) { 0381 hideColumn(ncol); 0382 if(idx > -1) { 0383 currentColumnOrder << ncol; 0384 } 0385 } else { 0386 setColumnHidden(ncol, false); 0387 setColumnWidth(ncol, columnWidths.at(idx)); 0388 currentColumnOrder << ncol; 0389 } 0390 } 0391 const int maxCount = qMin(currentColumnOrder.size(), columnOrder.size()); 0392 for(int i = 0; i < maxCount; ++i) { 0393 header()->moveSection(header()->visualIndex(currentColumnOrder.at(i)), columnOrder.at(i)); 0394 } 0395 updateHeaderMenu(); 0396 } 0397 0398 void DetailedListView::saveConfig(Tellico::Data::CollPtr coll_, int configIndex_) { 0399 const QString configGroup = QStringLiteral("Options - %1").arg(CollectionFactory::typeName(coll_)); 0400 KConfigGroup config(KSharedConfig::openConfig(), configGroup); 0401 0402 // all of this is to have custom settings on a per file basis 0403 QString configN; 0404 if(coll_->type() == Data::Collection::Base) { 0405 QList<ConfigInfo> info; 0406 for(int i = 0; i < Config::maxCustomURLSettings(); ++i) { 0407 QUrl u(config.readEntry(QStringLiteral("URL_%1").arg(i))); 0408 if(!u.isEmpty() && i != configIndex_) { 0409 configN = QStringLiteral("_%1").arg(i); 0410 ConfigInfo ci; 0411 ci.cols = config.readEntry(QLatin1String("ColumnNames") + configN, QStringList()); 0412 ci.widths = config.readEntry(QLatin1String("ColumnWidths") + configN, QList<int>()); 0413 ci.order = config.readEntry(QLatin1String("ColumnOrder") + configN, QList<int>()); 0414 ci.prevSort = config.readEntry(QLatin1String("PrevSortColumn") + configN, 0); 0415 ci.prev2Sort = config.readEntry(QLatin1String("Prev2SortColumn") + configN, 0); 0416 ci.sortOrder = config.readEntry(QLatin1String("SortOrder") + configN, static_cast<int>(Qt::AscendingOrder)); 0417 info.append(ci); 0418 } 0419 } 0420 // subtract one since we're writing the current settings, too 0421 int limit = qMin(info.count(), Config::maxCustomURLSettings()-1); 0422 for(int i = 0; i < limit; ++i) { 0423 // starts at one since the current config will be written below 0424 configN = QStringLiteral("_%1").arg(i+1); 0425 config.writeEntry(QLatin1String("ColumnNames") + configN, info[i].cols); 0426 config.writeEntry(QLatin1String("ColumnWidths") + configN, info[i].widths); 0427 config.writeEntry(QLatin1String("ColumnOrder") + configN, info[i].order); 0428 config.writeEntry(QLatin1String("PrevSortColumn") + configN, info[i].prevSort); 0429 config.writeEntry(QLatin1String("Prev2SortColumn") + configN, info[i].prev2Sort); 0430 config.writeEntry(QLatin1String("SortOrder") + configN, info[i].sortOrder); 0431 // legacy entry item 0432 config.deleteEntry(QLatin1String("ColumnState") + configN); 0433 } 0434 configN = QStringLiteral("_0"); 0435 } 0436 0437 QStringList colNames; 0438 QList<int> widths, order; 0439 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0440 // ignore hidden columns 0441 if(!isColumnHidden(ncol)) { 0442 colNames << columnFieldName(ncol); 0443 widths << columnWidth(ncol); 0444 order << header()->visualIndex(ncol); 0445 } 0446 } 0447 0448 config.writeEntry(QLatin1String("ColumnNames") + configN, colNames); 0449 config.writeEntry(QLatin1String("ColumnWidths") + configN, widths); 0450 config.writeEntry(QLatin1String("ColumnOrder") + configN, order); 0451 0452 const int sortCol1 = sortModel()->sortColumn(); 0453 const int sortCol2 = sortModel()->secondarySortColumn(); 0454 const int sortCol3 = sortModel()->tertiarySortColumn(); 0455 const int sortOrder = static_cast<int>(sortModel()->sortOrder()); 0456 config.writeEntry(QLatin1String("SortColumn") + configN, sortCol1); 0457 config.writeEntry(QLatin1String("PrevSortColumn") + configN, sortCol2); 0458 config.writeEntry(QLatin1String("Prev2SortColumn") + configN, sortCol3); 0459 config.writeEntry(QLatin1String("SortOrder") + configN, sortOrder); 0460 // remove old entry item 0461 config.deleteEntry(QLatin1String("ColumnState") + configN); 0462 } 0463 0464 QString DetailedListView::sortColumnTitle1() const { 0465 return model()->headerData(header()->sortIndicatorSection(), Qt::Horizontal).toString(); 0466 } 0467 0468 QString DetailedListView::sortColumnTitle2() const { 0469 return model()->headerData(sortModel()->secondarySortColumn(), Qt::Horizontal).toString(); 0470 } 0471 0472 QString DetailedListView::sortColumnTitle3() const { 0473 return model()->headerData(sortModel()->tertiarySortColumn(), Qt::Horizontal).toString(); 0474 } 0475 0476 QStringList DetailedListView::visibleColumns() const { 0477 // we want the visual order, so use a QMap and sort by visualIndex 0478 QMap<int, QString> titleMap; 0479 for(int i = 0; i < header()->count(); ++i) { 0480 if(!isColumnHidden(i)) { 0481 titleMap.insert(header()->visualIndex(i), model()->headerData(i, Qt::Horizontal).toString()); 0482 } 0483 } 0484 return titleMap.values(); 0485 } 0486 0487 // can't be const 0488 Tellico::Data::EntryList DetailedListView::visibleEntries() { 0489 // We could just return the full collection entry list if the filter is 0 0490 // but printing depends on the sorted order 0491 Data::EntryList entries; 0492 for(int i = 0; i < model()->rowCount(); ++i) { 0493 Data::EntryPtr tmp = model()->data(model()->index(i, 0), EntryPtrRole).value<Data::EntryPtr>(); 0494 if(tmp) { 0495 entries += tmp; 0496 } 0497 } 0498 return entries; 0499 } 0500 0501 void DetailedListView::selectAllVisible() { 0502 QModelIndex topLeft = model()->index(0, 0); 0503 QModelIndex bottomRight = model()->index(model()->rowCount()-1, model()->columnCount()-1); 0504 QItemSelection selection(topLeft, bottomRight); 0505 selectionModel()->select(selection, QItemSelectionModel::Select); 0506 } 0507 0508 int DetailedListView::visibleItems() const { 0509 return model()->rowCount(); 0510 } 0511 0512 void DetailedListView::resetEntryStatus() { 0513 sourceModel()->clearSaveState(); 0514 } 0515 0516 void DetailedListView::updateHeaderMenu() { 0517 // we only want to update the menu when the header count and model count agree 0518 if(model()->columnCount() != header()->count()) { 0519 myDebug() << "column counts disagree"; 0520 return; 0521 } 0522 m_headerMenu->clear(); 0523 m_headerMenu->addSection(i18n("View Columns")); 0524 0525 m_columnMenu->clear(); 0526 0527 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0528 Data::FieldPtr field = model()->headerData(ncol, Qt::Horizontal, FieldPtrRole).value<Data::FieldPtr>(); 0529 if(field && (field->type() == Data::Field::Table || field->type() == Data::Field::Para)) { 0530 continue; 0531 } 0532 QAction* act = m_columnMenu->addAction(model()->headerData(ncol, Qt::Horizontal).toString()); 0533 act->setData(ncol); 0534 act->setCheckable(true); 0535 act->setChecked(!isColumnHidden(ncol)); 0536 } 0537 QAction* columnAction = m_headerMenu->addMenu(m_columnMenu); 0538 columnAction->setText(i18nc("Noun, Menu name", "Columns")); 0539 columnAction->setIcon(QIcon::fromTheme(QStringLiteral("view-file-columns"))); 0540 0541 QAction* actHideThis = m_headerMenu->addAction(i18n("Hide This Column")); 0542 connect(actHideThis, &QAction::triggered, this, &DetailedListView::hideCurrentColumn); 0543 QAction* actResize = m_headerMenu->addAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), i18n("Resize to Content")); 0544 connect(actResize, &QAction::triggered, this, &DetailedListView::resizeColumnsToContents); 0545 0546 m_headerMenu->addSeparator(); 0547 0548 QAction* actShowAll = m_headerMenu->addAction(i18n("Show All Columns")); 0549 connect(actShowAll, &QAction::triggered, this, &DetailedListView::showAllColumns); 0550 QAction* actHideAll = m_headerMenu->addAction(i18n("Hide All Columns")); 0551 connect(actHideAll, &QAction::triggered, this, &DetailedListView::hideAllColumns); 0552 } 0553 0554 void DetailedListView::updateColumnDelegates() { 0555 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0556 Data::FieldPtr field = model()->headerData(ncol, Qt::Horizontal, FieldPtrRole).value<Data::FieldPtr>(); 0557 if(field && field->type() == Data::Field::Rating) { 0558 /// if we're not using the overall delegate, delete the delegate since we're setting a new on 0559 if(itemDelegateForColumn(ncol) != itemDelegate()) { 0560 delete itemDelegateForColumn(ncol); 0561 } 0562 RatingDelegate* delegate = new RatingDelegate(this); 0563 bool ok; // not used 0564 delegate->setMaxRating(Tellico::toUInt(field->property(QStringLiteral("maximum")), &ok)); 0565 setItemDelegateForColumn(ncol, delegate); 0566 } else { 0567 // reset column delegate to overall delegate 0568 setItemDelegateForColumn(ncol, itemDelegate()); 0569 } 0570 } 0571 } 0572 0573 void DetailedListView::slotRefreshImages() { 0574 sourceModel()->setImagesAreAvailable(true); 0575 } 0576 0577 void DetailedListView::adjustColumnWidths() { 0578 // this function is called when a column is shown 0579 // reduce all visible columns to their size hint, if they are wider than that 0580 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0581 if(!isColumnHidden(ncol)) { 0582 const int width = sizeHintForColumn(ncol); 0583 if(columnWidth(ncol) > width) { 0584 resizeColumnToContents(ncol); 0585 } 0586 } 0587 } 0588 } 0589 0590 void DetailedListView::resizeColumnsToContents() { 0591 for(int ncol = 0; ncol < header()->count(); ++ncol) { 0592 if(!isColumnHidden(ncol)) { 0593 resizeColumnToContents(ncol); 0594 } 0595 } 0596 } 0597 0598 void DetailedListView::hideNewColumn(const QModelIndex& index_, int start_, int end_) { 0599 Q_UNUSED(index_); 0600 for(int ncol = start_; ncol <= end_; ++ncol) { 0601 hideColumn(ncol); 0602 } 0603 updateHeaderMenu(); // make sure to update checkable actions 0604 } 0605 0606 void DetailedListView::checkHeader() { 0607 // the header disappears if all columns are hidden, so if the user hides all 0608 // columns, we turn around and show the title 0609 // 0610 // normally, I would expect a check like header()->count() == header()->hiddenSectionCount() 0611 // to tell me if all sections are hidden, but it often doesn't work, with the hiddenSectionCount() 0612 // being greater than count()! From testing, if the sizeHint() width is 0, then the header is hidden 0613 if(!header()->sizeHint().isEmpty()) { 0614 return; 0615 } 0616 // find title action in menu and activate it 0617 QAction* action = nullptr; 0618 QAction* fallbackAction = nullptr; 0619 foreach(QAction* tryAction, m_columnMenu->actions()) { 0620 const int ncol = tryAction->data().toInt(); 0621 if(ncol > -1 && columnFieldName(ncol) == QLatin1String("title")) { 0622 action = tryAction; 0623 break; 0624 } else if(ncol > -1 && !fallbackAction) { 0625 fallbackAction = tryAction; 0626 } 0627 } 0628 if(!action) { 0629 action = fallbackAction; 0630 } 0631 if(action) { 0632 action->setChecked(true); 0633 const int col = action->data().toInt(); 0634 // calling slotColumnMenuActivated() would be infinite loop 0635 setColumnHidden(col, false); 0636 resizeColumnToContents(col); 0637 } else { 0638 myDebug() << "found no action to show, still empty header!"; 0639 } 0640 } 0641 0642 QString DetailedListView::columnFieldName(int ncol_) const { 0643 Data::FieldPtr field = model()->headerData(ncol_, Qt::Horizontal, FieldPtrRole).value<Data::FieldPtr>(); 0644 return field ? field->name() : QString(); 0645 }