/*************************************************************************** * __________ __ ___. * 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 "utils.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'))); } void SocFieldEditor::SetRegField(const soc_reg_field_t& field) { setValidator(0); delete m_validator; m_validator = new SocFieldValidator(field); setValidator(m_validator); m_reg_field = field; } /** * SocFieldCachedValue */ SocFieldCachedValue::SocFieldCachedValue(const soc_reg_field_t& field, uint value) :m_field(field), m_value(value) { int idx = field.find_value(value); if(idx != -1) m_name = QString::fromStdString(field.value[idx].name); } /** * SocFieldCachedItemDelegate */ SocFieldCachedItemDelegate::SocFieldCachedItemDelegate(QObject *parent) :QStyledItemDelegate(parent) { m_mode = DisplayValueAndName; } 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; QString name = v.value_name(); QString strval = QString("0x%1").arg(v.value(), (bitcount + 3) / 4, 16, QChar('0')); switch(m_mode) { case DisplayName: if(name.size() > 0) return name; /* fallthrough */ case DisplayValueAndName: if(name.size() > 0) return QString("%1 (%2)").arg(strval).arg(name); /* fallthrough */ case DisplayValue: default: return strval; } } else if(value.type() == QVariant::UserType && value.userType() == qMetaTypeId< SocFieldBitRange >()) { const SocFieldBitRange& br = value.value< SocFieldBitRange >(); if(br.GetFirstBit() == br.GetLastBit()) return QString("%1").arg(br.GetFirstBit()); else return QString("%1:%2").arg(br.GetLastBit()).arg(br.GetFirstBit()); } 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"); } /** * RegFieldTableModel */ RegFieldTableModel::RegFieldTableModel(QObject *parent) :QAbstractTableModel(parent) { m_read_only = true; } int RegFieldTableModel::rowCount(const QModelIndex& /* parent */) const { return m_reg.field.size(); } int RegFieldTableModel::columnCount(const QModelIndex& /* parent */) const { return ColumnCountOffset + m_value.size(); } QVariant RegFieldTableModel::data(const QModelIndex& index, int role) const { int section = index.column(); const soc_reg_field_t& field = m_reg.field[index.row()]; /* column independent code */ const RegThemeGroup *theme = 0; switch(m_status[index.row()]) { case Normal: theme = &m_theme.normal; break; case Diff: theme = &m_theme.diff; break; case Error: theme = &m_theme.error; break; case None: default: break; } if(role == Qt::FontRole) return theme ? QVariant(theme->font) : QVariant(); if(role == Qt::BackgroundRole) return theme ? QVariant(theme->background) : QVariant(); if(role == Qt::ForegroundRole) return theme ? QVariant(theme->foreground) : QVariant(); /* column dependent code */ if(section == BitRangeColumn) { if(role == Qt::DisplayRole) return QVariant::fromValue(SocFieldBitRange(field)); else if(role == Qt::TextAlignmentRole) return QVariant(Qt::AlignVCenter | Qt::AlignHCenter); else return QVariant(); } if(section == NameColumn) { if(role == Qt::DisplayRole) return QVariant(QString::fromStdString(field.name)); else return QVariant(); } if(section < FirstValueColumn + m_value.size()) { int idx = section - FirstValueColumn; if(role == Qt::DisplayRole) { if(!m_value[idx].isValid()) return QVariant(""); return QVariant::fromValue(SocFieldCachedValue(field, field.extract(m_value[idx].value< soc_word_t >()))); } else if(role == Qt::EditRole) { if(!m_value[idx].isValid()) return QVariant(); return QVariant::fromValue(SocFieldCachedValue(field, field.extract(m_value[idx].value< soc_word_t >()))); } else if(role == Qt::TextAlignmentRole) return QVariant(Qt::AlignVCenter | Qt::AlignHCenter); else return QVariant(); } section -= m_value.size(); if(section == DescColumnOffset) { if(role == Qt::DisplayRole) return QVariant(QString::fromStdString(field.desc)); else return QVariant(); } return QVariant(); } bool RegFieldTableModel::setData(const QModelIndex& idx, const QVariant& value, int role) { if(role != Qt::EditRole) return false; int section = idx.column(); if(section < FirstValueColumn || section >= FirstValueColumn + m_value.size()) return false; section -= FirstValueColumn; const SocFieldCachedValue& v = value.value< SocFieldCachedValue >(); if(!m_value[section].isValid()) return false; soc_word_t old_val = m_value[section].value< soc_word_t >(); m_value[section] = QVariant(v.field().replace(old_val, v.value())); // update column RecomputeTheme(); emit dataChanged(index(0, section), index(rowCount() - 1, section)); emit OnValueModified(section); return true; } Qt::ItemFlags RegFieldTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; int section = index.column(); if(section < FirstValueColumn || section >= FirstValueColumn + m_value.size()) return flags; section -= FirstValueColumn; if(m_value[section].isValid() && !m_read_only) flags |= Qt::ItemIsEditable; return flags; } QVariant RegFieldTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Vertical) return QVariant(); if(role != Qt::DisplayRole) return QVariant(); if(section == BitRangeColumn) return QVariant("Bits"); if(section == NameColumn) return QVariant("Name"); if(section < FirstValueColumn + m_value.size()) { int idx = section - FirstValueColumn; if(m_value.size() == 1) return QVariant("Value"); else return QVariant(QString("Value %1").arg((QChar)('A' + idx))); } section -= m_value.size(); if(section == DescColumnOffset) return QVariant("Description"); return QVariant(); } void RegFieldTableModel::SetReadOnly(bool en) { if(en == m_read_only) return; m_read_only = en; } void RegFieldTableModel::SetRegister(const soc_reg_t& reg) { /* remove all rows */ beginResetModel(); m_reg.field.clear(); endResetModel(); /* add them all */ beginInsertRows(QModelIndex(), 0, reg.field.size() - 1); m_reg = reg; RecomputeTheme(); endInsertRows(); } void RegFieldTableModel::SetValues(const QVector< QVariant >& values) { /* remove all value columns */ beginRemoveColumns(QModelIndex(), FirstValueColumn, FirstValueColumn + m_value.size() - 1); m_value.clear(); endRemoveColumns(); /* add them back */ beginInsertColumns(QModelIndex(), FirstValueColumn, FirstValueColumn + values.size() - 1); m_value = values; RecomputeTheme(); endInsertColumns(); } QVariant RegFieldTableModel::GetValue(int index) { return m_value[index]; } void RegFieldTableModel::SetTheme(const RegTheme& theme) { m_theme = theme; RecomputeTheme(); emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); } void RegFieldTableModel::RecomputeTheme() { m_status.resize(m_reg.field.size()); for(size_t i = 0; i < m_reg.field.size(); i++) { m_status[i] = None; if(!m_theme.valid || m_value.size() == 0) continue; m_status[i] = Normal; const soc_reg_field_t& field = m_reg.field[i]; QVariant val; for(int j = 0; j < m_value.size(); j++) { QVariant val2 = m_value[j]; if(!val2.isValid()) continue; val2 = QVariant(field.extract(val2.value< soc_word_t >())); if(!val.isValid()) val = val2; else if(val != val2) m_status[i] = Diff; } } } /** * RegSexyDisplay2 */ RegSexyDisplay2::RegSexyDisplay2(QWidget *parent) :QAbstractItemView(parent) { m_is_dirty = true; // the frame around the register is ugly, disable it setFrameShape(QFrame::NoFrame); } QModelIndex RegSexyDisplay2::indexAt(const QPoint& point) const { Q_UNUSED(point); return QModelIndex(); } void RegSexyDisplay2::scrollTo(const QModelIndex& index, ScrollHint hint) { Q_UNUSED(index); Q_UNUSED(hint); } QRect RegSexyDisplay2::visualRect(const QModelIndex& index) const { Q_UNUSED(index); return QRect(); } bool RegSexyDisplay2::isIndexHidden(const QModelIndex& index) const { Q_UNUSED(index); return false; } QModelIndex RegSexyDisplay2::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) { Q_UNUSED(cursorAction); Q_UNUSED(modifiers); return QModelIndex(); } void RegSexyDisplay2::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags) { Q_UNUSED(rect); Q_UNUSED(flags); } int RegSexyDisplay2::verticalOffset() const { return verticalScrollBar()->value(); } int RegSexyDisplay2::horizontalOffset() const { return horizontalScrollBar()->value(); } void RegSexyDisplay2::scrollContentsBy(int dx, int dy) { viewport()->scroll(dx, dy); } void RegSexyDisplay2::setModel(QAbstractItemModel *model) { QAbstractItemView::setModel(model); m_is_dirty = true; } void RegSexyDisplay2::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { m_is_dirty = true; QAbstractItemView::dataChanged(topLeft, bottomRight); } void RegSexyDisplay2::rowsInserted(const QModelIndex &parent, int start, int end) { m_is_dirty = true; QAbstractItemView::rowsInserted(parent, start, end); } void RegSexyDisplay2::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { m_is_dirty = true; QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); } int RegSexyDisplay2::GetSeparatorSize() const { return 1; } int RegSexyDisplay2::GetMarginSize() const { return viewOptions().fontMetrics.height() / 3; } int RegSexyDisplay2::GetHeaderTextSep() const { return GetMarginSize() / 2; } int RegSexyDisplay2::GetHeaderHeight() const { return 2 * GetMarginSize() + GetHeaderTextSep() + 2 * viewOptions().fontMetrics.height(); } int RegSexyDisplay2::GetColumnWidth() const { return 2 * GetMarginSize() + viewOptions().fontMetrics.height(); } int RegSexyDisplay2::GetMaxContentHeight() const { int max = 0; QFontMetrics metrics = viewOptions().fontMetrics; if(model()) { for(int i = 0; i < model()->rowCount(); i++) { QModelIndex index = model()->index(i, 1, rootIndex()); QString s = model()->data(index).toString(); max = qMax(max, metrics.boundingRect(s).width()); } } return 2 * GetMarginSize() + max; } int RegSexyDisplay2::GetGapHeight() const { return GetMarginSize() / 2; } QRegion RegSexyDisplay2::visualRegionForSelection(const QItemSelection& selection) const { Q_UNUSED(selection); return QRegion(); } void RegSexyDisplay2::paintEvent(QPaintEvent *event) { Q_UNUSED(event); int txt_h = viewOptions().fontMetrics.height(); int sep_sz = GetSeparatorSize(); int w = qMax(m_minimum_width, viewport()->width()); int h = qMax(m_minimum_height, viewport()->height()); int nr_bits = 32; int col_w = (w - (nr_bits + 1) * sep_sz) / nr_bits; int hdr_h = GetHeaderHeight(); int gap_h = GetGapHeight(); int tot_w = (nr_bits + 1) * sep_sz + nr_bits * col_w; int margin = GetMarginSize(); int txt_sep = GetHeaderTextSep(); int tot_hdr_sz = 2 * sep_sz + hdr_h; int x_shift = (w - tot_w) / 2; #define ith_col_x(i) (x_shift + (i) * (sep_sz + col_w)) QPainter painter(viewport()); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); painter.translate(-horizontalScrollBar()->value(), -verticalScrollBar()->value()); QStyleOptionViewItem option = viewOptions(); QBrush back_brush = option.palette.base(); QBrush line_brush = option.palette.dark(); // fill interesting zone with base painter.fillRect(event->rect(), option.palette.window()); painter.fillRect(x_shift, 0, tot_w, h, back_brush); // draw top and bottom lines painter.setPen(QPen(line_brush, 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 < nr_bits; 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 < nr_bits; i++) { QRect r(ith_col_x(i), sep_sz + margin, col_w, txt_h); painter.drawText(r, Qt::AlignCenter, QString("%1").arg((nr_bits - 1 - i) / 10)); r.translate(0, txt_h + txt_sep); painter.drawText(r, Qt::AlignCenter, QString("%1").arg((nr_bits - 1 - i) % 10)); } // display content if(model()) { for(int i = 0; i < model()->rowCount(); i++) { QVariant vrange = model()->data(model()->index(i, 0, rootIndex())); if(!vrange.canConvert< SocFieldBitRange >()) continue; SocFieldBitRange range = vrange.value< SocFieldBitRange >(); QString name = model()->data(model()->index(i, 1, rootIndex())).toString(); QRect r(QPoint(ith_col_x(nr_bits - 1 - range.GetLastBit()) + sep_sz, tot_hdr_sz), QPoint(ith_col_x(nr_bits - range.GetFirstBit()), 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, name); painter.restore(); } } #undef ith_col_x } void RegSexyDisplay2::RecomputeGeometry() { if(!m_is_dirty) return; /* height: header + gap + sep + content + sep */ m_minimum_height = 0; m_minimum_height += GetHeaderHeight() + GetGapHeight(); m_minimum_height += 2 * GetSeparatorSize() + GetMaxContentHeight(); /* width: sep + (col + sep) * n */ m_minimum_width = GetSeparatorSize() * 33 + GetColumnWidth() * 32; m_is_dirty = false; viewport()->update(); } void RegSexyDisplay2::resizeEvent(QResizeEvent*) { m_is_dirty = true; RecomputeGeometry(); updateGeometries(); } void RegSexyDisplay2::updateGeometries() { horizontalScrollBar()->setSingleStep(1); horizontalScrollBar()->setPageStep(viewport()->width()); horizontalScrollBar()->setRange(0, qMax(0, m_minimum_width - viewport()->width())); verticalScrollBar()->setSingleStep(1); verticalScrollBar()->setPageStep(viewport()->height()); verticalScrollBar()->setRange(0, qMax(0, m_minimum_height - viewport()->height())); } /** * GrowingTableView */ GrowingTableView::GrowingTableView(QWidget *parent) :QTableView(parent) { } void GrowingTableView::setModel(QAbstractItemModel *m) { if(model()) disconnect(model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(DataChanged(const QModelIndex&, const QModelIndex&))); QTableView::setModel(m); connect(model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(DataChanged(const QModelIndex&, const QModelIndex&))); DataChanged(QModelIndex(), QModelIndex()); } void GrowingTableView::DataChanged(const QModelIndex& tl, const QModelIndex& br) { Q_UNUSED(tl); Q_UNUSED(br); resizeColumnsToContents(); int h = contentsMargins().top() + contentsMargins().bottom(); h += horizontalHeader()->height(); for(int i = 0; i < model()->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->show(); 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(); } /** * BackendSelector */ BackendSelector::BackendSelector(Backend *backend, QWidget *parent) :QWidget(parent), m_backend(backend) { m_data_selector = new QComboBox(this); m_data_selector->addItem(QIcon::fromTheme("text-x-generic"), "Nothing...", QVariant(DataSelNothing)); m_data_selector->addItem(QIcon::fromTheme("document-open"), "File...", QVariant(DataSelFile)); #ifdef HAVE_HWSTUB m_data_selector->addItem(QIcon::fromTheme("multimedia-player"), "Device...", QVariant(DataSelDevice)); #endif m_data_sel_edit = new QLineEdit(this); m_data_sel_edit->setReadOnly(true); m_nothing_text = new QLabel(this); m_nothing_text->setTextFormat(Qt::RichText); QHBoxLayout *data_sel_layout = new QHBoxLayout(this); data_sel_layout->addWidget(m_data_selector); data_sel_layout->addWidget(m_data_sel_edit, 1); data_sel_layout->addWidget(m_nothing_text, 1); data_sel_layout->addStretch(0); #ifdef HAVE_HWSTUB m_dev_selector = new QComboBox; data_sel_layout->addWidget(m_dev_selector, 1); #endif m_io_backend = m_backend->CreateDummyIoBackend(); connect(m_data_selector, SIGNAL(activated(int)), this, SLOT(OnDataSelChanged(int))); #ifdef HAVE_HWSTUB connect(m_dev_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(OnDevChanged(int))); connect(&m_hwstub_helper, SIGNAL(OnDevListChanged(bool, struct libusb_device *)), this, SLOT(OnDevListChanged2(bool, struct libusb_device *))); #endif OnDataSelChanged(0); } BackendSelector::~BackendSelector() { #ifdef HAVE_HWSTUB ClearDevList(); #endif delete m_io_backend; } void BackendSelector::SetNothingMessage(const QString& msg) { m_nothing_text->setText(msg); } void BackendSelector::OnDataSelChanged(int index) { if(index == -1) return; QVariant var = m_data_selector->itemData(index); if(var == DataSelFile) { m_nothing_text->hide(); m_data_sel_edit->show(); #ifdef HAVE_HWSTUB m_dev_selector->hide(); #endif QFileDialog *fd = new QFileDialog(m_data_selector); fd->setFilter("Textual files (*.txt);;All files (*)"); fd->setDirectory(Settings::Get()->value("regtab/loaddatadir", QDir::currentPath()).toString()); if(fd->exec()) { QStringList filenames = fd->selectedFiles(); ChangeBackend(m_backend->CreateFileIoBackend(filenames[0])); m_data_sel_edit->setText(filenames[0]); } Settings::Get()->setValue("regtab/loaddatadir", fd->directory().absolutePath()); } #ifdef HAVE_HWSTUB else if(var == DataSelDevice) { m_nothing_text->hide(); m_data_sel_edit->hide(); m_dev_selector->show(); OnDevListChanged(); } #endif else { m_data_sel_edit->hide(); m_nothing_text->show(); #ifdef HAVE_HWSTUB m_dev_selector->hide(); #endif ChangeBackend(m_backend->CreateDummyIoBackend()); } } #ifdef HAVE_HWSTUB void BackendSelector::OnDevListChanged2(bool arrived, struct libusb_device *dev) { Q_UNUSED(arrived); Q_UNUSED(dev); OnDevListChanged(); } void BackendSelector::OnDevListChanged() { ClearDevList(); QList< HWStubDevice* > list = m_hwstub_helper.GetDevList(); foreach(HWStubDevice *dev, list) { QString name = QString("Bus %1 Device %2: %3").arg(dev->GetBusNumber()) .arg(dev->GetDevAddress()).arg(dev->GetTargetInfo().bName); m_dev_selector->addItem(QIcon::fromTheme("multimedia-player"), name, QVariant::fromValue((void *)dev)); } if(list.size() > 0) m_dev_selector->setCurrentIndex(0); } void BackendSelector::OnDevChanged(int index) { if(index == -1) return; HWStubDevice *dev = reinterpret_cast< HWStubDevice* >(m_dev_selector->itemData(index).value< void* >()); delete m_io_backend; /* NOTE: make a copy of the HWStubDevice device because the one in the list * might get destroyed when clearing the list while the backend is still * active: this would result in a double free when the backend is also destroyed */ m_io_backend = m_backend->CreateHWStubIoBackend(new HWStubDevice(dev)); emit OnSelect(m_io_backend); } void BackendSelector::ClearDevList() { while(m_dev_selector->count() > 0) { HWStubDevice *dev = reinterpret_cast< HWStubDevice* >(m_dev_selector->itemData(0).value< void* >()); delete dev; m_dev_selector->removeItem(0); } } #endif IoBackend *BackendSelector::GetBackend() { return m_io_backend; } void BackendSelector::ChangeBackend(IoBackend *new_backend) { /* WARNING: delete old backend *after* calling the signal, otherwise the old backend * might get used after delete */ emit OnSelect(new_backend); delete m_io_backend; m_io_backend = new_backend; } /** * MessageWidget */ MessageWidget::MessageWidget(QWidget *parent) :QFrame(parent) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); m_icon = new QLabel(this); m_icon->hide(); m_text = new QLabel(this); m_text->setTextFormat(Qt::RichText); m_close = new QToolButton(this); m_close->setText("close"); m_close->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton)); m_close->setAutoRaise(true); QHBoxLayout *layout = new QHBoxLayout(this); layout->addWidget(m_icon, 0); layout->addWidget(m_text, 1); layout->addWidget(m_close, 0); m_id = 0; connect(m_close, SIGNAL(clicked(bool)), this, SLOT(OnClose(bool))); hide(); } MessageWidget::~MessageWidget() { } void MessageWidget::UpdateType() { /* style stolen from KMessageWidget */ QColor bg, border; switch(m_type) { case Positive: bg.setRgb(140, 228, 124); border.setRgb(56, 175, 58); break; case Information: bg.setRgb(161, 178, 202); border.setRgb(59, 79, 175); break; case Warning: bg.setRgb(228, 227, 127); border.setRgb(175, 169, 61); break; case Error: bg.setRgb(233, 199, 196); border.setRgb(175, 74, 60); break; default: break; } setStyleSheet(QString( "QFrame { background-color: %1;" "border-radius: 5px;" "border: 1px solid %2;" "}" "QLabel { border: none; }") .arg(bg.name()) .arg(border.name())); } int MessageWidget::SetMessage(MessageType type, const QString& msg) { m_type = type; m_text->setText(msg); UpdateType(); show(); return ++m_id; } void MessageWidget::HideMessage(int id) { if(m_id == id) OnClose(true); } void MessageWidget::OnClose(bool clicked) { Q_UNUSED(clicked); hide(); }