#include "aux.h" #include #include #include #include #include #include #include #include #include #include /** * SocBitRangeValidator */ SocBitRangeValidator::SocBitRangeValidator(QObject *parent) :QValidator(parent) { } void SocBitRangeValidator::fixup(QString& input) const { input = input.trimmed(); } QValidator::State SocBitRangeValidator::validate(QString& input, int& pos) const { Q_UNUSED(pos); int first, last; State state = parse(input, last, first); return state; } QValidator::State SocBitRangeValidator::parse(const QString& input, int& last, int& first) const { // the empty string is always intermediate if(input.size() == 0) return Intermediate; // check if there is ':' int pos = input.indexOf(':'); if(pos == -1) pos = input.size(); // if field start with ':', the last bit is implicit and is 31 if(pos > 0) { // parse last bit and check it's between 0 and 31 bool ok = false; last = input.left(pos).toInt(&ok); if(!ok || last < 0 || last >= 32) return Invalid; } else last = 31; // parse first bit if(pos < input.size() - 1) { bool ok = false; first = input.mid(pos + 1).toInt(&ok); if(!ok || first < 0 || first > last) return Invalid; } // if input ends with ':', first bit is implicit and is 0 else if(pos == input.size() - 1) first = 0; // if there no ':', first=last else first = last; return Acceptable; } /** * SocFieldValidator */ SocFieldValidator::SocFieldValidator(QObject *parent) :QValidator(parent) { m_field.first_bit = 0; m_field.last_bit = 31; } SocFieldValidator::SocFieldValidator(const soc_reg_field_t& field, QObject *parent) :QValidator(parent), m_field(field) { } void SocFieldValidator::fixup(QString& input) const { input = input.trimmed(); } QValidator::State SocFieldValidator::validate(QString& input, int& pos) const { Q_UNUSED(pos); soc_word_t val; State state = parse(input, val); return state; } QValidator::State SocFieldValidator::parse(const QString& input, soc_word_t& val) const { // the empty string is always intermediate if(input.size() == 0) return Intermediate; // first check named values State state = Invalid; foreach(const soc_reg_field_value_t& value, m_field.value) { QString name = QString::fromLocal8Bit(value.name.c_str()); // cannot be a substring if too long or empty if(input.size() > name.size()) continue; // check equal string if(input == name) { state = Acceptable; val = value.value; break; } // check substring if(name.startsWith(input)) state = Intermediate; } // early return for exact match if(state == Acceptable) return state; // do a few special cases for convenience if(input.compare("0x", Qt::CaseInsensitive) == 0 || input.compare("0b", Qt::CaseInsensitive) == 0) return Intermediate; // try by parsing unsigned basis, pos; if(input.size() >= 2 && input.startsWith("0x", Qt::CaseInsensitive)) { basis = 16; pos = 2; } else if(input.size() >= 2 && input.startsWith("0b", Qt::CaseInsensitive)) { basis = 2; pos = 2; } else if(input.size() >= 2 && input.startsWith("0")) { basis = 8; pos = 1; } else { basis = 10; pos = 0; } bool ok = false; unsigned long v = input.mid(pos).toULong(&ok, basis); // if not ok, return result of name parsing if(!ok) return state; // if ok, check if it fits in the number of bits unsigned nr_bits = m_field.last_bit - m_field.first_bit + 1; unsigned long max = nr_bits == 32 ? 0xffffffff : (1 << nr_bits) - 1; if(v <= max) { val = v; return Acceptable; } return state; } /** * RegLineEdit */ RegLineEdit::RegLineEdit(QWidget *parent) :QWidget(parent) { m_layout = new QHBoxLayout(this); m_button = new QToolButton(this); m_button->setCursor(Qt::ArrowCursor); m_button->setStyleSheet("QToolButton { font-weight: bold; color: white; background: black; }"); m_button->setPopupMode(QToolButton::InstantPopup); m_edit = new QLineEdit(this); m_layout->addWidget(m_button); m_layout->addWidget(m_edit); m_menu = new QMenu(this); connect(m_menu->addAction("Write"), SIGNAL(triggered()), this, SLOT(OnWriteAct())); connect(m_menu->addAction("Set"), SIGNAL(triggered()), this, SLOT(OnSetAct())); connect(m_menu->addAction("Clear"), SIGNAL(triggered()), this, SLOT(OnClearAct())); connect(m_menu->addAction("Toggle"), SIGNAL(triggered()), this, SLOT(OnToggleAct())); EnableSCT(false); SetReadOnly(false); ShowMode(true); SetMode(Write); } void RegLineEdit::SetReadOnly(bool ro) { m_edit->setReadOnly(ro); m_readonly = ro; ShowMode(!ro); } void RegLineEdit::EnableSCT(bool en) { m_has_sct = en; if(!m_has_sct) { m_button->setMenu(0); SetMode(Write); } else m_button->setMenu(m_menu); } RegLineEdit::~RegLineEdit() { } QLineEdit *RegLineEdit::GetLineEdit() { return m_edit; } void RegLineEdit::ShowMode(bool show) { if(show) m_button->show(); else m_button->hide(); } void RegLineEdit::OnWriteAct() { SetMode(Write); } void RegLineEdit::OnSetAct() { SetMode(Set); } void RegLineEdit::OnClearAct() { SetMode(Clear); } void RegLineEdit::OnToggleAct() { SetMode(Toggle); } void RegLineEdit::SetMode(EditMode mode) { m_mode = mode; switch(m_mode) { case Write: m_button->setText("WR"); break; case Set: m_button->setText("SET"); break; case Clear: m_button->setText("CLR"); break; case Toggle: m_button->setText("TOG"); break; default: break; } } RegLineEdit::EditMode RegLineEdit::GetMode() { return m_mode; } void RegLineEdit::setText(const QString& text) { m_edit->setText(text); } QString RegLineEdit::text() const { return m_edit->text(); } /** * SocFieldItemDelegate */ QString SocFieldItemDelegate::displayText(const QVariant& value, const QLocale& locale) const { if(value.type() == QVariant::UInt) return QString("0x%1").arg(value.toUInt(), (m_bitcount + 3) / 4, 16, QChar('0')); else return QStyledItemDelegate::displayText(value, locale); } /** * SocFieldEditor */ SocFieldEditor::SocFieldEditor(const soc_reg_field_t& field, QWidget *parent) :QLineEdit(parent), m_reg_field(field) { m_validator = new SocFieldValidator(field); setValidator(m_validator); } SocFieldEditor::~SocFieldEditor() { delete m_validator; } uint SocFieldEditor::field() const { soc_word_t v; /* in case validator fails to parse, return old value */ if(m_validator->parse(text(), v) == QValidator::Acceptable) return v; else return m_field; } void SocFieldEditor::setField(uint field) { m_field = field; int digits = (m_reg_field.last_bit - m_reg_field.first_bit + 4) / 4; setText(QString("0x%1").arg(field, digits, 16, QChar('0'))); } /** * SocFieldCachedItemDelegate */ QString SocFieldCachedItemDelegate::displayText(const QVariant& value, const QLocale& locale) const { // FIXME see QTBUG-30392 if(value.type() == QVariant::UserType && value.userType() == qMetaTypeId< SocFieldCachedValue >()) { const SocFieldCachedValue& v = value.value< SocFieldCachedValue >(); int bitcount = v.field().last_bit - v.field().first_bit; return QString("0x%1").arg(v.value(), (bitcount + 3) / 4, 16, QChar('0')); } else return QStyledItemDelegate::displayText(value, locale); } /** * SocFieldCachedEditor */ SocFieldCachedEditor::SocFieldCachedEditor(QWidget *parent) :SocFieldEditor(soc_reg_field_t(), parent) { } SocFieldCachedEditor::~SocFieldCachedEditor() { } SocFieldCachedValue SocFieldCachedEditor::value() const { return SocFieldCachedValue(m_value.field(), field()); } void SocFieldCachedEditor::setValue(SocFieldCachedValue val) { m_value = val; SetRegField(m_value.field()); setField(m_value.value()); } /** * SocFieldEditorCreator */ QWidget *SocFieldEditorCreator::createWidget(QWidget *parent) const { return new SocFieldEditor(m_field, parent); } QByteArray SocFieldEditorCreator::valuePropertyName() const { return QByteArray("field"); } /** * SocFieldCachedEditorCreator */ QWidget *SocFieldCachedEditorCreator::createWidget(QWidget *parent) const { return new SocFieldCachedEditor(parent); } QByteArray SocFieldCachedEditorCreator::valuePropertyName() const { return QByteArray("value"); } /** * RegSexyDisplay */ RegSexyDisplay::RegSexyDisplay(const SocRegRef& reg, QWidget *parent) :QWidget(parent), m_reg(reg) { m_size = QSize(); } int RegSexyDisplay::separatorSize() const { return 1; } int RegSexyDisplay::marginSize() const { return fontMetrics().height() / 3; } int RegSexyDisplay::textSep() const { return marginSize() / 2; } int RegSexyDisplay::headerHeight() const { return 2 * marginSize() + textSep() + 2 * fontMetrics().height(); } int RegSexyDisplay::columnWidth() const { return 2 * marginSize() + fontMetrics().height(); } int RegSexyDisplay::maxContentHeight() const { int max = 0; QFontMetrics metrics = fontMetrics(); for(size_t i = 0; i < m_reg.GetReg().field.size(); i++) { QString s = QString::fromStdString(m_reg.GetReg().field[i].name); // add extra spaces arounds s = " " + s + " "; max = qMax(max, metrics.boundingRect(s).width()); } return 2 * marginSize() + max; } int RegSexyDisplay::gapHeight() const { return marginSize() / 2; } QSize RegSexyDisplay::minimumSizeHint() const { /* cache computation because it's expensive */ if(m_size.isValid()) return m_size; /* width: display 32 columns + 33 vertical separators */ m_size.setWidth(32 * columnWidth() + 33 * separatorSize()); /* height: one separator + two digits + one separator + margin + separator * + names + separator */ m_size.setHeight(4 * separatorSize() + headerHeight() + gapHeight() + maxContentHeight()); return m_size; } QSize RegSexyDisplay::sizeHint() const { return minimumSizeHint(); } void RegSexyDisplay::paintEvent(QPaintEvent *event) { // FIXME could be optimised with QStaticText Q_UNUSED(event); int txt_h = fontMetrics().height(); int sep_sz = separatorSize(); int w = width(); int h = height() - 1; int col_w = (w - 33 * sep_sz) / 32; int hdr_h = headerHeight(); int gap_h = gapHeight(); int tot_w = 33 * sep_sz + 32 * col_w; int margin = marginSize(); int txt_sep = textSep(); int tot_hdr_sz = 2 * sep_sz + hdr_h; // computer xshift int x_shift = (w - tot_w) / 2; #define ith_col_x(i) (x_shift + (i) * (sep_sz + col_w)) QPainter painter(this); QBrush back_brush = palette().base(); QBrush line_brush = palette().dark(); // fill interesting zone with base painter.fillRect(x_shift, 0, tot_w, h, back_brush); // draw top and bottom lines painter.setPen(QPen(palette().dark(), sep_sz)); painter.fillRect(x_shift, 0, tot_w, sep_sz, line_brush); painter.fillRect(x_shift, h - sep_sz, tot_w, sep_sz, line_brush); // draw intemediate lines for(int i = 0; i <= 32; i++) painter.fillRect(ith_col_x(i), 0, sep_sz, 2 * sep_sz + hdr_h, line_brush); // draw bottom header lines painter.fillRect(ith_col_x(0), sep_sz + hdr_h, tot_w, sep_sz, line_brush); painter.fillRect(ith_col_x(0), tot_hdr_sz + gap_h, tot_w, sep_sz, line_brush); // redraw some lines but wider for(int i = 4; i < 32; i += 4) painter.fillRect(ith_col_x(i) - sep_sz, 0, 3 * sep_sz, tot_hdr_sz, line_brush); // draw numbers in the header painter.setPen(palette().brush(QPalette::ButtonText).color()); for(int i = 0; i < 32; i++) { QRect r(ith_col_x(i), sep_sz + margin, col_w, txt_h); painter.drawText(r, Qt::AlignCenter, QString("%1").arg((31 - i) / 10)); r.translate(0, txt_h + txt_sep); painter.drawText(r, Qt::AlignCenter, QString("%1").arg((31 - i) % 10)); } // display content for(size_t i = 0; i < m_reg.GetReg().field.size(); i++) { const soc_reg_field_t& field = m_reg.GetReg().field[i]; QRect r(QPoint(ith_col_x(31 - field.last_bit) + sep_sz, tot_hdr_sz), QPoint(ith_col_x(32 - field.first_bit), h - sep_sz)); painter.fillRect(r.x() - sep_sz, r.y(), sep_sz, r.height(), line_brush); painter.fillRect(r.right(), r.y(), sep_sz, r.height(), line_brush); r.setY(r.y() + gap_h + sep_sz); // draw rotated text painter.save(); painter.translate(r.bottomLeft()); painter.rotate(-90); //painter.fillRect(QRect(0, 0, r.height(), r.width()), QBrush(Qt::red)); QRect r2(0, 0, r.height(), r.width()); painter.drawText(r2, Qt::AlignCenter, QString::fromStdString(field.name)); painter.restore(); } #undef ith_col_x } /** * GrowingTextEdit */ GrowingTextEdit::GrowingTextEdit(QWidget *parent) :QTextEdit(parent) { connect(this, SIGNAL(textChanged()), this, SLOT(TextChanged())); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } void GrowingTextEdit::TextChanged() { int content_size = document()->documentLayout()->documentSize().height(); content_size = qMax(content_size, fontMetrics().height()); setFixedHeight(content_size + contentsMargins().top() + contentsMargins().bottom()); } /** * GrowingTableWidget */ GrowingTableWidget::GrowingTableWidget(QWidget *parent) :QTableWidget(parent) { connect(model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(DataChanged(const QModelIndex&, const QModelIndex&))); } void GrowingTableWidget::DataChanged(const QModelIndex& tl, const QModelIndex& br) { Q_UNUSED(tl); Q_UNUSED(br); resizeRowsToContents(); resizeColumnsToContents(); int h = contentsMargins().top() + contentsMargins().bottom(); h += horizontalHeader()->height(); for(int i = 0; i < rowCount(); i++) h += rowHeight(i); setMinimumHeight(h); } /** * MyTextEditor */ MyTextEditor::MyTextEditor(QWidget *parent) :QWidget(parent) { QVBoxLayout *layout = new QVBoxLayout; m_toolbar = new QToolBar(this); m_edit = new QTextEdit(this); layout->addWidget(m_toolbar, 0); layout->addWidget(m_edit, 1); setLayout(layout); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_edit->setAcceptRichText(false); m_edit->setAutoFormatting(QTextEdit::AutoAll); m_bold_button = new QToolButton(this); m_bold_button->setIcon(QIcon::fromTheme("format-text-bold")); m_bold_button->setText("bold"); m_bold_button->setCheckable(true); m_italic_button = new QToolButton(this); m_italic_button->setIcon(QIcon::fromTheme("format-text-italic")); m_italic_button->setText("italic"); m_italic_button->setCheckable(true); m_underline_button = new QToolButton(this); m_underline_button->setIcon(QIcon::fromTheme("format-text-underline")); m_underline_button->setText("underline"); m_underline_button->setCheckable(true); m_toolbar->addWidget(m_bold_button); m_toolbar->addWidget(m_italic_button); m_toolbar->addWidget(m_underline_button); connect(m_bold_button, SIGNAL(toggled(bool)), this, SLOT(OnTextBold(bool))); connect(m_italic_button, SIGNAL(toggled(bool)), this, SLOT(OnTextItalic(bool))); connect(m_underline_button, SIGNAL(toggled(bool)), this, SLOT(OnTextUnderline(bool))); connect(m_edit, SIGNAL(textChanged()), this, SLOT(OnInternalTextChanged())); connect(m_edit, SIGNAL(currentCharFormatChanged(const QTextCharFormat&)), this, SLOT(OnCharFormatChanged(const QTextCharFormat&))); SetGrowingMode(false); SetReadOnly(false); } void MyTextEditor::SetReadOnly(bool en) { m_read_only = en; if(en) m_toolbar->hide(); else m_toolbar->hide(); m_edit->setReadOnly(en); } void MyTextEditor::SetGrowingMode(bool en) { m_growing_mode = en; if(en) { m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); OnTextChanged(); } else { m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); } } void MyTextEditor::OnInternalTextChanged() { if(m_growing_mode) { int content_size = m_edit->document()->documentLayout()->documentSize().height(); content_size = qMax(content_size, m_edit->fontMetrics().height()); m_edit->setMinimumHeight(content_size + m_edit->contentsMargins().top() + m_edit->contentsMargins().bottom()); } emit OnTextChanged(); } void MyTextEditor::OnTextBold(bool checked) { QTextCursor cursor = m_edit->textCursor(); QTextCharFormat fmt = cursor.charFormat(); fmt.setFontWeight(checked ? QFont::Bold : QFont::Normal); cursor.setCharFormat(fmt); m_edit->setTextCursor(cursor); } void MyTextEditor::OnTextItalic(bool checked) { QTextCursor cursor = m_edit->textCursor(); QTextCharFormat fmt = cursor.charFormat(); fmt.setFontItalic(checked); cursor.setCharFormat(fmt); m_edit->setTextCursor(cursor); } void MyTextEditor::OnTextUnderline(bool checked) { QTextCursor cursor = m_edit->textCursor(); QTextCharFormat fmt = cursor.charFormat(); fmt.setFontUnderline(checked); cursor.setCharFormat(fmt); m_edit->setTextCursor(cursor); } void MyTextEditor::OnCharFormatChanged(const QTextCharFormat& fmt) { /* NOTE: changing the button states programmaticaly doesn't trigger * the toggled() signals, otherwise it would result in a loop * between this function and OnText{Bold,Italic,Underline,...} */ m_bold_button->setChecked(fmt.fontWeight() > QFont::Normal); m_italic_button->setChecked(fmt.fontItalic()); m_underline_button->setChecked(fmt.fontUnderline()); } void MyTextEditor::SetTextHtml(const QString& text) { m_edit->setHtml(text); } QString MyTextEditor::GetTextHtml() { return m_edit->toPlainText(); } bool MyTextEditor::IsModified() { return m_edit->document()->isModified(); } /** * MySwitchableTextEditor */ MySwitchableTextEditor::MySwitchableTextEditor(QWidget *parent) :QWidget(parent) { QVBoxLayout *layout = new QVBoxLayout(this); m_edit = new MyTextEditor(this); m_label = new QLabel(this); m_label->setTextFormat(Qt::RichText); m_label->setAlignment(Qt::AlignTop); m_line = new QLineEdit(this); layout->addWidget(m_label); layout->addWidget(m_edit); layout->addWidget(m_line); setLayout(layout); m_editor_mode = false; m_line_mode = false; UpdateVisibility(); } void MySwitchableTextEditor::SetEditorMode(bool edit) { if(edit == m_editor_mode) return; QString text = GetTextHtml(); m_editor_mode = edit; UpdateVisibility(); SetTextHtml(text); } QString MySwitchableTextEditor::GetTextHtml() { if(m_editor_mode) return m_line_mode ? m_line->text() : m_edit->GetTextHtml(); else return m_label->text(); } void MySwitchableTextEditor::SetTextHtml(const QString& text) { if(m_editor_mode) { if(m_line_mode) m_line->setText(text); else m_edit->SetTextHtml(text); } else m_label->setText(text); } MyTextEditor *MySwitchableTextEditor::GetEditor() { return m_edit; } void MySwitchableTextEditor::SetLineMode(bool en) { if(m_line_mode == en) return; QString text = GetTextHtml(); m_line_mode = en; SetTextHtml(text); UpdateVisibility(); } QLineEdit *MySwitchableTextEditor::GetLineEdit() { return m_line; } void MySwitchableTextEditor::UpdateVisibility() { m_label->setVisible(!m_editor_mode); m_edit->setVisible(m_editor_mode && !m_line_mode); m_line->setVisible(m_editor_mode && m_line_mode); } QLabel *MySwitchableTextEditor::GetLabel() { return m_label; } bool MySwitchableTextEditor::IsModified() { if(!m_editor_mode) return false; return m_line_mode ? m_line->isModified() : m_edit->IsModified(); }