vdr/skins.c
Klaus Schmidinger c23992b807 Version 1.3.42
- Removed leftover 'needsBufferReserve' variable from cTransfer (thanks to Marco
  Schlüßler).
- Fixed setting "No title" for broken event data (reported by Ronny Kornexl).
- Fixed channel up/down switching on single card systems (reported by Stefan
  Huelswitt).
- Fixed handling "pending" timers that blocked others that actually could record
  (reported by Thomas Koch).
- Speeded up cVideoRepacker (thanks to Reinhard Nissl).
- Added an 'Id' parameter to cDevice::PlayAudio() to allow plugins to easier
  process the audio data (thanks to Marco Schlüßler).
- Added Czech language texts (thanks to Vladimír Bárta).
  Plugin authors may want to add the new entries to their I18N texts and contact
  the translators to have their texts translated. Note that there are now 21
  different OSD languages, so please make sure you have 21 versions for each of
  your texts.
- Updated the Polish OSD texts (thanks to Jaroslaw Swierczynski).
- Fixed auto advance in string entry fields when pressing Up/Down in insert mode
  (reported by Udo Richter).
- Fixed handling the "Setup/OSD/Menu button closes" option when set to 'yes' in
  case a replay is active (thanks to Udo Richter).
- Improved cUnbufferedFile; USE_FADVISE is now defined in tools.c by default, so
  if you don't want to use "fadvise" you need to comment out that line (thanks to
  Artur Skawina).
- Fixed a missing ',' in the Swedish OSD texts (thanks to Arthur Konovalov).
- cDevice::Transferring() can now be used to determine whether the (primary)
  device is currently playing in Transfer Mode (based on a suggestion by
  Reinhard Nissl).
- The 'runvdr' script no longer uses the $VDRUSR environment variable to set
  the user id under which 'vdr' shall run. Just add the '-u username' option
  when you call 'runvdr'.
- Fixed multiple entries of the same subdirectory in the "Recordings" menu
  (reported by Christian Jacobsen).
- Enabled generating a core dump if VDR is run with a different user id (thanks
  to Ville Skyttä).
- Fixed handling the "Blue" key in the "Schedule" menu for the current channel
  (thanks to Rolf Ahrenberg).
- Renamed the Makefile target 'plugins-clean' to 'clean-plugins' (suggested by
  Sebastian Frei).
- Made all font and image data 'const' (thanks to Darren Salt).
- Fixed scrolling with Up/Down in case there are non-selectable items at the
  beginning or end of the menu (reported by Helmut Auer).
- Added cSkin::GetTextAreaWidth() and cSkin::GetTextAreaFont(), so that a plugin
  that wants to do special text formatting can do so (thanks to Alexander Rieger).
2006-02-05 18:00:00 +01:00

345 lines
8.7 KiB
C

/*
* skins.c: The optical appearance of the OSD
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skins.c 1.8 2006/02/05 14:53:04 kls Exp $
*/
#include "skins.h"
#include "interface.h"
#include "status.h"
// --- cSkinQueuedMessage ----------------------------------------------------
class cSkinQueuedMessage : public cListObject {
friend class cSkins;
private:
eMessageType type;
char *message;
int seconds;
int timeout;
tThreadId threadId;
eKeys key;
int state;
cMutex mutex;
cCondVar condVar;
public:
cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout);
virtual ~cSkinQueuedMessage();
};
cSkinQueuedMessage::cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
{
type = Type;
message = s ? strdup(s) : NULL;
seconds = Seconds;
timeout = Timeout;
threadId = cThread::ThreadId();
key = kNone;
state = 0; // waiting
}
cSkinQueuedMessage::~cSkinQueuedMessage()
{
free(message);
}
cList<cSkinQueuedMessage> SkinQueuedMessages;
// --- cSkinDisplay ----------------------------------------------------------
cSkinDisplay *cSkinDisplay::current = NULL;
cSkinDisplay::cSkinDisplay(void)
{
current = this;
editableWidth = 100; //XXX
}
cSkinDisplay::~cSkinDisplay()
{
current = NULL;
}
// --- cSkinDisplayMenu ------------------------------------------------------
cSkinDisplayMenu::cSkinDisplayMenu(void)
{
SetTabs(0);
}
void cSkinDisplayMenu::SetTabs(int Tab1, int Tab2, int Tab3, int Tab4, int Tab5)
{
tabs[0] = 0;
tabs[1] = Tab1 ? tabs[0] + Tab1 : 0;
tabs[2] = Tab2 ? tabs[1] + Tab2 : 0;
tabs[3] = Tab3 ? tabs[2] + Tab3 : 0;
tabs[4] = Tab4 ? tabs[3] + Tab4 : 0;
tabs[5] = Tab5 ? tabs[4] + Tab5 : 0;
for (int i = 1; i < MaxTabs; i++)
tabs[i] *= 12;//XXX average character width of font used for items!!!
}
void cSkinDisplayMenu::Scroll(bool Up, bool Page)
{
textScroller.Scroll(Up, Page);
}
const char *cSkinDisplayMenu::GetTabbedText(const char *s, int Tab)
{
if (!s)
return NULL;
static char buffer[1000];
const char *a = s;
const char *b = strchrnul(a, '\t');
while (*b && Tab-- > 0) {
a = b + 1;
b = strchrnul(a, '\t');
}
if (!*b)
return (Tab <= 0) ? a : NULL;
unsigned int n = b - a;
if (n >= sizeof(buffer))
n = sizeof(buffer) - 1;
strncpy(buffer, a, n);
buffer[n] = 0;
return buffer;
}
int cSkinDisplayMenu::GetTextAreaWidth(void) const
{
return 0;
}
const cFont *cSkinDisplayMenu::GetTextAreaFont(bool) const
{
return NULL;
}
// --- cSkinDisplayReplay::cProgressBar --------------------------------------
cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
:cBitmap(Width, Height, 2)
{
total = Total;
if (total > 0) {
int p = Pos(Current);
DrawRectangle(0, 0, p, Height - 1, ColorSeen);
DrawRectangle(p + 1, 0, Width - 1, Height - 1, ColorRest);
if (Marks) {
bool Start = true;
for (const cMark *m = Marks->First(); m; m = Marks->Next(m)) {
int p1 = Pos(m->position);
if (Start) {
const cMark *m2 = Marks->Next(m);
int p2 = Pos(m2 ? m2->position : total);
int h = Height / 3;
DrawRectangle(p1, h, p2, Height - h, ColorSelected);
}
Mark(p1, Start, m->position == Current, ColorMark, ColorCurrent);
Start = !Start;
}
}
}
}
void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent)
{
DrawRectangle(x, 0, x, Height() - 1, ColorMark);
const int d = Height() / (Current ? 3 : 9);
for (int i = 0; i < d; i++) {
int h = Start ? i : Height() - 1 - i;
DrawRectangle(x - d + i, h, x + d - i, h, Current ? ColorCurrent : ColorMark);
}
}
// --- cSkinDisplayReplay ----------------------------------------------------
cSkinDisplayReplay::cSkinDisplayReplay(void)
{
marks = NULL;
}
void cSkinDisplayReplay::SetMarks(const cMarks *Marks)
{
marks = Marks;
}
// --- cSkin -----------------------------------------------------------------
cSkin::cSkin(const char *Name, cTheme *Theme)
{
name = strdup(Name);
theme = Theme;
if (theme)
cThemes::Save(name, theme);
Skins.Add(this);
Skins.SetCurrent(Name);
}
cSkin::~cSkin()
{
free(name);
}
// --- cSkins ----------------------------------------------------------------
cSkins Skins;
cSkins::cSkins(void)
{
displayMessage = NULL;
}
cSkins::~cSkins()
{
delete displayMessage;
}
bool cSkins::SetCurrent(const char *Name)
{
if (Name) {
for (cSkin *Skin = First(); Skin; Skin = Next(Skin)) {
if (strcmp(Skin->Name(), Name) == 0) {
current = Skin;
return true;
}
}
}
current = First();
return current != NULL;
}
eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
{
switch (Type) {
case mtInfo: isyslog("info: %s", s); break;
case mtWarning: isyslog("warning: %s", s); break;
case mtError: esyslog("ERROR: %s", s); break;
default: ;
}
if (!Current())
return kNone;
if (!cSkinDisplay::Current() && !displayMessage)
displayMessage = Current()->DisplayMessage();
cSkinDisplay::Current()->SetMessage(Type, s);
cSkinDisplay::Current()->Flush();
cStatus::MsgOsdStatusMessage(s);
eKeys k = kNone;
if (Type != mtStatus) {
k = Interface->Wait(Seconds);
if (displayMessage) {
delete displayMessage;
displayMessage = NULL;
cStatus::MsgOsdClear();
}
else {
cSkinDisplay::Current()->SetMessage(Type, NULL);
cStatus::MsgOsdStatusMessage(NULL);
}
}
else if (!s && displayMessage) {
delete displayMessage;
displayMessage = NULL;
cStatus::MsgOsdClear();
}
return k;
}
int cSkins::QueueMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
{
if (Type == mtStatus) {
dsyslog("cSkins::QueueMessage() called with mtStatus - ignored!");
return kNone;
}
if (isempty(s)) {
dsyslog("cSkins::QueueMessage() called with empty message - ignored!");
return kNone;
}
int k = kNone;
if (Timeout > 0) {
if (cThread::IsMainThread()) {
dsyslog("cSkins::QueueMessage() called from main thread with Timeout = %d - ignored!", Timeout);
return k;
}
cSkinQueuedMessage *m = new cSkinQueuedMessage(Type, s, Seconds, Timeout);
queueMessageMutex.Lock();
SkinQueuedMessages.Add(m);
m->mutex.Lock();
queueMessageMutex.Unlock();
if (m->condVar.TimedWait(m->mutex, Timeout * 1000))
k = m->key;
else
k = -1; // timeout, nothing has been displayed
m->state = 2; // done
m->mutex.Unlock();
}
else {
queueMessageMutex.Lock();
// Check if there is a waiting message w/o timeout for this thread:
if (Timeout == -1) {
for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
if (m->threadId == cThread::ThreadId()) {
if (m->state == 0 && m->timeout == -1)
m->state = 2; // done
break;
}
}
}
// Add the new message:
SkinQueuedMessages.Add(new cSkinQueuedMessage(Type, s, Seconds, Timeout));
queueMessageMutex.Unlock();
}
return k;
}
void cSkins::ProcessQueuedMessages(void)
{
if (!cThread::IsMainThread()) {
dsyslog("cSkins::ProcessQueuedMessages() called from background thread - ignored!");
return;
}
cSkinQueuedMessage *msg = NULL;
// Get the first waiting message:
queueMessageMutex.Lock();
for (cSkinQueuedMessage *m = SkinQueuedMessages.First(); m; m = SkinQueuedMessages.Next(m)) {
if (m->state == 0) { // waiting
m->state = 1; // active
msg = m;
break;
}
}
queueMessageMutex.Unlock();
// Display the message:
if (msg) {
msg->mutex.Lock();
if (msg->state == 1) { // might have changed since we got it
msg->key = Skins.Message(msg->type, msg->message, msg->seconds);
if (msg->timeout == 0)
msg->state = 2; // done
else
msg->condVar.Broadcast();
}
msg->mutex.Unlock();
}
// Remove done messages from the queue:
queueMessageMutex.Lock();
for (;;) {
cSkinQueuedMessage *m = SkinQueuedMessages.First();
if (m && m->state == 2) { // done
SkinQueuedMessages.Del(m);
}
else
break;
}
queueMessageMutex.Unlock();
}
void cSkins::Flush(void)
{
if (cSkinDisplay::Current())
cSkinDisplay::Current()->Flush();
}