A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 2220 lines 66 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2014 by Amaury Pouly 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21#include "utils.h" 22#include <QFontMetrics> 23#include <QPainter> 24#include <QTextDocument> 25#include <QAbstractTextDocumentLayout> 26#include <QHeaderView> 27#include <QDebug> 28#include <QElapsedTimer> 29#include <QXmlStreamReader> 30#include <QXmlStreamWriter> 31#include <QTextBlock> 32#include <QApplication> 33#include <QPushButton> 34#include <QMessageBox> 35#include <QStandardItemModel> 36 37/** 38 * SocBitRangeValidator 39 */ 40SocBitRangeValidator::SocBitRangeValidator(QObject *parent) 41 :QValidator(parent) 42{ 43 m_width = 32; 44} 45 46void SocBitRangeValidator::fixup(QString& input) const 47{ 48 input = input.trimmed(); 49} 50 51QValidator::State SocBitRangeValidator::validate(QString& input, int& pos) const 52{ 53 Q_UNUSED(pos); 54 int first, last; 55 State state = parse(input, last, first); 56 return state; 57} 58 59QValidator::State SocBitRangeValidator::parse(const QString& input, int& last, int& first) const 60{ 61 // the empty string is always intermediate 62 if(input.size() == 0) 63 return Intermediate; 64 // check if there is ':' 65 int pos = input.indexOf(':'); 66 if(pos == -1) 67 pos = input.size(); 68 // if field start with ':', the last bit is implicit and is 31 69 if(pos > 0) 70 { 71 // parse last bit and check it's between 0 and 31 72 bool ok = false; 73 last = input.left(pos).toInt(&ok); 74 if(!ok || last < 0 || last >= m_width) 75 return Invalid; 76 } 77 else 78 last = m_width - 1; 79 // parse first bit 80 if(pos < input.size() - 1) 81 { 82 bool ok = false; 83 first = input.mid(pos + 1).toInt(&ok); 84 if(!ok || first < 0 || first > last) 85 return Invalid; 86 } 87 // if input ends with ':', first bit is implicit and is 0 88 else if(pos == input.size() - 1) 89 first = 0; 90 // if there no ':', first=last 91 else 92 first = last; 93 return Acceptable; 94} 95 96void SocBitRangeValidator::setWidth(int nr_bits) 97{ 98 m_width = nr_bits; 99} 100 101QString SocBitRangeValidator::generate(int last_bit, int first_bit) const 102{ 103 if(last_bit == first_bit) 104 return QString("%1").arg(first_bit); 105 else 106 return QString("%1:%2").arg(last_bit).arg(first_bit); 107} 108 109/** 110 * SocFieldValidator 111 */ 112 113SocFieldValidator::SocFieldValidator(QObject *parent) 114 :QValidator(parent) 115{ 116 m_field.pos = 0; 117 m_field.width = 32; 118} 119 120SocFieldValidator::SocFieldValidator(const soc_desc::field_t& field, QObject *parent) 121 :QValidator(parent), m_field(field) 122{ 123} 124 125void SocFieldValidator::fixup(QString& input) const 126{ 127 input = input.trimmed(); 128} 129 130QValidator::State SocFieldValidator::validate(QString& input, int& pos) const 131{ 132 Q_UNUSED(pos); 133 soc_word_t val; 134 State state = parse(input, val); 135 return state; 136} 137 138QValidator::State SocFieldValidator::parse(const QString& input, soc_word_t& val) const 139{ 140 // the empty string is always intermediate 141 if(input.size() == 0) 142 return Intermediate; 143 // first check named values 144 State state = Invalid; 145 foreach(const soc_desc::enum_t& value, m_field.enum_) 146 { 147 QString name = QString::fromLocal8Bit(value.name.c_str()); 148 // cannot be a substring if too long or empty 149 if(input.size() > name.size()) 150 continue; 151 // check equal string 152 if(input == name) 153 { 154 state = Acceptable; 155 val = value.value; 156 break; 157 } 158 // check substring 159 if(name.startsWith(input)) 160 state = Intermediate; 161 } 162 // early return for exact match 163 if(state == Acceptable) 164 return state; 165 // do a few special cases for convenience 166 if(input.compare("0x", Qt::CaseInsensitive) == 0 || 167 input.compare("0b", Qt::CaseInsensitive) == 0) 168 return Intermediate; 169 // try by parsing 170 unsigned basis, pos; 171 if(input.size() >= 2 && input.startsWith("0x", Qt::CaseInsensitive)) 172 { 173 basis = 16; 174 pos = 2; 175 } 176 else if(input.size() >= 2 && input.startsWith("0b", Qt::CaseInsensitive)) 177 { 178 basis = 2; 179 pos = 2; 180 } 181 else if(input.size() >= 2 && input.startsWith("0")) 182 { 183 basis = 8; 184 pos = 1; 185 } 186 else 187 { 188 basis = 10; 189 pos = 0; 190 } 191 bool ok = false; 192 unsigned long v = input.mid(pos).toULong(&ok, basis); 193 // if not ok, return result of name parsing 194 if(!ok) 195 return state; 196 // if ok, check if it fits in the number of bits 197 unsigned nr_bits = m_field.width; 198 unsigned long max = nr_bits == 32 ? 0xffffffff : (1 << nr_bits) - 1; 199 if(v <= max) 200 { 201 val = v; 202 return Acceptable; 203 } 204 205 return state; 206} 207 208/** 209 * RegLineEdit 210 */ 211RegLineEdit::RegLineEdit(QWidget *parent) 212 :QWidget(parent) 213{ 214 m_layout = new QHBoxLayout(this); 215 m_button = new QToolButton(this); 216 m_button->setCursor(Qt::ArrowCursor); 217 m_button->setStyleSheet("QToolButton { font-weight: bold; color: white; background: black; }"); 218 m_button->setPopupMode(QToolButton::InstantPopup); 219 m_edit = new QLineEdit(this); 220 m_layout->addWidget(m_button); 221 m_layout->addWidget(m_edit); 222 m_menu = new QMenu(this); 223 connect(m_menu->addAction("Write"), SIGNAL(triggered()), this, SLOT(OnWriteAct())); 224 connect(m_menu->addAction("Set"), SIGNAL(triggered()), this, SLOT(OnSetAct())); 225 connect(m_menu->addAction("Clear"), SIGNAL(triggered()), this, SLOT(OnClearAct())); 226 connect(m_menu->addAction("Toggle"), SIGNAL(triggered()), this, SLOT(OnToggleAct())); 227 EnableSCT(false); 228 SetReadOnly(false); 229 ShowMode(true); 230 SetMode(Write); 231} 232 233void RegLineEdit::SetReadOnly(bool ro) 234{ 235 m_edit->setReadOnly(ro); 236 m_readonly = ro; 237 ShowMode(!ro); 238} 239 240void RegLineEdit::EnableSCT(bool en) 241{ 242 m_has_sct = en; 243 if(!m_has_sct) 244 { 245 m_button->setMenu(0); 246 SetMode(Write); 247 } 248 else 249 m_button->setMenu(m_menu); 250} 251 252RegLineEdit::~RegLineEdit() 253{ 254} 255 256QLineEdit *RegLineEdit::GetLineEdit() 257{ 258 return m_edit; 259} 260 261void RegLineEdit::ShowMode(bool show) 262{ 263 if(show) 264 m_button->show(); 265 else 266 m_button->hide(); 267} 268 269void RegLineEdit::OnWriteAct() 270{ 271 SetMode(Write); 272} 273 274void RegLineEdit::OnSetAct() 275{ 276 SetMode(Set); 277} 278 279void RegLineEdit::OnClearAct() 280{ 281 SetMode(Clear); 282} 283 284void RegLineEdit::OnToggleAct() 285{ 286 SetMode(Toggle); 287} 288 289void RegLineEdit::SetMode(EditMode mode) 290{ 291 m_mode = mode; 292 switch(m_mode) 293 { 294 case Write: m_button->setText("WR"); break; 295 case Set: m_button->setText("SET"); break; 296 case Clear: m_button->setText("CLR"); break; 297 case Toggle: m_button->setText("TOG"); break; 298 default: break; 299 } 300} 301 302RegLineEdit::EditMode RegLineEdit::GetMode() 303{ 304 return m_mode; 305} 306 307void RegLineEdit::setText(const QString& text) 308{ 309 m_edit->setText(text); 310} 311 312QString RegLineEdit::text() const 313{ 314 return m_edit->text(); 315} 316 317/** 318 * SocFieldItemDelegate 319 */ 320 321QString SocFieldItemDelegate::displayText(const QVariant& value, const QLocale& locale) const 322{ 323 if(value.type() == QVariant::UInt) 324 return QString("0x%1").arg(value.toUInt(), (m_bitcount + 3) / 4, 16, QChar('0')); 325 else 326 return QStyledItemDelegate::displayText(value, locale); 327} 328 329void SocFieldItemDelegate::setWidth(int bitcount) 330{ 331 m_bitcount = bitcount; 332} 333 334/** 335 * SocFieldEditor 336 */ 337SocFieldEditor::SocFieldEditor(const soc_desc::field_t& field, QWidget *parent) 338 :QLineEdit(parent), m_reg_field(field) 339{ 340 m_validator = new SocFieldValidator(field); 341 setValidator(m_validator); 342 connect(this, SIGNAL(editingFinished()), this, SLOT(editDone())); 343 setAlignment(Qt::AlignCenter); 344 setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum); 345} 346 347SocFieldEditor::~SocFieldEditor() 348{ 349 delete m_validator; 350} 351 352void SocFieldEditor::editDone() 353{ 354 emit editingFinished(field()); 355} 356 357uint SocFieldEditor::field() const 358{ 359 soc_word_t v; 360 /* in case validator fails to parse, return old value */ 361 if(m_validator->parse(text(), v) == QValidator::Acceptable) 362 return v; 363 else 364 return m_field; 365} 366 367void SocFieldEditor::setField(uint field) 368{ 369 m_field = field; 370 int digits = (m_reg_field.width + 3) / 4; 371 setText(QString("0x%1").arg(field, digits, 16, QChar('0'))); 372} 373 374void SocFieldEditor::SetRegField(const soc_desc::field_t& field) 375{ 376 setValidator(0); 377 delete m_validator; 378 m_validator = new SocFieldValidator(field); 379 setValidator(m_validator); 380 m_reg_field = field; 381} 382 383/** 384 * SocFieldCachedValue 385 */ 386SocFieldCachedValue::SocFieldCachedValue(const soc_desc::field_t& field, uint value) 387 :m_field(field), m_value(value) 388{ 389 int idx = field.find_value(value); 390 if(idx != -1) 391 m_name = QString::fromStdString(field.enum_[idx].name); 392} 393 394bool SocFieldCachedValue::operator<(const SocFieldCachedValue& o) const 395{ 396 return m_value < o.m_value; 397} 398 399/** 400 * SocFieldBitRange 401 */ 402 403bool SocFieldBitRange::operator<(const SocFieldBitRange& o) const 404{ 405 if(m_first_bit < o.m_first_bit) 406 return true; 407 if(m_first_bit > o.m_first_bit) 408 return false; 409 return m_last_bit < o.m_last_bit; 410} 411 412bool SocFieldBitRange::operator!=(const SocFieldBitRange& o) const 413{ 414 return m_first_bit != o.m_first_bit || m_last_bit != o.m_last_bit; 415} 416 417/** 418 * SocFieldCachedItemDelegate 419 */ 420 421SocFieldCachedItemDelegate::SocFieldCachedItemDelegate(QObject *parent) 422 :QStyledItemDelegate(parent) 423{ 424 m_mode = DisplayValueAndName; 425} 426 427QString SocFieldCachedItemDelegate::displayText(const QVariant& value, const QLocale& locale) const 428{ 429 if(isUserType< SocFieldCachedValue >(value)) 430 { 431 const SocFieldCachedValue& v = value.value< SocFieldCachedValue >(); 432 int bitcount = v.field().width; 433 QString name = v.value_name(); 434 QString strval = QString("0x%1").arg(v.value(), (bitcount + 3) / 4, 16, QChar('0')); 435 switch(m_mode) 436 { 437 case DisplayName: 438 if(name.size() > 0) 439 return name; 440 /* fallthrough */ 441 case DisplayValueAndName: 442 if(name.size() > 0) 443 return QString("%1 (%2)").arg(strval).arg(name); 444 /* fallthrough */ 445 case DisplayValue: 446 default: 447 return strval; 448 } 449 } 450 else if(value.type() == QVariant::UserType && value.userType() == qMetaTypeId< SocFieldBitRange >()) 451 { 452 const SocFieldBitRange& br = value.value< SocFieldBitRange >(); 453 if(br.GetFirstBit() == br.GetLastBit()) 454 return QString("%1").arg(br.GetFirstBit()); 455 else 456 return QString("%1:%2").arg(br.GetLastBit()).arg(br.GetFirstBit()); 457 } 458 else 459 return QStyledItemDelegate::displayText(value, locale); 460} 461 462/** 463 * SocFieldCachedEditor 464 */ 465SocFieldCachedEditor::SocFieldCachedEditor(QWidget *parent) 466 :SocFieldEditor(soc_desc::field_t(), parent) 467{ 468} 469 470SocFieldCachedEditor::~SocFieldCachedEditor() 471{ 472} 473 474SocFieldCachedValue SocFieldCachedEditor::value() const 475{ 476 return SocFieldCachedValue(m_value.field(), field()); 477} 478 479void SocFieldCachedEditor::setValue(SocFieldCachedValue val) 480{ 481 m_value = val; 482 SetRegField(m_value.field()); 483 setField(m_value.value()); 484} 485 486/** 487 * SocAccessItemDelegate 488 */ 489QString SocAccessItemDelegate::displayText(const QVariant& value, const QLocale& locale) const 490{ 491 if(isUserType< soc_desc::access_t >(value)) 492 { 493 soc_desc::access_t acc = value.value< soc_desc::access_t >(); 494 switch(acc) 495 { 496 case soc_desc::UNSPECIFIED: return m_unspec_text; 497 case soc_desc::READ_ONLY: return "Read-Only"; 498 case soc_desc::READ_WRITE: return "Read-Write"; 499 case soc_desc::WRITE_ONLY: return "Write-Only"; 500 default: return "<bug>"; 501 } 502 } 503 else 504 return QStyledItemDelegate::displayText(value, locale); 505} 506 507/** 508 * SocAccessEditor 509 */ 510SocAccessEditor::SocAccessEditor(const QString& unspec_text, QWidget *parent) 511 :QComboBox(parent) 512{ 513 addItem(unspec_text, QVariant::fromValue(soc_desc::UNSPECIFIED)); 514 addItem("Read-Only", QVariant::fromValue(soc_desc::READ_ONLY)); 515 addItem("Read-Write", QVariant::fromValue(soc_desc::READ_WRITE)); 516 addItem("Write-Only", QVariant::fromValue(soc_desc::WRITE_ONLY)); 517} 518 519SocAccessEditor::~SocAccessEditor() 520{ 521} 522 523soc_desc::access_t SocAccessEditor::access() const 524{ 525 return itemData(currentIndex()).value< soc_desc::access_t >(); 526} 527 528void SocAccessEditor::setAccess(soc_desc::access_t acc) 529{ 530 setCurrentIndex(findData(QVariant::fromValue(acc))); 531} 532 533/** 534 * SocFieldEditorCreator 535 */ 536QWidget *SocFieldEditorCreator::createWidget(QWidget *parent) const 537{ 538 return new SocFieldEditor(m_field, parent); 539} 540 541QByteArray SocFieldEditorCreator::valuePropertyName() const 542{ 543 return QByteArray("field"); 544} 545 546void SocFieldEditorCreator::setWidth(int bitcount) 547{ 548 m_field.width = bitcount; 549} 550 551/** 552 * SocAccessEditorCreator 553 */ 554QWidget *SocAccessEditorCreator::createWidget(QWidget *parent) const 555{ 556 return new SocAccessEditor(m_unspec_text, parent); 557} 558 559QByteArray SocAccessEditorCreator::valuePropertyName() const 560{ 561 return QByteArray("access"); 562} 563 564/** 565 * SocFieldCachedEditorCreator 566 */ 567QWidget *SocFieldCachedEditorCreator::createWidget(QWidget *parent) const 568{ 569 return new SocFieldCachedEditor(parent); 570} 571 572QByteArray SocFieldCachedEditorCreator::valuePropertyName() const 573{ 574 return QByteArray("value"); 575} 576 577/** 578 * RegFieldTableModel 579 */ 580 581RegFieldTableModel::RegFieldTableModel(QObject *parent) 582 :QAbstractTableModel(parent) 583{ 584 m_read_only = true; 585} 586 587int RegFieldTableModel::rowCount(const QModelIndex& /* parent */) const 588{ 589 return m_reg.field.size(); 590} 591 592int RegFieldTableModel::columnCount(const QModelIndex& /* parent */) const 593{ 594 return ColumnCountOffset + m_value.size(); 595} 596 597QVariant RegFieldTableModel::data(const QModelIndex& index, int role) const 598{ 599 if(index.row() < 0 || (size_t)index.row() >= m_reg.field.size()) 600 return QVariant(); 601 int section = index.column(); 602 const soc_desc::field_t& field = m_reg.field[index.row()]; 603 /* column independent code */ 604 const RegThemeGroup *theme = 0; 605 switch(m_status[index.row()]) 606 { 607 case Normal: theme = &m_theme.normal; break; 608 case Diff: theme = &m_theme.diff; break; 609 case Error: theme = &m_theme.error; break; 610 case None: default: break; 611 } 612 if(role == Qt::FontRole) 613 return theme ? QVariant(theme->font) : QVariant(); 614 if(role == Qt::BackgroundRole) 615 return theme ? QVariant(theme->background) : QVariant(); 616 if(role == Qt::ForegroundRole) 617 return theme ? QVariant(theme->foreground) : QVariant(); 618 /* column dependent code */ 619 if(section == BitRangeColumn) 620 { 621 if(role == Qt::DisplayRole) 622 return QVariant::fromValue(SocFieldBitRange(field)); 623 else if(role == Qt::TextAlignmentRole) 624 return QVariant(Qt::AlignVCenter | Qt::AlignHCenter); 625 else 626 return QVariant(); 627 } 628 if(section == NameColumn) 629 { 630 if(role == Qt::DisplayRole) 631 return QVariant(QString::fromStdString(field.name)); 632 else 633 return QVariant(); 634 } 635 if(section < FirstValueColumn + m_value.size()) 636 { 637 int idx = section - FirstValueColumn; 638 if(role == Qt::DisplayRole) 639 { 640 if(!m_value[idx].isValid()) 641 return QVariant("<error>"); 642 return QVariant::fromValue(SocFieldCachedValue(field, 643 field.extract(m_value[idx].value< soc_word_t >()))); 644 } 645 else if(role == Qt::EditRole) 646 { 647 if(!m_value[idx].isValid()) 648 return QVariant(); 649 return QVariant::fromValue(SocFieldCachedValue(field, 650 field.extract(m_value[idx].value< soc_word_t >()))); 651 } 652 else if(role == Qt::TextAlignmentRole) 653 return QVariant(Qt::AlignVCenter | Qt::AlignHCenter); 654 else 655 return QVariant(); 656 } 657 section -= m_value.size(); 658 if(section == DescColumnOffset) 659 { 660 if(role == Qt::DisplayRole) 661 return QVariant(QString::fromStdString(field.desc)); 662 else 663 return QVariant(); 664 } 665 return QVariant(); 666} 667 668bool RegFieldTableModel::setData(const QModelIndex& idx, const QVariant& value, int role) 669{ 670 if(role != Qt::EditRole) 671 return false; 672 int section = idx.column(); 673 if(section == BitRangeColumn) 674 { 675 if(idx.row() < 0 || idx.row() >= rowCount()) 676 return false; 677 if(value.type() != QVariant::UserType && value.userType() == qMetaTypeId< SocFieldBitRange >()) 678 return false; 679 SocFieldBitRange bitrange = value.value< SocFieldBitRange >(); 680 m_reg.field[idx.row()].pos = bitrange.GetFirstBit(); 681 m_reg.field[idx.row()].width = bitrange.GetLastBit() - bitrange.GetFirstBit() + 1; 682 emit OnBitrangeModified(idx.row()); 683 } 684 if(section < FirstValueColumn || section >= FirstValueColumn + m_value.size()) 685 { 686 qDebug() << "ignore setData to column " << section; 687 return false; 688 } 689 section -= FirstValueColumn; 690 const SocFieldCachedValue& v = value.value< SocFieldCachedValue >(); 691 if(!m_value[section].isValid()) 692 return false; 693 soc_word_t old_val = m_value[section].value< soc_word_t >(); 694 m_value[section] = QVariant(v.field().replace(old_val, v.value())); 695 // update column 696 RecomputeTheme(); 697 emit dataChanged(index(0, section), index(rowCount() - 1, section)); 698 emit OnValueModified(section); 699 return true; 700} 701 702Qt::ItemFlags RegFieldTableModel::flags(const QModelIndex& index) const 703{ 704 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; 705 int section = index.column(); 706 if(section < FirstValueColumn || section >= FirstValueColumn + m_value.size()) 707 { 708 /* bitrange or name */ 709 if(!m_read_only) 710 flags |= Qt::ItemIsEditable; 711 return flags; 712 } 713 section -= FirstValueColumn; 714 if(m_value[section].isValid() && !m_read_only) 715 flags |= Qt::ItemIsEditable; 716 return flags; 717} 718 719QVariant RegFieldTableModel::headerData(int section, Qt::Orientation orientation, 720 int role) const 721{ 722 if(orientation == Qt::Vertical) 723 return QVariant(); 724 if(role != Qt::DisplayRole) 725 return QVariant(); 726 if(section == BitRangeColumn) 727 return QVariant("Bits"); 728 if(section == NameColumn) 729 return QVariant("Name"); 730 if(section < FirstValueColumn + m_value.size()) 731 { 732 int idx = section - FirstValueColumn; 733 if(m_value.size() == 1) 734 return QVariant("Value"); 735 else 736 return QVariant(QString("Value %1").arg((QChar)('A' + idx))); 737 } 738 section -= m_value.size(); 739 if(section == DescColumnOffset) 740 return QVariant("Description"); 741 return QVariant(); 742} 743 744void RegFieldTableModel::SetReadOnly(bool en) 745{ 746 if(en == m_read_only) 747 return; 748 m_read_only = en; 749} 750 751void RegFieldTableModel::SetRegister(const soc_desc::register_t& reg) 752{ 753 /* remove all rows */ 754 beginResetModel(); 755 m_reg.field.clear(); 756 endResetModel(); 757 /* add them all */ 758 beginInsertRows(QModelIndex(), 0, reg.field.size() - 1); 759 m_reg = reg; 760 RecomputeTheme(); 761 endInsertRows(); 762} 763 764void RegFieldTableModel::UpdateRegister(const soc_desc::register_t& reg) 765{ 766 m_reg = reg; 767 RecomputeTheme(); 768 emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); 769} 770 771soc_desc::register_t RegFieldTableModel::GetRegister() const 772{ 773 return m_reg; 774} 775 776void RegFieldTableModel::SetValues(const QVector< QVariant >& values) 777{ 778 /* remove all value columns */ 779 beginRemoveColumns(QModelIndex(), FirstValueColumn, 780 FirstValueColumn + m_value.size() - 1); 781 m_value.clear(); 782 endRemoveColumns(); 783 /* add them back */ 784 beginInsertColumns(QModelIndex(), FirstValueColumn, 785 FirstValueColumn + values.size() - 1); 786 m_value = values; 787 RecomputeTheme(); 788 endInsertColumns(); 789} 790 791QVariant RegFieldTableModel::GetValue(int index) 792{ 793 return m_value[index]; 794} 795 796void RegFieldTableModel::SetTheme(const RegTheme& theme) 797{ 798 m_theme = theme; 799 RecomputeTheme(); 800 emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); 801} 802 803void RegFieldTableModel::RecomputeTheme() 804{ 805 m_status.resize(m_reg.field.size()); 806 for(size_t i = 0; i < m_reg.field.size(); i++) 807 { 808 m_status[i] = None; 809 if(!m_theme.valid || m_value.size() == 0) 810 continue; 811 m_status[i] = Normal; 812 const soc_desc::field_t& field = m_reg.field[i]; 813 QVariant val; 814 for(int j = 0; j < m_value.size(); j++) 815 { 816 QVariant val2 = m_value[j]; 817 if(!val2.isValid()) 818 continue; 819 val2 = QVariant(field.extract(val2.value< soc_word_t >())); 820 if(!val.isValid()) 821 val = val2; 822 else if(val != val2) 823 m_status[i] = Diff; 824 } 825 } 826} 827 828/** 829 * RegFieldProxyModel 830 */ 831 832bool RegFieldProxyModel::lessThan(const QModelIndex& left, 833 const QModelIndex& right) const 834{ 835 QVariant ldata = sourceModel()->data(left); 836 QVariant rdata = sourceModel()->data(right); 837 if(isUserType< SocFieldBitRange >(ldata) && 838 isUserType< SocFieldBitRange >(rdata)) 839 { 840 return ldata.value< SocFieldBitRange >() < 841 rdata.value< SocFieldBitRange >(); 842 } 843 else if(isUserType< SocFieldCachedValue >(ldata) && 844 isUserType< SocFieldCachedValue >(rdata)) 845 { 846 return ldata.value< SocFieldCachedValue >() < 847 rdata.value< SocFieldCachedValue >(); 848 } 849 else 850 return QSortFilterProxyModel::lessThan(left, right); 851} 852 853/** 854 * YRegDisplayItemEditor 855 */ 856YRegDisplayItemEditor::YRegDisplayItemEditor(QWidget *parent, YRegDisplay *display, 857 YRegDisplayItemDelegate *delegate, QModelIndex bitrange_index, 858 QModelIndex name_index) 859 :QWidget(parent), m_display_delegate(delegate), 860 m_display(display), m_state(Idle) 861{ 862 m_col_width = m_display->BitrangeRect(SocFieldBitRange(0, 0)).width(); 863 m_resize_margin = m_col_width / 4; 864 setEditorData(bitrange_index, name_index); 865 setMouseTracking(true); 866 setAutoFillBackground(true); 867 setFocusPolicy(Qt::StrongFocus); // QItemDelegate says it's important 868} 869 870void YRegDisplayItemEditor::setEditorData(QModelIndex bitrange_index, QModelIndex name_index) 871{ 872 if(m_state != Idle) 873 { 874 m_state = Idle; 875 QApplication::restoreOverrideCursor(); 876 } 877 m_bitrange_index = bitrange_index; 878 m_name_index = name_index; 879 m_bitrange = bitrange_index.data().value< SocFieldBitRange >(); 880} 881 882void YRegDisplayItemEditor::getEditorData(QVariant& name, QVariant& bitrange) 883{ 884 name = QVariant(); /* don't touch the name */ 885 bitrange = QVariant::fromValue(m_bitrange); 886} 887 888YRegDisplayItemEditor::~YRegDisplayItemEditor() 889{ 890 /* make sure to restore cursor if modified */ 891 if(m_state != Idle) 892 { 893 m_state = Idle; 894 QApplication::restoreOverrideCursor(); 895 } 896} 897 898YRegDisplayItemEditor::Zone YRegDisplayItemEditor::GetZone(const QPoint& pt) 899{ 900 if(!rect().contains(pt)) 901 return NoZone; 902 if(pt.x() >= 0 && pt.x() <= m_resize_margin) 903 return ResizeLeftZone; 904 if(pt.x() >= width() - m_resize_margin && pt.x() <= width()) 905 return ResizeRightZone; 906 return MoveZone; 907} 908 909void YRegDisplayItemEditor::mouseMoveEvent(QMouseEvent *event) 910{ 911 Zone zone = GetZone(event->pos()); 912 bool in_resize_zone = (zone == ResizeLeftZone || zone == ResizeRightZone); 913 /* resizing/moving has priority */ 914 if(m_state == ResizingLeft || m_state == ResizingRight || m_state == Moving) 915 { 916 SocFieldBitRange new_bitrange = m_bitrange; 917 if(m_state == Moving) 918 { 919 /* Compute new bitrange: we know the offset of the mouse relative to the 920 * left of the register: use that offset to compute the new position of 921 * the MSB bit. To make it more natural, add half of a column of margin 922 * so that the register does not move until half of a bit column displacement 923 * was made */ 924 int bit = m_display->bitColumnAt(mapTo(m_display, 925 event->pos() - QPoint(m_move_offset - m_col_width / 2, 0))); 926 new_bitrange.SetLastBit(bit); 927 int w = m_bitrange.GetLastBit() - m_bitrange.GetFirstBit(); 928 /* make sure range is valid */ 929 if(bit - w < 0) 930 return; 931 new_bitrange.SetFirstBit(bit - w); 932 } 933 else 934 { 935 /* Compute new bitrange. To make it more natural, add quarter of a column of margin 936 * so that the register does not resize until quarter of a bit column displacement 937 * was made */ 938 int bit = m_display->bitColumnAt(mapTo(m_display, event->pos() 939 + QPoint(m_col_width / 4, 0))); 940 if(m_state == ResizingLeft) 941 new_bitrange.SetLastBit(bit); 942 else 943 new_bitrange.SetFirstBit(bit); 944 /* make sure range is valid */ 945 if(new_bitrange.GetLastBit() < new_bitrange.GetFirstBit()) 946 return; 947 } 948 /* make sure range does not overlap with other fields */ 949 /* TODO */ 950 /* update current bitrange (display only) and resize widget */ 951 if(m_bitrange != new_bitrange) 952 { 953 m_bitrange = new_bitrange; 954 /* resize widget */ 955 QRect rect = m_display->BitrangeRect(m_bitrange); 956 rect.moveTopLeft(parentWidget()->mapFromGlobal(m_display->mapToGlobal(rect.topLeft()))); 957 setGeometry(rect); 958 } 959 } 960 /* any zone -> resize zone */ 961 else if(in_resize_zone) 962 { 963 /* don't do unnecessary changes */ 964 if(m_state != InResizeZone) 965 { 966 /* restore old cursor if needed */ 967 if(m_state != Idle) 968 QApplication::restoreOverrideCursor(); 969 m_state = InResizeZone; 970 QApplication::setOverrideCursor(QCursor(Qt::SizeHorCursor)); 971 } 972 } 973 /* any zone -> move zone */ 974 else if(zone == MoveZone) 975 { 976 /* don't do unnecessary changes */ 977 if(m_state != InMoveZone) 978 { 979 /* restore old cursor if needed */ 980 if(m_state != Idle) 981 QApplication::restoreOverrideCursor(); 982 m_state = InMoveZone; 983 QApplication::setOverrideCursor(QCursor(Qt::SizeAllCursor)); 984 } 985 } 986 /* any zone -> no zone */ 987 else if(zone == NoZone) 988 { 989 if(m_state != Idle) 990 { 991 m_state = Idle; 992 QApplication::restoreOverrideCursor(); 993 } 994 } 995} 996 997void YRegDisplayItemEditor::leaveEvent(QEvent *event) 998{ 999 Q_UNUSED(event); 1000 if(m_state == InResizeZone) 1001 { 1002 m_state = Idle; 1003 QApplication::restoreOverrideCursor(); 1004 } 1005} 1006 1007void YRegDisplayItemEditor::mousePressEvent(QMouseEvent *event) 1008{ 1009 /* just in case the mouseMove event was not done */ 1010 mouseMoveEvent(event); 1011 /* we need to track mouse outside of widget but Qt already grabs the mouse 1012 * for us on mouse press in widget */ 1013 if(m_state == InResizeZone) 1014 { 1015 if(GetZone(event->pos()) == ResizeLeftZone) 1016 m_state = ResizingLeft; 1017 else 1018 m_state = ResizingRight; 1019 } 1020 else if(m_state == InMoveZone) 1021 { 1022 m_state = Moving; 1023 /* store offset from the left, to keep relative position of the register 1024 * with respect to the mouse */ 1025 m_move_offset = event->pos().x(); 1026 } 1027} 1028 1029void YRegDisplayItemEditor::mouseReleaseEvent(QMouseEvent *event) 1030{ 1031 if(m_state == ResizingLeft || m_state == ResizingRight || m_state == Moving) 1032 { 1033 QApplication::restoreOverrideCursor(); 1034 m_state = Idle; 1035 /* update cursor */ 1036 mouseMoveEvent(event); 1037 } 1038} 1039 1040void YRegDisplayItemEditor::paintEvent(QPaintEvent *event) 1041{ 1042 Q_UNUSED(event); 1043 QPainter painter(this); 1044 /* reuse delegate code to paint */ 1045 QStyleOptionViewItem opt = m_display->viewOptions(); 1046 opt.state |= QStyle::State_HasFocus | QStyle::State_Selected | QStyle::State_Active; 1047 opt.displayAlignment = Qt::AlignHCenter | Qt::AlignVCenter; 1048 opt.rect = rect(); 1049 opt.showDecorationSelected = true; 1050 m_display_delegate->initStyleOption(&opt, m_name_index); 1051 m_display_delegate->MyPaint(&painter, opt); 1052} 1053 1054/** 1055 * YRegDisplayItemDelegate 1056 */ 1057 1058YRegDisplayItemDelegate::YRegDisplayItemDelegate(QObject *parent) 1059 :QStyledItemDelegate(parent) 1060{ 1061} 1062 1063void YRegDisplayItemDelegate::MyPaint(QPainter *painter, const QStyleOptionViewItem& option) const 1064{ 1065 QStyleOptionViewItem opt = option; 1066 painter->save(); 1067 // draw everything rotated, requires careful manipulation of the 1068 // rects involved 1069 painter->translate(opt.rect.bottomLeft()); 1070 painter->rotate(-90); 1071 opt.rect = QRect(0, 0, opt.rect.height(), opt.rect.width()); 1072 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); 1073 style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget); 1074 painter->restore(); 1075} 1076 1077void YRegDisplayItemDelegate::paint(QPainter *painter, 1078 const QStyleOptionViewItem& option, const QModelIndex& index) const 1079{ 1080 QStyleOptionViewItem opt = option; 1081 // default alignment is centered unless specified 1082 opt.displayAlignment = Qt::AlignHCenter | Qt::AlignVCenter; 1083 initStyleOption(&opt, index); 1084 1085 MyPaint(painter, opt); 1086 1087} 1088 1089QSize YRegDisplayItemDelegate::sizeHint(const QStyleOptionViewItem& option, 1090 const QModelIndex& index) const 1091{ 1092 /* useless in our case, the view ignores this */ 1093 Q_UNUSED(option); 1094 Q_UNUSED(index); 1095 return QSize(); 1096} 1097 1098QWidget *YRegDisplayItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, 1099 const QModelIndex& index) const 1100{ 1101 Q_UNUSED(option); 1102 Q_UNUSED(index); 1103 YRegDisplay *display = dynamic_cast< YRegDisplay* >(parent->parent()); 1104 Q_ASSERT(display != nullptr); 1105 /* column 0 is name, column 1 is range */ 1106 return new YRegDisplayItemEditor(parent, display, const_cast< YRegDisplayItemDelegate* >(this), 1107 index.sibling(index.row(), 0), index.sibling(index.row(), 1)); 1108} 1109 1110void YRegDisplayItemDelegate::setEditorData(QWidget *editor, const QModelIndex& index) const 1111{ 1112 dynamic_cast< YRegDisplayItemEditor* >(editor)->setEditorData( 1113 index.sibling(index.row(), 0), index.sibling(index.row(), 1)); 1114} 1115 1116void YRegDisplayItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, 1117 const QModelIndex& index) const 1118{ 1119 QVariant name, bitrange; 1120 dynamic_cast< YRegDisplayItemEditor* >(editor)->getEditorData(name, bitrange); 1121 if(name.isValid()) 1122 model->setData(index.sibling(index.row(), 1), name); 1123 if(bitrange.isValid()) 1124 model->setData(index.sibling(index.row(), 0), bitrange); 1125} 1126 1127/** 1128 * YRegDisplay 1129 */ 1130 1131YRegDisplay::YRegDisplay(QWidget *parent) 1132 :QAbstractItemView(parent) 1133{ 1134 m_is_dirty = true; 1135 m_range_col = 0; 1136 m_data_col = 1; 1137 m_nr_bits = 32; 1138 // the frame around the register is ugly, disable it 1139 setFrameShape(QFrame::NoFrame); 1140 setSelectionMode(SingleSelection); 1141 setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked); 1142 setItemDelegate(new YRegDisplayItemDelegate(this)); 1143} 1144 1145void YRegDisplay::setWidth(int nr_bits) 1146{ 1147 m_nr_bits = nr_bits; 1148 m_is_dirty = true; 1149 recomputeGeometry(); 1150 updateGeometries(); 1151} 1152 1153int YRegDisplay::bitColumnAt(const QPoint& point, bool closest) const 1154{ 1155 int wx = point.x() + horizontalScrollBar()->value(); 1156 for(int bit = 0; bit < m_nr_bits; bit++) 1157 { 1158 int off = columnOffset(bitToColumn(bit)); 1159 int w = columnWidth(bitToColumn(bit)); 1160 if(wx >= off && wx < off + w) 1161 return bit; 1162 if(wx >= off + w && closest) 1163 return bit; 1164 } 1165 return closest ? m_nr_bits - 1 : -1; 1166} 1167 1168QModelIndex YRegDisplay::indexAt(const QPoint& point) const 1169{ 1170 if(!model()) 1171 return QModelIndex(); 1172 int wx = point.x() + horizontalScrollBar()->value(); 1173 int wy = point.y() + verticalScrollBar()->value(); 1174 1175 for(int i = 0; i < model()->rowCount(); i++) 1176 { 1177 QModelIndex index = model()->index(i, m_data_col, rootIndex()); 1178 QRect r = itemRect(index); 1179 if(!r.isValid()) 1180 continue; 1181 if(r.contains(wx, wy)) 1182 return index; 1183 } 1184 return QModelIndex(); 1185} 1186 1187void YRegDisplay::scrollTo(const QModelIndex& index, ScrollHint hint) 1188{ 1189 Q_UNUSED(index); 1190 Q_UNUSED(hint); 1191} 1192 1193QRect YRegDisplay::visualRect(const QModelIndex &index) const 1194{ 1195 QRect rect = itemRect(index); 1196 if(rect.isValid()) 1197 return QRect(rect.left() - horizontalScrollBar()->value(), 1198 rect.top() - verticalScrollBar()->value(), 1199 rect.width(), rect.height()); 1200 else 1201 return rect; 1202} 1203 1204bool YRegDisplay::isIndexHidden(const QModelIndex& index) const 1205{ 1206 Q_UNUSED(index); 1207 return false; 1208} 1209 1210QModelIndex YRegDisplay::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) 1211{ 1212 Q_UNUSED(cursorAction); 1213 Q_UNUSED(modifiers); 1214 return QModelIndex(); 1215} 1216 1217void YRegDisplay::setSelection(const QRect& r, QItemSelectionModel::SelectionFlags flags) 1218{ 1219 if(!model()) 1220 return; 1221 QRect rect = r.translated(horizontalScrollBar()->value(), 1222 verticalScrollBar()->value()).normalized(); 1223 1224 QItemSelection sel; 1225 for(int i = 0; i < model()->rowCount(); i++) 1226 { 1227 QModelIndex index = model()->index(i, m_data_col, rootIndex()); 1228 QRect r = itemRect(index); 1229 if(!r.isValid()) 1230 continue; 1231 if(r.intersects(rect)) 1232 sel.select(index, index); 1233 } 1234 selectionModel()->select(sel, flags); 1235} 1236 1237int YRegDisplay::verticalOffset() const 1238{ 1239 return verticalScrollBar()->value(); 1240} 1241 1242int YRegDisplay::horizontalOffset() const 1243{ 1244 return horizontalScrollBar()->value(); 1245} 1246 1247void YRegDisplay::scrollContentsBy(int dx, int dy) 1248{ 1249 viewport()->scroll(dx, dy); 1250} 1251 1252void YRegDisplay::setModel(QAbstractItemModel *model) 1253{ 1254 QAbstractItemView::setModel(model); 1255 m_is_dirty = true; 1256} 1257 1258void YRegDisplay::dataChanged(const QModelIndex &topLeft, 1259 const QModelIndex &bottomRight) 1260{ 1261 m_is_dirty = true; 1262 QAbstractItemView::dataChanged(topLeft, bottomRight); 1263} 1264 1265void YRegDisplay::rowsInserted(const QModelIndex &parent, int start, int end) 1266{ 1267 m_is_dirty = true; 1268 QAbstractItemView::rowsInserted(parent, start, end); 1269} 1270 1271void YRegDisplay::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 1272{ 1273 m_is_dirty = true; 1274 QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); 1275} 1276 1277int YRegDisplay::separatorSize() const 1278{ 1279 return 1; 1280} 1281 1282int YRegDisplay::marginSize() const 1283{ 1284 return viewOptions().fontMetrics.height() / 3; 1285} 1286 1287int YRegDisplay::headerTextSep() const 1288{ 1289 return marginSize() / 2; 1290} 1291 1292int YRegDisplay::headerHeight() const 1293{ 1294 return 2 * marginSize() + headerTextSep() + 2 * viewOptions().fontMetrics.height(); 1295} 1296 1297int YRegDisplay::minColumnWidth() const 1298{ 1299 return 2 * marginSize() + viewOptions().fontMetrics.height(); 1300} 1301 1302int YRegDisplay::maxColumnWidth() const 1303{ 1304 return 2 * minColumnWidth(); 1305} 1306 1307int YRegDisplay::columnWidth(int col) const 1308{ 1309 int avail = width() - (m_nr_bits + 1) * separatorSize(); 1310 int small_w = qMin(avail / m_nr_bits, maxColumnWidth()); 1311 int nr_big = avail - small_w * m_nr_bits; 1312 if(col < nr_big) 1313 return small_w + 1; 1314 else 1315 return small_w; 1316} 1317 1318int YRegDisplay::columnOffset(int col) const 1319{ 1320 int off = separatorSize(); 1321 for(int i = 0; i < col; i++) 1322 off += columnWidth(i) + separatorSize(); 1323 int all_w = off; 1324 for(int i = col; i < m_nr_bits; i++) 1325 all_w += columnWidth(i) + separatorSize(); 1326 return off + (width() - all_w) / 2; 1327} 1328 1329int YRegDisplay::maxContentHeight() const 1330{ 1331 int max = 0; 1332 QFontMetrics metrics = viewOptions().fontMetrics; 1333 if(model()) 1334 { 1335 for(int i = 0; i < model()->rowCount(); i++) 1336 { 1337 QModelIndex index = model()->index(i, m_data_col, rootIndex()); 1338 QString s = model()->data(index).toString(); 1339 max = qMax(max, metrics.boundingRect(s).width()); 1340 } 1341 } 1342 return 2 * marginSize() + max; 1343} 1344 1345int YRegDisplay::gapHeight() const 1346{ 1347 return marginSize() / 2; 1348} 1349 1350int YRegDisplay::bitToColumn(int bit) const 1351{ 1352 return m_nr_bits - 1 - bit; 1353} 1354 1355QRegion YRegDisplay::visualRegionForSelection(const QItemSelection& selection) const 1356{ 1357 QRegion region; 1358 foreach(const QItemSelectionRange &range, selection) 1359 { 1360 for(int row = range.top(); row <= range.bottom(); ++row) 1361 { 1362 for(int column = range.left(); column < range.right(); ++column) 1363 { 1364 QModelIndex index = model()->index(row, column, rootIndex()); 1365 region += visualRect(index); 1366 } 1367 } 1368 } 1369 return region; 1370} 1371 1372QRect YRegDisplay::itemRect(const QModelIndex& index) const 1373{ 1374 if(!index.isValid()) 1375 return QRect(); 1376 QVariant vrange = model()->data(model()->index(index.row(), m_range_col, rootIndex())); 1377 if(!vrange.canConvert< SocFieldBitRange >()) 1378 return QRect(); 1379 SocFieldBitRange range = vrange.value< SocFieldBitRange >(); 1380 return itemRect(range, index.column()); 1381} 1382 1383QRect YRegDisplay::BitrangeRect(const SocFieldBitRange& range) const 1384{ 1385 return itemRect(range, m_data_col); 1386} 1387 1388QRect YRegDisplay::itemRect(const SocFieldBitRange& range, int col) const 1389{ 1390 int top, bot; 1391 if(col == m_range_col) 1392 { 1393 top = separatorSize(); 1394 bot = separatorSize() + headerHeight() - 1; 1395 } 1396 else if(col == m_data_col) 1397 { 1398 top = headerHeight() + 3 * separatorSize() + gapHeight(); 1399 bot = height() - separatorSize() - 1; 1400 } 1401 else 1402 return QRect(); 1403 int first_col = bitToColumn(range.GetFirstBit()); 1404 return QRect(QPoint(columnOffset(bitToColumn(range.GetLastBit())), top), 1405 QPoint(columnOffset(first_col) + columnWidth(first_col) - 1, bot)); 1406} 1407 1408void YRegDisplay::paintEvent(QPaintEvent *event) 1409{ 1410 Q_UNUSED(event); 1411 QPainter painter(viewport()); 1412 painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); 1413 painter.translate(-horizontalScrollBar()->value(), -verticalScrollBar()->value()); 1414 QStyleOptionViewItem option = viewOptions(); 1415 int txt_h = option.fontMetrics.height(); 1416 int grid_hint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this); 1417 QBrush grid_brush(static_cast< QRgb >(grid_hint)); 1418 QBrush back_brush = option.palette.base(); 1419 int sep_sz = separatorSize(); 1420 QItemSelectionModel *selections = selectionModel(); 1421 1422 // paint header 1423 for(int bit = 0; bit < m_nr_bits; bit++) 1424 { 1425 QRect r = itemRect(SocFieldBitRange(bit, bit), m_range_col); 1426 // paint background 1427 painter.fillRect(r, back_brush); 1428 // paint top digit 1429 r.setTop(r.top() + marginSize()); 1430 r.setBottom(r.top() + txt_h); 1431 style()->drawItemText(&painter, r, Qt::AlignHCenter, option.palette, 1432 true, QString("%1").arg(bit / 10), foregroundRole()); 1433 // paint bottom digit 1434 r.setTop(r.bottom() + headerTextSep()); 1435 r.setBottom(r.top() + txt_h); 1436 style()->drawItemText(&painter, r, Qt::AlignHCenter, option.palette, 1437 true, QString("%1").arg(bit % 10), foregroundRole()); 1438 } 1439 // paint header grid 1440 for(int bit = 1; bit < m_nr_bits; bit++) 1441 { 1442 QRect r = itemRect(SocFieldBitRange(bit, bit), m_range_col); 1443 r.setCoords(r.right() + 1, r.top(), r.right() + sep_sz, r.bottom()); 1444 if((bit % 4) == 0) 1445 r.setCoords(r.left() - sep_sz, r.top(), r.right() + sep_sz, r.bottom()); 1446 painter.fillRect(r, grid_brush); 1447 } 1448 QRect hdr_r = itemRect(SocFieldBitRange(0, m_nr_bits - 1), m_range_col); 1449 painter.fillRect(QRect(hdr_r.left(), hdr_r.top() - sep_sz, hdr_r.width(), sep_sz), grid_brush); 1450 painter.fillRect(QRect(hdr_r.left(), hdr_r.bottom() + 1, hdr_r.width(), sep_sz), grid_brush); 1451 // paint header gap 1452 QRect gap_r(hdr_r.left(), hdr_r.bottom() + sep_sz + 1, hdr_r.width(), gapHeight()); 1453 painter.fillRect(gap_r, back_brush); 1454 // paint header bottom line 1455 painter.fillRect(QRect(gap_r.left(), gap_r.bottom() + 1, gap_r.width(), sep_sz), grid_brush); 1456 // paint background 1457 QRect data_r = itemRect(SocFieldBitRange(0, m_nr_bits - 1), m_data_col); 1458 //painter.fillRect(data_r, back_brush); 1459 // paint data bottom line 1460 painter.fillRect(QRect(data_r.left(), data_r.bottom() + 1, data_r.width(), sep_sz), grid_brush); 1461 // paint left/right lines 1462 painter.fillRect(QRect(hdr_r.left() - sep_sz, hdr_r.top() - sep_sz, sep_sz, height()), grid_brush); 1463 painter.fillRect(QRect(hdr_r.right() + 1, hdr_r.top() - sep_sz, sep_sz, height()), grid_brush); 1464 1465 // paint model 1466 if(!model()) 1467 return; 1468 1469 for(int i = 0; i < model()->rowCount(); i++) 1470 { 1471 QModelIndex index = model()->index(i, m_data_col, rootIndex()); 1472 QRect r = itemRect(index); 1473 if(!r.isValid()) 1474 continue; 1475 QString name = index.data().toString(); 1476 // paint background 1477 QStyleOptionViewItem opt = viewOptions(); 1478 opt.rect = r; 1479 //opt.showDecorationSelected = true; 1480 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, &painter, this); 1481 if(selections->isSelected(index)) 1482 opt.state |= QStyle::State_Selected; 1483 if(currentIndex() == index) 1484 opt.state |= QStyle::State_HasFocus; 1485 if(m_hover == index) 1486 opt.state |= QStyle::State_MouseOver; 1487 itemDelegate(index)->paint(&painter, opt, index); 1488 // paint left/right lines 1489 painter.fillRect(QRect(r.left() - sep_sz, r.top(), sep_sz, r.height()), grid_brush); 1490 painter.fillRect(QRect(r.right() + 1, r.top(), sep_sz, r.height()), grid_brush); 1491 } 1492} 1493 1494void YRegDisplay::recomputeGeometry() 1495{ 1496 if(!m_is_dirty) 1497 return; 1498 /* height: header + gap + sep + content + sep */ 1499 m_minimum_height = 0; 1500 m_minimum_height += headerHeight() + gapHeight(); 1501 m_minimum_height += 2 * separatorSize() + maxContentHeight(); 1502 /* width: sep + (col + sep) * n */ 1503 m_minimum_width = separatorSize() * (m_nr_bits + 1) + minColumnWidth() * m_nr_bits; 1504 m_is_dirty = false; 1505 viewport()->update(); 1506} 1507 1508void YRegDisplay::resizeEvent(QResizeEvent*) 1509{ 1510 m_is_dirty = true; 1511 recomputeGeometry(); 1512 updateGeometries(); 1513} 1514 1515void YRegDisplay::updateGeometries() 1516{ 1517 horizontalScrollBar()->setSingleStep(1); 1518 horizontalScrollBar()->setPageStep(viewport()->width()); 1519 horizontalScrollBar()->setRange(0, qMax(0, m_minimum_width - viewport()->width())); 1520 verticalScrollBar()->setSingleStep(1); 1521 verticalScrollBar()->setPageStep(viewport()->height()); 1522 verticalScrollBar()->setRange(0, qMax(0, m_minimum_height - viewport()->height())); 1523} 1524 1525bool YRegDisplay::viewportEvent(QEvent *event) 1526{ 1527 /* FIXME Apparently QAbstractItemView tracks the hovered index but keeps it 1528 * in its private part which is not accessible, which makes it useless... 1529 * This code reimplements it */ 1530 switch (event->type()) 1531 { 1532 case QEvent::HoverEnter: 1533 m_hover = indexAt(static_cast<QHoverEvent*>(event)->pos()); 1534 update(m_hover); 1535 break; 1536 case QEvent::HoverLeave: 1537 update(m_hover); // update old 1538 m_hover = QModelIndex(); 1539 break; 1540 case QEvent::HoverMove: 1541 { 1542 QModelIndex old = m_hover; 1543 m_hover = indexAt(static_cast<QHoverEvent*>(event)->pos()); 1544 if(m_hover != old) 1545 viewport()->update(visualRect(old)|visualRect(m_hover)); 1546 break; 1547 } 1548 default: 1549 break; 1550 } 1551 return QAbstractItemView::viewportEvent(event); 1552} 1553 1554/** 1555 * GrowingTableView 1556 */ 1557GrowingTableView::GrowingTableView(QWidget *parent) 1558 :QTableView(parent) 1559{ 1560} 1561 1562void GrowingTableView::setModel(QAbstractItemModel *m) 1563{ 1564 if(model()) 1565 disconnect(model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), 1566 this, SLOT(DataChanged(const QModelIndex&, const QModelIndex&))); 1567 QTableView::setModel(m); 1568 connect(model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), 1569 this, SLOT(DataChanged(const QModelIndex&, const QModelIndex&))); 1570 DataChanged(QModelIndex(), QModelIndex()); 1571} 1572 1573void GrowingTableView::DataChanged(const QModelIndex& tl, const QModelIndex& br) 1574{ 1575 Q_UNUSED(tl); 1576 Q_UNUSED(br); 1577 resizeColumnsToContents(); 1578 int h = contentsMargins().top() + contentsMargins().bottom(); 1579 h += horizontalHeader()->height(); 1580 for(int i = 0; i < model()->rowCount(); i++) 1581 h += rowHeight(i); 1582 setMinimumHeight(h); 1583} 1584 1585/** 1586 * MyTextEditor 1587 */ 1588MyTextEditor::MyTextEditor(QWidget *parent) 1589 :QWidget(parent) 1590{ 1591 QVBoxLayout *layout = new QVBoxLayout; 1592 m_toolbar = new QToolBar(this); 1593 m_edit = new QTextEdit(this); 1594#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) 1595 /* Qt 5.2 have a hardcoded sizeHint for QAbstractScrollArea which makes it 1596 * hard to have a good behaviour for the text editor. Fortunately 5.2 introduces 1597 * a new option to adjust this to the content. */ 1598 m_edit->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); 1599#endif 1600 layout->addWidget(m_toolbar, 0); 1601 layout->addWidget(m_edit, 1); 1602 setLayout(layout); 1603 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 1604 1605 m_edit->setAcceptRichText(false); 1606 m_edit->setAutoFormatting(QTextEdit::AutoAll); 1607 1608 m_bold_button = new QToolButton(this); 1609 m_bold_button->setIcon(YIconManager::Get()->GetIcon(YIconManager::FormatTextBold)); 1610 m_bold_button->setText("bold"); 1611 m_bold_button->setCheckable(true); 1612 1613 m_italic_button = new QToolButton(this); 1614 m_italic_button->setIcon(YIconManager::Get()->GetIcon(YIconManager::FormatTextItalic)); 1615 m_italic_button->setText("italic"); 1616 m_italic_button->setCheckable(true); 1617 1618 m_underline_button = new QToolButton(this); 1619 m_underline_button->setIcon(YIconManager::Get()->GetIcon(YIconManager::FormatTextUnderline)); 1620 m_underline_button->setText("underline"); 1621 m_underline_button->setCheckable(true); 1622 1623 m_toolbar->addWidget(m_bold_button); 1624 m_toolbar->addWidget(m_italic_button); 1625 m_toolbar->addWidget(m_underline_button); 1626 1627 connect(m_bold_button, SIGNAL(toggled(bool)), this, SLOT(OnTextBold(bool))); 1628 connect(m_italic_button, SIGNAL(toggled(bool)), this, SLOT(OnTextItalic(bool))); 1629 connect(m_underline_button, SIGNAL(toggled(bool)), this, SLOT(OnTextUnderline(bool))); 1630 connect(m_edit, SIGNAL(textChanged()), this, SLOT(OnInternalTextChanged())); 1631 connect(m_edit, SIGNAL(currentCharFormatChanged(const QTextCharFormat&)), 1632 this, SLOT(OnCharFormatChanged(const QTextCharFormat&))); 1633 1634 m_edit->installEventFilter(this); 1635 1636 SetGrowingMode(false); 1637 SetReadOnly(false); 1638 m_toolbar->hide(); 1639} 1640 1641void MyTextEditor::SetReadOnly(bool en) 1642{ 1643 m_read_only = en; 1644 if(en) 1645 m_toolbar->hide(); 1646 else 1647 m_toolbar->show(); 1648 m_edit->setReadOnly(en); 1649} 1650 1651void MyTextEditor::SetGrowingMode(bool en) 1652{ 1653 m_growing_mode = en; 1654 if(en) 1655 { 1656 m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); 1657 m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 1658 OnInternalTextChanged(); 1659 } 1660 else 1661 { 1662 m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 1663 m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); 1664 } 1665} 1666 1667void MyTextEditor::OnInternalTextChanged() 1668{ 1669 if(m_growing_mode) 1670 { 1671 int content_size = m_edit->document()->documentLayout()->documentSize().height(); 1672 content_size = qMax(content_size, m_edit->fontMetrics().height()); 1673 m_edit->setMinimumHeight(content_size + m_edit->contentsMargins().top() + 1674 m_edit->contentsMargins().bottom()); 1675 } 1676 emit OnTextChanged(); 1677 emit OnTextChanged(GetTextHtml()); 1678} 1679 1680void MyTextEditor::OnTextBold(bool checked) 1681{ 1682 QTextCursor cursor = m_edit->textCursor(); 1683 QTextCharFormat fmt = cursor.charFormat(); 1684 fmt.setFontWeight(checked ? QFont::Bold : QFont::Normal); 1685 cursor.setCharFormat(fmt); 1686 m_edit->setTextCursor(cursor); 1687} 1688 1689void MyTextEditor::OnTextItalic(bool checked) 1690{ 1691 QTextCursor cursor = m_edit->textCursor(); 1692 QTextCharFormat fmt = cursor.charFormat(); 1693 fmt.setFontItalic(checked); 1694 cursor.setCharFormat(fmt); 1695 m_edit->setTextCursor(cursor); 1696} 1697 1698void MyTextEditor::OnTextUnderline(bool checked) 1699{ 1700 QTextCursor cursor = m_edit->textCursor(); 1701 QTextCharFormat fmt = cursor.charFormat(); 1702 fmt.setFontUnderline(checked); 1703 cursor.setCharFormat(fmt); 1704 m_edit->setTextCursor(cursor); 1705} 1706 1707void MyTextEditor::OnCharFormatChanged(const QTextCharFormat& fmt) 1708{ 1709 /* NOTE: changing the button states programmaticaly doesn't trigger 1710 * the toggled() signals, otherwise it would result in a loop 1711 * between this function and OnText{Bold,Italic,Underline,...} */ 1712 m_bold_button->setChecked(fmt.fontWeight() > QFont::Normal); 1713 m_italic_button->setChecked(fmt.fontItalic()); 1714 m_underline_button->setChecked(fmt.fontUnderline()); 1715} 1716 1717void MyTextEditor::SetTextHtml(const QString& text) 1718{ 1719 m_edit->setHtml(text); 1720} 1721 1722QString MyTextEditor::GetTextHtml() 1723{ 1724 return m_edit->toPlainText(); 1725} 1726 1727bool MyTextEditor::IsModified() 1728{ 1729 return m_edit->document()->isModified(); 1730} 1731 1732bool MyTextEditor::eventFilter(QObject *object, QEvent *event) 1733{ 1734 if(object != m_edit) 1735 return false; 1736 if(m_read_only) 1737 return false; 1738 if(event->type() == QEvent::FocusIn) 1739 m_toolbar->show(); 1740 else if(event->type() == QEvent::FocusOut) 1741 m_toolbar->hide(); 1742 return false; 1743} 1744 1745/** 1746 * BackendSelector 1747 */ 1748BackendSelector::BackendSelector(Backend *backend, QWidget *parent) 1749 :QWidget(parent), m_backend(backend) 1750{ 1751 m_data_selector = new QComboBox(this); 1752 m_data_selector->addItem(YIconManager::Get()->GetIcon(YIconManager::TextGeneric), 1753 "Nothing...", QVariant(DataSelNothing)); 1754 m_data_selector->addItem(YIconManager::Get()->GetIcon(YIconManager::DocumentOpen), 1755 "File...", QVariant(DataSelFile)); 1756#ifdef HAVE_HWSTUB 1757 m_data_selector->addItem(YIconManager::Get()->GetIcon(YIconManager::MultimediaPlayer), 1758 "USB Device...", QVariant(DataSelDevice)); 1759#endif 1760 m_data_sel_edit = new QLineEdit(this); 1761 m_data_sel_edit->setReadOnly(true); 1762 m_nothing_text = new QLabel(this); 1763 m_nothing_text->setTextFormat(Qt::RichText); 1764 QHBoxLayout *data_sel_layout = new QHBoxLayout(this); 1765 data_sel_layout->addWidget(m_data_selector); 1766 data_sel_layout->addWidget(m_data_sel_edit, 1); 1767 data_sel_layout->addWidget(m_nothing_text, 1); 1768 data_sel_layout->addStretch(0); 1769#ifdef HAVE_HWSTUB 1770 m_dev_selector = new QComboBox; 1771 m_ctx_model = new HWStubContextModel; 1772 m_ctx_model->EnableDummy(true, "Please select a device..."); 1773 m_dev_selector->setModel(m_ctx_model); /* m_dev_selector will delete m_ctx_model */ 1774 m_ctx_selector = new QComboBox; 1775 m_ctx_manager = HWStubManager::Get(); 1776 m_ctx_selector->setModel(m_ctx_manager); 1777 m_ctx_manage_button = new QPushButton(); 1778 m_ctx_manage_button->setIcon(YIconManager::Get()->GetIcon(YIconManager::Preferences)); 1779 m_ctx_manage_button->setToolTip("Manage contexts"); 1780 data_sel_layout->addWidget(m_dev_selector, 1); 1781 data_sel_layout->addWidget(m_ctx_selector); 1782 data_sel_layout->addWidget(m_ctx_manage_button); 1783#endif 1784 1785 m_io_backend = m_backend->CreateDummyIoBackend(); 1786 1787 connect(m_data_selector, SIGNAL(activated(int)), 1788 this, SLOT(OnDataSelChanged(int))); 1789#ifdef HAVE_HWSTUB 1790 connect(m_ctx_selector, SIGNAL(currentIndexChanged(int)), this, 1791 SLOT(OnContextSelChanged(int))); 1792 connect(m_dev_selector, SIGNAL(currentIndexChanged(int)), this, 1793 SLOT(OnDeviceSelChanged(int))); 1794 connect(m_dev_selector, SIGNAL(activated(int)), this, 1795 SLOT(OnDeviceSelActivated(int))); 1796#endif 1797 1798#ifdef HAVE_HWSTUB 1799 OnContextSelChanged(0); 1800#endif 1801 OnDataSelChanged(0); 1802} 1803 1804BackendSelector::~BackendSelector() 1805{ 1806 /* avoid m_ctx_selector from deleting HWStubManager */ 1807#ifdef HAVE_HWSTUB 1808 m_ctx_selector->setModel(new QStandardItemModel()); 1809#endif 1810 delete m_io_backend; 1811} 1812 1813void BackendSelector::SetNothingMessage(const QString& msg) 1814{ 1815 m_nothing_text->setText(msg); 1816} 1817 1818void BackendSelector::OnDataSelChanged(int index) 1819{ 1820 if(index == -1) 1821 return; 1822 QVariant var = m_data_selector->itemData(index); 1823 if(var == DataSelFile) 1824 { 1825 m_nothing_text->hide(); 1826 m_data_sel_edit->show(); 1827#ifdef HAVE_HWSTUB 1828 m_dev_selector->hide(); 1829 m_ctx_selector->hide(); 1830 m_ctx_manage_button->hide(); 1831#endif 1832 QFileDialog *fd = new QFileDialog(m_data_selector); 1833 QStringList filters; 1834 filters << "Textual files (*.txt)"; 1835 filters << "All files (*)"; 1836 fd->setNameFilters(filters); 1837 fd->setDirectory(Settings::Get()->value("regtab/loaddatadir", QDir::currentPath()).toString()); 1838 if(fd->exec()) 1839 { 1840 QStringList filenames = fd->selectedFiles(); 1841 ChangeBackend(m_backend->CreateFileIoBackend(filenames[0])); 1842 m_data_sel_edit->setText(filenames[0]); 1843 } 1844 Settings::Get()->setValue("regtab/loaddatadir", fd->directory().absolutePath()); 1845 } 1846#ifdef HAVE_HWSTUB 1847 else if(var == DataSelDevice) 1848 { 1849 m_nothing_text->hide(); 1850 m_data_sel_edit->hide(); 1851 m_dev_selector->show(); 1852 m_ctx_selector->show(); 1853 m_ctx_manage_button->show(); 1854 /* explicitely change the backend now */ 1855 OnDeviceSelActivated(m_dev_selector->currentIndex()); 1856 } 1857#endif 1858 else 1859 { 1860 m_data_sel_edit->hide(); 1861 m_nothing_text->show(); 1862#ifdef HAVE_HWSTUB 1863 m_dev_selector->hide(); 1864 m_ctx_selector->hide(); 1865 m_ctx_manage_button->hide(); 1866#endif 1867 1868 ChangeBackend(m_backend->CreateDummyIoBackend()); 1869 } 1870} 1871 1872IoBackend *BackendSelector::GetBackend() 1873{ 1874 return m_io_backend; 1875} 1876 1877void BackendSelector::ChangeBackend(IoBackend *new_backend) 1878{ 1879 /* WARNING: delete old backend *after* calling the signal, otherwise the old backend 1880 * might get used after delete */ 1881 emit OnSelect(new_backend); 1882 delete m_io_backend; 1883 m_io_backend = new_backend; 1884} 1885 1886#ifdef HAVE_HWSTUB 1887void BackendSelector::OnContextSelChanged(int index) 1888{ 1889 m_ctx_model->SetContext(m_ctx_manager->GetContext(index)); 1890 m_dev_selector->setCurrentIndex(0); 1891} 1892 1893void BackendSelector::OnDeviceSelChanged(int index) 1894{ 1895 /* if current selection is -1, because device was removed or a new context 1896 * was selected, select entry 0, which is dummy. Not that this will not 1897 * call activate(), we don't want to change the current backend if the user 1898 * is using another type of backend. */ 1899 if(index == -1) 1900 m_dev_selector->setCurrentIndex(0); 1901} 1902 1903void BackendSelector::OnDeviceSelActivated(int index) 1904{ 1905 auto dev = new HWStubDevice(m_ctx_model->GetDevice(index)); 1906 if(!dev->IsValid()) 1907 { 1908 delete dev; 1909 ChangeBackend(m_backend->CreateDummyIoBackend()); 1910 } 1911 else 1912 ChangeBackend(new HWStubIoBackend(dev)); 1913} 1914#endif 1915 1916/** 1917 * YTabWidget 1918 */ 1919YTabWidget::YTabWidget(QTabBar *bar, QWidget *parent) 1920 :QTabWidget(parent), m_other_button(0) 1921{ 1922 if(bar != 0) 1923 setTabBar(bar); 1924 m_tab_open_button = new QToolButton(this); 1925 m_tab_open_button->setIcon(YIconManager::Get()->GetIcon(YIconManager::ListAdd)); 1926 m_tab_open_button->setAutoRaise(true); 1927 m_tab_open_button->setPopupMode(QToolButton::InstantPopup); 1928 /* the arrow with an icon only is pretty ugly and QToolButton has no way 1929 * to remove the arrow programmaticaly, so use the CSS to do that */ 1930 m_tab_open_button->setStyleSheet("QToolButton::menu-indicator { image: none; }"); 1931 setCornerWidget(m_tab_open_button, Qt::TopLeftCorner); 1932 setTabOpenable(false); 1933 connect(m_tab_open_button, SIGNAL(clicked(bool)), this, SLOT(OnOpenButton(bool))); 1934 /* there is a quirk in the default QStyle: if the tab bar is empty, it 1935 * returns the minimum size of the corner widget, which is 0 for tool buttons */ 1936 m_tab_open_button->setMinimumSize(m_tab_open_button->sizeHint()); 1937 setMinimumSize(m_tab_open_button->sizeHint()); 1938} 1939 1940void YTabWidget::setTabOpenable(bool openable) 1941{ 1942 m_tab_openable = openable; 1943 m_tab_open_button->setVisible(openable); 1944} 1945 1946void YTabWidget::OnOpenButton(bool checked) 1947{ 1948 Q_UNUSED(checked); 1949 emit tabOpenRequested(); 1950} 1951 1952void YTabWidget::setTabOpenMenu(QMenu *menu) 1953{ 1954 m_tab_open_button->setMenu(menu); 1955} 1956 1957void YTabWidget::setOtherMenu(QMenu *menu) 1958{ 1959 if(menu == nullptr) 1960 { 1961 if(m_other_button) 1962 delete m_other_button; 1963 m_other_button = nullptr; 1964 } 1965 else 1966 { 1967 if(m_other_button == nullptr) 1968 { 1969 m_other_button = new QToolButton(this); 1970 m_other_button->setText("Menu"); 1971 m_other_button->setAutoRaise(true); 1972 m_other_button->setPopupMode(QToolButton::InstantPopup); 1973 setCornerWidget(m_other_button, Qt::TopRightCorner); 1974 } 1975 m_other_button->setMenu(menu); 1976 } 1977} 1978 1979/** 1980 * MessageWidget 1981 */ 1982MessageWidget::MessageWidget(QWidget *parent) 1983 :QFrame(parent) 1984{ 1985 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); 1986 1987 m_icon = new QLabel(this); 1988 m_icon->hide(); 1989 m_text = new QLabel(this); 1990 m_text->setTextFormat(Qt::RichText); 1991 m_text->setWordWrap(true); 1992 m_close = new QToolButton(this); 1993 m_close->setText("close"); 1994 m_close->setIcon(style()->standardIcon(QStyle::QStyle::SP_DialogCloseButton)); 1995 m_close->setAutoRaise(true); 1996 1997 QHBoxLayout *layout = new QHBoxLayout(this); 1998 layout->addWidget(m_icon, 0); 1999 layout->addWidget(m_text, 1); 2000 layout->addWidget(m_close, 0); 2001 2002 m_id = 0; 2003 2004 connect(m_close, SIGNAL(clicked(bool)), this, SLOT(OnClose(bool))); 2005 2006 hide(); 2007} 2008 2009MessageWidget::~MessageWidget() 2010{ 2011} 2012 2013void MessageWidget::UpdateType() 2014{ 2015 /* style stolen from KMessageWidget */ 2016 QColor bg, border; 2017 switch(m_type) 2018 { 2019 case Positive: 2020 bg.setRgb(140, 228, 124); 2021 border.setRgb(56, 175, 58); 2022 break; 2023 case Information: 2024 bg.setRgb(161, 178, 202); 2025 border.setRgb(59, 79, 175); 2026 break; 2027 case Warning: 2028 bg.setRgb(228, 227, 127); 2029 border.setRgb(175, 169, 61); 2030 break; 2031 case Error: 2032 bg.setRgb(233, 199, 196); 2033 border.setRgb(175, 74, 60); 2034 break; 2035 default: 2036 break; 2037 } 2038 setStyleSheet(QString( 2039 "QFrame { background-color: %1;" 2040 "border-radius: 5px;" 2041 "border: 1px solid %2;" 2042 "}" 2043 "QLabel { border: none; }") 2044 .arg(bg.name()) 2045 .arg(border.name())); 2046} 2047 2048int MessageWidget::SetMessage(MessageType type, const QString& msg) 2049{ 2050 m_type = type; 2051 m_text->setText(msg); 2052 UpdateType(); 2053 show(); 2054 return ++m_id; 2055} 2056 2057void MessageWidget::HideMessage(int id) 2058{ 2059 if(m_id == id) 2060 OnClose(true); 2061} 2062 2063void MessageWidget::OnClose(bool clicked) 2064{ 2065 Q_UNUSED(clicked); 2066 hide(); 2067} 2068 2069/* 2070 * YIconManager 2071 */ 2072YIconManager *YIconManager::m_singleton = nullptr; 2073 2074YIconManager::YIconManager() 2075{ 2076 m_icon_name[ListAdd] = "list-add"; 2077 m_icon_fallback[ListAdd] = QStyle::SP_CustomBase; /* drawn by RenderListAdd */ 2078 m_icon_name[ListRemove] = "list-remove"; 2079 m_icon_fallback[ListRemove] = QStyle::SP_CustomBase; /* drawn by RenderListAdd */ 2080 m_icon_name[DocumentNew] = "document-new"; 2081 m_icon_fallback[DocumentNew] = QStyle::SP_FileDialogNewFolder; 2082 m_icon_name[DocumentEdit] = "document-edit"; 2083 m_icon_fallback[DocumentEdit] = QStyle::SP_FileDialogContentsView; 2084 m_icon_name[DocumentOpen] = "document-open"; 2085 m_icon_fallback[DocumentOpen] = QStyle::SP_DialogOpenButton; 2086 m_icon_name[DocumentSave] = "document-save"; 2087 m_icon_fallback[DocumentSave] = QStyle::SP_DialogSaveButton; 2088 m_icon_name[DocumentSaveAs] = "document-save-as"; 2089 m_icon_fallback[DocumentSaveAs] = QStyle::SP_DialogSaveButton; 2090 m_icon_name[Preferences] = "preferences-system"; 2091 m_icon_fallback[Preferences] = QStyle::SP_FileDialogInfoView; 2092 m_icon_name[FolderNew] = "folder-new"; 2093 m_icon_fallback[FolderNew] = QStyle::SP_FileDialogNewFolder; 2094 m_icon_name[Computer] = "computer"; 2095 m_icon_fallback[Computer] = QStyle::SP_ComputerIcon; 2096 m_icon_name[Cpu] = "cpu"; 2097 m_icon_fallback[Cpu] = QStyle::SP_DriveHDIcon; 2098 m_icon_name[DialogError] = "dialog-error"; 2099 m_icon_fallback[DialogError] = QStyle::SP_MessageBoxCritical; 2100 m_icon_name[ViewRefresh] = "view-refresh"; 2101 m_icon_fallback[ViewRefresh] = QStyle::SP_BrowserReload; 2102 m_icon_name[SytemRun] = "system-run"; 2103 m_icon_fallback[SytemRun] = QStyle::SP_MediaPlay; 2104 m_icon_name[ApplicationExit] = "application-exit"; 2105 m_icon_fallback[ApplicationExit] = QStyle::SP_TitleBarCloseButton; 2106 m_icon_name[HelpAbout] = "help-about"; 2107 m_icon_fallback[HelpAbout] = QStyle::SP_MessageBoxInformation; 2108 m_icon_name[FormatTextBold] = "format-text-bold"; 2109 m_icon_fallback[FormatTextBold] = QStyle::SP_CustomBase; /* drawn by RenderLetter */ 2110 m_icon_name[FormatTextItalic] = "format-text-italic"; 2111 m_icon_fallback[FormatTextItalic] = QStyle::SP_CustomBase; /* drawn by RenderLetter */ 2112 m_icon_name[FormatTextUnderline] = "format-text-underline"; 2113 m_icon_fallback[FormatTextUnderline] = QStyle::SP_CustomBase; /* drawn by RenderLetter */ 2114 m_icon_name[TextGeneric] = "text-x-generic"; 2115 m_icon_fallback[TextGeneric] = QStyle::SP_FileDialogDetailedView; 2116 m_icon_name[MultimediaPlayer] = "multimedia-player"; 2117 m_icon_fallback[MultimediaPlayer] = QStyle::SP_ComputerIcon; 2118} 2119 2120YIconManager::~YIconManager() 2121{ 2122} 2123 2124YIconManager *YIconManager::Get() 2125{ 2126 if(m_singleton == nullptr) 2127 m_singleton = new YIconManager(); 2128 return m_singleton; 2129} 2130 2131QIcon YIconManager::GetIcon(IconType type) 2132{ 2133 if(type < 0 || type >= MaxIcon) 2134 return QIcon(); 2135 /* cache icons */ 2136 if(m_icon[type].isNull()) 2137 m_icon[type] = QIcon::fromTheme(m_icon_name[type], GetFallbackIcon(type)); 2138 return m_icon[type]; 2139} 2140 2141namespace 2142{ 2143 QIcon RenderListAdd() 2144 { 2145 QPixmap pix(64, 64); 2146 pix.fill(Qt::transparent); 2147 QPainter paint(&pix); 2148 paint.fillRect(30, 12, 4, 40, QColor(255, 0, 0)); 2149 paint.fillRect(12, 30, 40, 4, QColor(255, 0, 0)); 2150 return QIcon(pix); 2151 } 2152 2153 QIcon RenderListRemove() 2154 { 2155 QPixmap pix(64, 64); 2156 pix.fill(Qt::transparent); 2157 QPainter paint(&pix); 2158 paint.setPen(QColor(255, 0, 0)); 2159 paint.drawLine(12, 12, 52, 52); 2160 paint.drawLine(12, 52, 52, 16); 2161 return QIcon(pix); 2162 } 2163 2164 QIcon RenderUnknown() 2165 { 2166 QPixmap pix(64, 64); 2167 pix.fill(); 2168 QPainter paint(&pix); 2169 paint.fillRect(0, 0, 64, 64, QColor(255, 0, 0)); 2170 return QIcon(pix); 2171 } 2172 2173 QIcon RenderText(const QString& str, bool bold, bool italic, bool underline) 2174 { 2175 QPixmap pix(64, 64); 2176 pix.fill(); 2177 QPainter paint(&pix); 2178 QFont font = QApplication::font("QButton"); 2179 font.setBold(bold); 2180 font.setItalic(italic); 2181 font.setUnderline(underline); 2182 font.setPixelSize(64); 2183 paint.setFont(font); 2184 paint.drawText(0, 0, 64, 64, Qt::AlignCenter, str); 2185 return QIcon(pix); 2186 } 2187} 2188 2189QIcon YIconManager::GetFallbackIcon(IconType type) 2190{ 2191 if(m_icon_fallback[type] != QStyle::SP_CustomBase) 2192 return QApplication::style()->standardIcon(m_icon_fallback[type]); 2193 switch(type) 2194 { 2195 case ListAdd: return RenderListAdd(); break; 2196 case ListRemove: return RenderListRemove(); break; 2197 case FormatTextBold: return RenderText("B", true, false, false); 2198 case FormatTextItalic: return RenderText("I", false, true, false); 2199 case FormatTextUnderline: return RenderText("U", false, false, true); 2200 default: return RenderUnknown(); break; 2201 } 2202} 2203 2204/** 2205 * Misc 2206 */ 2207 2208QGroupBox *Misc::EncloseInBox(const QString& name, QWidget *widget) 2209{ 2210 QVBoxLayout *layout = new QVBoxLayout; 2211 layout->addWidget(widget); 2212 return Misc::EncloseInBox(name, layout); 2213} 2214 2215QGroupBox *Misc::EncloseInBox(const QString& name, QLayout *layout) 2216{ 2217 QGroupBox *group = new QGroupBox(name); 2218 group->setLayout(layout); 2219 return group; 2220}