[mythtv] Stop recording patch and some small fixes

Tako Schotanus quintesse at palacio-cristal.com
Thu Sep 18 04:05:13 EDT 2003


1) The reason for this patch is to implement a "Stop" for recordings.
Right now the only thing you can do is delete a recording which often is 
not what you want.

2) The second reason for this patch is the fact that I didn't like the 
fact that the Watch/Delete Recording pages (among others) can not be 
used by using Up/Down and Enter alone. What I was missing was a way to 
access all the possible actions from a menu or something like that. 
That's what this patch does as well: it adds a pop-up menu with all the 
possible actions. The actions are:
 - Play
 - Stop (only if selected item is currently recording)
 - Auto Expire / Don't Auto Expire (toggle)
 - Delete
 - Cancel

NB: Both Stop and Delete ask for permission first.

I connected the pop-up to the I key and folded the existing 
functionality of the I key (Auto Expire switch) into the menu.

When you stop a recording its end time will be set to the current time 
instead of leaving it the way it was programmed. This way you'll be able 
to see that the program was stopped (it would be strange not to be able 
to see that a program was stopped, the info would still tell you that 1 
1/2 hours had been recorded when in fact it might have been only 10 
minutes). NB: This also means that the .nuv file will be renamed to 
match the new times!

3) More or less as a side-effect of the changes to the end time of a 
recording I also made a change to the start time of a recording. Now 
when a recording is started it will register the actual starting time of 
the recording, not the starting time of the program it is supposed to be 
recording. Why? This was discussed today: currently when the backend is 
stopped or crashes while recording it will overwrite what it had been 
recording the moment it gets started again. This happend because it sees 
it still has an unfinshed recording at has no knowledge it had already 
started recording it before it was stopped/crashed so it will start 
recording the rest of the program overwriting the old data. Because this 
patch uses the actual time of the recording it won't overwrite the old 
data anymore (unless it was less than a minute ago, but hey, you would 
only lose a minute).

4) A fix for the fact that deleting a program didn't actually stop the 
backend from continuing to record the program. This was due to the fact 
that the item in the record table was not removed so the backend would 
keep restarting the recording.

5) A fix that prevented programs that had been started using the Instant 
Recording option from being deleted (and stopped). This had to do with 
the fact that almost all the code expects programs to start and finish 
at exact minute intervals. The Instant Recording option was writing 
program info with seconds in them messing up several SQL queries.

Ok, that's all for the functional changes.

Now for the one big code change that might or might not make it past Isaac.

Isaac, when looking at the code in playbackbox I decided that to make 
the action menu pop-up I would have to do too much code duplication. The 
code would have gotten even uglier with me just patching my changes onto 
what was already there. So I decided to take out the entire code that 
makes the pop-up menu and put it in a seperate class (SimpleMythPopup). 
That made the code a lot easier to read and manage because making any 
kind of simple pop-up (only labels and buttons) is now extremely simple.

But there are a couple of things that I have no answer to:
1. maybe there is already something like the SimpleMythPopup that I just 
don't know about (but then again, neither did the playbackbox' original 
author in that case (you?)). If so please tell me so I can take my stuff 
out and use whatever is the standard.
2. if nothing like it exists it could maybe be useful to others and 
should therefore be moved to a more central place, but that's up to you 
to decide I think.
3. right now I haven't even put the class in its own file! I feel 
strongly that it should at least be put in its own files but then again 
you might feel even stronger about _not_ doing that ;-)

Phew, quite some e-mail, the text is almost larger than the patch ;-)

Cheers,
 -Tako
-------------- next part --------------
Index: libs/libmythtv/programinfo.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/programinfo.cpp,v
retrieving revision 1.69
diff -b -u -2 -r1.69 programinfo.cpp
--- libs/libmythtv/programinfo.cpp	13 Aug 2003 15:28:51 -0000	1.69
+++ libs/libmythtv/programinfo.cpp	18 Sep 2003 00:24:12 -0000
@@ -712,5 +712,5 @@
     }
 
-    QString starts = startts.toString("yyyyMMddhhmm");
+    QString starts = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
     QString ends = endts.toString("yyyyMMddhhmm");
 
Index: libs/libmythtv/remoteutil.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/remoteutil.cpp,v
retrieving revision 1.17
diff -b -u -2 -r1.17 remoteutil.cpp
--- libs/libmythtv/remoteutil.cpp	7 Sep 2003 00:40:28 -0000	1.17
+++ libs/libmythtv/remoteutil.cpp	18 Sep 2003 00:24:13 -0000
@@ -69,4 +69,12 @@
 }
 
+void RemoteStopRecording(ProgramInfo *pginfo)
+{
+    QStringList strlist = QString("STOP_RECORDING");
+    pginfo->ToStringList(strlist);
+
+    gContext->SendReceiveStringList(strlist);
+}
+
 void RemoteDeleteRecording(ProgramInfo *pginfo)
 {
Index: libs/libmythtv/remoteutil.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/libs/libmythtv/remoteutil.h,v
retrieving revision 1.14
diff -b -u -2 -r1.14 remoteutil.h
--- libs/libmythtv/remoteutil.h	7 Sep 2003 00:40:28 -0000	1.14
+++ libs/libmythtv/remoteutil.h	18 Sep 2003 00:24:13 -0000
@@ -12,4 +12,5 @@
 bool RemoteCheckFile(ProgramInfo *pginfo);
 void RemoteQueueTranscode(ProgramInfo *pginfo, int state);
+void RemoteStopRecording(ProgramInfo *pginfo);
 void RemoteDeleteRecording(ProgramInfo *pginfo);
 bool RemoteGetAllPendingRecordings(vector<ProgramInfo *> &recordinglist);
Index: programs/mythbackend/mainserver.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/programs/mythbackend/mainserver.cpp,v
retrieving revision 1.79
diff -b -u -2 -r1.79 mainserver.cpp
--- programs/mythbackend/mainserver.cpp	12 Sep 2003 16:49:54 -0000	1.79
+++ programs/mythbackend/mainserver.cpp	18 Sep 2003 00:24:21 -0000
@@ -3,4 +3,5 @@
 #include <qdatetime.h>
 #include <qfile.h>
+#include <qdir.h>
 #include <qurl.h>
 #include <qthread.h>
@@ -228,4 +229,8 @@
         HandleQueueTranscode(listline, pbs, TRANSCODE_STOP);
     }
+    else if (command == "STOP_RECORDING")
+    {
+        HandleStopRecording(listline, pbs);
+    }
     else if (command == "DELETE_RECORDING")
     {
@@ -766,4 +771,154 @@
 }
 
+void MainServer::HandleStopRecording(QStringList &slist, PlaybackSock *pbs)
+{
+    ProgramInfo *pginfo = new ProgramInfo();
+    pginfo->FromStringList(slist, 1);
+
+    DoHandleStopRecording(pginfo, pbs);
+}
+
+void MainServer::DoHandleStopRecording(ProgramInfo *pginfo, PlaybackSock *pbs)
+{
+    QSocket *pbssock = NULL;
+    if (pbs)
+        pbssock = pbs->getSocket();
+
+    if (ismaster && pginfo->hostname != gContext->GetHostName())
+    {
+        PlaybackSock *slave = getSlaveByHostname(pginfo->hostname);
+
+        int num = -1;
+
+        if (slave) 
+        {
+           num = slave->StopRecording(pginfo);
+
+           if (num > 0)
+              (*encoderList)[num]->StopRecording();
+
+           if (pbssock)
+           {
+               QStringList outputlist = "0";
+               SendResponse(pbssock, outputlist);
+           }
+
+           delete pginfo;
+           return;
+        }
+    }
+
+    int recnum = -1;
+
+    QMap<int, EncoderLink *>::Iterator iter = encoderList->begin();
+    for (; iter != encoderList->end(); ++iter)
+    {
+        EncoderLink *elink = iter.data();
+
+        if (elink->isLocal() && elink->MatchesRecording(pginfo))
+        {
+            recnum = iter.key();
+
+            elink->StopRecording();
+
+            while (elink->IsBusyRecording() || 
+                   elink->GetState() == kState_ChangingState)
+            {
+                usleep(100);
+            }
+        }
+    }
+
+    QString thequery;
+	QSqlQuery query;
+	
+    dblock.lock();
+
+    MythContext::KickDatabase(m_db);
+
+	ScheduledRecording *rec = pginfo->GetScheduledRecording(m_db);
+	if (rec)
+	{
+		thequery = QString("DELETE FROM record WHERE recordid = %1").arg(rec->getRecordID());
+		
+		query = m_db->exec(thequery);
+	
+		if (!query.isActive())
+		{
+			cerr << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
+				 << " DB Error: currently recording program deletion failed, SQL query "
+				 << "was:" << endl;
+			cerr << thequery << endl;
+		}
+	}
+
+    QString startts = pginfo->startts.toString("yyyyMMddhhmm");
+    startts += "00";
+    QString endts = pginfo->endts.toString("yyyyMMddhhmm");
+    endts += "00";
+	QDateTime now(QDateTime::currentDateTime());
+    QString newendts = now.toString("yyyyMMddhhmm");
+    newendts += "00";
+
+	// Set the recorded end time to the current time
+	// (we're stopping the recording so it'll never get to its originally intended end time)
+    thequery = QString("UPDATE recorded SET starttime = %1, endtime = %2 "
+						"WHERE chanid = %3 AND title "
+                       "= \"%4\" AND starttime = %5 AND endtime = %6;")
+                       .arg(startts).arg(newendts).arg(pginfo->chanid)
+					   .arg(pginfo->title.utf8()).arg(startts).arg(endts);
+
+	query = m_db->exec(thequery);
+
+    if (!query.isActive())
+    {
+        cerr << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
+             << " DB Error: recorded program update failed, SQL query "
+             << "was:" << endl;
+        cerr << thequery << endl;
+    }
+
+    dblock.unlock();
+
+	// If we change the recording times we must also adjust the .nuv file's name
+    QString fileprefix = gContext->GetFilePrefix();
+    QString oldfilename = pginfo->GetRecordFilename(fileprefix);
+	pginfo->endts = now;
+    QString newfilename = pginfo->GetRecordFilename(fileprefix);
+    QFile checkFile(oldfilename);
+    if (checkFile.exists())
+    {
+			if (!QDir::root().rename(oldfilename, newfilename, TRUE))
+			{
+				cerr << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
+					 << " Could not rename: " << oldfilename << " to: " << oldfilename << endl;
+			}
+    }
+    else
+    {
+        cerr << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
+             << " Strange.  File: " << oldfilename << " doesn't exist." << endl;
+
+        if (pbssock)
+        {
+            QStringList outputlist;
+            outputlist << "BAD: Tried to rename a file that was in "
+                          "the database but wasn't on the disk.";
+            SendResponse(pbssock, outputlist);
+
+            delete pginfo;
+            return;
+        }
+    }
+	
+    if (pbssock)
+    {
+        QStringList outputlist = QString::number(recnum);
+        SendResponse(pbssock, outputlist);
+    }
+
+    delete pginfo;
+}
+
 void MainServer::HandleDeleteRecording(QStringList &slist, PlaybackSock *pbs)
 {
@@ -836,4 +991,20 @@
     MythContext::KickDatabase(m_db);
 
+	ScheduledRecording *rec = pginfo->GetScheduledRecording(m_db);
+	if (rec)
+	{
+		thequery = QString("DELETE FROM record WHERE recordid = %1").arg(rec->getRecordID());
+		
+		QSqlQuery query = m_db->exec(thequery);
+	
+		if (!query.isActive())
+		{
+			cerr << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
+				 << " DB Error: currently recording program deletion failed, SQL query "
+				 << "was:" << endl;
+			cerr << thequery << endl;
+		}
+	}
+	
     thequery = QString("DELETE FROM recorded WHERE chanid = %1 AND title "
                        "= \"%2\" AND starttime = %3 AND endtime = %4;")
Index: programs/mythbackend/mainserver.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/programs/mythbackend/mainserver.h,v
retrieving revision 1.32
diff -b -u -2 -r1.32 mainserver.h
--- programs/mythbackend/mainserver.h	3 Sep 2003 02:37:37 -0000	1.32
+++ programs/mythbackend/mainserver.h	18 Sep 2003 00:24:21 -0000
@@ -51,4 +51,6 @@
     // playback
     void HandleQueryRecordings(QString type, PlaybackSock *pbs);
+    void HandleStopRecording(QStringList &slist, PlaybackSock *pbs);
+    void DoHandleStopRecording(ProgramInfo *pginfo, PlaybackSock *pbs);
     void HandleDeleteRecording(QStringList &slist, PlaybackSock *pbs);
     void DoHandleDeleteRecording(ProgramInfo *pginfo, PlaybackSock *pbs);
Index: programs/mythbackend/playbacksock.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/programs/mythbackend/playbacksock.cpp,v
retrieving revision 1.14
diff -b -u -2 -r1.14 playbacksock.cpp
--- programs/mythbackend/playbacksock.cpp	12 Sep 2003 16:49:54 -0000	1.14
+++ programs/mythbackend/playbacksock.cpp	18 Sep 2003 00:24:22 -0000
@@ -68,4 +68,14 @@
 }
 
