rockbox/utils/regtools/qeditor/regedit.cpp
Amaury Pouly 5ac0166388 qeditor: port to the new hwstub library and add features
This commit adds support for the version of the hwstub library, which requires
a lot of changes. It also adds some editing features, such as register access
and much better editing of fields using the mouse (double click on a field
to be able to resize and move it).

Change-Id: I3c4e4cc855cb44911c72bc8127bad841b68efe52
2016-04-08 18:46:46 +01:00

1904 lines
No EOL
64 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 "regedit.h"
#include <QFileDialog>
#include <QDebug>
#include <QHeaderView>
#include <QMessageBox>
#include <QInputDialog>
#include <QStandardItemModel>
/**
* EmptyEditPanel
*/
EmptyEditPanel::EmptyEditPanel(QWidget *parent)
:QWidget(parent)
{
}
/**
* SocEditPanel
*/
namespace
{
template< typename T >
void my_remove_at(std::vector< T >& v, size_t at)
{
v.erase(v.begin() + at);
}
enum
{
SocEditPanelDelType = QTableWidgetItem::UserType,
SocEditPanelAddType,
};
}
SocEditPanel::SocEditPanel(const soc_desc::soc_ref_t& ref, QWidget *parent)
:QWidget(parent), m_ref(ref)
{
QLineEdit *name_edit = new QLineEdit(this);
QLineEdit *title_edit = new QLineEdit(this);
QLineEdit *isa_edit = new QLineEdit(this);
QLineEdit *version_edit = new QLineEdit(this);
m_authors_list = new QTableWidget(this);
QGroupBox *authors_group = Misc::EncloseInBox("Authors", m_authors_list);
m_desc_edit = new MyTextEditor(this);
QGroupBox *desc_group = Misc::EncloseInBox("Description", m_desc_edit);
QFormLayout *banner_left_layout = new QFormLayout;
banner_left_layout->addRow("Name:", name_edit);
banner_left_layout->addRow("Title:", title_edit);
banner_left_layout->addRow("Instruction Set:", isa_edit);
banner_left_layout->addRow("Version:", version_edit);
QGroupBox *banner_left_group = new QGroupBox("Information");
banner_left_group->setLayout(banner_left_layout);
QHBoxLayout *banner_layout = new QHBoxLayout;
banner_layout->addWidget(banner_left_group);
banner_layout->addWidget(authors_group);
banner_layout->addStretch(0);
QVBoxLayout *layout = new QVBoxLayout;
layout->addLayout(banner_layout);
layout->addWidget(desc_group);
layout->addStretch(1);
/* fill data */
name_edit->setText(QString::fromStdString(ref.get()->name));
title_edit->setText(QString::fromStdString(ref.get()->title));
isa_edit->setText(QString::fromStdString(ref.get()->isa));
version_edit->setText(QString::fromStdString(ref.get()->version));
m_desc_edit->SetTextHtml(QString::fromStdString(ref.get()->desc));
m_authors_list->setColumnCount(2);
m_authors_list->setHorizontalHeaderItem(0, new QTableWidgetItem(""));
m_authors_list->setHorizontalHeaderItem(1, new QTableWidgetItem("Name"));
m_authors_list->horizontalHeader()->setVisible(false);
m_authors_list->verticalHeader()->setVisible(false);
m_authors_list->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
std::vector< std::string >& authors = ref.get()->author;
m_authors_list->setRowCount(authors.size() + 1);
for(size_t i = 0; i < authors.size(); i++)
{
QTableWidgetItem *item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListRemove), "", SocEditPanelDelType);
item->setToolTip("Remove this author");
item->setFlags(Qt::ItemIsEnabled);
m_authors_list->setItem(i, 0, item);
item = new QTableWidgetItem(QString::fromStdString(authors[i]));
m_authors_list->setItem(i, 1, item);
}
QTableWidgetItem *new_item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListAdd), "", SocEditPanelAddType);
new_item->setFlags(Qt::ItemIsEnabled);
m_authors_list->setItem(authors.size(), 0, new_item);
new_item = new QTableWidgetItem("New author...", QTableWidgetItem::UserType);
new_item->setFlags(Qt::ItemIsEnabled);
QFont font = new_item->font();
font.setItalic(true);
new_item->setFont(font);
m_authors_list->setItem(authors.size(), 1, new_item);
m_authors_list->resizeColumnsToContents();
m_authors_list->horizontalHeader()->setStretchLastSection(true);
connect(name_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnNameEdited(const QString&)));
connect(m_desc_edit, SIGNAL(OnTextChanged()), this, SLOT(OnTextEdited()));
connect(title_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnTitleEdited(const QString&)));
connect(version_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnVersionEdited(const QString&)));
connect(isa_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnIsaEdited(const QString&)));
connect(m_authors_list, SIGNAL(itemActivated(QTableWidgetItem *)), this,
SLOT(OnAuthorActivated(QTableWidgetItem *)));
connect(m_authors_list, SIGNAL(itemChanged(QTableWidgetItem *)), this,
SLOT(OnAuthorChanged(QTableWidgetItem *)));
setLayout(layout);
}
void SocEditPanel::OnNameEdited(const QString& text)
{
m_ref.get()->name = text.toStdString();
OnModified();
}
void SocEditPanel::OnTitleEdited(const QString& text)
{
m_ref.get()->title = text.toStdString();
OnModified();
}
void SocEditPanel::OnVersionEdited(const QString& text)
{
m_ref.get()->version = text.toStdString();
OnModified();
}
void SocEditPanel::OnIsaEdited(const QString& text)
{
m_ref.get()->isa = text.toStdString();
OnModified();
}
void SocEditPanel::OnTextEdited()
{
m_ref.get()->desc = m_desc_edit->GetTextHtml().toStdString();
OnModified();
}
void SocEditPanel::OnAuthorActivated(QTableWidgetItem *item)
{
if(item->type() == SocEditPanelDelType)
{
int row = item->row();
my_remove_at(m_ref.get()->author, row);
m_authors_list->removeRow(row);
OnModified();
}
else if(item->type() == SocEditPanelAddType)
{
int row = m_ref.get()->author.size();
m_ref.get()->author.push_back("Anonymous");
m_authors_list->insertRow(row);
QTableWidgetItem *item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListRemove), "", SocEditPanelDelType);
item->setToolTip("Remove this author");
item->setFlags(Qt::ItemIsEnabled);
m_authors_list->setItem(row, 0, item);
item = new QTableWidgetItem(QString::fromStdString(m_ref.get()->author.back()));
m_authors_list->setItem(row, 1, item);
OnModified();
}
}
void SocEditPanel::OnAuthorChanged(QTableWidgetItem *item)
{
if((size_t)item->row() >= m_ref.get()->author.size())
return;
if(item->column() == 1)
m_ref.get()->author[item->row()] = item->text().toStdString();
OnModified();
}
/**
* NodeInstanceEditPanel
*/
namespace
{
template< typename T >
soc_id_t GetFreshId(const std::vector< T >& list)
{
soc_id_t id = 0;
for(size_t i = 0; i < list.size(); i++)
id = std::max(id, list[i].id);
return id + 1;
}
template< typename T >
int GetIndexById(const std::vector< T >& list, soc_id_t id)
{
for(size_t i = 0; i < list.size(); i++)
if(list[i].id == id)
return i;
return -1;
}
soc_desc::instance_t *GetInstanceById(const soc_desc::node_ref_t& node, soc_id_t id)
{
std::vector< soc_desc::instance_t >& inst_list = node.get()->instance;
for(size_t i = 0; i < inst_list.size(); i++)
if(inst_list[i].id == id)
return &inst_list[i];
return 0;
}
bool RemoveInstanceById(const soc_desc::node_ref_t& node, soc_id_t id)
{
std::vector< soc_desc::instance_t >& inst_list = node.get()->instance;
for(size_t i = 0; i < inst_list.size(); i++)
if(inst_list[i].id == id)
{
my_remove_at(inst_list, i);
return true;
}
return false;
}
enum
{
InstTypeSingle,
InstTypeRangeStride,
InstTypeRangeFormula,
InstTypeRangeList
};
enum
{
NodeInstEditPanelDelType = QTableWidgetItem::UserType,
NodeInstEditPanelAddType
};
}
NodeInstanceEditPanel::NodeInstanceEditPanel(const soc_desc::node_ref_t& ref,
soc_id_t inst_id, QWidget *parent)
:QWidget(parent), m_ref(ref), m_id(inst_id)
{
QLineEdit *name_edit = new QLineEdit(this);
QLineEdit *title_edit = new QLineEdit(this);
m_type_combo = new QComboBox(this);
QLabel *type_label = new QLabel("Type:", this);
QFont f = type_label->font();
f.setBold(true);
type_label->setFont(f);
QHBoxLayout *type_layout = new QHBoxLayout;
type_layout->addWidget(type_label);
type_layout->addWidget(m_type_combo);
type_layout->addStretch(0);
soc_desc::field_t fake_field;
fake_field.pos = 0;
fake_field.width = 32;
m_single_group = new QWidget(this);
QHBoxLayout *sg_layout = new QHBoxLayout;
sg_layout->addWidget(new QLabel("Address:", m_single_group));
SocFieldEditor *addr_edit = new SocFieldEditor(fake_field, m_single_group);
sg_layout->addWidget(addr_edit);
m_single_group->setLayout(sg_layout);
m_range_group = new QWidget(this);
QGridLayout *rg_layout = new QGridLayout;
rg_layout->addWidget(new QLabel("First:", m_range_group), 0, 0);
QSpinBox *first_spin = new QSpinBox(m_range_group);
rg_layout->addWidget(first_spin, 0, 1);
rg_layout->addWidget(new QLabel("Count:", m_range_group), 1, 0);
QSpinBox *count_spin = new QSpinBox(m_range_group);
rg_layout->addWidget(count_spin, 1, 1);
m_range_group->setLayout(rg_layout);
m_stride_group = new QWidget(m_range_group);
QGridLayout *rsg_layout = new QGridLayout;
rsg_layout->addWidget(new QLabel("Base:", m_stride_group), 0, 0);
SocFieldEditor *base_edit = new SocFieldEditor(fake_field, m_stride_group);
rsg_layout->addWidget(base_edit, 0, 1);
rsg_layout->addWidget(new QLabel("Stride:", m_stride_group), 1, 0);
SocFieldEditor *stride_edit = new SocFieldEditor(fake_field, m_stride_group);
rsg_layout->addWidget(stride_edit, 1, 1);
m_stride_group->setLayout(rsg_layout);
m_formula_group = new QWidget(m_range_group);
QGridLayout *fsg_layout = new QGridLayout;
fsg_layout->addWidget(new QLabel("Variable:", m_formula_group), 0, 0);
QLineEdit *variable_edit = new QLineEdit(m_formula_group);
fsg_layout->addWidget(variable_edit, 0, 1);
fsg_layout->addWidget(new QLabel("Formula:", m_formula_group), 1, 0);
QLineEdit *formula_edit = new QLineEdit(m_formula_group);
fsg_layout->addWidget(formula_edit, 1, 1);
m_formula_group->setLayout(fsg_layout);
QTableWidget *addr_list = new QTableWidget(m_range_group);
m_list_group = addr_list;
QHBoxLayout *inst_layout = new QHBoxLayout;
inst_layout->addWidget(m_single_group);
inst_layout->addWidget(m_range_group);
inst_layout->addWidget(m_stride_group);
inst_layout->addWidget(m_formula_group);
inst_layout->addWidget(m_list_group);
inst_layout->addStretch(0);
QGroupBox *inst_groupbox = new QGroupBox(this);
inst_groupbox->setLayout(inst_layout);
MyTextEditor *desc_edit = new MyTextEditor(this);
QVBoxLayout *ii_layout = new QVBoxLayout;
QFormLayout *info_layout = new QFormLayout();
info_layout->addRow("Name", name_edit);
info_layout->addRow("Title", title_edit);
QGroupBox *info_group = Misc::EncloseInBox("Information", info_layout);
QGroupBox *desc_group = Misc::EncloseInBox("Description", desc_edit);
QHBoxLayout *name_title_desc_layout = new QHBoxLayout;
name_title_desc_layout->addWidget(info_group, 1);
name_title_desc_layout->addWidget(desc_group, 2);
ii_layout->addLayout(name_title_desc_layout);
ii_layout->addLayout(type_layout);
ii_layout->addWidget(inst_groupbox);
ii_layout->addStretch(1);
m_type_combo->addItem("Single", QVariant(InstTypeSingle));
m_type_combo->addItem("Range > Stride", QVariant(InstTypeRangeStride));
m_type_combo->addItem("Range > Formula", QVariant(InstTypeRangeFormula));
m_type_combo->addItem("Range > List", QVariant(InstTypeRangeList));
/* fill info */
soc_desc::instance_t& inst = GetInstance();
name_edit->setText(QString::fromStdString(inst.name));
title_edit->setText(QString::fromStdString(inst.title));
desc_edit->SetTextHtml(QString::fromStdString(inst.desc));
addr_edit->setField(inst.addr);
base_edit->setField(inst.range.base);
stride_edit->setField(inst.range.stride);
first_spin->setValue(inst.range.first);
count_spin->setValue(inst.range.count);
formula_edit->setText(QString::fromStdString(inst.range.formula));
variable_edit->setText(QString::fromStdString(inst.range.variable));
addr_list->setColumnCount(2);
addr_list->setHorizontalHeaderItem(0, new QTableWidgetItem(""));
addr_list->setHorizontalHeaderItem(1, new QTableWidgetItem("Address"));
addr_list->horizontalHeader()->setVisible(false);
addr_list->verticalHeader()->setVisible(false);
addr_list->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
std::vector< soc_word_t >& addrs = inst.range.list;
addr_list->setRowCount(addrs.size() + 1);
for(size_t i = 0; i < addrs.size(); i++)
{
QTableWidgetItem *item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListRemove), "", NodeInstEditPanelDelType);
item->setToolTip("Remove this address");
item->setFlags(Qt::ItemIsEnabled);
addr_list->setItem(i, 0, item);
item = new QTableWidgetItem();
item->setData(Qt::EditRole, QVariant::fromValue(addrs[i]));
addr_list->setItem(i, 1, item);
}
QTableWidgetItem *new_item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListAdd), "", NodeInstEditPanelAddType);
new_item->setFlags(Qt::ItemIsEnabled);
addr_list->setItem(addrs.size(), 0, new_item);
new_item = new QTableWidgetItem("New address...", QTableWidgetItem::UserType);
new_item->setFlags(Qt::ItemIsEnabled);
QFont font = new_item->font();
font.setItalic(true);
new_item->setFont(font);
addr_list->setItem(addrs.size(), 1, new_item);
addr_list->resizeColumnsToContents();
addr_list->horizontalHeader()->setStretchLastSection(true);
m_table_delegate = new SocFieldItemDelegate(this);
m_table_delegate->setItemEditorFactory(new QItemEditorFactory);
m_table_edit_factory = new SocFieldEditorCreator;
m_table_delegate->itemEditorFactory()->registerEditor(QVariant::UInt, m_table_edit_factory);
addr_list->setItemDelegate(m_table_delegate);
setLayout(ii_layout);
connect(name_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnNameEdited(const QString&)));
connect(desc_edit, SIGNAL(OnTextChanged(const QString&)), this,
SLOT(OnDescEdited(const QString&)));
connect(title_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnTitleEdited(const QString&)));
connect(addr_edit, SIGNAL(editingFinished(uint)), this, SLOT(OnAddrChanged(uint)));
connect(base_edit, SIGNAL(editingFinished(uint)), this, SLOT(OnBaseChanged(uint)));
connect(stride_edit, SIGNAL(editingFinished(uint)), this, SLOT(OnStrideChanged(uint)));
connect(first_spin, SIGNAL(valueChanged(int)), this, SLOT(OnFirstChanged(int)));
connect(count_spin, SIGNAL(valueChanged(int)), this, SLOT(OnCountChanged(int)));
connect(formula_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnFormulaChanged(const QString&)));
connect(variable_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnVariableChanged(const QString&)));
connect(m_type_combo, SIGNAL(currentIndexChanged(int)),
this, SLOT(OnTypeChanged(int)));
connect(addr_list, SIGNAL(itemActivated(QTableWidgetItem *)), this,
SLOT(OnAddressActivated(QTableWidgetItem *)));
connect(addr_list, SIGNAL(itemChanged(QTableWidgetItem *)), this,
SLOT(OnAddressChanged(QTableWidgetItem *)));
/* fill info */
int combo_type;
if(inst.type == soc_desc::instance_t::RANGE)
{
if(inst.range.type == soc_desc::range_t::STRIDE)
combo_type = InstTypeRangeStride;
else if(inst.range.type == soc_desc::range_t::FORMULA)
combo_type = InstTypeRangeFormula;
else /* LIST */
combo_type = InstTypeRangeList;
}
else
combo_type = InstTypeSingle;
m_type_combo->setCurrentIndex(m_type_combo->findData(QVariant(combo_type)));
UpdateType(combo_type);
}
soc_desc::instance_t& NodeInstanceEditPanel::GetInstance()
{
return *GetInstanceById(m_ref, m_id);
}
void NodeInstanceEditPanel::OnNameEdited(const QString& text)
{
GetInstance().name = text.toStdString();
OnModified();
}
void NodeInstanceEditPanel::OnTitleEdited(const QString& text)
{
GetInstance().title = text.toStdString();
OnModified();
}
void NodeInstanceEditPanel::OnDescEdited(const QString& text)
{
GetInstance().desc = text.toStdString();
OnModified();
}
void NodeInstanceEditPanel::OnAddrChanged(uint addr)
{
GetInstance().addr = addr;
OnModified();
}
void NodeInstanceEditPanel::OnBaseChanged(uint base)
{
GetInstance().range.base = base;
OnModified();
}
void NodeInstanceEditPanel::OnStrideChanged(uint stride)
{
GetInstance().range.stride = stride;
OnModified();
}
void NodeInstanceEditPanel::OnFirstChanged(int first)
{
GetInstance().range.first = first;
OnModified();
}
void NodeInstanceEditPanel::OnCountChanged(int count)
{
GetInstance().range.count = count;
OnModified();
}
void NodeInstanceEditPanel::OnFormulaChanged(const QString& formula)
{
GetInstance().range.formula = formula.toStdString();
OnModified();
}
void NodeInstanceEditPanel::OnVariableChanged(const QString& variable)
{
GetInstance().range.variable = variable.toStdString();
OnModified();
}
void NodeInstanceEditPanel::OnAddressActivated(QTableWidgetItem *item)
{
QTableWidget *table = item->tableWidget();
soc_desc::instance_t& inst = GetInstance();
if(item->type() == NodeInstEditPanelDelType)
{
int row = item->row();
my_remove_at(inst.range.list, row);
table->removeRow(row);
OnModified();
}
else if(item->type() == NodeInstEditPanelAddType)
{
int row = inst.range.list.size();
soc_word_t new_addr = 0;
GetInstance().range.list.push_back(new_addr);
table->insertRow(row);
QTableWidgetItem *item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListRemove), "", NodeInstEditPanelDelType);
item->setToolTip("Remove this address");
item->setFlags(Qt::ItemIsEnabled);
table->setItem(row, 0, item);
item = new QTableWidgetItem();
item->setData(Qt::EditRole, QVariant(new_addr));
table->setItem(row, 1, item);
OnModified();
}
}
void NodeInstanceEditPanel::OnAddressChanged(QTableWidgetItem *item)
{
soc_desc::instance_t& inst = GetInstance();
if((size_t)item->row() >= inst.range.list.size())
return;
soc_word_t& addr = inst.range.list[item->row()];
if(item->column() == 1)
addr = item->data(Qt::EditRole).value< soc_word_t >();
OnModified();
}
void NodeInstanceEditPanel::UpdateType(int type)
{
m_single_group->hide();
m_range_group->hide();
m_stride_group->hide();
m_formula_group->hide();
m_list_group->hide();
switch(type)
{
case InstTypeSingle:
m_single_group->show();
GetInstance().type = soc_desc::instance_t::SINGLE;
break;
case InstTypeRangeStride:
m_range_group->show();
m_stride_group->show();
GetInstance().type = soc_desc::instance_t::RANGE;
GetInstance().range.type = soc_desc::range_t::STRIDE;
break;
case InstTypeRangeFormula:
m_range_group->show();
m_formula_group->show();
GetInstance().type = soc_desc::instance_t::RANGE;
GetInstance().range.type = soc_desc::range_t::FORMULA;
break;
case InstTypeRangeList:
m_range_group->show();
m_formula_group->hide();
m_list_group->show();
GetInstance().type = soc_desc::instance_t::RANGE;
GetInstance().range.type = soc_desc::range_t::LIST;
break;
default:
break;
}
}
void NodeInstanceEditPanel::OnTypeChanged(int index)
{
if(index == -1)
return;
UpdateType(m_type_combo->itemData(index).toInt());
OnModified();
}
soc_id_t NodeInstanceEditPanel::GetId()
{
return m_id;
}
/**
* NodeEditPanel
*/
NodeEditPanel::NodeEditPanel(const soc_desc::node_ref_t& ref, QWidget *parent)
:QWidget(parent), m_ref(ref)
{
/* top layout: name, title then desc */
QLineEdit *name_edit = new QLineEdit(this);
name_edit->setText(QString::fromStdString(ref.get()->name));
QLineEdit *title_edit = new QLineEdit(this);
title_edit->setText(QString::fromStdString(ref.get()->title));
QFormLayout *info_layout = new QFormLayout();
info_layout->addRow("Name", name_edit);
info_layout->addRow("Title", title_edit);
QGroupBox *info_group = Misc::EncloseInBox("Information", info_layout);
m_desc_edit = new MyTextEditor(this);
m_desc_edit->SetTextHtml(QString::fromStdString(ref.get()->desc));
QGroupBox *desc_group = Misc::EncloseInBox("Description", m_desc_edit);
QHBoxLayout *name_title_desc_layout = new QHBoxLayout;
name_title_desc_layout->addWidget(info_group, 1);
name_title_desc_layout->addWidget(desc_group, 2);
/* instance tab */
m_instances_tab = new YTabWidget(0, this);
m_instances_tab->setTabOpenable(true);
std::vector< soc_desc::instance_t >& inst_list = m_ref.get()->instance;
m_instances_tab->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
m_instances_tab->setTabsClosable(true);
QGroupBox *instance_tab_group = Misc::EncloseInBox("Instances", m_instances_tab);
for(size_t i = 0; i < inst_list.size(); i++)
{
NodeInstanceEditPanel *p = new NodeInstanceEditPanel(m_ref, inst_list[i].id, this);
connect(p, SIGNAL(OnModified()), this, SLOT(OnInstModified()));
m_instances_tab->addTab(p, QString::fromStdString(inst_list[i].name));
}
/* boring */
QVBoxLayout *layout = new QVBoxLayout;
layout->addLayout(name_title_desc_layout);
layout->addWidget(instance_tab_group);
layout->addStretch(1);
setLayout(layout);
connect(name_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnNameEdited(const QString&)));
connect(m_desc_edit, SIGNAL(OnTextChanged()), this, SLOT(OnDescEdited()));
connect(title_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnTitleEdited(const QString&)));
connect(m_instances_tab, SIGNAL(tabCloseRequested(int)), this,
SLOT(OnInstRemove(int)));
connect(m_instances_tab, SIGNAL(tabOpenRequested()), this, SLOT(OnInstCreate()));
}
void NodeEditPanel::OnNameEdited(const QString& text)
{
m_ref.get()->name = text.toStdString();
OnModified();
}
void NodeEditPanel::OnTitleEdited(const QString& text)
{
m_ref.get()->title = text.toStdString();
OnModified();
}
void NodeEditPanel::OnDescEdited()
{
m_ref.get()->desc = m_desc_edit->GetTextHtml().toStdString();
OnModified();
}
void NodeEditPanel::OnInstRemove(int index)
{
NodeInstanceEditPanel *panel =
dynamic_cast< NodeInstanceEditPanel * >(m_instances_tab->widget(index));
RemoveInstanceById(m_ref, panel->GetId());
m_instances_tab->removeTab(index);
delete panel;
OnModified();
}
void NodeEditPanel::OnInstModified()
{
int index = m_instances_tab->currentIndex();
NodeInstanceEditPanel *panel =
dynamic_cast< NodeInstanceEditPanel * >(m_instances_tab->widget(index));
m_instances_tab->setTabText(index, QString::fromStdString(panel->GetInstance().name));
OnModified();
}
QString NodeEditPanel::GuessName()
{
/* try to find instances named Xddd where X is the node name (case insensitive)
* and d are digits. */
int max_nr_digits = -1;
int max_value = -1;
QString node_name = QString::fromStdString(m_ref.get()->name);
std::vector< soc_desc::instance_t >& inst_list = m_ref.get()->instance;
for(size_t i = 0; i < inst_list.size(); i++)
{
QString inst_name = QString::fromStdString(inst_list[i].name);
/* ignore name if it doesn't start like the node name */
if(!inst_name.startsWith(node_name, Qt::CaseInsensitive))
continue;
/* check if the suffix is a digit */
QString suffix = inst_name.mid(node_name.size());
if(suffix.size() == 0)
{
max_nr_digits = qMax(max_nr_digits, 0);
continue;
}
bool ok;
int value = suffix.toUInt(&ok);
if(!ok)
continue;
max_value = qMax(max_value, value);
max_nr_digits = qMax(max_nr_digits, suffix.size());
}
/* if no match, use node name */
if(max_nr_digits == -1)
return node_name;
/* match of size 0, add "1" at the end */
if(max_nr_digits == 0)
return node_name + "1";
/* otherwise, pick next value */
return QString("%1%2").arg(node_name)
.arg(max_value + 1, max_nr_digits, 10, QChar('0'));
}
void NodeEditPanel::OnInstCreate()
{
std::vector< soc_desc::instance_t >& inst_list = m_ref.get()->instance;
soc_desc::instance_t inst;
inst.id = GetFreshId(inst_list);
inst.name = GuessName().toStdString();
inst.type = soc_desc::instance_t::SINGLE;
inst.addr = 0;
inst.range.type = soc_desc::range_t::STRIDE;
inst.range.first = 0;
inst.range.count = 0;
inst.range.stride = 0;
inst_list.push_back(inst);
NodeInstanceEditPanel *p = new NodeInstanceEditPanel(m_ref, inst.id, this);
connect(p, SIGNAL(OnModified()), this, SLOT(OnInstModified()));
int idx = m_instances_tab->addTab(p, QString::fromStdString(inst.name));
m_instances_tab->setCurrentIndex(idx);
OnModified();
}
/**
* RegFieldEditPanel
*/
namespace
{
enum
{
RegFieldEditPanelDelType = QTableWidgetItem::UserType,
RegFieldEditPanelAddType,
};
}
RegFieldEditPanel::RegFieldEditPanel(const soc_desc::field_ref_t& ref, QWidget *parent)
:QWidget(parent), m_ref(ref)
{
m_name_edit = new QLineEdit(this);
m_range_edit = new QLineEdit(this);
m_range_validator = new SocBitRangeValidator(this);
m_range_validator->setWidth(m_ref.reg().get()->width);
m_range_edit->setValidator(m_range_validator);
m_desc_edit = new MyTextEditor(this);
QHBoxLayout *namepos_layout = new QHBoxLayout;
namepos_layout->addWidget(new QLabel("Name:"));
namepos_layout->addWidget(m_name_edit);
namepos_layout->addWidget(new QLabel("Range:"));
namepos_layout->addWidget(m_range_edit);
QVBoxLayout *nameposdesc_layout = new QVBoxLayout;
nameposdesc_layout->addWidget(Misc::EncloseInBox("Information", namepos_layout));
nameposdesc_layout->addWidget(Misc::EncloseInBox("Description", m_desc_edit));
nameposdesc_layout->addStretch(0);
m_enum_table = new QTableWidget(this);
m_enum_table->setColumnCount(4);
m_enum_table->setHorizontalHeaderItem(0, new QTableWidgetItem(""));
m_enum_table->setHorizontalHeaderItem(1, new QTableWidgetItem("Name"));
m_enum_table->setHorizontalHeaderItem(2, new QTableWidgetItem("Value"));
m_enum_table->setHorizontalHeaderItem(3, new QTableWidgetItem("Description"));
m_enum_table->verticalHeader()->setVisible(false);
m_enum_table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_enum_delegate = new SocFieldItemDelegate(this);
m_enum_delegate->setItemEditorFactory(new QItemEditorFactory);
m_enum_editor = new SocFieldEditorCreator;
m_enum_delegate->itemEditorFactory()->registerEditor(QVariant::UInt, m_enum_editor);
m_enum_table->setItemDelegate(m_enum_delegate);
QHBoxLayout *field_layout = new QHBoxLayout;
field_layout->addLayout(nameposdesc_layout);
field_layout->addWidget(Misc::EncloseInBox("Special Values", m_enum_table), 1);
setLayout(field_layout);
const soc_desc::field_t& field = *m_ref.get();
m_name_edit->setText(QString::fromStdString(field.name));
m_range_edit->setText(m_range_validator->generate(
field.pos + field.width - 1, field.pos));
m_desc_edit->SetTextHtml(QString::fromStdString(field.desc));
m_enum_delegate->setWidth(field.width);
m_enum_editor->setWidth(field.width);
m_enum_table->setRowCount(field.enum_.size() + 1);
for(size_t i = 0; i < field.enum_.size(); i++)
{
QTableWidgetItem *item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListRemove), "", RegFieldEditPanelDelType);
item->setToolTip("Remove this enum");
item->setFlags(Qt::ItemIsEnabled);
m_enum_table->setItem(i, 0, item);
item = new QTableWidgetItem(QString::fromStdString(field.enum_[i].name));
m_enum_table->setItem(i, 1, item);
item = new QTableWidgetItem();
item->setData(Qt::EditRole, QVariant(field.enum_[i].value));
m_enum_table->setItem(i, 2, item);
item = new QTableWidgetItem(QString::fromStdString(field.enum_[i].desc));
m_enum_table->setItem(i, 3, item);
}
QTableWidgetItem *new_item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListAdd), "", RegFieldEditPanelAddType);
new_item->setFlags(Qt::ItemIsEnabled);
m_enum_table->setItem(field.enum_.size(), 0, new_item);
new_item = new QTableWidgetItem("New field...");
new_item->setFlags(Qt::ItemIsEnabled);
QFont font = new_item->font();
font.setItalic(true);
new_item->setFont(font);
m_enum_table->setItem(field.enum_.size(), 1, new_item);
new_item = new QTableWidgetItem();
new_item->setFlags(Qt::ItemIsEnabled);
m_enum_table->setItem(field.enum_.size(), 2, new_item);
new_item = new QTableWidgetItem();
new_item->setFlags(Qt::ItemIsEnabled);
m_enum_table->setItem(field.enum_.size(), 3, new_item);
m_enum_table->resizeColumnsToContents();
m_enum_table->horizontalHeader()->setStretchLastSection(true);
connect(m_name_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnFieldNameChanged(const QString&)));
connect(m_range_edit, SIGNAL(textChanged(const QString&)), this,
SLOT(OnFieldRangeChanged(const QString&)));
connect(m_desc_edit, SIGNAL(OnTextChanged(const QString&)), this,
SLOT(OnFieldDescChanged(const QString&)));
connect(m_enum_table, SIGNAL(itemActivated(QTableWidgetItem *)), this,
SLOT(OnFieldValueActivated(QTableWidgetItem *)));
connect(m_enum_table, SIGNAL(itemChanged(QTableWidgetItem *)), this,
SLOT(OnFieldValueChanged(QTableWidgetItem *)));
}
void RegFieldEditPanel::UpdateWidth()
{
m_range_validator->setWidth(m_ref.reg().get()->width);
}
void RegFieldEditPanel::UpdateRange()
{
soc_desc::field_t *field = m_ref.get();
m_range_edit->setText(m_range_validator->generate(
field->pos + field->width - 1, field->pos));
}
void RegFieldEditPanel::OnFieldValueActivated(QTableWidgetItem *item)
{
if(item->type() == RegFieldEditPanelDelType)
{
int row = item->row();
my_remove_at(m_ref.get()->enum_, row);
m_enum_table->removeRow(row);
OnModified();
}
else if(item->type() == RegFieldEditPanelAddType)
{
soc_desc::field_t& field = *m_ref.get();
int row = field.enum_.size();
soc_desc::enum_t new_enum;
new_enum.id = GetFreshId(field.enum_);
new_enum.name = "UNNAMED";
new_enum.value = 0;
field.enum_.push_back(new_enum);
m_enum_table->insertRow(row);
QTableWidgetItem *item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListRemove), "", RegFieldEditPanelDelType);
item->setToolTip("Remove this enum");
item->setFlags(Qt::ItemIsEnabled);
m_enum_table->setItem(row, 0, item);
item = new QTableWidgetItem(QString::fromStdString(new_enum.name));
m_enum_table->setItem(row, 1, item);
item = new QTableWidgetItem();
item->setData(Qt::EditRole, QVariant(new_enum.value));
m_enum_table->setItem(row, 2, item);
item = new QTableWidgetItem(QString::fromStdString(new_enum.desc));
m_enum_table->setItem(row, 3, item);
OnModified();
}
}
void RegFieldEditPanel::OnFieldValueChanged(QTableWidgetItem *item)
{
soc_desc::field_t& field = *m_ref.get();
if((size_t)item->row() >= field.enum_.size())
return;
soc_desc::enum_t& enum_ = field.enum_[item->row()];
if(item->column() == 1)
enum_.name = item->text().toStdString();
else if(item->column() == 2)
enum_.value = item->data(Qt::EditRole).value< soc_word_t >();
else if(item->column() == 3)
enum_.desc = item->text().toStdString();
OnModified();
}
void RegFieldEditPanel::OnFieldNameChanged(const QString& name)
{
m_ref.get()->name = name.toStdString();
OnModified();
}
void RegFieldEditPanel::OnFieldRangeChanged(const QString& range)
{
soc_desc::field_t *field = m_ref.get();
int last, first;
if(m_range_validator->parse(range, last, first) != QValidator::Acceptable)
return;
field->pos = first;
field->width = last - first + 1;
m_enum_delegate->setWidth(field->width);
m_enum_editor->setWidth(field->width);
OnModified();
}
void RegFieldEditPanel::OnFieldDescChanged(const QString& desc)
{
m_ref.get()->desc = desc.toStdString();
OnModified();
}
soc_desc::field_ref_t RegFieldEditPanel::GetField()
{
return m_ref;
}
/**
* RegEditPanel
*/
namespace
{
enum
{
RegVariantEditPanelDelType = QTableWidgetItem::UserType,
RegVariantEditPanelAddType,
};
}
RegEditPanel::RegEditPanel(const soc_desc::register_ref_t& ref, QWidget *parent)
:QWidget(parent), m_ref(ref), m_reg_font(font())
{
m_reg_font.setWeight(100);
m_reg_font.setKerning(false);
m_value_model = new RegFieldTableModel(this); // view takes ownership
m_value_model->SetRegister(*ref.get());
m_value_model->SetReadOnly(false);
m_sexy_display2 = new Unscroll<YRegDisplay>(this);
m_sexy_display2->setFont(m_reg_font);
m_sexy_display2->setModel(m_value_model);
m_sexy_display2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_sexy_display2->setContextMenuPolicy(Qt::CustomContextMenu);
m_sexy_display2->setWidth(m_ref.get()->width);
m_view_tab = new QTabWidget(this);
m_view_tab->setTabPosition(QTabWidget::West);
/* field tab */
m_fields_tab = new YTabWidget(0, this);
m_fields_tab->setTabOpenable(true);
std::vector< soc_desc::field_ref_t > field_list = m_ref.fields();
m_fields_tab->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
m_fields_tab->setTabsClosable(true);
m_fields_tab->setElideMode(Qt::ElideRight);
for(size_t i = 0; i < field_list.size(); i++)
{
RegFieldEditPanel *p = new RegFieldEditPanel(field_list[i], this);
connect(p, SIGNAL(OnModified()), this, SLOT(OnFieldModified()));
m_fields_tab->addTab(p, QString::fromStdString(field_list[i].get()->name));
}
m_reg_size_group = new QButtonGroup(this);
QRadioButton *reg_size_32 = new QRadioButton("32-bit");
QRadioButton *reg_size_16 = new QRadioButton("16-bit");
QRadioButton *reg_size_8 = new QRadioButton("8-bit");
m_reg_size_group->addButton(reg_size_32, 32);
m_reg_size_group->addButton(reg_size_16, 16);
m_reg_size_group->addButton(reg_size_8, 8);
if(m_reg_size_group->button(m_ref.get()->width))
m_reg_size_group->button(m_ref.get()->width)->click();
QVBoxLayout *width_group_layout = new QVBoxLayout;
width_group_layout->addWidget(reg_size_32);
width_group_layout->addWidget(reg_size_16);
width_group_layout->addWidget(reg_size_8);
QGroupBox *width_group = new QGroupBox("Width");
width_group->setLayout(width_group_layout);
m_reg_access_group = new QButtonGroup(this);
QRadioButton *reg_dflt = new QRadioButton("Unspecified");
QRadioButton *reg_rw = new QRadioButton("Read-Write");
QRadioButton *reg_ro = new QRadioButton("Read-Only");
QRadioButton *reg_wo = new QRadioButton("Write-Only");
m_reg_access_group->addButton(reg_dflt, soc_desc::UNSPECIFIED);
m_reg_access_group->addButton(reg_rw, soc_desc::READ_WRITE);
m_reg_access_group->addButton(reg_ro, soc_desc::READ_ONLY);
m_reg_access_group->addButton(reg_wo, soc_desc::WRITE_ONLY);
if(m_reg_access_group->button(m_ref.get()->access))
m_reg_access_group->button(m_ref.get()->access)->click();
QVBoxLayout *access_group_layout = new QVBoxLayout;
access_group_layout->addWidget(reg_dflt);
access_group_layout->addWidget(reg_rw);
access_group_layout->addWidget(reg_ro);
access_group_layout->addWidget(reg_wo);
QGroupBox *access_group = new QGroupBox("Access");
access_group->setLayout(access_group_layout);
QVBoxLayout *width_access_layout = new QVBoxLayout;
width_access_layout->addWidget(width_group);
width_access_layout->addWidget(access_group);
width_access_layout->addStretch(0);
m_variant_table = new QTableWidget;
m_variant_table->setColumnCount(4);
m_variant_table->setHorizontalHeaderItem(0, new QTableWidgetItem(""));
m_variant_table->setHorizontalHeaderItem(1, new QTableWidgetItem("Type"));
m_variant_table->setHorizontalHeaderItem(2, new QTableWidgetItem("Offset"));
m_variant_table->setHorizontalHeaderItem(3, new QTableWidgetItem("Access"));
m_variant_table->verticalHeader()->setVisible(false);
m_variant_table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_variant_delegate = new SocFieldItemDelegate(this);
m_variant_delegate->setItemEditorFactory(new QItemEditorFactory);
m_variant_delegate->itemEditorFactory()->registerEditor(QVariant::UInt,
new SocFieldEditorCreator);
m_variant_table->setItemDelegate(m_variant_delegate);
m_access_delegate = new SocAccessItemDelegate("Unspecified", this);
m_access_delegate->setItemEditorFactory(new QItemEditorFactory);
// FIXME see QTBUG-30392
m_access_delegate->itemEditorFactory()->registerEditor(
(QVariant::Type)qMetaTypeId< soc_desc::access_t >(),
new SocAccessEditorCreator);
m_variant_table->setItemDelegateForColumn(3, m_access_delegate);
std::vector< soc_desc::variant_ref_t > variants = m_ref.variants();
m_variant_table->setRowCount(variants.size() + 1);
for(size_t i = 0; i < variants.size(); i++)
{
QTableWidgetItem *item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListRemove), "", RegVariantEditPanelDelType);
item->setToolTip("Remove this variant");
item->setFlags(Qt::ItemIsEnabled);
m_variant_table->setItem(i, 0, item);
item = new QTableWidgetItem(QString::fromStdString(variants[i].get()->type));
m_variant_table->setItem(i, 1, item);
item = new QTableWidgetItem();
item->setData(Qt::EditRole, QVariant(variants[i].get()->offset));
m_variant_table->setItem(i, 2, item);
item = new QTableWidgetItem();
item->setData(Qt::EditRole, QVariant::fromValue(variants[i].get()->access));
m_variant_table->setItem(i, 3, item);
}
QTableWidgetItem *new_item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListAdd), "", RegVariantEditPanelAddType);
new_item->setFlags(Qt::ItemIsEnabled);
m_variant_table->setItem(variants.size(), 0, new_item);
new_item = new QTableWidgetItem("New variant...");
new_item->setFlags(Qt::ItemIsEnabled);
QFont font = new_item->font();
font.setItalic(true);
new_item->setFont(font);
m_variant_table->setItem(variants.size(), 1, new_item);
new_item = new QTableWidgetItem();
new_item->setFlags(Qt::ItemIsEnabled);
m_variant_table->setItem(variants.size(), 2, new_item);
new_item = new QTableWidgetItem();
new_item->setFlags(Qt::ItemIsEnabled);
m_variant_table->resizeColumnsToContents();
m_variant_table->horizontalHeader()->setStretchLastSection(true);
m_desc_edit = new MyTextEditor(this);
m_desc_edit->SetTextHtml(QString::fromStdString(ref.get()->desc));
QHBoxLayout *top_info_layout = new QHBoxLayout;
top_info_layout->addLayout(width_access_layout);
top_info_layout->addWidget(Misc::EncloseInBox("Variants", m_variant_table));
top_info_layout->addWidget(Misc::EncloseInBox("Description", m_desc_edit));
QWidget *reg_info = new QWidget(this);
QVBoxLayout *reg_info_layout = new QVBoxLayout;
reg_info_layout->addLayout(top_info_layout);
reg_info_layout->addStretch(0);
reg_info->setLayout(reg_info_layout);
m_view_tab->addTab(reg_info, "Information");
m_view_tab->addTab(m_fields_tab, "Fields");
QVBoxLayout *main_layout = new QVBoxLayout;
main_layout->addWidget(m_sexy_display2, 1);
main_layout->addWidget(m_view_tab, 2);
m_delete_action = new QAction("&Delete", this);
m_delete_action->setIcon(YIconManager::Get()->GetIcon(YIconManager::ListRemove));
m_new_action = new QAction("&New field", this);
m_new_action->setIcon(YIconManager::Get()->GetIcon(YIconManager::ListAdd));
setLayout(main_layout);
OnRegFieldActivated(QModelIndex());
UpdateWidthRestrictions();
connect(m_sexy_display2, SIGNAL(clicked(const QModelIndex&)), this,
SLOT(OnRegFieldActivated(const QModelIndex&)));
connect(m_sexy_display2, SIGNAL(customContextMenuRequested(QPoint)), this,
SLOT(OnRegDisplayContextMenu(QPoint)));
connect(m_reg_size_group, SIGNAL(buttonClicked(int)), this, SLOT(OnWidthChanged(int)));
connect(m_reg_access_group, SIGNAL(buttonClicked(int)), this, SLOT(OnAccessChanged(int)));
connect(m_delete_action, SIGNAL(triggered()), this, SLOT(OnRegFieldDelete()));
connect(m_new_action, SIGNAL(triggered()), this, SLOT(OnRegFieldNew()));
connect(m_variant_table, SIGNAL(itemActivated(QTableWidgetItem *)), this,
SLOT(OnVariantActivated(QTableWidgetItem *)));
connect(m_variant_table, SIGNAL(itemChanged(QTableWidgetItem *)), this,
SLOT(OnVariantValueChanged(QTableWidgetItem *)));
connect(m_desc_edit, SIGNAL(OnTextChanged()), this, SLOT(OnDescEdited()));
connect(m_fields_tab, SIGNAL(tabCloseRequested(int)), this, SLOT(OnFieldRemove(int)));
connect(m_fields_tab, SIGNAL(tabOpenRequested()), this, SLOT(OnFieldCreate()));
connect(m_value_model, SIGNAL(OnBitrangeModified(int)), this, SLOT(OnBitrangeModified(int)));
}
void RegEditPanel::UpdateWidthRestrictions()
{
/* only allow width large enough to fit all fields */
size_t max_bit = 0;
std::vector< soc_desc::field_ref_t > field_list = m_ref.fields();
for(size_t i = 0; i < field_list.size(); i++)
{
soc_desc::field_t& f = *field_list[i].get();
max_bit = std::max(max_bit, f.pos + f.width - 1);
}
/* update buttons */
m_reg_size_group->button(8)->setEnabled(max_bit < 8);
m_reg_size_group->button(16)->setEnabled(max_bit < 16);
m_reg_size_group->button(32)->setEnabled(max_bit < 32);
}
int RegEditPanel::IndexById(soc_id_t id)
{
for(int i = 0; i < m_fields_tab->count(); i++)
{
RegFieldEditPanel *p = dynamic_cast< RegFieldEditPanel * >(m_fields_tab->widget(i));
if(p->GetField().get()->id == id)
return i;
}
return -1;
}
void RegEditPanel::OnWidthChanged(int w)
{
m_ref.get()->width = w;
m_sexy_display2->setWidth(w);
for(int i = 0; i < m_fields_tab->count(); i++)
dynamic_cast< RegFieldEditPanel * >(m_fields_tab->widget(i))->UpdateWidth();
OnModified();
}
void RegEditPanel::OnBitrangeModified(int index)
{
soc_desc::register_t reg = m_value_model->GetRegister();
m_ref.get()->field[index].pos = reg.field[index].pos;
m_ref.get()->field[index].width = reg.field[index].width;
for(int i = 0; i < m_fields_tab->count(); i++)
dynamic_cast< RegFieldEditPanel * >(m_fields_tab->widget(i))->UpdateRange();
OnModified();
}
void RegEditPanel::OnAccessChanged(int acc)
{
m_ref.get()->access = (soc_desc::access_t)acc;
OnModified();
}
void RegEditPanel::OnDescEdited()
{
m_ref.get()->desc = m_desc_edit->GetTextHtml().toStdString();
OnModified();
}
void RegEditPanel::OnVariantActivated(QTableWidgetItem *item)
{
if(item->type() == RegVariantEditPanelDelType)
{
int row = item->row();
my_remove_at(m_ref.get()->variant, row);
m_variant_table->removeRow(row);
OnModified();
}
else if(item->type() == RegVariantEditPanelAddType)
{
int row = m_ref.get()->variant.size();
soc_desc::variant_t& variant = *m_ref.create_variant().get();
variant.type = "untyped";
variant.offset = 0;
variant.access = soc_desc::UNSPECIFIED;
m_variant_table->insertRow(row);
QTableWidgetItem *item = new QTableWidgetItem(
YIconManager::Get()->GetIcon(YIconManager::ListRemove), "", RegVariantEditPanelDelType);
item->setToolTip("Remove this variant");
item->setFlags(Qt::ItemIsEnabled);
m_variant_table->setItem(row, 0, item);
item = new QTableWidgetItem(QString::fromStdString(variant.type));
m_variant_table->setItem(row, 1, item);
item = new QTableWidgetItem();
item->setData(Qt::EditRole, QVariant(variant.offset));
m_variant_table->setItem(row, 2, item);
item = new QTableWidgetItem();
item->setData(Qt::EditRole, QVariant::fromValue(variant.access));
m_variant_table->setItem(row, 3, item);
OnModified();
}
}
void RegEditPanel::OnVariantValueChanged(QTableWidgetItem *item)
{
soc_desc::register_t& reg = *m_ref.get();
if((size_t)item->row() >= reg.variant.size())
return;
soc_desc::variant_t& var = reg.variant[item->row()];
if(item->column() == 1)
var.type = item->text().toStdString();
else if(item->column() == 2)
var.offset = item->data(Qt::EditRole).value< soc_word_t >();
else if(item->column() == 3)
var.access = item->data(Qt::EditRole).value< soc_desc::access_t >();
OnModified();
}
void RegEditPanel::OnRegFieldActivated(const QModelIndex& index)
{
if(!index.isValid())
return;
m_fields_tab->setCurrentIndex(IndexById(m_ref.get()->field[index.row()].id));
m_view_tab->setCurrentIndex(1);
}
void RegEditPanel::OnFieldModified()
{
int idx = m_fields_tab->currentIndex();
RegFieldEditPanel *p = dynamic_cast< RegFieldEditPanel * >(m_fields_tab->widget(idx));
m_fields_tab->setTabText(idx, QString::fromStdString(p->GetField().get()->name));
DoModify();
}
void RegEditPanel::DoModify()
{
m_value_model->UpdateRegister(*m_ref.get());
UpdateWidthRestrictions();
OnModified();
}
void RegEditPanel::OnRegFieldDelete()
{
QModelIndex current = m_sexy_display2->currentIndex();
if(!current.isValid())
return;
QMessageBox msgbox(QMessageBox::Question, "Delete field ?",
"Are you sure you want to delete this field ?",
QMessageBox::Yes | QMessageBox::No, this);
msgbox.setDefaultButton(QMessageBox::No);
int ret = msgbox.exec();
if(ret != QMessageBox::Yes)
return;
m_fields_tab->removeTab(IndexById(m_ref.get()->field[current.row()].id));
my_remove_at(m_ref.get()->field, current.row());
DoModify();
OnRegFieldActivated(QModelIndex());
}
void RegEditPanel::OnFieldRemove(int index)
{
Q_UNUSED(index);
}
int RegEditPanel::FindFreeBit(int preferred)
{
int nr_bits = m_ref.get()->width;
soc_word_t free_mask = (nr_bits == 32) ? 0xffffffff : (1 << nr_bits) - 1;
soc_desc::register_t& reg = *m_ref.get();
for(size_t i = 0; i < reg.field.size(); i++)
free_mask &= ~reg.field[i].bitmask();
/* any space ? */
if(free_mask == 0)
return -1;
int closest_bit = -1;
int closest_dist = nr_bits;
for(int bit = 0; bit < nr_bits; bit++)
{
if(!(free_mask & (1 << bit)))
continue;
if(abs(bit - preferred) < closest_dist)
{
closest_bit = bit;
closest_dist = abs(bit - preferred);
}
}
return closest_bit;
}
void RegEditPanel::OnRegFieldNew()
{
int bit_col = m_sexy_display2->bitColumnAt(m_menu_point);
/* we need to make sure the created field does not overlap something */
bit_col = FindFreeBit(bit_col);
if(bit_col == -1)
return; /* impossible to find a free position */
soc_desc::field_ref_t ref = m_ref.create_field();
soc_desc::field_t& field = *ref.get();
field.pos = bit_col;
field.width = 1;
field.name = "UNNAMED";
RegFieldEditPanel *p = new RegFieldEditPanel(ref, this);
connect(p, SIGNAL(OnModified()), this, SLOT(OnFieldModified()));
m_fields_tab->addTab(p, QString::fromStdString(field.name));
DoModify();
}
void RegEditPanel::OnFieldCreate()
{
OnRegFieldNew();
}
void RegEditPanel::OnRegDisplayContextMenu(QPoint point)
{
m_menu_point = point;
QMenu *menu = new QMenu(this);
QModelIndex item = m_sexy_display2->indexAt(point);
menu->addAction(m_new_action);
if(item.isValid())
menu->addAction(m_delete_action);
menu->popup(m_sexy_display2->viewport()->mapToGlobal(point));
}
namespace
{
enum
{
SocTreeSocType = QTreeWidgetItem::UserType, // SocRefRole -> node_ref_t to root
SocTreeNodeType, // SocRefRole -> node_ref_t
SocTreeRegType, // SocRefRole -> register_ref_t
};
enum
{
SocRefRole = Qt::UserRole,
};
template<typename T>
T SocTreeItemVal(QTreeWidgetItem *item)
{
return item->data(0, SocRefRole).value<T>();
}
template<typename T>
QTreeWidgetItem *MakeSocTreeItem(int type, const T& val)
{
QTreeWidgetItem *item = new QTreeWidgetItem(type);
item->setData(0, SocRefRole, QVariant::fromValue(val));
return item;
}
}
/**
* RegEdit
*/
RegEdit::RegEdit(Backend *backend, QWidget *parent)
:QWidget(parent), m_backend(backend)
{
QVBoxLayout *m_vert_layout = new QVBoxLayout();
QLabel *file_static = new QLabel("File:");
m_file_edit = new QLineEdit(this);
m_file_edit->setReadOnly(true);
m_file_open = new QToolButton(this);
m_file_open->setText("Open");
m_file_open->setIcon(YIconManager::Get()->GetIcon(YIconManager::DocumentOpen));
m_file_open->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
QMenu *file_open_menu = new QMenu(this);
QAction *new_act = file_open_menu->addAction(YIconManager::Get()->GetIcon(YIconManager::DocumentNew),
"New...");
m_file_open->setPopupMode(QToolButton::MenuButtonPopup);
m_file_open->setMenu(file_open_menu);
m_file_save = new QToolButton(this);
m_file_save->setText("Save");
m_file_save->setIcon(YIconManager::Get()->GetIcon(YIconManager::DocumentSave));
m_file_save->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_file_save->setPopupMode(QToolButton::MenuButtonPopup);
QMenu *file_save_menu = new QMenu(this);
QAction *saveas_act = file_save_menu->addAction(YIconManager::Get()->GetIcon(YIconManager::DocumentSaveAs),
"Save as...");
m_file_save->setMenu(file_save_menu);
QHBoxLayout *file_group_layout = new QHBoxLayout();
file_group_layout->addWidget(m_file_open);
file_group_layout->addWidget(m_file_save);
file_group_layout->addWidget(file_static);
file_group_layout->addWidget(m_file_edit);
m_splitter = new QSplitter(this);
m_soc_tree = new QTreeWidget(this);
m_soc_tree->setColumnCount(1);
m_soc_tree->setHeaderLabel(QString("Name"));
m_soc_tree->setContextMenuPolicy(Qt::CustomContextMenu);
m_delete_action = new QAction("&Delete", this);
m_delete_action->setIcon(YIconManager::Get()->GetIcon(YIconManager::ListRemove));
m_new_action = new QAction("&New", this);
m_new_action->setIcon(YIconManager::Get()->GetIcon(YIconManager::ListAdd));
m_create_action = new QAction("&Create register", this);
m_create_action->setIcon(YIconManager::Get()->GetIcon(YIconManager::FolderNew));
m_splitter->addWidget(m_soc_tree);
m_splitter->setStretchFactor(0, 0);
m_msg = new MessageWidget(this);
QWidget *splitter_right = new QWidget(this);
m_right_panel_layout = new QVBoxLayout;
m_right_panel_layout->addWidget(m_msg, 0);
splitter_right->setLayout(m_right_panel_layout);
m_splitter->addWidget(splitter_right);
m_splitter->setStretchFactor(1, 2);
m_msg_welcome_id = SetMessage(MessageWidget::Information,
"Open a description file to edit, or create a new file from scratch.");
m_msg_name_error_id = 0;
m_vert_layout->addLayout(file_group_layout);
m_vert_layout->addWidget(m_splitter, 1);
setLayout(m_vert_layout);
SetModified(false, false);
m_right_panel = 0;
SetPanel(new EmptyEditPanel(this));
UpdateTabName();
connect(m_soc_tree, SIGNAL(customContextMenuRequested(QPoint)), this,
SLOT(OnSocTreeContextMenu(QPoint)));
connect(m_delete_action, SIGNAL(triggered()), this, SLOT(OnSocItemDelete()));
connect(m_new_action, SIGNAL(triggered()), this, SLOT(OnSocItemNew()));
connect(m_create_action, SIGNAL(triggered()), this, SLOT(OnSocItemCreate()));
connect(m_file_open, SIGNAL(clicked()), this, SLOT(OnOpen()));
connect(m_file_save, SIGNAL(clicked()), this, SLOT(OnSave()));
connect(new_act, SIGNAL(triggered()), this, SLOT(OnNew()));
connect(saveas_act, SIGNAL(triggered()), this, SLOT(OnSaveAs()));
connect(m_soc_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
this, SLOT(OnSocItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
}
QWidget *RegEdit::GetWidget()
{
return this;
}
RegEdit::~RegEdit()
{
}
int RegEdit::SetMessage(MessageWidget::MessageType type, const QString& msg)
{
return m_msg->SetMessage(type, msg);
}
void RegEdit::HideMessage(int id)
{
m_msg->HideMessage(id);
}
void RegEdit::OnSave()
{
SaveSoc();
}
void RegEdit::OnSaveAs()
{
SaveSocAs();
}
bool RegEdit::CloseSoc()
{
if(!m_modified)
return true;
QMessageBox msgbox(QMessageBox::Question, "Save changes ?",
"The description has been modified. Do you want to save your changes ?",
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, this);
msgbox.setDefaultButton(QMessageBox::Cancel);
int ret = msgbox.exec();
if(ret == QMessageBox::Discard)
return true;
if(ret == QMessageBox::Cancel)
return false;
return SaveSoc();
}
bool RegEdit::SaveSoc()
{
if(m_file_edit->text().size() == 0)
return SaveSocAs();
else
return SaveSocFile(m_file_edit->text());
}
bool RegEdit::GetFilename(QString& filename, bool save)
{
QFileDialog *fd = new QFileDialog(this);
if(save)
fd->setAcceptMode(QFileDialog::AcceptSave);
QStringList filters;
filters << "Description files (*.xml)";
filters << "All files (*)";
fd->setNameFilters(filters);
fd->setDirectory(Settings::Get()->value("regedit/loaddescdir", QDir::currentPath()).toString());
if(fd->exec())
{
QStringList filenames = fd->selectedFiles();
filename = filenames[0];
Settings::Get()->setValue("regedit/loaddescdir", fd->directory().absolutePath());
return true;
}
else
return false;
}
bool RegEdit::SaveSocAs()
{
QString filename;
if(!GetFilename(filename, true))
return false;
m_file_edit->setText(filename);
return SaveSocFile(filename);
}
void RegEdit::OnOpen()
{
if(!CloseSoc())
return;
QString filename;
if(!GetFilename(filename, false))
return;
LoadSocFile(filename);
}
void RegEdit::OnNew()
{
if(!CloseSoc())
return;
m_cur_socfile = SocFile();
m_file_edit->setText("");
SetModified(false, false);
UpdateSocFile();
}
bool RegEdit::SaveSocFile(const QString& filename)
{
soc_desc::error_context_t ctx;
soc_desc::normalize(m_cur_socfile.GetSoc());
if(!soc_desc::produce_xml(filename.toStdString(), m_cur_socfile.GetSoc(), ctx))
{
QMessageBox::warning(this, "The description was not saved",
"There was an error when saving the file");
return false;
}
UpdateSocFile();
SetModified(false, false);
return true;
}
void RegEdit::UpdateTabName()
{
QFileInfo info(m_cur_socfile.GetFilename());
if(info.exists())
SetTabName(info.fileName());
else
SetTabName("Register Editor");
}
void RegEdit::LoadSocFile(const QString& filename)
{
m_cur_socfile = SocFile(filename);
if(!m_cur_socfile.IsValid())
{
QMessageBox::warning(this, "The description was not loaded",
"There was an error when loading the file");
return;
}
m_file_edit->setText(filename);
SetModified(false, false);
UpdateSocFile();
UpdateTabName();
m_msg_welcome_id = SetMessage(MessageWidget::Information,
"Select items to edit in tree, or right-click on them to see available actions.");
}
void RegEdit::FillNodeTreeItem(QTreeWidgetItem *item)
{
soc_desc::node_ref_t node = SocTreeItemVal< soc_desc::node_ref_t >(item);
soc_desc::register_ref_t reg = node.reg();
/* put register if there, otherwise offer to create one */
if(reg.valid() && reg.node() == node)
{
QTreeWidgetItem *reg_item = MakeSocTreeItem(SocTreeRegType, reg);
FixupItem(reg_item);
item->addChild(reg_item);
}
std::vector< soc_desc::node_ref_t > list = node.children();
for(size_t i = 0; i < list.size(); i++)
{
QTreeWidgetItem *node_item = MakeSocTreeItem(SocTreeNodeType, list[i]);
FixupItem(node_item);
FillNodeTreeItem(node_item);
item->addChild(node_item);
}
}
void RegEdit::FillSocTree()
{
soc_desc::soc_ref_t ref = m_cur_socfile.GetSocRef();
QTreeWidgetItem *root_item = MakeSocTreeItem(SocTreeSocType, ref.root());
FixupItem(root_item);
FillNodeTreeItem(root_item);
m_soc_tree->addTopLevelItem(root_item);
root_item->setExpanded(true);
}
void RegEdit::MakeItalic(QTreeWidgetItem *item, bool it)
{
QFont font = item->font(0);
font.setItalic(it);
item->setFont(0, font);
}
QIcon RegEdit::GetIconFromType(int type)
{
switch(type)
{
case SocTreeSocType: return YIconManager::Get()->GetIcon(YIconManager::Computer);
case SocTreeNodeType: return YIconManager::Get()->GetIcon(YIconManager::Cpu);
case SocTreeRegType: return style()->standardIcon(QStyle::SP_ArrowRight);
default: return QIcon();
}
}
bool RegEdit::ValidateName(const QString& name)
{
if(name.size() == 0)
return false;
for(int i = 0; i < name.size(); i++)
if(!name[i].isLetterOrNumber() && name[i] != QChar('_'))
return false;
return true;
}
void RegEdit::FixupItem(QTreeWidgetItem *item)
{
UpdateName(item);
if(!ValidateName(item->text(0)))
{
item->setIcon(0, YIconManager::Get()->GetIcon(YIconManager::DialogError));
if(item->text(0).size() == 0)
{
MakeItalic(item, true);
item->setText(0, "Unnamed");
}
m_msg_name_error_id = SetMessage(MessageWidget::Error,
"The item name is invalid. It must be non-empty and consists only "
"of alphanumerical or underscore characters");
}
else
{
HideMessage(m_msg_name_error_id);
item->setIcon(0, GetIconFromType(item->type()));
MakeItalic(item, false);
}
}
void RegEdit::UpdateSocFile()
{
m_soc_tree->clear();
FillSocTree();
SetPanel(new EmptyEditPanel(this));
}
void RegEdit::SetPanel(QWidget *panel)
{
delete m_right_panel;
m_right_panel = panel;
connect(m_right_panel, SIGNAL(OnModified()), this,
SLOT(OnSocModified()));
m_right_panel_layout->addWidget(m_right_panel, 1);
}
void RegEdit::SetModified(bool add, bool mod)
{
m_modified = add ? (m_modified || mod) : mod;
OnModified(mod);
}
void RegEdit::OnSocItemNew()
{
QTreeWidgetItem *current = m_soc_tree->currentItem();
if(current == 0)
return;
soc_desc::node_ref_t node = SocTreeItemVal< soc_desc::node_ref_t >(current);
node = node.create();
node.get()->name = "unnamed";
QTreeWidgetItem *node_item = MakeSocTreeItem(SocTreeNodeType, node);
FixupItem(node_item);
current->addChild(node_item);
m_soc_tree->setCurrentItem(node_item);
}
void RegEdit::OnSocItemCreate()
{
QTreeWidgetItem *current = m_soc_tree->currentItem();
if(current == 0)
return;
soc_desc::register_t reg;
reg.width = 32;
reg.access = soc_desc::UNSPECIFIED;
soc_desc::node_ref_t node = SocTreeItemVal< soc_desc::node_ref_t >(current);
node.get()->register_.push_back(reg);
QTreeWidgetItem *reg_item = MakeSocTreeItem(SocTreeRegType, node.reg());
FixupItem(reg_item);
current->insertChild(0, reg_item);
m_soc_tree->setCurrentItem(reg_item);
}
void RegEdit::OnSocItemDelete()
{
QTreeWidgetItem *current = m_soc_tree->currentItem();
if(current == 0)
return;
QMessageBox msgbox(QMessageBox::Question, "Delete item ?",
"Are you sure you want to delete this item ?",
QMessageBox::Yes | QMessageBox::No, this);
msgbox.setDefaultButton(QMessageBox::No);
int ret = msgbox.exec();
if(ret != QMessageBox::Yes)
return;
if(current->type() == SocTreeSocType)
{
SocTreeItemVal< soc_desc::node_ref_t >(current).remove();
current->takeChildren();
OnSocModified();
}
else if(current->type() == SocTreeNodeType)
{
SocTreeItemVal< soc_desc::node_ref_t >(current).remove();
current->parent()->removeChild(current);
OnSocModified();
}
else if(current->type() == SocTreeRegType)
{
SocTreeItemVal< soc_desc::register_ref_t >(current).remove();
current->parent()->removeChild(current);
OnSocModified();
}
}
void RegEdit::OnSocModified()
{
// we might need to update the name in the tree
FixupItem(m_soc_tree->currentItem());
SetModified(true, true);
}
void RegEdit::DisplaySoc(const soc_desc::soc_ref_t& ref)
{
SetPanel(new SocEditPanel(ref, this));
}
void RegEdit::DisplayNode(const soc_desc::node_ref_t& ref)
{
SetPanel(new NodeEditPanel(ref, this));
}
void RegEdit::DisplayReg(const soc_desc::register_ref_t& ref)
{
SetPanel(new RegEditPanel(ref, this));
}
void RegEdit::UpdateName(QTreeWidgetItem *current)
{
if(current == 0)
return;
if(current->type() == SocTreeSocType)
{
current->setText(0, QString::fromStdString(
SocTreeItemVal< soc_desc::node_ref_t >(current).soc().get()->name));
}
else if(current->type() == SocTreeNodeType)
{
current->setText(0, QString::fromStdString(
SocTreeItemVal< soc_desc::node_ref_t >(current).get()->name));
}
else if(current->type() == SocTreeRegType)
{
current->setText(0, "register");
}
}
void RegEdit::OnSocTreeContextMenu(QPoint point)
{
HideMessage(m_msg_welcome_id);
QTreeWidgetItem *item = m_soc_tree->itemAt(point);
if(item == 0)
return;
/* customise messages with item */
m_action_item = item;
QMenu *menu = new QMenu(this);
switch(item->type())
{
case SocTreeSocType:
m_new_action->setText("New node...");
m_delete_action->setText("Delete all nodes...");
menu->addAction(m_new_action);
menu->addAction(m_delete_action);
break;
case SocTreeNodeType:
{
m_new_action->setText("New node...");
m_delete_action->setText("Delete node...");
soc_desc::node_ref_t node = SocTreeItemVal< soc_desc::node_ref_t >(item);
if(node.reg().node() != node)
menu->addAction(m_create_action);
menu->addAction(m_new_action);
menu->addAction(m_delete_action);
break;
}
case SocTreeRegType:
m_delete_action->setText("Delete register...");
menu->addAction(m_new_action);
menu->addAction(m_delete_action);
break;
}
menu->popup(m_soc_tree->viewport()->mapToGlobal(point));
}
void RegEdit::OnSocItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
Q_UNUSED(previous);
HideMessage(m_msg_welcome_id);
if(current == 0)
return;
if(current->type() == SocTreeSocType)
DisplaySoc(SocTreeItemVal< soc_desc::node_ref_t >(current).soc());
else if(current->type() == SocTreeNodeType)
DisplayNode(SocTreeItemVal< soc_desc::node_ref_t >(current));
else if(current->type() == SocTreeRegType)
DisplayReg(SocTreeItemVal< soc_desc::register_ref_t >(current));
}
bool RegEdit::Quit()
{
return CloseSoc();
}