Patch to add a busy spinner to LCD and fix ticket#520.

1) Added a flush when writing to the mythlcdserver. I might have been
confused by the lag causes by the menus, but I'm fairly sure I
observed lack of updates that the flush fixed. Anyway, it can't hurt.

2) Fix for ticket#520. Add two new calls to LCD, setMusicShuffle and
setMusicRepeat, used by MythMusic to set what the shuffle and repeat
mode is.

3) Changed the layout of the music screen :
    height == 2 :    Artist[-Album-]-Title
                        hh:mm / HH:MM

    height == 3 :    Artist[-Album-]-Title 
                        hh:mm / HH:MM
                      [shuffle] [repeat]

    height >  3 :    Artist[-Album-]-Title 
                        hh:mm / HH:MM
                     ====progress==-
                      [shuffle] [repeat]

4) Added support for a busy spinner. Calling LCD::setGenericBusy will
animate a busy spinner. Note, the busy spinner does *not* selfanimate
(it could), but I've chosen to only animate when setGenericBusy is
called. 

5) when checking recording status, and failing to talk to the backend,
only fallback to time screen if we're showing the recording
status. Otherwise, if the backend is unreachable (ie. a machine used
for mythmusic only) will constantly fallback to time screen.

6) Moved all the LCD::switchToMusic calls in playbackbox to a single
function (setTrackOnLCD).



Index: mythtv/libs/libmyth/lcddevice.cpp
===================================================================
--- mythtv/libs/libmyth/lcddevice.cpp	(revision 7592)
+++ mythtv/libs/libmyth/lcddevice.cpp	(working copy)
@@ -192,7 +192,7 @@
 #endif
         // Just stream the text out the socket
 
-        os << someText << "\n";
+        os << someText << "\n" << flush;
     }
     else
     {
@@ -387,9 +387,18 @@
     else if (value > 1.0)
         value = 1.0;
         
-    sendToServer("SET_GENERIC_PROGRESS " + QString().setNum(value));    
+    sendToServer("SET_GENERIC_PROGRESS " + QString().setNum (false) + 
+                 " " + QString().setNum(value));    
 }
 
+void LCD::setGenericBusy ()
+{
+    if (!lcd_ready || !lcd_showgeneric)
+        return;
+
+    sendToServer("SET_GENERIC_PROGRESS 1 0.0");
+}
+
 void LCD::setMusicProgress(QString time, float value)
 {
     if (!lcd_ready || !lcd_showmusic)
@@ -404,6 +413,22 @@
             QString().setNum(value));    
 }
 
+void LCD::setMusicShuffle(int shuffle)
+{
+    if (!lcd_ready || !lcd_showmusic)
+        return;
+
+    sendToServer("SET_MUSIC_PLAYER_PROP SHUFFLE "+ QString().setNum(shuffle));    
+}
+
+void LCD::setMusicRepeat(int repeat)
+{
+    if (!lcd_ready || !lcd_showmusic)
+        return;
+
+    sendToServer("SET_MUSIC_PLAYER_PROP REPEAT "+ QString().setNum(repeat));    
+}
+
 void LCD::setVolumeLevel(float value)
 {
     if (!lcd_ready || !lcd_showvolume)
Index: mythtv/libs/libmyth/lcddevice.h
===================================================================
--- mythtv/libs/libmyth/lcddevice.h	(revision 7592)
+++ mythtv/libs/libmyth/lcddevice.h	(working copy)
@@ -104,6 +104,18 @@
   public:
    ~LCD();
 
+    enum { 
+        MUSIC_REPEAT_NONE = 0, 
+        MUSIC_REPEAT_TRACK = 1, 
+        MUSIC_REPEAT_ALL = 2
+    };
+
+    enum { 
+        MUSIC_SHUFFLE_NONE = 0, 
+        MUSIC_SHUFFLE_RAND = 1, 
+        MUSIC_SHUFFLE_SMART = 2
+    };
+
     static class LCD * Get(void);
     static void SetupLCD (void);
 
@@ -147,12 +159,31 @@
     // define the screen, row, and alignment of the text
     void switchToGeneric(QPtrList<LCDTextItem> *textItems);
 
-    // Do a progress bar with the generic level between 0 and 1.0
+    /** Update the generic progress bar.
+        \param generic_progress a value between 0 and 1.0
+    */
     void setGenericProgress(float generic_progress);
 
+    /** Update the generic screen to display a busy spinner.
+
+		\note The LCD busy spinner only 'moves' when this is called
+		instead of the lcdserver just handling it itself. 
+    */
+    void setGenericBusy();
+
     // Do a music progress bar with the generic level between 0 and 1.0
     void setMusicProgress(QString time, float generic_progress);
 
+	/** \brief Set music player's repeat properties         
+        \param repeat the state of repeat
+	 */
+    void setMusicRepeat (int repeat);
+
+	/** \brief Set music player's shuffle properties             
+        \param repeat the state of repeat
+    */
+    void setMusicShuffle (int shuffle);
+
     // Show the Volume Level top_text scrolls
     void switchToVolume(QString app_name);
 
Index: mythtv/programs/mythlcdserver/lcdserver.h
===================================================================
--- mythtv/programs/mythlcdserver/lcdserver.h	(revision 7592)
+++ mythtv/programs/mythlcdserver/lcdserver.h	(working copy)
@@ -55,6 +55,7 @@
     void switchToMenu(const QStringList &tokens, QSocket *socket);
     void setChannelProgress(const QStringList &tokens, QSocket *socket);
     void setMusicProgress(const QStringList &tokens, QSocket *socket);
+    void setMusicProp(const QStringList &tokens, QSocket *socket);
     void setGenericProgress(const QStringList &tokens, QSocket *socket);
     void setVolumeLevel(const QStringList &tokens, QSocket *socket);
     void updateLEDs(const QStringList &tokens, QSocket *socket);
Index: mythtv/programs/mythlcdserver/lcdprocclient.cpp
===================================================================
--- mythtv/programs/mythlcdserver/lcdprocclient.cpp	(revision 7592)
+++ mythtv/programs/mythlcdserver/lcdprocclient.cpp	(working copy)
@@ -470,6 +470,7 @@
     setPriority("Music", LOW);
     sendToServer("widget_add Music topWidget string");
     sendToServer("widget_add Music timeWidget string");
+    sendToServer("widget_add Music infoWidget string");
     sendToServer("widget_add Music progressBar hbar");
     
     // The Channel Screen
@@ -959,6 +960,7 @@
 void LCDProcClient::startMusic(QString artist, QString album, QString track)
 {
     QString aString;
+    music_progress = 0.0;
     if (lcd_showmusic)
       setPriority("Music", HIGH);
     aString = artist;
@@ -1016,7 +1018,7 @@
     QString aString;
 
     if (lcd_showgeneric)
-      setPriority("Generic", HIGH);
+      setPriority("Generic", TOP);
 
     // Clear out the LCD.  Do this before checking if its empty incase the user
     //  wants to just clear the lcd
@@ -1029,6 +1031,11 @@
         return;
 
     activeScreen = "Generic";
+
+    busy_progress = false;
+    busy_pos = 1;
+    busy_direction = 1;
+    busy_indicator_size = 2.0;
     generic_progress = 0.0;
 
     // Return if there are no more items
@@ -1505,7 +1512,7 @@
     outputChannel();    
 }
 
-void LCDProcClient::setGenericProgress(float value)
+void LCDProcClient::setGenericProgress(bool b, float value)
 {
     if (!lcd_ready)
         return;
@@ -1517,6 +1524,21 @@
     else if (generic_progress > 1.0)
         generic_progress = 1.0;
 
+    // Note, this will let us switch to/from busy indicator by alternating between
+    // being passed true or false for b.
+    busy_progress = b;
+    if (busy_progress) {
+        // If we're at either end of the line, switch direction
+        if (busy_pos + busy_direction > (signed int)lcdWidth - busy_indicator_size || 
+            busy_pos + busy_direction < 1) {
+            busy_direction = -busy_direction;
+        }
+        busy_pos += busy_direction;
+        generic_progress = busy_indicator_size / (float)lcdWidth;
+    } else {
+        busy_pos = 1;
+    }
+
     outputGeneric();
 }
 
@@ -1536,6 +1558,26 @@
     outputMusic();
 }
 
+void LCDProcClient::setMusicRepeat (int repeat) 
+{
+    if (!lcd_ready)
+        return;
+
+    music_repeat = repeat;
+
+    outputMusic ();
+}
+
+void LCDProcClient::setMusicShuffle (int shuffle) 
+{
+    if (!lcd_ready)
+        return;
+
+    music_shuffle = shuffle;
+
+    outputMusic ();
+}
+
 void LCDProcClient::setVolumeLevel(float value)
 {
     if (!lcd_ready)
@@ -1767,18 +1809,37 @@
 
 void LCDProcClient::outputMusic()
 {
-    QString aString;
+    outputCenteredText("Music", music_time, "timeWidget", 2);
 
     if (lcdHeight > 2) {
-      outputCenteredText("Music", music_time, "timeWidget", 2);
+      QString props;
+      QString shuffle = "      ";
+      QString repeat = "      ";
+
+      if (music_shuffle == 1) {
+          shuffle = "Shfl ?";
+      } else if (music_shuffle == 2) {
+          shuffle = "Shfl i";         
+      }
+
+      if (music_repeat == 1) {
+          repeat = "Rpt 1";
+      } else if (music_repeat == 2) {
+          repeat = "Rpt *";         
+      }
+
+      props.sprintf ("%s  %s", shuffle.ascii (), repeat.ascii ());
+
+      outputCenteredText("Music", props, "infoWidget", lcdHeight);
     }
 
-    aString = "widget_set Music progressBar 1 ";
-    aString += QString::number(lcdHeight);
-    aString += " ";
-    aString += QString::number((int)rint(music_progress * lcdWidth * 
+    if (lcdHeight > 3) {
+        QString aString;
+        aString = "widget_set Music progressBar 1 3 ";
+        aString += QString::number((int)rint(music_progress * lcdWidth * 
                                cellWidth));
-    sendToServer(aString);
+        sendToServer(aString);
+    }
 }
 
 void LCDProcClient::outputChannel()
@@ -1794,7 +1855,9 @@
 void LCDProcClient::outputGeneric()
 {
     QString aString;
-    aString = "widget_set Generic progressBar 1 ";
+    aString = "widget_set Generic progressBar ";
+    aString += QString::number (busy_pos);
+    aString += " ";
     aString += QString::number(lcdHeight);
     aString += " ";
     aString += QString::number((int)rint(generic_progress * lcdWidth * 
@@ -1935,6 +1998,7 @@
     sendToServer("screen_del Menu menuWidget5");
 
     sendToServer("widget_del Music progressBar");
+    sendToServer("widget_del Music infoWidget");
     sendToServer("widget_del Music timeWidget");
     sendToServer("widget_del Music topWidget");
     sendToServer("screen_del Music");
@@ -2003,7 +2067,12 @@
                                   "- is the master server running?\n\t\t\t"
                                   "Will retry in 30 seconds");
             QTimer::singleShot(30 * 1000, this, SLOT(updateRecordingList()));
-            switchToTime();
+
+            // If we can't get the recording status and we're showing
+            // it, switch back to time. Maybe it would be even better
+            // to show that the backend is unreachable ?
+            if  (activeScreen == "RecStatus")
+                switchToTime();
             return;
         }    
     }
Index: mythtv/programs/mythlcdserver/lcdserver.cpp
===================================================================
--- mythtv/programs/mythlcdserver/lcdserver.cpp	(revision 7592)
+++ mythtv/programs/mythlcdserver/lcdserver.cpp	(working copy)
@@ -39,7 +39,12 @@
         time is a string
         progress is a float between 0.0 and 1.0
 
-    SET_GENERIC_PROGRESS <progress>
+    SET_MUSIC_PLAYER_PROP <field> <val>
+        field is a string, currently SHUFFLE or REPEAT
+        val depends on field, currently integer
+
+    SET_GENERIC_PROGRESS <busy> <progress>
+        busy is 0 for busy spinner, 0 for normal progess bar
         progress is a float between 0.0 and 1.0
     
     UPDATE_LEDS
@@ -213,6 +218,10 @@
     {
         setMusicProgress(tokens, socket);
     }
+    else if (tokens[0] == "SET_MUSIC_PLAYER_PROP")
+    {
+        setMusicProp(tokens, socket);
+    }
     else if (tokens[0] == "SET_CHANNEL_PROGRESS")
     {
         setChannelProgress(tokens, socket);
@@ -577,7 +586,7 @@
 
     QString flat = tokens.join(" ");
    
-    if (tokens.count() != 2)
+    if (tokens.count() != 3)
     {
         VERBOSE(VB_IMPORTANT, "LCDServer: bad SET_GENERIC_PROGRESS command: "
                 << flat);
@@ -586,9 +595,17 @@
     }
      
     bool bOK;
-    float progress = tokens[1].toFloat(&bOK);
+    bool busy = tokens[1].toInt (&bOK);
     if (!bOK)
     {
+        VERBOSE(VB_IMPORTANT, "LCDServer: bad bool value in "
+                "SET_GENERIC_PROGRESS command: %1 %2" << tokens[1] << tokens[2]);
+        sendMessage(socket, "HUH?");
+        return;
+    }
+    float progress = tokens[2].toFloat(&bOK);
+    if (!bOK)
+    {
         VERBOSE(VB_IMPORTANT, "LCDServer: bad float value in "
                 "SET_GENERIC_PROGRESS command: %1" << tokens[1]);
         sendMessage(socket, "HUH?");
@@ -596,7 +613,7 @@
     }
     
     if (m_lcd)
-        m_lcd->setGenericProgress(progress);
+        m_lcd->setGenericProgress(busy, progress);
         
     sendMessage(socket, "OK");
 }
@@ -632,6 +649,65 @@
     sendMessage(socket, "OK");
 }
 
+void LCDServer::setMusicProp(const QStringList &tokens, QSocket *socket)
+{
+    if (debug_level > 0)
+        VERBOSE(VB_GENERAL, "LCDServer: SET_MUSIC_PROP");
+
+    QString flat = tokens.join(" ");
+   
+    if (tokens.count() < 3)
+    {
+        VERBOSE(VB_IMPORTANT, "LCDServer: bad SET_MUSIC_PROP command: " 
+                << flat);
+        sendMessage(socket, "HUH?");
+        return;
+    }
+     
+    if (tokens[1] == "SHUFFLE") {
+        if (tokens.count () != 3) {
+            VERBOSE(VB_IMPORTANT, "LCDServer: missing argument for "
+                    "SET_MUSIC_PROP SHUFFLE command: " << flat);
+            sendMessage(socket, "HUH?");
+            return;
+        }
+        bool bOk;
+        int state = tokens[2].toInt (&bOk);
+        if (!bOk) {
+            VERBOSE(VB_IMPORTANT, "LCDServer: bad argument for "
+                    "SET_MUSIC_PROP SHUFFLE command: " << tokens[2]);
+            sendMessage(socket, "HUH?");
+        return;
+        }
+        if (m_lcd) 
+            m_lcd->setMusicShuffle (state);
+    } else if (tokens[1] == "REPEAT") {
+        if (tokens.count () != 3) {
+            VERBOSE(VB_IMPORTANT, "LCDServer: missing argument for "
+                    "SET_MUSIC_PROP REPEAT command: " << flat);
+            sendMessage(socket, "HUH?");
+            return;
+        }
+        bool bOk;
+        int state = tokens[2].toInt (&bOk);
+        if (!bOk) {
+            VERBOSE(VB_IMPORTANT, "LCDServer: bad argument for "
+                    "SET_MUSIC_PROP REPEAT command: " << tokens[2]);
+            sendMessage(socket, "HUH?");
+            return;
+        }
+        if (m_lcd) 
+            m_lcd->setMusicRepeat (state);
+    } else {
+        VERBOSE(VB_IMPORTANT, "LCDServer: bad argument for "
+                "SET_MUSIC_PROP command: " << tokens[1]);
+        sendMessage(socket, "HUH?");
+        return;
+    }
+        
+    sendMessage(socket, "OK");
+}
+
 void LCDServer::setVolumeLevel(const QStringList &tokens, QSocket *socket)
 {
     if (debug_level > 0)
Index: mythtv/programs/mythlcdserver/lcdprocclient.h
===================================================================
--- mythtv/programs/mythlcdserver/lcdprocclient.h	(revision 7592)
+++ mythtv/programs/mythlcdserver/lcdprocclient.h	(working copy)
@@ -39,6 +39,8 @@
     
     void switchToMusic(const QString &artist, const QString &album, const QString &track);
     void setMusicProgress(QString time, float generic_progress);
+    void setMusicRepeat (int repeat);
+    void setMusicShuffle (int shuffle);
     void setLevels(int numbLevels, float *values);
     
     void switchToChannel(QString channum = "", QString title = "", 
@@ -49,7 +51,7 @@
                       bool popMenu = true);
     
     void switchToGeneric(QPtrList<LCDTextItem> *textItems);
-    void setGenericProgress(float generic_progress);
+    void setGenericProgress(bool busy, float generic_progress);
 
     void switchToVolume(QString app_name);
     void setVolumeLevel(float volume_level);
@@ -162,11 +164,21 @@
         
     float EQlevels[10];
     float progress;
+    /** TRUE if the generic progress indicator is a busy (ie. doesn't have a known total steps */
+    bool busy_progress;
+    /** Current position of the busy indicator, used if @p busy_progress is true. */
+    int busy_pos;
+    /** How many "blocks" the busy indicator must be, used if @p busy_progress is true. */
+    float busy_indicator_size;
+    /** Dicrection of the busy indicator on the, -1 or 1, used if @p busy_progress is true. */
+    int busy_direction;
     float generic_progress;
     float volume_level;
 
     float music_progress;
     QString music_time;
+    int music_repeat;
+    int music_shuffle;
 
     QString scrollingText;
     QString scrollScreen, scrollWidget;
Index: mythplugins/mythmusic/mythmusic/playbackbox.cpp
===================================================================
--- mythplugins/mythmusic/mythmusic/playbackbox.cpp	(revision 7592)
+++ mythplugins/mythmusic/mythmusic/playbackbox.cpp	(working copy)
@@ -772,15 +772,7 @@
             
             if (curMeta) 
             {
-                if (class LCD * lcd = LCD::Get()) 
-                {
-                    // Set track info on the LCD
-                    QPtrList<LCDTextItem> textItems;
-                    textItems.setAutoDelete(true);
-                    lcd->switchToMusic(curMeta->Artist (), 
-                                       curMeta->Album (), 
-                                       curMeta->Title ());
-                }
+                setTrackOnLCD (curMeta);
             }
         }    
         else
@@ -850,14 +842,7 @@
 
                     if (curMeta)
                     {
-                        if (class LCD * lcd = LCD::Get())
-                        {
-                            //Show the artist stuff on the LCD
-                            lcd->switchToMusic(curMeta->Artist (), 
-                                               curMeta->Album (), 
-                                               curMeta->Title ());
-                            lcd_volume_visible = false;
-                        }
+                        setTrackOnLCD (curMeta);
                     }
                 }
             }
@@ -1036,6 +1021,15 @@
     }
 }
 
+void PlaybackBoxMusic::setTrackOnLCD (Metadata *mdata) {
+    if (class LCD * lcd = LCD::Get())
+    {
+        // Set the Artist and Tract on the LCD
+        lcd->switchToMusic(mdata->Artist (), 
+                           mdata->Album (), 
+                           mdata->Title ());
+    }
+}
 
 void PlaybackBoxMusic::pause(void)
 {
@@ -1247,6 +1241,9 @@
                     shuffle_button->setText(tr("Shuffle: Smart"));
             }
             music_tree_list->scrambleParents(true);
+
+            if (class LCD * lcd = LCD::Get ())
+                lcd->setMusicShuffle (LCD::MUSIC_SHUFFLE_SMART);
             break;
         case SHUFFLE_RANDOM:
             if(shuffle_button)
@@ -1257,6 +1254,9 @@
                     shuffle_button->setText(tr("Shuffle: Rand"));
             }
             music_tree_list->scrambleParents(true);
+
+            if (class LCD * lcd = LCD::Get ())
+                lcd->setMusicShuffle (LCD::MUSIC_SHUFFLE_RAND);
             break;
         default:
             if(shuffle_button)
@@ -1267,6 +1267,9 @@
                     shuffle_button->setText(tr("Shuffle: None"));
             }
             music_tree_list->scrambleParents(false);
