|
11 | 11 | #include "fontsettings.h" |
12 | 12 |
|
13 | 13 |
|
14 | | -GenericStringsModel::GenericStringsModel(QWidget* parent, BinaryViewRef data) : QAbstractItemModel(parent) |
| 14 | +GenericStringsModel::GenericStringsModel(QWidget* parent, BinaryViewRef data) : QAbstractItemModel(parent), BinaryDataNotification(StringUpdates) |
15 | 15 | { |
16 | 16 | m_data = data; |
17 | 17 | m_totalCols = 3; |
18 | 18 | m_sortCol = 0; |
19 | 19 | m_sortOrder = Qt::AscendingOrder; |
20 | | - m_allEntries = data->GetStrings(); |
21 | | - m_entries = m_allEntries; |
| 20 | + |
| 21 | + m_updateTimer = new QTimer(this); |
| 22 | + m_updateTimer->setInterval(500); |
| 23 | + connect(m_updateTimer, &QTimer::timeout, this, &GenericStringsModel::updateModel); |
| 24 | + connect(this, &GenericStringsModel::updateTimerOnUIThread, this, [=, this]() { |
| 25 | + updateTimer(m_needsUpdate); |
| 26 | + }, Qt::QueuedConnection); |
| 27 | + |
| 28 | + m_data->RegisterNotification(this); |
| 29 | + |
| 30 | + updateModel(); |
| 31 | +} |
| 32 | + |
| 33 | + |
| 34 | +GenericStringsModel::~GenericStringsModel() |
| 35 | +{ |
| 36 | + m_data->UnregisterNotification(this); |
22 | 37 | } |
23 | 38 |
|
24 | 39 |
|
@@ -168,22 +183,119 @@ void GenericStringsModel::sort(int col, Qt::SortOrder order) |
168 | 183 | } |
169 | 184 |
|
170 | 185 |
|
171 | | -void GenericStringsModel::setFilter(const std::string& filterText) |
| 186 | +void GenericStringsModel::applyFilter() |
172 | 187 | { |
173 | | - beginResetModel(); |
174 | 188 | m_entries.clear(); |
175 | 189 | for (auto& entry : m_allEntries) |
176 | 190 | { |
177 | 191 | auto s = stringRefToQString(entry).toStdString(); |
178 | | - |
179 | | - if (FilteredView::match(s, filterText)) |
| 192 | + |
| 193 | + if (FilteredView::match(s, m_filter)) |
180 | 194 | m_entries.push_back(entry); |
181 | 195 | } |
182 | 196 | performSort(m_sortCol, m_sortOrder); |
| 197 | +} |
| 198 | + |
| 199 | + |
| 200 | +void GenericStringsModel::setFilter(const std::string& filterText) |
| 201 | +{ |
| 202 | + m_filter = filterText; |
| 203 | + beginResetModel(); |
| 204 | + applyFilter(); |
183 | 205 | endResetModel(); |
184 | 206 | } |
185 | 207 |
|
186 | 208 |
|
| 209 | +void GenericStringsModel::updateModel() |
| 210 | +{ |
| 211 | + if (!m_needsUpdate) |
| 212 | + return; |
| 213 | + |
| 214 | + setNeedsUpdate(false); |
| 215 | + beginResetModel(); |
| 216 | + m_allEntries = m_data->GetStrings(); |
| 217 | + applyFilter(); |
| 218 | + endResetModel(); |
| 219 | +} |
| 220 | + |
| 221 | + |
| 222 | +void GenericStringsModel::setNeedsUpdate(bool needed) |
| 223 | +{ |
| 224 | + if (m_needsUpdate.exchange(needed) == needed) |
| 225 | + return; |
| 226 | + |
| 227 | + updateTimer(needed); |
| 228 | +} |
| 229 | + |
| 230 | + |
| 231 | +void GenericStringsModel::updateTimer(bool needsUpdate) |
| 232 | +{ |
| 233 | + if (needsUpdate && !m_updateTimer->isActive()) |
| 234 | + m_updateTimer->start(); |
| 235 | + if (!needsUpdate && m_updateTimer->isActive()) |
| 236 | + m_updateTimer->stop(); |
| 237 | +} |
| 238 | + |
| 239 | + |
| 240 | +void GenericStringsModel::pauseUpdates() |
| 241 | +{ |
| 242 | + m_updatesPaused = true; |
| 243 | + m_dirtyWhilePaused = false; |
| 244 | + setNeedsUpdate(false); |
| 245 | +} |
| 246 | + |
| 247 | + |
| 248 | +void GenericStringsModel::resumeUpdates() |
| 249 | +{ |
| 250 | + m_updatesPaused = false; |
| 251 | + // Only refresh if we got notifications while paused |
| 252 | + if (m_dirtyWhilePaused.exchange(false)) |
| 253 | + setNeedsUpdate(true); |
| 254 | +} |
| 255 | + |
| 256 | + |
| 257 | +void GenericStringsModel::onBinaryViewNotification() |
| 258 | +{ |
| 259 | + if (m_updatesPaused) |
| 260 | + { |
| 261 | + // Track that updates occurred while hidden |
| 262 | + m_dirtyWhilePaused = true; |
| 263 | + return; |
| 264 | + } |
| 265 | + |
| 266 | + // This can be called from any thread so we cannot directly |
| 267 | + // update the timer. Emitting a signal is relatively expensive |
| 268 | + // given how frequently we receive notifications, so we only |
| 269 | + // emit a signal if we didn't already need an update. |
| 270 | + if (!m_needsUpdate.exchange(true)) |
| 271 | + emit updateTimerOnUIThread(); |
| 272 | +} |
| 273 | + |
| 274 | + |
| 275 | +void GenericStringsModel::OnStringFound(BinaryNinja::BinaryView* view, BNStringType type, uint64_t offset, size_t len) |
| 276 | +{ |
| 277 | + onBinaryViewNotification(); |
| 278 | +} |
| 279 | + |
| 280 | + |
| 281 | +void GenericStringsModel::OnStringRemoved(BinaryNinja::BinaryView* view, BNStringType type, uint64_t offset, size_t len) |
| 282 | +{ |
| 283 | + onBinaryViewNotification(); |
| 284 | +} |
| 285 | + |
| 286 | + |
| 287 | +void GenericStringsModel::OnDerivedStringFound(BinaryNinja::BinaryView* view, const BinaryNinja::DerivedString& str) |
| 288 | +{ |
| 289 | + onBinaryViewNotification(); |
| 290 | +} |
| 291 | + |
| 292 | + |
| 293 | +void GenericStringsModel::OnDerivedStringRemoved(BinaryNinja::BinaryView* view, const BinaryNinja::DerivedString& str) |
| 294 | +{ |
| 295 | + onBinaryViewNotification(); |
| 296 | +} |
| 297 | + |
| 298 | + |
187 | 299 | StringsTreeView::StringsTreeView(StringsWidget* parent, TriageView* view, BinaryViewRef data) : QTreeView(parent) |
188 | 300 | { |
189 | 301 | m_data = data; |
@@ -362,6 +474,20 @@ void StringsTreeView::keyPressEvent(QKeyEvent* event) |
362 | 474 | } |
363 | 475 |
|
364 | 476 |
|
| 477 | +void StringsTreeView::showEvent(QShowEvent* event) |
| 478 | +{ |
| 479 | + QTreeView::showEvent(event); |
| 480 | + m_model->resumeUpdates(); |
| 481 | +} |
| 482 | + |
| 483 | + |
| 484 | +void StringsTreeView::hideEvent(QHideEvent* event) |
| 485 | +{ |
| 486 | + QTreeView::hideEvent(event); |
| 487 | + m_model->pauseUpdates(); |
| 488 | +} |
| 489 | + |
| 490 | + |
365 | 491 | StringsWidget::StringsWidget(QWidget* parent, TriageView* view, BinaryViewRef data) : QWidget(parent) |
366 | 492 | { |
367 | 493 | QVBoxLayout* layout = new QVBoxLayout(); |
|
0 commit comments