Advertisement
Guest User

Untitled

a guest
Jul 19th, 2016
152
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.39 KB | None | 0 0
  1. const Applet = imports.ui.applet;
  2. const Lang = imports.lang;
  3. const Main = imports.ui.main;
  4. const Gtk = imports.gi.Gtk;
  5. const Gio = imports.gi.Gio;
  6. const PopupMenu = imports.ui.popupMenu;
  7. const St = imports.gi.St;
  8. const Clutter = imports.gi.Clutter;
  9. const Mainloop = imports.mainloop;
  10. const MessageTray = imports.ui.messageTray;
  11. const Urgency = imports.ui.messageTray.Urgency;
  12. const NotificationDestroyedReason = imports.ui.messageTray.NotificationDestroyedReason;
  13. const Settings = imports.ui.settings;
  14.  
  15. function MyApplet(metadata, orientation, panel_height, instanceId) {
  16. this._init(metadata, orientation, panel_height, instanceId);
  17. }
  18.  
  19. MyApplet.prototype = {
  20. __proto__: Applet.TextIconApplet.prototype,
  21.  
  22. _init: function(metadata, orientation, panel_height, instanceId) {
  23. Applet.TextIconApplet.prototype._init.call(this, orientation, panel_height, instanceId);
  24.  
  25. // Settings
  26. this.settings = new Settings.AppletSettings(this, metadata.uuid, instanceId);
  27. this.settings.bindProperty(Settings.BindingDirection.IN, "ignoreTransientNotifications", "ignoreTransientNotifications", null, null);
  28. this.settings.bindProperty(Settings.BindingDirection.IN, "showEmptyTray", "showEmptyTray", this._show_hide_tray, null);
  29.  
  30. // Layout
  31. this._orientation = orientation;
  32. this.menuManager = new PopupMenu.PopupMenuManager(this);
  33.  
  34. // Lists
  35. this.notifications = []; // The list of notifications, in order from oldest to newest.
  36.  
  37. // Events
  38. Main.messageTray.connect('notify-applet-update', Lang.bind(this, this._notification_added));
  39. global.settings.connect('changed::panel-edit-mode', Lang.bind(this, this.on_panel_edit_mode_changed));
  40.  
  41. // States
  42. this._blinking = false;
  43. this._blink_toggle = false;
  44. },
  45.  
  46. _display: function() {
  47. // Always start the applet empty, void of any notifications.
  48. this.set_applet_icon_symbolic_name("empty-notif");
  49. this.set_applet_tooltip(_("Notifications"));
  50.  
  51. // Setup the notification container.
  52. this._maincontainer = new St.BoxLayout({name: 'traycontainer', vertical: true});
  53. this._notificationbin = new St.BoxLayout({vertical:true});
  54. this.button_label_box = new St.BoxLayout();
  55.  
  56. // Setup the tray icon.
  57. this.menu_label = new PopupMenu.PopupMenuItem(stringify(this.notifications.length));
  58. this.menu_label.actor.reactive = false;
  59. this.menu_label.actor.can_focus = false;
  60. this.menu_label.label.add_style_class_name('popup-subtitle-menu-item');
  61.  
  62. this.clear_separator = new PopupMenu.PopupSeparatorMenuItem();
  63.  
  64. this.clear_action = new PopupMenu.PopupMenuItem(_("Clear notifications"));
  65. this.clear_action.connect('activate', Lang.bind(this, this._clear_all));
  66. this.clear_action.actor.hide();
  67.  
  68. if (this._orientation == St.Side.BOTTOM) {
  69. this.menu.addMenuItem(this.menu_label);
  70. this.menu.addActor(this._maincontainer);
  71. this.menu.addMenuItem(this.clear_separator);
  72. this.menu.addMenuItem(this.clear_action);
  73. } else {
  74. this.menu.addMenuItem(this.clear_action);
  75. this.menu.addMenuItem(this.clear_separator);
  76. this.menu.addMenuItem(this.menu_label);
  77. this.menu.addActor(this._maincontainer);
  78. }
  79.  
  80. this.scrollview = new St.ScrollView({ x_fill: true, y_fill: true, y_align: St.Align.START, style_class: "vfade"});
  81. this._maincontainer.add(this.scrollview);
  82. this.scrollview.add_actor(this._notificationbin);
  83. this.scrollview.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
  84.  
  85. let vscroll = this.scrollview.get_vscroll_bar();
  86. vscroll.connect('scroll-start', Lang.bind(this, function() {
  87. this.menu.passEvents = true;
  88. }));
  89. vscroll.connect('scroll-stop', Lang.bind(this, function() {
  90. this.menu.passEvents = false;
  91. }));
  92.  
  93. // Alternative tray icons.
  94. this._crit_icon = new St.Icon({icon_name: 'critical-notif', icon_type: St.IconType.SYMBOLIC, reactive: true, track_hover: true, style_class: 'system-status-icon' });
  95. this._alt_crit_icon = new St.Icon({icon_name: 'alt-critical-notif', icon_type: St.IconType.SYMBOLIC, reactive: true, track_hover: true, style_class: 'system-status-icon' });
  96.  
  97. this.update_list();
  98. },
  99.  
  100. _notification_added: function (mtray, notification) { // Notification event handler.
  101. // Ignore transient notifications?
  102. if (this.ignoreTransientNotifications && notification.isTransient) {
  103. return;
  104. }
  105.  
  106. if (notification.enter_id > 0) {
  107. notification.actor.disconnect(notification.enter_id);
  108. notification.enter_id = 0;
  109. }
  110. if (notification.leave_id > 0) {
  111. notification.actor.disconnect(notification.leave_id);
  112. notification.leave_id = 0;
  113. }
  114.  
  115. notification.actor.opacity = (notification._table.get_theme_node().get_length('opacity') / global.ui_scale) || 255;
  116.  
  117. notification.actor.unparent();
  118. let existing_index = this.notifications.indexOf(notification);
  119. if (existing_index != -1) { // This notification is already listed.
  120. if (notification._destroyed) {
  121. this.notifications.splice(existing_index, 1);
  122. } else {
  123. notification._inNotificationBin = true;
  124. global.reparentActor(notification.actor, this._notificationbin);
  125. notification.expand();
  126. notification._timeLabel.show();
  127. }
  128. this.update_list();
  129. return;
  130. } else if (notification._destroyed) {
  131. return;
  132. }
  133. // Add notification to list.
  134. notification._inNotificationBin = true;
  135. this.notifications.push(notification);
  136. // Steal the notication panel.
  137. notification.expand();
  138. this._notificationbin.add(notification.actor)
  139. notification.actor._parent_container = this._notificationbin;
  140. notification.actor.add_style_class_name('notification-applet-padding');
  141. // Register for destruction.
  142. notification.connect('clicked', Lang.bind(this, this._item_clicked, false));
  143. notification.connect('destroy', Lang.bind(this, this._item_clicked, true));
  144. notification._timeLabel.show();
  145.  
  146. this.update_list();
  147. },
  148.  
  149. _item_clicked: function(notification, destroyed) {
  150. let i = this.notifications.indexOf(notification);
  151. if (i != -1) {
  152. this.notifications.splice(i, 1);
  153. if (!destroyed) {
  154. notification.destroy(NotificationDestroyedReason.DISMISSED);
  155. }
  156. }
  157. this.update_list();
  158. },
  159.  
  160. update_list: function () {
  161. try {
  162. let count = this.notifications.length;
  163. if (count > 0) { // There are notifications.
  164. this.actor.show();
  165. this.clear_action.actor.show();
  166. this.set_applet_label(count.toString());
  167. // Find max urgency and derive list icon.
  168. let max_urgency = -1;
  169. for (let i = 0; i < count; i++) {
  170. let cur_urgency = this.notifications[i].urgency;
  171. if (cur_urgency > max_urgency)
  172. max_urgency = cur_urgency;
  173. }
  174. switch (max_urgency) {
  175. case Urgency.LOW:
  176. this._blinking = false;
  177. this.set_applet_icon_symbolic_name("low-notif");
  178. break;
  179. case Urgency.NORMAL:
  180. case Urgency.HIGH:
  181. this._blinking = false;
  182. this.set_applet_icon_symbolic_name("normal-notif");
  183. break;
  184. case Urgency.CRITICAL:
  185. if (!this._blinking) {
  186. this._blinking = true;
  187. this.critical_blink();
  188. }
  189. break;
  190. }
  191. } else { // There are no notifications.
  192. this._blinking = false;
  193. this.set_applet_label('');
  194. this.set_applet_icon_symbolic_name("empty-notif");
  195. this.clear_action.actor.hide();
  196. if (!this.showEmptyTray) {
  197. this.actor.hide();
  198. }
  199. }
  200. this.menu_label.label.set_text(stringify(count));
  201. this._notificationbin.queue_relayout();
  202. }
  203. catch (e) {
  204. global.logError(e);
  205. }
  206. },
  207.  
  208. _clear_all: function() {
  209. let count = this.notifications.length;
  210. if (count > 0) {
  211. for (let i = count-1; i >=0; i--) {
  212. this._notificationbin.remove_actor(this.notifications[i].actor);
  213. this.notifications[i].destroy(NotificationDestroyedReason.DISMISSED);
  214. }
  215. }
  216. this.notifications = [];
  217. this.update_list();
  218. },
  219.  
  220. _show_hide_tray: function() { // Show or hide the notification tray.
  221. if (this.notifications.length || this.showEmptyTray) {
  222. this.actor.show();
  223. } else {
  224. this.actor.hide();
  225. }
  226. },
  227.  
  228. on_panel_edit_mode_changed: function () {
  229. },
  230.  
  231. on_applet_added_to_panel: function() {
  232. this.on_orientation_changed(this._orientation);
  233. },
  234.  
  235. on_orientation_changed: function (orientation) {
  236. this._orientation = orientation;
  237. if (this.menu) {
  238. this.menu.destroy();
  239. }
  240. this.menu = new Applet.AppletPopupMenu(this, orientation);
  241. this.menuManager.addMenu(this.menu);
  242. this._display();
  243. },
  244.  
  245. on_applet_clicked: function(event) {
  246. this._update_timestamp();
  247. this.menu.toggle();
  248. },
  249.  
  250. _update_timestamp: function () {
  251. let dateFormat = _("%l:%M %p");
  252. let actors = this._notificationbin.get_children();
  253. if (actors) {
  254. for (let i = 0; i < actors.length; i++) {
  255. let notification = actors[i]._delegate;
  256. let orig_time = notification._timestamp;
  257. notification._timeLabel.clutter_text.set_markup(timeify(orig_time));
  258. }
  259. }
  260. },
  261.  
  262. critical_blink: function () {
  263. if (!this._blinking)
  264. return;
  265. if (this._blink_toggle) {
  266. this._applet_icon_box.child = this._crit_icon;
  267. } else {
  268. this._applet_icon_box.child = this._alt_crit_icon;
  269. }
  270. this._blink_toggle = !this._blink_toggle;
  271. Mainloop.timeout_add_seconds(1, Lang.bind(this, this.critical_blink));
  272. }
  273. };
  274.  
  275. function main(metadata, orientation, panel_height, instanceId) {
  276. let myApplet = new MyApplet(metadata, orientation, panel_height, instanceId);
  277. return myApplet;
  278. }
  279.  
  280. function stringify(count) {
  281. let str;
  282. switch (true) {
  283. case (count == 0):
  284. str = _("No notifications");
  285. break;
  286. case (count == 1):
  287. str = count.toString() + _(" notification");
  288. break;
  289. case (count > 1):
  290. str = count.toString() + _(" notifications");
  291. break;
  292. default:
  293. str = "";
  294. }
  295. return str;
  296. }
  297.  
  298. function timeify(orig_time) {
  299. let settings = new Gio.Settings({schema_id: 'org.cinnamon.desktop.interface'});
  300. let use_24h = settings.get_boolean('clock-use-24h');
  301. let now = new Date();
  302. let diff = Math.floor((now.getTime() - orig_time.getTime()) / 1000); // get diff in seconds
  303. let str;
  304. if (use_24h) {
  305. str = orig_time.toLocaleFormat('%T');
  306. } else {
  307. str = orig_time.toLocaleFormat('%r');
  308. }
  309. switch (true) {
  310. case (diff <= 15):
  311. str += _(" (Just now)");
  312. break;
  313. case (diff > 15 && diff <= 59):
  314. str += _(" (%s seconds ago)").format(diff.toString());
  315. break;
  316. case (diff > 59 && diff <= 119):
  317. str += _(" (%s minute ago)").format(Math.floor(diff / 60).toString());
  318. break;
  319. case (diff > 119 && diff <= 3540):
  320. str += _(" (%s minutes ago)").format(Math.floor(diff / 60).toString());
  321. break;
  322. }
  323. return str;
  324. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement