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).
This commit is contained in:
John Preston 2017-12-08 16:44:52 +04:00
parent 80bb6b65a7
commit de8de84a33
1 changed files with 39 additions and 17 deletions

View File

@ -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); });
}