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: