vdr
1.7.27
|
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 }