2023-09-28 Fred Gleason <fredg@paravelsystems.com>

* Added a 'RDJsonEscape(const QString &str)' function in
	'lib/rdweb.cpp' and 'lib/rdweb.h'.
	* Added a '--dump-panel-updates' switch to rdpanel(1).
	* Added a rdpanel(1) man page.

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason
2023-09-28 15:51:08 -04:00
parent 4e48d86522
commit 5118c36219
14 changed files with 302 additions and 7 deletions

View File

@@ -24399,3 +24399,8 @@
2023-09-27 Fred Gleason <fredg@paravelsystems.com>
* Fixed a bug in 'RDSoundPanel' that threw a segfault when adding
a new button to a User panel.
2023-09-28 Fred Gleason <fredg@paravelsystems.com>
* Added a 'RDJsonEscape(const QString &str)' function in
'lib/rdweb.cpp' and 'lib/rdweb.h'.
* Added a '--dump-panel-updates' switch to rdpanel(1).
* Added a rdpanel(1) man page.

View File

@@ -81,6 +81,9 @@ all-local: rdadmin.1\
rdmetadata.1\
rdmetadata.html\
rdmetadata.pdf\
rdpanel.1\
rdpanel.html\
rdpanel.pdf\
rdrender.1\
rdrender.html\
rdrender.pdf\
@@ -112,6 +115,7 @@ man_MANS = rdadmin.1\
rdlogmanager.1\
rdmarkerset.8\
rdmetadata.1\
rdpanel.1\
rdrender.1\
rmlsend.1\
rdservice.8\
@@ -168,6 +172,10 @@ EXTRA_DIST = exitcodes.xml\
rdmetadata.html\
rdmetadata.pdf\
rdmetadata.xml\
rdpanel.1\
rdpanel.html\
rdpanel.pdf\
rdpanel.xml\
rdrender.1\
rdrender.html\
rdrender.pdf\

70
docs/manpages/rdpanel.xml Normal file
View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<refentry id="stdin" xmlns="http://docbook.org/ns/docbook" version="5.0">
<!--
Header
-->
<refmeta>
<refentrytitle>rdpanel</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class='source'>September 2023</refmiscinfo>
<refmiscinfo class='manual'>Linux Audio Manual</refmiscinfo>
</refmeta>
<refnamediv>
<refname>rdpanel</refname>
<refpurpose>
Module for stand-alone sound panel
</refpurpose>
</refnamediv>
<info>
<author>
<personname>
<firstname>Fred</firstname>
<surname>Gleason</surname>
<email>fredg@paravelsystems.com</email>
</personname>
<contrib>Application Author</contrib>
</author>
</info>
<!--
Body
-->
<refsynopsisdiv id='synopsis'>
<cmdsynopsis>
<command>rdpanel</command>
<arg choice='opt'><replaceable>OPTIONS</replaceable></arg>
<sbr/>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1 id='description'>
<title>Description</title>
<para>
The <command>rdpanel</command><manvolnum>1</manvolnum> modules provides
a single, stand-alone &quot;cart wall&quot; application that allows
users to play carts by means of a single button press.
</para>
</refsect1>
<refsect1 id='options'>
<title>Options</title>
<variablelist remap='TP'>
<varlistentry>
<term>
<option>--dump-panel-updates</option>
</term>
<listitem>
<para>
When this option is given,
<command>rdpanel</command><manvolnum>1</manvolnum> will print
a dump of the currently loaded sound panel configuration to
standard output each time the Rivendell user logged into the system
is changed.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
</refentry>

View File