+
+            if (class LCD * lcd = LCD::Get ())
+                lcd->setMusicShuffle (LCD::MUSIC_SHUFFLE_NONE);
             break;
     }
     music_tree_list->setTreeOrdering(shufflemode + 1);
@@ -1275,6 +1278,10 @@
     else
         music_tree_list->setVisualOrdering(1);
     music_tree_list->refresh();
+    
+    if (isplaying) {
+        setTrackOnLCD (curMeta);
+    }    
 }
 
 void PlaybackBoxMusic::toggleShuffle(void)
@@ -1325,18 +1332,27 @@
                 repeat_button->setText(tr("2 Repeat: All"));
             else
                 repeat_button->setText(tr("Repeat: All"));
+
+            if (class LCD * lcd = LCD::Get ())
+                lcd->setMusicRepeat (LCD::MUSIC_REPEAT_ALL);
             break;
         case REPEAT_TRACK:
             if (keyboard_accelerators)
                 repeat_button->setText(tr("2 Repeat: Track"));
             else
                 repeat_button->setText(tr("Repeat: Track"));
+
+            if (class LCD * lcd = LCD::Get ())
+                lcd->setMusicRepeat (LCD::MUSIC_REPEAT_TRACK);
             break;
         default:
             if (keyboard_accelerators)
                 repeat_button->setText(tr("2 Repeat: None"));
             else
                 repeat_button->setText(tr("Repeat: None"));
