* Get the hyperlink at the given position. * @param pt The point to check. * @returns The hyperlink at the given position, or nullptr if there is no hyperlink. */
| 282 | * @returns The hyperlink at the given position, or nullptr if there is no hyperlink. |
| 283 | */ |
| 284 | const TextfileWindow::Hyperlink *TextfileWindow::GetHyperlink(Point pt) const |
| 285 | { |
| 286 | if (this->links.empty()) return nullptr; |
| 287 | |
| 288 | /* Which line was clicked. */ |
| 289 | const int clicked_row = this->GetRowFromWidget(pt.y, WID_TF_BACKGROUND, WidgetDimensions::scaled.frametext.top, GetCharacterHeight(FS_MONO)) + this->GetScrollbar(WID_TF_VSCROLLBAR)->GetPosition(); |
| 290 | |
| 291 | int visible_line = 0; |
| 292 | auto it = std::ranges::find_if(this->lines, [&visible_line, clicked_row](const Line &l) { |
| 293 | visible_line += l.num_lines; |
| 294 | return (visible_line - l.num_lines) <= clicked_row && visible_line > clicked_row; |
| 295 | }); |
| 296 | if (it == this->lines.cend()) return nullptr; |
| 297 | |
| 298 | size_t line_index = it - this->lines.cbegin(); |
| 299 | size_t subline = clicked_row - (visible_line - it->num_lines); |
| 300 | Debug(misc, 4, "TextfileWindow check hyperlink: clicked_row={}, line_index={}, line.top={}, subline={}", clicked_row, line_index, visible_line - it->num_lines, subline); |
| 301 | |
| 302 | /* Find hyperlinks in this line. */ |
| 303 | std::vector<const Hyperlink *> found_links; |
| 304 | for (const auto &link : this->links) { |
| 305 | if (link.line == line_index) found_links.push_back(&link); |
| 306 | } |
| 307 | if (found_links.empty()) return nullptr; |
| 308 | |
| 309 | /* Build line layout to figure out character position that was clicked. */ |
| 310 | const Line &line = this->lines[line_index]; |
| 311 | Layouter layout(line.text, line.wrapped_width == 0 ? INT32_MAX : line.wrapped_width, FS_MONO); |
| 312 | assert(subline < layout.size()); |
| 313 | ptrdiff_t char_index = layout.GetCharAtPosition(pt.x - WidgetDimensions::scaled.frametext.left, subline); |
| 314 | if (char_index < 0) return nullptr; |
| 315 | Debug(misc, 4, "TextfileWindow check hyperlink click: line={}, subline={}, char_index={}", line_index, subline, char_index); |
| 316 | |
| 317 | /* Found character index in line, check if any links are at that position. */ |
| 318 | for (const Hyperlink *link : found_links) { |
| 319 | Debug(misc, 4, "Checking link from char {} to {}", link->begin, link->end); |
| 320 | if (static_cast<size_t>(char_index) >= link->begin && static_cast<size_t>(char_index) < link->end) { |
| 321 | Debug(misc, 4, "Returning link with destination: {}", link->destination); |
| 322 | return link; |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | return nullptr; |
| 327 | } |
| 328 | |
| 329 | /** |
| 330 | * Append the new location to the history, so the user can go back. |
no test coverage detected