/* Neutrino-GUI - DBoxII-Project UPnP Browser (c) 2007 by Jochen Friedrich (c) 2009-2011,2016 Stefan Seyfried (c) 2016 Thilo Graf License: GPL 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, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern cVideo * videoDecoder; extern CPictureViewer * g_PicViewer; const struct button_label RescanButton = {NEUTRINO_ICON_BUTTON_BLUE , LOCALE_UPNPBROWSER_RESCAN}; const struct button_label BrowseButtons[] = { { NEUTRINO_ICON_BUTTON_RED , LOCALE_FILEBROWSER_NEXTPAGE }, { NEUTRINO_ICON_BUTTON_GREEN , LOCALE_FILEBROWSER_PREVPAGE }, { NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_AUDIOPLAYER_STOP }, { NEUTRINO_ICON_BUTTON_OKAY , LOCALE_AUDIOPLAYER_PLAY }, { NEUTRINO_ICON_BUTTON_HOME , LOCALE_MENU_BACK, } }; CUpnpBrowserGui::CUpnpBrowserGui() { m_socket = new CUPnPSocket(); m_frameBuffer = CFrameBuffer::getInstance(); m_playing_entry_is_shown = false; Init(); dline = NULL; image = NULL; sigc::slot0 reinit = sigc::mem_fun(this, &CUpnpBrowserGui::Init); CNeutrinoApp::getInstance()->OnAfterSetupFonts.connect(reinit); CFrameBuffer::getInstance()->OnAfterSetPallette.connect(reinit); } void CUpnpBrowserGui::Init() { topbox.enableFrame(true, 2); topbox.setCorner(RADIUS_LARGE); topbox.setColorAll(COL_MENUCONTENT_PLUS_6, COL_MENUHEAD_PLUS_0, COL_MENUCONTENTDARK_PLUS_0, COL_MENUHEAD_TEXT); topbox.setTextFont(g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]); topbox.enableColBodyGradient(g_settings.theme.menu_Head_gradient, COL_SHADOW_PLUS_0, g_settings.theme.menu_Head_gradient_direction); ibox.enableFrame(true, 2); ibox.setCorner(RADIUS_LARGE); ibox.setColorAll(topbox.getColorFrame(), COL_MENUCONTENTDARK_PLUS_0); ibox.setTextFont(g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMLARGE]); ibox.enableColBodyGradient(g_settings.theme.menu_Hint_gradient, COL_SHADOW_PLUS_0, g_settings.theme.menu_Hint_gradient_direction); timebox.enableFrame(true, 2); timebox.setCorner(RADIUS_LARGE); timebox.setColorAll(ibox.getColorFrame(), ibox.getColorBody()); timebox.setTextFont(g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_ITEMLARGE]); timebox.enableColBodyGradient(g_settings.theme.menu_Hint_gradient, COL_SHADOW_PLUS_0, g_settings.theme.menu_Hint_gradient_direction); m_width = m_frameBuffer->getScreenWidthRel(); m_height = m_frameBuffer->getScreenHeightRel(); m_sheight = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight(); m_theight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight(); m_buttonHeight = m_theight; m_mheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight(); m_fheight = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->getHeight(); m_title_height = m_mheight*2 + 20 + m_sheight + 4; m_info_height = m_mheight*2; m_listmaxshow = (m_height - m_info_height - m_title_height - m_theight - 2*m_buttonHeight) / (m_fheight); m_height = m_theight + m_info_height + m_title_height + 2*m_buttonHeight + m_listmaxshow * m_fheight; // recalc height footer.setHeight(m_buttonHeight); m_x=getScreenStartX(m_width); if (m_x < ConnectLineBox_Width) m_x = ConnectLineBox_Width; m_y=getScreenStartY(m_height); } CUpnpBrowserGui::~CUpnpBrowserGui() { delete m_socket; if (dline){ delete dline; dline = NULL; } if (image) delete image, image = NULL; } int CUpnpBrowserGui::exec(CMenuTarget* parent, const std::string & /*actionKey*/) { CAudioPlayer::getInstance()->init(); if (parent) parent->hide(); /* stop playback, disable playback start */ CNeutrinoApp::getInstance()->stopPlayBack(true); m_frameBuffer->showFrame("mp3.jpg"); // tell neutrino we're in upnp mode CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE , NeutrinoMessages::mode_upnp); // remember last mode m_LastMode=(CNeutrinoApp::getInstance()->getLastMode()); // Stop sectionsd g_Sectionsd->setPauseScanning(true); m_deviceliststart=0; m_selecteddevice=0; timeout = 0; selectDevice(); if (CAudioPlayer::getInstance()->getState() != CBaseDec::STOP) CAudioPlayer::getInstance()->stop(); // Start Sectionsd g_Sectionsd->setPauseScanning(false); m_frameBuffer->stopFrame(); m_frameBuffer->Clear(); CZapit::getInstance()->EnablePlayback(true); CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE , m_LastMode); g_RCInput->postMsg(NeutrinoMessages::SHOW_INFOBAR, 0); return menu_return::RETURN_REPAINT; } void CUpnpBrowserGui::splitProtocol(std::string &protocol, std::string &prot, std::string &network, std::string &mime, std::string &additional) { std::string::size_type pos; std::string::size_type startpos = 0; pos = protocol.find(":", startpos); if (pos != std::string::npos) { prot = protocol.substr(startpos, pos-startpos); startpos = pos + 1; pos = protocol.find(":", startpos); if (pos != std::string::npos) { network = protocol.substr(startpos, pos-startpos); startpos = pos + 1; pos = protocol.find(":", startpos); if (pos != std::string::npos) { mime = protocol.substr(startpos, pos-startpos); startpos = pos + 1; pos = protocol.find(":", startpos); if (pos != std::string::npos) { additional = protocol.substr(startpos, pos-startpos); } } } } //printf("%s -> %s - %s - %s - %s\n", protocol.c_str(), prot.c_str(), network.c_str(), mime.c_str(), additional.c_str()); } bool CUpnpBrowserGui::discoverDevices() { if (!m_devices.empty()) return true; CHintBox *scanBox = new CHintBox(LOCALE_MESSAGEBOX_INFO, g_Locale->getText(LOCALE_UPNPBROWSER_SCANNING)); // UTF-8 scanBox->paint(); try { m_devices = m_socket->Discover("urn:schemas-upnp-org:service:ContentDirectory:1"); } catch (std::runtime_error error) { delete scanBox; ShowMsg(LOCALE_MESSAGEBOX_INFO, error.what(), CMessageBox::mbrBack, CMessageBox::mbBack, NEUTRINO_ICON_INFO); return false; } delete scanBox; if (m_devices.empty()) { ShowMsg(LOCALE_MESSAGEBOX_INFO, LOCALE_UPNPBROWSER_NOSERVERS, CMessageBox::mbrBack, CMessageBox::mbBack, NEUTRINO_ICON_INFO); return false; } return true; } bool CUpnpBrowserGui::getResults(std::string id, unsigned int start, unsigned int count, std::list &results) { std::listattribs; std::stringstream sindex; std::stringstream scount; sindex << start; scount << count; attribs.push_back(UPnPAttribute("ObjectID", id)); attribs.push_back(UPnPAttribute("BrowseFlag", "BrowseDirectChildren")); attribs.push_back(UPnPAttribute("Filter", "*")); attribs.push_back(UPnPAttribute("StartingIndex", sindex.str())); attribs.push_back(UPnPAttribute("RequestedCount", scount.str())); attribs.push_back(UPnPAttribute("SortCriteria", "")); try { results=m_devices[m_selecteddevice].SendSOAP("urn:schemas-upnp-org:service:ContentDirectory:1", "Browse", attribs); } catch (std::runtime_error error) { ShowMsg(LOCALE_MESSAGEBOX_INFO, error.what(), CMessageBox::mbrBack, CMessageBox::mbBack, NEUTRINO_ICON_INFO); return false; } return true; } std::vector *CUpnpBrowserGui::decodeResult(std::string result) { xmlNodePtr root, node, snode; std::vector *entries; xmlDocPtr parser = parseXml(result.c_str(),"UTF-8"); root = xmlDocGetRootElement(parser); if (!root) { xmlFreeDoc(parser); return NULL; } entries = new std::vector; for (node=xmlChildrenNode(root); node; node=xmlNextNode(node)) { bool isdir; std::string title, artist = "", album = "", albumArtURI = "", id, children; const char *type, *p; if (!strcmp(xmlGetName(node), "container")) { std::vector resources; isdir=true; for (snode=xmlChildrenNode(node); snode; snode=xmlNextNode(snode)) { type=xmlGetName(snode); p = strchr(type,':'); if (p) type=p+1; if (!strcmp(type,"title")) { p=xmlGetData(snode); if (!p) p = ""; title=std::string(p); } } p = xmlGetAttribute(node, "id"); if (!p) p = ""; id=std::string(p); p = xmlGetAttribute(node, "childCount"); if (!p) p = ""; children=std::string(p); UPnPEntry entry={id, isdir, title, artist, album, albumArtURI, children, "", "", resources, -1, CFile::FILE_DIR}; entries->push_back(entry); } if (!strcmp(xmlGetName(node), "item")) { std::vector resources; int preferred = -1; std::string protocol, prot, network, mime, additional; CFile::FileType ftype = CFile::FILE_UNKNOWN; isdir=false; for (snode=xmlChildrenNode(node); snode; snode=xmlNextNode(snode)) { std::string duration, url, size; unsigned int i; type=xmlGetName(snode); p = strchr(type,':'); if (p) type=p+1; if (!strcmp(type,"title")) { p=xmlGetData(snode); if (!p) p = ""; title=std::string(p); } else if (!strcmp(type,"artist")) { p=xmlGetData(snode); if (!p) p = ""; artist=std::string(p); } else if (!strcmp(type,"album")) { p=xmlGetData(snode); if (!p) p = ""; album=std::string(p); } else if (!strcmp(type,"albumArtURI")) { p=xmlGetData(snode); if (!p) p = ""; albumArtURI=std::string(p); } else if (!strcmp(type,"res")) { p = xmlGetData(snode); if (!p) p = ""; url=std::string(p); p = xmlGetAttribute(snode, "size"); if (!p) p = "0"; size=std::string(p); p = xmlGetAttribute(snode, "duration"); if (!p) p = ""; duration=std::string(p); p = xmlGetAttribute(snode, "protocolInfo"); if (!p) p = ""; protocol=std::string(p); UPnPResource resource = {url, protocol, size, duration}; resources.push_back(resource); } int pref=0; preferred=-1; for (i=0; ipush_back(entry); } } xmlFreeDoc(parser); return entries; } void CUpnpBrowserGui::updateDeviceSelection(int newpos) { if (newpos < 0) /* do not explode if called with -1 arg... */ return; if((int) m_selecteddevice != newpos) { int prev_selected = m_selecteddevice; unsigned int oldliststart = m_deviceliststart; m_selecteddevice = newpos; m_deviceliststart = (m_selecteddevice/m_listmaxshow)*m_listmaxshow; if (oldliststart != m_deviceliststart) paintDevices(); else { paintDevice(prev_selected - m_deviceliststart); paintDevice(m_selecteddevice - m_deviceliststart); } } } void CUpnpBrowserGui::selectDevice() { bool loop = true; bool refresh = true; neutrino_msg_t msg; neutrino_msg_data_t data; if (!discoverDevices()) return; CAudioMute::getInstance()->enableMuteIcon(false); while (loop) { if (refresh) { paintDevices(); refresh=false; } g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout to update play/stop state display neutrino_msg_t msg_repeatok = msg & ~CRCInput::RC_Repeat; if (msg == CRCInput::RC_timeout) { // nothing } else if (msg == CRCInput::RC_home) { loop=false; } else if (msg_repeatok == (neutrino_msg_t) g_settings.key_list_start) { updateDeviceSelection(0); } else if (msg_repeatok == (neutrino_msg_t) g_settings.key_list_end) { updateDeviceSelection(m_devices.size()-1); } else if (msg_repeatok == CRCInput::RC_up || (int)msg == g_settings.key_pageup || msg_repeatok == CRCInput::RC_down || (int)msg == g_settings.key_pagedown) { int new_selected = UpDownKey(m_devices, msg_repeatok, m_listmaxshow, m_selecteddevice); updateDeviceSelection(new_selected); } else if (msg == CRCInput::RC_right || msg == CRCInput::RC_ok) { m_folderplay = false; selectItem("0"); refresh=true; } else if (msg == CRCInput::RC_blue) { m_devices.clear(); if (!discoverDevices()) return; refresh=true; } else if (msg == NeutrinoMessages::RECORD_START || msg == NeutrinoMessages::ZAPTO || msg == NeutrinoMessages::STANDBY_ON || msg == NeutrinoMessages::LEAVE_ALL || msg == NeutrinoMessages::SHUTDOWN || msg == NeutrinoMessages::SLEEPTIMER) { loop=false; g_RCInput->postMsg(msg, data); } #if 0 else if (msg == NeutrinoMessages::EVT_TIMER) { CNeutrinoApp::getInstance()->handleMsg(msg, data); } else if (msg > CRCInput::RC_MaxRC) #endif else { printf("msg: %x\n", (int) msg); if (CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all) loop = false; } } CAudioMute::getInstance()->enableMuteIcon(true); } void CUpnpBrowserGui::playnext(void) { std::vector *entries = NULL; while (true) { timeout = 0; std::listresults; std::list::iterator i; printf("playnext: getResults m_playfolder %s m_playid %d\n", m_playfolder.c_str(), m_playid); if (!getResults(m_playfolder, m_playid, 1, results)) { m_folderplay = false; return; } for (i=results.begin(); i!=results.end(); ++i) { if (i->first=="NumberReturned") { if (atoi(i->second.c_str()) != 1) { m_folderplay = false; return; } } else if (i->first=="Result") { entries=decodeResult(i->second); } } //m_playid++; if ((entries != NULL) && (!(*entries)[0].isdir)) { int preferred=(*entries)[0].preferred; if (preferred != -1) { std::string &mime = (*entries)[0].mime; if (mime.substr(0,6) == "audio/") { m_playing_entry = (*entries)[0]; m_playing_entry_is_shown = false; playAudio((*entries)[0].resources[preferred].url, (*entries)[0].type); } else if (mime.substr(0,6) == "video/") { playVideo((*entries)[0].title, (*entries)[0].resources[preferred].url); m_folderplay = false; // FIXME else no way to stop in video folder } else if (mime.substr(0,6) == "image/") { if (m_folderplay) timeout = time(NULL) + g_settings.picviewer_slide_time; showPicture((*entries)[0].resources[preferred].url); } return; } } else { neutrino_msg_t msg; neutrino_msg_data_t data; g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout to update play/stop state display if (msg == CRCInput::RC_home) { m_folderplay = false; break; } } } delete entries; m_frameBuffer->Clear(); } bool CUpnpBrowserGui::getItems(std::string id, unsigned int index, std::vector * &entries, unsigned int &total) { bool tfound = false; bool rfound = false; bool nfound = false; unsigned int returned = 0; std::listresults; std::list::iterator i; delete entries; entries = NULL; printf("getItems: getResults: index %d count %d\n", index, m_listmaxshow); if (!getResults(id, index, m_listmaxshow, results)) return false; for (i=results.begin(); i!=results.end(); ++i) { if (i->first=="NumberReturned") { returned=atoi(i->second.c_str()); nfound=true; } else if (i->first=="TotalMatches") { total=atoi(i->second.c_str()); tfound=true; } else if (i->first=="Result") { entries=decodeResult(i->second); rfound=true; } } if (!entries || !nfound || !tfound || !rfound || returned != entries->size() || returned == 0) return false; return true; } bool CUpnpBrowserGui::updateItemSelection(std::string id, std::vector * &entries, int newpos, unsigned int &selected, unsigned int &liststart) { if((int) selected != newpos) { int prev_selected = selected; unsigned int oldliststart = liststart; selected = newpos; liststart = (selected/m_listmaxshow)*m_listmaxshow; printf("updateItemSelection: list start old %u new %d selected old %d new %d\n", oldliststart, liststart, prev_selected, selected); if (oldliststart != liststart) { unsigned int total; if (!getItems(id, liststart, entries, total)) return false; paintItems(entries, selected - liststart, total - liststart, liststart); } else { paintItem(entries, prev_selected - liststart, selected - liststart); paintItem(entries, selected - liststart, selected - liststart); } } return true; } bool CUpnpBrowserGui::selectItem(std::string id) { bool loop = true; bool endall = false; bool refresh = true; neutrino_msg_t msg; neutrino_msg_data_t data; std::vector *entries = NULL; unsigned int liststart = 0; unsigned int selected = 0; unsigned int total = 0; printf("selectItem: [%s]\n", id.c_str()); if (!getItems(id, liststart, entries, total)) return endall; while (loop) { updateTimes(); if (refresh) { printf("selectItem: refresh, timeout = %d\n", (int) timeout); if (!timeout) paintItems(entries, selected - liststart, total - liststart, liststart); refresh=false; } g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout to update play/stop state display neutrino_msg_t msg_repeatok = msg & ~CRCInput::RC_Repeat; if (msg == CRCInput::RC_timeout) { // nothing } else if (msg == CRCInput::RC_home) { loop=false; endall=true; } else if (!timeout && (msg == CRCInput::RC_left)) { loop=false; } else if (!timeout && (msg_repeatok == (neutrino_msg_t) g_settings.key_list_start)) { updateItemSelection(id, entries, 0, selected, liststart); } else if (!timeout && (msg_repeatok == (neutrino_msg_t) g_settings.key_list_end)) { updateItemSelection(id, entries, total-1, selected, liststart); } else if (!timeout && (msg_repeatok == CRCInput::RC_up || (int) msg == g_settings.key_pageup)) { int step = ((int) msg == g_settings.key_pageup) ? m_listmaxshow : 1; // browse or step 1 int new_selected = selected - step; if (new_selected < 0) { if (selected != 0 && step != 1) new_selected = 0; else new_selected = total - 1; } updateItemSelection(id, entries, new_selected, selected, liststart); } else if (!timeout && (msg_repeatok == CRCInput::RC_down || (int) msg == g_settings.key_pagedown)) { int step = ((int) msg == g_settings.key_pagedown) ? m_listmaxshow : 1; // browse or step 1 int new_selected = selected + step; if (new_selected >= (int) total) { if ((total - m_listmaxshow -1 < selected) && (selected != (total - 1)) && (step != 1)) new_selected = total - 1; else if (((total / m_listmaxshow) + 1) * m_listmaxshow == total + m_listmaxshow) // last page has full entries new_selected = 0; else new_selected = ((step == (int) m_listmaxshow) && (new_selected < (int) (((total / m_listmaxshow)+1) * m_listmaxshow))) ? (total - 1) : 0; } updateItemSelection(id, entries, new_selected, selected, liststart); } else if (!timeout && (msg == CRCInput::RC_ok || msg == CRCInput::RC_right)) { if ((selected - liststart) >= (*entries).size()) continue; if ((*entries)[selected - liststart].isdir) { endall=selectItem((*entries)[selected - liststart].id); if (endall) loop=false; refresh=true; } else { m_folderplay = false; int preferred=(*entries)[selected - liststart].preferred; if (preferred != -1) { std::string &mime = (*entries)[selected - liststart].mime; if (mime.substr(0,6) == "audio/") { m_playing_entry = (*entries)[selected - liststart]; m_playing_entry_is_shown = false; playAudio((*entries)[selected - liststart].resources[preferred].url, (*entries)[selected - liststart].type); } else if (mime.substr(0,6) == "video/") { m_frameBuffer->Clear(); playVideo((*entries)[selected - liststart].title, (*entries)[selected - liststart].resources[preferred].url); m_frameBuffer->showFrame("mp3.jpg"); refresh = true; } else if (mime.substr(0,6) == "image/") { videoDecoder->setBlank(true); showPicture((*entries)[selected - liststart].resources[preferred].url); m_playid = selected; while (true) { g_RCInput->getMsg(&msg, &data, 10); // 1 sec timeout if (msg == CRCInput::RC_home || msg == CRCInput::RC_ok) break; else if (msg == CRCInput::RC_right || msg == CRCInput::RC_down) { m_playfolder = id; m_playid = (m_playid + 1)%total; playnext(); } else if (msg == CRCInput::RC_left || msg == CRCInput::RC_up) { m_playfolder = id; m_playid--; if (m_playid < 0) m_playid = total - 1; playnext(); } else CNeutrinoApp::getInstance()->handleMsg(msg, data); } m_frameBuffer->Clear(); videoDecoder->setBlank(false); refresh = true; } } } } else if (msg == CRCInput::RC_play) { if ((selected - liststart) >= (*entries).size()) continue; m_folderplay = true; m_playfolder = (*entries)[selected - liststart].id; m_playid = 0; playnext(); m_playid++; } else if (msg == CRCInput::RC_yellow) { if (CAudioPlayer::getInstance()->getState() != CBaseDec::STOP) CAudioPlayer::getInstance()->stop(); m_folderplay = false; } else if (m_folderplay && msg == (neutrino_msg_t) CRCInput::RC_stop) { timeout = 0; m_folderplay = false; m_frameBuffer->Clear(); refresh = true; } else if (m_folderplay && msg == (neutrino_msg_t) CRCInput::RC_prev) { timeout = 0; m_playid -= 2; if (m_playid < 0) m_playid = 0; } else if (m_folderplay && msg == (neutrino_msg_t) CRCInput::RC_next) { timeout = 0; if (CAudioPlayer::getInstance()->getState() != CBaseDec::STOP) CAudioPlayer::getInstance()->stop(); } else if (msg == NeutrinoMessages::RECORD_START || msg == NeutrinoMessages::ZAPTO || msg == NeutrinoMessages::STANDBY_ON || msg == NeutrinoMessages::LEAVE_ALL || msg == NeutrinoMessages::SHUTDOWN || msg == NeutrinoMessages::SLEEPTIMER) { loop = false; g_RCInput->postMsg(msg, data); } #if 0 else if (msg == NeutrinoMessages::EVT_TIMER) { CNeutrinoApp::getInstance()->handleMsg(msg, data); } else if (msg > CRCInput::RC_MaxRC) #endif else { if (CNeutrinoApp::getInstance()->handleMsg(msg, data) & messages_return::cancel_all) loop = false; //refresh=true; } if (m_folderplay && ((!timeout || (timeout <= time(NULL))) && (CAudioPlayer::getInstance()->getState() == CBaseDec::STOP))) { playnext(); m_playid++; } } delete entries; timeout = 0; m_frameBuffer->Clear(); return endall; } void CUpnpBrowserGui::paintDeviceInfo() { CVFD::getInstance()->showMenuText(0, m_devices[m_selecteddevice].friendlyname.c_str(), -1, true); // Info std::string tmp; // first line tmp = m_devices[m_selecteddevice].manufacturer + " " + m_devices[m_selecteddevice].manufacturerurl + "\n"; // second line tmp += m_devices[m_selecteddevice].modelname + " " + m_devices[m_selecteddevice].modelnumber + " " + m_devices[m_selecteddevice].modeldescription + "\n"; // third line tmp += m_devices[m_selecteddevice].modelurl; topbox.setDimensionsAll(m_x, m_y, m_width, m_title_height-10); topbox.setText(tmp, CTextBox::AUTO_WIDTH); topbox.paint0(); } void CUpnpBrowserGui::paintDevice(unsigned int _pos) { int ypos = m_y + m_title_height + m_theight + _pos*m_fheight; fb_pixel_t color; fb_pixel_t bgcolor; unsigned int pos = m_deviceliststart + _pos; if (pos == m_selecteddevice) { color = COL_MENUCONTENT_TEXT_PLUS_2; bgcolor = COL_MENUCONTENT_PLUS_2; paintDeviceInfo(); } else { color = COL_MENUCONTENT_TEXT; bgcolor = COL_MENUCONTENT_PLUS_0; } m_frameBuffer->paintBoxRel(m_x, ypos, m_width - 15, m_fheight, bgcolor); if (pos >= m_devices.size()) return; char sNr[20]; sprintf(sNr, "%2d", pos + 1); std::string num = sNr; std::string name = m_devices[pos].friendlyname; int w = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->getRenderWidth(name) + 5; g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + 10, ypos + m_fheight, m_width - 30 - w, num, color, m_fheight); g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + m_width - 15 - w, ypos + m_fheight, w, name, color, m_fheight); } void CUpnpBrowserGui::paintDevices() { int ypos, top; // LCD CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, "Select UPnP Device"); // Head CComponentsHeaderLocalized header(m_x, m_y + m_title_height, m_width, m_theight, LOCALE_UPNPBROWSER_HEAD, NEUTRINO_ICON_UPNP); if (CNeutrinoApp::getInstance()->isMuted()) //TODO: consider mute mode on runtime header.addContextButton(NEUTRINO_ICON_BUTTON_MUTE_SMALL); else header.removeContextButtons(); header.paint(CC_SAVE_SCREEN_NO); // Items for (unsigned int count=0; countpaintBoxRel(m_x + m_width - 15, ypos, 15, sb, COL_MENUCONTENT_PLUS_1); unsigned int tmp_max = m_listmaxshow; if(!tmp_max) tmp_max = 1; int sbc = ((m_devices.size() - 1) / tmp_max) + 1; int sbs = ((m_selecteddevice) / tmp_max); m_frameBuffer->paintBoxRel(m_x + m_width - 13, ypos + 2 + sbs*(sb-4)/sbc, 11, (sb-4)/sbc, COL_MENUCONTENT_PLUS_3); // Foot top = m_y + (m_height - m_info_height - 2 * m_buttonHeight); footer.paintButtons(m_x, top, m_width, m_buttonHeight, 1, &RescanButton, m_width/2); paintItem2DetailsLine (-1); // clear it } void CUpnpBrowserGui::paintItem(std::vector *entries, unsigned int pos, unsigned int selected) { int ypos = m_y + m_title_height + m_theight + pos*m_fheight; fb_pixel_t color; fb_pixel_t bgcolor; if (pos == selected) { color = COL_MENUCONTENT_TEXT_PLUS_2; bgcolor = COL_MENUCONTENT_PLUS_2; } else { color = COL_MENUCONTENT_TEXT; bgcolor = COL_MENUCONTENT_PLUS_0; } m_frameBuffer->paintBoxRel(m_x, ypos, m_width - 15, m_fheight, bgcolor); if (pos >= (*entries).size()) return; UPnPEntry *entry = &(*entries)[pos]; if (pos == selected) { paintItemInfo(entry); paintDetails(entry); if (entry->isdir) paintItem2DetailsLine (-1); // clear it else paintItem2DetailsLine (pos); } int preferred=entry->preferred; std::string info; std::string fileicon; if (entry->isdir) { info = ""; fileicon = NEUTRINO_ICON_FOLDER; } else { if (preferred != -1) { info = entry->resources[preferred].duration; fileicon = NEUTRINO_ICON_MP3; } else { info = "(none)"; fileicon = NEUTRINO_ICON_FILE; } } std::string name = entry->title; char tmp_time[] = "00:00:00.0"; int w = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->getRenderWidth(tmp_time); int icon_w = 0; int icon_h = 0; int icon_o = 0; m_frameBuffer->getIconSize(fileicon.c_str(), &icon_w, &icon_h); if (icon_w && icon_h) { icon_o = icon_w + 10; m_frameBuffer->paintIcon(fileicon, m_x + 10, ypos + (m_fheight - icon_h) / 2); } g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + m_width - 15 - 10 - w, ypos + m_fheight, w, info, color, m_fheight); g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(m_x + 10 + icon_o, ypos + m_fheight, m_width - icon_o - 15 - 2*10 - w, name, color, m_fheight); } void CUpnpBrowserGui::paintItemInfo(UPnPEntry *entry) { std::string tmp; std::stringstream ts; int preferred=entry->preferred; // LCD CVFD::getInstance()->showMenuText(0, entry->title.c_str(), -1, true); // first line ts << "Resources: " << entry->resources.size() << " Selected: " << preferred+1 << " "; tmp = ts.str(); if (preferred != -1) tmp = tmp + "Duration: " + entry->resources[preferred].duration; else tmp = tmp + "No resource for Item"; tmp += "\n"; // second line if (entry->isdir) tmp += "Directory"; else { tmp += ""; if (preferred != -1) tmp += "Protocol: " + entry->proto + ", MIME-Type: " + entry->mime; } tmp += "\n"; //third line if (!entry->isdir && preferred != -1) tmp += "URL: " + entry->resources[preferred].url; static std::string lastname = "", tmpname = ""; if(!entry->albumArtURI.empty()){ if(lastname != entry->albumArtURI){ tmpname = lastname = entry->albumArtURI.c_str(); tmpname = g_PicViewer->DownloadImage(tmpname); int h_image = ibox.getHeight()- SHADOW_OFFSET - ibox.getCornerRadius(); int y_image = ibox.getYPos() + ibox.getHeight()/2 - h_image/2; if (!image){ image = new CComponentsPicture(100, y_image, tmpname, NULL, CC_SHADOW_OFF, COL_MENUCONTENTDARK_PLUS_0); image->doPaintBg(false); image->SetTransparent(CFrameBuffer::TM_BLACK); } image->setPicture(tmpname); image->setHeight(h_image, true); int x_image = ibox.getXPos() + ibox.getWidth()- image->getWidth()- SHADOW_OFFSET - ibox.getCornerRadius(); image->setXPos(x_image); } }else{ if (image){ delete image; image = NULL; } } topbox.setText(tmp, CTextBox::AUTO_WIDTH); topbox.paint0(); } void CUpnpBrowserGui::paintItems(std::vector *entry, unsigned int selected, unsigned int max, unsigned int offset) { printf("CUpnpBrowserGui::paintItem:s selected %d max %d offset %d\n", selected, max, offset); int ypos, top; //block infoclock CInfoClock::getInstance()->block(); // LCD CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, "Select UPnP Entry"); // Head std::string name = g_Locale->getText(LOCALE_UPNPBROWSER_HEAD); name += " : "; name += m_devices[m_selecteddevice].friendlyname; CComponentsHeader header(m_x, m_y + m_title_height, m_width, m_theight, name, NEUTRINO_ICON_UPNP); if (CNeutrinoApp::getInstance()->isMuted()) header.setContextButton(NEUTRINO_ICON_BUTTON_MUTE_SMALL); header.paint(CC_SAVE_SCREEN_NO); // Items for (unsigned int count=0; countpaintBoxRel(m_x + m_width - 15, ypos, 15, sb, COL_MENUCONTENT_PLUS_1); unsigned int tmp = m_listmaxshow ? m_listmaxshow : 1;//avoid division by zero int sbc = ((max + offset - 1) / tmp) + 1; int sbs = ((selected + offset) / tmp); int sbh = 0; if ((sbc > 0) && (sbc > sb-4)) sbh = 2; m_frameBuffer->paintBoxRel(m_x + m_width - 13, ypos + 2 + sbs*((sb-4)/sbc+sbh), 11, (sb-4)/sbc + sbh, COL_MENUCONTENT_PLUS_3); // Foot buttons top = m_y + (m_height - m_info_height - 2 * m_buttonHeight); size_t numbuttons = sizeof(BrowseButtons)/sizeof(BrowseButtons[0]); footer.paintButtons(m_x, top, m_width, m_buttonHeight, numbuttons, BrowseButtons, m_width/numbuttons); } void CUpnpBrowserGui::paintDetails(UPnPEntry *entry, bool use_playing) { // Foot info int i_height = 2 * m_buttonHeight; ibox.setDimensionsAll(m_x, footer.getYPos()+ footer.getHeight()+SHADOW_OFFSET, m_width-i_height-SHADOW_OFFSET, i_height); timebox.setDimensionsAll(m_x + m_width - i_height, ibox.getYPos(), i_height, i_height); printf("paintDetails: use_playing %d shown %d\n", use_playing, m_playing_entry_is_shown); if ((!use_playing) && entry->isdir){ ibox.kill(); m_playing_entry_is_shown = false; }else{ string text = ""; if (use_playing){ if (!m_playing_entry_is_shown){ m_playing_entry_is_shown = true; text = m_playing_entry.title; text += !m_playing_entry.artist.empty() ? " - " + m_playing_entry.artist : ""; text += "\n" + m_playing_entry.album; ibox.setText(text, CTextBox::AUTO_WIDTH); ibox.paint0(); } }else{ if (!entry) return; m_playing_entry_is_shown = false; text = entry->title; text += !entry->artist.empty() ? " - " + entry->artist : ""; text += "\n" + entry->album; ibox.setText(text, CTextBox::AUTO_WIDTH); ibox.paint0(); } if (image) image->paint0(); timebox.paint0(); } } void CUpnpBrowserGui::paintItem2DetailsLine (int pos) { if (pos < 0) return; int xpos = m_x - ConnectLineBox_Width; int ypos1 = m_y + m_title_height+0 + m_theight + pos*m_fheight; int ypos2 = ibox.getYPos()+ ibox.getHeight()-ibox.getHeight()/2; int ypos1a = ypos1 + (m_fheight/2); if (!dline) dline = new CComponentsDetailLine(); dline->setDimensionsAll(xpos, ypos1a, ypos2, m_fheight/2, ibox.getHeight()-RADIUS_LARGE*3); dline->paint(); } void CUpnpBrowserGui::updateTimes(const bool force) { if (CAudioPlayer::getInstance()->getState() != CBaseDec::STOP){ bool updatePlayed = force; if ((m_time_played != CAudioPlayer::getInstance()->getTimePlayed())){ m_time_played = CAudioPlayer::getInstance()->getTimePlayed(); updatePlayed = true; } printf("updateTimes: force %d updatePlayed %d\n", force, updatePlayed); char play_time[8]; snprintf(play_time, 7, "%ld:%02ld", m_time_played / 60, m_time_played % 60); if (updatePlayed){ timebox.setText(play_time, CTextBox::CENTER); timebox.paint0(); } } } void CUpnpBrowserGui::playAudio(std::string name, int type) { CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE, NeutrinoMessages::mode_audio); CAudiofile mp3(name, (CFile::FileType) type); CAudioPlayer::getInstance()->play(&mp3, g_settings.audioplayer_highprio == 1); CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE, NeutrinoMessages::mode_upnp | NeutrinoMessages::norezap); } void CUpnpBrowserGui::showPicture(std::string name) { CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE, NeutrinoMessages::mode_pic); g_PicViewer->SetScaling((CPictureViewer::ScalingMode)g_settings.picviewer_scaling); g_PicViewer->SetVisible(g_settings.screen_StartX, g_settings.screen_EndX, g_settings.screen_StartY, g_settings.screen_EndY); if (g_settings.video_Format==3) g_PicViewer->SetAspectRatio(16.0/9); else g_PicViewer->SetAspectRatio(4.0/3); g_PicViewer->ShowImage(name, false); g_PicViewer->Cleanup(); CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE, NeutrinoMessages::mode_upnp | NeutrinoMessages::norezap); } void CUpnpBrowserGui::playVideo(std::string name, std::string url) { CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE, NeutrinoMessages::mode_ts); if (CAudioPlayer::getInstance()->getState() != CBaseDec::STOP) CAudioPlayer::getInstance()->stop(); m_frameBuffer->stopFrame(); CMoviePlayerGui::getInstance().SetFile(name, url); CMoviePlayerGui::getInstance().exec(NULL, "upnp"); CNeutrinoApp::getInstance()->handleMsg(NeutrinoMessages::CHANGEMODE, NeutrinoMessages::mode_upnp | NeutrinoMessages::norezap); }