@@ -60,10 +60,11 @@ DSCTriageView::DSCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(pare
6060 initStringsTab ();
6161 initCacheInfoTables ();
6262
63- // The string scan is expensive, so the panel starts its load only once the Strings tab is
64- // first shown, and clears its large result set after the tab leaves the screen.
63+ // The string scan and symbol fetch are expensive, so each panel starts its load only once its
64+ // tab is first shown, and clears its large result set after the tab leaves the screen.
6565 connect (m_triageTabs, &SplitTabWidget::currentChanged, this , [this ](QWidget* widget) {
6666 m_stringsPanel->setCurrentTabWidget (widget);
67+ m_symbolsPanel->setCurrentTabWidget (widget);
6768 });
6869
6970 m_layout = new QVBoxLayout (this );
@@ -357,59 +358,31 @@ QWidget* DSCTriageView::initImageTable()
357358void DSCTriageView::initSymbolTable ()
358359{
359360 m_symbolTable = new SymbolTableView (this );
360-
361- // Apply custom column styling
362- m_symbolTable->setItemDelegateForColumn (0 , new AddressColorDelegate (m_symbolTable));
363-
364- auto symbolFilterEdit = new FilterEdit (m_symbolTable);
365- symbolFilterEdit->setPlaceholderText (" Filter symbols" );
366- connect (symbolFilterEdit, &FilterEdit::textChanged, [this , symbolFilterEdit](const QString& filter) {
367- m_symbolTable->setFilter (filter.toStdString (), symbolFilterEdit->getFilterOptions ());
368- });
369- connect (symbolFilterEdit, &FilterEdit::optionsChanged, [this , symbolFilterEdit](FilterOptions options) {
370- m_symbolTable->setFilter (symbolFilterEdit->text ().toStdString (), options);
361+ m_symbolsPanel = new TriageTablePanel (this , m_symbolTable, " Filter symbols" , " symbols" );
362+ m_symbolsPanel->setLoader ([this ] { return startSymbolLoad (); });
363+ m_symbolsPanel->setClearHandler ([this ] {
364+ // Discard an in-flight symbol fetch rather than letting its results repopulate the
365+ // cleared table.
366+ if (m_symbolsWatcher)
367+ {
368+ m_symbolsWatcher->disconnect ();
369+ m_symbolsWatcher->deleteLater ();
370+ }
371371 });
372372
373- auto loadSymbolImageButton = new QPushButton ();
373+ m_symbolsPanel->addFilterToggle (" :/icons/images/folder.png" , " Match Image Names" ,
374+ [this ](bool checked) { m_symbolTable->symbolsModel ()->setMatchImageNames (checked); });
375+
376+ auto loadSymbolImageButton = m_symbolsPanel->addSelectionButton (" Load Image" );
374377 connect (loadSymbolImageButton, &QPushButton::clicked, [this ](bool ) {
375378 auto selected = m_symbolTable->selectionModel ()->selectedRows ();
376379 std::vector<uint64_t > addresses;
377380 for (const auto & row : selected)
378- addresses.push_back (row.data (). toString (). toULongLong ( nullptr , 16 ) );
381+ addresses.push_back (m_symbolTable-> getSymbolAtRow ( row.row ()). address );
379382 loadImagesWithAddr (addresses);
380383 });
381- loadSymbolImageButton->setText (" Load Image" );
382-
383- // Shows the current selected rows image name.
384- auto currentImageLabel = new QLabel (this );
385- currentImageLabel->setText (" " );
386- currentImageLabel->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
387- connect (m_symbolTable->selectionModel (), &QItemSelectionModel::currentRowChanged, this , [this , currentImageLabel](const QModelIndex ¤t, const QModelIndex &) {
388- auto symbol = m_symbolTable->getSymbolAtRow (current.row ());
389- auto controller = SharedCacheController::GetController (*this ->m_data );
390- if (!controller)
391- return ;
392- auto image = controller->GetImageContaining (symbol.address );
393- if (image)
394- currentImageLabel->setText (" Image: " + QString::fromStdString (image->name ));
395- else
396- currentImageLabel->setText (" " );
397- });
398-
399- auto symbolFooterLayout = new QHBoxLayout;
400- symbolFooterLayout->addWidget (loadSymbolImageButton);
401- symbolFooterLayout->addWidget (currentImageLabel);
402- symbolFooterLayout->setAlignment (Qt::AlignLeft);
403-
404- auto symbolLayout = new QVBoxLayout;
405- symbolLayout->addWidget (symbolFilterEdit);
406- symbolLayout->addWidget (m_symbolTable);
407- symbolLayout->addLayout (symbolFooterLayout);
408-
409- auto symbolWidget = new QWidget;
410- symbolWidget->setLayout (symbolLayout);
411384
412- connect (m_symbolTable, &SymbolTableView::activated, this , [=, this ](const QModelIndex& index){
385+ connect (m_symbolTable, &SymbolTableView::activated, this , [this ](const QModelIndex& index){
413386 auto symbol = m_symbolTable->getSymbolAtRow (index.row ());
414387
415388 auto controller = SharedCacheController::GetController (*this ->m_data );
@@ -426,21 +399,43 @@ void DSCTriageView::initSymbolTable()
426399 return ;
427400 }
428401
429- auto dialog = new QMessageBox (this );
430- dialog->setText (" Load " + QString::fromStdString (image->name ) + " ?" );
431- dialog->setStandardButtons (QMessageBox::Yes | QMessageBox::No);
402+ promptToLoadImage (image->name , image->headerAddress , symbol.address );
403+ });
432404
433- connect (dialog, &QMessageBox::buttonClicked, this , [=, this ](QAbstractButton* button)
434- {
435- if (button == dialog->button (QMessageBox::Yes))
436- loadImagesWithAddr ({image->headerAddress }, false , symbol.address );
437- });
405+ m_triageTabs->addTab (m_symbolsPanel, " Symbols" );
406+ m_triageTabs->setCanCloseTab (m_symbolsPanel, false );
407+ }
438408
439- dialog->exec ();
440- });
441409
442- m_triageTabs->addTab (symbolWidget, " Symbols" );
443- m_triageTabs->setCanCloseTab (symbolWidget, false );
410+ bool DSCTriageView::startSymbolLoad ()
411+ {
412+ // The controller is not available until view init has finished. Retry on the next activation.
413+ auto controller = SharedCacheController::GetController (*m_data);
414+ if (!controller)
415+ return false ;
416+
417+ m_symbolTable->setNameSources (*controller);
418+ m_symbolsPanel->statusLabel ()->setText (" Loading…" );
419+
420+ typedef std::vector<CacheSymbol> SymbolList;
421+ QPointer<QFutureWatcher<SymbolList>> watcher = new QFutureWatcher<SymbolList>(this );
422+ m_symbolsWatcher = watcher;
423+ connect (watcher, &QFutureWatcher<SymbolList>::finished, this , [watcher, this ]() {
424+ if (!watcher)
425+ return ;
426+ m_symbolTable->symbolsModel ()->appendSymbols (watcher->result ());
427+ m_symbolsPanel->finishLoad ();
428+ watcher->deleteLater ();
429+ });
430+ QFuture<SymbolList> future = QtConcurrent::run ([controller]() { return controller->GetSymbols (); });
431+ watcher->setFuture (future);
432+ connect (this , &QObject::destroyed, this , [watcher]() {
433+ if (watcher && watcher->isRunning ()) {
434+ watcher->cancel ();
435+ watcher->waitForFinished ();
436+ }
437+ });
438+ return true ;
444439}
445440
446441
@@ -537,13 +532,15 @@ void DSCTriageView::showEvent(QShowEvent* event)
537532{
538533 QWidget::showEvent (event);
539534 m_stringsPanel->setViewVisible (true );
535+ m_symbolsPanel->setViewVisible (true );
540536}
541537
542538
543539void DSCTriageView::hideEvent (QHideEvent* event)
544540{
545541 QWidget::hideEvent (event);
546542 m_stringsPanel->setViewVisible (false );
543+ m_symbolsPanel->setViewVisible (false );
547544}
548545
549546
@@ -863,6 +860,8 @@ void DSCTriageView::RefreshData()
863860 // TODO: This should use `QSortFilterProxyModel`, but that's a bigger change.
864861 m_mappingTable->setSortingEnabled (true );
865862
866-
867- m_symbolTable->populateSymbols (*m_data);
863+ // Symbols and strings are loaded lazily when their tabs are shown.
864+ // Discard any loaded content so the reload picks up the refreshed cache information.
865+ m_symbolsPanel->resetContent ();
866+ m_stringsPanel->resetContent ();
868867}
0 commit comments