1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2021 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2019-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "nel/gui/group_html.h"
27 #include "nel/misc/types_nl.h"
28 #include "nel/misc/rgba.h"
29 #include "nel/misc/algo.h"
30 #include "nel/misc/utf_string_view.h"
31 #include "nel/gui/libwww.h"
32 #include "nel/gui/group_html.h"
33 #include "nel/gui/group_list.h"
34 #include "nel/gui/group_menu.h"
35 #include "nel/gui/group_container.h"
36 #include "nel/gui/view_link.h"
37 #include "nel/gui/ctrl_scroll.h"
38 #include "nel/gui/ctrl_button.h"
39 #include "nel/gui/ctrl_text_button.h"
40 #include "nel/gui/action_handler.h"
41 #include "nel/gui/group_paragraph.h"
42 #include "nel/gui/group_editbox.h"
43 #include "nel/gui/widget_manager.h"
44 #include "nel/gui/lua_manager.h"
45 #include "nel/gui/view_bitmap.h"
46 #include "nel/gui/dbgroup_combo_box.h"
47 #include "nel/gui/lua_ihm.h"
48 #include "nel/misc/i18n.h"
49 #include "nel/misc/md5.h"
50 #include "nel/3d/texture_file.h"
51 #include "nel/misc/big_file.h"
52 #include "nel/gui/url_parser.h"
53 #include "nel/gui/http_cache.h"
54 #include "nel/gui/http_hsts.h"
55 #include "nel/web/curl_certificates.h"
56 #include "nel/gui/html_parser.h"
57 #include "nel/gui/html_element.h"
58 #include "nel/gui/css_style.h"
59 #include "nel/gui/css_parser.h"
60 #include "nel/gui/css_border_renderer.h"
61 #include "nel/gui/css_background_renderer.h"
63 #include <curl/curl.h>
66 using namespace NLMISC
;
72 // Default maximum time the request is allowed to take
73 #define DEFAULT_RYZOM_CONNECTION_TIMEOUT (300.0)
74 // Allow up to 10 redirects, then give up
75 #define DEFAULT_RYZOM_REDIRECT_LIMIT (10)
77 #define FONT_WEIGHT_NORMAL 400
78 #define FONT_WEIGHT_BOLD 700
83 // Uncomment nlwarning() to see the log about curl downloads
84 #define LOG_DL(fmt, ...) //nlwarning(fmt, ## __VA_ARGS__)
85 // Uncomment to log curl progess
86 //#define LOG_CURL_PROGRESS 1
88 CGroupHTML::SWebOptions
CGroupHTML::options
;
90 // Return URL with https is host is in HSTS list
91 static std::string
upgradeInsecureUrl(const std::string
&url
)
93 if (toLowerAscii(url
.substr(0, 7)) != "http://") {
98 if (!CStrictTransportSecurity::getInstance()->isSecureHost(uri
.host
)){
102 LOG_DL("HSTS url : '%s', using https", url
.c_str());
103 uri
.scheme
= "https";
105 return uri
.toString();
108 // Active cURL www transfer
112 CCurlWWWData(CURL
*curl
, const std::string
&url
)
113 : Request(curl
), Url(url
), Content(""), HeadersSent(NULL
)
119 curl_easy_cleanup(Request
);
122 curl_slist_free_all(HeadersSent
);
125 void sendHeaders(const std::vector
<std::string
> headers
)
127 for(uint i
= 0; i
< headers
.size(); ++i
)
129 HeadersSent
= curl_slist_append(HeadersSent
, headers
[i
].c_str());
131 curl_easy_setopt(Request
, CURLOPT_HTTPHEADER
, HeadersSent
);
134 void setRecvHeader(const std::string
&header
)
136 size_t pos
= header
.find(": ");
137 if (pos
== std::string::npos
)
140 std::string key
= toLowerAscii(header
.substr(0, pos
));
141 if (pos
!= std::string::npos
)
143 HeadersRecv
[key
] = header
.substr(pos
+ 2);
144 //nlinfo(">> received header '%s' = '%s'", key.c_str(), HeadersRecv[key].c_str());
148 // return last received "Location: <url>" header or empty string if no header set
149 const std::string
getLocationHeader()
151 if (HeadersRecv
.count("location") > 0)
152 return HeadersRecv
["location"];
157 const uint32
getExpires()
160 if (HeadersRecv
.count("expires") > 0)
161 ret
= curl_getdate(HeadersRecv
["expires"].c_str(), NULL
);
163 return ret
> -1 ? ret
: 0;
166 const std::string
getLastModified()
168 if (HeadersRecv
.count("last-modified") > 0)
170 return HeadersRecv
["last-modified"];
176 const std::string
getEtag()
178 if (HeadersRecv
.count("etag") > 0)
180 return HeadersRecv
["etag"];
188 // ignore header if not secure connection
189 if (toLowerAscii(Url
.substr(0, 8)) != "https://")
194 return HeadersRecv
.count("strict-transport-security") > 0;
197 const std::string
getHSTSHeader()
201 return HeadersRecv
["strict-transport-security"];
214 // headers sent with curl request, must be released after transfer
215 curl_slist
* HeadersSent
;
217 // headers received from curl transfer
218 std::map
<std::string
, std::string
> HeadersRecv
;
221 // cURL transfer callbacks
222 // ***************************************************************************
223 static size_t curlHeaderCallback(char *buffer
, size_t size
, size_t nmemb
, void *pCCurlWWWData
)
225 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
229 header
.append(buffer
, size
* nmemb
);
230 me
->setRecvHeader(header
.substr(0, header
.find_first_of("\n\r")));
236 // ***************************************************************************
237 static size_t curlDataCallback(char *buffer
, size_t size
, size_t nmemb
, void *pCCurlWWWData
)
239 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
241 me
->Content
.append(buffer
, size
* nmemb
);
246 // ***************************************************************************
247 static size_t curlProgressCallback(void *pCCurlWWWData
, curl_off_t dltotal
, curl_off_t dlnow
, curl_off_t ultotal
, curl_off_t ulnow
)
249 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
252 if (dltotal
> 0 || dlnow
> 0 || ultotal
> 0 || ulnow
> 0)
254 #ifdef LOG_CURL_PROGRESS
255 nlwarning("> dltotal %ld, dlnow %ld, ultotal %ld, ulnow %ld, url '%s'", dltotal
, dlnow
, ultotal
, ulnow
, me
->Url
.c_str());
260 // return 1 to cancel download
264 CGroupHTML::CDataDownload::~CDataDownload()
270 void CGroupHTML::StylesheetDownloadCB::finish()
272 if (CFile::fileExists(tmpdest
))
274 if (CFile::fileExists(dest
))
276 CFile::deleteFile(dest
);
278 CFile::moveFile(dest
, tmpdest
);
280 Parent
->cssDownloadFinished(url
, dest
);
283 void CGroupHTML::ImageDownloadCB::addImage(CViewBase
*img
, const CStyleParams
&style
, TImageType type
)
285 Images
.push_back(SImageInfo(img
, style
, type
));
288 void CGroupHTML::ImageDownloadCB::removeImage(CViewBase
*img
)
290 for(std::vector
<SImageInfo
>::iterator it
= Images
.begin(); it
!= Images
.end(); ++it
)
292 if (it
->Image
== img
)
300 void CGroupHTML::ImageDownloadCB::finish()
302 // Image setTexture will remove itself from Images while iterating over it.
303 // Do the swap to keep iterator safe.
304 std::vector
<SImageInfo
> vec
;
307 // tmpdest file does not exist if download skipped (ie cache was used)
308 if (CFile::fileExists(tmpdest
) || CFile::getFileSize(tmpdest
) == 0)
311 // verify that image is not corrupted
313 CBitmap::loadSize(tmpdest
, w
, h
);
314 if (w
!= 0 && h
!= 0)
316 if (CFile::fileExists(dest
))
317 CFile::deleteFile(dest
);
320 catch(const NLMISC::Exception
&e
)
322 // exception message has .tmp file name, so keep it for further analysis
323 nlwarning("Invalid image (%s) from url (%s): %s", tmpdest
.c_str(), url
.c_str(), e
.what());
326 // to reload image on page, the easiest seems to be changing texture
327 // to temp file temporarily. that forces driver to reload texture from disk
328 // ITexture::touch() seem not to do this.
329 // cache was updated, first set texture as temp file
330 for(std::vector
<SImageInfo
>::iterator it
= vec
.begin(); it
!= vec
.end(); ++it
)
332 SImageInfo
&img
= *it
;
333 Parent
->setImage(img
.Image
, tmpdest
, img
.Type
);
334 Parent
->setImageSize(img
.Image
, img
.Style
);
337 CFile::moveFile(dest
, tmpdest
);
340 if (!CFile::fileExists(dest
) || CFile::getFileSize(dest
) == 0)
342 // placeholder if cached image failed
343 dest
= "web_del.tga";
346 // even if image was cached, incase there was 'http://' image set to CViewBitmap
347 for(std::vector
<SImageInfo
>::iterator it
= vec
.begin(); it
!= vec
.end(); ++it
)
349 SImageInfo
&img
= *it
;
350 Parent
->setImage(img
.Image
, dest
, img
.Type
);
351 Parent
->setImageSize(img
.Image
, img
.Style
);
355 void CGroupHTML::TextureDownloadCB::finish()
357 // tmpdest file does not exist if download skipped (ie cache was used)
358 if (CFile::fileExists(tmpdest
) && CFile::getFileSize(tmpdest
) > 0)
360 if (CFile::fileExists(dest
))
361 CFile::deleteFile(dest
);
363 CFile::moveFile(dest
, tmpdest
);
366 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
367 for(uint i
= 0; i
< TextureIds
.size(); i
++)
369 rVR
.reloadTexture(TextureIds
[i
].first
, dest
);
370 TextureIds
[i
].second
->invalidateCoords();
374 void CGroupHTML::BnpDownloadCB::finish()
376 bool verified
= false;
377 // no tmpfile if file was already in cache
378 if (CFile::fileExists(tmpdest
))
380 verified
= m_md5sum
.empty() || (m_md5sum
!= getMD5(tmpdest
).toString());
383 if (CFile::fileExists(dest
))
385 CFile::deleteFile(dest
);
387 CFile::moveFile(dest
, tmpdest
);
391 CFile::deleteFile(tmpdest
);
394 else if (CFile::fileExists(dest
))
396 verified
= m_md5sum
.empty() || (m_md5sum
!= getMD5(dest
).toString());
401 std::string script
= "\nlocal __CURRENT_WINDOW__ = \""+Parent
->getId()+"\"";
402 script
+= toString("\nlocal __DOWNLOAD_STATUS__ = %s\n", verified
? "true" : "false");
404 CLuaManager::getInstance().executeLuaScript(script
, true );
408 // Check if domain is on TrustedDomain
409 bool CGroupHTML::isTrustedDomain(const string
&domain
)
411 vector
<string
>::iterator it
;
412 it
= find ( options
.trustedDomains
.begin(), options
.trustedDomains
.end(), domain
);
413 return it
!= options
.trustedDomains
.end();
416 // Update view after download has finished
417 void CGroupHTML::setImage(CViewBase
* view
, const string
&file
, const TImageType type
)
419 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
422 if (type
== NormalImage
)
424 btn
->setTexture (file
);
425 btn
->setTexturePushed(file
);
426 btn
->invalidateCoords();
427 btn
->invalidateContent();
432 btn
->setTextureOver(file
);
438 CViewBitmap
*btm
= dynamic_cast<CViewBitmap
*>(view
);
441 btm
->setTexture (file
);
442 btm
->invalidateCoords();
443 btm
->invalidateContent();
449 CGroupCell
*btgc
= dynamic_cast<CGroupCell
*>(view
);
452 btgc
->setTexture (file
);
453 btgc
->invalidateCoords();
454 btgc
->invalidateContent();
460 CGroupTable
*table
= dynamic_cast<CGroupTable
*>(view
);
463 table
->setTexture(file
);
469 // Force image width, height
470 void CGroupHTML::setImageSize(CViewBase
*view
, const CStyleParams
&style
)
472 sint32 width
= style
.Width
;
473 sint32 height
= style
.Height
;
474 sint32 maxw
= style
.MaxWidth
;
475 sint32 maxh
= style
.MaxHeight
;
477 sint32 imageWidth
, imageHeight
;
480 // get image texture size
481 // if image is being downloaded, then correct size is set after thats done
482 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
486 imageWidth
= btn
->getW(false);
487 imageHeight
= btn
->getH(false);
491 CViewBitmap
*btm
= dynamic_cast<CViewBitmap
*>(view
);
495 imageWidth
= btm
->getW(false);
496 imageHeight
= btm
->getH(false);
505 // if width/height is not requested, then use image size
506 // else recalculate missing value, keep image ratio
507 if (width
== -1 && height
== -1)
510 height
= imageHeight
;
515 if (width
== -1 || height
== -1) {
516 float ratio
= (float) imageWidth
/ std::max(1, imageHeight
);
518 width
= height
* ratio
;
520 height
= width
/ ratio
;
523 // apply max-width, max-height rules if asked
524 if (maxw
> -1 || maxh
> -1)
526 _Style
.applyCssMinMax(width
, height
, 0, 0, maxw
, maxh
);
532 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
541 CViewBitmap
*image
= dynamic_cast<CViewBitmap
*>(view
);
544 image
->setScale(true);
552 void CGroupHTML::setTextButtonStyle(CCtrlTextButton
*ctrlButton
, const CStyleParams
&style
)
554 // this will also set size for <a class="ryzom-ui-button"> treating it like "display: inline-block;"
555 if (style
.Width
> 0) ctrlButton
->setWMin(style
.Width
);
556 if (style
.Height
> 0) ctrlButton
->setHMin(style
.Height
);
558 CViewText
*pVT
= ctrlButton
->getViewText();
561 setTextStyle(pVT
, style
);
564 if (style
.hasStyle("background-color"))
566 ctrlButton
->setColor(style
.Background
.color
);
567 if (style
.hasStyle("-ryzom-background-color-over"))
569 ctrlButton
->setColorOver(style
.BackgroundColorOver
);
573 ctrlButton
->setColorOver(style
.Background
.color
);
575 ctrlButton
->setTexture("", "blank.tga", "", false);
576 ctrlButton
->setTextureOver("", "blank.tga", "");
577 ctrlButton
->setProperty("force_text_over", "true");
579 else if (style
.hasStyle("-ryzom-background-color-over"))
581 ctrlButton
->setColorOver(style
.BackgroundColorOver
);
582 ctrlButton
->setProperty("force_text_over", "true");
583 ctrlButton
->setTextureOver("blank.tga", "blank.tga", "blank.tga");
587 void CGroupHTML::setTextStyle(CViewText
*pVT
, const CStyleParams
&style
)
591 pVT
->setColor(style
.TextColor
);
592 pVT
->setFontName(style
.FontFamily
);
593 pVT
->setFontSize(style
.FontSize
, false);
594 pVT
->setEmbolden(style
.FontWeight
>= FONT_WEIGHT_BOLD
);
595 pVT
->setOblique(style
.FontOblique
);
596 pVT
->setUnderlined(style
.Underlined
);
597 pVT
->setStrikeThrough(style
.StrikeThrough
);
598 if (style
.TextShadow
.Enabled
)
600 pVT
->setShadow(true);
601 pVT
->setShadowColor(style
.TextShadow
.Color
);
602 pVT
->setShadowOutline(style
.TextShadow
.Outline
);
603 pVT
->setShadowOffset(style
.TextShadow
.X
, style
.TextShadow
.Y
);
608 // Get an url and return the local filename with the path where the url image should be
609 string
CGroupHTML::localImageName(const string
&url
)
611 string dest
= "cache/";
612 dest
+= getMD5((uint8
*)url
.c_str(), (uint32
)url
.size()).toString();
617 void CGroupHTML::pumpCurlQueue()
619 if (RunningCurls
< options
.curlMaxConnections
)
621 std::list
<CDataDownload
*>::iterator it
=Curls
.begin();
622 while(it
!= Curls
.end() && RunningCurls
< options
.curlMaxConnections
)
624 if ((*it
)->data
== NULL
)
626 LOG_DL("(%s) starting new download '%s'", _Id
.c_str(), it
->url
.c_str());
627 if (!startCurlDownload(*it
))
629 LOG_DL("(%s) failed to start '%s)'", _Id
.c_str(), it
->url
.c_str());
630 finishCurlDownload(*it
);
632 it
= Curls
.erase(it
);
641 if (RunningCurls
> 0 || !Curls
.empty())
642 LOG_DL("(%s) RunningCurls %d, _Curls %d", _Id
.c_str(), RunningCurls
, Curls
.size());
645 // Add url to MultiCurl queue and return cURL handle
646 bool CGroupHTML::startCurlDownload(CDataDownload
*download
)
650 nlwarning("Invalid MultiCurl handle, unable to download '%s'", download
->url
.c_str());
657 CHttpCacheObject cache
;
658 if (CFile::fileExists(download
->dest
))
659 cache
= CHttpCache::getInstance()->lookup(download
->dest
);
661 if (cache
.Expires
> currentTime
)
663 LOG_DL("Cache for (%s) is not expired (%s, expires:%d)", download
->url
.c_str(), download
->dest
.c_str(), cache
.Expires
- currentTime
);
667 // use browser Id so that two browsers would not use same temp file
668 download
->tmpdest
= localImageName(_Id
+ download
->dest
) + ".tmp";
670 // erase the tmp file if exists
671 if (CFile::fileExists(download
->tmpdest
))
673 CFile::deleteFile(download
->tmpdest
);
676 FILE *fp
= nlfopen (download
->tmpdest
, "wb");
679 nlwarning("Can't open file '%s' for writing: code=%d '%s'", download
->tmpdest
.c_str (), errno
, strerror(errno
));
683 CURL
*curl
= curl_easy_init();
687 CFile::deleteFile(download
->tmpdest
);
689 nlwarning("Creating cURL handle failed, unable to download '%s'", download
->url
.c_str());
692 LOG_DL("curl easy handle %p created for '%s'", curl
, download
->url
.c_str());
695 if (toLowerAscii(download
->url
.substr(0, 8)) == "https://")
697 // if supported, use custom SSL context function to load certificates
698 NLWEB::CCurlCertificates::useCertificates(curl
);
701 download
->data
= new CCurlWWWData(curl
, download
->url
);
704 // initial connection timeout, curl default is 300sec
705 curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, download
->ConnectionTimeout
);
707 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, true);
708 curl_easy_setopt(curl
, CURLOPT_URL
, download
->url
.c_str());
710 // limit curl to HTTP and HTTPS protocols only
711 curl_easy_setopt(curl
, CURLOPT_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
712 curl_easy_setopt(curl
, CURLOPT_REDIR_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
714 std::vector
<std::string
> headers
;
715 if (!cache
.Etag
.empty())
716 headers
.push_back("If-None-Match: " + cache
.Etag
);
718 if (!cache
.LastModified
.empty())
719 headers
.push_back("If-Modified-Since: " + cache
.LastModified
);
721 if (headers
.size() > 0)
722 download
->data
->sendHeaders(headers
);
725 curl_easy_setopt(curl
, CURLOPT_HEADERFUNCTION
, NLGUI::curlHeaderCallback
);
726 curl_easy_setopt(curl
, CURLOPT_WRITEHEADER
, download
->data
);
728 std::string userAgent
= options
.appName
+ "/" + options
.appVersion
;
729 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, userAgent
.c_str());
731 CUrlParser
uri(download
->url
);
732 if (!uri
.host
.empty())
733 sendCookies(curl
, uri
.host
, isTrustedDomain(uri
.host
));
735 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, fp
);
736 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
738 CURLMcode ret
= curl_multi_add_handle(MultiCurl
, curl
);
741 nlwarning("cURL multi handle %p error %d on '%s'", curl
, ret
, download
->url
.c_str());
749 void CGroupHTML::finishCurlDownload(CDataDownload
*download
)
758 nlwarning("Unknown CURL download (nullptr)");
762 // Add a image download request in the multi_curl
763 // return new textureId and download callback
764 ICurlDownloadCB
*CGroupHTML::addTextureDownload(const string
&url
, sint32
&texId
, CViewBase
*view
)
766 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
767 // data:image/png;base64,AA...==
768 if (startsWith(url
, "data:image/"))
770 texId
= rVR
.createTextureFromDataURL(url
);
774 std::string finalUrl
;
775 // load the image from local files/bnp
776 if (lookupLocalFile(finalUrl
, std::string(CFile::getPath(url
) + CFile::getFilenameWithoutExtension(url
) + ".tga").c_str(), false))
778 texId
= rVR
.createTexture(finalUrl
);
782 finalUrl
= upgradeInsecureUrl(getAbsoluteUrl(url
));
784 // use requested url for local name (cache)
785 string dest
= localImageName(url
);
786 LOG_DL("add to download '%s' dest '%s'", finalUrl
.c_str(), dest
.c_str());
788 if (CFile::fileExists(dest
) && CFile::getFileSize(dest
) > 0)
789 texId
= rVR
.createTexture(dest
);
791 texId
= rVR
.newTextureId(dest
);
793 // Search if we are not already downloading this url.
794 for(std::list
<CDataDownload
*>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
796 if((*it
)->url
== finalUrl
)
798 LOG_DL("already downloading '%s' img %p", finalUrl
.c_str(), img
);
799 TextureDownloadCB
*cb
= dynamic_cast<TextureDownloadCB
*>(*it
);
802 cb
->addTexture(texId
, view
);
803 // return pointer to shared ImageDownloadCB
808 nlwarning("Found texture download '%s', but casting to TextureDownloadCB failed", finalUrl
.c_str());
813 Curls
.push_back(new TextureDownloadCB(finalUrl
, dest
, texId
, this));
814 // as we return pointer to callback, skip starting downloads just now
819 // Add a image download request in the multi_curl
820 ICurlDownloadCB
*CGroupHTML::addImageDownload(const string
&url
, CViewBase
*img
, const CStyleParams
&style
, TImageType type
, const std::string
&placeholder
)
822 std::string finalUrl
;
823 img
->setModulateGlobalColor(style
.GlobalColor
);
825 // data:image/png;base64,AA...==
826 if (startsWith(url
, "data:image/"))
828 setImage(img
, decodeURIComponent(url
), type
);
829 setImageSize(img
, style
);
833 // load the image from local files/bnp
834 std::string image
= CFile::getPath(url
) + CFile::getFilenameWithoutExtension(url
) + ".tga";
835 if (lookupLocalFile(finalUrl
, image
.c_str(), false))
837 setImage(img
, image
, type
);
838 setImageSize(img
, style
);
842 finalUrl
= upgradeInsecureUrl(getAbsoluteUrl(url
));
844 // use requested url for local name (cache)
845 string dest
= localImageName(url
);
846 LOG_DL("add to download '%s' dest '%s' img %p", finalUrl
.c_str(), dest
.c_str(), img
);
848 // Display cached image while downloading new
849 if (type
!= OverImage
)
851 std::string temp
= dest
;
852 if (!CFile::fileExists(temp
) || CFile::getFileSize(temp
) == 0)
856 setImage(img
, temp
, type
);
857 setImageSize(img
, style
);
860 // Search if we are not already downloading this url.
861 for(std::list
<CDataDownload
*>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
863 if((*it
)->url
== finalUrl
)
865 LOG_DL("already downloading '%s' img %p", finalUrl
.c_str(), img
);
866 ImageDownloadCB
*cb
= dynamic_cast<ImageDownloadCB
*>(*it
);
869 cb
->addImage(img
, style
, type
);
870 // return pointer to shared ImageDownloadCB
875 nlwarning("Found image download '%s', but casting to ImageDownloadCB failed", finalUrl
.c_str());
880 Curls
.push_back(new ImageDownloadCB(finalUrl
, dest
, img
, style
, type
, this));
881 // as we return pointer to callback, skip starting downloads just now
886 void CGroupHTML::removeImageDownload(ICurlDownloadCB
*handle
, CViewBase
*img
)
888 ImageDownloadCB
*cb
= dynamic_cast<ImageDownloadCB
*>(handle
);
890 nlwarning("Trying to remove image from downloads, but ICurlDownloadCB pointer did not cast to ImageDownloadCB");
893 // image will be removed from handle, but handle is kept and image will be downloaded
894 cb
->removeImage(img
);
897 void CGroupHTML::initImageDownload()
899 LOG_DL("Init Image Download");
901 string pathName
= "cache";
902 if ( ! CFile::isExists( pathName
) )
903 CFile::createDirectory( pathName
);
907 // Get an url and return the local filename with the path where the bnp should be
908 string
CGroupHTML::localBnpName(const string
&url
)
910 size_t lastIndex
= url
.find_last_of("/");
911 string dest
= "user/"+url
.substr(lastIndex
+1);
915 // Add a bnp download request in the multi_curl, return true if already downloaded
916 bool CGroupHTML::addBnpDownload(string url
, const string
&action
, const string
&script
, const string
&md5sum
)
918 url
= upgradeInsecureUrl(getAbsoluteUrl(url
));
920 // Search if we are not already downloading this url.
921 for(std::list
<CDataDownload
*>::const_iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
923 if((*it
)->url
== url
)
925 LOG_DL("already downloading '%s'", url
.c_str());
930 string dest
= localBnpName(url
);
931 LOG_DL("add to download '%s' dest '%s'", url
.c_str(), dest
.c_str());
933 // create/delete the local file
934 if (NLMISC::CFile::fileExists(dest
))
936 if (action
== "override" || action
== "delete")
938 CFile::setRWAccess(dest
);
939 NLMISC::CFile::deleteFile(dest
);
946 if (action
!= "delete")
948 Curls
.push_back(new BnpDownloadCB(url
, dest
, md5sum
, script
, this));
957 void CGroupHTML::initBnpDownload()
962 LOG_DL("Init Bnp Download");
963 string pathName
= "user";
964 if ( ! CFile::isExists( pathName
) )
965 CFile::createDirectory( pathName
);
968 void CGroupHTML::addStylesheetDownload(const std::vector
<CHtmlParser::StyleLink
> links
)
970 for(uint i
= 0; i
< links
.size(); ++i
)
972 _StylesheetQueue
.push_back(links
[i
]);
973 std::string url
= getAbsoluteUrl(links
[i
].Url
);
974 _StylesheetQueue
.back().Url
= url
;
976 // push to the front of the queue
977 Curls
.push_front(new StylesheetDownloadCB(url
, localImageName(url
), this));
982 // Call this evenly to check if an element is downloaded and then manage it
983 void CGroupHTML::checkDownloads()
985 //nlassert(_CrtCheckMemory());
987 if(Curls
.empty() && _CurlWWW
== NULL
)
992 int NewRunningCurls
= 0;
993 while(CURLM_CALL_MULTI_PERFORM
== curl_multi_perform(MultiCurl
, &NewRunningCurls
))
995 LOG_DL("more to do now %d - %d curls", NewRunningCurls
, Curls
.size());
998 LOG_DL("NewRunningCurls:%d, RunningCurls:%d", NewRunningCurls
, RunningCurls
);
1000 // check which downloads are done
1003 while ((msg
= curl_multi_info_read(MultiCurl
, &msgs_left
)))
1005 LOG_DL("> (%s) msgs_left %d", _Id
.c_str(), msgs_left
);
1006 if (msg
->msg
== CURLMSG_DONE
)
1008 if (_CurlWWW
&& _CurlWWW
->Request
&& _CurlWWW
->Request
== msg
->easy_handle
)
1011 bool success
= msg
->data
.result
== CURLE_OK
;
1014 error
= curl_easy_strerror(msg
->data
.result
);
1016 LOG_DL("html download finished with curl code %d (%s)", (sint
)msg
->msg
, error
.c_str());
1017 htmlDownloadFinished(success
, error
);
1021 for(std::list
<CDataDownload
*>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
1023 if((*it
)->data
&& (*it
)->data
->Request
== msg
->easy_handle
)
1026 bool success
= msg
->data
.result
== CURLE_OK
;
1029 error
= curl_easy_strerror(msg
->data
.result
);
1031 LOG_DL("data download finished with curl code %d (%s)", (sint
)msg
->msg
, error
.c_str());
1032 dataDownloadFinished(success
, error
, *it
);
1042 RunningCurls
= NewRunningCurls
;
1047 void CGroupHTML::releaseDownloads()
1049 LOG_DL("Release Downloads");
1053 LOG_DL("(%s) stop html url '%s'", _Id
.c_str(), _CurlWWW
->Url
.c_str());
1055 curl_multi_remove_handle(MultiCurl
, _CurlWWW
->Request
);
1061 releaseDataDownloads();
1064 void CGroupHTML::releaseDataDownloads()
1066 LOG_DL("Clear pointers to %d curls", Curls
.size());
1068 // remove all queued and already started downloads
1069 for(std::list
<CDataDownload
*>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
1071 CDataDownload
&dl
= *(*it
);
1074 LOG_DL("(%s) stop data url '%s'", _Id
.c_str(), dl
.url
.c_str());
1077 curl_multi_remove_handle(MultiCurl
, dl
.data
->Request
);
1080 // close and remove temp file
1085 if (CFile::fileExists(dl
.tmpdest
))
1087 CFile::deleteFile(dl
.tmpdest
);
1091 // release CDataDownload
1096 // also clear css queue as it depends on Curls
1097 _StylesheetQueue
.clear();
1100 class CGroupListAdaptor
: public CInterfaceGroup
1103 CGroupListAdaptor(const TCtorParam
¶m
)
1104 : CInterfaceGroup(param
)
1112 // Get the W max from the parent
1113 _W
= std::min(_Parent
->getMaxWReal(), _Parent
->getWReal());
1116 CInterfaceGroup::updateCoords();
1120 // ***************************************************************************
1122 template<class A
> void popIfNotEmpty(A
&vect
) { if(!vect
.empty()) vect
.pop_back(); }
1124 // ***************************************************************************
1125 TStyle
CGroupHTML::parseStyle (const string
&str_styles
)
1128 vector
<string
> elements
;
1129 NLMISC::splitString(str_styles
, ";", elements
);
1131 for(uint i
= 0; i
< elements
.size(); ++i
)
1133 vector
<string
> style
;
1134 NLMISC::splitString(elements
[i
], ":", style
);
1135 if (style
.size() >= 2)
1137 string fullstyle
= style
[1];
1138 for (uint j
=2; j
< style
.size(); j
++)
1139 fullstyle
+= ":"+style
[j
];
1140 styles
[trim(style
[0])] = trimSeparators(fullstyle
);
1147 // ***************************************************************************
1149 void CGroupHTML::addText (const char *buf
, int len
)
1156 // Build a UTF8 string
1157 if (_ParsingLua
&& _TrustedDomain
)
1159 // we are parsing a lua script
1160 _LuaScript
+= string(buf
, buf
+ len
);
1165 // Build a unicode string
1166 CUtfStringView
inputStringView(buf
, len
);
1168 // Build the final unicode string
1171 u32char lastChar
= 0;
1172 u32char inputStringView0
= *inputStringView
.begin();
1173 for (CUtfStringView::iterator
it(inputStringView
.begin()), end(inputStringView
.end()); it
!= end
; ++it
)
1177 // special treatment for 'nbsp' (which is returned as a discreet space)
1178 if (len
== 1 && inputStringView0
== 32)
1180 // this is a nbsp entity
1186 // not nbsp, use normal white space removal routine
1187 keep
= translateChar (output
, *it
, lastChar
);
1192 CUtfStringView::append(tmp
, output
);
1202 // ***************************************************************************
1204 #define registerAnchorName(prefix) \
1206 if (present[prefix##_ID] && value[prefix##_ID]) \
1207 _AnchorName.push_back(value[prefix##_ID]); \
1210 // ***************************************************************************
1211 void CGroupHTML::beginElement (CHtmlElement
&elm
)
1214 _CurrentHTMLElement
= &elm
;
1215 _CurrentHTMLNextSibling
= elm
.nextSibling
;
1217 // set element style from css and style attribute
1218 _Style
.getStyleFor(elm
);
1219 if (!elm
.Style
.empty())
1221 _Style
.applyStyle(elm
.Style
);
1224 if (elm
.hasNonEmptyAttribute("name"))
1226 _AnchorName
.push_back(elm
.getAttribute("name"));
1228 if (elm
.hasNonEmptyAttribute("id"))
1230 _AnchorName
.push_back(elm
.getAttribute("id"));
1233 if (_Style
.Current
.DisplayBlock
)
1240 case HTML_A
: htmlA(elm
); break;
1241 case HTML_BASE
: htmlBASE(elm
); break;
1242 case HTML_BODY
: htmlBODY(elm
); break;
1243 case HTML_BR
: htmlBR(elm
); break;
1244 case HTML_BUTTON
: htmlBUTTON(elm
); break;
1245 case HTML_DD
: htmlDD(elm
); break;
1246 case HTML_DEL
: renderPseudoElement(":before", elm
); break;
1247 case HTML_DIV
: htmlDIV(elm
); break;
1248 case HTML_DL
: htmlDL(elm
); break;
1249 case HTML_DT
: htmlDT(elm
); break;
1250 case HTML_EM
: renderPseudoElement(":before", elm
); break;
1251 case HTML_FONT
: htmlFONT(elm
); break;
1252 case HTML_FORM
: htmlFORM(elm
); break;
1253 case HTML_H1
://no-break
1254 case HTML_H2
://no-break
1255 case HTML_H3
://no-break
1256 case HTML_H4
://no-break
1257 case HTML_H5
://no-break
1258 case HTML_H6
: htmlH(elm
); break;
1259 case HTML_HEAD
: htmlHEAD(elm
); break;
1260 case HTML_HR
: htmlHR(elm
); break;
1261 case HTML_HTML
: htmlHTML(elm
); break;
1262 case HTML_I
: htmlI(elm
); break;
1263 case HTML_IMG
: htmlIMG(elm
); break;
1264 case HTML_INPUT
: htmlINPUT(elm
); break;
1265 case HTML_LI
: htmlLI(elm
); break;
1266 case HTML_LUA
: htmlLUA(elm
); break;
1267 case HTML_META
: htmlMETA(elm
); break;
1268 case HTML_METER
: htmlMETER(elm
); break;
1269 case HTML_OBJECT
: htmlOBJECT(elm
); break;
1270 case HTML_OL
: htmlOL(elm
); break;
1271 case HTML_OPTION
: htmlOPTION(elm
); break;
1272 case HTML_P
: htmlP(elm
); break;
1273 case HTML_PRE
: htmlPRE(elm
); break;
1274 case HTML_PROGRESS
: htmlPROGRESS(elm
); break;
1275 case HTML_SCRIPT
: htmlSCRIPT(elm
); break;
1276 case HTML_SELECT
: htmlSELECT(elm
); break;
1277 case HTML_SMALL
: renderPseudoElement(":before", elm
); break;
1278 case HTML_SPAN
: renderPseudoElement(":before", elm
); break;
1279 case HTML_STRONG
: renderPseudoElement(":before", elm
); break;
1280 case HTML_STYLE
: htmlSTYLE(elm
); break;
1281 case HTML_TABLE
: htmlTABLE(elm
); break;
1282 case HTML_TBODY
: renderPseudoElement(":before", elm
); break;
1283 case HTML_TD
: htmlTD(elm
); break;
1284 case HTML_TEXTAREA
: htmlTEXTAREA(elm
); break;
1285 case HTML_TFOOT
: renderPseudoElement(":before", elm
); break;
1286 case HTML_TH
: htmlTH(elm
); break;
1287 case HTML_TITLE
: htmlTITLE(elm
); break;
1288 case HTML_TR
: htmlTR(elm
); break;
1289 case HTML_U
: renderPseudoElement(":before", elm
); break;
1290 case HTML_UL
: htmlUL(elm
); break;
1292 renderPseudoElement(":before", elm
);
1297 // ***************************************************************************
1298 void CGroupHTML::endElement(CHtmlElement
&elm
)
1300 _CurrentHTMLElement
= &elm
;
1304 case HTML_A
: htmlAend(elm
); break;
1305 case HTML_BASE
: break;
1306 case HTML_BODY
: renderPseudoElement(":after", elm
); break;
1307 case HTML_BR
: break;
1308 case HTML_BUTTON
: htmlBUTTONend(elm
); break;
1309 case HTML_DD
: htmlDDend(elm
); break;
1310 case HTML_DEL
: renderPseudoElement(":after", elm
); break;
1311 case HTML_DIV
: htmlDIVend(elm
); break;
1312 case HTML_DL
: htmlDLend(elm
); break;
1313 case HTML_DT
: htmlDTend(elm
); break;
1314 case HTML_EM
: renderPseudoElement(":after", elm
);break;
1315 case HTML_FONT
: break;
1316 case HTML_FORM
: htmlFORMend(elm
); break;
1317 case HTML_H1
://no-break
1318 case HTML_H2
://no-break
1319 case HTML_H3
://no-break
1320 case HTML_H4
://no-break
1321 case HTML_H5
://no-break
1322 case HTML_H6
: htmlHend(elm
); break;
1323 case HTML_HEAD
: htmlHEADend(elm
); break;
1324 case HTML_HR
: break;
1325 case HTML_HTML
: break;
1326 case HTML_I
: htmlIend(elm
); break;
1327 case HTML_IMG
: break;
1328 case HTML_INPUT
: break;
1329 case HTML_LI
: htmlLIend(elm
); break;
1330 case HTML_LUA
: htmlLUAend(elm
); break;
1331 case HTML_META
: break;
1332 case HTML_METER
: break;
1333 case HTML_OBJECT
: htmlOBJECTend(elm
); break;
1334 case HTML_OL
: htmlOLend(elm
); break;
1335 case HTML_OPTION
: htmlOPTIONend(elm
); break;
1336 case HTML_P
: htmlPend(elm
); break;
1337 case HTML_PRE
: htmlPREend(elm
); break;
1338 case HTML_SCRIPT
: htmlSCRIPTend(elm
); break;
1339 case HTML_SELECT
: htmlSELECTend(elm
); break;
1340 case HTML_SMALL
: renderPseudoElement(":after", elm
);break;
1341 case HTML_SPAN
: renderPseudoElement(":after", elm
);break;
1342 case HTML_STRONG
: renderPseudoElement(":after", elm
);break;
1343 case HTML_STYLE
: htmlSTYLEend(elm
); break;
1344 case HTML_TABLE
: htmlTABLEend(elm
); break;
1345 case HTML_TD
: htmlTDend(elm
); break;
1346 case HTML_TBODY
: renderPseudoElement(":after", elm
); break;
1347 case HTML_TEXTAREA
: break;
1348 case HTML_TFOOT
: renderPseudoElement(":after", elm
); break;
1349 case HTML_TH
: htmlTHend(elm
); break;
1350 case HTML_TITLE
: break;
1351 case HTML_TR
: htmlTRend(elm
); break;
1352 case HTML_U
: renderPseudoElement(":after", elm
); break;
1353 case HTML_UL
: htmlULend(elm
); break;
1355 renderPseudoElement(":after", elm
);
1359 if (_Style
.Current
.DisplayBlock
)
1367 // ***************************************************************************
1368 void CGroupHTML::renderPseudoElement(const std::string
&pseudo
, const CHtmlElement
&elm
)
1370 if (pseudo
!= ":before" && pseudo
!= ":after")
1373 if (!elm
.hasPseudo(pseudo
))
1377 _Style
.applyStyle(elm
.getPseudo(pseudo
));
1379 // TODO: 'content' should already be tokenized in css parser as it has all the functions for that
1380 std::string content
= trim(_Style
.getStyle("content"));
1381 if (toLowerAscii(content
) == "none" || toLowerAscii(content
) == "normal")
1387 std::string::size_type pos
= 0;
1388 // TODO: tokenize by whitespace
1389 while(pos
< content
.size())
1391 std::string::size_type start
;
1395 // counter, open-quote, close-quote, no-open-quote, no-close-quote
1396 if (content
[pos
] == '"' || content
[pos
] == '\'')
1398 char quote
= content
[pos
];
1401 while(pos
< content
.size() && content
[pos
] != quote
)
1403 if (content
[pos
] == '\\') pos
++;
1406 token
= content
.substr(start
, pos
- start
);
1409 // skip closing quote
1412 else if (content
[pos
] == 'u' && pos
< content
.size() - 6 && toLowerAscii(content
.substr(pos
, 4)) == "url(")
1414 // url(/path-to/image.jpg) / "Alt!"
1415 // url("/path to/image.jpg") / "Alt!"
1416 std::string tooltip
;
1419 // fails if url contains ')'
1420 pos
= content
.find(")", start
);
1421 if (pos
== std::string::npos
)
1424 token
= trim(content
.substr(start
, pos
- start
));
1430 while(pos
< content
.size() && content
[pos
] == ' ' && content
[pos
] != '/')
1434 if (pos
< content
.size() && content
[pos
] == '/')
1440 while(pos
< content
.size() && content
[pos
] == ' ')
1444 if (pos
< content
.size() && (content
[pos
] == '\'' || content
[pos
] == '"'))
1446 char openQuote
= content
[pos
];
1449 while(pos
< content
.size() && content
[pos
] != openQuote
)
1451 if (content
[pos
] == '\\') pos
++;
1454 tooltip
= content
.substr(start
, pos
- start
);
1456 // skip closing quote
1461 // tooltip should be quoted
1472 if (tooltip
.empty())
1474 addImage(getId() + pseudo
, token
, false, _Style
.Current
);
1478 tooltip
= trimQuotes(tooltip
);
1479 addButton(CCtrlButton::PushButton
, getId() + pseudo
, token
, token
, "", "", "", tooltip
.c_str(), _Style
.Current
);
1482 else if (content
[pos
] == 'a' && pos
< content
.size() - 7)
1486 std::string::size_type end
= 0;
1487 end
= content
.find(")", start
);
1488 if (end
!= std::string::npos
)
1490 token
= content
.substr(start
, end
- start
);
1493 if (elm
.hasAttribute(token
))
1495 addString(elm
.getAttribute(token
));
1513 // ***************************************************************************
1514 void CGroupHTML::renderDOM(CHtmlElement
&elm
)
1516 if (elm
.Type
== CHtmlElement::TEXT_NODE
)
1518 addText(elm
.Value
.c_str(), elm
.Value
.size());
1524 if (!_IgnoreChildElements
)
1526 std::list
<CHtmlElement
>::iterator it
= elm
.Children
.begin();
1527 while(it
!= elm
.Children
.end())
1534 _IgnoreChildElements
= false;
1540 // ***************************************************************************
1541 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupHTML
, std::string
, "html");
1544 // ***************************************************************************
1545 uint32
CGroupHTML::_GroupHtmlUIDPool
= 0;
1546 CGroupHTML::TGroupHtmlByUIDMap
CGroupHTML::_GroupHtmlByUID
;
1549 // ***************************************************************************
1550 CGroupHTML::CGroupHTML(const TCtorParam
¶m
)
1551 : CGroupScrollText(param
),
1552 _TimeoutValue(DEFAULT_RYZOM_CONNECTION_TIMEOUT
),
1553 _RedirectsRemaining(DEFAULT_RYZOM_REDIRECT_LIMIT
),
1554 _CurrentHTMLElement(NULL
)
1556 // add it to map of group html created
1557 _GroupHtmlUID
= ++_GroupHtmlUIDPool
; // valid assigned Id begin to 1!
1558 _GroupHtmlByUID
[_GroupHtmlUID
]= this;
1561 _TrustedDomain
= false;
1562 _ParsingLua
= false;
1563 _LuaHrefHack
= false;
1564 _IgnoreText
= false;
1565 _IgnoreChildElements
= false;
1566 _BrowseNextTime
= false;
1567 _PostNextTime
= false;
1569 _CurrentViewLink
= NULL
;
1570 _CurrentViewImage
= NULL
;
1573 _SelectOption
= false;
1574 _GroupListAdaptor
= NULL
;
1575 _UrlFragment
.clear();
1576 _RefreshUrl
.clear();
1577 _NextRefreshTime
= 0.0;
1578 _LastRefreshTime
= 0.0;
1579 _RenderNextTime
= false;
1580 _WaitingForStylesheet
= false;
1585 CWidgetManager::getInstance()->registerClockMsgTarget(this);
1588 ErrorColor
= CRGBA(255, 0, 0);
1589 LinkColor
= CRGBA(0, 0, 255);
1590 ErrorColorGlobalColor
= false;
1591 LinkColorGlobalColor
= false;
1592 TextColorGlobalColor
= false;
1598 LineSpaceFontFactor
= 0.5f
;
1599 DefaultButtonGroup
= "html_text_button";
1600 DefaultFormTextGroup
= "edit_box_widget";
1601 DefaultFormTextAreaGroup
= "edit_box_widget_multiline";
1602 DefaultFormSelectGroup
= "html_form_select_widget";
1603 DefaultFormSelectBoxMenuGroup
= "html_form_select_box_menu_widget";
1604 DefaultCheckBoxBitmapNormal
= "checkbox_normal.tga";
1605 DefaultCheckBoxBitmapPushed
= "checkbox_pushed.tga";
1606 DefaultCheckBoxBitmapOver
= "checkbox_over.tga";
1607 DefaultRadioButtonBitmapNormal
= "w_radiobutton.png";
1608 DefaultRadioButtonBitmapPushed
= "w_radiobutton_pushed.png";
1609 DefaultBackgroundBitmapView
= "bg";
1612 MultiCurl
= curl_multi_init();
1613 #ifdef CURLMOPT_MAX_HOST_CONNECTIONS
1616 // added in libcurl 7.30.0
1617 curl_multi_setopt(MultiCurl
, CURLMOPT_MAX_HOST_CONNECTIONS
, options
.curlMaxConnections
);
1618 curl_multi_setopt(MultiCurl
, CURLMOPT_PIPELINING
, 1);
1624 initImageDownload();
1627 // setup default browser style
1628 setProperty("browser_css_file", "browser.css");
1631 // ***************************************************************************
1633 CGroupHTML::~CGroupHTML()
1635 //releaseImageDownload();
1638 //nlinfo("** CGroupHTML Destroy: %x, %s, uid%d", this, _Id.c_str(), _GroupHtmlUID);
1640 /* Erase from map of Group HTML (thus requestTerminated() callback won't be called)
1641 Do it first, just because don't want requestTerminated() to be called while I'm destroying
1642 (useless and may be dangerous)
1644 _GroupHtmlByUID
.erase(_GroupHtmlUID
);
1653 curl_multi_cleanup(MultiCurl
);
1656 std::string
CGroupHTML::getProperty( const std::string
&name
) const
1663 if( name
== "title_prefix" )
1665 return _TitlePrefix
;
1668 if( name
== "error_color" )
1670 return toString( ErrorColor
);
1673 if( name
== "link_color" )
1675 return toString( LinkColor
);
1678 if( name
== "error_color_global_color" )
1680 return toString( ErrorColorGlobalColor
);
1683 if( name
== "link_color_global_color" )
1685 return toString( LinkColorGlobalColor
);
1688 if( name
== "text_color_global_color" )
1690 return toString( TextColorGlobalColor
);
1693 if( name
== "td_begin_space" )
1695 return toString( TDBeginSpace
);
1698 if( name
== "paragraph_begin_space" )
1700 return toString( PBeginSpace
);
1703 if( name
== "li_begin_space" )
1705 return toString( LIBeginSpace
);
1708 if( name
== "ul_begin_space" )
1710 return toString( ULBeginSpace
);
1713 if( name
== "ul_indent" )
1715 return toString( ULIndent
);
1718 if( name
== "multi_line_space_factor" )
1720 return toString( LineSpaceFontFactor
);
1723 if( name
== "form_text_area_group" )
1725 return DefaultFormTextGroup
;
1728 if( name
== "form_select_group" )
1730 return DefaultFormSelectGroup
;
1733 if( name
== "checkbox_bitmap_normal" )
1735 return DefaultCheckBoxBitmapNormal
;
1738 if( name
== "checkbox_bitmap_pushed" )
1740 return DefaultCheckBoxBitmapPushed
;
1743 if( name
== "checkbox_bitmap_over" )
1745 return DefaultCheckBoxBitmapOver
;
1748 if( name
== "radiobutton_bitmap_normal" )
1750 return DefaultRadioButtonBitmapNormal
;
1753 if( name
== "radiobutton_bitmap_pushed" )
1755 return DefaultRadioButtonBitmapPushed
;
1758 if( name
== "radiobutton_bitmap_over" )
1760 return DefaultRadioButtonBitmapOver
;
1763 if( name
== "background_bitmap_view" )
1765 return DefaultBackgroundBitmapView
;
1768 if( name
== "home" )
1773 if( name
== "browse_next_time" )
1775 return toString( _BrowseNextTime
);
1778 if( name
== "browse_tree" )
1783 if( name
== "browse_undo" )
1785 return _BrowseUndoButton
;
1788 if( name
== "browse_redo" )
1790 return _BrowseRedoButton
;
1793 if( name
== "browse_refresh" )
1795 return _BrowseRefreshButton
;
1798 if( name
== "timeout" )
1800 return toString( _TimeoutValue
);
1803 if( name
== "browser_css_file" )
1805 return _BrowserCssFile
;
1808 return CGroupScrollText::getProperty( name
);
1811 void CGroupHTML::setProperty( const std::string
&name
, const std::string
&value
)
1819 if( name
== "title_prefix" )
1821 _TitlePrefix
= value
;
1825 if( name
== "error_color" )
1828 if( fromString( value
, c
) )
1833 if( name
== "link_color" )
1836 if( fromString( value
, c
) )
1841 if( name
== "error_color_global_color" )
1844 if( fromString( value
, b
) )
1845 ErrorColorGlobalColor
= b
;
1849 if( name
== "link_color_global_color" )
1852 if( fromString( value
, b
) )
1853 LinkColorGlobalColor
= b
;
1857 if( name
== "text_color_global_color" )
1860 if( fromString( value
, b
) )
1861 TextColorGlobalColor
= b
;
1865 if( name
== "td_begin_space" )
1868 if( fromString( value
, i
) )
1873 if( name
== "paragraph_begin_space" )
1876 if( fromString( value
, i
) )
1881 if( name
== "li_begin_space" )
1884 if( fromString( value
, i
) )
1889 if( name
== "ul_begin_space" )
1892 if( fromString( value
, i
) )
1897 if( name
== "ul_indent" )
1900 if( fromString( value
, i
) )
1905 if( name
== "multi_line_space_factor" )
1908 if( fromString( value
, f
) )
1909 LineSpaceFontFactor
= f
;
1913 if( name
== "form_text_area_group" )
1915 DefaultFormTextGroup
= value
;
1919 if( name
== "form_select_group" )
1921 DefaultFormSelectGroup
= value
;
1925 if( name
== "checkbox_bitmap_normal" )
1927 DefaultCheckBoxBitmapNormal
= value
;
1931 if( name
== "checkbox_bitmap_pushed" )
1933 DefaultCheckBoxBitmapPushed
= value
;
1937 if( name
== "checkbox_bitmap_over" )
1939 DefaultCheckBoxBitmapOver
= value
;
1943 if( name
== "radiobutton_bitmap_normal" )
1945 DefaultRadioButtonBitmapNormal
= value
;
1949 if( name
== "radiobutton_bitmap_pushed" )
1951 DefaultRadioButtonBitmapPushed
= value
;
1955 if( name
== "radiobutton_bitmap_over" )
1957 DefaultRadioButtonBitmapOver
= value
;
1961 if( name
== "background_bitmap_view" )
1963 DefaultBackgroundBitmapView
= value
;
1967 if( name
== "home" )
1973 if( name
== "browse_next_time" )
1976 if( fromString( value
, b
) )
1977 _BrowseNextTime
= b
;
1981 if( name
== "browse_tree" )
1983 _BrowseTree
= value
;
1987 if( name
== "browse_undo" )
1989 _BrowseUndoButton
= value
;
1993 if( name
== "browse_redo" )
1995 _BrowseRedoButton
= value
;
1999 if( name
== "browse_refresh" )
2001 _BrowseRefreshButton
= value
;
2005 if( name
== "timeout" )
2008 if( fromString( value
, d
) )
2013 if( name
== "browser_css_file")
2015 _BrowserStyle
.reset();
2016 _BrowserCssFile
= value
;
2017 if (!_BrowserCssFile
.empty())
2019 std::string filename
= CPath::lookup(_BrowserCssFile
, false, true, true);
2020 if (!filename
.empty())
2023 if (in
.open(filename
))
2026 if (in
.readAll(css
))
2027 _BrowserStyle
.parseStylesheet(css
);
2029 nlwarning("Failed to read browser css from '%s'", filename
.c_str());
2033 nlwarning("Failed to open browser css file '%s'", filename
.c_str());
2038 nlwarning("Browser css file '%s' not found", _BrowserCssFile
.c_str());
2043 CGroupScrollText::setProperty( name
, value
);
2046 xmlNodePtr
CGroupHTML::serialize( xmlNodePtr parentNode
, const char *type
) const
2048 xmlNodePtr node
= CGroupScrollText::serialize( parentNode
, type
);
2052 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"html" );
2053 xmlSetProp( node
, BAD_CAST
"url", BAD_CAST _URL
.c_str() );
2054 xmlSetProp( node
, BAD_CAST
"title_prefix", BAD_CAST _TitlePrefix
.c_str() );
2055 xmlSetProp( node
, BAD_CAST
"error_color", BAD_CAST
toString( ErrorColor
).c_str() );
2056 xmlSetProp( node
, BAD_CAST
"link_color", BAD_CAST
toString( LinkColor
).c_str() );
2058 xmlSetProp( node
, BAD_CAST
"error_color_global_color",
2059 BAD_CAST
toString( ErrorColorGlobalColor
).c_str() );
2060 xmlSetProp( node
, BAD_CAST
"link_color_global_color",
2061 BAD_CAST
toString( LinkColorGlobalColor
).c_str() );
2062 xmlSetProp( node
, BAD_CAST
"text_color_global_color",
2063 BAD_CAST
toString( TextColorGlobalColor
).c_str() );
2065 xmlSetProp( node
, BAD_CAST
"td_begin_space", BAD_CAST
toString( TDBeginSpace
).c_str() );
2066 xmlSetProp( node
, BAD_CAST
"paragraph_begin_space", BAD_CAST
toString( PBeginSpace
).c_str() );
2067 xmlSetProp( node
, BAD_CAST
"li_begin_space", BAD_CAST
toString( LIBeginSpace
).c_str() );
2068 xmlSetProp( node
, BAD_CAST
"ul_begin_space", BAD_CAST
toString( ULBeginSpace
).c_str() );
2069 xmlSetProp( node
, BAD_CAST
"ul_indent", BAD_CAST
toString( ULIndent
).c_str() );
2070 xmlSetProp( node
, BAD_CAST
"multi_line_space_factor", BAD_CAST
toString( LineSpaceFontFactor
).c_str() );
2071 xmlSetProp( node
, BAD_CAST
"form_text_area_group", BAD_CAST DefaultFormTextGroup
.c_str() );
2072 xmlSetProp( node
, BAD_CAST
"form_select_group", BAD_CAST DefaultFormSelectGroup
.c_str() );
2073 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_normal", BAD_CAST DefaultCheckBoxBitmapNormal
.c_str() );
2074 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_pushed", BAD_CAST DefaultCheckBoxBitmapPushed
.c_str() );
2075 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_over", BAD_CAST DefaultCheckBoxBitmapOver
.c_str() );
2076 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_normal", BAD_CAST DefaultRadioButtonBitmapNormal
.c_str() );
2077 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_pushed", BAD_CAST DefaultRadioButtonBitmapPushed
.c_str() );
2078 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_over", BAD_CAST DefaultRadioButtonBitmapOver
.c_str() );
2079 xmlSetProp( node
, BAD_CAST
"background_bitmap_view", BAD_CAST DefaultBackgroundBitmapView
.c_str() );
2080 xmlSetProp( node
, BAD_CAST
"home", BAD_CAST Home
.c_str() );
2081 xmlSetProp( node
, BAD_CAST
"browse_next_time", BAD_CAST
toString( _BrowseNextTime
).c_str() );
2082 xmlSetProp( node
, BAD_CAST
"browse_tree", BAD_CAST _BrowseTree
.c_str() );
2083 xmlSetProp( node
, BAD_CAST
"browse_undo", BAD_CAST _BrowseUndoButton
.c_str() );
2084 xmlSetProp( node
, BAD_CAST
"browse_redo", BAD_CAST _BrowseRedoButton
.c_str() );
2085 xmlSetProp( node
, BAD_CAST
"browse_refresh", BAD_CAST _BrowseRefreshButton
.c_str() );
2086 xmlSetProp( node
, BAD_CAST
"timeout", BAD_CAST
toString( _TimeoutValue
).c_str() );
2087 xmlSetProp( node
, BAD_CAST
"browser_css_file", BAD_CAST _BrowserCssFile
.c_str() );
2092 // ***************************************************************************
2094 bool CGroupHTML::parse(xmlNodePtr cur
,CInterfaceGroup
*parentGroup
)
2096 nlassert( CWidgetManager::getInstance()->isClockMsgTarget(this));
2099 if(!CGroupScrollText::parse(cur
, parentGroup
))
2103 //nlinfo("** CGroupHTML parsed Ok: %x, %s, %s, uid%d", this, _Id.c_str(), typeid(this).name(), _GroupHtmlUID);
2108 ptr
= xmlGetProp (cur
, (xmlChar
*)"url");
2110 _URL
= (const char*)ptr
;
2112 // Bkup default for undo/redo
2115 ptr
= xmlGetProp (cur
, (xmlChar
*)"title_prefix");
2117 _TitlePrefix
= CI18N::get((const char*)ptr
);
2120 ptr
= xmlGetProp (cur
, (xmlChar
*)"error_color");
2122 ErrorColor
= convertColor(ptr
);
2123 ptr
= xmlGetProp (cur
, (xmlChar
*)"link_color");
2125 LinkColor
= convertColor(ptr
);
2126 ptr
= xmlGetProp (cur
, (xmlChar
*)"error_color_global_color");
2128 ErrorColorGlobalColor
= convertBool(ptr
);
2129 ptr
= xmlGetProp (cur
, (xmlChar
*)"link_color_global_color");
2131 LinkColorGlobalColor
= convertBool(ptr
);
2132 ptr
= xmlGetProp (cur
, (xmlChar
*)"text_color_global_color");
2134 TextColorGlobalColor
= convertBool(ptr
);
2135 ptr
= xmlGetProp (cur
, (xmlChar
*)"td_begin_space");
2137 fromString((const char*)ptr
, TDBeginSpace
);
2138 ptr
= xmlGetProp (cur
, (xmlChar
*)"paragraph_begin_space");
2140 fromString((const char*)ptr
, PBeginSpace
);
2141 ptr
= xmlGetProp (cur
, (xmlChar
*)"li_begin_space");
2143 fromString((const char*)ptr
, LIBeginSpace
);
2144 ptr
= xmlGetProp (cur
, (xmlChar
*)"ul_begin_space");
2146 fromString((const char*)ptr
, ULBeginSpace
);
2147 ptr
= xmlGetProp (cur
, (xmlChar
*)"ul_indent");
2149 fromString((const char*)ptr
, ULIndent
);
2150 ptr
= xmlGetProp (cur
, (xmlChar
*)"multi_line_space_factor");
2152 fromString((const char*)ptr
, LineSpaceFontFactor
);
2153 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_text_group");
2155 DefaultFormTextGroup
= (const char*)(ptr
);
2156 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_text_area_group");
2158 DefaultFormTextAreaGroup
= (const char*)(ptr
);
2159 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_select_group");
2161 DefaultFormSelectGroup
= (const char*)(ptr
);
2162 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_normal");
2164 DefaultCheckBoxBitmapNormal
= (const char*)(ptr
);
2165 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_pushed");
2167 DefaultCheckBoxBitmapPushed
= (const char*)(ptr
);
2168 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_over");
2170 DefaultCheckBoxBitmapOver
= (const char*)(ptr
);
2171 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_normal");
2173 DefaultRadioButtonBitmapNormal
= (const char*)(ptr
);
2174 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_pushed");
2176 DefaultRadioButtonBitmapPushed
= (const char*)(ptr
);
2177 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_over");
2179 DefaultRadioButtonBitmapOver
= (const char*)(ptr
);
2180 ptr
= xmlGetProp (cur
, (xmlChar
*)"background_bitmap_view");
2182 DefaultBackgroundBitmapView
= (const char*)(ptr
);
2183 ptr
= xmlGetProp (cur
, (xmlChar
*)"home");
2185 Home
= (const char*)(ptr
);
2186 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_next_time");
2188 _BrowseNextTime
= convertBool(ptr
);
2189 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_tree");
2191 _BrowseTree
= (const char*)ptr
;
2192 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_undo");
2194 _BrowseUndoButton
= (const char*)ptr
;
2195 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_redo");
2197 _BrowseRedoButton
= (const char*)ptr
;
2198 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_refresh");
2200 _BrowseRefreshButton
= (const char*)ptr
;
2201 ptr
= xmlGetProp (cur
, (xmlChar
*)"timeout");
2203 fromString((const char*)ptr
, _TimeoutValue
);
2205 ptr
= xmlGetProp (cur
, (xmlChar
*)"browser_css_file");
2208 setProperty("browser_css_file", (const char *)ptr
);
2214 // ***************************************************************************
2216 bool CGroupHTML::handleEvent (const NLGUI::CEventDescriptor
& eventDesc
)
2218 bool traited
= false;
2220 if (eventDesc
.getType() == NLGUI::CEventDescriptor::mouse
)
2222 const NLGUI::CEventDescriptorMouse
&mouseEvent
= (const NLGUI::CEventDescriptorMouse
&)eventDesc
;
2223 if (mouseEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mousewheel
)
2225 // Check if mouse wheel event was on any of multiline select box widgets
2226 // Must do this before CGroupScrollText
2227 for (uint i
=0; i
<_Forms
.size() && !traited
; i
++)
2229 for (uint j
=0; j
<_Forms
[i
].Entries
.size() && !traited
; j
++)
2231 if (_Forms
[i
].Entries
[j
].SelectBox
)
2233 if (_Forms
[i
].Entries
[j
].SelectBox
->handleEvent(eventDesc
))
2245 traited
= CGroupScrollText::handleEvent (eventDesc
);
2247 if (eventDesc
.getType() == NLGUI::CEventDescriptor::system
)
2249 const NLGUI::CEventDescriptorSystem
&systemEvent
= (const NLGUI::CEventDescriptorSystem
&) eventDesc
;
2250 if (systemEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::clocktick
)
2255 if (systemEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::activecalledonparent
)
2257 if (!((NLGUI::CEventDescriptorActiveCalledOnParent
&) systemEvent
).getActive())
2259 // stop refresh when window gets hidden
2260 _NextRefreshTime
= 0;
2267 // ***************************************************************************
2269 void CGroupHTML::endParagraph()
2276 // ***************************************************************************
2278 void CGroupHTML::newParagraph(uint beginSpace
)
2280 // Add a new paragraph
2281 CGroupParagraph
*newParagraph
= new CGroupParagraph(CViewBase::TCtorParam());
2282 newParagraph
->setId(getCurrentGroup()->getId() + ":PARAGRAPH" + toString(getNextAutoIdSeq()));
2283 newParagraph
->setResizeFromChildH(true);
2285 newParagraph
->setMarginLeft(getIndent());
2286 if (!_Style
.Current
.TextAlign
.empty())
2288 if (_Style
.Current
.TextAlign
== "left")
2289 newParagraph
->setTextAlign(CGroupParagraph::AlignLeft
);
2290 else if (_Style
.Current
.TextAlign
== "center")
2291 newParagraph
->setTextAlign(CGroupParagraph::AlignCenter
);
2292 else if (_Style
.Current
.TextAlign
== "right")
2293 newParagraph
->setTextAlign(CGroupParagraph::AlignRight
);
2294 else if (_Style
.Current
.TextAlign
== "justify")
2295 newParagraph
->setTextAlign(CGroupParagraph::AlignJustify
);
2299 addHtmlGroup (newParagraph
, beginSpace
);
2300 _Paragraph
= newParagraph
;
2305 // ***************************************************************************
2307 void CGroupHTML::browse(const char *url
)
2310 pushUrlUndoRedo(url
);
2312 // do the browse, with no undo/redo
2316 // ***************************************************************************
2317 void CGroupHTML::refresh()
2320 doBrowse(_URL
.c_str(), true);
2323 // ***************************************************************************
2324 void CGroupHTML::doBrowse(const char *url
, bool force
)
2326 LOG_DL("(%s) Browsing URL : '%s'", _Id
.c_str(), url
);
2328 CUrlParser
uri(url
);
2329 if (!uri
.hash
.empty())
2331 // Anchor to scroll after page has loaded
2332 _UrlFragment
= uri
.hash
;
2334 uri
.inherit(_DocumentUrl
);
2337 // compare urls and see if we only navigating to new anchor
2338 if (!force
&& _DocumentUrl
== uri
.toString())
2340 // scroll happens in updateCoords()
2346 _UrlFragment
.clear();
2349 _URL
= uri
.toString();
2350 _BrowseNextTime
= true;
2351 _WaitingForStylesheet
= false;
2353 // if a BrowseTree is bound to us, try to select the node that opens this URL (auto-locate)
2354 if(!_BrowseTree
.empty())
2356 CGroupTree
*groupTree
=dynamic_cast<CGroupTree
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseTree
));
2359 string nodeId
= selectTreeNodeRecurs(groupTree
->getRootNode(), url
);
2363 groupTree
->selectNodeById(nodeId
);
2369 // ***************************************************************************
2371 void CGroupHTML::browseError (const char *msg
)
2375 // Get the list group from CGroupScrollText
2378 CViewText
*viewText
= new CViewText ("", (string("Error : ")+msg
).c_str());
2379 viewText
->setColor (ErrorColor
);
2380 viewText
->setModulateGlobalColor(ErrorColorGlobalColor
);
2381 viewText
->setMultiLine (true);
2382 getParagraph()->addChild (viewText
);
2383 if(!_TitlePrefix
.empty())
2384 setTitle (_TitlePrefix
);
2386 updateRefreshButton();
2390 void CGroupHTML::browseErrorHtml(const std::string
&html
)
2395 renderHtmlString(html
);
2397 updateRefreshButton();
2401 // ***************************************************************************
2403 bool CGroupHTML::isBrowsing()
2405 // do not show spinning cursor for image downloads (!Curls.empty())
2406 return _BrowseNextTime
|| _PostNextTime
|| _RenderNextTime
||
2407 _Browsing
|| _WaitingForStylesheet
||
2411 // ***************************************************************************
2413 void CGroupHTML::updateCoords()
2415 CGroupScrollText::updateCoords();
2417 // all elements are in their correct place, tell scrollbar to scroll to anchor
2418 if (!_Browsing
&& !_UrlFragment
.empty())
2420 doBrowseAnchor(_UrlFragment
);
2421 _UrlFragment
.clear();
2424 if (!m_HtmlBackground
.isEmpty() || !m_BodyBackground
.isEmpty())
2426 // get scroll offset from list
2427 CGroupList
*list
= getList();
2430 CInterfaceElement
* vp
= list
->getParentPos() ? list
->getParentPos() : this;
2431 sint htmlW
= std::max(vp
->getWReal(), list
->getWReal());
2432 sint htmlH
= list
->getHReal();
2433 sint htmlX
= list
->getXReal() + list
->getOfsX();
2434 sint htmlY
= list
->getYReal() + list
->getOfsY();
2436 if (!m_HtmlBackground
.isEmpty())
2438 m_HtmlBackground
.setFillViewport(true);
2439 m_HtmlBackground
.setBorderArea(htmlX
, htmlY
, htmlW
, htmlH
);
2440 m_HtmlBackground
.setPaddingArea(htmlX
, htmlY
, htmlW
, htmlH
);
2441 m_HtmlBackground
.setContentArea(htmlX
, htmlY
, htmlW
, htmlH
);
2444 if (!m_BodyBackground
.isEmpty())
2446 // TODO: html padding + html border
2447 m_BodyBackground
.setBorderArea(htmlX
, htmlY
, htmlW
, htmlH
);
2448 // TODO: html padding + html border + body border
2449 m_BodyBackground
.setPaddingArea(htmlX
, htmlY
, htmlW
, htmlH
);
2450 // TODO: html padding + html_border + body padding
2451 m_BodyBackground
.setContentArea(htmlX
, htmlY
, htmlW
, htmlH
);
2457 // ***************************************************************************
2459 bool CGroupHTML::translateChar(u32char
&output
, u32char input
, u32char lastCharParam
) const
2464 // char is between table elements
2465 // TODO: only whitespace is handled, text is added to either TD, or after TABLE (should be before)
2466 bool tableWhitespace
= getTable() && (_Cells
.empty() || _Cells
.back() == NULL
);
2470 // Return / tab only in <PRE> mode
2474 if (tableWhitespace
)
2480 // Get the last char
2481 u32char lastChar
= lastCharParam
;
2483 lastChar
= getLastChar();
2484 keep
= ((lastChar
!= (u32char
)' ') &&
2485 (lastChar
!= 0)) || getPRE() || (_CurrentViewImage
&& (lastChar
== 0));
2487 input
= (u32char
)' ';
2493 if (tableWhitespace
)
2499 // Get the last char
2500 u32char lastChar
= lastCharParam
;
2502 lastChar
= getLastChar();
2503 keep
= ((lastChar
!= (u32char
)' ') &&
2504 (lastChar
!= (u32char
)'\n') &&
2505 (lastChar
!= 0)) || getPRE() || (_CurrentViewImage
&& (lastChar
== 0));
2522 // ***************************************************************************
2524 void CGroupHTML::registerAnchor(CInterfaceElement
* elm
)
2526 if (!_AnchorName
.empty())
2528 for(uint32 i
=0; i
< _AnchorName
.size(); ++i
)
2530 // filter out duplicates and register only first
2531 if (!_AnchorName
[i
].empty() && _Anchors
.count(_AnchorName
[i
]) == 0)
2533 _Anchors
[_AnchorName
[i
]] = elm
;
2537 _AnchorName
.clear();
2541 // ***************************************************************************
2542 bool CGroupHTML::isSameStyle(CViewLink
*text
, const CStyleParams
&style
) const
2544 if (!text
) return false;
2546 bool embolden
= style
.FontWeight
>= FONT_WEIGHT_BOLD
;
2547 bool sameShadow
= style
.TextShadow
.Enabled
&& text
->getShadow();
2548 if (sameShadow
&& style
.TextShadow
.Enabled
)
2551 text
->getShadowOffset(sx
, sy
);
2552 sameShadow
= (style
.TextShadow
.Color
== text
->getShadowColor());
2553 sameShadow
= sameShadow
&& (style
.TextShadow
.Outline
== text
->getShadowOutline());
2554 sameShadow
= sameShadow
&& (style
.TextShadow
.X
== sx
) && (style
.TextShadow
.Y
== sy
);
2556 // Compatible with current parameters ?
2557 return sameShadow
&&
2558 (style
.TextColor
== text
->getColor()) &&
2559 (style
.FontFamily
== text
->getFontName()) &&
2560 (style
.FontSize
== (uint
)text
->getFontSize()) &&
2561 (style
.Underlined
== text
->getUnderlined()) &&
2562 (style
.StrikeThrough
== text
->getStrikeThrough()) &&
2563 (embolden
== text
->getEmbolden()) &&
2564 (style
.FontOblique
== text
->getOblique()) &&
2565 (getLink() == text
->Link
) &&
2566 (style
.GlobalColorText
== text
->getModulateGlobalColor());
2569 // ***************************************************************************
2570 void CGroupHTML::newTextButton(const std::string
&text
, const std::string
&tpl
)
2572 _CurrentViewLink
= NULL
;
2573 _CurrentViewImage
= NULL
;
2575 // Action handler parameters : "name=group_html_id|form=id_of_the_form|submit_button=button_name"
2576 string param
= "name=" + this->_Id
+ "|url=" + getLink();
2578 if (!_AnchorName
.empty())
2579 name
= _AnchorName
.back();
2581 typedef pair
<string
, string
> TTmplParam
;
2582 vector
<TTmplParam
> tmplParams
;
2583 tmplParams
.push_back(TTmplParam("id", ""));
2584 tmplParams
.push_back(TTmplParam("onclick", "browse"));
2585 tmplParams
.push_back(TTmplParam("onclick_param", param
));
2586 tmplParams
.push_back(TTmplParam("active", "true"));
2587 CInterfaceGroup
*buttonGroup
= CWidgetManager::getInstance()->getParser()->createGroupInstance(tpl
, getId()+":"+name
, tmplParams
);
2590 nlinfo("Text button template '%s' not found", tpl
.c_str());
2593 buttonGroup
->setId(getId()+":"+name
);
2595 // Add the ctrl button
2596 CCtrlTextButton
*ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("button"));
2597 if (!ctrlButton
) ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("b"));
2600 nlinfo("Text button template '%s' is missing :button or :b text element", tpl
.c_str());
2603 ctrlButton
->setModulateGlobalColorAll(_Style
.Current
.GlobalColor
);
2604 ctrlButton
->setTextModulateGlobalColorNormal(_Style
.Current
.GlobalColorText
);
2605 ctrlButton
->setTextModulateGlobalColorOver(_Style
.Current
.GlobalColorText
);
2606 ctrlButton
->setTextModulateGlobalColorPushed(_Style
.Current
.GlobalColorText
);
2608 // Translate the tooltip
2609 ctrlButton
->setText(text
);
2610 ctrlButton
->setDefaultContextHelp(std::string(getLinkTitle()));
2611 // empty url / button disabled
2612 ctrlButton
->setFrozen(*getLink() == '\0');
2614 setTextButtonStyle(ctrlButton
, _Style
.Current
);
2616 _Paragraph
->addChild(buttonGroup
);
2619 // ***************************************************************************
2620 void CGroupHTML::newTextLink(const std::string
&text
)
2622 CViewLink
*newLink
= new CViewLink(CViewBase::TCtorParam());
2625 newLink
->Link
= getLink();
2626 newLink
->LinkTitle
= getLinkTitle();
2627 if (!newLink
->Link
.empty())
2629 newLink
->setHTMLView (this);
2630 newLink
->setActionOnLeftClick("browse");
2631 newLink
->setParamsOnLeftClick("name=" + getId() + "|url=" + newLink
->Link
);
2634 newLink
->setText(text
);
2635 newLink
->setMultiLineSpace((uint
)((float)(_Style
.Current
.FontSize
)*LineSpaceFontFactor
));
2636 newLink
->setMultiLine(true);
2637 newLink
->setModulateGlobalColor(_Style
.Current
.GlobalColorText
);
2638 setTextStyle(newLink
, _Style
.Current
);
2640 registerAnchor(newLink
);
2642 if (getA() && !newLink
->Link
.empty())
2643 getParagraph()->addChildLink(newLink
);
2645 getParagraph()->addChild(newLink
);
2647 _CurrentViewLink
= newLink
;
2648 _CurrentViewImage
= NULL
;
2651 // ***************************************************************************
2653 void CGroupHTML::addString(const std::string
&str
)
2655 string tmpStr
= str
;
2659 string _str
= tmpStr
;
2660 string::size_type p
= _str
.find('#');
2661 if (p
== string::npos
)
2663 tmpStr
= CI18N::get(_str
);
2667 string cmd
= _str
.substr(0, p
);
2668 string arg
= _str
.substr(p
+1);
2672 uint year
, month
, day
;
2673 sscanf(arg
.c_str(), "%d/%d/%d", &year
, &month
, &day
);
2674 tmpStr
= CI18N::get( "uiMFIDate");
2676 year
+= (year
> 70 ? 1900 : 2000);
2678 strFindReplace(tmpStr
, "%year", toString("%d", year
) );
2679 strFindReplace(tmpStr
, "%month", CI18N::get(toString("uiMonth%02d", month
)) );
2680 strFindReplace(tmpStr
, "%day", toString("%d", day
) );
2692 _ObjectScript
+= tmpStr
;
2694 else if (_SelectOption
)
2696 if (!(_Forms
.empty()))
2698 if (!_Forms
.back().Entries
.empty())
2700 _SelectOptionStr
+= tmpStr
;
2713 CStyleParams
&style
= _Style
.Current
;
2718 if (_CurrentViewLink
)
2720 bool skipLine
= !_CurrentViewLink
->getText().empty() && *(_CurrentViewLink
->getText().rbegin()) == '\n';
2721 if (!skipLine
&& isSameStyle(_CurrentViewLink
, style
))
2724 _CurrentViewLink
->setText(_CurrentViewLink
->getText()+tmpStr
);
2725 _CurrentViewLink
->invalidateContent();
2733 if (getA() && string(getLinkClass()) == "ryzom-ui-button")
2734 newTextButton(tmpStr
, DefaultButtonGroup
);
2736 newTextLink(tmpStr
);
2741 // ***************************************************************************
2743 void CGroupHTML::addImage(const std::string
&id
, const std::string
&img
, bool reloadImg
, const CStyleParams
&style
)
2752 // No more text in this text view
2753 _CurrentViewLink
= NULL
;
2756 CViewBitmap
*newImage
= new CViewBitmap (TCtorParam());
2757 newImage
->setId(id
);
2759 addImageDownload(img
, newImage
, style
, NormalImage
);
2760 newImage
->setRenderLayer(getRenderLayer()+1);
2762 getParagraph()->addChild(newImage
);
2765 setImageSize(newImage
, style
);
2768 // ***************************************************************************
2770 CInterfaceGroup
*CGroupHTML::addTextArea(const std::string
&templateName
, const char *name
, uint rows
, uint cols
, bool multiLine
, const std::string
&content
, uint maxlength
)
2779 // No more text in this text view
2780 _CurrentViewLink
= NULL
;
2782 CStyleParams
&style
= _Style
.Current
;
2784 // override cols/rows values from style
2785 if (style
.Width
> 0) cols
= style
.Width
/ style
.FontSize
;
2786 if (style
.Height
> 0) rows
= style
.Height
/ style
.FontSize
;
2789 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
2790 templateParams
.push_back (std::pair
<std::string
,std::string
> ("w", toString (cols
*style
.FontSize
)));
2791 templateParams
.push_back (std::pair
<std::string
,std::string
> ("id", name
));
2792 templateParams
.push_back (std::pair
<std::string
,std::string
> ("prompt", ""));
2793 templateParams
.push_back (std::pair
<std::string
,std::string
> ("multiline", multiLine
?"true":"false"));
2794 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontsize", toString (style
.FontSize
)));
2795 templateParams
.push_back (std::pair
<std::string
,std::string
> ("color", style
.TextColor
.toString()));
2796 if (style
.FontWeight
>= FONT_WEIGHT_BOLD
)
2797 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontweight", "bold"));
2798 if (style
.FontOblique
)
2799 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontstyle", "oblique"));
2801 templateParams
.push_back (std::pair
<std::string
,std::string
> ("multi_min_line", toString(rows
)));
2802 templateParams
.push_back (std::pair
<std::string
,std::string
> ("want_return", multiLine
?"true":"false"));
2803 templateParams
.push_back (std::pair
<std::string
,std::string
> ("onenter", ""));
2804 templateParams
.push_back (std::pair
<std::string
,std::string
> ("enter_recover_focus", "false"));
2806 templateParams
.push_back (std::pair
<std::string
,std::string
> ("max_num_chars", toString(maxlength
)));
2807 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow", toString(style
.TextShadow
.Enabled
)));
2808 if (style
.TextShadow
.Enabled
)
2810 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_x", toString(style
.TextShadow
.X
)));
2811 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_y", toString(style
.TextShadow
.Y
)));
2812 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_color", style
.TextShadow
.Color
.toString()));
2813 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_outline", toString(style
.TextShadow
.Outline
)));
2816 CInterfaceGroup
*textArea
= CWidgetManager::getInstance()->getParser()->createGroupInstance (templateName
.c_str(),
2817 getParagraph()->getId(), templateParams
.empty()?NULL
:&(templateParams
[0]), (uint
)templateParams
.size());
2823 CGroupEditBox
*eb
= dynamic_cast<CGroupEditBox
*>(textArea
->getGroup("eb"));
2826 eb
->setInputString(decodeHTMLEntities(content
));
2827 if (style
.hasStyle("background-color"))
2829 CViewBitmap
*bg
= dynamic_cast<CViewBitmap
*>(eb
->getView("bg"));
2832 bg
->setTexture("blank.tga");
2833 bg
->setColor(style
.Background
.color
);
2838 textArea
->invalidateCoords();
2839 getParagraph()->addChild (textArea
);
2846 // Not group created
2850 // ***************************************************************************
2851 CDBGroupComboBox
*CGroupHTML::addComboBox(const std::string
&templateName
, const char *name
)
2863 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
2864 templateParams
.push_back (std::pair
<std::string
,std::string
> ("id", name
));
2865 CInterfaceGroup
*group
= CWidgetManager::getInstance()->getParser()->createGroupInstance (templateName
.c_str(),
2866 getParagraph()->getId(), templateParams
.empty()?NULL
:&(templateParams
[0]), (uint
)templateParams
.size());
2872 CDBGroupComboBox
*cb
= dynamic_cast<CDBGroupComboBox
*>(group
);
2875 nlwarning("'%s' template has bad type, combo box expected", templateName
.c_str());
2881 getParagraph()->addChild (cb
);
2888 // Not group created
2892 // ***************************************************************************
2893 CGroupMenu
*CGroupHTML::addSelectBox(const std::string
&templateName
, const char *name
)
2903 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
2904 templateParams
.push_back(std::pair
<std::string
,std::string
> ("id", name
));
2905 CInterfaceGroup
*group
= CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName
.c_str(),
2906 getParagraph()->getId(), &(templateParams
[0]), (uint
)templateParams
.size());
2912 CGroupMenu
*sb
= dynamic_cast<CGroupMenu
*>(group
);
2915 nlwarning("'%s' template has bad type, CGroupMenu expected", templateName
.c_str());
2921 getParagraph()->addChild (sb
);
2931 // ***************************************************************************
2933 CCtrlButton
*CGroupHTML::addButton(CCtrlButton::EType type
, const std::string
&name
, const std::string
&normalBitmap
, const std::string
&pushedBitmap
,
2934 const std::string
&overBitmap
, const char *actionHandler
, const char *actionHandlerParams
,
2935 const std::string
&tooltip
, const CStyleParams
&style
)
2944 // Add the ctrl button
2945 CCtrlButton
*ctrlButton
= new CCtrlButton(TCtorParam());
2948 ctrlButton
->setId(name
);
2952 if (startsWith(normalBitmap
, "data:image/"))
2954 normal
= decodeURIComponent(normalBitmap
);
2958 // Load only tga files.. (conversion in dds filename is done in the lookup procedure)
2959 normal
= normalBitmap
.empty()?"":CFile::getPath(normalBitmap
) + CFile::getFilenameWithoutExtension(normalBitmap
) + ".tga";
2961 // if the image doesn't exist on local, we check in the cache
2962 if(!CPath::exists(normal
))
2964 // search in the compressed texture
2965 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
2966 sint32 id
= rVR
.getTextureIdFromName(normal
);
2969 normal
= localImageName(normalBitmap
);
2970 addImageDownload(normalBitmap
, ctrlButton
, style
);
2976 if (startsWith(pushedBitmap
, "data:image/"))
2978 pushed
= decodeURIComponent(pushedBitmap
);
2982 pushed
= pushedBitmap
.empty()?"":CFile::getPath(pushedBitmap
) + CFile::getFilenameWithoutExtension(pushedBitmap
) + ".tga";
2983 // if the image doesn't exist on local, we check in the cache, don't download it because the "normal" will already setuped it
2984 if(!CPath::exists(pushed
))
2986 // search in the compressed texture
2987 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
2988 sint32 id
= rVR
.getTextureIdFromName(pushed
);
2991 pushed
= localImageName(pushedBitmap
);
2997 if (startsWith(overBitmap
, "data:image/"))
2999 over
= decodeURIComponent(overBitmap
);
3003 over
= overBitmap
.empty()?"":CFile::getPath(overBitmap
) + CFile::getFilenameWithoutExtension(overBitmap
) + ".tga";
3004 // schedule mouseover bitmap for download if its different from normal
3005 if (!over
.empty() && !CPath::exists(over
))
3007 if (overBitmap
!= normalBitmap
)
3009 over
= localImageName(overBitmap
);
3010 addImageDownload(overBitmap
, ctrlButton
, style
, OverImage
);
3015 ctrlButton
->setType (type
);
3016 if (!normal
.empty())
3017 ctrlButton
->setTexture (normal
);
3018 if (!pushed
.empty())
3019 ctrlButton
->setTexturePushed (pushed
);
3021 ctrlButton
->setTextureOver (over
);
3022 ctrlButton
->setModulateGlobalColorAll (style
.GlobalColor
);
3023 ctrlButton
->setActionOnLeftClick (actionHandler
);
3024 ctrlButton
->setParamsOnLeftClick (actionHandlerParams
);
3026 // Translate the tooltip or display raw text (tooltip from webig)
3027 if (!tooltip
.empty())
3029 if (CI18N::hasTranslation(tooltip
))
3031 ctrlButton
->setDefaultContextHelp(CI18N::get(tooltip
));
3032 //ctrlButton->setOnContextHelp(CI18N::get(tooltip).toString());
3036 ctrlButton
->setDefaultContextHelp(tooltip
);
3037 //ctrlButton->setOnContextHelp(string(tooltip));
3040 ctrlButton
->setInstantContextHelp(true);
3041 ctrlButton
->setToolTipParent(TTMouse
);
3042 ctrlButton
->setToolTipParentPosRef(Hotspot_TTAuto
);
3043 ctrlButton
->setToolTipPosRef(Hotspot_TTAuto
);
3046 getParagraph()->addChild (ctrlButton
);
3049 setImageSize(ctrlButton
, style
);
3054 // ***************************************************************************
3056 void CGroupHTML::flushString()
3058 _CurrentViewLink
= NULL
;
3061 // ***************************************************************************
3063 void CGroupHTML::clearContext()
3079 _FormSubmit
.clear();
3083 _AnchorName
.clear();
3084 _CellParams
.clear();
3087 _ReadingHeadTag
= false;
3088 _IgnoreHeadTag
= false;
3089 _IgnoreBaseUrlTag
= false;
3091 m_TableRowBackgroundColor
.clear();
3095 releaseDataDownloads();
3098 // ***************************************************************************
3100 u32char
CGroupHTML::getLastChar() const
3102 if (_CurrentViewLink
)
3104 ::u32string str
= CUtfStringView(_CurrentViewLink
->getText()).toUtf32(); // FIXME: Optimize reverse UTF iteration
3106 return str
[str
.length()-1];
3111 // ***************************************************************************
3113 void CGroupHTML::paragraphChange ()
3115 _CurrentViewLink
= NULL
;
3116 _CurrentViewImage
= NULL
;
3117 CGroupParagraph
*paragraph
= getParagraph();
3120 // Number of child in this paragraph
3121 uint numChild
= paragraph
->getNumChildren();
3124 // Get the last child
3125 CViewBase
*child
= paragraph
->getChild(numChild
-1);
3127 // Is this a string view ?
3128 _CurrentViewLink
= dynamic_cast<CViewLink
*>(child
);
3129 _CurrentViewImage
= dynamic_cast<CViewBitmap
*>(child
);
3134 // ***************************************************************************
3136 CInterfaceGroup
*CGroupHTML::getCurrentGroup()
3138 if (!_Cells
.empty() && _Cells
.back())
3139 return _Cells
.back()->Group
;
3141 return _GroupListAdaptor
;
3144 // ***************************************************************************
3146 void CGroupHTML::addHtmlGroup (CInterfaceGroup
*group
, uint beginSpace
)
3151 registerAnchor(group
);
3153 if (!_DivName
.empty())
3155 group
->setName(_DivName
);
3156 _Groups
.push_back(group
);
3159 group
->setSizeRef(CInterfaceElement::width
);
3161 // Compute begin space between paragraph and tables
3162 // * If first in group, no begin space
3164 // Pointer on the current paragraph (can be a table too)
3165 CGroupParagraph
*p
= dynamic_cast<CGroupParagraph
*>(group
);
3167 CInterfaceGroup
*parentGroup
= CGroupHTML::getCurrentGroup();
3168 const std::vector
<CInterfaceGroup
*> &groups
= parentGroup
->getGroups ();
3169 group
->setParent(parentGroup
);
3170 group
->setParentSize(parentGroup
);
3173 group
->setParentPos(parentGroup
);
3174 group
->setPosRef(Hotspot_TL
);
3175 group
->setParentPosRef(Hotspot_TL
);
3180 // Last is a paragraph ?
3181 group
->setParentPos(groups
.back());
3182 group
->setPosRef(Hotspot_TL
);
3183 group
->setParentPosRef(Hotspot_BL
);
3186 // Set the begin space
3188 p
->setTopSpace(beginSpace
);
3190 group
->setY(-(sint32
)beginSpace
);
3191 parentGroup
->addGroup (group
);
3194 // ***************************************************************************
3196 void CGroupHTML::setContainerTitle (const std::string
&title
)
3198 CInterfaceElement
*parent
= getParent();
3201 if ((parent
= parent
->getParent()))
3203 CGroupContainer
*container
= dynamic_cast<CGroupContainer
*>(parent
);
3206 container
->setTitle(title
);
3212 void CGroupHTML::setTitle(const std::string
&title
)
3214 if(_TitlePrefix
.empty())
3215 _TitleString
= title
;
3217 _TitleString
= _TitlePrefix
+ " - " + title
;
3219 setContainerTitle(_TitleString
);
3222 std::string
CGroupHTML::getTitle() const {
3223 return _TitleString
;
3226 // ***************************************************************************
3228 bool CGroupHTML::lookupLocalFile (string
&result
, const char *url
, bool isUrl
)
3233 if (toLowerAscii(result
).find("file:") == 0 && result
.size() > 5)
3235 result
= result
.substr(5, result
.size()-5);
3237 else if (result
.find("://") != string::npos
|| result
.find("//") == 0)
3239 // http://, https://, etc or protocol-less url "//domain.com/image.png"
3243 tmp
= CPath::lookup (CFile::getFilename(result
), false, false, false);
3246 // try to find in local directory
3247 tmp
= CPath::lookup (result
, false, false, true);
3252 // Normalize the path
3254 //result = "file:"+toLowerAscii(CPath::standardizePath (CPath::getFullPath (CFile::getPath(result)))+CFile::getFilename(result));*/
3255 result
= "file:/"+tmp
;
3262 // Is it a texture in the big texture ?
3263 if (CViewRenderer::getInstance()->getTextureIdFromName (result
) >= 0)
3269 // This is not a file in the CPath, let libwww open this URL
3276 // ***************************************************************************
3278 void CGroupHTML::submitForm(uint button
, sint32 x
, sint32 y
)
3280 if (button
>= _FormSubmit
.size())
3283 for(uint formId
= 0; formId
< _Forms
.size(); formId
++)
3285 // case sensitive search (user id is lowecase, auto id is uppercase)
3286 if (_Forms
[formId
].id
== _FormSubmit
[button
].form
)
3288 _PostNextTime
= true;
3289 _PostFormId
= formId
;
3290 _PostFormAction
= _FormSubmit
[button
].formAction
;
3291 _PostFormSubmitType
= _FormSubmit
[button
].type
;
3292 _PostFormSubmitButton
= _FormSubmit
[button
].name
;
3293 _PostFormSubmitValue
= _FormSubmit
[button
].value
;
3294 _PostFormSubmitX
= x
;
3295 _PostFormSubmitY
= y
;
3301 nlwarning("Unable to find form '%s' to submit (button '%s')", _FormSubmit
[button
].form
.c_str(), _FormSubmit
[button
].name
.c_str());
3304 // ***************************************************************************
3306 void CGroupHTML::setupBackground(CSSBackgroundRenderer
*bg
)
3310 bg
->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
3311 bg
->setBackground(_Style
.Current
.Background
);
3312 bg
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
3314 bg
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
3316 if (!_Style
.Current
.Background
.image
.empty())
3317 addTextureDownload(_Style
.Current
.Background
.image
, bg
->TextureId
, this);
3320 // ***************************************************************************
3322 void CGroupHTML::setBackgroundColor (const CRGBA
&bgcolor
)
3324 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
3325 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
3327 view
->setActive(false);
3329 m_HtmlBackground
.setColor(bgcolor
);
3332 // ***************************************************************************
3334 void CGroupHTML::setBackground (const string
&bgtex
, bool scale
, bool tile
)
3336 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
3337 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
3339 view
->setActive(false);
3341 m_HtmlBackground
.setImage(bgtex
);
3342 m_HtmlBackground
.setImageRepeat(tile
);
3343 m_HtmlBackground
.setImageCover(scale
);
3346 addTextureDownload(bgtex
, m_HtmlBackground
.TextureId
, this);
3350 struct CButtonFreezer
: public CInterfaceElementVisitor
3352 virtual void visitCtrl(CCtrlBase
*ctrl
)
3354 CCtrlBaseButton
*textButt
= dynamic_cast<CCtrlTextButton
*>(ctrl
);
3357 textButt
->setFrozen(true);
3362 // ***************************************************************************
3364 void CGroupHTML::handle ()
3366 H_AUTO(RZ_Interface_Html_handle
)
3368 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
3370 // handle curl downloads
3373 // handle refresh timer
3374 if (_NextRefreshTime
> 0 && _NextRefreshTime
<= (times
.thisFrameMs
/ 1000.0f
) )
3376 // there might be valid uses for 0sec refresh, but two in a row is probably a mistake
3377 if (_NextRefreshTime
- _LastRefreshTime
>= 1.0)
3379 _LastRefreshTime
= _NextRefreshTime
;
3380 doBrowse(_RefreshUrl
.c_str());
3383 nlwarning("Ignore second 0sec http-equiv refresh in a row (url '%s')", _URL
.c_str());
3385 _NextRefreshTime
= 0;
3390 // still transfering html page
3391 if (_TimeoutValue
!= 0 && _ConnectingTimeout
<= ( times
.thisFrameMs
/ 1000.0f
) )
3393 browseError(("Connection timeout : "+_URL
).c_str());
3397 if (_RenderNextTime
)
3399 _RenderNextTime
= false;
3400 renderHtmlString(_DocumentHtml
);
3403 if (_WaitingForStylesheet
)
3408 if (_BrowseNextTime
|| _PostNextTime
)
3411 _ConnectingTimeout
= ( times
.thisFrameMs
/ 1000.0f
) + _TimeoutValue
;
3413 // freeze form buttons
3414 CButtonFreezer freezer
;
3415 this->visit(&freezer
);
3422 bool isLocal
= lookupLocalFile (finalUrl
, _URL
.c_str(), true);
3426 CUrlParser
uri (_URL
);
3427 _TrustedDomain
= isTrustedDomain(uri
.host
);
3428 _DocumentDomain
= uri
.host
;
3430 // file is probably from bnp (ingame help)
3433 doBrowseLocalFile(finalUrl
);
3437 SFormFields formfields
;
3440 buildHTTPPostParams(formfields
);
3441 // _URL is set from form.Action
3446 // Add custom get params from child classes
3447 addHTTPGetParams (finalUrl
, _TrustedDomain
);
3450 doBrowseRemoteUrl(finalUrl
, "", _PostNextTime
, formfields
);
3453 _BrowseNextTime
= false;
3454 _PostNextTime
= false;
3458 // ***************************************************************************
3459 void CGroupHTML::buildHTTPPostParams (SFormFields
&formfields
)
3461 // Add text area text
3464 if (_PostFormId
>= _Forms
.size())
3466 nlwarning("(%s) invalid form index %d, _Forms %d", _Id
.c_str(), _PostFormId
, _Forms
.size());
3470 CForm
&form
= _Forms
[_PostFormId
];
3472 // button can override form action url (and methor, but we only do POST)
3473 _URL
= _PostFormAction
.empty() ? form
.Action
: _PostFormAction
;
3475 CUrlParser
uri(_URL
);
3476 _TrustedDomain
= isTrustedDomain(uri
.host
);
3477 _DocumentDomain
= uri
.host
;
3479 for (i
=0; i
<form
.Entries
.size(); i
++)
3482 bool addEntry
= false;
3484 if (form
.Entries
[i
].TextArea
)
3486 // Get the edit box view
3487 CInterfaceGroup
*group
= form
.Entries
[i
].TextArea
->getGroup ("eb");
3490 // Should be a CGroupEditBox
3491 CGroupEditBox
*editBox
= dynamic_cast<CGroupEditBox
*>(group
);
3494 entryData
= editBox
->getViewText()->getText();
3499 else if (form
.Entries
[i
].Checkbox
)
3501 // todo handle unicode POST here
3502 if (form
.Entries
[i
].Checkbox
->getPushed ())
3504 entryData
= form
.Entries
[i
].Value
;
3508 else if (form
.Entries
[i
].ComboBox
)
3510 CDBGroupComboBox
*cb
= form
.Entries
[i
].ComboBox
;
3511 entryData
= form
.Entries
[i
].SelectValues
[cb
->getSelection()];
3514 else if (form
.Entries
[i
].SelectBox
)
3516 CGroupMenu
*sb
= form
.Entries
[i
].SelectBox
;
3517 CGroupSubMenu
*rootMenu
= sb
->getRootMenu();
3520 for(uint j
=0; j
<rootMenu
->getNumLine(); ++j
)
3522 CInterfaceGroup
*ig
= rootMenu
->getUserGroupLeft(j
);
3525 CCtrlBaseButton
*cb
= dynamic_cast<CCtrlBaseButton
*>(ig
->getCtrl("b"));
3526 if (cb
&& cb
->getPushed())
3527 formfields
.add(form
.Entries
[i
].Name
, form
.Entries
[i
].SelectValues
[j
]);
3532 // This is a hidden value
3535 entryData
= form
.Entries
[i
].Value
;
3542 formfields
.add(form
.Entries
[i
].Name
, entryData
);
3546 if (_PostFormSubmitType
== "image")
3548 // Add the button coordinates
3549 if (_PostFormSubmitButton
.find_first_of("[") == string::npos
)
3551 formfields
.add(_PostFormSubmitButton
+ "_x", NLMISC::toString(_PostFormSubmitX
));
3552 formfields
.add(_PostFormSubmitButton
+ "_y", NLMISC::toString(_PostFormSubmitY
));
3556 formfields
.add(_PostFormSubmitButton
, NLMISC::toString(_PostFormSubmitX
));
3557 formfields
.add(_PostFormSubmitButton
, NLMISC::toString(_PostFormSubmitY
));
3561 formfields
.add(_PostFormSubmitButton
, _PostFormSubmitValue
);
3563 // Add custom params from child classes
3564 addHTTPPostParams(formfields
, _TrustedDomain
);
3567 // ***************************************************************************
3568 void CGroupHTML::doBrowseLocalFile(const std::string
&uri
)
3571 updateRefreshButton();
3573 std::string filename
;
3574 if (toLowerAscii(uri
).find("file:/") == 0)
3576 filename
= uri
.substr(6, uri
.size() - 6);
3583 LOG_DL("browse local file '%s'", filename
.c_str());
3585 _TrustedDomain
= true;
3586 _DocumentDomain
= "localhost";
3589 if (in
.open(filename
))
3595 in
.getline(buf
, 1024);
3596 html
+= std::string(buf
) + "\n";
3600 if (!renderHtmlString(html
))
3602 browseError((string("Failed to parse html from file : ")+filename
).c_str());
3607 browseError((string("The page address is malformed : ")+filename
).c_str());
3611 // ***************************************************************************
3612 void CGroupHTML::doBrowseRemoteUrl(std::string url
, const std::string
&referer
, bool doPost
, const SFormFields
&formfields
)
3614 // stop all downloads from previous page
3616 updateRefreshButton();
3619 if(_TitlePrefix
.empty())
3620 setTitle (CI18N::get("uiPleaseWait"));
3622 setTitle (_TitlePrefix
+ " - " + CI18N::get("uiPleaseWait"));
3624 url
= upgradeInsecureUrl(url
);
3626 LOG_DL("(%s) browse url (trusted=%s) '%s', referer='%s', post='%s', nb form values %d",
3627 _Id
.c_str(), (_TrustedDomain
? "true" :"false"), url
.c_str(), referer
.c_str(), (doPost
? "true" : "false"), formfields
.Values
.size());
3631 browseError(string("Invalid MultCurl handle, loading url failed : "+url
).c_str());
3635 CURL
*curl
= curl_easy_init();
3638 nlwarning("(%s) failed to create curl handle", _Id
.c_str());
3639 browseError(string("Failed to create cURL handle : " + url
).c_str());
3644 if (toLowerAscii(url
.substr(0, 8)) == "https://")
3646 // if supported, use custom SSL context function to load certificates
3647 NLWEB::CCurlCertificates::useCertificates(curl
);
3650 // do not follow redirects, we have own handler
3651 curl_easy_setopt(curl
, CURLOPT_FOLLOWLOCATION
, 0);
3653 curl_easy_setopt(curl
, CURLOPT_FRESH_CONNECT
, 1);
3655 // tell curl to use compression if possible (gzip, deflate)
3656 // leaving this empty allows all encodings that curl supports
3657 //curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
3659 // limit curl to HTTP and HTTPS protocols only
3660 curl_easy_setopt(curl
, CURLOPT_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
3661 curl_easy_setopt(curl
, CURLOPT_REDIR_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
3664 curl_easy_setopt(curl
, CURLOPT_URL
, url
.c_str());
3667 std::string userAgent
= options
.appName
+ "/" + options
.appVersion
;
3668 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, userAgent
.c_str());
3671 sendCookies(curl
, _DocumentDomain
, _TrustedDomain
);
3674 if (!referer
.empty())
3676 curl_easy_setopt(curl
, CURLOPT_REFERER
, referer
.c_str());
3677 LOG_DL("(%s) set referer '%s'", _Id
.c_str(), referer
.c_str());
3682 // serialize form data and add it to curl
3684 for(uint i
=0; i
<formfields
.Values
.size(); ++i
)
3686 char * escapedName
= curl_easy_escape(curl
, formfields
.Values
[i
].name
.c_str(), formfields
.Values
[i
].name
.size());
3687 char * escapedValue
= curl_easy_escape(curl
, formfields
.Values
[i
].value
.c_str(), formfields
.Values
[i
].value
.size());
3692 data
+= std::string(escapedName
) + "=" + escapedValue
;
3694 curl_free(escapedName
);
3695 curl_free(escapedValue
);
3697 curl_easy_setopt(curl
, CURLOPT_POST
, 1);
3698 curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, data
.size());
3699 curl_easy_setopt(curl
, CURLOPT_COPYPOSTFIELDS
, data
.c_str());
3703 curl_easy_setopt(curl
, CURLOPT_HTTPGET
, 1);
3707 _CurlWWW
= new CCurlWWWData(curl
, url
);
3709 // set the language code used by the client
3710 std::vector
<std::string
> headers
;
3711 headers
.push_back("Accept-Language: "+options
.languageCode
);
3712 headers
.push_back("Accept-Charset: utf-8");
3713 _CurlWWW
->sendHeaders(headers
);
3715 // catch headers for redirect
3716 curl_easy_setopt(curl
, CURLOPT_HEADERFUNCTION
, NLGUI::curlHeaderCallback
);
3717 curl_easy_setopt(curl
, CURLOPT_WRITEHEADER
, _CurlWWW
);
3720 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, NLGUI::curlDataCallback
);
3721 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, _CurlWWW
);
3723 #ifdef LOG_CURL_PROGRESS
3724 // progress callback
3725 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 0);
3726 curl_easy_setopt(curl
, CURLOPT_XFERINFOFUNCTION
, NLGUI::curlProgressCallback
);
3727 curl_easy_setopt(curl
, CURLOPT_XFERINFODATA
, _CurlWWW
);
3730 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 1);
3734 curl_multi_add_handle(MultiCurl
, curl
);
3736 // start the transfer
3737 int NewRunningCurls
= 0;
3738 curl_multi_perform(MultiCurl
, &NewRunningCurls
);
3741 _RedirectsRemaining
= DEFAULT_RYZOM_REDIRECT_LIMIT
;
3744 // ***************************************************************************
3745 void CGroupHTML::htmlDownloadFinished(bool success
, const std::string
&error
)
3749 CUrlParser
uri(_CurlWWW
->Url
);
3751 // potentially unwanted chars
3752 std::string url
= _CurlWWW
->Url
;
3753 url
= strFindReplaceAll(url
, string("<"), string("%3C"));
3754 url
= strFindReplaceAll(url
, string(">"), string("%3E"));
3755 url
= strFindReplaceAll(url
, string("\""), string("%22"));
3756 url
= strFindReplaceAll(url
, string("'"), string("%27"));
3759 err
= "<html><head><title>cURL error</title></head><body>";
3760 err
+= "<h1>Connection failed with cURL error</h1>";
3762 err
+= "<hr>(" + uri
.scheme
+ "://" + uri
.host
+ ") <a href=\"" + url
+ "\">reload</a>";
3763 err
+= "</body></html>";
3764 browseErrorHtml(err
);
3768 // received content from remote
3769 std::string content
= trim(_CurlWWW
->Content
);
3771 // save HSTS header from all requests regardless of HTTP code
3772 if (_CurlWWW
->hasHSTSHeader())
3774 CUrlParser
uri(_CurlWWW
->Url
);
3775 CStrictTransportSecurity::getInstance()->setFromHeader(uri
.host
, _CurlWWW
->getHSTSHeader());
3778 receiveCookies(_CurlWWW
->Request
, _DocumentDomain
, _TrustedDomain
);
3781 curl_easy_getinfo(_CurlWWW
->Request
, CURLINFO_RESPONSE_CODE
, &code
);
3782 LOG_DL("(%s) web transfer '%p' completed with http code %d, url (len %d) '%s'", _Id
.c_str(), _CurlWWW
->Request
, code
, _CurlWWW
->Url
.size(), _CurlWWW
->Url
.c_str());
3784 if ((code
>= 301 && code
<= 303) || code
== 307 || code
== 308)
3786 if (_RedirectsRemaining
< 0)
3788 browseError(string("Redirect limit reached : " + _URL
).c_str());
3792 // redirect, get the location and try browse again
3793 // we cant use curl redirection because 'addHTTPGetParams()' must be called on new destination
3794 std::string
location(_CurlWWW
->getLocationHeader());
3795 if (location
.empty())
3797 browseError(string("Request was redirected, but location was not set : "+_URL
).c_str());
3801 LOG_DL("(%s) request (%d) redirected to (len %d) '%s'", _Id
.c_str(), _RedirectsRemaining
, location
.size(), location
.c_str());
3802 location
= getAbsoluteUrl(location
);
3804 _PostNextTime
= false;
3805 _RedirectsRemaining
--;
3807 doBrowse(location
.c_str());
3809 else if ( (code
< 200 || code
>= 300) )
3811 // catches 304 not modified, but html is not in cache anyway
3812 // if server did not send any error back
3813 if (content
.empty())
3815 content
= string("<html><head><title>ERROR</title></head><body><h1>Connection failed</h1><p>HTTP code '" + toString((sint32
)code
) + "'</p><p>URL '" + _CurlWWW
->Url
+ "'</p></body></html>");
3820 std::string contentType
;
3821 CURLcode res
= curl_easy_getinfo(_CurlWWW
->Request
, CURLINFO_CONTENT_TYPE
, &ch
);
3822 if (res
== CURLE_OK
&& ch
!= NULL
)
3827 htmlDownloadFinished(content
, contentType
, code
);
3829 // clear curl handler
3832 curl_multi_remove_handle(MultiCurl
, _CurlWWW
->Request
);
3838 // refresh button uses _CurlWWW. refresh button may stay disabled if
3839 // there is no css files to download and page is rendered before _CurlWWW is freed
3840 updateRefreshButton();
3843 void CGroupHTML::dataDownloadFinished(bool success
, const std::string
&error
, CDataDownload
*data
)
3847 CUrlParser
uri(data
->url
);
3848 if (!uri
.host
.empty())
3850 receiveCookies(data
->data
->Request
, uri
.host
, isTrustedDomain(uri
.host
));
3854 curl_easy_getinfo(data
->data
->Request
, CURLINFO_RESPONSE_CODE
, &code
);
3856 LOG_DL("(%s) transfer '%p' completed with http code %d, url (len %d) '%s'", _Id
.c_str(), data
->data
->Request
, code
, data
->url
.size(), data
->url
.c_str());
3857 curl_multi_remove_handle(MultiCurl
, data
->data
->Request
);
3859 // save HSTS header from all requests regardless of HTTP code
3862 if (data
->data
->hasHSTSHeader())
3864 CStrictTransportSecurity::getInstance()->setFromHeader(uri
.host
, data
->data
->getHSTSHeader());
3867 // 2XX success, 304 Not Modified
3868 if ((code
>= 200 && code
<= 204) || code
== 304)
3870 CHttpCacheObject obj
;
3871 obj
.Expires
= data
->data
->getExpires();
3872 obj
.Etag
= data
->data
->getEtag();
3873 obj
.LastModified
= data
->data
->getLastModified();
3875 CHttpCache::getInstance()->store(data
->dest
, obj
);
3876 if (code
== 304 && CFile::fileExists(data
->tmpdest
))
3878 CFile::deleteFile(data
->tmpdest
);
3881 else if ((code
>= 301 && code
<= 303) || code
== 307 || code
== 308)
3883 if (data
->redirects
< DEFAULT_RYZOM_REDIRECT_LIMIT
)
3885 std::string
location(data
->data
->getLocationHeader());
3886 if (!location
.empty())
3888 CUrlParser
uri(location
);
3889 if (!uri
.isAbsolute())
3891 uri
.inherit(data
->url
);
3892 location
= uri
.toString();
3895 // clear old request state, and curl easy handle
3899 data
->url
= location
;
3902 // push same request in the front of the queue
3903 // cache filename is based of original url
3904 Curls
.push_front(data
);
3906 LOG_DL("Redirect '%s'", location
.c_str());
3907 // no finished callback called, so cleanup old temp
3908 if (CFile::fileExists(data
->tmpdest
))
3910 CFile::deleteFile(data
->tmpdest
);
3915 nlwarning("Redirected to empty url '%s'", data
->url
.c_str());
3919 nlwarning("Redirect limit reached for '%s'", data
->url
.c_str());
3924 nlwarning("HTTP request failed with code [%d] for '%s'\n",code
, data
->url
.c_str());
3926 if (CFile::fileExists(data
->dest
))
3928 CFile::deleteFile(data
->dest
);
3934 nlwarning("DATA download failed '%s', error '%s'", data
->url
.c_str(), error
.c_str());
3937 finishCurlDownload(data
);
3940 void CGroupHTML::htmlDownloadFinished(const std::string
&content
, const std::string
&type
, long code
)
3942 LOG_DL("(%s) HTML download finished, content length %d, type '%s', code %d", _Id
.c_str(), content
.size(), type
.c_str(), code
);
3944 // create <html> markup for image downloads
3945 if (type
.find("image/") == 0 && !content
.empty())
3949 std::string dest
= localImageName(_URL
);
3952 out
.serialBuffer((uint8
*)(content
.c_str()), content
.size());
3954 LOG_DL("(%s) image saved to '%s', url '%s'", _Id
.c_str(), dest
.c_str(), _URL
.c_str());
3958 // create html code with image url inside and do the request again
3959 renderHtmlString("<html><head><title>"+_URL
+"</title></head><body><img src=\"" + _URL
+ "\"></body></html>");
3961 else if (_TrustedDomain
&& type
.find("text/lua") == 0)
3963 setTitle(_TitleString
);
3965 _LuaScript
= "\nlocal __CURRENT_WINDOW__=\""+this->_Id
+"\" \n"+content
;
3966 CLuaManager::getInstance().executeLuaScript(_LuaScript
, true);
3969 // disable refresh button
3971 // disable redo into this url
3976 // Sanitize downloaded HTML UTF-8 encoding, and render
3977 renderHtmlString(CUtfStringView(content
).toUtf8(true));
3981 // ***************************************************************************
3982 void CGroupHTML::cssDownloadFinished(const std::string
&url
, const std::string
&local
)
3984 for(std::vector
<CHtmlParser::StyleLink
>::iterator it
= _StylesheetQueue
.begin();
3985 it
!= _StylesheetQueue
.end(); ++it
)
3989 // read downloaded file into HtmlStyles
3990 if (CFile::fileExists(local
) && it
->Index
< _HtmlStyles
.size())
3995 if (!in
.readAll(_HtmlStyles
[it
->Index
]))
3997 nlwarning("Failed to read downloaded css file(%s), url(%s)", local
.c_str(), url
.c_str());
4002 _StylesheetQueue
.erase(it
);
4008 void CGroupHTML::renderDocument()
4010 if (!Curls
.empty() && !_StylesheetQueue
.empty())
4012 // waiting for stylesheets to finish downloading
4015 _WaitingForStylesheet
= false;
4017 //TGameTime renderStart = CTime::getLocalTime();
4019 // clear previous state and page
4023 // process all <style> and <link rel=stylesheet> elements
4024 for(uint i
= 0; i
< _HtmlStyles
.size(); ++i
)
4026 if (!_HtmlStyles
[i
].empty())
4028 _Style
.parseStylesheet(_HtmlStyles
[i
]);
4031 _HtmlStyles
.clear();
4033 std::list
<CHtmlElement
>::iterator it
= _HtmlDOM
.Children
.begin();
4034 while(it
!= _HtmlDOM
.Children
.end())
4042 //TGameTime renderStop = CTime::getLocalTime();
4043 //nlwarning("[%s] render: %.1fms (%s)\n", _Id.c_str(), (renderStop - renderStart), _URL.c_str());
4046 // ***************************************************************************
4048 bool CGroupHTML::renderHtmlString(const std::string
&html
)
4052 // if we are already rendering, then queue up the next page
4055 _DocumentHtml
= html
;
4056 _RenderNextTime
= true;
4062 _DocumentUrl
= _URL
;
4063 _DocumentHtml
= html
;
4064 _NextRefreshTime
= 0;
4065 _RefreshUrl
.clear();
4067 if (trim(html
).empty())
4072 // clear previous page and state
4084 // start new rendering
4085 _HtmlDOM
= CHtmlElement(CHtmlElement::NONE
, "<root>");
4086 _CurrentHTMLElement
= NULL
;
4087 success
= parseHtml(html
);
4090 _WaitingForStylesheet
= !_StylesheetQueue
.empty();
4095 std::string error
= "ERROR: HTML parse failed.";
4096 error
+= toString("\nsize %d bytes", html
.size());
4097 error
+= toString("\n---start---\n%s\n---end---\n", html
.c_str());
4098 browseError(error
.c_str());
4105 // ***************************************************************************
4106 void CGroupHTML::doBrowseAnchor(const std::string
&anchor
)
4108 if (_Anchors
.count(anchor
) == 0)
4113 CInterfaceElement
*pIE
= _Anchors
.find(anchor
)->second
;
4116 // hotspot depends on vertical/horizontal scrollbar
4117 CCtrlScroll
*pSB
= getScrollBar();
4120 pSB
->ensureVisible(pIE
, Hotspot_Tx
, Hotspot_Tx
);
4125 // ***************************************************************************
4127 void CGroupHTML::draw ()
4129 uint8 CurrentAlpha
= 255;
4130 // search a parent container
4131 CInterfaceGroup
*gr
= getParent();
4134 if (gr
->isGroupContainer())
4136 CGroupContainer
*gc
= static_cast<CGroupContainer
*>(gr
);
4137 CurrentAlpha
= gc
->getCurrentContainerAlpha();
4140 gr
= gr
->getParent();
4142 m_HtmlBackground
.CurrentAlpha
= CurrentAlpha
;
4143 m_BodyBackground
.CurrentAlpha
= CurrentAlpha
;
4145 m_HtmlBackground
.draw();
4146 m_BodyBackground
.draw();
4147 CGroupScrollText::draw ();
4150 // ***************************************************************************
4152 void CGroupHTML::beginBuild ()
4157 void CGroupHTML::endBuild ()
4159 // set the browser as complete
4161 updateRefreshButton();
4163 // check that the title is set, or reset it (in the case the page
4164 // does not provide a title)
4165 if (_TitleString
.empty())
4167 setTitle(_TitlePrefix
);
4173 // ***************************************************************************
4175 void CGroupHTML::addHTTPGetParams (string
&/* url */, bool /*trustedDomain*/)
4179 // ***************************************************************************
4181 void CGroupHTML::addHTTPPostParams (SFormFields
&/* formfields */, bool /*trustedDomain*/)
4185 // ***************************************************************************
4187 string
CGroupHTML::home () const
4192 // ***************************************************************************
4194 void CGroupHTML::removeContent ()
4196 // Remove old document
4197 if (!_GroupListAdaptor
)
4199 _GroupListAdaptor
= new CGroupListAdaptor(CViewBase::TCtorParam()); // deleted by the list
4200 _GroupListAdaptor
->setId(getList()->getId() + ":GLA");
4201 _GroupListAdaptor
->setResizeFromChildH(true);
4202 getList()->addChild (_GroupListAdaptor
, true);
4205 // Group list adaptor not exist ?
4206 _GroupListAdaptor
->clearGroups();
4207 _GroupListAdaptor
->clearControls();
4208 _GroupListAdaptor
->clearViews();
4209 CWidgetManager::getInstance()->clearViewUnders();
4210 CWidgetManager::getInstance()->clearCtrlsUnders();
4212 // Clear all the context
4215 // Reset default background
4216 m_HtmlBackground
.clear();
4217 m_BodyBackground
.clear();
4219 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
4220 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
4222 view
->setActive(false);
4227 // ***************************************************************************
4228 const std::string
&CGroupHTML::selectTreeNodeRecurs(CGroupTree::SNode
*node
, const std::string
&url
)
4230 static std::string emptyString
;
4236 // if this node match
4237 if(actionLaunchUrlRecurs(node
->AHName
, node
->AHParams
, url
))
4241 // fails => look into children
4244 for(uint i
=0;i
<node
->Children
.size();i
++)
4246 const string
&childRes
= selectTreeNodeRecurs(node
->Children
[i
], url
);
4247 if(!childRes
.empty())
4256 // ***************************************************************************
4257 bool CGroupHTML::actionLaunchUrlRecurs(const std::string
&ah
, const std::string
¶ms
, const std::string
&url
)
4259 // check if this action match
4260 if( (ah
=="launch_help" || ah
=="browse") && IActionHandler::getParam (params
, "url") == url
)
4264 // can be a proc that contains launch_help/browse => look recurs
4267 const std::string
&procName
= params
;
4268 // look into this proc
4269 uint numActions
= CWidgetManager::getInstance()->getParser()->getProcedureNumActions(procName
);
4270 for(uint i
=0;i
<numActions
;i
++)
4272 string procAh
, procParams
;
4273 if( CWidgetManager::getInstance()->getParser()->getProcedureAction(procName
, i
, procAh
, procParams
))
4275 // recurs proc if needed!
4276 if (actionLaunchUrlRecurs(procAh
, procParams
, url
))
4285 // ***************************************************************************
4286 void CGroupHTML::clearRefresh()
4289 updateRefreshButton();
4292 // ***************************************************************************
4293 void CGroupHTML::clearUndoRedo()
4295 // erase any undo/redo
4296 _BrowseUndo
.clear();
4297 _BrowseRedo
.clear();
4299 // update buttons validation
4300 updateUndoRedoButtons();
4303 // ***************************************************************************
4304 void CGroupHTML::pushUrlUndoRedo(const std::string
&url
)
4306 // if same url, no op
4310 // erase any redo, push undo, set current
4311 _BrowseRedo
.clear();
4312 if(!_AskedUrl
.empty())
4313 _BrowseUndo
.push_back(_AskedUrl
);
4317 while(_BrowseUndo
.size()>MaxUrlUndoRedo
)
4318 _BrowseUndo
.pop_front();
4320 // update buttons validation
4321 updateUndoRedoButtons();
4324 // ***************************************************************************
4325 void CGroupHTML::browseUndo()
4327 if(_BrowseUndo
.empty())
4330 // push to redo, pop undo, and set current
4331 if (!_AskedUrl
.empty())
4332 _BrowseRedo
.push_front(_AskedUrl
);
4334 _AskedUrl
= _BrowseUndo
.back();
4335 _BrowseUndo
.pop_back();
4337 // update buttons validation
4338 updateUndoRedoButtons();
4340 // and then browse the undoed url, with no undo/redo
4341 doBrowse(_AskedUrl
.c_str());
4344 // ***************************************************************************
4345 void CGroupHTML::browseRedo()
4347 if(_BrowseRedo
.empty())
4350 // push to undo, pop redo, and set current
4351 _BrowseUndo
.push_back(_AskedUrl
);
4352 _AskedUrl
= _BrowseRedo
.front();
4353 _BrowseRedo
.pop_front();
4355 // update buttons validation
4356 updateUndoRedoButtons();
4358 // and then browse the redoed url, with no undo/redo
4359 doBrowse(_AskedUrl
.c_str());
4362 // ***************************************************************************
4363 void CGroupHTML::updateUndoRedoButtons()
4365 CCtrlBaseButton
*butUndo
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseUndoButton
));
4366 CCtrlBaseButton
*butRedo
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseRedoButton
));
4368 // gray according to list size
4370 butUndo
->setFrozen(_BrowseUndo
.empty());
4372 butRedo
->setFrozen(_BrowseRedo
.empty());
4375 // ***************************************************************************
4376 void CGroupHTML::updateRefreshButton()
4378 CCtrlBaseButton
*butRefresh
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseRefreshButton
));
4381 // connecting, rendering, or is missing url
4382 bool frozen
= _CurlWWW
|| _Browsing
|| _URL
.empty();
4383 butRefresh
->setFrozen(frozen
);
4387 // ***************************************************************************
4389 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupHTMLInputOffset
, std::string
, "html_input_offset");
4391 CGroupHTMLInputOffset::CGroupHTMLInputOffset(const TCtorParam
¶m
)
4392 : CInterfaceGroup(param
),
4397 xmlNodePtr
CGroupHTMLInputOffset::serialize( xmlNodePtr parentNode
, const char *type
) const
4399 xmlNodePtr node
= CInterfaceGroup::serialize( parentNode
, type
);
4403 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"html_input_offset" );
4404 xmlSetProp( node
, BAD_CAST
"y_offset", BAD_CAST
toString( Offset
).c_str() );
4409 // ***************************************************************************
4410 bool CGroupHTMLInputOffset::parse(xmlNodePtr cur
, CInterfaceGroup
*parentGroup
)
4412 if (!CInterfaceGroup::parse(cur
, parentGroup
)) return false;
4415 ptr
= xmlGetProp (cur
, (xmlChar
*)"y_offset");
4417 fromString((const char*)ptr
, Offset
);
4421 // ***************************************************************************
4422 int CGroupHTML::luaParseHtml(CLuaState
&ls
)
4424 const char *funcName
= "parseHtml";
4425 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4426 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4427 std::string html
= ls
.toString(1);
4434 int CGroupHTML::luaClearRefresh(CLuaState
&ls
)
4436 const char *funcName
= "clearRefresh";
4437 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4444 int CGroupHTML::luaClearUndoRedo(CLuaState
&ls
)
4446 const char *funcName
= "clearUndoRedo";
4447 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4453 // ***************************************************************************
4454 int CGroupHTML::luaBrowse(CLuaState
&ls
)
4456 const char *funcName
= "browse";
4457 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4458 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4459 browse(ls
.toString(1));
4463 // ***************************************************************************
4464 int CGroupHTML::luaRefresh(CLuaState
&ls
)
4466 const char *funcName
= "refresh";
4467 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4472 // ***************************************************************************
4473 int CGroupHTML::luaRemoveContent(CLuaState
&ls
)
4475 const char *funcName
= "removeContent";
4476 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4481 // ***************************************************************************
4482 int CGroupHTML::luaRenderHtml(CLuaState
&ls
)
4484 const char *funcName
= "renderHtml";
4485 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4486 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4487 std::string html
= ls
.toString(1);
4489 // Always trust domain if rendered from lua
4490 _TrustedDomain
= true;
4491 renderHtmlString(html
);
4496 // ***************************************************************************
4497 int CGroupHTML::luaSetBackground(CLuaState
&ls
)
4499 const char *funcName
= "setBackground";
4500 CLuaIHM::checkArgCount(ls
, funcName
, 3);
4501 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4502 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4503 CLuaIHM::checkArgType(ls
, funcName
, 3, LUA_TBOOLEAN
);
4504 std::string image
= ls
.toString(1);
4505 bool scale
= ls
.toBoolean(2);
4506 bool repeat
= ls
.toBoolean(3);
4508 setBackground(image
, scale
, repeat
);
4513 // ***************************************************************************
4514 int CGroupHTML::luaInsertText(CLuaState
&ls
)
4516 const char *funcName
= "insertText";
4517 CLuaIHM::checkArgCount(ls
, funcName
, 3);
4518 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4519 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TSTRING
);
4520 CLuaIHM::checkArgType(ls
, funcName
, 3, LUA_TBOOLEAN
);
4522 string name
= ls
.toString(1);
4523 string text
= ls
.toString(2);
4525 if (!_Forms
.empty())
4527 for (uint i
=0; i
<_Forms
.back().Entries
.size(); i
++)
4529 if (_Forms
.back().Entries
[i
].TextArea
&& _Forms
.back().Entries
[i
].Name
== name
)
4531 // Get the edit box view
4532 CInterfaceGroup
*group
= _Forms
.back().Entries
[i
].TextArea
->getGroup ("eb");
4535 // Should be a CGroupEditBox
4536 CGroupEditBox
*editBox
= dynamic_cast<CGroupEditBox
*>(group
);
4538 editBox
->writeString(text
, false, ls
.toBoolean(3));
4547 // ***************************************************************************
4548 int CGroupHTML::luaAddString(CLuaState
&ls
)
4550 const char *funcName
= "addString";
4551 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4552 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4553 addString(ls
.toString(1));
4557 // ***************************************************************************
4558 int CGroupHTML::luaAddImage(CLuaState
&ls
)
4560 const char *funcName
= "addImage";
4561 CLuaIHM::checkArgCount(ls
, funcName
, 2);
4562 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4563 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4571 style
.GlobalColor
= ls
.toBoolean(2);
4573 string url
= getLink();
4576 string params
= "name=" + getId() + "|url=" + getLink ();
4577 addButton(CCtrlButton::PushButton
, "", ls
.toString(1), ls
.toString(1),
4578 "", "browse", params
.c_str(), "", style
);
4582 addImage("", ls
.toString(1), false, style
);
4589 // ***************************************************************************
4590 int CGroupHTML::luaShowDiv(CLuaState
&ls
)
4592 const char *funcName
= "showDiv";
4593 CLuaIHM::checkArgCount(ls
, funcName
, 2);
4594 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4595 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4597 if (!_Groups
.empty())
4599 for (uint i
=0; i
<_Groups
.size(); i
++)
4601 CInterfaceGroup
*group
= _Groups
[i
];
4602 if (group
->getName() == ls
.toString(1))
4604 group
->setActive(ls
.toBoolean(2));
4611 // ***************************************************************************
4612 void CGroupHTML::setURL(const std::string
&url
)
4614 browse(url
.c_str());
4617 void CGroupHTML::setHTML(const std::string
&html
)
4619 renderHtmlString(html
);
4622 void CGroupHTML::setHome(const std::string
&home
)
4627 // ***************************************************************************
4628 void CGroupHTML::parseStylesheetFile(const std::string
&fname
)
4631 if (css
.open(fname
))
4633 uint32 remaining
= css
.getFileSize();
4634 std::string content
;
4636 while(!css
.eof() && remaining
> 0)
4638 const uint BUF_SIZE
= 4096;
4641 uint32 readJustNow
= std::min(remaining
, BUF_SIZE
);
4642 css
.serialBuffer((uint8
*)&buf
, readJustNow
);
4643 content
.append(buf
, readJustNow
);
4644 remaining
-= readJustNow
;
4647 _Style
.parseStylesheet(content
);
4649 catch(const Exception
&e
)
4651 nlwarning("exception while reading css file '%s'", e
.what());
4656 nlwarning("Stylesheet file '%s' not found (%s)", fname
.c_str(), _URL
.c_str());
4660 // ***************************************************************************
4661 bool CGroupHTML::parseHtml(const std::string
&htmlString
)
4663 CHtmlElement
*parsedDOM
;
4664 if (_CurrentHTMLElement
== NULL
)
4666 // parse under <root> element (clean dom)
4667 parsedDOM
= &_HtmlDOM
;
4671 // parse under currently rendered <lua> element
4672 parsedDOM
= _CurrentHTMLElement
;
4675 std::vector
<CHtmlParser::StyleLink
> links
;
4678 parser
.getDOM(htmlString
, *parsedDOM
, _HtmlStyles
, links
);
4680 // <link> elements inserted from lua::parseHtml are ignored
4681 if (_CurrentHTMLElement
== NULL
&& !links
.empty())
4683 addStylesheetDownload(links
);
4685 else if (_CurrentHTMLElement
!= NULL
)
4687 // Called from active element (lua)
4688 // <style> order is not preserved as document is already being rendered
4689 for(uint i
= 0; i
< _HtmlStyles
.size(); ++i
)
4691 if (!_HtmlStyles
[i
].empty())
4693 _Style
.parseStylesheet(_HtmlStyles
[i
]);
4696 _HtmlStyles
.clear();
4699 // this should rarely fail as first element should be <html>
4700 bool success
= parsedDOM
->Children
.size() > 0;
4702 std::list
<CHtmlElement
>::iterator it
= parsedDOM
->Children
.begin();
4703 while(it
!= parsedDOM
->Children
.end())
4705 if (it
->Type
== CHtmlElement::ELEMENT_NODE
&& it
->Value
== "html")
4707 // move newly parsed childs from <body> into siblings
4708 if (_CurrentHTMLElement
) {
4709 std::list
<CHtmlElement
>::iterator it2
= it
->Children
.begin();
4710 while(it2
!= it
->Children
.end())
4712 if (it2
->Type
== CHtmlElement::ELEMENT_NODE
&& it2
->Value
== "body")
4714 spliceFragment(it2
);
4719 // remove <html> fragment from current element child
4720 it
= parsedDOM
->Children
.erase(it
);
4724 // remove link to <root> (html->parent == '<root>') or css selector matching will break
4731 // skip over other non-handled element
4738 void CGroupHTML::spliceFragment(std::list
<CHtmlElement
>::iterator src
)
4740 if(!_CurrentHTMLElement
->parent
)
4742 nlwarning("BUG: Current node is missing parent element. unable to splice fragment");
4746 // get the iterators for current element (<lua>) and next sibling
4747 std::list
<CHtmlElement
>::iterator currentElement
;
4748 currentElement
= std::find(_CurrentHTMLElement
->parent
->Children
.begin(), _CurrentHTMLElement
->parent
->Children
.end(), *_CurrentHTMLElement
);
4749 if (currentElement
== _CurrentHTMLElement
->parent
->Children
.end())
4751 nlwarning("BUG: unable to find current element iterator from parent");
4755 // where fragment should be moved
4756 std::list
<CHtmlElement
>::iterator insertBefore
;
4757 if (_CurrentHTMLNextSibling
== NULL
)
4759 insertBefore
= _CurrentHTMLElement
->parent
->Children
.end();
4761 // get iterator for nextSibling
4762 insertBefore
= std::find(_CurrentHTMLElement
->parent
->Children
.begin(), _CurrentHTMLElement
->parent
->Children
.end(), *_CurrentHTMLNextSibling
);
4765 _CurrentHTMLElement
->parent
->Children
.splice(insertBefore
, src
->Children
);
4767 // reindex moved elements
4768 CHtmlElement
*prev
= NULL
;
4769 uint childIndex
= _CurrentHTMLElement
->childIndex
;
4770 while(currentElement
!= _CurrentHTMLElement
->parent
->Children
.end())
4772 if (currentElement
->Type
== CHtmlElement::ELEMENT_NODE
)
4776 currentElement
->parent
= _CurrentHTMLElement
->parent
;
4777 currentElement
->childIndex
= childIndex
;
4778 currentElement
->previousSibling
= prev
;
4779 prev
->nextSibling
= &(*currentElement
);
4783 prev
= &(*currentElement
);
4789 // ***************************************************************************
4790 inline bool isDigit(char c
, uint base
= 16)
4792 if (c
>='0' && c
<='9') return true;
4793 if (base
!= 16) return false;
4794 if (c
>='A' && c
<='F') return true;
4795 if (c
>='a' && c
<='f') return true;
4799 // ***************************************************************************
4800 inline char convertHexDigit(char c
)
4802 if (c
>='0' && c
<='9') return c
-'0';
4803 if (c
>='A' && c
<='F') return c
-'A'+10;
4804 if (c
>='a' && c
<='f') return c
-'a'+10;
4808 // ***************************************************************************
4809 std::string
CGroupHTML::decodeHTMLEntities(const std::string
&str
)
4812 result
.reserve(str
.size() + (str
.size() >> 2));
4815 for (uint i
=0; i
<str
.length(); ++i
)
4818 if (str
[i
] == '&' && (str
.length()-i
) >= 4)
4822 // unicode character
4823 if (str
[pos
] == '#')
4827 // using decimal by default
4830 // using hexadecimal if &#x
4831 if (str
[pos
] == 'x')
4837 // setup "last" to point at the first character following "&#x?[0-9a-f]+"
4838 for (last
= pos
; last
< str
.length(); ++last
) if (!isDigit(str
[last
], base
)) break;
4840 // make sure that at least 1 digit was found
4841 // and have the terminating ';' to complete the token: "&#x?[0-9a-f]+;"
4842 if (last
== pos
|| str
[last
] != ';')
4850 // convert digits to unicode character
4851 while (pos
< last
) c
= convertHexDigit(str
[pos
++]) + (c
* u32char(base
));
4853 // append our new character to the result string
4854 CUtfStringView::append(result
, c
);
4856 // move 'i' forward to point at the ';' .. the for(...) will increment i to point to next char
4862 // special xml characters
4863 if (str
.substr(i
+1,5)=="quot;") { i
+=5; result
+='\"'; continue; }
4864 if (str
.substr(i
+1,4)=="amp;") { i
+=4; result
+='&'; continue; }
4865 if (str
.substr(i
+1,3)=="lt;") { i
+=3; result
+='<'; continue; }
4866 if (str
.substr(i
+1,3)=="gt;") { i
+=3; result
+='>'; continue; }
4869 // all the special cases are catered for... treat this as a normal character
4876 // ***************************************************************************
4877 std::string
CGroupHTML::getAbsoluteUrl(const std::string
&url
)
4879 CUrlParser
uri(url
);
4880 if (uri
.isAbsolute())
4885 return uri
.toString();
4888 // ***************************************************************************
4889 void CGroupHTML::resetCssStyle()
4891 _WaitingForStylesheet
= false;
4892 _StylesheetQueue
.clear();
4894 _Style
= _BrowserStyle
;
4897 // ***************************************************************************
4898 std::string
CGroupHTML::HTMLOListElement::getListMarkerText() const
4901 sint32 number
= Value
;
4906 ret
= "\xe2\x88\x99 ";
4908 else if (Type
== "circle")
4911 ret
= "\xe2\x9a\xaa ";
4913 else if (Type
== "square")
4916 ret
= "\xe2\x96\xaa ";
4918 else if (Type
== "a" || Type
== "A")
4920 // @see toAlphabeticOrNumeric in WebKit
4921 static const char lower
[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
4922 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
4923 static const char upper
[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
4924 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
4928 ret
= toString(number
);
4932 const char* digits
= (Type
== "A" ? upper
: lower
);
4936 ret
.insert(ret
.begin(), digits
[number
% size
]);
4942 else if (Type
== "i" || Type
== "I")
4944 // @see toRoman in WebKit
4945 static const char lower
[7] = {'i', 'v', 'x', 'l', 'c', 'd', 'm'};
4946 static const char upper
[7] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
4948 if (number
< 1 || number
> 3999)
4950 ret
= toString(number
);
4954 const char* digits
= (Type
== "I" ? upper
: lower
);
4958 uint32 num
= number
% 10;
4961 for (i
= num
% 5; i
> 0; i
--)
4963 ret
.insert(ret
.begin(), digits
[d
]);
4966 if (num
>= 4 && num
<= 8)
4968 ret
.insert(ret
.begin(), digits
[d
+ 1]);
4972 ret
.insert(ret
.begin(), digits
[d
+ 2]);
4976 ret
.insert(ret
.begin(), digits
[d
]);
4992 ret
= toString(Value
) + ". ";
4998 void CGroupHTML::HTMLMeterElement::readValues(const CHtmlElement
&elm
)
5000 if (!elm
.hasAttribute("value") || !fromString(elm
.getAttribute("value"), value
))
5002 if (!elm
.hasAttribute("min") || !fromString(elm
.getAttribute("min"), min
))
5004 if (!elm
.hasAttribute("max") || !fromString(elm
.getAttribute("max"), max
))
5009 std::swap(min
, max
);
5011 if (!elm
.hasAttribute("low") || !fromString(elm
.getAttribute("low"), low
))
5013 if (!elm
.hasAttribute("high") || !fromString(elm
.getAttribute("high"), high
))
5016 if (!elm
.hasAttribute("optimum") || !fromString(elm
.getAttribute("optimum"), optimum
))
5017 optimum
= (max
- min
) / 2.f
;
5019 // ensure low < high
5021 std::swap(low
, high
);
5028 float CGroupHTML::HTMLMeterElement::getValueRatio() const
5033 return (value
- min
) / (max
- min
);
5036 CGroupHTML::HTMLMeterElement::EValueRegion
CGroupHTML::HTMLMeterElement::getValueRegion() const
5040 // low region is optimum
5042 return VALUE_OPTIMUM
;
5043 else if (value
<= high
)
5044 return VALUE_SUB_OPTIMAL
;
5046 return VALUE_EVEN_LESS_GOOD
;
5048 else if (optimum
>= high
)
5050 // high region is optimum
5052 return VALUE_OPTIMUM
;
5053 else if (value
>= low
)
5054 return VALUE_SUB_OPTIMAL
;
5056 return VALUE_EVEN_LESS_GOOD
;
5059 // middle region is optimum
5060 if (value
>= low
&& value
<= high
)
5061 return VALUE_OPTIMUM
;
5063 return VALUE_SUB_OPTIMAL
;
5066 NLMISC::CRGBA
CGroupHTML::HTMLMeterElement::getBarColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5068 // color meter (inactive) bar segment
5069 // firefox:: meter { background:none; background-color: #555; },
5070 // webkit:: meter::-webkit-meter-bar { background:none; background-color: #555; }
5071 // webkit makes background color visible when padding is added
5072 CRGBA
color(150, 150, 150, 255);
5074 // use webkit pseudo elements as thats easier than firefox pseudo classes
5075 // background-color is expected to be set from browser.css
5077 style
.applyStyle(elm
.getPseudo(":-webkit-meter-bar"));
5078 if(style
.hasStyle("background-color"))
5079 color
= style
.Current
.Background
.color
;
5085 NLMISC::CRGBA
CGroupHTML::HTMLMeterElement::getValueColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5087 // background-color is expected to be set from browser.css
5090 switch(getValueRegion())
5094 style
.applyStyle(elm
.getPseudo(":-webkit-meter-optimum-value"));
5095 if (style
.hasStyle("background-color"))
5096 color
= style
.Current
.Background
.color
;
5099 case VALUE_SUB_OPTIMAL
:
5101 style
.applyStyle(elm
.getPseudo(":-webkit-meter-suboptimum-value"));
5102 if (style
.hasStyle("background-color"))
5103 color
= style
.Current
.Background
.color
;
5106 case VALUE_EVEN_LESS_GOOD
: // fall through
5109 style
.applyStyle(elm
.getPseudo(":-webkit-meter-even-less-good-value"));
5110 if (style
.hasStyle("background-color"))
5111 color
= style
.Current
.Background
.color
;
5120 // ****************************************************************************
5121 void CGroupHTML::HTMLProgressElement::readValues(const CHtmlElement
&elm
)
5123 if (!elm
.hasAttribute("value") || !fromString(elm
.getAttribute("value"), value
))
5125 if (!elm
.hasAttribute("max") || !fromString(elm
.getAttribute("max"), max
))
5132 // ****************************************************************************
5133 float CGroupHTML::HTMLProgressElement::getValueRatio() const
5140 // ****************************************************************************
5141 NLMISC::CRGBA
CGroupHTML::HTMLProgressElement::getBarColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5146 style
.applyStyle(elm
.getPseudo(":-webkit-progress-bar"));
5147 if (style
.hasStyle("background-color"))
5148 color
= style
.Current
.Background
.color
;
5154 // ****************************************************************************
5155 NLMISC::CRGBA
CGroupHTML::HTMLProgressElement::getValueColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5160 style
.applyStyle(elm
.getPseudo(":-webkit-progress-value"));
5161 if (style
.hasStyle("background-color"))
5162 color
= style
.Current
.Background
.color
;
5168 // ****************************************************************************
5169 void CGroupHTML::getCellsParameters(const CHtmlElement
&elm
, bool inherit
)
5171 CGroupHTML::CCellParams cellParams
;
5172 if (!_CellParams
.empty() && inherit
)
5173 cellParams
= _CellParams
.back();
5175 if (!_Style
.hasStyle("background-color") && elm
.hasNonEmptyAttribute("bgcolor"))
5178 if (scanHTMLColor(elm
.getAttribute("bgcolor").c_str(), c
))
5179 _Style
.Current
.Background
.color
= c
;
5181 cellParams
.BgColor
= _Style
.Current
.Background
.color
;
5183 if (elm
.hasAttribute("nowrap") || _Style
.Current
.WhiteSpace
== "nowrap")
5184 cellParams
.NoWrap
= true;
5186 if (elm
.hasNonEmptyAttribute("l_margin"))
5187 fromString(elm
.getAttribute("l_margin"), cellParams
.LeftMargin
);
5189 if (_Style
.hasStyle("height"))
5190 cellParams
.Height
= _Style
.Current
.Height
;
5191 else if (elm
.hasNonEmptyAttribute("height"))
5192 fromString(elm
.getAttribute("height"), cellParams
.Height
);
5196 // having text-align on table/tr should not override td align attribute
5197 if (_Style
.hasStyle("text-align"))
5198 align
= _Style
.Current
.TextAlign
;
5199 else if (elm
.hasNonEmptyAttribute("align"))
5200 align
= toLowerAscii(elm
.getAttribute("align"));
5202 if (align
== "left")
5203 cellParams
.Align
= CGroupCell::Left
;
5204 else if (align
== "center")
5205 cellParams
.Align
= CGroupCell::Center
;
5206 else if (align
== "right")
5207 cellParams
.Align
= CGroupCell::Right
;
5208 else if (align
!= "justify")
5211 // copy td align (can be empty) attribute back into css
5212 _Style
.Current
.TextAlign
= align
;
5217 if (_Style
.hasStyle("vertical-align"))
5218 valign
= _Style
.Current
.VerticalAlign
;
5219 else if (elm
.hasNonEmptyAttribute("valign"))
5220 valign
= toLowerAscii(elm
.getAttribute("valign"));
5222 if (valign
== "top")
5223 cellParams
.VAlign
= CGroupCell::Top
;
5224 else if (valign
== "middle")
5225 cellParams
.VAlign
= CGroupCell::Middle
;
5226 else if (valign
== "bottom")
5227 cellParams
.VAlign
= CGroupCell::Bottom
;
5230 _CellParams
.push_back (cellParams
);
5233 // ***************************************************************************
5234 void CGroupHTML::insertFormImageButton(const std::string
&name
, const std::string
&tooltip
, const std::string
&src
, const std::string
&over
, const std::string
&formId
, const std::string
&action
, uint32 minWidth
, const std::string
&templateName
)
5236 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, "", "image", action
));
5237 // Action handler parameters
5238 std::string param
= "name=" + getId() + "|button=" + toString(_FormSubmit
.size()-1);
5240 // Add the ctrl button
5241 addButton (CCtrlButton::PushButton
, name
, src
, src
, over
, "html_submit_form", param
.c_str(), tooltip
.c_str(), _Style
.Current
);
5244 // ***************************************************************************
5245 void CGroupHTML::insertFormTextButton(const std::string
&name
, const std::string
&tooltip
, const std::string
&value
, const std::string
&formId
, const std::string
&formAction
, uint32 minWidth
, const std::string
&templateName
)
5247 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, value
, "submit", formAction
));
5248 // Action handler parameters
5249 string param
= "name=" + getId() + "|button=" + toString(_FormSubmit
.size()-1);
5251 // Add the ctrl button
5258 string
buttonTemplate(!templateName
.empty() ? templateName
: DefaultButtonGroup
);
5259 typedef pair
<string
, string
> TTmplParam
;
5260 vector
<TTmplParam
> tmplParams
;
5261 tmplParams
.push_back(TTmplParam("id", name
));
5262 tmplParams
.push_back(TTmplParam("onclick", "html_submit_form"));
5263 tmplParams
.push_back(TTmplParam("onclick_param", param
));
5264 tmplParams
.push_back(TTmplParam("active", "true"));
5265 if (minWidth
> 0) tmplParams
.push_back(TTmplParam("wmin", toString(minWidth
)));
5266 CInterfaceGroup
*buttonGroup
= CWidgetManager::getInstance()->getParser()->createGroupInstance(buttonTemplate
, _Paragraph
->getId(), tmplParams
);
5269 // Add the ctrl button
5270 CCtrlTextButton
*ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("button"));
5271 if (!ctrlButton
) ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("b"));
5274 ctrlButton
->setModulateGlobalColorAll (_Style
.Current
.GlobalColor
);
5275 ctrlButton
->setTextModulateGlobalColorNormal(_Style
.Current
.GlobalColorText
);
5276 ctrlButton
->setTextModulateGlobalColorOver(_Style
.Current
.GlobalColorText
);
5277 ctrlButton
->setTextModulateGlobalColorPushed(_Style
.Current
.GlobalColorText
);
5279 // Translate the tooltip
5280 if (!tooltip
.empty())
5282 if (CI18N::hasTranslation(tooltip
))
5284 ctrlButton
->setDefaultContextHelp(CI18N::get(tooltip
));
5288 ctrlButton
->setDefaultContextHelp(tooltip
);
5292 ctrlButton
->setText(value
);
5294 setTextButtonStyle(ctrlButton
, _Style
.Current
);
5296 getParagraph()->addChild (buttonGroup
);
5301 // ***************************************************************************
5302 void CGroupHTML::htmlA(const CHtmlElement
&elm
)
5305 _Link
.push_back ("");
5306 _LinkTitle
.push_back("");
5307 _LinkClass
.push_back("");
5308 if (elm
.hasClass("ryzom-ui-button"))
5309 _LinkClass
.back() = "ryzom-ui-button";
5311 // #fragment works with both ID and NAME so register both
5312 if (elm
.hasNonEmptyAttribute("name"))
5313 _AnchorName
.push_back(elm
.getAttribute("name"));
5314 if (elm
.hasNonEmptyAttribute("title"))
5315 _LinkTitle
.back() = elm
.getAttribute("title");
5316 if (elm
.hasNonEmptyAttribute("href"))
5318 string suri
= elm
.getAttribute("href");
5319 if(suri
.find("ah:") == 0)
5321 if (_TrustedDomain
|| suri
.find("ah:script:") == 0)
5322 _Link
.back() = suri
;
5326 // convert href from "?key=val" into "http://domain.com/?key=val"
5327 _Link
.back() = getAbsoluteUrl(suri
);
5331 renderPseudoElement(":before", elm
);
5334 void CGroupHTML::htmlAend(const CHtmlElement
&elm
)
5336 renderPseudoElement(":after", elm
);
5339 popIfNotEmpty(_Link
);
5340 popIfNotEmpty(_LinkTitle
);
5341 popIfNotEmpty(_LinkClass
);
5344 // ***************************************************************************
5345 void CGroupHTML::htmlBASE(const CHtmlElement
&elm
)
5347 if (!_ReadingHeadTag
|| _IgnoreBaseUrlTag
)
5350 if (elm
.hasNonEmptyAttribute("href"))
5352 CUrlParser
uri(elm
.getAttribute("href"));
5353 if (uri
.isAbsolute())
5355 _URL
= uri
.toString();
5356 _IgnoreBaseUrlTag
= true;
5361 // ***************************************************************************
5362 void CGroupHTML::htmlBODY(const CHtmlElement
&elm
)
5364 // override <body> (or <html>) css style attribute
5365 if (elm
.hasNonEmptyAttribute("bgcolor"))
5366 _Style
.applyStyle("background-color: " + elm
.getAttribute("bgcolor"));
5368 if (m_HtmlBackground
.isEmpty())
5369 setupBackground(&m_HtmlBackground
);
5371 setupBackground(&m_BodyBackground
);
5373 renderPseudoElement(":before", elm
);
5376 // ***************************************************************************
5377 void CGroupHTML::htmlBR(const CHtmlElement
&elm
)
5379 if (!_Paragraph
|| _Paragraph
->getNumChildren() == 0)
5389 // ***************************************************************************
5390 void CGroupHTML::htmlBUTTON(const CHtmlElement
&elm
)
5392 std::string name
= elm
.getAttribute("name");
5393 std::string value
= elm
.getAttribute("value");
5394 std::string formId
= elm
.getAttribute("form");
5395 std::string formAction
= elm
.getAttribute("formaction");
5396 std::string tooltip
= elm
.getAttribute("tooltip");
5397 bool disabled
= elm
.hasAttribute("disabled");
5399 if (formId
.empty() && _FormOpen
)
5401 formId
= _Forms
.back().id
;
5404 if (!formAction
.empty())
5406 formAction
= getAbsoluteUrl(formAction
);
5409 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, value
, "text", formAction
));
5410 // Action handler parameters
5414 if (elm
.getAttribute("type") == "submit")
5416 param
= "ah:html_submit_form&name=" + getId() + "&button=" + toString(_FormSubmit
.size()-1);
5425 _Link
.push_back(param
);
5426 _LinkTitle
.push_back(tooltip
);
5427 _LinkClass
.push_back("ryzom-ui-button");
5429 // TODO: this creates separate button element
5430 //renderPseudoElement(":before", elm);
5432 void CGroupHTML::htmlBUTTONend(const CHtmlElement
&elm
)
5434 // TODO: this creates separate button element
5435 //renderPseudoElement(":after", elm);
5438 popIfNotEmpty(_Link
);
5439 popIfNotEmpty(_LinkTitle
);
5440 popIfNotEmpty(_LinkClass
);
5443 // ***************************************************************************
5444 void CGroupHTML::htmlDD(const CHtmlElement
&elm
)
5449 // if there was no closing tag for <dt>, then remove <dt> style
5452 nlwarning("BUG: nested DT in DD");
5453 _DL
.back().DT
= false;
5458 nlwarning("BUG: nested DD in DD");
5459 _DL
.back().DD
= false;
5460 popIfNotEmpty(_Indent
);
5463 _DL
.back().DD
= true;
5464 _Indent
.push_back(getIndent() + ULIndent
);
5469 newParagraph(ULBeginSpace
);
5473 newParagraph(LIBeginSpace
);
5476 renderPseudoElement(":before", elm
);
5479 void CGroupHTML::htmlDDend(const CHtmlElement
&elm
)
5484 renderPseudoElement(":after", elm
);
5486 // parser will process two DD in a row as nested when first DD is not closed
5489 _DL
.back().DD
= false;
5490 popIfNotEmpty(_Indent
);
5494 // ***************************************************************************
5495 void CGroupHTML::htmlDIV(const CHtmlElement
&elm
)
5497 _DivName
= elm
.getAttribute("name");
5499 string instClass
= elm
.getAttribute("class");
5501 // use generic template system
5502 if (_TrustedDomain
&& !instClass
.empty() && instClass
== "ryzom-ui-grouptemplate")
5504 string style
= elm
.getAttribute("style");
5505 string id
= elm
.getAttribute("id");
5507 id
= "DIV" + toString(getNextAutoIdSeq());
5509 typedef pair
<string
, string
> TTmplParam
;
5510 vector
<TTmplParam
> tmplParams
;
5512 string templateName
;
5515 TStyle styles
= parseStyle(style
);
5516 TStyle::iterator it
;
5517 for (it
=styles
.begin(); it
!= styles
.end(); it
++)
5519 if ((*it
).first
== "template")
5520 templateName
= (*it
).second
;
5522 tmplParams
.push_back(TTmplParam((*it
).first
, (*it
).second
));
5526 if (!templateName
.empty())
5529 bool haveParentDiv
= getDiv() != NULL
;
5531 parentId
= getDiv()->getId();
5537 parentId
= _Paragraph
->getId();
5540 CInterfaceGroup
*inst
= CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName
, parentId
, tmplParams
);
5543 inst
->setId(parentId
+":"+id
);
5544 inst
->updateCoords();
5547 inst
->setParent(getDiv());
5548 inst
->setParentSize(getDiv());
5549 inst
->setParentPos(getDiv());
5550 inst
->setPosRef(Hotspot_TL
);
5551 inst
->setParentPosRef(Hotspot_TL
);
5552 getDiv()->addGroup(inst
);
5556 getParagraph()->addChild(inst
);
5559 _Divs
.push_back(inst
);
5564 renderPseudoElement(":before", elm
);
5567 void CGroupHTML::htmlDIVend(const CHtmlElement
&elm
)
5569 renderPseudoElement(":after", elm
);
5571 popIfNotEmpty(_Divs
);
5574 // ***************************************************************************
5575 void CGroupHTML::htmlDL(const CHtmlElement
&elm
)
5577 _DL
.push_back(HTMLDListElement());
5578 _LI
= _DL
.size() > 1 || !_UL
.empty();
5580 renderPseudoElement(":before", elm
);
5583 void CGroupHTML::htmlDLend(const CHtmlElement
&elm
)
5588 renderPseudoElement(":after", elm
);
5593 nlwarning("BUG: unclosed DT in DL");
5599 popIfNotEmpty(_Indent
);
5600 nlwarning("BUG: unclosed DD in DL");
5603 popIfNotEmpty (_DL
);
5606 // ***************************************************************************
5607 void CGroupHTML::htmlDT(const CHtmlElement
&elm
)
5612 // TODO: check if nested tags still happen and fix it in parser
5613 // : remove special handling for nesting and let it happen
5615 // html parser and libxml2 should prevent nested tags like these
5618 nlwarning("BUG: nested DD in DT");
5620 _DL
.back().DD
= false;
5621 popIfNotEmpty(_Indent
);
5624 // html parser and libxml2 should prevent nested tags like these
5627 nlwarning("BUG: nested DT in DT");
5630 _DL
.back().DT
= true;
5635 newParagraph(ULBeginSpace
);
5639 newParagraph(LIBeginSpace
);
5642 renderPseudoElement(":before", elm
);
5645 void CGroupHTML::htmlDTend(const CHtmlElement
&elm
)
5650 renderPseudoElement(":after", elm
);
5652 _DL
.back().DT
= false;
5655 // ***************************************************************************
5656 void CGroupHTML::htmlFONT(const CHtmlElement
&elm
)
5658 if (elm
.hasNonEmptyAttribute("color"))
5661 if (scanHTMLColor(elm
.getAttribute("color").c_str(), color
))
5662 _Style
.Current
.TextColor
= color
;
5665 if (elm
.hasNonEmptyAttribute("size"))
5668 fromString(elm
.getAttribute("size"), fontsize
);
5669 _Style
.Current
.FontSize
= fontsize
;
5673 // ***************************************************************************
5674 void CGroupHTML::htmlFORM(const CHtmlElement
&elm
)
5679 CGroupHTML::CForm form
;
5680 // id check is case sensitive and auto id's are uppercase
5681 form
.id
= toLowerAscii(trim(elm
.getAttribute("id")));
5682 if (form
.id
.empty())
5684 form
.id
= toString("FORM%d", _Forms
.size());
5687 // Get the action name
5688 if (elm
.hasNonEmptyAttribute("action"))
5690 form
.Action
= getAbsoluteUrl(elm
.getAttribute("action"));
5697 _Forms
.push_back(form
);
5699 renderPseudoElement(":before", elm
);
5702 void CGroupHTML::htmlFORMend(const CHtmlElement
&elm
)
5705 renderPseudoElement(":after", elm
);
5708 // ***************************************************************************
5709 void CGroupHTML::htmlH(const CHtmlElement
&elm
)
5711 newParagraph(PBeginSpace
);
5712 renderPseudoElement(":before", elm
);
5715 void CGroupHTML::htmlHend(const CHtmlElement
&elm
)
5717 renderPseudoElement(":after", elm
);
5720 // ***************************************************************************
5721 void CGroupHTML::htmlHEAD(const CHtmlElement
&elm
)
5723 _ReadingHeadTag
= !_IgnoreHeadTag
;
5724 _IgnoreHeadTag
= true;
5727 void CGroupHTML::htmlHEADend(const CHtmlElement
&elm
)
5729 _ReadingHeadTag
= false;
5732 // ***************************************************************************
5733 void CGroupHTML::htmlHR(const CHtmlElement
&elm
)
5735 CInterfaceGroup
*sep
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_hr", "", NULL
, 0);
5738 CViewBitmap
*bitmap
= dynamic_cast<CViewBitmap
*>(sep
->getView("hr"));
5741 bitmap
->setColor(_Style
.Current
.TextColor
);
5742 if (_Style
.Current
.Width
> 0)
5744 clamp(_Style
.Current
.Width
, 1, 32000);
5745 bitmap
->setW(_Style
.Current
.Width
);
5746 bitmap
->setSizeRef(CInterfaceElement::none
);
5748 if (_Style
.Current
.Height
> 0)
5750 clamp(_Style
.Current
.Height
, 1, 1000);
5751 bitmap
->setH(_Style
.Current
.Height
);
5755 renderPseudoElement(":before", elm
);
5756 addHtmlGroup(sep
, 0);
5757 renderPseudoElement(":after", elm
);
5761 // ***************************************************************************
5762 void CGroupHTML::htmlHTML(const CHtmlElement
&elm
)
5764 if (elm
.hasNonEmptyAttribute("style"))
5765 _Style
.applyStyle(elm
.getAttribute("style"));
5767 _Style
.Root
= _Style
.Current
;
5769 setupBackground(&m_HtmlBackground
);
5772 // ***************************************************************************
5773 void CGroupHTML::htmlI(const CHtmlElement
&elm
)
5776 renderPseudoElement(":before", elm
);
5779 void CGroupHTML::htmlIend(const CHtmlElement
&elm
)
5781 renderPseudoElement(":after", elm
);
5785 // ***************************************************************************
5786 void CGroupHTML::htmlIMG(const CHtmlElement
&elm
)
5788 std::string src
= trim(elm
.getAttribute("src"));
5791 // no 'src' attribute, or empty
5796 std::string id
= elm
.getAttribute("id");
5798 if (elm
.hasNonEmptyAttribute("width"))
5799 getPercentage(_Style
.Current
.Width
, tmpf
, elm
.getAttribute("width").c_str());
5800 if (elm
.hasNonEmptyAttribute("height"))
5801 getPercentage(_Style
.Current
.Height
, tmpf
, elm
.getAttribute("height").c_str());
5803 // Get the global color name
5804 if (elm
.hasAttribute("global_color"))
5805 _Style
.Current
.GlobalColor
= true;
5808 // keep "alt" attribute for backward compatibility
5809 std::string tooltip
= elm
.getAttribute("alt");
5811 if (elm
.hasNonEmptyAttribute("title"))
5812 tooltip
= elm
.getAttribute("title");
5815 string overSrc
= elm
.getAttribute("data-over-src");
5817 // inside a/button with valid url (ie, button is not disabled)
5818 string url
= getLink();
5819 if (getA() && !url
.empty() && getParent() && getParent()->getParent())
5821 string params
= "name=" + getId() + "|url=" + url
;
5822 addButton(CCtrlButton::PushButton
, id
, src
, src
, overSrc
, "browse", params
.c_str(), tooltip
, _Style
.Current
);
5825 if (!tooltip
.empty() || !overSrc
.empty())
5827 addButton(CCtrlButton::PushButton
, id
, src
, src
, overSrc
, "", "", tooltip
, _Style
.Current
);
5831 // Get the option to reload (class==reload)
5832 bool reloadImg
= false;
5834 if (elm
.hasNonEmptyAttribute("style"))
5836 string styleString
= elm
.getAttribute("style");
5837 TStyle styles
= parseStyle(styleString
);
5838 TStyle::iterator it
;
5840 it
= styles
.find("reload");
5841 if (it
!= styles
.end() && (*it
).second
== "1")
5845 addImage(id
, elm
.getAttribute("src"), reloadImg
, _Style
.Current
);
5849 // ***************************************************************************
5850 void CGroupHTML::htmlINPUT(const CHtmlElement
&elm
)
5855 // read general property
5856 string id
= elm
.getAttribute("id");
5858 // Widget template name (old)
5859 string templateName
= elm
.getAttribute("z_btn_tmpl");
5860 // Input name is the new
5861 if (elm
.hasNonEmptyAttribute("z_input_tmpl"))
5862 templateName
= elm
.getAttribute("z_input_tmpl");
5864 // Widget minimal width
5865 uint32 minWidth
= 0;
5866 fromString(elm
.getAttribute("z_input_width"), minWidth
);
5868 // <input type="...">
5869 std::string type
= trim(elm
.getAttribute("type"));
5872 // no 'type' attribute, or empty
5876 // Global color flag
5877 if (elm
.hasAttribute("global_color"))
5878 _Style
.Current
.GlobalColor
= true;
5881 std::string tooltip
= elm
.getAttribute("alt");
5883 if (type
== "image")
5885 string name
= elm
.getAttribute("name");
5886 string src
= elm
.getAttribute("src");
5887 string over
= elm
.getAttribute("data-over-src");
5888 string formId
= elm
.getAttribute("form");
5889 string formAction
= elm
.getAttribute("formaction");
5891 if (formId
.empty() && _FormOpen
) {
5892 formId
= _Forms
.back().id
;
5895 insertFormImageButton(name
, tooltip
, src
, over
, formId
, formAction
, minWidth
, templateName
);
5897 else if (type
== "button" || type
== "submit")
5899 string name
= elm
.getAttribute("name");
5900 string value
= elm
.getAttribute("value");
5901 string formId
= elm
.getAttribute("form");
5902 string formAction
= elm
.getAttribute("formaction");
5904 if (formId
.empty() && _FormOpen
) {
5905 formId
= _Forms
.back().id
;
5908 insertFormTextButton(name
, tooltip
, value
, formId
, formAction
, minWidth
, templateName
);
5910 else if (type
== "text")
5912 // Get the string name
5913 string name
= elm
.getAttribute("name");
5914 string ucValue
= elm
.getAttribute("value");
5917 uint maxlength
= 1024;
5918 if (elm
.hasNonEmptyAttribute("size"))
5919 fromString(elm
.getAttribute("size"), size
);
5920 if (elm
.hasNonEmptyAttribute("maxlength"))
5921 fromString(elm
.getAttribute("maxlength"), maxlength
);
5923 // ryzom client used to have 'size' attribute in pixels, (12 == was default font size)
5924 if (_Style
.hasStyle("-ryzom-input-size-px") && _Style
.getStyle("-ryzom-input-size-px") == "true")
5927 string
textTemplate(!templateName
.empty() ? templateName
: DefaultFormTextGroup
);
5929 CInterfaceGroup
*textArea
= addTextArea (textTemplate
, name
.c_str (), 1, size
, false, ucValue
, maxlength
);
5932 // Add the text area to the form
5933 CGroupHTML::CForm::CEntry entry
;
5935 entry
.TextArea
= textArea
;
5936 _Forms
.back().Entries
.push_back (entry
);
5939 else if (type
== "checkbox" || type
== "radio")
5941 renderPseudoElement(":before", elm
);
5943 CCtrlButton::EType btnType
;
5944 string name
= elm
.getAttribute("name");
5945 string normal
= elm
.getAttribute("src");
5948 string ucValue
= "on";
5949 bool checked
= elm
.hasAttribute("checked");
5951 // TODO: unknown if empty attribute should override or not
5952 if (elm
.hasNonEmptyAttribute("value"))
5953 ucValue
= elm
.getAttribute("value");
5955 if (type
== "radio")
5957 btnType
= CCtrlButton::RadioButton
;
5958 normal
= DefaultRadioButtonBitmapNormal
;
5959 pushed
= DefaultRadioButtonBitmapPushed
;
5960 over
= DefaultRadioButtonBitmapOver
;
5964 btnType
= CCtrlButton::ToggleButton
;
5965 normal
= DefaultCheckBoxBitmapNormal
;
5966 pushed
= DefaultCheckBoxBitmapPushed
;
5967 over
= DefaultCheckBoxBitmapOver
;
5970 // Add the ctrl button
5971 CCtrlButton
*checkbox
= addButton (btnType
, name
, normal
, pushed
, over
, "", "", tooltip
, _Style
.Current
);
5974 if (btnType
== CCtrlButton::RadioButton
)
5976 // override with 'id' because radio buttons share same name
5978 checkbox
->setId(id
);
5980 // group together buttons with same name
5981 CForm
&form
= _Forms
.back();
5982 bool notfound
= true;
5983 for (uint i
=0; i
<form
.Entries
.size(); i
++)
5985 if (form
.Entries
[i
].Name
== name
&& form
.Entries
[i
].Checkbox
!= NULL
&& form
.Entries
[i
].Checkbox
->getType() == CCtrlButton::RadioButton
)
5987 checkbox
->initRBRefFromRadioButton(form
.Entries
[i
].Checkbox
);
5994 // this will start a new group (initRBRef() would take first button in group container otherwise)
5995 checkbox
->initRBRefFromRadioButton(checkbox
);
5999 checkbox
->setPushed (checked
);
6001 // Add the button to the form
6002 CGroupHTML::CForm::CEntry entry
;
6004 entry
.Value
= decodeHTMLEntities(ucValue
);
6005 entry
.Checkbox
= checkbox
;
6006 _Forms
.back().Entries
.push_back (entry
);
6008 renderPseudoElement(":after", elm
);
6010 else if (type
== "hidden")
6012 if (elm
.hasNonEmptyAttribute("name"))
6015 string name
= elm
.getAttribute("name");
6018 string ucValue
= elm
.getAttribute("value");
6021 CGroupHTML::CForm::CEntry entry
;
6023 entry
.Value
= decodeHTMLEntities(ucValue
);
6024 _Forms
.back().Entries
.push_back (entry
);
6029 // ***************************************************************************
6030 void CGroupHTML::htmlLI(const CHtmlElement
&elm
)
6035 // UL, OL top margin if this is the first LI
6039 newParagraph(ULBeginSpace
);
6043 newParagraph(LIBeginSpace
);
6046 // OL list index can be overridden by <li value="1"> attribute
6047 if (elm
.hasNonEmptyAttribute("value"))
6048 fromString(elm
.getAttribute("value"), _UL
.back().Value
);
6050 string str
= _UL
.back().getListMarkerText();
6053 // list-style-type: outside
6054 if (_CurrentViewLink
)
6056 getParagraph()->setFirstViewIndent(-_CurrentViewLink
->getMaxUsedW());
6062 renderPseudoElement(":before", elm
);
6067 void CGroupHTML::htmlLIend(const CHtmlElement
&elm
)
6069 renderPseudoElement(":after", elm
);
6072 // ***************************************************************************
6073 void CGroupHTML::htmlLUA(const CHtmlElement
&elm
)
6075 // we receive an embeded lua script
6076 _ParsingLua
= _TrustedDomain
; // Only parse lua if TrustedDomain
6080 void CGroupHTML::htmlLUAend(const CHtmlElement
&elm
)
6082 if (_ParsingLua
&& _TrustedDomain
)
6084 _ParsingLua
= false;
6085 // execute the embeded lua script
6086 _LuaScript
= "\nlocal __CURRENT_WINDOW__=\""+this->_Id
+"\" \n"+_LuaScript
;
6087 CLuaManager::getInstance().executeLuaScript(_LuaScript
, true);
6091 // ***************************************************************************
6092 void CGroupHTML::htmlMETA(const CHtmlElement
&elm
)
6094 if (!_ReadingHeadTag
)
6097 std::string httpEquiv
= elm
.getAttribute("http-equiv");
6098 std::string httpContent
= elm
.getAttribute("content");
6099 if (httpEquiv
.empty() || httpContent
.empty())
6104 // only first http-equiv="refresh" should be handled
6105 if (_RefreshUrl
.empty() && httpEquiv
== "refresh")
6107 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
6108 double timeSec
= times
.thisFrameMs
/ 1000.0f
;
6110 string::size_type pos
= httpContent
.find_first_of(";");
6111 if (pos
== string::npos
)
6113 fromString(httpContent
, _NextRefreshTime
);
6118 fromString(httpContent
.substr(0, pos
), _NextRefreshTime
);
6120 pos
= toLowerAscii(httpContent
).find("url=");
6121 if (pos
!= string::npos
)
6122 _RefreshUrl
= getAbsoluteUrl(httpContent
.substr(pos
+ 4));
6125 _NextRefreshTime
+= timeSec
;
6129 // ***************************************************************************
6130 void CGroupHTML::htmlMETER(const CHtmlElement
&elm
)
6132 HTMLMeterElement meter
;
6133 meter
.readValues(elm
);
6135 std::string id
= "meter";
6136 if (elm
.hasAttribute("id"))
6137 id
= elm
.getAttribute("id");
6139 // width: 5em, height: 1em
6140 uint32 width
= _Style
.Current
.Width
> -1 ? _Style
.Current
.Width
: _Style
.Current
.FontSize
* 5;
6141 uint32 height
= _Style
.Current
.Height
> -1 ? _Style
.Current
.Height
: _Style
.Current
.FontSize
;
6142 // FIXME: only using border-top
6143 uint32 border
= _Style
.Current
.Border
.Top
.Width
.getValue() > -1 ? _Style
.Current
.Border
.Top
.Width
.getValue() : 0;
6145 uint barw
= (uint
) (width
* meter
.getValueRatio());
6146 CRGBA bgColor
= meter
.getBarColor(elm
, _Style
);
6147 CRGBA valueColor
= meter
.getValueColor(elm
, _Style
);
6149 typedef pair
<string
, string
> TTmplParam
;
6150 vector
<TTmplParam
> tmplParams
;
6151 tmplParams
.push_back(TTmplParam("id", id
));
6152 tmplParams
.push_back(TTmplParam("active", "true"));
6153 tmplParams
.push_back(TTmplParam("w", toString(width
)));
6154 tmplParams
.push_back(TTmplParam("h", toString(height
)));
6155 tmplParams
.push_back(TTmplParam("border_x2", toString(border
*2)));
6156 tmplParams
.push_back(TTmplParam("bgtexture", "blank.tga"));
6157 tmplParams
.push_back(TTmplParam("bgcolor", bgColor
.toString()));
6158 tmplParams
.push_back(TTmplParam("value_w", toString(barw
)));
6159 tmplParams
.push_back(TTmplParam("value_texture", "blank.tga"));
6160 tmplParams
.push_back(TTmplParam("value_color", valueColor
.toString()));
6162 CInterfaceGroup
*gr
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_meter", getParagraph()->getId(), &tmplParams
[0], (uint
)tmplParams
.size());
6165 renderPseudoElement(":before", elm
);
6166 getParagraph()->addChild(gr
);
6167 renderPseudoElement(":after", elm
);
6169 // ignore any inner elements
6170 _IgnoreChildElements
= true;
6174 // ***************************************************************************
6175 void CGroupHTML::htmlOBJECT(const CHtmlElement
&elm
)
6177 _ObjectType
= elm
.getAttribute("type");
6178 _ObjectData
= elm
.getAttribute("data");
6179 _ObjectMD5Sum
= elm
.getAttribute("id");
6180 _ObjectAction
= elm
.getAttribute("standby");
6184 void CGroupHTML::htmlOBJECTend(const CHtmlElement
&elm
)
6188 if (_ObjectType
=="application/ryzom-data")
6190 if (!_TrustedDomain
)
6193 if (!_ObjectData
.empty())
6195 if (addBnpDownload(_ObjectData
, _ObjectAction
, _ObjectScript
, _ObjectMD5Sum
))
6197 CLuaManager::getInstance().executeLuaScript("\nlocal __ALLREADYDL__=true\n"+_ObjectScript
, true);
6199 _ObjectScript
.clear();
6202 else if (_ObjectType
=="application/ryzom-tutorial")
6204 while(strFindReplace(_ObjectScript
, "[", "〈"));
6205 while(strFindReplace(_ObjectScript
, "]", "〉"));
6206 CLuaManager::getInstance().executeLuaScript("\ngame:executeTutorial([["+_ObjectScript
+"]])\n", true);
6207 _ObjectScript
.clear();
6209 else if (_ObjectType
=="application/ryzom-script")
6211 while(strFindReplace(_ObjectScript
, "[", "〈"));
6212 while(strFindReplace(_ObjectScript
, "]", "〉"));
6213 CLuaManager::getInstance().executeLuaScript("\ngame:executeRyzomScript([["+_ObjectScript
+"]])\n", true);
6214 _ObjectScript
.clear();
6219 // ***************************************************************************
6220 void CGroupHTML::htmlOL(const CHtmlElement
&elm
)
6223 std::string
type("1");
6225 if (elm
.hasNonEmptyAttribute("start"))
6226 fromString(elm
.getAttribute("start"), start
);
6227 if (elm
.hasNonEmptyAttribute("type"))
6228 type
= elm
.getAttribute("type");
6230 _UL
.push_back(HTMLOListElement(start
, type
));
6231 // if LI is already present
6232 _LI
= _UL
.size() > 1 || _DL
.size() > 1;
6233 _Indent
.push_back(getIndent() + ULIndent
);
6235 renderPseudoElement(":before", elm
);
6238 void CGroupHTML::htmlOLend(const CHtmlElement
&elm
)
6243 // ***************************************************************************
6244 void CGroupHTML::htmlOPTION(const CHtmlElement
&elm
)
6246 _SelectOption
= true;
6247 _SelectOptionStr
.clear();
6250 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6253 _Forms
.back().Entries
.back().SelectValues
.push_back(elm
.getAttribute("value"));
6255 if (elm
.hasAttribute("selected"))
6256 _Forms
.back().Entries
.back().InitialSelection
= (sint
)_Forms
.back().Entries
.back().SelectValues
.size() - 1;
6258 if (elm
.hasAttribute("disabled"))
6259 _Forms
.back().Entries
.back().sbOptionDisabled
= (sint
)_Forms
.back().Entries
.back().SelectValues
.size() - 1;
6262 void CGroupHTML::htmlOPTIONend(const CHtmlElement
&elm
)
6264 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6267 // use option text as value
6268 if (!elm
.hasAttribute("value"))
6270 _Forms
.back().Entries
.back().SelectValues
.back() = _SelectOptionStr
;
6273 // insert the parsed text into the select control
6274 CDBGroupComboBox
*cb
= _Forms
.back().Entries
.back().ComboBox
;
6277 uint lineIndex
= cb
->getNumTexts();
6278 cb
->addText(_SelectOptionStr
);
6279 if (_Forms
.back().Entries
.back().sbOptionDisabled
== lineIndex
)
6281 cb
->setGrayed(lineIndex
, true);
6286 CGroupMenu
*sb
= _Forms
.back().Entries
.back().SelectBox
;
6289 uint lineIndex
= sb
->getNumLine();
6290 sb
->addLine(_SelectOptionStr
, "", "");
6292 if (_Forms
.back().Entries
.back().sbOptionDisabled
== lineIndex
)
6294 sb
->setGrayedLine(lineIndex
, true);
6298 // create option line checkbox, CGroupMenu is taking ownership of the checbox
6299 CInterfaceGroup
*ig
= CWidgetManager::getInstance()->getParser()->createGroupInstance("menu_checkbox", "", NULL
, 0);
6302 CCtrlButton
*cb
= dynamic_cast<CCtrlButton
*>(ig
->getCtrl("b"));
6305 if (_Forms
.back().Entries
.back().sbMultiple
)
6307 cb
->setType(CCtrlButton::ToggleButton
);
6308 cb
->setTexture(DefaultCheckBoxBitmapNormal
);
6309 cb
->setTexturePushed(DefaultCheckBoxBitmapPushed
);
6310 cb
->setTextureOver(DefaultCheckBoxBitmapOver
);
6314 cb
->setType(CCtrlButton::RadioButton
);
6315 cb
->setTexture(DefaultRadioButtonBitmapNormal
);
6316 cb
->setTexturePushed(DefaultRadioButtonBitmapPushed
);
6317 cb
->setTextureOver(DefaultRadioButtonBitmapOver
);
6319 if (_Forms
.back().Entries
.back().sbRBRef
== NULL
)
6320 _Forms
.back().Entries
.back().sbRBRef
= cb
;
6322 cb
->initRBRefFromRadioButton(_Forms
.back().Entries
.back().sbRBRef
);
6325 cb
->setPushed(_Forms
.back().Entries
.back().InitialSelection
== lineIndex
);
6326 sb
->setUserGroupLeft(lineIndex
, ig
);
6330 nlwarning("Failed to get 'b' element from 'menu_checkbox' template");
6339 // ***************************************************************************
6340 void CGroupHTML::htmlP(const CHtmlElement
&elm
)
6342 newParagraph(PBeginSpace
);
6343 renderPseudoElement(":before", elm
);
6346 void CGroupHTML::htmlPend(const CHtmlElement
&elm
)
6348 renderPseudoElement(":after", elm
);
6351 // ***************************************************************************
6352 void CGroupHTML::htmlPRE(const CHtmlElement
&elm
)
6354 _PRE
.push_back(true);
6357 renderPseudoElement(":before", elm
);
6360 void CGroupHTML::htmlPREend(const CHtmlElement
&elm
)
6362 renderPseudoElement(":after", elm
);
6364 popIfNotEmpty(_PRE
);
6367 // ***************************************************************************
6368 void CGroupHTML::htmlPROGRESS(const CHtmlElement
&elm
)
6370 HTMLProgressElement progress
;
6371 progress
.readValues(elm
);
6373 std::string id
= "progress";
6374 if (elm
.hasAttribute("id"))
6375 id
= elm
.getAttribute("id");
6377 // width: 10em, height: 1em
6378 uint32 width
= _Style
.Current
.Width
> -1 ? _Style
.Current
.Width
: _Style
.Current
.FontSize
* 10;
6379 uint32 height
= _Style
.Current
.Height
> -1 ? _Style
.Current
.Height
: _Style
.Current
.FontSize
;
6380 // FIXME: only using border-top
6381 uint32 border
= _Style
.Current
.Border
.Top
.Width
.getValue() > -1 ? _Style
.Current
.Border
.Top
.Width
.getValue() : 0;
6383 uint barw
= (uint
) (width
* progress
.getValueRatio());
6384 CRGBA bgColor
= progress
.getBarColor(elm
, _Style
);
6385 CRGBA valueColor
= progress
.getValueColor(elm
, _Style
);
6387 typedef pair
<string
, string
> TTmplParam
;
6388 vector
<TTmplParam
> tmplParams
;
6389 tmplParams
.push_back(TTmplParam("id", id
));
6390 tmplParams
.push_back(TTmplParam("active", "true"));
6391 tmplParams
.push_back(TTmplParam("w", toString(width
)));
6392 tmplParams
.push_back(TTmplParam("h", toString(height
)));
6393 tmplParams
.push_back(TTmplParam("border_x2", toString(border
*2)));
6394 tmplParams
.push_back(TTmplParam("bgtexture", "blank.tga"));
6395 tmplParams
.push_back(TTmplParam("bgcolor", bgColor
.toString()));
6396 tmplParams
.push_back(TTmplParam("value_w", toString(barw
)));
6397 tmplParams
.push_back(TTmplParam("value_texture", "blank.tga"));
6398 tmplParams
.push_back(TTmplParam("value_color", valueColor
.toString()));
6400 CInterfaceGroup
*gr
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_progress", getParagraph()->getId(), &tmplParams
[0], (uint
)tmplParams
.size());
6403 renderPseudoElement(":before", elm
);
6404 getParagraph()->addChild(gr
);
6405 renderPseudoElement(":after", elm
);
6407 // ignore any inner elements
6408 _IgnoreChildElements
= true;
6412 // ***************************************************************************
6413 void CGroupHTML::htmlSCRIPT(const CHtmlElement
&elm
)
6418 void CGroupHTML::htmlSCRIPTend(const CHtmlElement
&elm
)
6420 _IgnoreText
= false;
6423 // ***************************************************************************
6424 void CGroupHTML::htmlSELECT(const CHtmlElement
&elm
)
6430 string name
= elm
.getAttribute("name");
6431 bool multiple
= elm
.hasAttribute("multiple");
6434 if (elm
.hasNonEmptyAttribute("size"))
6435 fromString(elm
.getAttribute("size"), size
);
6437 CGroupHTML::CForm::CEntry entry
;
6439 entry
.sbMultiple
= multiple
;
6440 if (size
> 1 || multiple
)
6442 entry
.InitialSelection
= -1;
6443 CGroupMenu
*sb
= addSelectBox(DefaultFormSelectBoxMenuGroup
, name
.c_str());
6449 if (_Style
.Current
.Width
> -1)
6450 sb
->setMinW(_Style
.Current
.Width
);
6452 if (_Style
.Current
.Height
> -1)
6453 sb
->setMinH(_Style
.Current
.Height
);
6455 sb
->setMaxVisibleLine(size
);
6456 sb
->setFontSize(_Style
.Current
.FontSize
, false);
6459 entry
.SelectBox
= sb
;
6463 CDBGroupComboBox
*cb
= addComboBox(DefaultFormSelectGroup
, name
.c_str());
6464 entry
.ComboBox
= cb
;
6470 setTextStyle(cb
->getViewText(), _Style
.Current
);
6473 _Forms
.back().Entries
.push_back (entry
);
6476 void CGroupHTML::htmlSELECTend(const CHtmlElement
&elm
)
6478 _SelectOption
= false;
6479 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6482 CDBGroupComboBox
*cb
= _Forms
.back().Entries
.back().ComboBox
;
6485 cb
->setSelectionNoTrigger(_Forms
.back().Entries
.back().InitialSelection
);
6486 // TODO: magic padding
6487 cb
->setW(cb
->evalContentWidth() + 16);
6491 // ***************************************************************************
6492 void CGroupHTML::htmlSTYLE(const CHtmlElement
&elm
)
6497 void CGroupHTML::htmlSTYLEend(const CHtmlElement
&elm
)
6499 _IgnoreText
= false;
6502 // ***************************************************************************
6503 void CGroupHTML::htmlTABLE(const CHtmlElement
&elm
)
6505 // Get cells parameters
6506 getCellsParameters(elm
, false);
6508 CGroupTable
*table
= new CGroupTable(TCtorParam());
6510 if (elm
.hasNonEmptyAttribute("id"))
6511 table
->setId(getCurrentGroup()->getId() + ":" + elm
.getAttribute("id"));
6513 table
->setId(getCurrentGroup()->getId() + ":TABLE" + toString(getNextAutoIdSeq()));
6515 // TODO: border-spacing: 2em;
6517 if (elm
.hasNonEmptyAttribute("cellspacing"))
6518 fromString(elm
.getAttribute("cellspacing"), table
->CellSpacing
);
6520 // TODO: cssLength, horiz/vert values
6521 if (_Style
.hasStyle("border-spacing"))
6522 fromString(_Style
.getStyle("border-spacing"), table
->CellSpacing
);
6524 // overrides border-spacing if set to 'collapse'
6525 if (_Style
.checkStyle("border-collapse", "collapse"))
6526 table
->CellSpacing
= 0;
6529 if (elm
.hasNonEmptyAttribute("cellpadding"))
6530 fromString(elm
.getAttribute("cellpadding"), table
->CellPadding
);
6532 if (_Style
.hasStyle("width"))
6534 // _Style.Width does not handle '%' unit currently
6535 if (_Style
.Current
.Width
> 0)
6537 table
->ForceWidthMin
= _Style
.Current
.Width
;
6538 table
->TableRatio
= 0;
6542 getPercentage (table
->ForceWidthMin
, table
->TableRatio
, _Style
.getStyle("width").c_str());
6545 else if (elm
.hasNonEmptyAttribute("width"))
6547 getPercentage (table
->ForceWidthMin
, table
->TableRatio
, elm
.getAttribute("width").c_str());
6550 // border from css or from attribute
6552 CSSRect
<CSSBorder
> border
;
6553 border
.Top
.Color
= _Style
.Current
.TextColor
;
6554 border
.Right
.Color
= _Style
.Current
.TextColor
;
6555 border
.Bottom
.Color
= _Style
.Current
.TextColor
;
6556 border
.Left
.Color
= _Style
.Current
.TextColor
;
6558 if (elm
.hasAttribute("border"))
6560 uint32 borderWidth
= 0;
6561 CRGBA borderColor
= CRGBA::Transparent
;
6563 std::string s
= elm
.getAttribute("border");
6567 fromString(elm
.getAttribute("border"), borderWidth
);
6569 if (elm
.hasNonEmptyAttribute("bordercolor"))
6570 scanHTMLColor(elm
.getAttribute("bordercolor").c_str(), borderColor
);
6572 borderColor
= CRGBA(128, 128, 128, 255);
6574 table
->CellBorder
= (borderWidth
> 0);
6576 border
.Top
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6577 border
.Right
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6578 border
.Bottom
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6579 border
.Left
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6582 if (_Style
.hasStyle("border-top-width")) border
.Top
.Width
= _Style
.Current
.Border
.Top
.Width
;
6583 if (_Style
.hasStyle("border-right-width")) border
.Right
.Width
= _Style
.Current
.Border
.Right
.Width
;
6584 if (_Style
.hasStyle("border-bottom-width")) border
.Bottom
.Width
= _Style
.Current
.Border
.Bottom
.Width
;
6585 if (_Style
.hasStyle("border-left-width")) border
.Left
.Width
= _Style
.Current
.Border
.Left
.Width
;
6587 if (_Style
.hasStyle("border-top-color")) border
.Top
.Color
= _Style
.Current
.Border
.Top
.Color
;
6588 if (_Style
.hasStyle("border-right-color")) border
.Right
.Color
= _Style
.Current
.Border
.Right
.Color
;
6589 if (_Style
.hasStyle("border-bottom-color")) border
.Bottom
.Color
= _Style
.Current
.Border
.Bottom
.Color
;
6590 if (_Style
.hasStyle("border-left-color")) border
.Left
.Color
= _Style
.Current
.Border
.Left
.Color
;
6592 if (_Style
.hasStyle("border-top-style")) border
.Top
.Style
= _Style
.Current
.Border
.Top
.Style
;
6593 if (_Style
.hasStyle("border-right-style")) border
.Right
.Style
= _Style
.Current
.Border
.Right
.Style
;
6594 if (_Style
.hasStyle("border-bottom-style")) border
.Bottom
.Style
= _Style
.Current
.Border
.Bottom
.Style
;
6595 if (_Style
.hasStyle("border-left-style")) border
.Left
.Style
= _Style
.Current
.Border
.Left
.Style
;
6597 table
->Border
->setBorder(border
);
6598 table
->Border
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
6599 table
->Border
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
6602 setupBackground(table
->Background
);
6603 table
->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
6605 table
->setMarginLeft(getIndent());
6606 addHtmlGroup (table
, 0);
6608 renderPseudoElement(":before", elm
);
6610 _Tables
.push_back(table
);
6612 // Add a cell pointer
6613 _Cells
.push_back(NULL
);
6614 _TR
.push_back(false);
6615 _Indent
.push_back(0);
6618 void CGroupHTML::htmlTABLEend(const CHtmlElement
&elm
)
6620 popIfNotEmpty(_CellParams
);
6622 popIfNotEmpty(_Cells
);
6623 popIfNotEmpty(_Tables
);
6624 popIfNotEmpty(_Indent
);
6626 renderPseudoElement(":after", elm
);
6629 // ***************************************************************************
6630 void CGroupHTML::htmlTD(const CHtmlElement
&elm
)
6632 // Get cells parameters
6633 getCellsParameters(elm
, true);
6635 if (!m_TableRowBackgroundColor
.empty() && m_TableRowBackgroundColor
.back().A
> 0)
6636 _Style
.Current
.Background
.color
.blendFromui(m_TableRowBackgroundColor
.back(), _Style
.Current
.Background
.color
, _Style
.Current
.Background
.color
.A
);
6638 if (elm
.ID
== HTML_TH
)
6640 if (!_Style
.hasStyle("font-weight"))
6641 _Style
.Current
.FontWeight
= FONT_WEIGHT_BOLD
;
6642 // center if not specified otherwise.
6643 if (!elm
.hasNonEmptyAttribute("align") && !_Style
.hasStyle("text-align"))
6644 _CellParams
.back().Align
= CGroupCell::Center
;
6647 CGroupTable
*table
= getTable();
6650 // <td> appears to be outside <table>
6656 // <table> not started
6660 _Cells
.back() = new CGroupCell(CViewBase::TCtorParam());
6661 if (elm
.hasNonEmptyAttribute("id"))
6662 _Cells
.back()->setId(table
->getId() + ":" + elm
.getAttribute("id"));
6664 _Cells
.back()->setId(table
->getId() + ":TD" + toString(getNextAutoIdSeq()));
6665 // inner cell content
6666 _Cells
.back()->Group
->setId(_Cells
.back()->getId() + ":CELL");
6668 setupBackground(_Cells
.back()->Background
);
6669 _Cells
.back()->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
6671 if (elm
.hasNonEmptyAttribute("colspan"))
6672 fromString(elm
.getAttribute("colspan"), _Cells
.back()->ColSpan
);
6673 if (elm
.hasNonEmptyAttribute("rowspan"))
6674 fromString(elm
.getAttribute("rowspan"), _Cells
.back()->RowSpan
);
6676 _Cells
.back()->Align
= _CellParams
.back().Align
;
6677 _Cells
.back()->VAlign
= _CellParams
.back().VAlign
;
6678 _Cells
.back()->LeftMargin
= _CellParams
.back().LeftMargin
;
6679 _Cells
.back()->NoWrap
= _CellParams
.back().NoWrap
;
6680 _Cells
.back()->ColSpan
= std::max(1, _Cells
.back()->ColSpan
);
6681 _Cells
.back()->RowSpan
= std::max(1, _Cells
.back()->RowSpan
);
6682 _Cells
.back()->Height
= _CellParams
.back().Height
;
6685 if (_Style
.hasStyle("width"))
6687 // _Style.Width does not handle '%' unit currently
6688 if (_Style
.Current
.Width
> 0)
6690 _Cells
.back()->WidthWanted
= _Style
.Current
.Width
;
6691 _Cells
.back()->TableRatio
= 0;
6695 getPercentage (_Cells
.back()->WidthWanted
, _Cells
.back()->TableRatio
, _Style
.getStyle("width").c_str());
6698 else if (elm
.hasNonEmptyAttribute("width"))
6700 getPercentage (_Cells
.back()->WidthWanted
, _Cells
.back()->TableRatio
, elm
.getAttribute("width").c_str());
6703 _Cells
.back()->NewLine
= getTR();
6705 CSSRect
<CSSBorder
> border
;
6706 border
.Top
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6707 border
.Right
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6708 border
.Bottom
.set(table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6709 border
.Left
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6711 if (_Style
.hasStyle("border-top-width")) border
.Top
.Width
= _Style
.Current
.Border
.Top
.Width
;
6712 if (_Style
.hasStyle("border-right-width")) border
.Right
.Width
= _Style
.Current
.Border
.Right
.Width
;
6713 if (_Style
.hasStyle("border-bottom-width")) border
.Bottom
.Width
= _Style
.Current
.Border
.Bottom
.Width
;
6714 if (_Style
.hasStyle("border-left-width")) border
.Left
.Width
= _Style
.Current
.Border
.Left
.Width
;
6716 if (_Style
.hasStyle("border-top-color")) border
.Top
.Color
= _Style
.Current
.Border
.Top
.Color
;
6717 if (_Style
.hasStyle("border-right-color")) border
.Right
.Color
= _Style
.Current
.Border
.Right
.Color
;
6718 if (_Style
.hasStyle("border-bottom-color")) border
.Bottom
.Color
= _Style
.Current
.Border
.Bottom
.Color
;
6719 if (_Style
.hasStyle("border-left-color")) border
.Left
.Color
= _Style
.Current
.Border
.Left
.Color
;
6721 if (_Style
.hasStyle("border-top-style")) border
.Top
.Style
= _Style
.Current
.Border
.Top
.Style
;
6722 if (_Style
.hasStyle("border-right-style")) border
.Right
.Style
= _Style
.Current
.Border
.Right
.Style
;
6723 if (_Style
.hasStyle("border-bottom-style")) border
.Bottom
.Style
= _Style
.Current
.Border
.Bottom
.Style
;
6724 if (_Style
.hasStyle("border-left-style")) border
.Left
.Style
= _Style
.Current
.Border
.Left
.Style
;
6726 _Cells
.back()->Border
->setBorder(border
);
6727 _Cells
.back()->Border
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
6728 _Cells
.back()->Border
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
6730 // padding from <table cellpadding="1">
6731 if (table
->CellPadding
)
6733 // FIXME: padding is ignored by vertical align
6734 _Cells
.back()->PaddingTop
= table
->CellPadding
;
6735 _Cells
.back()->PaddingRight
= table
->CellPadding
;
6736 _Cells
.back()->PaddingBottom
= table
->CellPadding
;
6737 _Cells
.back()->PaddingLeft
= table
->CellPadding
;
6740 if (_Style
.hasStyle("padding-top")) _Cells
.back()->PaddingTop
= _Style
.Current
.PaddingTop
;
6741 if (_Style
.hasStyle("padding-right")) _Cells
.back()->PaddingRight
= _Style
.Current
.PaddingRight
;
6742 if (_Style
.hasStyle("padding-bottom")) _Cells
.back()->PaddingBottom
= _Style
.Current
.PaddingBottom
;
6743 if (_Style
.hasStyle("padding-left")) _Cells
.back()->PaddingLeft
= _Style
.Current
.PaddingLeft
;
6745 table
->addChild (_Cells
.back());
6747 // reusing indent pushed by table
6750 newParagraph(TDBeginSpace
);
6751 // indent is already 0, getParagraph()->setMarginLeft(0); // maybe setIndent(0) if LI is using one
6757 renderPseudoElement(":before", elm
);
6760 void CGroupHTML::htmlTDend(const CHtmlElement
&elm
)
6762 renderPseudoElement(":after", elm
);
6764 popIfNotEmpty(_CellParams
);
6765 if (!_Cells
.empty())
6766 _Cells
.back() = NULL
;
6769 // ***************************************************************************
6770 void CGroupHTML::htmlTEXTAREA(const CHtmlElement
&elm
)
6772 _IgnoreChildElements
= true;
6774 // TODO: allow textarea without form
6778 // read general property
6779 string templateName
;
6781 // Widget template name
6782 if (elm
.hasNonEmptyAttribute("z_input_tmpl"))
6783 templateName
= elm
.getAttribute("z_input_tmpl");
6785 // Get the string name
6786 _TextAreaName
.clear();
6789 _TextAreaMaxLength
= 1024;
6790 if (elm
.hasNonEmptyAttribute("name"))
6791 _TextAreaName
= elm
.getAttribute("name");
6792 if (elm
.hasNonEmptyAttribute("rows"))
6793 fromString(elm
.getAttribute("rows"), _TextAreaRow
);
6794 if (elm
.hasNonEmptyAttribute("cols"))
6795 fromString(elm
.getAttribute("cols"), _TextAreaCols
);
6796 if (elm
.hasNonEmptyAttribute("maxlength"))
6797 fromString(elm
.getAttribute("maxlength"), _TextAreaMaxLength
);
6799 _TextAreaTemplate
= !templateName
.empty() ? templateName
: DefaultFormTextAreaGroup
;
6801 std::string content
= strFindReplaceAll(elm
.serializeChilds(false), std::string("\r"), std::string(""));
6803 CInterfaceGroup
*textArea
= addTextArea (_TextAreaTemplate
, _TextAreaName
.c_str (), _TextAreaRow
, _TextAreaCols
, true, content
, _TextAreaMaxLength
);
6806 // Add the text area to the form
6807 CGroupHTML::CForm::CEntry entry
;
6808 entry
.Name
= _TextAreaName
;
6809 entry
.TextArea
= textArea
;
6810 _Forms
.back().Entries
.push_back (entry
);
6814 // ***************************************************************************
6815 void CGroupHTML::htmlTH(const CHtmlElement
&elm
)
6820 void CGroupHTML::htmlTHend(const CHtmlElement
&elm
)
6825 // ***************************************************************************
6826 void CGroupHTML::htmlTITLE(const CHtmlElement
&elm
)
6828 _IgnoreChildElements
= true;
6830 // TODO: only from <head>
6831 // if (!_ReadingHeadTag) return;
6833 // consume all child elements
6834 _TitleString
= strFindReplaceAll(elm
.serializeChilds(false), std::string("\t"), std::string(" "));
6835 _TitleString
= strFindReplaceAll(_TitleString
, std::string("\n"), std::string(" "));
6836 setTitle(_TitleString
);
6839 // ***************************************************************************
6840 void CGroupHTML::htmlTR(const CHtmlElement
&elm
)
6842 // prevent inheriting from table
6843 if (!_CellParams
.empty())
6845 _CellParams
.back().BgColor
= CRGBA::Transparent
;
6846 _CellParams
.back().Height
= 0;
6849 // Get cells parameters
6850 getCellsParameters(elm
, true);
6852 m_TableRowBackgroundColor
.push_back(_CellParams
.back().BgColor
);
6853 _CellParams
.back().BgColor
= CRGBA::Transparent
;
6855 // TODO: this probably ends up in first cell
6856 renderPseudoElement(":before", elm
);
6863 void CGroupHTML::htmlTRend(const CHtmlElement
&elm
)
6865 // TODO: this probably ends up in last cell
6866 renderPseudoElement(":after", elm
);
6868 popIfNotEmpty(_CellParams
);
6869 popIfNotEmpty(m_TableRowBackgroundColor
);
6872 // ***************************************************************************
6873 void CGroupHTML::htmlUL(const CHtmlElement
&elm
)
6876 _UL
.push_back(HTMLOListElement(1, "disc"));
6877 else if (_UL
.size() == 1)
6878 _UL
.push_back(HTMLOListElement(1, "circle"));
6880 _UL
.push_back(HTMLOListElement(1, "square"));
6882 // if LI is already present
6883 _LI
= _UL
.size() > 1 || _DL
.size() > 1;
6884 _Indent
.push_back(getIndent() + ULIndent
);
6886 renderPseudoElement(":before", elm
);
6889 void CGroupHTML::htmlULend(const CHtmlElement
&elm
)
6894 renderPseudoElement(":after", elm
);
6897 popIfNotEmpty(_Indent
);