345 lines
9.6 KiB
C++
345 lines
9.6 KiB
C++
#include "MarkdownTip.hpp"
|
|
#include "GUI_App.hpp"
|
|
#include "GUI.hpp"
|
|
#include "MainFrame.hpp"
|
|
#include "Widgets/WebView.hpp"
|
|
|
|
#include "libslic3r/Utils.hpp"
|
|
#include "I18N.hpp"
|
|
|
|
#include <wx/display.h>
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
namespace Slic3r { namespace GUI {
|
|
|
|
// CMGUO
|
|
|
|
static std::string url_encode(const std::string& value) {
|
|
std::ostringstream escaped;
|
|
escaped.fill('0');
|
|
escaped << std::hex;
|
|
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
|
|
std::string::value_type c = (*i);
|
|
|
|
// Keep alphanumeric and other accepted characters intact
|
|
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
|
escaped << c;
|
|
continue;
|
|
}
|
|
|
|
// Any other characters are percent-encoded
|
|
escaped << std::uppercase;
|
|
escaped << '%' << std::setw(2) << int((unsigned char)c);
|
|
escaped << std::nouppercase;
|
|
}
|
|
return escaped.str();
|
|
}
|
|
/*
|
|
* Edge browser not support WebViewHandler
|
|
*
|
|
class MyWebViewHandler : public wxWebViewArchiveHandler
|
|
{
|
|
public:
|
|
MyWebViewHandler() : wxWebViewArchiveHandler("tooltip") {}
|
|
wxFSFile* GetFile(const wxString& uri) override {
|
|
// file:///resources/tooltip/test.md
|
|
wxFSFile* direct = wxWebViewArchiveHandler::GetFile(uri);
|
|
if (direct)
|
|
return direct;
|
|
// file:///data/tooltips.zip;protocol=zip/test.md
|
|
int n = uri.Find("resources/tooltip");
|
|
if (n == wxString::npos)
|
|
return direct;
|
|
set_var_dir(data_dir());
|
|
auto url = var("tooltips.zip");
|
|
std::replace(url.begin(), url.end(), '\\', '/');
|
|
auto uri2 = "file:///" + wxString(url) + ";protocol=zip" + uri.substr(n + 17);
|
|
return wxWebViewArchiveHandler::GetFile(uri2);
|
|
}
|
|
};
|
|
*/
|
|
|
|
/*
|
|
TODO:
|
|
1. Fix height correctly now h * 1.25 + 50
|
|
2. Async RunScript avoid long call stack risc
|
|
3. Fetch markdown content in javascript (*)
|
|
4. Use scheme handler to support zip archive & make code tidy
|
|
*/
|
|
|
|
MarkdownTip::MarkdownTip()
|
|
: wxPopupTransientWindow(wxGetApp().mainframe, wxBORDER_NONE)
|
|
{
|
|
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
_tipView = CreateTipView(this);
|
|
topsizer->Add(_tipView, wxSizerFlags().Expand().Proportion(1));
|
|
|
|
SetSizer(topsizer);
|
|
SetSize({400, 300});
|
|
|
|
LoadStyle();
|
|
|
|
_timer = new wxTimer;
|
|
_timer->Bind(wxEVT_TIMER, &MarkdownTip::OnTimer, this);
|
|
}
|
|
|
|
MarkdownTip::~MarkdownTip() { delete _timer; }
|
|
|
|
void MarkdownTip::LoadStyle()
|
|
{
|
|
_language = GUI::into_u8(GUI::wxGetApp().current_language_code());
|
|
fs::path ph(data_dir());
|
|
ph /= "resources/tooltip/common/styled.html";
|
|
_data_dir = true;
|
|
if (!fs::exists(ph)) {
|
|
ph = resources_dir();
|
|
ph /= "tooltip/styled.html";
|
|
_data_dir = false;
|
|
}
|
|
auto url = ph.string();
|
|
std::replace(url.begin(), url.end(), '\\', '/');
|
|
url = "file:///" + url;
|
|
_tipView->LoadURL(from_u8(url));
|
|
_lastTip.clear();
|
|
}
|
|
|
|
bool MarkdownTip::ShowTip(wxPoint pos, std::string const &tip, std::string const &tooltip)
|
|
{
|
|
if (tip.empty()) {
|
|
if (_tipView->GetParent() != this)
|
|
return false;
|
|
if (pos.x) {
|
|
_hide = true;
|
|
BOOST_LOG_TRIVIAL(info) << "MarkdownTip::ShowTip: hide soon on empty tip.";
|
|
this->Hide();
|
|
}
|
|
else if (!_hide) {
|
|
_hide = true;
|
|
BOOST_LOG_TRIVIAL(info) << "MarkdownTip::ShowTip: start hide timer (300)...";
|
|
_timer->StartOnce(300);
|
|
}
|
|
return false;
|
|
}
|
|
bool tipChanged = _lastTip != tip;
|
|
if (tipChanged) {
|
|
auto content = LoadTip(tip, tooltip);
|
|
if (content.empty()) {
|
|
_hide = true;
|
|
this->Hide();
|
|
BOOST_LOG_TRIVIAL(info) << "MarkdownTip::ShowTip: hide soon on empty content.";
|
|
return false;
|
|
}
|
|
auto script = "window.showMarkdown('" + url_encode(content) + "', true);";
|
|
if (!_pendingScript.empty()) {
|
|
_pendingScript = script;
|
|
}
|
|
else {
|
|
RunScript(script);
|
|
}
|
|
_lastTip = tip;
|
|
if (_tipView->GetParent() == this)
|
|
this->Hide();
|
|
}
|
|
if (_tipView->GetParent() == this) {
|
|
wxSize size = wxDisplay(this).GetClientArea().GetSize();
|
|
_requestPos = pos;
|
|
if (pos.y + this->GetSize().y > size.y)
|
|
pos.y = size.y - this->GetSize().y;
|
|
this->SetPosition(pos);
|
|
if (tipChanged || _hide) {
|
|
_hide = false;
|
|
BOOST_LOG_TRIVIAL(info) << "MarkdownTip::ShowTip: start show timer (500)...";
|
|
_timer->StartOnce(500);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string MarkdownTip::LoadTip(std::string const &tip, std::string const &tooltip)
|
|
{
|
|
fs::path ph;
|
|
wxString file;
|
|
wxFile f;
|
|
if (_data_dir) {
|
|
if (!_language.empty()) {
|
|
ph = data_dir();
|
|
ph /= "resources/tooltip/" + _language + "/" + tip + ".md";
|
|
file = from_u8(ph.string());
|
|
if (wxFile::Exists(file) && f.Open(file)) {
|
|
std::string content(f.Length(), 0);
|
|
f.Read(&content[0], content.size());
|
|
return content;
|
|
}
|
|
}
|
|
ph = data_dir();
|
|
ph /= "resources/tooltip/common/" + tip + ".md";
|
|
file = from_u8(ph.string());
|
|
if (wxFile::Exists(file) && f.Open(file)) {
|
|
std::string content(f.Length(), 0);
|
|
f.Read(&content[0], content.size());
|
|
return content;
|
|
}
|
|
}
|
|
/*
|
|
file = var("tooltips.zip");
|
|
if (wxFile::Exists(file) && f.Open(file)) {
|
|
wxFileInputStream fs(f);
|
|
wxZipInputStream zip(fs);
|
|
file = tip + ".md";
|
|
while (auto e = zip.GetNextEntry()) {
|
|
if (e->GetName() == file) {
|
|
if (zip.OpenEntry(*e)) {
|
|
std::string content(f.Length(), 0);
|
|
zip.Read(&content[0], content.size());
|
|
return content;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
ph = resources_dir();
|
|
ph /= "tooltip/" + _language + "/" + tip + ".md";
|
|
file = from_u8(ph.string());
|
|
if (wxFile::Exists(file) && f.Open(file)) {
|
|
std::string content(f.Length(), 0);
|
|
f.Read(&content[0], content.size());
|
|
return content;
|
|
}
|
|
ph = resources_dir();
|
|
ph /= "tooltip/" + tip + ".md";
|
|
file = from_u8(ph.string());
|
|
if (wxFile::Exists(file) && f.Open(file)) {
|
|
std::string content(f.Length(), 0);
|
|
f.Read(&content[0], content.size());
|
|
return content;
|
|
}
|
|
if (!tooltip.empty()) return "#### " + _utf8(tip) + "\n" + tooltip;
|
|
return (_tipView->GetParent() == this && tip.empty()) ? "" : LoadTip("", "");
|
|
}
|
|
|
|
void MarkdownTip::RunScript(std::string const& script)
|
|
{
|
|
WebView::RunScript(_tipView, script);
|
|
}
|
|
|
|
wxWebView* MarkdownTip::CreateTipView(wxWindow* parent)
|
|
{
|
|
wxWebView *tipView = WebView::CreateWebView(parent, "");
|
|
Bind(wxEVT_WEBVIEW_LOADED, &MarkdownTip::OnLoaded, this);
|
|
Bind(wxEVT_WEBVIEW_TITLE_CHANGED, &MarkdownTip::OnTitleChanged, this);
|
|
Bind(wxEVT_WEBVIEW_ERROR, &MarkdownTip::OnError, this);
|
|
return tipView;
|
|
}
|
|
|
|
void MarkdownTip::OnLoaded(wxWebViewEvent& event)
|
|
{
|
|
}
|
|
|
|
void MarkdownTip::OnTitleChanged(wxWebViewEvent& event)
|
|
{
|
|
if (!_pendingScript.empty()) {
|
|
RunScript(_pendingScript);
|
|
_pendingScript.clear();
|
|
return;
|
|
}
|
|
#ifdef __linux__
|
|
wxString str = "0";
|
|
#else
|
|
wxString str = event.GetString();
|
|
#endif
|
|
double height = 0;
|
|
if (str.ToDouble(&height)) {
|
|
if (height > _lastHeight - 10 && height < _lastHeight + 10)
|
|
return;
|
|
_lastHeight = height;
|
|
height *= 1.25; height += 50;
|
|
wxSize size = wxDisplay(this).GetClientArea().GetSize();
|
|
if (height > size.y)
|
|
height = size.y;
|
|
wxPoint pos = _requestPos;
|
|
if (pos.y + height > size.y)
|
|
pos.y = size.y - height;
|
|
this->SetSize({ 400, (int)height });
|
|
this->SetPosition(pos);
|
|
}
|
|
}
|
|
void MarkdownTip::OnError(wxWebViewEvent& event)
|
|
{
|
|
}
|
|
|
|
void MarkdownTip::OnTimer(wxTimerEvent& event)
|
|
{
|
|
if (_hide) {
|
|
wxPoint pos = ScreenToClient(wxGetMousePosition());
|
|
if (GetClientRect().Contains(pos)) {
|
|
BOOST_LOG_TRIVIAL(info) << "MarkdownTip::OnTimer: restart hide timer...";
|
|
_timer->StartOnce();
|
|
return;
|
|
}
|
|
BOOST_LOG_TRIVIAL(info) << "MarkdownTip::OnTimer: hide.";
|
|
this->Hide();
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(info) << "MarkdownTip::OnTimer: show.";
|
|
this->Show();
|
|
}
|
|
}
|
|
|
|
MarkdownTip* MarkdownTip::markdownTip(bool create)
|
|
{
|
|
static MarkdownTip * markdownTip = nullptr;
|
|
if (markdownTip == nullptr && create)
|
|
markdownTip = new MarkdownTip;
|
|
return markdownTip;
|
|
}
|
|
|
|
bool MarkdownTip::ShowTip(std::string const& tip, std::string const & tooltip, wxPoint pos)
|
|
{
|
|
#ifdef NDEBUG
|
|
return false;
|
|
#endif
|
|
return markdownTip()->ShowTip(pos, tip, tooltip);
|
|
}
|
|
|
|
void MarkdownTip::ExitTip()
|
|
{
|
|
//if (auto tip = markdownTip(false))
|
|
// tip->Destroy();
|
|
}
|
|
|
|
void MarkdownTip::Reload()
|
|
{
|
|
if (auto tip = markdownTip(false))
|
|
tip->LoadStyle();
|
|
}
|
|
|
|
void MarkdownTip::Recreate(wxWindow *parent)
|
|
{
|
|
if (auto tip = markdownTip(false)) {
|
|
tip->Reparent(parent);
|
|
tip->LoadStyle(); // switch language
|
|
}
|
|
}
|
|
|
|
wxWindow* MarkdownTip::AttachTo(wxWindow* parent)
|
|
{
|
|
MarkdownTip& tip = *markdownTip();
|
|
tip._tipView = tip.CreateTipView(parent);
|
|
tip._pendingScript = " ";
|
|
return tip._tipView;
|
|
}
|
|
|
|
wxWindow* MarkdownTip::DetachFrom(wxWindow* parent)
|
|
{
|
|
MarkdownTip& tip = *markdownTip();
|
|
if (tip._tipView->GetParent() == parent) {
|
|
tip.Destroy();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
}
|