1 /*
2 This file is part of the Okteta Kasten module, made within the KDE community.
3
4 SPDX-FileCopyrightText: 2006-2009 Friedrich W. H. Kossebau <kossebau@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9 #include "podtableview.hpp"
10
11 // controller
12 #include "podtablemodel.hpp"
13 #include "poddelegate.hpp"
14 #include "poddecodertool.hpp"
15 // KF
16 #include <KComboBox>
17 #include <KLocalizedString>
18 #include <KMessageBox>
19 // Qt
20 #include <QLabel>
21 #include <QLayout>
22 #include <QCheckBox>
23 #include <QTreeView>
24 #include <QHeaderView>
25 #include <QFocusEvent>
26 #include <QFontMetrics>
27
28 namespace Kasten {
29
PODTableView(PODDecoderTool * tool,QWidget * parent)30 PODTableView::PODTableView(PODDecoderTool* tool, QWidget* parent)
31 : QWidget(parent)
32 , mTool(tool)
33 {
34 QBoxLayout* baseLayout = new QVBoxLayout(this);
35 baseLayout->setContentsMargins(0, 0, 0, 0);
36
37 // table
38 mPODTableModel = new PODTableModel(mTool, this);
39 mPODTableView = new QTreeView(this);
40 mPODTableView->setObjectName(QStringLiteral("PODTable"));
41 mPODTableView->setRootIsDecorated(false);
42 mPODTableView->setAlternatingRowColors(true);
43 mPODTableView->setItemsExpandable(false);
44 mPODTableView->setUniformRowHeights(true);
45 mPODTableView->setAllColumnsShowFocus(true);
46 mPODTableView->setItemDelegate(new PODDelegate(mTool, this));
47 mPODTableView->setEditTriggers(QAbstractItemView::EditKeyPressed | QAbstractItemView::DoubleClicked);
48 mPODTableView->setDragEnabled(true);
49 mPODTableView->setSortingEnabled(false);
50 mPODTableView->setModel(mPODTableModel);
51 mPODTableView->installEventFilter(this);
52 QHeaderView* header = mPODTableView->header();
53 header->setSectionResizeMode(QHeaderView::Interactive);
54 header->setStretchLastSection(false);
55 connect(mPODTableView->selectionModel(),
56 &QItemSelectionModel::currentRowChanged,
57 this, &PODTableView::onCurrentRowChanged);
58
59 baseLayout->addWidget(mPODTableView, 10);
60
61 // settings
62 QBoxLayout* settingsLayout = new QHBoxLayout();
63 settingsLayout->setContentsMargins(0, 0, 0, 0);
64
65 mByteOrderSelection = new KComboBox(this);
66 mByteOrderSelection->addItem(i18nc("@item:inlistbox", "Big-endian")); // add first for index
67 mByteOrderSelection->addItem(i18nc("@item:inlistbox", "Little-endian")); // add second for index
68 mByteOrderSelection->setCurrentIndex(mTool->byteOrder());
69 connect(mByteOrderSelection, QOverload<int>::of(&KComboBox::activated),
70 mTool, &PODDecoderTool::setByteOrder);
71 const QString byteOrderToolTip =
72 i18nc("@info:tooltip",
73 "The byte order to use for decoding the bytes.");
74 mByteOrderSelection->setToolTip(byteOrderToolTip);
75 settingsLayout->addWidget(mByteOrderSelection);
76
77 QLabel* unsignedAsHexLabel = new QLabel(i18nc("@option:check", "Unsigned as hexadecimal:"), this);
78 settingsLayout->addWidget(unsignedAsHexLabel);
79
80 mUnsignedAsHexCheck = new QCheckBox(this);
81 mUnsignedAsHexCheck->setChecked(mTool->isUnsignedAsHex());
82 connect(mUnsignedAsHexCheck, &QCheckBox::toggled,
83 mTool, &PODDecoderTool::setUnsignedAsHex);
84 unsignedAsHexLabel->setBuddy(mUnsignedAsHexCheck);
85 const QString unsignedAsHexToolTip =
86 i18nc("@info:tooltip",
87 "Sets whether the values of the unsigned integer types are shown as hexadecimal instead of as decimal.");
88 unsignedAsHexLabel->setToolTip(unsignedAsHexToolTip);
89 mUnsignedAsHexCheck->setToolTip(unsignedAsHexToolTip);
90 settingsLayout->addWidget(mUnsignedAsHexCheck);
91 settingsLayout->addStretch();
92
93 baseLayout->addLayout(settingsLayout);
94
95 mTool->setDifferentSizeDialog(this);
96
97 // resize to fit width of contents
98 // this is much (!) faster than using setResizeMode(QHeaderView::ResizeToContents)
99 QFont f;
100 QFontMetrics metrics(f);
101 // ideally we should check the width of the longest translated string, but this should be wide enough for most
102 // anyway this is just an initial setting and the width can be changed manually
103 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
104 header->resizeSection(0, metrics.horizontalAdvance(QStringLiteral("Hexadecimal 8-bit")) + 30);
105 header->resizeSection(1, metrics.horizontalAdvance(QStringLiteral("1.01234567890123456789e-111")) + 15);
106 #else
107 header->resizeSection(0, metrics.width(QStringLiteral("Hexadecimal 8-bit")) + 30);
108 header->resizeSection(1, metrics.width(QStringLiteral("1.01234567890123456789e-111")) + 15);
109 #endif
110 }
111
112 PODTableView::~PODTableView() = default;
113
query(int newValueSize,int oldValueSize,int sizeLeft)114 Answer PODTableView::query(int newValueSize, int oldValueSize, int sizeLeft)
115 {
116 Q_UNUSED(sizeLeft);
117
118 Answer answer;
119
120 int messageBoxAnswer;
121 if (newValueSize < oldValueSize) {
122 const QString message =
123 xi18nc("@info",
124 "The new value needs <emphasis>fewer</emphasis> bytes (%1 instead of %2).<nl/>"
125 "Keep the unused bytes or remove them?", newValueSize, oldValueSize);
126
127 const KGuiItem keepGuiItem =
128 KGuiItem(i18nc("@action:button keep the unused bytes",
129 "&Keep"),
130 QString(),
131 i18nc("@info:tooltip",
132 "Keep the unused bytes with their old values."));
133
134 messageBoxAnswer = KMessageBox::warningYesNoCancel(this, message, mTool->title(),
135 keepGuiItem,
136 KStandardGuiItem::remove());
137 } else {
138 const QString message =
139 xi18nc("@info",
140 "The new value needs <emphasis>more</emphasis> bytes (%1 instead of %2).<nl/>"
141 "Overwrite the following bytes or insert new ones as needed?", newValueSize, oldValueSize);
142
143 messageBoxAnswer = KMessageBox::warningYesNoCancel(this, message, mTool->title(),
144 KStandardGuiItem::overwrite(),
145 KStandardGuiItem::insert());
146 }
147
148 answer = (messageBoxAnswer == KMessageBox::Yes) ? Overwrite :
149 (messageBoxAnswer == KMessageBox::No) ? AdaptSize :
150 Cancel;
151 return answer;
152 }
153
eventFilter(QObject * object,QEvent * event)154 bool PODTableView::eventFilter(QObject* object, QEvent* event)
155 {
156 if (object == mPODTableView) {
157 if (event->type() == QEvent::FocusIn) {
158 const QModelIndex current = mPODTableView->selectionModel()->currentIndex();
159 const int podId = current.row();
160 if (current.isValid() && mTool->isApplyable() && !mTool->value(podId).isNull()) {
161 mTool->markPOD(podId);
162 }
163 } else if (event->type() == QEvent::FocusOut) {
164 QWidget* tableViewFocusWidget = mPODTableView->focusWidget();
165 const bool subChildHasFocus = (tableViewFocusWidget != mPODTableView);
166 if (subChildHasFocus) {
167 mPODTableViewFocusChild = tableViewFocusWidget;
168 mPODTableViewFocusChild->installEventFilter(this);
169 } else if (mTool->isApplyable()) {
170 mTool->unmarkPOD();
171 }
172 }
173 } else if (object == mPODTableViewFocusChild) {
174 // TODO: it is only assumed the edit widget will be removed if it loses the focus
175 if (event->type() == QEvent::FocusOut) {
176 if (!mPODTableView->hasFocus() && mTool->isApplyable()) {
177 mTool->unmarkPOD();
178 }
179 mPODTableViewFocusChild->removeEventFilter(this);
180 mPODTableViewFocusChild = nullptr;
181 }
182 }
183
184 return QWidget::eventFilter(object, event);
185 }
186
onCurrentRowChanged(const QModelIndex & current,const QModelIndex & previous)187 void PODTableView::onCurrentRowChanged(const QModelIndex& current, const QModelIndex& previous)
188 {
189 Q_UNUSED(previous)
190
191 if (!mTool->isApplyable()) {
192 return;
193 }
194
195 const int podId = current.row();
196 if (current.isValid() && !mTool->value(podId).isNull()) {
197 mTool->markPOD(podId);
198 } else {
199 mTool->unmarkPOD();
200 }
201 }
202
203 }
204