+int PlaybackSock::StopRecording(ProgramInfo *pginfo)
+{
+    QStringList strlist = QString("STOP_RECORDING");
+    pginfo->ToStringList(strlist);
+
+    SendReceiveStringList(strlist);
+
+    return strlist[0].toInt();
+}
+
 int PlaybackSock::DeleteRecording(ProgramInfo *pginfo)
 {
Index: programs/mythbackend/playbacksock.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/programs/mythbackend/playbacksock.h,v
retrieving revision 1.9
diff -b -u -2 -r1.9 playbacksock.h
--- programs/mythbackend/playbacksock.h	12 Sep 2003 16:49:54 -0000	1.9
+++ programs/mythbackend/playbacksock.h	18 Sep 2003 00:24:22 -0000
@@ -30,4 +30,5 @@
 
     void GetFreeSpace(int &totalspace, int &usedspace);
+    int StopRecording(ProgramInfo *pginfo);
     int DeleteRecording(ProgramInfo *pginfo);
     void FillProgramInfo(ProgramInfo *pginfo, QString &playbackhost);
Index: programs/mythfrontend/manualbox.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/programs/mythfrontend/manualbox.cpp,v
retrieving revision 1.7
diff -b -u -2 -r1.7 manualbox.cpp
--- programs/mythfrontend/manualbox.cpp	16 Jul 2003 23:57:43 -0000	1.7
+++ programs/mythfrontend/manualbox.cpp	18 Sep 2003 00:24:23 -0000
@@ -325,6 +325,9 @@
     progInfo.chanid = m_chanidString;
 
-    progInfo.startts = QDateTime::currentDateTime();
-    progInfo.endts = QDateTime::currentDateTime().addSecs(m_duration->value() * 60);
+    QDateTime now = QDateTime::currentDateTime();
+	// Make sure we set the seconds to 0 because lots of code expects it that way!!
+	now = now.addSecs(-now.time().second());
+	progInfo.startts = now;
+    progInfo.endts = now.addSecs(m_duration->value() * 60);
     
     progInfo.Save(db);
Index: programs/mythfrontend/playbackbox.cpp
===================================================================
RCS file: /var/lib/mythcvs/mythtv/programs/mythfrontend/playbackbox.cpp,v
retrieving revision 1.92
diff -b -u -2 -r1.92 playbackbox.cpp
--- programs/mythfrontend/playbackbox.cpp	6 Sep 2003 20:38:35 -0000	1.92
+++ programs/mythfrontend/playbackbox.cpp	18 Sep 2003 00:24:32 -0000
@@ -803,28 +803,4 @@
 }
 
-QString PlaybackBox::cutDown(QString info, QFont *testFont, int maxwidth)
-{
-    QFontMetrics fm(*testFont);
-
-    int curFontWidth = fm.width(info);
-    if (curFontWidth > maxwidth)
-    {
-        QString testInfo = "";
-        curFontWidth = fm.width(testInfo);
-        int tmaxwidth = maxwidth - fm.width("LLL");
-        int count = 0;
-
-        while (curFontWidth < tmaxwidth)
-        {
-            testInfo = info.left(count);
-            curFontWidth = fm.width(testInfo);
-            count = count + 1;
-        }
-        testInfo = testInfo + "...";
-        info = testInfo;
-    }
-    return info;
-}
-
 void PlaybackBox::cursorLeft()
 {
@@ -1216,4 +1192,14 @@
 }
 
+void PlaybackBox::stopSelected()
+{
+    killPlayer();
+
+    if (!curitem || ignoreevents)
+        return;
+
+    stop(curitem);
+}
+
 void PlaybackBox::deleteSelected()
 {
@@ -1236,4 +1222,14 @@
 }
 
