4
4
using System . Collections . Generic ;
5
5
using System . ComponentModel ;
6
6
using System . Diagnostics ;
7
+ using System . Text . RegularExpressions ;
7
8
using Windows . Storage ;
8
9
using Windows . UI . Xaml ;
9
10
using Windows . UI . Xaml . Controls ;
11
+ using Windows . UI . Xaml . Documents ;
10
12
using Windows . UI . Xaml . Media ;
11
13
12
14
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
@@ -15,6 +17,9 @@ namespace Signal_Windows.Controls
15
17
{
16
18
public sealed partial class Message : UserControl , INotifyPropertyChanged
17
19
{
20
+ private const string UrlRegexString = @"(?i)\b((?:https?:(?:/{1,3}|[a-z0-9%])|[a-z0-9.\-]+[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)/)(?:[^\s()<>{}\[\]]+|\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\))+(?:\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\)|[^\s`!()\[\]{};:'"".,<>?«»“”‘’])|(?:(?<!@)[a-z0-9]+(?:[.\-][a-z0-9]+)*[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)\b/?(?!@)))" ;
21
+ private static Regex urlRegex = new Regex ( UrlRegexString ) ;
22
+
18
23
public event PropertyChangedEventHandler PropertyChanged ;
19
24
20
25
public SignalMessageContainer Model
@@ -53,7 +58,7 @@ private void UpdateUI()
53
58
{
54
59
if ( Model != null )
55
60
{
56
- MessageContentTextBlock . Text = Model . Message . Content . Content ;
61
+ UpdateMessageTextBlock ( ) ;
57
62
if ( Model . Message . Author == null )
58
63
{
59
64
MessageAuthor . Visibility = Visibility . Collapsed ;
@@ -119,6 +124,62 @@ private void UpdateUI()
119
124
}
120
125
}
121
126
127
+ private void UpdateMessageTextBlock ( )
128
+ {
129
+ string messageText = Model . Message . Content . Content ;
130
+ var matches = urlRegex . Matches ( messageText ) ;
131
+ if ( matches . Count == 0 )
132
+ {
133
+ MessageContentTextBlock . Text = messageText ;
134
+ }
135
+ else
136
+ {
137
+ MessageContentTextBlock . Inlines . Clear ( ) ;
138
+ int previousIndex = 0 ;
139
+ int currentIndex = 0 ;
140
+ foreach ( Match match in matches )
141
+ {
142
+ // First create a Run of the text before the link
143
+ currentIndex = match . Index ;
144
+ var length = currentIndex - previousIndex ;
145
+ if ( length > 0 )
146
+ {
147
+ Run run = new Run ( ) ;
148
+ run . Text = messageText . Substring ( previousIndex , currentIndex - previousIndex ) ;
149
+ MessageContentTextBlock . Inlines . Add ( run ) ;
150
+ }
151
+
152
+ // Now add the hyperlink
153
+ string link = match . Value ;
154
+ Hyperlink hyperlink = new Hyperlink ( ) ;
155
+ Run hyperlinkRun = new Run ( ) ;
156
+ hyperlinkRun . Text = link ;
157
+ try
158
+ {
159
+ hyperlink . NavigateUri = new Uri ( link ) ;
160
+ }
161
+ catch ( Exception )
162
+ {
163
+ continue ;
164
+ }
165
+ hyperlink . UnderlineStyle = UnderlineStyle . Single ;
166
+ hyperlink . Inlines . Add ( hyperlinkRun ) ;
167
+ MessageContentTextBlock . Inlines . Add ( hyperlink ) ;
168
+ previousIndex = currentIndex + match . Length ;
169
+ currentIndex = previousIndex ;
170
+ }
171
+
172
+ // Then finish up by adding the rest of the message text to the TextBox
173
+ var restLength = messageText . Length - currentIndex ;
174
+ if ( restLength > 0 )
175
+ {
176
+ Run restRun = new Run ( ) ;
177
+ restRun . Text = messageText . Substring ( currentIndex , restLength ) ;
178
+ MessageContentTextBlock . Inlines . Add ( restRun ) ;
179
+ }
180
+ }
181
+ }
182
+
122
183
private void MessageBox_DataContextChanged ( FrameworkElement sender , DataContextChangedEventArgs args )
123
184
{
124
185
UpdateUI ( ) ;
@@ -136,4 +197,4 @@ internal bool HandleUpdate(SignalMessage updatedMessage)
136
197
return updatedMessage . Status != SignalMessageStatus . Received ;
137
198
}
138
199
}
139
- }
200
+ }
0 commit comments