A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 414 lines 12 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 "regtab.h" 22 23#include <QSizePolicy> 24#include <QStringBuilder> 25#include <QFileDialog> 26#include <QDebug> 27#include <QStyle> 28#include <QMessageBox> 29#include "backend.h" 30#include "analyser.h" 31#include "regdisplaypanel.h" 32 33namespace 34{ 35 36enum 37{ 38 NodeInstRole = Qt::UserRole, 39}; 40 41} 42 43/** 44 * EmptyRegTabPanel 45 */ 46EmptyRegTabPanel::EmptyRegTabPanel(QWidget *parent) 47 :QWidget(parent) 48{ 49 QVBoxLayout *l = new QVBoxLayout; 50 l->addStretch(); 51 setLayout(l); 52} 53 54void EmptyRegTabPanel::AllowWrite(bool en) 55{ 56 Q_UNUSED(en); 57} 58 59QWidget *EmptyRegTabPanel::GetWidget() 60{ 61 return this; 62} 63 64 65/** 66 * RegTab 67 */ 68 69RegTab::RegTab(Backend *backend, QWidget *parent) 70 :QSplitter(parent), m_backend(backend) 71{ 72 QWidget *left = new QWidget; 73 this->addWidget(left); 74 this->setStretchFactor(0, 1); 75 QVBoxLayout *left_layout = new QVBoxLayout; 76 left->setLayout(left_layout); 77 78 QGroupBox *top_group = new QGroupBox(""); 79 QHBoxLayout *top_group_layout = new QHBoxLayout; 80 m_soc_selector = new QComboBox; 81 top_group_layout->addWidget(new QLabel("SOC:")); 82 top_group_layout->addWidget(m_soc_selector, 1); 83 top_group->setLayout(top_group_layout); 84 85 m_reg_tree = new QTreeWidget(); 86 m_reg_tree->setColumnCount(1); 87 m_reg_tree->setHeaderLabel(QString("Name")); 88 89 m_analysers_list = new QListWidget; 90 91 m_type_selector = new QTabWidget; 92 m_type_selector->addTab(m_reg_tree, "Registers"); 93 m_type_selector->addTab(m_analysers_list, "Analyzers"); 94 m_type_selector->setTabPosition(QTabWidget::West); 95 96 left_layout->addWidget(top_group); 97 left_layout->addWidget(m_type_selector); 98 99 m_right_panel = new QVBoxLayout; 100 QGroupBox *data_sel_group = new QGroupBox(""); 101 QHBoxLayout *data_sel_layout = new QHBoxLayout; 102 m_backend_selector = new BackendSelector(m_backend, this); 103 m_backend_selector->SetNothingMessage("<i>Select a data source to analyse its content.</i>"); 104 m_readonly_check = new QCheckBox("Read-only"); 105 m_readonly_check->setCheckState(Qt::Checked); 106 m_data_soc_label = new QLabel; 107 m_dump = new QPushButton("Dump", this); 108 m_dump->setIcon(QIcon::fromTheme("system-run")); 109 m_data_sel_reload = new QPushButton(this); 110 m_data_sel_reload->setIcon(QIcon::fromTheme("view-refresh")); 111 m_data_sel_reload->setToolTip("Reload data"); 112 data_sel_layout->addWidget(new QLabel("Data:")); 113 data_sel_layout->addWidget(m_backend_selector); 114 data_sel_layout->addWidget(m_readonly_check); 115 data_sel_layout->addWidget(m_data_soc_label); 116 data_sel_layout->addWidget(m_dump); 117 data_sel_layout->addWidget(m_data_sel_reload); 118 data_sel_group->setLayout(data_sel_layout); 119 m_data_soc_label->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); 120 m_msg = new MessageWidget(this); 121 122 m_right_panel->addWidget(data_sel_group, 0); 123 m_right_panel->addWidget(m_msg, 0); 124 m_right_content = 0; 125 SetPanel(new EmptyRegTabPanel); 126 QWidget *w = new QWidget; 127 w->setLayout(m_right_panel); 128 this->addWidget(w); 129 this->setStretchFactor(1, 2); 130 131 m_io_backend = m_backend_selector->GetBackend(); 132 133 connect(m_soc_selector, SIGNAL(currentIndexChanged(int)), 134 this, SLOT(OnSocChanged(int))); 135 connect(m_backend, SIGNAL(OnSocAdded(const SocFileRef&)), this, 136 SLOT(OnSocAdded(const SocFileRef&))); 137 connect(m_reg_tree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), 138 this, SLOT(OnRegItemClicked(QTreeWidgetItem*, int))); 139 connect(m_data_soc_label, SIGNAL(linkActivated(const QString&)), this, 140 SLOT(OnDataSocActivated(const QString&))); 141 connect(m_analysers_list, SIGNAL(itemClicked(QListWidgetItem *)), 142 this, SLOT(OnAnalyserClicked(QListWidgetItem *))); 143 connect(m_backend_selector, SIGNAL(OnSelect(IoBackend *)), 144 this, SLOT(OnBackendSelect(IoBackend *))); 145 connect(m_readonly_check, SIGNAL(clicked(bool)), this, SLOT(OnReadOnlyClicked(bool))); 146 connect(m_dump, SIGNAL(clicked(bool)), this, SLOT(OnDumpRegs(bool))); 147 connect(m_data_sel_reload, SIGNAL(clicked(bool)), this, SLOT(OnBackendReload(bool))); 148 connect(m_type_selector, SIGNAL(currentChanged(int)), this, SLOT(OnTypeChanged(int))); 149 150 m_msg_select_id = SetMessage(MessageWidget::Information, 151 "You can browse the registers using the left panel. " 152 "Select a data source at the top to analyse the values. " 153 "You can also use the analyzers available in the left panel, if any."); 154 m_msg_error_id = 0; 155 156 QList< SocFileRef > socs = m_backend->GetSocFileList(); 157 for(int i = 0; i < socs.size(); i++) 158 OnSocAdded(socs[i]); 159 SetDataSocName(""); 160 UpdateTabName(); 161} 162 163QWidget *RegTab::GetWidget() 164{ 165 return this; 166} 167 168RegTab::~RegTab() 169{ 170 /* backend will be deleted by backend selector */ 171} 172 173bool RegTab::Quit() 174{ 175 return true; 176} 177 178void RegTab::SetDataSocName(const QString& socname) 179{ 180 if(socname.size() != 0) 181 { 182 m_data_soc_label->setText("<a href=\"" + socname + "\">" + socname + "</a>"); 183 m_data_soc_label->setTextFormat(Qt::RichText); 184 m_data_soc_label->show(); 185 } 186 else 187 { 188 m_data_soc_label->setText(""); 189 m_data_soc_label->hide(); 190 } 191} 192 193void RegTab::OnDataSocActivated(const QString& str) 194{ 195 int index = m_soc_selector->findText(str); 196 if(index != -1) 197 m_soc_selector->setCurrentIndex(index); 198 else if(str.size() > 0) 199 { 200 m_msg_error_id = SetMessage(MessageWidget::Error, 201 "Description file for this SoC is not available."); 202 SetPanel(new EmptyRegTabPanel); 203 } 204} 205 206void RegTab::UpdateTabName() 207{ 208 /* do it the ugly way: try to cast to the different possible backends */ 209 FileIoBackend *file = dynamic_cast< FileIoBackend* >(m_io_backend); 210#ifdef HAVE_HWSTUB 211 HWStubIoBackend *hwstub = dynamic_cast< HWStubIoBackend* >(m_io_backend); 212#endif 213 if(file) 214 { 215 QFileInfo info(file->GetFileName()); 216 SetTabName(info.fileName()); 217 } 218#ifdef HAVE_HWSTUB 219 else if(hwstub) 220 { 221 HWStubDevice *dev = hwstub->GetDevice(); 222 SetTabName(dev->GetFriendlyName()); 223 } 224#endif 225 else 226 { 227 SetTabName("Register Tab"); 228 } 229} 230 231void RegTab::OnBackendSelect(IoBackend *backend) 232{ 233 /* Hide "Please select two SoC" and errors message */ 234 HideMessage(m_msg_select_id); 235 HideMessage(m_msg_error_id); 236 m_io_backend = backend; 237 /* Check if the backend is valid, otherwise it might confuse the user */ 238 if(m_io_backend->IsValid()) 239 { 240 SetReadOnlyIndicator(); 241 SetDataSocName(m_io_backend->GetSocName()); 242 OnDataSocActivated(m_io_backend->GetSocName()); 243 OnDataChanged(); 244 UpdateTabName(); 245 } 246 /* But don't display it for the dummy backend either */ 247 else if(dynamic_cast< DummyIoBackend * >(m_io_backend) == 0) 248 { 249 m_msg_error_id = SetMessage(MessageWidget::Error, 250 "Data source is not available."); 251 } 252} 253 254void RegTab::SetReadOnlyIndicator() 255{ 256 if(m_io_backend->IsReadOnly()) 257 m_readonly_check->setCheckState(Qt::Checked); 258} 259 260void RegTab::OnDataChanged() 261{ 262 OnRegItemClicked(m_reg_tree->currentItem(), 0); 263} 264 265void RegTab::OnRegItemClicked(QTreeWidgetItem *current, int col) 266{ 267 Q_UNUSED(col); 268 if(current == 0) 269 return; 270 DisplayNode(NodeInst(current)); 271} 272 273void RegTab::OnAnalyserClicked(QListWidgetItem *current) 274{ 275 if(current == 0) 276 return; 277 AnalyserFactory *ana = AnalyserFactory::GetAnalyserByName(current->text()); 278 SetPanel(ana->Create(m_cur_soc, m_io_backend)); 279} 280 281void RegTab::DisplayNode(const soc_desc::node_inst_t& ref) 282{ 283 HideMessage(m_msg_select_id); 284 if(ref.node().is_root()) 285 SetPanel(new SocDisplayPanel(this, m_io_backend, ref.soc())); 286 else if(ref.node().reg().valid()) 287 SetPanel(new RegDisplayPanel(this, m_io_backend, ref)); 288 else 289 SetPanel(new NodeDisplayPanel(this, m_io_backend, ref)); 290} 291 292int RegTab::SetMessage(MessageWidget::MessageType type, const QString& msg) 293{ 294 return m_msg->SetMessage(type, msg); 295} 296 297void RegTab::HideMessage(int id) 298{ 299 m_msg->HideMessage(id); 300} 301 302void RegTab::SetPanel(RegTabPanel *panel) 303{ 304 delete m_right_content; 305 m_right_content = panel; 306 m_right_content->AllowWrite(m_readonly_check->checkState() == Qt::Unchecked); 307 m_right_panel->addWidget(m_right_content->GetWidget(), 1); 308} 309 310void RegTab::OnSocAdded(const SocFileRef& ref) 311{ 312 soc_desc::soc_ref_t soc = ref.GetSocFile()->GetSocRef(); 313 QVariant v; 314 v.setValue(soc); 315 m_soc_selector->addItem(QString::fromStdString(soc.get()->name), v); 316} 317 318QTreeWidgetItem *RegTab::MakeNode(const soc_desc::node_inst_t& inst, const QString& s) 319{ 320 QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(s)); 321 item->setData(0, NodeInstRole, QVariant::fromValue(inst)); 322 return item; 323} 324 325soc_desc::node_inst_t RegTab::NodeInst(QTreeWidgetItem *item) 326{ 327 return item->data(0, NodeInstRole).value< soc_desc::node_inst_t >(); 328} 329 330void RegTab::FillSubTree(QTreeWidgetItem *item) 331{ 332 std::vector< soc_desc::node_inst_t > list = NodeInst(item).children(); 333 for(size_t i = 0; i < list.size(); i++) 334 { 335 QString name = QString::fromStdString(list[i].name()); 336 if(list[i].is_indexed()) 337 name = QString("%1[%2]").arg(name).arg(list[i].index()); 338 QTreeWidgetItem *child = MakeNode(list[i], name); 339 FillSubTree(child); 340 item->addChild(child); 341 } 342} 343 344void RegTab::FillRegTree() 345{ 346 QTreeWidgetItem *soc_item = MakeNode(m_cur_soc.root_inst(), 347 QString::fromStdString(m_cur_soc.get()->name)); 348 FillSubTree(soc_item); 349 m_reg_tree->addTopLevelItem(soc_item); 350 m_reg_tree->expandItem(soc_item); 351} 352 353void RegTab::FillAnalyserList() 354{ 355 m_analysers_list->clear(); 356 m_analysers_list->addItems(AnalyserFactory::GetAnalysersForSoc( 357 QString::fromStdString(m_cur_soc.get()->name))); 358} 359 360void RegTab::OnSocChanged(int index) 361{ 362 if(index == -1) 363 return; 364 m_reg_tree->clear(); 365 m_cur_soc = m_soc_selector->itemData(index).value< soc_desc::soc_ref_t >(); 366 FillRegTree(); 367 FillAnalyserList(); 368} 369 370void RegTab::OnReadOnlyClicked(bool checked) 371{ 372 if(m_io_backend->IsReadOnly()) 373 return SetReadOnlyIndicator(); 374 m_right_content->AllowWrite(!checked); 375} 376 377void RegTab::OnDumpRegs(bool c) 378{ 379 Q_UNUSED(c); 380 QFileDialog *fd = new QFileDialog(this); 381 fd->setAcceptMode(QFileDialog::AcceptSave); 382 QStringList filters; 383 filters << "Textual files (*.txt)"; 384 filters << "All files (*)"; 385 fd->setNameFilters(filters); 386 fd->setDirectory(Settings::Get()->value("regtab/loaddatadir", QDir::currentPath()).toString()); 387 if(!fd->exec()) 388 return; 389 QStringList filenames = fd->selectedFiles(); 390 Settings::Get()->setValue("regtab/loaddatadir", fd->directory().absolutePath()); 391 BackendHelper bh(m_io_backend, m_cur_soc); 392 if(!bh.DumpAllRegisters(filenames[0])) 393 { 394 QMessageBox::warning(this, "The register dump was not saved", 395 "There was an error when dumping the registers"); 396 } 397} 398 399void RegTab::OnBackendReload(bool c) 400{ 401 Q_UNUSED(c); 402 m_io_backend->Reload(); 403 OnDataChanged(); 404} 405 406void RegTab::OnTypeChanged(int index) 407{ 408 if(index == -1) 409 return; 410 if(index == 0) /* registers */ 411 OnRegItemClicked(m_reg_tree->currentItem(), 0); 412 else if(index == 1) /* analysers */ 413 OnAnalyserClicked(m_analysers_list->currentItem()); 414}