summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de>2007-06-03 22:18:54 +0000
committerDieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de>2007-06-03 22:18:54 +0000
commitcd260aabdb23fd97d9cb8b0105e61d90ca844f01 (patch)
treedac9b3f1a903dac9510974f443e0b376d27e6252
parent41687a7bbe9c8d304b805a9a5f7e14101f1d75a5 (diff)
downloadvdr-plugin-live-cd260aabdb23fd97d9cb8b0105e61d90ca844f01.tar.gz
vdr-plugin-live-cd260aabdb23fd97d9cb8b0105e61d90ca844f01.tar.bz2
- general CSS based themeing support.
- added setup option to select theme. - added search scheme for themable images and stylesheets. - added marine (default) and redwine theme. - documented new features. All developers must read doc/dev-conventions.txt
-rw-r--r--css/styles.css4
-rw-r--r--doc/ChangeLog28
-rw-r--r--doc/css-themeing.txt117
-rw-r--r--doc/dev-conventions.txt42
-rw-r--r--filecache.h8
-rw-r--r--pages/content.ecpp4
-rw-r--r--pages/edit_timer.ecpp2
-rw-r--r--pages/login.ecpp2
-rw-r--r--pages/pageelems.ecpp16
-rw-r--r--pages/recordings.ecpp10
-rw-r--r--pages/remote.ecpp4
-rw-r--r--pages/schedule.ecpp4
-rw-r--r--pages/searchtimers.ecpp10
-rw-r--r--pages/setup.ecpp30
-rw-r--r--pages/timers.ecpp8
-rw-r--r--pages/tooltip.ecpp10
-rw-r--r--pages/whats_on.ecpp8
-rw-r--r--setup.cpp14
-rw-r--r--setup.h23
-rw-r--r--themes/marine/css/theme.css5
-rw-r--r--themes/marine/img/zap.pngbin0 -> 873 bytes
-rw-r--r--themes/redwine/css/theme.css982
-rw-r--r--themes/redwine/img/bg_box_h.pngbin0 -> 188 bytes
-rw-r--r--themes/redwine/img/bg_box_l.pngbin0 -> 213 bytes
-rw-r--r--themes/redwine/img/bg_box_r.pngbin0 -> 221 bytes
-rw-r--r--themes/redwine/img/bg_header_h.pngbin0 -> 185 bytes
-rw-r--r--themes/redwine/img/bg_header_l.pngbin0 -> 245 bytes
-rw-r--r--themes/redwine/img/bg_header_r.pngbin0 -> 252 bytes
-rw-r--r--themes/redwine/img/bg_line.pngbin0 -> 138 bytes
-rw-r--r--themes/redwine/img/bg_line_top.pngbin0 -> 140 bytes
-rw-r--r--themes/redwine/img/bg_tools.pngbin0 -> 143 bytes
-rw-r--r--themes/redwine/img/menu_line_bg.pngbin0 -> 182 bytes
-rw-r--r--themes/redwine/img/zap.pngbin0 -> 873 bytes
-rw-r--r--tntconfig.cpp13
34 files changed, 1262 insertions, 82 deletions
diff --git a/css/styles.css b/css/styles.css
index 3bda69a..2f89a67 100644
--- a/css/styles.css
+++ b/css/styles.css
@@ -124,7 +124,7 @@ div.domTTepg {
div.menu {
background: #000057 url(menu_line_bg.png) repeat-x;
- min-height: 30px;
+ min-height: 27px;
margin: 0;
padding: 0 0 0 10px;
line-height: 20px;
@@ -204,7 +204,7 @@ div.pagemenu span {
div.pagemenu span.sep {
text-decoration: none;
- color: grey;
+ color: #C0C1DA;
font-weight: lighter;
padding: 5px;
}
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 69bac21..078e35c 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,16 +1,28 @@
+2007-06-03 Dieter Hametner <dieter@air.mittelstation.de>
+
+ Added CSS based themeing support. For details please read
+ doc/css-themeing.txt and doc/dev-conventions.txt.
+ * setup.h, setup.cpp, setup.ecpp: added setup for theme and
+ selection of theme.
+ * pages/*.ecpp: added support for themable images.
+ * tntconfig.cpp: cascaded search for images, to support themeing.
+
2007-06-03 Christian Wieninger <cwieninger at gmx dot de>
- Setup includes now a local net mask specifying the address range without
- necessary login (#321)
+
+ Setup includes now a local net mask specifying the address range
+ without necessary login (#321)
2007-06-02 Christian Wieninger <cwieninger at gmx dot de>
+
required version of VDR is now >= 1.4.0-2
2007-06-01 Sascha Volkenandt <sascha at akv-soft dot de>
- The detection of featured plugins was uniformed. The display in the about
- box now reads "active: <version>" or "required: <version>"
+ The detection of featured plugins was uniformed. The display in
+ the about box now reads "active: <version>" or "required:
+ <version>"
-2007-06-01 Dieter Hametner <dh+vdr@gekrumbel.de>
+2007-06-01 Dieter Hametner <dh+vdr at gekrumbel dot de>
These changes fix bug entry #339
* css-themeing.txt: describe how to do css themeing.
@@ -26,6 +38,6 @@
unrecorded Sascha Volkenandt <sascha at akv-soft dot de>
- Due to the introduction of a uniform header for C++ standard extenstions,
- the boost library is now only necessary if the used g++ compiler version
- is less than 4.0
+ Due to the introduction of a uniform header for C++ standard
+ extenstions, the boost library is now only necessary if the used
+ g++ compiler version is less than 4.0
diff --git a/doc/css-themeing.txt b/doc/css-themeing.txt
index be84e26..3148838 100644
--- a/doc/css-themeing.txt
+++ b/doc/css-themeing.txt
@@ -3,18 +3,111 @@ How to do live theming with CSS.
Live supports CSS theming. While the structure of the html pages is
given by the plugin, there is the possibility to change the look
-through CSS.
-
-Currently a predefined, compiled into the plugin stylesheet 'styles.css' is
-delivered first. Then a stylesheet 'css/siteprefs.css' is sent to the
-browser. 'css/siteprefs.css' is a normal file that is found in
-USRDIR. The default of USRDIR is /usr/share/vdr-plugin-live. This
-can be changed at compile time by setting the USRDIR variable
-(i.E. USRDIR=/tmp/live make)
-
-One of the next steps will be a mechanism to select additional style
-files and images through a theme setting in vdr. The different themes
-will be also located in theme related subdirs beneath USRDIR.
+through CSS and exchanged images.
+
+
+Themable resources
+------------------
+
+CSS stylesheets and referenced images are themable. That means a theme
+can replace the icons and background images in the markup.
+
+
+Access scheme for the css stylesheets
+-------------------------------------
+
+Each live page requests at least three stylesheets in the following
+order:
+
+ 1. 'styles.css' (the build in stylesheet) is requested.
+ 2. The theme master stylesheet 'theme.css' is requested.
+ 3. A site preferences stylesheet is requested ('siteprefs.css')
+
+
+Location for the stylesheets
+----------------------------
+
+The initial stylesheet 'styles.css' provides a basic layout. It is a
+builtin stylesheet and can not be altered after live is compiled and
+installed.
+
+The theme stylesheed 'theme.css' is requested through following url:
+
+ themes/<themename>/css/theme.css
+
+The site preference stylesheet is requested through this url:
+
+ css/siteprefs.css
+
+
+Access scheme for themable images
+---------------------------------
+
+All themable images in the pages, that live delivers to the browser are
+accessed through the url
+
+ themes/<themename>/img/<imagename>
+
+If a image is not found under that url, the image is searched in
+ common/img/<imagename>
+
+And if not found there, an attempt to deliver a built in image is taken.
+
+
+Location of the resources in the file system
+--------------------------------------------
+
+All themable content must be present in the directory specified by
+USRDIR. The default USRDIR is /usr/share/vdr-plugin-live. This can be
+changed at compile time by setting the USRDIR variable
+(i.E. USRDIR=/tmp/live make).
+
+That means all themes must reside in USRDIR/themes.
+
+
+Structure of a theme package
+----------------------------
+
+A theme package consists of directory named after the theme name. It
+must contain the subdirectories 'css' and 'img'. Under css and img no
+other subdirectories are allowed for security reasons (see below).
+
+In the subdirectory css a stylesheet theme.css must exist in oder to
+override or extend the already defined styles from 'styles.css'.
+
+Additional images referenced through the stylesheet and images
+replacing the default images go to the 'img' subdirectory. Replacing
+images must have the same name like the image to be replaced.
+
+The live distribution comes with a few predefinded theme packages. You
+should take look into them to better understand this structure.
+
+
+Selecting a theme in live
+-------------------------
+
+In the live setup page, the user can select the desired theme. When
+the settings are saved the selected themes become active. Live detects
+the available themes dynamicaly by scanning the 'themes' directory in
+USERDIR for available themes and creates the select box from this
+information.
+
+So the installation of a new theme is easyly done by unpacking a
+theme-archive in USRDIR/themes. This assumes the theme-archive follows
+the structure of a theme package as described above.
+
+
+Security provisions
+-------------------
+
+Live will map every url starting with themes/<themename>/css or
+themes/<themename>/img to exactly these directories under USRDIR. That
+means any path components after 'img' or 'css' are discarded. Only the
+basename of the url is appended to these directories. This is to
+prevent possible malicous requests to other locations in the
+filesystem by adding '..' to the request path. The downside of this
+is, that no additional directories below 'img' and 'css' are possible
+for the theme designer.
User Contribution
diff --git a/doc/dev-conventions.txt b/doc/dev-conventions.txt
index c3472fd..07d026d 100644
--- a/doc/dev-conventions.txt
+++ b/doc/dev-conventions.txt
@@ -1,4 +1,7 @@
-This file contains some guidlines for developers about what to obey
+Live development guidelines
+===========================
+
+This file contains some guidelines for developers about what to obey
when adding new functionality to live plugin.
First of all please look at the existing code and how it was done
@@ -8,12 +11,15 @@ We want to support a broad range of browsers. On one side are hand held
devices like WEB-enabled PDAs or mobile phones. They often lack full
grown support for ECMAScript and have small screen sizes. The other
extreme are the desktop browsers like FireFox, Konqueror, Opera and
-perhabs IE (if the 'powers that be' make him more CSS compliant).
-Here WEB 2.0 featuers can improve the users experience.
+perhaps IE (if the 'powers that be' make him more CSS compliant).
+Here WEB 2.0 features can improve the users experience.
+
+
+With or without ECMAScript
+--------------------------
-In the future some themeing in live might help to cover this broad
-range. Meanwhile we suggest the following rule to activate
-functionality with and without ECMAScript support:
+Since not all browsers support ECMAScript, we suggest the following
+rule to activate functionality with and without ECMAScript support:
Use anchors analog to this example
@@ -23,3 +29,27 @@ Use anchors analog to this example
to retrieve and display extra information either in a WEB 2.0 fashion
in an popup box or on a separte page in a html fashion.
+
+Themeing
+--------
+
+Current CSS based themeing in live depends on additional stylesheets
+and a configurable location to retrieve images from (see
+css-themeing.txt).
+
+Developers must use the <& pageelems.stylesheets &> component in their
+pages to include both the default and the themed stylesheet. This is
+the easy part, because stylesheets are referred in the header at a
+central location.
+
+More difficult is the access to images, which is spread around the
+pages at the corresponding locations. To support this, a new method in
+the live Setup class (see file setup.h) has been added. It is called
+'GetThemedLink'. For every image, that might be customized, you must
+use a img tag according to this example:
+
+ <img src="<$ LiveSetup().GetThemedLink("img", "<imagename>") $>"
+ alt="someimage" />
+
+Please take a look in the existing ecpp pages for additional usage
+examples.
diff --git a/filecache.h b/filecache.h
index 5c1e83d..eaf9739 100644
--- a/filecache.h
+++ b/filecache.h
@@ -42,11 +42,11 @@ public:
ptr_type get( key_type const& key )
{
cMutexLock lock( &m_mutex );
- dsyslog( "vdrlive::FileCache::get( %s )", key.c_str() );
- dsyslog( "vdrlive::FileCache had %u entries (weight: %u)", count(), weight() );
+ // dsyslog( "vdrlive::FileCache::get( %s )", key.c_str() );
+ // dsyslog( "vdrlive::FileCache had %u entries (weight: %u)", count(), weight() );
ptr_type result = base_type::get( key );
- dsyslog( "vdrlive::FileCache now has %u entries (weight: %u)", count(), weight() );
- dsyslog( "vdrlive::FileCache::get( %s ) = %p", key.c_str(), result.get() );
+ // dsyslog( "vdrlive::FileCache now has %u entries (weight: %u)", count(), weight() );
+ // dsyslog( "vdrlive::FileCache::get( %s ) = %p", key.c_str(), result.get() );
return result;
}
diff --git a/pages/content.ecpp b/pages/content.ecpp
index 61b8100..8f3cbbb 100644
--- a/pages/content.ecpp
+++ b/pages/content.ecpp
@@ -18,10 +18,10 @@ bool logged_in(false);
string mime("image/png");
if (request.getArgsCount() > 0) {
mime = request.getArg(0);
- dsyslog("vdrlive::content found mime arg (%s)", mime.c_str());
+ // dsyslog("vdrlive::content found mime arg (%s)", mime.c_str());
}
reply.setContentType(mime);
-dsyslog("vdrlive::content::mimetype(%s)", mime.c_str());
+// dsyslog("vdrlive::content::mimetype(%s)", mime.c_str());
// FileCache::ptr_type f = LiveFileCache().get("/tmp/live/" + request.getPathInfo());
string path(request.getPathInfo());
diff --git a/pages/edit_timer.ecpp b/pages/edit_timer.ecpp
index cc51f9d..176db0e 100644
--- a/pages/edit_timer.ecpp
+++ b/pages/edit_timer.ecpp
@@ -183,7 +183,7 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<label for="wday_tue"><$ tr("Tuesday") $></label>
</div>
<div class="dotted">
- <input id="wdry_wed" type="checkbox" name="wday_wed" value="1" <{ reply.out() << ( wday_wed ? "checked=\"checked\"" : "" ); }> />
+ <input id="wday_wed" type="checkbox" name="wday_wed" value="1" <{ reply.out() << ( wday_wed ? "checked=\"checked\"" : "" ); }> />
<label for="wday_wed"><$ tr("Wednesday") $></label>
</div>
<div class="dotted">
diff --git a/pages/login.ecpp b/pages/login.ecpp
index c3863ff..70fe7d8 100644
--- a/pages/login.ecpp
+++ b/pages/login.ecpp
@@ -45,7 +45,7 @@ if (logged_in || !LiveSetup().UseAuth()) return reply.redirect(LiveSetup().GetSt
<div style="width: 350px; margin: 0 auto; text-align: center; padding: 20px">
<div class="boxheader"><div><div><$ tr("VDR Live Login") $></div></div></div>
<div style="border: 1px solid black; padding: 15px">
- <img src="logo_login.png" alt="VDR Live"></img>
+ <img src="<$ LiveSetup().GetThemedLink("img", "logo_login.png") $>" alt="VDR Live"></img>
<form name="auth">
<input type="hidden" name="action" value="login" />
<table class="login">
diff --git a/pages/pageelems.ecpp b/pages/pageelems.ecpp
index 928e7b5..c8ac171 100644
--- a/pages/pageelems.ecpp
+++ b/pages/pageelems.ecpp
@@ -24,6 +24,7 @@ using namespace vdrlive;
<%def stylesheets>
<link rel="stylesheet" type="text/css" href="styles.css"/>
+<link rel="stylesheet" type="text/css" href="<$ LiveSetup().GetThemedLink("css", "theme.css") $>"/>
<link rel="stylesheet" type="text/css" href="css/siteprefs.css"/>
</%def>
@@ -31,9 +32,8 @@ using namespace vdrlive;
<%def logo>
<div class="page_header">
-
-<a href="<$ LiveSetup().GetStartScreenLink()$>">
-<img src="logo.png" alt="VDR Live!" class="logo"></img></a>
+<a href="<$ LiveSetup().GetStartScreenLink()$>">
+<img src="<$ LiveSetup().GetThemedLink("img", "logo.png") $>" alt="VDR Live!" class="logo"></img></a>
<& infobox &>
</div>
<div style="clear: both"></div>
@@ -54,7 +54,7 @@ using namespace vdrlive;
</div>
<div class="st_controls">
<div class="st_update">
- <a href="javascript:LiveStatusToggleUpdate()" <& tooltip.hint text=(tr("Stop updates")) &>><img id="statusReloadBtn" src="stop_update.png" alt="" /></a>
+ <a href="javascript:LiveStatusToggleUpdate()" <& tooltip.hint text=(tr("Stop updates")) &>><img id="statusReloadBtn" src="<$ LiveSetup().GetThemedLink("img", "stop_update.png") $>" alt="" /></a>
</div>
<div id="infobox_recording_buttons" style="display: none">
<& ajax_action_href action=("stop_recording") id=("infobox_stop") image=("stop.png") tip=(tr("stop playback")) &>
@@ -111,10 +111,10 @@ using namespace vdrlive;
if (timer) {
</%cpp>
<a href="edit_timer.html?timerid=<$ LiveTimerManager().GetTimers().GetTimerId(*timer) $>">
- <img src="record_timer.png" alt="" <& tooltip.hint text=(tr("Edit this")) &> />
+ <img src="LiveSetup().GetThemedLink("img", "record_timer.png") $>" alt="" <& tooltip.hint text=(tr("Edit this")) &> />
<%cpp> } else { </%cpp>
<a href="edit_timer.html?channelid=<$ channelid $>&eventid=<$ eventid $>">
- <img src="record.png" alt="" <& tooltip.hint text=(tr("Record this")) &> />
+ <img src="<$ LiveSetup().GetThemedLink("img", "record.png") $>" alt="" <& tooltip.hint text=(tr("Record this")) &> />
<%cpp> } </%cpp>
</a>
</%def>
@@ -137,7 +137,7 @@ using namespace vdrlive;
string alt;
string id;
</%args>
-<%cpp> { </%cpp> <a <%cpp> if (!id.empty()) { </%cpp> id="<$ id $>" <%cpp> } </%cpp><& hide_element hide=(!id.empty()) &> href="javascript:LiveSimpleAjaxRequest('<$ action $>.xml', 'param', '<$ param $>');" <%cpp>if (!tip.empty()) { </%cpp><& tooltip.hint text=(tip) &> <%cpp> } </%cpp>><img src="<$ image $>" alt="<$ alt $>"></img></a> <%cpp> } </%cpp>
+<%cpp> { </%cpp> <a <%cpp> if (!id.empty()) { </%cpp> id="<$ id $>" <%cpp> } </%cpp><& hide_element hide=(!id.empty()) &> href="javascript:LiveSimpleAjaxRequest('<$ action $>.xml', 'param', '<$ param $>');" <%cpp>if (!tip.empty()) { </%cpp><& tooltip.hint text=(tip) &> <%cpp> } </%cpp>><img src="<$ LiveSetup().GetThemedLink("img", image) $>" alt="<$ alt $>"></img></a> <%cpp> } </%cpp>
</%def>
<# ---------------------------------------------------------------------- #>
@@ -205,7 +205,7 @@ using namespace vdrlive;
<{
Features< features::epgsearch >& epgsearch = LiveFeatures< features::epgsearch >();
}>
- <img align="center" src="<$ epgsearch.Recent() ? "" : "in" $>active.png"/> EPGsearch
+ <img align="center" src="<$ LiveSetup().GetThemedLink("img", epgsearch.Recent() ? "active.png" : "inactive.png") $>"/> EPGsearch
% if ( epgsearch.Recent() ) {
(<$ tr("active") $>: <$ epgsearch.Version() $>)
% } else {
diff --git a/pages/recordings.ecpp b/pages/recordings.ecpp
index b1b6b55..6695925 100644
--- a/pages/recordings.ecpp
+++ b/pages/recordings.ecpp
@@ -146,7 +146,7 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) {
<%args>
string archived;
</%args>
-<img src="on_dvd.png" alt="on_dvd" <& tooltip.hint text=(archived) &> />
+<img src="<$ LiveSetup().GetThemedLink("img", "on_dvd.png") $>" alt="on_dvd" <& tooltip.hint text=(archived) &> />
</%def>
<# ---------------------------------------------------------------------- #>
@@ -159,7 +159,7 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) {
string folderimg = "folder_closed.png";
</%args>
<div class="recording_item" onclick="Toggle(this)">
- <div class="recording_imgs"><%cpp> reply.out() << StringRepeat(level, "<img src=\"transparent.png\" alt=\"\" width=\"16px\" height=\"16px\" />"); </%cpp><img class="recording_expander" src="<$ collapseimg $>" alt="" /><img class="recording_folder" src="<$ folderimg $>" alt="" /></div>
+ <div class="recording_imgs"><%cpp> reply.out() << StringRepeat(level, "<img src=\"transparent.png\" alt=\"\" width=\"16px\" height=\"16px\" />"); </%cpp><img class="recording_expander" src="<$ LiveSetup().GetThemedLink("img", collapseimg) $>" alt="" /><img class="recording_folder" src="<$ LiveSetup().GetThemedLink("img", folderimg) $>" alt="" /></div>
<div class="recording_spec">
<div class="recording_name"><$ name $></div>
</div>
@@ -183,7 +183,7 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) {
string archiveId;
</%args>
<div class="recording_item">
- <div class="recording_imgs"><%cpp> reply.out() << StringRepeat(level + 1, "<img src=\"transparent.png\" alt=\"\" width=\"16px\" height=\"16px\" />"); </%cpp><%cpp> if (!archived.empty()) { </%cpp><& archived_disc archived=(archived) &><%cpp> } else { </%cpp><img src="movie.png" alt="movie" /><%cpp> } </%cpp></div>
+ <div class="recording_imgs"><%cpp> reply.out() << StringRepeat(level + 1, "<img src=\"transparent.png\" alt=\"\" width=\"16px\" height=\"16px\" />"); </%cpp><%cpp> if (!archived.empty()) { </%cpp><& archived_disc archived=(archived) &><%cpp> } else { </%cpp><img src="<$ LiveSetup().GetThemedLink("img", "movie.png") $>" alt="movie" /><%cpp> } </%cpp></div>
<div class="recording_spec">
<div class="recording_day" style="width: <$ dayLen $>"><$ day $></div>
<div class="recording_date"><$ FormatDateTime(tr("%b %d %y"), startTime) $></div>
@@ -203,8 +203,8 @@ for (iter = recordingsTree.begin(path); iter != end; ++iter) {
<%cpp>
}
</%cpp>
- <img src="edit.png" alt="" />
- <img src="del.png" alt="" />
+ <img src="<$ LiveSetup().GetThemedLink("img", "edit.png") $>" alt="" />
+ <img src="<$ LiveSetup().GetThemedLink("img", "del.png") $>" alt="" />
</div>
<%cpp>
if (! archived.empty()) {
diff --git a/pages/remote.ecpp b/pages/remote.ecpp
index 12850f0..e4a7826 100644
--- a/pages/remote.ecpp
+++ b/pages/remote.ecpp
@@ -80,11 +80,11 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<{ if (!LiveGrabImageManager().CanGrab()) { }>
bloek
<{ } else { }>
- <img src="screenshot.jpg" name="vdrlive" /><br />
+ <img src="<$ LiveSetup().GetThemedLink("img", "screenshot.jpg") $>" name="vdrlive" /><br />
<{ } }>
</div>
<div>
- <img src="remotecontrol.jpg" width="162" height="363" border="0" usemap="#remote" alt="" />
+ <img src="<$ LiveSetup().GetThemedLink("img", "remotecontrol.jpg") $>" width="162" height="363" border="0" usemap="#remote" alt="" />
<map name="remote">
<area href="#" shape="circle" coords="37,36,10" alt="Power" onclick="KeyPress(<$ kPower $>)" nohref="nohref" alt="" />
<area href="#" shape="rect" coords="27,60,59,75" alt="1" onclick="KeyPress(<$ k1 $>)" nohref="nohref" alt="" />
diff --git a/pages/schedule.ecpp b/pages/schedule.ecpp
index 5357b3d..fd5ece1 100644
--- a/pages/schedule.ecpp
+++ b/pages/schedule.ecpp
@@ -109,9 +109,9 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
<tr class="<? active_line ? "active" ?>">
<td style="border-left: 1px solid black"><& pageelems.event_timer channelid=(channel_id) eventid=(event) &>
% if ( LiveFeatures< features::epgsearch >().Recent() ) {
- <a href="searchresults.html?searchplain=<$ StringUrlEncode(title) $>"><img src="/search.png" border="0" alt="" <& tooltip.hint text=(tr("Search for repeats.")) &>></img></a>
+ <a href="searchresults.html?searchplain=<$ StringUrlEncode(title) $>"><img src="<$ LiveSetup().GetThemedLink("img", "search.png") $>" border="0" alt="" <& tooltip.hint text=(tr("Search for repeats.")) &>></img></a>
% }
- <a href="http://akas.imdb.com/Tsearch?title=<$ StringUrlEncode(title) $>"><img src="imdb.png" border="0" alt="" <& tooltip.hint text=(tr("Find more at the Internet Movie Database.")) &>></img></a>
+ <a href="http://akas.imdb.com/Tsearch?title=<$ StringUrlEncode(title) $>"><img src="<$ LiveSetup().GetThemedLink("img", "imdb.png") $>" border="0" alt="" <& tooltip.hint text=(tr("Find more at the Internet Movie Database.")) &>></img></a>
</td>
<td><$ start $> - <$ end $></td>
<td>
diff --git a/pages/searchtimers.ecpp b/pages/searchtimers.ecpp
index 4a39402..296a110 100644
--- a/pages/searchtimers.ecpp
+++ b/pages/searchtimers.ecpp
@@ -66,14 +66,14 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
}>
<tr>
<td class="border" style="border-left: 1px solid black"></td>
- <td><{ if(timer->UseAsSearchTimer()) { }><img src="arrow.png" alt=""></img><{ } }></td>
+ <td><{ if(timer->UseAsSearchTimer()) { }><img src="<$ LiveSetup().GetThemedLink("img", "arrow.png") $>" alt=""></img><{ } }></td>
<td><$ timer->Search() $></td>
<td><$ timer->ChannelText() $></td>
<td><? timer->UseTime() ? timer->StartTimeFormatted()+" - "+timer->StopTimeFormatted() ?></td>
- <td><a href="searchtimers.html?searchtimerid=<$ timer->Id() $>&action=toggle"><img src="<$ timer->UseAsSearchTimer() ? "active" : "inactive" $>.png" alt="<$ tr("Toggle search timer actions (in)active") $>" <& tooltip.hint text=(tr("Toggle search timer actions (in)active")) &>></img></a></td>
- <td><a href="searchresults.html?searchtimerid=<$ timer->Id() $>"><img src="search.png" border="0" alt="<$ tr("Browse search timer results") $>" <& tooltip.hint text=(tr("Browse search timer results")) &>></img></a></td>
- <td><a href="edit_searchtimer.html?searchtimerid=<$ timer->Id() $>"><img src="edit.png" alt="<$ tr("Edit search timer") $>" <& tooltip.hint text=(tr("Edit search timer")) &>></img></a></td>
- <td><a href="searchtimers.html?searchtimerid=<$ timer->Id() $>&action=delete" onclick="return confirm('<$ tr("Delete this search timer?") $>')"><img src="del.png" alt="<$ tr("Delete search timer") $>" <& tooltip.hint text=(tr("Delete search timer")) &>></img></a></td>
+ <td><a href="searchtimers.html?searchtimerid=<$ timer->Id() $>&action=toggle"><img src="<$ LiveSetup().GetThemedLink("img", timer->UseAsSearchTimer() ? "active.png" : "inactive.png") $>" alt="<$ tr("Toggle search timer actions (in)active") $>" <& tooltip.hint text=(tr("Toggle search timer actions (in)active")) &>></img></a></td>
+ <td><a href="searchresults.html?searchtimerid=<$ timer->Id() $>"><img src="<$ LiveSetup().GetThemedLink("img", "search.png") $>" alt="<$ tr("Browse search timer results") $>" <& tooltip.hint text=(tr("Browse search timer results")) &>></img></a></td>
+ <td><a href="edit_searchtimer.html?searchtimerid=<$ timer->Id() $>"><img src="<$ LiveSetup().GetThemedLink("img", "edit.png") $>" alt="<$ tr("Edit search timer") $>" <& tooltip.hint text=(tr("Edit search timer")) &>></img></a></td>
+ <td><a href="searchtimers.html?searchtimerid=<$ timer->Id() $>&action=delete" onclick="return confirm('<$ tr("Delete this search timer?") $>')"><img src="<$ LiveSetup().GetThemedLink("img", "del.png") $>" alt="<$ tr("Delete search timer") $>" <& tooltip.hint text=(tr("Delete search timer")) &>></img></a></td>
<td class="border" style="border-right: 1px solid black"></td>
</tr>
<{
diff --git a/pages/setup.ecpp b/pages/setup.ecpp
index b099c0f..43c0de8 100644
--- a/pages/setup.ecpp
+++ b/pages/setup.ecpp
@@ -1,4 +1,5 @@
<%pre>
+#include <vdr/tools.h>
#include "setup.h"
#include "tools.h"
@@ -13,6 +14,7 @@ using namespace std;
string pass;
string times;
string startscreen;
+ string theme;
string localnetmask;
</%args>
<%session scope="global">
@@ -37,6 +39,7 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
}
LiveSetup().SetTimes(times);
LiveSetup().SetStartScreen(startscreen);
+ LiveSetup().SetTheme(theme);
LiveSetup().SaveSetup();
}
pageTitle = tr("Setup");
@@ -50,6 +53,7 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
useauth = LiveSetup().GetUseAuth();
times = LiveSetup().GetTimes();
startscreen = LiveSetup().GetStartScreen();
+ theme = LiveSetup().GetTheme();
localnetmask = LiveSetup().GetLocalNetMask();
</%cpp>
<& pageelems.doc_type &>
@@ -130,6 +134,32 @@ if (!logged_in && LiveSetup().UseAuth()) return reply.redirect("login.html");
</tr>
<tr>
<td class="border" style="border-left: 1px solid black"></td>
+ <td class="label"><$ tr("Theme") $>:</td>
+ <td><select name="theme" size="1" id="theme">
+<%cpp>
+{
+ cReadDir d(USRDIR "/themes");
+ struct dirent* e;
+ string parent("..");
+ string current(".");
+ while ((e = d.Next())) {
+ if ((current == e->d_name) || (parent == e->d_name)) {
+ continue;
+ }
+ if (DT_DIR != e->d_type) {
+ continue;
+ }
+</%cpp>
+ <option value="<$ e->d_name $>" <{ SELECTIF(theme == e->d_name) }>><$ e->d_name $></option>
+<%cpp>
+ }
+}
+</%cpp>
+ </select></td>
+ <td class="border" style="border-right: 1px solid black"></td>
+ </tr>
+ <tr>
+ <td class="border" style="border-left: 1px solid black"></td>
<td class="buttonpanel" colspan="2">
<button class="green" type="submit" name="save" onclick="return checksearch();"><$ tr("Save") $></button>
</td>
diff --git a/pages/timers.ecpp b/pages/timers.ecpp
index dc67178..01eacc9 100644
--- a/pages/timers.ecpp
+++ b/pages/timers.ecpp
@@ -99,14 +99,14 @@ using namespace vdrlive;
</%cpp>
<tr>
<td class="border" style="border-left: 1px solid black"></td>
- <td><img src="<$ timerStateImg $>" alt=""></img></td>
+ <td><img src="<$ LiveSetup().GetThemedLink("img", timerStateImg) $>" alt=""></img></td>
<td><a href="schedule.html?channel=<$ timer->Channel()->Number()$>"><$ timer->Channel()->Name() $></a></td>
<td><$ FormatDateTime(tr("%I:%M %p"), timer->StartTime()) $></td>
<td><$ FormatDateTime(tr("%I:%M %p"), timer->StopTime()) $></td>
<td><$ timer->File() $></td>
- <td><a href="timers.html?timerid=<$ timers.GetTimerId(*timer) $>&action=toggle"><img src="<$ (timer->Flags() & tfActive) ? "active" : "inactive" $>.png" alt="" <& tooltip.hint text=(tr("Toggle timer active/inactive")) &>></img></a></td>
- <td><a href="edit_timer.html?timerid=<$ timers.GetTimerId(*timer) $>"><img src="edit.png" alt="" <& tooltip.hint text=(tr("Edit timer")) &>></img></a></td>
- <td><a href="timers.html?timerid=<$ timers.GetTimerId(*timer) $>&action=delete"><img src="del.png" alt="" <& tooltip.hint text=(tr("Delete timer")) &>></img></a></td>
+ <td><a href="timers.html?timerid=<$ timers.GetTimerId(*timer) $>&action=toggle"><img src="<$ LiveSetup().GetThemedLink("img", (timer->Flags() & tfActive) ? "active.png" : "inactive.png") $>" alt="" <& tooltip.hint text=(tr("Toggle timer active/inactive")) &>></img></a></td>
+ <td><a href="edit_timer.html?timerid=<$ timers.GetTimerId(*timer) $>"><img src="<$ LiveSetup().GetThemedLink("img", "edit.png") $>" alt="" <& tooltip.hint text=(tr("Edit timer")) &>></img></a></td>
+ <td><a href="timers.html?timerid=<$ timers.GetTimerId(*timer) $>&action=delete"><img src="<$ LiveSetup().GetThemedLink("img", "del.png") $>" alt="" <& tooltip.hint text=(tr("Delete timer")) &>></img></a></td>
<td class="border" style="border-right: 1px solid black"></td>
</tr>
<%cpp>
diff --git a/pages/tooltip.ecpp b/pages/tooltip.ecpp
index bdba57c..bd11415 100644
--- a/pages/tooltip.ecpp
+++ b/pages/tooltip.ecpp
@@ -1,3 +1,9 @@
+<%pre>
+#include "setup.h"
+
+using namespace vdrlive;
+
+</%pre>
<%def javascript>
<%args>
styleClass="domTThint";
@@ -44,12 +50,12 @@ domId;
<%args>
domId;
</%args>
- <a href="#void" onclick="domTT_close('<$ (domId + "_tip") $>')"><img src="close.png" alt="" /></a>
+ <a href="#void" onclick="domTT_close('<$ (domId + "_tip") $>')"><img src="<$ LiveSetup().GetThemedLink("img", "close.png") $>" alt="" /></a>
</%def>
<%def help>
<%args>
text;
</%args>
- <img src="help.png" onmouseover="domTT_close(domTT_lastOpened); domTT_activate(this, event, 'predefined', 'tipHint', 'content', '<$ text $>');"></img>
+ <img src="<$ LiveSetup().GetThemedLink("img", "help.png") $>" onmouseover="domTT_close(domTT_lastOpened); domTT_activate(this, event, 'predefined', 'tipHint', 'content', '<$ text $>');"></img>
</%def>
diff --git a/pages/whats_on.ecpp b/pages/whats_on.ecpp
index d537262..09bb560 100644
--- a/pages/whats_on.ecpp
+++ b/pages/whats_on.ecpp
@@ -143,9 +143,9 @@ if (type == "now") {
<& pageelems.ajax_action_href action="switch_channel" tip=(tr("Switch to this channel.")) param=(channel_id) image="zap.png" alt="" &>
% }
% if ( LiveFeatures< features::epgsearch >().Recent() ) {
- <a href="searchresults.html?searchplain=<$ StringEscapeAndBreak(epgEvent->Title()) $>"><img src="search.png" border="0" alt="" <& tooltip.hint text=(tr("Search for repeats.")) &>></img></a>
+ <a href="searchresults.html?searchplain=<$ StringEscapeAndBreak(epgEvent->Title()) $>"><img src="<$ LiveSetup().GetThemedLink("img", "search.png") $>" alt="" <& tooltip.hint text=(tr("Search for repeats.")) &>></img></a>
% }
- <a href="http://akas.imdb.com/Tsearch?title=<$ StringUrlEncode(epgEvent->Title()) $>"><img src="imdb.png" border="0" alt="" <& tooltip.hint text=(tr("Find more at the Internet Movie Database.")) &>></img></a>
+ <a href="http://akas.imdb.com/Tsearch?title=<$ StringUrlEncode(epgEvent->Title()) $>"><img src="<$ LiveSetup().GetThemedLink("img", "imdb.png") $>" alt="" <& tooltip.hint text=(tr("Find more at the Internet Movie Database.")) &>></img></a>
</div>
<div>
<div class="info"><$ (epgEvent->StartTime(tr("%I:%M %p"))) $> - <$ (epgEvent->EndTime(tr("%I:%M %p"))) $></div>
@@ -166,9 +166,9 @@ if (type == "now") {
<& pageelems.ajax_action_href action="switch_channel" tip=(tr("Switch to this channel.")) param=(channel_id) image="zap.png" alt="" &>
% }
% if ( LiveFeatures< features::epgsearch >().Recent() ) {
- <a href="searchresults.html?searchplain=<$ StringUrlEncode(epgEvent->Title()) $>"><img src="/search.png" border="0" alt="" <& tooltip.hint text=(tr("Search for repeats.")) &>></img></a>
+ <a href="searchresults.html?searchplain=<$ StringUrlEncode(epgEvent->Title()) $>"><img src="<$ LiveSetup().GetThemedLink("img", "search.png") $>" border="0" alt="" <& tooltip.hint text=(tr("Search for repeats.")) &>></img></a>
% }
- <a href="http://akas.imdb.com/Tsearch?title=<$ StringUrlEncode(epgEvent->Title()) $>"><img src="imdb.png" border="0" alt="" <& tooltip.hint text=(tr("Find more at the Internet Movie Database.")) &>></img></a>
+ <a href="http://akas.imdb.com/Tsearch?title=<$ StringUrlEncode(epgEvent->Title()) $>"><img src="<$ LiveSetup().GetThemedLink("img", "imdb.png") $>" border="0" alt="" <& tooltip.hint text=(tr("Find more at the Internet Movie Database.")) &>></img></a>
</td>
<td>
<div><$ (epgEvent->StartTime(tr("%I:%M %p"))) $> - <$ (epgEvent->EndTime(tr("%I:%M %p"))) $></div>
diff --git a/setup.cpp b/setup.cpp
index 4e9291b..8a9da16 100644
--- a/setup.cpp
+++ b/setup.cpp
@@ -77,6 +77,7 @@ bool Setup::ParseSetupEntry( char const* name, char const* value )
else if ( strcmp( name, "AdminPasswordMD5" ) == 0 ) m_adminPasswordMD5 = value;
else if ( strcmp( name, "UserdefTimes" ) == 0 ) m_times = value;
else if ( strcmp( name, "StartPage" ) == 0 ) m_startscreen = value;
+ else if ( strcmp( name, "Theme" ) == 0 ) m_theme = value;
else if ( strcmp( name, "LocalNetMask" ) == 0 ) { m_localnetmask = value; }
else if ( strcmp( name, "LastWhatsOnListMode" ) == 0 ) { m_lastwhatsonlistmode = value; }
else return false;
@@ -154,14 +155,14 @@ bool Setup::UseAuth() const
bool Setup::CheckLocalNet(const std::string& ip)
{
// split local net mask in net and range
- vector< string > parts = StringSplit( m_localnetmask, '/' );
+ vector< string > parts = StringSplit( m_localnetmask, '/' );
if (parts.size() != 2) return false;
- string net = parts[0];
+ string net = parts[0];
- int range = lexical_cast< int >(parts[1]);
+ int range = lexical_cast< int >(parts[1]);
// split net and ip addr in its 4 subcomponents
- vector< string > netparts = StringSplit( net, '.' );
- vector< string > addrparts = StringSplit( ip, '.' );
+ vector< string > netparts = StringSplit( net, '.' );
+ vector< string > addrparts = StringSplit( ip, '.' );
if (netparts.size() != 4 || addrparts.size() != 4) return false;
// to binary representation
@@ -183,7 +184,7 @@ bool Setup::CheckLocalNet(const std::string& ip)
string bin_net_range(bin_net.begin(), bin_net.begin() + range);
string addr_net_range(bin_addr.begin(), bin_addr.begin() + range);
m_islocalnet = (bin_net_range == addr_net_range);
-
+
return m_islocalnet;
}
@@ -200,6 +201,7 @@ bool Setup::SaveSetup()
}
liveplugin->SetupStore("UserdefTimes", m_times.c_str());
liveplugin->SetupStore("StartPage", m_startscreen.c_str());
+ liveplugin->SetupStore("Theme", m_theme.c_str());
liveplugin->SetupStore("LastWhatsOnListMode", m_lastwhatsonlistmode.c_str());
return true;
}
diff --git a/setup.h b/setup.h
index b4db7c1..c888dea 100644
--- a/setup.h
+++ b/setup.h
@@ -19,7 +19,7 @@ class cMenuSetupLive;
class Setup
{
friend Setup& LiveSetup();
- friend class cMenuSetupLive; // friend declaration is not forward
+ friend class cMenuSetupLive; // friend declaration is not forward
// declaration, although gcc 3.3 claims so
public:
@@ -39,10 +39,12 @@ public:
std::string GetTimes() const { return m_times; }
std::string GetStartScreen() const { return m_startscreen; }
std::string GetStartScreenLink() const;
+ std::string GetTheme() const { return m_theme; }
+ std::string GetThemedLink(const std::string& type, const std::string& name) const { return "themes/" + GetTheme() + "/" + type + "/" + name; }
std::string GetLocalNetMask() const { return m_localnetmask; };
bool GetIsLocalNet() const { return m_islocalnet; };
std::string GetLastWhatsOnListMode() const { return m_lastwhatsonlistmode; }
-
+
void SetLastChannel(int lastChannel) { m_lastChannel = lastChannel; }
void SetAdminLogin(std::string login) { m_adminLogin = login; }
std::string SetAdminPassword(std::string password);
@@ -50,16 +52,22 @@ public:
void SetScrenshotInterval(int interval) { m_screenshotInterval = interval; }
void SetTimes(std::string times) { m_times = times; }
void SetStartScreen(std::string startscreen) { m_startscreen = startscreen; }
+ void SetTheme(std::string theme) { m_theme = theme; }
void SetLocalNetMask(std::string localnetmask) { m_localnetmask = localnetmask; }
void SetIsLocalNet(bool islocalnet) { m_islocalnet = islocalnet; }
+
void SetLastWhatsOnListMode(std::string mode) { m_lastwhatsonlistmode = mode; SaveSetup(); }
+
bool SaveSetup();
bool ParseCommandLine( int argc, char* argv[] );
char const* CommandLineHelp() const;
bool ParseSetupEntry( char const* name, char const* value );
+
+ bool HaveEPGSearch(void);
bool CheckLocalNet(const std::string& ip);
+
private:
Setup();
Setup( Setup const& );
@@ -74,16 +82,17 @@ private:
// setup options
int m_lastChannel;
int m_screenshotInterval;
-
+
int m_useAuth;
std::string m_adminLogin;
std::string m_adminPasswordMD5;
std::string m_times;
std::string m_startscreen;
+ std::string m_theme;
std::string m_localnetmask;
bool m_islocalnet;
std::string m_lastwhatsonlistmode;
-
+
bool CheckServerPort();
bool CheckServerIps();
};
@@ -97,15 +106,15 @@ protected:
virtual eOSState ProcessKey(eKeys Key);
public:
cMenuSetupLive();
-
+
private:
int m_lastChannel;
int m_screenshotInterval;
-
+
int m_useAuth;
char m_adminLogin[20];
char m_adminPassword[20];
- char m_tmpPassword[20];
+ char m_tmpPassword[20];
std::string m_oldpasswordMD5;
std::string m_newpasswordMD5;
diff --git a/themes/marine/css/theme.css b/themes/marine/css/theme.css
new file mode 100644
index 0000000..6d9c293
--- /dev/null
+++ b/themes/marine/css/theme.css
@@ -0,0 +1,5 @@
+/* ##############################################
+ # This is the theme file for the marine theme.
+ # It is empty because marine theme is the default live theme.
+ ##############################################
+*/
diff --git a/themes/marine/img/zap.png b/themes/marine/img/zap.png
new file mode 100644
index 0000000..d5d8628
--- /dev/null
+++ b/themes/marine/img/zap.png
Binary files differ
diff --git a/themes/redwine/css/theme.css b/themes/redwine/css/theme.css
new file mode 100644
index 0000000..33872df
--- /dev/null
+++ b/themes/redwine/css/theme.css
@@ -0,0 +1,982 @@
+/* ######################
+ # Globals
+ ######################
+*/
+
+body {
+ margin: 0px;
+ padding: 0px;
+ font-size: 11px;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+table {
+ font-size: 11px;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ margin: 0px;
+}
+
+tr, td {
+ padding-top: 0px;
+ padding-bottom: 0px;
+}
+
+input {
+ border: 1px solid #963B5F;
+ font-size: 11px;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ background: #FEFEFE;
+ margin: 0;
+}
+
+select {
+ border: 1px solid #963B5F;
+ font-size: 11px;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+img {
+ border: 0;
+}
+
+/* ######################
+ # global style properties
+ ######################
+*/
+
+.bold {
+ font-weight: bold;
+}
+
+/* ######################
+ # Tooltip style for hints
+ ######################
+*/
+
+div.domTThint {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ border: 1px solid #EBC94C;
+ background-color: #F4FFC3;
+ max-width: 35em;
+}
+
+div.domTThint .caption {
+ font-weight: bold;
+}
+
+div.domTThint .contents {
+ padding: 2px;
+}
+
+/* ##############################
+ # Tooltip style for epg infos
+ ##############################
+*/
+
+div.domTTepg {
+ width: 66%;
+ border: none;
+}
+
+.domTTepg div.epg_description {
+}
+
+.domTTepg div.epg_content {
+ padding: 0;
+ margin: 0;
+
+ border-left: 1px solid #000000;
+ border-right: 1px solid #000000;
+ border-bottom: 1px solid #000000;
+ background: white url(../img/bg_tools.png) top left repeat-y;
+}
+
+.domTTepg div.epg_content div.epg_tools {
+ float: left;
+ width: 26px;
+ margin: 0;
+ padding: 0;
+
+ text-align: center;
+ vertical-align: top;
+}
+
+.domTTepg div.epg_content div div.progress div {
+ padding-left: 0px;
+}
+
+.domTTepg div.epg_content div div {
+ padding-left: 35px;
+}
+
+.domTTepg div.epg_content div.epg_tools img {
+ margin-top: 5px;
+}
+
+.domTTepg div.boxheader div div a {
+}
+
+/* #######################
+ # Menue
+ #######################
+*/
+
+div.menu {
+ background: #000057 url(../img/menu_line_bg.png) repeat-x;
+ min-height: 27px;
+ margin: 0;
+ padding: 0 0 0 10px;
+ line-height: 20px;
+ vertical-align: middle;
+ border-top: 1px solid black;
+ border-bottom: 1px solid black;
+ color: #FFE9FA;
+}
+
+div.menu a {
+ text-decoration: none;
+ color: white;
+ font-weight: bold;
+}
+
+a#login {
+ color: #F5FF2D;
+}
+
+div.menu a.active {
+ text-decoration: none;
+ color: #FFDB88;
+ font-weight: bold;
+}
+
+
+div.menu a:hover {
+ text-decoration: underline;
+}
+
+div.menu form {
+ display: inline;
+}
+
+div.inhalt {
+ height: 200px;
+ width: 100%
+ overflow: auto;
+ padding: 10px;
+}
+
+div.pagemenu {
+ margin-top: 2px;
+ padding-top: 6px;
+ background: #FFFFFF url(../img/bg_line.png) top repeat-x;
+}
+
+div.pagemenu div {
+ padding-bottom: 6px;
+ background: #FFFFFF url(../img/bg_line_top.png) bottom repeat-x;
+}
+
+div.pagemenu div div {
+ padding: 2px 0px 2px 10px;
+ background: #FFE9FA;
+ border-top: 1px solid #DA8DA8;
+ border-bottom: 1px solid #DA8DA8;
+}
+
+div.pagemenu a {
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+}
+
+div.pagemenu a.active {
+ text-decoration: none;
+ color: #984E79;
+ font-weight: bold;
+}
+div.pagemenu span {
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+ padding: 5px;
+}
+
+div.pagemenu span.sep {
+ text-decoration: none;
+ color: #DA8DA8;
+ font-weight: lighter;
+ padding: 5px;
+}
+
+/* #######################
+ # Info Box (near logo)
+ #######################
+*/
+img.logo {
+ float: left;
+ margin-top: 5px;
+ margin-left: 5px;
+ margin-right: 25px;
+}
+
+div#infobox {
+ float: left;
+ border: 1px solid #DA8DA8;
+ margin: 5px;
+ width: 320px;
+}
+
+div#infobox div.st_header {
+ overflow: hidden;
+ padding: 1px 4px;
+ background: #FFE9FA;
+ border-bottom: 1px solid #DA8DA8;
+}
+
+div#infobox div.st_header div.now {
+ float: right;
+}
+
+div#infobox div.st_header div.caption {
+ float: left;
+ overflow: hidden;
+ font-weight: bold;
+}
+
+div#infobox div.st_content {
+ overflow: hidden;
+ padding: 4px;
+ background: white url(../img/bg_line_top.png) top left repeat-x;
+}
+
+div#infobox div.st_content div.duration {
+ float: right;
+}
+
+div#infobox div.st_content div.name {
+ float: left;
+ overflow: hidden;
+ font-weight: bold;
+}
+
+div#infobox div.st_controls {
+ overflow: hidden;
+ padding: 4px;
+}
+
+div#infobox div.st_controls div.st_pbar {
+ padding-top: 4px;
+ float: right;
+}
+
+div#infobox div.st_controls div {
+ float: left;
+}
+
+div#infobox div.st_controls div.st_update {
+ padding-right: 5px;
+ border-right: 1px solid #DA8DA8;
+}
+
+div#infobox div.st_controls div#infobox_recording_buttons {
+ padding-left: 5px;
+}
+
+div#infobox div.st_controls div#infobox_channel_buttons {
+ padding-left: 5px;
+}
+
+/* #######################
+ # Head Box
+ #######################
+*/
+
+div.head_box_l {
+ background-image: url(../img/bg_header_l.png);
+ background-position: top left;
+ background-repeat: no-repeat;
+ margin: 0;
+ margin-bottom: 2ex;
+ padding: 0;
+}
+
+div.head_box_m {
+ background-image: url(../img/bg_header_h.png);
+ background-repeat: repeat-x;
+ margin: 3px;
+ padding: 0;
+}
+
+div.head_box_r {
+ background-image: url(../img/bg_header_r.png);
+ background-position: top right;
+ background-repeat: no-repeat;
+ margin: -3px;
+ padding: 0;
+ padding-left: 0.5em;
+}
+
+table.head_box_text {
+ color: #ffffff;
+ font-weight: bold;
+ padding: 0;
+ margin: 0;
+ height: 30px;
+}
+
+button.smallbutton {
+ width: 51px;
+ height: 17px;
+ background-color: inherit;
+ background-image: url(../img/button_blue.png);
+ background-repeat: no-repeat;
+ color: #FFFFFF;
+ font-size: 11px;
+ border: 0px;
+ vertical-align: middle;
+ text-align: center;
+ cursor: pointer;
+}
+
+button.green {
+ width: 100px;
+ height: 20px;
+ background-color: inherit;
+ background-image: url(../img/button_green.png);
+ background-repeat: no-repeat;
+ color: #FFFFFF;
+ font-size: 11px;
+ border: 0px;
+ vertical-align: middle;
+ text-align: center;
+ cursor: pointer;
+ padding-bottom: 3px;
+}
+
+button.red {
+ width: 100px;
+ height: 20px;
+ background-color: inherit;
+ background-image: url(../img/button_red.png);
+ background-repeat: no-repeat;
+ color: #FFFFFF;
+ font-size: 11px;
+ border: 0px;
+ vertical-align: middle;
+ text-align: center;
+ cursor: pointer;
+ padding-bottom: 3px;
+}
+
+button.blue {
+ width: 100px;
+ height: 20px;
+ background-color: inherit;
+ background-image: url(../img/button_blue.png);
+ background-repeat: no-repeat;
+ color: #FFFFFF;
+ font-size: 11px;
+ border: 0px;
+ vertical-align: middle;
+ text-align: center;
+ cursor: pointer;
+ padding-bottom: 3px;
+}
+
+table td.buttonpanel {
+ text-align: right;
+}
+
+/* ################
+ # Event
+ ################
+*/
+
+div.event {
+ width: 255px;
+ height: 255px;
+ padding: 0;
+ margin-right: 5px;
+ float: left;
+}
+
+div.event div.station {
+ margin: 0;
+ padding: 0;
+ width: 255px;
+}
+
+div.station div {
+ margin: 0;
+ padding: 0;
+ background: url(../img/bg_box_l.png) top left no-repeat;
+ height: 23px;
+}
+
+div.station div div {
+ background: url(../img/bg_box_r.png) top right no-repeat;
+}
+
+div.station div div div {
+ background: url(../img/bg_box_h.png) repeat-x;
+ vertical-align: middle;
+ text-align: left;
+ margin-right: 3px;
+ margin-left: 3px;
+ padding-left: 5px;
+ padding-top: 2px;
+ color: white;
+ font-weight: bold;
+}
+
+div.station div div div a {
+ color: #ffffff;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+div.station div div div a:hover {
+ text-decoration: underline;
+}
+
+td div.station {
+ vertical-align: middle;
+}
+
+td div.station a {
+ color: black;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+td div.station a:hover {
+ text-decoration: underline;
+}
+
+div.content {
+ width: 253px;
+ height: 220px;
+ padding: 0;
+ margin: 0;
+
+ background: white url(../img/bg_tools.png) top left repeat-y;
+ border-left: 1px solid #000000;
+ border-right: 1px solid #000000;
+ border-bottom: 1px solid #000000;
+}
+
+div.content div.tools {
+ float: left;
+ width: 26px;
+ height: 220px;
+ margin: 0;
+ padding: 0;
+
+ text-align: center;
+ vertical-align: top;
+}
+
+div.content div.tools img {
+ margin: 5px 2px;
+ border: 0px;
+}
+
+div.content div {
+ padding-left: 15px;
+}
+
+div.description {
+ margin: 5px;
+}
+
+div.info {
+ text-align: right;
+ padding: 5px;
+}
+
+div.progress {
+ overflow: hidden;
+ padding-right: 4px;
+}
+
+div.progress div {
+ float: right;
+ padding: 0px;
+}
+
+div.title {
+ font-weight: bold;
+ margin: 5px;
+}
+
+div.short {
+ font-weight: normal;
+ margin: 5px;
+}
+
+div.more {
+ margin: 5px;
+ font-weight: bold;
+ cursor: pointer;
+}
+
+div.__progress {
+ overflow: hidden;
+ width: 100px;
+ height: 8px;
+ border: 1px solid #DA8DA8;
+}
+
+div.__progress div.__elapsed {
+ float: left;
+ height: 8px;
+ background-color: #FFE9FA;
+}
+
+
+/* #############
+ # Timers
+ #############
+*/
+
+table.timers {
+ padding: 0;
+ margin: 0;
+ margin-top: 10px;
+}
+
+table.timers tr td {
+ padding: 3px 7px 3px 3px;
+ background: url(../img/bg_line.png) bottom repeat-x;
+ border-bottom: 1px solid #DA8DA8;
+}
+
+table.timers td.border {
+ padding: 0;
+ margin: 0;
+ width: 1px;
+}
+
+table.timers tr.head td {
+ color: white;
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+ border-bottom: 1px solid black;
+}
+
+table.timers tr.description td {
+ font-weight: bold;
+ background: #FFE9FA;
+}
+
+table.timers a {
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+}
+
+
+/*
+ ##############################
+ # Schedule
+ ##############################
+*/
+
+table.schedule {
+ margin: 10px 0 0 0 ;
+ padding: 0;
+}
+
+table.schedule tr td.head {
+ background: #963B5F;
+ color: white;
+ font-weight: bold;
+ margin: 0;
+ padding: 3px;
+}
+
+table.schedule tr td {
+ vertical-align: top;
+ padding: 3px 7px 3px 3px;
+ background: url(../img/bg_line.png) bottom repeat-x;
+ border-bottom: 1px solid #DA8DA8;
+}
+
+table.schedule tr td.day {
+ vertical-align: top;
+ padding: 0;
+ margin: 0;
+ border:none;
+}
+
+table.schedule tr.active {
+ background: #DEE6EE;
+}
+
+table.schedule div.more {
+ margin: 0px;
+ font-weight: bold;
+ cursor: pointer;
+}
+
+/*
+ ##############################
+ # Blue Background Thingy
+ ##############################
+*/
+
+div.boxheader {
+ margin: 0;
+ padding: 0;
+ background: url(../img/bg_box_l.png) top left no-repeat;
+}
+
+div.boxheader div {
+ background: url(../img/bg_box_r.png) top right no-repeat;
+}
+
+div.boxheader div div {
+ background: url(../img/bg_box_h.png) repeat-x;
+ vertical-align: middle;
+ text-align: left;
+ margin-right: 3px;
+ margin-left: 3px;
+ padding-left: 5px;
+ padding-top: 2px;
+ color: white;
+ font-weight: bold;
+ height: 21px;
+ line-height: 20px;
+}
+
+/*
+ ##############################
+ # Recordings
+ ##############################
+*/
+
+div.recordings {
+ border: 1px solid black;
+}
+
+.recordings ul {
+ list-style-type: none;
+ padding: 0px;
+ margin: 0px;
+}
+
+div.recording_item {
+ overflow: hidden;
+ background: url(../img/bg_line.png) bottom repeat-x;
+ border-bottom: 1px solid #DA8DA8;
+}
+
+.recording_item div {
+ float: left;
+}
+
+.recording_item div.recording_imgs{
+ margin-right: 0.5em;
+}
+
+.recording_item div.recording_spec {
+ padding-top: 0.5ex;
+}
+
+.recording_item div.recording_day {
+}
+
+.recording_item div.recording_date {
+ width: 5.25em;
+}
+
+.recording_item div.recording_time {
+ width: 5.75em;
+}
+
+.recording_item div.recording_name {
+ font-weight: bold;
+ cursor: pointer;
+}
+
+.recording_item div.recording_name span {
+ font-weight: normal;
+ cursor: pointer;
+}
+
+.recording_item div.recording_arch {
+ float: right;
+ padding-top: 0.5ex;
+ padding-left: 0.5em;
+ padding-right: 0.5em;
+}
+
+.recording_item div.recording_actions {
+ float: right;
+ padding-right: 3em;
+}
+
+/*
+ ##############################
+ # Remote Control Keypad
+ ##############################
+*/
+
+div.screenshot {
+ background-image: url(../img/tv.jpg);
+ background-repeat: no-repeat;
+ height: 240px;
+ width: 320px;
+ float: left;
+ padding: 20px 20px 98px 21px;
+ margin-right: 20px;
+}
+
+/*
+ ##############################
+ # Dotted Frame
+ ##############################
+*/
+
+div.dotted {
+ border: 1px dotted #bbbbbb;
+ padding: 3px;
+ margin: 2px;
+ float: left;
+ background-color: #f3f3f3;
+}
+
+
+/*
+ ##############################
+ # Error widget
+ ##############################
+*/
+
+table.error {
+ border: 1px solid #E9360D;
+ margin: 2px;
+ padding: 0px;
+}
+
+table.error tr td.message {
+ padding: 5px;
+}
+
+table.error tr td.title {
+ background: #E9360D;
+ color: white;
+ font-weight: bold;
+ margin: 0;
+ padding: 3px 3px 3px 10px;
+}
+
+table.error td.border {
+ padding: 0;
+ margin: 0;
+ width: 1px;
+}
+
+
+/*
+ ##############################
+ # Edit Tables
+ ##############################
+*/
+
+table.edit {
+ margin-top: 10px;
+}
+
+table.edit tr td.head {
+ background: white;
+ color: white;
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+ border:none;
+}
+
+table.edit tr td {
+ vertical-align: top;
+ padding: 6px 7px 6px 3px;
+ vertical-align: middle;
+ background: url(../img/bg_line.png) bottom repeat-x;
+ border-bottom: 1px solid #DA8DA8;
+}
+
+table.edit tr td.label {
+ font-weight: bold;
+ vertical-align: top;
+}
+
+table.edit tr.active {
+ background: #DEE6EE;
+}
+
+table.edit td.blank {
+ background: none;
+ border: none;
+}
+
+table.dependent {
+ background-color: #DEE6EE;
+ margin-top: 10px;
+}
+
+table.dependent tr td {
+ background: none;
+ vertical-align: middle;
+}
+
+table.dependent tr td.title {
+ background: none;
+ vertical-align: top;
+}
+
+
+div.dependent {
+ background-color: #DEE6EE;
+ margin-top: 10px;
+ padding: 6px 7px 6px 3px;
+}
+
+/*
+ ##############################
+ # Search results
+ ##############################
+*/
+
+table.searchresults {
+ margin: 10px 0 0 0;
+ padding: 0;
+}
+
+table.searchresults tr td.head {
+ background: #963B5F;
+ color: white;
+ font-weight: bold;
+ margin: 0;
+ padding: 3px;
+}
+
+table.searchresults tr td {
+ vertical-align: top;
+ padding: 3px 7px 7px 3px;
+ background: url(../img/bg_line.png) bottom repeat-x;
+ border-bottom: 1px solid #DA8DA8;
+}
+
+table.searchresults tr td.day {
+ vertical-align: top;
+ padding: 0;
+ margin: 0;
+ border:none;
+}
+
+table.searchresults td.border {
+ padding: 0;
+ margin: 0;
+ width: 1px;
+}
+
+table.searchresults div.more {
+ margin: 0px;
+ font-weight: bold;
+ cursor: pointer;
+}
+
+table.searchresults a {
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+}
+/*
+ ##############################
+ # Login
+ ##############################
+*/
+
+table.login {
+ margin: 0 auto;
+}
+
+table.login tr td {
+ padding: 3px 5px;
+ text-align: right;
+}
+
+/* ##############################
+ # About box
+ ##############################
+*/
+
+div.about_box {
+ border: none;
+}
+
+div.about_box a {
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+}
+
+div#aboutBox_tip {
+ width: 45%;
+}
+
+.about_box div.about_description {
+}
+
+.about_box div.about_content {
+ padding: 0;
+ margin: 0;
+
+ border-left: 1px solid #000000;
+ border-right: 1px solid #000000;
+ border-bottom: 1px solid #000000;
+}
+
+.about_box div.about_content div {
+ background-color: white;
+ padding-bottom: 6px;
+}
+
+.about_box div.boxheader div div a {
+}
+
+.about_box div.about_content div.about_left {
+ text-align: right;
+ float: left;
+ width: 175px;
+}
+
+.about_box div.about_content div.about_right {
+ padding-left: 200px;
+}
+
+.about_box div.about_content div.about_line {
+ padding-left: 10px;
+}
+
+.about_box div.about_content div.about_head {
+ font-weight: bold;
+ margin-top: 0px;
+ padding-top: 6px;
+ margin-bottom: 6px;
+ background: #FFFFFF url(../img/bg_line.png) top repeat-x;
+}
+
+.about_box div.about_content div.about_head div {
+ padding-bottom: 6px;
+ background: #FFFFFF url(../img/bg_line_top.png) bottom repeat-x;
+}
+
+.about_box div.about_content div.about_head div div {
+ padding: 2px 0px 2px 10px;
+ background: #FFE9FA;
+ border-top: 1px solid #DA8DA8;
+ border-bottom: 1px solid #DA8DA8;
+}
diff --git a/themes/redwine/img/bg_box_h.png b/themes/redwine/img/bg_box_h.png
new file mode 100644
index 0000000..7763272
--- /dev/null
+++ b/themes/redwine/img/bg_box_h.png
Binary files differ
diff --git a/themes/redwine/img/bg_box_l.png b/themes/redwine/img/bg_box_l.png
new file mode 100644
index 0000000..e3d7dd8
--- /dev/null
+++ b/themes/redwine/img/bg_box_l.png
Binary files differ
diff --git a/themes/redwine/img/bg_box_r.png b/themes/redwine/img/bg_box_r.png
new file mode 100644
index 0000000..e1c72d5
--- /dev/null
+++ b/themes/redwine/img/bg_box_r.png
Binary files differ
diff --git a/themes/redwine/img/bg_header_h.png b/themes/redwine/img/bg_header_h.png
new file mode 100644
index 0000000..aaf8d6d
--- /dev/null
+++ b/themes/redwine/img/bg_header_h.png
Binary files differ
diff --git a/themes/redwine/img/bg_header_l.png b/themes/redwine/img/bg_header_l.png
new file mode 100644
index 0000000..e49a7e9
--- /dev/null
+++ b/themes/redwine/img/bg_header_l.png
Binary files differ
diff --git a/themes/redwine/img/bg_header_r.png b/themes/redwine/img/bg_header_r.png
new file mode 100644
index 0000000..f121aa8
--- /dev/null
+++ b/themes/redwine/img/bg_header_r.png
Binary files differ
diff --git a/themes/redwine/img/bg_line.png b/themes/redwine/img/bg_line.png
new file mode 100644
index 0000000..50e358e
--- /dev/null
+++ b/themes/redwine/img/bg_line.png
Binary files differ
diff --git a/themes/redwine/img/bg_line_top.png b/themes/redwine/img/bg_line_top.png
new file mode 100644
index 0000000..59f2ec0
--- /dev/null
+++ b/themes/redwine/img/bg_line_top.png
Binary files differ
diff --git a/themes/redwine/img/bg_tools.png b/themes/redwine/img/bg_tools.png
new file mode 100644
index 0000000..1457e1d
--- /dev/null
+++ b/themes/redwine/img/bg_tools.png
Binary files differ
diff --git a/themes/redwine/img/menu_line_bg.png b/themes/redwine/img/menu_line_bg.png
new file mode 100644
index 0000000..a942e6f
--- /dev/null
+++ b/themes/redwine/img/menu_line_bg.png
Binary files differ
diff --git a/themes/redwine/img/zap.png b/themes/redwine/img/zap.png
new file mode 100644
index 0000000..d5d8628
--- /dev/null
+++ b/themes/redwine/img/zap.png
Binary files differ
diff --git a/tntconfig.cpp b/tntconfig.cpp
index 6a6e576..e417de4 100644
--- a/tntconfig.cpp
+++ b/tntconfig.cpp
@@ -35,7 +35,18 @@ void TntConfig::WriteConfig()
// XXX modularize
file << "MapUrl ^/$ login@" << endl;
- file << "MapUrl /css.*/(.+) content@ css/$1 text/css" << endl;
+ file << "MapUrl ^/themes/([^/]*)/css.*/(.+\\.css) content@ themes/$1/css/$2 text/css" << endl;
+
+ // the following rules provide a search scheme for images. The first
+ // rule where a image is found, terminates the search.
+ // 1. /themes/<theme>/img/<imgname>.<ext>
+ // 2. /dist/img/<imgname>.<ext>
+ // 3. <imgname>.<ext> (builtin images)
+ file << "MapUrl ^/themes/([^/]*)/img.*/(.+)\\.(.+) content@ themes/$1/img/$2.$3 image/$3" << endl;
+ file << "MapUrl ^/themes/([^/]*)/img.*/(.+)\\.(.+) content@ dist/img/$2.$3 image/$3" << endl;
+ file << "MapUrl ^/themes/([^/]*)/img.*/(.+)\\.(.+) $2@" << endl;
+
+ file << "MapUrl ^/css.*/(.+) content@ css/$1 text/css" << endl;
file << "MapUrl /([^/]+/.+) content@ $1" << endl;
file << "MapUrl /([^.]+)(\\..+)? $1@" << endl;
file << "PropertyFile " << m_propertiesPath << endl;