A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 393 lines 12 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2010 Robert Bieber 11 * 12 * This program is free software; 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 optiyouon) 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 22#include "skindocument.h" 23 24#include <QFile> 25#include <QSettings> 26#include <QColor> 27#include <QMessageBox> 28#include <QFileDialog> 29 30#include <iostream> 31 32#include <QDebug> 33 34const int SkinDocument::updateInterval = 500; 35 36SkinDocument::SkinDocument(QLabel* statusLabel, ProjectModel* project, 37 DeviceState* device, QWidget *parent) 38 :TabContent(parent), statusLabel(statusLabel), 39 project(project), device(device), 40 treeInSync(true) 41{ 42 setupUI(); 43 44 titleText = "Untitled"; 45 fileName = ""; 46 saved = ""; 47 parseStatus = tr("Empty document"); 48 blockUpdate = false; 49 currentLine = -1; 50} 51 52SkinDocument::SkinDocument(QLabel* statusLabel, QString file, 53 ProjectModel* project, DeviceState* device, 54 QWidget *parent) 55 :TabContent(parent), fileName(file), 56 statusLabel(statusLabel), project(project), 57 device(device), treeInSync(true) 58{ 59 setupUI(); 60 blockUpdate = false; 61 62 /* Loading the file */ 63 if(QFile::exists(fileName)) 64 { 65 QFile fin(fileName); 66 fin.open(QFile::ReadOnly); 67 editor->document()->setPlainText(QString(fin.readAll())); 68 saved = editor->document()->toPlainText(); 69 editor->setTextCursor(QTextCursor(editor->document()->begin())); 70 fin.close(); 71 } 72 73 /* Setting the title */ 74 QStringList decomposed = fileName.split('/'); 75 titleText = decomposed.last(); 76 77 /* Setting the current screen device setting */ 78 QString extension = titleText.split(".").last().toLower().right(3); 79 if(extension == "wps") 80 { 81 device->setData("cs", "WPS"); 82 } 83 else if(extension == "sbs") 84 { 85 device->setData("cs", "Menus"); 86 } 87 else if(extension == "fms") 88 { 89 device->setData("cs", "FM Radio Screen"); 90 } 91 92 lastUpdate = QTime::currentTime(); 93} 94 95SkinDocument::~SkinDocument() 96{ 97 highlighter->deleteLater(); 98 model->deleteLater(); 99} 100 101void SkinDocument::connectPrefs(PreferencesDialog* prefs) 102{ 103 QObject::connect(prefs, SIGNAL(accepted()), 104 this, SLOT(settingsChanged())); 105 QObject::connect(prefs, SIGNAL(accepted()), 106 highlighter, SLOT(loadSettings())); 107} 108 109bool SkinDocument::requestClose() 110{ 111 /* Storing the response in blockUpdate will also block updates to the 112 status bar if the tab is being closed */ 113 if(editor->document()->toPlainText() != saved) 114 { 115 /* Spawning the "Are you sure?" dialog */ 116 QMessageBox confirm(this); 117 confirm.setWindowTitle(tr("Confirm Close")); 118 confirm.setText(titleText + tr(" has been modified.")); 119 confirm.setInformativeText(tr("Do you want to save your changes?")); 120 confirm.setStandardButtons(QMessageBox::Save | QMessageBox::Discard 121 | QMessageBox::Cancel); 122 confirm.setDefaultButton(QMessageBox::Save); 123 int confirmation = confirm.exec(); 124 125 switch(confirmation) 126 { 127 case QMessageBox::Save: 128 save(); 129 /* After calling save, make sure the user actually went through */ 130 if(editor->document()->toPlainText() != saved) 131 blockUpdate = false; 132 else 133 blockUpdate = true; 134 break; 135 136 case QMessageBox::Discard: 137 blockUpdate = true; 138 break; 139 140 case QMessageBox::Cancel: 141 blockUpdate = false; 142 break; 143 } 144 } 145 else 146 blockUpdate = true; 147 148 return blockUpdate; 149} 150 151void SkinDocument::setupUI() 152{ 153 /* Setting up the text edit */ 154 layout = new QHBoxLayout; 155 editor = new CodeEditor(this); 156 editor->setLineWrapMode(QPlainTextEdit::NoWrap); 157 layout->addWidget(editor); 158 159 setLayout(layout); 160 161 /* Attaching the syntax highlighter */ 162 highlighter = new SkinHighlighter(editor->document()); 163 164 /* Setting up the model */ 165 model = new ParseTreeModel(""); 166 167 QObject::connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), 168 this, SLOT(modelChanged())); 169 170 /* Connecting the editor's signal */ 171 QObject::connect(editor, SIGNAL(textChanged()), 172 this, SLOT(codeChanged())); 173 QObject::connect(editor, SIGNAL(cursorPositionChanged()), 174 this, SLOT(cursorChanged())); 175 176 /* Connecting to device setting changes */ 177 QObject::connect(device, SIGNAL(settingsChanged()), 178 this, SLOT(deviceChanged())); 179 180 /* Attaching the find/replace dialog */ 181 findReplace = new FindReplaceDialog(this); 182 findReplace->setModal(false); 183 findReplace->setTextEdit(editor); 184 findReplace->hide(); 185 186 settingsChanged(); 187 188 /* Setting up a timer to check for updates */ 189 checkUpdate.setInterval(500); 190 QObject::connect(&checkUpdate, SIGNAL(timeout()), 191 this, SLOT(codeChanged())); 192} 193 194void SkinDocument::settingsChanged() 195{ 196 /* Setting the editor colors */ 197 QSettings settings; 198 settings.beginGroup("SkinDocument"); 199 200 QColor fg = settings.value("fgColor", QColor(Qt::black)).value<QColor>(); 201 QColor bg = settings.value("bgColor", QColor(Qt::white)).value<QColor>(); 202 QPalette palette; 203 palette.setColor(QPalette::All, QPalette::Base, bg); 204 palette.setColor(QPalette::All, QPalette::Text, fg); 205 editor->setPalette(palette); 206 207 QColor highlight = settings.value("errorColor", QColor(Qt::red)).value<QColor>(); 208 editor->setErrorColor(highlight); 209 210 /* Setting the font */ 211 QFont def("Monospace"); 212 def.setStyleHint(QFont::TypeWriter); 213 QFont family = settings.value("fontFamily", def).value<QFont>(); 214 family.setPointSize(settings.value("fontSize", 12).toInt()); 215 editor->setFont(family); 216 217 editor->repaint(); 218 219 settings.endGroup(); 220 221} 222 223void SkinDocument::cursorChanged() 224{ 225 if(editor->isError(editor->textCursor().blockNumber() + 1)) 226 { 227 QTextCursor line = editor->textCursor(); 228 line.movePosition(QTextCursor::StartOfLine); 229 line.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); 230 skin_parse(line.selectedText().toLatin1()); 231 if(skin_error_line() > 0) 232 parseStatus = tr("Error on line ") + 233 QString::number(line.blockNumber() + 1) 234 + tr(", column ") + QString::number(skin_error_col()) 235 + tr(": ") + 236 skin_error_message(); 237 statusLabel->setText(parseStatus); 238 } 239 else if(editor->hasErrors()) 240 { 241 parseStatus = tr("Errors in document"); 242 statusLabel->setText(parseStatus); 243 } 244 else if(editor->textCursor().blockNumber() != currentLine) 245 { 246 currentLine = editor->textCursor().blockNumber(); 247 emit lineChanged(editor->textCursor().blockNumber() + 1); 248 } 249 250} 251 252void SkinDocument::codeChanged() 253{ 254 if(blockUpdate) 255 return; 256 257 if(editor->document()->isEmpty()) 258 { 259 parseStatus = tr("Empty document"); 260 statusLabel->setText(parseStatus); 261 return; 262 } 263 264 editor->clearErrors(); 265 parseStatus = model->changeTree(editor->document()-> 266 toPlainText().toLatin1()); 267 268 treeInSync = true; 269 emit antiSync(false); 270 271 if(skin_error_line() > 0) 272 parseStatus = tr("Errors in document"); 273 statusLabel->setText(parseStatus); 274 275 /* Highlighting if an error was found */ 276 if(skin_error_line() > 0) 277 { 278 editor->addError(skin_error_line()); 279 280 /* Now we're going to attempt parsing again at each line, until we find 281 one that won't error out*/ 282 QTextDocument doc(editor->document()->toPlainText()); 283 int base = 0; 284 while(skin_error_line() > 0 && !doc.isEmpty()) 285 { 286 QTextCursor rest(&doc); 287 288 for(int i = 0; i < skin_error_line(); i++) 289 rest.movePosition(QTextCursor::NextBlock, 290 QTextCursor::KeepAnchor); 291 if(skin_error_line() == doc.blockCount()) 292 rest.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); 293 294 rest.removeSelectedText(); 295 base += skin_error_line(); 296 297 skin_parse(doc.toPlainText().toLatin1()); 298 299 if(skin_error_line() > 0) 300 editor->addError(base + skin_error_line()); 301 302 } 303 } 304 305 if(editor->document()->toPlainText() != saved) 306 emit titleChanged(titleText + QChar('*')); 307 else 308 emit titleChanged(titleText); 309 310 if(lastUpdate.msecsTo(QTime::currentTime()) >= updateInterval) 311 { 312 model->render(project, device, this, &fileName); 313 checkUpdate.stop(); 314 lastUpdate = QTime::currentTime(); 315 } 316 else 317 { 318 checkUpdate.start(); 319 } 320 cursorChanged(); 321 322} 323 324void SkinDocument::modelChanged() 325{ 326 treeInSync = false; 327 emit antiSync(true); 328} 329 330void SkinDocument::save() 331{ 332 QFile fout(fileName); 333 334 if(!fout.exists()) 335 { 336 saveAs(); 337 return; 338 } 339 340 fout.open(QFile::WriteOnly); 341 fout.write(editor->document()->toPlainText().toLatin1()); 342 fout.close(); 343 344 saved = editor->document()->toPlainText(); 345 QStringList decompose = fileName.split('/'); 346 titleText = decompose.last(); 347 emit titleChanged(titleText); 348 349 scene(); 350 351} 352 353void SkinDocument::saveAs() 354{ 355 /* Determining the directory to open */ 356 QString directory = fileName; 357 358 QSettings settings; 359 settings.beginGroup("SkinDocument"); 360 if(directory == "") 361 directory = settings.value("defaultDirectory", "").toString(); 362 363 fileName = QFileDialog::getSaveFileName(this, tr("Save Document"), 364 directory, fileFilter()); 365 directory = fileName; 366 if(fileName == "") 367 return; 368 369 directory.chop(fileName.length() - fileName.lastIndexOf('/') - 1); 370 settings.setValue("defaultDirectory", directory); 371 settings.endGroup(); 372 373 QFile fout(fileName); 374 fout.open(QFile::WriteOnly); 375 fout.write(editor->document()->toPlainText().toLatin1()); 376 fout.close(); 377 378 saved = editor->document()->toPlainText(); 379 QStringList decompose = fileName.split('/'); 380 titleText = decompose[decompose.count() - 1]; 381 emit titleChanged(titleText); 382 383 scene(); 384 385} 386 387QString SkinDocument::findSetting(QString key, QString fallback) 388{ 389 if(!project) 390 return fallback; 391 else 392 return project->getSetting(key, fallback); 393}