+void PlaybackBox::showActionsSelected()
+{
+    killPlayer();
+
+    if (!curitem || ignoreevents)
+        return;
+
+    showActions(curitem);
+}
+
 void PlaybackBox::selected()
 {
@@ -1325,4 +1321,14 @@
 }
 
+void PlaybackBox::stop(ProgramInfo *rec)
+{
+    if (noUpdate)
+        return;
+
+    noUpdate = true;
+    RemoteStopRecording(rec);
+    noUpdate = false;
+}
+
 void PlaybackBox::doRemove(ProgramInfo *rec)
 {
@@ -1399,5 +1405,5 @@
 
     delitem = new ProgramInfo(*toDel);
-    showDeletePopup(2);
+    showDeletePopup(delitem, 2);
 }
 
@@ -1412,29 +1418,24 @@
 
     delitem = new ProgramInfo(*toExp);
-    showDeletePopup(3);
+    showDeletePopup(delitem, 3);
 }
 
-void PlaybackBox::showDeletePopup(int types)
+void PlaybackBox::showActions(ProgramInfo *toExp)
 {
-    QFont bigFont("Arial", (int)(gContext->GetBigFontSize() * hmult), 
-                  QFont::Bold);
-    QFont medFont("Arial", (int)(gContext->GetMediumFontSize() * hmult), 
-                  QFont::Bold);
-    QFont smallFont("Arial", (int)(gContext->GetSmallFontSize() * hmult), 
-                    QFont::Bold);
+    if (ignoreevents)
+        return;
 
-    ignoreevents = true;
+    killPlayer();
 
-    QDateTime startts = delitem->startts;
-    QDateTime endts = delitem->endts;
+    ignoreevents = true;
 
-    QString timedate = startts.date().toString(dateformat) + QString(", ") +
-                       startts.time().toString(timeformat) + QString(" - ") +
-                       endts.time().toString(timeformat);
+    delitem = new ProgramInfo(*toExp);
+    showActionPopup(delitem);
+}
 
-    QString descrip = delitem->description;
-    descrip = cutDown(descrip, &medFont, (int)(width() / 2));
-    QString titl = delitem->title;
-    titl = cutDown(titl, &bigFont, (int)(width() / 2));
+void PlaybackBox::showDeletePopup(ProgramInfo *program, int types)
+{
+    ignoreevents = true;
+    expectingPopup = true;
 
     timer->stop();
@@ -1446,201 +1447,233 @@
     noUpdate = true;
 
-    popup = new MythPopupBox(gContext->GetMainWindow(), "delete popup");
-    popup->setFrameStyle( QFrame::Box | QFrame::Plain );
-    if (graphicPopup == false)
-        popup->setPaletteBackgroundColor(popupBackground);
-    else
-        gContext->ThemeWidget(popup);
-    popup->setPaletteForegroundColor(popupHighlight);
-    QLabel *msg = NULL;
+    QString msg;
     if (types == 1)
-        msg = new QLabel(tr("You have finished watching:"), popup);
+        msg = tr("You have finished watching:");
     else if (types == 2)
-        msg = new QLabel(tr("Are you sure you want to delete:"), popup);
+        msg = tr("Are you sure you want to delete:");
     else if (types == 3)
-        msg = new QLabel(tr("Allow this program to AutoExpire?"), popup);
-    msg->setBackgroundOrigin(ParentOrigin); 
-    msg->setPaletteForegroundColor(popupForeground);
-    QLabel *filler1 = new QLabel("", popup);
-    filler1->setBackgroundOrigin(ParentOrigin);
-    QLabel *title = new QLabel(delitem->title, popup);
-    title->setPaletteForegroundColor(popupForeground);
-    title->setBackgroundOrigin(ParentOrigin);
-    title->setFont(bigFont);
-    title->setMaximumWidth((int)(width() / 2));
-    QLabel *subtitle = new QLabel("\"" + delitem->subtitle + "\"", popup);
-    subtitle->setPaletteForegroundColor(popupForeground);
-    subtitle->setBackgroundOrigin(ParentOrigin);
-    QLabel *times = new QLabel(timedate, popup);
-    times->setPaletteForegroundColor(popupForeground);
-    times->setBackgroundOrigin(ParentOrigin);
-    QLabel *filler2 = new QLabel("", popup);
-    filler2->setBackgroundOrigin(ParentOrigin);
-    QLabel *msg2 = NULL;
+        msg = tr("Allow this program to AutoExpire?");
+    else if (types == 3)
+        msg = tr("Are you sure you want to stop:");
 
+    QString msg2;
     if (types == 1)
-        msg2 = new QLabel(tr("Delete this recording?"), popup);
+        msg2 = tr("Delete this recording?");
     else
-        msg2 = new QLabel(" ", popup);
-
-    msg2->setPaletteForegroundColor(popupForeground);
-    msg2->setBackgroundOrigin(ParentOrigin);
-
-    MythPushButton *yesButton;
+        msg2 = QString(" ");
 
+    QString yesButton;
     if ((types == 1) || (types == 2))
-        yesButton = new MythPushButton(tr("Yes, get rid of it"), popup);
-    else
-        yesButton = new MythPushButton(tr("Yes, AutoExpire"), popup);
-
-    MythPushButton *noButton = NULL;
+        yesButton = tr("Yes, get rid of it");
+    else if (types == 3)
+        yesButton = tr("Yes, AutoExpire");
+    else if (types == 4)
+        yesButton = tr("Yes, stop recording it");
 
+    QString noButton;
     if (types == 1)
-        noButton = new MythPushButton(tr("No, I might want to watch it again."),
-                                      popup);
+        noButton = tr("No, I might want to watch it again.");
     else if (types == 2)
-        noButton = new MythPushButton(tr("No, keep it, I changed my mind"),
-                                      popup);
+        noButton = tr("No, keep it, I changed my mind");
     else if (types == 3)
-        noButton = new MythPushButton(tr("No, Do Not AutoExpire"), popup);
+        noButton = tr("No, do not AutoExpire");
+    else if (types == 4)
+        noButton = tr("No, continue recording it");
 
-    popup->addWidget(msg, false);
-    popup->addWidget(filler1, false);
-    popup->addWidget(title, false);
-    if ((delitem->subtitle).stripWhiteSpace().length() > 0)
-        popup->addWidget(subtitle, false);
-    else
-        subtitle->hide();
-    popup->addWidget(times, false);
-    popup->addWidget(filler2, false);
-    popup->addWidget(msg2, false);
+	const char *yesSlot = NULL;
+	const char *noSlot = NULL;
+    if ((types == 1) || (types == 2))
+    {
+        yesSlot = SLOT(doDelete());
+        noSlot = SLOT(noDelete());
+    }
+    else if (types == 3)
+    {
+        yesSlot = SLOT(doAutoExpire());
+        noSlot = SLOT(noAutoExpire());
+    }
+    else if (types == 4)
+    {
+        yesSlot = SLOT(doStop());
+        noSlot = SLOT(noStop());
+    }
 
-    popup->addWidget(yesButton, false);
-    popup->addWidget(noButton, false);
+	popup = new SimpleMythPopup(this, SLOT(doCancel()));
+	popup->setForegroundColor(popupForeground);
+	popup->setBackgroundColor(popupBackground);
+	popup->setHighlightColor(popupHighlight);
+	popup->setGraphic(graphicPopup);
 
-    if ((types == 1) || (types == 2))
-        noButton->setFocus();
+	initPopup(popup, program, msg, msg2);
+	
+	uint yesNr = popup->addButton(yesButton, yesSlot);
+	uint noNr = popup->addButton(noButton, noSlot);
+	
+	int focus;
+    if ((types == 1) || (types == 2) || (types == 4))
+        focus = noNr;
     else
     {
         QSqlDatabase *db = QSqlDatabase::database();
         if (delitem->GetAutoExpireFromRecorded(db))
-            yesButton->setFocus();
+			focus = yesNr;
         else
-            noButton->setFocus();
+			focus = noNr;
     }
+	popup->setFocus(focus);
+	popup->show();
+}
   
