2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
8 #include <QFontDatabase>
10 #include <QMouseEvent>
13 #include <klocalizedstring.h>
14 #include <kconfigbase.h>
15 #include <kconfiggroup.h>
18 const int COL_ADDR
= 0;
19 const int COL_DUMP_ASCII
= 9;
20 const int MAX_COL
= 10;
22 MemoryWindow::MemoryWindow(QWidget
* parent
) :
27 m_layout(QBoxLayout::TopToBottom
, this),
28 m_format(MDTword
| MDThex
)
30 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont
));
32 m_expression
.setEditable(true);
33 QSize minSize
= m_expression
.sizeHint();
34 m_expression
.setMinimumSize(minSize
);
35 m_expression
.setInsertPolicy(QComboBox::NoInsert
);
36 m_expression
.setMaxCount(15);
38 m_memory
.setColumnCount(10);
39 for (int i
= 0; i
< MAX_COL
; i
++) {
40 m_memory
.header()->setSectionResizeMode(i
, QHeaderView::Fixed
);
42 m_memory
.header()->setStretchLastSection(false);
44 m_memory
.setSortingEnabled(false); /* don't sort */
45 m_memory
.setAllColumnsShowFocus(true);
46 m_memory
.setRootIsDecorated(false);
47 m_memory
.header()->setSectionsClickable(false);
48 m_memory
.header()->setSectionsMovable(false); // don't move columns
49 m_memory
.setHeaderHidden(true); // hide header
50 m_memory
.setContextMenuPolicy(Qt::NoContextMenu
); // defer to parent
53 new QTreeWidgetItem(&m_memory
, QStringList("0x179bf"));
54 m_memoryRowHeight
= m_memory
.visualRect(m_memory
.indexAt(QPoint(0, 0))).height();
58 m_layout
.setSpacing(2);
59 m_layout
.addWidget(&m_expression
, 0);
60 m_layout
.addWidget(&m_memory
, 10);
63 connect(&m_expression
, SIGNAL(activated(const QString
&)),
64 this, SLOT(slotNewExpression(const QString
&)));
65 connect(m_expression
.lineEdit(), SIGNAL(returnPressed()),
66 this, SLOT(slotNewExpression()));
67 connect(m_memory
.verticalScrollBar(), SIGNAL(valueChanged(int)),
68 this, SLOT(verticalScrollBarMoved(int)));
69 connect(m_memory
.verticalScrollBar(), SIGNAL(rangeChanged(int, int)),
70 this, SLOT(verticalScrollBarRangeChanged(int, int)));
74 pAction
= m_popup
.addAction(i18n("B&ytes"));
75 pAction
->setData(MDTbyte
);
76 pAction
= m_popup
.addAction(i18n("Halfwords (&2 Bytes)"));
77 pAction
->setData(MDThalfword
);
78 pAction
= m_popup
.addAction(i18n("Words (&4 Bytes)"));
79 pAction
->setData(MDTword
);
80 pAction
= m_popup
.addAction(i18n("Giantwords (&8 Bytes)"));
81 pAction
->setData(MDTgiantword
);
82 m_popup
.addSeparator();
83 pAction
= m_popup
.addAction(i18n("He&xadecimal"));
84 pAction
->setData(MDThex
);
85 pAction
= m_popup
.addAction(i18n("Signed &decimal"));
86 pAction
->setData(MDTsigned
);
87 pAction
= m_popup
.addAction(i18n("&Unsigned decimal"));
88 pAction
->setData(MDTunsigned
);
89 pAction
= m_popup
.addAction(i18n("&Octal"));
90 pAction
->setData(MDToctal
);
91 pAction
= m_popup
.addAction(i18n("&Binary"));
92 pAction
->setData(MDTbinary
);
93 pAction
= m_popup
.addAction(i18n("&Addresses"));
94 pAction
->setData(MDTaddress
);
95 pAction
= m_popup
.addAction(i18n("&Character"));
96 pAction
->setData(MDTchar
);
97 pAction
= m_popup
.addAction(i18n("&Floatingpoint"));
98 pAction
->setData(MDTfloat
);
99 pAction
= m_popup
.addAction(i18n("&Strings"));
100 pAction
->setData(MDTstring
);
101 pAction
= m_popup
.addAction(i18n("&Instructions"));
102 pAction
->setData(MDTinsn
);
103 connect(&m_popup
, SIGNAL(triggered(QAction
*)), this, SLOT(slotTypeChange(QAction
*)));
106 MemoryWindow::~MemoryWindow()
110 void MemoryWindow::requestMemoryDump(const QString
&expr
)
113 * Memory dump same length for all cases
114 * Length of memory dump to request is n_rows (window size / row_height) * column_size
120 * Strings and instructions format are column size 1
122 int nrows
= m_memory
.height() / m_memoryRowHeight
;
123 unsigned int colum_size
= 1;
124 unsigned int format
= m_format
& MDTformatmask
;
126 if (m_dumpMemRegionEnd
) {
130 /* Move to driver ???? */
131 if (!(format
== MDTinsn
|| format
== MDTstring
)) {
132 switch(m_format
& MDTsizemask
) {
145 unsigned request_length
= nrows
* colum_size
* 2;
146 m_dumpLength
+= request_length
;
147 m_debugger
->setMemoryExpression(m_expression
.lineEdit()->text(), m_dumpLength
, expr
, request_length
);
150 void MemoryWindow::verticalScrollBarRangeChanged(int min
, int max
)
152 if (min
== 0 && max
== 0 && !m_dumpLastAddr
.isEmpty()) {
153 requestMemoryDump(m_dumpLastAddr
.asString());
157 void MemoryWindow::verticalScrollBarMoved(int value
)
159 int scrollmax
= m_memory
.verticalScrollBar()->maximum();
160 if (value
== scrollmax
&& !m_dumpLastAddr
.isEmpty()) {
161 requestMemoryDump(m_dumpLastAddr
.asString());
165 void MemoryWindow::contextMenuEvent(QContextMenuEvent
* ev
)
167 m_popup
.popup(ev
->globalPos());
171 void MemoryWindow::slotNewExpression()
173 slotNewExpression(m_expression
.lineEdit()->text());
176 void MemoryWindow::slotNewExpression(const QString
& newText
)
178 QString text
= newText
.simplified();
180 // see if the string is in the list
181 // (note: must count downwards because of removeItem!)
182 for (int i
= m_expression
.count()-1; i
>= 0; i
--)
184 if (m_expression
.itemText(i
) == text
) {
186 // look up the format that was used last time for this expr
187 QMap
<QString
,unsigned>::iterator pFormat
= m_formatCache
.find(text
);
188 if (pFormat
!= m_formatCache
.end()) {
190 m_debugger
->setMemoryFormat(m_format
);
192 // remove this text, will be inserted at the top
193 m_expression
.removeItem(i
);
196 m_expression
.insertItem(0, text
);
197 m_expression
.setCurrentIndex(0);
199 if (!text
.isEmpty()) {
200 m_formatCache
[text
] = m_format
;
203 displayNewExpression(text
);
206 void MemoryWindow::displayNewExpression(const QString
& expr
)
209 m_dumpMemRegionEnd
= false;
210 m_dumpLastAddr
= DbgAddr
{};
213 if (m_memory
.verticalScrollBar())
214 m_memory
.verticalScrollBar()->setValue(0);
217 m_memoryColumnsWidth
.clear();
218 for (int i
= 0; i
< MAX_COL
; i
++) {
219 m_memoryColumnsWidth
.append(0);
222 requestMemoryDump(expr
);
223 m_expression
.setEditText(expr
);
226 void MemoryWindow::slotTypeChange(QAction
* action
)
228 int id
= action
->data().toInt();
231 if (id
& MDTsizemask
)
232 m_format
= (m_format
& ~MDTsizemask
) | id
;
233 if (id
& MDTformatmask
)
234 m_format
= (m_format
& ~MDTformatmask
) | id
;
235 m_debugger
->setMemoryFormat(m_format
);
237 // change the format in the cache
238 QString expr
= m_expression
.currentText();
239 m_formatCache
[expr
.simplified()] = m_format
;
242 displayNewExpression(expr
);
245 QString
MemoryWindow::parseMemoryDumpLineToAscii(const QString
& line
, bool littleendian
)
247 QStringList hexdata
= line
.split("\t");
249 // Get the size of value from hex str representation length
250 // 0x00 = (4 - 2) / 2 -> 1 byte
251 // 0x0000 = (6 - 2) / 2 -> 2 half-word
252 // 0x00000000 = (10 - 2) / 2 -> 4 word
253 // 0x0000000000000000 = (18 - 2) / 2 -> 8 giant word
255 int len
= (hexdata
[0].length()-2) / 2;
257 for (const auto& hex
: hexdata
)
260 unsigned long long parsedValue
= hex
.toULongLong(&ok
, 16);
263 for (int s
= 0; s
< len
; s
++)
265 unsigned char b
= parsedValue
& 0xFF;
274 std::reverse(dump
.begin(), dump
.end());
275 dumpAscii
+= QString::fromLatin1(dump
);
281 void MemoryWindow::slotNewMemoryDump(const QString
& msg
, const std::list
<MemoryDump
>& memdump
)
283 QFontMetrics
fm(font());
285 if (!msg
.isEmpty()) {
286 for (int i
= MAX_COL
; i
> 0; i
--) {
287 m_memory
.setColumnHidden(i
, true);
289 new QTreeWidgetItem(&m_memory
, QStringList() << msg
);
290 m_memory
.header()->resizeSection(COL_ADDR
, fm
.width(msg
)+10);
294 bool showDumpAscii
= (m_format
& MDTformatmask
) == MDThex
;
296 std::list
<MemoryDump
>::const_iterator md
= memdump
.begin();
298 // show only needed columns
299 QStringList sl
= md
->dump
.split( "\t" );
300 for (int i
= COL_DUMP_ASCII
-1; i
> 0; i
--)
301 m_memory
.setColumnHidden(i
, i
> sl
.count());
303 m_memory
.setColumnHidden(COL_DUMP_ASCII
, !showDumpAscii
);
305 for (; md
!= memdump
.end(); ++md
)
307 QString addr
= md
->address
.asString() + " " + md
->address
.fnoffs
;
308 QStringList sl
= md
->dump
.split( "\t" );
310 if (fm
.width(addr
) > m_memoryColumnsWidth
[COL_ADDR
]) {
311 m_memoryColumnsWidth
[COL_ADDR
] = fm
.width(addr
);
314 QTreeWidgetItem
* line
= nullptr;
315 QList
<QTreeWidgetItem
*> items
= m_memory
.findItems(addr
, Qt::MatchExactly
, 0);
316 if (items
.count() != 0) {
319 if (!line
) { // line not found in memory view, append new one
320 for (int i
= 0; i
< sl
.count(); i
++) {
321 if (fm
.width(sl
[i
]) > m_memoryColumnsWidth
[i
+1]) {
322 m_memoryColumnsWidth
[i
+1] = fm
.width(sl
[i
]);
326 line
= new QTreeWidgetItem(&m_memory
, QStringList(addr
) << sl
);
327 m_dumpLastAddr
= md
->address
;
329 m_dumpMemRegionEnd
= true;
330 } else { // line found in memory view updated it
331 for (int i
= 0; i
< sl
.count(); i
++) {
332 if (fm
.width(sl
[i
]) > m_memoryColumnsWidth
[i
+1]) {
333 m_memoryColumnsWidth
[i
+1] = fm
.width(sl
[i
]);
335 bool changed
= i
< (line
->columnCount() - showDumpAscii
) && sl
[i
] != line
->text(i
+1);
336 line
->setForeground(i
+1, changed
? QBrush(QColor(Qt::red
)) : palette().text());
337 line
->setText(i
+1, sl
[i
]);
342 QString dumpAscii
= parseMemoryDumpLineToAscii(md
->dump
, md
->littleendian
);
344 * Add space padding to have always same number of chars in all lines.
345 * Workaround necessary to display correctly aligned when Qt::AlignRight.
347 int dumpAsciiFixedLen
= ((m_format
& MDTsizemask
) == MDTbyte
) ? 8:16;
348 if (dumpAscii
.size() < dumpAsciiFixedLen
) {
349 dumpAscii
+= QString().sprintf("%*c", dumpAsciiFixedLen
- dumpAscii
.size(), ' ');
351 line
->setText(COL_DUMP_ASCII
, dumpAscii
);
352 line
->setTextAlignment(COL_DUMP_ASCII
, Qt::AlignRight
);
353 if (fm
.width(dumpAscii
) > m_memoryColumnsWidth
[COL_DUMP_ASCII
]) {
354 m_memoryColumnsWidth
[COL_DUMP_ASCII
] = fm
.width(dumpAscii
);
359 // resize to longest string and add padding
361 m_memory
.header()->resizeSection(COL_ADDR
, m_memoryColumnsWidth
[COL_ADDR
] + padding
);
362 m_memory
.header()->resizeSection(COL_DUMP_ASCII
, m_memoryColumnsWidth
[COL_DUMP_ASCII
] + padding
);
363 for (int i
= 1; i
< COL_DUMP_ASCII
; i
++) {
364 m_memory
.header()->resizeSection(i
, m_memoryColumnsWidth
[i
] + 10);
368 static const char MemoryGroup
[] = "Memory";
369 static const char NumExprs
[] = "NumExprs";
370 static const char ExpressionFmt
[] = "Expression%d";
371 static const char FormatFmt
[] = "Format%d";
372 static const char ColumnWidths
[] = "ColumnWidths";
374 void MemoryWindow::saveProgramSpecific(KConfigBase
* config
)
376 KConfigGroup g
= config
->group(MemoryGroup
);
378 int numEntries
= m_expression
.count();
379 g
.writeEntry(NumExprs
, numEntries
);
382 for (int i
= 0; i
< numEntries
;) {
383 QString text
= m_expression
.itemText(i
);
384 i
++; /* entries are counted 1-based */
385 exprEntry
.sprintf(ExpressionFmt
, i
);
386 fmtEntry
.sprintf(FormatFmt
, i
);
387 g
.writeEntry(exprEntry
, text
);
388 QMap
<QString
,unsigned>::iterator pFormat
= m_formatCache
.find(text
);
389 unsigned fmt
= pFormat
!= m_formatCache
.end() ? *pFormat
: MDTword
| MDThex
;
390 g
.writeEntry(fmtEntry
, fmt
);
395 for (int i
= 0; i
< 2; i
++) {
396 int w
= m_memory
.columnWidth(i
);
399 g
.writeEntry(ColumnWidths
, widths
);
402 void MemoryWindow::restoreProgramSpecific(KConfigBase
* config
)
404 KConfigGroup g
= config
->group(MemoryGroup
);
406 int numEntries
= g
.readEntry(NumExprs
, 0);
407 m_expression
.clear();
411 // entries are counted 1-based
412 for (int i
= 1; i
<= numEntries
; i
++) {
413 exprEntry
.sprintf(ExpressionFmt
, i
);
414 fmtEntry
.sprintf(FormatFmt
, i
);
415 QString expr
= g
.readEntry(exprEntry
, QString());
416 unsigned fmt
= g
.readEntry(fmtEntry
, MDTword
| MDThex
);
417 m_expression
.addItem(expr
);
418 m_formatCache
[expr
] = fmt
& (MDTsizemask
| MDTformatmask
);
421 // initialize with top expression
422 if (numEntries
> 0) {
423 m_expression
.setCurrentIndex(0);
424 QString expr
= m_expression
.itemText(0);
425 m_format
= m_formatCache
[expr
];
426 m_debugger
->setMemoryFormat(m_format
);
427 displayNewExpression(expr
);
431 QList
<int> widths
= g
.readEntry(ColumnWidths
, QList
<int>());
432 QList
<int>::iterator w
= widths
.begin();
433 for (int i
= 0; i
< 2 && w
!= widths
.end(); ++i
, ++w
) {
434 m_memory
.setColumnWidth(i
, *w
);
439 #include "memwindow.moc"