/*************************************************************************** * __________ __ ___. * 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 #include #include #include #include #include /** * 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(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 T SocTreeItemVal(QTreeWidgetItem *item) { return item->data(0, SocRefRole).value(); } template 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(); }