+
+            if (class LCD * lcd = LCD::Get ())
+                lcd->setMusicRepeat (LCD::MUSIC_REPEAT_NONE);
             break;
     }
 }
@@ -1437,14 +1453,7 @@
     {
         case OutputEvent::Playing:
         {
-            if (class LCD * lcd = LCD::Get()) {
-                // Set track info on the LCD
-                QPtrList<LCDTextItem> textItems;
-                textItems.setAutoDelete(true);
-                lcd->switchToMusic(curMeta->Artist (), 
-                                   curMeta->Album (), 
-                                   curMeta->Title ());
-            }
+            setTrackOnLCD (curMeta);
             statusString = tr("Playing stream.");
             break;
         }
@@ -1613,13 +1622,7 @@
         if (album_text)
             album_text->SetText(curMeta->Album());
 
-        if (class LCD * lcd = LCD::Get())
-        {
-            // Set the Artist and Tract on the LCD
-            lcd->switchToMusic(curMeta->Artist (), 
-                               curMeta->Album (), 
-                               curMeta->Title ());
-        }
+        setTrackOnLCD (curMeta);
 
         maxTime = curMeta->Length() / 1000;
 
Index: mythplugins/mythmusic/mythmusic/playbackbox.h
===================================================================
--- mythplugins/mythmusic/mythmusic/playbackbox.h	(revision 7592)
+++ mythplugins/mythmusic/mythmusic/playbackbox.h	(working copy)
@@ -100,6 +100,7 @@
     void updatePlaylistFromSmartPlaylist(QString category, QString name);
     void CycleVisualizer(void);
     void updatePlaylistFromCD(void);
+    void setTrackOnLCD (Metadata *mdata);
 
     void postUpdate();
 
