File indexing completed on 2024-05-12 05:14:44
0001 /* 0002 * eventlistview.cpp - base class for 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 "eventlistview.h" 0010 0011 #include "find.h" 0012 #include "resources/eventmodel.h" 0013 #include "resources/resourcedatamodelbase.h" 0014 #include "kalarm_debug.h" 0015 0016 #include <KLocalizedString> 0017 0018 #include <QHeaderView> 0019 #include <QMouseEvent> 0020 #include <QToolTip> 0021 #include <QApplication> 0022 0023 0024 EventListView::EventListView(QWidget* parent) 0025 : QTreeView(parent) 0026 { 0027 setRootIsDecorated(false); // don't show expander icons for child-less items 0028 setAllColumnsShowFocus(true); 0029 setSelectionMode(ExtendedSelection); 0030 setSelectionBehavior(SelectRows); 0031 setTextElideMode(Qt::ElideRight); 0032 // Set default WhatsThis text to be displayed when no actual item is clicked on 0033 setWhatsThis(i18nc("@info:whatsthis", "List of scheduled alarms")); 0034 header()->setSortIndicatorShown(true); 0035 connect(header(), &QHeaderView::sortIndicatorChanged, this, &EventListView::sortChanged); 0036 header()->setSectionsClickable(true); 0037 } 0038 0039 void EventListView::setModel(QAbstractItemModel* model) 0040 { 0041 auto elm = qobject_cast<EventListModel*>(model); 0042 Q_ASSERT(elm); // model must be derived from EventListModel 0043 0044 QTreeView::setModel(model); 0045 connect(elm, &EventListModel::haveEventsStatus, this, &EventListView::initSections); 0046 } 0047 0048 /****************************************************************************** 0049 * Return the source data model. 0050 */ 0051 EventListModel* EventListView::itemModel() const 0052 { 0053 return static_cast<EventListModel*>(model()); 0054 } 0055 0056 /****************************************************************************** 0057 * Return the event referred to by an index. 0058 */ 0059 KAEvent EventListView::event(const QModelIndex& index) const 0060 { 0061 return itemModel()->event(index); 0062 } 0063 0064 KAEvent EventListView::event(int row) const 0065 { 0066 return itemModel()->event(itemModel()->index(row, 0)); 0067 } 0068 0069 /****************************************************************************** 0070 * Select one event and make it the current item. 0071 */ 0072 void EventListView::select(const QModelIndex& index, bool scrollToIndex) 0073 { 0074 selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); 0075 if (scrollToIndex) 0076 scrollTo(index); 0077 } 0078 0079 /****************************************************************************** 0080 * Return the single selected item. 0081 * Reply = invalid if no items are selected, or if multiple items are selected. 0082 */ 0083 QModelIndex EventListView::selectedIndex() const 0084 { 0085 const QModelIndexList list = selectionModel()->selectedRows(); 0086 if (list.count() != 1) 0087 return {}; 0088 return list[0]; 0089 } 0090 0091 /****************************************************************************** 0092 * Return the single selected event. 0093 * Reply = null if no items are selected, or if multiple items are selected. 0094 */ 0095 KAEvent EventListView::selectedEvent() const 0096 { 0097 const QModelIndexList list = selectionModel()->selectedRows(); 0098 if (list.count() != 1) 0099 return {}; 0100 const auto model = static_cast<const EventListModel*>(list[0].model()); 0101 return model->event(list[0]); 0102 } 0103 0104 /****************************************************************************** 0105 * Return the selected events. 0106 */ 0107 QList<KAEvent> EventListView::selectedEvents() const 0108 { 0109 QList<KAEvent> elist; 0110 const QModelIndexList ixlist = selectionModel()->selectedRows(); 0111 int count = ixlist.count(); 0112 if (count) 0113 { 0114 const auto model = static_cast<const EventListModel*>(ixlist[0].model()); 0115 elist.reserve(count); 0116 for (int i = 0; i < count; ++i) 0117 elist += model->event(ixlist[i]); 0118 } 0119 return elist; 0120 } 0121 0122 /****************************************************************************** 0123 * Called when the Find action is selected. 0124 * Display the non-modal Find dialog. 0125 */ 0126 void EventListView::slotFind() 0127 { 0128 if (!mFind) 0129 { 0130 mFind = new Find(this); 0131 connect(mFind, &Find::active, this, &EventListView::findActive); 0132 } 0133 mFind->display(); 0134 } 0135 0136 /****************************************************************************** 0137 * Called when the Find Next or Find Prev action is selected. 0138 */ 0139 void EventListView::findNext(bool forward) 0140 { 0141 if (mFind) 0142 mFind->findNext(forward); 0143 } 0144 0145 /****************************************************************************** 0146 * Called when the user has changed the sort order in a column. 0147 */ 0148 void EventListView::sortChanged(int column, Qt::SortOrder order) 0149 { 0150 sortByColumn(column, order); 0151 } 0152 0153 void EventListView::resizeEvent(QResizeEvent* se) 0154 { 0155 QTreeView::resizeEvent(se); 0156 initSections(); 0157 } 0158 0159 /****************************************************************************** 0160 * Called when a ToolTip or WhatsThis event occurs. 0161 */ 0162 bool EventListView::viewportEvent(QEvent* e) 0163 { 0164 if (e->type() == QEvent::ToolTip && isActiveWindow()) 0165 { 0166 auto he = static_cast<QHelpEvent*>(e); 0167 const QModelIndex index = indexAt(he->pos()); 0168 QVariant value = model()->data(index, Qt::ToolTipRole); 0169 if (value.canConvert<QString>()) 0170 { 0171 QString toolTip = value.toString(); 0172 int i = toolTip.indexOf(QLatin1Char('\n')); 0173 if (i < 0) 0174 { 0175 auto m = qobject_cast<EventListModel*>(model()); 0176 if (index.column() == ResourceDataModelBase::TextColumn 0177 || index.column() == ResourceDataModelBase::NameColumn 0178 || !m || m->event(index).commandError() == KAEvent::CmdErr::None) 0179 { 0180 // Single line tooltip. Only display it if the text column 0181 // is truncated in the view display. 0182 value = model()->data(index, Qt::FontRole); 0183 const QFontMetrics fm(qvariant_cast<QFont>(value).resolve(listViewOptions().font)); 0184 const int textWidth = fm.boundingRect(toolTip).width() + 1; 0185 const int margin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; 0186 const int left = columnViewportPosition(index.column()) + margin; 0187 const int right = left + textWidth; 0188 if (left >= 0 && right <= width() - 2*frameWidth()) 0189 toolTip.clear(); // prevent any tooltip showing 0190 } 0191 } 0192 QToolTip::showText(he->globalPos(), toolTip, this); 0193 return true; 0194 } 0195 } 0196 return QTreeView::viewportEvent(e); 0197 } 0198 0199 /****************************************************************************** 0200 * Called when a context menu event is requested by mouse or key. 0201 */ 0202 void EventListView::contextMenuEvent(QContextMenuEvent* e) 0203 { 0204 Q_EMIT contextMenuRequested(e->globalPos()); 0205 } 0206 0207 QStyleOptionViewItem EventListView::listViewOptions() const 0208 { 0209 QStyleOptionViewItem option; 0210 initViewItemOption(&option); 0211 return option; 0212 } 0213 0214 0215 bool EventListDelegate::editorEvent(QEvent* e, QAbstractItemModel* model, const QStyleOptionViewItem&, const QModelIndex& index) 0216 { 0217 // Don't invoke the editor unless it's either a double click or, 0218 // if KDE is in single click mode and it's a left button release 0219 // with no other buttons pressed and no keyboard modifiers. 0220 switch (e->type()) 0221 { 0222 case QEvent::MouseButtonPress: 0223 case QEvent::MouseMove: 0224 return false; 0225 case QEvent::MouseButtonDblClick: 0226 break; 0227 case QEvent::MouseButtonRelease: 0228 { 0229 auto view = static_cast<EventListView*>(parent()); 0230 if (!view->editOnSingleClick() 0231 || !view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, view)) 0232 return false; 0233 auto me = static_cast<QMouseEvent*>(e); 0234 if (me->button() != Qt::LeftButton || me->buttons() 0235 || me->modifiers() != Qt::NoModifier) 0236 return false; 0237 break; 0238 } 0239 default: 0240 break; 0241 } 0242 if (index.isValid()) 0243 { 0244 qCDebug(KALARM_LOG) << "EventListDelegate::editorEvent"; 0245 auto itemModel = qobject_cast<EventListModel*>(model); 0246 if (!itemModel) 0247 qCCritical(KALARM_LOG) << "EventListDelegate::editorEvent: Invalid cast to EventListModel*"; 0248 else 0249 { 0250 KAEvent event = itemModel->event(index); 0251 edit(event, static_cast<EventListView*>(parent())); 0252 return true; 0253 } 0254 } 0255 return false; // indicate that the event has not been handled 0256 } 0257 0258 #include "moc_eventlistview.cpp" 0259 0260 // vim: et sw=4: