1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2021 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2019-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "nel/gui/group_html.h"
27 #include "nel/misc/types_nl.h"
28 #include "nel/misc/rgba.h"
29 #include "nel/misc/algo.h"
30 #include "nel/misc/utf_string_view.h"
31 #include "nel/gui/libwww.h"
32 #include "nel/gui/group_html.h"
33 #include "nel/gui/group_list.h"
34 #include "nel/gui/group_menu.h"
35 #include "nel/gui/group_container.h"
36 #include "nel/gui/view_link.h"
37 #include "nel/gui/ctrl_scroll.h"
38 #include "nel/gui/ctrl_button.h"
39 #include "nel/gui/ctrl_text_button.h"
40 #include "nel/gui/action_handler.h"
41 #include "nel/gui/group_paragraph.h"
42 #include "nel/gui/group_editbox.h"
43 #include "nel/gui/widget_manager.h"
44 #include "nel/gui/lua_manager.h"
45 #include "nel/gui/view_bitmap.h"
46 #include "nel/gui/dbgroup_combo_box.h"
47 #include "nel/gui/lua_ihm.h"
48 #include "nel/misc/i18n.h"
49 #include "nel/misc/md5.h"
50 #include "nel/3d/texture_file.h"
51 #include "nel/misc/big_file.h"
52 #include "nel/gui/url_parser.h"
53 #include "nel/gui/http_cache.h"
54 #include "nel/gui/http_hsts.h"
55 #include "nel/web/curl_certificates.h"
56 #include "nel/gui/html_parser.h"
57 #include "nel/gui/html_element.h"
58 #include "nel/gui/css_style.h"
59 #include "nel/gui/css_parser.h"
60 #include "nel/gui/css_border_renderer.h"
61 #include "nel/gui/css_background_renderer.h"
63 #include <curl/curl.h>
66 using namespace NLMISC
;
72 // Default maximum time the request is allowed to take
73 #define DEFAULT_RYZOM_CONNECTION_TIMEOUT (300.0)
74 // Allow up to 10 redirects, then give up
75 #define DEFAULT_RYZOM_REDIRECT_LIMIT (10)
77 #define FONT_WEIGHT_NORMAL 400
78 #define FONT_WEIGHT_BOLD 700
83 // Uncomment nlwarning() to see the log about curl downloads
84 #define LOG_DL(fmt, ...) //nlwarning(fmt, ## __VA_ARGS__)
85 // Uncomment to log curl progess
86 //#define LOG_CURL_PROGRESS 1
88 CGroupHTML::SWebOptions
CGroupHTML::options
;
90 // Return URL with https is host is in HSTS list
91 static std::string
upgradeInsecureUrl(const std::string
&url
)
93 if (toLowerAscii(url
.substr(0, 7)) != "http://") {
98 if (!CStrictTransportSecurity::getInstance()->isSecureHost(uri
.host
)){
102 LOG_DL("HSTS url : '%s', using https", url
.c_str());
103 uri
.scheme
= "https";
105 return uri
.toString();
108 // Active cURL www transfer
112 CCurlWWWData(CURL
*curl
, const std::string
&url
)
113 : Request(curl
), Url(url
), Content(""), HeadersSent(NULL
)
119 curl_easy_cleanup(Request
);
122 curl_slist_free_all(HeadersSent
);
125 void sendHeaders(const std::vector
<std::string
> headers
)
127 for(uint i
= 0; i
< headers
.size(); ++i
)
129 HeadersSent
= curl_slist_append(HeadersSent
, headers
[i
].c_str());
131 curl_easy_setopt(Request
, CURLOPT_HTTPHEADER
, HeadersSent
);
134 void setRecvHeader(const std::string
&header
)
136 size_t pos
= header
.find(": ");
137 if (pos
== std::string::npos
)
140 std::string key
= toLowerAscii(header
.substr(0, pos
));
141 if (pos
!= std::string::npos
)
143 HeadersRecv
[key
] = header
.substr(pos
+ 2);
144 //nlinfo(">> received header '%s' = '%s'", key.c_str(), HeadersRecv[key].c_str());
148 // return last received "Location: <url>" header or empty string if no header set
149 const std::string
getLocationHeader()
151 if (HeadersRecv
.count("location") > 0)
152 return HeadersRecv
["location"];
157 const uint32
getExpires()
160 if (HeadersRecv
.count("expires") > 0)
161 ret
= curl_getdate(HeadersRecv
["expires"].c_str(), NULL
);
163 return ret
> -1 ? ret
: 0;
166 const std::string
getLastModified()
168 if (HeadersRecv
.count("last-modified") > 0)
170 return HeadersRecv
["last-modified"];
176 const std::string
getEtag()
178 if (HeadersRecv
.count("etag") > 0)
180 return HeadersRecv
["etag"];
188 // ignore header if not secure connection
189 if (toLowerAscii(Url
.substr(0, 8)) != "https://")
194 return HeadersRecv
.count("strict-transport-security") > 0;
197 const std::string
getHSTSHeader()
201 return HeadersRecv
["strict-transport-security"];
214 // headers sent with curl request, must be released after transfer
215 curl_slist
* HeadersSent
;
217 // headers received from curl transfer
218 std::map
<std::string
, std::string
> HeadersRecv
;
221 // cURL transfer callbacks
222 // ***************************************************************************
223 static size_t curlHeaderCallback(char *buffer
, size_t size
, size_t nmemb
, void *pCCurlWWWData
)
225 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
229 header
.append(buffer
, size
* nmemb
);
230 me
->setRecvHeader(header
.substr(0, header
.find_first_of("\n\r")));
236 // ***************************************************************************
237 static size_t curlDataCallback(char *buffer
, size_t size
, size_t nmemb
, void *pCCurlWWWData
)
239 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
241 me
->Content
.append(buffer
, size
* nmemb
);
246 // ***************************************************************************
247 static size_t curlProgressCallback(void *pCCurlWWWData
, curl_off_t dltotal
, curl_off_t dlnow
, curl_off_t ultotal
, curl_off_t ulnow
)
249 CCurlWWWData
* me
= static_cast<CCurlWWWData
*>(pCCurlWWWData
);
252 if (dltotal
> 0 || dlnow
> 0 || ultotal
> 0 || ulnow
> 0)
254 #ifdef LOG_CURL_PROGRESS
255 nlwarning("> dltotal %ld, dlnow %ld, ultotal %ld, ulnow %ld, url '%s'", dltotal
, dlnow
, ultotal
, ulnow
, me
->Url
.c_str());
260 // return 1 to cancel download
264 CGroupHTML::CDataDownload::~CDataDownload()
270 void CGroupHTML::StylesheetDownloadCB::finish()
272 if (CFile::fileExists(tmpdest
))
274 if (CFile::fileExists(dest
))
276 CFile::deleteFile(dest
);
278 CFile::moveFile(dest
, tmpdest
);
280 Parent
->cssDownloadFinished(url
, dest
);
283 void CGroupHTML::ImageDownloadCB::addImage(CViewBase
*img
, const CStyleParams
&style
, TImageType type
)
285 Images
.push_back(SImageInfo(img
, style
, type
));
288 void CGroupHTML::ImageDownloadCB::removeImage(CViewBase
*img
)
290 for(std::vector
<SImageInfo
>::iterator it
= Images
.begin(); it
!= Images
.end(); ++it
)
292 if (it
->Image
== img
)
300 void CGroupHTML::ImageDownloadCB::finish()
302 // tmpdest file does not exist if download skipped (ie cache was used)
303 if (CFile::fileExists(tmpdest
) || CFile::getFileSize(tmpdest
) == 0)
306 // verify that image is not corrupted
308 CBitmap::loadSize(tmpdest
, w
, h
);
309 if (w
!= 0 && h
!= 0)
311 if (CFile::fileExists(dest
))
312 CFile::deleteFile(dest
);
315 catch(const NLMISC::Exception
&e
)
317 // exception message has .tmp file name, so keep it for further analysis
318 nlwarning("Invalid image (%s) from url (%s): %s", tmpdest
.c_str(), url
.c_str(), e
.what());
321 // to reload image on page, the easiest seems to be changing texture
322 // to temp file temporarily. that forces driver to reload texture from disk
323 // ITexture::touch() seem not to do this.
324 // cache was updated, first set texture as temp file
325 for(std::vector
<SImageInfo
>::iterator it
= Images
.begin(); it
!= Images
.end(); ++it
)
327 SImageInfo
&img
= *it
;
328 Parent
->setImage(img
.Image
, tmpdest
, img
.Type
);
329 Parent
->setImageSize(img
.Image
, img
.Style
);
332 CFile::moveFile(dest
, tmpdest
);
335 if (!CFile::fileExists(dest
) || CFile::getFileSize(dest
) == 0)
337 // placeholder if cached image failed
338 dest
= "web_del.tga";
341 // even if image was cached, incase there was 'http://' image set to CViewBitmap
342 for(std::vector
<SImageInfo
>::iterator it
= Images
.begin(); it
!= Images
.end(); ++it
)
344 SImageInfo
&img
= *it
;
345 Parent
->setImage(img
.Image
, dest
, img
.Type
);
346 Parent
->setImageSize(img
.Image
, img
.Style
);
350 void CGroupHTML::TextureDownloadCB::finish()
352 // tmpdest file does not exist if download skipped (ie cache was used)
353 if (CFile::fileExists(tmpdest
) && CFile::getFileSize(tmpdest
) > 0)
355 if (CFile::fileExists(dest
))
356 CFile::deleteFile(dest
);
358 CFile::moveFile(dest
, tmpdest
);
361 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
362 for(uint i
= 0; i
< TextureIds
.size(); i
++)
364 rVR
.reloadTexture(TextureIds
[i
].first
, dest
);
365 TextureIds
[i
].second
->invalidateCoords();
369 void CGroupHTML::BnpDownloadCB::finish()
371 bool verified
= false;
372 // no tmpfile if file was already in cache
373 if (CFile::fileExists(tmpdest
))
375 verified
= m_md5sum
.empty() || (m_md5sum
!= getMD5(tmpdest
).toString());
378 if (CFile::fileExists(dest
))
380 CFile::deleteFile(dest
);
382 CFile::moveFile(dest
, tmpdest
);
386 CFile::deleteFile(tmpdest
);
389 else if (CFile::fileExists(dest
))
391 verified
= m_md5sum
.empty() || (m_md5sum
!= getMD5(dest
).toString());
396 std::string script
= "\nlocal __CURRENT_WINDOW__ = \""+Parent
->getId()+"\"";
397 script
+= toString("\nlocal __DOWNLOAD_STATUS__ = %s\n", verified
? "true" : "false");
399 CLuaManager::getInstance().executeLuaScript(script
, true );
403 // Check if domain is on TrustedDomain
404 bool CGroupHTML::isTrustedDomain(const string
&domain
)
406 vector
<string
>::iterator it
;
407 it
= find ( options
.trustedDomains
.begin(), options
.trustedDomains
.end(), domain
);
408 return it
!= options
.trustedDomains
.end();
411 // Update view after download has finished
412 void CGroupHTML::setImage(CViewBase
* view
, const string
&file
, const TImageType type
)
414 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
417 if (type
== NormalImage
)
419 btn
->setTexture (file
);
420 btn
->setTexturePushed(file
);
421 btn
->invalidateCoords();
422 btn
->invalidateContent();
427 btn
->setTextureOver(file
);
433 CViewBitmap
*btm
= dynamic_cast<CViewBitmap
*>(view
);
436 btm
->setTexture (file
);
437 btm
->invalidateCoords();
438 btm
->invalidateContent();
444 CGroupCell
*btgc
= dynamic_cast<CGroupCell
*>(view
);
447 btgc
->setTexture (file
);
448 btgc
->invalidateCoords();
449 btgc
->invalidateContent();
455 CGroupTable
*table
= dynamic_cast<CGroupTable
*>(view
);
458 table
->setTexture(file
);
464 // Force image width, height
465 void CGroupHTML::setImageSize(CViewBase
*view
, const CStyleParams
&style
)
467 sint32 width
= style
.Width
;
468 sint32 height
= style
.Height
;
469 sint32 maxw
= style
.MaxWidth
;
470 sint32 maxh
= style
.MaxHeight
;
472 sint32 imageWidth
, imageHeight
;
475 // get image texture size
476 // if image is being downloaded, then correct size is set after thats done
477 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
481 imageWidth
= btn
->getW(false);
482 imageHeight
= btn
->getH(false);
486 CViewBitmap
*btm
= dynamic_cast<CViewBitmap
*>(view
);
490 imageWidth
= btm
->getW(false);
491 imageHeight
= btm
->getH(false);
500 // if width/height is not requested, then use image size
501 // else recalculate missing value, keep image ratio
502 if (width
== -1 && height
== -1)
505 height
= imageHeight
;
510 if (width
== -1 || height
== -1) {
511 float ratio
= (float) imageWidth
/ std::max(1, imageHeight
);
513 width
= height
* ratio
;
515 height
= width
/ ratio
;
518 // apply max-width, max-height rules if asked
519 if (maxw
> -1 || maxh
> -1)
521 _Style
.applyCssMinMax(width
, height
, 0, 0, maxw
, maxh
);
527 CCtrlButton
*btn
= dynamic_cast<CCtrlButton
*>(view
);
536 CViewBitmap
*image
= dynamic_cast<CViewBitmap
*>(view
);
539 image
->setScale(true);
547 void CGroupHTML::setTextButtonStyle(CCtrlTextButton
*ctrlButton
, const CStyleParams
&style
)
549 // this will also set size for <a class="ryzom-ui-button"> treating it like "display: inline-block;"
550 if (style
.Width
> 0) ctrlButton
->setWMin(style
.Width
);
551 if (style
.Height
> 0) ctrlButton
->setHMin(style
.Height
);
553 CViewText
*pVT
= ctrlButton
->getViewText();
556 setTextStyle(pVT
, style
);
559 if (style
.hasStyle("background-color"))
561 ctrlButton
->setColor(style
.Background
.color
);
562 if (style
.hasStyle("-ryzom-background-color-over"))
564 ctrlButton
->setColorOver(style
.BackgroundColorOver
);
568 ctrlButton
->setColorOver(style
.Background
.color
);
570 ctrlButton
->setTexture("", "blank.tga", "", false);
571 ctrlButton
->setTextureOver("", "blank.tga", "");
572 ctrlButton
->setProperty("force_text_over", "true");
574 else if (style
.hasStyle("-ryzom-background-color-over"))
576 ctrlButton
->setColorOver(style
.BackgroundColorOver
);
577 ctrlButton
->setProperty("force_text_over", "true");
578 ctrlButton
->setTextureOver("blank.tga", "blank.tga", "blank.tga");
582 void CGroupHTML::setTextStyle(CViewText
*pVT
, const CStyleParams
&style
)
586 pVT
->setColor(style
.TextColor
);
587 pVT
->setFontName(style
.FontFamily
);
588 pVT
->setFontSize(style
.FontSize
, false);
589 pVT
->setEmbolden(style
.FontWeight
>= FONT_WEIGHT_BOLD
);
590 pVT
->setOblique(style
.FontOblique
);
591 pVT
->setUnderlined(style
.Underlined
);
592 pVT
->setStrikeThrough(style
.StrikeThrough
);
593 if (style
.TextShadow
.Enabled
)
595 pVT
->setShadow(true);
596 pVT
->setShadowColor(style
.TextShadow
.Color
);
597 pVT
->setShadowOutline(style
.TextShadow
.Outline
);
598 pVT
->setShadowOffset(style
.TextShadow
.X
, style
.TextShadow
.Y
);
603 // Get an url and return the local filename with the path where the url image should be
604 string
CGroupHTML::localImageName(const string
&url
)
606 string dest
= "cache/";
607 dest
+= getMD5((uint8
*)url
.c_str(), (uint32
)url
.size()).toString();
612 void CGroupHTML::pumpCurlQueue()
614 if (RunningCurls
< options
.curlMaxConnections
)
616 std::list
<CDataDownload
*>::iterator it
=Curls
.begin();
617 while(it
!= Curls
.end() && RunningCurls
< options
.curlMaxConnections
)
619 if ((*it
)->data
== NULL
)
621 LOG_DL("(%s) starting new download '%s'", _Id
.c_str(), it
->url
.c_str());
622 if (!startCurlDownload(*it
))
624 LOG_DL("(%s) failed to start '%s)'", _Id
.c_str(), it
->url
.c_str());
625 finishCurlDownload(*it
);
627 it
= Curls
.erase(it
);
636 if (RunningCurls
> 0 || !Curls
.empty())
637 LOG_DL("(%s) RunningCurls %d, _Curls %d", _Id
.c_str(), RunningCurls
, Curls
.size());
640 // Add url to MultiCurl queue and return cURL handle
641 bool CGroupHTML::startCurlDownload(CDataDownload
*download
)
645 nlwarning("Invalid MultiCurl handle, unable to download '%s'", download
->url
.c_str());
652 CHttpCacheObject cache
;
653 if (CFile::fileExists(download
->dest
))
654 cache
= CHttpCache::getInstance()->lookup(download
->dest
);
656 if (cache
.Expires
> currentTime
)
658 LOG_DL("Cache for (%s) is not expired (%s, expires:%d)", download
->url
.c_str(), download
->dest
.c_str(), cache
.Expires
- currentTime
);
662 // use browser Id so that two browsers would not use same temp file
663 download
->tmpdest
= localImageName(_Id
+ download
->dest
) + ".tmp";
665 // erase the tmp file if exists
666 if (CFile::fileExists(download
->tmpdest
))
668 CFile::deleteFile(download
->tmpdest
);
671 FILE *fp
= nlfopen (download
->tmpdest
, "wb");
674 nlwarning("Can't open file '%s' for writing: code=%d '%s'", download
->tmpdest
.c_str (), errno
, strerror(errno
));
678 CURL
*curl
= curl_easy_init();
682 CFile::deleteFile(download
->tmpdest
);
684 nlwarning("Creating cURL handle failed, unable to download '%s'", download
->url
.c_str());
687 LOG_DL("curl easy handle %p created for '%s'", curl
, download
->url
.c_str());
690 if (toLowerAscii(download
->url
.substr(0, 8)) == "https://")
692 // if supported, use custom SSL context function to load certificates
693 NLWEB::CCurlCertificates::useCertificates(curl
);
696 download
->data
= new CCurlWWWData(curl
, download
->url
);
699 // initial connection timeout, curl default is 300sec
700 curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, download
->ConnectionTimeout
);
702 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, true);
703 curl_easy_setopt(curl
, CURLOPT_URL
, download
->url
.c_str());
705 // limit curl to HTTP and HTTPS protocols only
706 curl_easy_setopt(curl
, CURLOPT_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
707 curl_easy_setopt(curl
, CURLOPT_REDIR_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
709 std::vector
<std::string
> headers
;
710 if (!cache
.Etag
.empty())
711 headers
.push_back("If-None-Match: " + cache
.Etag
);
713 if (!cache
.LastModified
.empty())
714 headers
.push_back("If-Modified-Since: " + cache
.LastModified
);
716 if (headers
.size() > 0)
717 download
->data
->sendHeaders(headers
);
720 curl_easy_setopt(curl
, CURLOPT_HEADERFUNCTION
, NLGUI::curlHeaderCallback
);
721 curl_easy_setopt(curl
, CURLOPT_WRITEHEADER
, download
->data
);
723 std::string userAgent
= options
.appName
+ "/" + options
.appVersion
;
724 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, userAgent
.c_str());
726 CUrlParser
uri(download
->url
);
727 if (!uri
.host
.empty())
728 sendCookies(curl
, uri
.host
, isTrustedDomain(uri
.host
));
730 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, fp
);
731 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
733 CURLMcode ret
= curl_multi_add_handle(MultiCurl
, curl
);
736 nlwarning("cURL multi handle %p error %d on '%s'", curl
, ret
, download
->url
.c_str());
744 void CGroupHTML::finishCurlDownload(CDataDownload
*download
)
753 nlwarning("Unknown CURL download (nullptr)");
757 // Add a image download request in the multi_curl
758 // return new textureId and download callback
759 ICurlDownloadCB
*CGroupHTML::addTextureDownload(const string
&url
, sint32
&texId
, CViewBase
*view
)
761 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
762 // data:image/png;base64,AA...==
763 if (startsWith(url
, "data:image/"))
765 texId
= rVR
.createTextureFromDataURL(url
);
769 std::string finalUrl
;
770 // load the image from local files/bnp
771 if (lookupLocalFile(finalUrl
, std::string(CFile::getPath(url
) + CFile::getFilenameWithoutExtension(url
) + ".tga").c_str(), false))
773 texId
= rVR
.createTexture(finalUrl
);
777 finalUrl
= upgradeInsecureUrl(getAbsoluteUrl(url
));
779 // use requested url for local name (cache)
780 string dest
= localImageName(url
);
781 LOG_DL("add to download '%s' dest '%s'", finalUrl
.c_str(), dest
.c_str());
783 if (CFile::fileExists(dest
) && CFile::getFileSize(dest
) > 0)
784 texId
= rVR
.createTexture(dest
);
786 texId
= rVR
.newTextureId(dest
);
788 // Search if we are not already downloading this url.
789 for(std::list
<CDataDownload
*>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
791 if((*it
)->url
== finalUrl
)
793 LOG_DL("already downloading '%s' img %p", finalUrl
.c_str(), img
);
794 TextureDownloadCB
*cb
= dynamic_cast<TextureDownloadCB
*>(*it
);
797 cb
->addTexture(texId
, view
);
798 // return pointer to shared ImageDownloadCB
803 nlwarning("Found texture download '%s', but casting to TextureDownloadCB failed", finalUrl
.c_str());
808 Curls
.push_back(new TextureDownloadCB(finalUrl
, dest
, texId
, this));
809 // as we return pointer to callback, skip starting downloads just now
814 // Add a image download request in the multi_curl
815 ICurlDownloadCB
*CGroupHTML::addImageDownload(const string
&url
, CViewBase
*img
, const CStyleParams
&style
, TImageType type
, const std::string
&placeholder
)
817 std::string finalUrl
;
818 img
->setModulateGlobalColor(style
.GlobalColor
);
820 // data:image/png;base64,AA...==
821 if (startsWith(url
, "data:image/"))
823 setImage(img
, decodeURIComponent(url
), type
);
824 setImageSize(img
, style
);
828 // load the image from local files/bnp
829 std::string image
= CFile::getPath(url
) + CFile::getFilenameWithoutExtension(url
) + ".tga";
830 if (lookupLocalFile(finalUrl
, image
.c_str(), false))
832 setImage(img
, image
, type
);
833 setImageSize(img
, style
);
837 finalUrl
= upgradeInsecureUrl(getAbsoluteUrl(url
));
839 // use requested url for local name (cache)
840 string dest
= localImageName(url
);
841 LOG_DL("add to download '%s' dest '%s' img %p", finalUrl
.c_str(), dest
.c_str(), img
);
843 // Display cached image while downloading new
844 if (type
!= OverImage
)
846 std::string temp
= dest
;
847 if (!CFile::fileExists(temp
) || CFile::getFileSize(temp
) == 0)
851 setImage(img
, temp
, type
);
852 setImageSize(img
, style
);
855 // Search if we are not already downloading this url.
856 for(std::list
<CDataDownload
*>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
858 if((*it
)->url
== finalUrl
)
860 LOG_DL("already downloading '%s' img %p", finalUrl
.c_str(), img
);
861 ImageDownloadCB
*cb
= dynamic_cast<ImageDownloadCB
*>(*it
);
864 cb
->addImage(img
, style
, type
);
865 // return pointer to shared ImageDownloadCB
870 nlwarning("Found image download '%s', but casting to ImageDownloadCB failed", finalUrl
.c_str());
875 Curls
.push_back(new ImageDownloadCB(finalUrl
, dest
, img
, style
, type
, this));
876 // as we return pointer to callback, skip starting downloads just now
881 void CGroupHTML::removeImageDownload(ICurlDownloadCB
*handle
, CViewBase
*img
)
883 ImageDownloadCB
*cb
= dynamic_cast<ImageDownloadCB
*>(handle
);
885 nlwarning("Trying to remove image from downloads, but ICurlDownloadCB pointer did not cast to ImageDownloadCB");
888 // image will be removed from handle, but handle is kept and image will be downloaded
889 cb
->removeImage(img
);
892 void CGroupHTML::initImageDownload()
894 LOG_DL("Init Image Download");
896 string pathName
= "cache";
897 if ( ! CFile::isExists( pathName
) )
898 CFile::createDirectory( pathName
);
902 // Get an url and return the local filename with the path where the bnp should be
903 string
CGroupHTML::localBnpName(const string
&url
)
905 size_t lastIndex
= url
.find_last_of("/");
906 string dest
= "user/"+url
.substr(lastIndex
+1);
910 // Add a bnp download request in the multi_curl, return true if already downloaded
911 bool CGroupHTML::addBnpDownload(string url
, const string
&action
, const string
&script
, const string
&md5sum
)
913 url
= upgradeInsecureUrl(getAbsoluteUrl(url
));
915 // Search if we are not already downloading this url.
916 for(std::list
<CDataDownload
*>::const_iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
918 if((*it
)->url
== url
)
920 LOG_DL("already downloading '%s'", url
.c_str());
925 string dest
= localBnpName(url
);
926 LOG_DL("add to download '%s' dest '%s'", url
.c_str(), dest
.c_str());
928 // create/delete the local file
929 if (NLMISC::CFile::fileExists(dest
))
931 if (action
== "override" || action
== "delete")
933 CFile::setRWAccess(dest
);
934 NLMISC::CFile::deleteFile(dest
);
941 if (action
!= "delete")
943 Curls
.push_back(new BnpDownloadCB(url
, dest
, md5sum
, script
, this));
952 void CGroupHTML::initBnpDownload()
957 LOG_DL("Init Bnp Download");
958 string pathName
= "user";
959 if ( ! CFile::isExists( pathName
) )
960 CFile::createDirectory( pathName
);
963 void CGroupHTML::addStylesheetDownload(const std::vector
<CHtmlParser::StyleLink
> links
)
965 for(uint i
= 0; i
< links
.size(); ++i
)
967 _StylesheetQueue
.push_back(links
[i
]);
968 std::string url
= getAbsoluteUrl(links
[i
].Url
);
969 _StylesheetQueue
.back().Url
= url
;
971 // push to the front of the queue
972 Curls
.push_front(new StylesheetDownloadCB(url
, localImageName(url
), this));
977 // Call this evenly to check if an element is downloaded and then manage it
978 void CGroupHTML::checkDownloads()
980 //nlassert(_CrtCheckMemory());
982 if(Curls
.empty() && _CurlWWW
== NULL
)
987 int NewRunningCurls
= 0;
988 while(CURLM_CALL_MULTI_PERFORM
== curl_multi_perform(MultiCurl
, &NewRunningCurls
))
990 LOG_DL("more to do now %d - %d curls", NewRunningCurls
, Curls
.size());
993 LOG_DL("NewRunningCurls:%d, RunningCurls:%d", NewRunningCurls
, RunningCurls
);
995 // check which downloads are done
998 while ((msg
= curl_multi_info_read(MultiCurl
, &msgs_left
)))
1000 LOG_DL("> (%s) msgs_left %d", _Id
.c_str(), msgs_left
);
1001 if (msg
->msg
== CURLMSG_DONE
)
1003 if (_CurlWWW
&& _CurlWWW
->Request
&& _CurlWWW
->Request
== msg
->easy_handle
)
1006 bool success
= msg
->data
.result
== CURLE_OK
;
1009 error
= curl_easy_strerror(msg
->data
.result
);
1011 LOG_DL("html download finished with curl code %d (%s)", (sint
)msg
->msg
, error
.c_str());
1012 htmlDownloadFinished(success
, error
);
1016 for(std::list
<CDataDownload
*>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
1018 if((*it
)->data
&& (*it
)->data
->Request
== msg
->easy_handle
)
1021 bool success
= msg
->data
.result
== CURLE_OK
;
1024 error
= curl_easy_strerror(msg
->data
.result
);
1026 LOG_DL("data download finished with curl code %d (%s)", (sint
)msg
->msg
, error
.c_str());
1027 dataDownloadFinished(success
, error
, *it
);
1037 RunningCurls
= NewRunningCurls
;
1042 void CGroupHTML::releaseDownloads()
1044 LOG_DL("Release Downloads");
1048 LOG_DL("(%s) stop html url '%s'", _Id
.c_str(), _CurlWWW
->Url
.c_str());
1050 curl_multi_remove_handle(MultiCurl
, _CurlWWW
->Request
);
1056 // remove all queued and already started downloads
1057 for(std::list
<CDataDownload
*>::iterator it
= Curls
.begin(); it
!= Curls
.end(); ++it
)
1059 CDataDownload
&dl
= *(*it
);
1062 LOG_DL("(%s) stop data url '%s'", _Id
.c_str(), dl
.url
.c_str());
1065 curl_multi_remove_handle(MultiCurl
, dl
.data
->Request
);
1068 // close and remove temp file
1073 if (CFile::fileExists(dl
.tmpdest
))
1075 CFile::deleteFile(dl
.tmpdest
);
1079 // release CDataDownload
1084 // also clear css queue as it depends on Curls
1085 _StylesheetQueue
.clear();
1088 class CGroupListAdaptor
: public CInterfaceGroup
1091 CGroupListAdaptor(const TCtorParam
¶m
)
1092 : CInterfaceGroup(param
)
1100 // Get the W max from the parent
1101 _W
= std::min(_Parent
->getMaxWReal(), _Parent
->getWReal());
1104 CInterfaceGroup::updateCoords();
1108 // ***************************************************************************
1110 template<class A
> void popIfNotEmpty(A
&vect
) { if(!vect
.empty()) vect
.pop_back(); }
1112 // ***************************************************************************
1113 TStyle
CGroupHTML::parseStyle (const string
&str_styles
)
1116 vector
<string
> elements
;
1117 NLMISC::splitString(str_styles
, ";", elements
);
1119 for(uint i
= 0; i
< elements
.size(); ++i
)
1121 vector
<string
> style
;
1122 NLMISC::splitString(elements
[i
], ":", style
);
1123 if (style
.size() >= 2)
1125 string fullstyle
= style
[1];
1126 for (uint j
=2; j
< style
.size(); j
++)
1127 fullstyle
+= ":"+style
[j
];
1128 styles
[trim(style
[0])] = trimSeparators(fullstyle
);
1135 // ***************************************************************************
1137 void CGroupHTML::addText (const char *buf
, int len
)
1144 // Build a UTF8 string
1145 if (_ParsingLua
&& _TrustedDomain
)
1147 // we are parsing a lua script
1148 _LuaScript
+= string(buf
, buf
+ len
);
1153 // Build a unicode string
1154 CUtfStringView
inputStringView(buf
, len
);
1156 // Build the final unicode string
1159 u32char lastChar
= 0;
1160 u32char inputStringView0
= *inputStringView
.begin();
1161 for (CUtfStringView::iterator
it(inputStringView
.begin()), end(inputStringView
.end()); it
!= end
; ++it
)
1165 // special treatment for 'nbsp' (which is returned as a discreet space)
1166 if (len
== 1 && inputStringView0
== 32)
1168 // this is a nbsp entity
1174 // not nbsp, use normal white space removal routine
1175 keep
= translateChar (output
, *it
, lastChar
);
1180 CUtfStringView::append(tmp
, output
);
1190 // ***************************************************************************
1192 #define registerAnchorName(prefix) \
1194 if (present[prefix##_ID] && value[prefix##_ID]) \
1195 _AnchorName.push_back(value[prefix##_ID]); \
1198 // ***************************************************************************
1199 void CGroupHTML::beginElement (CHtmlElement
&elm
)
1202 _CurrentHTMLElement
= &elm
;
1203 _CurrentHTMLNextSibling
= elm
.nextSibling
;
1205 // set element style from css and style attribute
1206 _Style
.getStyleFor(elm
);
1207 if (!elm
.Style
.empty())
1209 _Style
.applyStyle(elm
.Style
);
1212 if (elm
.hasNonEmptyAttribute("name"))
1214 _AnchorName
.push_back(elm
.getAttribute("name"));
1216 if (elm
.hasNonEmptyAttribute("id"))
1218 _AnchorName
.push_back(elm
.getAttribute("id"));
1221 if (_Style
.Current
.DisplayBlock
)
1228 case HTML_A
: htmlA(elm
); break;
1229 case HTML_BASE
: htmlBASE(elm
); break;
1230 case HTML_BODY
: htmlBODY(elm
); break;
1231 case HTML_BR
: htmlBR(elm
); break;
1232 case HTML_BUTTON
: htmlBUTTON(elm
); break;
1233 case HTML_DD
: htmlDD(elm
); break;
1234 case HTML_DEL
: renderPseudoElement(":before", elm
); break;
1235 case HTML_DIV
: htmlDIV(elm
); break;
1236 case HTML_DL
: htmlDL(elm
); break;
1237 case HTML_DT
: htmlDT(elm
); break;
1238 case HTML_EM
: renderPseudoElement(":before", elm
); break;
1239 case HTML_FONT
: htmlFONT(elm
); break;
1240 case HTML_FORM
: htmlFORM(elm
); break;
1241 case HTML_H1
://no-break
1242 case HTML_H2
://no-break
1243 case HTML_H3
://no-break
1244 case HTML_H4
://no-break
1245 case HTML_H5
://no-break
1246 case HTML_H6
: htmlH(elm
); break;
1247 case HTML_HEAD
: htmlHEAD(elm
); break;
1248 case HTML_HR
: htmlHR(elm
); break;
1249 case HTML_HTML
: htmlHTML(elm
); break;
1250 case HTML_I
: htmlI(elm
); break;
1251 case HTML_IMG
: htmlIMG(elm
); break;
1252 case HTML_INPUT
: htmlINPUT(elm
); break;
1253 case HTML_LI
: htmlLI(elm
); break;
1254 case HTML_LUA
: htmlLUA(elm
); break;
1255 case HTML_META
: htmlMETA(elm
); break;
1256 case HTML_METER
: htmlMETER(elm
); break;
1257 case HTML_OBJECT
: htmlOBJECT(elm
); break;
1258 case HTML_OL
: htmlOL(elm
); break;
1259 case HTML_OPTION
: htmlOPTION(elm
); break;
1260 case HTML_P
: htmlP(elm
); break;
1261 case HTML_PRE
: htmlPRE(elm
); break;
1262 case HTML_PROGRESS
: htmlPROGRESS(elm
); break;
1263 case HTML_SCRIPT
: htmlSCRIPT(elm
); break;
1264 case HTML_SELECT
: htmlSELECT(elm
); break;
1265 case HTML_SMALL
: renderPseudoElement(":before", elm
); break;
1266 case HTML_SPAN
: renderPseudoElement(":before", elm
); break;
1267 case HTML_STRONG
: renderPseudoElement(":before", elm
); break;
1268 case HTML_STYLE
: htmlSTYLE(elm
); break;
1269 case HTML_TABLE
: htmlTABLE(elm
); break;
1270 case HTML_TBODY
: renderPseudoElement(":before", elm
); break;
1271 case HTML_TD
: htmlTD(elm
); break;
1272 case HTML_TEXTAREA
: htmlTEXTAREA(elm
); break;
1273 case HTML_TFOOT
: renderPseudoElement(":before", elm
); break;
1274 case HTML_TH
: htmlTH(elm
); break;
1275 case HTML_TITLE
: htmlTITLE(elm
); break;
1276 case HTML_TR
: htmlTR(elm
); break;
1277 case HTML_U
: renderPseudoElement(":before", elm
); break;
1278 case HTML_UL
: htmlUL(elm
); break;
1280 renderPseudoElement(":before", elm
);
1285 // ***************************************************************************
1286 void CGroupHTML::endElement(CHtmlElement
&elm
)
1288 _CurrentHTMLElement
= &elm
;
1292 case HTML_A
: htmlAend(elm
); break;
1293 case HTML_BASE
: break;
1294 case HTML_BODY
: renderPseudoElement(":after", elm
); break;
1295 case HTML_BR
: break;
1296 case HTML_BUTTON
: htmlBUTTONend(elm
); break;
1297 case HTML_DD
: htmlDDend(elm
); break;
1298 case HTML_DEL
: renderPseudoElement(":after", elm
); break;
1299 case HTML_DIV
: htmlDIVend(elm
); break;
1300 case HTML_DL
: htmlDLend(elm
); break;
1301 case HTML_DT
: htmlDTend(elm
); break;
1302 case HTML_EM
: renderPseudoElement(":after", elm
);break;
1303 case HTML_FONT
: break;
1304 case HTML_FORM
: htmlFORMend(elm
); break;
1305 case HTML_H1
://no-break
1306 case HTML_H2
://no-break
1307 case HTML_H3
://no-break
1308 case HTML_H4
://no-break
1309 case HTML_H5
://no-break
1310 case HTML_H6
: htmlHend(elm
); break;
1311 case HTML_HEAD
: htmlHEADend(elm
); break;
1312 case HTML_HR
: break;
1313 case HTML_HTML
: break;
1314 case HTML_I
: htmlIend(elm
); break;
1315 case HTML_IMG
: break;
1316 case HTML_INPUT
: break;
1317 case HTML_LI
: htmlLIend(elm
); break;
1318 case HTML_LUA
: htmlLUAend(elm
); break;
1319 case HTML_META
: break;
1320 case HTML_METER
: break;
1321 case HTML_OBJECT
: htmlOBJECTend(elm
); break;
1322 case HTML_OL
: htmlOLend(elm
); break;
1323 case HTML_OPTION
: htmlOPTIONend(elm
); break;
1324 case HTML_P
: htmlPend(elm
); break;
1325 case HTML_PRE
: htmlPREend(elm
); break;
1326 case HTML_SCRIPT
: htmlSCRIPTend(elm
); break;
1327 case HTML_SELECT
: htmlSELECTend(elm
); break;
1328 case HTML_SMALL
: renderPseudoElement(":after", elm
);break;
1329 case HTML_SPAN
: renderPseudoElement(":after", elm
);break;
1330 case HTML_STRONG
: renderPseudoElement(":after", elm
);break;
1331 case HTML_STYLE
: htmlSTYLEend(elm
); break;
1332 case HTML_TABLE
: htmlTABLEend(elm
); break;
1333 case HTML_TD
: htmlTDend(elm
); break;
1334 case HTML_TBODY
: renderPseudoElement(":after", elm
); break;
1335 case HTML_TEXTAREA
: break;
1336 case HTML_TFOOT
: renderPseudoElement(":after", elm
); break;
1337 case HTML_TH
: htmlTHend(elm
); break;
1338 case HTML_TITLE
: break;
1339 case HTML_TR
: htmlTRend(elm
); break;
1340 case HTML_U
: renderPseudoElement(":after", elm
); break;
1341 case HTML_UL
: htmlULend(elm
); break;
1343 renderPseudoElement(":after", elm
);
1347 if (_Style
.Current
.DisplayBlock
)
1355 // ***************************************************************************
1356 void CGroupHTML::renderPseudoElement(const std::string
&pseudo
, const CHtmlElement
&elm
)
1358 if (pseudo
!= ":before" && pseudo
!= ":after")
1361 if (!elm
.hasPseudo(pseudo
))
1365 _Style
.applyStyle(elm
.getPseudo(pseudo
));
1367 // TODO: 'content' should already be tokenized in css parser as it has all the functions for that
1368 std::string content
= trim(_Style
.getStyle("content"));
1369 if (toLowerAscii(content
) == "none" || toLowerAscii(content
) == "normal")
1375 std::string::size_type pos
= 0;
1376 // TODO: tokenize by whitespace
1377 while(pos
< content
.size())
1379 std::string::size_type start
;
1383 // counter, open-quote, close-quote, no-open-quote, no-close-quote
1384 if (content
[pos
] == '"' || content
[pos
] == '\'')
1386 char quote
= content
[pos
];
1389 while(pos
< content
.size() && content
[pos
] != quote
)
1391 if (content
[pos
] == '\\') pos
++;
1394 token
= content
.substr(start
, pos
- start
);
1397 // skip closing quote
1400 else if (content
[pos
] == 'u' && pos
< content
.size() - 6 && toLowerAscii(content
.substr(pos
, 4)) == "url(")
1402 // url(/path-to/image.jpg) / "Alt!"
1403 // url("/path to/image.jpg") / "Alt!"
1404 std::string tooltip
;
1407 // fails if url contains ')'
1408 pos
= content
.find(")", start
);
1409 if (pos
== std::string::npos
)
1412 token
= trim(content
.substr(start
, pos
- start
));
1418 while(pos
< content
.size() && content
[pos
] == ' ' && content
[pos
] != '/')
1422 if (pos
< content
.size() && content
[pos
] == '/')
1428 while(pos
< content
.size() && content
[pos
] == ' ')
1432 if (pos
< content
.size() && (content
[pos
] == '\'' || content
[pos
] == '"'))
1434 char openQuote
= content
[pos
];
1437 while(pos
< content
.size() && content
[pos
] != openQuote
)
1439 if (content
[pos
] == '\\') pos
++;
1442 tooltip
= content
.substr(start
, pos
- start
);
1444 // skip closing quote
1449 // tooltip should be quoted
1460 if (tooltip
.empty())
1462 addImage(getId() + pseudo
, token
, false, _Style
.Current
);
1466 tooltip
= trimQuotes(tooltip
);
1467 addButton(CCtrlButton::PushButton
, getId() + pseudo
, token
, token
, "", "", "", tooltip
.c_str(), _Style
.Current
);
1470 else if (content
[pos
] == 'a' && pos
< content
.size() - 7)
1474 std::string::size_type end
= 0;
1475 end
= content
.find(")", start
);
1476 if (end
!= std::string::npos
)
1478 token
= content
.substr(start
, end
- start
);
1481 if (elm
.hasAttribute(token
))
1483 addString(elm
.getAttribute(token
));
1501 // ***************************************************************************
1502 void CGroupHTML::renderDOM(CHtmlElement
&elm
)
1504 if (elm
.Type
== CHtmlElement::TEXT_NODE
)
1506 addText(elm
.Value
.c_str(), elm
.Value
.size());
1512 if (!_IgnoreChildElements
)
1514 std::list
<CHtmlElement
>::iterator it
= elm
.Children
.begin();
1515 while(it
!= elm
.Children
.end())
1522 _IgnoreChildElements
= false;
1528 // ***************************************************************************
1529 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupHTML
, std::string
, "html");
1532 // ***************************************************************************
1533 uint32
CGroupHTML::_GroupHtmlUIDPool
= 0;
1534 CGroupHTML::TGroupHtmlByUIDMap
CGroupHTML::_GroupHtmlByUID
;
1537 // ***************************************************************************
1538 CGroupHTML::CGroupHTML(const TCtorParam
¶m
)
1539 : CGroupScrollText(param
),
1540 _TimeoutValue(DEFAULT_RYZOM_CONNECTION_TIMEOUT
),
1541 _RedirectsRemaining(DEFAULT_RYZOM_REDIRECT_LIMIT
),
1542 _CurrentHTMLElement(NULL
)
1544 // add it to map of group html created
1545 _GroupHtmlUID
= ++_GroupHtmlUIDPool
; // valid assigned Id begin to 1!
1546 _GroupHtmlByUID
[_GroupHtmlUID
]= this;
1549 _TrustedDomain
= false;
1550 _ParsingLua
= false;
1551 _LuaHrefHack
= false;
1552 _IgnoreText
= false;
1553 _IgnoreChildElements
= false;
1554 _BrowseNextTime
= false;
1555 _PostNextTime
= false;
1557 _CurrentViewLink
= NULL
;
1558 _CurrentViewImage
= NULL
;
1561 _SelectOption
= false;
1562 _GroupListAdaptor
= NULL
;
1563 _UrlFragment
.clear();
1564 _RefreshUrl
.clear();
1565 _NextRefreshTime
= 0.0;
1566 _LastRefreshTime
= 0.0;
1567 _RenderNextTime
= false;
1568 _WaitingForStylesheet
= false;
1573 CWidgetManager::getInstance()->registerClockMsgTarget(this);
1576 ErrorColor
= CRGBA(255, 0, 0);
1577 LinkColor
= CRGBA(0, 0, 255);
1578 ErrorColorGlobalColor
= false;
1579 LinkColorGlobalColor
= false;
1580 TextColorGlobalColor
= false;
1586 LineSpaceFontFactor
= 0.5f
;
1587 DefaultButtonGroup
= "html_text_button";
1588 DefaultFormTextGroup
= "edit_box_widget";
1589 DefaultFormTextAreaGroup
= "edit_box_widget_multiline";
1590 DefaultFormSelectGroup
= "html_form_select_widget";
1591 DefaultFormSelectBoxMenuGroup
= "html_form_select_box_menu_widget";
1592 DefaultCheckBoxBitmapNormal
= "checkbox_normal.tga";
1593 DefaultCheckBoxBitmapPushed
= "checkbox_pushed.tga";
1594 DefaultCheckBoxBitmapOver
= "checkbox_over.tga";
1595 DefaultRadioButtonBitmapNormal
= "w_radiobutton.png";
1596 DefaultRadioButtonBitmapPushed
= "w_radiobutton_pushed.png";
1597 DefaultBackgroundBitmapView
= "bg";
1600 MultiCurl
= curl_multi_init();
1601 #ifdef CURLMOPT_MAX_HOST_CONNECTIONS
1604 // added in libcurl 7.30.0
1605 curl_multi_setopt(MultiCurl
, CURLMOPT_MAX_HOST_CONNECTIONS
, options
.curlMaxConnections
);
1606 curl_multi_setopt(MultiCurl
, CURLMOPT_PIPELINING
, 1);
1612 initImageDownload();
1615 // setup default browser style
1616 setProperty("browser_css_file", "browser.css");
1619 // ***************************************************************************
1621 CGroupHTML::~CGroupHTML()
1623 //releaseImageDownload();
1626 //nlinfo("** CGroupHTML Destroy: %x, %s, uid%d", this, _Id.c_str(), _GroupHtmlUID);
1628 /* Erase from map of Group HTML (thus requestTerminated() callback won't be called)
1629 Do it first, just because don't want requestTerminated() to be called while I'm destroying
1630 (useless and may be dangerous)
1632 _GroupHtmlByUID
.erase(_GroupHtmlUID
);
1641 curl_multi_cleanup(MultiCurl
);
1644 std::string
CGroupHTML::getProperty( const std::string
&name
) const
1651 if( name
== "title_prefix" )
1653 return _TitlePrefix
;
1656 if( name
== "error_color" )
1658 return toString( ErrorColor
);
1661 if( name
== "link_color" )
1663 return toString( LinkColor
);
1666 if( name
== "error_color_global_color" )
1668 return toString( ErrorColorGlobalColor
);
1671 if( name
== "link_color_global_color" )
1673 return toString( LinkColorGlobalColor
);
1676 if( name
== "text_color_global_color" )
1678 return toString( TextColorGlobalColor
);
1681 if( name
== "td_begin_space" )
1683 return toString( TDBeginSpace
);
1686 if( name
== "paragraph_begin_space" )
1688 return toString( PBeginSpace
);
1691 if( name
== "li_begin_space" )
1693 return toString( LIBeginSpace
);
1696 if( name
== "ul_begin_space" )
1698 return toString( ULBeginSpace
);
1701 if( name
== "ul_indent" )
1703 return toString( ULIndent
);
1706 if( name
== "multi_line_space_factor" )
1708 return toString( LineSpaceFontFactor
);
1711 if( name
== "form_text_area_group" )
1713 return DefaultFormTextGroup
;
1716 if( name
== "form_select_group" )
1718 return DefaultFormSelectGroup
;
1721 if( name
== "checkbox_bitmap_normal" )
1723 return DefaultCheckBoxBitmapNormal
;
1726 if( name
== "checkbox_bitmap_pushed" )
1728 return DefaultCheckBoxBitmapPushed
;
1731 if( name
== "checkbox_bitmap_over" )
1733 return DefaultCheckBoxBitmapOver
;
1736 if( name
== "radiobutton_bitmap_normal" )
1738 return DefaultRadioButtonBitmapNormal
;
1741 if( name
== "radiobutton_bitmap_pushed" )
1743 return DefaultRadioButtonBitmapPushed
;
1746 if( name
== "radiobutton_bitmap_over" )
1748 return DefaultRadioButtonBitmapOver
;
1751 if( name
== "background_bitmap_view" )
1753 return DefaultBackgroundBitmapView
;
1756 if( name
== "home" )
1761 if( name
== "browse_next_time" )
1763 return toString( _BrowseNextTime
);
1766 if( name
== "browse_tree" )
1771 if( name
== "browse_undo" )
1773 return _BrowseUndoButton
;
1776 if( name
== "browse_redo" )
1778 return _BrowseRedoButton
;
1781 if( name
== "browse_refresh" )
1783 return _BrowseRefreshButton
;
1786 if( name
== "timeout" )
1788 return toString( _TimeoutValue
);
1791 if( name
== "browser_css_file" )
1793 return _BrowserCssFile
;
1796 return CGroupScrollText::getProperty( name
);
1799 void CGroupHTML::setProperty( const std::string
&name
, const std::string
&value
)
1807 if( name
== "title_prefix" )
1809 _TitlePrefix
= value
;
1813 if( name
== "error_color" )
1816 if( fromString( value
, c
) )
1821 if( name
== "link_color" )
1824 if( fromString( value
, c
) )
1829 if( name
== "error_color_global_color" )
1832 if( fromString( value
, b
) )
1833 ErrorColorGlobalColor
= b
;
1837 if( name
== "link_color_global_color" )
1840 if( fromString( value
, b
) )
1841 LinkColorGlobalColor
= b
;
1845 if( name
== "text_color_global_color" )
1848 if( fromString( value
, b
) )
1849 TextColorGlobalColor
= b
;
1853 if( name
== "td_begin_space" )
1856 if( fromString( value
, i
) )
1861 if( name
== "paragraph_begin_space" )
1864 if( fromString( value
, i
) )
1869 if( name
== "li_begin_space" )
1872 if( fromString( value
, i
) )
1877 if( name
== "ul_begin_space" )
1880 if( fromString( value
, i
) )
1885 if( name
== "ul_indent" )
1888 if( fromString( value
, i
) )
1893 if( name
== "multi_line_space_factor" )
1896 if( fromString( value
, f
) )
1897 LineSpaceFontFactor
= f
;
1901 if( name
== "form_text_area_group" )
1903 DefaultFormTextGroup
= value
;
1907 if( name
== "form_select_group" )
1909 DefaultFormSelectGroup
= value
;
1913 if( name
== "checkbox_bitmap_normal" )
1915 DefaultCheckBoxBitmapNormal
= value
;
1919 if( name
== "checkbox_bitmap_pushed" )
1921 DefaultCheckBoxBitmapPushed
= value
;
1925 if( name
== "checkbox_bitmap_over" )
1927 DefaultCheckBoxBitmapOver
= value
;
1931 if( name
== "radiobutton_bitmap_normal" )
1933 DefaultRadioButtonBitmapNormal
= value
;
1937 if( name
== "radiobutton_bitmap_pushed" )
1939 DefaultRadioButtonBitmapPushed
= value
;
1943 if( name
== "radiobutton_bitmap_over" )
1945 DefaultRadioButtonBitmapOver
= value
;
1949 if( name
== "background_bitmap_view" )
1951 DefaultBackgroundBitmapView
= value
;
1955 if( name
== "home" )
1961 if( name
== "browse_next_time" )
1964 if( fromString( value
, b
) )
1965 _BrowseNextTime
= b
;
1969 if( name
== "browse_tree" )
1971 _BrowseTree
= value
;
1975 if( name
== "browse_undo" )
1977 _BrowseUndoButton
= value
;
1981 if( name
== "browse_redo" )
1983 _BrowseRedoButton
= value
;
1987 if( name
== "browse_refresh" )
1989 _BrowseRefreshButton
= value
;
1993 if( name
== "timeout" )
1996 if( fromString( value
, d
) )
2001 if( name
== "browser_css_file")
2003 _BrowserStyle
.reset();
2004 _BrowserCssFile
= value
;
2005 if (!_BrowserCssFile
.empty())
2007 std::string filename
= CPath::lookup(_BrowserCssFile
, false, true, true);
2008 if (!filename
.empty())
2011 if (in
.open(filename
))
2014 if (in
.readAll(css
))
2015 _BrowserStyle
.parseStylesheet(css
);
2017 nlwarning("Failed to read browser css from '%s'", filename
.c_str());
2021 nlwarning("Failed to open browser css file '%s'", filename
.c_str());
2026 nlwarning("Browser css file '%s' not found", _BrowserCssFile
.c_str());
2031 CGroupScrollText::setProperty( name
, value
);
2034 xmlNodePtr
CGroupHTML::serialize( xmlNodePtr parentNode
, const char *type
) const
2036 xmlNodePtr node
= CGroupScrollText::serialize( parentNode
, type
);
2040 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"html" );
2041 xmlSetProp( node
, BAD_CAST
"url", BAD_CAST _URL
.c_str() );
2042 xmlSetProp( node
, BAD_CAST
"title_prefix", BAD_CAST _TitlePrefix
.c_str() );
2043 xmlSetProp( node
, BAD_CAST
"error_color", BAD_CAST
toString( ErrorColor
).c_str() );
2044 xmlSetProp( node
, BAD_CAST
"link_color", BAD_CAST
toString( LinkColor
).c_str() );
2046 xmlSetProp( node
, BAD_CAST
"error_color_global_color",
2047 BAD_CAST
toString( ErrorColorGlobalColor
).c_str() );
2048 xmlSetProp( node
, BAD_CAST
"link_color_global_color",
2049 BAD_CAST
toString( LinkColorGlobalColor
).c_str() );
2050 xmlSetProp( node
, BAD_CAST
"text_color_global_color",
2051 BAD_CAST
toString( TextColorGlobalColor
).c_str() );
2053 xmlSetProp( node
, BAD_CAST
"td_begin_space", BAD_CAST
toString( TDBeginSpace
).c_str() );
2054 xmlSetProp( node
, BAD_CAST
"paragraph_begin_space", BAD_CAST
toString( PBeginSpace
).c_str() );
2055 xmlSetProp( node
, BAD_CAST
"li_begin_space", BAD_CAST
toString( LIBeginSpace
).c_str() );
2056 xmlSetProp( node
, BAD_CAST
"ul_begin_space", BAD_CAST
toString( ULBeginSpace
).c_str() );
2057 xmlSetProp( node
, BAD_CAST
"ul_indent", BAD_CAST
toString( ULIndent
).c_str() );
2058 xmlSetProp( node
, BAD_CAST
"multi_line_space_factor", BAD_CAST
toString( LineSpaceFontFactor
).c_str() );
2059 xmlSetProp( node
, BAD_CAST
"form_text_area_group", BAD_CAST DefaultFormTextGroup
.c_str() );
2060 xmlSetProp( node
, BAD_CAST
"form_select_group", BAD_CAST DefaultFormSelectGroup
.c_str() );
2061 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_normal", BAD_CAST DefaultCheckBoxBitmapNormal
.c_str() );
2062 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_pushed", BAD_CAST DefaultCheckBoxBitmapPushed
.c_str() );
2063 xmlSetProp( node
, BAD_CAST
"checkbox_bitmap_over", BAD_CAST DefaultCheckBoxBitmapOver
.c_str() );
2064 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_normal", BAD_CAST DefaultRadioButtonBitmapNormal
.c_str() );
2065 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_pushed", BAD_CAST DefaultRadioButtonBitmapPushed
.c_str() );
2066 xmlSetProp( node
, BAD_CAST
"radiobutton_bitmap_over", BAD_CAST DefaultRadioButtonBitmapOver
.c_str() );
2067 xmlSetProp( node
, BAD_CAST
"background_bitmap_view", BAD_CAST DefaultBackgroundBitmapView
.c_str() );
2068 xmlSetProp( node
, BAD_CAST
"home", BAD_CAST Home
.c_str() );
2069 xmlSetProp( node
, BAD_CAST
"browse_next_time", BAD_CAST
toString( _BrowseNextTime
).c_str() );
2070 xmlSetProp( node
, BAD_CAST
"browse_tree", BAD_CAST _BrowseTree
.c_str() );
2071 xmlSetProp( node
, BAD_CAST
"browse_undo", BAD_CAST _BrowseUndoButton
.c_str() );
2072 xmlSetProp( node
, BAD_CAST
"browse_redo", BAD_CAST _BrowseRedoButton
.c_str() );
2073 xmlSetProp( node
, BAD_CAST
"browse_refresh", BAD_CAST _BrowseRefreshButton
.c_str() );
2074 xmlSetProp( node
, BAD_CAST
"timeout", BAD_CAST
toString( _TimeoutValue
).c_str() );
2075 xmlSetProp( node
, BAD_CAST
"browser_css_file", BAD_CAST _BrowserCssFile
.c_str() );
2080 // ***************************************************************************
2082 bool CGroupHTML::parse(xmlNodePtr cur
,CInterfaceGroup
*parentGroup
)
2084 nlassert( CWidgetManager::getInstance()->isClockMsgTarget(this));
2087 if(!CGroupScrollText::parse(cur
, parentGroup
))
2091 //nlinfo("** CGroupHTML parsed Ok: %x, %s, %s, uid%d", this, _Id.c_str(), typeid(this).name(), _GroupHtmlUID);
2096 ptr
= xmlGetProp (cur
, (xmlChar
*)"url");
2098 _URL
= (const char*)ptr
;
2100 // Bkup default for undo/redo
2103 ptr
= xmlGetProp (cur
, (xmlChar
*)"title_prefix");
2105 _TitlePrefix
= CI18N::get((const char*)ptr
);
2108 ptr
= xmlGetProp (cur
, (xmlChar
*)"error_color");
2110 ErrorColor
= convertColor(ptr
);
2111 ptr
= xmlGetProp (cur
, (xmlChar
*)"link_color");
2113 LinkColor
= convertColor(ptr
);
2114 ptr
= xmlGetProp (cur
, (xmlChar
*)"error_color_global_color");
2116 ErrorColorGlobalColor
= convertBool(ptr
);
2117 ptr
= xmlGetProp (cur
, (xmlChar
*)"link_color_global_color");
2119 LinkColorGlobalColor
= convertBool(ptr
);
2120 ptr
= xmlGetProp (cur
, (xmlChar
*)"text_color_global_color");
2122 TextColorGlobalColor
= convertBool(ptr
);
2123 ptr
= xmlGetProp (cur
, (xmlChar
*)"td_begin_space");
2125 fromString((const char*)ptr
, TDBeginSpace
);
2126 ptr
= xmlGetProp (cur
, (xmlChar
*)"paragraph_begin_space");
2128 fromString((const char*)ptr
, PBeginSpace
);
2129 ptr
= xmlGetProp (cur
, (xmlChar
*)"li_begin_space");
2131 fromString((const char*)ptr
, LIBeginSpace
);
2132 ptr
= xmlGetProp (cur
, (xmlChar
*)"ul_begin_space");
2134 fromString((const char*)ptr
, ULBeginSpace
);
2135 ptr
= xmlGetProp (cur
, (xmlChar
*)"ul_indent");
2137 fromString((const char*)ptr
, ULIndent
);
2138 ptr
= xmlGetProp (cur
, (xmlChar
*)"multi_line_space_factor");
2140 fromString((const char*)ptr
, LineSpaceFontFactor
);
2141 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_text_group");
2143 DefaultFormTextGroup
= (const char*)(ptr
);
2144 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_text_area_group");
2146 DefaultFormTextAreaGroup
= (const char*)(ptr
);
2147 ptr
= xmlGetProp (cur
, (xmlChar
*)"form_select_group");
2149 DefaultFormSelectGroup
= (const char*)(ptr
);
2150 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_normal");
2152 DefaultCheckBoxBitmapNormal
= (const char*)(ptr
);
2153 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_pushed");
2155 DefaultCheckBoxBitmapPushed
= (const char*)(ptr
);
2156 ptr
= xmlGetProp (cur
, (xmlChar
*)"checkbox_bitmap_over");
2158 DefaultCheckBoxBitmapOver
= (const char*)(ptr
);
2159 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_normal");
2161 DefaultRadioButtonBitmapNormal
= (const char*)(ptr
);
2162 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_pushed");
2164 DefaultRadioButtonBitmapPushed
= (const char*)(ptr
);
2165 ptr
= xmlGetProp (cur
, (xmlChar
*)"radiobutton_bitmap_over");
2167 DefaultRadioButtonBitmapOver
= (const char*)(ptr
);
2168 ptr
= xmlGetProp (cur
, (xmlChar
*)"background_bitmap_view");
2170 DefaultBackgroundBitmapView
= (const char*)(ptr
);
2171 ptr
= xmlGetProp (cur
, (xmlChar
*)"home");
2173 Home
= (const char*)(ptr
);
2174 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_next_time");
2176 _BrowseNextTime
= convertBool(ptr
);
2177 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_tree");
2179 _BrowseTree
= (const char*)ptr
;
2180 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_undo");
2182 _BrowseUndoButton
= (const char*)ptr
;
2183 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_redo");
2185 _BrowseRedoButton
= (const char*)ptr
;
2186 ptr
= xmlGetProp (cur
, (xmlChar
*)"browse_refresh");
2188 _BrowseRefreshButton
= (const char*)ptr
;
2189 ptr
= xmlGetProp (cur
, (xmlChar
*)"timeout");
2191 fromString((const char*)ptr
, _TimeoutValue
);
2193 ptr
= xmlGetProp (cur
, (xmlChar
*)"browser_css_file");
2196 setProperty("browser_css_file", (const char *)ptr
);
2202 // ***************************************************************************
2204 bool CGroupHTML::handleEvent (const NLGUI::CEventDescriptor
& eventDesc
)
2206 bool traited
= false;
2208 if (eventDesc
.getType() == NLGUI::CEventDescriptor::mouse
)
2210 const NLGUI::CEventDescriptorMouse
&mouseEvent
= (const NLGUI::CEventDescriptorMouse
&)eventDesc
;
2211 if (mouseEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorMouse::mousewheel
)
2213 // Check if mouse wheel event was on any of multiline select box widgets
2214 // Must do this before CGroupScrollText
2215 for (uint i
=0; i
<_Forms
.size() && !traited
; i
++)
2217 for (uint j
=0; j
<_Forms
[i
].Entries
.size() && !traited
; j
++)
2219 if (_Forms
[i
].Entries
[j
].SelectBox
)
2221 if (_Forms
[i
].Entries
[j
].SelectBox
->handleEvent(eventDesc
))
2233 traited
= CGroupScrollText::handleEvent (eventDesc
);
2235 if (eventDesc
.getType() == NLGUI::CEventDescriptor::system
)
2237 const NLGUI::CEventDescriptorSystem
&systemEvent
= (const NLGUI::CEventDescriptorSystem
&) eventDesc
;
2238 if (systemEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::clocktick
)
2243 if (systemEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::activecalledonparent
)
2245 if (!((NLGUI::CEventDescriptorActiveCalledOnParent
&) systemEvent
).getActive())
2247 // stop refresh when window gets hidden
2248 _NextRefreshTime
= 0;
2255 // ***************************************************************************
2257 void CGroupHTML::endParagraph()
2264 // ***************************************************************************
2266 void CGroupHTML::newParagraph(uint beginSpace
)
2268 // Add a new paragraph
2269 CGroupParagraph
*newParagraph
= new CGroupParagraph(CViewBase::TCtorParam());
2270 newParagraph
->setId(getCurrentGroup()->getId() + ":PARAGRAPH" + toString(getNextAutoIdSeq()));
2271 newParagraph
->setResizeFromChildH(true);
2273 newParagraph
->setMarginLeft(getIndent());
2274 if (!_Style
.Current
.TextAlign
.empty())
2276 if (_Style
.Current
.TextAlign
== "left")
2277 newParagraph
->setTextAlign(CGroupParagraph::AlignLeft
);
2278 else if (_Style
.Current
.TextAlign
== "center")
2279 newParagraph
->setTextAlign(CGroupParagraph::AlignCenter
);
2280 else if (_Style
.Current
.TextAlign
== "right")
2281 newParagraph
->setTextAlign(CGroupParagraph::AlignRight
);
2282 else if (_Style
.Current
.TextAlign
== "justify")
2283 newParagraph
->setTextAlign(CGroupParagraph::AlignJustify
);
2287 addHtmlGroup (newParagraph
, beginSpace
);
2288 _Paragraph
= newParagraph
;
2293 // ***************************************************************************
2295 void CGroupHTML::browse(const char *url
)
2298 pushUrlUndoRedo(url
);
2300 // do the browse, with no undo/redo
2304 // ***************************************************************************
2305 void CGroupHTML::refresh()
2308 doBrowse(_URL
.c_str(), true);
2311 // ***************************************************************************
2312 void CGroupHTML::doBrowse(const char *url
, bool force
)
2314 LOG_DL("(%s) Browsing URL : '%s'", _Id
.c_str(), url
);
2316 CUrlParser
uri(url
);
2317 if (!uri
.hash
.empty())
2319 // Anchor to scroll after page has loaded
2320 _UrlFragment
= uri
.hash
;
2322 uri
.inherit(_DocumentUrl
);
2325 // compare urls and see if we only navigating to new anchor
2326 if (!force
&& _DocumentUrl
== uri
.toString())
2328 // scroll happens in updateCoords()
2334 _UrlFragment
.clear();
2337 _URL
= uri
.toString();
2338 _BrowseNextTime
= true;
2339 _WaitingForStylesheet
= false;
2341 // if a BrowseTree is bound to us, try to select the node that opens this URL (auto-locate)
2342 if(!_BrowseTree
.empty())
2344 CGroupTree
*groupTree
=dynamic_cast<CGroupTree
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseTree
));
2347 string nodeId
= selectTreeNodeRecurs(groupTree
->getRootNode(), url
);
2351 groupTree
->selectNodeById(nodeId
);
2357 // ***************************************************************************
2359 void CGroupHTML::browseError (const char *msg
)
2363 // Get the list group from CGroupScrollText
2366 CViewText
*viewText
= new CViewText ("", (string("Error : ")+msg
).c_str());
2367 viewText
->setColor (ErrorColor
);
2368 viewText
->setModulateGlobalColor(ErrorColorGlobalColor
);
2369 viewText
->setMultiLine (true);
2370 getParagraph()->addChild (viewText
);
2371 if(!_TitlePrefix
.empty())
2372 setTitle (_TitlePrefix
);
2374 updateRefreshButton();
2378 void CGroupHTML::browseErrorHtml(const std::string
&html
)
2383 renderHtmlString(html
);
2385 updateRefreshButton();
2389 // ***************************************************************************
2391 bool CGroupHTML::isBrowsing()
2393 // do not show spinning cursor for image downloads (!Curls.empty())
2394 return _BrowseNextTime
|| _PostNextTime
|| _RenderNextTime
||
2395 _Browsing
|| _WaitingForStylesheet
||
2399 // ***************************************************************************
2401 void CGroupHTML::updateCoords()
2403 CGroupScrollText::updateCoords();
2405 // all elements are in their correct place, tell scrollbar to scroll to anchor
2406 if (!_Browsing
&& !_UrlFragment
.empty())
2408 doBrowseAnchor(_UrlFragment
);
2409 _UrlFragment
.clear();
2412 if (!m_HtmlBackground
.isEmpty() || !m_BodyBackground
.isEmpty())
2414 // get scroll offset from list
2415 CGroupList
*list
= getList();
2418 CInterfaceElement
* vp
= list
->getParentPos() ? list
->getParentPos() : this;
2419 sint htmlW
= std::max(vp
->getWReal(), list
->getWReal());
2420 sint htmlH
= list
->getHReal();
2421 sint htmlX
= list
->getXReal() + list
->getOfsX();
2422 sint htmlY
= list
->getYReal() + list
->getOfsY();
2424 if (!m_HtmlBackground
.isEmpty())
2426 m_HtmlBackground
.setFillViewport(true);
2427 m_HtmlBackground
.setBorderArea(htmlX
, htmlY
, htmlW
, htmlH
);
2428 m_HtmlBackground
.setPaddingArea(htmlX
, htmlY
, htmlW
, htmlH
);
2429 m_HtmlBackground
.setContentArea(htmlX
, htmlY
, htmlW
, htmlH
);
2432 if (!m_BodyBackground
.isEmpty())
2434 // TODO: html padding + html border
2435 m_BodyBackground
.setBorderArea(htmlX
, htmlY
, htmlW
, htmlH
);
2436 // TODO: html padding + html border + body border
2437 m_BodyBackground
.setPaddingArea(htmlX
, htmlY
, htmlW
, htmlH
);
2438 // TODO: html padding + html_border + body padding
2439 m_BodyBackground
.setContentArea(htmlX
, htmlY
, htmlW
, htmlH
);
2445 // ***************************************************************************
2447 bool CGroupHTML::translateChar(u32char
&output
, u32char input
, u32char lastCharParam
) const
2452 // char is between table elements
2453 // TODO: only whitespace is handled, text is added to either TD, or after TABLE (should be before)
2454 bool tableWhitespace
= getTable() && (_Cells
.empty() || _Cells
.back() == NULL
);
2458 // Return / tab only in <PRE> mode
2462 if (tableWhitespace
)
2468 // Get the last char
2469 u32char lastChar
= lastCharParam
;
2471 lastChar
= getLastChar();
2472 keep
= ((lastChar
!= (u32char
)' ') &&
2473 (lastChar
!= 0)) || getPRE() || (_CurrentViewImage
&& (lastChar
== 0));
2475 input
= (u32char
)' ';
2481 if (tableWhitespace
)
2487 // Get the last char
2488 u32char lastChar
= lastCharParam
;
2490 lastChar
= getLastChar();
2491 keep
= ((lastChar
!= (u32char
)' ') &&
2492 (lastChar
!= (u32char
)'\n') &&
2493 (lastChar
!= 0)) || getPRE() || (_CurrentViewImage
&& (lastChar
== 0));
2510 // ***************************************************************************
2512 void CGroupHTML::registerAnchor(CInterfaceElement
* elm
)
2514 if (!_AnchorName
.empty())
2516 for(uint32 i
=0; i
< _AnchorName
.size(); ++i
)
2518 // filter out duplicates and register only first
2519 if (!_AnchorName
[i
].empty() && _Anchors
.count(_AnchorName
[i
]) == 0)
2521 _Anchors
[_AnchorName
[i
]] = elm
;
2525 _AnchorName
.clear();
2529 // ***************************************************************************
2530 bool CGroupHTML::isSameStyle(CViewLink
*text
, const CStyleParams
&style
) const
2532 if (!text
) return false;
2534 bool embolden
= style
.FontWeight
>= FONT_WEIGHT_BOLD
;
2535 bool sameShadow
= style
.TextShadow
.Enabled
&& text
->getShadow();
2536 if (sameShadow
&& style
.TextShadow
.Enabled
)
2539 text
->getShadowOffset(sx
, sy
);
2540 sameShadow
= (style
.TextShadow
.Color
== text
->getShadowColor());
2541 sameShadow
= sameShadow
&& (style
.TextShadow
.Outline
== text
->getShadowOutline());
2542 sameShadow
= sameShadow
&& (style
.TextShadow
.X
== sx
) && (style
.TextShadow
.Y
== sy
);
2544 // Compatible with current parameters ?
2545 return sameShadow
&&
2546 (style
.TextColor
== text
->getColor()) &&
2547 (style
.FontFamily
== text
->getFontName()) &&
2548 (style
.FontSize
== (uint
)text
->getFontSize()) &&
2549 (style
.Underlined
== text
->getUnderlined()) &&
2550 (style
.StrikeThrough
== text
->getStrikeThrough()) &&
2551 (embolden
== text
->getEmbolden()) &&
2552 (style
.FontOblique
== text
->getOblique()) &&
2553 (getLink() == text
->Link
) &&
2554 (style
.GlobalColorText
== text
->getModulateGlobalColor());
2557 // ***************************************************************************
2558 void CGroupHTML::newTextButton(const std::string
&text
, const std::string
&tpl
)
2560 _CurrentViewLink
= NULL
;
2561 _CurrentViewImage
= NULL
;
2563 // Action handler parameters : "name=group_html_id|form=id_of_the_form|submit_button=button_name"
2564 string param
= "name=" + this->_Id
+ "|url=" + getLink();
2566 if (!_AnchorName
.empty())
2567 name
= _AnchorName
.back();
2569 typedef pair
<string
, string
> TTmplParam
;
2570 vector
<TTmplParam
> tmplParams
;
2571 tmplParams
.push_back(TTmplParam("id", ""));
2572 tmplParams
.push_back(TTmplParam("onclick", "browse"));
2573 tmplParams
.push_back(TTmplParam("onclick_param", param
));
2574 tmplParams
.push_back(TTmplParam("active", "true"));
2575 CInterfaceGroup
*buttonGroup
= CWidgetManager::getInstance()->getParser()->createGroupInstance(tpl
, getId()+":"+name
, tmplParams
);
2578 nlinfo("Text button template '%s' not found", tpl
.c_str());
2581 buttonGroup
->setId(getId()+":"+name
);
2583 // Add the ctrl button
2584 CCtrlTextButton
*ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("button"));
2585 if (!ctrlButton
) ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("b"));
2588 nlinfo("Text button template '%s' is missing :button or :b text element", tpl
.c_str());
2591 ctrlButton
->setModulateGlobalColorAll(_Style
.Current
.GlobalColor
);
2592 ctrlButton
->setTextModulateGlobalColorNormal(_Style
.Current
.GlobalColorText
);
2593 ctrlButton
->setTextModulateGlobalColorOver(_Style
.Current
.GlobalColorText
);
2594 ctrlButton
->setTextModulateGlobalColorPushed(_Style
.Current
.GlobalColorText
);
2596 // Translate the tooltip
2597 ctrlButton
->setText(text
);
2598 ctrlButton
->setDefaultContextHelp(std::string(getLinkTitle()));
2599 // empty url / button disabled
2600 ctrlButton
->setFrozen(*getLink() == '\0');
2602 setTextButtonStyle(ctrlButton
, _Style
.Current
);
2604 _Paragraph
->addChild(buttonGroup
);
2607 // ***************************************************************************
2608 void CGroupHTML::newTextLink(const std::string
&text
)
2610 CViewLink
*newLink
= new CViewLink(CViewBase::TCtorParam());
2613 newLink
->Link
= getLink();
2614 newLink
->LinkTitle
= getLinkTitle();
2615 if (!newLink
->Link
.empty())
2617 newLink
->setHTMLView (this);
2618 newLink
->setActionOnLeftClick("browse");
2619 newLink
->setParamsOnLeftClick("name=" + getId() + "|url=" + newLink
->Link
);
2622 newLink
->setText(text
);
2623 newLink
->setMultiLineSpace((uint
)((float)(_Style
.Current
.FontSize
)*LineSpaceFontFactor
));
2624 newLink
->setMultiLine(true);
2625 newLink
->setModulateGlobalColor(_Style
.Current
.GlobalColorText
);
2626 setTextStyle(newLink
, _Style
.Current
);
2628 registerAnchor(newLink
);
2630 if (getA() && !newLink
->Link
.empty())
2631 getParagraph()->addChildLink(newLink
);
2633 getParagraph()->addChild(newLink
);
2635 _CurrentViewLink
= newLink
;
2636 _CurrentViewImage
= NULL
;
2639 // ***************************************************************************
2641 void CGroupHTML::addString(const std::string
&str
)
2643 string tmpStr
= str
;
2647 string _str
= tmpStr
;
2648 string::size_type p
= _str
.find('#');
2649 if (p
== string::npos
)
2651 tmpStr
= CI18N::get(_str
);
2655 string cmd
= _str
.substr(0, p
);
2656 string arg
= _str
.substr(p
+1);
2660 uint year
, month
, day
;
2661 sscanf(arg
.c_str(), "%d/%d/%d", &year
, &month
, &day
);
2662 tmpStr
= CI18N::get( "uiMFIDate");
2664 year
+= (year
> 70 ? 1900 : 2000);
2666 strFindReplace(tmpStr
, "%year", toString("%d", year
) );
2667 strFindReplace(tmpStr
, "%month", CI18N::get(toString("uiMonth%02d", month
)) );
2668 strFindReplace(tmpStr
, "%day", toString("%d", day
) );
2680 _ObjectScript
+= tmpStr
;
2682 else if (_SelectOption
)
2684 if (!(_Forms
.empty()))
2686 if (!_Forms
.back().Entries
.empty())
2688 _SelectOptionStr
+= tmpStr
;
2701 CStyleParams
&style
= _Style
.Current
;
2706 if (_CurrentViewLink
)
2708 bool skipLine
= !_CurrentViewLink
->getText().empty() && *(_CurrentViewLink
->getText().rbegin()) == '\n';
2709 if (!skipLine
&& isSameStyle(_CurrentViewLink
, style
))
2712 _CurrentViewLink
->setText(_CurrentViewLink
->getText()+tmpStr
);
2713 _CurrentViewLink
->invalidateContent();
2721 if (getA() && string(getLinkClass()) == "ryzom-ui-button")
2722 newTextButton(tmpStr
, DefaultButtonGroup
);
2724 newTextLink(tmpStr
);
2729 // ***************************************************************************
2731 void CGroupHTML::addImage(const std::string
&id
, const std::string
&img
, bool reloadImg
, const CStyleParams
&style
)
2740 // No more text in this text view
2741 _CurrentViewLink
= NULL
;
2744 CViewBitmap
*newImage
= new CViewBitmap (TCtorParam());
2745 newImage
->setId(id
);
2747 addImageDownload(img
, newImage
, style
, NormalImage
);
2748 newImage
->setRenderLayer(getRenderLayer()+1);
2750 getParagraph()->addChild(newImage
);
2753 setImageSize(newImage
, style
);
2756 // ***************************************************************************
2758 CInterfaceGroup
*CGroupHTML::addTextArea(const std::string
&templateName
, const char *name
, uint rows
, uint cols
, bool multiLine
, const std::string
&content
, uint maxlength
)
2767 // No more text in this text view
2768 _CurrentViewLink
= NULL
;
2770 CStyleParams
&style
= _Style
.Current
;
2772 // override cols/rows values from style
2773 if (style
.Width
> 0) cols
= style
.Width
/ style
.FontSize
;
2774 if (style
.Height
> 0) rows
= style
.Height
/ style
.FontSize
;
2777 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
2778 templateParams
.push_back (std::pair
<std::string
,std::string
> ("w", toString (cols
*style
.FontSize
)));
2779 templateParams
.push_back (std::pair
<std::string
,std::string
> ("id", name
));
2780 templateParams
.push_back (std::pair
<std::string
,std::string
> ("prompt", ""));
2781 templateParams
.push_back (std::pair
<std::string
,std::string
> ("multiline", multiLine
?"true":"false"));
2782 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontsize", toString (style
.FontSize
)));
2783 templateParams
.push_back (std::pair
<std::string
,std::string
> ("color", style
.TextColor
.toString()));
2784 if (style
.FontWeight
>= FONT_WEIGHT_BOLD
)
2785 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontweight", "bold"));
2786 if (style
.FontOblique
)
2787 templateParams
.push_back (std::pair
<std::string
,std::string
> ("fontstyle", "oblique"));
2789 templateParams
.push_back (std::pair
<std::string
,std::string
> ("multi_min_line", toString(rows
)));
2790 templateParams
.push_back (std::pair
<std::string
,std::string
> ("want_return", multiLine
?"true":"false"));
2791 templateParams
.push_back (std::pair
<std::string
,std::string
> ("onenter", ""));
2792 templateParams
.push_back (std::pair
<std::string
,std::string
> ("enter_recover_focus", "false"));
2794 templateParams
.push_back (std::pair
<std::string
,std::string
> ("max_num_chars", toString(maxlength
)));
2795 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow", toString(style
.TextShadow
.Enabled
)));
2796 if (style
.TextShadow
.Enabled
)
2798 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_x", toString(style
.TextShadow
.X
)));
2799 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_y", toString(style
.TextShadow
.Y
)));
2800 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_color", style
.TextShadow
.Color
.toString()));
2801 templateParams
.push_back (std::pair
<std::string
,std::string
> ("shadow_outline", toString(style
.TextShadow
.Outline
)));
2804 CInterfaceGroup
*textArea
= CWidgetManager::getInstance()->getParser()->createGroupInstance (templateName
.c_str(),
2805 getParagraph()->getId(), templateParams
.empty()?NULL
:&(templateParams
[0]), (uint
)templateParams
.size());
2811 CGroupEditBox
*eb
= dynamic_cast<CGroupEditBox
*>(textArea
->getGroup("eb"));
2814 eb
->setInputString(decodeHTMLEntities(content
));
2815 if (style
.hasStyle("background-color"))
2817 CViewBitmap
*bg
= dynamic_cast<CViewBitmap
*>(eb
->getView("bg"));
2820 bg
->setTexture("blank.tga");
2821 bg
->setColor(style
.Background
.color
);
2826 textArea
->invalidateCoords();
2827 getParagraph()->addChild (textArea
);
2834 // Not group created
2838 // ***************************************************************************
2839 CDBGroupComboBox
*CGroupHTML::addComboBox(const std::string
&templateName
, const char *name
)
2851 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
2852 templateParams
.push_back (std::pair
<std::string
,std::string
> ("id", name
));
2853 CInterfaceGroup
*group
= CWidgetManager::getInstance()->getParser()->createGroupInstance (templateName
.c_str(),
2854 getParagraph()->getId(), templateParams
.empty()?NULL
:&(templateParams
[0]), (uint
)templateParams
.size());
2860 CDBGroupComboBox
*cb
= dynamic_cast<CDBGroupComboBox
*>(group
);
2863 nlwarning("'%s' template has bad type, combo box expected", templateName
.c_str());
2869 getParagraph()->addChild (cb
);
2876 // Not group created
2880 // ***************************************************************************
2881 CGroupMenu
*CGroupHTML::addSelectBox(const std::string
&templateName
, const char *name
)
2891 std::vector
<std::pair
<std::string
,std::string
> > templateParams
;
2892 templateParams
.push_back(std::pair
<std::string
,std::string
> ("id", name
));
2893 CInterfaceGroup
*group
= CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName
.c_str(),
2894 getParagraph()->getId(), &(templateParams
[0]), (uint
)templateParams
.size());
2900 CGroupMenu
*sb
= dynamic_cast<CGroupMenu
*>(group
);
2903 nlwarning("'%s' template has bad type, CGroupMenu expected", templateName
.c_str());
2909 getParagraph()->addChild (sb
);
2919 // ***************************************************************************
2921 CCtrlButton
*CGroupHTML::addButton(CCtrlButton::EType type
, const std::string
&name
, const std::string
&normalBitmap
, const std::string
&pushedBitmap
,
2922 const std::string
&overBitmap
, const char *actionHandler
, const char *actionHandlerParams
,
2923 const std::string
&tooltip
, const CStyleParams
&style
)
2932 // Add the ctrl button
2933 CCtrlButton
*ctrlButton
= new CCtrlButton(TCtorParam());
2936 ctrlButton
->setId(name
);
2940 if (startsWith(normalBitmap
, "data:image/"))
2942 normal
= decodeURIComponent(normalBitmap
);
2946 // Load only tga files.. (conversion in dds filename is done in the lookup procedure)
2947 normal
= normalBitmap
.empty()?"":CFile::getPath(normalBitmap
) + CFile::getFilenameWithoutExtension(normalBitmap
) + ".tga";
2949 // if the image doesn't exist on local, we check in the cache
2950 if(!CPath::exists(normal
))
2952 // search in the compressed texture
2953 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
2954 sint32 id
= rVR
.getTextureIdFromName(normal
);
2957 normal
= localImageName(normalBitmap
);
2958 addImageDownload(normalBitmap
, ctrlButton
, style
);
2964 if (startsWith(pushedBitmap
, "data:image/"))
2966 pushed
= decodeURIComponent(pushedBitmap
);
2970 pushed
= pushedBitmap
.empty()?"":CFile::getPath(pushedBitmap
) + CFile::getFilenameWithoutExtension(pushedBitmap
) + ".tga";
2971 // if the image doesn't exist on local, we check in the cache, don't download it because the "normal" will already setuped it
2972 if(!CPath::exists(pushed
))
2974 // search in the compressed texture
2975 CViewRenderer
&rVR
= *CViewRenderer::getInstance();
2976 sint32 id
= rVR
.getTextureIdFromName(pushed
);
2979 pushed
= localImageName(pushedBitmap
);
2985 if (startsWith(overBitmap
, "data:image/"))
2987 over
= decodeURIComponent(overBitmap
);
2991 over
= overBitmap
.empty()?"":CFile::getPath(overBitmap
) + CFile::getFilenameWithoutExtension(overBitmap
) + ".tga";
2992 // schedule mouseover bitmap for download if its different from normal
2993 if (!over
.empty() && !CPath::exists(over
))
2995 if (overBitmap
!= normalBitmap
)
2997 over
= localImageName(overBitmap
);
2998 addImageDownload(overBitmap
, ctrlButton
, style
, OverImage
);
3003 ctrlButton
->setType (type
);
3004 if (!normal
.empty())
3005 ctrlButton
->setTexture (normal
);
3006 if (!pushed
.empty())
3007 ctrlButton
->setTexturePushed (pushed
);
3009 ctrlButton
->setTextureOver (over
);
3010 ctrlButton
->setModulateGlobalColorAll (style
.GlobalColor
);
3011 ctrlButton
->setActionOnLeftClick (actionHandler
);
3012 ctrlButton
->setParamsOnLeftClick (actionHandlerParams
);
3014 // Translate the tooltip or display raw text (tooltip from webig)
3015 if (!tooltip
.empty())
3017 if (CI18N::hasTranslation(tooltip
))
3019 ctrlButton
->setDefaultContextHelp(CI18N::get(tooltip
));
3020 //ctrlButton->setOnContextHelp(CI18N::get(tooltip).toString());
3024 ctrlButton
->setDefaultContextHelp(tooltip
);
3025 //ctrlButton->setOnContextHelp(string(tooltip));
3028 ctrlButton
->setInstantContextHelp(true);
3029 ctrlButton
->setToolTipParent(TTMouse
);
3030 ctrlButton
->setToolTipParentPosRef(Hotspot_TTAuto
);
3031 ctrlButton
->setToolTipPosRef(Hotspot_TTAuto
);
3034 getParagraph()->addChild (ctrlButton
);
3037 setImageSize(ctrlButton
, style
);
3042 // ***************************************************************************
3044 void CGroupHTML::flushString()
3046 _CurrentViewLink
= NULL
;
3049 // ***************************************************************************
3051 void CGroupHTML::clearContext()
3067 _FormSubmit
.clear();
3071 _AnchorName
.clear();
3072 _CellParams
.clear();
3075 _ReadingHeadTag
= false;
3076 _IgnoreHeadTag
= false;
3077 _IgnoreBaseUrlTag
= false;
3082 // clear the pointer to the current image download since all the button are deleted
3083 LOG_DL("Clear pointers to %d curls", Curls
.size());
3085 // remove image refs from downloads
3086 /*for(std::list<CDataDownload>::iterator it = Curls.begin(); it != Curls.end(); ++it)
3092 // ***************************************************************************
3094 u32char
CGroupHTML::getLastChar() const
3096 if (_CurrentViewLink
)
3098 ::u32string str
= CUtfStringView(_CurrentViewLink
->getText()).toUtf32(); // FIXME: Optimize reverse UTF iteration
3100 return str
[str
.length()-1];
3105 // ***************************************************************************
3107 void CGroupHTML::paragraphChange ()
3109 _CurrentViewLink
= NULL
;
3110 _CurrentViewImage
= NULL
;
3111 CGroupParagraph
*paragraph
= getParagraph();
3114 // Number of child in this paragraph
3115 uint numChild
= paragraph
->getNumChildren();
3118 // Get the last child
3119 CViewBase
*child
= paragraph
->getChild(numChild
-1);
3121 // Is this a string view ?
3122 _CurrentViewLink
= dynamic_cast<CViewLink
*>(child
);
3123 _CurrentViewImage
= dynamic_cast<CViewBitmap
*>(child
);
3128 // ***************************************************************************
3130 CInterfaceGroup
*CGroupHTML::getCurrentGroup()
3132 if (!_Cells
.empty() && _Cells
.back())
3133 return _Cells
.back()->Group
;
3135 return _GroupListAdaptor
;
3138 // ***************************************************************************
3140 void CGroupHTML::addHtmlGroup (CInterfaceGroup
*group
, uint beginSpace
)
3145 registerAnchor(group
);
3147 if (!_DivName
.empty())
3149 group
->setName(_DivName
);
3150 _Groups
.push_back(group
);
3153 group
->setSizeRef(CInterfaceElement::width
);
3155 // Compute begin space between paragraph and tables
3156 // * If first in group, no begin space
3158 // Pointer on the current paragraph (can be a table too)
3159 CGroupParagraph
*p
= dynamic_cast<CGroupParagraph
*>(group
);
3161 CInterfaceGroup
*parentGroup
= CGroupHTML::getCurrentGroup();
3162 const std::vector
<CInterfaceGroup
*> &groups
= parentGroup
->getGroups ();
3163 group
->setParent(parentGroup
);
3164 group
->setParentSize(parentGroup
);
3167 group
->setParentPos(parentGroup
);
3168 group
->setPosRef(Hotspot_TL
);
3169 group
->setParentPosRef(Hotspot_TL
);
3174 // Last is a paragraph ?
3175 group
->setParentPos(groups
.back());
3176 group
->setPosRef(Hotspot_TL
);
3177 group
->setParentPosRef(Hotspot_BL
);
3180 // Set the begin space
3182 p
->setTopSpace(beginSpace
);
3184 group
->setY(-(sint32
)beginSpace
);
3185 parentGroup
->addGroup (group
);
3188 // ***************************************************************************
3190 void CGroupHTML::setContainerTitle (const std::string
&title
)
3192 CInterfaceElement
*parent
= getParent();
3195 if ((parent
= parent
->getParent()))
3197 CGroupContainer
*container
= dynamic_cast<CGroupContainer
*>(parent
);
3200 container
->setTitle(title
);
3206 void CGroupHTML::setTitle(const std::string
&title
)
3208 if(_TitlePrefix
.empty())
3209 _TitleString
= title
;
3211 _TitleString
= _TitlePrefix
+ " - " + title
;
3213 setContainerTitle(_TitleString
);
3216 std::string
CGroupHTML::getTitle() const {
3217 return _TitleString
;
3220 // ***************************************************************************
3222 bool CGroupHTML::lookupLocalFile (string
&result
, const char *url
, bool isUrl
)
3227 if (toLowerAscii(result
).find("file:") == 0 && result
.size() > 5)
3229 result
= result
.substr(5, result
.size()-5);
3231 else if (result
.find("://") != string::npos
|| result
.find("//") == 0)
3233 // http://, https://, etc or protocol-less url "//domain.com/image.png"
3237 tmp
= CPath::lookup (CFile::getFilename(result
), false, false, false);
3240 // try to find in local directory
3241 tmp
= CPath::lookup (result
, false, false, true);
3246 // Normalize the path
3248 //result = "file:"+toLowerAscii(CPath::standardizePath (CPath::getFullPath (CFile::getPath(result)))+CFile::getFilename(result));*/
3249 result
= "file:/"+tmp
;
3256 // Is it a texture in the big texture ?
3257 if (CViewRenderer::getInstance()->getTextureIdFromName (result
) >= 0)
3263 // This is not a file in the CPath, let libwww open this URL
3270 // ***************************************************************************
3272 void CGroupHTML::submitForm(uint button
, sint32 x
, sint32 y
)
3274 if (button
>= _FormSubmit
.size())
3277 for(uint formId
= 0; formId
< _Forms
.size(); formId
++)
3279 // case sensitive search (user id is lowecase, auto id is uppercase)
3280 if (_Forms
[formId
].id
== _FormSubmit
[button
].form
)
3282 _PostNextTime
= true;
3283 _PostFormId
= formId
;
3284 _PostFormAction
= _FormSubmit
[button
].formAction
;
3285 _PostFormSubmitType
= _FormSubmit
[button
].type
;
3286 _PostFormSubmitButton
= _FormSubmit
[button
].name
;
3287 _PostFormSubmitValue
= _FormSubmit
[button
].value
;
3288 _PostFormSubmitX
= x
;
3289 _PostFormSubmitY
= y
;
3295 nlwarning("Unable to find form '%s' to submit (button '%s')", _FormSubmit
[button
].form
.c_str(), _FormSubmit
[button
].name
.c_str());
3298 // ***************************************************************************
3300 void CGroupHTML::setupBackground(CSSBackgroundRenderer
*bg
)
3304 bg
->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
3305 bg
->setBackground(_Style
.Current
.Background
);
3306 bg
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
3308 bg
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
3310 if (!_Style
.Current
.Background
.image
.empty())
3311 addTextureDownload(_Style
.Current
.Background
.image
, bg
->TextureId
, this);
3314 // ***************************************************************************
3316 void CGroupHTML::setBackgroundColor (const CRGBA
&bgcolor
)
3318 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
3319 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
3321 view
->setActive(false);
3323 m_HtmlBackground
.setColor(bgcolor
);
3326 // ***************************************************************************
3328 void CGroupHTML::setBackground (const string
&bgtex
, bool scale
, bool tile
)
3330 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
3331 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
3333 view
->setActive(false);
3335 m_HtmlBackground
.setImage(bgtex
);
3336 m_HtmlBackground
.setImageRepeat(tile
);
3337 m_HtmlBackground
.setImageCover(scale
);
3340 addTextureDownload(bgtex
, m_HtmlBackground
.TextureId
, this);
3344 struct CButtonFreezer
: public CInterfaceElementVisitor
3346 virtual void visitCtrl(CCtrlBase
*ctrl
)
3348 CCtrlBaseButton
*textButt
= dynamic_cast<CCtrlTextButton
*>(ctrl
);
3351 textButt
->setFrozen(true);
3356 // ***************************************************************************
3358 void CGroupHTML::handle ()
3360 H_AUTO(RZ_Interface_Html_handle
)
3362 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
3364 // handle curl downloads
3367 // handle refresh timer
3368 if (_NextRefreshTime
> 0 && _NextRefreshTime
<= (times
.thisFrameMs
/ 1000.0f
) )
3370 // there might be valid uses for 0sec refresh, but two in a row is probably a mistake
3371 if (_NextRefreshTime
- _LastRefreshTime
>= 1.0)
3373 _LastRefreshTime
= _NextRefreshTime
;
3374 doBrowse(_RefreshUrl
.c_str());
3377 nlwarning("Ignore second 0sec http-equiv refresh in a row (url '%s')", _URL
.c_str());
3379 _NextRefreshTime
= 0;
3384 // still transfering html page
3385 if (_TimeoutValue
!= 0 && _ConnectingTimeout
<= ( times
.thisFrameMs
/ 1000.0f
) )
3387 browseError(("Connection timeout : "+_URL
).c_str());
3391 if (_RenderNextTime
)
3393 _RenderNextTime
= false;
3394 renderHtmlString(_DocumentHtml
);
3397 if (_WaitingForStylesheet
)
3402 if (_BrowseNextTime
|| _PostNextTime
)
3405 _ConnectingTimeout
= ( times
.thisFrameMs
/ 1000.0f
) + _TimeoutValue
;
3407 // freeze form buttons
3408 CButtonFreezer freezer
;
3409 this->visit(&freezer
);
3416 bool isLocal
= lookupLocalFile (finalUrl
, _URL
.c_str(), true);
3420 CUrlParser
uri (_URL
);
3421 _TrustedDomain
= isTrustedDomain(uri
.host
);
3422 _DocumentDomain
= uri
.host
;
3424 // file is probably from bnp (ingame help)
3427 doBrowseLocalFile(finalUrl
);
3431 SFormFields formfields
;
3434 buildHTTPPostParams(formfields
);
3435 // _URL is set from form.Action
3440 // Add custom get params from child classes
3441 addHTTPGetParams (finalUrl
, _TrustedDomain
);
3444 doBrowseRemoteUrl(finalUrl
, "", _PostNextTime
, formfields
);
3447 _BrowseNextTime
= false;
3448 _PostNextTime
= false;
3452 // ***************************************************************************
3453 void CGroupHTML::buildHTTPPostParams (SFormFields
&formfields
)
3455 // Add text area text
3458 if (_PostFormId
>= _Forms
.size())
3460 nlwarning("(%s) invalid form index %d, _Forms %d", _Id
.c_str(), _PostFormId
, _Forms
.size());
3464 CForm
&form
= _Forms
[_PostFormId
];
3466 // button can override form action url (and methor, but we only do POST)
3467 _URL
= _PostFormAction
.empty() ? form
.Action
: _PostFormAction
;
3469 CUrlParser
uri(_URL
);
3470 _TrustedDomain
= isTrustedDomain(uri
.host
);
3471 _DocumentDomain
= uri
.host
;
3473 for (i
=0; i
<form
.Entries
.size(); i
++)
3476 bool addEntry
= false;
3478 if (form
.Entries
[i
].TextArea
)
3480 // Get the edit box view
3481 CInterfaceGroup
*group
= form
.Entries
[i
].TextArea
->getGroup ("eb");
3484 // Should be a CGroupEditBox
3485 CGroupEditBox
*editBox
= dynamic_cast<CGroupEditBox
*>(group
);
3488 entryData
= editBox
->getViewText()->getText();
3493 else if (form
.Entries
[i
].Checkbox
)
3495 // todo handle unicode POST here
3496 if (form
.Entries
[i
].Checkbox
->getPushed ())
3498 entryData
= form
.Entries
[i
].Value
;
3502 else if (form
.Entries
[i
].ComboBox
)
3504 CDBGroupComboBox
*cb
= form
.Entries
[i
].ComboBox
;
3505 entryData
= form
.Entries
[i
].SelectValues
[cb
->getSelection()];
3508 else if (form
.Entries
[i
].SelectBox
)
3510 CGroupMenu
*sb
= form
.Entries
[i
].SelectBox
;
3511 CGroupSubMenu
*rootMenu
= sb
->getRootMenu();
3514 for(uint j
=0; j
<rootMenu
->getNumLine(); ++j
)
3516 CInterfaceGroup
*ig
= rootMenu
->getUserGroupLeft(j
);
3519 CCtrlBaseButton
*cb
= dynamic_cast<CCtrlBaseButton
*>(ig
->getCtrl("b"));
3520 if (cb
&& cb
->getPushed())
3521 formfields
.add(form
.Entries
[i
].Name
, form
.Entries
[i
].SelectValues
[j
]);
3526 // This is a hidden value
3529 entryData
= form
.Entries
[i
].Value
;
3536 formfields
.add(form
.Entries
[i
].Name
, entryData
);
3540 if (_PostFormSubmitType
== "image")
3542 // Add the button coordinates
3543 if (_PostFormSubmitButton
.find_first_of("[") == string::npos
)
3545 formfields
.add(_PostFormSubmitButton
+ "_x", NLMISC::toString(_PostFormSubmitX
));
3546 formfields
.add(_PostFormSubmitButton
+ "_y", NLMISC::toString(_PostFormSubmitY
));
3550 formfields
.add(_PostFormSubmitButton
, NLMISC::toString(_PostFormSubmitX
));
3551 formfields
.add(_PostFormSubmitButton
, NLMISC::toString(_PostFormSubmitY
));
3555 formfields
.add(_PostFormSubmitButton
, _PostFormSubmitValue
);
3557 // Add custom params from child classes
3558 addHTTPPostParams(formfields
, _TrustedDomain
);
3561 // ***************************************************************************
3562 void CGroupHTML::doBrowseLocalFile(const std::string
&uri
)
3565 updateRefreshButton();
3567 std::string filename
;
3568 if (toLowerAscii(uri
).find("file:/") == 0)
3570 filename
= uri
.substr(6, uri
.size() - 6);
3577 LOG_DL("browse local file '%s'", filename
.c_str());
3579 _TrustedDomain
= true;
3580 _DocumentDomain
= "localhost";
3583 if (in
.open(filename
))
3589 in
.getline(buf
, 1024);
3590 html
+= std::string(buf
) + "\n";
3594 if (!renderHtmlString(html
))
3596 browseError((string("Failed to parse html from file : ")+filename
).c_str());
3601 browseError((string("The page address is malformed : ")+filename
).c_str());
3605 // ***************************************************************************
3606 void CGroupHTML::doBrowseRemoteUrl(std::string url
, const std::string
&referer
, bool doPost
, const SFormFields
&formfields
)
3608 // stop all downloads from previous page
3610 updateRefreshButton();
3613 if(_TitlePrefix
.empty())
3614 setTitle (CI18N::get("uiPleaseWait"));
3616 setTitle (_TitlePrefix
+ " - " + CI18N::get("uiPleaseWait"));
3618 url
= upgradeInsecureUrl(url
);
3620 LOG_DL("(%s) browse url (trusted=%s) '%s', referer='%s', post='%s', nb form values %d",
3621 _Id
.c_str(), (_TrustedDomain
? "true" :"false"), url
.c_str(), referer
.c_str(), (doPost
? "true" : "false"), formfields
.Values
.size());
3625 browseError(string("Invalid MultCurl handle, loading url failed : "+url
).c_str());
3629 CURL
*curl
= curl_easy_init();
3632 nlwarning("(%s) failed to create curl handle", _Id
.c_str());
3633 browseError(string("Failed to create cURL handle : " + url
).c_str());
3638 if (toLowerAscii(url
.substr(0, 8)) == "https://")
3640 // if supported, use custom SSL context function to load certificates
3641 NLWEB::CCurlCertificates::useCertificates(curl
);
3644 // do not follow redirects, we have own handler
3645 curl_easy_setopt(curl
, CURLOPT_FOLLOWLOCATION
, 0);
3647 curl_easy_setopt(curl
, CURLOPT_FRESH_CONNECT
, 1);
3649 // tell curl to use compression if possible (gzip, deflate)
3650 // leaving this empty allows all encodings that curl supports
3651 //curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
3653 // limit curl to HTTP and HTTPS protocols only
3654 curl_easy_setopt(curl
, CURLOPT_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
3655 curl_easy_setopt(curl
, CURLOPT_REDIR_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
3658 curl_easy_setopt(curl
, CURLOPT_URL
, url
.c_str());
3661 std::string userAgent
= options
.appName
+ "/" + options
.appVersion
;
3662 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, userAgent
.c_str());
3665 sendCookies(curl
, _DocumentDomain
, _TrustedDomain
);
3668 if (!referer
.empty())
3670 curl_easy_setopt(curl
, CURLOPT_REFERER
, referer
.c_str());
3671 LOG_DL("(%s) set referer '%s'", _Id
.c_str(), referer
.c_str());
3676 // serialize form data and add it to curl
3678 for(uint i
=0; i
<formfields
.Values
.size(); ++i
)
3680 char * escapedName
= curl_easy_escape(curl
, formfields
.Values
[i
].name
.c_str(), formfields
.Values
[i
].name
.size());
3681 char * escapedValue
= curl_easy_escape(curl
, formfields
.Values
[i
].value
.c_str(), formfields
.Values
[i
].value
.size());
3686 data
+= std::string(escapedName
) + "=" + escapedValue
;
3688 curl_free(escapedName
);
3689 curl_free(escapedValue
);
3691 curl_easy_setopt(curl
, CURLOPT_POST
, 1);
3692 curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, data
.size());
3693 curl_easy_setopt(curl
, CURLOPT_COPYPOSTFIELDS
, data
.c_str());
3697 curl_easy_setopt(curl
, CURLOPT_HTTPGET
, 1);
3701 _CurlWWW
= new CCurlWWWData(curl
, url
);
3703 // set the language code used by the client
3704 std::vector
<std::string
> headers
;
3705 headers
.push_back("Accept-Language: "+options
.languageCode
);
3706 headers
.push_back("Accept-Charset: utf-8");
3707 _CurlWWW
->sendHeaders(headers
);
3709 // catch headers for redirect
3710 curl_easy_setopt(curl
, CURLOPT_HEADERFUNCTION
, NLGUI::curlHeaderCallback
);
3711 curl_easy_setopt(curl
, CURLOPT_WRITEHEADER
, _CurlWWW
);
3714 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, NLGUI::curlDataCallback
);
3715 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, _CurlWWW
);
3717 #ifdef LOG_CURL_PROGRESS
3718 // progress callback
3719 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 0);
3720 curl_easy_setopt(curl
, CURLOPT_XFERINFOFUNCTION
, NLGUI::curlProgressCallback
);
3721 curl_easy_setopt(curl
, CURLOPT_XFERINFODATA
, _CurlWWW
);
3724 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 1);
3728 curl_multi_add_handle(MultiCurl
, curl
);
3730 // start the transfer
3731 int NewRunningCurls
= 0;
3732 curl_multi_perform(MultiCurl
, &NewRunningCurls
);
3735 _RedirectsRemaining
= DEFAULT_RYZOM_REDIRECT_LIMIT
;
3738 // ***************************************************************************
3739 void CGroupHTML::htmlDownloadFinished(bool success
, const std::string
&error
)
3743 CUrlParser
uri(_CurlWWW
->Url
);
3745 // potentially unwanted chars
3746 std::string url
= _CurlWWW
->Url
;
3747 url
= strFindReplaceAll(url
, string("<"), string("%3C"));
3748 url
= strFindReplaceAll(url
, string(">"), string("%3E"));
3749 url
= strFindReplaceAll(url
, string("\""), string("%22"));
3750 url
= strFindReplaceAll(url
, string("'"), string("%27"));
3753 err
= "<html><head><title>cURL error</title></head><body>";
3754 err
+= "<h1>Connection failed with cURL error</h1>";
3756 err
+= "<hr>(" + uri
.scheme
+ "://" + uri
.host
+ ") <a href=\"" + url
+ "\">reload</a>";
3757 err
+= "</body></html>";
3758 browseErrorHtml(err
);
3762 // received content from remote
3763 std::string content
= trim(_CurlWWW
->Content
);
3765 // save HSTS header from all requests regardless of HTTP code
3766 if (_CurlWWW
->hasHSTSHeader())
3768 CUrlParser
uri(_CurlWWW
->Url
);
3769 CStrictTransportSecurity::getInstance()->setFromHeader(uri
.host
, _CurlWWW
->getHSTSHeader());
3772 receiveCookies(_CurlWWW
->Request
, _DocumentDomain
, _TrustedDomain
);
3775 curl_easy_getinfo(_CurlWWW
->Request
, CURLINFO_RESPONSE_CODE
, &code
);
3776 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());
3778 if ((code
>= 301 && code
<= 303) || code
== 307 || code
== 308)
3780 if (_RedirectsRemaining
< 0)
3782 browseError(string("Redirect limit reached : " + _URL
).c_str());
3786 // redirect, get the location and try browse again
3787 // we cant use curl redirection because 'addHTTPGetParams()' must be called on new destination
3788 std::string
location(_CurlWWW
->getLocationHeader());
3789 if (location
.empty())
3791 browseError(string("Request was redirected, but location was not set : "+_URL
).c_str());
3795 LOG_DL("(%s) request (%d) redirected to (len %d) '%s'", _Id
.c_str(), _RedirectsRemaining
, location
.size(), location
.c_str());
3796 location
= getAbsoluteUrl(location
);
3798 _PostNextTime
= false;
3799 _RedirectsRemaining
--;
3801 doBrowse(location
.c_str());
3803 else if ( (code
< 200 || code
>= 300) )
3805 // catches 304 not modified, but html is not in cache anyway
3806 // if server did not send any error back
3807 if (content
.empty())
3809 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>");
3814 std::string contentType
;
3815 CURLcode res
= curl_easy_getinfo(_CurlWWW
->Request
, CURLINFO_CONTENT_TYPE
, &ch
);
3816 if (res
== CURLE_OK
&& ch
!= NULL
)
3821 htmlDownloadFinished(content
, contentType
, code
);
3823 // clear curl handler
3826 curl_multi_remove_handle(MultiCurl
, _CurlWWW
->Request
);
3832 // refresh button uses _CurlWWW. refresh button may stay disabled if
3833 // there is no css files to download and page is rendered before _CurlWWW is freed
3834 updateRefreshButton();
3837 void CGroupHTML::dataDownloadFinished(bool success
, const std::string
&error
, CDataDownload
*data
)
3841 CUrlParser
uri(data
->url
);
3842 if (!uri
.host
.empty())
3844 receiveCookies(data
->data
->Request
, uri
.host
, isTrustedDomain(uri
.host
));
3848 curl_easy_getinfo(data
->data
->Request
, CURLINFO_RESPONSE_CODE
, &code
);
3850 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());
3851 curl_multi_remove_handle(MultiCurl
, data
->data
->Request
);
3853 // save HSTS header from all requests regardless of HTTP code
3856 if (data
->data
->hasHSTSHeader())
3858 CStrictTransportSecurity::getInstance()->setFromHeader(uri
.host
, data
->data
->getHSTSHeader());
3861 // 2XX success, 304 Not Modified
3862 if ((code
>= 200 && code
<= 204) || code
== 304)
3864 CHttpCacheObject obj
;
3865 obj
.Expires
= data
->data
->getExpires();
3866 obj
.Etag
= data
->data
->getEtag();
3867 obj
.LastModified
= data
->data
->getLastModified();
3869 CHttpCache::getInstance()->store(data
->dest
, obj
);
3870 if (code
== 304 && CFile::fileExists(data
->tmpdest
))
3872 CFile::deleteFile(data
->tmpdest
);
3875 else if ((code
>= 301 && code
<= 303) || code
== 307 || code
== 308)
3877 if (data
->redirects
< DEFAULT_RYZOM_REDIRECT_LIMIT
)
3879 std::string
location(data
->data
->getLocationHeader());
3880 if (!location
.empty())
3882 CUrlParser
uri(location
);
3883 if (!uri
.isAbsolute())
3885 uri
.inherit(data
->url
);
3886 location
= uri
.toString();
3889 // clear old request state, and curl easy handle
3893 data
->url
= location
;
3896 // push same request in the front of the queue
3897 // cache filename is based of original url
3898 Curls
.push_front(data
);
3900 LOG_DL("Redirect '%s'", location
.c_str());
3901 // no finished callback called, so cleanup old temp
3902 if (CFile::fileExists(data
->tmpdest
))
3904 CFile::deleteFile(data
->tmpdest
);
3909 nlwarning("Redirected to empty url '%s'", data
->url
.c_str());
3913 nlwarning("Redirect limit reached for '%s'", data
->url
.c_str());
3918 nlwarning("HTTP request failed with code [%d] for '%s'\n",code
, data
->url
.c_str());
3920 if (CFile::fileExists(data
->dest
))
3922 CFile::deleteFile(data
->dest
);
3928 nlwarning("DATA download failed '%s', error '%s'", data
->url
.c_str(), error
.c_str());
3931 finishCurlDownload(data
);
3934 void CGroupHTML::htmlDownloadFinished(const std::string
&content
, const std::string
&type
, long code
)
3936 LOG_DL("(%s) HTML download finished, content length %d, type '%s', code %d", _Id
.c_str(), content
.size(), type
.c_str(), code
);
3938 // create <html> markup for image downloads
3939 if (type
.find("image/") == 0 && !content
.empty())
3943 std::string dest
= localImageName(_URL
);
3946 out
.serialBuffer((uint8
*)(content
.c_str()), content
.size());
3948 LOG_DL("(%s) image saved to '%s', url '%s'", _Id
.c_str(), dest
.c_str(), _URL
.c_str());
3952 // create html code with image url inside and do the request again
3953 renderHtmlString("<html><head><title>"+_URL
+"</title></head><body><img src=\"" + _URL
+ "\"></body></html>");
3955 else if (_TrustedDomain
&& type
.find("text/lua") == 0)
3957 setTitle(_TitleString
);
3959 _LuaScript
= "\nlocal __CURRENT_WINDOW__=\""+this->_Id
+"\" \n"+content
;
3960 CLuaManager::getInstance().executeLuaScript(_LuaScript
, true);
3963 // disable refresh button
3965 // disable redo into this url
3970 // Sanitize downloaded HTML UTF-8 encoding, and render
3971 renderHtmlString(CUtfStringView(content
).toUtf8(true));
3975 // ***************************************************************************
3976 void CGroupHTML::cssDownloadFinished(const std::string
&url
, const std::string
&local
)
3978 for(std::vector
<CHtmlParser::StyleLink
>::iterator it
= _StylesheetQueue
.begin();
3979 it
!= _StylesheetQueue
.end(); ++it
)
3983 // read downloaded file into HtmlStyles
3984 if (CFile::fileExists(local
) && it
->Index
< _HtmlStyles
.size())
3989 if (!in
.readAll(_HtmlStyles
[it
->Index
]))
3991 nlwarning("Failed to read downloaded css file(%s), url(%s)", local
.c_str(), url
.c_str());
3996 _StylesheetQueue
.erase(it
);
4002 void CGroupHTML::renderDocument()
4004 if (!Curls
.empty() && !_StylesheetQueue
.empty())
4006 // waiting for stylesheets to finish downloading
4009 _WaitingForStylesheet
= false;
4011 //TGameTime renderStart = CTime::getLocalTime();
4013 // clear previous state and page
4017 // process all <style> and <link rel=stylesheet> elements
4018 for(uint i
= 0; i
< _HtmlStyles
.size(); ++i
)
4020 if (!_HtmlStyles
[i
].empty())
4022 _Style
.parseStylesheet(_HtmlStyles
[i
]);
4025 _HtmlStyles
.clear();
4027 std::list
<CHtmlElement
>::iterator it
= _HtmlDOM
.Children
.begin();
4028 while(it
!= _HtmlDOM
.Children
.end())
4036 //TGameTime renderStop = CTime::getLocalTime();
4037 //nlwarning("[%s] render: %.1fms (%s)\n", _Id.c_str(), (renderStop - renderStart), _URL.c_str());
4040 // ***************************************************************************
4042 bool CGroupHTML::renderHtmlString(const std::string
&html
)
4046 // if we are already rendering, then queue up the next page
4049 _DocumentHtml
= html
;
4050 _RenderNextTime
= true;
4056 _DocumentUrl
= _URL
;
4057 _DocumentHtml
= html
;
4058 _NextRefreshTime
= 0;
4059 _RefreshUrl
.clear();
4061 if (trim(html
).empty())
4066 // clear previous page and state
4078 // start new rendering
4079 _HtmlDOM
= CHtmlElement(CHtmlElement::NONE
, "<root>");
4080 _CurrentHTMLElement
= NULL
;
4081 success
= parseHtml(html
);
4084 _WaitingForStylesheet
= !_StylesheetQueue
.empty();
4089 std::string error
= "ERROR: HTML parse failed.";
4090 error
+= toString("\nsize %d bytes", html
.size());
4091 error
+= toString("\n---start---\n%s\n---end---\n", html
.c_str());
4092 browseError(error
.c_str());
4099 // ***************************************************************************
4100 void CGroupHTML::doBrowseAnchor(const std::string
&anchor
)
4102 if (_Anchors
.count(anchor
) == 0)
4107 CInterfaceElement
*pIE
= _Anchors
.find(anchor
)->second
;
4110 // hotspot depends on vertical/horizontal scrollbar
4111 CCtrlScroll
*pSB
= getScrollBar();
4114 pSB
->ensureVisible(pIE
, Hotspot_Tx
, Hotspot_Tx
);
4119 // ***************************************************************************
4121 void CGroupHTML::draw ()
4123 uint8 CurrentAlpha
= 255;
4124 // search a parent container
4125 CInterfaceGroup
*gr
= getParent();
4128 if (gr
->isGroupContainer())
4130 CGroupContainer
*gc
= static_cast<CGroupContainer
*>(gr
);
4131 CurrentAlpha
= gc
->getCurrentContainerAlpha();
4134 gr
= gr
->getParent();
4136 m_HtmlBackground
.CurrentAlpha
= CurrentAlpha
;
4137 m_BodyBackground
.CurrentAlpha
= CurrentAlpha
;
4139 m_HtmlBackground
.draw();
4140 m_BodyBackground
.draw();
4141 CGroupScrollText::draw ();
4144 // ***************************************************************************
4146 void CGroupHTML::beginBuild ()
4151 void CGroupHTML::endBuild ()
4153 // set the browser as complete
4155 updateRefreshButton();
4157 // check that the title is set, or reset it (in the case the page
4158 // does not provide a title)
4159 if (_TitleString
.empty())
4161 setTitle(_TitlePrefix
);
4167 // ***************************************************************************
4169 void CGroupHTML::addHTTPGetParams (string
&/* url */, bool /*trustedDomain*/)
4173 // ***************************************************************************
4175 void CGroupHTML::addHTTPPostParams (SFormFields
&/* formfields */, bool /*trustedDomain*/)
4179 // ***************************************************************************
4181 string
CGroupHTML::home () const
4186 // ***************************************************************************
4188 void CGroupHTML::removeContent ()
4190 // Remove old document
4191 if (!_GroupListAdaptor
)
4193 _GroupListAdaptor
= new CGroupListAdaptor(CViewBase::TCtorParam()); // deleted by the list
4194 _GroupListAdaptor
->setId(getList()->getId() + ":GLA");
4195 _GroupListAdaptor
->setResizeFromChildH(true);
4196 getList()->addChild (_GroupListAdaptor
, true);
4199 // Group list adaptor not exist ?
4200 _GroupListAdaptor
->clearGroups();
4201 _GroupListAdaptor
->clearControls();
4202 _GroupListAdaptor
->clearViews();
4203 CWidgetManager::getInstance()->clearViewUnders();
4204 CWidgetManager::getInstance()->clearCtrlsUnders();
4206 // Clear all the context
4209 // Reset default background
4210 m_HtmlBackground
.clear();
4211 m_BodyBackground
.clear();
4213 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
4214 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
4216 view
->setActive(false);
4221 // ***************************************************************************
4222 const std::string
&CGroupHTML::selectTreeNodeRecurs(CGroupTree::SNode
*node
, const std::string
&url
)
4224 static std::string emptyString
;
4230 // if this node match
4231 if(actionLaunchUrlRecurs(node
->AHName
, node
->AHParams
, url
))
4235 // fails => look into children
4238 for(uint i
=0;i
<node
->Children
.size();i
++)
4240 const string
&childRes
= selectTreeNodeRecurs(node
->Children
[i
], url
);
4241 if(!childRes
.empty())
4250 // ***************************************************************************
4251 bool CGroupHTML::actionLaunchUrlRecurs(const std::string
&ah
, const std::string
¶ms
, const std::string
&url
)
4253 // check if this action match
4254 if( (ah
=="launch_help" || ah
=="browse") && IActionHandler::getParam (params
, "url") == url
)
4258 // can be a proc that contains launch_help/browse => look recurs
4261 const std::string
&procName
= params
;
4262 // look into this proc
4263 uint numActions
= CWidgetManager::getInstance()->getParser()->getProcedureNumActions(procName
);
4264 for(uint i
=0;i
<numActions
;i
++)
4266 string procAh
, procParams
;
4267 if( CWidgetManager::getInstance()->getParser()->getProcedureAction(procName
, i
, procAh
, procParams
))
4269 // recurs proc if needed!
4270 if (actionLaunchUrlRecurs(procAh
, procParams
, url
))
4279 // ***************************************************************************
4280 void CGroupHTML::clearRefresh()
4283 updateRefreshButton();
4286 // ***************************************************************************
4287 void CGroupHTML::clearUndoRedo()
4289 // erase any undo/redo
4290 _BrowseUndo
.clear();
4291 _BrowseRedo
.clear();
4293 // update buttons validation
4294 updateUndoRedoButtons();
4297 // ***************************************************************************
4298 void CGroupHTML::pushUrlUndoRedo(const std::string
&url
)
4300 // if same url, no op
4304 // erase any redo, push undo, set current
4305 _BrowseRedo
.clear();
4306 if(!_AskedUrl
.empty())
4307 _BrowseUndo
.push_back(_AskedUrl
);
4311 while(_BrowseUndo
.size()>MaxUrlUndoRedo
)
4312 _BrowseUndo
.pop_front();
4314 // update buttons validation
4315 updateUndoRedoButtons();
4318 // ***************************************************************************
4319 void CGroupHTML::browseUndo()
4321 if(_BrowseUndo
.empty())
4324 // push to redo, pop undo, and set current
4325 if (!_AskedUrl
.empty())
4326 _BrowseRedo
.push_front(_AskedUrl
);
4328 _AskedUrl
= _BrowseUndo
.back();
4329 _BrowseUndo
.pop_back();
4331 // update buttons validation
4332 updateUndoRedoButtons();
4334 // and then browse the undoed url, with no undo/redo
4335 doBrowse(_AskedUrl
.c_str());
4338 // ***************************************************************************
4339 void CGroupHTML::browseRedo()
4341 if(_BrowseRedo
.empty())
4344 // push to undo, pop redo, and set current
4345 _BrowseUndo
.push_back(_AskedUrl
);
4346 _AskedUrl
= _BrowseRedo
.front();
4347 _BrowseRedo
.pop_front();
4349 // update buttons validation
4350 updateUndoRedoButtons();
4352 // and then browse the redoed url, with no undo/redo
4353 doBrowse(_AskedUrl
.c_str());
4356 // ***************************************************************************
4357 void CGroupHTML::updateUndoRedoButtons()
4359 CCtrlBaseButton
*butUndo
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseUndoButton
));
4360 CCtrlBaseButton
*butRedo
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseRedoButton
));
4362 // gray according to list size
4364 butUndo
->setFrozen(_BrowseUndo
.empty());
4366 butRedo
->setFrozen(_BrowseRedo
.empty());
4369 // ***************************************************************************
4370 void CGroupHTML::updateRefreshButton()
4372 CCtrlBaseButton
*butRefresh
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseRefreshButton
));
4375 // connecting, rendering, or is missing url
4376 bool frozen
= _CurlWWW
|| _Browsing
|| _URL
.empty();
4377 butRefresh
->setFrozen(frozen
);
4381 // ***************************************************************************
4383 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupHTMLInputOffset
, std::string
, "html_input_offset");
4385 CGroupHTMLInputOffset::CGroupHTMLInputOffset(const TCtorParam
¶m
)
4386 : CInterfaceGroup(param
),
4391 xmlNodePtr
CGroupHTMLInputOffset::serialize( xmlNodePtr parentNode
, const char *type
) const
4393 xmlNodePtr node
= CInterfaceGroup::serialize( parentNode
, type
);
4397 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"html_input_offset" );
4398 xmlSetProp( node
, BAD_CAST
"y_offset", BAD_CAST
toString( Offset
).c_str() );
4403 // ***************************************************************************
4404 bool CGroupHTMLInputOffset::parse(xmlNodePtr cur
, CInterfaceGroup
*parentGroup
)
4406 if (!CInterfaceGroup::parse(cur
, parentGroup
)) return false;
4409 ptr
= xmlGetProp (cur
, (xmlChar
*)"y_offset");
4411 fromString((const char*)ptr
, Offset
);
4415 // ***************************************************************************
4416 int CGroupHTML::luaParseHtml(CLuaState
&ls
)
4418 const char *funcName
= "parseHtml";
4419 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4420 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4421 std::string html
= ls
.toString(1);
4428 int CGroupHTML::luaClearRefresh(CLuaState
&ls
)
4430 const char *funcName
= "clearRefresh";
4431 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4438 int CGroupHTML::luaClearUndoRedo(CLuaState
&ls
)
4440 const char *funcName
= "clearUndoRedo";
4441 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4447 // ***************************************************************************
4448 int CGroupHTML::luaBrowse(CLuaState
&ls
)
4450 const char *funcName
= "browse";
4451 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4452 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4453 browse(ls
.toString(1));
4457 // ***************************************************************************
4458 int CGroupHTML::luaRefresh(CLuaState
&ls
)
4460 const char *funcName
= "refresh";
4461 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4466 // ***************************************************************************
4467 int CGroupHTML::luaRemoveContent(CLuaState
&ls
)
4469 const char *funcName
= "removeContent";
4470 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4475 // ***************************************************************************
4476 int CGroupHTML::luaRenderHtml(CLuaState
&ls
)
4478 const char *funcName
= "renderHtml";
4479 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4480 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4481 std::string html
= ls
.toString(1);
4483 // Always trust domain if rendered from lua
4484 _TrustedDomain
= true;
4485 renderHtmlString(html
);
4490 // ***************************************************************************
4491 int CGroupHTML::luaSetBackground(CLuaState
&ls
)
4493 const char *funcName
= "setBackground";
4494 CLuaIHM::checkArgCount(ls
, funcName
, 3);
4495 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4496 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4497 CLuaIHM::checkArgType(ls
, funcName
, 3, LUA_TBOOLEAN
);
4498 std::string image
= ls
.toString(1);
4499 bool scale
= ls
.toBoolean(2);
4500 bool repeat
= ls
.toBoolean(3);
4502 setBackground(image
, scale
, repeat
);
4507 // ***************************************************************************
4508 int CGroupHTML::luaInsertText(CLuaState
&ls
)
4510 const char *funcName
= "insertText";
4511 CLuaIHM::checkArgCount(ls
, funcName
, 3);
4512 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4513 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TSTRING
);
4514 CLuaIHM::checkArgType(ls
, funcName
, 3, LUA_TBOOLEAN
);
4516 string name
= ls
.toString(1);
4517 string text
= ls
.toString(2);
4519 if (!_Forms
.empty())
4521 for (uint i
=0; i
<_Forms
.back().Entries
.size(); i
++)
4523 if (_Forms
.back().Entries
[i
].TextArea
&& _Forms
.back().Entries
[i
].Name
== name
)
4525 // Get the edit box view
4526 CInterfaceGroup
*group
= _Forms
.back().Entries
[i
].TextArea
->getGroup ("eb");
4529 // Should be a CGroupEditBox
4530 CGroupEditBox
*editBox
= dynamic_cast<CGroupEditBox
*>(group
);
4532 editBox
->writeString(text
, false, ls
.toBoolean(3));
4541 // ***************************************************************************
4542 int CGroupHTML::luaAddString(CLuaState
&ls
)
4544 const char *funcName
= "addString";
4545 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4546 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4547 addString(ls
.toString(1));
4551 // ***************************************************************************
4552 int CGroupHTML::luaAddImage(CLuaState
&ls
)
4554 const char *funcName
= "addImage";
4555 CLuaIHM::checkArgCount(ls
, funcName
, 2);
4556 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4557 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4565 style
.GlobalColor
= ls
.toBoolean(2);
4567 string url
= getLink();
4570 string params
= "name=" + getId() + "|url=" + getLink ();
4571 addButton(CCtrlButton::PushButton
, "", ls
.toString(1), ls
.toString(1),
4572 "", "browse", params
.c_str(), "", style
);
4576 addImage("", ls
.toString(1), false, style
);
4583 // ***************************************************************************
4584 int CGroupHTML::luaShowDiv(CLuaState
&ls
)
4586 const char *funcName
= "showDiv";
4587 CLuaIHM::checkArgCount(ls
, funcName
, 2);
4588 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4589 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4591 if (!_Groups
.empty())
4593 for (uint i
=0; i
<_Groups
.size(); i
++)
4595 CInterfaceGroup
*group
= _Groups
[i
];
4596 if (group
->getName() == ls
.toString(1))
4598 group
->setActive(ls
.toBoolean(2));
4605 // ***************************************************************************
4606 void CGroupHTML::setURL(const std::string
&url
)
4608 browse(url
.c_str());
4611 void CGroupHTML::setHTML(const std::string
&html
)
4613 renderHtmlString(html
);
4616 void CGroupHTML::setHome(const std::string
&home
)
4621 // ***************************************************************************
4622 void CGroupHTML::parseStylesheetFile(const std::string
&fname
)
4625 if (css
.open(fname
))
4627 uint32 remaining
= css
.getFileSize();
4628 std::string content
;
4630 while(!css
.eof() && remaining
> 0)
4632 const uint BUF_SIZE
= 4096;
4635 uint32 readJustNow
= std::min(remaining
, BUF_SIZE
);
4636 css
.serialBuffer((uint8
*)&buf
, readJustNow
);
4637 content
.append(buf
, readJustNow
);
4638 remaining
-= readJustNow
;
4641 _Style
.parseStylesheet(content
);
4643 catch(const Exception
&e
)
4645 nlwarning("exception while reading css file '%s'", e
.what());
4650 nlwarning("Stylesheet file '%s' not found (%s)", fname
.c_str(), _URL
.c_str());
4654 // ***************************************************************************
4655 bool CGroupHTML::parseHtml(const std::string
&htmlString
)
4657 CHtmlElement
*parsedDOM
;
4658 if (_CurrentHTMLElement
== NULL
)
4660 // parse under <root> element (clean dom)
4661 parsedDOM
= &_HtmlDOM
;
4665 // parse under currently rendered <lua> element
4666 parsedDOM
= _CurrentHTMLElement
;
4669 std::vector
<CHtmlParser::StyleLink
> links
;
4672 parser
.getDOM(htmlString
, *parsedDOM
, _HtmlStyles
, links
);
4674 // <link> elements inserted from lua::parseHtml are ignored
4675 if (_CurrentHTMLElement
== NULL
&& !links
.empty())
4677 addStylesheetDownload(links
);
4679 else if (_CurrentHTMLElement
!= NULL
)
4681 // Called from active element (lua)
4682 // <style> order is not preserved as document is already being rendered
4683 for(uint i
= 0; i
< _HtmlStyles
.size(); ++i
)
4685 if (!_HtmlStyles
[i
].empty())
4687 _Style
.parseStylesheet(_HtmlStyles
[i
]);
4690 _HtmlStyles
.clear();
4693 // this should rarely fail as first element should be <html>
4694 bool success
= parsedDOM
->Children
.size() > 0;
4696 std::list
<CHtmlElement
>::iterator it
= parsedDOM
->Children
.begin();
4697 while(it
!= parsedDOM
->Children
.end())
4699 if (it
->Type
== CHtmlElement::ELEMENT_NODE
&& it
->Value
== "html")
4701 // move newly parsed childs from <body> into siblings
4702 if (_CurrentHTMLElement
) {
4703 std::list
<CHtmlElement
>::iterator it2
= it
->Children
.begin();
4704 while(it2
!= it
->Children
.end())
4706 if (it2
->Type
== CHtmlElement::ELEMENT_NODE
&& it2
->Value
== "body")
4708 spliceFragment(it2
);
4713 // remove <html> fragment from current element child
4714 it
= parsedDOM
->Children
.erase(it
);
4718 // remove link to <root> (html->parent == '<root>') or css selector matching will break
4725 // skip over other non-handled element
4732 void CGroupHTML::spliceFragment(std::list
<CHtmlElement
>::iterator src
)
4734 if(!_CurrentHTMLElement
->parent
)
4736 nlwarning("BUG: Current node is missing parent element. unable to splice fragment");
4740 // get the iterators for current element (<lua>) and next sibling
4741 std::list
<CHtmlElement
>::iterator currentElement
;
4742 currentElement
= std::find(_CurrentHTMLElement
->parent
->Children
.begin(), _CurrentHTMLElement
->parent
->Children
.end(), *_CurrentHTMLElement
);
4743 if (currentElement
== _CurrentHTMLElement
->parent
->Children
.end())
4745 nlwarning("BUG: unable to find current element iterator from parent");
4749 // where fragment should be moved
4750 std::list
<CHtmlElement
>::iterator insertBefore
;
4751 if (_CurrentHTMLNextSibling
== NULL
)
4753 insertBefore
= _CurrentHTMLElement
->parent
->Children
.end();
4755 // get iterator for nextSibling
4756 insertBefore
= std::find(_CurrentHTMLElement
->parent
->Children
.begin(), _CurrentHTMLElement
->parent
->Children
.end(), *_CurrentHTMLNextSibling
);
4759 _CurrentHTMLElement
->parent
->Children
.splice(insertBefore
, src
->Children
);
4761 // reindex moved elements
4762 CHtmlElement
*prev
= NULL
;
4763 uint childIndex
= _CurrentHTMLElement
->childIndex
;
4764 while(currentElement
!= _CurrentHTMLElement
->parent
->Children
.end())
4766 if (currentElement
->Type
== CHtmlElement::ELEMENT_NODE
)
4770 currentElement
->parent
= _CurrentHTMLElement
->parent
;
4771 currentElement
->childIndex
= childIndex
;
4772 currentElement
->previousSibling
= prev
;
4773 prev
->nextSibling
= &(*currentElement
);
4777 prev
= &(*currentElement
);
4783 // ***************************************************************************
4784 inline bool isDigit(char c
, uint base
= 16)
4786 if (c
>='0' && c
<='9') return true;
4787 if (base
!= 16) return false;
4788 if (c
>='A' && c
<='F') return true;
4789 if (c
>='a' && c
<='f') return true;
4793 // ***************************************************************************
4794 inline char convertHexDigit(char c
)
4796 if (c
>='0' && c
<='9') return c
-'0';
4797 if (c
>='A' && c
<='F') return c
-'A'+10;
4798 if (c
>='a' && c
<='f') return c
-'a'+10;
4802 // ***************************************************************************
4803 std::string
CGroupHTML::decodeHTMLEntities(const std::string
&str
)
4806 result
.reserve(str
.size() + (str
.size() >> 2));
4809 for (uint i
=0; i
<str
.length(); ++i
)
4812 if (str
[i
] == '&' && (str
.length()-i
) >= 4)
4816 // unicode character
4817 if (str
[pos
] == '#')
4821 // using decimal by default
4824 // using hexadecimal if &#x
4825 if (str
[pos
] == 'x')
4831 // setup "last" to point at the first character following "&#x?[0-9a-f]+"
4832 for (last
= pos
; last
< str
.length(); ++last
) if (!isDigit(str
[last
], base
)) break;
4834 // make sure that at least 1 digit was found
4835 // and have the terminating ';' to complete the token: "&#x?[0-9a-f]+;"
4836 if (last
== pos
|| str
[last
] != ';')
4844 // convert digits to unicode character
4845 while (pos
< last
) c
= convertHexDigit(str
[pos
++]) + (c
* u32char(base
));
4847 // append our new character to the result string
4848 CUtfStringView::append(result
, c
);
4850 // move 'i' forward to point at the ';' .. the for(...) will increment i to point to next char
4856 // special xml characters
4857 if (str
.substr(i
+1,5)=="quot;") { i
+=5; result
+='\"'; continue; }
4858 if (str
.substr(i
+1,4)=="amp;") { i
+=4; result
+='&'; continue; }
4859 if (str
.substr(i
+1,3)=="lt;") { i
+=3; result
+='<'; continue; }
4860 if (str
.substr(i
+1,3)=="gt;") { i
+=3; result
+='>'; continue; }
4863 // all the special cases are catered for... treat this as a normal character
4870 // ***************************************************************************
4871 std::string
CGroupHTML::getAbsoluteUrl(const std::string
&url
)
4873 CUrlParser
uri(url
);
4874 if (uri
.isAbsolute())
4879 return uri
.toString();
4882 // ***************************************************************************
4883 void CGroupHTML::resetCssStyle()
4885 _WaitingForStylesheet
= false;
4886 _StylesheetQueue
.clear();
4888 _Style
= _BrowserStyle
;
4891 // ***************************************************************************
4892 std::string
CGroupHTML::HTMLOListElement::getListMarkerText() const
4895 sint32 number
= Value
;
4900 ret
= "\xe2\x88\x99 ";
4902 else if (Type
== "circle")
4905 ret
= "\xe2\x9a\xaa ";
4907 else if (Type
== "square")
4910 ret
= "\xe2\x96\xaa ";
4912 else if (Type
== "a" || Type
== "A")
4914 // @see toAlphabeticOrNumeric in WebKit
4915 static const char lower
[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
4916 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
4917 static const char upper
[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
4918 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
4922 ret
= toString(number
);
4926 const char* digits
= (Type
== "A" ? upper
: lower
);
4930 ret
.insert(ret
.begin(), digits
[number
% size
]);
4936 else if (Type
== "i" || Type
== "I")
4938 // @see toRoman in WebKit
4939 static const char lower
[7] = {'i', 'v', 'x', 'l', 'c', 'd', 'm'};
4940 static const char upper
[7] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
4942 if (number
< 1 || number
> 3999)
4944 ret
= toString(number
);
4948 const char* digits
= (Type
== "I" ? upper
: lower
);
4952 uint32 num
= number
% 10;
4955 for (i
= num
% 5; i
> 0; i
--)
4957 ret
.insert(ret
.begin(), digits
[d
]);
4960 if (num
>= 4 && num
<= 8)
4962 ret
.insert(ret
.begin(), digits
[d
+ 1]);
4966 ret
.insert(ret
.begin(), digits
[d
+ 2]);
4970 ret
.insert(ret
.begin(), digits
[d
]);
4986 ret
= toString(Value
) + ". ";
4992 void CGroupHTML::HTMLMeterElement::readValues(const CHtmlElement
&elm
)
4994 if (!elm
.hasAttribute("value") || !fromString(elm
.getAttribute("value"), value
))
4996 if (!elm
.hasAttribute("min") || !fromString(elm
.getAttribute("min"), min
))
4998 if (!elm
.hasAttribute("max") || !fromString(elm
.getAttribute("max"), max
))
5003 std::swap(min
, max
);
5005 if (!elm
.hasAttribute("low") || !fromString(elm
.getAttribute("low"), low
))
5007 if (!elm
.hasAttribute("high") || !fromString(elm
.getAttribute("high"), high
))
5010 if (!elm
.hasAttribute("optimum") || !fromString(elm
.getAttribute("optimum"), optimum
))
5011 optimum
= (max
- min
) / 2.f
;
5013 // ensure low < high
5015 std::swap(low
, high
);
5022 float CGroupHTML::HTMLMeterElement::getValueRatio() const
5027 return (value
- min
) / (max
- min
);
5030 CGroupHTML::HTMLMeterElement::EValueRegion
CGroupHTML::HTMLMeterElement::getValueRegion() const
5034 // low region is optimum
5036 return VALUE_OPTIMUM
;
5037 else if (value
<= high
)
5038 return VALUE_SUB_OPTIMAL
;
5040 return VALUE_EVEN_LESS_GOOD
;
5042 else if (optimum
>= high
)
5044 // high region is optimum
5046 return VALUE_OPTIMUM
;
5047 else if (value
>= low
)
5048 return VALUE_SUB_OPTIMAL
;
5050 return VALUE_EVEN_LESS_GOOD
;
5053 // middle region is optimum
5054 if (value
>= low
&& value
<= high
)
5055 return VALUE_OPTIMUM
;
5057 return VALUE_SUB_OPTIMAL
;
5060 NLMISC::CRGBA
CGroupHTML::HTMLMeterElement::getBarColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5062 // color meter (inactive) bar segment
5063 // firefox:: meter { background:none; background-color: #555; },
5064 // webkit:: meter::-webkit-meter-bar { background:none; background-color: #555; }
5065 // webkit makes background color visible when padding is added
5066 CRGBA
color(150, 150, 150, 255);
5068 // use webkit pseudo elements as thats easier than firefox pseudo classes
5069 // background-color is expected to be set from browser.css
5071 style
.applyStyle(elm
.getPseudo(":-webkit-meter-bar"));
5072 if(style
.hasStyle("background-color"))
5073 color
= style
.Current
.Background
.color
;
5079 NLMISC::CRGBA
CGroupHTML::HTMLMeterElement::getValueColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5081 // background-color is expected to be set from browser.css
5084 switch(getValueRegion())
5088 style
.applyStyle(elm
.getPseudo(":-webkit-meter-optimum-value"));
5089 if (style
.hasStyle("background-color"))
5090 color
= style
.Current
.Background
.color
;
5093 case VALUE_SUB_OPTIMAL
:
5095 style
.applyStyle(elm
.getPseudo(":-webkit-meter-suboptimum-value"));
5096 if (style
.hasStyle("background-color"))
5097 color
= style
.Current
.Background
.color
;
5100 case VALUE_EVEN_LESS_GOOD
: // fall through
5103 style
.applyStyle(elm
.getPseudo(":-webkit-meter-even-less-good-value"));
5104 if (style
.hasStyle("background-color"))
5105 color
= style
.Current
.Background
.color
;
5114 // ****************************************************************************
5115 void CGroupHTML::HTMLProgressElement::readValues(const CHtmlElement
&elm
)
5117 if (!elm
.hasAttribute("value") || !fromString(elm
.getAttribute("value"), value
))
5119 if (!elm
.hasAttribute("max") || !fromString(elm
.getAttribute("max"), max
))
5126 // ****************************************************************************
5127 float CGroupHTML::HTMLProgressElement::getValueRatio() const
5134 // ****************************************************************************
5135 NLMISC::CRGBA
CGroupHTML::HTMLProgressElement::getBarColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5140 style
.applyStyle(elm
.getPseudo(":-webkit-progress-bar"));
5141 if (style
.hasStyle("background-color"))
5142 color
= style
.Current
.Background
.color
;
5148 // ****************************************************************************
5149 NLMISC::CRGBA
CGroupHTML::HTMLProgressElement::getValueColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5154 style
.applyStyle(elm
.getPseudo(":-webkit-progress-value"));
5155 if (style
.hasStyle("background-color"))
5156 color
= style
.Current
.Background
.color
;
5162 // ****************************************************************************
5163 void CGroupHTML::getCellsParameters(const CHtmlElement
&elm
, bool inherit
)
5165 CGroupHTML::CCellParams cellParams
;
5166 if (!_CellParams
.empty() && inherit
)
5167 cellParams
= _CellParams
.back();
5169 if (!_Style
.hasStyle("background-color") && elm
.hasNonEmptyAttribute("bgcolor"))
5172 if (scanHTMLColor(elm
.getAttribute("bgcolor").c_str(), c
))
5173 _Style
.Current
.Background
.color
= c
;
5175 cellParams
.BgColor
= _Style
.Current
.Background
.color
;
5177 if (elm
.hasAttribute("nowrap") || _Style
.Current
.WhiteSpace
== "nowrap")
5178 cellParams
.NoWrap
= true;
5180 if (elm
.hasNonEmptyAttribute("l_margin"))
5181 fromString(elm
.getAttribute("l_margin"), cellParams
.LeftMargin
);
5183 if (_Style
.hasStyle("height"))
5184 cellParams
.Height
= _Style
.Current
.Height
;
5185 else if (elm
.hasNonEmptyAttribute("height"))
5186 fromString(elm
.getAttribute("height"), cellParams
.Height
);
5190 // having text-align on table/tr should not override td align attribute
5191 if (_Style
.hasStyle("text-align"))
5192 align
= _Style
.Current
.TextAlign
;
5193 else if (elm
.hasNonEmptyAttribute("align"))
5194 align
= toLowerAscii(elm
.getAttribute("align"));
5196 if (align
== "left")
5197 cellParams
.Align
= CGroupCell::Left
;
5198 else if (align
== "center")
5199 cellParams
.Align
= CGroupCell::Center
;
5200 else if (align
== "right")
5201 cellParams
.Align
= CGroupCell::Right
;
5202 else if (align
!= "justify")
5205 // copy td align (can be empty) attribute back into css
5206 _Style
.Current
.TextAlign
= align
;
5211 if (_Style
.hasStyle("vertical-align"))
5212 valign
= _Style
.Current
.VerticalAlign
;
5213 else if (elm
.hasNonEmptyAttribute("valign"))
5214 valign
= toLowerAscii(elm
.getAttribute("valign"));
5216 if (valign
== "top")
5217 cellParams
.VAlign
= CGroupCell::Top
;
5218 else if (valign
== "middle")
5219 cellParams
.VAlign
= CGroupCell::Middle
;
5220 else if (valign
== "bottom")
5221 cellParams
.VAlign
= CGroupCell::Bottom
;
5224 _CellParams
.push_back (cellParams
);
5227 // ***************************************************************************
5228 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
)
5230 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, "", "image", action
));
5231 // Action handler parameters
5232 std::string param
= "name=" + getId() + "|button=" + toString(_FormSubmit
.size()-1);
5234 // Add the ctrl button
5235 addButton (CCtrlButton::PushButton
, name
, src
, src
, over
, "html_submit_form", param
.c_str(), tooltip
.c_str(), _Style
.Current
);
5238 // ***************************************************************************
5239 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
)
5241 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, value
, "submit", formAction
));
5242 // Action handler parameters
5243 string param
= "name=" + getId() + "|button=" + toString(_FormSubmit
.size()-1);
5245 // Add the ctrl button
5252 string
buttonTemplate(!templateName
.empty() ? templateName
: DefaultButtonGroup
);
5253 typedef pair
<string
, string
> TTmplParam
;
5254 vector
<TTmplParam
> tmplParams
;
5255 tmplParams
.push_back(TTmplParam("id", name
));
5256 tmplParams
.push_back(TTmplParam("onclick", "html_submit_form"));
5257 tmplParams
.push_back(TTmplParam("onclick_param", param
));
5258 tmplParams
.push_back(TTmplParam("active", "true"));
5259 if (minWidth
> 0) tmplParams
.push_back(TTmplParam("wmin", toString(minWidth
)));
5260 CInterfaceGroup
*buttonGroup
= CWidgetManager::getInstance()->getParser()->createGroupInstance(buttonTemplate
, _Paragraph
->getId(), tmplParams
);
5263 // Add the ctrl button
5264 CCtrlTextButton
*ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("button"));
5265 if (!ctrlButton
) ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("b"));
5268 ctrlButton
->setModulateGlobalColorAll (_Style
.Current
.GlobalColor
);
5269 ctrlButton
->setTextModulateGlobalColorNormal(_Style
.Current
.GlobalColorText
);
5270 ctrlButton
->setTextModulateGlobalColorOver(_Style
.Current
.GlobalColorText
);
5271 ctrlButton
->setTextModulateGlobalColorPushed(_Style
.Current
.GlobalColorText
);
5273 // Translate the tooltip
5274 if (!tooltip
.empty())
5276 if (CI18N::hasTranslation(tooltip
))
5278 ctrlButton
->setDefaultContextHelp(CI18N::get(tooltip
));
5282 ctrlButton
->setDefaultContextHelp(tooltip
);
5286 ctrlButton
->setText(value
);
5288 setTextButtonStyle(ctrlButton
, _Style
.Current
);
5290 getParagraph()->addChild (buttonGroup
);
5295 // ***************************************************************************
5296 void CGroupHTML::htmlA(const CHtmlElement
&elm
)
5299 _Link
.push_back ("");
5300 _LinkTitle
.push_back("");
5301 _LinkClass
.push_back("");
5302 if (elm
.hasClass("ryzom-ui-button"))
5303 _LinkClass
.back() = "ryzom-ui-button";
5305 // #fragment works with both ID and NAME so register both
5306 if (elm
.hasNonEmptyAttribute("name"))
5307 _AnchorName
.push_back(elm
.getAttribute("name"));
5308 if (elm
.hasNonEmptyAttribute("title"))
5309 _LinkTitle
.back() = elm
.getAttribute("title");
5310 if (elm
.hasNonEmptyAttribute("href"))
5312 string suri
= elm
.getAttribute("href");
5313 if(suri
.find("ah:") == 0)
5316 _Link
.back() = suri
;
5320 // convert href from "?key=val" into "http://domain.com/?key=val"
5321 _Link
.back() = getAbsoluteUrl(suri
);
5325 renderPseudoElement(":before", elm
);
5328 void CGroupHTML::htmlAend(const CHtmlElement
&elm
)
5330 renderPseudoElement(":after", elm
);
5333 popIfNotEmpty(_Link
);
5334 popIfNotEmpty(_LinkTitle
);
5335 popIfNotEmpty(_LinkClass
);
5338 // ***************************************************************************
5339 void CGroupHTML::htmlBASE(const CHtmlElement
&elm
)
5341 if (!_ReadingHeadTag
|| _IgnoreBaseUrlTag
)
5344 if (elm
.hasNonEmptyAttribute("href"))
5346 CUrlParser
uri(elm
.getAttribute("href"));
5347 if (uri
.isAbsolute())
5349 _URL
= uri
.toString();
5350 _IgnoreBaseUrlTag
= true;
5355 // ***************************************************************************
5356 void CGroupHTML::htmlBODY(const CHtmlElement
&elm
)
5358 // override <body> (or <html>) css style attribute
5359 if (elm
.hasNonEmptyAttribute("bgcolor"))
5360 _Style
.applyStyle("background-color: " + elm
.getAttribute("bgcolor"));
5362 if (m_HtmlBackground
.isEmpty())
5363 setupBackground(&m_HtmlBackground
);
5365 setupBackground(&m_BodyBackground
);
5367 renderPseudoElement(":before", elm
);
5370 // ***************************************************************************
5371 void CGroupHTML::htmlBR(const CHtmlElement
&elm
)
5373 if (!_Paragraph
|| _Paragraph
->getNumChildren() == 0)
5383 // ***************************************************************************
5384 void CGroupHTML::htmlBUTTON(const CHtmlElement
&elm
)
5386 std::string name
= elm
.getAttribute("name");
5387 std::string value
= elm
.getAttribute("value");
5388 std::string formId
= elm
.getAttribute("form");
5389 std::string formAction
= elm
.getAttribute("formaction");
5390 std::string tooltip
= elm
.getAttribute("tooltip");
5391 bool disabled
= elm
.hasAttribute("disabled");
5393 if (formId
.empty() && _FormOpen
)
5395 formId
= _Forms
.back().id
;
5398 if (!formAction
.empty())
5400 formAction
= getAbsoluteUrl(formAction
);
5403 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, value
, "text", formAction
));
5404 // Action handler parameters
5408 if (elm
.getAttribute("type") == "submit")
5410 param
= "ah:html_submit_form&name=" + getId() + "&button=" + toString(_FormSubmit
.size()-1);
5419 _Link
.push_back(param
);
5420 _LinkTitle
.push_back(tooltip
);
5421 _LinkClass
.push_back("ryzom-ui-button");
5423 // TODO: this creates separate button element
5424 //renderPseudoElement(":before", elm);
5426 void CGroupHTML::htmlBUTTONend(const CHtmlElement
&elm
)
5428 // TODO: this creates separate button element
5429 //renderPseudoElement(":after", elm);
5432 popIfNotEmpty(_Link
);
5433 popIfNotEmpty(_LinkTitle
);
5434 popIfNotEmpty(_LinkClass
);
5437 // ***************************************************************************
5438 void CGroupHTML::htmlDD(const CHtmlElement
&elm
)
5443 // if there was no closing tag for <dt>, then remove <dt> style
5446 nlwarning("BUG: nested DT in DD");
5447 _DL
.back().DT
= false;
5452 nlwarning("BUG: nested DD in DD");
5453 _DL
.back().DD
= false;
5454 popIfNotEmpty(_Indent
);
5457 _DL
.back().DD
= true;
5458 _Indent
.push_back(getIndent() + ULIndent
);
5463 newParagraph(ULBeginSpace
);
5467 newParagraph(LIBeginSpace
);
5470 renderPseudoElement(":before", elm
);
5473 void CGroupHTML::htmlDDend(const CHtmlElement
&elm
)
5478 renderPseudoElement(":after", elm
);
5480 // parser will process two DD in a row as nested when first DD is not closed
5483 _DL
.back().DD
= false;
5484 popIfNotEmpty(_Indent
);
5488 // ***************************************************************************
5489 void CGroupHTML::htmlDIV(const CHtmlElement
&elm
)
5491 _DivName
= elm
.getAttribute("name");
5493 string instClass
= elm
.getAttribute("class");
5495 // use generic template system
5496 if (_TrustedDomain
&& !instClass
.empty() && instClass
== "ryzom-ui-grouptemplate")
5498 string style
= elm
.getAttribute("style");
5499 string id
= elm
.getAttribute("id");
5501 id
= "DIV" + toString(getNextAutoIdSeq());
5503 typedef pair
<string
, string
> TTmplParam
;
5504 vector
<TTmplParam
> tmplParams
;
5506 string templateName
;
5509 TStyle styles
= parseStyle(style
);
5510 TStyle::iterator it
;
5511 for (it
=styles
.begin(); it
!= styles
.end(); it
++)
5513 if ((*it
).first
== "template")
5514 templateName
= (*it
).second
;
5516 tmplParams
.push_back(TTmplParam((*it
).first
, (*it
).second
));
5520 if (!templateName
.empty())
5523 bool haveParentDiv
= getDiv() != NULL
;
5525 parentId
= getDiv()->getId();
5531 parentId
= _Paragraph
->getId();
5534 CInterfaceGroup
*inst
= CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName
, parentId
, tmplParams
);
5537 inst
->setId(parentId
+":"+id
);
5538 inst
->updateCoords();
5541 inst
->setParent(getDiv());
5542 inst
->setParentSize(getDiv());
5543 inst
->setParentPos(getDiv());
5544 inst
->setPosRef(Hotspot_TL
);
5545 inst
->setParentPosRef(Hotspot_TL
);
5546 getDiv()->addGroup(inst
);
5550 getParagraph()->addChild(inst
);
5553 _Divs
.push_back(inst
);
5558 renderPseudoElement(":before", elm
);
5561 void CGroupHTML::htmlDIVend(const CHtmlElement
&elm
)
5563 renderPseudoElement(":after", elm
);
5565 popIfNotEmpty(_Divs
);
5568 // ***************************************************************************
5569 void CGroupHTML::htmlDL(const CHtmlElement
&elm
)
5571 _DL
.push_back(HTMLDListElement());
5572 _LI
= _DL
.size() > 1 || !_UL
.empty();
5574 renderPseudoElement(":before", elm
);
5577 void CGroupHTML::htmlDLend(const CHtmlElement
&elm
)
5582 renderPseudoElement(":after", elm
);
5587 nlwarning("BUG: unclosed DT in DL");
5593 popIfNotEmpty(_Indent
);
5594 nlwarning("BUG: unclosed DD in DL");
5597 popIfNotEmpty (_DL
);
5600 // ***************************************************************************
5601 void CGroupHTML::htmlDT(const CHtmlElement
&elm
)
5606 // TODO: check if nested tags still happen and fix it in parser
5607 // : remove special handling for nesting and let it happen
5609 // html parser and libxml2 should prevent nested tags like these
5612 nlwarning("BUG: nested DD in DT");
5614 _DL
.back().DD
= false;
5615 popIfNotEmpty(_Indent
);
5618 // html parser and libxml2 should prevent nested tags like these
5621 nlwarning("BUG: nested DT in DT");
5624 _DL
.back().DT
= true;
5629 newParagraph(ULBeginSpace
);
5633 newParagraph(LIBeginSpace
);
5636 renderPseudoElement(":before", elm
);
5639 void CGroupHTML::htmlDTend(const CHtmlElement
&elm
)
5644 renderPseudoElement(":after", elm
);
5646 _DL
.back().DT
= false;
5649 // ***************************************************************************
5650 void CGroupHTML::htmlFONT(const CHtmlElement
&elm
)
5652 if (elm
.hasNonEmptyAttribute("color"))
5655 if (scanHTMLColor(elm
.getAttribute("color").c_str(), color
))
5656 _Style
.Current
.TextColor
= color
;
5659 if (elm
.hasNonEmptyAttribute("size"))
5662 fromString(elm
.getAttribute("size"), fontsize
);
5663 _Style
.Current
.FontSize
= fontsize
;
5667 // ***************************************************************************
5668 void CGroupHTML::htmlFORM(const CHtmlElement
&elm
)
5673 CGroupHTML::CForm form
;
5674 // id check is case sensitive and auto id's are uppercase
5675 form
.id
= toLowerAscii(trim(elm
.getAttribute("id")));
5676 if (form
.id
.empty())
5678 form
.id
= toString("FORM%d", _Forms
.size());
5681 // Get the action name
5682 if (elm
.hasNonEmptyAttribute("action"))
5684 form
.Action
= getAbsoluteUrl(elm
.getAttribute("action"));
5691 _Forms
.push_back(form
);
5693 renderPseudoElement(":before", elm
);
5696 void CGroupHTML::htmlFORMend(const CHtmlElement
&elm
)
5699 renderPseudoElement(":after", elm
);
5702 // ***************************************************************************
5703 void CGroupHTML::htmlH(const CHtmlElement
&elm
)
5705 newParagraph(PBeginSpace
);
5706 renderPseudoElement(":before", elm
);
5709 void CGroupHTML::htmlHend(const CHtmlElement
&elm
)
5711 renderPseudoElement(":after", elm
);
5714 // ***************************************************************************
5715 void CGroupHTML::htmlHEAD(const CHtmlElement
&elm
)
5717 _ReadingHeadTag
= !_IgnoreHeadTag
;
5718 _IgnoreHeadTag
= true;
5721 void CGroupHTML::htmlHEADend(const CHtmlElement
&elm
)
5723 _ReadingHeadTag
= false;
5726 // ***************************************************************************
5727 void CGroupHTML::htmlHR(const CHtmlElement
&elm
)
5729 CInterfaceGroup
*sep
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_hr", "", NULL
, 0);
5732 CViewBitmap
*bitmap
= dynamic_cast<CViewBitmap
*>(sep
->getView("hr"));
5735 bitmap
->setColor(_Style
.Current
.TextColor
);
5736 if (_Style
.Current
.Width
> 0)
5738 clamp(_Style
.Current
.Width
, 1, 32000);
5739 bitmap
->setW(_Style
.Current
.Width
);
5740 bitmap
->setSizeRef(CInterfaceElement::none
);
5742 if (_Style
.Current
.Height
> 0)
5744 clamp(_Style
.Current
.Height
, 1, 1000);
5745 bitmap
->setH(_Style
.Current
.Height
);
5749 renderPseudoElement(":before", elm
);
5750 addHtmlGroup(sep
, 0);
5751 renderPseudoElement(":after", elm
);
5755 // ***************************************************************************
5756 void CGroupHTML::htmlHTML(const CHtmlElement
&elm
)
5758 if (elm
.hasNonEmptyAttribute("style"))
5759 _Style
.applyStyle(elm
.getAttribute("style"));
5761 _Style
.Root
= _Style
.Current
;
5763 setupBackground(&m_HtmlBackground
);
5766 // ***************************************************************************
5767 void CGroupHTML::htmlI(const CHtmlElement
&elm
)
5770 renderPseudoElement(":before", elm
);
5773 void CGroupHTML::htmlIend(const CHtmlElement
&elm
)
5775 renderPseudoElement(":after", elm
);
5779 // ***************************************************************************
5780 void CGroupHTML::htmlIMG(const CHtmlElement
&elm
)
5782 std::string src
= trim(elm
.getAttribute("src"));
5785 // no 'src' attribute, or empty
5790 std::string id
= elm
.getAttribute("id");
5792 if (elm
.hasNonEmptyAttribute("width"))
5793 getPercentage(_Style
.Current
.Width
, tmpf
, elm
.getAttribute("width").c_str());
5794 if (elm
.hasNonEmptyAttribute("height"))
5795 getPercentage(_Style
.Current
.Height
, tmpf
, elm
.getAttribute("height").c_str());
5797 // Get the global color name
5798 if (elm
.hasAttribute("global_color"))
5799 _Style
.Current
.GlobalColor
= true;
5802 // keep "alt" attribute for backward compatibility
5803 std::string tooltip
= elm
.getAttribute("alt");
5805 if (elm
.hasNonEmptyAttribute("title"))
5806 tooltip
= elm
.getAttribute("title");
5809 string overSrc
= elm
.getAttribute("data-over-src");
5811 // inside a/button with valid url (ie, button is not disabled)
5812 string url
= getLink();
5813 if (getA() && !url
.empty() && getParent() && getParent()->getParent())
5815 string params
= "name=" + getId() + "|url=" + url
;
5816 addButton(CCtrlButton::PushButton
, id
, src
, src
, overSrc
, "browse", params
.c_str(), tooltip
, _Style
.Current
);
5819 if (!tooltip
.empty() || !overSrc
.empty())
5821 addButton(CCtrlButton::PushButton
, id
, src
, src
, overSrc
, "", "", tooltip
, _Style
.Current
);
5825 // Get the option to reload (class==reload)
5826 bool reloadImg
= false;
5828 if (elm
.hasNonEmptyAttribute("style"))
5830 string styleString
= elm
.getAttribute("style");
5831 TStyle styles
= parseStyle(styleString
);
5832 TStyle::iterator it
;
5834 it
= styles
.find("reload");
5835 if (it
!= styles
.end() && (*it
).second
== "1")
5839 addImage(id
, elm
.getAttribute("src"), reloadImg
, _Style
.Current
);
5843 // ***************************************************************************
5844 void CGroupHTML::htmlINPUT(const CHtmlElement
&elm
)
5849 // read general property
5850 string id
= elm
.getAttribute("id");
5852 // Widget template name (old)
5853 string templateName
= elm
.getAttribute("z_btn_tmpl");
5854 // Input name is the new
5855 if (elm
.hasNonEmptyAttribute("z_input_tmpl"))
5856 templateName
= elm
.getAttribute("z_input_tmpl");
5858 // Widget minimal width
5859 uint32 minWidth
= 0;
5860 fromString(elm
.getAttribute("z_input_width"), minWidth
);
5862 // <input type="...">
5863 std::string type
= trim(elm
.getAttribute("type"));
5866 // no 'type' attribute, or empty
5870 // Global color flag
5871 if (elm
.hasAttribute("global_color"))
5872 _Style
.Current
.GlobalColor
= true;
5875 std::string tooltip
= elm
.getAttribute("alt");
5877 if (type
== "image")
5879 string name
= elm
.getAttribute("name");
5880 string src
= elm
.getAttribute("src");
5881 string over
= elm
.getAttribute("data-over-src");
5882 string formId
= elm
.getAttribute("form");
5883 string formAction
= elm
.getAttribute("formaction");
5885 if (formId
.empty() && _FormOpen
) {
5886 formId
= _Forms
.back().id
;
5889 insertFormImageButton(name
, tooltip
, src
, over
, formId
, formAction
, minWidth
, templateName
);
5891 else if (type
== "button" || type
== "submit")
5893 string name
= elm
.getAttribute("name");
5894 string value
= elm
.getAttribute("value");
5895 string formId
= elm
.getAttribute("form");
5896 string formAction
= elm
.getAttribute("formaction");
5898 if (formId
.empty() && _FormOpen
) {
5899 formId
= _Forms
.back().id
;
5902 insertFormTextButton(name
, tooltip
, value
, formId
, formAction
, minWidth
, templateName
);
5904 else if (type
== "text")
5906 // Get the string name
5907 string name
= elm
.getAttribute("name");
5908 string ucValue
= elm
.getAttribute("value");
5911 uint maxlength
= 1024;
5912 if (elm
.hasNonEmptyAttribute("size"))
5913 fromString(elm
.getAttribute("size"), size
);
5914 if (elm
.hasNonEmptyAttribute("maxlength"))
5915 fromString(elm
.getAttribute("maxlength"), maxlength
);
5917 // ryzom client used to have 'size' attribute in pixels, (12 == was default font size)
5918 if (_Style
.hasStyle("-ryzom-input-size-px") && _Style
.getStyle("-ryzom-input-size-px") == "true")
5921 string
textTemplate(!templateName
.empty() ? templateName
: DefaultFormTextGroup
);
5923 CInterfaceGroup
*textArea
= addTextArea (textTemplate
, name
.c_str (), 1, size
, false, ucValue
, maxlength
);
5926 // Add the text area to the form
5927 CGroupHTML::CForm::CEntry entry
;
5929 entry
.TextArea
= textArea
;
5930 _Forms
.back().Entries
.push_back (entry
);
5933 else if (type
== "checkbox" || type
== "radio")
5935 renderPseudoElement(":before", elm
);
5937 CCtrlButton::EType btnType
;
5938 string name
= elm
.getAttribute("name");
5939 string normal
= elm
.getAttribute("src");
5942 string ucValue
= "on";
5943 bool checked
= elm
.hasAttribute("checked");
5945 // TODO: unknown if empty attribute should override or not
5946 if (elm
.hasNonEmptyAttribute("value"))
5947 ucValue
= elm
.getAttribute("value");
5949 if (type
== "radio")
5951 btnType
= CCtrlButton::RadioButton
;
5952 normal
= DefaultRadioButtonBitmapNormal
;
5953 pushed
= DefaultRadioButtonBitmapPushed
;
5954 over
= DefaultRadioButtonBitmapOver
;
5958 btnType
= CCtrlButton::ToggleButton
;
5959 normal
= DefaultCheckBoxBitmapNormal
;
5960 pushed
= DefaultCheckBoxBitmapPushed
;
5961 over
= DefaultCheckBoxBitmapOver
;
5964 // Add the ctrl button
5965 CCtrlButton
*checkbox
= addButton (btnType
, name
, normal
, pushed
, over
, "", "", tooltip
, _Style
.Current
);
5968 if (btnType
== CCtrlButton::RadioButton
)
5970 // override with 'id' because radio buttons share same name
5972 checkbox
->setId(id
);
5974 // group together buttons with same name
5975 CForm
&form
= _Forms
.back();
5976 bool notfound
= true;
5977 for (uint i
=0; i
<form
.Entries
.size(); i
++)
5979 if (form
.Entries
[i
].Name
== name
&& form
.Entries
[i
].Checkbox
->getType() == CCtrlButton::RadioButton
)
5981 checkbox
->initRBRefFromRadioButton(form
.Entries
[i
].Checkbox
);
5988 // this will start a new group (initRBRef() would take first button in group container otherwise)
5989 checkbox
->initRBRefFromRadioButton(checkbox
);
5993 checkbox
->setPushed (checked
);
5995 // Add the button to the form
5996 CGroupHTML::CForm::CEntry entry
;
5998 entry
.Value
= decodeHTMLEntities(ucValue
);
5999 entry
.Checkbox
= checkbox
;
6000 _Forms
.back().Entries
.push_back (entry
);
6002 renderPseudoElement(":after", elm
);
6004 else if (type
== "hidden")
6006 if (elm
.hasNonEmptyAttribute("name"))
6009 string name
= elm
.getAttribute("name");
6012 string ucValue
= elm
.getAttribute("value");
6015 CGroupHTML::CForm::CEntry entry
;
6017 entry
.Value
= decodeHTMLEntities(ucValue
);
6018 _Forms
.back().Entries
.push_back (entry
);
6023 // ***************************************************************************
6024 void CGroupHTML::htmlLI(const CHtmlElement
&elm
)
6029 // UL, OL top margin if this is the first LI
6033 newParagraph(ULBeginSpace
);
6037 newParagraph(LIBeginSpace
);
6040 // OL list index can be overridden by <li value="1"> attribute
6041 if (elm
.hasNonEmptyAttribute("value"))
6042 fromString(elm
.getAttribute("value"), _UL
.back().Value
);
6044 string str
= _UL
.back().getListMarkerText();
6047 // list-style-type: outside
6048 if (_CurrentViewLink
)
6050 getParagraph()->setFirstViewIndent(-_CurrentViewLink
->getMaxUsedW());
6056 renderPseudoElement(":before", elm
);
6061 void CGroupHTML::htmlLIend(const CHtmlElement
&elm
)
6063 renderPseudoElement(":after", elm
);
6066 // ***************************************************************************
6067 void CGroupHTML::htmlLUA(const CHtmlElement
&elm
)
6069 // we receive an embeded lua script
6070 _ParsingLua
= _TrustedDomain
; // Only parse lua if TrustedDomain
6074 void CGroupHTML::htmlLUAend(const CHtmlElement
&elm
)
6076 if (_ParsingLua
&& _TrustedDomain
)
6078 _ParsingLua
= false;
6079 // execute the embeded lua script
6080 _LuaScript
= "\nlocal __CURRENT_WINDOW__=\""+this->_Id
+"\" \n"+_LuaScript
;
6081 CLuaManager::getInstance().executeLuaScript(_LuaScript
, true);
6085 // ***************************************************************************
6086 void CGroupHTML::htmlMETA(const CHtmlElement
&elm
)
6088 if (!_ReadingHeadTag
)
6091 std::string httpEquiv
= elm
.getAttribute("http-equiv");
6092 std::string httpContent
= elm
.getAttribute("content");
6093 if (httpEquiv
.empty() || httpContent
.empty())
6098 // only first http-equiv="refresh" should be handled
6099 if (_RefreshUrl
.empty() && httpEquiv
== "refresh")
6101 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
6102 double timeSec
= times
.thisFrameMs
/ 1000.0f
;
6104 string::size_type pos
= httpContent
.find_first_of(";");
6105 if (pos
== string::npos
)
6107 fromString(httpContent
, _NextRefreshTime
);
6112 fromString(httpContent
.substr(0, pos
), _NextRefreshTime
);
6114 pos
= toLowerAscii(httpContent
).find("url=");
6115 if (pos
!= string::npos
)
6116 _RefreshUrl
= getAbsoluteUrl(httpContent
.substr(pos
+ 4));
6119 _NextRefreshTime
+= timeSec
;
6123 // ***************************************************************************
6124 void CGroupHTML::htmlMETER(const CHtmlElement
&elm
)
6126 HTMLMeterElement meter
;
6127 meter
.readValues(elm
);
6129 std::string id
= "meter";
6130 if (elm
.hasAttribute("id"))
6131 id
= elm
.getAttribute("id");
6133 // width: 5em, height: 1em
6134 uint32 width
= _Style
.Current
.Width
> -1 ? _Style
.Current
.Width
: _Style
.Current
.FontSize
* 5;
6135 uint32 height
= _Style
.Current
.Height
> -1 ? _Style
.Current
.Height
: _Style
.Current
.FontSize
;
6136 // FIXME: only using border-top
6137 uint32 border
= _Style
.Current
.Border
.Top
.Width
.getValue() > -1 ? _Style
.Current
.Border
.Top
.Width
.getValue() : 0;
6139 uint barw
= (uint
) (width
* meter
.getValueRatio());
6140 CRGBA bgColor
= meter
.getBarColor(elm
, _Style
);
6141 CRGBA valueColor
= meter
.getValueColor(elm
, _Style
);
6143 typedef pair
<string
, string
> TTmplParam
;
6144 vector
<TTmplParam
> tmplParams
;
6145 tmplParams
.push_back(TTmplParam("id", id
));
6146 tmplParams
.push_back(TTmplParam("active", "true"));
6147 tmplParams
.push_back(TTmplParam("w", toString(width
)));
6148 tmplParams
.push_back(TTmplParam("h", toString(height
)));
6149 tmplParams
.push_back(TTmplParam("border_x2", toString(border
*2)));
6150 tmplParams
.push_back(TTmplParam("bgtexture", "blank.tga"));
6151 tmplParams
.push_back(TTmplParam("bgcolor", bgColor
.toString()));
6152 tmplParams
.push_back(TTmplParam("value_w", toString(barw
)));
6153 tmplParams
.push_back(TTmplParam("value_texture", "blank.tga"));
6154 tmplParams
.push_back(TTmplParam("value_color", valueColor
.toString()));
6156 CInterfaceGroup
*gr
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_meter", getParagraph()->getId(), &tmplParams
[0], (uint
)tmplParams
.size());
6159 renderPseudoElement(":before", elm
);
6160 getParagraph()->addChild(gr
);
6161 renderPseudoElement(":after", elm
);
6163 // ignore any inner elements
6164 _IgnoreChildElements
= true;
6168 // ***************************************************************************
6169 void CGroupHTML::htmlOBJECT(const CHtmlElement
&elm
)
6171 _ObjectType
= elm
.getAttribute("type");
6172 _ObjectData
= elm
.getAttribute("data");
6173 _ObjectMD5Sum
= elm
.getAttribute("id");
6174 _ObjectAction
= elm
.getAttribute("standby");
6178 void CGroupHTML::htmlOBJECTend(const CHtmlElement
&elm
)
6180 if (!_TrustedDomain
)
6183 if (_ObjectType
=="application/ryzom-data")
6185 if (!_ObjectData
.empty())
6187 if (addBnpDownload(_ObjectData
, _ObjectAction
, _ObjectScript
, _ObjectMD5Sum
))
6189 CLuaManager::getInstance().executeLuaScript("\nlocal __ALLREADYDL__=true\n"+_ObjectScript
, true);
6191 _ObjectScript
.clear();
6197 // ***************************************************************************
6198 void CGroupHTML::htmlOL(const CHtmlElement
&elm
)
6201 std::string
type("1");
6203 if (elm
.hasNonEmptyAttribute("start"))
6204 fromString(elm
.getAttribute("start"), start
);
6205 if (elm
.hasNonEmptyAttribute("type"))
6206 type
= elm
.getAttribute("type");
6208 _UL
.push_back(HTMLOListElement(start
, type
));
6209 // if LI is already present
6210 _LI
= _UL
.size() > 1 || _DL
.size() > 1;
6211 _Indent
.push_back(getIndent() + ULIndent
);
6213 renderPseudoElement(":before", elm
);
6216 void CGroupHTML::htmlOLend(const CHtmlElement
&elm
)
6221 // ***************************************************************************
6222 void CGroupHTML::htmlOPTION(const CHtmlElement
&elm
)
6224 _SelectOption
= true;
6225 _SelectOptionStr
.clear();
6228 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6231 _Forms
.back().Entries
.back().SelectValues
.push_back(elm
.getAttribute("value"));
6233 if (elm
.hasAttribute("selected"))
6234 _Forms
.back().Entries
.back().InitialSelection
= (sint
)_Forms
.back().Entries
.back().SelectValues
.size() - 1;
6236 if (elm
.hasAttribute("disabled"))
6237 _Forms
.back().Entries
.back().sbOptionDisabled
= (sint
)_Forms
.back().Entries
.back().SelectValues
.size() - 1;
6240 void CGroupHTML::htmlOPTIONend(const CHtmlElement
&elm
)
6242 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6245 // use option text as value
6246 if (!elm
.hasAttribute("value"))
6248 _Forms
.back().Entries
.back().SelectValues
.back() = _SelectOptionStr
;
6251 // insert the parsed text into the select control
6252 CDBGroupComboBox
*cb
= _Forms
.back().Entries
.back().ComboBox
;
6255 uint lineIndex
= cb
->getNumTexts();
6256 cb
->addText(_SelectOptionStr
);
6257 if (_Forms
.back().Entries
.back().sbOptionDisabled
== lineIndex
)
6259 cb
->setGrayed(lineIndex
, true);
6264 CGroupMenu
*sb
= _Forms
.back().Entries
.back().SelectBox
;
6267 uint lineIndex
= sb
->getNumLine();
6268 sb
->addLine(_SelectOptionStr
, "", "");
6270 if (_Forms
.back().Entries
.back().sbOptionDisabled
== lineIndex
)
6272 sb
->setGrayedLine(lineIndex
, true);
6276 // create option line checkbox, CGroupMenu is taking ownership of the checbox
6277 CInterfaceGroup
*ig
= CWidgetManager::getInstance()->getParser()->createGroupInstance("menu_checkbox", "", NULL
, 0);
6280 CCtrlButton
*cb
= dynamic_cast<CCtrlButton
*>(ig
->getCtrl("b"));
6283 if (_Forms
.back().Entries
.back().sbMultiple
)
6285 cb
->setType(CCtrlButton::ToggleButton
);
6286 cb
->setTexture(DefaultCheckBoxBitmapNormal
);
6287 cb
->setTexturePushed(DefaultCheckBoxBitmapPushed
);
6288 cb
->setTextureOver(DefaultCheckBoxBitmapOver
);
6292 cb
->setType(CCtrlButton::RadioButton
);
6293 cb
->setTexture(DefaultRadioButtonBitmapNormal
);
6294 cb
->setTexturePushed(DefaultRadioButtonBitmapPushed
);
6295 cb
->setTextureOver(DefaultRadioButtonBitmapOver
);
6297 if (_Forms
.back().Entries
.back().sbRBRef
== NULL
)
6298 _Forms
.back().Entries
.back().sbRBRef
= cb
;
6300 cb
->initRBRefFromRadioButton(_Forms
.back().Entries
.back().sbRBRef
);
6303 cb
->setPushed(_Forms
.back().Entries
.back().InitialSelection
== lineIndex
);
6304 sb
->setUserGroupLeft(lineIndex
, ig
);
6308 nlwarning("Failed to get 'b' element from 'menu_checkbox' template");
6317 // ***************************************************************************
6318 void CGroupHTML::htmlP(const CHtmlElement
&elm
)
6320 newParagraph(PBeginSpace
);
6321 renderPseudoElement(":before", elm
);
6324 void CGroupHTML::htmlPend(const CHtmlElement
&elm
)
6326 renderPseudoElement(":after", elm
);
6329 // ***************************************************************************
6330 void CGroupHTML::htmlPRE(const CHtmlElement
&elm
)
6332 _PRE
.push_back(true);
6335 renderPseudoElement(":before", elm
);
6338 void CGroupHTML::htmlPREend(const CHtmlElement
&elm
)
6340 renderPseudoElement(":after", elm
);
6342 popIfNotEmpty(_PRE
);
6345 // ***************************************************************************
6346 void CGroupHTML::htmlPROGRESS(const CHtmlElement
&elm
)
6348 HTMLProgressElement progress
;
6349 progress
.readValues(elm
);
6351 std::string id
= "progress";
6352 if (elm
.hasAttribute("id"))
6353 id
= elm
.getAttribute("id");
6355 // width: 10em, height: 1em
6356 uint32 width
= _Style
.Current
.Width
> -1 ? _Style
.Current
.Width
: _Style
.Current
.FontSize
* 10;
6357 uint32 height
= _Style
.Current
.Height
> -1 ? _Style
.Current
.Height
: _Style
.Current
.FontSize
;
6358 // FIXME: only using border-top
6359 uint32 border
= _Style
.Current
.Border
.Top
.Width
.getValue() > -1 ? _Style
.Current
.Border
.Top
.Width
.getValue() : 0;
6361 uint barw
= (uint
) (width
* progress
.getValueRatio());
6362 CRGBA bgColor
= progress
.getBarColor(elm
, _Style
);
6363 CRGBA valueColor
= progress
.getValueColor(elm
, _Style
);
6365 typedef pair
<string
, string
> TTmplParam
;
6366 vector
<TTmplParam
> tmplParams
;
6367 tmplParams
.push_back(TTmplParam("id", id
));
6368 tmplParams
.push_back(TTmplParam("active", "true"));
6369 tmplParams
.push_back(TTmplParam("w", toString(width
)));
6370 tmplParams
.push_back(TTmplParam("h", toString(height
)));
6371 tmplParams
.push_back(TTmplParam("border_x2", toString(border
*2)));
6372 tmplParams
.push_back(TTmplParam("bgtexture", "blank.tga"));
6373 tmplParams
.push_back(TTmplParam("bgcolor", bgColor
.toString()));
6374 tmplParams
.push_back(TTmplParam("value_w", toString(barw
)));
6375 tmplParams
.push_back(TTmplParam("value_texture", "blank.tga"));
6376 tmplParams
.push_back(TTmplParam("value_color", valueColor
.toString()));
6378 CInterfaceGroup
*gr
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_progress", getParagraph()->getId(), &tmplParams
[0], (uint
)tmplParams
.size());
6381 renderPseudoElement(":before", elm
);
6382 getParagraph()->addChild(gr
);
6383 renderPseudoElement(":after", elm
);
6385 // ignore any inner elements
6386 _IgnoreChildElements
= true;
6390 // ***************************************************************************
6391 void CGroupHTML::htmlSCRIPT(const CHtmlElement
&elm
)
6396 void CGroupHTML::htmlSCRIPTend(const CHtmlElement
&elm
)
6398 _IgnoreText
= false;
6401 // ***************************************************************************
6402 void CGroupHTML::htmlSELECT(const CHtmlElement
&elm
)
6408 string name
= elm
.getAttribute("name");
6409 bool multiple
= elm
.hasAttribute("multiple");
6412 if (elm
.hasNonEmptyAttribute("size"))
6413 fromString(elm
.getAttribute("size"), size
);
6415 CGroupHTML::CForm::CEntry entry
;
6417 entry
.sbMultiple
= multiple
;
6418 if (size
> 1 || multiple
)
6420 entry
.InitialSelection
= -1;
6421 CGroupMenu
*sb
= addSelectBox(DefaultFormSelectBoxMenuGroup
, name
.c_str());
6427 if (_Style
.Current
.Width
> -1)
6428 sb
->setMinW(_Style
.Current
.Width
);
6430 if (_Style
.Current
.Height
> -1)
6431 sb
->setMinH(_Style
.Current
.Height
);
6433 sb
->setMaxVisibleLine(size
);
6434 sb
->setFontSize(_Style
.Current
.FontSize
, false);
6437 entry
.SelectBox
= sb
;
6441 CDBGroupComboBox
*cb
= addComboBox(DefaultFormSelectGroup
, name
.c_str());
6442 entry
.ComboBox
= cb
;
6448 setTextStyle(cb
->getViewText(), _Style
.Current
);
6451 _Forms
.back().Entries
.push_back (entry
);
6454 void CGroupHTML::htmlSELECTend(const CHtmlElement
&elm
)
6456 _SelectOption
= false;
6457 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6460 CDBGroupComboBox
*cb
= _Forms
.back().Entries
.back().ComboBox
;
6463 cb
->setSelectionNoTrigger(_Forms
.back().Entries
.back().InitialSelection
);
6464 // TODO: magic padding
6465 cb
->setW(cb
->evalContentWidth() + 16);
6469 // ***************************************************************************
6470 void CGroupHTML::htmlSTYLE(const CHtmlElement
&elm
)
6475 void CGroupHTML::htmlSTYLEend(const CHtmlElement
&elm
)
6477 _IgnoreText
= false;
6480 // ***************************************************************************
6481 void CGroupHTML::htmlTABLE(const CHtmlElement
&elm
)
6483 // Get cells parameters
6484 getCellsParameters(elm
, false);
6486 CGroupTable
*table
= new CGroupTable(TCtorParam());
6488 if (elm
.hasNonEmptyAttribute("id"))
6489 table
->setId(getCurrentGroup()->getId() + ":" + elm
.getAttribute("id"));
6491 table
->setId(getCurrentGroup()->getId() + ":TABLE" + toString(getNextAutoIdSeq()));
6493 // TODO: border-spacing: 2em;
6495 if (elm
.hasNonEmptyAttribute("cellspacing"))
6496 fromString(elm
.getAttribute("cellspacing"), table
->CellSpacing
);
6498 // TODO: cssLength, horiz/vert values
6499 if (_Style
.hasStyle("border-spacing"))
6500 fromString(_Style
.getStyle("border-spacing"), table
->CellSpacing
);
6502 // overrides border-spacing if set to 'collapse'
6503 if (_Style
.checkStyle("border-collapse", "collapse"))
6504 table
->CellSpacing
= 0;
6507 if (elm
.hasNonEmptyAttribute("cellpadding"))
6508 fromString(elm
.getAttribute("cellpadding"), table
->CellPadding
);
6510 if (_Style
.hasStyle("width"))
6512 // _Style.Width does not handle '%' unit currently
6513 if (_Style
.Current
.Width
> 0)
6515 table
->ForceWidthMin
= _Style
.Current
.Width
;
6516 table
->TableRatio
= 0;
6520 getPercentage (table
->ForceWidthMin
, table
->TableRatio
, _Style
.getStyle("width").c_str());
6523 else if (elm
.hasNonEmptyAttribute("width"))
6525 getPercentage (table
->ForceWidthMin
, table
->TableRatio
, elm
.getAttribute("width").c_str());
6528 // border from css or from attribute
6530 CSSRect
<CSSBorder
> border
;
6531 border
.Top
.Color
= _Style
.Current
.TextColor
;
6532 border
.Right
.Color
= _Style
.Current
.TextColor
;
6533 border
.Bottom
.Color
= _Style
.Current
.TextColor
;
6534 border
.Left
.Color
= _Style
.Current
.TextColor
;
6536 if (elm
.hasAttribute("border"))
6538 uint32 borderWidth
= 0;
6539 CRGBA borderColor
= CRGBA::Transparent
;
6541 std::string s
= elm
.getAttribute("border");
6545 fromString(elm
.getAttribute("border"), borderWidth
);
6547 if (elm
.hasNonEmptyAttribute("bordercolor"))
6548 scanHTMLColor(elm
.getAttribute("bordercolor").c_str(), borderColor
);
6550 borderColor
= CRGBA(128, 128, 128, 255);
6552 table
->CellBorder
= (borderWidth
> 0);
6554 border
.Top
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6555 border
.Right
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6556 border
.Bottom
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6557 border
.Left
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6560 if (_Style
.hasStyle("border-top-width")) border
.Top
.Width
= _Style
.Current
.Border
.Top
.Width
;
6561 if (_Style
.hasStyle("border-right-width")) border
.Right
.Width
= _Style
.Current
.Border
.Right
.Width
;
6562 if (_Style
.hasStyle("border-bottom-width")) border
.Bottom
.Width
= _Style
.Current
.Border
.Bottom
.Width
;
6563 if (_Style
.hasStyle("border-left-width")) border
.Left
.Width
= _Style
.Current
.Border
.Left
.Width
;
6565 if (_Style
.hasStyle("border-top-color")) border
.Top
.Color
= _Style
.Current
.Border
.Top
.Color
;
6566 if (_Style
.hasStyle("border-right-color")) border
.Right
.Color
= _Style
.Current
.Border
.Right
.Color
;
6567 if (_Style
.hasStyle("border-bottom-color")) border
.Bottom
.Color
= _Style
.Current
.Border
.Bottom
.Color
;
6568 if (_Style
.hasStyle("border-left-color")) border
.Left
.Color
= _Style
.Current
.Border
.Left
.Color
;
6570 if (_Style
.hasStyle("border-top-style")) border
.Top
.Style
= _Style
.Current
.Border
.Top
.Style
;
6571 if (_Style
.hasStyle("border-right-style")) border
.Right
.Style
= _Style
.Current
.Border
.Right
.Style
;
6572 if (_Style
.hasStyle("border-bottom-style")) border
.Bottom
.Style
= _Style
.Current
.Border
.Bottom
.Style
;
6573 if (_Style
.hasStyle("border-left-style")) border
.Left
.Style
= _Style
.Current
.Border
.Left
.Style
;
6575 table
->Border
->setBorder(border
);
6576 table
->Border
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
6577 table
->Border
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
6580 setupBackground(table
->Background
);
6581 table
->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
6583 table
->setMarginLeft(getIndent());
6584 addHtmlGroup (table
, 0);
6586 renderPseudoElement(":before", elm
);
6588 _Tables
.push_back(table
);
6590 // Add a cell pointer
6591 _Cells
.push_back(NULL
);
6592 _TR
.push_back(false);
6593 _Indent
.push_back(0);
6596 void CGroupHTML::htmlTABLEend(const CHtmlElement
&elm
)
6598 popIfNotEmpty(_CellParams
);
6600 popIfNotEmpty(_Cells
);
6601 popIfNotEmpty(_Tables
);
6602 popIfNotEmpty(_Indent
);
6604 renderPseudoElement(":after", elm
);
6607 // ***************************************************************************
6608 void CGroupHTML::htmlTD(const CHtmlElement
&elm
)
6610 CRGBA rowColor
= CRGBA::Transparent
;
6611 // remember row color so we can blend it with cell color
6612 if (!_CellParams
.empty())
6613 rowColor
= _CellParams
.back().BgColor
;
6615 // Get cells parameters
6616 getCellsParameters(elm
, true);
6618 // if cell has own background,then it must be blended with row
6619 if (rowColor
.A
> 0 && _Style
.Current
.Background
.color
.A
< 255)
6621 _Style
.Current
.Background
.color
.blendFromui(rowColor
,
6622 _Style
.Current
.Background
.color
, _Style
.Current
.Background
.color
.A
);
6625 if (elm
.ID
== HTML_TH
)
6627 if (!_Style
.hasStyle("font-weight"))
6628 _Style
.Current
.FontWeight
= FONT_WEIGHT_BOLD
;
6629 // center if not specified otherwise.
6630 if (!elm
.hasNonEmptyAttribute("align") && !_Style
.hasStyle("text-align"))
6631 _CellParams
.back().Align
= CGroupCell::Center
;
6634 CGroupTable
*table
= getTable();
6637 // <td> appears to be outside <table>
6643 // <table> not started
6647 _Cells
.back() = new CGroupCell(CViewBase::TCtorParam());
6648 if (elm
.hasNonEmptyAttribute("id"))
6649 _Cells
.back()->setId(table
->getId() + ":" + elm
.getAttribute("id"));
6651 _Cells
.back()->setId(table
->getId() + ":TD" + toString(getNextAutoIdSeq()));
6652 // inner cell content
6653 _Cells
.back()->Group
->setId(_Cells
.back()->getId() + ":CELL");
6655 setupBackground(_Cells
.back()->Background
);
6656 _Cells
.back()->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
6658 if (elm
.hasNonEmptyAttribute("colspan"))
6659 fromString(elm
.getAttribute("colspan"), _Cells
.back()->ColSpan
);
6660 if (elm
.hasNonEmptyAttribute("rowspan"))
6661 fromString(elm
.getAttribute("rowspan"), _Cells
.back()->RowSpan
);
6663 _Cells
.back()->Align
= _CellParams
.back().Align
;
6664 _Cells
.back()->VAlign
= _CellParams
.back().VAlign
;
6665 _Cells
.back()->LeftMargin
= _CellParams
.back().LeftMargin
;
6666 _Cells
.back()->NoWrap
= _CellParams
.back().NoWrap
;
6667 _Cells
.back()->ColSpan
= std::max(1, _Cells
.back()->ColSpan
);
6668 _Cells
.back()->RowSpan
= std::max(1, _Cells
.back()->RowSpan
);
6669 _Cells
.back()->Height
= _CellParams
.back().Height
;
6672 if (_Style
.hasStyle("width"))
6674 // _Style.Width does not handle '%' unit currently
6675 if (_Style
.Current
.Width
> 0)
6677 _Cells
.back()->WidthWanted
= _Style
.Current
.Width
;
6678 _Cells
.back()->TableRatio
= 0;
6682 getPercentage (_Cells
.back()->WidthWanted
, _Cells
.back()->TableRatio
, _Style
.getStyle("width").c_str());
6685 else if (elm
.hasNonEmptyAttribute("width"))
6687 getPercentage (_Cells
.back()->WidthWanted
, _Cells
.back()->TableRatio
, elm
.getAttribute("width").c_str());
6690 _Cells
.back()->NewLine
= getTR();
6692 CSSRect
<CSSBorder
> border
;
6693 border
.Top
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6694 border
.Right
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6695 border
.Bottom
.set(table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6696 border
.Left
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6698 if (_Style
.hasStyle("border-top-width")) border
.Top
.Width
= _Style
.Current
.Border
.Top
.Width
;
6699 if (_Style
.hasStyle("border-right-width")) border
.Right
.Width
= _Style
.Current
.Border
.Right
.Width
;
6700 if (_Style
.hasStyle("border-bottom-width")) border
.Bottom
.Width
= _Style
.Current
.Border
.Bottom
.Width
;
6701 if (_Style
.hasStyle("border-left-width")) border
.Left
.Width
= _Style
.Current
.Border
.Left
.Width
;
6703 if (_Style
.hasStyle("border-top-color")) border
.Top
.Color
= _Style
.Current
.Border
.Top
.Color
;
6704 if (_Style
.hasStyle("border-right-color")) border
.Right
.Color
= _Style
.Current
.Border
.Right
.Color
;
6705 if (_Style
.hasStyle("border-bottom-color")) border
.Bottom
.Color
= _Style
.Current
.Border
.Bottom
.Color
;
6706 if (_Style
.hasStyle("border-left-color")) border
.Left
.Color
= _Style
.Current
.Border
.Left
.Color
;
6708 if (_Style
.hasStyle("border-top-style")) border
.Top
.Style
= _Style
.Current
.Border
.Top
.Style
;
6709 if (_Style
.hasStyle("border-right-style")) border
.Right
.Style
= _Style
.Current
.Border
.Right
.Style
;
6710 if (_Style
.hasStyle("border-bottom-style")) border
.Bottom
.Style
= _Style
.Current
.Border
.Bottom
.Style
;
6711 if (_Style
.hasStyle("border-left-style")) border
.Left
.Style
= _Style
.Current
.Border
.Left
.Style
;
6713 _Cells
.back()->Border
->setBorder(border
);
6714 _Cells
.back()->Border
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
6715 _Cells
.back()->Border
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
6717 // padding from <table cellpadding="1">
6718 if (table
->CellPadding
)
6720 // FIXME: padding is ignored by vertical align
6721 _Cells
.back()->PaddingTop
= table
->CellPadding
;
6722 _Cells
.back()->PaddingRight
= table
->CellPadding
;
6723 _Cells
.back()->PaddingBottom
= table
->CellPadding
;
6724 _Cells
.back()->PaddingLeft
= table
->CellPadding
;
6727 if (_Style
.hasStyle("padding-top")) _Cells
.back()->PaddingTop
= _Style
.Current
.PaddingTop
;
6728 if (_Style
.hasStyle("padding-right")) _Cells
.back()->PaddingRight
= _Style
.Current
.PaddingRight
;
6729 if (_Style
.hasStyle("padding-bottom")) _Cells
.back()->PaddingBottom
= _Style
.Current
.PaddingBottom
;
6730 if (_Style
.hasStyle("padding-left")) _Cells
.back()->PaddingLeft
= _Style
.Current
.PaddingLeft
;
6732 table
->addChild (_Cells
.back());
6734 // reusing indent pushed by table
6737 newParagraph(TDBeginSpace
);
6738 // indent is already 0, getParagraph()->setMarginLeft(0); // maybe setIndent(0) if LI is using one
6744 renderPseudoElement(":before", elm
);
6747 void CGroupHTML::htmlTDend(const CHtmlElement
&elm
)
6749 renderPseudoElement(":after", elm
);
6751 popIfNotEmpty(_CellParams
);
6752 if (!_Cells
.empty())
6753 _Cells
.back() = NULL
;
6756 // ***************************************************************************
6757 void CGroupHTML::htmlTEXTAREA(const CHtmlElement
&elm
)
6759 _IgnoreChildElements
= true;
6761 // TODO: allow textarea without form
6765 // read general property
6766 string templateName
;
6768 // Widget template name
6769 if (elm
.hasNonEmptyAttribute("z_input_tmpl"))
6770 templateName
= elm
.getAttribute("z_input_tmpl");
6772 // Get the string name
6773 _TextAreaName
.clear();
6776 _TextAreaMaxLength
= 1024;
6777 if (elm
.hasNonEmptyAttribute("name"))
6778 _TextAreaName
= elm
.getAttribute("name");
6779 if (elm
.hasNonEmptyAttribute("rows"))
6780 fromString(elm
.getAttribute("rows"), _TextAreaRow
);
6781 if (elm
.hasNonEmptyAttribute("cols"))
6782 fromString(elm
.getAttribute("cols"), _TextAreaCols
);
6783 if (elm
.hasNonEmptyAttribute("maxlength"))
6784 fromString(elm
.getAttribute("maxlength"), _TextAreaMaxLength
);
6786 _TextAreaTemplate
= !templateName
.empty() ? templateName
: DefaultFormTextAreaGroup
;
6788 std::string content
= strFindReplaceAll(elm
.serializeChilds(), std::string("\r"), std::string(""));
6790 CInterfaceGroup
*textArea
= addTextArea (_TextAreaTemplate
, _TextAreaName
.c_str (), _TextAreaRow
, _TextAreaCols
, true, content
, _TextAreaMaxLength
);
6793 // Add the text area to the form
6794 CGroupHTML::CForm::CEntry entry
;
6795 entry
.Name
= _TextAreaName
;
6796 entry
.TextArea
= textArea
;
6797 _Forms
.back().Entries
.push_back (entry
);
6801 // ***************************************************************************
6802 void CGroupHTML::htmlTH(const CHtmlElement
&elm
)
6807 void CGroupHTML::htmlTHend(const CHtmlElement
&elm
)
6812 // ***************************************************************************
6813 void CGroupHTML::htmlTITLE(const CHtmlElement
&elm
)
6815 _IgnoreChildElements
= true;
6817 // TODO: only from <head>
6818 // if (!_ReadingHeadTag) return;
6820 // consume all child elements
6821 _TitleString
= strFindReplaceAll(elm
.serializeChilds(), std::string("\t"), std::string(" "));
6822 _TitleString
= strFindReplaceAll(_TitleString
, std::string("\n"), std::string(" "));
6823 setTitle(_TitleString
);
6826 // ***************************************************************************
6827 void CGroupHTML::htmlTR(const CHtmlElement
&elm
)
6829 // prevent inheriting from table
6830 if (!_CellParams
.empty())
6832 _CellParams
.back().BgColor
= CRGBA::Transparent
;
6833 _CellParams
.back().Height
= 0;
6836 // Get cells parameters
6837 getCellsParameters(elm
, true);
6839 // TODO: this probably ends up in first cell
6840 renderPseudoElement(":before", elm
);
6847 void CGroupHTML::htmlTRend(const CHtmlElement
&elm
)
6849 // TODO: this probably ends up in last cell
6850 renderPseudoElement(":after", elm
);
6852 popIfNotEmpty(_CellParams
);
6855 // ***************************************************************************
6856 void CGroupHTML::htmlUL(const CHtmlElement
&elm
)
6859 _UL
.push_back(HTMLOListElement(1, "disc"));
6860 else if (_UL
.size() == 1)
6861 _UL
.push_back(HTMLOListElement(1, "circle"));
6863 _UL
.push_back(HTMLOListElement(1, "square"));
6865 // if LI is already present
6866 _LI
= _UL
.size() > 1 || _DL
.size() > 1;
6867 _Indent
.push_back(getIndent() + ULIndent
);
6869 renderPseudoElement(":before", elm
);
6872 void CGroupHTML::htmlULend(const CHtmlElement
&elm
)
6877 renderPseudoElement(":after", elm
);
6880 popIfNotEmpty(_Indent
);