mirror of
https://github.com/elementary/gala.git
synced 2024-11-23 20:07:21 +03:00
add basic support for confirmation notifications
This commit is contained in:
parent
3694419365
commit
904cf24f2e
86
plugins/notify/ConfirmationNotification.vala
Normal file
86
plugins/notify/ConfirmationNotification.vala
Normal file
@ -0,0 +1,86 @@
|
||||
//
|
||||
// Copyright (C) 2014 Tom Beckmann
|
||||
//
|
||||
// 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/>.
|
||||
//
|
||||
|
||||
using Clutter;
|
||||
using Meta;
|
||||
|
||||
namespace Gala.Plugins.Notify
|
||||
{
|
||||
public class ConfirmationNotification : Notification
|
||||
{
|
||||
const int DURATION = 2000;
|
||||
|
||||
public bool has_progress { get; private set; }
|
||||
|
||||
int _progress;
|
||||
public int progress {
|
||||
get {
|
||||
return _progress;
|
||||
}
|
||||
private set {
|
||||
_progress = value;
|
||||
content.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
public ConfirmationNotification (uint32 id, Gdk.Pixbuf? icon, int progress)
|
||||
{
|
||||
base (id, icon, NotificationUrgency.LOW, DURATION);
|
||||
|
||||
this.has_progress = progress > -1;
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
public override void update_allocation (out float content_height, AllocationFlags flags)
|
||||
{
|
||||
content_height = ICON_SIZE;
|
||||
}
|
||||
|
||||
const int PROGRESS_HEIGHT = 12;
|
||||
|
||||
public override void draw_content (Cairo.Context cr)
|
||||
{
|
||||
if (!has_progress)
|
||||
return;
|
||||
|
||||
var x = MARGIN + PADDING + ICON_SIZE + SPACING;
|
||||
var y = MARGIN + PADDING + (ICON_SIZE - PROGRESS_HEIGHT) / 2;
|
||||
var width = WIDTH - x - PADDING - MARGIN;
|
||||
var fraction = (int) Math.floor (progress.clamp (0, 100) / 100.0 * width);
|
||||
|
||||
Granite.Drawing.Utilities.cairo_rounded_rectangle (cr, x, y, width,
|
||||
PROGRESS_HEIGHT, PROGRESS_HEIGHT / 2);
|
||||
cr.set_source_rgb (0.8, 0.8, 0.8);
|
||||
cr.fill ();
|
||||
|
||||
if (progress > 0) {
|
||||
Granite.Drawing.Utilities.cairo_rounded_rectangle (cr, x, y, fraction,
|
||||
PROGRESS_HEIGHT, PROGRESS_HEIGHT / 2);
|
||||
cr.set_source_rgb (0.3, 0.3, 0.3);
|
||||
cr.fill ();
|
||||
}
|
||||
}
|
||||
|
||||
public void update (Gdk.Pixbuf? icon, int progress)
|
||||
{
|
||||
this.progress = progress;
|
||||
|
||||
update_base (icon, DURATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,7 @@ namespace Gala.Plugins.Notify
|
||||
freeze_track = running;
|
||||
});
|
||||
|
||||
server = new NotifyServer ();
|
||||
server.show_notification.connect (stack.show_notification);
|
||||
server = new NotifyServer (stack);
|
||||
|
||||
update_position ();
|
||||
screen.monitors_changed.connect (update_position);
|
||||
|
@ -34,6 +34,8 @@ libgala_notify_la_LIBADD = \
|
||||
|
||||
libgala_notify_la_VALASOURCES = \
|
||||
Main.vala \
|
||||
ConfirmationNotification.vala \
|
||||
NormalNotification.vala \
|
||||
Notification.vala \
|
||||
NotificationStack.vala \
|
||||
NotifyServer.vala \
|
||||
|
171
plugins/notify/NormalNotification.vala
Normal file
171
plugins/notify/NormalNotification.vala
Normal file
@ -0,0 +1,171 @@
|
||||
//
|
||||
// Copyright (C) 2014 Tom Beckmann
|
||||
//
|
||||
// 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/>.
|
||||
//
|
||||
|
||||
using Clutter;
|
||||
using Meta;
|
||||
|
||||
namespace Gala.Plugins.Notify
|
||||
{
|
||||
public class NormalNotification : Notification
|
||||
{
|
||||
public string summary { get; private set; }
|
||||
public string body { get; private set; }
|
||||
public uint32 sender_pid { get; private set; }
|
||||
public string[] notification_actions { get; private set; }
|
||||
public Screen screen { get; private set; }
|
||||
|
||||
Text summary_label;
|
||||
Text body_label;
|
||||
|
||||
public NormalNotification (Screen screen, uint32 id, string summary, string body, Gdk.Pixbuf? icon,
|
||||
NotificationUrgency urgency, int32 expire_timeout, uint32 pid, string[] actions)
|
||||
{
|
||||
base (id, icon, urgency, expire_timeout);
|
||||
|
||||
this.screen = screen;
|
||||
this.summary = summary;
|
||||
this.body = body;
|
||||
this.sender_pid = pid;
|
||||
this.notification_actions = actions;
|
||||
|
||||
summary_label = new Text.with_text (null, "");
|
||||
summary_label.line_wrap = true;
|
||||
summary_label.use_markup = true;
|
||||
summary_label.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
|
||||
body_label = new Text.with_text (null, "");
|
||||
body_label.line_wrap = true;
|
||||
body_label.use_markup = true;
|
||||
body_label.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
|
||||
add_child (summary_label);
|
||||
add_child (body_label);
|
||||
|
||||
set_values ();
|
||||
}
|
||||
|
||||
public void update (string summary, string body, Gdk.Pixbuf? icon, int32 expire_timeout,
|
||||
string[] actions)
|
||||
{
|
||||
var visible_change = this.summary != summary || this.body != body;
|
||||
|
||||
this.summary = summary;
|
||||
this.body = body;
|
||||
|
||||
set_values ();
|
||||
update_base (icon, expire_timeout);
|
||||
|
||||
if (!visible_change)
|
||||
return;
|
||||
|
||||
if (get_transition ("update") != null)
|
||||
remove_transition ("update");
|
||||
|
||||
var opacity_transition = new PropertyTransition ("opacity");
|
||||
opacity_transition.set_from_value (255);
|
||||
opacity_transition.set_to_value (0);
|
||||
opacity_transition.duration = 400;
|
||||
opacity_transition.auto_reverse = true;
|
||||
opacity_transition.repeat_count = 1;
|
||||
opacity_transition.remove_on_complete = true;
|
||||
|
||||
add_transition ("update", opacity_transition);
|
||||
}
|
||||
|
||||
void set_values ()
|
||||
{
|
||||
summary_label.set_markup ("<b>" + summary + "</b>");
|
||||
body_label.set_markup (body);
|
||||
}
|
||||
|
||||
public override void update_allocation (out float content_height, AllocationFlags flags)
|
||||
{
|
||||
var label_x = MARGIN + PADDING + ICON_SIZE + SPACING;
|
||||
var label_width = WIDTH - label_x - MARGIN - SPACING;
|
||||
|
||||
float summary_height, body_height;
|
||||
summary_label.get_preferred_height (label_width, null, out summary_height);
|
||||
body_label.get_preferred_height (label_width, null, out body_height);
|
||||
|
||||
var label_height = summary_height + SPACING + body_height;
|
||||
var label_y = MARGIN + PADDING;
|
||||
// center
|
||||
if (label_height < ICON_SIZE)
|
||||
label_y += (ICON_SIZE - (int) label_height) / 2;
|
||||
|
||||
var summary_alloc = ActorBox ();
|
||||
summary_alloc.set_origin (label_x, label_y);
|
||||
summary_alloc.set_size (label_width, summary_height);
|
||||
summary_label.allocate (summary_alloc, flags);
|
||||
|
||||
var body_alloc = ActorBox ();
|
||||
body_alloc.set_origin (label_x, label_y + summary_height + SPACING);
|
||||
body_alloc.set_size (label_width, body_height);
|
||||
body_label.allocate (body_alloc, flags);
|
||||
|
||||
content_height = label_height < ICON_SIZE ? ICON_SIZE : label_height;
|
||||
}
|
||||
|
||||
public override void get_preferred_height (float for_width, out float min_height, out float nat_height)
|
||||
{
|
||||
var label_x = MARGIN + PADDING + ICON_SIZE + SPACING;
|
||||
var label_width = WIDTH - label_x - MARGIN - SPACING;
|
||||
|
||||
float summary_height, body_height;
|
||||
summary_label.get_preferred_height (label_width, null, out summary_height);
|
||||
body_label.get_preferred_height (label_width, null, out body_height);
|
||||
|
||||
var label_height = summary_height + SPACING + body_height;
|
||||
var content_height = label_height < ICON_SIZE ? ICON_SIZE : label_height;
|
||||
|
||||
min_height = nat_height = content_height + (MARGIN + SPACING) * 2;
|
||||
}
|
||||
|
||||
public override void activate ()
|
||||
{
|
||||
var window = get_window ();
|
||||
if (window != null) {
|
||||
var workspace = window.get_workspace ();
|
||||
var time = screen.get_display ().get_current_time ();
|
||||
|
||||
if (workspace != screen.get_active_workspace ())
|
||||
workspace.activate_with_focus (window, time);
|
||||
else
|
||||
window.activate (time);
|
||||
}
|
||||
}
|
||||
|
||||
Window? get_window ()
|
||||
{
|
||||
if (sender_pid == 0)
|
||||
return null;
|
||||
|
||||
foreach (var actor in Compositor.get_window_actors (screen)) {
|
||||
var window = actor.get_meta_window ();
|
||||
|
||||
// the windows are sorted by stacking order when returned
|
||||
// from meta_get_window_actors, so we can just pick the first
|
||||
// one we find and have a pretty good match
|
||||
if (window.get_pid () == sender_pid)
|
||||
return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,48 +20,36 @@ using Meta;
|
||||
|
||||
namespace Gala.Plugins.Notify
|
||||
{
|
||||
public class Notification : Actor
|
||||
public abstract class Notification : Actor
|
||||
{
|
||||
public static const int WIDTH = 300;
|
||||
public static const int ICON_SIZE = 48;
|
||||
public static const int MARGIN = 12;
|
||||
public const int WIDTH = 300;
|
||||
public const int ICON_SIZE = 48;
|
||||
public const int MARGIN = 12;
|
||||
|
||||
const int SPACING = 6;
|
||||
const int PADDING = 4;
|
||||
public const int SPACING = 6;
|
||||
public const int PADDING = 4;
|
||||
|
||||
public Screen screen { get; construct; }
|
||||
public uint32 id { get; construct; }
|
||||
public string summary { get; construct set; }
|
||||
public string body { get; construct set; }
|
||||
public Gdk.Pixbuf? icon { get; construct set; }
|
||||
public NotificationUrgency urgency { get; construct; }
|
||||
public int32 expire_timeout { get; construct set; }
|
||||
public uint32 sender_pid { get; construct; }
|
||||
public string[] notification_actions { get; construct set; }
|
||||
|
||||
public uint64 relevancy_time { get; private set; }
|
||||
public bool being_destroyed { get; private set; default = false; }
|
||||
|
||||
Text summary_label;
|
||||
Text body_label;
|
||||
GtkClutter.Texture icon_texture;
|
||||
GtkClutter.Texture close_button;
|
||||
|
||||
uint remove_timeout = 0;
|
||||
|
||||
public Notification (Screen screen, uint32 id, string summary, string body, Gdk.Pixbuf? icon,
|
||||
NotificationUrgency urgency, int32 expire_timeout, uint32 pid, string[] actions)
|
||||
public Notification (uint32 id, Gdk.Pixbuf? icon, NotificationUrgency urgency,
|
||||
int32 expire_timeout)
|
||||
{
|
||||
Object (
|
||||
screen: screen,
|
||||
id: id,
|
||||
summary: summary,
|
||||
body: body,
|
||||
icon: icon,
|
||||
urgency: urgency,
|
||||
expire_timeout: expire_timeout,
|
||||
sender_pid: pid,
|
||||
notification_actions: actions
|
||||
expire_timeout: expire_timeout
|
||||
);
|
||||
|
||||
relevancy_time = new DateTime.now_local ().to_unix ();
|
||||
@ -70,16 +58,6 @@ namespace Gala.Plugins.Notify
|
||||
margin_left = 12;
|
||||
set_pivot_point (0.5f, 0.5f);
|
||||
|
||||
summary_label = new Text.with_text (null, "");
|
||||
summary_label.line_wrap = true;
|
||||
summary_label.use_markup = true;
|
||||
summary_label.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
|
||||
body_label = new Text.with_text (null, "");
|
||||
body_label.line_wrap = true;
|
||||
body_label.use_markup = true;
|
||||
body_label.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
|
||||
icon_texture = new GtkClutter.Texture ();
|
||||
icon_texture.set_pivot_point (0.5f, 0.5f);
|
||||
|
||||
@ -88,8 +66,6 @@ namespace Gala.Plugins.Notify
|
||||
close_button.reactive = true;
|
||||
close_button.set_easing_duration (300);
|
||||
|
||||
add_child (summary_label);
|
||||
add_child (body_label);
|
||||
add_child (icon_texture);
|
||||
add_child (close_button);
|
||||
|
||||
@ -101,16 +77,7 @@ namespace Gala.Plugins.Notify
|
||||
|
||||
var click = new ClickAction ();
|
||||
click.clicked.connect (() => {
|
||||
var window = get_window ();
|
||||
if (window != null) {
|
||||
var workspace = window.get_workspace ();
|
||||
var time = screen.get_display ().get_current_time ();
|
||||
|
||||
if (workspace != screen.get_active_workspace ())
|
||||
workspace.activate_with_focus (window, time);
|
||||
else
|
||||
window.activate (time);
|
||||
}
|
||||
activate ();
|
||||
});
|
||||
add_action (click);
|
||||
|
||||
@ -225,59 +192,17 @@ namespace Gala.Plugins.Notify
|
||||
destroy ();
|
||||
}
|
||||
|
||||
Window? get_window ()
|
||||
public void update_base (Gdk.Pixbuf? icon, int32 expire_timeout)
|
||||
{
|
||||
if (sender_pid == 0)
|
||||
return null;
|
||||
|
||||
foreach (var actor in Compositor.get_window_actors (screen)) {
|
||||
var window = actor.get_meta_window ();
|
||||
|
||||
// the windows are sorted by stacking order when returned
|
||||
// from meta_get_window_actors, so we can just pick the first
|
||||
// one we find and have a pretty good match
|
||||
if (window.get_pid () == sender_pid)
|
||||
return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void update (string summary, string body, Gdk.Pixbuf? icon, int32 expire_timeout,
|
||||
string[] actions)
|
||||
{
|
||||
var visible_change = this.summary != summary || this.body != body;
|
||||
|
||||
this.summary = summary;
|
||||
this.body = body;
|
||||
this.icon = icon;
|
||||
this.expire_timeout = expire_timeout;
|
||||
this.relevancy_time = new DateTime.now_local ().to_unix ();
|
||||
|
||||
set_values ();
|
||||
|
||||
if (!visible_change)
|
||||
return;
|
||||
|
||||
if (get_transition ("update") != null)
|
||||
remove_transition ("update");
|
||||
|
||||
var opacity_transition = new PropertyTransition ("opacity");
|
||||
opacity_transition.set_from_value (255);
|
||||
opacity_transition.set_to_value (0);
|
||||
opacity_transition.duration = 400;
|
||||
opacity_transition.auto_reverse = true;
|
||||
opacity_transition.repeat_count = 1;
|
||||
opacity_transition.remove_on_complete = true;
|
||||
|
||||
add_transition ("update", opacity_transition);
|
||||
}
|
||||
|
||||
void set_values ()
|
||||
{
|
||||
summary_label.set_markup ("<b>" + summary + "</b>");
|
||||
body_label.set_markup (body);
|
||||
|
||||
if (icon != null) {
|
||||
try {
|
||||
icon_texture.set_from_pixbuf (icon);
|
||||
@ -290,7 +215,7 @@ namespace Gala.Plugins.Notify
|
||||
void set_timeout ()
|
||||
{
|
||||
// crtitical notifications have to be dismissed manually
|
||||
if (urgency == NotificationUrgency.CRITICAL)
|
||||
if (expire_timeout == 0 || urgency == NotificationUrgency.CRITICAL)
|
||||
return;
|
||||
|
||||
clear_timeout ();
|
||||
@ -329,36 +254,23 @@ namespace Gala.Plugins.Notify
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void activate ()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void draw_content (Cairo.Context cr)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void update_allocation (out float content_height, AllocationFlags flags);
|
||||
|
||||
public override void allocate (ActorBox box, AllocationFlags flags)
|
||||
{
|
||||
var label_x = MARGIN + PADDING + ICON_SIZE + SPACING;
|
||||
var label_width = WIDTH - label_x - MARGIN - SPACING;
|
||||
|
||||
float summary_height, body_height;
|
||||
summary_label.get_preferred_height (label_width, null, out summary_height);
|
||||
body_label.get_preferred_height (label_width, null, out body_height);
|
||||
|
||||
var label_height = summary_height + SPACING + body_height;
|
||||
var label_y = MARGIN + PADDING;
|
||||
// center
|
||||
if (label_height < ICON_SIZE)
|
||||
label_y += (ICON_SIZE - (int) label_height) / 2;
|
||||
|
||||
var icon_alloc = ActorBox ();
|
||||
icon_alloc.set_origin (MARGIN + PADDING, MARGIN + PADDING);
|
||||
icon_alloc.set_size (ICON_SIZE, ICON_SIZE);
|
||||
icon_texture.allocate (icon_alloc, flags);
|
||||
|
||||
var summary_alloc = ActorBox ();
|
||||
summary_alloc.set_origin (label_x, label_y);
|
||||
summary_alloc.set_size (label_width, summary_height);
|
||||
summary_label.allocate (summary_alloc, flags);
|
||||
|
||||
var body_alloc = ActorBox ();
|
||||
body_alloc.set_origin (label_x, label_y + summary_height + SPACING);
|
||||
body_alloc.set_size (label_width, body_height);
|
||||
body_label.allocate (body_alloc, flags);
|
||||
|
||||
var close_alloc = ActorBox ();
|
||||
close_alloc.set_origin (MARGIN + PADDING - close_button.width / 2,
|
||||
MARGIN + PADDING - close_button.height / 2);
|
||||
@ -369,7 +281,8 @@ namespace Gala.Plugins.Notify
|
||||
close_click.clicked.connect (close);
|
||||
close_button.add_action (close_click);
|
||||
|
||||
var content_height = label_height < ICON_SIZE ? ICON_SIZE : label_height;
|
||||
float content_height;
|
||||
update_allocation (out content_height, flags);
|
||||
box.set_size (MARGIN * 2 + WIDTH, (MARGIN + PADDING) * 2 + content_height);
|
||||
|
||||
base.allocate (box, flags);
|
||||
@ -383,17 +296,7 @@ namespace Gala.Plugins.Notify
|
||||
|
||||
public override void get_preferred_height (float for_width, out float min_height, out float nat_height)
|
||||
{
|
||||
var label_x = MARGIN + PADDING + ICON_SIZE + SPACING;
|
||||
var label_width = WIDTH - label_x - MARGIN - SPACING;
|
||||
|
||||
float summary_height, body_height;
|
||||
summary_label.get_preferred_height (label_width, null, out summary_height);
|
||||
body_label.get_preferred_height (label_width, null, out body_height);
|
||||
|
||||
var label_height = summary_height + SPACING + body_height;
|
||||
var content_height = label_height < ICON_SIZE ? ICON_SIZE : label_height;
|
||||
|
||||
min_height = nat_height = content_height + (MARGIN + SPACING) * 2;
|
||||
min_height = nat_height = ICON_SIZE + (MARGIN + PADDING) * 2;
|
||||
}
|
||||
|
||||
bool draw (Cairo.Context canvas_cr)
|
||||
@ -428,6 +331,9 @@ namespace Gala.Plugins.Notify
|
||||
cr.set_source (gradient);
|
||||
cr.stroke ();
|
||||
|
||||
// TODO move buffer out and optimize content drawing
|
||||
draw_content (cr);
|
||||
|
||||
canvas_cr.set_operator (Cairo.Operator.CLEAR);
|
||||
canvas_cr.paint ();
|
||||
canvas_cr.set_operator (Cairo.Operator.OVER);
|
||||
|
@ -35,37 +35,18 @@ namespace Gala.Plugins.Notify
|
||||
width = Notification.WIDTH + 2 * Notification.MARGIN;
|
||||
}
|
||||
|
||||
public void show_notification (uint32 id, string summary, string body, Gdk.Pixbuf? icon,
|
||||
NotificationUrgency urgency, int32 expire_timeout, uint32 sender_pid, string[] actions)
|
||||
public void show_notification (Notification notification)
|
||||
{
|
||||
if (animation_counter == 0)
|
||||
animations_changed (true);
|
||||
|
||||
foreach (var child in get_children ()) {
|
||||
var notification = (Notification) child;
|
||||
|
||||
if (notification.id == id && !notification.being_destroyed) {
|
||||
notification.update (summary, body, icon, expire_timeout, actions);
|
||||
|
||||
var transition = notification.get_transition ("update");
|
||||
if (transition != null) {
|
||||
animation_counter++;
|
||||
transition.completed.connect (() => {
|
||||
if (--animation_counter == 0)
|
||||
animations_changed (false);
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var notification = new Notification (screen, id, summary, body, icon,
|
||||
urgency, expire_timeout, sender_pid, actions);
|
||||
notification.destroy.connect (() => {
|
||||
update_positions ();
|
||||
});
|
||||
|
||||
float height;
|
||||
notification.get_preferred_height (Notification.WIDTH, out height, null);
|
||||
//update_positions (height);
|
||||
update_positions (height);
|
||||
|
||||
insert_child_at_index (notification, 0);
|
||||
|
||||
|
@ -42,18 +42,22 @@ namespace Gala.Plugins.Notify
|
||||
const string FALLBACK_ICON = "dialog-information";
|
||||
|
||||
[DBus (visible = false)]
|
||||
public signal void show_notification (uint32 id, string summary, string body, Gdk.Pixbuf? icon,
|
||||
NotificationUrgency urgency, int32 expire_timeout, uint32 sender_pid, string[] actions);
|
||||
public signal void show_notification (Notification notification);
|
||||
|
||||
[DBus (visible = false)]
|
||||
public signal void notification_closed (uint32 id);
|
||||
|
||||
[DBus (visible = false)]
|
||||
public NotificationStack stack { get; construct; }
|
||||
|
||||
uint32 id_counter = 0;
|
||||
|
||||
DBus? bus_proxy = null;
|
||||
|
||||
public NotifyServer ()
|
||||
public NotifyServer (NotificationStack stack)
|
||||
{
|
||||
Object (stack: stack);
|
||||
|
||||
try {
|
||||
bus_proxy = Bus.get_proxy_sync (BusType.SESSION, "org.freedesktop.DBus", "/");
|
||||
} catch (Error e) {
|
||||
@ -69,7 +73,12 @@ namespace Gala.Plugins.Notify
|
||||
|
||||
public string [] get_capabilities ()
|
||||
{
|
||||
return { "body", "body-markup" };
|
||||
return {
|
||||
"body",
|
||||
"body-markup",
|
||||
"x-canonical-private-synchronous",
|
||||
"x-canonical-private-icon-only"
|
||||
};
|
||||
}
|
||||
|
||||
public void get_server_information (out string name, out string vendor,
|
||||
@ -90,14 +99,54 @@ namespace Gala.Plugins.Notify
|
||||
var urgency = hints.contains ("urgency") ?
|
||||
(NotificationUrgency) hints.lookup ("urgency").get_byte () : NotificationUrgency.NORMAL;
|
||||
|
||||
var icon_only = hints.contains ("x-canonical-private-icon-only");
|
||||
var confirmation = hints.contains ("x-canonical-private-synchronous");
|
||||
var progress = confirmation && hints.contains ("value");
|
||||
|
||||
#if true //debug notifications
|
||||
print ("Notification from '%s', replaces: %u\n" +
|
||||
"\tapp icon: '%s'\n\tsummary: '%s'\n\tbody: '%s'\n\tn actions: %u\n\texpire: %i\n\tHints:\n",
|
||||
app_name, replaces_id, app_icon, summary, body, actions.length);
|
||||
hints.@foreach ((key, val) => {
|
||||
print ("\t\t%s => %s\n", key, val.is_of_type (VariantType.STRING) ?
|
||||
val.get_string () : "<" + val.get_type ().dup_string () + ">");
|
||||
});
|
||||
#endif
|
||||
|
||||
uint32 pid = 0;
|
||||
try {
|
||||
pid = bus_proxy.get_connection_unix_process_id (sender);
|
||||
} catch (Error e) { warning (e.message); }
|
||||
|
||||
show_notification (id, summary, body, pixbuf, urgency, timeout, pid, actions);
|
||||
foreach (var child in stack.get_children ()) {
|
||||
unowned Notification notification = (Notification) child;
|
||||
|
||||
return id_counter;
|
||||
if (notification.id == id && !notification.being_destroyed) {
|
||||
var normal_notification = notification as NormalNotification;
|
||||
var confirmation_notification = notification as ConfirmationNotification;
|
||||
|
||||
if (normal_notification != null)
|
||||
normal_notification.update (summary, body, pixbuf, expire_timeout, actions);
|
||||
|
||||
if (confirmation_notification != null)
|
||||
confirmation_notification.update (pixbuf,
|
||||
progress ? hints.@get ("value").get_int32 () : 0);
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
Notification notification;
|
||||
if (confirmation)
|
||||
notification = new ConfirmationNotification (id, pixbuf,
|
||||
progress ? hints.@get ("value").get_int32 () : -1);
|
||||
else
|
||||
notification = new NormalNotification (stack.screen, id, summary, body, pixbuf,
|
||||
urgency, timeout, pid, actions);
|
||||
|
||||
stack.show_notification (notification);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
Gdk.Pixbuf? get_pixbuf (HashTable<string, Variant> hints, string app, string icon)
|
||||
@ -138,20 +187,17 @@ namespace Gala.Plugins.Notify
|
||||
|
||||
} else if (icon != "") {
|
||||
|
||||
var actual_icon = icon;
|
||||
// fix icon names that are sent to notify-osd to the ones that actually exist
|
||||
if (icon.has_prefix ("notification-"))
|
||||
actual_icon = icon.substring (13) + "-symbolic";
|
||||
|
||||
try {
|
||||
pixbuf = Gtk.IconTheme.get_default ().load_icon (icon, size, 0);
|
||||
} catch (Error e) {}
|
||||
pixbuf = Gtk.IconTheme.get_default ().load_icon (actual_icon, size, 0);
|
||||
} catch (Error e) { warning (e.message); }
|
||||
|
||||
} else if (hints.contains ("icon_data")) {
|
||||
print ("IMPLEMENT ICON_DATA!!!!!!!!\n");
|
||||
|
||||
Gdk.Pixdata data = {};
|
||||
try {
|
||||
if (data.deserialize ((uint8[])hints.lookup ("icon_data").get_data ()))
|
||||
pixbuf = Gdk.Pixbuf.from_pixdata (data);
|
||||
else
|
||||
warning ("Error while deserializing icon_data");
|
||||
} catch (Error e) { warning (e.message); }
|
||||
warning ("icon data is not supported");
|
||||
}
|
||||
|
||||
if (pixbuf == null) {
|
||||
|
Loading…
Reference in New Issue
Block a user