1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 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"
62 #include <curl/curl.h>
65 using namespace NLMISC
;
71 // Default maximum time the request is allowed to take
72 #define DEFAULT_RYZOM_CONNECTION_TIMEOUT (300.0)
73 // Allow up to 10 redirects, then give up
74 #define DEFAULT_RYZOM_REDIRECT_LIMIT (10)
76 #define FONT_WEIGHT_NORMAL 400
77 #define FONT_WEIGHT_BOLD 700
82 // Uncomment nlwarning() to see the log about curl downloads
83 #define LOG_DL(fmt, ...) //nlwarning(fmt, ## __VA_ARGS__)
84 // Uncomment to log curl progess
85 //#define LOG_CURL_PROGRESS 1
87 CGroupHTML::SWebOptions
CGroupHTML::options
;
89 // Return URL with https is host is in HSTS list
90 static std::string
upgradeInsecureUrl(const std::string
&url
)
92 if (toLowerAscii(url
.substr(0, 7)) != "http://") {
97 if (!CStrictTransportSecurity::getInstance()->isSecureHost(uri
.host
)){
101 LOG_DL("HSTS url : '%s', using https", url
.c_str());
102 uri
.scheme
= "https";
104 return uri
.toString();
107 // Active cURL www transfer
111 CCurlWWWData(CURL
*curl
, const std::string
&url
)
112 : Request(curl
), Url(url
), Content(""), HeadersSent(NULL
)
118 curl_easy_cleanup(Request
);
121 curl_slist_free_all(HeadersSent
);
124 void sendHeaders(const std::vector
<std::string
> headers
)
126 for(uint i
= 0; i
< headers
.size(); ++i
)
128 HeadersSent
= curl_slist_append(HeadersSent
, headers
[i
].c_str());
130 curl_easy_setopt(Request
, CURLOPT_HTTPHEADER
, HeadersSent
);
133 void setRecvHeader(const std::string
&header
)
135 size_t pos
= header
.find(": ");
136 if (pos
== std::string::npos
)
139 std::string key
= toLowerAscii(header
.substr(0, pos
));
140 if (pos
!= std::string::npos
)
142 HeadersRecv
[key
] = header
.substr(pos
+ 2);
143 //nlinfo(">> received header '%s' = '%s'", key.c_str(), HeadersRecv[key].c_str());
147 // return last received "Location: <url>" header or empty string if no header set
148 const std::string
getLocationHeader()
150 if (HeadersRecv
.count("location") > 0)
151 return HeadersRecv
["location"];
156 const uint32
getExpires()
159 if (HeadersRecv
.count("expires") > 0)
160 ret
= curl_getdate(HeadersRecv
["expires"].c_str(), NULL
);
162 return ret
> -1 ? ret
: 0;
165 const std::string
getLastModified()
167 if (HeadersRecv
.count("last-modified") > 0)
169 return HeadersRecv
["last-modified"];
175 const std::string
getEtag()
177 if (HeadersRecv
.count("etag") > 0)
179 return HeadersRecv
["etag"];
187 // ignore header if not secure connection
188 if (toLowerAscii(Url
.substr(0, 8)) != "https://")
193 return HeadersRecv
.count("strict-transport-security") > 0;
196 const std::string
getHSTSHeader()
200 return HeadersRecv
["strict-transport-security"];
213 // headers sent with curl request, must be released after transfer
214 curl_slist
* HeadersSent
;
216 // headers received from curl transfer
217 std::map
<std::string
, std::string
> HeadersRecv
;
220 // cURL transfer callbacks
221 // ***************************************************************************
222 static size_t curlHeaderCallback(char *buffer
, size_t size
, size_t nmemb
, void *pCCurlWWWData
)
224 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
228 header
.append(buffer
, size
* nmemb
);
229 me
->setRecvHeader(header
.substr(0, header
.find_first_of("\n\r")));
235 // ***************************************************************************
236 static size_t curlDataCallback(char *buffer
, size_t size
, size_t nmemb
, void *pCCurlWWWData
)
238 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
240 me
->Content
.append(buffer
, size
* nmemb
);
245 // ***************************************************************************
246 static size_t curlProgressCallback(void *pCCurlWWWData
, curl_off_t dltotal
, curl_off_t dlnow
, curl_off_t ultotal
, curl_off_t ulnow
)
248 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
251 if (dltotal
> 0 || dlnow
> 0 || ultotal
> 0 || ulnow
> 0)
253 #ifdef LOG_CURL_PROGRESS
254 nlwarning("> dltotal %ld, dlnow %ld, ultotal %ld, ulnow %ld, url '%s'", dltotal
, dlnow
, ultotal
, ulnow
, me
->Url
.c_str());
259 // return 1 to cancel download
263 CGroupHTML::CDataDownload::~CDataDownload()
269 // Check if domain is on TrustedDomain
270 bool CGroupHTML::isTrustedDomain(const string
&domain
)
272 vector
<string
>::iterator it
;
273 it
= find ( options
.trustedDomains
.begin(), options
.trustedDomains
.end(), domain
);
274 return it
!= options
.trustedDomains
.end();
277 // Update view after download has finished
278 void CGroupHTML::setImage(CViewBase
* view
, const string
&file
, const TImageType type
)
280 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
283 if (type
== NormalImage
)
285 btn
->setTexture (file
);
286 btn
->setTexturePushed(file
);
287 btn
->invalidateCoords();
288 btn
->invalidateContent();
293 btn
->setTextureOver(file
);
299 CViewBitmap
*btm
= dynamic_cast<CViewBitmap
*>(view
);
302 btm
->setTexture (file
);
303 btm
->invalidateCoords();
304 btm
->invalidateContent();
310 CGroupCell
*btgc
= dynamic_cast<CGroupCell
*>(view
);
313 btgc
->setTexture (file
);
314 btgc
->invalidateCoords();
315 btgc
->invalidateContent();
321 CGroupTable
*table
= dynamic_cast<CGroupTable
*>(view
);
324 table
->setTexture(file
);
330 // Force image width, height
331 void CGroupHTML::setImageSize(CViewBase
*view
, const CStyleParams
&style
)
333 sint32 width
= style
.Width
;
334 sint32 height
= style
.Height
;
335 sint32 maxw
= style
.MaxWidth
;
336 sint32 maxh
= style
.MaxHeight
;
338 sint32 imageWidth
, imageHeight
;
341 // get image texture size
342 // if image is being downloaded, then correct size is set after thats done
343 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
347 imageWidth
= btn
->getW(false);
348 imageHeight
= btn
->getH(false);
352 CViewBitmap
*btm
= dynamic_cast<CViewBitmap
*>(view
);
356 imageWidth
= btm
->getW(false);
357 imageHeight
= btm
->getH(false);
366 // if width/height is not requested, then use image size
367 // else recalculate missing value, keep image ratio
368 if (width
== -1 && height
== -1)
371 height
= imageHeight
;
376 if (width
== -1 || height
== -1) {
377 float ratio
= (float) imageWidth
/ std::max(1, imageHeight
);
379 width
= height
* ratio
;
381 height
= width
/ ratio
;
384 // apply max-width, max-height rules if asked
385 if (maxw
> -1 || maxh
> -1)
387 _Style
.applyCssMinMax(width
, height
, 0, 0, maxw
, maxh
);
393 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
402 CViewBitmap
*image
= dynamic_cast<CViewBitmap
*>(view
);
405 image
->setScale(true);
413 void CGroupHTML::setTextButtonStyle(CCtrlTextButton
*ctrlButton
, const CStyleParams
&style
)
415 // this will also set size for <a class="ryzom-ui-button"> treating it like "display: inline-block;"
416 if (style
.Width
> 0) ctrlButton
->setWMin(style
.Width
);
417 if (style
.Height
> 0) ctrlButton
->setHMin(style
.Height
);
419 CViewText
*pVT
= ctrlButton
->getViewText();
422 setTextStyle(pVT
, style
);
425 if (style
.hasStyle("background-color"))
427 ctrlButton
->setColor(style
.BackgroundColor
);
428 if (style
.hasStyle("-ryzom-background-color-over"))
430 ctrlButton
->setColorOver(style
.BackgroundColorOver
);
434 ctrlButton
->setColorOver(style
.BackgroundColor
);
436 ctrlButton
->setTexture("", "blank.tga", "", false);
437 ctrlButton
->setTextureOver("", "blank.tga", "");
438 ctrlButton
->setProperty("force_text_over", "true");
440 else if (style
.hasStyle("-ryzom-background-color-over"))
442 ctrlButton
->setColorOver(style
.BackgroundColorOver
);
443 ctrlButton
->setProperty("force_text_over", "true");
444 ctrlButton
->setTextureOver("blank.tga", "blank.tga", "blank.tga");
448 void CGroupHTML::setTextStyle(CViewText
*pVT
, const CStyleParams
&style
)
452 pVT
->setColor(style
.TextColor
);
453 pVT
->setFontName(style
.FontFamily
);
454 pVT
->setFontSize(style
.FontSize
, false);
455 pVT
->setEmbolden(style
.FontWeight
>= FONT_WEIGHT_BOLD
);
456 pVT
->setOblique(style
.FontOblique
);
457 pVT
->setUnderlined(style
.Underlined
);
458 pVT
->setStrikeThrough(style
.StrikeThrough
);
459 if (style
.TextShadow
.Enabled
)
461 pVT
->setShadow(true);
462 pVT
->setShadowColor(style
.TextShadow
.Color
);
463 pVT
->setShadowOutline(style
.TextShadow
.Outline
);
464 pVT
->setShadowOffset(style
.TextShadow
.X
, style
.TextShadow
.Y
);
469 // Get an url and return the local filename with the path where the url image should be
470 string
CGroupHTML::localImageName(const string
&url
)
472 string dest
= "cache/";
473 dest
+= getMD5((uint8
*)url
.c_str(), (uint32
)url
.size()).toString();
478 void CGroupHTML::pumpCurlQueue()
480 if (RunningCurls
< options
.curlMaxConnections
)
482 std::list
<CDataDownload
>::iterator it
=Curls
.begin();
484 while(it
!= Curls
.end() && RunningCurls
< options
.curlMaxConnections
)
486 if (it
->data
== NULL
)
488 LOG_DL("(%s) starting new download '%s'", _Id
.c_str(), it
->url
.c_str());
489 if (!startCurlDownload(*it
))
491 LOG_DL("(%s) failed to start '%s)'", _Id
.c_str(), it
->url
.c_str());
492 finishCurlDownload(*it
);
493 it
= Curls
.erase(it
);
502 if (RunningCurls
> 0 || !Curls
.empty())
503 LOG_DL("(%s) RunningCurls %d, _Curls %d", _Id
.c_str(), RunningCurls
, Curls
.size());
506 // Add url to MultiCurl queue and return cURL handle
507 bool CGroupHTML::startCurlDownload(CDataDownload
&download
)
511 nlwarning("Invalid MultiCurl handle, unable to download '%s'", download
.url
.c_str());
518 CHttpCacheObject cache
;
519 if (CFile::fileExists(download
.dest
))
520 cache
= CHttpCache::getInstance()->lookup(download
.dest
);
522 if (cache
.Expires
> currentTime
)
524 LOG_DL("Cache for (%s) is not expired (%s, expires:%d)", download
.url
.c_str(), download
.dest
.c_str(), cache
.Expires
- currentTime
);
528 string tmpdest
= download
.dest
+ ".tmp";
530 // erase the tmp file if exists
531 if (CFile::fileExists(tmpdest
))
533 CFile::deleteFile(tmpdest
);
536 FILE *fp
= nlfopen (tmpdest
, "wb");
539 nlwarning("Can't open file '%s' for writing: code=%d '%s'", tmpdest
.c_str (), errno
, strerror(errno
));
543 CURL
*curl
= curl_easy_init();
547 CFile::deleteFile(tmpdest
);
549 nlwarning("Creating cURL handle failed, unable to download '%s'", download
.url
.c_str());
552 LOG_DL("curl easy handle %p created for '%s'", curl
, download
.url
.c_str());
555 if (toLowerAscii(download
.url
.substr(0, 8)) == "https://")
557 // if supported, use custom SSL context function to load certificates
558 NLWEB::CCurlCertificates::useCertificates(curl
);
561 download
.data
= new CCurlWWWData(curl
, download
.url
);
564 // initial connection timeout, curl default is 300sec
565 curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, download
.ConnectionTimeout
);
567 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, true);
568 curl_easy_setopt(curl
, CURLOPT_URL
, download
.url
.c_str());
570 // limit curl to HTTP and HTTPS protocols only
571 curl_easy_setopt(curl
, CURLOPT_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
572 curl_easy_setopt(curl
, CURLOPT_REDIR_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
574 std::vector
<std::string
> headers
;
575 if (!cache
.Etag
.empty())
576 headers
.push_back("If-None-Match: " + cache
.Etag
);
578 if (!cache
.LastModified
.empty())
579 headers
.push_back("If-Modified-Since: " + cache
.LastModified
);
581 if (headers
.size() > 0)
582 download
.data
->sendHeaders(headers
);
585 curl_easy_setopt(curl
, CURLOPT_HEADERFUNCTION
, NLGUI::curlHeaderCallback
);
586 curl_easy_setopt(curl
, CURLOPT_WRITEHEADER
, download
.data
);
588 std::string userAgent
= options
.appName
+ "/" + options
.appVersion
;
589 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, userAgent
.c_str());
591 CUrlParser
uri(download
.url
);
592 if (!uri
.host
.empty())
593 sendCookies(curl
, uri
.host
, isTrustedDomain(uri
.host
));
595 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, fp
);
596 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
598 CURLMcode ret
= curl_multi_add_handle(MultiCurl
, curl
);
601 nlwarning("cURL multi handle %p error %d on '%s'", curl
, ret
, download
.url
.c_str());
609 void CGroupHTML::finishCurlDownload(const CDataDownload
&download
)
611 std::string tmpfile
= download
.dest
+ ".tmp";
613 if (download
.type
== ImgType
)
615 // there is race condition if two browser instances are downloading same file
616 // second instance deletes first tmpfile and creates new file for itself.
617 if (CFile::getFileSize(tmpfile
) > 0)
621 // verify that image is not corrupted
623 CBitmap::loadSize(tmpfile
, w
, h
);
624 if (w
!= 0 && h
!= 0)
626 // if not tmpfile, then img is already in cache
627 if (CFile::fileExists(tmpfile
))
629 if (CFile::fileExists(download
.dest
))
631 CFile::deleteFile(download
.dest
);
634 // to reload image on page, the easiest seems to be changing texture
635 // to temp file temporarily. that forces driver to reload texture from disk
636 // ITexture::touch() seem not to do this.
637 // cache was updated, first set texture as temp file
638 for(uint i
= 0; i
< download
.imgs
.size(); i
++)
640 setImage(download
.imgs
[i
].Image
, tmpfile
, download
.imgs
[i
].Type
);
641 setImageSize(download
.imgs
[i
].Image
, download
.imgs
[i
].Style
);
644 CFile::moveFile(download
.dest
, tmpfile
);
647 for(uint i
= 0; i
< download
.imgs
.size(); i
++)
649 setImage(download
.imgs
[i
].Image
, download
.dest
, download
.imgs
[i
].Type
);
650 setImageSize(download
.imgs
[i
].Image
, download
.imgs
[i
].Style
);
655 catch(const NLMISC::Exception
&e
)
657 // exception message has .tmp file name, so keep it for further analysis
658 nlwarning("Invalid image (%s): %s", download
.url
.c_str(), e
.what());
665 if (download
.type
== StylesheetType
)
667 if (CFile::fileExists(tmpfile
))
669 if (CFile::fileExists(download
.dest
))
671 CFile::deleteFile(download
.dest
);
673 CFile::moveFile(download
.dest
, tmpfile
);
675 cssDownloadFinished(download
.url
, download
.dest
);
680 if (download
.type
== BnpType
)
682 bool verified
= false;
683 // no tmpfile if file was already in cache
684 if (CFile::fileExists(tmpfile
))
686 verified
= download
.md5sum
.empty() || (download
.md5sum
!= getMD5(tmpfile
).toString());
689 if (CFile::fileExists(download
.dest
))
691 CFile::deleteFile(download
.dest
);
693 CFile::moveFile(download
.dest
, tmpfile
);
697 CFile::deleteFile(tmpfile
);
700 else if (CFile::fileExists(download
.dest
))
702 verified
= download
.md5sum
.empty() || (download
.md5sum
!= getMD5(download
.dest
).toString());
705 std::string script
= "\nlocal __CURRENT_WINDOW__ = \""+this->_Id
+"\"";
706 script
+= toString("\nlocal __DOWNLOAD_STATUS__ = %s\n", verified
? "true" : "false");
707 script
+= download
.luaScript
;
708 CLuaManager::getInstance().executeLuaScript(script
, true );
713 nlwarning("Unknown CURL download type (%d) finished '%s'", download
.type
, download
.url
.c_str());
716 // Add a image download request in the multi_curl
717 void CGroupHTML::addImageDownload(const string
&url
, CViewBase
*img
, const CStyleParams
&style
, TImageType type
, const std::string
&placeholder
)
719 std::string finalUrl
;
720 img
->setModulateGlobalColor(style
.GlobalColor
);
722 // data:image/png;base64,AA...==
723 if (startsWith(url
, "data:image/"))
725 setImage(img
, decodeURIComponent(url
), type
);
726 setImageSize(img
, style
);
730 // load the image from local files/bnp
731 std::string image
= CFile::getPath(url
) + CFile::getFilenameWithoutExtension(url
) + ".tga";
732 if (lookupLocalFile(finalUrl
, image
.c_str(), false))
734 setImage(img
, image
, type
);
735 setImageSize(img
, style
);
739 finalUrl
= upgradeInsecureUrl(getAbsoluteUrl(url
));
741 // use requested url for local name (cache)
742 string dest
= localImageName(url
);
743 LOG_DL("add to download '%s' dest '%s' img %p", finalUrl
.c_str(), dest
.c_str(), img
);
745 // Display cached image while downloading new
746 if (type
!= OverImage
)
748 std::string temp
= dest
;
749 if (!CFile::fileExists(temp
))
753 setImage(img
, temp
, type
);
754 setImageSize(img
, style
);
757 // Search if we are not already downloading this url.
758 for(std::list
<CDataDownload
>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
760 if(it
->url
== finalUrl
)
762 LOG_DL("already downloading '%s' img %p", finalUrl
.c_str(), img
);
763 it
->imgs
.push_back(CDataImageDownload(img
, style
, type
));
768 Curls
.push_back(CDataDownload(finalUrl
, dest
, ImgType
, img
, "", "", style
, type
));
772 void CGroupHTML::removeImageDownload(CViewBase
*img
)
774 for(std::list
<CDataDownload
>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
776 // check all active downloads because image does not keep url around
777 std::vector
<CDataImageDownload
>::iterator imgIter
= it
->imgs
.begin();
778 while(imgIter
!= it
->imgs
.end())
780 if (imgIter
->Image
== img
)
782 it
->imgs
.erase(imgIter
);
790 void CGroupHTML::initImageDownload()
792 LOG_DL("Init Image Download");
794 string pathName
= "cache";
795 if ( ! CFile::isExists( pathName
) )
796 CFile::createDirectory( pathName
);
800 // Get an url and return the local filename with the path where the bnp should be
801 string
CGroupHTML::localBnpName(const string
&url
)
803 size_t lastIndex
= url
.find_last_of("/");
804 string dest
= "user/"+url
.substr(lastIndex
+1);
808 // Add a bnp download request in the multi_curl, return true if already downloaded
809 bool CGroupHTML::addBnpDownload(string url
, const string
&action
, const string
&script
, const string
&md5sum
)
811 url
= upgradeInsecureUrl(getAbsoluteUrl(url
));
813 // Search if we are not already downloading this url.
814 for(std::list
<CDataDownload
>::const_iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
818 LOG_DL("already downloading '%s'", url
.c_str());
823 string dest
= localBnpName(url
);
824 LOG_DL("add to download '%s' dest '%s'", url
.c_str(), dest
.c_str());
826 // create/delete the local file
827 if (NLMISC::CFile::fileExists(dest
))
829 if (action
== "override" || action
== "delete")
831 CFile::setRWAccess(dest
);
832 NLMISC::CFile::deleteFile(dest
);
839 if (action
!= "delete")
841 Curls
.push_back(CDataDownload(url
, dest
, BnpType
, NULL
, script
, md5sum
));
850 void CGroupHTML::initBnpDownload()
855 LOG_DL("Init Bnp Download");
856 string pathName
= "user";
857 if ( ! CFile::isExists( pathName
) )
858 CFile::createDirectory( pathName
);
861 void CGroupHTML::addStylesheetDownload(const std::vector
<CHtmlParser::StyleLink
> links
)
863 for(uint i
= 0; i
< links
.size(); ++i
)
865 _StylesheetQueue
.push_back(links
[i
]);
866 std::string url
= getAbsoluteUrl(links
[i
].Url
);
867 _StylesheetQueue
.back().Url
= url
;
869 // push to the front of the queue
870 Curls
.push_front(CDataDownload(url
, localImageName(url
), StylesheetType
, NULL
, "", ""));
875 // Call this evenly to check if an element is downloaded and then manage it
876 void CGroupHTML::checkDownloads()
878 //nlassert(_CrtCheckMemory());
880 if(Curls
.empty() && _CurlWWW
== NULL
)
885 int NewRunningCurls
= 0;
886 while(CURLM_CALL_MULTI_PERFORM
== curl_multi_perform(MultiCurl
, &NewRunningCurls
))
888 LOG_DL("more to do now %d - %d curls", NewRunningCurls
, Curls
.size());
891 LOG_DL("NewRunningCurls:%d, RunningCurls:%d", NewRunningCurls
, RunningCurls
);
893 // check which downloads are done
896 while ((msg
= curl_multi_info_read(MultiCurl
, &msgs_left
)))
898 LOG_DL("> (%s) msgs_left %d", _Id
.c_str(), msgs_left
);
899 if (msg
->msg
== CURLMSG_DONE
)
901 if (_CurlWWW
&& _CurlWWW
->Request
&& _CurlWWW
->Request
== msg
->easy_handle
)
904 bool success
= msg
->data
.result
== CURLE_OK
;
907 error
= curl_easy_strerror(msg
->data
.result
);
909 LOG_DL("html download finished with curl code %d (%s)", (sint
)msg
->msg
, error
.c_str());
910 htmlDownloadFinished(success
, error
);
914 for(std::list
<CDataDownload
>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
916 if(it
->data
&& it
->data
->Request
== msg
->easy_handle
)
919 bool success
= msg
->data
.result
== CURLE_OK
;
922 error
= curl_easy_strerror(msg
->data
.result
);
924 LOG_DL("data download finished with curl code %d (%s)", (sint
)msg
->msg
, error
.c_str());
925 dataDownloadFinished(success
, error
, *it
);
935 RunningCurls
= NewRunningCurls
;
940 void CGroupHTML::releaseDownloads()
942 LOG_DL("Release Downloads");
946 LOG_DL("(%s) stop html url '%s'", _Id
.c_str(), _CurlWWW
->Url
.c_str());
948 curl_multi_remove_handle(MultiCurl
, _CurlWWW
->Request
);
954 // remove all queued and already started downloads
955 for(std::list
<CDataDownload
>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
959 LOG_DL("(%s) stop data url '%s'", _Id
.c_str(), it
->url
.c_str());
962 curl_multi_remove_handle(MultiCurl
, it
->data
->Request
);
965 // close and remove temp file
970 if (CFile::fileExists(it
->dest
+ ".tmp"))
972 CFile::deleteFile(it
->dest
+ ".tmp");
979 // also clear css queue as it depends on Curls
980 _StylesheetQueue
.clear();
983 class CGroupListAdaptor
: public CInterfaceGroup
986 CGroupListAdaptor(const TCtorParam
¶m
)
987 : CInterfaceGroup(param
)
995 // Get the W max from the parent
996 _W
= std::min(_Parent
->getMaxWReal(), _Parent
->getWReal());
999 CInterfaceGroup::updateCoords();
1003 // ***************************************************************************
1005 template<class A
> void popIfNotEmpty(A
&vect
) { if(!vect
.empty()) vect
.pop_back(); }
1007 // ***************************************************************************
1008 TStyle
CGroupHTML::parseStyle (const string
&str_styles
)
1011 vector
<string
> elements
;
1012 NLMISC::splitString(str_styles
, ";", elements
);
1014 for(uint i
= 0; i
< elements
.size(); ++i
)
1016 vector
<string
> style
;
1017 NLMISC::splitString(elements
[i
], ":", style
);
1018 if (style
.size() >= 2)
1020 string fullstyle
= style
[1];
1021 for (uint j
=2; j
< style
.size(); j
++)
1022 fullstyle
+= ":"+style
[j
];
1023 styles
[trim(style
[0])] = trimSeparators(fullstyle
);
1030 // ***************************************************************************
1032 void CGroupHTML::addText (const char *buf
, int len
)
1039 // Build a UTF8 string
1040 if (_ParsingLua
&& _TrustedDomain
)
1042 // we are parsing a lua script
1043 _LuaScript
+= string(buf
, buf
+ len
);
1048 // Build a unicode string
1049 CUtfStringView
inputStringView(buf
, len
);
1051 // Build the final unicode string
1054 u32char lastChar
= 0;
1055 u32char inputStringView0
= *inputStringView
.begin();
1056 for (CUtfStringView::iterator
it(inputStringView
.begin()), end(inputStringView
.end()); it
!= end
; ++it
)
1060 // special treatment for 'nbsp' (which is returned as a discreet space)
1061 if (len
== 1 && inputStringView0
== 32)
1063 // this is a nbsp entity
1069 // not nbsp, use normal white space removal routine
1070 keep
= translateChar (output
, *it
, lastChar
);
1075 CUtfStringView::append(tmp
, output
);
1085 // ***************************************************************************
1087 #define registerAnchorName(prefix) \
1089 if (present[prefix##_ID] && value[prefix##_ID]) \
1090 _AnchorName.push_back(value[prefix##_ID]); \
1093 // ***************************************************************************
1094 void CGroupHTML::beginElement (CHtmlElement
&elm
)
1097 _CurrentHTMLElement
= &elm
;
1098 _CurrentHTMLNextSibling
= elm
.nextSibling
;
1100 // set element style from css and style attribute
1101 _Style
.getStyleFor(elm
);
1102 if (!elm
.Style
.empty())
1104 _Style
.applyStyle(elm
.Style
);
1107 if (elm
.hasNonEmptyAttribute("name"))
1109 _AnchorName
.push_back(elm
.getAttribute("name"));
1111 if (elm
.hasNonEmptyAttribute("id"))
1113 _AnchorName
.push_back(elm
.getAttribute("id"));
1116 if (_Style
.Current
.DisplayBlock
)
1123 case HTML_A
: htmlA(elm
); break;
1124 case HTML_BASE
: htmlBASE(elm
); break;
1125 case HTML_BODY
: htmlBODY(elm
); break;
1126 case HTML_BR
: htmlBR(elm
); break;
1127 case HTML_BUTTON
: htmlBUTTON(elm
); break;
1128 case HTML_DD
: htmlDD(elm
); break;
1129 case HTML_DEL
: renderPseudoElement(":before", elm
); break;
1130 case HTML_DIV
: htmlDIV(elm
); break;
1131 case HTML_DL
: htmlDL(elm
); break;
1132 case HTML_DT
: htmlDT(elm
); break;
1133 case HTML_EM
: renderPseudoElement(":before", elm
); break;
1134 case HTML_FONT
: htmlFONT(elm
); break;
1135 case HTML_FORM
: htmlFORM(elm
); break;
1136 case HTML_H1
://no-break
1137 case HTML_H2
://no-break
1138 case HTML_H3
://no-break
1139 case HTML_H4
://no-break
1140 case HTML_H5
://no-break
1141 case HTML_H6
: htmlH(elm
); break;
1142 case HTML_HEAD
: htmlHEAD(elm
); break;
1143 case HTML_HR
: htmlHR(elm
); break;
1144 case HTML_HTML
: htmlHTML(elm
); break;
1145 case HTML_I
: htmlI(elm
); break;
1146 case HTML_IMG
: htmlIMG(elm
); break;
1147 case HTML_INPUT
: htmlINPUT(elm
); break;
1148 case HTML_LI
: htmlLI(elm
); break;
1149 case HTML_LUA
: htmlLUA(elm
); break;
1150 case HTML_META
: htmlMETA(elm
); break;
1151 case HTML_METER
: htmlMETER(elm
); break;
1152 case HTML_OBJECT
: htmlOBJECT(elm
); break;
1153 case HTML_OL
: htmlOL(elm
); break;
1154 case HTML_OPTION
: htmlOPTION(elm
); break;
1155 case HTML_P
: htmlP(elm
); break;
1156 case HTML_PRE
: htmlPRE(elm
); break;
1157 case HTML_PROGRESS
: htmlPROGRESS(elm
); break;
1158 case HTML_SCRIPT
: htmlSCRIPT(elm
); break;
1159 case HTML_SELECT
: htmlSELECT(elm
); break;
1160 case HTML_SMALL
: renderPseudoElement(":before", elm
); break;
1161 case HTML_SPAN
: renderPseudoElement(":before", elm
); break;
1162 case HTML_STRONG
: renderPseudoElement(":before", elm
); break;
1163 case HTML_STYLE
: htmlSTYLE(elm
); break;
1164 case HTML_TABLE
: htmlTABLE(elm
); break;
1165 case HTML_TBODY
: renderPseudoElement(":before", elm
); break;
1166 case HTML_TD
: htmlTD(elm
); break;
1167 case HTML_TEXTAREA
: htmlTEXTAREA(elm
); break;
1168 case HTML_TFOOT
: renderPseudoElement(":before", elm
); break;
1169 case HTML_TH
: htmlTH(elm
); break;
1170 case HTML_TITLE
: htmlTITLE(elm
); break;
1171 case HTML_TR
: htmlTR(elm
); break;
1172 case HTML_U
: renderPseudoElement(":before", elm
); break;
1173 case HTML_UL
: htmlUL(elm
); break;
1175 renderPseudoElement(":before", elm
);
1180 // ***************************************************************************
1181 void CGroupHTML::endElement(CHtmlElement
&elm
)
1183 _CurrentHTMLElement
= &elm
;
1187 case HTML_A
: htmlAend(elm
); break;
1188 case HTML_BASE
: break;
1189 case HTML_BODY
: renderPseudoElement(":after", elm
); break;
1190 case HTML_BR
: break;
1191 case HTML_BUTTON
: htmlBUTTONend(elm
); break;
1192 case HTML_DD
: htmlDDend(elm
); break;
1193 case HTML_DEL
: renderPseudoElement(":after", elm
); break;
1194 case HTML_DIV
: htmlDIVend(elm
); break;
1195 case HTML_DL
: htmlDLend(elm
); break;
1196 case HTML_DT
: htmlDTend(elm
); break;
1197 case HTML_EM
: renderPseudoElement(":after", elm
);break;
1198 case HTML_FONT
: break;
1199 case HTML_FORM
: htmlFORMend(elm
); break;
1200 case HTML_H1
://no-break
1201 case HTML_H2
://no-break
1202 case HTML_H3
://no-break
1203 case HTML_H4
://no-break
1204 case HTML_H5
://no-break
1205 case HTML_H6
: htmlHend(elm
); break;
1206 case HTML_HEAD
: htmlHEADend(elm
); break;
1207 case HTML_HR
: break;
1208 case HTML_HTML
: break;
1209 case HTML_I
: htmlIend(elm
); break;
1210 case HTML_IMG
: break;
1211 case HTML_INPUT
: break;
1212 case HTML_LI
: htmlLIend(elm
); break;
1213 case HTML_LUA
: htmlLUAend(elm
); break;
1214 case HTML_META
: break;
1215 case HTML_METER
: break;
1216 case HTML_OBJECT
: htmlOBJECTend(elm
); break;
1217 case HTML_OL
: htmlOLend(elm
); break;
1218 case HTML_OPTION
: htmlOPTIONend(elm
); break;
1219 case HTML_P
: htmlPend(elm
); break;
1220 case HTML_PRE
: htmlPREend(elm
); break;
1221 case HTML_SCRIPT
: htmlSCRIPTend(elm
); break;
1222 case HTML_SELECT
: htmlSELECTend(elm
); break;
1223 case HTML_SMALL
: renderPseudoElement(":after", elm
);break;
1224 case HTML_SPAN
: renderPseudoElement(":after", elm
);break;
1225 case HTML_STRONG
: renderPseudoElement(":after", elm
);break;
1226 case HTML_STYLE
: htmlSTYLEend(elm
); break;
1227 case HTML_TABLE
: htmlTABLEend(elm
); break;
1228 case HTML_TD
: htmlTDend(elm
); break;
1229 case HTML_TBODY
: renderPseudoElement(":after", elm
); break;
1230 case HTML_TEXTAREA
: htmlTEXTAREAend(elm
); break;
1231 case HTML_TFOOT
: renderPseudoElement(":after", elm
); break;
1232 case HTML_TH
: htmlTHend(elm
); break;
1233 case HTML_TITLE
: htmlTITLEend(elm
); break;
1234 case HTML_TR
: htmlTRend(elm
); break;
1235 case HTML_U
: renderPseudoElement(":after", elm
); break;
1236 case HTML_UL
: htmlULend(elm
); break;
1238 renderPseudoElement(":after", elm
);
1242 if (_Style
.Current
.DisplayBlock
)
1250 // ***************************************************************************
1251 void CGroupHTML::renderPseudoElement(const std::string
&pseudo
, const CHtmlElement
&elm
)
1253 if (pseudo
!= ":before" && pseudo
!= ":after")
1256 if (!elm
.hasPseudo(pseudo
))
1260 _Style
.applyStyle(elm
.getPseudo(pseudo
));
1262 // TODO: 'content' should already be tokenized in css parser as it has all the functions for that
1263 std::string content
= trim(_Style
.getStyle("content"));
1264 if (toLowerAscii(content
) == "none" || toLowerAscii(content
) == "normal")
1270 std::string::size_type pos
= 0;
1271 while(pos
< content
.size())
1273 std::string::size_type start
;
1277 // counter, open-quote, close-quote, no-open-quote, no-close-quote
1278 if (content
[pos
] == '"' || content
[pos
] == '\'')
1280 char quote
= content
[pos
];
1283 while(pos
< content
.size() && content
[pos
] != quote
)
1285 if (content
[pos
] == '\\') pos
++;
1288 token
= content
.substr(start
, pos
- start
);
1291 // skip closing quote
1294 else if (content
[pos
] == 'u' && pos
< content
.size() - 6 && toLowerAscii(content
.substr(pos
, 4)) == "url(")
1296 // url(/path-to/image.jpg) / "Alt!"
1297 // url("/path to/image.jpg") / "Alt!"
1298 std::string tooltip
;
1301 // fails if url contains ')'
1302 pos
= content
.find(")", start
);
1303 token
= trim(content
.substr(start
, pos
- start
));
1309 while(pos
< content
.size() && content
[pos
] == ' ' && content
[pos
] != '/')
1313 if (pos
< content
.size() && content
[pos
] == '/')
1319 while(pos
< content
.size() && content
[pos
] == ' ')
1323 if (pos
< content
.size() && (content
[pos
] == '\'' || content
[pos
] == '"'))
1325 char openQuote
= content
[pos
];
1328 while(pos
< content
.size() && content
[pos
] != openQuote
)
1330 if (content
[pos
] == '\\') pos
++;
1333 tooltip
= content
.substr(start
, pos
- start
);
1335 // skip closing quote
1340 // tooltip should be quoted
1351 if (tooltip
.empty())
1353 addImage(getId() + pseudo
, token
, false, _Style
.Current
);
1357 tooltip
= trimQuotes(tooltip
);
1358 addButton(CCtrlButton::PushButton
, getId() + pseudo
, token
, token
, "", "", "", tooltip
.c_str(), _Style
.Current
);
1361 else if (content
[pos
] == 'a' && pos
< content
.size() - 7)
1365 pos
= content
.find(")", start
);
1366 token
= content
.substr(start
, pos
- start
);
1370 if (elm
.hasAttribute(token
))
1372 addString(elm
.getAttribute(token
));
1384 // ***************************************************************************
1385 void CGroupHTML::renderDOM(CHtmlElement
&elm
)
1387 if (elm
.Type
== CHtmlElement::TEXT_NODE
)
1389 addText(elm
.Value
.c_str(), elm
.Value
.size());
1395 std::list
<CHtmlElement
>::iterator it
= elm
.Children
.begin();
1396 if (!_IgnoreChildElements
)
1398 while(it
!= elm
.Children
.end())
1405 _IgnoreChildElements
= false;
1411 // ***************************************************************************
1412 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupHTML
, std::string
, "html");
1415 // ***************************************************************************
1416 uint32
CGroupHTML::_GroupHtmlUIDPool
= 0;
1417 CGroupHTML::TGroupHtmlByUIDMap
CGroupHTML::_GroupHtmlByUID
;
1420 // ***************************************************************************
1421 CGroupHTML::CGroupHTML(const TCtorParam
¶m
)
1422 : CGroupScrollText(param
),
1423 _TimeoutValue(DEFAULT_RYZOM_CONNECTION_TIMEOUT
),
1424 _RedirectsRemaining(DEFAULT_RYZOM_REDIRECT_LIMIT
),
1425 _CurrentHTMLElement(NULL
)
1427 // add it to map of group html created
1428 _GroupHtmlUID
= ++_GroupHtmlUIDPool
; // valid assigned Id begin to 1!
1429 _GroupHtmlByUID
[_GroupHtmlUID
]= this;
1432 _TrustedDomain
= false;
1433 _ParsingLua
= false;
1434 _LuaHrefHack
= false;
1435 _IgnoreText
= false;
1436 _IgnoreChildElements
= false;
1437 _BrowseNextTime
= false;
1438 _PostNextTime
= false;
1440 _CurrentViewLink
= NULL
;
1441 _CurrentViewImage
= NULL
;
1444 _SelectOption
= false;
1445 _GroupListAdaptor
= NULL
;
1446 _UrlFragment
.clear();
1447 _RefreshUrl
.clear();
1448 _NextRefreshTime
= 0.0;
1449 _LastRefreshTime
= 0.0;
1450 _RenderNextTime
= false;
1451 _WaitingForStylesheet
= false;
1456 CWidgetManager::getInstance()->registerClockMsgTarget(this);
1459 BgColor
= CRGBA::Black
;
1460 ErrorColor
= CRGBA(255, 0, 0);
1461 LinkColor
= CRGBA(0, 0, 255);
1462 TextColor
= CRGBA(255, 255, 255);
1463 H1Color
= CRGBA(255, 255, 255);
1464 H2Color
= CRGBA(255, 255, 255);
1465 H3Color
= CRGBA(255, 255, 255);
1466 H4Color
= CRGBA(255, 255, 255);
1467 H5Color
= CRGBA(255, 255, 255);
1468 H6Color
= CRGBA(255, 255, 255);
1469 ErrorColorGlobalColor
= false;
1470 LinkColorGlobalColor
= false;
1471 TextColorGlobalColor
= false;
1472 H1ColorGlobalColor
= false;
1473 H2ColorGlobalColor
= false;
1474 H3ColorGlobalColor
= false;
1475 H4ColorGlobalColor
= false;
1476 H5ColorGlobalColor
= false;
1477 H6ColorGlobalColor
= false;
1491 LineSpaceFontFactor
= 0.5f
;
1492 DefaultButtonGroup
= "html_text_button";
1493 DefaultFormTextGroup
= "edit_box_widget";
1494 DefaultFormTextAreaGroup
= "edit_box_widget_multiline";
1495 DefaultFormSelectGroup
= "html_form_select_widget";
1496 DefaultFormSelectBoxMenuGroup
= "html_form_select_box_menu_widget";
1497 DefaultCheckBoxBitmapNormal
= "checkbox_normal.tga";
1498 DefaultCheckBoxBitmapPushed
= "checkbox_pushed.tga";
1499 DefaultCheckBoxBitmapOver
= "checkbox_over.tga";
1500 DefaultRadioButtonBitmapNormal
= "w_radiobutton.png";
1501 DefaultRadioButtonBitmapPushed
= "w_radiobutton_pushed.png";
1502 DefaultBackgroundBitmapView
= "bg";
1505 MultiCurl
= curl_multi_init();
1506 #ifdef CURLMOPT_MAX_HOST_CONNECTIONS
1509 // added in libcurl 7.30.0
1510 curl_multi_setopt(MultiCurl
, CURLMOPT_MAX_HOST_CONNECTIONS
, options
.curlMaxConnections
);
1511 curl_multi_setopt(MultiCurl
, CURLMOPT_PIPELINING
, 1);
1517 initImageDownload();
1520 // setup default browser style
1521 setProperty("browser_css_file", "browser.css");
1524 // ***************************************************************************
1526 CGroupHTML::~CGroupHTML()
1528 //releaseImageDownload();
1531 //nlinfo("** CGroupHTML Destroy: %x, %s, uid%d", this, _Id.c_str(), _GroupHtmlUID);
1533 /* Erase from map of Group HTML (thus requestTerminated() callback won't be called)
1534 Do it first, just because don't want requestTerminated() to be called while I'm destroying
1535 (useless and may be dangerous)
1537 _GroupHtmlByUID
.erase(_GroupHtmlUID
);
1546 curl_multi_cleanup(MultiCurl
);
1549 std::string
CGroupHTML::getProperty( const std::string
&name
) const
1556 if( name
== "title_prefix" )
1558 return _TitlePrefix
;
1561 if( name
== "background_color" )
1563 return toString( BgColor
);
1566 if( name
== "error_color" )
1568 return toString( ErrorColor
);
1571 if( name
== "link_color" )
1573 return toString( LinkColor
);
1576 if( name
== "h1_color" )
1578 return toString( H1Color
);
1581 if( name
== "h2_color" )
1583 return toString( H2Color
);
1586 if( name
== "h3_color" )
1588 return toString( H3Color
);
1591 if( name
== "h4_color" )
1593 return toString( H4Color
);
1596 if( name
== "h5_color" )
1598 return toString( H5Color
);
1601 if( name
== "h6_color" )
1603 return toString( H6Color
);
1606 if( name
== "error_color_global_color" )
1608 return toString( ErrorColorGlobalColor
);
1611 if( name
== "link_color_global_color" )
1613 return toString( LinkColorGlobalColor
);
1616 if( name
== "text_color_global_color" )
1618 return toString( TextColorGlobalColor
);
1621 if( name
== "h1_color_global_color" )
1623 return toString( H1ColorGlobalColor
);
1626 if( name
== "h2_color_global_color" )
1628 return toString( H2ColorGlobalColor
);
1631 if( name
== "h3_color_global_color" )
1633 return toString( H3ColorGlobalColor
);
1636 if( name
== "h4_color_global_color" )
1638 return toString( H4ColorGlobalColor
);
1641 if( name
== "h5_color_global_color" )
1643 return toString( H5ColorGlobalColor
);
1646 if( name
== "h6_color_global_color" )
1648 return toString( H6ColorGlobalColor
);
1651 if( name
== "text_font_size" )
1653 return toString( TextFontSize
);
1656 if( name
== "h1_font_size" )
1658 return toString( H1FontSize
);
1661 if( name
== "h2_font_size" )
1663 return toString( H2FontSize
);
1666 if( name
== "h3_font_size" )
1668 return toString( H3FontSize
);
1671 if( name
== "h4_font_size" )
1673 return toString( H4FontSize
);
1676 if( name
== "h5_font_size" )
1678 return toString( H5FontSize
);
1681 if( name
== "h6_font_size" )
1683 return toString( H6FontSize
);
1686 if( name
== "td_begin_space" )
1688 return toString( TDBeginSpace
);
1691 if( name
== "paragraph_begin_space" )
1693 return toString( PBeginSpace
);
1696 if( name
== "li_begin_space" )
1698 return toString( LIBeginSpace
);
1701 if( name
== "ul_begin_space" )
1703 return toString( ULBeginSpace
);
1706 if( name
== "li_indent" )
1708 return toString( LIIndent
);
1711 if( name
== "ul_indent" )
1713 return toString( ULIndent
);
1716 if( name
== "multi_line_space_factor" )
1718 return toString( LineSpaceFontFactor
);
1721 if( name
== "form_text_area_group" )
1723 return DefaultFormTextGroup
;
1726 if( name
== "form_select_group" )
1728 return DefaultFormSelectGroup
;
1731 if( name
== "checkbox_bitmap_normal" )
1733 return DefaultCheckBoxBitmapNormal
;
1736 if( name
== "checkbox_bitmap_pushed" )
1738 return DefaultCheckBoxBitmapPushed
;
1741 if( name
== "checkbox_bitmap_over" )
1743 return DefaultCheckBoxBitmapOver
;
1746 if( name
== "radiobutton_bitmap_normal" )
1748 return DefaultRadioButtonBitmapNormal
;
1751 if( name
== "radiobutton_bitmap_pushed" )
1753 return DefaultRadioButtonBitmapPushed
;
1756 if( name
== "radiobutton_bitmap_over" )
1758 return DefaultRadioButtonBitmapOver
;
1761 if( name
== "background_bitmap_view" )
1763 return DefaultBackgroundBitmapView
;
1766 if( name
== "home" )
1771 if( name
== "browse_next_time" )
1773 return toString( _BrowseNextTime
);
1776 if( name
== "browse_tree" )
1781 if( name
== "browse_undo" )
1783 return _BrowseUndoButton
;
1786 if( name
== "browse_redo" )
1788 return _BrowseRedoButton
;
1791 if( name
== "browse_refresh" )
1793 return _BrowseRefreshButton
;
1796 if( name
== "timeout" )
1798 return toString( _TimeoutValue
);
1801 if( name
== "browser_css_file" )
1803 return _BrowserCssFile
;
1806 return CGroupScrollText::getProperty( name
);
1809 void CGroupHTML::setProperty( const std::string
&name
, const std::string
&value
)
1817 if( name
== "title_prefix" )
1819 _TitlePrefix
= value
;
1823 if( name
== "background_color" )
1826 if( fromString( value
, c
) )
1831 if( name
== "error_color" )
1834 if( fromString( value
, c
) )
1839 if( name
== "link_color" )
1842 if( fromString( value
, c
) )
1847 if( name
== "h1_color" )
1850 if( fromString( value
, c
) )
1855 if( name
== "h2_color" )
1858 if( fromString( value
, c
) )
1863 if( name
== "h3_color" )
1866 if( fromString( value
, c
) )
1871 if( name
== "h4_color" )
1874 if( fromString( value
, c
) )
1879 if( name
== "h5_color" )
1882 if( fromString( value
, c
) )
1887 if( name
== "h6_color" )
1890 if( fromString( value
, c
) )
1895 if( name
== "error_color_global_color" )
1898 if( fromString( value
, b
) )
1899 ErrorColorGlobalColor
= b
;
1903 if( name
== "link_color_global_color" )
1906 if( fromString( value
, b
) )
1907 LinkColorGlobalColor
= b
;
1911 if( name
== "text_color_global_color" )
1914 if( fromString( value
, b
) )
1915 TextColorGlobalColor
= b
;
1919 if( name
== "h1_color_global_color" )
1922 if( fromString( value
, b
) )
1923 H1ColorGlobalColor
= b
;
1927 if( name
== "h2_color_global_color" )
1930 if( fromString( value
, b
) )
1931 H2ColorGlobalColor
= b
;
1935 if( name
== "h3_color_global_color" )
1938 if( fromString( value
, b
) )
1939 H3ColorGlobalColor
= b
;
1943 if( name
== "h4_color_global_color" )
1946 if( fromString( value
, b
) )
1947 H4ColorGlobalColor
= b
;
1951 if( name
== "h5_color_global_color" )
1954 if( fromString( value
, b
) )
1955 H5ColorGlobalColor
= b
;
1959 if( name
== "h6_color_global_color" )
1962 if( fromString( value
, b
) )
1963 H6ColorGlobalColor
= b
;
1967 if( name
== "text_font_size" )
1970 if( fromString( value
, i
) )
1975 if( name
== "h1_font_size" )
1978 if( fromString( value
, i
) )
1983 if( name
== "h2_font_size" )
1986 if( fromString( value
, i
) )
1991 if( name
== "h3_font_size" )
1994 if( fromString( value
, i
) )
1999 if( name
== "h4_font_size" )
2002 if( fromString( value
, i
) )
2007 if( name
== "h5_font_size" )
2010 if( fromString( value
, i
) )
2015 if( name
== "h6_font_size" )
2018 if( fromString( value
, i
) )
2023 if( name
== "td_begin_space" )
2026 if( fromString( value
, i
) )
2031 if( name
== "paragraph_begin_space" )
2034 if( fromString( value
, i
) )
2039 if( name
== "li_begin_space" )
2042 if( fromString( value
, i
) )
2047 if( name
== "ul_begin_space" )
2050 if( fromString( value
, i
) )
2055 if( name
== "li_indent" )
2058 if( fromString( value
, i
) )
2063 if( name
== "ul_indent" )
2066 if( fromString( value
, i
) )
2071 if( name
== "multi_line_space_factor" )
2074 if( fromString( value
, f
) )
2075 LineSpaceFontFactor
= f
;
2079 if( name
== "form_text_area_group" )
2081 DefaultFormTextGroup
= value
;
2085 if( name
== "form_select_group" )
2087 DefaultFormSelectGroup
= value
;
2091 if( name
== "checkbox_bitmap_normal" )
2093 DefaultCheckBoxBitmapNormal
= value
;
2097 if( name
== "checkbox_bitmap_pushed" )
2099 DefaultCheckBoxBitmapPushed
= value
;
2103 if( name
== "checkbox_bitmap_over" )
2105 DefaultCheckBoxBitmapOver
= value
;
2109 if( name
== "radiobutton_bitmap_normal" )
2111 DefaultRadioButtonBitmapNormal
= value
;
2115 if( name
== "radiobutton_bitmap_pushed" )
2117 DefaultRadioButtonBitmapPushed
= value
;
2121 if( name
== "radiobutton_bitmap_over" )
2123 DefaultRadioButtonBitmapOver
= value
;
2127 if( name
== "background_bitmap_view" )
2129 DefaultBackgroundBitmapView
= value
;
2133 if( name
== "home" )
2139 if( name
== "browse_next_time" )
2142 if( fromString( value
, b
) )
2143 _BrowseNextTime
= b
;
2147 if( name
== "browse_tree" )
2149 _BrowseTree
= value
;
2153 if( name
== "browse_undo" )
2155 _BrowseUndoButton
= value
;
2159 if( name
== "browse_redo" )
2161 _BrowseRedoButton
= value
;
2165 if( name
== "browse_refresh" )
2167 _BrowseRefreshButton
= value
;
2171 if( name
== "timeout" )
2174 if( fromString( value
, d
) )
2179 if( name
== "browser_css_file")
2181 _BrowserStyle
.reset();
2182 _BrowserCssFile
= value
;
2183 if (!_BrowserCssFile
.empty())
2185 std::string filename
= CPath::lookup(_BrowserCssFile
, false, true, true);
2186 if (!filename
.empty())
2189 if (in
.open(filename
))
2192 if (in
.readAll(css
))
2193 _BrowserStyle
.parseStylesheet(css
);
2195 nlwarning("Failed to read browser css from '%s'", filename
.c_str());
2199 nlwarning("Failed to open browser css file '%s'", filename
.c_str());
2204 nlwarning("Browser css file '%s' not found", _BrowserCssFile
.c_str());
2209 CGroupScrollText::setProperty( name
, value
);
2212 xmlNodePtr
CGroupHTML::serialize( xmlNodePtr parentNode
, const char *type
) const
2214 xmlNodePtr node
= CGroupScrollText::serialize( parentNode
, type
);
2218 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"html" );
2219 xmlSetProp( node
, BAD_CAST
"url", BAD_CAST _URL
.c_str() );
2220 xmlSetProp( node
, BAD_CAST
"title_prefix", BAD_CAST _TitlePrefix
.c_str() );
2221 xmlSetProp( node
, BAD_CAST
"background_color", BAD_CAST
toString( BgColor
).c_str() );
2222 xmlSetProp( node
, BAD_CAST
"error_color", BAD_CAST
toString( ErrorColor
).c_str() );
2223 xmlSetProp( node
, BAD_CAST
"link_color", BAD_CAST
toString( LinkColor
).c_str() );
2224 xmlSetProp( node
, BAD_CAST
"background_color", BAD_CAST
toString( BgColor
).c_str() );
2225 xmlSetProp( node
, BAD_CAST
"h1_color", BAD_CAST
toString( H1Color
).c_str() );
2226 xmlSetProp( node
, BAD_CAST
"h2_color", BAD_CAST
toString( H2Color
).c_str() );
2227 xmlSetProp( node
, BAD_CAST
"h3_color", BAD_CAST
toString( H3Color
).c_str() );
2228 xmlSetProp( node
, BAD_CAST
"h4_color", BAD_CAST
toString( H4Color
).c_str() );
2229 xmlSetProp( node
, BAD_CAST
"h5_color", BAD_CAST
toString( H5Color
).c_str() );
2230 xmlSetProp( node
, BAD_CAST
"h6_color", BAD_CAST
toString( H6Color
).c_str() );
2232 xmlSetProp( node
, BAD_CAST
"error_color_global_color",
2233 BAD_CAST
toString( ErrorColorGlobalColor
).c_str() );
2234 xmlSetProp( node
, BAD_CAST
"link_color_global_color",
2235 BAD_CAST
toString( LinkColorGlobalColor
).c_str() );
2236 xmlSetProp( node
, BAD_CAST
"text_color_global_color",
2237 BAD_CAST
toString( TextColorGlobalColor
).c_str() );
2238 xmlSetProp( node
, BAD_CAST
"h1_color_global_color",
2239 BAD_CAST
toString( H1ColorGlobalColor
).c_str() );
2240 xmlSetProp( node
, BAD_CAST
"h2_color_global_color",
2241 BAD_CAST
toString( H2ColorGlobalColor
).c_str() );
2242 xmlSetProp( node
, BAD_CAST
"h3_color_global_color",
2243 BAD_CAST
toString( H3ColorGlobalColor
).c_str() );
2244 xmlSetProp( node
, BAD_CAST
"h4_color_global_color",
2245 BAD_CAST
toString( H4ColorGlobalColor
).c_str() );
2246 xmlSetProp( node
, BAD_CAST
"h5_color_global_color",
2247 BAD_CAST
toString( H5ColorGlobalColor
).c_str() );
2248 xmlSetProp( node
, BAD_CAST
"h6_color_global_color",
2249 BAD_CAST
toString( H6ColorGlobalColor
).c_str() );
2251 xmlSetProp( node
, BAD_CAST
"text_font_size", BAD_CAST
toString( TextFontSize
).c_str() );
2252 xmlSetProp( node
, BAD_CAST
"h1_font_size", BAD_CAST
toString( H1FontSize
).c_str() );
2253 xmlSetProp( node
, BAD_CAST
"h2_font_size", BAD_CAST
toString( H2FontSize
).c_str() );
2254 xmlSetProp( node
, BAD_CAST
"h3_font_size", BAD_CAST
toString( H3FontSize
).c_str() );
2255 xmlSetProp( node
, BAD_CAST
"h4_font_size", BAD_CAST
toString( H4FontSize
).c_str() );
2256 xmlSetProp( node
, BAD_CAST
"h5_font_size", BAD_CAST
toString( H5FontSize
).c_str() );
2257 xmlSetProp( node
, BAD_CAST
"h6_font_size", BAD_CAST
toString( H6FontSize
).c_str() );
2258 xmlSetProp( node
, BAD_CAST
"td_begin_space", BAD_CAST
toString( TDBeginSpace
).c_str() );
2259 xmlSetProp( node
, BAD_CAST
"paragraph_begin_space", BAD_CAST
toString( PBeginSpace
).c_str() );
2260 xmlSetProp( node
, BAD_CAST
"li_begin_space", BAD_CAST
toString( LIBeginSpace
).c_str() );
2261 xmlSetProp( node
, BAD_CAST
"ul_begin_space", BAD_CAST
toString( ULBeginSpace
).c_str() );
2262 xmlSetProp( node
, BAD_CAST
"li_indent", BAD_CAST
toString( LIIndent
).c_str() );
2263 xmlSetProp( node
, BAD_CAST
"ul_indent", BAD_CAST
toString( ULIndent
).c_str() );
2264 xmlSetProp( node
, BAD_CAST
"multi_line_space_factor", BAD_CAST
toString( LineSpaceFontFactor
).c_str() );
2265 xmlSetProp( node
, BAD_CAST
"form_text_area_group", BAD_CAST DefaultFormTextGroup
.c_str() );
2266 xmlSetProp( node
, BAD_CAST
"form_select_group", BAD_CAST DefaultFormSelectGroup
.c_str() );
2267 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_normal", BAD_CAST DefaultCheckBoxBitmapNormal
.c_str() );
2268 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_pushed", BAD_CAST DefaultCheckBoxBitmapPushed
.c_str() );
2269 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_over", BAD_CAST DefaultCheckBoxBitmapOver
.c_str() );
2270 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_normal", BAD_CAST DefaultRadioButtonBitmapNormal
.c_str() );
2271 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_pushed", BAD_CAST DefaultRadioButtonBitmapPushed
.c_str() );
2272 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_over", BAD_CAST DefaultRadioButtonBitmapOver
.c_str() );
2273 xmlSetProp( node
, BAD_CAST
"background_bitmap_view", BAD_CAST DefaultBackgroundBitmapView
.c_str() );
2274 xmlSetProp( node
, BAD_CAST
"home", BAD_CAST Home
.c_str() );
2275 xmlSetProp( node
, BAD_CAST
"browse_next_time", BAD_CAST
toString( _BrowseNextTime
).c_str() );
2276 xmlSetProp( node
, BAD_CAST
"browse_tree", BAD_CAST _BrowseTree
.c_str() );
2277 xmlSetProp( node
, BAD_CAST
"browse_undo", BAD_CAST _BrowseUndoButton
.c_str() );
2278 xmlSetProp( node
, BAD_CAST
"browse_redo", BAD_CAST _BrowseRedoButton
.c_str() );
2279 xmlSetProp( node
, BAD_CAST
"browse_refresh", BAD_CAST _BrowseRefreshButton
.c_str() );
2280 xmlSetProp( node
, BAD_CAST
"timeout", BAD_CAST
toString( _TimeoutValue
).c_str() );
2281 xmlSetProp( node
, BAD_CAST
"browser_css_file", BAD_CAST _BrowserCssFile
.c_str() );
2286 // ***************************************************************************
2288 bool CGroupHTML::parse(xmlNodePtr cur
,CInterfaceGroup
*parentGroup
)
2290 nlassert( CWidgetManager::getInstance()->isClockMsgTarget(this));
2293 if(!CGroupScrollText::parse(cur
, parentGroup
))
2297 //nlinfo("** CGroupHTML parsed Ok: %x, %s, %s, uid%d", this, _Id.c_str(), typeid(this).name(), _GroupHtmlUID);
2302 ptr
= xmlGetProp (cur
, (xmlChar
*)"url");
2304 _URL
= (const char*)ptr
;
2306 // Bkup default for undo/redo
2309 ptr
= xmlGetProp (cur
, (xmlChar
*)"title_prefix");
2311 _TitlePrefix
= CI18N::get((const char*)ptr
);
2314 ptr
= xmlGetProp (cur
, (xmlChar
*)"background_color");
2316 BgColor
= convertColor(ptr
);
2317 ptr
= xmlGetProp (cur
, (xmlChar
*)"error_color");
2319 ErrorColor
= convertColor(ptr
);
2320 ptr
= xmlGetProp (cur
, (xmlChar
*)"link_color");
2322 LinkColor
= convertColor(ptr
);
2323 ptr
= xmlGetProp (cur
, (xmlChar
*)"text_color");
2325 TextColor
= convertColor(ptr
);
2326 ptr
= xmlGetProp (cur
, (xmlChar
*)"h1_color");
2328 H1Color
= convertColor(ptr
);
2329 ptr
= xmlGetProp (cur
, (xmlChar
*)"h2_color");
2331 H2Color
= convertColor(ptr
);
2332 ptr
= xmlGetProp (cur
, (xmlChar
*)"h3_color");
2334 H3Color
= convertColor(ptr
);
2335 ptr
= xmlGetProp (cur
, (xmlChar
*)"h4_color");
2337 H4Color
= convertColor(ptr
);
2338 ptr
= xmlGetProp (cur
, (xmlChar
*)"h5_color");
2340 H5Color
= convertColor(ptr
);
2341 ptr
= xmlGetProp (cur
, (xmlChar
*)"h6_color");
2343 H6Color
= convertColor(ptr
);
2344 ptr
= xmlGetProp (cur
, (xmlChar
*)"error_color_global_color");
2346 ErrorColorGlobalColor
= convertBool(ptr
);
2347 ptr
= xmlGetProp (cur
, (xmlChar
*)"link_color_global_color");
2349 LinkColorGlobalColor
= convertBool(ptr
);
2350 ptr
= xmlGetProp (cur
, (xmlChar
*)"text_color_global_color");
2352 TextColorGlobalColor
= convertBool(ptr
);
2353 ptr
= xmlGetProp (cur
, (xmlChar
*)"h1_color_global_color");
2355 H1ColorGlobalColor
= convertBool(ptr
);
2356 ptr
= xmlGetProp (cur
, (xmlChar
*)"h2_color_global_color");
2358 H2ColorGlobalColor
= convertBool(ptr
);
2359 ptr
= xmlGetProp (cur
, (xmlChar
*)"h3_color_global_color");
2361 H3ColorGlobalColor
= convertBool(ptr
);
2362 ptr
= xmlGetProp (cur
, (xmlChar
*)"h4_color_global_color");
2364 H4ColorGlobalColor
= convertBool(ptr
);
2365 ptr
= xmlGetProp (cur
, (xmlChar
*)"h5_color_global_color");
2367 H5ColorGlobalColor
= convertBool(ptr
);
2368 ptr
= xmlGetProp (cur
, (xmlChar
*)"h6_color_global_color");
2370 H6ColorGlobalColor
= convertBool(ptr
);
2371 ptr
= xmlGetProp (cur
, (xmlChar
*)"text_font_size");
2373 fromString((const char*)ptr
, TextFontSize
);
2374 ptr
= xmlGetProp (cur
, (xmlChar
*)"h1_font_size");
2376 fromString((const char*)ptr
, H1FontSize
);
2377 ptr
= xmlGetProp (cur
, (xmlChar
*)"h2_font_size");
2379 fromString((const char*)ptr
, H2FontSize
);
2380 ptr
= xmlGetProp (cur
, (xmlChar
*)"h3_font_size");
2382 fromString((const char*)ptr
, H3FontSize
);
2383 ptr
= xmlGetProp (cur
, (xmlChar
*)"h4_font_size");
2385 fromString((const char*)ptr
, H4FontSize
);
2386 ptr
= xmlGetProp (cur
, (xmlChar
*)"h5_font_size");
2388 fromString((const char*)ptr
, H5FontSize
);
2389 ptr
= xmlGetProp (cur
, (xmlChar
*)"h6_font_size");
2391 fromString((const char*)ptr
, H6FontSize
);
2392 ptr
= xmlGetProp (cur
, (xmlChar
*)"td_begin_space");
2394 fromString((const char*)ptr
, TDBeginSpace
);
2395 ptr
= xmlGetProp (cur
, (xmlChar
*)"paragraph_begin_space");
2397 fromString((const char*)ptr
, PBeginSpace
);
2398 ptr
= xmlGetProp (cur
, (xmlChar
*)"li_begin_space");
2400 fromString((const char*)ptr
, LIBeginSpace
);
2401 ptr
= xmlGetProp (cur
, (xmlChar
*)"ul_begin_space");
2403 fromString((const char*)ptr
, ULBeginSpace
);
2404 ptr
= xmlGetProp (cur
, (xmlChar
*)"li_indent");
2406 fromString((const char*)ptr
, LIIndent
);
2407 ptr
= xmlGetProp (cur
, (xmlChar
*)"ul_indent");
2409 fromString((const char*)ptr
, ULIndent
);
2410 ptr
= xmlGetProp (cur
, (xmlChar
*)"multi_line_space_factor");
2412 fromString((const char*)ptr
, LineSpaceFontFactor
);
2413 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_text_group");
2415 DefaultFormTextGroup
= (const char*)(ptr
);
2416 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_text_area_group");
2418 DefaultFormTextAreaGroup
= (const char*)(ptr
);
2419 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_select_group");
2421 DefaultFormSelectGroup
= (const char*)(ptr
);
2422 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_normal");
2424 DefaultCheckBoxBitmapNormal
= (const char*)(ptr
);
2425 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_pushed");
2427 DefaultCheckBoxBitmapPushed
= (const char*)(ptr
);
2428 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_over");
2430 DefaultCheckBoxBitmapOver
= (const char*)(ptr
);
2431 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_normal");
2433 DefaultRadioButtonBitmapNormal
= (const char*)(ptr
);
2434 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_pushed");
2436 DefaultRadioButtonBitmapPushed
= (const char*)(ptr
);
2437 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_over");
2439 DefaultRadioButtonBitmapOver
= (const char*)(ptr
);
2440 ptr
= xmlGetProp (cur
, (xmlChar
*)"background_bitmap_view");
2442 DefaultBackgroundBitmapView
= (const char*)(ptr
);
2443 ptr
= xmlGetProp (cur
, (xmlChar
*)"home");
2445 Home
= (const char*)(ptr
);
2446 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_next_time");
2448 _BrowseNextTime
= convertBool(ptr
);
2449 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_tree");
2451 _BrowseTree
= (const char*)ptr
;
2452 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_undo");
2454 _BrowseUndoButton
= (const char*)ptr
;
2455 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_redo");
2457 _BrowseRedoButton
= (const char*)ptr
;
2458 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_refresh");
2460 _BrowseRefreshButton
= (const char*)ptr
;
2461 ptr
= xmlGetProp (cur
, (xmlChar
*)"timeout");
2463 fromString((const char*)ptr
, _TimeoutValue
);
2465 ptr
= xmlGetProp (cur
, (xmlChar
*)"browser_css_file");
2468 setProperty("browser_css_file", (const char *)ptr
);
2474 // ***************************************************************************
2476 bool CGroupHTML::handleEvent (const NLGUI::CEventDescriptor
& eventDesc
)
2478 bool traited
= false;
2480 if (eventDesc
.getType() == NLGUI::CEventDescriptor::mouse
)
2482 const NLGUI::CEventDescriptorMouse
&mouseEvent
= (const NLGUI::CEventDescriptorMouse
&)eventDesc
;
2483 if (mouseEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mousewheel
)
2485 // Check if mouse wheel event was on any of multiline select box widgets
2486 // Must do this before CGroupScrollText
2487 for (uint i
=0; i
<_Forms
.size() && !traited
; i
++)
2489 for (uint j
=0; j
<_Forms
[i
].Entries
.size() && !traited
; j
++)
2491 if (_Forms
[i
].Entries
[j
].SelectBox
)
2493 if (_Forms
[i
].Entries
[j
].SelectBox
->handleEvent(eventDesc
))
2505 traited
= CGroupScrollText::handleEvent (eventDesc
);
2507 if (eventDesc
.getType() == NLGUI::CEventDescriptor::system
)
2509 const NLGUI::CEventDescriptorSystem
&systemEvent
= (const NLGUI::CEventDescriptorSystem
&) eventDesc
;
2510 if (systemEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::clocktick
)
2515 if (systemEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::activecalledonparent
)
2517 if (!((NLGUI::CEventDescriptorActiveCalledOnParent
&) systemEvent
).getActive())
2519 // stop refresh when window gets hidden
2520 _NextRefreshTime
= 0;
2527 // ***************************************************************************
2529 void CGroupHTML::endParagraph()
2536 // ***************************************************************************
2538 void CGroupHTML::newParagraph(uint beginSpace
)
2540 // Add a new paragraph
2541 CGroupParagraph
*newParagraph
= new CGroupParagraph(CViewBase::TCtorParam());
2542 newParagraph
->setId(getCurrentGroup()->getId() + ":PARAGRAPH" + toString(getNextAutoIdSeq()));
2543 newParagraph
->setResizeFromChildH(true);
2545 newParagraph
->setMarginLeft(getIndent());
2548 addHtmlGroup (newParagraph
, beginSpace
);
2549 _Paragraph
= newParagraph
;
2554 // ***************************************************************************
2556 void CGroupHTML::browse(const char *url
)
2559 pushUrlUndoRedo(url
);
2561 // do the browse, with no undo/redo
2565 // ***************************************************************************
2566 void CGroupHTML::refresh()
2569 doBrowse(_URL
.c_str(), true);
2572 // ***************************************************************************
2573 void CGroupHTML::doBrowse(const char *url
, bool force
)
2575 LOG_DL("(%s) Browsing URL : '%s'", _Id
.c_str(), url
);
2577 CUrlParser
uri(url
);
2578 if (!uri
.hash
.empty())
2580 // Anchor to scroll after page has loaded
2581 _UrlFragment
= uri
.hash
;
2583 uri
.inherit(_DocumentUrl
);
2586 // compare urls and see if we only navigating to new anchor
2587 if (!force
&& _DocumentUrl
== uri
.toString())
2589 // scroll happens in updateCoords()
2595 _UrlFragment
.clear();
2598 _URL
= uri
.toString();
2599 _BrowseNextTime
= true;
2600 _WaitingForStylesheet
= false;
2602 // if a BrowseTree is bound to us, try to select the node that opens this URL (auto-locate)
2603 if(!_BrowseTree
.empty())
2605 CGroupTree
*groupTree
=dynamic_cast<CGroupTree
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseTree
));
2608 string nodeId
= selectTreeNodeRecurs(groupTree
->getRootNode(), url
);
2612 groupTree
->selectNodeById(nodeId
);
2618 // ***************************************************************************
2620 void CGroupHTML::browseError (const char *msg
)
2624 // Get the list group from CGroupScrollText
2627 CViewText
*viewText
= new CViewText ("", (string("Error : ")+msg
).c_str());
2628 viewText
->setColor (ErrorColor
);
2629 viewText
->setModulateGlobalColor(ErrorColorGlobalColor
);
2630 viewText
->setMultiLine (true);
2631 getParagraph()->addChild (viewText
);
2632 if(!_TitlePrefix
.empty())
2633 setTitle (_TitlePrefix
);
2635 updateRefreshButton();
2639 // ***************************************************************************
2641 bool CGroupHTML::isBrowsing()
2643 // do not show spinning cursor for image downloads (!Curls.empty())
2644 return _BrowseNextTime
|| _PostNextTime
|| _RenderNextTime
||
2645 _Browsing
|| _WaitingForStylesheet
||
2649 // ***************************************************************************
2651 void CGroupHTML::updateCoords()
2653 CGroupScrollText::updateCoords();
2655 // all elements are in their correct place, tell scrollbar to scroll to anchor
2656 if (!_Browsing
&& !_UrlFragment
.empty())
2658 doBrowseAnchor(_UrlFragment
);
2659 _UrlFragment
.clear();
2663 // ***************************************************************************
2665 bool CGroupHTML::translateChar(u32char
&output
, u32char input
, u32char lastCharParam
) const
2670 // char is between table elements
2671 // TODO: only whitespace is handled, text is added to either TD, or after TABLE (should be before)
2672 bool tableWhitespace
= getTable() && (_Cells
.empty() || _Cells
.back() == NULL
);
2676 // Return / tab only in <PRE> mode
2680 if (tableWhitespace
)
2686 // Get the last char
2687 u32char lastChar
= lastCharParam
;
2689 lastChar
= getLastChar();
2690 keep
= ((lastChar
!= (u32char
)' ') &&
2691 (lastChar
!= 0)) || getPRE() || (_CurrentViewImage
&& (lastChar
== 0));
2693 input
= (u32char
)' ';
2699 if (tableWhitespace
)
2705 // Get the last char
2706 u32char lastChar
= lastCharParam
;
2708 lastChar
= getLastChar();
2709 keep
= ((lastChar
!= (u32char
)' ') &&
2710 (lastChar
!= (u32char
)'\n') &&
2711 (lastChar
!= 0)) || getPRE() || (_CurrentViewImage
&& (lastChar
== 0));
2728 // ***************************************************************************
2730 void CGroupHTML::registerAnchor(CInterfaceElement
* elm
)
2732 if (!_AnchorName
.empty())
2734 for(uint32 i
=0; i
< _AnchorName
.size(); ++i
)
2736 // filter out duplicates and register only first
2737 if (!_AnchorName
[i
].empty() && _Anchors
.count(_AnchorName
[i
]) == 0)
2739 _Anchors
[_AnchorName
[i
]] = elm
;
2743 _AnchorName
.clear();
2747 // ***************************************************************************
2749 void CGroupHTML::addString(const std::string
&str
)
2751 string tmpStr
= str
;
2755 string _str
= tmpStr
;
2756 string::size_type p
= _str
.find('#');
2757 if (p
== string::npos
)
2759 tmpStr
= CI18N::get(_str
);
2763 string cmd
= _str
.substr(0, p
);
2764 string arg
= _str
.substr(p
+1);
2768 uint year
, month
, day
;
2769 sscanf(arg
.c_str(), "%d/%d/%d", &year
, &month
, &day
);
2770 tmpStr
= CI18N::get( "uiMFIDate");
2772 year
+= (year
> 70 ? 1900 : 2000);
2774 strFindReplace(tmpStr
, "%year", toString("%d", year
) );
2775 strFindReplace(tmpStr
, "%month", CI18N::get(toString("uiMonth%02d", month
)) );
2776 strFindReplace(tmpStr
, "%day", toString("%d", day
) );
2788 _TitleString
+= tmpStr
;
2792 _TextAreaContent
+= tmpStr
;
2796 _ObjectScript
+= tmpStr
;
2798 else if (_SelectOption
)
2800 if (!(_Forms
.empty()))
2802 if (!_Forms
.back().Entries
.empty())
2804 _SelectOptionStr
+= tmpStr
;
2817 CStyleParams
&style
= _Style
.Current
;
2821 bool embolden
= style
.FontWeight
>= FONT_WEIGHT_BOLD
;
2823 // Number of child in this paragraph
2824 if (_CurrentViewLink
)
2826 bool skipLine
= !_CurrentViewLink
->getText().empty() && *(_CurrentViewLink
->getText().rbegin()) == '\n';
2827 bool sameShadow
= style
.TextShadow
.Enabled
&& _CurrentViewLink
->getShadow();
2828 if (sameShadow
&& style
.TextShadow
.Enabled
)
2831 _CurrentViewLink
->getShadowOffset(sx
, sy
);
2832 sameShadow
= (style
.TextShadow
.Color
== _CurrentViewLink
->getShadowColor());
2833 sameShadow
= sameShadow
&& (style
.TextShadow
.Outline
== _CurrentViewLink
->getShadowOutline());
2834 sameShadow
= sameShadow
&& (style
.TextShadow
.X
== sx
) && (style
.TextShadow
.Y
== sy
);
2836 // Compatible with current parameters ?
2837 if (!skipLine
&& sameShadow
&&
2838 (style
.TextColor
== _CurrentViewLink
->getColor()) &&
2839 (style
.FontFamily
== _CurrentViewLink
->getFontName()) &&
2840 (style
.FontSize
== (uint
)_CurrentViewLink
->getFontSize()) &&
2841 (style
.Underlined
== _CurrentViewLink
->getUnderlined()) &&
2842 (style
.StrikeThrough
== _CurrentViewLink
->getStrikeThrough()) &&
2843 (embolden
== _CurrentViewLink
->getEmbolden()) &&
2844 (style
.FontOblique
== _CurrentViewLink
->getOblique()) &&
2845 (getLink() == _CurrentViewLink
->Link
) &&
2846 (style
.GlobalColor
== _CurrentViewLink
->getModulateGlobalColor()))
2849 _CurrentViewLink
->setText(_CurrentViewLink
->getText()+tmpStr
);
2850 _CurrentViewLink
->invalidateContent();
2858 if (getA() && string(getLinkClass()) == "ryzom-ui-button")
2860 string buttonTemplate
= DefaultButtonGroup
;
2861 // Action handler parameters : "name=group_html_id|form=id_of_the_form|submit_button=button_name"
2862 string param
= "name=" + this->_Id
+ "|url=" + getLink();
2864 if (!_AnchorName
.empty())
2865 name
= _AnchorName
.back();
2866 typedef pair
<string
, string
> TTmplParam
;
2867 vector
<TTmplParam
> tmplParams
;
2868 tmplParams
.push_back(TTmplParam("id", ""));
2869 tmplParams
.push_back(TTmplParam("onclick", "browse"));
2870 tmplParams
.push_back(TTmplParam("onclick_param", param
));
2871 tmplParams
.push_back(TTmplParam("active", "true"));
2872 CInterfaceGroup
*buttonGroup
= CWidgetManager::getInstance()->getParser()->createGroupInstance(buttonTemplate
, getId()+":"+name
, tmplParams
);
2875 buttonGroup
->setId(getId()+":"+name
);
2876 // Add the ctrl button
2877 CCtrlTextButton
*ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("button"));
2878 if (!ctrlButton
) ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("b"));
2881 ctrlButton
->setModulateGlobalColorAll (false);
2883 // Translate the tooltip
2884 ctrlButton
->setDefaultContextHelp(getLinkTitle());
2885 ctrlButton
->setText(tmpStr
);
2886 // empty url / button disabled
2887 bool disabled
= string(getLink()).empty();
2888 ctrlButton
->setFrozen(disabled
);
2890 setTextButtonStyle(ctrlButton
, style
);
2892 getParagraph()->addChild (buttonGroup
);
2899 CViewLink
*newLink
= new CViewLink(CViewBase::TCtorParam());
2902 newLink
->Link
= getLink();
2903 newLink
->LinkTitle
= getLinkTitle();
2904 if (!newLink
->Link
.empty())
2906 newLink
->setHTMLView (this);
2908 newLink
->setActionOnLeftClick("browse");
2909 newLink
->setParamsOnLeftClick("name=" + getId() + "|url=" + newLink
->Link
);
2912 newLink
->setText(tmpStr
);
2913 newLink
->setMultiLineSpace((uint
)((float)(style
.FontSize
)*LineSpaceFontFactor
));
2914 newLink
->setMultiLine(true);
2915 newLink
->setModulateGlobalColor(style
.GlobalColor
);
2916 setTextStyle(newLink
, style
);
2917 // newLink->setLineAtBottom (true);
2919 registerAnchor(newLink
);
2921 if (getA() && !newLink
->Link
.empty())
2923 getParagraph()->addChildLink(newLink
);
2927 getParagraph()->addChild(newLink
);
2935 // ***************************************************************************
2937 void CGroupHTML::addImage(const std::string
&id
, const std::string
&img
, bool reloadImg
, const CStyleParams
&style
)
2946 // No more text in this text view
2947 _CurrentViewLink
= NULL
;
2950 CViewBitmap
*newImage
= new CViewBitmap (TCtorParam());
2951 newImage
->setId(id
);
2953 addImageDownload(img
, newImage
, style
, NormalImage
);
2954 newImage
->setRenderLayer(getRenderLayer()+1);
2956 getParagraph()->addChild(newImage
);
2959 setImageSize(newImage
, style
);
2962 // ***************************************************************************
2964 CInterfaceGroup
*CGroupHTML::addTextArea(const std::string
&templateName
, const char *name
, uint rows
, uint cols
, bool multiLine
, const std::string
&content
, uint maxlength
)
2973 // No more text in this text view
2974 _CurrentViewLink
= NULL
;
2976 CStyleParams
&style
= _Style
.Current
;
2978 // override cols/rows values from style
2979 if (style
.Width
> 0) cols
= style
.Width
/ style
.FontSize
;
2980 if (style
.Height
> 0) rows
= style
.Height
/ style
.FontSize
;
2983 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
2984 templateParams
.push_back (std::pair
<std::string
,std::string
> ("w", toString (cols
*style
.FontSize
)));
2985 templateParams
.push_back (std::pair
<std::string
,std::string
> ("id", name
));
2986 templateParams
.push_back (std::pair
<std::string
,std::string
> ("prompt", ""));
2987 templateParams
.push_back (std::pair
<std::string
,std::string
> ("multiline", multiLine
?"true":"false"));
2988 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontsize", toString (style
.FontSize
)));
2989 templateParams
.push_back (std::pair
<std::string
,std::string
> ("color", style
.TextColor
.toString()));
2990 if (style
.FontWeight
>= FONT_WEIGHT_BOLD
)
2991 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontweight", "bold"));
2992 if (style
.FontOblique
)
2993 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontstyle", "oblique"));
2995 templateParams
.push_back (std::pair
<std::string
,std::string
> ("multi_min_line", toString(rows
)));
2996 templateParams
.push_back (std::pair
<std::string
,std::string
> ("want_return", multiLine
?"true":"false"));
2997 templateParams
.push_back (std::pair
<std::string
,std::string
> ("onenter", ""));
2998 templateParams
.push_back (std::pair
<std::string
,std::string
> ("enter_recover_focus", "false"));
3000 templateParams
.push_back (std::pair
<std::string
,std::string
> ("max_num_chars", toString(maxlength
)));
3001 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow", toString(style
.TextShadow
.Enabled
)));
3002 if (style
.TextShadow
.Enabled
)
3004 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_x", toString(style
.TextShadow
.X
)));
3005 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_y", toString(style
.TextShadow
.Y
)));
3006 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_color", style
.TextShadow
.Color
.toString()));
3007 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_outline", toString(style
.TextShadow
.Outline
)));
3010 CInterfaceGroup
*textArea
= CWidgetManager::getInstance()->getParser()->createGroupInstance (templateName
.c_str(),
3011 getParagraph()->getId(), templateParams
.empty()?NULL
:&(templateParams
[0]), (uint
)templateParams
.size());
3017 CGroupEditBox
*eb
= dynamic_cast<CGroupEditBox
*>(textArea
->getGroup("eb"));
3020 eb
->setInputString(decodeHTMLEntities(content
));
3021 if (style
.hasStyle("background-color"))
3023 CViewBitmap
*bg
= dynamic_cast<CViewBitmap
*>(eb
->getView("bg"));
3026 bg
->setTexture("blank.tga");
3027 bg
->setColor(style
.BackgroundColor
);
3032 textArea
->invalidateCoords();
3033 getParagraph()->addChild (textArea
);
3040 // Not group created
3044 // ***************************************************************************
3045 CDBGroupComboBox
*CGroupHTML::addComboBox(const std::string
&templateName
, const char *name
)
3057 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
3058 templateParams
.push_back (std::pair
<std::string
,std::string
> ("id", name
));
3059 CInterfaceGroup
*group
= CWidgetManager::getInstance()->getParser()->createGroupInstance (templateName
.c_str(),
3060 getParagraph()->getId(), templateParams
.empty()?NULL
:&(templateParams
[0]), (uint
)templateParams
.size());
3066 CDBGroupComboBox
*cb
= dynamic_cast<CDBGroupComboBox
*>(group
);
3069 nlwarning("'%s' template has bad type, combo box expected", templateName
.c_str());
3075 getParagraph()->addChild (cb
);
3082 // Not group created
3086 // ***************************************************************************
3087 CGroupMenu
*CGroupHTML::addSelectBox(const std::string
&templateName
, const char *name
)
3097 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
3098 templateParams
.push_back(std::pair
<std::string
,std::string
> ("id", name
));
3099 CInterfaceGroup
*group
= CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName
.c_str(),
3100 getParagraph()->getId(), &(templateParams
[0]), (uint
)templateParams
.size());
3106 CGroupMenu
*sb
= dynamic_cast<CGroupMenu
*>(group
);
3109 nlwarning("'%s' template has bad type, CGroupMenu expected", templateName
.c_str());
3115 getParagraph()->addChild (sb
);
3125 // ***************************************************************************
3127 CCtrlButton
*CGroupHTML::addButton(CCtrlButton::EType type
, const std::string
&name
, const std::string
&normalBitmap
, const std::string
&pushedBitmap
,
3128 const std::string
&overBitmap
, const char *actionHandler
, const char *actionHandlerParams
,
3129 const std::string
&tooltip
, const CStyleParams
&style
)
3138 // Add the ctrl button
3139 CCtrlButton
*ctrlButton
= new CCtrlButton(TCtorParam());
3142 ctrlButton
->setId(name
);
3146 if (startsWith(normalBitmap
, "data:image/"))
3148 normal
= decodeURIComponent(normalBitmap
);
3152 // Load only tga files.. (conversion in dds filename is done in the lookup procedure)
3153 normal
= normalBitmap
.empty()?"":CFile::getPath(normalBitmap
) + CFile::getFilenameWithoutExtension(normalBitmap
) + ".tga";
3155 // if the image doesn't exist on local, we check in the cache
3156 if(!CPath::exists(normal
))
3158 // search in the compressed texture
3159 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
3160 sint32 id
= rVR
.getTextureIdFromName(normal
);
3163 normal
= localImageName(normalBitmap
);
3164 addImageDownload(normalBitmap
, ctrlButton
, style
);
3170 if (startsWith(pushedBitmap
, "data:image/"))
3172 pushed
= decodeURIComponent(pushedBitmap
);
3176 pushed
= pushedBitmap
.empty()?"":CFile::getPath(pushedBitmap
) + CFile::getFilenameWithoutExtension(pushedBitmap
) + ".tga";
3177 // if the image doesn't exist on local, we check in the cache, don't download it because the "normal" will already setuped it
3178 if(!CPath::exists(pushed
))
3180 // search in the compressed texture
3181 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
3182 sint32 id
= rVR
.getTextureIdFromName(pushed
);
3185 pushed
= localImageName(pushedBitmap
);
3191 if (startsWith(overBitmap
, "data:image/"))
3193 over
= decodeURIComponent(overBitmap
);
3197 over
= overBitmap
.empty()?"":CFile::getPath(overBitmap
) + CFile::getFilenameWithoutExtension(overBitmap
) + ".tga";
3198 // schedule mouseover bitmap for download if its different from normal
3199 if (!over
.empty() && !CPath::exists(over
))
3201 if (overBitmap
!= normalBitmap
)
3203 over
= localImageName(overBitmap
);
3204 addImageDownload(overBitmap
, ctrlButton
, style
, OverImage
);
3209 ctrlButton
->setType (type
);
3210 if (!normal
.empty())
3211 ctrlButton
->setTexture (normal
);
3212 if (!pushed
.empty())
3213 ctrlButton
->setTexturePushed (pushed
);
3215 ctrlButton
->setTextureOver (over
);
3216 ctrlButton
->setModulateGlobalColorAll (style
.GlobalColor
);
3217 ctrlButton
->setActionOnLeftClick (actionHandler
);
3218 ctrlButton
->setParamsOnLeftClick (actionHandlerParams
);
3220 // Translate the tooltip or display raw text (tooltip from webig)
3221 if (!tooltip
.empty())
3223 if (CI18N::hasTranslation(tooltip
))
3225 ctrlButton
->setDefaultContextHelp(CI18N::get(tooltip
));
3226 //ctrlButton->setOnContextHelp(CI18N::get(tooltip).toString());
3230 ctrlButton
->setDefaultContextHelp(tooltip
);
3231 //ctrlButton->setOnContextHelp(string(tooltip));
3234 ctrlButton
->setInstantContextHelp(true);
3235 ctrlButton
->setToolTipParent(TTMouse
);
3236 ctrlButton
->setToolTipParentPosRef(Hotspot_TTAuto
);
3237 ctrlButton
->setToolTipPosRef(Hotspot_TTAuto
);
3240 getParagraph()->addChild (ctrlButton
);
3243 setImageSize(ctrlButton
, style
);
3248 // ***************************************************************************
3250 void CGroupHTML::flushString()
3252 _CurrentViewLink
= NULL
;
3255 // ***************************************************************************
3257 void CGroupHTML::clearContext()
3273 _FormSubmit
.clear();
3277 _AnchorName
.clear();
3278 _CellParams
.clear();
3283 _ReadingHeadTag
= false;
3284 _IgnoreHeadTag
= false;
3285 _IgnoreBaseUrlTag
= false;
3290 // clear the pointer to the current image download since all the button are deleted
3291 LOG_DL("Clear pointers to %d curls", Curls
.size());
3293 // remove image refs from downloads
3294 for(std::list
<CDataDownload
>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
3300 // ***************************************************************************
3302 u32char
CGroupHTML::getLastChar() const
3304 if (_CurrentViewLink
)
3306 ::u32string str
= CUtfStringView(_CurrentViewLink
->getText()).toUtf32(); // FIXME: Optimize reverse UTF iteration
3308 return str
[str
.length()-1];
3313 // ***************************************************************************
3315 void CGroupHTML::paragraphChange ()
3317 _CurrentViewLink
= NULL
;
3318 _CurrentViewImage
= NULL
;
3319 CGroupParagraph
*paragraph
= getParagraph();
3322 // Number of child in this paragraph
3323 uint numChild
= paragraph
->getNumChildren();
3326 // Get the last child
3327 CViewBase
*child
= paragraph
->getChild(numChild
-1);
3329 // Is this a string view ?
3330 _CurrentViewLink
= dynamic_cast<CViewLink
*>(child
);
3331 _CurrentViewImage
= dynamic_cast<CViewBitmap
*>(child
);
3336 // ***************************************************************************
3338 CInterfaceGroup
*CGroupHTML::getCurrentGroup()
3340 if (!_Cells
.empty() && _Cells
.back())
3341 return _Cells
.back()->Group
;
3343 return _GroupListAdaptor
;
3346 // ***************************************************************************
3348 void CGroupHTML::addHtmlGroup (CInterfaceGroup
*group
, uint beginSpace
)
3353 registerAnchor(group
);
3355 if (!_DivName
.empty())
3357 group
->setName(_DivName
);
3358 _Groups
.push_back(group
);
3361 group
->setSizeRef(CInterfaceElement::width
);
3363 // Compute begin space between paragraph and tables
3364 // * If first in group, no begin space
3366 // Pointer on the current paragraph (can be a table too)
3367 CGroupParagraph
*p
= dynamic_cast<CGroupParagraph
*>(group
);
3369 CInterfaceGroup
*parentGroup
= CGroupHTML::getCurrentGroup();
3370 const std::vector
<CInterfaceGroup
*> &groups
= parentGroup
->getGroups ();
3371 group
->setParent(parentGroup
);
3372 group
->setParentSize(parentGroup
);
3375 group
->setParentPos(parentGroup
);
3376 group
->setPosRef(Hotspot_TL
);
3377 group
->setParentPosRef(Hotspot_TL
);
3382 // Last is a paragraph ?
3383 group
->setParentPos(groups
.back());
3384 group
->setPosRef(Hotspot_TL
);
3385 group
->setParentPosRef(Hotspot_BL
);
3388 // Set the begin space
3390 p
->setTopSpace(beginSpace
);
3392 group
->setY(-(sint32
)beginSpace
);
3393 parentGroup
->addGroup (group
);
3396 // ***************************************************************************
3398 void CGroupHTML::setContainerTitle (const std::string
&title
)
3400 CInterfaceElement
*parent
= getParent();
3403 if ((parent
= parent
->getParent()))
3405 CGroupContainer
*container
= dynamic_cast<CGroupContainer
*>(parent
);
3408 container
->setTitle(title
);
3414 void CGroupHTML::setTitle(const std::string
&title
)
3416 _TitleString
.clear();
3417 if(!_TitlePrefix
.empty())
3419 _TitleString
= _TitlePrefix
+ " - ";
3421 _TitleString
+= title
;
3423 setContainerTitle(_TitleString
);
3426 std::string
CGroupHTML::getTitle() const {
3427 return _TitleString
;
3430 // ***************************************************************************
3432 bool CGroupHTML::lookupLocalFile (string
&result
, const char *url
, bool isUrl
)
3437 if (toLowerAscii(result
).find("file:") == 0 && result
.size() > 5)
3439 result
= result
.substr(5, result
.size()-5);
3441 else if (result
.find("://") != string::npos
|| result
.find("//") == 0)
3443 // http://, https://, etc or protocol-less url "//domain.com/image.png"
3447 tmp
= CPath::lookup (CFile::getFilename(result
), false, false, false);
3450 // try to find in local directory
3451 tmp
= CPath::lookup (result
, false, false, true);
3456 // Normalize the path
3458 //result = "file:"+toLowerAscii(CPath::standardizePath (CPath::getFullPath (CFile::getPath(result)))+CFile::getFilename(result));*/
3459 result
= "file:/"+tmp
;
3466 // Is it a texture in the big texture ?
3467 if (CViewRenderer::getInstance()->getTextureIdFromName (result
) >= 0)
3473 // This is not a file in the CPath, let libwww open this URL
3480 // ***************************************************************************
3482 void CGroupHTML::submitForm(uint button
, sint32 x
, sint32 y
)
3484 if (button
>= _FormSubmit
.size())
3487 for(uint formId
= 0; formId
< _Forms
.size(); formId
++)
3489 // case sensitive search (user id is lowecase, auto id is uppercase)
3490 if (_Forms
[formId
].id
== _FormSubmit
[button
].form
)
3492 _PostNextTime
= true;
3493 _PostFormId
= formId
;
3494 _PostFormAction
= _FormSubmit
[button
].formAction
;
3495 _PostFormSubmitType
= _FormSubmit
[button
].type
;
3496 _PostFormSubmitButton
= _FormSubmit
[button
].name
;
3497 _PostFormSubmitValue
= _FormSubmit
[button
].value
;
3498 _PostFormSubmitX
= x
;
3499 _PostFormSubmitY
= y
;
3505 nlwarning("Unable to find form '%s' to submit (button '%s')", _FormSubmit
[button
].form
.c_str(), _FormSubmit
[button
].name
.c_str());
3508 // ***************************************************************************
3510 void CGroupHTML::setBackgroundColor (const CRGBA
&bgcolor
)
3512 // Should have a child named bg
3513 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
3516 CViewBitmap
*bitmap
= dynamic_cast<CViewBitmap
*> (view
);
3519 // TODO: background color should have separate bitmap from background texture
3520 // Change the background color
3521 bitmap
->setColor (bgcolor
);
3522 bitmap
->setModulateGlobalColor(false);
3527 // ***************************************************************************
3529 void CGroupHTML::setBackground (const string
&bgtex
, bool scale
, bool tile
)
3531 // Should have a child named bg
3532 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
3535 CViewBitmap
*bitmap
= dynamic_cast<CViewBitmap
*> (view
);
3538 bitmap
->setParentPosRef(Hotspot_TL
);
3539 bitmap
->setPosRef(Hotspot_TL
);
3542 // FIXME: renders behind container background
3543 bitmap
->setRenderLayer(-2);
3544 bitmap
->setScale(scale
);
3545 bitmap
->setTile(tile
);
3547 // clear size ref for non-scaled image or it does not show up
3549 bitmap
->setSizeRef("wh");
3551 bitmap
->setSizeRef("");
3553 addImageDownload(bgtex
, view
, CStyleParams(), NormalImage
, "");
3559 struct CButtonFreezer
: public CInterfaceElementVisitor
3561 virtual void visitCtrl(CCtrlBase
*ctrl
)
3563 CCtrlBaseButton
*textButt
= dynamic_cast<CCtrlTextButton
*>(ctrl
);
3566 textButt
->setFrozen(true);
3571 // ***************************************************************************
3573 void CGroupHTML::handle ()
3575 H_AUTO(RZ_Interface_Html_handle
)
3577 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
3579 // handle curl downloads
3582 // handle refresh timer
3583 if (_NextRefreshTime
> 0 && _NextRefreshTime
<= (times
.thisFrameMs
/ 1000.0f
) )
3585 // there might be valid uses for 0sec refresh, but two in a row is probably a mistake
3586 if (_NextRefreshTime
- _LastRefreshTime
>= 1.0)
3588 _LastRefreshTime
= _NextRefreshTime
;
3589 doBrowse(_RefreshUrl
.c_str());
3592 nlwarning("Ignore second 0sec http-equiv refresh in a row (url '%s')", _URL
.c_str());
3594 _NextRefreshTime
= 0;
3599 // still transfering html page
3600 if (_TimeoutValue
!= 0 && _ConnectingTimeout
<= ( times
.thisFrameMs
/ 1000.0f
) )
3602 browseError(("Connection timeout : "+_URL
).c_str());
3606 if (_RenderNextTime
)
3608 _RenderNextTime
= false;
3609 renderHtmlString(_DocumentHtml
);
3612 if (_WaitingForStylesheet
)
3617 if (_BrowseNextTime
|| _PostNextTime
)
3620 _ConnectingTimeout
= ( times
.thisFrameMs
/ 1000.0f
) + _TimeoutValue
;
3622 // freeze form buttons
3623 CButtonFreezer freezer
;
3624 this->visit(&freezer
);
3631 bool isLocal
= lookupLocalFile (finalUrl
, _URL
.c_str(), true);
3635 CUrlParser
uri (_URL
);
3636 _TrustedDomain
= isTrustedDomain(uri
.host
);
3637 _DocumentDomain
= uri
.host
;
3639 // file is probably from bnp (ingame help)
3642 doBrowseLocalFile(finalUrl
);
3646 SFormFields formfields
;
3649 buildHTTPPostParams(formfields
);
3650 // _URL is set from form.Action
3655 // Add custom get params from child classes
3656 addHTTPGetParams (finalUrl
, _TrustedDomain
);
3659 doBrowseRemoteUrl(finalUrl
, "", _PostNextTime
, formfields
);
3662 _BrowseNextTime
= false;
3663 _PostNextTime
= false;
3667 // ***************************************************************************
3668 void CGroupHTML::buildHTTPPostParams (SFormFields
&formfields
)
3670 // Add text area text
3673 if (_PostFormId
>= _Forms
.size())
3675 nlwarning("(%s) invalid form index %d, _Forms %d", _Id
.c_str(), _PostFormId
, _Forms
.size());
3679 CForm
&form
= _Forms
[_PostFormId
];
3681 // button can override form action url (and methor, but we only do POST)
3682 _URL
= _PostFormAction
.empty() ? form
.Action
: _PostFormAction
;
3684 CUrlParser
uri(_URL
);
3685 _TrustedDomain
= isTrustedDomain(uri
.host
);
3686 _DocumentDomain
= uri
.host
;
3688 for (i
=0; i
<form
.Entries
.size(); i
++)
3691 bool addEntry
= false;
3693 if (form
.Entries
[i
].TextArea
)
3695 // Get the edit box view
3696 CInterfaceGroup
*group
= form
.Entries
[i
].TextArea
->getGroup ("eb");
3699 // Should be a CGroupEditBox
3700 CGroupEditBox
*editBox
= dynamic_cast<CGroupEditBox
*>(group
);
3703 entryData
= editBox
->getViewText()->getText();
3708 else if (form
.Entries
[i
].Checkbox
)
3710 // todo handle unicode POST here
3711 if (form
.Entries
[i
].Checkbox
->getPushed ())
3713 entryData
= form
.Entries
[i
].Value
;
3717 else if (form
.Entries
[i
].ComboBox
)
3719 CDBGroupComboBox
*cb
= form
.Entries
[i
].ComboBox
;
3720 entryData
= form
.Entries
[i
].SelectValues
[cb
->getSelection()];
3723 else if (form
.Entries
[i
].SelectBox
)
3725 CGroupMenu
*sb
= form
.Entries
[i
].SelectBox
;
3726 CGroupSubMenu
*rootMenu
= sb
->getRootMenu();
3729 for(uint j
=0; j
<rootMenu
->getNumLine(); ++j
)
3731 CInterfaceGroup
*ig
= rootMenu
->getUserGroupLeft(j
);
3734 CCtrlBaseButton
*cb
= dynamic_cast<CCtrlBaseButton
*>(ig
->getCtrl("b"));
3735 if (cb
&& cb
->getPushed())
3736 formfields
.add(form
.Entries
[i
].Name
, form
.Entries
[i
].SelectValues
[j
]);
3741 // This is a hidden value
3744 entryData
= form
.Entries
[i
].Value
;
3751 formfields
.add(form
.Entries
[i
].Name
, entryData
);
3755 if (_PostFormSubmitType
== "image")
3757 // Add the button coordinates
3758 if (_PostFormSubmitButton
.find_first_of("[") == string::npos
)
3760 formfields
.add(_PostFormSubmitButton
+ "_x", NLMISC::toString(_PostFormSubmitX
));
3761 formfields
.add(_PostFormSubmitButton
+ "_y", NLMISC::toString(_PostFormSubmitY
));
3765 formfields
.add(_PostFormSubmitButton
, NLMISC::toString(_PostFormSubmitX
));
3766 formfields
.add(_PostFormSubmitButton
, NLMISC::toString(_PostFormSubmitY
));
3770 formfields
.add(_PostFormSubmitButton
, _PostFormSubmitValue
);
3772 // Add custom params from child classes
3773 addHTTPPostParams(formfields
, _TrustedDomain
);
3776 // ***************************************************************************
3777 void CGroupHTML::doBrowseLocalFile(const std::string
&uri
)
3780 updateRefreshButton();
3782 std::string filename
;
3783 if (toLowerAscii(uri
).find("file:/") == 0)
3785 filename
= uri
.substr(6, uri
.size() - 6);
3792 LOG_DL("browse local file '%s'", filename
.c_str());
3794 _TrustedDomain
= true;
3795 _DocumentDomain
= "localhost";
3798 if (in
.open(filename
))
3804 in
.getline(buf
, 1024);
3805 html
+= std::string(buf
) + "\n";
3809 if (!renderHtmlString(html
))
3811 browseError((string("Failed to parse html from file : ")+filename
).c_str());
3816 browseError((string("The page address is malformed : ")+filename
).c_str());
3820 // ***************************************************************************
3821 void CGroupHTML::doBrowseRemoteUrl(std::string url
, const std::string
&referer
, bool doPost
, const SFormFields
&formfields
)
3823 // stop all downloads from previous page
3825 updateRefreshButton();
3828 if(_TitlePrefix
.empty())
3829 setTitle (CI18N::get("uiPleaseWait"));
3831 setTitle (_TitlePrefix
+ " - " + CI18N::get("uiPleaseWait"));
3833 url
= upgradeInsecureUrl(url
);
3835 LOG_DL("(%s) browse url (trusted=%s) '%s', referer='%s', post='%s', nb form values %d",
3836 _Id
.c_str(), (_TrustedDomain
? "true" :"false"), url
.c_str(), referer
.c_str(), (doPost
? "true" : "false"), formfields
.Values
.size());
3840 browseError(string("Invalid MultCurl handle, loading url failed : "+url
).c_str());
3844 CURL
*curl
= curl_easy_init();
3847 nlwarning("(%s) failed to create curl handle", _Id
.c_str());
3848 browseError(string("Failed to create cURL handle : " + url
).c_str());
3853 if (toLowerAscii(url
.substr(0, 8)) == "https://")
3855 // if supported, use custom SSL context function to load certificates
3856 NLWEB::CCurlCertificates::useCertificates(curl
);
3859 // do not follow redirects, we have own handler
3860 curl_easy_setopt(curl
, CURLOPT_FOLLOWLOCATION
, 0);
3862 curl_easy_setopt(curl
, CURLOPT_FRESH_CONNECT
, 1);
3864 // tell curl to use compression if possible (gzip, deflate)
3865 // leaving this empty allows all encodings that curl supports
3866 //curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
3868 // limit curl to HTTP and HTTPS protocols only
3869 curl_easy_setopt(curl
, CURLOPT_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
3870 curl_easy_setopt(curl
, CURLOPT_REDIR_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
3873 curl_easy_setopt(curl
, CURLOPT_URL
, url
.c_str());
3876 std::string userAgent
= options
.appName
+ "/" + options
.appVersion
;
3877 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, userAgent
.c_str());
3880 sendCookies(curl
, _DocumentDomain
, _TrustedDomain
);
3883 if (!referer
.empty())
3885 curl_easy_setopt(curl
, CURLOPT_REFERER
, referer
.c_str());
3886 LOG_DL("(%s) set referer '%s'", _Id
.c_str(), referer
.c_str());
3891 // serialize form data and add it to curl
3893 for(uint i
=0; i
<formfields
.Values
.size(); ++i
)
3895 char * escapedName
= curl_easy_escape(curl
, formfields
.Values
[i
].name
.c_str(), formfields
.Values
[i
].name
.size());
3896 char * escapedValue
= curl_easy_escape(curl
, formfields
.Values
[i
].value
.c_str(), formfields
.Values
[i
].value
.size());
3901 data
+= std::string(escapedName
) + "=" + escapedValue
;
3903 curl_free(escapedName
);
3904 curl_free(escapedValue
);
3906 curl_easy_setopt(curl
, CURLOPT_POST
, 1);
3907 curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, data
.size());
3908 curl_easy_setopt(curl
, CURLOPT_COPYPOSTFIELDS
, data
.c_str());
3912 curl_easy_setopt(curl
, CURLOPT_HTTPGET
, 1);
3916 _CurlWWW
= new CCurlWWWData(curl
, url
);
3918 // set the language code used by the client
3919 std::vector
<std::string
> headers
;
3920 headers
.push_back("Accept-Language: "+options
.languageCode
);
3921 headers
.push_back("Accept-Charset: utf-8");
3922 _CurlWWW
->sendHeaders(headers
);
3924 // catch headers for redirect
3925 curl_easy_setopt(curl
, CURLOPT_HEADERFUNCTION
, NLGUI::curlHeaderCallback
);
3926 curl_easy_setopt(curl
, CURLOPT_WRITEHEADER
, _CurlWWW
);
3929 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, NLGUI::curlDataCallback
);
3930 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, _CurlWWW
);
3932 #ifdef LOG_CURL_PROGRESS
3933 // progress callback
3934 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 0);
3935 curl_easy_setopt(curl
, CURLOPT_XFERINFOFUNCTION
, NLGUI::curlProgressCallback
);
3936 curl_easy_setopt(curl
, CURLOPT_XFERINFODATA
, _CurlWWW
);
3939 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 1);
3943 curl_multi_add_handle(MultiCurl
, curl
);
3945 // start the transfer
3946 int NewRunningCurls
= 0;
3947 curl_multi_perform(MultiCurl
, &NewRunningCurls
);
3950 _RedirectsRemaining
= DEFAULT_RYZOM_REDIRECT_LIMIT
;
3953 // ***************************************************************************
3954 void CGroupHTML::htmlDownloadFinished(bool success
, const std::string
&error
)
3959 err
= "Connection failed with cURL error: ";
3961 err
+= "\nURL '" + _CurlWWW
->Url
+ "'";
3962 browseError(err
.c_str());
3966 // received content from remote
3967 std::string content
= trim(_CurlWWW
->Content
);
3969 // save HSTS header from all requests regardless of HTTP code
3970 if (_CurlWWW
->hasHSTSHeader())
3972 CUrlParser
uri(_CurlWWW
->Url
);
3973 CStrictTransportSecurity::getInstance()->setFromHeader(uri
.host
, _CurlWWW
->getHSTSHeader());
3976 receiveCookies(_CurlWWW
->Request
, _DocumentDomain
, _TrustedDomain
);
3979 curl_easy_getinfo(_CurlWWW
->Request
, CURLINFO_RESPONSE_CODE
, &code
);
3980 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());
3982 if ((code
>= 301 && code
<= 303) || code
== 307 || code
== 308)
3984 if (_RedirectsRemaining
< 0)
3986 browseError(string("Redirect limit reached : " + _URL
).c_str());
3990 // redirect, get the location and try browse again
3991 // we cant use curl redirection because 'addHTTPGetParams()' must be called on new destination
3992 std::string
location(_CurlWWW
->getLocationHeader());
3993 if (location
.empty())
3995 browseError(string("Request was redirected, but location was not set : "+_URL
).c_str());
3999 LOG_DL("(%s) request (%d) redirected to (len %d) '%s'", _Id
.c_str(), _RedirectsRemaining
, location
.size(), location
.c_str());
4000 location
= getAbsoluteUrl(location
);
4002 _PostNextTime
= false;
4003 _RedirectsRemaining
--;
4005 doBrowse(location
.c_str());
4007 else if ( (code
< 200 || code
>= 300) )
4009 // catches 304 not modified, but html is not in cache anyway
4010 // if server did not send any error back
4011 if (content
.empty())
4013 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>");
4018 std::string contentType
;
4019 CURLcode res
= curl_easy_getinfo(_CurlWWW
->Request
, CURLINFO_CONTENT_TYPE
, &ch
);
4020 if (res
== CURLE_OK
&& ch
!= NULL
)
4025 htmlDownloadFinished(content
, contentType
, code
);
4027 // clear curl handler
4030 curl_multi_remove_handle(MultiCurl
, _CurlWWW
->Request
);
4036 // refresh button uses _CurlWWW. refresh button may stay disabled if
4037 // there is no css files to download and page is rendered before _CurlWWW is freed
4038 updateRefreshButton();
4041 void CGroupHTML::dataDownloadFinished(bool success
, const std::string
&error
, CDataDownload
&data
)
4045 CUrlParser
uri(data
.url
);
4046 if (!uri
.host
.empty())
4048 receiveCookies(data
.data
->Request
, uri
.host
, isTrustedDomain(uri
.host
));
4052 curl_easy_getinfo(data
.data
->Request
, CURLINFO_RESPONSE_CODE
, &code
);
4054 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());
4055 curl_multi_remove_handle(MultiCurl
, data
.data
->Request
);
4057 // save HSTS header from all requests regardless of HTTP code
4060 if (data
.data
->hasHSTSHeader())
4062 CStrictTransportSecurity::getInstance()->setFromHeader(uri
.host
, data
.data
->getHSTSHeader());
4065 // 2XX success, 304 Not Modified
4066 if ((code
>= 200 && code
<= 204) || code
== 304)
4068 CHttpCacheObject obj
;
4069 obj
.Expires
= data
.data
->getExpires();
4070 obj
.Etag
= data
.data
->getEtag();
4071 obj
.LastModified
= data
.data
->getLastModified();
4073 CHttpCache::getInstance()->store(data
.dest
, obj
);
4074 std::string tmpfile
= data
.dest
+ ".tmp";
4075 if (code
== 304 && CFile::fileExists(tmpfile
))
4077 CFile::deleteFile(tmpfile
);
4080 else if ((code
>= 301 && code
<= 303) || code
== 307 || code
== 308)
4082 if (data
.redirects
< DEFAULT_RYZOM_REDIRECT_LIMIT
)
4084 std::string
location(data
.data
->getLocationHeader());
4085 if (!location
.empty())
4087 CUrlParser
uri(location
);
4088 if (!uri
.isAbsolute())
4090 uri
.inherit(data
.url
);
4091 location
= uri
.toString();
4094 // push same request in the front of the queue
4095 // cache filename is based of original url
4096 Curls
.push_front(data
);
4097 // clear old request state
4098 Curls
.front().data
= NULL
;
4099 Curls
.front().fp
= NULL
;
4100 Curls
.front().url
= location
;
4101 Curls
.front().redirects
++;
4103 LOG_DL("Redirect '%s'", location
.c_str());
4104 // no finished callback called, so cleanup old temp
4105 std::string tmpfile
= data
.dest
+ ".tmp";
4106 if (CFile::fileExists(tmpfile
))
4108 CFile::deleteFile(tmpfile
);
4113 nlwarning("Redirected to empty url '%s'", data
.url
.c_str());
4117 nlwarning("Redirect limit reached for '%s'", data
.url
.c_str());
4122 nlwarning("HTTP request failed with code [%d] for '%s'\n",code
, data
.url
.c_str());
4124 if (CFile::fileExists(data
.dest
))
4126 CFile::deleteFile(data
.dest
);
4132 nlwarning("DATA download failed '%s', error '%s'", data
.url
.c_str(), error
.c_str());
4135 finishCurlDownload(data
);
4138 void CGroupHTML::htmlDownloadFinished(const std::string
&content
, const std::string
&type
, long code
)
4140 LOG_DL("(%s) HTML download finished, content length %d, type '%s', code %d", _Id
.c_str(), content
.size(), type
.c_str(), code
);
4142 // create <html> markup for image downloads
4143 if (type
.find("image/") == 0 && !content
.empty())
4147 std::string dest
= localImageName(_URL
);
4150 out
.serialBuffer((uint8
*)(content
.c_str()), content
.size());
4152 LOG_DL("(%s) image saved to '%s', url '%s'", _Id
.c_str(), dest
.c_str(), _URL
.c_str());
4156 // create html code with image url inside and do the request again
4157 renderHtmlString("<html><head><title>"+_URL
+"</title></head><body><img src=\"" + _URL
+ "\"></body></html>");
4159 else if (_TrustedDomain
&& type
.find("text/lua") == 0)
4161 setTitle(_TitleString
);
4163 _LuaScript
= "\nlocal __CURRENT_WINDOW__=\""+this->_Id
+"\" \n"+content
;
4164 CLuaManager::getInstance().executeLuaScript(_LuaScript
, true);
4167 // disable refresh button
4169 // disable redo into this url
4174 // Sanitize downloaded HTML UTF-8 encoding, and render
4175 renderHtmlString(CUtfStringView(content
).toUtf8(true));
4179 // ***************************************************************************
4180 void CGroupHTML::cssDownloadFinished(const std::string
&url
, const std::string
&local
)
4182 for(std::vector
<CHtmlParser::StyleLink
>::iterator it
= _StylesheetQueue
.begin();
4183 it
!= _StylesheetQueue
.end(); ++it
)
4187 // read downloaded file into HtmlStyles
4188 if (CFile::fileExists(local
) && it
->Index
< _HtmlStyles
.size())
4193 if (!in
.readAll(_HtmlStyles
[it
->Index
]))
4195 nlwarning("Failed to read downloaded css file(%s), url(%s)", local
.c_str(), url
.c_str());
4200 _StylesheetQueue
.erase(it
);
4206 void CGroupHTML::renderDocument()
4208 if (!Curls
.empty() && !_StylesheetQueue
.empty())
4210 // waiting for stylesheets to finish downloading
4213 _WaitingForStylesheet
= false;
4215 //TGameTime renderStart = CTime::getLocalTime();
4217 // clear previous state and page
4221 // process all <style> and <link rel=stylesheet> elements
4222 for(uint i
= 0; i
< _HtmlStyles
.size(); ++i
)
4224 if (!_HtmlStyles
[i
].empty())
4226 _Style
.parseStylesheet(_HtmlStyles
[i
]);
4229 _HtmlStyles
.clear();
4231 std::list
<CHtmlElement
>::iterator it
= _HtmlDOM
.Children
.begin();
4232 while(it
!= _HtmlDOM
.Children
.end())
4240 //TGameTime renderStop = CTime::getLocalTime();
4241 //nlwarning("[%s] render: %.1fms (%s)\n", _Id.c_str(), (renderStop - renderStart), _URL.c_str());
4244 // ***************************************************************************
4246 bool CGroupHTML::renderHtmlString(const std::string
&html
)
4250 // if we are already rendering, then queue up the next page
4253 _DocumentHtml
= html
;
4254 _RenderNextTime
= true;
4260 _DocumentUrl
= _URL
;
4261 _DocumentHtml
= html
;
4262 _NextRefreshTime
= 0;
4263 _RefreshUrl
.clear();
4265 if (trim(html
).empty())
4270 // clear previous page and state
4282 // start new rendering
4283 _HtmlDOM
= CHtmlElement(CHtmlElement::NONE
, "<root>");
4284 _CurrentHTMLElement
= NULL
;
4285 success
= parseHtml(html
);
4288 _WaitingForStylesheet
= !_StylesheetQueue
.empty();
4293 std::string error
= "ERROR: HTML parse failed.";
4294 error
+= toString("\nsize %d bytes", html
.size());
4295 error
+= toString("\n---start---\n%s\n---end---\n", html
.c_str());
4296 browseError(error
.c_str());
4303 // ***************************************************************************
4304 void CGroupHTML::doBrowseAnchor(const std::string
&anchor
)
4306 if (_Anchors
.count(anchor
) == 0)
4311 CInterfaceElement
*pIE
= _Anchors
.find(anchor
)->second
;
4314 // hotspot depends on vertical/horizontal scrollbar
4315 CCtrlScroll
*pSB
= getScrollBar();
4318 pSB
->ensureVisible(pIE
, Hotspot_Tx
, Hotspot_Tx
);
4323 // ***************************************************************************
4325 void CGroupHTML::draw ()
4327 CGroupScrollText::draw ();
4330 // ***************************************************************************
4332 void CGroupHTML::beginBuild ()
4337 void CGroupHTML::endBuild ()
4339 // set the browser as complete
4341 updateRefreshButton();
4343 // check that the title is set, or reset it (in the case the page
4344 // does not provide a title)
4345 if (_TitleString
.empty())
4347 setTitle(_TitlePrefix
);
4353 // ***************************************************************************
4355 void CGroupHTML::addHTTPGetParams (string
&/* url */, bool /*trustedDomain*/)
4359 // ***************************************************************************
4361 void CGroupHTML::addHTTPPostParams (SFormFields
&/* formfields */, bool /*trustedDomain*/)
4365 // ***************************************************************************
4367 string
CGroupHTML::home () const
4372 // ***************************************************************************
4374 void CGroupHTML::removeContent ()
4376 // Remove old document
4377 if (!_GroupListAdaptor
)
4379 _GroupListAdaptor
= new CGroupListAdaptor(CViewBase::TCtorParam()); // deleted by the list
4380 _GroupListAdaptor
->setId(getList()->getId() + ":GLA");
4381 _GroupListAdaptor
->setResizeFromChildH(true);
4382 getList()->addChild (_GroupListAdaptor
, true);
4385 // Group list adaptor not exist ?
4386 _GroupListAdaptor
->clearGroups();
4387 _GroupListAdaptor
->clearControls();
4388 _GroupListAdaptor
->clearViews();
4389 CWidgetManager::getInstance()->clearViewUnders();
4390 CWidgetManager::getInstance()->clearCtrlsUnders();
4392 // Clear all the context
4395 // Reset default background color
4396 setBackgroundColor (BgColor
);
4397 setBackground ("blank.tga", true, false);
4402 // ***************************************************************************
4403 const std::string
&CGroupHTML::selectTreeNodeRecurs(CGroupTree::SNode
*node
, const std::string
&url
)
4405 static std::string emptyString
;
4411 // if this node match
4412 if(actionLaunchUrlRecurs(node
->AHName
, node
->AHParams
, url
))
4416 // fails => look into children
4419 for(uint i
=0;i
<node
->Children
.size();i
++)
4421 const string
&childRes
= selectTreeNodeRecurs(node
->Children
[i
], url
);
4422 if(!childRes
.empty())
4431 // ***************************************************************************
4432 bool CGroupHTML::actionLaunchUrlRecurs(const std::string
&ah
, const std::string
¶ms
, const std::string
&url
)
4434 // check if this action match
4435 if( (ah
=="launch_help" || ah
=="browse") && IActionHandler::getParam (params
, "url") == url
)
4439 // can be a proc that contains launch_help/browse => look recurs
4442 const std::string
&procName
= params
;
4443 // look into this proc
4444 uint numActions
= CWidgetManager::getInstance()->getParser()->getProcedureNumActions(procName
);
4445 for(uint i
=0;i
<numActions
;i
++)
4447 string procAh
, procParams
;
4448 if( CWidgetManager::getInstance()->getParser()->getProcedureAction(procName
, i
, procAh
, procParams
))
4450 // recurs proc if needed!
4451 if (actionLaunchUrlRecurs(procAh
, procParams
, url
))
4460 // ***************************************************************************
4461 void CGroupHTML::clearRefresh()
4464 updateRefreshButton();
4467 // ***************************************************************************
4468 void CGroupHTML::clearUndoRedo()
4470 // erase any undo/redo
4471 _BrowseUndo
.clear();
4472 _BrowseRedo
.clear();
4474 // update buttons validation
4475 updateUndoRedoButtons();
4478 // ***************************************************************************
4479 void CGroupHTML::pushUrlUndoRedo(const std::string
&url
)
4481 // if same url, no op
4485 // erase any redo, push undo, set current
4486 _BrowseRedo
.clear();
4487 if(!_AskedUrl
.empty())
4488 _BrowseUndo
.push_back(_AskedUrl
);
4492 while(_BrowseUndo
.size()>MaxUrlUndoRedo
)
4493 _BrowseUndo
.pop_front();
4495 // update buttons validation
4496 updateUndoRedoButtons();
4499 // ***************************************************************************
4500 void CGroupHTML::browseUndo()
4502 if(_BrowseUndo
.empty())
4505 // push to redo, pop undo, and set current
4506 if (!_AskedUrl
.empty())
4507 _BrowseRedo
.push_front(_AskedUrl
);
4509 _AskedUrl
= _BrowseUndo
.back();
4510 _BrowseUndo
.pop_back();
4512 // update buttons validation
4513 updateUndoRedoButtons();
4515 // and then browse the undoed url, with no undo/redo
4516 doBrowse(_AskedUrl
.c_str());
4519 // ***************************************************************************
4520 void CGroupHTML::browseRedo()
4522 if(_BrowseRedo
.empty())
4525 // push to undo, pop redo, and set current
4526 _BrowseUndo
.push_back(_AskedUrl
);
4527 _AskedUrl
= _BrowseRedo
.front();
4528 _BrowseRedo
.pop_front();
4530 // update buttons validation
4531 updateUndoRedoButtons();
4533 // and then browse the redoed url, with no undo/redo
4534 doBrowse(_AskedUrl
.c_str());
4537 // ***************************************************************************
4538 void CGroupHTML::updateUndoRedoButtons()
4540 CCtrlBaseButton
*butUndo
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseUndoButton
));
4541 CCtrlBaseButton
*butRedo
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseRedoButton
));
4543 // gray according to list size
4545 butUndo
->setFrozen(_BrowseUndo
.empty());
4547 butRedo
->setFrozen(_BrowseRedo
.empty());
4550 // ***************************************************************************
4551 void CGroupHTML::updateRefreshButton()
4553 CCtrlBaseButton
*butRefresh
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseRefreshButton
));
4556 // connecting, rendering, or is missing url
4557 bool frozen
= _CurlWWW
|| _Browsing
|| _URL
.empty();
4558 butRefresh
->setFrozen(frozen
);
4562 // ***************************************************************************
4564 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupHTMLInputOffset
, std::string
, "html_input_offset");
4566 CGroupHTMLInputOffset::CGroupHTMLInputOffset(const TCtorParam
¶m
)
4567 : CInterfaceGroup(param
),
4572 xmlNodePtr
CGroupHTMLInputOffset::serialize( xmlNodePtr parentNode
, const char *type
) const
4574 xmlNodePtr node
= CInterfaceGroup::serialize( parentNode
, type
);
4578 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"html_input_offset" );
4579 xmlSetProp( node
, BAD_CAST
"y_offset", BAD_CAST
toString( Offset
).c_str() );
4584 // ***************************************************************************
4585 bool CGroupHTMLInputOffset::parse(xmlNodePtr cur
, CInterfaceGroup
*parentGroup
)
4587 if (!CInterfaceGroup::parse(cur
, parentGroup
)) return false;
4590 ptr
= xmlGetProp (cur
, (xmlChar
*)"y_offset");
4592 fromString((const char*)ptr
, Offset
);
4596 // ***************************************************************************
4597 int CGroupHTML::luaParseHtml(CLuaState
&ls
)
4599 const char *funcName
= "parseHtml";
4600 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4601 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4602 std::string html
= ls
.toString(1);
4609 int CGroupHTML::luaClearRefresh(CLuaState
&ls
)
4611 const char *funcName
= "clearRefresh";
4612 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4619 int CGroupHTML::luaClearUndoRedo(CLuaState
&ls
)
4621 const char *funcName
= "clearUndoRedo";
4622 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4628 // ***************************************************************************
4629 int CGroupHTML::luaBrowse(CLuaState
&ls
)
4631 const char *funcName
= "browse";
4632 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4633 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4634 browse(ls
.toString(1));
4638 // ***************************************************************************
4639 int CGroupHTML::luaRefresh(CLuaState
&ls
)
4641 const char *funcName
= "refresh";
4642 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4647 // ***************************************************************************
4648 int CGroupHTML::luaRemoveContent(CLuaState
&ls
)
4650 const char *funcName
= "removeContent";
4651 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4656 // ***************************************************************************
4657 int CGroupHTML::luaRenderHtml(CLuaState
&ls
)
4659 const char *funcName
= "renderHtml";
4660 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4661 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4662 std::string html
= ls
.toString(1);
4664 // Always trust domain if rendered from lua
4665 _TrustedDomain
= true;
4666 renderHtmlString(html
);
4671 // ***************************************************************************
4672 int CGroupHTML::luaSetBackground(CLuaState
&ls
)
4674 const char *funcName
= "setBackground";
4675 CLuaIHM::checkArgCount(ls
, funcName
, 3);
4676 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4677 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4678 CLuaIHM::checkArgType(ls
, funcName
, 3, LUA_TBOOLEAN
);
4679 std::string image
= ls
.toString(1);
4680 bool scale
= ls
.toBoolean(2);
4681 bool repeat
= ls
.toBoolean(3);
4683 setBackground(image
, scale
, repeat
);
4688 // ***************************************************************************
4689 int CGroupHTML::luaInsertText(CLuaState
&ls
)
4691 const char *funcName
= "insertText";
4692 CLuaIHM::checkArgCount(ls
, funcName
, 3);
4693 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4694 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TSTRING
);
4695 CLuaIHM::checkArgType(ls
, funcName
, 3, LUA_TBOOLEAN
);
4697 string name
= ls
.toString(1);
4698 string text
= ls
.toString(2);
4700 if (!_Forms
.empty())
4702 for (uint i
=0; i
<_Forms
.back().Entries
.size(); i
++)
4704 if (_Forms
.back().Entries
[i
].TextArea
&& _Forms
.back().Entries
[i
].Name
== name
)
4706 // Get the edit box view
4707 CInterfaceGroup
*group
= _Forms
.back().Entries
[i
].TextArea
->getGroup ("eb");
4710 // Should be a CGroupEditBox
4711 CGroupEditBox
*editBox
= dynamic_cast<CGroupEditBox
*>(group
);
4713 editBox
->writeString(text
, false, ls
.toBoolean(3));
4722 // ***************************************************************************
4723 int CGroupHTML::luaAddString(CLuaState
&ls
)
4725 const char *funcName
= "addString";
4726 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4727 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4728 addString(ls
.toString(1));
4732 // ***************************************************************************
4733 int CGroupHTML::luaAddImage(CLuaState
&ls
)
4735 const char *funcName
= "addImage";
4736 CLuaIHM::checkArgCount(ls
, funcName
, 2);
4737 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4738 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4746 style
.GlobalColor
= ls
.toBoolean(2);
4748 string url
= getLink();
4751 string params
= "name=" + getId() + "|url=" + getLink ();
4752 addButton(CCtrlButton::PushButton
, "", ls
.toString(1), ls
.toString(1),
4753 "", "browse", params
.c_str(), "", style
);
4757 addImage("", ls
.toString(1), false, style
);
4764 // ***************************************************************************
4765 int CGroupHTML::luaShowDiv(CLuaState
&ls
)
4767 const char *funcName
= "showDiv";
4768 CLuaIHM::checkArgCount(ls
, funcName
, 2);
4769 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4770 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4772 if (!_Groups
.empty())
4774 for (uint i
=0; i
<_Groups
.size(); i
++)
4776 CInterfaceGroup
*group
= _Groups
[i
];
4777 if (group
->getName() == ls
.toString(1))
4779 group
->setActive(ls
.toBoolean(2));
4786 // ***************************************************************************
4787 void CGroupHTML::setURL(const std::string
&url
)
4789 browse(url
.c_str());
4792 void CGroupHTML::setHTML(const std::string
&html
)
4794 renderHtmlString(html
);
4797 void CGroupHTML::setHome(const std::string
&home
)
4802 // ***************************************************************************
4803 void CGroupHTML::parseStylesheetFile(const std::string
&fname
)
4806 if (css
.open(fname
))
4808 uint32 remaining
= css
.getFileSize();
4809 std::string content
;
4811 while(!css
.eof() && remaining
> 0)
4813 const uint BUF_SIZE
= 4096;
4816 uint32 readJustNow
= std::min(remaining
, BUF_SIZE
);
4817 css
.serialBuffer((uint8
*)&buf
, readJustNow
);
4818 content
.append(buf
, readJustNow
);
4819 remaining
-= readJustNow
;
4822 _Style
.parseStylesheet(content
);
4824 catch(const Exception
&e
)
4826 nlwarning("exception while reading css file '%s'", e
.what());
4831 nlwarning("Stylesheet file '%s' not found (%s)", fname
.c_str(), _URL
.c_str());
4835 // ***************************************************************************
4836 bool CGroupHTML::parseHtml(const std::string
&htmlString
)
4838 CHtmlElement
*parsedDOM
;
4839 if (_CurrentHTMLElement
== NULL
)
4841 // parse under <root> element (clean dom)
4842 parsedDOM
= &_HtmlDOM
;
4846 // parse under currently rendered <lua> element
4847 parsedDOM
= _CurrentHTMLElement
;
4850 std::vector
<CHtmlParser::StyleLink
> links
;
4853 parser
.getDOM(htmlString
, *parsedDOM
, _HtmlStyles
, links
);
4855 // <link> elements inserted from lua::parseHtml are ignored
4856 if (_CurrentHTMLElement
== NULL
&& !links
.empty())
4858 addStylesheetDownload(links
);
4860 else if (_CurrentHTMLElement
!= NULL
)
4862 // Called from active element (lua)
4863 // <style> order is not preserved as document is already being rendered
4864 for(uint i
= 0; i
< _HtmlStyles
.size(); ++i
)
4866 if (!_HtmlStyles
[i
].empty())
4868 _Style
.parseStylesheet(_HtmlStyles
[i
]);
4871 _HtmlStyles
.clear();
4874 // this should rarely fail as first element should be <html>
4875 bool success
= parsedDOM
->Children
.size() > 0;
4877 std::list
<CHtmlElement
>::iterator it
= parsedDOM
->Children
.begin();
4878 while(it
!= parsedDOM
->Children
.end())
4880 if (it
->Type
== CHtmlElement::ELEMENT_NODE
&& it
->Value
== "html")
4882 // move newly parsed childs from <body> into siblings
4883 if (_CurrentHTMLElement
) {
4884 std::list
<CHtmlElement
>::iterator it2
= it
->Children
.begin();
4885 while(it2
!= it
->Children
.end())
4887 if (it2
->Type
== CHtmlElement::ELEMENT_NODE
&& it2
->Value
== "body")
4889 spliceFragment(it2
);
4894 // remove <html> fragment from current element child
4895 it
= parsedDOM
->Children
.erase(it
);
4899 // remove link to <root> (html->parent == '<root>') or css selector matching will break
4906 // skip over other non-handled element
4913 void CGroupHTML::spliceFragment(std::list
<CHtmlElement
>::iterator src
)
4915 if(!_CurrentHTMLElement
->parent
)
4917 nlwarning("BUG: Current node is missing parent element. unable to splice fragment");
4921 // get the iterators for current element (<lua>) and next sibling
4922 std::list
<CHtmlElement
>::iterator currentElement
;
4923 currentElement
= std::find(_CurrentHTMLElement
->parent
->Children
.begin(), _CurrentHTMLElement
->parent
->Children
.end(), *_CurrentHTMLElement
);
4924 if (currentElement
== _CurrentHTMLElement
->parent
->Children
.end())
4926 nlwarning("BUG: unable to find current element iterator from parent");
4930 // where fragment should be moved
4931 std::list
<CHtmlElement
>::iterator insertBefore
;
4932 if (_CurrentHTMLNextSibling
== NULL
)
4934 insertBefore
= _CurrentHTMLElement
->parent
->Children
.end();
4936 // get iterator for nextSibling
4937 insertBefore
= std::find(_CurrentHTMLElement
->parent
->Children
.begin(), _CurrentHTMLElement
->parent
->Children
.end(), *_CurrentHTMLNextSibling
);
4940 _CurrentHTMLElement
->parent
->Children
.splice(insertBefore
, src
->Children
);
4942 // reindex moved elements
4943 CHtmlElement
*prev
= NULL
;
4944 uint childIndex
= _CurrentHTMLElement
->childIndex
;
4945 while(currentElement
!= _CurrentHTMLElement
->parent
->Children
.end())
4947 if (currentElement
->Type
== CHtmlElement::ELEMENT_NODE
)
4951 currentElement
->parent
= _CurrentHTMLElement
->parent
;
4952 currentElement
->childIndex
= childIndex
;
4953 currentElement
->previousSibling
= prev
;
4954 prev
->nextSibling
= &(*currentElement
);
4958 prev
= &(*currentElement
);
4964 // ***************************************************************************
4965 inline bool isDigit(char c
, uint base
= 16)
4967 if (c
>='0' && c
<='9') return true;
4968 if (base
!= 16) return false;
4969 if (c
>='A' && c
<='F') return true;
4970 if (c
>='a' && c
<='f') return true;
4974 // ***************************************************************************
4975 inline char convertHexDigit(char c
)
4977 if (c
>='0' && c
<='9') return c
-'0';
4978 if (c
>='A' && c
<='F') return c
-'A'+10;
4979 if (c
>='a' && c
<='f') return c
-'a'+10;
4983 // ***************************************************************************
4984 std::string
CGroupHTML::decodeHTMLEntities(const std::string
&str
)
4987 result
.reserve(str
.size() + (str
.size() >> 2));
4990 for (uint i
=0; i
<str
.length(); ++i
)
4993 if (str
[i
] == '&' && (str
.length()-i
) >= 4)
4997 // unicode character
4998 if (str
[pos
] == '#')
5002 // using decimal by default
5005 // using hexadecimal if &#x
5006 if (str
[pos
] == 'x')
5012 // setup "last" to point at the first character following "&#x?[0-9a-f]+"
5013 for (last
= pos
; last
< str
.length(); ++last
) if (!isDigit(str
[last
], base
)) break;
5015 // make sure that at least 1 digit was found
5016 // and have the terminating ';' to complete the token: "&#x?[0-9a-f]+;"
5017 if (last
== pos
|| str
[last
] != ';')
5025 // convert digits to unicode character
5026 while (pos
< last
) c
= convertHexDigit(str
[pos
++]) + (c
* u32char(base
));
5028 // append our new character to the result string
5029 CUtfStringView::append(result
, c
);
5031 // move 'i' forward to point at the ';' .. the for(...) will increment i to point to next char
5037 // special xml characters
5038 if (str
.substr(i
+1,5)=="quot;") { i
+=5; result
+='\"'; continue; }
5039 if (str
.substr(i
+1,4)=="amp;") { i
+=4; result
+='&'; continue; }
5040 if (str
.substr(i
+1,3)=="lt;") { i
+=3; result
+='<'; continue; }
5041 if (str
.substr(i
+1,3)=="gt;") { i
+=3; result
+='>'; continue; }
5044 // all the special cases are catered for... treat this as a normal character
5051 // ***************************************************************************
5052 std::string
CGroupHTML::getAbsoluteUrl(const std::string
&url
)
5054 CUrlParser
uri(url
);
5055 if (uri
.isAbsolute())
5060 return uri
.toString();
5063 // ***************************************************************************
5064 void CGroupHTML::resetCssStyle()
5066 _WaitingForStylesheet
= false;
5067 _StylesheetQueue
.clear();
5069 _Style
= _BrowserStyle
;
5072 // ***************************************************************************
5073 std::string
CGroupHTML::HTMLOListElement::getListMarkerText() const
5076 sint32 number
= Value
;
5081 ret
= "\xe2\x88\x99 ";
5083 else if (Type
== "circle")
5086 ret
= "\xe2\x9a\xaa ";
5088 else if (Type
== "square")
5091 ret
= "\xe2\x96\xaa ";
5093 else if (Type
== "a" || Type
== "A")
5095 // @see toAlphabeticOrNumeric in WebKit
5096 static const char lower
[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
5097 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
5098 static const char upper
[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
5099 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
5103 ret
= toString(number
);
5107 const char* digits
= (Type
== "A" ? upper
: lower
);
5111 ret
.insert(ret
.begin(), digits
[number
% size
]);
5117 else if (Type
== "i" || Type
== "I")
5119 // @see toRoman in WebKit
5120 static const char lower
[7] = {'i', 'v', 'x', 'l', 'c', 'd', 'm'};
5121 static const char upper
[7] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
5123 if (number
< 1 || number
> 3999)
5125 ret
= toString(number
);
5129 const char* digits
= (Type
== "I" ? upper
: lower
);
5133 uint32 num
= number
% 10;
5136 for (i
= num
% 5; i
> 0; i
--)
5138 ret
.insert(ret
.begin(), digits
[d
]);
5141 if (num
>= 4 && num
<= 8)
5143 ret
.insert(ret
.begin(), digits
[d
+ 1]);
5147 ret
.insert(ret
.begin(), digits
[d
+ 2]);
5151 ret
.insert(ret
.begin(), digits
[d
]);
5167 ret
= toString(Value
) + ". ";
5173 void CGroupHTML::HTMLMeterElement::readValues(const CHtmlElement
&elm
)
5175 if (!elm
.hasAttribute("value") || !fromString(elm
.getAttribute("value"), value
))
5177 if (!elm
.hasAttribute("min") || !fromString(elm
.getAttribute("min"), min
))
5179 if (!elm
.hasAttribute("max") || !fromString(elm
.getAttribute("max"), max
))
5184 std::swap(min
, max
);
5186 if (!elm
.hasAttribute("low") || !fromString(elm
.getAttribute("low"), low
))
5188 if (!elm
.hasAttribute("high") || !fromString(elm
.getAttribute("high"), high
))
5191 if (!elm
.hasAttribute("optimum") || !fromString(elm
.getAttribute("optimum"), optimum
))
5192 optimum
= (max
- min
) / 2.f
;
5194 // ensure low < high
5196 std::swap(low
, high
);
5203 float CGroupHTML::HTMLMeterElement::getValueRatio() const
5208 return (value
- min
) / (max
- min
);
5211 CGroupHTML::HTMLMeterElement::EValueRegion
CGroupHTML::HTMLMeterElement::getValueRegion() const
5215 // low region is optimum
5217 return VALUE_OPTIMUM
;
5218 else if (value
<= high
)
5219 return VALUE_SUB_OPTIMAL
;
5221 return VALUE_EVEN_LESS_GOOD
;
5223 else if (optimum
>= high
)
5225 // high region is optimum
5227 return VALUE_OPTIMUM
;
5228 else if (value
>= low
)
5229 return VALUE_SUB_OPTIMAL
;
5231 return VALUE_EVEN_LESS_GOOD
;
5234 // middle region is optimum
5235 if (value
>= low
&& value
<= high
)
5236 return VALUE_OPTIMUM
;
5238 return VALUE_SUB_OPTIMAL
;
5241 NLMISC::CRGBA
CGroupHTML::HTMLMeterElement::getBarColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5243 // color meter (inactive) bar segment
5244 // firefox:: meter { background:none; background-color: #555; },
5245 // webkit:: meter::-webkit-meter-bar { background:none; background-color: #555; }
5246 // webkit makes background color visible when padding is added
5247 CRGBA
color(150, 150, 150, 255);
5249 // use webkit pseudo elements as thats easier than firefox pseudo classes
5250 // background-color is expected to be set from browser.css
5252 style
.applyStyle(elm
.getPseudo(":-webkit-meter-bar"));
5253 if(style
.hasStyle("background-color"))
5254 color
= style
.Current
.BackgroundColor
;
5260 NLMISC::CRGBA
CGroupHTML::HTMLMeterElement::getValueColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5262 // background-color is expected to be set from browser.css
5265 switch(getValueRegion())
5269 style
.applyStyle(elm
.getPseudo(":-webkit-meter-optimum-value"));
5270 if (style
.hasStyle("background-color"))
5271 color
= style
.Current
.BackgroundColor
;
5274 case VALUE_SUB_OPTIMAL
:
5276 style
.applyStyle(elm
.getPseudo(":-webkit-meter-suboptimum-value"));
5277 if (style
.hasStyle("background-color"))
5278 color
= style
.Current
.BackgroundColor
;
5281 case VALUE_EVEN_LESS_GOOD
: // fall through
5284 style
.applyStyle(elm
.getPseudo(":-webkit-meter-even-less-good-value"));
5285 if (style
.hasStyle("background-color"))
5286 color
= style
.Current
.BackgroundColor
;
5295 // ****************************************************************************
5296 void CGroupHTML::HTMLProgressElement::readValues(const CHtmlElement
&elm
)
5298 if (!elm
.hasAttribute("value") || !fromString(elm
.getAttribute("value"), value
))
5300 if (!elm
.hasAttribute("max") || !fromString(elm
.getAttribute("max"), max
))
5307 // ****************************************************************************
5308 float CGroupHTML::HTMLProgressElement::getValueRatio() const
5315 // ****************************************************************************
5316 NLMISC::CRGBA
CGroupHTML::HTMLProgressElement::getBarColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5321 style
.applyStyle(elm
.getPseudo(":-webkit-progress-bar"));
5322 if (style
.hasStyle("background-color"))
5323 color
= style
.Current
.BackgroundColor
;
5329 // ****************************************************************************
5330 NLMISC::CRGBA
CGroupHTML::HTMLProgressElement::getValueColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5335 style
.applyStyle(elm
.getPseudo(":-webkit-progress-value"));
5336 if (style
.hasStyle("background-color"))
5337 color
= style
.Current
.BackgroundColor
;
5343 // ****************************************************************************
5344 void CGroupHTML::getCellsParameters(const CHtmlElement
&elm
, bool inherit
)
5346 CGroupHTML::CCellParams cellParams
;
5347 if (!_CellParams
.empty() && inherit
)
5348 cellParams
= _CellParams
.back();
5350 if (_Style
.hasStyle("background-color"))
5351 cellParams
.BgColor
= _Style
.Current
.BackgroundColor
;
5352 else if (elm
.hasNonEmptyAttribute("bgcolor"))
5353 scanHTMLColor(elm
.getAttribute("bgcolor").c_str(), cellParams
.BgColor
);
5355 if (elm
.hasAttribute("nowrap") || _Style
.Current
.WhiteSpace
== "nowrap")
5356 cellParams
.NoWrap
= true;
5358 if (elm
.hasNonEmptyAttribute("l_margin"))
5359 fromString(elm
.getAttribute("l_margin"), cellParams
.LeftMargin
);
5361 if (_Style
.hasStyle("height"))
5362 cellParams
.Height
= _Style
.Current
.Height
;
5363 else if (elm
.hasNonEmptyAttribute("height"))
5364 fromString(elm
.getAttribute("height"), cellParams
.Height
);
5368 // having text-align on table/tr should not override td align attribute
5369 if (_Style
.hasStyle("text-align"))
5370 align
= _Style
.Current
.TextAlign
;
5371 else if (elm
.hasNonEmptyAttribute("align"))
5372 align
= toLowerAscii(elm
.getAttribute("align"));
5374 if (align
== "left")
5375 cellParams
.Align
= CGroupCell::Left
;
5376 else if (align
== "center")
5377 cellParams
.Align
= CGroupCell::Center
;
5378 else if (align
== "right")
5379 cellParams
.Align
= CGroupCell::Right
;
5384 if (_Style
.hasStyle("vertical-align"))
5385 valign
= _Style
.Current
.VerticalAlign
;
5386 else if (elm
.hasNonEmptyAttribute("valign"))
5387 valign
= toLowerAscii(elm
.getAttribute("valign"));
5389 if (valign
== "top")
5390 cellParams
.VAlign
= CGroupCell::Top
;
5391 else if (valign
== "middle")
5392 cellParams
.VAlign
= CGroupCell::Middle
;
5393 else if (valign
== "bottom")
5394 cellParams
.VAlign
= CGroupCell::Bottom
;
5397 _CellParams
.push_back (cellParams
);
5400 // ***************************************************************************
5401 void CGroupHTML::applyBackground(const CHtmlElement
&elm
)
5403 bool root
= elm
.Value
== "html" || elm
.Value
== "body";
5406 if (_Style
.hasStyle("background-image"))
5408 bool repeat
= _Style
.checkStyle("background-repeat", "repeat");
5409 bool scale
= _Style
.checkStyle("background-size", "100%");
5410 std::string image
= _Style
.getStyle("background-image");
5415 setBackground (image
, scale
, repeat
);
5419 // default background color is transparent, so image does not show
5420 if (!_Style
.hasStyle("background-color") || _Style
.checkStyle("background-color", "transparent"))
5422 _Style
.applyStyle("background-color: #fff;");
5427 if (_Style
.hasStyle("background-color"))
5429 CRGBA bgColor
= _Style
.Current
.BackgroundColor
;
5430 scanHTMLColor(elm
.getAttribute("bgcolor").c_str(), bgColor
);
5433 setBackgroundColor(bgColor
);
5440 // ***************************************************************************
5441 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
)
5443 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, "", "image", action
));
5444 // Action handler parameters
5445 std::string param
= "name=" + getId() + "|button=" + toString(_FormSubmit
.size()-1);
5447 // Add the ctrl button
5448 addButton (CCtrlButton::PushButton
, name
, src
, src
, over
, "html_submit_form", param
.c_str(), tooltip
.c_str(), _Style
.Current
);
5451 // ***************************************************************************
5452 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
)
5454 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, value
, "submit", formAction
));
5455 // Action handler parameters
5456 string param
= "name=" + getId() + "|button=" + toString(_FormSubmit
.size()-1);
5458 // Add the ctrl button
5465 string
buttonTemplate(!templateName
.empty() ? templateName
: DefaultButtonGroup
);
5466 typedef pair
<string
, string
> TTmplParam
;
5467 vector
<TTmplParam
> tmplParams
;
5468 tmplParams
.push_back(TTmplParam("id", name
));
5469 tmplParams
.push_back(TTmplParam("onclick", "html_submit_form"));
5470 tmplParams
.push_back(TTmplParam("onclick_param", param
));
5471 tmplParams
.push_back(TTmplParam("active", "true"));
5472 if (minWidth
> 0) tmplParams
.push_back(TTmplParam("wmin", toString(minWidth
)));
5473 CInterfaceGroup
*buttonGroup
= CWidgetManager::getInstance()->getParser()->createGroupInstance(buttonTemplate
, _Paragraph
->getId(), tmplParams
);
5476 // Add the ctrl button
5477 CCtrlTextButton
*ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("button"));
5478 if (!ctrlButton
) ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("b"));
5481 ctrlButton
->setModulateGlobalColorAll (_Style
.Current
.GlobalColor
);
5483 // Translate the tooltip
5484 if (!tooltip
.empty())
5486 if (CI18N::hasTranslation(tooltip
))
5488 ctrlButton
->setDefaultContextHelp(CI18N::get(tooltip
));
5492 ctrlButton
->setDefaultContextHelp(tooltip
);
5496 ctrlButton
->setText(value
);
5498 setTextButtonStyle(ctrlButton
, _Style
.Current
);
5500 getParagraph()->addChild (buttonGroup
);
5505 // ***************************************************************************
5506 void CGroupHTML::htmlA(const CHtmlElement
&elm
)
5509 _Link
.push_back ("");
5510 _LinkTitle
.push_back("");
5511 _LinkClass
.push_back("");
5512 if (elm
.hasClass("ryzom-ui-button"))
5513 _LinkClass
.back() = "ryzom-ui-button";
5515 // #fragment works with both ID and NAME so register both
5516 if (elm
.hasNonEmptyAttribute("name"))
5517 _AnchorName
.push_back(elm
.getAttribute("name"));
5518 if (elm
.hasNonEmptyAttribute("title"))
5519 _LinkTitle
.back() = elm
.getAttribute("title");
5520 if (elm
.hasNonEmptyAttribute("href"))
5522 string suri
= elm
.getAttribute("href");
5523 if(suri
.find("ah:") == 0)
5526 _Link
.back() = suri
;
5530 // convert href from "?key=val" into "http://domain.com/?key=val"
5531 _Link
.back() = getAbsoluteUrl(suri
);
5535 renderPseudoElement(":before", elm
);
5538 void CGroupHTML::htmlAend(const CHtmlElement
&elm
)
5540 renderPseudoElement(":after", elm
);
5543 popIfNotEmpty(_Link
);
5544 popIfNotEmpty(_LinkTitle
);
5545 popIfNotEmpty(_LinkClass
);
5548 // ***************************************************************************
5549 void CGroupHTML::htmlBASE(const CHtmlElement
&elm
)
5551 if (!_ReadingHeadTag
|| _IgnoreBaseUrlTag
)
5554 if (elm
.hasNonEmptyAttribute("href"))
5556 CUrlParser
uri(elm
.getAttribute("href"));
5557 if (uri
.isAbsolute())
5559 _URL
= uri
.toString();
5560 _IgnoreBaseUrlTag
= true;
5565 // ***************************************************************************
5566 void CGroupHTML::htmlBODY(const CHtmlElement
&elm
)
5568 // override <body> (or <html>) css style attribute
5569 if (elm
.hasNonEmptyAttribute("bgcolor"))
5571 _Style
.applyStyle("background-color: " + elm
.getAttribute("bgcolor"));
5574 applyBackground(elm
);
5576 renderPseudoElement(":before", elm
);
5579 // ***************************************************************************
5580 void CGroupHTML::htmlBR(const CHtmlElement
&elm
)
5582 if (!_Paragraph
|| _Paragraph
->getNumChildren() == 0)
5592 // ***************************************************************************
5593 void CGroupHTML::htmlBUTTON(const CHtmlElement
&elm
)
5595 std::string name
= elm
.getAttribute("name");
5596 std::string value
= elm
.getAttribute("value");
5597 std::string formId
= elm
.getAttribute("form");
5598 std::string formAction
= elm
.getAttribute("formaction");
5599 std::string tooltip
= elm
.getAttribute("tooltip");
5600 bool disabled
= elm
.hasAttribute("disabled");
5602 if (formId
.empty() && _FormOpen
)
5604 formId
= _Forms
.back().id
;
5607 if (!formAction
.empty())
5609 formAction
= getAbsoluteUrl(formAction
);
5612 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, value
, "text", formAction
));
5613 // Action handler parameters
5617 if (elm
.getAttribute("type") == "submit")
5619 param
= "ah:html_submit_form&name=" + getId() + "&button=" + toString(_FormSubmit
.size()-1);
5628 _Link
.push_back(param
);
5629 _LinkTitle
.push_back(tooltip
);
5630 _LinkClass
.push_back("ryzom-ui-button");
5632 // TODO: this creates separate button element
5633 //renderPseudoElement(":before", elm);
5635 void CGroupHTML::htmlBUTTONend(const CHtmlElement
&elm
)
5637 // TODO: this creates separate button element
5638 //renderPseudoElement(":after", elm);
5641 popIfNotEmpty(_Link
);
5642 popIfNotEmpty(_LinkTitle
);
5643 popIfNotEmpty(_LinkClass
);
5646 // ***************************************************************************
5647 void CGroupHTML::htmlDD(const CHtmlElement
&elm
)
5652 // if there was no closing tag for <dt>, then remove <dt> style
5655 nlwarning("BUG: nested DT in DD");
5656 _DL
.back().DT
= false;
5661 nlwarning("BUG: nested DD in DD");
5662 _DL
.back().DD
= false;
5663 popIfNotEmpty(_Indent
);
5666 _DL
.back().DD
= true;
5667 _Indent
.push_back(getIndent() + ULIndent
);
5672 newParagraph(ULBeginSpace
);
5676 newParagraph(LIBeginSpace
);
5679 renderPseudoElement(":before", elm
);
5682 void CGroupHTML::htmlDDend(const CHtmlElement
&elm
)
5687 renderPseudoElement(":after", elm
);
5689 // parser will process two DD in a row as nested when first DD is not closed
5692 _DL
.back().DD
= false;
5693 popIfNotEmpty(_Indent
);
5697 // ***************************************************************************
5698 void CGroupHTML::htmlDIV(const CHtmlElement
&elm
)
5700 _DivName
= elm
.getAttribute("name");
5702 string instClass
= elm
.getAttribute("class");
5704 // use generic template system
5705 if (_TrustedDomain
&& !instClass
.empty() && instClass
== "ryzom-ui-grouptemplate")
5707 string style
= elm
.getAttribute("style");
5708 string id
= elm
.getAttribute("id");
5710 id
= "DIV" + toString(getNextAutoIdSeq());
5712 typedef pair
<string
, string
> TTmplParam
;
5713 vector
<TTmplParam
> tmplParams
;
5715 string templateName
;
5718 TStyle styles
= parseStyle(style
);
5719 TStyle::iterator it
;
5720 for (it
=styles
.begin(); it
!= styles
.end(); it
++)
5722 if ((*it
).first
== "template")
5723 templateName
= (*it
).second
;
5725 tmplParams
.push_back(TTmplParam((*it
).first
, (*it
).second
));
5729 if (!templateName
.empty())
5732 bool haveParentDiv
= getDiv() != NULL
;
5734 parentId
= getDiv()->getId();
5740 parentId
= _Paragraph
->getId();
5743 CInterfaceGroup
*inst
= CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName
, parentId
, tmplParams
);
5746 inst
->setId(parentId
+":"+id
);
5747 inst
->updateCoords();
5750 inst
->setParent(getDiv());
5751 inst
->setParentSize(getDiv());
5752 inst
->setParentPos(getDiv());
5753 inst
->setPosRef(Hotspot_TL
);
5754 inst
->setParentPosRef(Hotspot_TL
);
5755 getDiv()->addGroup(inst
);
5759 getParagraph()->addChild(inst
);
5762 _Divs
.push_back(inst
);
5767 renderPseudoElement(":before", elm
);
5770 void CGroupHTML::htmlDIVend(const CHtmlElement
&elm
)
5772 renderPseudoElement(":after", elm
);
5774 popIfNotEmpty(_Divs
);
5777 // ***************************************************************************
5778 void CGroupHTML::htmlDL(const CHtmlElement
&elm
)
5780 _DL
.push_back(HTMLDListElement());
5781 _LI
= _DL
.size() > 1 || !_UL
.empty();
5783 renderPseudoElement(":before", elm
);
5786 void CGroupHTML::htmlDLend(const CHtmlElement
&elm
)
5791 renderPseudoElement(":after", elm
);
5796 nlwarning("BUG: unclosed DT in DL");
5802 popIfNotEmpty(_Indent
);
5803 nlwarning("BUG: unclosed DD in DL");
5806 popIfNotEmpty (_DL
);
5809 // ***************************************************************************
5810 void CGroupHTML::htmlDT(const CHtmlElement
&elm
)
5815 // TODO: check if nested tags still happen and fix it in parser
5816 // : remove special handling for nesting and let it happen
5818 // html parser and libxml2 should prevent nested tags like these
5821 nlwarning("BUG: nested DD in DT");
5823 _DL
.back().DD
= false;
5824 popIfNotEmpty(_Indent
);
5827 // html parser and libxml2 should prevent nested tags like these
5830 nlwarning("BUG: nested DT in DT");
5833 _DL
.back().DT
= true;
5838 newParagraph(ULBeginSpace
);
5842 newParagraph(LIBeginSpace
);
5845 renderPseudoElement(":before", elm
);
5848 void CGroupHTML::htmlDTend(const CHtmlElement
&elm
)
5853 renderPseudoElement(":after", elm
);
5855 _DL
.back().DT
= false;
5858 // ***************************************************************************
5859 void CGroupHTML::htmlFONT(const CHtmlElement
&elm
)
5861 if (elm
.hasNonEmptyAttribute("color"))
5864 if (scanHTMLColor(elm
.getAttribute("color").c_str(), color
))
5865 _Style
.Current
.TextColor
= color
;
5868 if (elm
.hasNonEmptyAttribute("size"))
5871 fromString(elm
.getAttribute("size"), fontsize
);
5872 _Style
.Current
.FontSize
= fontsize
;
5876 // ***************************************************************************
5877 void CGroupHTML::htmlFORM(const CHtmlElement
&elm
)
5882 CGroupHTML::CForm form
;
5883 // id check is case sensitive and auto id's are uppercase
5884 form
.id
= toLowerAscii(trim(elm
.getAttribute("id")));
5885 if (form
.id
.empty())
5887 form
.id
= toString("FORM%d", _Forms
.size());
5890 // Get the action name
5891 if (elm
.hasNonEmptyAttribute("action"))
5893 form
.Action
= getAbsoluteUrl(elm
.getAttribute("action"));
5900 _Forms
.push_back(form
);
5902 renderPseudoElement(":before", elm
);
5905 void CGroupHTML::htmlFORMend(const CHtmlElement
&elm
)
5908 renderPseudoElement(":after", elm
);
5911 // ***************************************************************************
5912 void CGroupHTML::htmlH(const CHtmlElement
&elm
)
5914 newParagraph(PBeginSpace
);
5915 renderPseudoElement(":before", elm
);
5918 void CGroupHTML::htmlHend(const CHtmlElement
&elm
)
5920 renderPseudoElement(":after", elm
);
5923 // ***************************************************************************
5924 void CGroupHTML::htmlHEAD(const CHtmlElement
&elm
)
5926 _ReadingHeadTag
= !_IgnoreHeadTag
;
5927 _IgnoreHeadTag
= true;
5930 void CGroupHTML::htmlHEADend(const CHtmlElement
&elm
)
5932 _ReadingHeadTag
= false;
5935 // ***************************************************************************
5936 void CGroupHTML::htmlHR(const CHtmlElement
&elm
)
5938 CInterfaceGroup
*sep
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_hr", "", NULL
, 0);
5941 CViewBitmap
*bitmap
= dynamic_cast<CViewBitmap
*>(sep
->getView("hr"));
5944 bitmap
->setColor(_Style
.Current
.TextColor
);
5945 if (_Style
.Current
.Width
> 0)
5947 clamp(_Style
.Current
.Width
, 1, 32000);
5948 bitmap
->setW(_Style
.Current
.Width
);
5949 bitmap
->setSizeRef(CInterfaceElement::none
);
5951 if (_Style
.Current
.Height
> 0)
5953 clamp(_Style
.Current
.Height
, 1, 1000);
5954 bitmap
->setH(_Style
.Current
.Height
);
5958 renderPseudoElement(":before", elm
);
5959 addHtmlGroup(sep
, 0);
5960 renderPseudoElement(":after", elm
);
5964 // ***************************************************************************
5965 void CGroupHTML::htmlHTML(const CHtmlElement
&elm
)
5967 if (elm
.hasNonEmptyAttribute("style"))
5969 _Style
.applyStyle(elm
.getAttribute("style"));
5971 _Style
.Root
= _Style
.Current
;
5972 applyBackground(elm
);
5975 // ***************************************************************************
5976 void CGroupHTML::htmlI(const CHtmlElement
&elm
)
5979 renderPseudoElement(":before", elm
);
5982 void CGroupHTML::htmlIend(const CHtmlElement
&elm
)
5984 renderPseudoElement(":after", elm
);
5988 // ***************************************************************************
5989 void CGroupHTML::htmlIMG(const CHtmlElement
&elm
)
5991 std::string src
= trim(elm
.getAttribute("src"));
5994 // no 'src' attribute, or empty
5999 std::string id
= elm
.getAttribute("id");
6001 if (elm
.hasNonEmptyAttribute("width"))
6002 getPercentage(_Style
.Current
.Width
, tmpf
, elm
.getAttribute("width").c_str());
6003 if (elm
.hasNonEmptyAttribute("height"))
6004 getPercentage(_Style
.Current
.Height
, tmpf
, elm
.getAttribute("height").c_str());
6006 // Get the global color name
6007 if (elm
.hasAttribute("global_color"))
6008 _Style
.Current
.GlobalColor
= true;
6011 // keep "alt" attribute for backward compatibility
6012 std::string tooltip
= elm
.getAttribute("alt");
6014 if (elm
.hasNonEmptyAttribute("title"))
6015 tooltip
= elm
.getAttribute("title");
6018 string overSrc
= elm
.getAttribute("data-over-src");
6020 // inside a/button with valid url (ie, button is not disabled)
6021 string url
= getLink();
6022 if (getA() && !url
.empty() && getParent() && getParent()->getParent())
6024 string params
= "name=" + getId() + "|url=" + url
;
6025 addButton(CCtrlButton::PushButton
, id
, src
, src
, overSrc
, "browse", params
.c_str(), tooltip
, _Style
.Current
);
6028 if (!tooltip
.empty() || !overSrc
.empty())
6030 addButton(CCtrlButton::PushButton
, id
, src
, src
, overSrc
, "", "", tooltip
, _Style
.Current
);
6034 // Get the option to reload (class==reload)
6035 bool reloadImg
= false;
6037 if (elm
.hasNonEmptyAttribute("style"))
6039 string styleString
= elm
.getAttribute("style");
6040 TStyle styles
= parseStyle(styleString
);
6041 TStyle::iterator it
;
6043 it
= styles
.find("reload");
6044 if (it
!= styles
.end() && (*it
).second
== "1")
6048 addImage(id
, elm
.getAttribute("src"), reloadImg
, _Style
.Current
);
6052 // ***************************************************************************
6053 void CGroupHTML::htmlINPUT(const CHtmlElement
&elm
)
6058 // read general property
6059 string id
= elm
.getAttribute("id");
6061 // Widget template name (old)
6062 string templateName
= elm
.getAttribute("z_btn_tmpl");
6063 // Input name is the new
6064 if (elm
.hasNonEmptyAttribute("z_input_tmpl"))
6065 templateName
= elm
.getAttribute("z_input_tmpl");
6067 // Widget minimal width
6068 uint32 minWidth
= 0;
6069 fromString(elm
.getAttribute("z_input_width"), minWidth
);
6071 // <input type="...">
6072 std::string type
= trim(elm
.getAttribute("type"));
6075 // no 'type' attribute, or empty
6079 // Global color flag
6080 if (elm
.hasAttribute("global_color"))
6081 _Style
.Current
.GlobalColor
= true;
6084 std::string tooltip
= elm
.getAttribute("alt");
6086 if (type
== "image")
6088 string name
= elm
.getAttribute("name");
6089 string src
= elm
.getAttribute("src");
6090 string over
= elm
.getAttribute("data-over-src");
6091 string formId
= elm
.getAttribute("form");
6092 string formAction
= elm
.getAttribute("formaction");
6094 if (formId
.empty() && _FormOpen
) {
6095 formId
= _Forms
.back().id
;
6098 insertFormImageButton(name
, tooltip
, src
, over
, formId
, formAction
, minWidth
, templateName
);
6100 else if (type
== "button" || type
== "submit")
6102 string name
= elm
.getAttribute("name");
6103 string value
= elm
.getAttribute("value");
6104 string formId
= elm
.getAttribute("form");
6105 string formAction
= elm
.getAttribute("formaction");
6107 if (formId
.empty() && _FormOpen
) {
6108 formId
= _Forms
.back().id
;
6111 insertFormTextButton(name
, tooltip
, value
, formId
, formAction
, minWidth
, templateName
);
6113 else if (type
== "text")
6115 // Get the string name
6116 string name
= elm
.getAttribute("name");
6117 string ucValue
= elm
.getAttribute("value");
6120 uint maxlength
= 1024;
6121 if (elm
.hasNonEmptyAttribute("size"))
6122 fromString(elm
.getAttribute("size"), size
);
6123 if (elm
.hasNonEmptyAttribute("maxlength"))
6124 fromString(elm
.getAttribute("maxlength"), maxlength
);
6126 // ryzom client used to have 'size' attribute in pixels, (12 == was default font size)
6127 if (_Style
.hasStyle("-ryzom-input-size-px") && _Style
.getStyle("-ryzom-input-size-px") == "true")
6130 string
textTemplate(!templateName
.empty() ? templateName
: DefaultFormTextGroup
);
6132 CInterfaceGroup
*textArea
= addTextArea (textTemplate
, name
.c_str (), 1, size
, false, ucValue
, maxlength
);
6135 // Add the text area to the form
6136 CGroupHTML::CForm::CEntry entry
;
6138 entry
.TextArea
= textArea
;
6139 _Forms
.back().Entries
.push_back (entry
);
6142 else if (type
== "checkbox" || type
== "radio")
6144 renderPseudoElement(":before", elm
);
6146 CCtrlButton::EType btnType
;
6147 string name
= elm
.getAttribute("name");
6148 string normal
= elm
.getAttribute("src");
6151 string ucValue
= "on";
6152 bool checked
= elm
.hasAttribute("checked");
6154 // TODO: unknown if empty attribute should override or not
6155 if (elm
.hasNonEmptyAttribute("value"))
6156 ucValue
= elm
.getAttribute("value");
6158 if (type
== "radio")
6160 btnType
= CCtrlButton::RadioButton
;
6161 normal
= DefaultRadioButtonBitmapNormal
;
6162 pushed
= DefaultRadioButtonBitmapPushed
;
6163 over
= DefaultRadioButtonBitmapOver
;
6167 btnType
= CCtrlButton::ToggleButton
;
6168 normal
= DefaultCheckBoxBitmapNormal
;
6169 pushed
= DefaultCheckBoxBitmapPushed
;
6170 over
= DefaultCheckBoxBitmapOver
;
6173 // Add the ctrl button
6174 CCtrlButton
*checkbox
= addButton (btnType
, name
, normal
, pushed
, over
, "", "", tooltip
, _Style
.Current
);
6177 if (btnType
== CCtrlButton::RadioButton
)
6179 // override with 'id' because radio buttons share same name
6181 checkbox
->setId(id
);
6183 // group together buttons with same name
6184 CForm
&form
= _Forms
.back();
6185 bool notfound
= true;
6186 for (uint i
=0; i
<form
.Entries
.size(); i
++)
6188 if (form
.Entries
[i
].Name
== name
&& form
.Entries
[i
].Checkbox
->getType() == CCtrlButton::RadioButton
)
6190 checkbox
->initRBRefFromRadioButton(form
.Entries
[i
].Checkbox
);
6197 // this will start a new group (initRBRef() would take first button in group container otherwise)
6198 checkbox
->initRBRefFromRadioButton(checkbox
);
6202 checkbox
->setPushed (checked
);
6204 // Add the button to the form
6205 CGroupHTML::CForm::CEntry entry
;
6207 entry
.Value
= decodeHTMLEntities(ucValue
);
6208 entry
.Checkbox
= checkbox
;
6209 _Forms
.back().Entries
.push_back (entry
);
6211 renderPseudoElement(":after", elm
);
6213 else if (type
== "hidden")
6215 if (elm
.hasNonEmptyAttribute("name"))
6218 string name
= elm
.getAttribute("name");
6221 string ucValue
= elm
.getAttribute("value");
6224 CGroupHTML::CForm::CEntry entry
;
6226 entry
.Value
= decodeHTMLEntities(ucValue
);
6227 _Forms
.back().Entries
.push_back (entry
);
6232 // ***************************************************************************
6233 void CGroupHTML::htmlLI(const CHtmlElement
&elm
)
6238 // UL, OL top margin if this is the first LI
6242 newParagraph(ULBeginSpace
);
6246 newParagraph(LIBeginSpace
);
6249 // OL list index can be overridden by <li value="1"> attribute
6250 if (elm
.hasNonEmptyAttribute("value"))
6251 fromString(elm
.getAttribute("value"), _UL
.back().Value
);
6253 string str
= _UL
.back().getListMarkerText();
6256 // list-style-type: outside
6257 if (_CurrentViewLink
)
6259 getParagraph()->setFirstViewIndent(-_CurrentViewLink
->getMaxUsedW());
6265 renderPseudoElement(":before", elm
);
6270 void CGroupHTML::htmlLIend(const CHtmlElement
&elm
)
6272 renderPseudoElement(":after", elm
);
6275 // ***************************************************************************
6276 void CGroupHTML::htmlLUA(const CHtmlElement
&elm
)
6278 // we receive an embeded lua script
6279 _ParsingLua
= _TrustedDomain
; // Only parse lua if TrustedDomain
6283 void CGroupHTML::htmlLUAend(const CHtmlElement
&elm
)
6285 if (_ParsingLua
&& _TrustedDomain
)
6287 _ParsingLua
= false;
6288 // execute the embeded lua script
6289 _LuaScript
= "\nlocal __CURRENT_WINDOW__=\""+this->_Id
+"\" \n"+_LuaScript
;
6290 CLuaManager::getInstance().executeLuaScript(_LuaScript
, true);
6294 // ***************************************************************************
6295 void CGroupHTML::htmlMETA(const CHtmlElement
&elm
)
6297 if (!_ReadingHeadTag
)
6300 std::string httpEquiv
= elm
.getAttribute("http-equiv");
6301 std::string httpContent
= elm
.getAttribute("content");
6302 if (httpEquiv
.empty() || httpContent
.empty())
6307 // only first http-equiv="refresh" should be handled
6308 if (_RefreshUrl
.empty() && httpEquiv
== "refresh")
6310 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
6311 double timeSec
= times
.thisFrameMs
/ 1000.0f
;
6313 string::size_type pos
= httpContent
.find_first_of(";");
6314 if (pos
== string::npos
)
6316 fromString(httpContent
, _NextRefreshTime
);
6321 fromString(httpContent
.substr(0, pos
), _NextRefreshTime
);
6323 pos
= toLowerAscii(httpContent
).find("url=");
6324 if (pos
!= string::npos
)
6325 _RefreshUrl
= getAbsoluteUrl(httpContent
.substr(pos
+ 4));
6328 _NextRefreshTime
+= timeSec
;
6332 // ***************************************************************************
6333 void CGroupHTML::htmlMETER(const CHtmlElement
&elm
)
6335 HTMLMeterElement meter
;
6336 meter
.readValues(elm
);
6338 std::string id
= "meter";
6339 if (elm
.hasAttribute("id"))
6340 id
= elm
.getAttribute("id");
6342 // width: 5em, height: 1em
6343 uint32 width
= _Style
.Current
.Width
> -1 ? _Style
.Current
.Width
: _Style
.Current
.FontSize
* 5;
6344 uint32 height
= _Style
.Current
.Height
> -1 ? _Style
.Current
.Height
: _Style
.Current
.FontSize
;
6345 // FIXME: only using border-top
6346 uint32 border
= _Style
.Current
.BorderTopWidth
> -1 ? _Style
.Current
.BorderTopWidth
: 0;
6348 uint barw
= (uint
) (width
* meter
.getValueRatio());
6349 CRGBA bgColor
= meter
.getBarColor(elm
, _Style
);
6350 CRGBA valueColor
= meter
.getValueColor(elm
, _Style
);
6352 typedef pair
<string
, string
> TTmplParam
;
6353 vector
<TTmplParam
> tmplParams
;
6354 tmplParams
.push_back(TTmplParam("id", id
));
6355 tmplParams
.push_back(TTmplParam("active", "true"));
6356 tmplParams
.push_back(TTmplParam("w", toString(width
)));
6357 tmplParams
.push_back(TTmplParam("h", toString(height
)));
6358 tmplParams
.push_back(TTmplParam("border_x2", toString(border
*2)));
6359 tmplParams
.push_back(TTmplParam("bgtexture", "blank.tga"));
6360 tmplParams
.push_back(TTmplParam("bgcolor", bgColor
.toString()));
6361 tmplParams
.push_back(TTmplParam("value_w", toString(barw
)));
6362 tmplParams
.push_back(TTmplParam("value_texture", "blank.tga"));
6363 tmplParams
.push_back(TTmplParam("value_color", valueColor
.toString()));
6365 CInterfaceGroup
*gr
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_meter", getParagraph()->getId(), &tmplParams
[0], (uint
)tmplParams
.size());
6368 renderPseudoElement(":before", elm
);
6369 getParagraph()->addChild(gr
);
6370 renderPseudoElement(":after", elm
);
6372 // ignore any inner elements
6373 _IgnoreChildElements
= true;
6377 // ***************************************************************************
6378 void CGroupHTML::htmlOBJECT(const CHtmlElement
&elm
)
6380 _ObjectType
= elm
.getAttribute("type");
6381 _ObjectData
= elm
.getAttribute("data");
6382 _ObjectMD5Sum
= elm
.getAttribute("id");
6383 _ObjectAction
= elm
.getAttribute("standby");
6387 void CGroupHTML::htmlOBJECTend(const CHtmlElement
&elm
)
6389 if (!_TrustedDomain
)
6392 if (_ObjectType
=="application/ryzom-data")
6394 if (!_ObjectData
.empty())
6396 if (addBnpDownload(_ObjectData
, _ObjectAction
, _ObjectScript
, _ObjectMD5Sum
))
6398 CLuaManager::getInstance().executeLuaScript("\nlocal __ALLREADYDL__=true\n"+_ObjectScript
, true);
6400 _ObjectScript
.clear();
6406 // ***************************************************************************
6407 void CGroupHTML::htmlOL(const CHtmlElement
&elm
)
6410 std::string
type("1");
6412 if (elm
.hasNonEmptyAttribute("start"))
6413 fromString(elm
.getAttribute("start"), start
);
6414 if (elm
.hasNonEmptyAttribute("type"))
6415 type
= elm
.getAttribute("type");
6417 _UL
.push_back(HTMLOListElement(start
, type
));
6418 // if LI is already present
6419 _LI
= _UL
.size() > 1 || _DL
.size() > 1;
6420 _Indent
.push_back(getIndent() + ULIndent
);
6422 renderPseudoElement(":before", elm
);
6425 void CGroupHTML::htmlOLend(const CHtmlElement
&elm
)
6430 // ***************************************************************************
6431 void CGroupHTML::htmlOPTION(const CHtmlElement
&elm
)
6433 _SelectOption
= true;
6434 _SelectOptionStr
.clear();
6437 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6440 _Forms
.back().Entries
.back().SelectValues
.push_back(elm
.getAttribute("value"));
6442 if (elm
.hasAttribute("selected"))
6443 _Forms
.back().Entries
.back().InitialSelection
= (sint
)_Forms
.back().Entries
.back().SelectValues
.size() - 1;
6445 if (elm
.hasAttribute("disabled"))
6446 _Forms
.back().Entries
.back().sbOptionDisabled
= (sint
)_Forms
.back().Entries
.back().SelectValues
.size() - 1;
6449 void CGroupHTML::htmlOPTIONend(const CHtmlElement
&elm
)
6451 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6454 // use option text as value
6455 if (!elm
.hasAttribute("value"))
6457 _Forms
.back().Entries
.back().SelectValues
.back() = _SelectOptionStr
;
6460 // insert the parsed text into the select control
6461 CDBGroupComboBox
*cb
= _Forms
.back().Entries
.back().ComboBox
;
6464 uint lineIndex
= cb
->getNumTexts();
6465 cb
->addText(_SelectOptionStr
);
6466 if (_Forms
.back().Entries
.back().sbOptionDisabled
== lineIndex
)
6468 cb
->setGrayed(lineIndex
, true);
6473 CGroupMenu
*sb
= _Forms
.back().Entries
.back().SelectBox
;
6476 uint lineIndex
= sb
->getNumLine();
6477 sb
->addLine(_SelectOptionStr
, "", "");
6479 if (_Forms
.back().Entries
.back().sbOptionDisabled
== lineIndex
)
6481 sb
->setGrayedLine(lineIndex
, true);
6485 // create option line checkbox, CGroupMenu is taking ownership of the checbox
6486 CInterfaceGroup
*ig
= CWidgetManager::getInstance()->getParser()->createGroupInstance("menu_checkbox", "", NULL
, 0);
6489 CCtrlButton
*cb
= dynamic_cast<CCtrlButton
*>(ig
->getCtrl("b"));
6492 if (_Forms
.back().Entries
.back().sbMultiple
)
6494 cb
->setType(CCtrlButton::ToggleButton
);
6495 cb
->setTexture(DefaultCheckBoxBitmapNormal
);
6496 cb
->setTexturePushed(DefaultCheckBoxBitmapPushed
);
6497 cb
->setTextureOver(DefaultCheckBoxBitmapOver
);
6501 cb
->setType(CCtrlButton::RadioButton
);
6502 cb
->setTexture(DefaultRadioButtonBitmapNormal
);
6503 cb
->setTexturePushed(DefaultRadioButtonBitmapPushed
);
6504 cb
->setTextureOver(DefaultRadioButtonBitmapOver
);
6506 if (_Forms
.back().Entries
.back().sbRBRef
== NULL
)
6507 _Forms
.back().Entries
.back().sbRBRef
= cb
;
6509 cb
->initRBRefFromRadioButton(_Forms
.back().Entries
.back().sbRBRef
);
6512 cb
->setPushed(_Forms
.back().Entries
.back().InitialSelection
== lineIndex
);
6513 sb
->setUserGroupLeft(lineIndex
, ig
);
6517 nlwarning("Failed to get 'b' element from 'menu_checkbox' template");
6526 // ***************************************************************************
6527 void CGroupHTML::htmlP(const CHtmlElement
&elm
)
6529 newParagraph(PBeginSpace
);
6530 renderPseudoElement(":before", elm
);
6533 void CGroupHTML::htmlPend(const CHtmlElement
&elm
)
6535 renderPseudoElement(":after", elm
);
6538 // ***************************************************************************
6539 void CGroupHTML::htmlPRE(const CHtmlElement
&elm
)
6541 _PRE
.push_back(true);
6544 renderPseudoElement(":before", elm
);
6547 void CGroupHTML::htmlPREend(const CHtmlElement
&elm
)
6549 renderPseudoElement(":after", elm
);
6551 popIfNotEmpty(_PRE
);
6554 // ***************************************************************************
6555 void CGroupHTML::htmlPROGRESS(const CHtmlElement
&elm
)
6557 HTMLProgressElement progress
;
6558 progress
.readValues(elm
);
6560 std::string id
= "progress";
6561 if (elm
.hasAttribute("id"))
6562 id
= elm
.getAttribute("id");
6564 // width: 10em, height: 1em
6565 uint32 width
= _Style
.Current
.Width
> -1 ? _Style
.Current
.Width
: _Style
.Current
.FontSize
* 10;
6566 uint32 height
= _Style
.Current
.Height
> -1 ? _Style
.Current
.Height
: _Style
.Current
.FontSize
;
6567 // FIXME: only using border-top
6568 uint32 border
= _Style
.Current
.BorderTopWidth
> -1 ? _Style
.Current
.BorderTopWidth
: 0;
6570 uint barw
= (uint
) (width
* progress
.getValueRatio());
6571 CRGBA bgColor
= progress
.getBarColor(elm
, _Style
);
6572 CRGBA valueColor
= progress
.getValueColor(elm
, _Style
);
6574 typedef pair
<string
, string
> TTmplParam
;
6575 vector
<TTmplParam
> tmplParams
;
6576 tmplParams
.push_back(TTmplParam("id", id
));
6577 tmplParams
.push_back(TTmplParam("active", "true"));
6578 tmplParams
.push_back(TTmplParam("w", toString(width
)));
6579 tmplParams
.push_back(TTmplParam("h", toString(height
)));
6580 tmplParams
.push_back(TTmplParam("border_x2", toString(border
*2)));
6581 tmplParams
.push_back(TTmplParam("bgtexture", "blank.tga"));
6582 tmplParams
.push_back(TTmplParam("bgcolor", bgColor
.toString()));
6583 tmplParams
.push_back(TTmplParam("value_w", toString(barw
)));
6584 tmplParams
.push_back(TTmplParam("value_texture", "blank.tga"));
6585 tmplParams
.push_back(TTmplParam("value_color", valueColor
.toString()));
6587 CInterfaceGroup
*gr
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_progress", getParagraph()->getId(), &tmplParams
[0], (uint
)tmplParams
.size());
6590 renderPseudoElement(":before", elm
);
6591 getParagraph()->addChild(gr
);
6592 renderPseudoElement(":after", elm
);
6594 // ignore any inner elements
6595 _IgnoreChildElements
= true;
6599 // ***************************************************************************
6600 void CGroupHTML::htmlSCRIPT(const CHtmlElement
&elm
)
6605 void CGroupHTML::htmlSCRIPTend(const CHtmlElement
&elm
)
6607 _IgnoreText
= false;
6610 // ***************************************************************************
6611 void CGroupHTML::htmlSELECT(const CHtmlElement
&elm
)
6617 string name
= elm
.getAttribute("name");
6618 bool multiple
= elm
.hasAttribute("multiple");
6621 if (elm
.hasNonEmptyAttribute("size"))
6622 fromString(elm
.getAttribute("size"), size
);
6624 CGroupHTML::CForm::CEntry entry
;
6626 entry
.sbMultiple
= multiple
;
6627 if (size
> 1 || multiple
)
6629 entry
.InitialSelection
= -1;
6630 CGroupMenu
*sb
= addSelectBox(DefaultFormSelectBoxMenuGroup
, name
.c_str());
6636 if (_Style
.Current
.Width
> -1)
6637 sb
->setMinW(_Style
.Current
.Width
);
6639 if (_Style
.Current
.Height
> -1)
6640 sb
->setMinH(_Style
.Current
.Height
);
6642 sb
->setMaxVisibleLine(size
);
6643 sb
->setFontSize(_Style
.Current
.FontSize
, false);
6646 entry
.SelectBox
= sb
;
6650 CDBGroupComboBox
*cb
= addComboBox(DefaultFormSelectGroup
, name
.c_str());
6651 entry
.ComboBox
= cb
;
6657 setTextStyle(cb
->getViewText(), _Style
.Current
);
6660 _Forms
.back().Entries
.push_back (entry
);
6663 void CGroupHTML::htmlSELECTend(const CHtmlElement
&elm
)
6665 _SelectOption
= false;
6666 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6669 CDBGroupComboBox
*cb
= _Forms
.back().Entries
.back().ComboBox
;
6672 cb
->setSelectionNoTrigger(_Forms
.back().Entries
.back().InitialSelection
);
6673 // TODO: magic padding
6674 cb
->setW(cb
->evalContentWidth() + 16);
6678 // ***************************************************************************
6679 void CGroupHTML::htmlSTYLE(const CHtmlElement
&elm
)
6684 void CGroupHTML::htmlSTYLEend(const CHtmlElement
&elm
)
6686 _IgnoreText
= false;
6689 // ***************************************************************************
6690 void CGroupHTML::htmlTABLE(const CHtmlElement
&elm
)
6692 // Get cells parameters
6693 getCellsParameters(elm
, false);
6695 CGroupTable
*table
= new CGroupTable(TCtorParam());
6696 table
->BgColor
= _CellParams
.back().BgColor
;
6697 if (elm
.hasNonEmptyAttribute("id"))
6698 table
->setId(getCurrentGroup()->getId() + ":" + elm
.getAttribute("id"));
6700 table
->setId(getCurrentGroup()->getId() + ":TABLE" + toString(getNextAutoIdSeq()));
6702 // TODO: border-spacing: 2em;
6704 if (elm
.hasNonEmptyAttribute("cellspacing"))
6705 fromString(elm
.getAttribute("cellspacing"), table
->CellSpacing
);
6707 // TODO: cssLength, horiz/vert values
6708 if (_Style
.hasStyle("border-spacing"))
6709 fromString(_Style
.getStyle("border-spacing"), table
->CellSpacing
);
6711 // overrides border-spacing if set to 'collapse'
6712 if (_Style
.checkStyle("border-collapse", "collapse"))
6713 table
->CellSpacing
= 0;
6716 if (elm
.hasNonEmptyAttribute("cellpadding"))
6717 fromString(elm
.getAttribute("cellpadding"), table
->CellPadding
);
6719 if (_Style
.hasStyle("width"))
6721 // _Style.Width does not handle '%' unit currently
6722 if (_Style
.Current
.Width
> 0)
6724 table
->ForceWidthMin
= _Style
.Current
.Width
;
6725 table
->TableRatio
= 0;
6729 getPercentage (table
->ForceWidthMin
, table
->TableRatio
, _Style
.getStyle("width").c_str());
6732 else if (elm
.hasNonEmptyAttribute("width"))
6734 getPercentage (table
->ForceWidthMin
, table
->TableRatio
, elm
.getAttribute("width").c_str());
6737 // border from css or from attribute
6739 uint32 borderWidth
= 0;
6740 CRGBA borderColor
= CRGBA::Transparent
;
6742 if (elm
.hasAttribute("border"))
6744 std::string s
= elm
.getAttribute("border");
6748 fromString(elm
.getAttribute("border"), borderWidth
);
6750 if (elm
.hasNonEmptyAttribute("bordercolor"))
6751 scanHTMLColor(elm
.getAttribute("bordercolor").c_str(), borderColor
);
6753 borderColor
= CRGBA(128, 128, 128, 255);
6755 table
->CellBorder
= (borderWidth
> 0);
6756 table
->Border
->setWidth(borderWidth
, borderWidth
, borderWidth
, borderWidth
);
6757 table
->Border
->setColor(borderColor
, borderColor
, borderColor
, borderColor
);
6758 table
->Border
->setStyle(CSS_LINE_STYLE_OUTSET
, CSS_LINE_STYLE_OUTSET
, CSS_LINE_STYLE_OUTSET
, CSS_LINE_STYLE_OUTSET
);
6762 table
->CellBorder
= false;
6765 if (_Style
.hasStyle("border-top-width")) table
->Border
->TopWidth
= _Style
.Current
.BorderTopWidth
;
6766 if (_Style
.hasStyle("border-right-width")) table
->Border
->RightWidth
= _Style
.Current
.BorderRightWidth
;
6767 if (_Style
.hasStyle("border-bottom-width")) table
->Border
->BottomWidth
= _Style
.Current
.BorderBottomWidth
;
6768 if (_Style
.hasStyle("border-left-width")) table
->Border
->LeftWidth
= _Style
.Current
.BorderLeftWidth
;
6770 if (_Style
.hasStyle("border-top-color")) table
->Border
->TopColor
= _Style
.Current
.BorderTopColor
;
6771 if (_Style
.hasStyle("border-right-color")) table
->Border
->RightColor
= _Style
.Current
.BorderRightColor
;
6772 if (_Style
.hasStyle("border-bottom-color")) table
->Border
->BottomColor
= _Style
.Current
.BorderBottomColor
;
6773 if (_Style
.hasStyle("border-left-color")) table
->Border
->LeftColor
= _Style
.Current
.BorderLeftColor
;
6775 if (_Style
.hasStyle("border-top-style")) table
->Border
->TopStyle
= _Style
.Current
.BorderTopStyle
;
6776 if (_Style
.hasStyle("border-right-style")) table
->Border
->RightStyle
= _Style
.Current
.BorderRightStyle
;
6777 if (_Style
.hasStyle("border-bottom-style")) table
->Border
->BottomStyle
= _Style
.Current
.BorderBottomStyle
;
6778 if (_Style
.hasStyle("border-left-style")) table
->Border
->LeftStyle
= _Style
.Current
.BorderLeftStyle
;
6781 if (_Style
.hasStyle("background-image"))
6783 if (_Style
.checkStyle("background-repeat", "repeat"))
6784 table
->setTextureTile(true);
6786 if (_Style
.checkStyle("background-size", "100%"))
6787 table
->setTextureScale(true);
6789 string image
= _Style
.getStyle("background-image");
6790 addImageDownload(image
, table
, CStyleParams(), NormalImage
, "");
6793 // setting ModulateGlobalColor must be after addImageDownload
6794 if (_Style
.checkStyle("-ryzom-modulate-bgcolor", "true"))
6795 table
->setModulateGlobalColor(true);
6796 table
->setMarginLeft(getIndent());
6797 addHtmlGroup (table
, 0);
6799 renderPseudoElement(":before", elm
);
6801 _Tables
.push_back(table
);
6803 // Add a cell pointer
6804 _Cells
.push_back(NULL
);
6805 _TR
.push_back(false);
6806 _Indent
.push_back(0);
6809 void CGroupHTML::htmlTABLEend(const CHtmlElement
&elm
)
6811 popIfNotEmpty(_CellParams
);
6813 popIfNotEmpty(_Cells
);
6814 popIfNotEmpty(_Tables
);
6815 popIfNotEmpty(_Indent
);
6817 renderPseudoElement(":after", elm
);
6820 // ***************************************************************************
6821 void CGroupHTML::htmlTD(const CHtmlElement
&elm
)
6823 CRGBA rowColor
= CRGBA::Transparent
;
6824 // remember row color so we can blend it with cell color
6825 if (!_CellParams
.empty())
6826 rowColor
= _CellParams
.back().BgColor
;
6828 // Get cells parameters
6829 getCellsParameters(elm
, true);
6831 // if cell has own background,then it must be blended with row
6832 if (rowColor
.A
> 0 && (elm
.hasNonEmptyAttribute("bgcolor") || _Style
.hasStyle("background-color")))
6834 if (_CellParams
.back().BgColor
.A
< 255)
6835 _CellParams
.back().BgColor
.blendFromui(rowColor
, _CellParams
.back().BgColor
, _CellParams
.back().BgColor
.A
);
6838 if (elm
.ID
== HTML_TH
)
6840 if (!_Style
.hasStyle("font-weight"))
6841 _Style
.Current
.FontWeight
= FONT_WEIGHT_BOLD
;
6842 // center if not specified otherwise.
6843 if (!elm
.hasNonEmptyAttribute("align") && !_Style
.hasStyle("text-align"))
6844 _CellParams
.back().Align
= CGroupCell::Center
;
6847 CGroupTable
*table
= getTable();
6850 // <td> appears to be outside <table>
6856 // <table> not started
6860 _Cells
.back() = new CGroupCell(CViewBase::TCtorParam());
6861 if (elm
.hasNonEmptyAttribute("id"))
6862 _Cells
.back()->setId(table
->getId() + ":" + elm
.getAttribute("id"));
6864 _Cells
.back()->setId(table
->getId() + ":TD" + toString(getNextAutoIdSeq()));
6865 // inner cell content
6866 _Cells
.back()->Group
->setId(_Cells
.back()->getId() + ":CELL");
6868 if (_Style
.checkStyle("background-repeat", "repeat"))
6869 _Cells
.back()->setTextureTile(true);
6871 if (_Style
.checkStyle("background-size", "100%"))
6872 _Cells
.back()->setTextureScale(true);
6874 if (_Style
.hasStyle("background-image"))
6876 string image
= _Style
.getStyle("background-image");
6877 addImageDownload(image
, _Cells
.back(), CStyleParams(), NormalImage
, "");
6880 if (elm
.hasNonEmptyAttribute("colspan"))
6881 fromString(elm
.getAttribute("colspan"), _Cells
.back()->ColSpan
);
6882 if (elm
.hasNonEmptyAttribute("rowspan"))
6883 fromString(elm
.getAttribute("rowspan"), _Cells
.back()->RowSpan
);
6885 _Cells
.back()->BgColor
= _CellParams
.back().BgColor
;
6886 _Cells
.back()->Align
= _CellParams
.back().Align
;
6887 _Cells
.back()->VAlign
= _CellParams
.back().VAlign
;
6888 _Cells
.back()->LeftMargin
= _CellParams
.back().LeftMargin
;
6889 _Cells
.back()->NoWrap
= _CellParams
.back().NoWrap
;
6890 _Cells
.back()->ColSpan
= std::max(1, _Cells
.back()->ColSpan
);
6891 _Cells
.back()->RowSpan
= std::max(1, _Cells
.back()->RowSpan
);
6892 _Cells
.back()->Height
= _CellParams
.back().Height
;
6895 if (_Style
.hasStyle("width"))
6897 // _Style.Width does not handle '%' unit currently
6898 if (_Style
.Current
.Width
> 0)
6900 _Cells
.back()->WidthWanted
= _Style
.Current
.Width
;
6901 _Cells
.back()->TableRatio
= 0;
6905 getPercentage (_Cells
.back()->WidthWanted
, _Cells
.back()->TableRatio
, _Style
.getStyle("width").c_str());
6908 else if (elm
.hasNonEmptyAttribute("width"))
6910 getPercentage (_Cells
.back()->WidthWanted
, _Cells
.back()->TableRatio
, elm
.getAttribute("width").c_str());
6913 _Cells
.back()->NewLine
= getTR();
6915 // setting ModulateGlobalColor must be after addImageDownload
6916 if (_Style
.checkStyle("-ryzom-modulate-bgcolor", "true"))
6917 _Cells
.back()->setModulateGlobalColor(true);
6919 // border from <table border="1">
6920 if (table
->CellBorder
)
6922 _Cells
.back()->Border
->setWidth(1, 1, 1, 1);
6923 _Cells
.back()->Border
->setColor(table
->Border
->TopColor
, table
->Border
->RightColor
, table
->Border
->BottomColor
, table
->Border
->LeftColor
);
6924 _Cells
.back()->Border
->setStyle(CSS_LINE_STYLE_INSET
, CSS_LINE_STYLE_INSET
, CSS_LINE_STYLE_INSET
, CSS_LINE_STYLE_INSET
);
6927 if (_Style
.hasStyle("border-top-width")) _Cells
.back()->Border
->TopWidth
= _Style
.Current
.BorderTopWidth
;
6928 if (_Style
.hasStyle("border-right-width")) _Cells
.back()->Border
->RightWidth
= _Style
.Current
.BorderRightWidth
;
6929 if (_Style
.hasStyle("border-bottom-width")) _Cells
.back()->Border
->BottomWidth
= _Style
.Current
.BorderBottomWidth
;
6930 if (_Style
.hasStyle("border-left-width")) _Cells
.back()->Border
->LeftWidth
= _Style
.Current
.BorderLeftWidth
;
6932 if (_Style
.hasStyle("border-top-color")) _Cells
.back()->Border
->TopColor
= _Style
.Current
.BorderTopColor
;
6933 if (_Style
.hasStyle("border-right-color")) _Cells
.back()->Border
->RightColor
= _Style
.Current
.BorderRightColor
;
6934 if (_Style
.hasStyle("border-bottom-color")) _Cells
.back()->Border
->BottomColor
= _Style
.Current
.BorderBottomColor
;
6935 if (_Style
.hasStyle("border-left-color")) _Cells
.back()->Border
->LeftColor
= _Style
.Current
.BorderLeftColor
;
6937 if (_Style
.hasStyle("border-top-style")) _Cells
.back()->Border
->TopStyle
= _Style
.Current
.BorderTopStyle
;
6938 if (_Style
.hasStyle("border-right-style")) _Cells
.back()->Border
->RightStyle
= _Style
.Current
.BorderRightStyle
;
6939 if (_Style
.hasStyle("border-bottom-style")) _Cells
.back()->Border
->BottomStyle
= _Style
.Current
.BorderBottomStyle
;
6940 if (_Style
.hasStyle("border-left-style")) _Cells
.back()->Border
->LeftStyle
= _Style
.Current
.BorderLeftStyle
;
6942 // padding from <table cellpadding="1">
6943 if (table
->CellPadding
)
6945 _Cells
.back()->PaddingTop
= table
->CellPadding
;
6946 _Cells
.back()->PaddingRight
= table
->CellPadding
;
6947 _Cells
.back()->PaddingBottom
= table
->CellPadding
;
6948 _Cells
.back()->PaddingLeft
= table
->CellPadding
;
6951 if (_Style
.hasStyle("padding-top")) _Cells
.back()->PaddingTop
= _Style
.Current
.PaddingTop
;
6952 if (_Style
.hasStyle("padding-right")) _Cells
.back()->PaddingRight
= _Style
.Current
.PaddingRight
;
6953 if (_Style
.hasStyle("padding-bottom")) _Cells
.back()->PaddingBottom
= _Style
.Current
.PaddingBottom
;
6954 if (_Style
.hasStyle("padding-left")) _Cells
.back()->PaddingLeft
= _Style
.Current
.PaddingLeft
;
6956 table
->addChild (_Cells
.back());
6958 // reusing indent pushed by table
6961 newParagraph(TDBeginSpace
);
6962 // indent is already 0, getParagraph()->setMarginLeft(0); // maybe setIndent(0) if LI is using one
6968 renderPseudoElement(":before", elm
);
6971 void CGroupHTML::htmlTDend(const CHtmlElement
&elm
)
6973 renderPseudoElement(":after", elm
);
6975 popIfNotEmpty(_CellParams
);
6976 if (!_Cells
.empty())
6977 _Cells
.back() = NULL
;
6980 // ***************************************************************************
6981 void CGroupHTML::htmlTEXTAREA(const CHtmlElement
&elm
)
6986 // read general property
6987 string templateName
;
6989 // Widget template name
6990 if (elm
.hasNonEmptyAttribute("z_input_tmpl"))
6991 templateName
= elm
.getAttribute("z_input_tmpl");
6993 // Get the string name
6994 _TextAreaName
.clear();
6997 _TextAreaContent
.clear();
6998 _TextAreaMaxLength
= 1024;
6999 if (elm
.hasNonEmptyAttribute("name"))
7000 _TextAreaName
= elm
.getAttribute("name");
7001 if (elm
.hasNonEmptyAttribute("rows"))
7002 fromString(elm
.getAttribute("rows"), _TextAreaRow
);
7003 if (elm
.hasNonEmptyAttribute("cols"))
7004 fromString(elm
.getAttribute("cols"), _TextAreaCols
);
7005 if (elm
.hasNonEmptyAttribute("maxlength"))
7006 fromString(elm
.getAttribute("maxlength"), _TextAreaMaxLength
);
7008 _TextAreaTemplate
= !templateName
.empty() ? templateName
: DefaultFormTextAreaGroup
;
7010 _PRE
.push_back(true);
7013 void CGroupHTML::htmlTEXTAREAend(const CHtmlElement
&elm
)
7018 CInterfaceGroup
*textArea
= addTextArea (_TextAreaTemplate
, _TextAreaName
.c_str (), _TextAreaRow
, _TextAreaCols
, true, _TextAreaContent
, _TextAreaMaxLength
);
7021 // Add the text area to the form
7022 CGroupHTML::CForm::CEntry entry
;
7023 entry
.Name
= _TextAreaName
;
7024 entry
.TextArea
= textArea
;
7025 _Forms
.back().Entries
.push_back (entry
);
7029 popIfNotEmpty (_PRE
);
7032 // ***************************************************************************
7033 void CGroupHTML::htmlTH(const CHtmlElement
&elm
)
7038 void CGroupHTML::htmlTHend(const CHtmlElement
&elm
)
7043 // ***************************************************************************
7044 void CGroupHTML::htmlTITLE(const CHtmlElement
&elm
)
7046 // TODO: only from <head>
7047 // if (!_ReadingHeadTag) return;
7048 if(!_TitlePrefix
.empty())
7049 _TitleString
= _TitlePrefix
+ " - ";
7051 _TitleString
.clear();
7055 void CGroupHTML::htmlTITLEend(const CHtmlElement
&elm
)
7058 setTitle(_TitleString
);
7061 // ***************************************************************************
7062 void CGroupHTML::htmlTR(const CHtmlElement
&elm
)
7064 // prevent inheriting from table
7065 if (!_CellParams
.empty())
7067 _CellParams
.back().BgColor
= CRGBA::Transparent
;
7068 _CellParams
.back().Height
= 0;
7071 // Get cells parameters
7072 getCellsParameters(elm
, true);
7074 // TODO: this probably ends up in first cell
7075 renderPseudoElement(":before", elm
);
7082 void CGroupHTML::htmlTRend(const CHtmlElement
&elm
)
7084 // TODO: this probably ends up in last cell
7085 renderPseudoElement(":after", elm
);
7087 popIfNotEmpty(_CellParams
);
7090 // ***************************************************************************
7091 void CGroupHTML::htmlUL(const CHtmlElement
&elm
)
7094 _UL
.push_back(HTMLOListElement(1, "disc"));
7095 else if (_UL
.size() == 1)
7096 _UL
.push_back(HTMLOListElement(1, "circle"));
7098 _UL
.push_back(HTMLOListElement(1, "square"));
7100 // if LI is already present
7101 _LI
= _UL
.size() > 1 || _DL
.size() > 1;
7102 _Indent
.push_back(getIndent() + ULIndent
);
7104 renderPseudoElement(":before", elm
);
7107 void CGroupHTML::htmlULend(const CHtmlElement
&elm
)
7112 renderPseudoElement(":after", elm
);
7115 popIfNotEmpty(_Indent
);