/* Copyright (C) 2005 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "cdbrowser.h"
#include <string>
#include <QSettings>
#include <libupnpp/upnpavutils.hxx>
#include "HelperStructs/CSettingsStorage.h"
#include "HelperStructs/Style.h"
#include "upadapt/upputils.h"
using namespace std;
using namespace UPnPClient;
using namespace UPnPP;
// Notes for WebEngine
// - All links must begin with http:// for acceptNavigationRequest to be
// called.
// - The links passed to acceptNav.. have the host part
// lowercased -> we change S0 to http://h/S0, not http://S0
// The const part + the js we need for webengine. This is logically
// const after initialization by the first constructor.
static QString html_top_js;
// The const part + the js + changeable style, computed as needed.
static QString html_top;
// Initial post-header content of the servers page
static const QString empty_server_page_bot(
"</head><body>"
"<h2 id=\"cdstitle\">Content Directory Services</h2>"
"</body></html>"
);
static const QString init_server_page_bot(
"</head><body>"
"<p>Looking for servers...</p>"
"</body></html>"
);
// Mark for the beginning of titles (used with search() to jump to
// first title with given initial). Wanted to use htab, but this is
// stripped in the final html. Apparently \n is not. We need a mark,
// else the search for the first title with given initial could match
// anywhere, not just at the beginning of an entry.
const string CDBrowser::CTTitleStartMarker("\n");
void CDBrowser::init_HTML()
{
#ifdef USING_WEBENGINE
if (html_top_js.isEmpty()) {
QLoggingCategory("js").setEnabled(QtDebugMsg, true);
#if !defined(Q_OS_MACOS) && !defined(Q_OS_MAC)
QString jsfn = Helper::getSharePath() + "/cdbrowser/containerscript.js";
#else
QString jsfn = Helper::getSharePath() + "/Resources/containerscript.js";
#endif
QString js = "<script type=\"text/javascript\">\n";
js += QString::fromUtf8(Helper::readFileToByteArray(jsfn));
js += "</script>\n";
html_top_js = html_top + js;
}
#else
html_top_js = html_top;
#endif
setStyleSheet(CSettingsStorage::getInstance()->getPlayerStyle(), false);
}
// Insert style from on-disk config data to our static constant
// top-of-page. Computes html_top from the constant html_top_js
void CDBrowser::setStyleSheet(bool dark, bool redisplay)
{
QString cssfn = Helper::getCSSPath() + "cdbrowser.css";
QString cssdata = QString::fromUtf8(Helper::readFileToByteArray(cssfn));
float multiplier = QSettings().value("wholeuiscale").toFloat();
if (multiplier == 0)
multiplier = 1.0;
cssdata = u8s2qs(Style::scale_fonts(qs2utf8s(cssdata), multiplier));
if (dark) {
cssfn = Helper::getCSSPath() + "dark.css";
} else {
cssfn = Helper::getCSSPath() + "standard.css";
}
cssdata += Helper::readFileToByteArray(cssfn);
html_top = html_top_js;
html_top += "<style>\n";
html_top += cssdata;
html_top += "</style>\n";
if (redisplay) {
if (m_curpath.size()) {
curpathClicked(m_curpath.size() - 1);
} else {
initialPage(true);
}
}
}
// With message "looking for servers"
QString CDBrowser::initialServersPage()
{
return QString(html_top + init_server_page_bot);
}
QString CDBrowser::emptyServersPage()
{
return QString(html_top + empty_server_page_bot);
}
QString CDBrowser::DSToHtml(unsigned int idx, const UPnPDeviceDesc& dev)
{
QString out;
out += QString("<p class=\"cdserver\" cdsid=\"%1\">").arg(idx);
out += QString("<a href=\"http://h/S%1\">").arg(idx);
out += QString::fromUtf8(dev.friendlyName.c_str());
out += QString("</a></p>");
return out;
}
QString CDBrowser::CTToHtml(unsigned int idx, const UPnPDirObject& e)
{
bool isPlaylist = !e.getprop("upnp:class").compare(plContainerClass);
QString out;
out += QString("<tr class=\"container\" objid=\"%1\" objidx=\"%2\" "
"ispl=\"%3\"><td></td><td>")
.arg(e.m_id.c_str()).arg(idx).arg(isPlaylist);
out += QString("<a class=\"ct_title\" href=\"http://h/C%1\">").arg(idx);
out += u8s2qs(CTTitleStartMarker + Helper::escapeHtml(e.m_title));
out += "</a></td>";
string val;
e.getprop("upnp:artist", val);
QSettings settings;
if (!val.empty() && settings.value("showartwithalb").toBool()) {
int maxlen = settings.value("artwithalblen").toInt();
if (maxlen && int(val.size()) > maxlen) {
int len = maxlen-3 >= 0 ? maxlen-3 : 0;
val = val.substr(0,len) + "...";
}
out += "<td class=\"ct_artist\">";
out += QString::fromUtf8(Helper::escapeHtml(val).c_str());
out += "</td>";
}
out += "</tr>";
return out;
}
/** @arg idx index in entries array
@arg e array entry
*/
QString CDBrowser::ItemToHtml(unsigned int idx, const UPnPDirObject& e,
int maxartlen)
{
QString out;
string val;
out = QString("<tr class=\"item\" objid=\"%1\" objidx=\"%2\">").
arg(e.m_id.c_str()).arg(idx);
e.getprop("upnp:originalTrackNumber", val);
out += QString("<td class=\"tk_tracknum\">");
if (val.size() == 0) {
out += " ";
} else {
out += QString::fromUtf8(Helper::escapeHtml(val).c_str());
}
out += "</td>";
out += "<td class=\"tk_title\">";
if (e.m_title.size() == 0) {
out += " ";
} else {
out += QString("<a href=\"http://h/I%1\">").arg(idx);
out += QString::fromUtf8(Helper::escapeHtml(e.m_title).c_str());
out += "</a>";
}
out += "</td>";
val.clear();
e.getprop("upnp:artist", val);
out += "<td class=\"tk_artist\">";
if (maxartlen > 0 && int(val.size()) > maxartlen) {
int len = maxartlen-3 >= 0 ? maxartlen-3 : 0;
val = val.substr(0,len) + "...";
}
if (val.size() == 0) {
out += " ";
} else {
out += QString::fromUtf8(Helper::escapeHtml(val).c_str());
}
out += "</td>";
val.clear();
e.getprop("upnp:album", val);
out += "<td class=\"tk_album\">";
out += QString::fromUtf8(Helper::escapeHtml(val).c_str());
out += "</td>";
val.clear();
e.getrprop(0, "duration", val);
int seconds = upnpdurationtos(val);
// Format as mm:ss
int mins = seconds / 60;
int secs = seconds % 60;
char sdur[100];
sprintf(sdur, "%02d:%02d", mins, secs);
out += "<td class=\"tk_duration\">";
out += sdur;
out += "</td>";
out += "</tr>";
return out;
}
static const QString init_container_pagemid = QString::fromUtf8(
#ifdef USING_WEBENGINE
"</head><body onload=\"addEventListener('contextmenu', saveLoc)\">\n"
#else
"</head><body>\n"
#endif
);
static const QString init_container_pagebot = QString::fromUtf8(
"<table id=\"entrylist\">"
"<colgroup>"
"<col class=\"coltracknumber\">"
"<col class=\"coltitle\">"
"<col class=\"colartist\">"
"<col class=\"colalbum\">"
"<col class=\"colduration\">"
"</colgroup>"
);
QString CDBrowser::alphalinks(const string& initials)
{
QString html = QString("<div id='alphalist'>");
html += "<br clear='all'>";
for (unsigned int i = 0; i < initials.size(); i++) {
QString letter(1, initials[i]);
html += QString("<a href='http://h/a") + letter +
QString("'>") + letter + QString("</a>");
}
html += QString("</div>");
return html;
}
void CDBrowser::initContainerHtml(const string& ss)
{
LOGDEB1("CDBrowser::initContainerHtml\n");
QString htmlpath("<div id=\"fixedheader\"><div id=\"browsepath\"><ul>");
bool current_is_search = false;
for (unsigned i = 0; i < m_curpath.size(); i++) {
CtPathElt& e{m_curpath[i]};
QString sep(">");
if (!e.searchStr.empty()) {
if (current_is_search) {
// Indicate that searches are not nested by changing
// the separator
sep = "<>";
}
current_is_search = true;
}
if (i == 0)
sep = "";
htmlpath += QString("<li class=\"container\" objid=\"%3\" ispl=\"%5\">"
" %4 <a href=\"http://h/L%1\">%2</a></li>").
arg(i).arg(u8s2qs(e.title)).arg(u8s2qs(e.objid)).arg(sep)
.arg(e.isPlaylist);
}
htmlpath += QString("</ul></div>");
htmlpath += alphalinks("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
htmlpath += QString("</div><br clear=\"all\"/>");
if (!ss.empty()) {
htmlpath += QString("Search results for: ") +
QString::fromUtf8(ss.c_str()) + "<br/>";
}
QString html = html_top + init_container_pagemid +
htmlpath + init_container_pagebot;
LOGDEB1("initContainerHtml: initial content: " << qs2utf8s(html) << endl);
mySetHtml(html);
LOGDEB1("CDBrowser::initContainerHtml done\n");
}