hex-edit.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #if defined(Hiro_HexEdit)
  2. namespace hiro {
  3. auto pHexEdit::construct() -> void {
  4. qtWidget = qtHexEdit = new QtHexEdit(*this);
  5. qtHexEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  6. qtHexEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  7. qtHexEdit->setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
  8. qtLayout = new QHBoxLayout;
  9. qtLayout->setAlignment(Qt::AlignRight);
  10. qtLayout->setMargin(0);
  11. qtLayout->setSpacing(0);
  12. qtHexEdit->setLayout(qtLayout);
  13. qtScrollBar = new QtHexEditScrollBar(*this);
  14. qtScrollBar->setSingleStep(1);
  15. qtLayout->addWidget(qtScrollBar);
  16. qtScrollBar->connect(qtScrollBar, SIGNAL(actionTriggered(int)), SLOT(onScroll()));
  17. pWidget::construct();
  18. setBackgroundColor(state().backgroundColor);
  19. setForegroundColor(state().foregroundColor);
  20. _setState();
  21. }
  22. auto pHexEdit::destruct() -> void {
  23. delete qtScrollBar;
  24. delete qtLayout;
  25. delete qtHexEdit;
  26. qtWidget = qtHexEdit = nullptr;
  27. qtLayout = nullptr;
  28. qtScrollBar = nullptr;
  29. }
  30. auto pHexEdit::setAddress(unsigned address) -> void {
  31. _setState();
  32. }
  33. auto pHexEdit::setBackgroundColor(Color color) -> void {
  34. static auto defaultColor = qtHexEdit->palette().color(QPalette::Base);
  35. auto palette = qtHexEdit->palette();
  36. palette.setColor(QPalette::Base, CreateColor(color, defaultColor));
  37. qtHexEdit->setPalette(palette);
  38. qtHexEdit->setAutoFillBackground((bool)color);
  39. }
  40. auto pHexEdit::setColumns(unsigned columns) -> void {
  41. _setState();
  42. }
  43. auto pHexEdit::setForegroundColor(Color color) -> void {
  44. static auto defaultColor = qtHexEdit->palette().color(QPalette::Text);
  45. auto palette = qtHexEdit->palette();
  46. palette.setColor(QPalette::Text, color ? CreateColor(color) : defaultColor);
  47. qtHexEdit->setPalette(palette);
  48. }
  49. auto pHexEdit::setLength(unsigned length) -> void {
  50. _setState();
  51. }
  52. auto pHexEdit::setRows(unsigned rows) -> void {
  53. _setState();
  54. }
  55. auto pHexEdit::update() -> void {
  56. if(!state().onRead) {
  57. qtHexEdit->setPlainText("");
  58. return;
  59. }
  60. unsigned cursorPosition = qtHexEdit->textCursor().position();
  61. string output;
  62. unsigned address = state().address;
  63. for(unsigned row = 0; row < state().rows; row++) {
  64. output.append(hex(address, 8L));
  65. output.append(" ");
  66. string hexdata;
  67. string ansidata = " ";
  68. for(unsigned column = 0; column < state().columns; column++) {
  69. if(address < state().length) {
  70. uint8_t data = self().doRead(address++);
  71. hexdata.append(hex(data, 2L));
  72. hexdata.append(" ");
  73. ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
  74. } else {
  75. hexdata.append(" ");
  76. ansidata.append(" ");
  77. }
  78. }
  79. output.append(hexdata);
  80. output.append(ansidata);
  81. if(address >= state().length) break;
  82. if(row != state().rows - 1) output.append("\n");
  83. }
  84. qtHexEdit->setPlainText(QString::fromUtf8(output));
  85. QTextCursor cursor = qtHexEdit->textCursor();
  86. cursor.setPosition(cursorPosition);
  87. qtHexEdit->setTextCursor(cursor);
  88. }
  89. auto pHexEdit::_keyPressEvent(QKeyEvent* event) -> void {
  90. if(!state().onRead) return;
  91. //allow Ctrl+C (copy)
  92. if(event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) {
  93. qtHexEdit->keyPressEventAcknowledge(event);
  94. return;
  95. }
  96. //disallow other text operations (cut, paste, etc)
  97. if(event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) return;
  98. QTextCursor cursor = qtHexEdit->textCursor();
  99. signed lineWidth = 10 + (state().columns * 3) + 1 + state().columns + 1;
  100. signed cursorY = cursor.position() / lineWidth;
  101. signed cursorX = cursor.position() % lineWidth;
  102. unsigned nibble = 0;
  103. switch(event->key()) {
  104. default: return;
  105. case Qt::Key_Left:
  106. if(cursorX > 0) {
  107. cursor.setPosition(cursor.position() - 1);
  108. qtHexEdit->setTextCursor(cursor);
  109. }
  110. return;
  111. case Qt::Key_Right:
  112. if(cursorX < lineWidth - 1) {
  113. cursor.setPosition(cursor.position() + 1);
  114. qtHexEdit->setTextCursor(cursor);
  115. }
  116. return;
  117. case Qt::Key_Home:
  118. cursor.setPosition(cursorY * lineWidth + 10);
  119. qtHexEdit->setTextCursor(cursor);
  120. return;
  121. case Qt::Key_End:
  122. cursor.setPosition(cursorY * lineWidth + 57);
  123. qtHexEdit->setTextCursor(cursor);
  124. return;
  125. case Qt::Key_Up:
  126. if(cursorY > 0) {
  127. cursor.setPosition(cursor.position() - lineWidth);
  128. qtHexEdit->setTextCursor(cursor);
  129. } else {
  130. _scrollTo(qtScrollBar->sliderPosition() - 1);
  131. }
  132. return;
  133. case Qt::Key_Down:
  134. if(cursorY >= _rows() - 1) {
  135. //cannot scroll down further
  136. } else if(cursorY < state().rows - 1) {
  137. cursor.setPosition(cursor.position() + lineWidth);
  138. qtHexEdit->setTextCursor(cursor);
  139. } else {
  140. _scrollTo(qtScrollBar->sliderPosition() + 1);
  141. }
  142. return;
  143. case Qt::Key_PageUp:
  144. _scrollTo(qtScrollBar->sliderPosition() - state().rows);
  145. return;
  146. case Qt::Key_PageDown:
  147. _scrollTo(qtScrollBar->sliderPosition() + state().rows);
  148. return;
  149. case Qt::Key_0: nibble = 0; break;
  150. case Qt::Key_1: nibble = 1; break;
  151. case Qt::Key_2: nibble = 2; break;
  152. case Qt::Key_3: nibble = 3; break;
  153. case Qt::Key_4: nibble = 4; break;
  154. case Qt::Key_5: nibble = 5; break;
  155. case Qt::Key_6: nibble = 6; break;
  156. case Qt::Key_7: nibble = 7; break;
  157. case Qt::Key_8: nibble = 8; break;
  158. case Qt::Key_9: nibble = 9; break;
  159. case Qt::Key_A: nibble = 10; break;
  160. case Qt::Key_B: nibble = 11; break;
  161. case Qt::Key_C: nibble = 12; break;
  162. case Qt::Key_D: nibble = 13; break;
  163. case Qt::Key_E: nibble = 14; break;
  164. case Qt::Key_F: nibble = 15; break;
  165. }
  166. if(cursorX >= 10) {
  167. //not on an offset
  168. cursorX -= 10;
  169. if((cursorX % 3) != 2) {
  170. //not on a space
  171. bool cursorNibble = (cursorX % 3) == 1; //0 = high, 1 = low
  172. cursorX /= 3;
  173. if(cursorX < state().columns) {
  174. //not in ANSI region
  175. unsigned address = state().address + (cursorY * state().columns + cursorX);
  176. if(address >= state().length) return; //do not edit past end of file
  177. uint8_t data = self().doRead(address);
  178. //write modified value
  179. if(cursorNibble == 1) {
  180. data = (data & 0xf0) | (nibble << 0);
  181. } else {
  182. data = (data & 0x0f) | (nibble << 4);
  183. }
  184. self().doWrite(address, data);
  185. //auto-advance cursor to next nibble/byte
  186. unsigned step = 1;
  187. if(cursorNibble && cursorX != state().columns - 1) step = 2;
  188. cursor.setPosition(cursor.position() + step);
  189. qtHexEdit->setTextCursor(cursor);
  190. //refresh output to reflect modified data
  191. update();
  192. }
  193. }
  194. }
  195. }
  196. //number of actual rows
  197. auto pHexEdit::_rows() -> signed {
  198. return (max(1u, state().length) + state().columns - 1) / state().columns;
  199. }
  200. //number of scrollable row positions
  201. auto pHexEdit::_rowsScrollable() -> signed {
  202. return max(0u, _rows() - state().rows);
  203. }
  204. auto pHexEdit::_scrollTo(signed position) -> void {
  205. if(position > _rowsScrollable()) position = _rowsScrollable();
  206. if(position < 0) position = 0;
  207. qtScrollBar->setSliderPosition(position);
  208. }
  209. auto pHexEdit::_setState() -> void {
  210. auto lock = acquire();
  211. //add one if last row is not equal to column length (eg only part of the row is present)
  212. bool indivisible = state().columns == 0 || (state().length % state().columns) != 0;
  213. qtScrollBar->setRange(0, state().length / state().columns + indivisible - state().rows);
  214. qtScrollBar->setSliderPosition(state().address / state().columns);
  215. qtScrollBar->setPageStep(state().rows);
  216. update();
  217. }
  218. auto QtHexEdit::keyPressEvent(QKeyEvent* event) -> void {
  219. p._keyPressEvent(event);
  220. }
  221. auto QtHexEdit::keyPressEventAcknowledge(QKeyEvent* event) -> void {
  222. QTextEdit::keyPressEvent(event);
  223. }
  224. auto QtHexEdit::wheelEvent(QWheelEvent* event) -> void {
  225. if(event->orientation() == Qt::Vertical) {
  226. signed offset = event->delta() < 0 ? +1 : -1;
  227. p._scrollTo(p.qtScrollBar->sliderPosition() + offset);
  228. event->accept();
  229. }
  230. }
  231. auto QtHexEditScrollBar::event(QEvent* event) -> bool {
  232. if(event->type() == QEvent::Wheel) {
  233. auto wheelEvent = (QWheelEvent*)event;
  234. if(wheelEvent->orientation() == Qt::Vertical) {
  235. signed offset = wheelEvent->delta() < 0 ? +1 : -1;
  236. p._scrollTo(sliderPosition() + offset);
  237. return true;
  238. }
  239. }
  240. return QScrollBar::event(event);
  241. }
  242. auto QtHexEditScrollBar::onScroll() -> void {
  243. if(p.locked()) return;
  244. unsigned address = sliderPosition();
  245. p.state().address = address * p.state().columns;
  246. p.update();
  247. }
  248. }
  249. #endif