Use Label instead of TextView for message display
This commit is contained in:
parent
9575b192e4
commit
85d194e349
|
@ -52,7 +52,6 @@ public class NotificationEvents : StreamInteractionModule, Object {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!should_notify_message(message, conversation)) return;
|
if (!should_notify_message(message, conversation)) return;
|
||||||
if (!should_notify_message(message, conversation)) return;
|
|
||||||
if (stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus()) return;
|
if (stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus()) return;
|
||||||
notify_message(message, conversation);
|
notify_message(message, conversation);
|
||||||
}
|
}
|
||||||
|
@ -62,7 +61,7 @@ public class NotificationEvents : StreamInteractionModule, Object {
|
||||||
if (notify == Conversation.NotifySetting.OFF) return false;
|
if (notify == Conversation.NotifySetting.OFF) return false;
|
||||||
Jid? nick = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
|
Jid? nick = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
|
||||||
if (notify == Conversation.NotifySetting.HIGHLIGHT && nick != null) {
|
if (notify == Conversation.NotifySetting.HIGHLIGHT && nick != null) {
|
||||||
return Regex.match_simple("""\b""" + Regex.escape_string(nick.resourcepart) + """\b""", message.body, RegexCompileFlags.CASELESS);
|
return Regex.match_simple("\\b" + Regex.escape_string(nick.resourcepart) + "\\b", message.body, RegexCompileFlags.CASELESS);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,6 @@ SOURCES
|
||||||
src/ui/conversation_summary/conversation_item_skeleton.vala
|
src/ui/conversation_summary/conversation_item_skeleton.vala
|
||||||
src/ui/conversation_summary/conversation_view.vala
|
src/ui/conversation_summary/conversation_view.vala
|
||||||
src/ui/conversation_summary/date_separator_populator.vala
|
src/ui/conversation_summary/date_separator_populator.vala
|
||||||
src/ui/conversation_summary/message_textview.vala
|
|
||||||
src/ui/conversation_summary/subscription_notification.vala
|
src/ui/conversation_summary/subscription_notification.vala
|
||||||
src/ui/conversation_titlebar/menu_entry.vala
|
src/ui/conversation_titlebar/menu_entry.vala
|
||||||
src/ui/conversation_titlebar/occupants_entry.vala
|
src/ui/conversation_titlebar/occupants_entry.vala
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScrolledWindow" id="scrolled">
|
<object class="GtkScrolledWindow" id="scrolled">
|
||||||
<property name="hscrollbar_policy">never</property>
|
<property name="hscrollbar_policy">never</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
|
@ -29,12 +30,6 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="filler">
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
@ -133,6 +133,7 @@
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="spacing">25</property>
|
<property name="spacing">25</property>
|
||||||
<property name="margin">10</property>
|
<property name="margin">10</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
|
@ -29,10 +29,6 @@ window.dino-main .dino-conversation .highlight-once {
|
||||||
animation-name: highlight;
|
animation-name: highlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.dino-main .dino-conversation textview, window.dino-main .dino-conversation textview text {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.dino-main .dino-sidebar > frame {
|
window.dino-main .dino-sidebar > frame {
|
||||||
background: @insensitive_bg_color;
|
background: @insensitive_bg_color;
|
||||||
border-left: 1px solid @borders;
|
border-left: 1px solid @borders;
|
||||||
|
@ -51,11 +47,6 @@ window.dino-main .dino-sidebar frame.auto-complete list > row {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.dino-main .dino-sidebar textview,
|
|
||||||
window.dino-main .dino-sidebar textview text {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.dino-main .dino-chatinput frame box {
|
window.dino-main .dino-chatinput frame box {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Gee;
|
using Gee;
|
||||||
using Gdk;
|
using Gdk;
|
||||||
using Gtk;
|
using Gtk;
|
||||||
|
using Xmpp;
|
||||||
|
|
||||||
using Dino.Entities;
|
using Dino.Entities;
|
||||||
|
|
||||||
|
@ -51,33 +52,36 @@ public class MessageItemWidgetGenerator : WidgetGenerator, Object {
|
||||||
Conversation conversation = message_item.conversation;
|
Conversation conversation = message_item.conversation;
|
||||||
Message message = message_item.message;
|
Message message = message_item.message;
|
||||||
|
|
||||||
MessageTextView text_view = new MessageTextView() { vexpand=true, visible = true };
|
Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true };
|
||||||
|
string markup_text = message.body;
|
||||||
|
if (markup_text.length > 10000) {
|
||||||
|
markup_text = markup_text.substring(0, 10000) + " [" + _("Message too long") + "]";
|
||||||
|
}
|
||||||
if (message_item.message.body.has_prefix("/me")) {
|
if (message_item.message.body.has_prefix("/me")) {
|
||||||
text_view.add_text(message.body.substring(3));
|
markup_text = markup_text.substring(3);
|
||||||
} else {
|
|
||||||
text_view.add_text(message.body);
|
|
||||||
}
|
}
|
||||||
|
markup_text = Markup.escape_text(markup_text);
|
||||||
|
|
||||||
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
||||||
text_view.highlight_word(conversation.nickname);
|
markup_text = Util.make_word_bold_markup(markup_text, conversation.nickname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message_item.message.body.has_prefix("/me")) {
|
if (message_item.message.body.has_prefix("/me")) {
|
||||||
string display_name = Util.get_message_display_name(stream_interactor, message, conversation.account);
|
string display_name = Util.get_message_display_name(stream_interactor, message, conversation.account);
|
||||||
string color = Util.get_name_hex_color(stream_interactor, conversation.account, conversation.counterpart, Util.is_dark_theme(text_view));
|
update_me_style(stream_interactor, message.real_jid ?? message.from, display_name, conversation.account, label, markup_text);
|
||||||
TextTag nick_tag = text_view.buffer.create_tag(null, foreground: @"#$color");
|
label.realize.connect(() => update_me_style(stream_interactor, message.real_jid ?? message.from, display_name, conversation.account, label, markup_text));
|
||||||
TextIter iter;
|
label.style_updated.connect(() => update_me_style(stream_interactor, message.real_jid ?? message.from, display_name, conversation.account, label, markup_text));
|
||||||
text_view.buffer.get_start_iter(out iter);
|
|
||||||
text_view.buffer.insert_with_tags(ref iter, display_name, display_name.length, nick_tag);
|
|
||||||
|
|
||||||
text_view.style_updated.connect(() => update_style(stream_interactor, message, conversation, nick_tag, text_view));
|
|
||||||
text_view.realize.connect(() => update_style(stream_interactor, message, conversation, nick_tag, text_view));
|
|
||||||
}
|
|
||||||
return text_view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void update_style(StreamInteractor stream_interactor, Message message, Conversation conversation, TextTag nick_tag, TextView text_view) {
|
markup_text = Util.make_link_markup(markup_text);
|
||||||
string color = Util.get_name_hex_color(stream_interactor, conversation.account, message.real_jid ?? message.from, Util.is_dark_theme(text_view));
|
|
||||||
nick_tag.foreground = "#" + color;
|
label.label = markup_text;
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void update_me_style(StreamInteractor stream_interactor, Jid jid, string display_name, Account account, Label label, string action_text) {
|
||||||
|
string color = Util.get_name_hex_color(stream_interactor, account, jid, Util.is_dark_theme(label));
|
||||||
|
label.label = @"<span color=\"#$(color)\">$(Markup.escape_text(display_name))</span>" + action_text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,25 +61,21 @@ public class ConversationView : Box, Plugins.ConversationItemCollection {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround GTK TextView issues: Delay first load of contents
|
|
||||||
public void initialize_for_conversation(Conversation? conversation) {
|
public void initialize_for_conversation(Conversation? conversation) {
|
||||||
|
// Workaround for rendering issues
|
||||||
if (firstLoad) {
|
if (firstLoad) {
|
||||||
int timeout = firstLoad ? 1000 : 0;
|
main.visible = false;
|
||||||
Timeout.add(timeout, () => {
|
Idle.add(() => {
|
||||||
stack.set_visible_child_name("void");
|
main.visible=true;
|
||||||
initialize_for_conversation_(conversation);
|
|
||||||
display_latest();
|
|
||||||
stack.set_visible_child_name("main");
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
firstLoad = false;
|
firstLoad = false;
|
||||||
} else {
|
}
|
||||||
stack.set_visible_child_name("void");
|
stack.set_visible_child_name("void");
|
||||||
initialize_for_conversation_(conversation);
|
initialize_for_conversation_(conversation);
|
||||||
display_latest();
|
display_latest();
|
||||||
stack.set_visible_child_name("main");
|
stack.set_visible_child_name("main");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize_around_message(Conversation conversation, ContentItem content_item) {
|
public void initialize_around_message(Conversation conversation, ContentItem content_item) {
|
||||||
stack.set_visible_child_name("void");
|
stack.set_visible_child_name("void");
|
||||||
|
@ -232,7 +228,6 @@ public class ConversationView : Box, Plugins.ConversationItemCollection {
|
||||||
lower_start_item.encryption == item.encryption &&
|
lower_start_item.encryption == item.encryption &&
|
||||||
(item.mark == Message.Marked.WONTSEND) == (lower_start_item.mark == Message.Marked.WONTSEND)) {
|
(item.mark == Message.Marked.WONTSEND) == (lower_start_item.mark == Message.Marked.WONTSEND)) {
|
||||||
lower_skeleton.add_meta_item(item);
|
lower_skeleton.add_meta_item(item);
|
||||||
Util.force_alloc_width(lower_skeleton, main.get_allocated_width());
|
|
||||||
|
|
||||||
widgets[item] = widgets[lower_start_item];
|
widgets[item] = widgets[lower_start_item];
|
||||||
item_item_skeletons[item] = lower_skeleton;
|
item_item_skeletons[item] = lower_skeleton;
|
||||||
|
@ -275,7 +270,6 @@ public class ConversationView : Box, Plugins.ConversationItemCollection {
|
||||||
main.add(insert);
|
main.add(insert);
|
||||||
}
|
}
|
||||||
widgets[item] = insert;
|
widgets[item] = insert;
|
||||||
Util.force_alloc_width(insert, main.get_allocated_width());
|
|
||||||
main.reorder_child(insert, index);
|
main.reorder_child(insert, index);
|
||||||
|
|
||||||
// If an item from the past was added, add everything between that item and the (post-)first present item
|
// If an item from the past was added, add everything between that item and the (post-)first present item
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
using Gdk;
|
|
||||||
using Gtk;
|
|
||||||
|
|
||||||
using Dino.Entities;
|
|
||||||
|
|
||||||
namespace Dino.Ui.ConversationSummary {
|
|
||||||
|
|
||||||
public class MessageTextView : TextView {
|
|
||||||
|
|
||||||
private TextTag link_tag;
|
|
||||||
private TextTag bold_tag;
|
|
||||||
|
|
||||||
public MessageTextView() {
|
|
||||||
Object(editable:false, hexpand:true, wrap_mode:WrapMode.WORD_CHAR);
|
|
||||||
|
|
||||||
link_tag = buffer.create_tag("url", underline: Pango.Underline.SINGLE, foreground: "blue");
|
|
||||||
bold_tag = buffer.create_tag("semibold", weight: Pango.Weight.SEMIBOLD);
|
|
||||||
button_release_event.connect((event_button) => {
|
|
||||||
if (event_button.button == 1) {
|
|
||||||
open_url(event_button);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
motion_notify_event.connect(change_cursor_over_url);
|
|
||||||
|
|
||||||
update_display_style();
|
|
||||||
style_updated.connect(update_display_style);
|
|
||||||
populate_popup.connect(populate_context_menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workaround GTK TextView issues
|
|
||||||
public override void get_preferred_width (out int minimum_width, out int natural_width) {
|
|
||||||
base.get_preferred_width(out minimum_width, out natural_width);
|
|
||||||
minimum_width = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add_text(string text_) {
|
|
||||||
string text = text_;
|
|
||||||
if (text.length > 10000) {
|
|
||||||
text = text.slice(0, 10000) + " [" + _("Message too long") + "]";
|
|
||||||
}
|
|
||||||
TextIter end;
|
|
||||||
buffer.get_end_iter(out end);
|
|
||||||
buffer.insert(ref end, text, -1);
|
|
||||||
format_suffix_urls(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void highlight_word(string word) {
|
|
||||||
Regex word_regex = new Regex("""\b""" + Regex.escape_string(word) + """\b""");
|
|
||||||
MatchInfo match_info;
|
|
||||||
word_regex.match(buffer.text, 0, out match_info);
|
|
||||||
for (; match_info.matches(); match_info.next()) {
|
|
||||||
int start;
|
|
||||||
int end;
|
|
||||||
match_info.fetch_pos(0, out start, out end);
|
|
||||||
start = buffer.text[0:start].char_count();
|
|
||||||
end = buffer.text[0:end].char_count();
|
|
||||||
TextIter start_iter;
|
|
||||||
TextIter end_iter;
|
|
||||||
buffer.get_iter_at_offset(out start_iter, start);
|
|
||||||
buffer.get_iter_at_offset(out end_iter, end);
|
|
||||||
buffer.apply_tag(bold_tag, start_iter, end_iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void update_display_style() {
|
|
||||||
LinkButton lnk = new LinkButton("http://example.com");
|
|
||||||
RGBA link_color = lnk.get_style_context().get_color(StateFlags.LINK);
|
|
||||||
link_tag.foreground_rgba = link_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? find_url_at_location(int x, int y) {
|
|
||||||
TextIter iter;
|
|
||||||
get_iter_at_location(out iter, x, y);
|
|
||||||
TextIter start_iter = iter, end_iter = iter;
|
|
||||||
if (start_iter.backward_to_tag_toggle(link_tag) && end_iter.forward_to_tag_toggle(link_tag)) {
|
|
||||||
return start_iter.get_text(end_iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void populate_context_menu(Gtk.Menu popup) {
|
|
||||||
popup.@foreach((widget) => { widget.destroy(); });
|
|
||||||
|
|
||||||
Gdk.Window window = get_window(TextWindowType.TEXT);
|
|
||||||
List<weak Seat> seats = window.get_display().list_seats();
|
|
||||||
if (seats.length() > 0) {
|
|
||||||
int device_x, device_y;
|
|
||||||
window.get_device_position(seats.nth_data(0).get_pointer(), out device_x, out device_y, null);
|
|
||||||
string url = find_url_at_location(device_x, device_y);
|
|
||||||
if (url != null) {
|
|
||||||
Gtk.MenuItem copy_url_item = new Gtk.MenuItem.with_label(_("Copy Link Address")) { visible=true };
|
|
||||||
copy_url_item.activate.connect(() => {
|
|
||||||
Clipboard.get_default(window.get_display()).set_text(url, url.length);
|
|
||||||
});
|
|
||||||
popup.append(copy_url_item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.MenuItem copy_item = new Gtk.MenuItem.with_label(_("Copy")) { visible=true };
|
|
||||||
copy_item.sensitive = buffer.get_has_selection();
|
|
||||||
copy_item.activate.connect(() => this.copy_clipboard() );
|
|
||||||
popup.append(copy_item);
|
|
||||||
|
|
||||||
Gtk.MenuItem select_all_item = new Gtk.MenuItem.with_label(_("Select All")) { visible=true };
|
|
||||||
select_all_item.activate.connect(() => this.select_all(true) );
|
|
||||||
popup.append(select_all_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void format_suffix_urls(string text) {
|
|
||||||
int absolute_start = buffer.text.char_count() - text.char_count();
|
|
||||||
|
|
||||||
Regex url_regex = new Regex("""(?i)\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""");
|
|
||||||
MatchInfo match_info;
|
|
||||||
url_regex.match(text, 0, out match_info);
|
|
||||||
for (; match_info.matches(); match_info.next()) {
|
|
||||||
int start;
|
|
||||||
int end;
|
|
||||||
match_info.fetch_pos(0, out start, out end);
|
|
||||||
start = text[0:start].char_count();
|
|
||||||
end = text[0:end].char_count();
|
|
||||||
TextIter start_iter;
|
|
||||||
TextIter end_iter;
|
|
||||||
buffer.get_iter_at_offset(out start_iter, absolute_start + start);
|
|
||||||
buffer.get_iter_at_offset(out end_iter, absolute_start + end);
|
|
||||||
buffer.apply_tag(link_tag, start_iter, end_iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool open_url(EventButton event_button) {
|
|
||||||
int buffer_x, buffer_y;
|
|
||||||
window_to_buffer_coords(TextWindowType.TEXT, (int) event_button.x, (int) event_button.y, out buffer_x, out buffer_y);
|
|
||||||
string url = find_url_at_location(buffer_x, buffer_y);
|
|
||||||
if (url != null) {
|
|
||||||
try{
|
|
||||||
AppInfo.launch_default_for_uri(url, null);
|
|
||||||
} catch (Error err) {
|
|
||||||
print("Tried to open " + url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool change_cursor_over_url(EventMotion event_motion) {
|
|
||||||
TextIter iter;
|
|
||||||
get_iter_at_location(out iter, (int) event_motion.x, (int) event_motion.y);
|
|
||||||
if (iter.has_tag(buffer.tag_table.lookup("url"))) {
|
|
||||||
event_motion.window.set_cursor(new Cursor.for_display(get_display(), CursorType.HAND2));
|
|
||||||
} else {
|
|
||||||
event_motion.window.set_cursor(new Cursor.for_display(get_display(), CursorType.XTERM));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -148,7 +148,6 @@ class GlobalSearch : Overlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget match_widget = get_match_message_widget(item);
|
Widget match_widget = get_match_message_widget(item);
|
||||||
Util.force_alloc_width(match_widget, results_empty_stack.get_allocated_width() - results_box.margin * 2);
|
|
||||||
context_box.add(match_widget);
|
context_box.add(match_widget);
|
||||||
|
|
||||||
if (after_message != null && after_message.size > 0) {
|
if (after_message != null && after_message.size > 0) {
|
||||||
|
@ -188,30 +187,39 @@ class GlobalSearch : Overlay {
|
||||||
text = text.substring(0, 25) + " … " + text.substring(index - 50, 50) + text.substring(index, 100) + " … " + text.substring(text.length - 25, 25);
|
text = text.substring(0, 25) + " … " + text.substring(index - 50, 50) + text.substring(index, 100) + " … " + text.substring(text.length - 25, 25);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextView tv = new TextView() { wrap_mode=Gtk.WrapMode.WORD_CHAR, hexpand=true, visible=true };
|
Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true };
|
||||||
tv.buffer.text = text;
|
string markup_text = Markup.escape_text(text);
|
||||||
TextTag link_tag = tv.buffer.create_tag("hit", background: "yellow");
|
|
||||||
|
|
||||||
|
// Build regex containing all keywords
|
||||||
|
string regex_str = "(";
|
||||||
Gee.List<string> keywords = get_keywords(Regex.escape_string(search.down()));
|
Gee.List<string> keywords = get_keywords(Regex.escape_string(search.down()));
|
||||||
|
bool first = true;
|
||||||
foreach (string keyword in keywords) {
|
foreach (string keyword in keywords) {
|
||||||
Regex url_regex = new Regex(keyword.down());
|
if (first) {
|
||||||
MatchInfo match_info;
|
first = false;
|
||||||
url_regex.match(text.down(), 0, out match_info);
|
} else {
|
||||||
for (; match_info.matches(); match_info.next()) {
|
regex_str += "|";
|
||||||
int start;
|
|
||||||
int end;
|
|
||||||
match_info.fetch_pos(0, out start, out end);
|
|
||||||
start = text[0:start].char_count();
|
|
||||||
end = text[0:end].char_count();
|
|
||||||
TextIter start_iter;
|
|
||||||
TextIter end_iter;
|
|
||||||
tv.buffer.get_iter_at_offset(out start_iter, start);
|
|
||||||
tv.buffer.get_iter_at_offset(out end_iter, end);
|
|
||||||
tv.buffer.apply_tag(link_tag, start_iter, end_iter);
|
|
||||||
}
|
}
|
||||||
|
regex_str += "\\b" + keyword;
|
||||||
}
|
}
|
||||||
|
regex_str += ")";
|
||||||
|
|
||||||
grid.attach(tv, 1, 1, 1, 1);
|
// Color the keywords
|
||||||
|
int elongated_by = 0;
|
||||||
|
Regex highlight_regex = new Regex(regex_str);
|
||||||
|
MatchInfo match_info;
|
||||||
|
string markup_text_bak = markup_text.down();
|
||||||
|
highlight_regex.match(markup_text_bak, 0, out match_info);
|
||||||
|
for (; match_info.matches(); match_info.next()) {
|
||||||
|
int start, end;
|
||||||
|
match_info.fetch_pos(0, out start, out end);
|
||||||
|
markup_text = markup_text[0:start+elongated_by] + "<span bgcolor=\"yellow\">" + markup_text[start+elongated_by:end+elongated_by] + "</span>" + markup_text[end+elongated_by:markup_text.length];
|
||||||
|
elongated_by += "<span bgcolor=\"yellow\">".length + "</span>".length;
|
||||||
|
}
|
||||||
|
markup_text_bak += ""; // We need markup_text_bak to live until here because url_regex.match does not copy the string
|
||||||
|
|
||||||
|
label.label = markup_text;
|
||||||
|
grid.attach(label, 1, 1, 1, 1);
|
||||||
|
|
||||||
Button button = new Button() { relief=ReliefStyle.NONE, visible=true };
|
Button button = new Button() { relief=ReliefStyle.NONE, visible=true };
|
||||||
button.clicked.connect(() => {
|
button.clicked.connect(() => {
|
||||||
|
|
|
@ -138,13 +138,39 @@ public static bool is_24h_format() {
|
||||||
return settings_format == "24h" || p_format == " ";
|
return settings_format == "24h" || p_format == " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround GTK TextView issues
|
public static string make_word_bold_markup(string s, string word) {
|
||||||
public static void force_alloc_width(Widget widget, int width) {
|
string ret = s;
|
||||||
Allocation alloc = Allocation();
|
int elongated_by = 0;
|
||||||
widget.get_preferred_width(out alloc.width, null);
|
Regex highlight_regex = new Regex("\\b" + Regex.escape_string(word.down()) + "\\b");
|
||||||
widget.get_preferred_height(out alloc.height, null);
|
MatchInfo match_info;
|
||||||
alloc.width = width;
|
string markup_text_bak = s.down();
|
||||||
widget.size_allocate(alloc);
|
highlight_regex.match(markup_text_bak, 0, out match_info);
|
||||||
|
for (; match_info.matches(); match_info.next()) {
|
||||||
|
int start, end;
|
||||||
|
match_info.fetch_pos(0, out start, out end);
|
||||||
|
ret = ret[0:start+elongated_by] + "<b>" + ret[start+elongated_by:end+elongated_by] + "</b>" + ret[end+elongated_by:ret.length];
|
||||||
|
elongated_by += 7;
|
||||||
|
}
|
||||||
|
markup_text_bak += ""; // We need markup_text_bak to live until here because url_regex.match does not copy the string
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string make_link_markup(string s) {
|
||||||
|
string ret = s;
|
||||||
|
Regex url_regex = new Regex("""(?i)\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""");
|
||||||
|
int elongated_by = 0;
|
||||||
|
MatchInfo match_info;
|
||||||
|
string markup_text_bak = ret.down();
|
||||||
|
url_regex.match(markup_text_bak, 0, out match_info);
|
||||||
|
for (; match_info.matches(); match_info.next()) {
|
||||||
|
int start, end;
|
||||||
|
match_info.fetch_pos(0, out start, out end);
|
||||||
|
string link = ret[start+elongated_by:end+elongated_by];
|
||||||
|
ret = ret[0:start+elongated_by] + "<a href=\"" + link + "\">" + link + "</a>" + ret[end+elongated_by:ret.length];
|
||||||
|
elongated_by += 15 + link.length;
|
||||||
|
}
|
||||||
|
markup_text_bak += ""; // We need markup_text_bak to live until here because url_regex.match does not copy the string
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue