vdr  1.7.27
skins.c
Go to the documentation of this file.
00001 /*
00002  * skins.c: The optical appearance of the OSD
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: skins.c 2.5 2012/03/11 14:36:11 kls Exp $
00008  */
00009 
00010 #include "skins.h"
00011 #include "interface.h"
00012 #include "status.h"
00013 
00014 // --- cSkinQueuedMessage ----------------------------------------------------
00015 
00016 class cSkinQueuedMessage : public cListObject {
00017   friend class cSkins;
00018 private:
00019   eMessageType type;
00020   char *message;
00021   int seconds;
00022   int timeout;
00023   tThreadId threadId;
00024   eKeys key;
00025   int state;
00026   cMutex mutex;
00027   cCondVar condVar;
00028 public:
00029   cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout);
00030   virtual ~cSkinQueuedMessage();
00031   };
00032 
00033 cSkinQueuedMessage::cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
00034 {
00035   type = Type;
00036   message = s ? strdup(s) : NULL;
00037   seconds = Seconds;
00038   timeout = Timeout;
00039   threadId = cThread::ThreadId();
00040   key = kNone;
00041   state = 0; // waiting
00042 }
00043 
00044 cSkinQueuedMessage::~cSkinQueuedMessage()
00045 {
00046   free(message);
00047 }
00048 
00049 cList<cSkinQueuedMessage> SkinQueuedMessages;
00050 
00051 // --- cSkinDisplay ----------------------------------------------------------
00052 
00053 cSkinDisplay *cSkinDisplay::current = NULL;
00054 
00055 cSkinDisplay::cSkinDisplay(void)
00056 {
00057   current = this;
00058   editableWidth = 100; //XXX
00059 }
00060 
00061 cSkinDisplay::~cSkinDisplay()
00062 {
00063   current = NULL;
00064 }
00065 
00066 // --- cSkinDisplayMenu ------------------------------------------------------
00067 
00068 cSkinDisplayMenu::cSkinDisplayMenu(void)
00069 {
00070   SetTabs(0);
00071 }
00072 
00073 void cSkinDisplayMenu::SetTabs(int Tab1, int Tab2, int Tab3, int Tab4, int Tab5)
00074 {
00075   tabs[0] = 0;
00076   tabs[1] = Tab1 ? tabs[0] + Tab1 : 0;
00077   tabs[2] = Tab2 ? tabs[1] + Tab2 : 0;
00078   tabs[3] = Tab3 ? tabs[2] + Tab3 : 0;
00079   tabs[4] = Tab4 ? tabs[3] + Tab4 : 0;
00080   tabs[5] = Tab5 ? tabs[4] + Tab5 : 0;
00081   for (int i = 1; i < MaxTabs; i++)
00082       tabs[i] *= AvgCharWidth();
00083 }
00084 
00085 void cSkinDisplayMenu::Scroll(bool Up, bool Page)
00086 {
00087   textScroller.Scroll(Up, Page);
00088 }
00089 
00090 const char *cSkinDisplayMenu::GetTabbedText(const char *s, int Tab)
00091 {
00092   if (!s)
00093      return NULL;
00094   static char buffer[1000];
00095   const char *a = s;
00096   const char *b = strchrnul(a, '\t');
00097   while (*b && Tab-- > 0) {
00098         a = b + 1;
00099         b = strchrnul(a, '\t');
00100         }
00101   if (!*b)
00102      return (Tab <= 0) ? a : NULL;
00103   unsigned int n = b - a;
00104   if (n >= sizeof(buffer))
00105      n = sizeof(buffer) - 1;
00106   strncpy(buffer, a, n);
00107   buffer[n] = 0;
00108   return buffer;
00109 }
00110 
00111 void cSkinDisplayMenu::SetScrollbar(int Total, int Offset)
00112 {
00113 }
00114 
00115 int cSkinDisplayMenu::GetTextAreaWidth(void) const
00116 {
00117   return 0;
00118 }
00119 
00120 const cFont *cSkinDisplayMenu::GetTextAreaFont(bool) const
00121 {
00122   return NULL;
00123 }
00124 
00125 // --- cSkinDisplayReplay::cProgressBar --------------------------------------
00126 
00127 cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
00128 :cBitmap(Width, Height, 2)
00129 {
00130   total = Total;
00131   if (total > 0) {
00132      int p = Pos(Current);
00133      DrawRectangle(0, 0, p, Height - 1, ColorSeen);
00134      DrawRectangle(p + 1, 0, Width - 1, Height - 1, ColorRest);
00135      if (Marks) {
00136         bool Start = true;
00137         for (const cMark *m = Marks->First(); m; m = Marks->Next(m)) {
00138             int p1 = Pos(m->Position());
00139             if (Start) {
00140                const cMark *m2 = Marks->Next(m);
00141                int p2 = Pos(m2 ? m2->Position() : total);
00142                int h = Height / 3;
00143                DrawRectangle(p1, h, p2, Height - h, ColorSelected);
00144                }
00145             Mark(p1, Start, m->Position() == Current, ColorMark, ColorCurrent);
00146             Start = !Start;
00147             }
00148         }
00149      }
00150 }
00151 
00152 void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent)
00153 {
00154   DrawRectangle(x, 0, x, Height() - 1, ColorMark);
00155   const int d = Height() / (Current ? 3 : 9);
00156   for (int i = 0; i < d; i++) {
00157       int h = Start ? i : Height() - 1 - i;
00158       DrawRectangle(x - d + i, h, x + d - i, h, Current ? ColorCurrent : ColorMark);
00159       }
00160 }
00161 
00162 // --- cSkinDisplayReplay ----------------------------------------------------
00163 
00164 cSkinDisplayReplay::cSkinDisplayReplay(void)
00165 {
00166   marks = NULL;
00167 }
00168 
00169 void cSkinDisplayReplay::SetMarks(const cMarks *Marks)
00170 {
00171   marks = Marks;
00172 }
00173 
00174 // --- cSkin -----------------------------------------------------------------
00175 
00176 cSkin::cSkin(const char *Name, cTheme *Theme)
00177 {
00178   name = strdup(Name);
00179   theme = Theme;
00180   if (theme)
00181      cThemes::Save(name, theme);
00182   Skins.Add(this);
00183 }
00184 
00185 cSkin::~cSkin()
00186 {
00187   free(name);
00188 }
00189 
00190 // --- cSkins ----------------------------------------------------------------
00191 
00192 cSkins Skins;
00193 
00194 cSkins::cSkins(void)
00195 {
00196   displayMessage = NULL;
00197 }
00198 
00199 cSkins::~cSkins()
00200 {
00201   delete displayMessage;
00202 }
00203 
00204 bool cSkins::SetCurrent(const char *Name)
00205 {
00206   if (Name) {
00207      for (cSkin *Skin = First(); Skin; Skin = Next(Skin)) {
00208          if (strcmp(Skin->Name(), Name) == 0) {
00209             isyslog("setting current skin to \"%s\"", Name);
00210             current = Skin;
00211             return true;
00212             }
00213          }
00214      }
00215   current = First();
00216   if (current)
00217      isyslog("skin \"%s\" not available - using \"%s\" instead", Name, current->Name());
00218   else
00219      esyslog("ERROR: no skin available");
00220   return current != NULL;
00221 }
00222 
00223 eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
00224 {
00225   if (!cThread::IsMainThread()) {
00226      dsyslog("cSkins::Message() called from background thread - ignored! (Use cSkins::QueueMessage() instead)");
00227      return kNone;
00228      }
00229   switch (Type) {
00230     case mtInfo:    isyslog("info: %s", s); break;
00231     case mtWarning: isyslog("warning: %s", s); break;
00232     case mtError:   esyslog("ERROR: %s", s); break;
00233     default: ;
00234     }
00235   if (!Current())
00236      return kNone;
00237   if (!cSkinDisplay::Current()) {
00238      if (displayMessage)
00239         delete displayMessage;
00240      displayMessage = Current()->DisplayMessage();
00241      }
00242   cSkinDisplay::Current()->SetMessage(Type, s);
00243   cSkinDisplay::Current()->Flush();
00244   cStatus::MsgOsdStatusMessage(s);
00245   eKeys k = kNone;
00246   if (Type != mtStatus) {
00247      k = Interface->Wait(Seconds);
00248      if (displayMessage) {
00249         delete displayMessage;
00250         displayMessage = NULL;
00251         cStatus::MsgOsdClear();
00252         }
00253      else {
00254         cSkinDisplay::Current()->SetMessage(Type, NULL);
00255         cStatus::MsgOsdStatusMessage(NULL);
00256         }
00257      }
00258   else if (!s && displayMessage) {
00259      delete displayMessage;
00260      displayMessage = NULL;
00261      cStatus::MsgOsdClear();
00262      }
00263   return k;
00264 }
00265 
00266 int cSkins::QueueMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
00267 {
00268   if (Type == mtStatus) {
00269      dsyslog("cSkins::QueueMessage() called with mtStatus - ignored!");
00270      return kNone;
00271      }
00272   if (isempty(s)) {
00273      if (!cThread::IsMainThread()) {
00274         queueMessageMutex.Lock();
00275         for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
00276             if (m->threadId == cThread::ThreadId() && m->state == 0)
00277                m->state = 2; // done
00278             }
00279         queueMessageMutex.Unlock();
00280         }
00281      else
00282         dsyslog("cSkins::QueueMessage() called with empty message from main thread - ignored!");
00283      return kNone;
00284      }
00285   int k = kNone;
00286   if (Timeout > 0) {
00287      if (cThread::IsMainThread()) {
00288         dsyslog("cSkins::QueueMessage() called from main thread with Timeout = %d - ignored!", Timeout);
00289         return k;
00290         }
00291      cSkinQueuedMessage *m = new cSkinQueuedMessage(Type, s, Seconds, Timeout);
00292      queueMessageMutex.Lock();
00293      SkinQueuedMessages.Add(m);
00294      m->mutex.Lock();
00295      queueMessageMutex.Unlock();
00296      if (m->condVar.TimedWait(m->mutex, Timeout * 1000))
00297         k = m->key;
00298      else
00299         k = -1; // timeout, nothing has been displayed
00300      m->state = 2; // done
00301      m->mutex.Unlock();
00302      }
00303   else {
00304      queueMessageMutex.Lock();
00305      // Check if there is a waiting message w/o timeout for this thread:
00306      if (Timeout == -1) {
00307         for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
00308             if (m->threadId == cThread::ThreadId()) {
00309                if (m->state == 0 && m->timeout == -1)
00310                   m->state = 2; // done
00311                break;
00312                }
00313             }
00314          }
00315      // Add the new message:
00316      SkinQueuedMessages.Add(new cSkinQueuedMessage(Type, s, Seconds, Timeout));
00317      queueMessageMutex.Unlock();
00318      }
00319   return k;
00320 }
00321 
00322 void cSkins::ProcessQueuedMessages(void)
00323 {
00324   if (!cThread::IsMainThread()) {
00325      dsyslog("cSkins::ProcessQueuedMessages() called from background thread - ignored!");
00326      return;
00327      }
00328   cSkinQueuedMessage *msg = NULL;
00329   // Get the first waiting message:
00330   queueMessageMutex.Lock();
00331   for (cSkinQueuedMessage *m = SkinQueuedMessages.First(); m; m = SkinQueuedMessages.Next(m)) {
00332       if (m->state == 0) { // waiting
00333          m->state = 1; // active
00334          msg = m;
00335          break;
00336          }
00337       }
00338   queueMessageMutex.Unlock();
00339   // Display the message:
00340   if (msg) {
00341      msg->mutex.Lock();
00342      if (msg->state == 1) { // might have changed since we got it
00343         msg->key = Skins.Message(msg->type, msg->message, msg->seconds);
00344         if (msg->timeout == 0)
00345            msg->state = 2; // done
00346         else
00347            msg->condVar.Broadcast();
00348         }
00349      msg->mutex.Unlock();
00350      }
00351   // Remove done messages from the queue:
00352   queueMessageMutex.Lock();
00353   for (;;) {
00354       cSkinQueuedMessage *m = SkinQueuedMessages.First();
00355       if (m && m->state == 2) { // done
00356          SkinQueuedMessages.Del(m);
00357          }
00358       else
00359          break;
00360       }
00361   queueMessageMutex.Unlock();
00362 }
00363 
00364 void cSkins::Flush(void)
00365 {
00366   if (cSkinDisplay::Current())
00367      cSkinDisplay::Current()->Flush();
00368 }
00369 
00370 void cSkins::Clear(void)
00371 {
00372   if (displayMessage) {
00373      delete displayMessage;
00374      displayMessage = NULL;
00375      }
00376   cList<cSkin>::Clear();
00377 }