@@ -23,9 +23,12 @@
#include <rdbutton_panel.h>
#include <rdbutton_dialog.h>
RDButtonPanel::RDButtonPanel(RDAirPlayConf::PanelType type,QWidget *parent)
RDButtonPanel::RDButtonPanel(RDAirPlayConf::PanelType type,int number,
QWidget *parent)
: RDWidget(parent)
{
panel_number=number;
for(int i=0;i<PANEL_MAX_BUTTON_ROWS;i++) {
for(int j=0;j<PANEL_MAX_BUTTON_COLUMNS;j++) {
panel_button[i][j]=
@@ -76,6 +79,24 @@ QSizePolicy RDButtonPanel::sizePolicy() const
}
int RDButtonPanel::number() const
{
return panel_number;
}
QString RDButtonPanel::title() const
{
return panel_title;
}
void RDButtonPanel::setTitle(const QString &str)
{
panel_title=str;
}
RDPanelButton *RDButtonPanel::panelButton(int row,int col) const
{
return panel_button[row][col];
@@ -209,3 +230,41 @@ void RDButtonPanel::clear()
}
}
}
QString RDButtonPanel::json(int padding,bool final) const
{
QString ret;
//
// Get Button Count
//
int count=0;
for(int i=0;i<PANEL_MAX_BUTTON_ROWS;i++) {
for(int j=0;j<PANEL_MAX_BUTTON_COLUMNS;j++) {
if(!panel_button[i][j]->isEmpty()) {
count++;
}
}
}
ret+=RDJsonPadding(padding)+"\"panel\": {\r\n";
ret+=RDJsonField("number",panel_number,4+padding);
ret+=RDJsonField("title",panel_title,4+padding,count==0);
for(int i=0;i<PANEL_MAX_BUTTON_ROWS;i++) {
for(int j=0;j<PANEL_MAX_BUTTON_COLUMNS;j++) {
if(!panel_button[i][j]->isEmpty()) {
count--;
ret+=panel_button[i][j]->json(4+padding,count==0);
}
}
}
ret+=RDJsonPadding(padding)+"}";
if(!final) {
ret+=",";
}
ret+="\r\n";
return ret;
}

View File

@@ -29,6 +29,7 @@
#include <rduser.h>
#include <rdpanel_button.h>
#include <rdbutton_dialog.h>
#include <rdweb.h>
#include <rdwidget.h>
//
@@ -43,10 +44,13 @@
class RDButtonPanel : public RDWidget
{
public:
RDButtonPanel(RDAirPlayConf::PanelType type,QWidget *parent);
RDButtonPanel(RDAirPlayConf::PanelType type,int number,QWidget *parent);
~RDButtonPanel();
QSize sizeHint() const;
QSizePolicy sizePolicy() const;
int number() const;
QString title() const;
void setTitle(const QString &str);
RDPanelButton *panelButton(int row,int col) const;
void setActionMode(RDAirPlayConf::ActionMode mode);
void setAllowDrags(bool state);
@@ -54,8 +58,11 @@ class RDButtonPanel : public RDWidget
void hide();
void show();
void clear();
QString json(int padding=0,bool final=false) const;
private:
int panel_number;
QString panel_title;
RDPanelButton *panel_button[PANEL_MAX_BUTTON_ROWS][PANEL_MAX_BUTTON_COLUMNS];
};

View File

