From 40f567d464ac6917bab94fed28c90c4b8ca0567d Mon Sep 17 00:00:00 2001 From: PeterN Date: Mon, 15 May 2023 18:57:50 +0100 Subject: [PATCH] Fix #10811: Crash getting row from non-resizable widget. (#10833) GetScrolled*FromWidget took line height from the widget's resize_y value, however not all widgets are resizable, resulting in a division-by-zero. Allow passing line height explicitly in cases where a widget is not resizable. --- src/airport_gui.cpp | 2 +- src/widget.cpp | 5 +++-- src/widget_type.h | 7 ++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index f8015e274a..2f577357f7 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -501,7 +501,7 @@ public: break; case WID_AP_AIRPORT_LIST: { - int num_clicked = this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget); + int num_clicked = this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget, 0, this->line_height); if (num_clicked == INT_MAX) break; const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(num_clicked); if (as->IsAvailable()) this->SelectOtherAirport(num_clicked); diff --git a/src/widget.cpp b/src/widget.cpp index ae17fde96f..9b215e6878 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2365,11 +2365,12 @@ void NWidgetViewport::UpdateViewportCoordinates(Window *w) * @param w The window the click was in. * @param widget Widget number of the widget clicked in. * @param padding Amount of empty space between the widget edge and the top of the first row. Default value is \c 0. + * @param line_height Height of a single row. A negative value means using the vertical resize step of the widget. * @return Row number clicked at. If clicked at a wrong position, #INT_MAX is returned. */ -int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding) const +int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding, int line_height) const { - uint pos = w->GetRowFromWidget(clickpos, widget, padding, -1); + uint pos = w->GetRowFromWidget(clickpos, widget, padding, line_height); if (pos != INT_MAX) pos += this->GetPosition(); return (pos >= this->GetCount()) ? INT_MAX : pos; } diff --git a/src/widget_type.h b/src/widget_type.h index e7bbbcd842..ca08f2f571 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -788,7 +788,7 @@ public: } } - int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0) const; + int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0, int line_height = -1) const; /** * Return an iterator pointing to the element of a scrolled widget that a user clicked in. @@ -797,13 +797,14 @@ public: * @param w The window the click was in. * @param widget Widget number of the widget clicked in. * @param padding Amount of empty space between the widget edge and the top of the first row. Default value is \c 0. + * @param line_height Height of a single row. A negative value means using the vertical resize step of the widget. * @return Iterator to the element clicked at. If clicked at a wrong position, returns as interator to the end of the container. */ template - typename Tcontainer::iterator GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window * const w, int widget, int padding = 0) const + typename Tcontainer::iterator GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window * const w, int widget, int padding = 0, int line_height = -1) const { assert(this->GetCount() == container.size()); // Scrollbar and container size must match. - int row = this->GetScrolledRowFromWidget(clickpos, w, widget, padding); + int row = this->GetScrolledRowFromWidget(clickpos, w, widget, padding, line_height); if (row == INT_MAX) return std::end(container); typename Tcontainer::iterator it = std::begin(container);