HexEditor.cpp 26 KB

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