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;
3079 m_TableRowBackgroundColor
.clear();
3083 // clear the pointer to the current image download since all the button are deleted
3084 LOG_DL("Clear pointers to %d curls", Curls
.size());
3086 // remove image refs from downloads
3087 /*for(std::list<CDataDownload>::iterator it = Curls.begin(); it != Curls.end(); ++it)
3093 // ***************************************************************************
3095 u32char
CGroupHTML::getLastChar() const
3097 if (_CurrentViewLink
)
3099 ::u32string str
= CUtfStringView(_CurrentViewLink
->getText()).toUtf32(); // FIXME: Optimize reverse UTF iteration
3101 return str
[str
.length()-1];
3106 // ***************************************************************************
3108 void CGroupHTML::paragraphChange ()
3110 _CurrentViewLink
= NULL
;
3111 _CurrentViewImage
= NULL
;
3112 CGroupParagraph
*paragraph
= getParagraph();
3115 // Number of child in this paragraph
3116 uint numChild
= paragraph
->getNumChildren();
3119 // Get the last child
3120 CViewBase
*child
= paragraph
->getChild(numChild
-1);
3122 // Is this a string view ?
3123 _CurrentViewLink
= dynamic_cast<CViewLink
*>(child
);
3124 _CurrentViewImage
= dynamic_cast<CViewBitmap
*>(child
);
3129 // ***************************************************************************
3131 CInterfaceGroup
*CGroupHTML::getCurrentGroup()
3133 if (!_Cells
.empty() && _Cells
.back())
3134 return _Cells
.back()->Group
;
3136 return _GroupListAdaptor
;
3139 // ***************************************************************************
3141 void CGroupHTML::addHtmlGroup (CInterfaceGroup
*group
, uint beginSpace
)
3146 registerAnchor(group
);
3148 if (!_DivName
.empty())
3150 group
->setName(_DivName
);
3151 _Groups
.push_back(group
);
3154 group
->setSizeRef(CInterfaceElement::width
);
3156 // Compute begin space between paragraph and tables
3157 // * If first in group, no begin space
3159 // Pointer on the current paragraph (can be a table too)
3160 CGroupParagraph
*p
= dynamic_cast<CGroupParagraph
*>(group
);
3162 CInterfaceGroup
*parentGroup
= CGroupHTML::getCurrentGroup();
3163 const std::vector
<CInterfaceGroup
*> &groups
= parentGroup
->getGroups ();
3164 group
->setParent(parentGroup
);
3165 group
->setParentSize(parentGroup
);
3168 group
->setParentPos(parentGroup
);
3169 group
->setPosRef(Hotspot_TL
);
3170 group
->setParentPosRef(Hotspot_TL
);
3175 // Last is a paragraph ?
3176 group
->setParentPos(groups
.back());
3177 group
->setPosRef(Hotspot_TL
);
3178 group
->setParentPosRef(Hotspot_BL
);
3181 // Set the begin space
3183 p
->setTopSpace(beginSpace
);
3185 group
->setY(-(sint32
)beginSpace
);
3186 parentGroup
->addGroup (group
);
3189 // ***************************************************************************
3191 void CGroupHTML::setContainerTitle (const std::string
&title
)
3193 CInterfaceElement
*parent
= getParent();
3196 if ((parent
= parent
->getParent()))
3198 CGroupContainer
*container
= dynamic_cast<CGroupContainer
*>(parent
);
3201 container
->setTitle(title
);
3207 void CGroupHTML::setTitle(const std::string
&title
)
3209 if(_TitlePrefix
.empty())
3210 _TitleString
= title
;
3212 _TitleString
= _TitlePrefix
+ " - " + title
;
3214 setContainerTitle(_TitleString
);
3217 std::string
CGroupHTML::getTitle() const {
3218 return _TitleString
;
3221 // ***************************************************************************
3223 bool CGroupHTML::lookupLocalFile (string
&result
, const char *url
, bool isUrl
)
3228 if (toLowerAscii(result
).find("file:") == 0 && result
.size() > 5)
3230 result
= result
.substr(5, result
.size()-5);
3232 else if (result
.find("://") != string::npos
|| result
.find("//") == 0)
3234 // http://, https://, etc or protocol-less url "//domain.com/image.png"
3238 tmp
= CPath::lookup (CFile::getFilename(result
), false, false, false);
3241 // try to find in local directory
3242 tmp
= CPath::lookup (result
, false, false, true);
3247 // Normalize the path
3249 //result = "file:"+toLowerAscii(CPath::standardizePath (CPath::getFullPath (CFile::getPath(result)))+CFile::getFilename(result));*/
3250 result
= "file:/"+tmp
;
3257 // Is it a texture in the big texture ?
3258 if (CViewRenderer::getInstance()->getTextureIdFromName (result
) >= 0)
3264 // This is not a file in the CPath, let libwww open this URL
3271 // ***************************************************************************
3273 void CGroupHTML::submitForm(uint button
, sint32 x
, sint32 y
)
3275 if (button
>= _FormSubmit
.size())
3278 for(uint formId
= 0; formId
< _Forms
.size(); formId
++)
3280 // case sensitive search (user id is lowecase, auto id is uppercase)
3281 if (_Forms
[formId
].id
== _FormSubmit
[button
].form
)
3283 _PostNextTime
= true;
3284 _PostFormId
= formId
;
3285 _PostFormAction
= _FormSubmit
[button
].formAction
;
3286 _PostFormSubmitType
= _FormSubmit
[button
].type
;
3287 _PostFormSubmitButton
= _FormSubmit
[button
].name
;
3288 _PostFormSubmitValue
= _FormSubmit
[button
].value
;
3289 _PostFormSubmitX
= x
;
3290 _PostFormSubmitY
= y
;
3296 nlwarning("Unable to find form '%s' to submit (button '%s')", _FormSubmit
[button
].form
.c_str(), _FormSubmit
[button
].name
.c_str());
3299 // ***************************************************************************
3301 void CGroupHTML::setupBackground(CSSBackgroundRenderer
*bg
)
3305 bg
->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
3306 bg
->setBackground(_Style
.Current
.Background
);
3307 bg
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
3309 bg
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
3311 if (!_Style
.Current
.Background
.image
.empty())
3312 addTextureDownload(_Style
.Current
.Background
.image
, bg
->TextureId
, this);
3315 // ***************************************************************************
3317 void CGroupHTML::setBackgroundColor (const CRGBA
&bgcolor
)
3319 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
3320 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
3322 view
->setActive(false);
3324 m_HtmlBackground
.setColor(bgcolor
);
3327 // ***************************************************************************
3329 void CGroupHTML::setBackground (const string
&bgtex
, bool scale
, bool tile
)
3331 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
3332 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
3334 view
->setActive(false);
3336 m_HtmlBackground
.setImage(bgtex
);
3337 m_HtmlBackground
.setImageRepeat(tile
);
3338 m_HtmlBackground
.setImageCover(scale
);
3341 addTextureDownload(bgtex
, m_HtmlBackground
.TextureId
, this);
3345 struct CButtonFreezer
: public CInterfaceElementVisitor
3347 virtual void visitCtrl(CCtrlBase
*ctrl
)
3349 CCtrlBaseButton
*textButt
= dynamic_cast<CCtrlTextButton
*>(ctrl
);
3352 textButt
->setFrozen(true);
3357 // ***************************************************************************
3359 void CGroupHTML::handle ()
3361 H_AUTO(RZ_Interface_Html_handle
)
3363 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
3365 // handle curl downloads
3368 // handle refresh timer
3369 if (_NextRefreshTime
> 0 && _NextRefreshTime
<= (times
.thisFrameMs
/ 1000.0f
) )
3371 // there might be valid uses for 0sec refresh, but two in a row is probably a mistake
3372 if (_NextRefreshTime
- _LastRefreshTime
>= 1.0)
3374 _LastRefreshTime
= _NextRefreshTime
;
3375 doBrowse(_RefreshUrl
.c_str());
3378 nlwarning("Ignore second 0sec http-equiv refresh in a row (url '%s')", _URL
.c_str());
3380 _NextRefreshTime
= 0;
3385 // still transfering html page
3386 if (_TimeoutValue
!= 0 && _ConnectingTimeout
<= ( times
.thisFrameMs
/ 1000.0f
) )
3388 browseError(("Connection timeout : "+_URL
).c_str());
3392 if (_RenderNextTime
)
3394 _RenderNextTime
= false;
3395 renderHtmlString(_DocumentHtml
);
3398 if (_WaitingForStylesheet
)
3403 if (_BrowseNextTime
|| _PostNextTime
)
3406 _ConnectingTimeout
= ( times
.thisFrameMs
/ 1000.0f
) + _TimeoutValue
;
3408 // freeze form buttons
3409 CButtonFreezer freezer
;
3410 this->visit(&freezer
);
3417 bool isLocal
= lookupLocalFile (finalUrl
, _URL
.c_str(), true);
3421 CUrlParser
uri (_URL
);
3422 _TrustedDomain
= isTrustedDomain(uri
.host
);
3423 _DocumentDomain
= uri
.host
;
3425 // file is probably from bnp (ingame help)
3428 doBrowseLocalFile(finalUrl
);
3432 SFormFields formfields
;
3435 buildHTTPPostParams(formfields
);
3436 // _URL is set from form.Action
3441 // Add custom get params from child classes
3442 addHTTPGetParams (finalUrl
, _TrustedDomain
);
3445 doBrowseRemoteUrl(finalUrl
, "", _PostNextTime
, formfields
);
3448 _BrowseNextTime
= false;
3449 _PostNextTime
= false;
3453 // ***************************************************************************
3454 void CGroupHTML::buildHTTPPostParams (SFormFields
&formfields
)
3456 // Add text area text
3459 if (_PostFormId
>= _Forms
.size())
3461 nlwarning("(%s) invalid form index %d, _Forms %d", _Id
.c_str(), _PostFormId
, _Forms
.size());
3465 CForm
&form
= _Forms
[_PostFormId
];
3467 // button can override form action url (and methor, but we only do POST)
3468 _URL
= _PostFormAction
.empty() ? form
.Action
: _PostFormAction
;
3470 CUrlParser
uri(_URL
);
3471 _TrustedDomain
= isTrustedDomain(uri
.host
);
3472 _DocumentDomain
= uri
.host
;
3474 for (i
=0; i
<form
.Entries
.size(); i
++)
3477 bool addEntry
= false;
3479 if (form
.Entries
[i
].TextArea
)
3481 // Get the edit box view
3482 CInterfaceGroup
*group
= form
.Entries
[i
].TextArea
->getGroup ("eb");
3485 // Should be a CGroupEditBox
3486 CGroupEditBox
*editBox
= dynamic_cast<CGroupEditBox
*>(group
);
3489 entryData
= editBox
->getViewText()->getText();
3494 else if (form
.Entries
[i
].Checkbox
)
3496 // todo handle unicode POST here
3497 if (form
.Entries
[i
].Checkbox
->getPushed ())
3499 entryData
= form
.Entries
[i
].Value
;
3503 else if (form
.Entries
[i
].ComboBox
)
3505 CDBGroupComboBox
*cb
= form
.Entries
[i
].ComboBox
;
3506 entryData
= form
.Entries
[i
].SelectValues
[cb
->getSelection()];
3509 else if (form
.Entries
[i
].SelectBox
)
3511 CGroupMenu
*sb
= form
.Entries
[i
].SelectBox
;
3512 CGroupSubMenu
*rootMenu
= sb
->getRootMenu();
3515 for(uint j
=0; j
<rootMenu
->getNumLine(); ++j
)
3517 CInterfaceGroup
*ig
= rootMenu
->getUserGroupLeft(j
);
3520 CCtrlBaseButton
*cb
= dynamic_cast<CCtrlBaseButton
*>(ig
->getCtrl("b"));
3521 if (cb
&& cb
->getPushed())
3522 formfields
.add(form
.Entries
[i
].Name
, form
.Entries
[i
].SelectValues
[j
]);
3527 // This is a hidden value
3530 entryData
= form
.Entries
[i
].Value
;
3537 formfields
.add(form
.Entries
[i
].Name
, entryData
);
3541 if (_PostFormSubmitType
== "image")
3543 // Add the button coordinates
3544 if (_PostFormSubmitButton
.find_first_of("[") == string::npos
)
3546 formfields
.add(_PostFormSubmitButton
+ "_x", NLMISC::toString(_PostFormSubmitX
));
3547 formfields
.add(_PostFormSubmitButton
+ "_y", NLMISC::toString(_PostFormSubmitY
));
3551 formfields
.add(_PostFormSubmitButton
, NLMISC::toString(_PostFormSubmitX
));
3552 formfields
.add(_PostFormSubmitButton
, NLMISC::toString(_PostFormSubmitY
));
3556 formfields
.add(_PostFormSubmitButton
, _PostFormSubmitValue
);
3558 // Add custom params from child classes
3559 addHTTPPostParams(formfields
, _TrustedDomain
);
3562 // ***************************************************************************
3563 void CGroupHTML::doBrowseLocalFile(const std::string
&uri
)
3566 updateRefreshButton();
3568 std::string filename
;
3569 if (toLowerAscii(uri
).find("file:/") == 0)
3571 filename
= uri
.substr(6, uri
.size() - 6);
3578 LOG_DL("browse local file '%s'", filename
.c_str());
3580 _TrustedDomain
= true;
3581 _DocumentDomain
= "localhost";
3584 if (in
.open(filename
))
3590 in
.getline(buf
, 1024);
3591 html
+= std::string(buf
) + "\n";
3595 if (!renderHtmlString(html
))
3597 browseError((string("Failed to parse html from file : ")+filename
).c_str());
3602 browseError((string("The page address is malformed : ")+filename
).c_str());
3606 // ***************************************************************************
3607 void CGroupHTML::doBrowseRemoteUrl(std::string url
, const std::string
&referer
, bool doPost
, const SFormFields
&formfields
)
3609 // stop all downloads from previous page
3611 updateRefreshButton();
3614 if(_TitlePrefix
.empty())
3615 setTitle (CI18N::get("uiPleaseWait"));
3617 setTitle (_TitlePrefix
+ " - " + CI18N::get("uiPleaseWait"));
3619 url
= upgradeInsecureUrl(url
);
3621 LOG_DL("(%s) browse url (trusted=%s) '%s', referer='%s', post='%s', nb form values %d",
3622 _Id
.c_str(), (_TrustedDomain
? "true" :"false"), url
.c_str(), referer
.c_str(), (doPost
? "true" : "false"), formfields
.Values
.size());
3626 browseError(string("Invalid MultCurl handle, loading url failed : "+url
).c_str());
3630 CURL
*curl
= curl_easy_init();
3633 nlwarning("(%s) failed to create curl handle", _Id
.c_str());
3634 browseError(string("Failed to create cURL handle : " + url
).c_str());
3639 if (toLowerAscii(url
.substr(0, 8)) == "https://")
3641 // if supported, use custom SSL context function to load certificates
3642 NLWEB::CCurlCertificates::useCertificates(curl
);
3645 // do not follow redirects, we have own handler
3646 curl_easy_setopt(curl
, CURLOPT_FOLLOWLOCATION
, 0);
3648 curl_easy_setopt(curl
, CURLOPT_FRESH_CONNECT
, 1);
3650 // tell curl to use compression if possible (gzip, deflate)
3651 // leaving this empty allows all encodings that curl supports
3652 //curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
3654 // limit curl to HTTP and HTTPS protocols only
3655 curl_easy_setopt(curl
, CURLOPT_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
3656 curl_easy_setopt(curl
, CURLOPT_REDIR_PROTOCOLS
, CURLPROTO_HTTP
| CURLPROTO_HTTPS
);
3659 curl_easy_setopt(curl
, CURLOPT_URL
, url
.c_str());
3662 std::string userAgent
= options
.appName
+ "/" + options
.appVersion
;
3663 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, userAgent
.c_str());
3666 sendCookies(curl
, _DocumentDomain
, _TrustedDomain
);
3669 if (!referer
.empty())
3671 curl_easy_setopt(curl
, CURLOPT_REFERER
, referer
.c_str());
3672 LOG_DL("(%s) set referer '%s'", _Id
.c_str(), referer
.c_str());
3677 // serialize form data and add it to curl
3679 for(uint i
=0; i
<formfields
.Values
.size(); ++i
)
3681 char * escapedName
= curl_easy_escape(curl
, formfields
.Values
[i
].name
.c_str(), formfields
.Values
[i
].name
.size());
3682 char * escapedValue
= curl_easy_escape(curl
, formfields
.Values
[i
].value
.c_str(), formfields
.Values
[i
].value
.size());
3687 data
+= std::string(escapedName
) + "=" + escapedValue
;
3689 curl_free(escapedName
);
3690 curl_free(escapedValue
);
3692 curl_easy_setopt(curl
, CURLOPT_POST
, 1);
3693 curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, data
.size());
3694 curl_easy_setopt(curl
, CURLOPT_COPYPOSTFIELDS
, data
.c_str());
3698 curl_easy_setopt(curl
, CURLOPT_HTTPGET
, 1);
3702 _CurlWWW
= new CCurlWWWData(curl
, url
);
3704 // set the language code used by the client
3705 std::vector
<std::string
> headers
;
3706 headers
.push_back("Accept-Language: "+options
.languageCode
);
3707 headers
.push_back("Accept-Charset: utf-8");
3708 _CurlWWW
->sendHeaders(headers
);
3710 // catch headers for redirect
3711 curl_easy_setopt(curl
, CURLOPT_HEADERFUNCTION
, NLGUI::curlHeaderCallback
);
3712 curl_easy_setopt(curl
, CURLOPT_WRITEHEADER
, _CurlWWW
);
3715 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, NLGUI::curlDataCallback
);
3716 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, _CurlWWW
);
3718 #ifdef LOG_CURL_PROGRESS
3719 // progress callback
3720 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 0);
3721 curl_easy_setopt(curl
, CURLOPT_XFERINFOFUNCTION
, NLGUI::curlProgressCallback
);
3722 curl_easy_setopt(curl
, CURLOPT_XFERINFODATA
, _CurlWWW
);
3725 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 1);
3729 curl_multi_add_handle(MultiCurl
, curl
);
3731 // start the transfer
3732 int NewRunningCurls
= 0;
3733 curl_multi_perform(MultiCurl
, &NewRunningCurls
);
3736 _RedirectsRemaining
= DEFAULT_RYZOM_REDIRECT_LIMIT
;
3739 // ***************************************************************************
3740 void CGroupHTML::htmlDownloadFinished(bool success
, const std::string
&error
)
3744 CUrlParser
uri(_CurlWWW
->Url
);
3746 // potentially unwanted chars
3747 std::string url
= _CurlWWW
->Url
;
3748 url
= strFindReplaceAll(url
, string("<"), string("%3C"));
3749 url
= strFindReplaceAll(url
, string(">"), string("%3E"));
3750 url
= strFindReplaceAll(url
, string("\""), string("%22"));
3751 url
= strFindReplaceAll(url
, string("'"), string("%27"));
3754 err
= "<html><head><title>cURL error</title></head><body>";
3755 err
+= "<h1>Connection failed with cURL error</h1>";
3757 err
+= "<hr>(" + uri
.scheme
+ "://" + uri
.host
+ ") <a href=\"" + url
+ "\">reload</a>";
3758 err
+= "</body></html>";
3759 browseErrorHtml(err
);
3763 // received content from remote
3764 std::string content
= trim(_CurlWWW
->Content
);
3766 // save HSTS header from all requests regardless of HTTP code
3767 if (_CurlWWW
->hasHSTSHeader())
3769 CUrlParser
uri(_CurlWWW
->Url
);
3770 CStrictTransportSecurity::getInstance()->setFromHeader(uri
.host
, _CurlWWW
->getHSTSHeader());
3773 receiveCookies(_CurlWWW
->Request
, _DocumentDomain
, _TrustedDomain
);
3776 curl_easy_getinfo(_CurlWWW
->Request
, CURLINFO_RESPONSE_CODE
, &code
);
3777 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());
3779 if ((code
>= 301 && code
<= 303) || code
== 307 || code
== 308)
3781 if (_RedirectsRemaining
< 0)
3783 browseError(string("Redirect limit reached : " + _URL
).c_str());
3787 // redirect, get the location and try browse again
3788 // we cant use curl redirection because 'addHTTPGetParams()' must be called on new destination
3789 std::string
location(_CurlWWW
->getLocationHeader());
3790 if (location
.empty())
3792 browseError(string("Request was redirected, but location was not set : "+_URL
).c_str());
3796 LOG_DL("(%s) request (%d) redirected to (len %d) '%s'", _Id
.c_str(), _RedirectsRemaining
, location
.size(), location
.c_str());
3797 location
= getAbsoluteUrl(location
);
3799 _PostNextTime
= false;
3800 _RedirectsRemaining
--;
3802 doBrowse(location
.c_str());
3804 else if ( (code
< 200 || code
>= 300) )
3806 // catches 304 not modified, but html is not in cache anyway
3807 // if server did not send any error back
3808 if (content
.empty())
3810 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>");
3815 std::string contentType
;
3816 CURLcode res
= curl_easy_getinfo(_CurlWWW
->Request
, CURLINFO_CONTENT_TYPE
, &ch
);
3817 if (res
== CURLE_OK
&& ch
!= NULL
)
3822 htmlDownloadFinished(content
, contentType
, code
);
3824 // clear curl handler
3827 curl_multi_remove_handle(MultiCurl
, _CurlWWW
->Request
);
3833 // refresh button uses _CurlWWW. refresh button may stay disabled if
3834 // there is no css files to download and page is rendered before _CurlWWW is freed
3835 updateRefreshButton();
3838 void CGroupHTML::dataDownloadFinished(bool success
, const std::string
&error
, CDataDownload
*data
)
3842 CUrlParser
uri(data
->url
);
3843 if (!uri
.host
.empty())
3845 receiveCookies(data
->data
->Request
, uri
.host
, isTrustedDomain(uri
.host
));
3849 curl_easy_getinfo(data
->data
->Request
, CURLINFO_RESPONSE_CODE
, &code
);
3851 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());
3852 curl_multi_remove_handle(MultiCurl
, data
->data
->Request
);
3854 // save HSTS header from all requests regardless of HTTP code
3857 if (data
->data
->hasHSTSHeader())
3859 CStrictTransportSecurity::getInstance()->setFromHeader(uri
.host
, data
->data
->getHSTSHeader());
3862 // 2XX success, 304 Not Modified
3863 if ((code
>= 200 && code
<= 204) || code
== 304)
3865 CHttpCacheObject obj
;
3866 obj
.Expires
= data
->data
->getExpires();
3867 obj
.Etag
= data
->data
->getEtag();
3868 obj
.LastModified
= data
->data
->getLastModified();
3870 CHttpCache::getInstance()->store(data
->dest
, obj
);
3871 if (code
== 304 && CFile::fileExists(data
->tmpdest
))
3873 CFile::deleteFile(data
->tmpdest
);
3876 else if ((code
>= 301 && code
<= 303) || code
== 307 || code
== 308)
3878 if (data
->redirects
< DEFAULT_RYZOM_REDIRECT_LIMIT
)
3880 std::string
location(data
->data
->getLocationHeader());
3881 if (!location
.empty())
3883 CUrlParser
uri(location
);
3884 if (!uri
.isAbsolute())
3886 uri
.inherit(data
->url
);
3887 location
= uri
.toString();
3890 // clear old request state, and curl easy handle
3894 data
->url
= location
;
3897 // push same request in the front of the queue
3898 // cache filename is based of original url
3899 Curls
.push_front(data
);
3901 LOG_DL("Redirect '%s'", location
.c_str());
3902 // no finished callback called, so cleanup old temp
3903 if (CFile::fileExists(data
->tmpdest
))
3905 CFile::deleteFile(data
->tmpdest
);
3910 nlwarning("Redirected to empty url '%s'", data
->url
.c_str());
3914 nlwarning("Redirect limit reached for '%s'", data
->url
.c_str());
3919 nlwarning("HTTP request failed with code [%d] for '%s'\n",code
, data
->url
.c_str());
3921 if (CFile::fileExists(data
->dest
))
3923 CFile::deleteFile(data
->dest
);
3929 nlwarning("DATA download failed '%s', error '%s'", data
->url
.c_str(), error
.c_str());
3932 finishCurlDownload(data
);
3935 void CGroupHTML::htmlDownloadFinished(const std::string
&content
, const std::string
&type
, long code
)
3937 LOG_DL("(%s) HTML download finished, content length %d, type '%s', code %d", _Id
.c_str(), content
.size(), type
.c_str(), code
);
3939 // create <html> markup for image downloads
3940 if (type
.find("image/") == 0 && !content
.empty())
3944 std::string dest
= localImageName(_URL
);
3947 out
.serialBuffer((uint8
*)(content
.c_str()), content
.size());
3949 LOG_DL("(%s) image saved to '%s', url '%s'", _Id
.c_str(), dest
.c_str(), _URL
.c_str());
3953 // create html code with image url inside and do the request again
3954 renderHtmlString("<html><head><title>"+_URL
+"</title></head><body><img src=\"" + _URL
+ "\"></body></html>");
3956 else if (_TrustedDomain
&& type
.find("text/lua") == 0)
3958 setTitle(_TitleString
);
3960 _LuaScript
= "\nlocal __CURRENT_WINDOW__=\""+this->_Id
+"\" \n"+content
;
3961 CLuaManager::getInstance().executeLuaScript(_LuaScript
, true);
3964 // disable refresh button
3966 // disable redo into this url
3971 // Sanitize downloaded HTML UTF-8 encoding, and render
3972 renderHtmlString(CUtfStringView(content
).toUtf8(true));
3976 // ***************************************************************************
3977 void CGroupHTML::cssDownloadFinished(const std::string
&url
, const std::string
&local
)
3979 for(std::vector
<CHtmlParser::StyleLink
>::iterator it
= _StylesheetQueue
.begin();
3980 it
!= _StylesheetQueue
.end(); ++it
)
3984 // read downloaded file into HtmlStyles
3985 if (CFile::fileExists(local
) && it
->Index
< _HtmlStyles
.size())
3990 if (!in
.readAll(_HtmlStyles
[it
->Index
]))
3992 nlwarning("Failed to read downloaded css file(%s), url(%s)", local
.c_str(), url
.c_str());
3997 _StylesheetQueue
.erase(it
);
4003 void CGroupHTML::renderDocument()
4005 if (!Curls
.empty() && !_StylesheetQueue
.empty())
4007 // waiting for stylesheets to finish downloading
4010 _WaitingForStylesheet
= false;
4012 //TGameTime renderStart = CTime::getLocalTime();
4014 // clear previous state and page
4018 // process all <style> and <link rel=stylesheet> elements
4019 for(uint i
= 0; i
< _HtmlStyles
.size(); ++i
)
4021 if (!_HtmlStyles
[i
].empty())
4023 _Style
.parseStylesheet(_HtmlStyles
[i
]);
4026 _HtmlStyles
.clear();
4028 std::list
<CHtmlElement
>::iterator it
= _HtmlDOM
.Children
.begin();
4029 while(it
!= _HtmlDOM
.Children
.end())
4037 //TGameTime renderStop = CTime::getLocalTime();
4038 //nlwarning("[%s] render: %.1fms (%s)\n", _Id.c_str(), (renderStop - renderStart), _URL.c_str());
4041 // ***************************************************************************
4043 bool CGroupHTML::renderHtmlString(const std::string
&html
)
4047 // if we are already rendering, then queue up the next page
4050 _DocumentHtml
= html
;
4051 _RenderNextTime
= true;
4057 _DocumentUrl
= _URL
;
4058 _DocumentHtml
= html
;
4059 _NextRefreshTime
= 0;
4060 _RefreshUrl
.clear();
4062 if (trim(html
).empty())
4067 // clear previous page and state
4079 // start new rendering
4080 _HtmlDOM
= CHtmlElement(CHtmlElement::NONE
, "<root>");
4081 _CurrentHTMLElement
= NULL
;
4082 success
= parseHtml(html
);
4085 _WaitingForStylesheet
= !_StylesheetQueue
.empty();
4090 std::string error
= "ERROR: HTML parse failed.";
4091 error
+= toString("\nsize %d bytes", html
.size());
4092 error
+= toString("\n---start---\n%s\n---end---\n", html
.c_str());
4093 browseError(error
.c_str());
4100 // ***************************************************************************
4101 void CGroupHTML::doBrowseAnchor(const std::string
&anchor
)
4103 if (_Anchors
.count(anchor
) == 0)
4108 CInterfaceElement
*pIE
= _Anchors
.find(anchor
)->second
;
4111 // hotspot depends on vertical/horizontal scrollbar
4112 CCtrlScroll
*pSB
= getScrollBar();
4115 pSB
->ensureVisible(pIE
, Hotspot_Tx
, Hotspot_Tx
);
4120 // ***************************************************************************
4122 void CGroupHTML::draw ()
4124 uint8 CurrentAlpha
= 255;
4125 // search a parent container
4126 CInterfaceGroup
*gr
= getParent();
4129 if (gr
->isGroupContainer())
4131 CGroupContainer
*gc
= static_cast<CGroupContainer
*>(gr
);
4132 CurrentAlpha
= gc
->getCurrentContainerAlpha();
4135 gr
= gr
->getParent();
4137 m_HtmlBackground
.CurrentAlpha
= CurrentAlpha
;
4138 m_BodyBackground
.CurrentAlpha
= CurrentAlpha
;
4140 m_HtmlBackground
.draw();
4141 m_BodyBackground
.draw();
4142 CGroupScrollText::draw ();
4145 // ***************************************************************************
4147 void CGroupHTML::beginBuild ()
4152 void CGroupHTML::endBuild ()
4154 // set the browser as complete
4156 updateRefreshButton();
4158 // check that the title is set, or reset it (in the case the page
4159 // does not provide a title)
4160 if (_TitleString
.empty())
4162 setTitle(_TitlePrefix
);
4168 // ***************************************************************************
4170 void CGroupHTML::addHTTPGetParams (string
&/* url */, bool /*trustedDomain*/)
4174 // ***************************************************************************
4176 void CGroupHTML::addHTTPPostParams (SFormFields
&/* formfields */, bool /*trustedDomain*/)
4180 // ***************************************************************************
4182 string
CGroupHTML::home () const
4187 // ***************************************************************************
4189 void CGroupHTML::removeContent ()
4191 // Remove old document
4192 if (!_GroupListAdaptor
)
4194 _GroupListAdaptor
= new CGroupListAdaptor(CViewBase::TCtorParam()); // deleted by the list
4195 _GroupListAdaptor
->setId(getList()->getId() + ":GLA");
4196 _GroupListAdaptor
->setResizeFromChildH(true);
4197 getList()->addChild (_GroupListAdaptor
, true);
4200 // Group list adaptor not exist ?
4201 _GroupListAdaptor
->clearGroups();
4202 _GroupListAdaptor
->clearControls();
4203 _GroupListAdaptor
->clearViews();
4204 CWidgetManager::getInstance()->clearViewUnders();
4205 CWidgetManager::getInstance()->clearCtrlsUnders();
4207 // Clear all the context
4210 // Reset default background
4211 m_HtmlBackground
.clear();
4212 m_BodyBackground
.clear();
4214 // TODO: DefaultBackgroundBitmapView should be removed from interface xml
4215 CViewBase
*view
= getView (DefaultBackgroundBitmapView
);
4217 view
->setActive(false);
4222 // ***************************************************************************
4223 const std::string
&CGroupHTML::selectTreeNodeRecurs(CGroupTree::SNode
*node
, const std::string
&url
)
4225 static std::string emptyString
;
4231 // if this node match
4232 if(actionLaunchUrlRecurs(node
->AHName
, node
->AHParams
, url
))
4236 // fails => look into children
4239 for(uint i
=0;i
<node
->Children
.size();i
++)
4241 const string
&childRes
= selectTreeNodeRecurs(node
->Children
[i
], url
);
4242 if(!childRes
.empty())
4251 // ***************************************************************************
4252 bool CGroupHTML::actionLaunchUrlRecurs(const std::string
&ah
, const std::string
¶ms
, const std::string
&url
)
4254 // check if this action match
4255 if( (ah
=="launch_help" || ah
=="browse") && IActionHandler::getParam (params
, "url") == url
)
4259 // can be a proc that contains launch_help/browse => look recurs
4262 const std::string
&procName
= params
;
4263 // look into this proc
4264 uint numActions
= CWidgetManager::getInstance()->getParser()->getProcedureNumActions(procName
);
4265 for(uint i
=0;i
<numActions
;i
++)
4267 string procAh
, procParams
;
4268 if( CWidgetManager::getInstance()->getParser()->getProcedureAction(procName
, i
, procAh
, procParams
))
4270 // recurs proc if needed!
4271 if (actionLaunchUrlRecurs(procAh
, procParams
, url
))
4280 // ***************************************************************************
4281 void CGroupHTML::clearRefresh()
4284 updateRefreshButton();
4287 // ***************************************************************************
4288 void CGroupHTML::clearUndoRedo()
4290 // erase any undo/redo
4291 _BrowseUndo
.clear();
4292 _BrowseRedo
.clear();
4294 // update buttons validation
4295 updateUndoRedoButtons();
4298 // ***************************************************************************
4299 void CGroupHTML::pushUrlUndoRedo(const std::string
&url
)
4301 // if same url, no op
4305 // erase any redo, push undo, set current
4306 _BrowseRedo
.clear();
4307 if(!_AskedUrl
.empty())
4308 _BrowseUndo
.push_back(_AskedUrl
);
4312 while(_BrowseUndo
.size()>MaxUrlUndoRedo
)
4313 _BrowseUndo
.pop_front();
4315 // update buttons validation
4316 updateUndoRedoButtons();
4319 // ***************************************************************************
4320 void CGroupHTML::browseUndo()
4322 if(_BrowseUndo
.empty())
4325 // push to redo, pop undo, and set current
4326 if (!_AskedUrl
.empty())
4327 _BrowseRedo
.push_front(_AskedUrl
);
4329 _AskedUrl
= _BrowseUndo
.back();
4330 _BrowseUndo
.pop_back();
4332 // update buttons validation
4333 updateUndoRedoButtons();
4335 // and then browse the undoed url, with no undo/redo
4336 doBrowse(_AskedUrl
.c_str());
4339 // ***************************************************************************
4340 void CGroupHTML::browseRedo()
4342 if(_BrowseRedo
.empty())
4345 // push to undo, pop redo, and set current
4346 _BrowseUndo
.push_back(_AskedUrl
);
4347 _AskedUrl
= _BrowseRedo
.front();
4348 _BrowseRedo
.pop_front();
4350 // update buttons validation
4351 updateUndoRedoButtons();
4353 // and then browse the redoed url, with no undo/redo
4354 doBrowse(_AskedUrl
.c_str());
4357 // ***************************************************************************
4358 void CGroupHTML::updateUndoRedoButtons()
4360 CCtrlBaseButton
*butUndo
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseUndoButton
));
4361 CCtrlBaseButton
*butRedo
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseRedoButton
));
4363 // gray according to list size
4365 butUndo
->setFrozen(_BrowseUndo
.empty());
4367 butRedo
->setFrozen(_BrowseRedo
.empty());
4370 // ***************************************************************************
4371 void CGroupHTML::updateRefreshButton()
4373 CCtrlBaseButton
*butRefresh
= dynamic_cast<CCtrlBaseButton
*>(CWidgetManager::getInstance()->getElementFromId(_BrowseRefreshButton
));
4376 // connecting, rendering, or is missing url
4377 bool frozen
= _CurlWWW
|| _Browsing
|| _URL
.empty();
4378 butRefresh
->setFrozen(frozen
);
4382 // ***************************************************************************
4384 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupHTMLInputOffset
, std::string
, "html_input_offset");
4386 CGroupHTMLInputOffset::CGroupHTMLInputOffset(const TCtorParam
¶m
)
4387 : CInterfaceGroup(param
),
4392 xmlNodePtr
CGroupHTMLInputOffset::serialize( xmlNodePtr parentNode
, const char *type
) const
4394 xmlNodePtr node
= CInterfaceGroup::serialize( parentNode
, type
);
4398 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"html_input_offset" );
4399 xmlSetProp( node
, BAD_CAST
"y_offset", BAD_CAST
toString( Offset
).c_str() );
4404 // ***************************************************************************
4405 bool CGroupHTMLInputOffset::parse(xmlNodePtr cur
, CInterfaceGroup
*parentGroup
)
4407 if (!CInterfaceGroup::parse(cur
, parentGroup
)) return false;
4410 ptr
= xmlGetProp (cur
, (xmlChar
*)"y_offset");
4412 fromString((const char*)ptr
, Offset
);
4416 // ***************************************************************************
4417 int CGroupHTML::luaParseHtml(CLuaState
&ls
)
4419 const char *funcName
= "parseHtml";
4420 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4421 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4422 std::string html
= ls
.toString(1);
4429 int CGroupHTML::luaClearRefresh(CLuaState
&ls
)
4431 const char *funcName
= "clearRefresh";
4432 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4439 int CGroupHTML::luaClearUndoRedo(CLuaState
&ls
)
4441 const char *funcName
= "clearUndoRedo";
4442 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4448 // ***************************************************************************
4449 int CGroupHTML::luaBrowse(CLuaState
&ls
)
4451 const char *funcName
= "browse";
4452 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4453 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4454 browse(ls
.toString(1));
4458 // ***************************************************************************
4459 int CGroupHTML::luaRefresh(CLuaState
&ls
)
4461 const char *funcName
= "refresh";
4462 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4467 // ***************************************************************************
4468 int CGroupHTML::luaRemoveContent(CLuaState
&ls
)
4470 const char *funcName
= "removeContent";
4471 CLuaIHM::checkArgCount(ls
, funcName
, 0);
4476 // ***************************************************************************
4477 int CGroupHTML::luaRenderHtml(CLuaState
&ls
)
4479 const char *funcName
= "renderHtml";
4480 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4481 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4482 std::string html
= ls
.toString(1);
4484 // Always trust domain if rendered from lua
4485 _TrustedDomain
= true;
4486 renderHtmlString(html
);
4491 // ***************************************************************************
4492 int CGroupHTML::luaSetBackground(CLuaState
&ls
)
4494 const char *funcName
= "setBackground";
4495 CLuaIHM::checkArgCount(ls
, funcName
, 3);
4496 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4497 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4498 CLuaIHM::checkArgType(ls
, funcName
, 3, LUA_TBOOLEAN
);
4499 std::string image
= ls
.toString(1);
4500 bool scale
= ls
.toBoolean(2);
4501 bool repeat
= ls
.toBoolean(3);
4503 setBackground(image
, scale
, repeat
);
4508 // ***************************************************************************
4509 int CGroupHTML::luaInsertText(CLuaState
&ls
)
4511 const char *funcName
= "insertText";
4512 CLuaIHM::checkArgCount(ls
, funcName
, 3);
4513 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4514 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TSTRING
);
4515 CLuaIHM::checkArgType(ls
, funcName
, 3, LUA_TBOOLEAN
);
4517 string name
= ls
.toString(1);
4518 string text
= ls
.toString(2);
4520 if (!_Forms
.empty())
4522 for (uint i
=0; i
<_Forms
.back().Entries
.size(); i
++)
4524 if (_Forms
.back().Entries
[i
].TextArea
&& _Forms
.back().Entries
[i
].Name
== name
)
4526 // Get the edit box view
4527 CInterfaceGroup
*group
= _Forms
.back().Entries
[i
].TextArea
->getGroup ("eb");
4530 // Should be a CGroupEditBox
4531 CGroupEditBox
*editBox
= dynamic_cast<CGroupEditBox
*>(group
);
4533 editBox
->writeString(text
, false, ls
.toBoolean(3));
4542 // ***************************************************************************
4543 int CGroupHTML::luaAddString(CLuaState
&ls
)
4545 const char *funcName
= "addString";
4546 CLuaIHM::checkArgCount(ls
, funcName
, 1);
4547 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4548 addString(ls
.toString(1));
4552 // ***************************************************************************
4553 int CGroupHTML::luaAddImage(CLuaState
&ls
)
4555 const char *funcName
= "addImage";
4556 CLuaIHM::checkArgCount(ls
, funcName
, 2);
4557 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4558 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4566 style
.GlobalColor
= ls
.toBoolean(2);
4568 string url
= getLink();
4571 string params
= "name=" + getId() + "|url=" + getLink ();
4572 addButton(CCtrlButton::PushButton
, "", ls
.toString(1), ls
.toString(1),
4573 "", "browse", params
.c_str(), "", style
);
4577 addImage("", ls
.toString(1), false, style
);
4584 // ***************************************************************************
4585 int CGroupHTML::luaShowDiv(CLuaState
&ls
)
4587 const char *funcName
= "showDiv";
4588 CLuaIHM::checkArgCount(ls
, funcName
, 2);
4589 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
4590 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TBOOLEAN
);
4592 if (!_Groups
.empty())
4594 for (uint i
=0; i
<_Groups
.size(); i
++)
4596 CInterfaceGroup
*group
= _Groups
[i
];
4597 if (group
->getName() == ls
.toString(1))
4599 group
->setActive(ls
.toBoolean(2));
4606 // ***************************************************************************
4607 void CGroupHTML::setURL(const std::string
&url
)
4609 browse(url
.c_str());
4612 void CGroupHTML::setHTML(const std::string
&html
)
4614 renderHtmlString(html
);
4617 void CGroupHTML::setHome(const std::string
&home
)
4622 // ***************************************************************************
4623 void CGroupHTML::parseStylesheetFile(const std::string
&fname
)
4626 if (css
.open(fname
))
4628 uint32 remaining
= css
.getFileSize();
4629 std::string content
;
4631 while(!css
.eof() && remaining
> 0)
4633 const uint BUF_SIZE
= 4096;
4636 uint32 readJustNow
= std::min(remaining
, BUF_SIZE
);
4637 css
.serialBuffer((uint8
*)&buf
, readJustNow
);
4638 content
.append(buf
, readJustNow
);
4639 remaining
-= readJustNow
;
4642 _Style
.parseStylesheet(content
);
4644 catch(const Exception
&e
)
4646 nlwarning("exception while reading css file '%s'", e
.what());
4651 nlwarning("Stylesheet file '%s' not found (%s)", fname
.c_str(), _URL
.c_str());
4655 // ***************************************************************************
4656 bool CGroupHTML::parseHtml(const std::string
&htmlString
)
4658 CHtmlElement
*parsedDOM
;
4659 if (_CurrentHTMLElement
== NULL
)
4661 // parse under <root> element (clean dom)
4662 parsedDOM
= &_HtmlDOM
;
4666 // parse under currently rendered <lua> element
4667 parsedDOM
= _CurrentHTMLElement
;
4670 std::vector
<CHtmlParser::StyleLink
> links
;
4673 parser
.getDOM(htmlString
, *parsedDOM
, _HtmlStyles
, links
);
4675 // <link> elements inserted from lua::parseHtml are ignored
4676 if (_CurrentHTMLElement
== NULL
&& !links
.empty())
4678 addStylesheetDownload(links
);
4680 else if (_CurrentHTMLElement
!= NULL
)
4682 // Called from active element (lua)
4683 // <style> order is not preserved as document is already being rendered
4684 for(uint i
= 0; i
< _HtmlStyles
.size(); ++i
)
4686 if (!_HtmlStyles
[i
].empty())
4688 _Style
.parseStylesheet(_HtmlStyles
[i
]);
4691 _HtmlStyles
.clear();
4694 // this should rarely fail as first element should be <html>
4695 bool success
= parsedDOM
->Children
.size() > 0;
4697 std::list
<CHtmlElement
>::iterator it
= parsedDOM
->Children
.begin();
4698 while(it
!= parsedDOM
->Children
.end())
4700 if (it
->Type
== CHtmlElement::ELEMENT_NODE
&& it
->Value
== "html")
4702 // move newly parsed childs from <body> into siblings
4703 if (_CurrentHTMLElement
) {
4704 std::list
<CHtmlElement
>::iterator it2
= it
->Children
.begin();
4705 while(it2
!= it
->Children
.end())
4707 if (it2
->Type
== CHtmlElement::ELEMENT_NODE
&& it2
->Value
== "body")
4709 spliceFragment(it2
);
4714 // remove <html> fragment from current element child
4715 it
= parsedDOM
->Children
.erase(it
);
4719 // remove link to <root> (html->parent == '<root>') or css selector matching will break
4726 // skip over other non-handled element
4733 void CGroupHTML::spliceFragment(std::list
<CHtmlElement
>::iterator src
)
4735 if(!_CurrentHTMLElement
->parent
)
4737 nlwarning("BUG: Current node is missing parent element. unable to splice fragment");
4741 // get the iterators for current element (<lua>) and next sibling
4742 std::list
<CHtmlElement
>::iterator currentElement
;
4743 currentElement
= std::find(_CurrentHTMLElement
->parent
->Children
.begin(), _CurrentHTMLElement
->parent
->Children
.end(), *_CurrentHTMLElement
);
4744 if (currentElement
== _CurrentHTMLElement
->parent
->Children
.end())
4746 nlwarning("BUG: unable to find current element iterator from parent");
4750 // where fragment should be moved
4751 std::list
<CHtmlElement
>::iterator insertBefore
;
4752 if (_CurrentHTMLNextSibling
== NULL
)
4754 insertBefore
= _CurrentHTMLElement
->parent
->Children
.end();
4756 // get iterator for nextSibling
4757 insertBefore
= std::find(_CurrentHTMLElement
->parent
->Children
.begin(), _CurrentHTMLElement
->parent
->Children
.end(), *_CurrentHTMLNextSibling
);
4760 _CurrentHTMLElement
->parent
->Children
.splice(insertBefore
, src
->Children
);
4762 // reindex moved elements
4763 CHtmlElement
*prev
= NULL
;
4764 uint childIndex
= _CurrentHTMLElement
->childIndex
;
4765 while(currentElement
!= _CurrentHTMLElement
->parent
->Children
.end())
4767 if (currentElement
->Type
== CHtmlElement::ELEMENT_NODE
)
4771 currentElement
->parent
= _CurrentHTMLElement
->parent
;
4772 currentElement
->childIndex
= childIndex
;
4773 currentElement
->previousSibling
= prev
;
4774 prev
->nextSibling
= &(*currentElement
);
4778 prev
= &(*currentElement
);
4784 // ***************************************************************************
4785 inline bool isDigit(char c
, uint base
= 16)
4787 if (c
>='0' && c
<='9') return true;
4788 if (base
!= 16) return false;
4789 if (c
>='A' && c
<='F') return true;
4790 if (c
>='a' && c
<='f') return true;
4794 // ***************************************************************************
4795 inline char convertHexDigit(char c
)
4797 if (c
>='0' && c
<='9') return c
-'0';
4798 if (c
>='A' && c
<='F') return c
-'A'+10;
4799 if (c
>='a' && c
<='f') return c
-'a'+10;
4803 // ***************************************************************************
4804 std::string
CGroupHTML::decodeHTMLEntities(const std::string
&str
)
4807 result
.reserve(str
.size() + (str
.size() >> 2));
4810 for (uint i
=0; i
<str
.length(); ++i
)
4813 if (str
[i
] == '&' && (str
.length()-i
) >= 4)
4817 // unicode character
4818 if (str
[pos
] == '#')
4822 // using decimal by default
4825 // using hexadecimal if &#x
4826 if (str
[pos
] == 'x')
4832 // setup "last" to point at the first character following "&#x?[0-9a-f]+"
4833 for (last
= pos
; last
< str
.length(); ++last
) if (!isDigit(str
[last
], base
)) break;
4835 // make sure that at least 1 digit was found
4836 // and have the terminating ';' to complete the token: "&#x?[0-9a-f]+;"
4837 if (last
== pos
|| str
[last
] != ';')
4845 // convert digits to unicode character
4846 while (pos
< last
) c
= convertHexDigit(str
[pos
++]) + (c
* u32char(base
));
4848 // append our new character to the result string
4849 CUtfStringView::append(result
, c
);
4851 // move 'i' forward to point at the ';' .. the for(...) will increment i to point to next char
4857 // special xml characters
4858 if (str
.substr(i
+1,5)=="quot;") { i
+=5; result
+='\"'; continue; }
4859 if (str
.substr(i
+1,4)=="amp;") { i
+=4; result
+='&'; continue; }
4860 if (str
.substr(i
+1,3)=="lt;") { i
+=3; result
+='<'; continue; }
4861 if (str
.substr(i
+1,3)=="gt;") { i
+=3; result
+='>'; continue; }
4864 // all the special cases are catered for... treat this as a normal character
4871 // ***************************************************************************
4872 std::string
CGroupHTML::getAbsoluteUrl(const std::string
&url
)
4874 CUrlParser
uri(url
);
4875 if (uri
.isAbsolute())
4880 return uri
.toString();
4883 // ***************************************************************************
4884 void CGroupHTML::resetCssStyle()
4886 _WaitingForStylesheet
= false;
4887 _StylesheetQueue
.clear();
4889 _Style
= _BrowserStyle
;
4892 // ***************************************************************************
4893 std::string
CGroupHTML::HTMLOListElement::getListMarkerText() const
4896 sint32 number
= Value
;
4901 ret
= "\xe2\x88\x99 ";
4903 else if (Type
== "circle")
4906 ret
= "\xe2\x9a\xaa ";
4908 else if (Type
== "square")
4911 ret
= "\xe2\x96\xaa ";
4913 else if (Type
== "a" || Type
== "A")
4915 // @see toAlphabeticOrNumeric in WebKit
4916 static const char lower
[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
4917 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
4918 static const char upper
[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
4919 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
4923 ret
= toString(number
);
4927 const char* digits
= (Type
== "A" ? upper
: lower
);
4931 ret
.insert(ret
.begin(), digits
[number
% size
]);
4937 else if (Type
== "i" || Type
== "I")
4939 // @see toRoman in WebKit
4940 static const char lower
[7] = {'i', 'v', 'x', 'l', 'c', 'd', 'm'};
4941 static const char upper
[7] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
4943 if (number
< 1 || number
> 3999)
4945 ret
= toString(number
);
4949 const char* digits
= (Type
== "I" ? upper
: lower
);
4953 uint32 num
= number
% 10;
4956 for (i
= num
% 5; i
> 0; i
--)
4958 ret
.insert(ret
.begin(), digits
[d
]);
4961 if (num
>= 4 && num
<= 8)
4963 ret
.insert(ret
.begin(), digits
[d
+ 1]);
4967 ret
.insert(ret
.begin(), digits
[d
+ 2]);
4971 ret
.insert(ret
.begin(), digits
[d
]);
4987 ret
= toString(Value
) + ". ";
4993 void CGroupHTML::HTMLMeterElement::readValues(const CHtmlElement
&elm
)
4995 if (!elm
.hasAttribute("value") || !fromString(elm
.getAttribute("value"), value
))
4997 if (!elm
.hasAttribute("min") || !fromString(elm
.getAttribute("min"), min
))
4999 if (!elm
.hasAttribute("max") || !fromString(elm
.getAttribute("max"), max
))
5004 std::swap(min
, max
);
5006 if (!elm
.hasAttribute("low") || !fromString(elm
.getAttribute("low"), low
))
5008 if (!elm
.hasAttribute("high") || !fromString(elm
.getAttribute("high"), high
))
5011 if (!elm
.hasAttribute("optimum") || !fromString(elm
.getAttribute("optimum"), optimum
))
5012 optimum
= (max
- min
) / 2.f
;
5014 // ensure low < high
5016 std::swap(low
, high
);
5023 float CGroupHTML::HTMLMeterElement::getValueRatio() const
5028 return (value
- min
) / (max
- min
);
5031 CGroupHTML::HTMLMeterElement::EValueRegion
CGroupHTML::HTMLMeterElement::getValueRegion() const
5035 // low region is optimum
5037 return VALUE_OPTIMUM
;
5038 else if (value
<= high
)
5039 return VALUE_SUB_OPTIMAL
;
5041 return VALUE_EVEN_LESS_GOOD
;
5043 else if (optimum
>= high
)
5045 // high region is optimum
5047 return VALUE_OPTIMUM
;
5048 else if (value
>= low
)
5049 return VALUE_SUB_OPTIMAL
;
5051 return VALUE_EVEN_LESS_GOOD
;
5054 // middle region is optimum
5055 if (value
>= low
&& value
<= high
)
5056 return VALUE_OPTIMUM
;
5058 return VALUE_SUB_OPTIMAL
;
5061 NLMISC::CRGBA
CGroupHTML::HTMLMeterElement::getBarColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5063 // color meter (inactive) bar segment
5064 // firefox:: meter { background:none; background-color: #555; },
5065 // webkit:: meter::-webkit-meter-bar { background:none; background-color: #555; }
5066 // webkit makes background color visible when padding is added
5067 CRGBA
color(150, 150, 150, 255);
5069 // use webkit pseudo elements as thats easier than firefox pseudo classes
5070 // background-color is expected to be set from browser.css
5072 style
.applyStyle(elm
.getPseudo(":-webkit-meter-bar"));
5073 if(style
.hasStyle("background-color"))
5074 color
= style
.Current
.Background
.color
;
5080 NLMISC::CRGBA
CGroupHTML::HTMLMeterElement::getValueColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5082 // background-color is expected to be set from browser.css
5085 switch(getValueRegion())
5089 style
.applyStyle(elm
.getPseudo(":-webkit-meter-optimum-value"));
5090 if (style
.hasStyle("background-color"))
5091 color
= style
.Current
.Background
.color
;
5094 case VALUE_SUB_OPTIMAL
:
5096 style
.applyStyle(elm
.getPseudo(":-webkit-meter-suboptimum-value"));
5097 if (style
.hasStyle("background-color"))
5098 color
= style
.Current
.Background
.color
;
5101 case VALUE_EVEN_LESS_GOOD
: // fall through
5104 style
.applyStyle(elm
.getPseudo(":-webkit-meter-even-less-good-value"));
5105 if (style
.hasStyle("background-color"))
5106 color
= style
.Current
.Background
.color
;
5115 // ****************************************************************************
5116 void CGroupHTML::HTMLProgressElement::readValues(const CHtmlElement
&elm
)
5118 if (!elm
.hasAttribute("value") || !fromString(elm
.getAttribute("value"), value
))
5120 if (!elm
.hasAttribute("max") || !fromString(elm
.getAttribute("max"), max
))
5127 // ****************************************************************************
5128 float CGroupHTML::HTMLProgressElement::getValueRatio() const
5135 // ****************************************************************************
5136 NLMISC::CRGBA
CGroupHTML::HTMLProgressElement::getBarColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5141 style
.applyStyle(elm
.getPseudo(":-webkit-progress-bar"));
5142 if (style
.hasStyle("background-color"))
5143 color
= style
.Current
.Background
.color
;
5149 // ****************************************************************************
5150 NLMISC::CRGBA
CGroupHTML::HTMLProgressElement::getValueColor(const CHtmlElement
&elm
, CCssStyle
&style
) const
5155 style
.applyStyle(elm
.getPseudo(":-webkit-progress-value"));
5156 if (style
.hasStyle("background-color"))
5157 color
= style
.Current
.Background
.color
;
5163 // ****************************************************************************
5164 void CGroupHTML::getCellsParameters(const CHtmlElement
&elm
, bool inherit
)
5166 CGroupHTML::CCellParams cellParams
;
5167 if (!_CellParams
.empty() && inherit
)
5168 cellParams
= _CellParams
.back();
5170 if (!_Style
.hasStyle("background-color") && elm
.hasNonEmptyAttribute("bgcolor"))
5173 if (scanHTMLColor(elm
.getAttribute("bgcolor").c_str(), c
))
5174 _Style
.Current
.Background
.color
= c
;
5176 cellParams
.BgColor
= _Style
.Current
.Background
.color
;
5178 if (elm
.hasAttribute("nowrap") || _Style
.Current
.WhiteSpace
== "nowrap")
5179 cellParams
.NoWrap
= true;
5181 if (elm
.hasNonEmptyAttribute("l_margin"))
5182 fromString(elm
.getAttribute("l_margin"), cellParams
.LeftMargin
);
5184 if (_Style
.hasStyle("height"))
5185 cellParams
.Height
= _Style
.Current
.Height
;
5186 else if (elm
.hasNonEmptyAttribute("height"))
5187 fromString(elm
.getAttribute("height"), cellParams
.Height
);
5191 // having text-align on table/tr should not override td align attribute
5192 if (_Style
.hasStyle("text-align"))
5193 align
= _Style
.Current
.TextAlign
;
5194 else if (elm
.hasNonEmptyAttribute("align"))
5195 align
= toLowerAscii(elm
.getAttribute("align"));
5197 if (align
== "left")
5198 cellParams
.Align
= CGroupCell::Left
;
5199 else if (align
== "center")
5200 cellParams
.Align
= CGroupCell::Center
;
5201 else if (align
== "right")
5202 cellParams
.Align
= CGroupCell::Right
;
5203 else if (align
!= "justify")
5206 // copy td align (can be empty) attribute back into css
5207 _Style
.Current
.TextAlign
= align
;
5212 if (_Style
.hasStyle("vertical-align"))
5213 valign
= _Style
.Current
.VerticalAlign
;
5214 else if (elm
.hasNonEmptyAttribute("valign"))
5215 valign
= toLowerAscii(elm
.getAttribute("valign"));
5217 if (valign
== "top")
5218 cellParams
.VAlign
= CGroupCell::Top
;
5219 else if (valign
== "middle")
5220 cellParams
.VAlign
= CGroupCell::Middle
;
5221 else if (valign
== "bottom")
5222 cellParams
.VAlign
= CGroupCell::Bottom
;
5225 _CellParams
.push_back (cellParams
);
5228 // ***************************************************************************
5229 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
)
5231 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, "", "image", action
));
5232 // Action handler parameters
5233 std::string param
= "name=" + getId() + "|button=" + toString(_FormSubmit
.size()-1);
5235 // Add the ctrl button
5236 addButton (CCtrlButton::PushButton
, name
, src
, src
, over
, "html_submit_form", param
.c_str(), tooltip
.c_str(), _Style
.Current
);
5239 // ***************************************************************************
5240 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
)
5242 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, value
, "submit", formAction
));
5243 // Action handler parameters
5244 string param
= "name=" + getId() + "|button=" + toString(_FormSubmit
.size()-1);
5246 // Add the ctrl button
5253 string
buttonTemplate(!templateName
.empty() ? templateName
: DefaultButtonGroup
);
5254 typedef pair
<string
, string
> TTmplParam
;
5255 vector
<TTmplParam
> tmplParams
;
5256 tmplParams
.push_back(TTmplParam("id", name
));
5257 tmplParams
.push_back(TTmplParam("onclick", "html_submit_form"));
5258 tmplParams
.push_back(TTmplParam("onclick_param", param
));
5259 tmplParams
.push_back(TTmplParam("active", "true"));
5260 if (minWidth
> 0) tmplParams
.push_back(TTmplParam("wmin", toString(minWidth
)));
5261 CInterfaceGroup
*buttonGroup
= CWidgetManager::getInstance()->getParser()->createGroupInstance(buttonTemplate
, _Paragraph
->getId(), tmplParams
);
5264 // Add the ctrl button
5265 CCtrlTextButton
*ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("button"));
5266 if (!ctrlButton
) ctrlButton
= dynamic_cast<CCtrlTextButton
*>(buttonGroup
->getCtrl("b"));
5269 ctrlButton
->setModulateGlobalColorAll (_Style
.Current
.GlobalColor
);
5270 ctrlButton
->setTextModulateGlobalColorNormal(_Style
.Current
.GlobalColorText
);
5271 ctrlButton
->setTextModulateGlobalColorOver(_Style
.Current
.GlobalColorText
);
5272 ctrlButton
->setTextModulateGlobalColorPushed(_Style
.Current
.GlobalColorText
);
5274 // Translate the tooltip
5275 if (!tooltip
.empty())
5277 if (CI18N::hasTranslation(tooltip
))
5279 ctrlButton
->setDefaultContextHelp(CI18N::get(tooltip
));
5283 ctrlButton
->setDefaultContextHelp(tooltip
);
5287 ctrlButton
->setText(value
);
5289 setTextButtonStyle(ctrlButton
, _Style
.Current
);
5291 getParagraph()->addChild (buttonGroup
);
5296 // ***************************************************************************
5297 void CGroupHTML::htmlA(const CHtmlElement
&elm
)
5300 _Link
.push_back ("");
5301 _LinkTitle
.push_back("");
5302 _LinkClass
.push_back("");
5303 if (elm
.hasClass("ryzom-ui-button"))
5304 _LinkClass
.back() = "ryzom-ui-button";
5306 // #fragment works with both ID and NAME so register both
5307 if (elm
.hasNonEmptyAttribute("name"))
5308 _AnchorName
.push_back(elm
.getAttribute("name"));
5309 if (elm
.hasNonEmptyAttribute("title"))
5310 _LinkTitle
.back() = elm
.getAttribute("title");
5311 if (elm
.hasNonEmptyAttribute("href"))
5313 string suri
= elm
.getAttribute("href");
5314 if(suri
.find("ah:") == 0)
5317 _Link
.back() = suri
;
5321 // convert href from "?key=val" into "http://domain.com/?key=val"
5322 _Link
.back() = getAbsoluteUrl(suri
);
5326 renderPseudoElement(":before", elm
);
5329 void CGroupHTML::htmlAend(const CHtmlElement
&elm
)
5331 renderPseudoElement(":after", elm
);
5334 popIfNotEmpty(_Link
);
5335 popIfNotEmpty(_LinkTitle
);
5336 popIfNotEmpty(_LinkClass
);
5339 // ***************************************************************************
5340 void CGroupHTML::htmlBASE(const CHtmlElement
&elm
)
5342 if (!_ReadingHeadTag
|| _IgnoreBaseUrlTag
)
5345 if (elm
.hasNonEmptyAttribute("href"))
5347 CUrlParser
uri(elm
.getAttribute("href"));
5348 if (uri
.isAbsolute())
5350 _URL
= uri
.toString();
5351 _IgnoreBaseUrlTag
= true;
5356 // ***************************************************************************
5357 void CGroupHTML::htmlBODY(const CHtmlElement
&elm
)
5359 // override <body> (or <html>) css style attribute
5360 if (elm
.hasNonEmptyAttribute("bgcolor"))
5361 _Style
.applyStyle("background-color: " + elm
.getAttribute("bgcolor"));
5363 if (m_HtmlBackground
.isEmpty())
5364 setupBackground(&m_HtmlBackground
);
5366 setupBackground(&m_BodyBackground
);
5368 renderPseudoElement(":before", elm
);
5371 // ***************************************************************************
5372 void CGroupHTML::htmlBR(const CHtmlElement
&elm
)
5374 if (!_Paragraph
|| _Paragraph
->getNumChildren() == 0)
5384 // ***************************************************************************
5385 void CGroupHTML::htmlBUTTON(const CHtmlElement
&elm
)
5387 std::string name
= elm
.getAttribute("name");
5388 std::string value
= elm
.getAttribute("value");
5389 std::string formId
= elm
.getAttribute("form");
5390 std::string formAction
= elm
.getAttribute("formaction");
5391 std::string tooltip
= elm
.getAttribute("tooltip");
5392 bool disabled
= elm
.hasAttribute("disabled");
5394 if (formId
.empty() && _FormOpen
)
5396 formId
= _Forms
.back().id
;
5399 if (!formAction
.empty())
5401 formAction
= getAbsoluteUrl(formAction
);
5404 _FormSubmit
.push_back(SFormSubmitButton(formId
, name
, value
, "text", formAction
));
5405 // Action handler parameters
5409 if (elm
.getAttribute("type") == "submit")
5411 param
= "ah:html_submit_form&name=" + getId() + "&button=" + toString(_FormSubmit
.size()-1);
5420 _Link
.push_back(param
);
5421 _LinkTitle
.push_back(tooltip
);
5422 _LinkClass
.push_back("ryzom-ui-button");
5424 // TODO: this creates separate button element
5425 //renderPseudoElement(":before", elm);
5427 void CGroupHTML::htmlBUTTONend(const CHtmlElement
&elm
)
5429 // TODO: this creates separate button element
5430 //renderPseudoElement(":after", elm);
5433 popIfNotEmpty(_Link
);
5434 popIfNotEmpty(_LinkTitle
);
5435 popIfNotEmpty(_LinkClass
);
5438 // ***************************************************************************
5439 void CGroupHTML::htmlDD(const CHtmlElement
&elm
)
5444 // if there was no closing tag for <dt>, then remove <dt> style
5447 nlwarning("BUG: nested DT in DD");
5448 _DL
.back().DT
= false;
5453 nlwarning("BUG: nested DD in DD");
5454 _DL
.back().DD
= false;
5455 popIfNotEmpty(_Indent
);
5458 _DL
.back().DD
= true;
5459 _Indent
.push_back(getIndent() + ULIndent
);
5464 newParagraph(ULBeginSpace
);
5468 newParagraph(LIBeginSpace
);
5471 renderPseudoElement(":before", elm
);
5474 void CGroupHTML::htmlDDend(const CHtmlElement
&elm
)
5479 renderPseudoElement(":after", elm
);
5481 // parser will process two DD in a row as nested when first DD is not closed
5484 _DL
.back().DD
= false;
5485 popIfNotEmpty(_Indent
);
5489 // ***************************************************************************
5490 void CGroupHTML::htmlDIV(const CHtmlElement
&elm
)
5492 _DivName
= elm
.getAttribute("name");
5494 string instClass
= elm
.getAttribute("class");
5496 // use generic template system
5497 if (_TrustedDomain
&& !instClass
.empty() && instClass
== "ryzom-ui-grouptemplate")
5499 string style
= elm
.getAttribute("style");
5500 string id
= elm
.getAttribute("id");
5502 id
= "DIV" + toString(getNextAutoIdSeq());
5504 typedef pair
<string
, string
> TTmplParam
;
5505 vector
<TTmplParam
> tmplParams
;
5507 string templateName
;
5510 TStyle styles
= parseStyle(style
);
5511 TStyle::iterator it
;
5512 for (it
=styles
.begin(); it
!= styles
.end(); it
++)
5514 if ((*it
).first
== "template")
5515 templateName
= (*it
).second
;
5517 tmplParams
.push_back(TTmplParam((*it
).first
, (*it
).second
));
5521 if (!templateName
.empty())
5524 bool haveParentDiv
= getDiv() != NULL
;
5526 parentId
= getDiv()->getId();
5532 parentId
= _Paragraph
->getId();
5535 CInterfaceGroup
*inst
= CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName
, parentId
, tmplParams
);
5538 inst
->setId(parentId
+":"+id
);
5539 inst
->updateCoords();
5542 inst
->setParent(getDiv());
5543 inst
->setParentSize(getDiv());
5544 inst
->setParentPos(getDiv());
5545 inst
->setPosRef(Hotspot_TL
);
5546 inst
->setParentPosRef(Hotspot_TL
);
5547 getDiv()->addGroup(inst
);
5551 getParagraph()->addChild(inst
);
5554 _Divs
.push_back(inst
);
5559 renderPseudoElement(":before", elm
);
5562 void CGroupHTML::htmlDIVend(const CHtmlElement
&elm
)
5564 renderPseudoElement(":after", elm
);
5566 popIfNotEmpty(_Divs
);
5569 // ***************************************************************************
5570 void CGroupHTML::htmlDL(const CHtmlElement
&elm
)
5572 _DL
.push_back(HTMLDListElement());
5573 _LI
= _DL
.size() > 1 || !_UL
.empty();
5575 renderPseudoElement(":before", elm
);
5578 void CGroupHTML::htmlDLend(const CHtmlElement
&elm
)
5583 renderPseudoElement(":after", elm
);
5588 nlwarning("BUG: unclosed DT in DL");
5594 popIfNotEmpty(_Indent
);
5595 nlwarning("BUG: unclosed DD in DL");
5598 popIfNotEmpty (_DL
);
5601 // ***************************************************************************
5602 void CGroupHTML::htmlDT(const CHtmlElement
&elm
)
5607 // TODO: check if nested tags still happen and fix it in parser
5608 // : remove special handling for nesting and let it happen
5610 // html parser and libxml2 should prevent nested tags like these
5613 nlwarning("BUG: nested DD in DT");
5615 _DL
.back().DD
= false;
5616 popIfNotEmpty(_Indent
);
5619 // html parser and libxml2 should prevent nested tags like these
5622 nlwarning("BUG: nested DT in DT");
5625 _DL
.back().DT
= true;
5630 newParagraph(ULBeginSpace
);
5634 newParagraph(LIBeginSpace
);
5637 renderPseudoElement(":before", elm
);
5640 void CGroupHTML::htmlDTend(const CHtmlElement
&elm
)
5645 renderPseudoElement(":after", elm
);
5647 _DL
.back().DT
= false;
5650 // ***************************************************************************
5651 void CGroupHTML::htmlFONT(const CHtmlElement
&elm
)
5653 if (elm
.hasNonEmptyAttribute("color"))
5656 if (scanHTMLColor(elm
.getAttribute("color").c_str(), color
))
5657 _Style
.Current
.TextColor
= color
;
5660 if (elm
.hasNonEmptyAttribute("size"))
5663 fromString(elm
.getAttribute("size"), fontsize
);
5664 _Style
.Current
.FontSize
= fontsize
;
5668 // ***************************************************************************
5669 void CGroupHTML::htmlFORM(const CHtmlElement
&elm
)
5674 CGroupHTML::CForm form
;
5675 // id check is case sensitive and auto id's are uppercase
5676 form
.id
= toLowerAscii(trim(elm
.getAttribute("id")));
5677 if (form
.id
.empty())
5679 form
.id
= toString("FORM%d", _Forms
.size());
5682 // Get the action name
5683 if (elm
.hasNonEmptyAttribute("action"))
5685 form
.Action
= getAbsoluteUrl(elm
.getAttribute("action"));
5692 _Forms
.push_back(form
);
5694 renderPseudoElement(":before", elm
);
5697 void CGroupHTML::htmlFORMend(const CHtmlElement
&elm
)
5700 renderPseudoElement(":after", elm
);
5703 // ***************************************************************************
5704 void CGroupHTML::htmlH(const CHtmlElement
&elm
)
5706 newParagraph(PBeginSpace
);
5707 renderPseudoElement(":before", elm
);
5710 void CGroupHTML::htmlHend(const CHtmlElement
&elm
)
5712 renderPseudoElement(":after", elm
);
5715 // ***************************************************************************
5716 void CGroupHTML::htmlHEAD(const CHtmlElement
&elm
)
5718 _ReadingHeadTag
= !_IgnoreHeadTag
;
5719 _IgnoreHeadTag
= true;
5722 void CGroupHTML::htmlHEADend(const CHtmlElement
&elm
)
5724 _ReadingHeadTag
= false;
5727 // ***************************************************************************
5728 void CGroupHTML::htmlHR(const CHtmlElement
&elm
)
5730 CInterfaceGroup
*sep
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_hr", "", NULL
, 0);
5733 CViewBitmap
*bitmap
= dynamic_cast<CViewBitmap
*>(sep
->getView("hr"));
5736 bitmap
->setColor(_Style
.Current
.TextColor
);
5737 if (_Style
.Current
.Width
> 0)
5739 clamp(_Style
.Current
.Width
, 1, 32000);
5740 bitmap
->setW(_Style
.Current
.Width
);
5741 bitmap
->setSizeRef(CInterfaceElement::none
);
5743 if (_Style
.Current
.Height
> 0)
5745 clamp(_Style
.Current
.Height
, 1, 1000);
5746 bitmap
->setH(_Style
.Current
.Height
);
5750 renderPseudoElement(":before", elm
);
5751 addHtmlGroup(sep
, 0);
5752 renderPseudoElement(":after", elm
);
5756 // ***************************************************************************
5757 void CGroupHTML::htmlHTML(const CHtmlElement
&elm
)
5759 if (elm
.hasNonEmptyAttribute("style"))
5760 _Style
.applyStyle(elm
.getAttribute("style"));
5762 _Style
.Root
= _Style
.Current
;
5764 setupBackground(&m_HtmlBackground
);
5767 // ***************************************************************************
5768 void CGroupHTML::htmlI(const CHtmlElement
&elm
)
5771 renderPseudoElement(":before", elm
);
5774 void CGroupHTML::htmlIend(const CHtmlElement
&elm
)
5776 renderPseudoElement(":after", elm
);
5780 // ***************************************************************************
5781 void CGroupHTML::htmlIMG(const CHtmlElement
&elm
)
5783 std::string src
= trim(elm
.getAttribute("src"));
5786 // no 'src' attribute, or empty
5791 std::string id
= elm
.getAttribute("id");
5793 if (elm
.hasNonEmptyAttribute("width"))
5794 getPercentage(_Style
.Current
.Width
, tmpf
, elm
.getAttribute("width").c_str());
5795 if (elm
.hasNonEmptyAttribute("height"))
5796 getPercentage(_Style
.Current
.Height
, tmpf
, elm
.getAttribute("height").c_str());
5798 // Get the global color name
5799 if (elm
.hasAttribute("global_color"))
5800 _Style
.Current
.GlobalColor
= true;
5803 // keep "alt" attribute for backward compatibility
5804 std::string tooltip
= elm
.getAttribute("alt");
5806 if (elm
.hasNonEmptyAttribute("title"))
5807 tooltip
= elm
.getAttribute("title");
5810 string overSrc
= elm
.getAttribute("data-over-src");
5812 // inside a/button with valid url (ie, button is not disabled)
5813 string url
= getLink();
5814 if (getA() && !url
.empty() && getParent() && getParent()->getParent())
5816 string params
= "name=" + getId() + "|url=" + url
;
5817 addButton(CCtrlButton::PushButton
, id
, src
, src
, overSrc
, "browse", params
.c_str(), tooltip
, _Style
.Current
);
5820 if (!tooltip
.empty() || !overSrc
.empty())
5822 addButton(CCtrlButton::PushButton
, id
, src
, src
, overSrc
, "", "", tooltip
, _Style
.Current
);
5826 // Get the option to reload (class==reload)
5827 bool reloadImg
= false;
5829 if (elm
.hasNonEmptyAttribute("style"))
5831 string styleString
= elm
.getAttribute("style");
5832 TStyle styles
= parseStyle(styleString
);
5833 TStyle::iterator it
;
5835 it
= styles
.find("reload");
5836 if (it
!= styles
.end() && (*it
).second
== "1")
5840 addImage(id
, elm
.getAttribute("src"), reloadImg
, _Style
.Current
);
5844 // ***************************************************************************
5845 void CGroupHTML::htmlINPUT(const CHtmlElement
&elm
)
5850 // read general property
5851 string id
= elm
.getAttribute("id");
5853 // Widget template name (old)
5854 string templateName
= elm
.getAttribute("z_btn_tmpl");
5855 // Input name is the new
5856 if (elm
.hasNonEmptyAttribute("z_input_tmpl"))
5857 templateName
= elm
.getAttribute("z_input_tmpl");
5859 // Widget minimal width
5860 uint32 minWidth
= 0;
5861 fromString(elm
.getAttribute("z_input_width"), minWidth
);
5863 // <input type="...">
5864 std::string type
= trim(elm
.getAttribute("type"));
5867 // no 'type' attribute, or empty
5871 // Global color flag
5872 if (elm
.hasAttribute("global_color"))
5873 _Style
.Current
.GlobalColor
= true;
5876 std::string tooltip
= elm
.getAttribute("alt");
5878 if (type
== "image")
5880 string name
= elm
.getAttribute("name");
5881 string src
= elm
.getAttribute("src");
5882 string over
= elm
.getAttribute("data-over-src");
5883 string formId
= elm
.getAttribute("form");
5884 string formAction
= elm
.getAttribute("formaction");
5886 if (formId
.empty() && _FormOpen
) {
5887 formId
= _Forms
.back().id
;
5890 insertFormImageButton(name
, tooltip
, src
, over
, formId
, formAction
, minWidth
, templateName
);
5892 else if (type
== "button" || type
== "submit")
5894 string name
= elm
.getAttribute("name");
5895 string value
= elm
.getAttribute("value");
5896 string formId
= elm
.getAttribute("form");
5897 string formAction
= elm
.getAttribute("formaction");
5899 if (formId
.empty() && _FormOpen
) {
5900 formId
= _Forms
.back().id
;
5903 insertFormTextButton(name
, tooltip
, value
, formId
, formAction
, minWidth
, templateName
);
5905 else if (type
== "text")
5907 // Get the string name
5908 string name
= elm
.getAttribute("name");
5909 string ucValue
= elm
.getAttribute("value");
5912 uint maxlength
= 1024;
5913 if (elm
.hasNonEmptyAttribute("size"))
5914 fromString(elm
.getAttribute("size"), size
);
5915 if (elm
.hasNonEmptyAttribute("maxlength"))
5916 fromString(elm
.getAttribute("maxlength"), maxlength
);
5918 // ryzom client used to have 'size' attribute in pixels, (12 == was default font size)
5919 if (_Style
.hasStyle("-ryzom-input-size-px") && _Style
.getStyle("-ryzom-input-size-px") == "true")
5922 string
textTemplate(!templateName
.empty() ? templateName
: DefaultFormTextGroup
);
5924 CInterfaceGroup
*textArea
= addTextArea (textTemplate
, name
.c_str (), 1, size
, false, ucValue
, maxlength
);
5927 // Add the text area to the form
5928 CGroupHTML::CForm::CEntry entry
;
5930 entry
.TextArea
= textArea
;
5931 _Forms
.back().Entries
.push_back (entry
);
5934 else if (type
== "checkbox" || type
== "radio")
5936 renderPseudoElement(":before", elm
);
5938 CCtrlButton::EType btnType
;
5939 string name
= elm
.getAttribute("name");
5940 string normal
= elm
.getAttribute("src");
5943 string ucValue
= "on";
5944 bool checked
= elm
.hasAttribute("checked");
5946 // TODO: unknown if empty attribute should override or not
5947 if (elm
.hasNonEmptyAttribute("value"))
5948 ucValue
= elm
.getAttribute("value");
5950 if (type
== "radio")
5952 btnType
= CCtrlButton::RadioButton
;
5953 normal
= DefaultRadioButtonBitmapNormal
;
5954 pushed
= DefaultRadioButtonBitmapPushed
;
5955 over
= DefaultRadioButtonBitmapOver
;
5959 btnType
= CCtrlButton::ToggleButton
;
5960 normal
= DefaultCheckBoxBitmapNormal
;
5961 pushed
= DefaultCheckBoxBitmapPushed
;
5962 over
= DefaultCheckBoxBitmapOver
;
5965 // Add the ctrl button
5966 CCtrlButton
*checkbox
= addButton (btnType
, name
, normal
, pushed
, over
, "", "", tooltip
, _Style
.Current
);
5969 if (btnType
== CCtrlButton::RadioButton
)
5971 // override with 'id' because radio buttons share same name
5973 checkbox
->setId(id
);
5975 // group together buttons with same name
5976 CForm
&form
= _Forms
.back();
5977 bool notfound
= true;
5978 for (uint i
=0; i
<form
.Entries
.size(); i
++)
5980 if (form
.Entries
[i
].Name
== name
&& form
.Entries
[i
].Checkbox
->getType() == CCtrlButton::RadioButton
)
5982 checkbox
->initRBRefFromRadioButton(form
.Entries
[i
].Checkbox
);
5989 // this will start a new group (initRBRef() would take first button in group container otherwise)
5990 checkbox
->initRBRefFromRadioButton(checkbox
);
5994 checkbox
->setPushed (checked
);
5996 // Add the button to the form
5997 CGroupHTML::CForm::CEntry entry
;
5999 entry
.Value
= decodeHTMLEntities(ucValue
);
6000 entry
.Checkbox
= checkbox
;
6001 _Forms
.back().Entries
.push_back (entry
);
6003 renderPseudoElement(":after", elm
);
6005 else if (type
== "hidden")
6007 if (elm
.hasNonEmptyAttribute("name"))
6010 string name
= elm
.getAttribute("name");
6013 string ucValue
= elm
.getAttribute("value");
6016 CGroupHTML::CForm::CEntry entry
;
6018 entry
.Value
= decodeHTMLEntities(ucValue
);
6019 _Forms
.back().Entries
.push_back (entry
);
6024 // ***************************************************************************
6025 void CGroupHTML::htmlLI(const CHtmlElement
&elm
)
6030 // UL, OL top margin if this is the first LI
6034 newParagraph(ULBeginSpace
);
6038 newParagraph(LIBeginSpace
);
6041 // OL list index can be overridden by <li value="1"> attribute
6042 if (elm
.hasNonEmptyAttribute("value"))
6043 fromString(elm
.getAttribute("value"), _UL
.back().Value
);
6045 string str
= _UL
.back().getListMarkerText();
6048 // list-style-type: outside
6049 if (_CurrentViewLink
)
6051 getParagraph()->setFirstViewIndent(-_CurrentViewLink
->getMaxUsedW());
6057 renderPseudoElement(":before", elm
);
6062 void CGroupHTML::htmlLIend(const CHtmlElement
&elm
)
6064 renderPseudoElement(":after", elm
);
6067 // ***************************************************************************
6068 void CGroupHTML::htmlLUA(const CHtmlElement
&elm
)
6070 // we receive an embeded lua script
6071 _ParsingLua
= _TrustedDomain
; // Only parse lua if TrustedDomain
6075 void CGroupHTML::htmlLUAend(const CHtmlElement
&elm
)
6077 if (_ParsingLua
&& _TrustedDomain
)
6079 _ParsingLua
= false;
6080 // execute the embeded lua script
6081 _LuaScript
= "\nlocal __CURRENT_WINDOW__=\""+this->_Id
+"\" \n"+_LuaScript
;
6082 CLuaManager::getInstance().executeLuaScript(_LuaScript
, true);
6086 // ***************************************************************************
6087 void CGroupHTML::htmlMETA(const CHtmlElement
&elm
)
6089 if (!_ReadingHeadTag
)
6092 std::string httpEquiv
= elm
.getAttribute("http-equiv");
6093 std::string httpContent
= elm
.getAttribute("content");
6094 if (httpEquiv
.empty() || httpContent
.empty())
6099 // only first http-equiv="refresh" should be handled
6100 if (_RefreshUrl
.empty() && httpEquiv
== "refresh")
6102 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
6103 double timeSec
= times
.thisFrameMs
/ 1000.0f
;
6105 string::size_type pos
= httpContent
.find_first_of(";");
6106 if (pos
== string::npos
)
6108 fromString(httpContent
, _NextRefreshTime
);
6113 fromString(httpContent
.substr(0, pos
), _NextRefreshTime
);
6115 pos
= toLowerAscii(httpContent
).find("url=");
6116 if (pos
!= string::npos
)
6117 _RefreshUrl
= getAbsoluteUrl(httpContent
.substr(pos
+ 4));
6120 _NextRefreshTime
+= timeSec
;
6124 // ***************************************************************************
6125 void CGroupHTML::htmlMETER(const CHtmlElement
&elm
)
6127 HTMLMeterElement meter
;
6128 meter
.readValues(elm
);
6130 std::string id
= "meter";
6131 if (elm
.hasAttribute("id"))
6132 id
= elm
.getAttribute("id");
6134 // width: 5em, height: 1em
6135 uint32 width
= _Style
.Current
.Width
> -1 ? _Style
.Current
.Width
: _Style
.Current
.FontSize
* 5;
6136 uint32 height
= _Style
.Current
.Height
> -1 ? _Style
.Current
.Height
: _Style
.Current
.FontSize
;
6137 // FIXME: only using border-top
6138 uint32 border
= _Style
.Current
.Border
.Top
.Width
.getValue() > -1 ? _Style
.Current
.Border
.Top
.Width
.getValue() : 0;
6140 uint barw
= (uint
) (width
* meter
.getValueRatio());
6141 CRGBA bgColor
= meter
.getBarColor(elm
, _Style
);
6142 CRGBA valueColor
= meter
.getValueColor(elm
, _Style
);
6144 typedef pair
<string
, string
> TTmplParam
;
6145 vector
<TTmplParam
> tmplParams
;
6146 tmplParams
.push_back(TTmplParam("id", id
));
6147 tmplParams
.push_back(TTmplParam("active", "true"));
6148 tmplParams
.push_back(TTmplParam("w", toString(width
)));
6149 tmplParams
.push_back(TTmplParam("h", toString(height
)));
6150 tmplParams
.push_back(TTmplParam("border_x2", toString(border
*2)));
6151 tmplParams
.push_back(TTmplParam("bgtexture", "blank.tga"));
6152 tmplParams
.push_back(TTmplParam("bgcolor", bgColor
.toString()));
6153 tmplParams
.push_back(TTmplParam("value_w", toString(barw
)));
6154 tmplParams
.push_back(TTmplParam("value_texture", "blank.tga"));
6155 tmplParams
.push_back(TTmplParam("value_color", valueColor
.toString()));
6157 CInterfaceGroup
*gr
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_meter", getParagraph()->getId(), &tmplParams
[0], (uint
)tmplParams
.size());
6160 renderPseudoElement(":before", elm
);
6161 getParagraph()->addChild(gr
);
6162 renderPseudoElement(":after", elm
);
6164 // ignore any inner elements
6165 _IgnoreChildElements
= true;
6169 // ***************************************************************************
6170 void CGroupHTML::htmlOBJECT(const CHtmlElement
&elm
)
6172 _ObjectType
= elm
.getAttribute("type");
6173 _ObjectData
= elm
.getAttribute("data");
6174 _ObjectMD5Sum
= elm
.getAttribute("id");
6175 _ObjectAction
= elm
.getAttribute("standby");
6179 void CGroupHTML::htmlOBJECTend(const CHtmlElement
&elm
)
6181 if (!_TrustedDomain
)
6184 if (_ObjectType
=="application/ryzom-data")
6186 if (!_ObjectData
.empty())
6188 if (addBnpDownload(_ObjectData
, _ObjectAction
, _ObjectScript
, _ObjectMD5Sum
))
6190 CLuaManager::getInstance().executeLuaScript("\nlocal __ALLREADYDL__=true\n"+_ObjectScript
, true);
6192 _ObjectScript
.clear();
6198 // ***************************************************************************
6199 void CGroupHTML::htmlOL(const CHtmlElement
&elm
)
6202 std::string
type("1");
6204 if (elm
.hasNonEmptyAttribute("start"))
6205 fromString(elm
.getAttribute("start"), start
);
6206 if (elm
.hasNonEmptyAttribute("type"))
6207 type
= elm
.getAttribute("type");
6209 _UL
.push_back(HTMLOListElement(start
, type
));
6210 // if LI is already present
6211 _LI
= _UL
.size() > 1 || _DL
.size() > 1;
6212 _Indent
.push_back(getIndent() + ULIndent
);
6214 renderPseudoElement(":before", elm
);
6217 void CGroupHTML::htmlOLend(const CHtmlElement
&elm
)
6222 // ***************************************************************************
6223 void CGroupHTML::htmlOPTION(const CHtmlElement
&elm
)
6225 _SelectOption
= true;
6226 _SelectOptionStr
.clear();
6229 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6232 _Forms
.back().Entries
.back().SelectValues
.push_back(elm
.getAttribute("value"));
6234 if (elm
.hasAttribute("selected"))
6235 _Forms
.back().Entries
.back().InitialSelection
= (sint
)_Forms
.back().Entries
.back().SelectValues
.size() - 1;
6237 if (elm
.hasAttribute("disabled"))
6238 _Forms
.back().Entries
.back().sbOptionDisabled
= (sint
)_Forms
.back().Entries
.back().SelectValues
.size() - 1;
6241 void CGroupHTML::htmlOPTIONend(const CHtmlElement
&elm
)
6243 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6246 // use option text as value
6247 if (!elm
.hasAttribute("value"))
6249 _Forms
.back().Entries
.back().SelectValues
.back() = _SelectOptionStr
;
6252 // insert the parsed text into the select control
6253 CDBGroupComboBox
*cb
= _Forms
.back().Entries
.back().ComboBox
;
6256 uint lineIndex
= cb
->getNumTexts();
6257 cb
->addText(_SelectOptionStr
);
6258 if (_Forms
.back().Entries
.back().sbOptionDisabled
== lineIndex
)
6260 cb
->setGrayed(lineIndex
, true);
6265 CGroupMenu
*sb
= _Forms
.back().Entries
.back().SelectBox
;
6268 uint lineIndex
= sb
->getNumLine();
6269 sb
->addLine(_SelectOptionStr
, "", "");
6271 if (_Forms
.back().Entries
.back().sbOptionDisabled
== lineIndex
)
6273 sb
->setGrayedLine(lineIndex
, true);
6277 // create option line checkbox, CGroupMenu is taking ownership of the checbox
6278 CInterfaceGroup
*ig
= CWidgetManager::getInstance()->getParser()->createGroupInstance("menu_checkbox", "", NULL
, 0);
6281 CCtrlButton
*cb
= dynamic_cast<CCtrlButton
*>(ig
->getCtrl("b"));
6284 if (_Forms
.back().Entries
.back().sbMultiple
)
6286 cb
->setType(CCtrlButton::ToggleButton
);
6287 cb
->setTexture(DefaultCheckBoxBitmapNormal
);
6288 cb
->setTexturePushed(DefaultCheckBoxBitmapPushed
);
6289 cb
->setTextureOver(DefaultCheckBoxBitmapOver
);
6293 cb
->setType(CCtrlButton::RadioButton
);
6294 cb
->setTexture(DefaultRadioButtonBitmapNormal
);
6295 cb
->setTexturePushed(DefaultRadioButtonBitmapPushed
);
6296 cb
->setTextureOver(DefaultRadioButtonBitmapOver
);
6298 if (_Forms
.back().Entries
.back().sbRBRef
== NULL
)
6299 _Forms
.back().Entries
.back().sbRBRef
= cb
;
6301 cb
->initRBRefFromRadioButton(_Forms
.back().Entries
.back().sbRBRef
);
6304 cb
->setPushed(_Forms
.back().Entries
.back().InitialSelection
== lineIndex
);
6305 sb
->setUserGroupLeft(lineIndex
, ig
);
6309 nlwarning("Failed to get 'b' element from 'menu_checkbox' template");
6318 // ***************************************************************************
6319 void CGroupHTML::htmlP(const CHtmlElement
&elm
)
6321 newParagraph(PBeginSpace
);
6322 renderPseudoElement(":before", elm
);
6325 void CGroupHTML::htmlPend(const CHtmlElement
&elm
)
6327 renderPseudoElement(":after", elm
);
6330 // ***************************************************************************
6331 void CGroupHTML::htmlPRE(const CHtmlElement
&elm
)
6333 _PRE
.push_back(true);
6336 renderPseudoElement(":before", elm
);
6339 void CGroupHTML::htmlPREend(const CHtmlElement
&elm
)
6341 renderPseudoElement(":after", elm
);
6343 popIfNotEmpty(_PRE
);
6346 // ***************************************************************************
6347 void CGroupHTML::htmlPROGRESS(const CHtmlElement
&elm
)
6349 HTMLProgressElement progress
;
6350 progress
.readValues(elm
);
6352 std::string id
= "progress";
6353 if (elm
.hasAttribute("id"))
6354 id
= elm
.getAttribute("id");
6356 // width: 10em, height: 1em
6357 uint32 width
= _Style
.Current
.Width
> -1 ? _Style
.Current
.Width
: _Style
.Current
.FontSize
* 10;
6358 uint32 height
= _Style
.Current
.Height
> -1 ? _Style
.Current
.Height
: _Style
.Current
.FontSize
;
6359 // FIXME: only using border-top
6360 uint32 border
= _Style
.Current
.Border
.Top
.Width
.getValue() > -1 ? _Style
.Current
.Border
.Top
.Width
.getValue() : 0;
6362 uint barw
= (uint
) (width
* progress
.getValueRatio());
6363 CRGBA bgColor
= progress
.getBarColor(elm
, _Style
);
6364 CRGBA valueColor
= progress
.getValueColor(elm
, _Style
);
6366 typedef pair
<string
, string
> TTmplParam
;
6367 vector
<TTmplParam
> tmplParams
;
6368 tmplParams
.push_back(TTmplParam("id", id
));
6369 tmplParams
.push_back(TTmplParam("active", "true"));
6370 tmplParams
.push_back(TTmplParam("w", toString(width
)));
6371 tmplParams
.push_back(TTmplParam("h", toString(height
)));
6372 tmplParams
.push_back(TTmplParam("border_x2", toString(border
*2)));
6373 tmplParams
.push_back(TTmplParam("bgtexture", "blank.tga"));
6374 tmplParams
.push_back(TTmplParam("bgcolor", bgColor
.toString()));
6375 tmplParams
.push_back(TTmplParam("value_w", toString(barw
)));
6376 tmplParams
.push_back(TTmplParam("value_texture", "blank.tga"));
6377 tmplParams
.push_back(TTmplParam("value_color", valueColor
.toString()));
6379 CInterfaceGroup
*gr
= CWidgetManager::getInstance()->getParser()->createGroupInstance("html_progress", getParagraph()->getId(), &tmplParams
[0], (uint
)tmplParams
.size());
6382 renderPseudoElement(":before", elm
);
6383 getParagraph()->addChild(gr
);
6384 renderPseudoElement(":after", elm
);
6386 // ignore any inner elements
6387 _IgnoreChildElements
= true;
6391 // ***************************************************************************
6392 void CGroupHTML::htmlSCRIPT(const CHtmlElement
&elm
)
6397 void CGroupHTML::htmlSCRIPTend(const CHtmlElement
&elm
)
6399 _IgnoreText
= false;
6402 // ***************************************************************************
6403 void CGroupHTML::htmlSELECT(const CHtmlElement
&elm
)
6409 string name
= elm
.getAttribute("name");
6410 bool multiple
= elm
.hasAttribute("multiple");
6413 if (elm
.hasNonEmptyAttribute("size"))
6414 fromString(elm
.getAttribute("size"), size
);
6416 CGroupHTML::CForm::CEntry entry
;
6418 entry
.sbMultiple
= multiple
;
6419 if (size
> 1 || multiple
)
6421 entry
.InitialSelection
= -1;
6422 CGroupMenu
*sb
= addSelectBox(DefaultFormSelectBoxMenuGroup
, name
.c_str());
6428 if (_Style
.Current
.Width
> -1)
6429 sb
->setMinW(_Style
.Current
.Width
);
6431 if (_Style
.Current
.Height
> -1)
6432 sb
->setMinH(_Style
.Current
.Height
);
6434 sb
->setMaxVisibleLine(size
);
6435 sb
->setFontSize(_Style
.Current
.FontSize
, false);
6438 entry
.SelectBox
= sb
;
6442 CDBGroupComboBox
*cb
= addComboBox(DefaultFormSelectGroup
, name
.c_str());
6443 entry
.ComboBox
= cb
;
6449 setTextStyle(cb
->getViewText(), _Style
.Current
);
6452 _Forms
.back().Entries
.push_back (entry
);
6455 void CGroupHTML::htmlSELECTend(const CHtmlElement
&elm
)
6457 _SelectOption
= false;
6458 if (_Forms
.empty() || _Forms
.back().Entries
.empty())
6461 CDBGroupComboBox
*cb
= _Forms
.back().Entries
.back().ComboBox
;
6464 cb
->setSelectionNoTrigger(_Forms
.back().Entries
.back().InitialSelection
);
6465 // TODO: magic padding
6466 cb
->setW(cb
->evalContentWidth() + 16);
6470 // ***************************************************************************
6471 void CGroupHTML::htmlSTYLE(const CHtmlElement
&elm
)
6476 void CGroupHTML::htmlSTYLEend(const CHtmlElement
&elm
)
6478 _IgnoreText
= false;
6481 // ***************************************************************************
6482 void CGroupHTML::htmlTABLE(const CHtmlElement
&elm
)
6484 // Get cells parameters
6485 getCellsParameters(elm
, false);
6487 CGroupTable
*table
= new CGroupTable(TCtorParam());
6489 if (elm
.hasNonEmptyAttribute("id"))
6490 table
->setId(getCurrentGroup()->getId() + ":" + elm
.getAttribute("id"));
6492 table
->setId(getCurrentGroup()->getId() + ":TABLE" + toString(getNextAutoIdSeq()));
6494 // TODO: border-spacing: 2em;
6496 if (elm
.hasNonEmptyAttribute("cellspacing"))
6497 fromString(elm
.getAttribute("cellspacing"), table
->CellSpacing
);
6499 // TODO: cssLength, horiz/vert values
6500 if (_Style
.hasStyle("border-spacing"))
6501 fromString(_Style
.getStyle("border-spacing"), table
->CellSpacing
);
6503 // overrides border-spacing if set to 'collapse'
6504 if (_Style
.checkStyle("border-collapse", "collapse"))
6505 table
->CellSpacing
= 0;
6508 if (elm
.hasNonEmptyAttribute("cellpadding"))
6509 fromString(elm
.getAttribute("cellpadding"), table
->CellPadding
);
6511 if (_Style
.hasStyle("width"))
6513 // _Style.Width does not handle '%' unit currently
6514 if (_Style
.Current
.Width
> 0)
6516 table
->ForceWidthMin
= _Style
.Current
.Width
;
6517 table
->TableRatio
= 0;
6521 getPercentage (table
->ForceWidthMin
, table
->TableRatio
, _Style
.getStyle("width").c_str());
6524 else if (elm
.hasNonEmptyAttribute("width"))
6526 getPercentage (table
->ForceWidthMin
, table
->TableRatio
, elm
.getAttribute("width").c_str());
6529 // border from css or from attribute
6531 CSSRect
<CSSBorder
> border
;
6532 border
.Top
.Color
= _Style
.Current
.TextColor
;
6533 border
.Right
.Color
= _Style
.Current
.TextColor
;
6534 border
.Bottom
.Color
= _Style
.Current
.TextColor
;
6535 border
.Left
.Color
= _Style
.Current
.TextColor
;
6537 if (elm
.hasAttribute("border"))
6539 uint32 borderWidth
= 0;
6540 CRGBA borderColor
= CRGBA::Transparent
;
6542 std::string s
= elm
.getAttribute("border");
6546 fromString(elm
.getAttribute("border"), borderWidth
);
6548 if (elm
.hasNonEmptyAttribute("bordercolor"))
6549 scanHTMLColor(elm
.getAttribute("bordercolor").c_str(), borderColor
);
6551 borderColor
= CRGBA(128, 128, 128, 255);
6553 table
->CellBorder
= (borderWidth
> 0);
6555 border
.Top
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6556 border
.Right
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6557 border
.Bottom
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6558 border
.Left
.set(borderWidth
, CSS_LINE_STYLE_OUTSET
, borderColor
);
6561 if (_Style
.hasStyle("border-top-width")) border
.Top
.Width
= _Style
.Current
.Border
.Top
.Width
;
6562 if (_Style
.hasStyle("border-right-width")) border
.Right
.Width
= _Style
.Current
.Border
.Right
.Width
;
6563 if (_Style
.hasStyle("border-bottom-width")) border
.Bottom
.Width
= _Style
.Current
.Border
.Bottom
.Width
;
6564 if (_Style
.hasStyle("border-left-width")) border
.Left
.Width
= _Style
.Current
.Border
.Left
.Width
;
6566 if (_Style
.hasStyle("border-top-color")) border
.Top
.Color
= _Style
.Current
.Border
.Top
.Color
;
6567 if (_Style
.hasStyle("border-right-color")) border
.Right
.Color
= _Style
.Current
.Border
.Right
.Color
;
6568 if (_Style
.hasStyle("border-bottom-color")) border
.Bottom
.Color
= _Style
.Current
.Border
.Bottom
.Color
;
6569 if (_Style
.hasStyle("border-left-color")) border
.Left
.Color
= _Style
.Current
.Border
.Left
.Color
;
6571 if (_Style
.hasStyle("border-top-style")) border
.Top
.Style
= _Style
.Current
.Border
.Top
.Style
;
6572 if (_Style
.hasStyle("border-right-style")) border
.Right
.Style
= _Style
.Current
.Border
.Right
.Style
;
6573 if (_Style
.hasStyle("border-bottom-style")) border
.Bottom
.Style
= _Style
.Current
.Border
.Bottom
.Style
;
6574 if (_Style
.hasStyle("border-left-style")) border
.Left
.Style
= _Style
.Current
.Border
.Left
.Style
;
6576 table
->Border
->setBorder(border
);
6577 table
->Border
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
6578 table
->Border
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
6581 setupBackground(table
->Background
);
6582 table
->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
6584 table
->setMarginLeft(getIndent());
6585 addHtmlGroup (table
, 0);
6587 renderPseudoElement(":before", elm
);
6589 _Tables
.push_back(table
);
6591 // Add a cell pointer
6592 _Cells
.push_back(NULL
);
6593 _TR
.push_back(false);
6594 _Indent
.push_back(0);
6597 void CGroupHTML::htmlTABLEend(const CHtmlElement
&elm
)
6599 popIfNotEmpty(_CellParams
);
6601 popIfNotEmpty(_Cells
);
6602 popIfNotEmpty(_Tables
);
6603 popIfNotEmpty(_Indent
);
6605 renderPseudoElement(":after", elm
);
6608 // ***************************************************************************
6609 void CGroupHTML::htmlTD(const CHtmlElement
&elm
)
6611 // Get cells parameters
6612 getCellsParameters(elm
, true);
6614 if (!m_TableRowBackgroundColor
.empty() && m_TableRowBackgroundColor
.back().A
> 0)
6615 _Style
.Current
.Background
.color
.blendFromui(m_TableRowBackgroundColor
.back(), _Style
.Current
.Background
.color
, _Style
.Current
.Background
.color
.A
);
6617 if (elm
.ID
== HTML_TH
)
6619 if (!_Style
.hasStyle("font-weight"))
6620 _Style
.Current
.FontWeight
= FONT_WEIGHT_BOLD
;
6621 // center if not specified otherwise.
6622 if (!elm
.hasNonEmptyAttribute("align") && !_Style
.hasStyle("text-align"))
6623 _CellParams
.back().Align
= CGroupCell::Center
;
6626 CGroupTable
*table
= getTable();
6629 // <td> appears to be outside <table>
6635 // <table> not started
6639 _Cells
.back() = new CGroupCell(CViewBase::TCtorParam());
6640 if (elm
.hasNonEmptyAttribute("id"))
6641 _Cells
.back()->setId(table
->getId() + ":" + elm
.getAttribute("id"));
6643 _Cells
.back()->setId(table
->getId() + ":TD" + toString(getNextAutoIdSeq()));
6644 // inner cell content
6645 _Cells
.back()->Group
->setId(_Cells
.back()->getId() + ":CELL");
6647 setupBackground(_Cells
.back()->Background
);
6648 _Cells
.back()->setModulateGlobalColor(_Style
.Current
.GlobalColor
);
6650 if (elm
.hasNonEmptyAttribute("colspan"))
6651 fromString(elm
.getAttribute("colspan"), _Cells
.back()->ColSpan
);
6652 if (elm
.hasNonEmptyAttribute("rowspan"))
6653 fromString(elm
.getAttribute("rowspan"), _Cells
.back()->RowSpan
);
6655 _Cells
.back()->Align
= _CellParams
.back().Align
;
6656 _Cells
.back()->VAlign
= _CellParams
.back().VAlign
;
6657 _Cells
.back()->LeftMargin
= _CellParams
.back().LeftMargin
;
6658 _Cells
.back()->NoWrap
= _CellParams
.back().NoWrap
;
6659 _Cells
.back()->ColSpan
= std::max(1, _Cells
.back()->ColSpan
);
6660 _Cells
.back()->RowSpan
= std::max(1, _Cells
.back()->RowSpan
);
6661 _Cells
.back()->Height
= _CellParams
.back().Height
;
6664 if (_Style
.hasStyle("width"))
6666 // _Style.Width does not handle '%' unit currently
6667 if (_Style
.Current
.Width
> 0)
6669 _Cells
.back()->WidthWanted
= _Style
.Current
.Width
;
6670 _Cells
.back()->TableRatio
= 0;
6674 getPercentage (_Cells
.back()->WidthWanted
, _Cells
.back()->TableRatio
, _Style
.getStyle("width").c_str());
6677 else if (elm
.hasNonEmptyAttribute("width"))
6679 getPercentage (_Cells
.back()->WidthWanted
, _Cells
.back()->TableRatio
, elm
.getAttribute("width").c_str());
6682 _Cells
.back()->NewLine
= getTR();
6684 CSSRect
<CSSBorder
> border
;
6685 border
.Top
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6686 border
.Right
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6687 border
.Bottom
.set(table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6688 border
.Left
.set( table
->CellBorder
? 1 : 0, CSS_LINE_STYLE_INSET
, _Style
.Current
.TextColor
);
6690 if (_Style
.hasStyle("border-top-width")) border
.Top
.Width
= _Style
.Current
.Border
.Top
.Width
;
6691 if (_Style
.hasStyle("border-right-width")) border
.Right
.Width
= _Style
.Current
.Border
.Right
.Width
;
6692 if (_Style
.hasStyle("border-bottom-width")) border
.Bottom
.Width
= _Style
.Current
.Border
.Bottom
.Width
;
6693 if (_Style
.hasStyle("border-left-width")) border
.Left
.Width
= _Style
.Current
.Border
.Left
.Width
;
6695 if (_Style
.hasStyle("border-top-color")) border
.Top
.Color
= _Style
.Current
.Border
.Top
.Color
;
6696 if (_Style
.hasStyle("border-right-color")) border
.Right
.Color
= _Style
.Current
.Border
.Right
.Color
;
6697 if (_Style
.hasStyle("border-bottom-color")) border
.Bottom
.Color
= _Style
.Current
.Border
.Bottom
.Color
;
6698 if (_Style
.hasStyle("border-left-color")) border
.Left
.Color
= _Style
.Current
.Border
.Left
.Color
;
6700 if (_Style
.hasStyle("border-top-style")) border
.Top
.Style
= _Style
.Current
.Border
.Top
.Style
;
6701 if (_Style
.hasStyle("border-right-style")) border
.Right
.Style
= _Style
.Current
.Border
.Right
.Style
;
6702 if (_Style
.hasStyle("border-bottom-style")) border
.Bottom
.Style
= _Style
.Current
.Border
.Bottom
.Style
;
6703 if (_Style
.hasStyle("border-left-style")) border
.Left
.Style
= _Style
.Current
.Border
.Left
.Style
;
6705 _Cells
.back()->Border
->setBorder(border
);
6706 _Cells
.back()->Border
->setFontSize(_Style
.Root
.FontSize
, _Style
.Current
.FontSize
);
6707 _Cells
.back()->Border
->setViewport(getList()->getParentPos() ? getList()->getParentPos() : this);
6709 // padding from <table cellpadding="1">
6710 if (table
->CellPadding
)
6712 // FIXME: padding is ignored by vertical align
6713 _Cells
.back()->PaddingTop
= table
->CellPadding
;
6714 _Cells
.back()->PaddingRight
= table
->CellPadding
;
6715 _Cells
.back()->PaddingBottom
= table
->CellPadding
;
6716 _Cells
.back()->PaddingLeft
= table
->CellPadding
;
6719 if (_Style
.hasStyle("padding-top")) _Cells
.back()->PaddingTop
= _Style
.Current
.PaddingTop
;
6720 if (_Style
.hasStyle("padding-right")) _Cells
.back()->PaddingRight
= _Style
.Current
.PaddingRight
;
6721 if (_Style
.hasStyle("padding-bottom")) _Cells
.back()->PaddingBottom
= _Style
.Current
.PaddingBottom
;
6722 if (_Style
.hasStyle("padding-left")) _Cells
.back()->PaddingLeft
= _Style
.Current
.PaddingLeft
;
6724 table
->addChild (_Cells
.back());
6726 // reusing indent pushed by table
6729 newParagraph(TDBeginSpace
);
6730 // indent is already 0, getParagraph()->setMarginLeft(0); // maybe setIndent(0) if LI is using one
6736 renderPseudoElement(":before", elm
);
6739 void CGroupHTML::htmlTDend(const CHtmlElement
&elm
)
6741 renderPseudoElement(":after", elm
);
6743 popIfNotEmpty(_CellParams
);
6744 if (!_Cells
.empty())
6745 _Cells
.back() = NULL
;
6748 // ***************************************************************************
6749 void CGroupHTML::htmlTEXTAREA(const CHtmlElement
&elm
)
6751 _IgnoreChildElements
= true;
6753 // TODO: allow textarea without form
6757 // read general property
6758 string templateName
;
6760 // Widget template name
6761 if (elm
.hasNonEmptyAttribute("z_input_tmpl"))
6762 templateName
= elm
.getAttribute("z_input_tmpl");
6764 // Get the string name
6765 _TextAreaName
.clear();
6768 _TextAreaMaxLength
= 1024;
6769 if (elm
.hasNonEmptyAttribute("name"))
6770 _TextAreaName
= elm
.getAttribute("name");
6771 if (elm
.hasNonEmptyAttribute("rows"))
6772 fromString(elm
.getAttribute("rows"), _TextAreaRow
);
6773 if (elm
.hasNonEmptyAttribute("cols"))
6774 fromString(elm
.getAttribute("cols"), _TextAreaCols
);
6775 if (elm
.hasNonEmptyAttribute("maxlength"))
6776 fromString(elm
.getAttribute("maxlength"), _TextAreaMaxLength
);
6778 _TextAreaTemplate
= !templateName
.empty() ? templateName
: DefaultFormTextAreaGroup
;
6780 std::string content
= strFindReplaceAll(elm
.serializeChilds(false), std::string("\r"), std::string(""));
6782 CInterfaceGroup
*textArea
= addTextArea (_TextAreaTemplate
, _TextAreaName
.c_str (), _TextAreaRow
, _TextAreaCols
, true, content
, _TextAreaMaxLength
);
6785 // Add the text area to the form
6786 CGroupHTML::CForm::CEntry entry
;
6787 entry
.Name
= _TextAreaName
;
6788 entry
.TextArea
= textArea
;
6789 _Forms
.back().Entries
.push_back (entry
);
6793 // ***************************************************************************
6794 void CGroupHTML::htmlTH(const CHtmlElement
&elm
)
6799 void CGroupHTML::htmlTHend(const CHtmlElement
&elm
)
6804 // ***************************************************************************
6805 void CGroupHTML::htmlTITLE(const CHtmlElement
&elm
)
6807 _IgnoreChildElements
= true;
6809 // TODO: only from <head>
6810 // if (!_ReadingHeadTag) return;
6812 // consume all child elements
6813 _TitleString
= strFindReplaceAll(elm
.serializeChilds(false), std::string("\t"), std::string(" "));
6814 _TitleString
= strFindReplaceAll(_TitleString
, std::string("\n"), std::string(" "));
6815 setTitle(_TitleString
);
6818 // ***************************************************************************
6819 void CGroupHTML::htmlTR(const CHtmlElement
&elm
)
6821 // prevent inheriting from table
6822 if (!_CellParams
.empty())
6824 _CellParams
.back().BgColor
= CRGBA::Transparent
;
6825 _CellParams
.back().Height
= 0;
6828 // Get cells parameters
6829 getCellsParameters(elm
, true);
6831 m_TableRowBackgroundColor
.push_back(_CellParams
.back().BgColor
);
6832 _CellParams
.back().BgColor
= CRGBA::Transparent
;
6834 // TODO: this probably ends up in first cell
6835 renderPseudoElement(":before", elm
);
6842 void CGroupHTML::htmlTRend(const CHtmlElement
&elm
)
6844 // TODO: this probably ends up in last cell
6845 renderPseudoElement(":after", elm
);
6847 popIfNotEmpty(_CellParams
);
6848 popIfNotEmpty(m_TableRowBackgroundColor
);
6851 // ***************************************************************************
6852 void CGroupHTML::htmlUL(const CHtmlElement
&elm
)
6855 _UL
.push_back(HTMLOListElement(1, "disc"));
6856 else if (_UL
.size() == 1)
6857 _UL
.push_back(HTMLOListElement(1, "circle"));
6859 _UL
.push_back(HTMLOListElement(1, "square"));
6861 // if LI is already present
6862 _LI
= _UL
.size() > 1 || _DL
.size() > 1;
6863 _Indent
.push_back(getIndent() + ULIndent
);
6865 renderPseudoElement(":before", elm
);
6868 void CGroupHTML::htmlULend(const CHtmlElement
&elm
)
6873 renderPseudoElement(":after", elm
);
6876 popIfNotEmpty(_Indent
);