diff --git a/.gitignore b/.gitignore index 94894e57..3b6af1c3 100644 --- a/.gitignore +++ b/.gitignore @@ -198,6 +198,7 @@ utils/rdselect_helper/rdconfig.h utils/rdselect_helper/rdprofile.cpp utils/rdselect_helper/rdprofile.h utils/rdselect_helper/rdselect_helper +utils/rdsinglestart/rdsinglestart utils/sas_shim/sas_shim xdg/install_usermode.sh xdg/rdalsaconfig-root-consolehelper diff --git a/ChangeLog b/ChangeLog index c56bac79..2bae3e53 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23873,3 +23873,18 @@ 2022-12-27 Fred Gleason * Incremented the package version to 4.0.0rc1. * Incremented the Python API version to 4.0.0rc1. +2022-12-30 Fred Gleason + * Added a 'RDLIBRARY.IS_SINGLETON' field to the database. + * Added a 'RDLOGEDIT.IS_SINGLETON' field to the database. + * Incremented the database version to 367. + * Added 'RDLibraryConf::isSingleton()' and + 'RDLibraryConf::setIsSingleton()' methods. + * Added 'RDLogeditConf::isSingleton()' and + 'RDLogeditConf::setIsSingleton()' methods. + * Modified rdcartslots(1), rdcastmanager(1), rdcatch(1), + rdlogmanager(1) and rdpanel(1) to allow only a single instance to + run at a time. + * Modified rdlibrary(1) and rdlogedit(1) to conditionally allow + only a single instance to run at a time. + * Added an rdsinglestart(1) utility. + * Added a 'wmctrl' dependency to the 'rivendell' RPM package. diff --git a/configure.ac b/configure.ac index 1912eea0..2f6ab252 100644 --- a/configure.ac +++ b/configure.ac @@ -703,6 +703,7 @@ AC_CONFIG_FILES([rivendell.spec \ utils/rdpopup/Makefile \ utils/rdrender/Makefile \ utils/rdselect_helper/Makefile \ + utils/rdsinglestart/Makefile \ utils/rdsoftkeys/Makefile \ utils/rmlsend/Makefile \ xdg/Makefile \ diff --git a/docs/manpages/Makefile.am b/docs/manpages/Makefile.am index e0880a42..2c289b3d 100644 --- a/docs/manpages/Makefile.am +++ b/docs/manpages/Makefile.am @@ -47,6 +47,7 @@ all-local: rdairplay.1\ rdrender.1\ rmlsend.1\ rdservice.8\ + rdsinglestart.1\ rdsoftkeys.1 man_MANS = rdairplay.1\ @@ -63,6 +64,7 @@ man_MANS = rdairplay.1\ rdrender.1\ rmlsend.1\ rdservice.8\ + rdsinglestart.1\ rdsoftkeys.1 EXTRA_DIST = exitcodes.xml\ @@ -94,6 +96,8 @@ EXTRA_DIST = exitcodes.xml\ rmlsend.xml\ rdservice.8\ rdservice.xml\ + rdsinglestart.1\ + rdsinglestart.xml\ rdsoftkeys.1\ rdsoftkeys.xml diff --git a/docs/manpages/rdsinglestart.xml b/docs/manpages/rdsinglestart.xml new file mode 100644 index 00000000..a77d6bf1 --- /dev/null +++ b/docs/manpages/rdsinglestart.xml @@ -0,0 +1,77 @@ + + + + + rdsinglestart + 1 + December 2022 + Linux Audio Manual + + + rdsinglestart + + Start an X11 client program so as to ensure that only a + single instance is run + + + + + + Fred + Gleason + fredg@paravelsystems.com + + Application Author + + + + + + + rdsinglestart + cmd-name + cmd-opt1 + .. + + + + + Description + + rdsinglestart1 is used to start + an X11 client program so as to ensure that only a single instance is run. + When invoked, rdsinglestart1 + will first look for existing windows whose title begins with the + string specified in cmd-name (case-insensitive). + If found, such windows will be raised, following which + rdsinglestart1 will exit. + If no matching windows are found, + rdsinglestart1 + will attempt to start cmd-name, using any + added options specified, following which it will exit. + + + + Notes + + rdsinglestart1 uses + wmctrl1 to query and interact + with the X11 windowing system. As such, it will work only with X11 + client programs. Attempts to use it with non-X11 programs will have + undefined results. + + + + See Also + + + wmctrl1 + + + + + diff --git a/docs/opsguide/rdadmin.configure_rdlibrary_dialog.png b/docs/opsguide/rdadmin.configure_rdlibrary_dialog.png index 41ba3303..339f2482 100644 Binary files a/docs/opsguide/rdadmin.configure_rdlibrary_dialog.png and b/docs/opsguide/rdadmin.configure_rdlibrary_dialog.png differ diff --git a/docs/opsguide/rdadmin.configure_rdlogedit_dialog.png b/docs/opsguide/rdadmin.configure_rdlogedit_dialog.png index 96256731..f69f5404 100644 Binary files a/docs/opsguide/rdadmin.configure_rdlogedit_dialog.png and b/docs/opsguide/rdadmin.configure_rdlogedit_dialog.png differ diff --git a/docs/opsguide/rdadmin.xml b/docs/opsguide/rdadmin.xml index 1ea34c78..30f51f25 100644 --- a/docs/opsguide/rdadmin.xml +++ b/docs/opsguide/rdadmin.xml @@ -1024,7 +1024,7 @@ - + The Configure RDLibrary Dialog @@ -1177,6 +1177,12 @@ Show Only First 100 Matches box to be ticked when starting a new instance of rdlibrary(1). + + The Allow Multiple Instances, if set + to Yes, will allow multiple, independent + instances of rdlibrary1 to + be run simultaneously. + The Channels: dropdown sets the default value of the Channels: @@ -1658,7 +1664,7 @@ - + The Configure RDLogEdit Dialog @@ -1733,6 +1739,12 @@ the transition type to use by default when adding a new log event. + + The Allow Multiple Instances, if set + to Yes, will allow multiple, independent + instances of rdlogedit1 to + be run simultaneously. + Configuring RDCartSlots diff --git a/docs/tables/rd_library.txt b/docs/tables/rd_library.txt index 38c0b9c3..c3a0813d 100644 --- a/docs/tables/rd_library.txt +++ b/docs/tables/rd_library.txt @@ -31,3 +31,4 @@ ENABLE_EDITOR enum('N','Y') SRC_CONVERTER int(11) LIMIT_SEARCH int(11) 0 = No, 1 = Yes, 2 = Previous SEARCH_LIMITED enum('N','Y') +IS_SINGLETON enum('N','Y') diff --git a/docs/tables/rd_logedit.txt b/docs/tables/rd_logedit.txt index a7d969c8..571dc7b4 100644 --- a/docs/tables/rd_logedit.txt +++ b/docs/tables/rd_logedit.txt @@ -24,3 +24,4 @@ REC_STOP_CART int(10) unsigned TRIM_THRESHOLD int(11) RIPPER_LEVEL int(11) DEFAULT_TRANS_TYPE int(11) +IS_SINGLETON enum('N','Y') diff --git a/importers/nexgen_filter.cpp b/importers/nexgen_filter.cpp index 97794703..0555dd93 100644 --- a/importers/nexgen_filter.cpp +++ b/importers/nexgen_filter.cpp @@ -63,7 +63,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("nexgen_filter","nexgen_filter",NEXGEN_FILTER_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,false)) { fprintf(stderr,"nexgen_filter: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/importers/wings_filter.cpp b/importers/wings_filter.cpp index bf407fc4..f9193947 100644 --- a/importers/wings_filter.cpp +++ b/importers/wings_filter.cpp @@ -51,7 +51,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("wings_filter","wings_filter",WINGS_FILTER_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,false)) { fprintf(stderr,"wings_filter: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/lib/dbversion.h b/lib/dbversion.h index aa1b0bae..67ecf000 100644 --- a/lib/dbversion.h +++ b/lib/dbversion.h @@ -24,7 +24,7 @@ /* * Current Database Version */ -#define RD_VERSION_DATABASE 366 +#define RD_VERSION_DATABASE 367 #endif // DBVERSION_H diff --git a/lib/rdapplication.cpp b/lib/rdapplication.cpp index 5bdc5308..6bc92c70 100644 --- a/lib/rdapplication.cpp +++ b/lib/rdapplication.cpp @@ -2,7 +2,7 @@ // // Base GUI Application Class // -// (C) Copyright 2021 Fred Gleason +// (C) Copyright 2021-2022 Fred Gleason // // 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 @@ -18,6 +18,8 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // +#include + #include "rdapplication.h" RDApplication *rda=NULL; @@ -36,6 +38,54 @@ RDApplication::~RDApplication() } +bool RDApplication::makeSingleInstance(QString *err_msg) +{ + // + // If we're already running, then just raise the window. + // + QStringList args; + args.clear(); + args.push_back("-l"); + QProcess *proc=new QProcess(this); + proc->start("wmctrl",args); + proc->waitForFinished(); + if(proc->exitStatus()!=QProcess::NormalExit) { + *err_msg=tr("wmctrl(1) process crashed"); + delete proc; + return false; + } + if(proc->exitCode()!=0) { + QString errs=QString::fromUtf8(proc->readAllStandardError()); + if(errs.isEmpty()) { + *err_msg=tr("wmctrl(1) not found\n"); + } + else { + *err_msg=QString::asprintf("wmctrl(1) process returned error [%s]", + errs.toUtf8().constData()); + } + delete proc; + return false; + } + bool found=false; + QStringList f0=QString::fromUtf8(proc->readAllStandardOutput()). + split("\n",QString::SkipEmptyParts); + for(int i=0;i=4) { + if(f1.at(3).trimmed().toLower()==commandName()) { + Raise(f1.at(0)); + found=true; + } + } + } + delete proc; + if(found) { + exit(0); + } + return true; +} + + RDIconEngine *RDApplication::iconEngine() const { return app_icon_engine; @@ -51,3 +101,33 @@ QString RDApplication::locale() } return ret; } + + +void RDApplication::Raise(const QString win_id) +{ + QStringList args; + QProcess *proc=NULL; + + args.push_back("-i"); + args.push_back("-R"); + args.push_back(win_id); + proc=new QProcess(this); + proc->start("wmctrl",args); + proc->waitForFinished(); + if(proc->exitStatus()!=QProcess::NormalExit) { + perror("wmctrl(1) process crashed"); + exit(1); + } + if(proc->exitCode()!=0) { + QString errs=QString::fromUtf8(proc->readAllStandardError()); + if(errs.isEmpty()) { + fprintf(stderr,"rdsinglestart: wmctrl(1) not found\n"); + } + else { + fprintf(stderr,"rdsinglestart: wmctrl(1) process returned error [%s]\n", + errs.toUtf8().constData()); + } + exit(1); + } + delete proc; +} diff --git a/lib/rdapplication.h b/lib/rdapplication.h index 57aa2bcf..07cb5abc 100644 --- a/lib/rdapplication.h +++ b/lib/rdapplication.h @@ -2,7 +2,7 @@ // // Base GUI Application Class // -// (C) Copyright 2021 Fred Gleason +// (C) Copyright 2021-2022 Fred Gleason // // 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 @@ -29,12 +29,15 @@ class RDApplication : public RDCoreApplication Q_OBJECT; public: RDApplication(const QString &module_name,const QString &cmdname, - const QString &usage,QObject *parent=0); + const QString &usage,QObject *parent); ~RDApplication(); + bool makeSingleInstance(QString *err_msg); RDIconEngine *iconEngine() const; static QString locale(); private: + void Raise(const QString win_id); + bool app_singleton; RDIconEngine *app_icon_engine; }; diff --git a/lib/rdcoreapplication.cpp b/lib/rdcoreapplication.cpp index a8db6f03..3826c1e6 100644 --- a/lib/rdcoreapplication.cpp +++ b/lib/rdcoreapplication.cpp @@ -620,6 +620,18 @@ void RDCoreApplication::userChangedData() } +QString RDCoreApplication::moduleName() const +{ + return app_module_name; +} + + +QString RDCoreApplication::commandName() const +{ + return app_command_name; +} + + bool RDCoreApplication::CheckService(QString *err_msg) { bool ret=false; diff --git a/lib/rdcoreapplication.h b/lib/rdcoreapplication.h index fb0723aa..5d7ea835 100644 --- a/lib/rdcoreapplication.h +++ b/lib/rdcoreapplication.h @@ -58,7 +58,7 @@ class RDCoreApplication : public QObject RDCoreApplication(const QString &module_name,const QString &cmdname, const QString &usage,QObject *parent=0); ~RDCoreApplication(); - bool open(QString *err_msg,ErrorType *err_type=NULL,bool check_svc=true); + bool open(QString *err_msg,ErrorType *err_type,bool check_svc); RDAirPlayConf *airplayConf(); RDCae *cae(); RDCmdSwitch *cmdSwitch(); @@ -94,6 +94,10 @@ class RDCoreApplication : public QObject signals: void userChanged(); + protected: + QString moduleName() const; + QString commandName() const; + private: bool CheckService(QString *err_msg); RDAirPlayConf *app_airplay_conf; diff --git a/lib/rdlibrary_conf.cpp b/lib/rdlibrary_conf.cpp index 9d39d1bb..a0ff6567 100644 --- a/lib/rdlibrary_conf.cpp +++ b/lib/rdlibrary_conf.cpp @@ -370,6 +370,19 @@ void RDLibraryConf::setSearchLimited(bool state) const } +bool RDLibraryConf::isSingleton() const +{ + return RDBool(RDGetSqlValue("RDLIBRARY","ID",lib_id,"IS_SINGLETON"). + toString()); +} + + +void RDLibraryConf::setIsSingleton(bool state) const +{ + SetRow("IS_SINGLETON",RDYesNo(state)); +} + + void RDLibraryConf::getSettings(RDSettings *s) const { QString sql; diff --git a/lib/rdlibrary_conf.h b/lib/rdlibrary_conf.h index 1cd4dcec..22bc970c 100644 --- a/lib/rdlibrary_conf.h +++ b/lib/rdlibrary_conf.h @@ -83,6 +83,8 @@ class RDLibraryConf void setLimitSearch(RDLibraryConf::SearchLimit lmt) const; bool searchLimited() const; void setSearchLimited(bool state) const; + bool isSingleton() const; + void setIsSingleton(bool state) const; static QString cdServerTypeText(CdServerType type); static QPixmap cdServerLogo(CdServerType type); diff --git a/lib/rdlogedit_conf.cpp b/lib/rdlogedit_conf.cpp index 687b80d0..13a4dc3d 100644 --- a/lib/rdlogedit_conf.cpp +++ b/lib/rdlogedit_conf.cpp @@ -328,6 +328,19 @@ void RDLogeditConf::getSettings(RDSettings *s) const } +bool RDLogeditConf::isSingleton() const +{ + return RDBool(RDGetSqlValue("RDLOGEDIT","STATION",lib_station,"IS_SINGLETON"). + toString()); +} + + +void RDLogeditConf::setIsSingleton(bool state) const +{ + SetRow("IS_SINGLETON",RDYesNo(state)); +} + + void RDLogeditConf::SetRow(const QString ¶m,int value) const { RDSqlQuery *q; diff --git a/lib/rdlogedit_conf.h b/lib/rdlogedit_conf.h index 6c0e6d45..00cc0304 100644 --- a/lib/rdlogedit_conf.h +++ b/lib/rdlogedit_conf.h @@ -70,6 +70,8 @@ class RDLogeditConf RDLogLine::TransType defaultTransType() const; void setDefaultTransType(RDLogLine::TransType type); void getSettings(RDSettings *s) const; + bool isSingleton() const; + void setIsSingleton(bool state) const; private: void SetRow(const QString ¶m,int value) const; diff --git a/lib/rdstation.cpp b/lib/rdstation.cpp index 556d0cb1..1178703e 100644 --- a/lib/rdstation.cpp +++ b/lib/rdstation.cpp @@ -843,6 +843,20 @@ bool RDStation::create(const QString &name,QString *err_msg, QString::asprintf("`MACHINE`=%d",i); RDSqlQuery::apply(sql); } + + // + // RDLibrary Parameters + // + sql=QString("insert into `RDLIBRARY` set ")+ + "`STATION`='"+RDEscapeString(name)+"'"; + RDSqlQuery::apply(sql); + + // + // RDLogEdit Parameters + // + sql=QString("insert into `RDLOGEDIT` set ")+ + "`STATION`='"+RDEscapeString(name)+"'"; + RDSqlQuery::apply(sql); } else { // Use Template Host sql=QString("select ")+ @@ -964,7 +978,8 @@ bool RDStation::create(const QString &name,QString *err_msg, "`ENABLE_EDITOR`,"+ // 20 "`SRC_CONVERTER`,"+ // 21 "`LIMIT_SEARCH`,"+ // 22 - "`SEARCH_LIMITED` "+ // 23 + "`SEARCH_LIMITED`,"+ // 23 + "`IS_SINGLETON` "+ // 24 "from `RDLIBRARY` where "+ "`STATION`='"+RDEscapeString(exemplar)+"'"; q=new RDSqlQuery(sql); @@ -994,6 +1009,7 @@ bool RDStation::create(const QString &name,QString *err_msg, QString::asprintf("`SRC_CONVERTER`=%d,",q->value(21).toInt())+ QString::asprintf("`LIMIT_SEARCH`=%d,",q->value(22).toInt())+ "`SEARCH_LIMITED`='"+RDEscapeString(q->value(23).toString())+"',"+ + "`IS_SINGLETON`='"+q->value(24).toString()+"',"+ "`STATION`='"+RDEscapeString(name)+"'"; RDSqlQuery::apply(sql); } @@ -1020,7 +1036,8 @@ bool RDStation::create(const QString &name,QString *err_msg, "`TRIM_THRESHOLD`,"+ // 14 "`RIPPER_LEVEL`,"+ // 15 "`DEFAULT_TRANS_TYPE`,"+ // 16 - "`ENABLE_SECOND_START` "+ // 17 + "`ENABLE_SECOND_START`,"+ // 17 + "`IS_SINGLETON` "+ // 18 "from `RDLOGEDIT` where "+ "`STATION`='"+RDEscapeString(exemplar)+"'"; q=new RDSqlQuery(sql); @@ -1044,7 +1061,8 @@ bool RDStation::create(const QString &name,QString *err_msg, QString::asprintf("`TRIM_THRESHOLD`=%d,",q->value(14).toInt())+ QString::asprintf("`RIPPER_LEVEL`=%d,",q->value(15).toInt())+ QString::asprintf("`DEFAULT_TRANS_TYPE`=%d,",q->value(16).toInt())+ - "`ENABLE_SECOND_START`='"+RDEscapeString(q->value(17).toString())+"'"; + "`ENABLE_SECOND_START`='"+RDEscapeString(q->value(17).toString())+"',"+ + "`IS_SINGLETON`='"+q->value(18).toString()+"'"; RDSqlQuery::apply(sql); } delete q; diff --git a/rdadmin/edit_rdlibrary.cpp b/rdadmin/edit_rdlibrary.cpp index 26788eda..b10a8d30 100644 --- a/rdadmin/edit_rdlibrary.cpp +++ b/rdadmin/edit_rdlibrary.cpp @@ -91,8 +91,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_maxlength_label=new QLabel(tr("Max Record Time:"),this); lib_maxlength_label->setFont(labelFont()); lib_maxlength_label->setGeometry(25,101,160,19); - lib_maxlength_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_maxlength_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // VOX threshold @@ -104,13 +103,11 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_vox_spin_label=new QLabel(tr("VOX Threshold:"),this); lib_vox_spin_label->setFont(labelFont()); lib_vox_spin_label->setGeometry(25,122,160,19); - lib_vox_spin_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_vox_spin_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); QLabel *lib_vox_spin_unit=new QLabel(tr("dbFS"),this); lib_vox_spin_unit->setFont(labelFont()); lib_vox_spin_unit->setGeometry(235,122,120,19); - lib_vox_spin_unit-> - setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + lib_vox_spin_unit->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // AutoTrim threshold @@ -122,13 +119,11 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_trim_spin_label=new QLabel(tr("AutoTrim Threshold:"),this); lib_trim_spin_label->setFont(labelFont()); lib_trim_spin_label->setGeometry(25,144,160,19); - lib_trim_spin_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_trim_spin_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); QLabel *lib_trim_spin_unit=new QLabel(tr("dbFS"),this); lib_trim_spin_unit->setFont(labelFont()); lib_trim_spin_unit->setGeometry(235,144,120,19); - lib_trim_spin_unit-> - setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + lib_trim_spin_unit->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Tail Preroll @@ -141,13 +136,11 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_preroll_spin_label=new QLabel(tr("Tail Preroll:"),this); lib_preroll_spin_label->setFont(labelFont()); lib_preroll_spin_label->setGeometry(25,166,160,19); - lib_preroll_spin_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_preroll_spin_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); QLabel *lib_preroll_spin_unit=new QLabel(tr("milliseconds"),this); lib_preroll_spin_unit->setFont(labelFont()); lib_preroll_spin_unit->setGeometry(245,166,120,19); - lib_preroll_spin_unit-> - setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + lib_preroll_spin_unit->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Ripper Device @@ -158,8 +151,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_ripdev_label=new QLabel(tr("Ripper Device:"),this); lib_ripdev_label->setFont(labelFont()); lib_ripdev_label->setGeometry(25,188,160,19); - lib_ripdev_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_ripdev_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Paranoia Level @@ -169,8 +161,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_paranoia_label=new QLabel(tr("Paranoia Level:"),this); lib_paranoia_label->setFont(labelFont()); lib_paranoia_label->setGeometry(25,210,160,19); - lib_paranoia_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_paranoia_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Read ISRC @@ -180,8 +171,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_isrc_label=new QLabel(tr("Read ISRCs from CD:"),this); lib_isrc_label->setFont(labelFont()); lib_isrc_label->setGeometry(25,232,160,19); - lib_isrc_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_isrc_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // CD Server Type @@ -195,12 +185,10 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, } connect(lib_cd_server_type_box,SIGNAL(activated(int)), this,SLOT(cdServerTypeData(int))); - lib_cd_server_label= - new QLabel(tr("CD Metadata Source:"),this); + lib_cd_server_label=new QLabel(tr("CD Metadata Source:"),this); lib_cd_server_label->setFont(labelFont()); lib_cd_server_label->setGeometry(25,256,160,19); - lib_cd_server_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_cd_server_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // CD Server @@ -214,8 +202,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, lib_cd_server_label=new QLabel(tr("CDDB Server:"),this); lib_cd_server_label->setFont(labelFont()); lib_cd_server_label->setGeometry(25,278,160,19); - lib_cd_server_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_cd_server_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Format @@ -226,8 +213,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_format_label=new QLabel(tr("Format:"),this); lib_format_label->setFont(labelFont()); lib_format_label->setGeometry(25,302,160,19); - lib_format_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_format_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Bitrate @@ -242,8 +228,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_bitrate_unit=new QLabel("kbps/chan",this); lib_bitrate_unit->setFont(labelFont()); lib_bitrate_unit->setGeometry(245,326,160,19); - lib_bitrate_unit-> - setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + lib_bitrate_unit->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Enable Editor @@ -255,8 +240,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_editor_label=new QLabel(tr("Allow External Editing:"),this); lib_editor_label->setFont(labelFont()); lib_editor_label->setGeometry(25,350,160,19); - lib_editor_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_editor_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Sample Rate Converter @@ -271,8 +255,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, QLabel *lib_converter_label=new QLabel(tr("Sample Rate Converter:"),this); lib_converter_label->setFont(labelFont()); lib_converter_label->setGeometry(10,374,175,19); - lib_converter_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_converter_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Limit Searches at Startup @@ -286,14 +269,26 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, new QLabel(tr("Limit Searches at Startup")+":",this); lib_limit_search_label->setFont(labelFont()); lib_limit_search_label->setGeometry(10,398,175,19); - lib_limit_search_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_limit_search_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + + // + // Allow Multiple Instances + // + lib_singleton_box=new QComboBox(this); + lib_singleton_box->setGeometry(190,422,80,19); + lib_singleton_box->insertItem(0,tr("No")); + lib_singleton_box->insertItem(1,tr("Yes")); + QLabel *lib_singleton_label= + new QLabel(tr("Allow Multiple Instances")+":",this); + lib_singleton_label->setFont(labelFont()); + lib_singleton_label->setGeometry(10,422,175,19); + lib_singleton_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Defaults // QLabel *default_label=new QLabel(tr("Defaults"),this); - default_label->setGeometry(25,436,120,19); + default_label->setGeometry(25,460,120,19); default_label->setFont(sectionLabelFont()); default_label->setAlignment(Qt::AlignRight); @@ -301,52 +296,47 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, // Default Channels // lib_channels_box=new QComboBox(this); - lib_channels_box->setGeometry(190,454,60,19); + lib_channels_box->setGeometry(190,478,60,19); QLabel *lib_channels_label=new QLabel(tr("Channels:"),this); lib_channels_label->setFont(labelFont()); - lib_channels_label->setGeometry(25,454,160,19); - lib_channels_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_channels_label->setGeometry(25,478,160,19); + lib_channels_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Default Record Mode // lib_recmode_box=new QComboBox(this); - lib_recmode_box->setGeometry(190,478,100,19); + lib_recmode_box->setGeometry(190,502,100,19); QLabel *lib_recmode_label=new QLabel(tr("Record Mode:"),this); lib_recmode_label->setFont(labelFont()); - lib_recmode_label->setGeometry(25,478,160,19); - lib_recmode_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_recmode_label->setGeometry(25,502,160,19); + lib_recmode_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Default Trim State // lib_trimstate_box=new QComboBox(this); - lib_trimstate_box->setGeometry(190,502,100,19); + lib_trimstate_box->setGeometry(190,526,100,19); QLabel *lib_trimstate_label=new QLabel(tr("AutoTrim:"),this); lib_trimstate_label->setFont(labelFont()); - lib_trimstate_label->setGeometry(25,502,160,19); - lib_trimstate_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_trimstate_label->setGeometry(25,526,160,19); + lib_trimstate_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Normalization Level // lib_riplevel_spin=new QSpinBox(this); - lib_riplevel_spin->setGeometry(190,526,40,19); + lib_riplevel_spin->setGeometry(190,550,40,19); lib_riplevel_spin->setMinimum(-99); lib_riplevel_spin->setMaximum(0); QLabel *lib_riplevel_spin_label=new QLabel(tr("Normalization Level:"),this); lib_riplevel_spin_label->setFont(labelFont()); - lib_riplevel_spin_label->setGeometry(25,526,160,19); - lib_riplevel_spin_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_riplevel_spin_label->setGeometry(25,550,160,19); + lib_riplevel_spin_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); QLabel *lib_riplevel_spin_unit=new QLabel(tr("dbFS"),this); lib_riplevel_spin_unit->setFont(labelFont()); - lib_riplevel_spin_unit->setGeometry(235,526,120,19); - lib_riplevel_spin_unit-> - setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + lib_riplevel_spin_unit->setGeometry(235,550,120,19); + lib_riplevel_spin_unit->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Ok Button @@ -450,6 +440,7 @@ EditRDLibrary::EditRDLibrary(RDStation *station,RDStation *cae_station, lib_editor_box->setCurrentIndex(lib_lib->enableEditor()); lib_converter_box->setCurrentIndex(lib_lib->srcConverter()); lib_limit_search_box->setCurrentIndex((int)lib_lib->limitSearch()); + lib_singleton_box->setCurrentIndex(!lib_lib->isSingleton()); } @@ -466,7 +457,7 @@ EditRDLibrary::~EditRDLibrary() QSize EditRDLibrary::sizeHint() const { - return QSize(405,630); + return QSize(405,654); } @@ -574,6 +565,7 @@ void EditRDLibrary::okData() lib_lib->setSrcConverter(lib_converter_box->currentIndex()); lib_lib->setLimitSearch((RDLibraryConf::SearchLimit) lib_limit_search_box->currentIndex()); + lib_lib->setIsSingleton(lib_singleton_box->currentIndex()==0); done(0); } diff --git a/rdadmin/edit_rdlibrary.h b/rdadmin/edit_rdlibrary.h index 4db19cba..030d8ad6 100644 --- a/rdadmin/edit_rdlibrary.h +++ b/rdadmin/edit_rdlibrary.h @@ -2,7 +2,7 @@ // // Edit an RDLibrry Configuration // -// (C) Copyright 2002-2021 Fred Gleason +// (C) Copyright 2002-2022 Fred Gleason // // 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 @@ -71,6 +71,7 @@ class EditRDLibrary : public RDDialog QComboBox *lib_editor_box; QComboBox *lib_converter_box; QComboBox *lib_limit_search_box; + QComboBox *lib_singleton_box; }; diff --git a/rdadmin/edit_rdlogedit.cpp b/rdadmin/edit_rdlogedit.cpp index 6ea7d18d..cc0150aa 100644 --- a/rdadmin/edit_rdlogedit.cpp +++ b/rdadmin/edit_rdlogedit.cpp @@ -282,6 +282,20 @@ EditRDLogedit::EditRDLogedit(RDStation *station,RDStation *cae_station, lib_default_transtype_box->insertItem(1,tr("Segue")); lib_default_transtype_box->insertItem(2,tr("Stop")); + // + // Allow Multiple Instances + // + lib_singleton_box=new QComboBox(this); + lib_singleton_box->setGeometry(180,434,100,19); + QLabel *lib_singleton_label= + new QLabel(tr("Allow Multiple Instances")+":",this); + lib_singleton_label->setFont(labelFont()); + lib_singleton_label->setGeometry(0,434,175,19); + lib_singleton_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter); + lib_singleton_box->insertItem(0,tr("No")); + lib_singleton_box->insertItem(1,tr("Yes")); + // // Ok Button // @@ -365,6 +379,7 @@ EditRDLogedit::EditRDLogedit(RDStation *station,RDStation *cae_station, ShowBitRates(lib_format_box->currentIndex(),lib_lib->bitrate()); lib_enable_second_start_box->setCurrentIndex(lib_lib->enableSecondStart()); lib_default_transtype_box->setCurrentIndex(lib_lib->defaultTransType()); + lib_singleton_box->setCurrentIndex(!lib_lib->isSingleton()); } @@ -381,7 +396,7 @@ EditRDLogedit::~EditRDLogedit() QSize EditRDLogedit::sizeHint() const { - return QSize(395,500); + return QSize(395,524); } @@ -493,6 +508,8 @@ void EditRDLogedit::okData() lib_lib->setEnableSecondStart(lib_enable_second_start_box->currentIndex()); lib_lib->setDefaultTransType( (RDLogLine::TransType)lib_default_transtype_box->currentIndex()); + lib_lib->setIsSingleton(lib_singleton_box->currentIndex()==0); + done(0); } diff --git a/rdadmin/edit_rdlogedit.h b/rdadmin/edit_rdlogedit.h index 36cb1894..5401bafd 100644 --- a/rdadmin/edit_rdlogedit.h +++ b/rdadmin/edit_rdlogedit.h @@ -2,7 +2,7 @@ // // Edit an RDLogEdit Configuration // -// (C) Copyright 2002-2021 Fred Gleason +// (C) Copyright 2002-2022 Fred Gleason // // 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 @@ -66,6 +66,7 @@ class EditRDLogedit : public RDDialog QLineEdit *lib_recstartcart_edit; QLineEdit *lib_recendcart_edit; QComboBox *lib_default_transtype_box; + QComboBox *lib_singleton_box; QString lib_filter; QString lib_group; }; diff --git a/rdairplay/rdairplay.cpp b/rdairplay/rdairplay.cpp index 77455b8d..d50b5b5f 100644 --- a/rdairplay/rdairplay.cpp +++ b/rdairplay/rdairplay.cpp @@ -67,19 +67,18 @@ MainWidget::MainWidget(RDConfig *config,QWidget *parent) // Open the Database // rda=new RDApplication("RDAirPlay","rdairplay",RDAIRPLAY_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDAirPlay - "+tr("Error"),err_msg); exit(1); } // - // Ensure Single Instance + // Ensure that we're the only instance // - air_lock=new RDInstanceLock(RDHomeDir()+"/.rdairplaylock"); - if(!air_lock->lock()) { - QMessageBox::information(this,tr("RDAirPlay"), - tr("Multiple instances not allowed!")); - exit(1); + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDAirPlay - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); } // @@ -1767,7 +1766,6 @@ void MainWidget::closeEvent(QCloseEvent *e) } rda->airplayConf()->setExitCode(RDAirPlayConf::ExitClean); rda->syslog(LOG_INFO,"RDAirPlay exiting"); - air_lock->unlock(); saveSettings(); exit(0); } @@ -1782,7 +1780,6 @@ void MainWidget::closeEvent(QCloseEvent *e) } rda->airplayConf()->setExitCode(RDAirPlayConf::ExitClean); rda->syslog(LOG_INFO,"RDAirPlay exiting"); - air_lock->unlock(); saveSettings(); exit(0); } diff --git a/rdairplay/rdairplay.h b/rdairplay/rdairplay.h index afcf7ee5..4dacec5a 100644 --- a/rdairplay/rdairplay.h +++ b/rdairplay/rdairplay.h @@ -22,7 +22,6 @@ #define RDAIRPLAY_H #include -#include #include #include @@ -142,7 +141,6 @@ class MainWidget : public RDMainWindow int air_cue_card; int air_cue_port; RDLogLine::TransType air_default_trans_type; - RDInstanceLock *air_lock; bool air_clear_filter; RDAirPlayConf::BarAction air_bar_action; bool air_pause_enabled; diff --git a/rdcartslots/rdcartslots.cpp b/rdcartslots/rdcartslots.cpp index a061ce30..2b341232 100644 --- a/rdcartslots/rdcartslots.cpp +++ b/rdcartslots/rdcartslots.cpp @@ -35,11 +35,20 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // Open the Database // rda=new RDApplication("RDCartSlots","rdcartslots",RDCARTSLOTS_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,false)) { QMessageBox::critical(this,"RDCartSlots - "+tr("Error"),err_msg); exit(1); } + // + // Ensure that we're the only instance + // + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDCartSlots - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); + } + // // Read Command Options // diff --git a/rdcastmanager/rdcastmanager.cpp b/rdcastmanager/rdcastmanager.cpp index 57e9081d..9689941e 100644 --- a/rdcastmanager/rdcastmanager.cpp +++ b/rdcastmanager/rdcastmanager.cpp @@ -59,13 +59,22 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // rda=new RDApplication("RDCastManager","rdcastmanager",RDCASTMANAGER_USAGE, this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDCastManager - "+tr("Error"),err_msg); exit(1); } setWindowIcon(rda->iconEngine()-> applicationIcon(RDIconEngine::RdCastManager,22)); + // + // Ensure that we're the only instance + // + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDCastManager - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); + } + // // Read Command Options // diff --git a/rdcatch/rdcatch.cpp b/rdcatch/rdcatch.cpp index aa5ae6fc..43ca574a 100644 --- a/rdcatch/rdcatch.cpp +++ b/rdcatch/rdcatch.cpp @@ -67,11 +67,20 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // Open the Database // rda=new RDApplication("RDCatch","rdcatch",RDCATCH_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDCatch - "+tr("Error"),err_msg); exit(1); } + // + // Ensure that we're the only instance + // + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDCatch - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); + } + // // Read Command Options // diff --git a/rdlibrary/rdlibrary.cpp b/rdlibrary/rdlibrary.cpp index a2d55c88..33b59045 100644 --- a/rdlibrary/rdlibrary.cpp +++ b/rdlibrary/rdlibrary.cpp @@ -87,12 +87,23 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // Open the Database // rda=new RDApplication("RDLibrary","rdlibrary",RDLIBRARY_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDLibrary - "+tr("Error"),err_msg); exit(1); } setWindowIcon(rda->iconEngine()->applicationIcon(RDIconEngine::RdLibrary,22)); + if(rda->libraryConf()->isSingleton()) { + // + // Ensure that we're the only instance + // + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDLibrary - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); + } + } + // // Read Command Options // diff --git a/rdlogedit/rdlogedit.cpp b/rdlogedit/rdlogedit.cpp index 549b0c04..ae12a1ea 100644 --- a/rdlogedit/rdlogedit.cpp +++ b/rdlogedit/rdlogedit.cpp @@ -61,12 +61,23 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // Open the Database // rda=new RDApplication("RDLogEdit","rdlogedit",RDLOGEDIT_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDLogEdit - "+tr("Error"),err_msg); exit(1); } log_import_path=RDGetHomeDir(); + if(rda->logeditConf()->isSingleton()) { + // + // Ensure that we're the only instance + // + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDLogEdit - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); + } + } + // // Read Command Options // diff --git a/rdlogin/rdlogin.cpp b/rdlogin/rdlogin.cpp index 4d89cd3d..5e365c9d 100644 --- a/rdlogin/rdlogin.cpp +++ b/rdlogin/rdlogin.cpp @@ -2,7 +2,7 @@ // // The User Login/Logout Utility for Rivendell. // -// (C) Copyright 2002-2021 Fred Gleason +// (C) Copyright 2002-2022 Fred Gleason // // 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 @@ -44,13 +44,25 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) setMinimumSize(sizeHint()); setMaximumHeight(sizeHint().height()); + // + // Open the database + // rda=new RDApplication("RDLogin","rdlogin",RDLOGIN_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDLogin - "+tr("Error"),err_msg); exit(1); } setWindowIcon(rda->iconEngine()->applicationIcon(RDIconEngine::Rivendell,22)); + // + // Ensure that we're the only instance + // + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDLogin - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); + } + // // Read Command Options // diff --git a/rdlogmanager/commandline_ops.cpp b/rdlogmanager/commandline_ops.cpp index 3d48a3c7..3b2ed6cd 100644 --- a/rdlogmanager/commandline_ops.cpp +++ b/rdlogmanager/commandline_ops.cpp @@ -45,8 +45,9 @@ int RunReportOperation(int argc,char *argv[],const QString &rptname, return 256; } - rda=static_cast(new RDCoreApplication("RDLogManager","rdlogmanager",RDLOGMANAGER_USAGE)); - if(!rda->open(&err_msg)) { + rda=static_cast(new RDCoreApplication("RDLogManager", + "rdlogmanager",RDLOGMANAGER_USAGE)); + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdlogmanager: %s\n",err_msg.toUtf8().constData()); exit(RDApplication::ExitNoDb); } diff --git a/rdlogmanager/logobject.cpp b/rdlogmanager/logobject.cpp index 22713f32..1c76b7d1 100644 --- a/rdlogmanager/logobject.cpp +++ b/rdlogmanager/logobject.cpp @@ -45,7 +45,7 @@ LogObject::LogObject(const QString &svcname,int start_offset, // Open the Database // rda=static_cast(new RDCoreApplication("RDLogManager","rdlogmanager","")); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,false)) { fprintf(stderr,"rdlogmanager: %s\n",err_msg.toUtf8().constData()); exit(RDApplication::ExitNoDb); } diff --git a/rdlogmanager/rdlogmanager.cpp b/rdlogmanager/rdlogmanager.cpp index b19894e8..7f6b4ea6 100644 --- a/rdlogmanager/rdlogmanager.cpp +++ b/rdlogmanager/rdlogmanager.cpp @@ -53,7 +53,7 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // Open the Database // rda=new RDApplication("RDLogManager","rdlogmanager",RDLOGMANAGER_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDLogManager - "+tr("Error"),err_msg); exit(RDApplication::ExitNoDb); } @@ -61,6 +61,15 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) applicationIcon(RDIconEngine::RdLogManager,22)); setWindowTitle(tr("RDLogManager")); + // + // Ensure that we're the only instance + // + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDLogManager - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); + } + // // CAE Connection // diff --git a/rdpanel/rdpanel.cpp b/rdpanel/rdpanel.cpp index e6e30556..ee95d396 100644 --- a/rdpanel/rdpanel.cpp +++ b/rdpanel/rdpanel.cpp @@ -54,12 +54,21 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // Open the Database // rda=new RDApplication("RDPanel","rdpanel",RDPANEL_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDPanel - "+tr("Error"),err_msg); exit(1); } setWindowIcon(rda->iconEngine()->applicationIcon(RDIconEngine::RdPanel,22)); + // + // Ensure that we're the only instance + // + if(!rda->makeSingleInstance(&err_msg)) { + QMessageBox::critical(this,"RDPanel - "+tr("Error"), + tr("Startup error")+": "+err_msg+"."); + exit(RDCoreApplication::ExitPriorInstance); + } + // // Read Command Options // @@ -191,14 +200,12 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) int next_output=1; for(int i=0;iportNames()->portName(panel_panel->card(i),panel_panel->port(i)); for(int j=0;jcard(i)==panel_panel->card(j))&& (panel_panel->port(i)==panel_panel->port(j))) { unique=false; - output=panel_panel->outputText(j).toInt(); } } if(unique) { diff --git a/rivendell.spec.in b/rivendell.spec.in index e1184f24..80289d4d 100644 --- a/rivendell.spec.in +++ b/rivendell.spec.in @@ -30,7 +30,7 @@ Release: @RPM_RELEASE@ License: GPL Packager: Fred Gleason Source: rivendell-@VERSION@.tar.gz -Requires: @MYSQL_PKG@ @QT_MYSQL_PKG@ @APACHE_PKG@ curl @USERMODE_PKG@ rsyslog qt5-qtstyleplugins qt5-qttranslations @PYPAD_DEPS@ libxslt icedax +Requires: @MYSQL_PKG@ @QT_MYSQL_PKG@ @APACHE_PKG@ curl @USERMODE_PKG@ rsyslog qt5-qtstyleplugins qt5-qttranslations @PYPAD_DEPS@ libxslt icedax wmctrl BuildRequires: qt5-qtbase-devel qt5-linguist BuildRoot: /var/tmp/rivendell-@VERSION@ Obsoletes: rivendell-base < 4.0.0 rivendell-opsguide < 4.0.0 rivendell-pypad < 4.0.0 @@ -465,6 +465,7 @@ rm -rf $RPM_BUILD_ROOT %{_mandir}/man1/rdmetadata.1.gz %{_mandir}/man1/rdrender.1.gz %{_mandir}/man1/rmlsend.1.gz +%{_mandir}/man1/rdsinglestart.1.gz %{_mandir}/man1/rdsoftkeys.1.gz %{_mandir}/man5/rd.conf.5.gz %{_mandir}/man8/rddbmgr.8.gz @@ -488,6 +489,7 @@ rm -rf $RPM_BUILD_ROOT %{_sbindir}/rdrssd %{_sbindir}/rdmarkerset %{_sbindir}/rdcleandirs +%{_bindir}/rdsinglestart %{_sbindir}/rddbmgr @HPI_FILE1@ @HPI_FILE2@ diff --git a/tests/audio_convert_test.cpp b/tests/audio_convert_test.cpp index 6690510f..a0595356 100644 --- a/tests/audio_convert_test.cpp +++ b/tests/audio_convert_test.cpp @@ -2,7 +2,7 @@ // // Test the Rivendell file format converter. // -// (C) Copyright 2010-2021 Fred Gleason +// (C) Copyright 2010-2022 Fred Gleason // // 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 @@ -43,7 +43,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("audio_convert_test","audio_convert_test",AUDIO_CONVERT_TEST_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"audio_convert_test: %s\n",(const char *)err_msg.toUtf8()); exit(1); } diff --git a/tests/audio_export_test.cpp b/tests/audio_export_test.cpp index 050de6d5..06d793a8 100644 --- a/tests/audio_export_test.cpp +++ b/tests/audio_export_test.cpp @@ -2,7 +2,7 @@ // // Test the Rivendell file format exporter. // -// (C) Copyright 2010-2021 Fred Gleason +// (C) Copyright 2010-2022 Fred Gleason // // 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 @@ -45,7 +45,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("audio_export_test","audio_export_test",AUDIO_EXPORT_TEST_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"audio_export_test: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/tests/audio_import_test.cpp b/tests/audio_import_test.cpp index 810d2ef3..95ac2761 100644 --- a/tests/audio_import_test.cpp +++ b/tests/audio_import_test.cpp @@ -2,7 +2,7 @@ // // Test Rivendell file importing. // -// (C) Copyright 2010-2021 Fred Gleason +// (C) Copyright 2010-2022 Fred Gleason // // 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 @@ -46,7 +46,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("audio_import_test","audio_import_test",AUDIO_IMPORT_TEST_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"audio_import_test: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/tests/audio_metadata_test.cpp b/tests/audio_metadata_test.cpp index e64da2e0..ceb02ae0 100644 --- a/tests/audio_metadata_test.cpp +++ b/tests/audio_metadata_test.cpp @@ -2,7 +2,7 @@ // // Test the Rivendell audio file metadata reader. // -// (C) Copyright 2018-2021 Fred Gleason +// (C) Copyright 2018-2022 Fred Gleason // // 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 @@ -36,7 +36,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("audio_metadata_test","audio_metadata_test",AUDIO_METADATA_TEST_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"audio_metadata_test: %s\n",(const char *)err_msg.toUtf8()); exit(1); } diff --git a/tests/db_charset_test.cpp b/tests/db_charset_test.cpp index 453579ce..ad7b848a 100644 --- a/tests/db_charset_test.cpp +++ b/tests/db_charset_test.cpp @@ -2,7 +2,7 @@ // // Display charset/collation parameters for a DB connection // -// (C) Copyright 2018-2021 Fred Gleason +// (C) Copyright 2018-2022 Fred Gleason // // 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 @@ -43,7 +43,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDApplication("db_charset_test","rdvairplayd",DB_CHARSET_TEST_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"db_charset_test: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/tests/delete_test.cpp b/tests/delete_test.cpp index 554d7c1d..93905bd1 100644 --- a/tests/delete_test.cpp +++ b/tests/delete_test.cpp @@ -40,7 +40,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=new RDApplication("delete_test","delete_test",DELETE_TEST_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"delete_test: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/tests/download_test.cpp b/tests/download_test.cpp index 97496566..95a24bf4 100644 --- a/tests/download_test.cpp +++ b/tests/download_test.cpp @@ -2,7 +2,7 @@ // // Test Rivendell file downloading. // -// (C) Copyright 2010-2021 Fred Gleason +// (C) Copyright 2010-2022 Fred Gleason // // 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 @@ -42,7 +42,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=new RDApplication("download_test","download_test",DOWNLOAD_TEST_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"download_test: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/tests/feed_image_test.cpp b/tests/feed_image_test.cpp index 68cc3729..2547a6d4 100644 --- a/tests/feed_image_test.cpp +++ b/tests/feed_image_test.cpp @@ -2,7 +2,7 @@ // // Test Rivendell image storage // -// (C) Copyright 2010-2021 Fred Gleason +// (C) Copyright 2010-2022 Fred Gleason // // 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 @@ -46,7 +46,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=new RDApplication("feed_image_test","feed_image_test",FEED_IMAGE_TEST_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"feed_image_test: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/tests/metadata_wildcard_test.cpp b/tests/metadata_wildcard_test.cpp index b6033fcc..c6edc3ea 100644 --- a/tests/metadata_wildcard_test.cpp +++ b/tests/metadata_wildcard_test.cpp @@ -2,7 +2,7 @@ // // Test the Rivendell multicast receiver routines // -// (C) Copyright 2018-2021 Fred Gleason +// (C) Copyright 2018-2022 Fred Gleason // // 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 @@ -41,7 +41,7 @@ MainObject::MainObject(QObject *parent) // rda=new RDApplication("metadata_wildcard_test","metadata_wildcard_test", METADATA_WILDCARD_TEST_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"metadata_wildcard_test: %s\n",(const char *)err_msg.toUtf8()); exit(1); } diff --git a/tests/meterstrip_test.cpp b/tests/meterstrip_test.cpp index e00f091a..418d0f62 100644 --- a/tests/meterstrip_test.cpp +++ b/tests/meterstrip_test.cpp @@ -2,7 +2,7 @@ // // Test harness for RDWaveWidget // -// (C) Copyright 2021 Fred Gleason +// (C) Copyright 2021-2022 Fred Gleason // // 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 @@ -36,7 +36,7 @@ MainWidget::MainWidget(QWidget *parent) // rda=new RDApplication("meterstrip_test","meterstrip_test",METERSTRIP_TEST_USAGE, this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"meterstrip_test - "+tr("Error"),err_msg); exit(RDApplication::ExitNoDb); } diff --git a/tests/notification_test.cpp b/tests/notification_test.cpp index 7a9e60bd..fa423877 100644 --- a/tests/notification_test.cpp +++ b/tests/notification_test.cpp @@ -30,7 +30,7 @@ MainObject::MainObject(QObject *parent) QString err_msg; rda=new RDApplication("notification_test","notification_test",NOTIFICATION_TEST_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"notification_test: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/tests/upload_test.cpp b/tests/upload_test.cpp index b05d6e4e..4b393982 100644 --- a/tests/upload_test.cpp +++ b/tests/upload_test.cpp @@ -41,7 +41,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=new RDApplication("upload_test","upload_test",UPLOAD_TEST_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"upload_test: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/tests/wavefactory_test.cpp b/tests/wavefactory_test.cpp index 203b667a..44b158d0 100644 --- a/tests/wavefactory_test.cpp +++ b/tests/wavefactory_test.cpp @@ -2,7 +2,7 @@ // // Test harness for RDWavefactory // -// (C) Copyright 2021 Fred Gleason +// (C) Copyright 2022 Fred Gleason // // 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 @@ -44,7 +44,7 @@ MainWidget::MainWidget(QWidget *parent) // rda=new RDApplication("wavefactory_test","wavefactory_test",WAVEFACTORY_TEST_USAGE, this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"wavefactory_test - "+tr("Error"),err_msg); exit(RDApplication::ExitNoDb); } diff --git a/tests/wavescene_test.cpp b/tests/wavescene_test.cpp index 7f952c03..4f17cd4b 100644 --- a/tests/wavescene_test.cpp +++ b/tests/wavescene_test.cpp @@ -2,7 +2,7 @@ // // Test harness for RDWaveScene // -// (C) Copyright 2021 Fred Gleason +// (C) Copyright 2022 Fred Gleason // // 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 @@ -43,7 +43,7 @@ MainWidget::MainWidget(QWidget *parent) // rda=new RDApplication("wavescene_test","wavescene_test",WAVESCENE_TEST_USAGE, this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"wavescene_test - "+tr("Error"),err_msg); exit(RDApplication::ExitNoDb); } diff --git a/tests/wavewidget_test.cpp b/tests/wavewidget_test.cpp index 90df7721..d971a849 100644 --- a/tests/wavewidget_test.cpp +++ b/tests/wavewidget_test.cpp @@ -2,7 +2,7 @@ // // Test harness for RDWaveWidget // -// (C) Copyright 2021 Fred Gleason +// (C) Copyright 2021-2022 Fred Gleason // // 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 @@ -42,7 +42,7 @@ MainWidget::MainWidget(QWidget *parent) // rda=new RDApplication("wavewidget_test","wavewidget_test",WAVEWIDGET_TEST_USAGE, this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"wavewidget_test - "+tr("Error"),err_msg); exit(RDApplication::ExitNoDb); } diff --git a/utils/Makefile.am b/utils/Makefile.am index f89c6aea..38e5675e 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -2,7 +2,7 @@ ## ## Automake.am for rivendell/utils ## -## (C) Copyright 2002-2021 Fred Gleason +## (C) Copyright 2002-2022 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as @@ -42,6 +42,7 @@ SUBDIRS = $(ALSACONFIG_RD_OPT)\ rdpopup\ rdrender\ rdselect_helper\ + rdsinglestart\ rdsoftkeys\ rmlsend diff --git a/utils/rdalsaconfig/rdalsaconfig.cpp b/utils/rdalsaconfig/rdalsaconfig.cpp index 67db7844..97cbee8f 100644 --- a/utils/rdalsaconfig/rdalsaconfig.cpp +++ b/utils/rdalsaconfig/rdalsaconfig.cpp @@ -362,7 +362,7 @@ Autogen::Autogen() // // Open the Database // - rda=new RDApplication("RDAlsaConfig","rdalsaconfig",RDALSACONFIG_USAGE); + rda=new RDApplication("RDAlsaConfig","rdalsaconfig",RDALSACONFIG_USAGE,this); if(!rda->open(&err_msg,NULL,false)) { fprintf(stderr,"rdalsaconfig: unable to open database [%s]\n", (const char *)err_msg.toUtf8()); diff --git a/utils/rdcheckcuts/rdcheckcuts.cpp b/utils/rdcheckcuts/rdcheckcuts.cpp index eedf6dff..58378821 100644 --- a/utils/rdcheckcuts/rdcheckcuts.cpp +++ b/utils/rdcheckcuts/rdcheckcuts.cpp @@ -2,7 +2,7 @@ // // Check Rivendell Cuts for Valid Audio // -// (C) Copyright 2012-2021 Fred Gleason +// (C) Copyright 2012-2022 Fred Gleason // // 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 @@ -43,7 +43,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("rdcheckcuts","rdcheckcuts",RDCHECKCUTS_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdcheckcuts: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/utils/rdclilogedit/rdclilogedit.cpp b/utils/rdclilogedit/rdclilogedit.cpp index 621980bf..7bec5c0a 100644 --- a/utils/rdclilogedit/rdclilogedit.cpp +++ b/utils/rdclilogedit/rdclilogedit.cpp @@ -49,7 +49,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("rdclilogedit","rdclilogedit",RDCLILOGEDIT_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdclilogedit: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/utils/rdconvert/rdconvert.cpp b/utils/rdconvert/rdconvert.cpp index afa312f1..acb12704 100644 --- a/utils/rdconvert/rdconvert.cpp +++ b/utils/rdconvert/rdconvert.cpp @@ -2,7 +2,7 @@ // // Rivendell file format converter. // -// (C) Copyright 2017-2021 Fred Gleason +// (C) Copyright 2017-2022 Fred Gleason // // 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 @@ -43,7 +43,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("rdconvert","rdconvert",RDCONVERT_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdconvert: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/utils/rddbmgr/revertschema.cpp b/utils/rddbmgr/revertschema.cpp index 68821998..dfc595bb 100644 --- a/utils/rddbmgr/revertschema.cpp +++ b/utils/rddbmgr/revertschema.cpp @@ -41,6 +41,16 @@ bool MainObject::RevertSchema(int cur_schema,int set_schema,QString *err_msg) // NEW SCHEMA REVERSIONS GO HERE... + // + // Revert 367 + // + if((cur_schema==367)&&(set_schemacur_schema)) { + sql=QString("alter table `RDLIBRARY` ")+ + "add column `IS_SINGLETON` enum('N','Y') not null default 'Y' "+ + "after `SEARCH_LIMITED`"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + sql=QString("alter table `RDLOGEDIT` ")+ + "add column `IS_SINGLETON` enum('N','Y') not null default 'Y' "+ + "after `DEFAULT_TRANS_TYPE`"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + WriteSchemaVersion(++cur_schema); } diff --git a/utils/rddelete/rddelete.cpp b/utils/rddelete/rddelete.cpp index 1a2e640c..1725ed10 100644 --- a/utils/rddelete/rddelete.cpp +++ b/utils/rddelete/rddelete.cpp @@ -2,7 +2,7 @@ // // A Batch Deleter for Rivendell. // -// (C) Copyright 2013-2021 Fred Gleason +// (C) Copyright 2013-2022 Fred Gleason // // 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 @@ -44,7 +44,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("rddelete","rddelete",RDDELETE_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rddelete: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/utils/rdexport/rdexport.cpp b/utils/rdexport/rdexport.cpp index 631339c4..168dde29 100644 --- a/utils/rdexport/rdexport.cpp +++ b/utils/rdexport/rdexport.cpp @@ -2,7 +2,7 @@ // // A Batch Exporter for Rivendell. // -// (C) Copyright 2016-2021 Fred Gleason +// (C) Copyright 2016-2022 Fred Gleason // // 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 @@ -57,7 +57,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("rdexport","rdexport",RDEXPORT_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdexport: %s\n",(const char *)err_msg.toUtf8()); exit(1); } diff --git a/utils/rdgpimon/rdgpimon.cpp b/utils/rdgpimon/rdgpimon.cpp index 02277b9c..ee194af2 100644 --- a/utils/rdgpimon/rdgpimon.cpp +++ b/utils/rdgpimon/rdgpimon.cpp @@ -39,7 +39,7 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // Open the Database // rda=new RDApplication("RDGpiMon","rdgpimon",RDGPIMON_USAGE,this); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { QMessageBox::critical(this,"RDGpiMon - "+tr("Error"),err_msg); exit(1); } diff --git a/utils/rdimport/rdimport.cpp b/utils/rdimport/rdimport.cpp index cb02b9f8..bbefd6e5 100644 --- a/utils/rdimport/rdimport.cpp +++ b/utils/rdimport/rdimport.cpp @@ -109,7 +109,7 @@ MainObject::MainObject(QObject *parent) // rda=static_cast(new RDCoreApplication("rdimport","rdimport", RDIMPORT_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdimport: %s\n",err_msg.toUtf8().constData()); ErrorExit(RDApplication::ExitNoDb); } diff --git a/utils/rdmarkerset/rdmarkerset.cpp b/utils/rdmarkerset/rdmarkerset.cpp index 27b6d0c0..bc582775 100644 --- a/utils/rdmarkerset/rdmarkerset.cpp +++ b/utils/rdmarkerset/rdmarkerset.cpp @@ -2,7 +2,7 @@ // // Command-line tool for setting Rivendell Cut Markers // -// (C) Copyright 2014-2021 Fred Gleason +// (C) Copyright 2014-2022 Fred Gleason // // 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 @@ -58,7 +58,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("rdmarkerset","rdmarkerset",RDMARKERSET_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdmarkerset: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/utils/rdmetadata/rdmetadata.cpp b/utils/rdmetadata/rdmetadata.cpp index 8ee80a8f..b11f00fb 100644 --- a/utils/rdmetadata/rdmetadata.cpp +++ b/utils/rdmetadata/rdmetadata.cpp @@ -3,7 +3,7 @@ // Command-line tool for setting Rivendell Cart Metadata // // Patrick Linstruth -// (C) Copyright 2019-2021 Fred Gleason +// (C) Copyright 2019-2022 Fred Gleason // // 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 @@ -50,7 +50,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("rdmetadata","rdmetadata",RDMETADATA_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdmetadata: %s\n",err_msg.toUtf8().constData()); exit(1); } diff --git a/utils/rdrender/rdrender.cpp b/utils/rdrender/rdrender.cpp index b9720068..8c54f2a2 100644 --- a/utils/rdrender/rdrender.cpp +++ b/utils/rdrender/rdrender.cpp @@ -68,7 +68,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("rdrender","rdrender",RDRENDER_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { fprintf(stderr,"rdrender: %s\n",(const char *)err_msg.toUtf8()); exit(1); } diff --git a/utils/rdsinglestart/Makefile.am b/utils/rdsinglestart/Makefile.am new file mode 100644 index 00000000..4fb11411 --- /dev/null +++ b/utils/rdsinglestart/Makefile.am @@ -0,0 +1,48 @@ +## Makefile.am +## +## (C) Copyright 2022 Fred Gleason +## +## 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 +## published by the Free Software Foundation. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public +## License along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +## +## Use automake to process this into a Makefile.in + +AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -Wno-strict-aliasing -std=c++11 -fPIC -I$(top_srcdir)/lib @QT5_CFLAGS@ @MUSICBRAINZ_CFLAGS@ @IMAGEMAGICK_CFLAGS@ +LIBS = -L$(top_srcdir)/lib +MOC = @QT_MOC@ + +# The dependency for qt's Meta Object Compiler (moc) +moc_%.cpp: %.h + $(MOC) $< -o $@ + +bin_PROGRAMS = rdsinglestart + +dist_rdsinglestart_SOURCES = rdsinglestart.cpp rdsinglestart.h + +rdsinglestart_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT5_LIBS@ @MUSICBRAINZ_LIBS@ @IMAGEMAGICK_LIBS@ + +CLEANFILES = *~\ + *.exe\ + *.idb\ + *ilk\ + *.obj\ + *.pdb\ + *.qm\ + moc_* + +MAINTAINERCLEANFILES = *~\ + *.tar.gz\ + aclocal.m4\ + configure\ + Makefile.in\ + moc_* diff --git a/utils/rdsinglestart/rdsinglestart.cpp b/utils/rdsinglestart/rdsinglestart.cpp new file mode 100644 index 00000000..62f1070a --- /dev/null +++ b/utils/rdsinglestart/rdsinglestart.cpp @@ -0,0 +1,149 @@ +// rdsinglestart.cpp +// +// Start a program so as to allow only a single instance. +// +// (C) Copyright 2022 Fred Gleason +// +// 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 +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include + +#include +#include + +#include "rdsinglestart.h" + +MainObject::MainObject(QObject *parent) + :QObject(parent) +{ + QStringList args; + QProcess *proc=NULL; + + // + // Read Command Options + // + RDCmdSwitch *cmd=new RDCmdSwitch("rdsinglestart",RDSINGLESTART_USAGE); + if(cmd->keys()<1) { + perror("missing argument"); + exit(1); + } + for(unsigned i=0;i<(cmd->keys()-1);i++) { + } + QStringList f0=cmd->key(cmd->keys()-1).split("/",QString::SkipEmptyParts); + QString program=f0.last().trimmed(); + + // + // If we're already running, then just raise the window. + // + args.clear(); + args.push_back("-l"); + proc=new QProcess(this); + proc->start("wmctrl",args); + proc->waitForFinished(); + if(proc->exitStatus()!=QProcess::NormalExit) { + perror("wmctrl(1) process crashed"); + exit(1); + } + if(proc->exitCode()!=0) { + QString errs=QString::fromUtf8(proc->readAllStandardError()); + if(errs.isEmpty()) { + fprintf(stderr,"rdsinglestart: wmctrl(1) not found\n"); + } + else { + fprintf(stderr,"rdsinglestart: wmctrl(1) process returned error [%s]\n", + errs.toUtf8().constData()); + } + exit(1); + } + bool found=false; + f0=QString::fromUtf8(proc->readAllStandardOutput()). + split("\n",QString::SkipEmptyParts); + for(int i=0;i=4) { + if(f1.at(3).trimmed().toLower()==program) { + Raise(f1.at(0)); + found=true; + } + } + } + delete proc; + if(found) { + exit(0); + } + + // + // Otherwise, start a new process + // + Start(cmd); + + exit(0); +} + + +void MainObject::Raise(const QString win_id) +{ + QStringList args; + QProcess *proc=NULL; + + args.push_back("-i"); + args.push_back("-R"); + args.push_back(win_id); + proc=new QProcess(this); + proc->start("wmctrl",args); + proc->waitForFinished(); + if(proc->exitStatus()!=QProcess::NormalExit) { + perror("wmctrl(1) process crashed"); + exit(1); + } + if(proc->exitCode()!=0) { + QString errs=QString::fromUtf8(proc->readAllStandardError()); + if(errs.isEmpty()) { + fprintf(stderr,"rdsinglestart: wmctrl(1) not found\n"); + } + else { + fprintf(stderr,"rdsinglestart: wmctrl(1) process returned error [%s]\n", + errs.toUtf8().constData()); + } + exit(1); + } + delete proc; +} + + +void MainObject::Start(RDCmdSwitch *cmd) +{ + char *args[cmd->keys()+1]; + memset(args,0,sizeof(char *)*(cmd->keys()+1)); + + for(unsigned i=0;ikeys();i++) { + args[i]=(char *)malloc(cmd->key(i).toUtf8().size()+1); + strcpy(args[i],cmd->key(i).toUtf8().constData()); + } + if(fork()==0) { + execvp(args[0],args); + perror("rdsinglestart"); + } + exit(0); +} + + +int main(int argc,char *argv[]) +{ + QApplication a(argc,argv,false); + new MainObject(); + return a.exec(); +} diff --git a/utils/rdsinglestart/rdsinglestart.h b/utils/rdsinglestart/rdsinglestart.h new file mode 100644 index 00000000..e75301df --- /dev/null +++ b/utils/rdsinglestart/rdsinglestart.h @@ -0,0 +1,41 @@ +// rdsinglestart.h +// +// Start a program so as to allow only a single instance. +// +// (C) Copyright 2022 Fred Gleason +// +// 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 +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef RDSINGLESTART_H +#define RDSINGLESTART_H + +#include + +#include + +#define RDSINGLESTART_USAGE "\n" + +class MainObject : public QObject +{ + public: + MainObject(QObject *parent=0); + + private: + void Raise(const QString win_id); + void Start(RDCmdSwitch *cmd); +}; + + +#endif // RDSINGLESTART_H diff --git a/web/webget/webget.cpp b/web/webget/webget.cpp index ea35ec12..1445d811 100644 --- a/web/webget/webget.cpp +++ b/web/webget/webget.cpp @@ -2,7 +2,7 @@ // // Rivendell upload/download utility // -// (C) Copyright 2018-2021 Fred Gleason +// (C) Copyright 2018-2022 Fred Gleason // // 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 @@ -51,7 +51,7 @@ MainObject::MainObject(QObject *parent) // Open the Database // rda=static_cast(new RDCoreApplication("webget.cgi","webget.cgi",WEBGET_CGI_USAGE,this)); - if(!rda->open(&err_msg)) { + if(!rda->open(&err_msg,NULL,true)) { TextExit(err_msg,500,LINE_NUMBER); }