From de8de84a33e0b7b0ee83d4b8e3328679a5ba31a9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 8 Dec 2017 16:44:52 +0400 Subject: [PATCH] Fix possible crash in CalendarBox. If month change notification was posted async there was a possibility to get a mousePressEvent() with already new Context field values, but with old _selected value. Those two could be inconsistent leading to an assert violation in (_selected + _context->daysShift() >= 0). --- Telegram/SourceFiles/boxes/calendar_box.cpp | 56 ++++++++++++++------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/Telegram/SourceFiles/boxes/calendar_box.cpp b/Telegram/SourceFiles/boxes/calendar_box.cpp index 8d4af760e..846ca2245 100644 --- a/Telegram/SourceFiles/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/boxes/calendar_box.cpp @@ -117,11 +117,6 @@ void CalendarBox::Context::showMonth(QDate month) { } void CalendarBox::Context::applyMonth(const QDate &month, bool forced) { - if (forced) { - _month.setForced(month); - } else { - _month.set(month); - } _daysCount = month.daysInMonth(); _daysShift = daysShiftForMonth(month); _rowsCount = rowsCountForMonth(month); @@ -130,6 +125,11 @@ void CalendarBox::Context::applyMonth(const QDate &month, bool forced) { _highlightedIndex = month.daysTo(_highlighted); _minDayIndex = _min.isNull() ? INT_MIN : month.daysTo(_min); _maxDayIndex = _max.isNull() ? INT_MAX : month.daysTo(_max); + if (forced) { + _month.setForced(month, true); + } else { + _month.set(month, true); + } } void CalendarBox::Context::skipMonth(int skip) { @@ -219,6 +219,7 @@ protected: private: void monthChanged(QDate month); + void setSelected(int selected); void setPressed(int pressed); int rowsLeft() const; @@ -239,16 +240,21 @@ private: }; -CalendarBox::Inner::Inner(QWidget *parent, Context *context) : TWidget(parent) +CalendarBox::Inner::Inner(QWidget *parent, Context *context) +: TWidget(parent) , _context(context) { setMouseTracking(true); - subscribe(context->month(), [this](QDate month) { monthChanged(month); }); + subscribe(context->month(), [this](QDate month) { + monthChanged(month); + }); } void CalendarBox::Inner::monthChanged(QDate month) { + setSelected(kEmptySelection); _ripples.clear(); resizeToCurrent(); update(); + sendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton); } void CalendarBox::Inner::resizeToCurrent() { @@ -343,19 +349,33 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { } void CalendarBox::Inner::mouseMoveEvent(QMouseEvent *e) { - auto point = e->pos(); - auto row = floorclamp(point.y() - rowsTop(), st::calendarCellSize.height(), 0, _context->rowsCount()); - auto col = floorclamp(point.x() - rowsLeft(), st::calendarCellSize.width(), 0, kDaysInWeek); - auto index = row * kDaysInWeek + col - _context->daysShift(); - if (_context->isEnabled(index)) { - _selected = index; - setCursor(style::cur_pointer); + const auto size = st::calendarCellSize; + const auto point = e->pos(); + const auto inner = QRect( + rowsLeft(), + rowsTop(), + kDaysInWeek * size.width(), + _context->rowsCount() * size.height()); + if (inner.contains(point)) { + const auto row = (point.y() - rowsTop()) / size.height(); + const auto col = (point.x() - rowsLeft()) / size.width(); + const auto index = row * kDaysInWeek + col - _context->daysShift(); + setSelected(index); } else { - _selected = kEmptySelection; - setCursor(style::cur_default); + setSelected(kEmptySelection); } } +void CalendarBox::Inner::setSelected(int selected) { + if (selected != kEmptySelection && !_context->isEnabled(selected)) { + selected = kEmptySelection; + } + _selected = selected; + setCursor((_selected == kEmptySelection) + ? style::cur_default + : style::cur_pointer); +} + void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) { setPressed(_selected); if (_selected != kEmptySelection) { @@ -400,7 +420,9 @@ CalendarBox::Inner::~Inner() = default; class CalendarBox::Title : public TWidget, private base::Subscriber { public: - Title(QWidget *parent, Context *context) : TWidget(parent), _context(context) { + Title(QWidget *parent, Context *context) + : TWidget(parent) + , _context(context) { subscribe(_context->month(), [this](QDate date) { monthChanged(date); }); }