messageTray: fix handling of markup vs non-markup notifications
NotificationDaemon-based notifications have markup in the banner/body, but Telepathy-based notifications don't. (Eg, an XMPP message containing "<b>foo</b>" should show up angle brackets and all, not as bold.) Fix MessageTray.Notification to allow explicitly specifying where there should and shouldn't be markup, and use that appropriately. https://bugzilla.gnome.org/show_bug.cgi?id=610219
This commit is contained in:
parent
65f0b483f8
commit
d6f1c10b1b
@ -38,21 +38,27 @@ const State = {
|
|||||||
HIDING: 3
|
HIDING: 3
|
||||||
};
|
};
|
||||||
|
|
||||||
function _cleanMarkup(text) {
|
function _fixMarkup(text, allowMarkup) {
|
||||||
// Support &, ", ', < and >, escape all other
|
if (allowMarkup) {
|
||||||
// occurrences of '&'.
|
// Support &, ", ', < and >, escape all other
|
||||||
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&');
|
// occurrences of '&'.
|
||||||
// Support <b>, <i>, and <u>, escape anything else
|
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&');
|
||||||
// so it displays as raw markup.
|
// Support <b>, <i>, and <u>, escape anything else
|
||||||
return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '<$1');
|
// so it displays as raw markup.
|
||||||
|
return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '<$1');
|
||||||
|
} else {
|
||||||
|
// Escape everything
|
||||||
|
let _text = text.replace(/&/g, '&');
|
||||||
|
return _text.replace(/</g, '<');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function URLHighlighter(text, lineWrap) {
|
function URLHighlighter(text, lineWrap, allowMarkup) {
|
||||||
this._init(text, lineWrap);
|
this._init(text, lineWrap, allowMarkup);
|
||||||
}
|
}
|
||||||
|
|
||||||
URLHighlighter.prototype = {
|
URLHighlighter.prototype = {
|
||||||
_init: function(text, lineWrap) {
|
_init: function(text, lineWrap, allowMarkup) {
|
||||||
if (!text)
|
if (!text)
|
||||||
text = '';
|
text = '';
|
||||||
this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' });
|
this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' });
|
||||||
@ -74,7 +80,7 @@ URLHighlighter.prototype = {
|
|||||||
this.actor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
this.actor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setMarkup(text);
|
this.setMarkup(text, allowMarkup);
|
||||||
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
|
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
|
||||||
let urlId = this._findUrlAtPos(event);
|
let urlId = this._findUrlAtPos(event);
|
||||||
if (urlId != -1) {
|
if (urlId != -1) {
|
||||||
@ -112,8 +118,8 @@ URLHighlighter.prototype = {
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
setMarkup: function(text) {
|
setMarkup: function(text, allowMarkup) {
|
||||||
text = text ? _cleanMarkup(text) : '';
|
text = text ? _fixMarkup(text, allowMarkup) : '';
|
||||||
this._text = text;
|
this._text = text;
|
||||||
|
|
||||||
this.actor.clutter_text.set_markup(text);
|
this.actor.clutter_text.set_markup(text);
|
||||||
@ -187,7 +193,8 @@ URLHighlighter.prototype = {
|
|||||||
// area.
|
// area.
|
||||||
//
|
//
|
||||||
// @params can contain values for 'customContent', 'body', 'icon',
|
// @params can contain values for 'customContent', 'body', 'icon',
|
||||||
// and 'clear' parameters.
|
// 'titleMarkup', 'bannerMarkup', 'bodyMarkup', and 'clear'
|
||||||
|
// parameters.
|
||||||
//
|
//
|
||||||
// If @params contains a 'customContent' parameter with the value %true,
|
// If @params contains a 'customContent' parameter with the value %true,
|
||||||
// then @banner will not be shown in the body of the notification when the
|
// then @banner will not be shown in the body of the notification when the
|
||||||
@ -201,6 +208,12 @@ URLHighlighter.prototype = {
|
|||||||
// source.createNotificationIcon(). However, if @params contains an 'icon'
|
// source.createNotificationIcon(). However, if @params contains an 'icon'
|
||||||
// parameter, the passed in icon will be used.
|
// parameter, the passed in icon will be used.
|
||||||
//
|
//
|
||||||
|
// If @params contains a 'titleMarkup', 'bannerMarkup', or
|
||||||
|
// 'bodyMarkup' parameter with the value %true, then the corresponding
|
||||||
|
// element is assumed to use pango markup. If the parameter is not
|
||||||
|
// present for an element, then anything that looks like markup in
|
||||||
|
// that element will appear literally in the output.
|
||||||
|
//
|
||||||
// If @params contains a 'clear' parameter with the value %true, then
|
// If @params contains a 'clear' parameter with the value %true, then
|
||||||
// the content and the action area of the notification will be cleared.
|
// the content and the action area of the notification will be cleared.
|
||||||
// The content area is also always cleared if 'customContent' is false
|
// The content area is also always cleared if 'customContent' is false
|
||||||
@ -217,6 +230,7 @@ Notification.prototype = {
|
|||||||
this._useActionIcons = false;
|
this._useActionIcons = false;
|
||||||
this._customContent = false;
|
this._customContent = false;
|
||||||
this._bannerBodyText = null;
|
this._bannerBodyText = null;
|
||||||
|
this._bannerBodyMarkup = false;
|
||||||
this._titleFitsInBannerMode = true;
|
this._titleFitsInBannerMode = true;
|
||||||
this._spacing = 0;
|
this._spacing = 0;
|
||||||
|
|
||||||
@ -290,6 +304,9 @@ Notification.prototype = {
|
|||||||
params = Params.parse(params, { customContent: false,
|
params = Params.parse(params, { customContent: false,
|
||||||
body: null,
|
body: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
|
titleMarkup: false,
|
||||||
|
bannerMarkup: false,
|
||||||
|
bodyMarkup: false,
|
||||||
clear: false });
|
clear: false });
|
||||||
|
|
||||||
this._customContent = params.customContent;
|
this._customContent = params.customContent;
|
||||||
@ -320,7 +337,7 @@ Notification.prototype = {
|
|||||||
y_fill: false,
|
y_fill: false,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
title = title ? _cleanMarkup(title.replace(/\n/g, ' ')) : '';
|
title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : '';
|
||||||
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
|
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
|
||||||
|
|
||||||
// Unless the notification has custom content, we save this._bannerBodyText
|
// Unless the notification has custom content, we save this._bannerBodyText
|
||||||
@ -328,10 +345,11 @@ Notification.prototype = {
|
|||||||
// expandable due to other elements in its content area or due to the banner
|
// expandable due to other elements in its content area or due to the banner
|
||||||
// not fitting fully in the single-line mode.
|
// not fitting fully in the single-line mode.
|
||||||
this._bannerBodyText = this._customContent ? null : banner;
|
this._bannerBodyText = this._customContent ? null : banner;
|
||||||
|
this._bannerBodyMarkup = params.bannerMarkup;
|
||||||
|
|
||||||
banner = banner ? banner.replace(/\n/g, ' ') : '';
|
banner = banner ? banner.replace(/\n/g, ' ') : '';
|
||||||
|
|
||||||
this._bannerUrlHighlighter.setMarkup(banner);
|
this._bannerUrlHighlighter.setMarkup(banner, params.bannerMarkup);
|
||||||
this._bannerLabel.queue_relayout();
|
this._bannerLabel.queue_relayout();
|
||||||
|
|
||||||
// Add the bannerBody now if we know for sure we'll need it
|
// Add the bannerBody now if we know for sure we'll need it
|
||||||
@ -339,7 +357,7 @@ Notification.prototype = {
|
|||||||
this._addBannerBody();
|
this._addBannerBody();
|
||||||
|
|
||||||
if (params.body)
|
if (params.body)
|
||||||
this.addBody(params.body);
|
this.addBody(params.body, params.bodyMarkup);
|
||||||
this._updated();
|
this._updated();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -370,12 +388,13 @@ Notification.prototype = {
|
|||||||
|
|
||||||
// addBody:
|
// addBody:
|
||||||
// @text: the text
|
// @text: the text
|
||||||
|
// @markup: %true if @text contains pango markup
|
||||||
//
|
//
|
||||||
// Adds a multi-line label containing @text to the notification.
|
// Adds a multi-line label containing @text to the notification.
|
||||||
//
|
//
|
||||||
// Return value: the newly-added label
|
// Return value: the newly-added label
|
||||||
addBody: function(text) {
|
addBody: function(text, markup) {
|
||||||
let label = new URLHighlighter(text, true);
|
let label = new URLHighlighter(text, true, markup);
|
||||||
|
|
||||||
this.addActor(label.actor);
|
this.addActor(label.actor);
|
||||||
return label.actor;
|
return label.actor;
|
||||||
@ -385,7 +404,7 @@ Notification.prototype = {
|
|||||||
if (this._bannerBodyText) {
|
if (this._bannerBodyText) {
|
||||||
let text = this._bannerBodyText;
|
let text = this._bannerBodyText;
|
||||||
this._bannerBodyText = null;
|
this._bannerBodyText = null;
|
||||||
this.addBody(text);
|
this.addBody(text, this._bannerBodyMarkup);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -205,8 +205,6 @@ NotificationDaemon.prototype = {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
summary = GLib.markup_escape_text(summary, -1);
|
|
||||||
|
|
||||||
let rewrites = rewriteRules[appName];
|
let rewrites = rewriteRules[appName];
|
||||||
if (rewrites) {
|
if (rewrites) {
|
||||||
for (let i = 0; i < rewrites.length; i++) {
|
for (let i = 0; i < rewrites.length; i++) {
|
||||||
@ -281,7 +279,9 @@ NotificationDaemon.prototype = {
|
|||||||
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||||
|
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
notification = new MessageTray.Notification(source, summary, body, { icon: iconActor });
|
notification = new MessageTray.Notification(source, summary, body,
|
||||||
|
{ icon: iconActor,
|
||||||
|
bannerMarkup: true });
|
||||||
ndata.notification = notification;
|
ndata.notification = notification;
|
||||||
notification.connect('clicked', Lang.bind(this,
|
notification.connect('clicked', Lang.bind(this,
|
||||||
function(n) {
|
function(n) {
|
||||||
@ -294,6 +294,7 @@ NotificationDaemon.prototype = {
|
|||||||
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
||||||
} else {
|
} else {
|
||||||
notification.update(summary, body, { icon: iconActor,
|
notification.update(summary, body, { icon: iconActor,
|
||||||
|
bannerMarkup: true,
|
||||||
clear: true });
|
clear: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user