// recordlistmodel.cpp // // Data model for Rivendell RDCatch events. // // (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 // 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 #include "colors.h" RecordListModel::RecordListModel(QObject *parent) : QAbstractTableModel(parent) { d_sort_column=0; d_sort_order=Qt::AscendingOrder; d_sort_clauses[Qt::AscendingOrder]="asc"; d_sort_clauses[Qt::DescendingOrder]="desc"; // // Column Attributes // unsigned left=Qt::AlignLeft|Qt::AlignVCenter; unsigned center=Qt::AlignCenter; unsigned right=Qt::AlignRight|Qt::AlignVCenter; d_headers.push_back(tr("Description")); // 00 d_alignments.push_back(left); d_order_columns.push_back("`RECORDINGS`.`DESCRIPTION`"); d_headers.push_back(tr("Location")); // 01 d_alignments.push_back(left); d_order_columns.push_back("`RECORDINGS`.`STATION_NAME`"); d_headers.push_back(tr("Start")); // 02 d_alignments.push_back(left); d_order_columns.push_back("`RECORDINGS`.`START_TIME`"); d_headers.push_back(tr("End")); // 03 d_alignments.push_back(left); d_order_columns.push_back("`RECORDINGS`.`END_TIME`"); d_headers.push_back(tr("Router")); // 04 d_alignments.push_back(left); d_order_columns.push_back("`RECORDINGS`.`CHANNEL`"); d_headers.push_back(tr("Source")); // 05 d_alignments.push_back(left); d_order_columns.push_back("`RECORDINGS`.`SWITCH_INPUT`"); d_headers.push_back(tr("Destination")); // 06 d_alignments.push_back(left); d_order_columns.push_back("`RECORDINGS`.`SWITCH_OUTPUT`"); d_headers.push_back(tr("Su")); // 07 d_alignments.push_back(center); d_order_columns.push_back("`RECORDINGS`.`SUN`"); d_headers.push_back(tr("Mo")); // 08 d_alignments.push_back(center); d_order_columns.push_back(""); d_headers.push_back(tr("Tu")); // 09 d_alignments.push_back(center); d_order_columns.push_back(""); d_headers.push_back(tr("We")); // 10 d_alignments.push_back(center); d_order_columns.push_back(""); d_headers.push_back(tr("Th")); // 11 d_alignments.push_back(center); d_order_columns.push_back(""); d_headers.push_back(tr("Fr")); // 12 d_alignments.push_back(center); d_order_columns.push_back(""); d_headers.push_back(tr("Sa")); // 13 d_alignments.push_back(center); d_order_columns.push_back(""); d_headers.push_back(tr("Format")); // 14 d_alignments.push_back(left); d_order_columns.push_back(""); d_headers.push_back(tr("One Shot")); // 15 d_alignments.push_back(center); d_order_columns.push_back("`RECORDINGS`.`ONE_SHOT`"); d_headers.push_back(tr("Autotrim")); // 16 d_alignments.push_back(right); d_order_columns.push_back("`RECORDINGS`.`TRIM_THRESHOLD`"); d_headers.push_back(tr("Normalization")); // 17 d_alignments.push_back(right); d_order_columns.push_back("`RECORDINGS`.`NORMALIZE_LEVEL`"); d_headers.push_back(tr("Date Offsets")); // 18 d_alignments.push_back(center); d_order_columns.push_back(""); d_headers.push_back(tr("Error Status")); // 19 d_alignments.push_back(left); d_order_columns.push_back(""); d_headers.push_back(tr("Error Code")); // 20 d_alignments.push_back(right); d_order_columns.push_back("`RECORDINGS`.`EXIT_CODE`"); d_headers.push_back(tr("ID")); // 21 d_alignments.push_back(right); d_order_columns.push_back("`RECORDINGS`.`ID`"); setFilterSql(""); } RecordListModel::~RecordListModel() { } QPalette RecordListModel::palette() { return d_palette; } void RecordListModel::setPalette(const QPalette &pal) { d_palette=pal; } void RecordListModel::setFont(const QFont &font) { d_font=font; d_bold_font=font; d_bold_font.setWeight(QFont::Bold); } int RecordListModel::columnCount(const QModelIndex &parent) const { return d_headers.size(); } int RecordListModel::rowCount(const QModelIndex &parent) const { return d_texts.size(); } QVariant RecordListModel::headerData(int section,Qt::Orientation orient, int role) const { if((orient==Qt::Horizontal)&&(role==Qt::DisplayRole)) { return d_headers.at(section); } return QVariant(); } QVariant RecordListModel::data(const QModelIndex &index,int role) const { QString str; int col=index.column(); int row=index.row(); if(row=0)&&(d_is_nexts.at(index)!=state)) { d_is_nexts[index]=state; emit dataChanged(createIndex(index,0),createIndex(index,columnCount()-1)); } } void RecordListModel::clearNextRecords() { for(int i=0;i=0) { if(d_statuses.at(index)!=status) { d_statuses[index]=status; UpdateStatus(index); emit dataChanged(createIndex(index,0), createIndex(index,columnCount()-1)); } } } void RecordListModel::channelCounts(int chan,int *waiting,int *active, unsigned *rec_id) { *waiting=0; *active=0; *rec_id=0; for(int i=0;i=RDCATCHD_DYNAMIC_BASE_ID) { return QModelIndex(); // Don't process dynamic record events! } // // Find the insertion offset // int offset=d_texts.size(); for(int i=0;i list; for(int i=0;ifirst()) { updateRow(row.row(),q); emit dataChanged(createIndex(row.row(),0), createIndex(row.row(),columnCount())); } delete q; } } bool RecordListModel::refresh(unsigned id) { for(int i=0;itype()==RDNotification::CatchEventType) { switch(notify->action()) { case RDNotification::AddAction: if(d_ids.contains(notify->id().toUInt())) { refresh(notify->id().toUInt()); } else { addRecord(notify->id().toUInt()); } emit updateNextEvents(); break; case RDNotification::ModifyAction: refresh(notify->id().toUInt()); emit updateNextEvents(); break; case RDNotification::DeleteAction: removeRecord(notify->id().toUInt()); emit updateNextEvents(); break; case RDNotification::NoAction: case RDNotification::LastAction: break; } } } void RecordListModel::updateModel(const QString &filter_sql) { QList texts; QList icons; QString sql; RDSqlQuery *q=NULL; // // Load Router Table // d_router_names.clear(); sql=QString("select ")+ "`STATION_NAME`,"+ // 00 "`MATRIX`,"+ // 01 "`NAME` "+ // 02 "from `MATRICES`"; q=new RDSqlQuery(sql); while(q->next()) { d_router_names[q->value(0).toString()][q->value(1).toInt()]= q->value(2).toString(); } delete q; // // Load Feed Images // d_feed_images.clear(); sql=QString("select ")+ "`ID`,"+ // 00 "`DATA_SMALL_THUMB` "+ // 01 "from `FEED_IMAGES`"; q=new RDSqlQuery(sql); while(q->next()) { if(q->value(1).isNull()) { d_feed_images[q->value(0).toUInt()]=QVariant(); } else { /* d_feed_images[q->value(0).toUInt()]= QImage::fromData(q->value(1).toByteArray()). scaled(22,22,Qt::IgnoreAspectRatio,Qt::SmoothTransformation); */ d_feed_images[q->value(0).toUInt()]= QImage::fromData(q->value(1).toByteArray()); } } delete q; // // Load Model Rows // sql=sqlFields()+filter_sql; beginResetModel(); d_ids.clear(); d_hostnames.clear(); d_channels.clear(); d_cutnames.clear(); d_types.clear(); d_exit_codes.clear(); d_is_nexts.clear(); d_statuses.clear(); d_text_colors.clear(); d_back_colors.clear(); d_texts.clear(); d_icons.clear(); q=new RDSqlQuery(sql); while(q->next()) { d_ids.push_back(0); d_hostnames.push_back(QString()); d_channels.push_back(-1); d_cutnames.push_back(QString()); d_types.push_back(RDRecording::Recording); d_exit_codes.push_back(RDRecording::Ok); d_text_colors.push_back(QVariant()); d_back_colors.push_back(QVariant()); d_texts.push_back(texts); d_is_nexts.push_back(false); d_statuses.push_back(RDDeck::Idle); d_icons.push_back(texts); updateRow(d_texts.size()-1,q); } delete q; endResetModel(); } void RecordListModel::updateRowLine(int line) { if(linefirst()) { updateRow(line,q); } delete q; } } void RecordListModel::updateRow(int row,RDSqlQuery *q) { QString sql; RDSqlQuery *q1=NULL; RDCut *cut=NULL; QMap names; bool ok=false; // // Event Values // d_ids[row]=q->value(0).toUInt(); d_hostnames[row]=q->value(3).toString(); d_channels[row]=q->value(29).toUInt(); d_cutnames[row]=q->value(6).toString(); d_types[row]=(RDRecording::Type)q->value(23).toUInt(); d_exit_codes[row]=(RDRecording::ExitCode)q->value(25).toUInt(); // // Qt::TextColorRole // if(q->value(2).toString()=="Y") { d_text_colors[row]=QColor(EVENT_ACTIVE_TEXT_COLOR); } else { d_text_colors[row]=QColor(EVENT_INACTIVE_TEXT_COLOR); } // // Qt::BackgroundRole: // if(d_statuses.at(row)==RDDeck::Idle) { // So we don't trump a realtime status if(q->value(25).toInt()==0) { d_back_colors[row]=QVariant(); } else { d_back_colors[row]=QColor(EVENT_ERROR_COLOR); } } // // Qt::DecorationType // QList icons; for(int i=0;iiconEngine()->catchIcon((RDRecording::Type)q->value(23).toUInt()); icons[1]=rda->iconEngine()->stationIcon(); if(!q->value(40).isNull()) { icons[6]=d_feed_images.value(q->value(40).toUInt(),QVariant()); } // // Qt::DisplayType // QList texts; for(int i=0;ivalue(1); // Description if(q->value(7).toString()=="Y") { // Sun texts[7]=tr("Su"); } if(q->value(8).toString()=="Y") { // Mon texts[8]=tr("Mo"); } if(q->value(9).toString()=="Y") { // Tue texts[9]=tr("Tu"); } if(q->value(10).toString()=="Y") { // Wed texts[10]=tr("We"); } if(q->value(11).toString()=="Y") { // Thu texts[11]=tr("Th"); } if(q->value(12).toString()=="Y") { // Fri texts[12]=tr("Fr"); } if(q->value(13).toString()=="Y") { // Sat texts[13]=tr("Sa"); } texts[15]=q->value(26).toString(); // One Shot texts[19]=q->value(41).toString(); // Error Status texts[20]=QString::asprintf("%d",q->value(25).toInt()); // Error Code texts[21]=q->value(0).toString(); // Id // // Type Specific Attributes // switch((RDRecording::Type)q->value(23).toInt()) { case RDRecording::Recording: texts[1]=q->value(3).toString()+ QString::asprintf(" : %dR",q->value(21).toInt()); switch((RDRecording::StartType)q->value(27).toUInt()) { case RDRecording::HardStart: texts[2]=tr("Hard")+": "+rda->timeString(q->value(4).toTime()); break; case RDRecording::GpiStart: texts[2]=tr("Gpi")+": "+rda->timeString(q->value(4).toTime())+","+ rda->timeString(q->value(4).toTime().addMSecs(q->value(28).toInt()))+ ","+ QString::asprintf("%d:%d,",q->value(29).toInt(),q->value(30).toInt())+ QTime(0,0,0).addMSecs(q->value(31).toUInt()).toString("mm:ss"); break; } switch((RDRecording::EndType)q->value(32).toUInt()) { case RDRecording::LengthEnd: texts[3]=tr("Len")+": "+RDGetTimeLength(q->value(5).toUInt(),false,false); break; case RDRecording::HardEnd: texts[3]=tr("Hard")+": "+rda->timeString(q->value(33).toTime()); break; case RDRecording::GpiEnd: texts[3]=tr("Gpi")+": "+rda->timeString(q->value(33).toTime())+","+ rda->timeString(q->value(33).toTime().addMSecs(q->value(34).toInt()))+ QString::asprintf(",%d:%d",q->value(35).toInt(), q->value(36).toInt()); break; } texts[6]=RDCut::prettyText(q->value(6).toString()); icons[6]=rda->iconEngine()->catchIcon(RDRecording::Playout); sql=QString("select ")+ "`SWITCH_STATION`,"+ // 00 "`SWITCH_MATRIX` "+ // 01 "from `DECKS` where "+ "(`STATION_NAME`='"+RDEscapeString(q->value(3).toString())+"')&&"+ QString::asprintf("(`CHANNEL`=%d)",q->value(21).toInt()); q1=new RDSqlQuery(sql); if(q1->first()) { // Source texts[5]=GetSourceName(q1->value(0).toString(),q1->value(1).toInt(), q->value(14).toInt(),&ok); if(ok) { icons[5]=rda->iconEngine()->listIcon(RDIconEngine::Input); } } delete q1; texts[16]=LevelText(-q->value(17).toUInt()); // Autotrim texts[17]=LevelText(q->value(18).toInt()); // Normalization if((q->value(19).toUInt()==0)&&(q->value(20).toUInt()==0)) { texts[18]=tr("[none]"); } else { texts[18]= // Start/end Date Offsets QString::asprintf("%u / %u days", q->value(19).toUInt(),q->value(20).toUInt()); } break; case RDRecording::Playout: texts[1]=q->value(3).toString()+QString::asprintf(" : %dP", q->value(21).toInt()-128); texts[2]=tr("Hard")+": "+rda->timeString(q->value(4).toTime()); cut=new RDCut(q->value(6).toString()); if(cut->exists()) { texts[3]=tr("Len")+": "+RDGetTimeLength(cut->length(),false,false); } delete cut; texts[5]=RDCut::prettyText(q->value(6).toString()); icons[5]=rda->iconEngine()->catchIcon(RDRecording::Playout); break; case RDRecording::MacroEvent: texts[1]=q->value(3).toString(); texts[2]=tr("Hard")+": "+q->value(4).toTime(). toString(QString::asprintf("hh:mm:ss")); texts[5]=RDCart::prettyText(q->value(22).toUInt()); icons[5]=rda->iconEngine()->catchIcon(RDRecording::MacroEvent); break; case RDRecording::SwitchEvent: texts[1]=q->value(3).toString(); texts[2]=tr("Hard")+": "+rda->timeString(q->value(4).toTime()); texts[4]= d_router_names.value(q->value(3).toString()).value(q->value(21).toInt()); texts[5]=GetSourceName(q->value(3).toString(), // Source q->value(21).toInt(), q->value(14).toInt(),&ok); if(ok) { icons[5]=rda->iconEngine()->listIcon(RDIconEngine::Input); } texts[6]=GetDestinationName(q->value(3).toString(), // Dest q->value(21).toInt(), q->value(24).toInt()); icons[6]=rda->iconEngine()->listIcon(RDIconEngine::Output); break; case RDRecording::Download: texts[1]=q->value(3).toString(); texts[2]=tr("Hard")+": "+rda->timeString(q->value(4).toTime()); texts[5]=q->value(37).toString(); icons[5]=rda->iconEngine()->listIcon(RDIconEngine::Url); texts[6]=RDCut::prettyText(q->value(6).toString()); icons[6]=rda->iconEngine()->catchIcon(RDRecording::Playout); texts[16]=LevelText(-q->value(17).toUInt()); // Autotrim texts[17]=LevelText(q->value(18).toInt()); // Normalization break; case RDRecording::Upload: texts[1]=q->value(3).toString(); texts[2]=tr("Hard")+": "+rda->timeString(q->value(4).toTime()); texts[5]=RDCut::prettyText(q->value(6).toString()); icons[5]=rda->iconEngine()->catchIcon(RDRecording::Playout); if(q->value(39).toString().isEmpty()) { texts[6]=q->value(37).toString(); icons[6]=rda->iconEngine()->listIcon(RDIconEngine::Url); } else { texts[6]=q->value(39).toString(); // Feed Key Name } if(q->value(39).toString().isEmpty()) { texts[14]= RDSettings::description((RDSettings::Format)q->value(42).toUInt(), q->value(43).toUInt(),q->value(44).toUInt(), q->value(45).toUInt(),q->value(46).toUInt()); texts[17]=LevelText(q->value(18).toInt()); // Normalization } else { texts[14]= RDSettings::description((RDSettings::Format)q->value(47).toUInt(), q->value(48).toUInt(),q->value(49).toUInt(), q->value(50).toUInt(),q->value(51).toUInt()); texts[17]=LevelText(q->value(52).toInt()); // Normalization } break; case RDRecording::LastType: break; } d_icons[row]=icons; d_texts[row]=texts; } QString RecordListModel::sqlFields() const { QString sql=QString("select ")+ "`RECORDINGS`.`ID`,"+ // 00 "`RECORDINGS`.`DESCRIPTION`,"+ // 01 "`RECORDINGS`.`IS_ACTIVE`,"+ // 02 "`RECORDINGS`.`STATION_NAME`,"+ // 03 "`RECORDINGS`.`START_TIME`,"+ // 04 "`RECORDINGS`.`LENGTH`,"+ // 05 "`RECORDINGS`.`CUT_NAME`,"+ // 06 "`RECORDINGS`.`SUN`,"+ // 07 "`RECORDINGS`.`MON`,"+ // 08 "`RECORDINGS`.`TUE`,"+ // 09 "`RECORDINGS`.`WED`,"+ // 10 "`RECORDINGS`.`THU`,"+ // 11 "`RECORDINGS`.`FRI`,"+ // 12 "`RECORDINGS`.`SAT`,"+ // 13 "`RECORDINGS`.`SWITCH_INPUT`,"+ // 14 "`RECORDINGS`.`START_GPI`,"+ // 15 "`RECORDINGS`.`END_GPI`,"+ // 16 "`RECORDINGS`.`TRIM_THRESHOLD`,"+ // 17 "`RECORDINGS`.`NORMALIZE_LEVEL`,"+ // 18 "`RECORDINGS`.`STARTDATE_OFFSET`,"+ // 19 "`RECORDINGS`.`ENDDATE_OFFSET`,"+ // 20 "`RECORDINGS`.`CHANNEL`,"+ // 21 "`RECORDINGS`.`MACRO_CART`,"+ // 22 "`RECORDINGS`.`TYPE`,"+ // 23 "`RECORDINGS`.`SWITCH_OUTPUT`,"+ // 24 "`RECORDINGS`.`EXIT_CODE`,"+ // 25 "`RECORDINGS`.`ONE_SHOT`,"+ // 26 "`RECORDINGS`.`START_TYPE`,"+ // 27 "`RECORDINGS`.`START_LENGTH`,"+ // 28 "`RECORDINGS`.`START_MATRIX`,"+ // 29 "`RECORDINGS`.`START_LINE`,"+ // 30 "`RECORDINGS`.`START_OFFSET`,"+ // 31 "`RECORDINGS`.`END_TYPE`,"+ // 32 "`RECORDINGS`.`END_TIME`,"+ // 33 "`RECORDINGS`.`END_LENGTH`,"+ // 34 "`RECORDINGS`.`END_MATRIX`,"+ // 35 "`RECORDINGS`.`END_LINE`,"+ // 36 "`RECORDINGS`.`URL`,"+ // 37 "`RECORDINGS`.`QUALITY`,"+ // 38 "`FEEDS`.`KEY_NAME`,"+ // 39 "`FEEDS`.`CHANNEL_IMAGE_ID`,"+ // 40 "`RECORDINGS`.`EXIT_TEXT`,"+ // 41 "`RECORDINGS`.`FORMAT`,"+ // 42 "`RECORDINGS`.`CHANNELS`,"+ // 43 "`RECORDINGS`.`SAMPRATE`,"+ // 44 "`RECORDINGS`.`BITRATE`,"+ // 45 "`RECORDINGS`.`QUALITY`,"+ // 46 "`FEEDS`.`UPLOAD_FORMAT`,"+ // 47 "`FEEDS`.`UPLOAD_CHANNELS`,"+ // 48 "`FEEDS`.`UPLOAD_SAMPRATE`,"+ // 49 "`FEEDS`.`UPLOAD_BITRATE`,"+ // 50 "`FEEDS`.`UPLOAD_QUALITY`,"+ // 51 "`FEEDS`.`NORMALIZE_LEVEL` "+ // 52 "from `RECORDINGS` left join `CUTS` "+ "on (`RECORDINGS`.`CUT_NAME`=`CUTS`.`CUT_NAME`) left join `FEEDS` "+ "on (`RECORDINGS`.`FEED_ID`=`FEEDS`.`ID`) "; return sql; } QString RecordListModel::GetSourceName(QString station,int matrix,int input, bool *ok) { QString input_name=tr("[none]");; QString sql=QString("select `NAME` from `INPUTS` where ")+ "(`STATION_NAME`='"+RDEscapeString(station)+"')&&"+ QString::asprintf("(`MATRIX`=%d)&&",matrix)+ QString::asprintf("(`NUMBER`=%d)",input); RDSqlQuery *q=new RDSqlQuery(sql); if((*ok=q->first())) { input_name=q->value(0).toString(); } delete q; return input_name; } QString RecordListModel::GetDestinationName(QString station,int matrix, int output) { QString output_name; QString sql=QString("select `NAME` from `OUTPUTS` where ")+ "(`STATION_NAME`='"+RDEscapeString(station)+"')&&"+ QString::asprintf("(`MATRIX`=%d)&&",matrix)+ QString::asprintf("(`NUMBER`=%d)",output); RDSqlQuery *q=new RDSqlQuery(sql); if(q->first()) { output_name=q->value(0).toString(); } delete q; return output_name; } void RecordListModel::UpdateStatus(int line) { switch(d_statuses.at(line)) { case RDDeck::Offline: case RDDeck::LastStatus: d_back_colors[line]=QColor(EVENT_ERROR_COLOR); break; case RDDeck::Idle: d_back_colors[line]=QVariant(); break; case RDDeck::Ready: d_back_colors[line]=QColor(EVENT_READY_COLOR); break; case RDDeck::Waiting: d_back_colors[line]=QColor(EVENT_WAITING_COLOR); break; case RDDeck::Recording: d_back_colors[line]=QColor(EVENT_ACTIVE_COLOR); break; } return; RDRecording::ExitCode code=RDRecording::InternalError; QString err_text=tr("Unknown"); // // Get Exit Code And Text // QString sql=QString("select ")+ "`RECORDINGS`.`EXIT_CODE`,"+ // 00 "`CUTS`.`ORIGIN_NAME`,"+ // 01 "`CUTS`.`ORIGIN_DATETIME`,"+ // 02 "`RECORDINGS`.`EXIT_TEXT` "+ // 03 "from `RECORDINGS` left join `CUTS` "+ "on `RECORDINGS`.`CUT_NAME`=`CUTS`.`CUT_NAME` where "+ QString::asprintf("`RECORDINGS`.`ID`=%u",d_ids.at(line)); RDSqlQuery *q=new RDSqlQuery(sql); if(q->first()) { code=(RDRecording::ExitCode)q->value(0).toInt(); err_text=q->value(3).toString(); } delete q; // // Exit Code/Text // d_texts[line][20]=QString::asprintf("%u",code); switch(code) { case RDRecording::Ok: case RDRecording::Downloading: case RDRecording::Uploading: case RDRecording::RecordActive: case RDRecording::PlayActive: case RDRecording::Waiting: d_texts[line][19]=RDRecording::exitString(code); break; case RDRecording::Short: case RDRecording::LowLevel: case RDRecording::HighLevel: case RDRecording::Interrupted: case RDRecording::DeviceBusy: case RDRecording::NoCut: case RDRecording::UnknownFormat: d_texts[line][19]=RDRecording::exitString(code); d_back_colors[line]=QColor(EVENT_ERROR_COLOR); break; case RDRecording::ServerError: case RDRecording::InternalError: d_texts[line][19]=RDRecording::exitString(code)+": "+err_text; d_back_colors[line]=QColor(EVENT_ERROR_COLOR); break; } } QString RecordListModel::LevelText(int lvl) const { QString ret=tr("[none]"); if(lvl<0) { ret=QString::asprintf("%d dBFS",lvl/100); } return ret; }