@@ -323,6 +323,45 @@ int RDPanelButton::duckVolume() const
}
bool RDPanelButton::isEmpty() const
{
return button_cart==0;
}
QString RDPanelButton::json(int padding,bool final)
{
QString ret;
ret+=RDJsonPadding(padding)+"\"button\": {\r\n";
ret+=RDJsonField("column",button_col,4+padding);
ret+=RDJsonField("row",button_row,4+padding);
if(isEmpty()) {
ret+=RDJsonNullField("cart",4+padding);
ret+=RDJsonNullField("defaultColor",4+padding);
ret+=RDJsonNullField("length",4+padding);
ret+=RDJsonNullField("hookLength",4+padding);
ret+=RDJsonNullField("label",4+padding,true);
}
else {
ret+=RDJsonField("cart",button_cart,4+padding);
ret+=RDJsonField("defaultColor",button_default_color.name(),4+padding);
ret+=RDJsonField("length",RDGetTimeLength(button_length[0],true,false),
4+padding);
ret+=RDJsonField("hookLength",RDGetTimeLength(button_length[1],true,false),
4+padding);
ret+=RDJsonField("label",button_text,4+padding,true);
}
ret+=RDJsonPadding(padding)+"}";
if(!final) {
ret+=",";
}
ret+="\r\n";
return ret;
}
void RDPanelButton::tickClock()
{
int msecs;

View File

@@ -21,8 +21,9 @@
#ifndef RDPANEL_BUTTON_H
#define RDPANEL_BUTTON_H
#include <rdpushbutton.h>
#include <rdplay_deck.h>
#include <rdpushbutton.h>
#include <rdweb.h>
#define RDPANEL_BUTTON_MARGIN 5
@@ -70,6 +71,8 @@ class RDPanelButton : public RDPushButton
void setDuckVolume(int lvel);
void setAllowDrags(bool state);
void resetCounter();
bool isEmpty() const;
QString json(int padding=0,bool final=false);
signals:
void cartDropped(int row,int col,unsigned cartnum,const QColor &color,

View File

@@ -28,6 +28,7 @@
#include "rdlog_line.h"
#include "rdmacro.h"
#include "rdsound_panel.h"
#include "rdweb.h"
RDSoundPanel::RDSoundPanel(int station_panels,int user_panels,bool flash,
const QString &caption,const QString &label_template,
@@ -35,6 +36,7 @@ RDSoundPanel::RDSoundPanel(int station_panels,int user_panels,bool flash,
RDCartDialog *cart_dialog,QWidget *parent)
: RDWidget(parent)
{
panel_dump_panel_updates=false;
panel_playmode_box=NULL;
panel_button_columns=PANEL_MAX_BUTTON_COLUMNS;
panel_button_rows=PANEL_MAX_BUTTON_ROWS;
@@ -235,6 +237,18 @@ QSizePolicy RDSoundPanel::sizePolicy() const
}
bool RDSoundPanel::dumpPanelUpdates() const
{
return panel_dump_panel_updates;
}
void RDSoundPanel::setDumpPanelUpdates(bool state)
{
panel_dump_panel_updates=state;
}
int RDSoundPanel::card(int outnum) const
{
return panel_card[outnum];
@@ -476,6 +490,45 @@ RDAirPlayConf::PanelType RDSoundPanel::currentType() const
}
QString RDSoundPanel::json(const QString &owner,int padding,bool final) const
{
QString ret;
QList<RDButtonPanel *> panels=panel_panels.value(owner);
ret+=RDJsonPadding(padding)+"\"array\": {\r\n";
ret+=RDJsonField("owner",owner,4+padding);
for(int i=0;i<(panels.size()-1);i++) {
ret+=panels.at(i)->json(4+padding);
}
if(panels.size()>0) {
ret+=panels.last()->json(4+padding,true);
}
ret+=RDJsonPadding(padding)+"}";
if(!final) {
ret+=",";
}
ret+="\r\n";
return ret;
}
QString RDSoundPanel::json(int padding) const
{
QString ret;
int count=0;
for(QMap<QString,QList<RDButtonPanel *> >::const_iterator it=panel_panels.
begin();it!=panel_panels.end();it++) {
ret+=json(it.key(),4,++count==panel_panels.size());
}
return ret;
}
void RDSoundPanel::setButton(RDAirPlayConf::PanelType type,int panel,
int row,int col,unsigned cartnum,
const QString &title)
@@ -558,6 +611,9 @@ void RDSoundPanel::changeUser()
{
panel_config_panels=rda->user()->configPanels();
UpdatePanels(rda->user()->name());
if(panel_dump_panel_updates) {
printf("{\r\n%s}\r\n",json(4).toUtf8().constData());
}
//
// Remove Old Panel Names
@@ -1421,7 +1477,7 @@ void RDSoundPanel::UpdatePanels(const QString &username)
list=panel_panels.value(username);
}
for(int i=panel_panels.value(username).size();i<size;i++) {
RDButtonPanel *panel=new RDButtonPanel(type,this);
RDButtonPanel *panel=new RDButtonPanel(type,i,this);
for(int j=0;j<PANEL_MAX_BUTTON_COLUMNS;j++) {
for(int k=0;k<PANEL_MAX_BUTTON_ROWS;k++) {
RDPanelButton *button=panel->panelButton(k,j);

View File

@@ -55,6 +55,8 @@ class RDSoundPanel : public RDWidget
~RDSoundPanel();
QSize sizeHint() const;
QSizePolicy sizePolicy() const;
bool dumpPanelUpdates() const;
void setDumpPanelUpdates(bool state);
int card(int outnum) const;
void setCard(int outnum,int card);
int port(int outnum) const;
@@ -84,6 +86,8 @@ class RDSoundPanel : public RDWidget
void setPauseEnabled(bool state);
int currentNumber() const;
RDAirPlayConf::PanelType currentType() const;
QString json(const QString &owner,int padding=0,bool final=false) const;
QString json(int padding=0) const;
public slots:
void setButton(RDAirPlayConf::PanelType type,int panel,int row,int col,
@@ -197,6 +201,7 @@ class RDSoundPanel : public RDWidget
bool panel_onair_flag;
QTimer *panel_scan_timer;
QString panel_caption;
bool panel_dump_panel_updates;
};
#endif // RDSOUND_PANEL_H

View File

@@ -206,6 +206,39 @@ QString RDJsonPadding(int padding)
}
QString RDJsonEscape(const QString &str)
{
QString ret;
for(int i=0;i<str.length();i++) {
QChar c=str.at(i);
switch(c.category()) {
case QChar::Other_Control:
ret+=QString::asprintf("\\u%04X",c.unicode());
break;
default:
switch(c.unicode()) {
case 0x22: // Quote
ret+="\\\"";
break;
case 0x5C: // Backslash
ret+="\\\\";
break;
default:
ret+=c;
break;
}
break;
}
}
return ret;
}
QString RDJsonNullField(const QString &name,int padding,bool final)
{
QString comma=",";
@@ -269,6 +302,8 @@ QString RDJsonField(const QString &name,const QString &value,int padding,
comma="";
}
ret=RDJsonEscape(value);
/*
for(int i=0;i<value.length();i++) {
QChar c=value.at(i);
switch(c.category()) {
@@ -293,6 +328,7 @@ QString RDJsonField(const QString &name,const QString &value,int padding,
break;
}
}
*/
return RDJsonPadding(padding)+"\""+name+"\": \""+ret+"\""+comma+"\r\n";
}

View File

@@ -54,6 +54,7 @@ extern QString RDXmlField(const QString &tag);
extern QString RDXmlEscape(const QString &str);
extern QString RDXmlUnescape(const QString &str);
extern QString RDJsonPadding(int padding);
extern QString RDJsonEscape(const QString &str);
extern QString RDJsonNullField(const QString &name,int padding=0,
bool final=false);
extern QString RDJsonField(const QString &name,bool value,int padding=0,

View File

@@ -42,7 +42,7 @@ RDCartDialog *panel_cart_dialog;
MainWidget::MainWidget(RDConfig *c,QWidget *parent)
: RDMainWindow("rdpanel",c)
{
// QPixmap panel_skin_pixmap;
bool dump_panel_updates=false;
QString err_msg;
//
@@ -64,11 +64,15 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent)
// Read Command Options
//
for(unsigned i=0;i<rda->cmdSwitch()->keys();i++) {
if(rda->cmdSwitch()->key(i)=="--dump-panel-updates") {
dump_panel_updates=true;
rda->cmdSwitch()->setProcessed(i,true);
}
if(!rda->cmdSwitch()->processed(i)) {
QMessageBox::critical(this,"RDPanel - "+tr("Error"),
tr("Unknown command option")+": "+
rda->cmdSwitch()->key(i));
exit(2);
exit(RDCoreApplication::ExitInvalidOption);
}
}
@@ -129,6 +133,7 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent)
"RDPanel",
rda->panelConf()->buttonLabelTemplate(),true,
panel_player,panel_cart_dialog,this);
panel_panel->setDumpPanelUpdates(dump_panel_updates);
panel_panel->setPauseEnabled(rda->panelConf()->panelPauseEnabled());
panel_panel->setCard(0,rda->panelConf()->card(RDAirPlayConf::SoundPanel1Channel));
panel_panel->setPort(0,rda->panelConf()->port(RDAirPlayConf::SoundPanel1Channel));

View File

@@ -2,7 +2,7 @@
//
// A Dedicated Cart Wall Utility for Rivendell.
//
// (C) Copyright 2002-2021 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2002-2023 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as

View File

@@ -468,6 +468,7 @@ rm -rf $RPM_BUILD_ROOT
%{_mandir}/man1/rdimport.1.gz
%{_mandir}/man1/rdlogmanager.1.gz
%{_mandir}/man1/rdmetadata.1.gz
%{_mandir}/man1/rdpanel.1.gz
%{_mandir}/man1/rdrender.1.gz
%{_mandir}/man1/rmlsend.1.gz
%{_mandir}/man1/rdsinglestart.1.gz