calendar: Copy URLHighlighter and friends here
We will start using both URLHighlighter and the _fixMarkup() helper method the same way it's used in MessageTray. Usually we should make fixMarkup() public and call the existing methods, but we are planning for them to go away soon, so just keep two copies until the original one is removed. https://bugzilla.gnome.org/show_bug.cgi?id=744817
This commit is contained in:
parent
f84addcadc
commit
053e54f944
@ -9,8 +9,11 @@ const Signals = imports.signals;
|
|||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||||
const ELLIPSIS_CHAR = '\u2026';
|
const ELLIPSIS_CHAR = '\u2026';
|
||||||
@ -137,6 +140,147 @@ function _getEventDayAbbreviation(dayNumber) {
|
|||||||
return abbreviations[dayNumber];
|
return abbreviations[dayNumber];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _fixMarkup(text, allowMarkup) {
|
||||||
|
if (allowMarkup) {
|
||||||
|
// Support &, ", ', < and >, escape all other
|
||||||
|
// occurrences of '&'.
|
||||||
|
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&');
|
||||||
|
|
||||||
|
// Support <b>, <i>, and <u>, escape anything else
|
||||||
|
// so it displays as raw markup.
|
||||||
|
_text = _text.replace(/<(?!\/?[biu]>)/g, '<');
|
||||||
|
|
||||||
|
try {
|
||||||
|
Pango.parse_markup(_text, -1, '');
|
||||||
|
return _text;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// !allowMarkup, or invalid markup
|
||||||
|
return GLib.markup_escape_text(text, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const URLHighlighter = new Lang.Class({
|
||||||
|
Name: 'URLHighlighter',
|
||||||
|
|
||||||
|
_init: function(text, lineWrap, allowMarkup) {
|
||||||
|
if (!text)
|
||||||
|
text = '';
|
||||||
|
this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' });
|
||||||
|
this._linkColor = '#ccccff';
|
||||||
|
this.actor.connect('style-changed', Lang.bind(this, function() {
|
||||||
|
let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false);
|
||||||
|
if (hasColor) {
|
||||||
|
let linkColor = color.to_string().substr(0, 7);
|
||||||
|
if (linkColor != this._linkColor) {
|
||||||
|
this._linkColor = linkColor;
|
||||||
|
this._highlightUrls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this.actor.clutter_text.line_wrap = lineWrap;
|
||||||
|
this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||||
|
|
||||||
|
this.setMarkup(text, allowMarkup);
|
||||||
|
this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
|
||||||
|
// Don't try to URL highlight when invisible.
|
||||||
|
// The MessageTray doesn't actually hide us, so
|
||||||
|
// we need to check for paint opacities as well.
|
||||||
|
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
|
// Keep Notification.actor from seeing this and taking
|
||||||
|
// a pointer grab, which would block our button-release-event
|
||||||
|
// handler, if an URL is clicked
|
||||||
|
return this._findUrlAtPos(event) != -1;
|
||||||
|
}));
|
||||||
|
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
|
||||||
|
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
|
let urlId = this._findUrlAtPos(event);
|
||||||
|
if (urlId != -1) {
|
||||||
|
let url = this._urls[urlId].url;
|
||||||
|
if (url.indexOf(':') == -1)
|
||||||
|
url = 'http://' + url;
|
||||||
|
|
||||||
|
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
|
||||||
|
return Clutter.EVENT_STOP;
|
||||||
|
}
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
|
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
|
||||||
|
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
|
let urlId = this._findUrlAtPos(event);
|
||||||
|
if (urlId != -1 && !this._cursorChanged) {
|
||||||
|
global.screen.set_cursor(Meta.Cursor.POINTING_HAND);
|
||||||
|
this._cursorChanged = true;
|
||||||
|
} else if (urlId == -1) {
|
||||||
|
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||||
|
this._cursorChanged = false;
|
||||||
|
}
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
|
this.actor.connect('leave-event', Lang.bind(this, function() {
|
||||||
|
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
|
if (this._cursorChanged) {
|
||||||
|
this._cursorChanged = false;
|
||||||
|
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||||
|
}
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
setMarkup: function(text, allowMarkup) {
|
||||||
|
text = text ? _fixMarkup(text, allowMarkup) : '';
|
||||||
|
this._text = text;
|
||||||
|
|
||||||
|
this.actor.clutter_text.set_markup(text);
|
||||||
|
/* clutter_text.text contain text without markup */
|
||||||
|
this._urls = Util.findUrls(this.actor.clutter_text.text);
|
||||||
|
this._highlightUrls();
|
||||||
|
},
|
||||||
|
|
||||||
|
_highlightUrls: function() {
|
||||||
|
// text here contain markup
|
||||||
|
let urls = Util.findUrls(this._text);
|
||||||
|
let markup = '';
|
||||||
|
let pos = 0;
|
||||||
|
for (let i = 0; i < urls.length; i++) {
|
||||||
|
let url = urls[i];
|
||||||
|
let str = this._text.substr(pos, url.pos - pos);
|
||||||
|
markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>';
|
||||||
|
pos = url.pos + url.url.length;
|
||||||
|
}
|
||||||
|
markup += this._text.substr(pos);
|
||||||
|
this.actor.clutter_text.set_markup(markup);
|
||||||
|
},
|
||||||
|
|
||||||
|
_findUrlAtPos: function(event) {
|
||||||
|
let success;
|
||||||
|
let [x, y] = event.get_coords();
|
||||||
|
[success, x, y] = this.actor.transform_stage_point(x, y);
|
||||||
|
let find_pos = -1;
|
||||||
|
for (let i = 0; i < this.actor.clutter_text.text.length; i++) {
|
||||||
|
let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i);
|
||||||
|
if (py > y || py + line_height < y || x < px)
|
||||||
|
continue;
|
||||||
|
find_pos = i;
|
||||||
|
}
|
||||||
|
if (find_pos != -1) {
|
||||||
|
for (let i = 0; i < this._urls.length; i++)
|
||||||
|
if (find_pos >= this._urls[i].pos &&
|
||||||
|
this._urls[i].pos + this._urls[i].url.length > find_pos)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Abstraction for an appointment/event in a calendar
|
// Abstraction for an appointment/event in a calendar
|
||||||
|
|
||||||
const CalendarEvent = new Lang.Class({
|
const CalendarEvent = new Lang.Class({
|
||||||
|
Loading…
Reference in New Issue
Block a user