-    msg->adjustSize();
-    msg2->adjustSize();
-    filler1->adjustSize();
-    filler2->adjustSize();
-    title->adjustSize();
-    times->adjustSize();
-    subtitle->adjustSize();
-    yesButton->adjustSize();
-    noButton->adjustSize();
+void PlaybackBox::showActionPopup(ProgramInfo *program)
+{
+    ignoreevents = true;
+    expectingPopup = true;
 
-    popup->polish();
+    timer->stop();
+    playingVideo = false;
 
-    int x, y, maxw, poph;
-    poph = msg->height() + msg2->height() + filler1->height() + 
-           filler2->height() + title->height() + times->height() + 
-           subtitle->height() + yesButton->height() + noButton->height() +
-           (int)(110 * hmult);
-    popup->setMinimumHeight(poph);
-    maxw = 0;
+    backup.begin(this);
+    grayOut(&backup);
+    backup.end();
+    noUpdate = true;
 
-    if (title->width() > maxw)
-        maxw = title->width();
-    if (times->width() > maxw)
-        maxw = times->width();
-    if (subtitle->width() > maxw)
-        maxw = subtitle->width();
-    if (noButton->width() > maxw)
-        maxw = noButton->width();
+    QString msg = tr(" ");
+    QString msg2 = tr("Select action:");
 
-    maxw += (int)(80 * wmult);
+	popup = new SimpleMythPopup(this, SLOT(doCancel()));
+	popup->setForegroundColor(popupForeground);
+	popup->setBackgroundColor(popupBackground);
+	popup->setHighlightColor(popupHighlight);
+	popup->setGraphic(graphicPopup);
  
-    x = (int)(width() / 2) - (int)(maxw / 2);
-    y = (int)(height() / 2) - (int)(poph / 2);
+	initPopup(popup, program, msg, msg2);
 
-    popup->setFixedSize(maxw, poph);
-    popup->setGeometry(x, y, maxw, poph);
+	QSqlDatabase *db = QSqlDatabase::database();
+    QDateTime curtime = QDateTime::currentDateTime();
 
-    popup->Show();
+	uint playNr = popup->addButton(tr("Play"), SLOT(doPlay()));
+	if ((curtime >= program->startts) && (curtime < program->endts))
+		popup->addButton(tr("Stop Recording"), SLOT(askStop()));
+	if (delitem->GetAutoExpireFromRecorded(db))
+		popup->addButton(tr("Don't Auto Expire"), SLOT(noAutoExpire()));
+	else
+		popup->addButton(tr("Auto Expire"), SLOT(doAutoExpire()));
+	popup->addButton(tr("Delete"), SLOT(askDelete()));
+	popup->addButton(tr("Cancel"), SLOT(doCancel()));
 
-    if ((types == 1) || (types == 2))
+	int focus = playNr;
+	popup->setFocus(focus);
+	popup->show();
+}
+
+void PlaybackBox::initPopup(SimpleMythPopup *popup, ProgramInfo *program, QString message, QString message2)
+{
+    QDateTime startts = program->startts;
+    QDateTime endts = program->endts;
+
+    QString timedate = startts.date().toString(dateformat) + QString(", ") +
+                       startts.time().toString(timeformat) + QString(" - ") +
+                       endts.time().toString(timeformat);
+
+//	QString descrip = program->description;
+//	descrip = cutDown(descrip, &medFont, (int)(parent->width() / 2));
+//	QString titl = program->title;
+//	titl = cutDown(titl, &bigFont, (int)(parent->width() / 2));
+	
+	if (message.stripWhiteSpace().length() > 0)
     {
-        connect(yesButton, SIGNAL(pressed()), this, SLOT(doDelete()));
-        connect(noButton, SIGNAL(pressed()), this, SLOT(noDelete()));
+		popup->addLabel(message);
+		popup->addLabel("");
     }
-    else if (types == 3)
+	popup->addLabel(program->title, SimpleMythPopup::PopupItem::Large);
+    if ((program->subtitle).stripWhiteSpace().length() > 0)
+		popup->addLabel("\"" + program->subtitle + "\"");
+	popup->addLabel(timedate);
+    if (message2.stripWhiteSpace().length() > 0)
     {
-        connect(yesButton, SIGNAL(pressed()), this, SLOT(doAutoExpire()));
-        connect(noButton, SIGNAL(pressed()), this, SLOT(noAutoExpire()));
+		popup->addLabel("");
+		popup->addLabel(message2);
     }
+}
 
