File indexing completed on 2024-05-12 05:14:38
0001 /* 0002 * alarmlistview.cpp - widget showing list of alarms 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2007-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "alarmlistview.h" 0010 0011 #include "resources/resourcedatamodelbase.h" 0012 #include "resources/eventmodel.h" 0013 0014 #include <KSharedConfig> 0015 #include <KConfigGroup> 0016 0017 #include <QHeaderView> 0018 #include <QMenu> 0019 #include <QAction> 0020 #include <QApplication> 0021 #include <QStyledItemDelegate> 0022 #include <QPainter> 0023 0024 /*============================================================================= 0025 * Item delegate to draw an icon centered in the column. 0026 =============================================================================*/ 0027 class CentreDelegate : public QStyledItemDelegate 0028 { 0029 Q_OBJECT 0030 public: 0031 CentreDelegate(QWidget* parent = nullptr) : QStyledItemDelegate(parent) {} 0032 void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; 0033 }; 0034 0035 0036 AlarmListView::AlarmListView(const QString& configGroup, QWidget* parent) 0037 : EventListView(parent) 0038 , mConfigGroup(configGroup) 0039 { 0040 setEditOnSingleClick(true); 0041 setItemDelegateForColumn(AlarmListModel::TypeColumn, new CentreDelegate(this)); 0042 connect(header(), &QHeaderView::sectionMoved, this, &AlarmListView::saveColumnsState); 0043 header()->setContextMenuPolicy(Qt::CustomContextMenu); 0044 connect(header(), &QWidget::customContextMenuRequested, this, &AlarmListView::headerContextMenuRequested); 0045 Preferences::connect(&Preferences::useAlarmNameChanged, this, &AlarmListView::useAlarmNameChanged); 0046 } 0047 0048 /****************************************************************************** 0049 * Return which of the optional columns are currently shown. 0050 * Note that the column order must be the same as in setColumnsVisible(). 0051 * Reply = array of 5 columns if not using alarm names; 0052 * = array of 6 columns if not using alarm names. 0053 */ 0054 QList<bool> AlarmListView::columnsVisible() const 0055 { 0056 if (!model()) 0057 return {}; 0058 QList<bool> vis{ !header()->isSectionHidden(AlarmListModel::TimeColumn), 0059 !header()->isSectionHidden(AlarmListModel::TimeToColumn), 0060 !header()->isSectionHidden(AlarmListModel::RepeatColumn), 0061 !header()->isSectionHidden(AlarmListModel::ColourColumn), 0062 !header()->isSectionHidden(AlarmListModel::TypeColumn) }; 0063 if (Preferences::useAlarmName()) 0064 vis += !header()->isSectionHidden(AlarmListModel::TextColumn); 0065 return vis; 0066 } 0067 0068 /****************************************************************************** 0069 * Set which of the optional columns are to be shown. 0070 * 'show' = array of 5 columns if not using alarm names; 0071 * = array of 6 columns if not using alarm names. 0072 * Note that the column order must be the same as in columnsVisible(). 0073 */ 0074 void AlarmListView::setColumnsVisible(const QList<bool>& show) 0075 { 0076 if (!model()) 0077 return; 0078 const bool useName = Preferences::useAlarmName(); 0079 QList<bool> vis{ true, false, true, true, true, !useName }; 0080 const int colCount = useName ? 6 : 5; 0081 for (int i = 0, count = std::min<int>(colCount, show.count()); i < count; ++i) 0082 vis[i] = show[i]; 0083 header()->setSectionHidden(AlarmListModel::TimeColumn, !vis[0]); 0084 header()->setSectionHidden(AlarmListModel::TimeToColumn, !vis[1]); 0085 header()->setSectionHidden(AlarmListModel::RepeatColumn, !vis[2]); 0086 header()->setSectionHidden(AlarmListModel::ColourColumn, !vis[3]); 0087 header()->setSectionHidden(AlarmListModel::TypeColumn, !vis[4]); 0088 header()->setSectionHidden(AlarmListModel::NameColumn, !useName); 0089 header()->setSectionHidden(AlarmListModel::TextColumn, !vis[5]); 0090 setReplaceBlankName(); 0091 setSortingEnabled(false); // sortByColumn() won't work if sorting is already enabled! 0092 sortByColumn(vis[0] ? AlarmListModel::TimeColumn : AlarmListModel::TimeToColumn, Qt::AscendingOrder); 0093 } 0094 0095 /****************************************************************************** 0096 * Initialize column settings and sizing. 0097 */ 0098 void AlarmListView::initSections() 0099 { 0100 KConfigGroup config(KSharedConfig::openConfig(), mConfigGroup); 0101 const QByteArray settings = config.readEntry("ListHead", QByteArray()); 0102 if (!settings.isEmpty()) 0103 { 0104 header()->restoreState(settings); 0105 const bool useName = Preferences::useAlarmName(); 0106 header()->setSectionHidden(AlarmListModel::NameColumn, !useName); 0107 if (!useName) 0108 { 0109 header()->setSectionHidden(AlarmListModel::TextColumn, false); 0110 setReplaceBlankName(); 0111 } 0112 } 0113 header()->setSectionsMovable(true); 0114 header()->setSectionResizeMode(AlarmListModel::TimeColumn, QHeaderView::ResizeToContents); 0115 header()->setSectionResizeMode(AlarmListModel::TimeToColumn, QHeaderView::ResizeToContents); 0116 header()->setSectionResizeMode(AlarmListModel::RepeatColumn, QHeaderView::ResizeToContents); 0117 header()->setSectionResizeMode(AlarmListModel::ColourColumn, QHeaderView::Fixed); 0118 header()->setSectionResizeMode(AlarmListModel::TypeColumn, QHeaderView::Fixed); 0119 header()->setSectionResizeMode(AlarmListModel::NameColumn, QHeaderView::ResizeToContents); 0120 header()->setSectionResizeMode(AlarmListModel::TextColumn, QHeaderView::Stretch); 0121 header()->setStretchLastSection(true); // necessary to ensure ResizeToContents columns do resize to contents! 0122 const int minWidth = listViewOptions().fontMetrics.lineSpacing() * 3 / 4; 0123 header()->setMinimumSectionSize(minWidth); 0124 const int margin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin); 0125 header()->resizeSection(AlarmListModel::ColourColumn, minWidth); 0126 header()->resizeSection(AlarmListModel::TypeColumn, AlarmListModel::iconWidth() + 2*margin + 2); 0127 } 0128 0129 /****************************************************************************** 0130 * Called when the column order is changed. 0131 * Save the new order for restoration on program restart. 0132 */ 0133 void AlarmListView::saveColumnsState() 0134 { 0135 KConfigGroup config(KSharedConfig::openConfig(), mConfigGroup); 0136 config.writeEntry("ListHead", header()->saveState()); 0137 config.sync(); 0138 } 0139 0140 /****************************************************************************** 0141 * Called when a context menu is requested for the header. 0142 * Allow the user to choose which columns to display. 0143 */ 0144 void AlarmListView::headerContextMenuRequested(const QPoint& pt) 0145 { 0146 QAbstractItemModel* almodel = model(); 0147 QMenu menu; 0148 const bool useName = Preferences::useAlarmName(); 0149 for (int col = 0, count = header()->count(); col < count; ++col) 0150 { 0151 const QString title = almodel->headerData(col, Qt::Horizontal, ResourceDataModelBase::ColumnTitleRole).toString(); 0152 if (!title.isEmpty()) 0153 { 0154 QAction* act = menu.addAction(title); 0155 act->setData(col); 0156 act->setCheckable(true); 0157 act->setChecked(!header()->isSectionHidden(col)); 0158 // Always show Name column, but disabled. 0159 // If the alarm name feature is not enabled, this serves as a small 0160 // hint to the user that the name feature exists. 0161 if (col == AlarmListModel::NameColumn) 0162 act->setEnabled(false); // don't allow name column to be hidden if name is used 0163 else if (col == AlarmListModel::TextColumn && !useName) 0164 act->setEnabled(false); // don't allow text column to be hidden if name not used 0165 else 0166 QObject::connect(act, &QAction::triggered, 0167 this, [this, &menu, act] { showHideColumn(menu, act); }); //clazy:exclude=lambda-in-connect 0168 } 0169 } 0170 enableTimeColumns(&menu); 0171 menu.exec(header()->mapToGlobal(pt)); 0172 } 0173 0174 /****************************************************************************** 0175 * Called when the 'use alarm name' setting has changed. 0176 */ 0177 void AlarmListView::useAlarmNameChanged(bool use) 0178 { 0179 header()->setSectionHidden(AlarmListModel::NameColumn, !use); 0180 header()->setSectionHidden(AlarmListModel::TextColumn, use); 0181 setReplaceBlankName(); 0182 } 0183 0184 /****************************************************************************** 0185 * Show or hide a column according to the header context menu. 0186 */ 0187 void AlarmListView::showHideColumn(QMenu& menu, QAction* act) 0188 { 0189 int col = act->data().toInt(); 0190 if (col < 0 || col >= header()->count()) 0191 return; 0192 bool show = act->isChecked(); 0193 header()->setSectionHidden(col, !show); 0194 if (col == AlarmListModel::TimeColumn || col == AlarmListModel::TimeToColumn) 0195 enableTimeColumns(&menu); 0196 if (col == AlarmListModel::TextColumn) 0197 setReplaceBlankName(); 0198 saveColumnsState(); 0199 Q_EMIT columnsVisibleChanged(); 0200 } 0201 0202 /****************************************************************************** 0203 * Set whether to replace a blank alarm name with the alarm text. 0204 */ 0205 void AlarmListView::setReplaceBlankName() 0206 { 0207 bool textHidden = header()->isSectionHidden(AlarmListModel::TextColumn); 0208 auto almodel = qobject_cast<AlarmListModel*>(model()); 0209 if (almodel) 0210 almodel->setReplaceBlankName(textHidden); 0211 } 0212 0213 /****************************************************************************** 0214 * Disable Time or Time To in the context menu if the other one is not 0215 * selected to be displayed, to ensure that at least one is always shown. 0216 */ 0217 void AlarmListView::enableTimeColumns(QMenu* menu) 0218 { 0219 bool timeShown = !header()->isSectionHidden(AlarmListModel::TimeColumn); 0220 bool timeToShown = !header()->isSectionHidden(AlarmListModel::TimeToColumn); 0221 const QList<QAction*> actions = menu->actions(); 0222 if (!timeToShown) 0223 { 0224 header()->setSectionHidden(AlarmListModel::TimeColumn, false); 0225 for (QAction* act : actions) 0226 { 0227 if (act->data().toInt() == AlarmListModel::TimeColumn) 0228 { 0229 act->setEnabled(false); 0230 break; 0231 } 0232 } 0233 } 0234 else if (!timeShown) 0235 { 0236 header()->setSectionHidden(AlarmListModel::TimeToColumn, false); 0237 for (QAction* act : actions) 0238 { 0239 if (act->data().toInt() == AlarmListModel::TimeToColumn) 0240 { 0241 act->setEnabled(false); 0242 break; 0243 } 0244 } 0245 } 0246 } 0247 0248 0249 /****************************************************************************** 0250 * Draw the alarm's icon centered in the column. 0251 */ 0252 void CentreDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 0253 { 0254 // Draw the item background without any icon 0255 QStyleOptionViewItem opt = option; 0256 initStyleOption(&opt, index); 0257 opt.icon = QIcon(); 0258 QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, nullptr); 0259 0260 // Draw the icon centered within item 0261 const QPixmap icon = qvariant_cast<QPixmap>(index.data(Qt::DecorationRole)); 0262 const QRect r = option.rect; 0263 const QPoint p = QPoint((r.width() - icon.width())/2, (r.height() - icon.height())/2); 0264 painter->drawPixmap(r.topLeft() + p, icon); 0265 } 0266 0267 #include "alarmlistview.moc" 0268 #include "moc_alarmlistview.cpp" 0269 0270 // vim: et sw=4: