forked from len0rd/rockbox
This big commit port qeditor from v1 to v2 register file format. Although the display code was much simplified, the edit code had to be rewritten. The new code also brings many improvement to the register display widget. The new code also compiles with both Qt4 and Qt5, although it is recommended to use Qt5 to get some improvements, especially in the layout of editor. Change-Id: I24633ac37a144f25d9e705b565654269ec9cfbd3
400 lines
12 KiB
C++
400 lines
12 KiB
C++
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2014 by Amaury Pouly
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include "regtab.h"
|
|
|
|
#include <QSizePolicy>
|
|
#include <QStringBuilder>
|
|
#include <QFileDialog>
|
|
#include <QDebug>
|
|
#include <QStyle>
|
|
#include <QMessageBox>
|
|
#include "backend.h"
|
|
#include "analyser.h"
|
|
#include "regdisplaypanel.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
enum
|
|
{
|
|
NodeInstRole = Qt::UserRole,
|
|
};
|
|
|
|
}
|
|
|
|
/**
|
|
* EmptyRegTabPanel
|
|
*/
|
|
EmptyRegTabPanel::EmptyRegTabPanel(QWidget *parent)
|
|
:QWidget(parent)
|
|
{
|
|
QVBoxLayout *l = new QVBoxLayout;
|
|
l->addStretch();
|
|
setLayout(l);
|
|
}
|
|
|
|
void EmptyRegTabPanel::AllowWrite(bool en)
|
|
{
|
|
Q_UNUSED(en);
|
|
}
|
|
|
|
QWidget *EmptyRegTabPanel::GetWidget()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
|
|
/**
|
|
* RegTab
|
|
*/
|
|
|
|
RegTab::RegTab(Backend *backend, QWidget *parent)
|
|
:QSplitter(parent), m_backend(backend)
|
|
{
|
|
QWidget *left = new QWidget;
|
|
this->addWidget(left);
|
|
this->setStretchFactor(0, 1);
|
|
QVBoxLayout *left_layout = new QVBoxLayout;
|
|
left->setLayout(left_layout);
|
|
|
|
QGroupBox *top_group = new QGroupBox("SOC selection");
|
|
QVBoxLayout *top_group_layout = new QVBoxLayout;
|
|
m_soc_selector = new QComboBox;
|
|
top_group_layout->addWidget(m_soc_selector);
|
|
top_group->setLayout(top_group_layout);
|
|
|
|
m_reg_tree = new QTreeWidget();
|
|
m_reg_tree->setColumnCount(1);
|
|
m_reg_tree->setHeaderLabel(QString("Name"));
|
|
|
|
m_analysers_list = new QListWidget;
|
|
|
|
m_type_selector = new QTabWidget;
|
|
m_type_selector->addTab(m_reg_tree, "Registers");
|
|
m_type_selector->addTab(m_analysers_list, "Analyzers");
|
|
m_type_selector->setTabPosition(QTabWidget::West);
|
|
|
|
left_layout->addWidget(top_group);
|
|
left_layout->addWidget(m_type_selector);
|
|
|
|
m_right_panel = new QVBoxLayout;
|
|
QGroupBox *data_sel_group = new QGroupBox("Data selection");
|
|
QHBoxLayout *data_sel_layout = new QHBoxLayout;
|
|
m_backend_selector = new BackendSelector(m_backend, this);
|
|
m_backend_selector->SetNothingMessage("<i>Select a data source to analyse its content.</i>");
|
|
m_readonly_check = new QCheckBox("Read-only");
|
|
m_readonly_check->setCheckState(Qt::Checked);
|
|
m_data_soc_label = new QLabel;
|
|
m_dump = new QPushButton("Dump", this);
|
|
m_dump->setIcon(QIcon::fromTheme("system-run"));
|
|
m_data_sel_reload = new QPushButton(this);
|
|
m_data_sel_reload->setIcon(QIcon::fromTheme("view-refresh"));
|
|
m_data_sel_reload->setToolTip("Reload data");
|
|
data_sel_layout->addWidget(m_backend_selector);
|
|
data_sel_layout->addWidget(m_readonly_check);
|
|
data_sel_layout->addWidget(m_data_soc_label);
|
|
data_sel_layout->addWidget(m_dump);
|
|
data_sel_layout->addWidget(m_data_sel_reload);
|
|
data_sel_group->setLayout(data_sel_layout);
|
|
m_data_soc_label->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
|
|
m_msg = new MessageWidget(this);
|
|
|
|
m_right_panel->addWidget(data_sel_group, 0);
|
|
m_right_panel->addWidget(m_msg, 0);
|
|
m_right_content = 0;
|
|
SetPanel(new EmptyRegTabPanel);
|
|
QWidget *w = new QWidget;
|
|
w->setLayout(m_right_panel);
|
|
this->addWidget(w);
|
|
this->setStretchFactor(1, 2);
|
|
|
|
m_io_backend = m_backend_selector->GetBackend();
|
|
|
|
connect(m_soc_selector, SIGNAL(currentIndexChanged(int)),
|
|
this, SLOT(OnSocChanged(int)));
|
|
connect(m_backend, SIGNAL(OnSocAdded(const SocFileRef&)), this,
|
|
SLOT(OnSocAdded(const SocFileRef&)));
|
|
connect(m_reg_tree, SIGNAL(itemClicked(QTreeWidgetItem*, int)),
|
|
this, SLOT(OnRegItemClicked(QTreeWidgetItem*, int)));
|
|
connect(m_data_soc_label, SIGNAL(linkActivated(const QString&)), this,
|
|
SLOT(OnDataSocActivated(const QString&)));
|
|
connect(m_analysers_list, SIGNAL(itemClicked(QListWidgetItem *)),
|
|
this, SLOT(OnAnalyserClicked(QListWidgetItem *)));
|
|
connect(m_backend_selector, SIGNAL(OnSelect(IoBackend *)),
|
|
this, SLOT(OnBackendSelect(IoBackend *)));
|
|
connect(m_readonly_check, SIGNAL(clicked(bool)), this, SLOT(OnReadOnlyClicked(bool)));
|
|
connect(m_dump, SIGNAL(clicked(bool)), this, SLOT(OnDumpRegs(bool)));
|
|
connect(m_data_sel_reload, SIGNAL(clicked(bool)), this, SLOT(OnBackendReload(bool)));
|
|
connect(m_type_selector, SIGNAL(currentChanged(int)), this, SLOT(OnTypeChanged(int)));
|
|
|
|
m_msg_select_id = SetMessage(MessageWidget::Information,
|
|
"You can browse the registers. Select a data source to analyse the values.");
|
|
m_msg_error_id = 0;
|
|
|
|
QList< SocFileRef > socs = m_backend->GetSocFileList();
|
|
for(int i = 0; i < socs.size(); i++)
|
|
OnSocAdded(socs[i]);
|
|
SetDataSocName("");
|
|
UpdateTabName();
|
|
}
|
|
|
|
QWidget *RegTab::GetWidget()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
RegTab::~RegTab()
|
|
{
|
|
/* backend will be deleted by backend selector */
|
|
}
|
|
|
|
bool RegTab::Quit()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void RegTab::SetDataSocName(const QString& socname)
|
|
{
|
|
if(socname.size() != 0)
|
|
{
|
|
m_data_soc_label->setText("<a href=\"" + socname + "\">" + socname + "</a>");
|
|
m_data_soc_label->setTextFormat(Qt::RichText);
|
|
m_data_soc_label->show();
|
|
}
|
|
else
|
|
{
|
|
m_data_soc_label->setText("");
|
|
m_data_soc_label->hide();
|
|
}
|
|
}
|
|
|
|
void RegTab::OnDataSocActivated(const QString& str)
|
|
{
|
|
int index = m_soc_selector->findText(str);
|
|
if(index != -1)
|
|
m_soc_selector->setCurrentIndex(index);
|
|
else if(str.size() > 0)
|
|
{
|
|
m_msg_error_id = SetMessage(MessageWidget::Error,
|
|
"Description file for this SoC is not available.");
|
|
SetPanel(new EmptyRegTabPanel);
|
|
}
|
|
}
|
|
|
|
void RegTab::UpdateTabName()
|
|
{
|
|
/* do it the ugly way: try to cast to the different possible backends */
|
|
FileIoBackend *file = dynamic_cast< FileIoBackend* >(m_io_backend);
|
|
#ifdef HAVE_HWSTUB
|
|
HWStubIoBackend *hwstub = dynamic_cast< HWStubIoBackend* >(m_io_backend);
|
|
#endif
|
|
if(file)
|
|
{
|
|
QFileInfo info(file->GetFileName());
|
|
SetTabName(info.fileName());
|
|
}
|
|
#ifdef HAVE_HWSTUB
|
|
else if(hwstub)
|
|
{
|
|
HWStubDevice *dev = hwstub->GetDevice();
|
|
SetTabName(QString("HWStub %1.%2").arg(dev->GetBusNumber())
|
|
.arg(dev->GetDevAddress()));
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
SetTabName("Register Tab");
|
|
}
|
|
}
|
|
|
|
void RegTab::OnBackendSelect(IoBackend *backend)
|
|
{
|
|
/* Hide "Please select two SoC" and errors message */
|
|
HideMessage(m_msg_select_id);
|
|
HideMessage(m_msg_error_id);
|
|
m_io_backend = backend;
|
|
SetReadOnlyIndicator();
|
|
SetDataSocName(m_io_backend->GetSocName());
|
|
OnDataSocActivated(m_io_backend->GetSocName());
|
|
OnDataChanged();
|
|
UpdateTabName();
|
|
}
|
|
|
|
void RegTab::SetReadOnlyIndicator()
|
|
{
|
|
if(m_io_backend->IsReadOnly())
|
|
m_readonly_check->setCheckState(Qt::Checked);
|
|
}
|
|
|
|
void RegTab::OnDataChanged()
|
|
{
|
|
OnRegItemClicked(m_reg_tree->currentItem(), 0);
|
|
}
|
|
|
|
void RegTab::OnRegItemClicked(QTreeWidgetItem *current, int col)
|
|
{
|
|
Q_UNUSED(col);
|
|
if(current == 0)
|
|
return;
|
|
DisplayNode(NodeInst(current));
|
|
}
|
|
|
|
void RegTab::OnAnalyserClicked(QListWidgetItem *current)
|
|
{
|
|
if(current == 0)
|
|
return;
|
|
AnalyserFactory *ana = AnalyserFactory::GetAnalyserByName(current->text());
|
|
SetPanel(ana->Create(m_cur_soc, m_io_backend));
|
|
}
|
|
|
|
void RegTab::DisplayNode(const soc_desc::node_inst_t& ref)
|
|
{
|
|
if(ref.node().is_root())
|
|
SetPanel(new SocDisplayPanel(this, m_io_backend, ref.soc()));
|
|
else if(ref.node().reg().valid())
|
|
SetPanel(new RegDisplayPanel(this, m_io_backend, ref));
|
|
else
|
|
SetPanel(new NodeDisplayPanel(this, m_io_backend, ref));
|
|
}
|
|
|
|
int RegTab::SetMessage(MessageWidget::MessageType type, const QString& msg)
|
|
{
|
|
return m_msg->SetMessage(type, msg);
|
|
}
|
|
|
|
void RegTab::HideMessage(int id)
|
|
{
|
|
m_msg->HideMessage(id);
|
|
}
|
|
|
|
void RegTab::SetPanel(RegTabPanel *panel)
|
|
{
|
|
delete m_right_content;
|
|
m_right_content = panel;
|
|
m_right_content->AllowWrite(m_readonly_check->checkState() == Qt::Unchecked);
|
|
m_right_panel->addWidget(m_right_content->GetWidget(), 1);
|
|
}
|
|
|
|
void RegTab::OnSocAdded(const SocFileRef& ref)
|
|
{
|
|
soc_desc::soc_ref_t soc = ref.GetSocFile()->GetSocRef();
|
|
QVariant v;
|
|
v.setValue(soc);
|
|
m_soc_selector->addItem(QString::fromStdString(soc.get()->name), v);
|
|
}
|
|
|
|
QTreeWidgetItem *RegTab::MakeNode(const soc_desc::node_inst_t& inst, const QString& s)
|
|
{
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(s));
|
|
item->setData(0, NodeInstRole, QVariant::fromValue(inst));
|
|
return item;
|
|
}
|
|
|
|
soc_desc::node_inst_t RegTab::NodeInst(QTreeWidgetItem *item)
|
|
{
|
|
return item->data(0, NodeInstRole).value< soc_desc::node_inst_t >();
|
|
}
|
|
|
|
void RegTab::FillSubTree(QTreeWidgetItem *item)
|
|
{
|
|
std::vector< soc_desc::node_inst_t > list = NodeInst(item).children();
|
|
for(size_t i = 0; i < list.size(); i++)
|
|
{
|
|
QString name = QString::fromStdString(list[i].name());
|
|
if(list[i].is_indexed())
|
|
name = QString("%1[%2]").arg(name).arg(list[i].index());
|
|
QTreeWidgetItem *child = MakeNode(list[i], name);
|
|
FillSubTree(child);
|
|
item->addChild(child);
|
|
}
|
|
}
|
|
|
|
void RegTab::FillRegTree()
|
|
{
|
|
QTreeWidgetItem *soc_item = MakeNode(m_cur_soc.root_inst(),
|
|
QString::fromStdString(m_cur_soc.get()->name));
|
|
FillSubTree(soc_item);
|
|
m_reg_tree->addTopLevelItem(soc_item);
|
|
m_reg_tree->expandItem(soc_item);
|
|
}
|
|
|
|
void RegTab::FillAnalyserList()
|
|
{
|
|
m_analysers_list->clear();
|
|
m_analysers_list->addItems(AnalyserFactory::GetAnalysersForSoc(
|
|
QString::fromStdString(m_cur_soc.get()->name)));
|
|
}
|
|
|
|
void RegTab::OnSocChanged(int index)
|
|
{
|
|
if(index == -1)
|
|
return;
|
|
m_reg_tree->clear();
|
|
m_cur_soc = m_soc_selector->itemData(index).value< soc_desc::soc_ref_t >();
|
|
FillRegTree();
|
|
FillAnalyserList();
|
|
}
|
|
|
|
void RegTab::OnReadOnlyClicked(bool checked)
|
|
{
|
|
if(m_io_backend->IsReadOnly())
|
|
return SetReadOnlyIndicator();
|
|
m_right_content->AllowWrite(!checked);
|
|
}
|
|
|
|
void RegTab::OnDumpRegs(bool c)
|
|
{
|
|
Q_UNUSED(c);
|
|
QFileDialog *fd = new QFileDialog(this);
|
|
fd->setAcceptMode(QFileDialog::AcceptSave);
|
|
QStringList filters;
|
|
filters << "Textual files (*.txt)";
|
|
filters << "All files (*)";
|
|
fd->setNameFilters(filters);
|
|
fd->setDirectory(Settings::Get()->value("regtab/loaddatadir", QDir::currentPath()).toString());
|
|
if(!fd->exec())
|
|
return;
|
|
QStringList filenames = fd->selectedFiles();
|
|
Settings::Get()->setValue("regtab/loaddatadir", fd->directory().absolutePath());
|
|
BackendHelper bh(m_io_backend, m_cur_soc);
|
|
if(!bh.DumpAllRegisters(filenames[0]))
|
|
{
|
|
QMessageBox::warning(this, "The register dump was not saved",
|
|
"There was an error when dumping the registers");
|
|
}
|
|
}
|
|
|
|
void RegTab::OnBackendReload(bool c)
|
|
{
|
|
Q_UNUSED(c);
|
|
m_io_backend->Reload();
|
|
OnDataChanged();
|
|
}
|
|
|
|
void RegTab::OnTypeChanged(int index)
|
|
{
|
|
if(index == -1)
|
|
return;
|
|
if(index == 0) /* registers */
|
|
OnRegItemClicked(m_reg_tree->currentItem(), 0);
|
|
else if(index == 1) /* analysers */
|
|
OnAnalyserClicked(m_analysers_list->currentItem());
|
|
}
|