-    QAccel *popaccel = new QAccel(popup);
-    popaccel->connectItem(popaccel->insertItem(Key_Escape), this, 
-                          SLOT(noDelete()));
+void PlaybackBox::doPlay()
+{
+    if (!expectingPopup)
+        return;
 
-    expectingPopup = true;
+	cancelPopup();
+	
+	play(delitem);
+
+    delete delitem;
+    delitem = NULL;
 }
 
-void PlaybackBox::noDelete()
+void PlaybackBox::askStop()
 {
     if (!expectingPopup)
         return;
 
-    popup->hide();
-    expectingPopup = false;
+	cancelPopup();
 
-    noUpdate = false;
-    backup.begin(this);
-    backup.drawPixmap(0, 0, myBackground);
-    backup.end();
+    showDeletePopup(delitem, 4);
+}
+
+void PlaybackBox::noStop()
+{
+    if (!expectingPopup)
+        return;
+
+	cancelPopup();
 
-    ignoreevents = false;
     delete delitem;
-    delete popup;
-    popup = NULL;
     delitem = NULL;
+}
 
-    skipUpdate = false;
-    skipCnt = 2;
-    update(fullRect);
+void PlaybackBox::doStop()
+{
+    if (!expectingPopup)
+        return;
 
-    setActiveWindow();
+	cancelPopup();
 
-    timer->start(1000 / 30);
+    stop(delitem);
+
+    delete delitem;
+    delitem = NULL;
 }
 
-void PlaybackBox::doDelete()
+void PlaybackBox::askDelete()
 {
     if (!expectingPopup)
         return;
 
-    popup->hide();
-    expectingPopup = false;
-    noUpdate = false;
+	cancelPopup();
 
-    doRemove(delitem);
+    showDeletePopup(delitem, 2);
+}
 
-    backup.begin(this);
-    backup.drawPixmap(0, 0, myBackground);
-    backup.end();
+void PlaybackBox::noDelete()
+{
+    if (!expectingPopup)
+        return;
 
-    ignoreevents = false;
+	cancelPopup();
 
-    delete popup;
     delete delitem;
     delitem = NULL;
+}
 
-    skipUpdate = false;
-    skipCnt = 2;
-    update(fullRect);
+void PlaybackBox::doDelete()
+{
+    if (!expectingPopup)
+        return;
 
-    setActiveWindow();
+	cancelPopup();
 
-    timer->start(1000 / 30);
+    doRemove(delitem);
+
+    delete delitem;
+    delitem = NULL;
 }
 
@@ -1650,41 +1683,43 @@
         return;
 
-    popup->hide();
-    expectingPopup = false;
+	cancelPopup();
 
     QSqlDatabase *db = QSqlDatabase::database();
     delitem->SetAutoExpire(false, db);
 
-    noUpdate = false;
-    backup.begin(this);
-    backup.drawPixmap(0, 0, myBackground);
-    backup.end();
-
-    ignoreevents = false;
     delete delitem;
-    delete popup;
-    popup = NULL;
     delitem = NULL;
+}
 
-    skipUpdate = false;
-    skipCnt = 2;
-    update(fullRect);
+void PlaybackBox::doAutoExpire()
+{
+    if (!expectingPopup)
+        return;
 
-    setActiveWindow();
+	cancelPopup();
 
-    timer->start(1000 / 30);
+    QSqlDatabase *db = QSqlDatabase::database();
+    delitem->SetAutoExpire(true, db);
+
+    delete delitem;
+    delitem = NULL;
 }
 
-void PlaybackBox::doAutoExpire()
+void PlaybackBox::doCancel()
 {
     if (!expectingPopup)
         return;
 
+	cancelPopup();
+
+    delete delitem;
+    delitem = NULL;
+}
+
+void PlaybackBox::cancelPopup()
+{
     popup->hide();
     expectingPopup = false;
 
-    QSqlDatabase *db = QSqlDatabase::database();
-    delitem->SetAutoExpire(true, db);
-
     noUpdate = false;
     backup.begin(this);
@@ -1693,8 +1728,6 @@
 
     ignoreevents = false;
-
     delete popup;
-    delete delitem;
-    delitem = NULL;
+    popup = NULL;
 
     skipUpdate = false;
@@ -1723,5 +1756,5 @@
 
     delitem = new ProgramInfo(*rec);
-    showDeletePopup(1);
+    showDeletePopup(delitem, 1);
 }
 
@@ -1784,5 +1817,5 @@
                 break;
             case Key_I: 
-                expireSelected(); 
+                showActionsSelected(); 
                 break;
             case Key_P: 
@@ -1925,2 +1958,232 @@
 }
 
