HexEditor.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org>
  4. * Copyright (c) 2022, the SerenityOS developers.
  5. * Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include "HexEditor.h"
  10. #include "AK/Format.h"
  11. #include "SearchResultsModel.h"
  12. #include <AK/Debug.h>
  13. #include <AK/ScopeGuard.h>
  14. #include <AK/StringBuilder.h>
  15. #include <LibGUI/Action.h>
  16. #include <LibGUI/Clipboard.h>
  17. #include <LibGUI/Menu.h>
  18. #include <LibGUI/MessageBox.h>
  19. #include <LibGUI/Painter.h>
  20. #include <LibGUI/Scrollbar.h>
  21. #include <LibGUI/TextEditor.h>
  22. #include <LibGUI/Window.h>
  23. #include <LibGfx/Font/FontDatabase.h>
  24. #include <LibGfx/Palette.h>
  25. #include <ctype.h>
  26. #include <fcntl.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include <unistd.h>
  30. HexEditor::HexEditor()
  31. : m_blink_timer(Core::Timer::construct())
  32. , m_document(make<HexDocumentMemory>(ByteBuffer::create_zeroed(0).release_value_but_fixme_should_propagate_errors()))
  33. {
  34. set_should_hide_unnecessary_scrollbars(true);
  35. set_focus_policy(GUI::FocusPolicy::StrongFocus);
  36. set_scrollbars_enabled(true);
  37. set_font(Gfx::FontDatabase::default_fixed_width_font());
  38. set_background_role(ColorRole::Base);
  39. set_foreground_role(ColorRole::BaseText);
  40. vertical_scrollbar().set_step(line_height());
  41. m_blink_timer->set_interval(500);
  42. m_blink_timer->on_timeout = [this]() {
  43. m_cursor_blink_active = !m_cursor_blink_active;
  44. update();
  45. };
  46. m_blink_timer->start();
  47. }
  48. bool HexEditor::open_new_file(size_t size)
  49. {
  50. auto maybe_buffer = ByteBuffer::create_zeroed(size);
  51. if (maybe_buffer.is_error()) {
  52. return false;
  53. }
  54. m_document = make<HexDocumentMemory>(maybe_buffer.release_value());
  55. set_content_length(m_document->size());
  56. m_position = 0;
  57. m_cursor_at_low_nibble = false;
  58. m_selection_start = 0;
  59. m_selection_end = 0;
  60. scroll_position_into_view(m_position);
  61. update();
  62. update_status();
  63. return true;
  64. }
  65. void HexEditor::open_file(NonnullRefPtr<Core::File> file)
  66. {
  67. m_document = make<HexDocumentFile>(file);
  68. set_content_length(m_document->size());
  69. m_position = 0;
  70. m_cursor_at_low_nibble = false;
  71. m_selection_start = 0;
  72. m_selection_end = 0;
  73. scroll_position_into_view(m_position);
  74. update();
  75. update_status();
  76. }
  77. void HexEditor::fill_selection(u8 fill_byte)
  78. {
  79. if (!has_selection())
  80. return;
  81. for (size_t i = m_selection_start; i < m_selection_end; i++)
  82. m_document->set(i, fill_byte);
  83. update();
  84. did_change();
  85. }
  86. void HexEditor::set_position(size_t position)
  87. {
  88. if (position > m_document->size())
  89. return;
  90. m_position = position;
  91. m_cursor_at_low_nibble = false;
  92. reset_cursor_blink_state();
  93. scroll_position_into_view(position);
  94. update_status();
  95. }
  96. void HexEditor::set_selection(size_t position, size_t length)
  97. {
  98. if (position > m_document->size() || position + length > m_document->size())
  99. return;
  100. m_position = position;
  101. m_cursor_at_low_nibble = false;
  102. m_selection_start = position;
  103. m_selection_end = position + length;
  104. reset_cursor_blink_state();
  105. scroll_position_into_view(position);
  106. update_status();
  107. }
  108. bool HexEditor::save_as(NonnullRefPtr<Core::File> new_file)
  109. {
  110. if (m_document->type() == HexDocument::Type::File) {
  111. HexDocumentFile* fileDocument = static_cast<HexDocumentFile*>(m_document.ptr());
  112. if (!fileDocument->write_to_file(new_file))
  113. return false;
  114. fileDocument->set_file(new_file);
  115. } else {
  116. HexDocumentMemory* memoryDocument = static_cast<HexDocumentMemory*>(m_document.ptr());
  117. if (!memoryDocument->write_to_file(new_file))
  118. return false;
  119. m_document = make<HexDocumentFile>(new_file);
  120. }
  121. update();
  122. return true;
  123. }
  124. bool HexEditor::save()
  125. {
  126. if (m_document->type() != HexDocument::Type::File) {
  127. return false;
  128. }
  129. static_cast<HexDocumentFile*>(m_document.ptr())->write_to_file();
  130. return true;
  131. }
  132. size_t HexEditor::selection_size()
  133. {
  134. if (!has_selection())
  135. return 0;
  136. return m_selection_end - m_selection_start;
  137. }
  138. bool HexEditor::copy_selected_hex_to_clipboard()
  139. {
  140. if (!has_selection())
  141. return false;
  142. StringBuilder output_string_builder;
  143. for (size_t i = m_selection_start; i < m_selection_end; i++)
  144. output_string_builder.appendff("{:02X} ", m_document->get(i).value);
  145. GUI::Clipboard::the().set_plain_text(output_string_builder.to_string());
  146. return true;
  147. }
  148. bool HexEditor::copy_selected_text_to_clipboard()
  149. {
  150. if (!has_selection())
  151. return false;
  152. StringBuilder output_string_builder;
  153. for (size_t i = m_selection_start; i < m_selection_end; i++)
  154. output_string_builder.append(isprint(m_document->get(i).value) ? m_document->get(i).value : '.');
  155. GUI::Clipboard::the().set_plain_text(output_string_builder.to_string());
  156. return true;
  157. }
  158. bool HexEditor::copy_selected_hex_to_clipboard_as_c_code()
  159. {
  160. if (!has_selection())
  161. return false;
  162. StringBuilder output_string_builder;
  163. output_string_builder.appendff("unsigned char raw_data[{}] = {{\n", m_selection_end - m_selection_start);
  164. output_string_builder.append(" "sv);
  165. for (size_t i = m_selection_start, j = 1; i < m_selection_end; i++, j++) {
  166. output_string_builder.appendff("{:#02X}", m_document->get(i).value);
  167. if (i >= m_selection_end - 1)
  168. continue;
  169. if ((j % 12) == 0)
  170. output_string_builder.append(",\n "sv);
  171. else
  172. output_string_builder.append(", "sv);
  173. }
  174. output_string_builder.append("\n};\n"sv);
  175. GUI::Clipboard::the().set_plain_text(output_string_builder.to_string());
  176. return true;
  177. }
  178. void HexEditor::set_bytes_per_row(size_t bytes_per_row)
  179. {
  180. m_bytes_per_row = bytes_per_row;
  181. auto newWidth = offset_margin_width() + (m_bytes_per_row * cell_width()) + 2 * m_padding + (m_bytes_per_row * character_width()) + 4 * m_padding;
  182. auto newHeight = total_rows() * line_height() + 2 * m_padding;
  183. set_content_size({ static_cast<int>(newWidth), static_cast<int>(newHeight) });
  184. update();
  185. }
  186. void HexEditor::set_content_length(size_t length)
  187. {
  188. if (length == m_content_length)
  189. return;
  190. m_content_length = length;
  191. auto newWidth = offset_margin_width() + (m_bytes_per_row * cell_width()) + 2 * m_padding + (m_bytes_per_row * character_width()) + 4 * m_padding;
  192. auto newHeight = total_rows() * line_height() + 2 * m_padding;
  193. set_content_size({ static_cast<int>(newWidth), static_cast<int>(newHeight) });
  194. }
  195. Optional<u8> HexEditor::get_byte(size_t position)
  196. {
  197. if (position < m_document->size())
  198. return m_document->get(position).value;
  199. return {};
  200. }
  201. void HexEditor::mousedown_event(GUI::MouseEvent& event)
  202. {
  203. if (event.button() != GUI::MouseButton::Primary) {
  204. return;
  205. }
  206. auto absolute_x = horizontal_scrollbar().value() + event.x();
  207. auto absolute_y = vertical_scrollbar().value() + event.y();
  208. auto hex_start_x = frame_thickness() + m_address_bar_width;
  209. auto hex_start_y = frame_thickness() + m_padding;
  210. auto hex_end_x = static_cast<int>(hex_start_x + bytes_per_row() * cell_width());
  211. auto hex_end_y = static_cast<int>(hex_start_y + m_padding + total_rows() * line_height());
  212. auto text_start_x = static_cast<int>(frame_thickness() + m_address_bar_width + 2 * m_padding + bytes_per_row() * cell_width());
  213. auto text_start_y = frame_thickness() + m_padding;
  214. auto text_end_x = static_cast<int>(text_start_x + bytes_per_row() * character_width());
  215. auto text_end_y = static_cast<int>(text_start_y + m_padding + total_rows() * line_height());
  216. if (absolute_x >= hex_start_x && absolute_x <= hex_end_x && absolute_y >= hex_start_y && absolute_y <= hex_end_y) {
  217. if (absolute_x < hex_start_x || absolute_y < hex_start_y)
  218. return;
  219. auto byte_x = (absolute_x - hex_start_x) / cell_width();
  220. auto byte_y = (absolute_y - hex_start_y) / line_height();
  221. auto offset = (byte_y * m_bytes_per_row) + byte_x;
  222. if (offset >= m_document->size())
  223. return;
  224. dbgln_if(HEX_DEBUG, "HexEditor::mousedown_event(hex): offset={}", offset);
  225. m_edit_mode = EditMode::Hex;
  226. m_cursor_at_low_nibble = false;
  227. m_position = offset;
  228. m_in_drag_select = true;
  229. m_selection_start = offset;
  230. m_selection_end = offset;
  231. update();
  232. update_status();
  233. }
  234. if (absolute_x >= text_start_x && absolute_x <= text_end_x && absolute_y >= text_start_y && absolute_y <= text_end_y) {
  235. if (absolute_x < hex_start_x || absolute_y < hex_start_y)
  236. return;
  237. auto byte_x = (absolute_x - text_start_x) / character_width();
  238. auto byte_y = (absolute_y - text_start_y) / line_height();
  239. auto offset = (byte_y * m_bytes_per_row) + byte_x;
  240. if (offset >= m_document->size())
  241. return;
  242. dbgln_if(HEX_DEBUG, "HexEditor::mousedown_event(text): offset={}", offset);
  243. m_position = offset;
  244. m_cursor_at_low_nibble = false;
  245. m_in_drag_select = true;
  246. m_selection_start = offset;
  247. m_selection_end = offset;
  248. m_edit_mode = EditMode::Text;
  249. update();
  250. update_status();
  251. }
  252. }
  253. void HexEditor::mousemove_event(GUI::MouseEvent& event)
  254. {
  255. auto absolute_x = horizontal_scrollbar().value() + event.x();
  256. auto absolute_y = vertical_scrollbar().value() + event.y();
  257. auto hex_start_x = frame_thickness() + m_address_bar_width;
  258. auto hex_start_y = frame_thickness() + m_padding;
  259. auto hex_end_x = static_cast<int>(hex_start_x + bytes_per_row() * cell_width());
  260. auto hex_end_y = static_cast<int>(hex_start_y + m_padding + total_rows() * line_height());
  261. auto text_start_x = static_cast<int>(frame_thickness() + m_address_bar_width + 2 * m_padding + bytes_per_row() * cell_width());
  262. auto text_start_y = frame_thickness() + m_padding;
  263. auto text_end_x = static_cast<int>(text_start_x + bytes_per_row() * character_width());
  264. auto text_end_y = static_cast<int>(text_start_y + m_padding + total_rows() * line_height());
  265. if ((absolute_x >= hex_start_x && absolute_x <= hex_end_x
  266. && absolute_y >= hex_start_y && absolute_y <= hex_end_y)
  267. || (absolute_x >= text_start_x && absolute_x <= text_end_x
  268. && absolute_y >= text_start_y && absolute_y <= text_end_y)) {
  269. set_override_cursor(Gfx::StandardCursor::IBeam);
  270. } else {
  271. set_override_cursor(Gfx::StandardCursor::None);
  272. }
  273. if (m_in_drag_select) {
  274. if (absolute_x >= hex_start_x && absolute_x <= hex_end_x && absolute_y >= hex_start_y && absolute_y <= hex_end_y) {
  275. if (absolute_x < hex_start_x || absolute_y < hex_start_y)
  276. return;
  277. auto byte_x = (absolute_x - hex_start_x) / cell_width();
  278. auto byte_y = (absolute_y - hex_start_y) / line_height();
  279. auto offset = (byte_y * m_bytes_per_row) + byte_x;
  280. if (offset > m_document->size())
  281. return;
  282. m_selection_end = offset;
  283. m_position = offset;
  284. scroll_position_into_view(offset);
  285. }
  286. if (absolute_x >= text_start_x && absolute_x <= text_end_x && absolute_y >= text_start_y && absolute_y <= text_end_y) {
  287. if (absolute_x < hex_start_x || absolute_y < hex_start_y)
  288. return;
  289. auto byte_x = (absolute_x - text_start_x) / character_width();
  290. auto byte_y = (absolute_y - text_start_y) / line_height();
  291. auto offset = (byte_y * m_bytes_per_row) + byte_x;
  292. if (offset > m_document->size())
  293. return;
  294. m_selection_end = offset;
  295. m_position = offset;
  296. scroll_position_into_view(offset);
  297. }
  298. update_status();
  299. update();
  300. return;
  301. }
  302. }
  303. void HexEditor::mouseup_event(GUI::MouseEvent& event)
  304. {
  305. if (event.button() == GUI::MouseButton::Primary) {
  306. if (m_in_drag_select) {
  307. if (m_selection_end < m_selection_start) {
  308. // lets flip these around
  309. auto start = m_selection_end;
  310. m_selection_end = m_selection_start;
  311. m_selection_start = start;
  312. }
  313. m_in_drag_select = false;
  314. }
  315. update();
  316. update_status();
  317. }
  318. }
  319. void HexEditor::scroll_position_into_view(size_t position)
  320. {
  321. size_t y = position / bytes_per_row();
  322. size_t x = position % bytes_per_row();
  323. Gfx::IntRect rect {
  324. static_cast<int>(frame_thickness() + offset_margin_width() + x * cell_width() + 2 * m_padding),
  325. static_cast<int>(frame_thickness() + m_padding + y * line_height()),
  326. static_cast<int>(cell_width()),
  327. static_cast<int>(line_height() - m_line_spacing)
  328. };
  329. scroll_into_view(rect, true, true);
  330. }
  331. void HexEditor::keydown_event(GUI::KeyEvent& event)
  332. {
  333. dbgln_if(HEX_DEBUG, "HexEditor::keydown_event key={}", static_cast<u8>(event.key()));
  334. auto move_and_update_cursor_by = [&](i64 cursor_location_change) {
  335. size_t new_position = m_position + cursor_location_change;
  336. if (event.modifiers() & Mod_Shift) {
  337. size_t selection_pivot = m_position == m_selection_end ? m_selection_start : m_selection_end;
  338. m_position = new_position;
  339. m_selection_start = selection_pivot;
  340. m_selection_end = m_position;
  341. if (m_selection_start > m_selection_end)
  342. swap(m_selection_start, m_selection_end);
  343. } else
  344. m_selection_start = m_selection_end = m_position = new_position;
  345. m_cursor_at_low_nibble = false;
  346. reset_cursor_blink_state();
  347. scroll_position_into_view(m_position);
  348. update();
  349. update_status();
  350. };
  351. if (event.key() == KeyCode::Key_Up) {
  352. if (m_position >= bytes_per_row())
  353. move_and_update_cursor_by(-bytes_per_row());
  354. return;
  355. }
  356. if (event.key() == KeyCode::Key_Down) {
  357. if (m_position + bytes_per_row() < m_document->size())
  358. move_and_update_cursor_by(bytes_per_row());
  359. return;
  360. }
  361. if (event.key() == KeyCode::Key_Left) {
  362. if (m_position >= 1)
  363. move_and_update_cursor_by(-1);
  364. return;
  365. }
  366. if (event.key() == KeyCode::Key_Right) {
  367. if (m_position + 1 < m_document->size())
  368. move_and_update_cursor_by(1);
  369. return;
  370. }
  371. if (event.key() == KeyCode::Key_Backspace) {
  372. if (m_position > 0)
  373. move_and_update_cursor_by(-1);
  374. return;
  375. }
  376. if (event.key() == KeyCode::Key_PageUp) {
  377. auto cursor_location_change = min(bytes_per_row() * visible_content_rect().height(), m_position);
  378. if (cursor_location_change > 0)
  379. move_and_update_cursor_by(-cursor_location_change);
  380. return;
  381. }
  382. if (event.key() == KeyCode::Key_PageDown) {
  383. auto cursor_location_change = min(bytes_per_row() * visible_content_rect().height(), m_document->size() - m_position);
  384. if (cursor_location_change > 0)
  385. move_and_update_cursor_by(cursor_location_change);
  386. return;
  387. }
  388. if (!event.ctrl() && !event.alt() && !event.text().is_empty()) {
  389. if (m_edit_mode == EditMode::Hex) {
  390. hex_mode_keydown_event(event);
  391. } else {
  392. text_mode_keydown_event(event);
  393. }
  394. }
  395. }
  396. void HexEditor::hex_mode_keydown_event(GUI::KeyEvent& event)
  397. {
  398. if ((event.key() >= KeyCode::Key_0 && event.key() <= KeyCode::Key_9) || (event.key() >= KeyCode::Key_A && event.key() <= KeyCode::Key_F)) {
  399. if (m_document->size() == 0)
  400. return;
  401. VERIFY(m_position <= m_document->size());
  402. // yes, this is terrible... but it works.
  403. auto value = (event.key() >= KeyCode::Key_0 && event.key() <= KeyCode::Key_9)
  404. ? event.key() - KeyCode::Key_0
  405. : (event.key() - KeyCode::Key_A) + 0xA;
  406. if (!m_cursor_at_low_nibble) {
  407. u8 existing_change = m_document->get(m_position).value;
  408. existing_change = value << 4 | (existing_change & 0xF); // shift new value left 4 bits, OR with existing last 4 bits
  409. m_document->set(m_position, existing_change);
  410. m_cursor_at_low_nibble = true;
  411. } else {
  412. m_document->set(m_position, (m_document->get(m_position).value & 0xF0) | value); // save the first 4 bits, OR the new value in the last 4
  413. if (m_position + 1 < m_document->size())
  414. m_position++;
  415. m_cursor_at_low_nibble = false;
  416. }
  417. reset_cursor_blink_state();
  418. update();
  419. update_status();
  420. did_change();
  421. }
  422. }
  423. void HexEditor::text_mode_keydown_event(GUI::KeyEvent& event)
  424. {
  425. if (m_document->size() == 0)
  426. return;
  427. VERIFY(m_position < m_document->size());
  428. if (event.code_point() == 0) // This is a control key
  429. return;
  430. m_document->set(m_position, event.code_point());
  431. if (m_position + 1 < m_document->size())
  432. m_position++;
  433. m_cursor_at_low_nibble = false;
  434. reset_cursor_blink_state();
  435. update();
  436. update_status();
  437. did_change();
  438. }
  439. void HexEditor::update_status()
  440. {
  441. if (on_status_change)
  442. on_status_change(m_position, m_edit_mode, m_selection_start, m_selection_end);
  443. }
  444. void HexEditor::did_change()
  445. {
  446. if (on_change)
  447. on_change();
  448. }
  449. void HexEditor::paint_event(GUI::PaintEvent& event)
  450. {
  451. GUI::Frame::paint_event(event);
  452. GUI::Painter painter(*this);
  453. painter.add_clip_rect(widget_inner_rect());
  454. painter.add_clip_rect(event.rect());
  455. painter.fill_rect(event.rect(), palette().color(background_role()));
  456. if (m_document->size() == 0)
  457. return;
  458. painter.translate(frame_thickness(), frame_thickness());
  459. painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
  460. Gfx::IntRect offset_clip_rect {
  461. 0,
  462. vertical_scrollbar().value(),
  463. m_address_bar_width - m_padding,
  464. height() - height_occupied_by_horizontal_scrollbar() //(total_rows() * line_height()) + 5
  465. };
  466. painter.fill_rect(offset_clip_rect, palette().ruler());
  467. painter.draw_line(offset_clip_rect.top_right(), offset_clip_rect.bottom_right(), palette().ruler_border());
  468. auto margin_and_hex_width = static_cast<int>(offset_margin_width() + m_bytes_per_row * cell_width() + 3 * m_padding);
  469. painter.draw_line({ margin_and_hex_width, 0 },
  470. { margin_and_hex_width, vertical_scrollbar().value() + (height() - height_occupied_by_horizontal_scrollbar()) },
  471. palette().ruler_border());
  472. size_t view_height = (height() - height_occupied_by_horizontal_scrollbar());
  473. size_t min_row = max(0, vertical_scrollbar().value() / line_height()); // if below 0 then use 0
  474. size_t max_row = min(total_rows(), min_row + ceil_div(view_height, line_height())); // if above calculated rows, use calculated rows
  475. // paint offsets
  476. for (size_t i = min_row; i < max_row; i++) {
  477. Gfx::IntRect side_offset_rect {
  478. frame_thickness() + m_padding,
  479. static_cast<int>(frame_thickness() + m_padding + i * line_height()),
  480. width() - width_occupied_by_vertical_scrollbar(),
  481. height() - height_occupied_by_horizontal_scrollbar()
  482. };
  483. bool is_current_line = (m_position / bytes_per_row()) == i;
  484. auto line = String::formatted("{:#08X}", i * bytes_per_row());
  485. painter.draw_text(
  486. side_offset_rect,
  487. line,
  488. is_current_line ? font().bold_variant() : font(),
  489. Gfx::TextAlignment::TopLeft,
  490. is_current_line ? palette().ruler_active_text() : palette().ruler_inactive_text());
  491. }
  492. for (size_t i = min_row; i < max_row; i++) {
  493. for (size_t j = 0; j < bytes_per_row(); j++) {
  494. auto byte_position = (i * bytes_per_row()) + j;
  495. if (byte_position >= m_document->size())
  496. return;
  497. bool const edited_flag = m_document->get(byte_position).modified;
  498. bool const selection_inbetween_start_end = byte_position >= m_selection_start && byte_position < m_selection_end;
  499. bool const selection_inbetween_end_start = byte_position >= m_selection_end && byte_position < m_selection_start;
  500. bool const highlight_flag = selection_inbetween_start_end || selection_inbetween_end_start;
  501. Gfx::IntRect hex_display_rect {
  502. frame_thickness() + offset_margin_width() + static_cast<int>(j) * cell_width() + 2 * m_padding,
  503. frame_thickness() + m_padding + static_cast<int>(i) * line_height(),
  504. cell_width(),
  505. line_height() - m_line_spacing
  506. };
  507. const u8 cell_value = m_document->get(byte_position).value;
  508. auto line = String::formatted("{:02X}", cell_value);
  509. Gfx::Color background_color = palette().color(background_role());
  510. Gfx::Color text_color = [&]() -> Gfx::Color {
  511. if (edited_flag)
  512. return Color::Red;
  513. if (cell_value == 0x00)
  514. return palette().color(ColorRole::PlaceholderText);
  515. return palette().color(foreground_role());
  516. }();
  517. if (highlight_flag) {
  518. background_color = edited_flag ? palette().selection().inverted() : palette().selection();
  519. text_color = edited_flag ? palette().selection_text().inverted() : palette().selection_text();
  520. } else if (byte_position == m_position && m_edit_mode == EditMode::Text) {
  521. background_color = palette().inactive_selection();
  522. text_color = palette().inactive_selection_text();
  523. }
  524. painter.fill_rect(hex_display_rect, background_color);
  525. painter.draw_text(hex_display_rect, line, Gfx::TextAlignment::TopLeft, text_color);
  526. if (m_edit_mode == EditMode::Hex) {
  527. if (byte_position == m_position && m_cursor_blink_active) {
  528. Gfx::IntRect cursor_position_rect {
  529. static_cast<int>(hex_display_rect.left() + m_cursor_at_low_nibble * character_width()),
  530. hex_display_rect.top(),
  531. 2,
  532. hex_display_rect.height()
  533. };
  534. painter.fill_rect(cursor_position_rect, palette().text_cursor());
  535. }
  536. }
  537. Gfx::IntRect text_display_rect {
  538. static_cast<int>(frame_thickness() + offset_margin_width() + bytes_per_row() * cell_width() + j * character_width() + 4 * m_padding),
  539. static_cast<int>(frame_thickness() + m_padding + i * line_height()),
  540. static_cast<int>(character_width()),
  541. static_cast<int>(line_height() - m_line_spacing)
  542. };
  543. background_color = palette().color(background_role());
  544. text_color = [&]() -> Gfx::Color {
  545. if (edited_flag)
  546. return Color::Red;
  547. if (cell_value == 0x00)
  548. return palette().color(ColorRole::PlaceholderText);
  549. return palette().color(foreground_role());
  550. }();
  551. if (highlight_flag) {
  552. background_color = edited_flag ? palette().selection().inverted() : palette().selection();
  553. text_color = edited_flag ? palette().selection_text().inverted() : palette().selection_text();
  554. } else if (byte_position == m_position && m_edit_mode == EditMode::Hex) {
  555. background_color = palette().inactive_selection();
  556. text_color = palette().inactive_selection_text();
  557. }
  558. painter.fill_rect(text_display_rect, background_color);
  559. painter.draw_text(text_display_rect, String::formatted("{:c}", isprint(cell_value) ? cell_value : '.'), Gfx::TextAlignment::TopLeft, text_color);
  560. if (m_edit_mode == EditMode::Text) {
  561. if (byte_position == m_position && m_cursor_blink_active) {
  562. Gfx::IntRect cursor_position_rect {
  563. text_display_rect.left(), text_display_rect.top(), 2, text_display_rect.height()
  564. };
  565. painter.fill_rect(cursor_position_rect, palette().text_cursor());
  566. }
  567. }
  568. }
  569. }
  570. }
  571. void HexEditor::select_all()
  572. {
  573. highlight(0, m_document->size() - 1);
  574. set_position(0);
  575. }
  576. void HexEditor::highlight(size_t start, size_t end)
  577. {
  578. m_selection_start = start;
  579. m_selection_end = end;
  580. set_position(start);
  581. }
  582. Optional<size_t> HexEditor::find_and_highlight(ByteBuffer& needle, size_t start)
  583. {
  584. auto end_of_match = find(needle, start);
  585. if (end_of_match.has_value()) {
  586. highlight(end_of_match.value() - needle.size(), end_of_match.value() - 1);
  587. }
  588. return end_of_match;
  589. }
  590. Optional<size_t> HexEditor::find(ByteBuffer& needle, size_t start)
  591. {
  592. if (m_document->size() == 0)
  593. return {};
  594. for (size_t i = start; i < m_document->size(); i++) {
  595. if (m_document->get(i).value == *needle.data()) {
  596. bool found = true;
  597. for (size_t j = 1; j < needle.size(); j++) {
  598. if (m_document->get(i + j).value != needle.data()[j]) {
  599. found = false;
  600. break;
  601. }
  602. }
  603. if (found) {
  604. auto end_of_match = i + needle.size();
  605. return end_of_match;
  606. }
  607. }
  608. }
  609. return {};
  610. }
  611. Vector<Match> HexEditor::find_all(ByteBuffer& needle, size_t start)
  612. {
  613. if (m_document->size() == 0 || needle.size() == 0)
  614. return {};
  615. Vector<Match> matches;
  616. size_t i = start;
  617. for (; i < m_document->size(); i++) {
  618. if (m_document->get(i).value == *needle.data()) {
  619. bool found = true;
  620. for (size_t j = 1; j < needle.size(); j++) {
  621. if (m_document->get(i + j).value != needle.data()[j]) {
  622. found = false;
  623. break;
  624. }
  625. }
  626. if (found) {
  627. matches.append({ i, String::formatted("{}", StringView { needle }.to_string().characters()) });
  628. i += needle.size() - 1;
  629. }
  630. }
  631. }
  632. if (matches.is_empty())
  633. return {};
  634. auto first_match = matches.at(0);
  635. highlight(first_match.offset, first_match.offset + first_match.value.length());
  636. return matches;
  637. }
  638. Vector<Match> HexEditor::find_all_strings(size_t min_length)
  639. {
  640. if (m_document->size() == 0)
  641. return {};
  642. Vector<Match> matches;
  643. bool found_string = false;
  644. size_t offset = 0;
  645. StringBuilder builder;
  646. for (size_t i = 0; i < m_document->size(); i++) {
  647. char c = m_document->get(i).value;
  648. if (isprint(c)) {
  649. if (!found_string) {
  650. offset = i;
  651. found_string = true;
  652. }
  653. builder.append(c);
  654. } else {
  655. if (builder.length() >= min_length)
  656. matches.append({ offset, builder.to_string() });
  657. builder.clear();
  658. found_string = false;
  659. }
  660. }
  661. if (matches.is_empty())
  662. return {};
  663. auto first_match = matches.at(0);
  664. highlight(first_match.offset, first_match.offset + first_match.value.length());
  665. return matches;
  666. }
  667. void HexEditor::reset_cursor_blink_state()
  668. {
  669. m_cursor_blink_active = true;
  670. m_blink_timer->restart();
  671. }