1 /* Reverse Engineer's Hex Editor 2 * Copyright (C) 2017-2021 Daniel Collins <solemnwarning@solemnwarning.net> 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 as published by 6 * the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * this program; if not, write to the Free Software Foundation, Inc., 51 15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 */ 17 18 #ifndef REHEX_DOCUMENTCTRL_HPP 19 #define REHEX_DOCUMENTCTRL_HPP 20 21 #include <functional> 22 #include <jansson.h> 23 #include <list> 24 #include <memory> 25 #include <stdint.h> 26 #include <utility> 27 #include <vector> 28 #include <wx/dataobj.h> 29 #include <wx/wx.h> 30 31 #include "buffer.hpp" 32 #include "ByteRangeSet.hpp" 33 #include "document.hpp" 34 #include "Events.hpp" 35 #include "LRUCache.hpp" 36 #include "NestedOffsetLengthMap.hpp" 37 #include "Palette.hpp" 38 #include "SharedDocumentPointer.hpp" 39 #include "util.hpp" 40 41 namespace REHex { 42 class DocumentCtrl: public wxControl { 43 public: 44 /** 45 * @brief An on-screen rectangle in the DocumentCtrl. 46 */ 47 struct Rect 48 { 49 int x; /**< X co-ordinate, in pixels. */ 50 int64_t y; /**< Y co-ordinate, in lines. */ 51 52 int w; /**< Width, in pixels. */ 53 int64_t h; /**< Height, in lines. */ 54 RectREHex::DocumentCtrl::Rect55 Rect(): x(-1), y(-1), w(-1), h(-1) {} RectREHex::DocumentCtrl::Rect56 Rect(int x, int64_t y, int w, int64_t h): x(x), y(y), w(w), h(h) {} 57 }; 58 59 class Region 60 { 61 protected: 62 int64_t y_offset; /* First on-screen line in region */ 63 int64_t y_lines; /* Number of on-screen lines in region */ 64 65 int indent_depth; /* Indentation depth */ 66 int indent_final; /* Number of inner indentation levels we are the final region in */ 67 68 public: 69 const off_t indent_offset; 70 const off_t indent_length; 71 72 virtual ~Region(); 73 74 enum StateFlag 75 { 76 IDLE = 0, 77 PROCESSING = (1 << 0), 78 79 WIDTH_CHANGE = (1 << 1), 80 HEIGHT_CHANGE = (1 << 2), 81 REDRAW = (1 << 3), 82 }; 83 84 virtual unsigned int check(); 85 86 protected: 87 Region(off_t indent_offset, off_t indent_length); 88 89 virtual int calc_width(REHex::DocumentCtrl &doc); 90 virtual void calc_height(REHex::DocumentCtrl &doc, wxDC &dc) = 0; 91 92 /* Draw this region on the screen. 93 * 94 * doc - The parent Document object 95 * dc - The wxDC to draw in 96 * x,y - The top-left co-ordinates of this Region in the DC (MAY BE NEGATIVE) 97 * 98 * The implementation MAY skip rendering outside of the client area 99 * of the DC to improve performance. 100 */ 101 virtual void draw(REHex::DocumentCtrl &doc, wxDC &dc, int x, int64_t y) = 0; 102 103 virtual wxCursor cursor_for_point(REHex::DocumentCtrl &doc, int x, int64_t y_lines, int y_px); 104 105 void draw_container(REHex::DocumentCtrl &doc, wxDC &dc, int x, int64_t y); 106 void draw_full_height_line(DocumentCtrl *doc_ctrl, wxDC &dc, int x, int64_t y); 107 108 struct Highlight 109 { 110 public: 111 const bool enable; 112 113 const Palette::ColourIndex fg_colour_idx; 114 const Palette::ColourIndex bg_colour_idx; 115 const bool strong; 116 HighlightREHex::DocumentCtrl::Region::Highlight117 Highlight(Palette::ColourIndex fg_colour_idx, Palette::ColourIndex bg_colour_idx, bool strong): 118 enable(true), 119 fg_colour_idx(fg_colour_idx), 120 bg_colour_idx(bg_colour_idx), 121 strong(strong) {} 122 123 protected: HighlightREHex::DocumentCtrl::Region::Highlight124 Highlight(): 125 enable(false), 126 fg_colour_idx(Palette::PAL_INVALID), 127 bg_colour_idx(Palette::PAL_INVALID), 128 strong(false) {} 129 }; 130 131 struct NoHighlight: Highlight 132 { NoHighlightREHex::DocumentCtrl::Region::NoHighlight133 NoHighlight(): Highlight() {} 134 }; 135 136 static void draw_hex_line(DocumentCtrl *doc_ctrl, wxDC &dc, int x, int y, const unsigned char *data, size_t data_len, unsigned int pad_bytes, off_t base_off, bool alternate_row, const std::function<Highlight(off_t)> &highlight_at_off); 137 static void draw_ascii_line(DocumentCtrl *doc_ctrl, wxDC &dc, int x, int y, const unsigned char *data, size_t data_len, size_t data_extra_pre, size_t data_extra_post, off_t alignment_hint, unsigned int pad_bytes, off_t base_off, bool alternate_row, const std::function<Highlight(off_t)> &highlight_at_off); 138 139 /** 140 * @brief Calculate offset of byte at X co-ordinate. 141 * 142 * Calculates the offset of the byte at the given X 143 * co-ordinate in a line drawn with draw_hex_line(). Returns 144 * -1 if the co-ordinate is negative or falls between byte 145 * groups. 146 */ 147 static int offset_at_x_hex(DocumentCtrl *doc_ctrl, int rel_x); 148 149 /** 150 * @brief Calculate offset of byte near X co-ordinate. 151 * 152 * Calculates the offset of the byte nearest the given X 153 * co-ordinate in a line drawn with draw_hex_line(). Returns 154 * -1 if the co-ordinate is negative. 155 */ 156 static int offset_near_x_hex(DocumentCtrl *doc_ctrl, int rel_x); 157 158 friend DocumentCtrl; 159 }; 160 161 class GenericDataRegion: public Region 162 { 163 protected: 164 GenericDataRegion(off_t d_offset, off_t d_length, off_t indent_offset); 165 166 public: 167 const off_t d_offset; 168 const off_t d_length; 169 170 /** 171 * @brief Represents an on-screen area of the region. 172 */ 173 enum ScreenArea 174 { 175 SA_NONE = 0, /**< No/Unknown area. */ 176 SA_HEX = 1, /**< The hex (data) view. */ 177 SA_ASCII = 2, /**< The ASCII (text) view. */ 178 SA_SPECIAL = 4, /**< Region-specific data area. */ 179 }; 180 181 /** 182 * @brief Returns the offset of the byte at the given co-ordinates, negative if there isn't one. 183 */ 184 virtual std::pair<off_t, ScreenArea> offset_at_xy(DocumentCtrl &doc, int mouse_x_px, int64_t mouse_y_lines) = 0; 185 186 /** 187 * @brief Returns the offset of the byte nearest the given co-ordinates and the screen area. 188 * 189 * If type_hint is specified, and supported by the region 190 * type, the nearest character in that area will be 191 * returned rather than in the area under or closest to the 192 */ 193 virtual std::pair<off_t, ScreenArea> offset_near_xy(DocumentCtrl &doc, int mouse_x_px, int64_t mouse_y_lines, ScreenArea type_hint) = 0; 194 195 static const off_t CURSOR_PREV_REGION = -2; 196 static const off_t CURSOR_NEXT_REGION = -3; 197 198 /** 199 * @brief Returns the offset of the cursor position left of the given offset. May return CURSOR_PREV_REGION. 200 */ 201 virtual off_t cursor_left_from(off_t pos) = 0; 202 203 /** 204 * @brief Returns the offset of the cursor position right of the given offset. May return CURSOR_NEXT_REGION. 205 */ 206 virtual off_t cursor_right_from(off_t pos) = 0; 207 208 /** 209 * @brief Returns the offset of the cursor position up from the given offset. May return CURSOR_PREV_REGION. 210 */ 211 virtual off_t cursor_up_from(off_t pos) = 0; 212 213 /** 214 * @brief Returns the offset of the cursor position down from the given offset. May return CURSOR_NEXT_REGION. 215 */ 216 virtual off_t cursor_down_from(off_t pos) = 0; 217 218 /** 219 * @brief Returns the offset of the cursor position at the start of the line from the given offset. 220 */ 221 virtual off_t cursor_home_from(off_t pos) = 0; 222 223 /** 224 * @brief Returns the offset of the cursor position at the end of the line from the given offset. 225 */ 226 virtual off_t cursor_end_from(off_t pos) = 0; 227 228 /** 229 * @brief Returns the screen column index of the given offset within the region. 230 */ 231 virtual int cursor_column(off_t pos) = 0; 232 233 /** 234 * @brief Returns the offset of the cursor position nearest the given column on the first screen line of the region. 235 */ 236 virtual off_t first_row_nearest_column(int column) = 0; 237 238 /** 239 * @brief Returns the offset of the cursor position nearest the given column on the last screen line of the region. 240 */ 241 virtual off_t last_row_nearest_column(int column) = 0; 242 243 /** 244 * @brief Returns the offset of the cursor position nearest the given column on the given row within the region. 245 */ 246 virtual off_t nth_row_nearest_column(int64_t row, int column) = 0; 247 248 /** 249 * @brief Calculate the on-screen bounding box of a byte in the region. 250 */ 251 virtual Rect calc_offset_bounds(off_t offset, DocumentCtrl *doc_ctrl) = 0; 252 253 /** 254 * @brief Find which screen areas exist for the cursor to occupy at the given offset. 255 * @return SA_XXX constants bitwise OR'd together. 256 */ 257 virtual ScreenArea screen_areas_at_offset(off_t offset, DocumentCtrl *doc_ctrl) = 0; 258 259 /** 260 * @brief Process key presses while the cursor is in this region. 261 * @return true if the event was handled, false otherwise. 262 * 263 * This method is called to process keypresses while the 264 * cursor is in this region. 265 * 266 * If it returns true, no further processing of the event 267 * will be performed, if it returns false, processing will 268 * continue and any default processing of the key press 269 * will be used. 270 * 271 * The method may be called multiple times for the same 272 * event if it returns false, the method MUST be idempotent 273 * when it returns false. 274 */ 275 virtual bool OnChar(DocumentCtrl *doc_ctrl, wxKeyEvent &event); 276 277 /** 278 * @brief Process a clipboard copy operation within this region. 279 * @return wxDataObject pointer, or NULL. 280 * 281 * This method is called to process copy events when the 282 * selection is entirely within a single region. 283 * 284 * Returns a pointer to a wxDataObject object to be placed 285 * into the clipboard, or NULL if the region has no special 286 * clipboard handling, in which case the default copy 287 * behaviour will take over. 288 * 289 * The caller is responsible for ensuring any returned 290 * wxDataObject is deleted. 291 */ 292 virtual wxDataObject *OnCopy(DocumentCtrl &doc_ctrl); 293 294 /** 295 * @brief Process a clipboard paste operation within this region. 296 * @return true if the event was handled, false otherwise. 297 * 298 * This method is called when the user attempts to paste and 299 * one or both of the following is true: 300 * 301 * a) A range of bytes exclusively within this region are selected. 302 * 303 * b) The cursor is within this region. 304 * 305 * The clipboard will already be locked by the caller when 306 * this method is called. 307 * 308 * If this method returns false, default paste handling 309 * will be invoked. 310 */ 311 virtual bool OnPaste(DocumentCtrl *doc_ctrl); 312 }; 313 314 class DataRegion: public GenericDataRegion 315 { 316 protected: 317 off_t virt_offset; 318 319 int offset_text_x; /* Virtual X coord of left edge of offsets. */ 320 int hex_text_x; /* Virtual X coord of left edge of hex data. */ 321 int ascii_text_x; /* Virtual X coord of left edge of ASCII data. */ 322 323 unsigned int bytes_per_line_actual; /* Number of bytes being displayed per line. */ 324 unsigned int first_line_pad_bytes; /* Number of bytes to pad first line with. */ 325 326 public: 327 DataRegion(off_t d_offset, off_t d_length, off_t virt_offset); 328 329 int calc_width_for_bytes(DocumentCtrl &doc_ctrl, unsigned int line_bytes) const; 330 331 protected: 332 virtual int calc_width(REHex::DocumentCtrl &doc) override; 333 virtual void calc_height(REHex::DocumentCtrl &doc, wxDC &dc) override; 334 virtual void draw(REHex::DocumentCtrl &doc, wxDC &dc, int x, int64_t y) override; 335 virtual wxCursor cursor_for_point(REHex::DocumentCtrl &doc, int x, int64_t y_lines, int y_px) override; 336 337 off_t offset_at_xy_hex (REHex::DocumentCtrl &doc, int mouse_x_px, uint64_t mouse_y_lines); 338 off_t offset_at_xy_ascii(REHex::DocumentCtrl &doc, int mouse_x_px, uint64_t mouse_y_lines); 339 340 off_t offset_near_xy_hex (REHex::DocumentCtrl &doc, int mouse_x_px, uint64_t mouse_y_lines); 341 off_t offset_near_xy_ascii(REHex::DocumentCtrl &doc, int mouse_x_px, uint64_t mouse_y_lines); 342 343 virtual std::pair<off_t, ScreenArea> offset_at_xy(DocumentCtrl &doc, int mouse_x_px, int64_t mouse_y_lines) override; 344 virtual std::pair<off_t, ScreenArea> offset_near_xy(DocumentCtrl &doc, int mouse_x_px, int64_t mouse_y_lines, ScreenArea type_hint) override; 345 346 virtual off_t cursor_left_from(off_t pos) override; 347 virtual off_t cursor_right_from(off_t pos) override; 348 virtual off_t cursor_up_from(off_t pos) override; 349 virtual off_t cursor_down_from(off_t pos) override; 350 virtual off_t cursor_home_from(off_t pos) override; 351 virtual off_t cursor_end_from(off_t pos) override; 352 353 virtual int cursor_column(off_t pos) override; 354 virtual off_t first_row_nearest_column(int column) override; 355 virtual off_t last_row_nearest_column(int column) override; 356 virtual off_t nth_row_nearest_column(int64_t row, int column) override; 357 358 virtual Rect calc_offset_bounds(off_t offset, DocumentCtrl *doc_ctrl) override; 359 virtual ScreenArea screen_areas_at_offset(off_t offset, DocumentCtrl *doc_ctrl) override; 360 361 virtual Highlight highlight_at_off(off_t off) const; 362 363 friend DocumentCtrl; 364 }; 365 366 class DataRegionDocHighlight: public DataRegion 367 { 368 private: 369 Document &doc; 370 371 public: 372 DataRegionDocHighlight(off_t d_offset, off_t d_length, off_t virt_offset, Document &doc); 373 374 protected: 375 virtual Highlight highlight_at_off(off_t off) const override; 376 }; 377 378 class CommentRegion: public Region 379 { 380 public: 381 382 off_t c_offset, c_length; 383 const wxString &c_text; 384 385 bool truncate; 386 387 virtual void calc_height(REHex::DocumentCtrl &doc, wxDC &dc) override; 388 virtual void draw(REHex::DocumentCtrl &doc, wxDC &dc, int x, int64_t y) override; 389 virtual wxCursor cursor_for_point(REHex::DocumentCtrl &doc, int x, int64_t y_lines, int y_px) override; 390 391 CommentRegion(off_t c_offset, off_t c_length, const wxString &c_text, bool truncate, off_t indent_offset, off_t indent_length); 392 393 friend DocumentCtrl; 394 }; 395 396 DocumentCtrl(wxWindow *parent, SharedDocumentPointer &doc); 397 ~DocumentCtrl(); 398 399 static const int BYTES_PER_LINE_FIT_BYTES = 0; 400 static const int BYTES_PER_LINE_FIT_GROUPS = -1; 401 static const int BYTES_PER_LINE_MIN = 1; 402 static const int BYTES_PER_LINE_MAX = 128; 403 404 int get_bytes_per_line(); 405 void set_bytes_per_line(int bytes_per_line); 406 407 unsigned int get_bytes_per_group(); 408 void set_bytes_per_group(unsigned int bytes_per_group); 409 410 bool get_show_offsets(); 411 void set_show_offsets(bool show_offsets); 412 413 OffsetBase get_offset_display_base() const; 414 void set_offset_display_base(OffsetBase offset_display_base); 415 416 bool get_show_ascii(); 417 void set_show_ascii(bool show_ascii); 418 419 bool get_highlight_selection_match(); 420 void set_highlight_selection_match(bool highlight_selection_match); 421 422 off_t get_cursor_position() const; 423 Document::CursorState get_cursor_state() const; 424 425 bool hex_view_active() const; 426 bool ascii_view_active() const; 427 bool special_view_active() const; 428 429 void set_cursor_position(off_t position, Document::CursorState cursor_state = Document::CSTATE_GOTO); 430 431 bool has_prev_cursor_position() const; 432 void goto_prev_cursor_position(); 433 434 bool has_next_cursor_position() const; 435 void goto_next_cursor_position(); 436 437 bool get_insert_mode(); 438 void set_insert_mode(bool enabled); 439 440 void linked_scroll_insert_self_after(DocumentCtrl *p); 441 void linked_scroll_remove_self(); 442 443 /** 444 * @brief Set the selection range. 445 * 446 * @param begin Data offset at beginning of selection. 447 * @param end Data offset at end of selection (inclusive). 448 */ 449 bool set_selection_raw(off_t begin, off_t end); 450 451 /** 452 * @brief Clear the selection (if any). 453 */ 454 void clear_selection(); 455 456 /** 457 * @brief Returns true if there is a selection. 458 */ 459 bool has_selection(); 460 461 /** 462 * @brief Returns the "raw" selection as a begin and end offset. 463 * 464 * NOTE: Unlike most "end" pointers, the end offset returned from this 465 * method is the last byte in the selection, not one past it. 466 */ 467 std::pair<off_t, off_t> get_selection_raw(); 468 469 /** 470 * @brief Returns the subset of the current selection scoped to a region. 471 * 472 * The return value from this method is the offset (file relative) and the 473 * length of the current selection, scoped to the given region. 474 * 475 * If there is no selection, or the selection doesn't include any bytes 476 * from the given region, the returned length will be <= 0. 477 */ 478 std::pair<off_t, off_t> get_selection_in_region(GenericDataRegion *region); 479 480 /** 481 * @brief Returns the set of all bytes currently selected. 482 * 483 * NOTE: This method may be expensive to call, as it potentially has to 484 * iterate through all (data) regions in the file. 485 */ 486 OrderedByteRangeSet get_selection_ranges(); 487 488 /** 489 * @brief Returns the offset and length of the selection, if linear. 490 * 491 * If there is no selection, or the selection isn't linear and contiguous, the length 492 * will be zero. 493 */ 494 std::pair<off_t, off_t> get_selection_linear(); 495 496 const std::vector<Region*> &get_regions() const; 497 const std::vector<GenericDataRegion*> &get_data_regions() const; 498 void replace_all_regions(std::vector<Region*> &new_regions); 499 bool region_OnChar(wxKeyEvent &event); 500 GenericDataRegion *data_region_by_offset(off_t offset); 501 std::vector<Region*>::iterator region_by_y_offset(int64_t y_offset); 502 503 /** 504 * @brief Compare two offsets in the address space defined by the regions. 505 * 506 * Returns zero if the two offsets are equal, a negative integer if a is 507 * less than b and a positive integer if a is greater than b. 508 * 509 * Throws an exception of type std::invalid_argument if either of the 510 * offsets are invalid. 511 */ 512 int region_offset_cmp(off_t a, off_t b); 513 514 /** 515 * @brief Increment an offset in the address space defined by the regions. 516 * 517 * @param base Base offset to start at. 518 * @param add Number of bytes to increment base by. 519 * 520 * @return New offset, negative if invalid. 521 */ 522 off_t region_offset_add(off_t base, off_t add); 523 524 /** 525 * @brief Decrement an offset in the address space defined by the regions. 526 * 527 * @param base Base offset to start at. 528 * @param add Number of bytes to decrement base by. 529 * 530 * @return New offset, negative if invalid. 531 */ 532 off_t region_offset_sub(off_t base, off_t sub); 533 534 /** 535 * @brief Check if a range of offsets is linear and contiguous. 536 */ 537 bool region_range_linear(off_t begin_offset, off_t end_offset_incl); 538 539 wxFont &get_font(); 540 541 /** 542 * @brief Returns the current vertical scroll position, in lines. 543 */ 544 int64_t get_scroll_yoff() const; 545 546 /** 547 * @brief Set the vertical scroll position, in lines. 548 */ 549 void set_scroll_yoff(int64_t scroll_yoff, bool update_linked_scroll_others = true); 550 551 void OnPaint(wxPaintEvent &event); 552 void OnErase(wxEraseEvent& event); 553 void OnSize(wxSizeEvent &event); 554 void OnScroll(wxScrollWinEvent &event); 555 void OnWheel(wxMouseEvent &event); 556 void OnChar(wxKeyEvent &event); 557 void OnLeftDown(wxMouseEvent &event); 558 void OnLeftUp(wxMouseEvent &event); 559 void OnRightDown(wxMouseEvent &event); 560 void OnMotion(wxMouseEvent &event); 561 void OnSelectTick(wxTimerEvent &event); 562 void OnMotionTick(int mouse_x, int mouse_y); 563 void OnRedrawCursor(wxTimerEvent &event); 564 void OnClearHighlight(wxCommandEvent &event); 565 void OnIdle(wxIdleEvent &event); 566 void OnFontSizeAdjustmentChanged(FontSizeAdjustmentEvent &event); 567 568 #ifndef UNIT_TEST 569 private: 570 #endif 571 friend DataRegion; 572 friend CommentRegion; 573 574 SharedDocumentPointer doc; 575 576 std::vector<Region*> regions; /**< List of regions to be displayed. */ 577 std::vector<GenericDataRegion*> data_regions; /**< Subset of regions which are a GenericDataRegion. */ 578 std::vector<Region*> processing_regions; /**< Subset of regions which are doing background processing. */ 579 580 /** List of iterators into data_regions, sorted by d_offset. */ 581 std::vector< std::vector<GenericDataRegion*>::iterator > data_regions_sorted; 582 583 /* Fixed-width font used for drawing hex data. */ 584 wxFont hex_font; 585 586 /* Size of a character in hex_font. */ 587 unsigned char hf_height; 588 589 /* Size of the client area in pixels. */ 590 int client_width; 591 int client_height; 592 593 /* Height of client area in lines. */ 594 unsigned int visible_lines; 595 596 /* Width of the scrollable area. */ 597 int virtual_width; 598 599 /* Display options */ 600 int bytes_per_line; 601 unsigned int bytes_per_group; 602 603 bool offset_column{true}; 604 int offset_column_width; 605 OffsetBase offset_display_base; 606 607 bool show_ascii; 608 609 bool highlight_selection_match; 610 611 int scroll_xoff; 612 int64_t scroll_yoff; 613 int64_t scroll_yoff_max; 614 int64_t scroll_ydiv; 615 616 DocumentCtrl *linked_scroll_prev; 617 DocumentCtrl *linked_scroll_next; 618 619 int wheel_vert_accum; 620 int wheel_horiz_accum; 621 622 off_t cpos_off{0}; 623 bool insert_mode{false}; 624 625 static const size_t CPOS_HISTORY_LIMIT = 128; 626 627 std::vector<off_t> cpos_prev; 628 std::vector<off_t> cpos_next; 629 630 off_t selection_begin; 631 off_t selection_end; 632 633 bool cursor_visible; 634 wxTimer redraw_cursor_timer; 635 636 static const int MOUSE_SELECT_INTERVAL = 100; 637 638 GenericDataRegion::ScreenArea mouse_down_area; 639 off_t mouse_down_at_offset; 640 int mouse_down_at_x; 641 wxTimer mouse_select_timer; 642 off_t mouse_shift_initial; 643 644 Document::CursorState cursor_state; 645 646 void _set_cursor_position(off_t position, Document::CursorState cursor_state, bool preserve_cpos_hist = false); 647 648 std::vector<GenericDataRegion*>::iterator _data_region_by_offset(off_t offset); 649 650 std::list<Region*>::iterator _region_by_y_offset(int64_t y_offset); 651 652 void _make_line_visible(int64_t line); 653 void _make_x_visible(int x_px, int width_px); 654 655 void _make_byte_visible(off_t offset); 656 657 void _handle_width_change(); 658 void _handle_height_change(); 659 void _update_vscroll(); 660 void _update_vscroll_pos(bool update_linked_scroll_others = true); 661 662 /** 663 * @brief Fuzzy description of the DocumentCtrl scroll position. 664 * 665 * This struct describes the DocumentCtrl scroll position, in terms of its 666 * contents so it can be restored (as close as possible) when the window 667 * size changes or regions are added/removed/grow/shrink/etc. 668 */ 669 struct FuzzyScrollPosition 670 { 671 bool data_offset_valid; /**< True if data_offset and data_offset_line are valid. */ 672 off_t data_offset; /**< File offset used as reference point. */ 673 int64_t data_offset_line; /**< Visible (on-screen) line where data_offset is. */ 674 675 bool region_idx_valid; /**< True if region_idx and region_idx_line are valid. */ 676 size_t region_idx; /**< Index of region whose first line is our reference point. */ 677 int64_t region_idx_line; /**< Visible (on-screen) line where region begins (may be negative). */ 678 FuzzyScrollPositionREHex::DocumentCtrl::FuzzyScrollPosition679 FuzzyScrollPosition(): 680 data_offset_valid(false), 681 region_idx_valid(false) {} 682 }; 683 684 /** 685 * @brief Set scroll_yoff, clamped to valid range. 686 */ 687 void set_scroll_yoff_clamped(int64_t scroll_yoff); 688 689 /** 690 * @brief Fetch the current scroll position. 691 */ 692 FuzzyScrollPosition get_scroll_position_fuzzy(); 693 694 /** 695 * @brief Jump to a fuzzy scroll position. 696 */ 697 void set_scroll_position_fuzzy(const FuzzyScrollPosition &fsp); 698 699 FuzzyScrollPosition saved_scroll_position; 700 701 /** 702 * @brief Save the current scroll position. 703 */ 704 void save_scroll_position(); 705 706 /** 707 * @brief Restore the last saved scroll position. 708 */ 709 void restore_scroll_position(); 710 711 void linked_scroll_visit_others(const std::function<void(DocumentCtrl*)> &func); 712 713 static const int PRECOMP_HF_STRING_WIDTH_TO = 512; 714 unsigned int hf_string_width_precomp[PRECOMP_HF_STRING_WIDTH_TO]; 715 716 static const size_t GETTEXTEXTENT_CACHE_SIZE = 4096; 717 LRUCache<std::string, wxSize> hf_gte_cache; 718 719 public: 720 static std::list<wxString> format_text(const wxString &text, unsigned int cols, unsigned int from_line = 0, unsigned int max_lines = -1); 721 int indent_width(int depth); 722 int get_offset_column_width(); 723 int get_virtual_width(); 724 bool get_cursor_visible(); 725 726 int hf_char_width(); 727 int hf_char_height(); 728 int hf_string_width(int length); 729 int hf_char_at_x(int x_px); 730 731 /* Stays at the bottom because it changes the protection... */ 732 DECLARE_EVENT_TABLE() 733 }; 734 } 735 736 #endif /* !REHEX_DOCUMENTCTRL_HPP */ 737