+/*************************************************************/
+
+/*
+ * Constructor for SimpleMythPopup.
+ * parent - The MythDialog that is creating this pop-up
+ * exitSlot - The parent's method that will be called when the user hits Escape
+ */
+SimpleMythPopup::SimpleMythPopup(MythDialog *parent, const char *exitSlot) : parent(parent), exitSlot(exitSlot)
+{
+	itemList.setAutoDelete( TRUE );
+	focusItemNr = 0;
+    popup = NULL;
+}
+
+SimpleMythPopup::~SimpleMythPopup()
+{
+	hide();
+}
+
+/*
+ * Sets the focus to the specified widget.
+ * focus - The index of the widget to be given focus
+ */
+void SimpleMythPopup::setFocus(int focus)
+{
+	focusItemNr = focus;
+}
+
+/*
+ * Sets the foreground color to be used for this pop-up.
+ * color - The color to use
+ */
+void SimpleMythPopup::setForegroundColor(QColor color)
+{
+	foreground = color;
+}
+
+/*
+ * Sets the background color to be used for this pop-up.
+ * color - The color to use
+ */
+void SimpleMythPopup::setBackgroundColor(QColor color)
+{
+	background = color;
+}
+
+/*
+ * Sets the highlight color to be used for this pop-up.
+ * color - The color to use
+ */
+void SimpleMythPopup::setHighlightColor(QColor color)
+{
+	highlight = color;
+}
+
+/*
+ * Determines if the pop-up hould use the theme or not.
+ * graphic - Use theme or not
+ */
+void SimpleMythPopup::setGraphic(bool graphic)
+{
+	graphicPopup = graphic;
+}
+
+/*
+ * Adds a label of medium size to the pop-up.
+ * caption - The text to show in the label
+ * Returns the inddex of the newly created item
+ */
+uint SimpleMythPopup::addLabel(QString caption)
+{
+	return addLabel(caption, PopupItem::Medium);
+}
+
+/*
+ * Adds a label of the given size to the pop-up.
+ * caption - The text to show in the label
+ * size - The size to use for the caption: Large, Medium or Small.
+ * Returns the inddex of the newly created item
+ */
+uint SimpleMythPopup::addLabel(QString caption, PopupItem::PopupLabelSize size)
+{
+	itemList.append( new PopupItem(PopupItem::Label, caption, size, NULL) );
+	return itemList.count() - 1;
+}
+
+/*
+ * Adds a button to the pop-up.
+ * caption - The text to show in the label
+ * slot - The parent's method to call when the button is pressed.
+ * Returns the inddex of the newly created item
+ */
+uint SimpleMythPopup::addButton(QString caption, const char *slot)
+{
+	itemList.append( new PopupItem(PopupItem::Button, caption, PopupItem::Medium, slot) );
+	return itemList.count() - 1;
+}
+
+/*
+ * Show the pop-up
+ */
+void SimpleMythPopup::show()
+{
+    int w, h;
+    float wmult, hmult;
+
+    gContext->GetScreenSettings(w, wmult, h, hmult);
+
+    QFont bigFont("Arial", (int)(gContext->GetBigFontSize() * hmult), QFont::Bold);
+    QFont medFont("Arial", (int)(gContext->GetMediumFontSize() * hmult), QFont::Bold);
+    QFont smallFont("Arial", (int)(gContext->GetSmallFontSize() * hmult), QFont::Bold);
+
+    popup = new MythPopupBox(gContext->GetMainWindow(), "delete popup");
+    popup->setFrameStyle( QFrame::Box | QFrame::Plain );
+    if (graphicPopup == false)
+        popup->setPaletteBackgroundColor(background);
+    else
+        gContext->ThemeWidget(popup);
+    popup->setPaletteForegroundColor(highlight);
+
+	// Create the buttons as defined by the list of PopupItem
+	for (PopupItem *item = itemList.first(); item != NULL; item = itemList.next()) {
+		if (item->type == PopupItem::Label)
+		{
+			item->widget = new QLabel(item->caption, popup);
+			item->widget->setPaletteForegroundColor(foreground);
+			item->widget->setBackgroundOrigin(parent->ParentOrigin);
+			if (item->size == PopupItem::Large)
+				item->widget->setFont(bigFont);
+			else if (item->size == PopupItem::Medium)
+				item->widget->setFont(medFont);
+			else if (item->size == PopupItem::Small)
+				item->widget->setFont(smallFont);
+			item->widget->setMaximumWidth((int)(parent->width() / 2));
+		}
+		else if (item->type == PopupItem::Button)
+			item->widget = new MythPushButton(item->caption, popup);
+	}
+	
+	// Add the buttons to the dialog
+	int i = 0;
+	QWidget *focusItem = NULL;
+	for (PopupItem *item = itemList.first(); item != NULL; item = itemList.next()) {
+		popup->addWidget(item->widget, false);
+		if (focusItemNr == i) {
+			focusItem = item->widget;
+		}
+		i++;
+	}
+	// Set the focus on the requested button
+    if (focusItem) {
+        focusItem->setFocus();
+    }
+  
+	for (PopupItem *item = itemList.first(); item != NULL; item = itemList.next()) {
+		item->widget->adjustSize();
+	}
+
+    popup->polish();
+
+    int poph = (int)(110 * hmult);
+  	int maxw = 0;
+	for (PopupItem *item = itemList.first(); item != NULL; item = itemList.next()) {
+		poph += item->widget->height();
+		if (item->widget->width() > maxw)
+			maxw = item->widget->width();
+	}
+    popup->setMinimumHeight(poph);
+
+    maxw += (int)(80 * wmult);
+ 
+    int x, y;
+    x = (int)(parent->width() / 2) - (int)(maxw / 2);
+    y = (int)(parent->height() / 2) - (int)(poph / 2);
+
+    popup->setFixedSize(maxw, poph);
+    popup->setGeometry(x, y, maxw, poph);
+
+    popup->Show();
+
+	for (PopupItem *item = itemList.first(); item != NULL; item = itemList.next()) {
+		if (item->type == PopupItem::Button)
+		{
+			parent->connect(item->widget, SIGNAL(pressed()), parent, item->slot);
+		}
+	}
+
+    QAccel *popaccel = new QAccel(popup);
+    popaccel->connectItem(popaccel->insertItem(parent->Key_Escape), parent, exitSlot);
+}
+
+/*
+ * Hide the pop-up
+ */
+void SimpleMythPopup::hide()
+{
+	if (popup)
+	{
+		popup->hide();
+		delete popup;
+		popup = NULL;
+	}
+}
+
+QString SimpleMythPopup::cutDown(QString info, QFont *testFont, int maxwidth)
+{
+    QFontMetrics fm(*testFont);
+
+    int curFontWidth = fm.width(info);
+    if (curFontWidth > maxwidth)
+    {
+        QString testInfo = "";
+        curFontWidth = fm.width(testInfo);
+        int tmaxwidth = maxwidth - fm.width("LLL");
+        int count = 0;
+
+        while (curFontWidth < tmaxwidth)
+        {
+            testInfo = info.left(count);
+            curFontWidth = fm.width(testInfo);
+            count = count + 1;
+        }
+        testInfo = testInfo + "...";
+        info = testInfo;
+    }
+	
+    return info;
+}
+
+
Index: programs/mythfrontend/playbackbox.h
===================================================================
RCS file: /var/lib/mythcvs/mythtv/programs/mythfrontend/playbackbox.h,v
retrieving revision 1.30
diff -b -u -2 -r1.30 playbackbox.h
--- programs/mythfrontend/playbackbox.h	29 Jul 2003 05:39:24 -0000	1.30
+++ programs/mythfrontend/playbackbox.h	18 Sep 2003 00:24:32 -0000
@@ -21,4 +21,6 @@
 class LayerSet;
 
