blob: a17d7c4fa6ad8e46695d14bc5a442e65e10a79ab [file] [log] [blame]
/**************************************************************************
* Copyright (C) 2016 by Savoir-faire Linux *
* Author: Traczyk Andreas <traczyk.andreas@savoirfairelinux.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
#include "pch.h"
#include "TextBlockExtension.h"
#include "HttpUtils.h"
using namespace RingClientUWP;
using namespace UserAndCustomControls;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Media::Imaging;
using namespace Windows::UI::Xaml::Navigation;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Documents;
using namespace Windows::UI::Core;
TextBlockExtension::TextBlockExtension()
{}
void
embeddedLoadCompleted(Object^ sender, NavigationEventArgs^ e)
{
RingD::instance->raiseMessageDataLoaded();
}
void
imageLoadCompleted(Object^ sender, RoutedEventArgs^ e)
{
RingD::instance->raiseMessageDataLoaded();
}
void
appendUrlToParagraph(Uri^ uri, Run^ run, Paragraph^ paragraph, bool& frameAdded, String^ str)
{
Hyperlink^ link = ref new Hyperlink();
link->NavigateUri = uri;
link->Foreground = ref new Windows::UI::Xaml::Media::SolidColorBrush(Windows::UI::Colors::DarkBlue);
run->Text = str;
link->Inlines->Append(run);
if (frameAdded) {
paragraph->Inlines->Append(ref new LineBreak());
frameAdded = false;
}
paragraph->Inlines->Append(link);
}
DependencyProperty^ TextBlockExtension::FormattedTextProperty =
DependencyProperty::Register("FormattedText", String::typeid, TextBlockExtension::typeid,
ref new PropertyMetadata(nullptr,
ref new PropertyChangedCallback([](DependencyObject^ sender, DependencyPropertyChangedEventArgs^ e)
{
auto text = Utils::toString(static_cast<String^>(e->NewValue));
auto rtextBlock = static_cast<RichTextBlock^>(sender);
rtextBlock->Blocks->Clear();
auto paragraph = ref new Paragraph();
if (paragraph != nullptr) {
static auto regex_opts = std::regex_constants::icase;
const static std::regex abs_url_regex("((((https?):\/\/))[^\ ]+)", regex_opts);
const static std::regex url_regex("((((https?):\/\/)|www)[^\ ]+)", regex_opts);
const static std::regex youtube_regex("(https?\:\/\/)?(www\.)?(youtube\.com|youtu\.?be)([^\=]+)=([^\ ]+)", regex_opts);
const static std::regex image_regex("[^\ ]+\.(gif|jpg|jpeg|png)", regex_opts);
const static std::regex word_regex("([^\ ]+)", regex_opts);
// emojis
const static std::regex emoji_smile("(^|\s):-?\\)(?!\S)|:smile", regex_opts);
const static std::regex emoji_wink("(^|\s);-?\\)(?!\S)|:wink", regex_opts);
const static std::regex emoji_grin("(^|\s):-?D(?!\S)|:grin", regex_opts);
const static std::regex emoji_meh("(^|\s):-?\\)(?!\S)|:meh", regex_opts);
const static std::regex emoji_sad("(^|\s):-?\\((?!\S)|:sad", regex_opts);
const static std::regex emoji_crazy("(^|\s);-?P(?!\S)", regex_opts);
const static std::regex emoji_tongue("(^|\s):-?P(?!\S)", regex_opts);
const static std::regex emoji_hmm("(^|\s):-?\\/(?!\S)");
const static std::regex emoji_rofl(":rofl", regex_opts);
const static std::regex emoji_heart("(^|\s)<3(?!\S)|:heart", regex_opts);
std::sregex_iterator next(text.begin(), text.end(), word_regex);
std::sregex_iterator end;
unsigned elementCount = 0;
bool frameAdded = false;
while (next != end) {
Run^ run = ref new Run();
Run^ postSpace = ref new Run();
postSpace->Text = " ";
std::smatch match = *next;
auto str = Utils::toPlatformString(match.str());
if (std::regex_match(match.str(), url_regex)) {
// it's a url so make it absolute if it's not already
if (!std::regex_match(match.str(), abs_url_regex))
str = "http://" + str;
auto uri = ref new Uri(str);
// youtube embed
std::smatch groups;
std::string sub(match.str());
if (std::regex_search(sub, groups, youtube_regex)) {
if (Utils::hasInternet()) {
WebView^ webview = ref new WebView();
webview->Width = 372;
webview->Height = 208;
auto videoId = Utils::toPlatformString(groups[5].str());
String^ bodyStr = "<style>iframe,html,body{border:0px;margin:0px;padding:0px;overflow-y:hidden;}</style>";
auto iframeStr = "<iframe width='372' height='210' src='https://www.youtube.com/embed/" +
videoId + "' frameborder='0'></iframe>";
webview->Margin = Windows::UI::Xaml::Thickness(2.0, 6.0, 4.0, 4.0);
webview->NavigateToString(bodyStr + iframeStr);
webview->LoadCompleted += ref new LoadCompletedEventHandler(&embeddedLoadCompleted);
InlineUIContainer^ iuc = ref new InlineUIContainer();
iuc->Child = webview;
if (elementCount > 0)
paragraph->Inlines->Append(ref new LineBreak());
paragraph->Inlines->Append(iuc);
frameAdded = true;
}
else {
appendUrlToParagraph(uri, run, paragraph, frameAdded, str);
}
}
// image
else if (std::regex_match(match.str(), image_regex)) {
if (Utils::hasInternet()) {
Image^ image = ref new Image();
image->Source = ref new BitmapImage(uri);
image->MaxWidth = 300;
image->Margin = Windows::UI::Xaml::Thickness(2.0, 6.0, 4.0, 4.0);
image->Loaded += ref new RoutedEventHandler(&imageLoadCompleted);
InlineUIContainer^ iuc = ref new InlineUIContainer();
iuc->Child = image;
if (elementCount > 0)
paragraph->Inlines->Append(ref new LineBreak());
paragraph->Inlines->Append(iuc);
frameAdded = true;
}
else {
appendUrlToParagraph(uri, run, paragraph, frameAdded, str);
}
}
else {
appendUrlToParagraph(uri, run, paragraph, frameAdded, str);
}
}
// text chunk
else {
// convert text emoji representations to unicode
auto ansiEmojiString = std::regex_replace(match.str(), emoji_smile, "🙂");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_wink, "😉");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_grin, "😁");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_meh, "😐");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_sad, "☹");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_crazy, "😜");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_tongue, "😛");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_hmm, "🤔");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_rofl, "🤣");
ansiEmojiString = std::regex_replace(ansiEmojiString, emoji_heart, "❤");
run->Text = Utils::toPlatformString(ansiEmojiString);
if (frameAdded) {
paragraph->Inlines->Append(ref new LineBreak());
frameAdded = false;
}
paragraph->Inlines->Append(run);
}
// add space after everything
paragraph->Inlines->Append(postSpace);
elementCount++;
next++;
}
rtextBlock->Blocks->Append(paragraph);
}
})));