+class SimpleMythPopup;
+
 class PlaybackBox : public MythDialog
 {
@@ -46,7 +48,16 @@
     void selected();
     void playSelected();
+    void stopSelected();
     void deleteSelected();
     void expireSelected();
+    void showActionsSelected();
+
+    void doPlay();
+	
+    void askStop();
+    void doStop();
+    void noStop();
 
+    void askDelete();
     void doDelete();
     void noDelete();
@@ -55,4 +66,6 @@
     void noAutoExpire();
 
+    void doCancel();
+
     void exitWin();
 
@@ -69,6 +82,8 @@
     QPainter backup;
     void play(ProgramInfo *);
+    void stop(ProgramInfo *);
     void remove(ProgramInfo *);
     void expire(ProgramInfo *);
+    void showActions(ProgramInfo *);
 
     bool skipUpdate;
@@ -105,5 +120,8 @@
     void doRemove(ProgramInfo *);
     void promptEndOfRecording(ProgramInfo *);
-    void showDeletePopup(int);
+    void showDeletePopup(ProgramInfo *program, int);
+	void showActionPopup(ProgramInfo *program);
+	void initPopup(SimpleMythPopup *popup, ProgramInfo *program, QString message, QString message2);
+	void cancelPopup();
 
     bool fileExists(ProgramInfo *pginfo);
@@ -133,5 +151,5 @@
     QString showTimeFormat;
 
-    MythPopupBox *popup;
+	SimpleMythPopup *popup;
     QPixmap myBackground;
 
@@ -157,3 +175,67 @@
 };
 
+/*
+ * SimpleMythPopup
+ * 
+ * Can be used to very quickly make simple dialogs that only consist of label and buttons.
+ * The popup must be used from within a MythDialog.
+ *
+ * Usage:
+ *   SimpleMythPopup *popup = new SimpleMythPopup(this, SLOT(doExit()));
+ *   popup->addLabel("Down the rabbit hole...");
+ *   popup->addButton("Eat Me", SLOT(doEat()));
+ *   popup->addButton("Drink Me", SLOT(doDrink()));
+ *   popup->show();
+ */
+class SimpleMythPopup
+{
+	public:
+		struct PopupItem {
+			
+			public:
+				typedef enum { Label, Button } PopupItemType;
+				typedef enum { Large, Medium, Small } PopupLabelSize;
+				
+				PopupItemType type;
+				QString caption;
+				PopupLabelSize size;
+				const char *slot;
+			
+				QWidget *widget;
+			
+				PopupItem(PopupItemType type, QString caption, PopupLabelSize size, const char *slot) : type(type), caption(caption), size(size), slot(slot) {
+				}
+		};
+
+		SimpleMythPopup(MythDialog *parent, const char *exitSlot);
+		~SimpleMythPopup(void);
+		
+		void setFocus(int focus);
+		void setForegroundColor(QColor color);
+		void setBackgroundColor(QColor color);
+		void setHighlightColor(QColor color);
+		void setGraphic(bool graphic);
+		
+		uint addLabel(QString caption);
+		uint addLabel(QString caption, PopupItem::PopupLabelSize size);
+		uint addButton(QString caption, const char *slot);
+		void show();
+		void hide();
+	
+	private:
+		MythDialog *parent;
+		const char *exitSlot;
+
+		MythPopupBox *popup;
+		QList<PopupItem> itemList;
+
+		int focusItemNr;
+		QColor foreground;
+		QColor background;
+		QColor highlight;
+		bool graphicPopup;
+		
+		QString cutDown(QString info, QFont *testFont, int maxwidth);
+};
+
 #endif


More information about the mythtv-dev mailing list