Compare commits
	
		
			24 Commits
		
	
	
		
			3.13.90
			...
			message-tr
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 8fc9d0c8ba | ||
|   | 803a204604 | ||
|   | a02e6d30f7 | ||
|   | a424bbbabf | ||
|   | 4ab513ca77 | ||
|   | 09653fbaf6 | ||
|   | b1791951cb | ||
|   | af1a3b11f5 | ||
|   | ef49ada575 | ||
|   | 6c3b8e2add | ||
|   | 3658f8a8b4 | ||
|   | 74418f2129 | ||
|   | 3b4e2202f7 | ||
|   | 3b5c468cbf | ||
|   | b0a0ee297c | ||
|   | b48b21e578 | ||
|   | e823a3b554 | ||
|   | e5b12619ef | ||
|   | 242c2bce04 | ||
|   | 64373fe77e | ||
|   | f106ee7182 | ||
|   | 56d2691c31 | ||
|   | f883e32f26 | ||
|   | c985c3cf78 | 
| @@ -472,6 +472,27 @@ StTooltip { | ||||
|     color: #cccccc; | ||||
| } | ||||
|  | ||||
| /* Message Tray */ | ||||
| #message-tray { | ||||
|     background-gradient-direction: vertical; | ||||
|     background-gradient-start: rgba(0,0,0,0.01); | ||||
|     background-gradient-end: rgba(0,0,0,0.95); | ||||
|     height: 28px; | ||||
| } | ||||
|  | ||||
| #notification { | ||||
|     border-radius: 5px; | ||||
|     background: rgba(0,0,0,0.9); | ||||
|     color: white; | ||||
|     padding: 2px 10px; | ||||
|     spacing: 10px; | ||||
| } | ||||
|  | ||||
| #summary-mode { | ||||
|     spacing: 10px; | ||||
|     padding: 2px 4px; | ||||
| } | ||||
|  | ||||
| /* App Switcher */ | ||||
| .switcher-list { | ||||
|     background: rgba(0,0,0,0.8); | ||||
|   | ||||
| @@ -16,6 +16,8 @@ dist_jsui_DATA =		\ | ||||
| 	link.js			\ | ||||
| 	lookingGlass.js		\ | ||||
| 	main.js			\ | ||||
| 	messageTray.js          \ | ||||
| 	notificationDaemon.js   \ | ||||
| 	overview.js		\ | ||||
| 	panel.js		\ | ||||
| 	placeDisplay.js		\ | ||||
|   | ||||
| @@ -15,11 +15,13 @@ const St = imports.gi.St; | ||||
| const Chrome = imports.ui.chrome; | ||||
| const Environment = imports.ui.environment; | ||||
| const ExtensionSystem = imports.ui.extensionSystem; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Overview = imports.ui.overview; | ||||
| const Panel = imports.ui.panel; | ||||
| const PlaceDisplay = imports.ui.placeDisplay; | ||||
| const RunDialog = imports.ui.runDialog; | ||||
| const LookingGlass = imports.ui.lookingGlass; | ||||
| const NotificationDaemon = imports.ui.notificationDaemon; | ||||
| const ShellDBus = imports.ui.shellDBus; | ||||
| const Sidebar = imports.ui.sidebar; | ||||
| const WindowManager = imports.ui.windowManager; | ||||
| @@ -35,6 +37,9 @@ let overview = null; | ||||
| let runDialog = null; | ||||
| let lookingGlass = null; | ||||
| let wm = null; | ||||
| let notificationDaemon = null; | ||||
| let notificationPopup = null; | ||||
| let messageTray = null; | ||||
| let recorder = null; | ||||
| let shellDBusService = null; | ||||
| let modalCount = 0; | ||||
| @@ -113,6 +118,9 @@ function start() { | ||||
|     panel = new Panel.Panel(); | ||||
|     sidebar = new Sidebar.Sidebar(); | ||||
|     wm = new WindowManager.WindowManager(); | ||||
|     notificationDaemon = new NotificationDaemon.NotificationDaemon(); | ||||
|     notificationPopup = new MessageTray.Notification(); | ||||
|     messageTray = new MessageTray.MessageTray(); | ||||
|  | ||||
|     _startDate = new Date(); | ||||
|  | ||||
|   | ||||
							
								
								
									
										271
									
								
								js/ui/messageTray.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								js/ui/messageTray.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,271 @@ | ||||
| /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
| const Signals = imports.signals; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| const ANIMATION_TIME = 0.2; | ||||
| const NOTIFICATION_TIMEOUT = 4; | ||||
|  | ||||
| const MESSAGE_TRAY_TIMEOUT = 0.2; | ||||
|  | ||||
| const ICON_SIZE = 24; | ||||
|  | ||||
| function Notification(icon, text) { | ||||
|     this._init(icon, text); | ||||
| } | ||||
|  | ||||
| Notification.prototype = { | ||||
|     _init: function(icon, text) { | ||||
|         this.icon = icon; | ||||
|         this.text = text; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function NotificationBox() { | ||||
|     this._init(); | ||||
| } | ||||
|  | ||||
| NotificationBox.prototype = { | ||||
|     _init: function() { | ||||
|         this.actor = new St.BoxLayout({ name: 'notification' }); | ||||
|  | ||||
|         this._iconBox = new St.Bin(); | ||||
|         this.actor.add(this._iconBox); | ||||
|  | ||||
|         this._textBox = new Shell.GenericContainer(); | ||||
|         this._textBox.connect('get-preferred-width', Lang.bind(this, this._textBoxGetPreferredWidth)); | ||||
|         this._textBox.connect('get-preferred-height', Lang.bind(this, this._textBoxGetPreferredHeight)); | ||||
|         this._textBox.connect('allocate', Lang.bind(this, this._textBoxAllocate)); | ||||
|         this.actor.add(this._textBox, { expand: true, x_fill: false, y_fill: false, y_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         this._text = new St.Label(); | ||||
|         this._text.clutter_text.line_wrap = true; | ||||
|         this._text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._textBox.add_actor(this._text); | ||||
|     }, | ||||
|  | ||||
|     _textBoxGetPreferredWidth: function (actor, forHeight, alloc) { | ||||
|         let [min, nat] = this._text.get_preferred_width(forHeight); | ||||
|  | ||||
|         alloc.min_size = alloc.nat_size = Math.min(nat, global.screen_width / 2); | ||||
|     }, | ||||
|  | ||||
|     _textBoxGetPreferredHeight: function (actor, forWidth, alloc) { | ||||
|         // St.BoxLayout passes -1 for @forWidth, which isn't what we want. | ||||
|         let prefWidth = {}; | ||||
|         this._textBoxGetPreferredWidth(this._textBox, -1, prefWidth); | ||||
|         [alloc.min_size, alloc.nat_size] = this._text.get_preferred_height(prefWidth.nat_size); | ||||
|         log('for width ' + prefWidth.nat_size + ', height ' + alloc.nat_size); | ||||
|     }, | ||||
|  | ||||
|     _textBoxAllocate: function (actor, box, flags) { | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|         childBox.x1 = childBox.y1 = 0; | ||||
|         childBox.x2 = box.x2 - box.x1; | ||||
|         childBox.y2 = box.y2 - box.y1; | ||||
|         this._text.allocate(childBox, flags); | ||||
|     }, | ||||
|  | ||||
|     setContent: function(notification) { | ||||
|         this._iconBox.child = notification.icon; | ||||
|  | ||||
|         // Support <b>, <i>, and <u>, escape anything else | ||||
|         // so it displays as raw markup. | ||||
|         let markup = notification.text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, "<$1"); | ||||
|         this._text.clutter_text.set_markup(markup); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| function Source(id, createIcon) { | ||||
|     this._init(id, createIcon); | ||||
| } | ||||
|  | ||||
| Source.prototype = { | ||||
|     _init: function(id, createIcon) { | ||||
|         this.id = id; | ||||
|         if (createIcon) | ||||
|             this.createIcon = createIcon; | ||||
|     }, | ||||
|  | ||||
|     // This can be overridden by a subclass, or by the createIcon | ||||
|     // parameter to _init() | ||||
|     createIcon: function(size) { | ||||
|         throw new Error('no implementation of createIcon in ' + this); | ||||
|     }, | ||||
|  | ||||
|     notify: function(text) { | ||||
|         Main.messageTray.showNotification(new Notification(this.createIcon(ICON_SIZE), text)); | ||||
|     }, | ||||
|  | ||||
|     clicked: function() { | ||||
|         this.emit('clicked'); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         this.emit('destroy'); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Source.prototype); | ||||
|  | ||||
| function MessageTray() { | ||||
|     this._init(); | ||||
| } | ||||
|  | ||||
| MessageTray.prototype = { | ||||
|     _init: function() { | ||||
|         this.actor = new St.BoxLayout({ name: 'message-tray', | ||||
|                                         reactive: true }); | ||||
|  | ||||
|         let primary = global.get_primary_monitor(); | ||||
|         this.actor.x = 0; | ||||
|         this.actor.y = primary.height - 1; | ||||
|  | ||||
|         this.actor.width = primary.width; | ||||
|  | ||||
|         this._summaryBin = new St.Bin({ x_align: St.Align.END }); | ||||
|         this.actor.add(this._summaryBin, { expand: true }); | ||||
|         this._summaryBin.hide(); | ||||
|  | ||||
|         this._notificationBox = new NotificationBox(); | ||||
|         this._notificationQueue = []; | ||||
|         this.actor.add(this._notificationBox.actor); | ||||
|         this._notificationBox.actor.hide(); | ||||
|  | ||||
|         Main.chrome.addActor(this.actor, { affectsStruts: false }); | ||||
|  | ||||
|         this.actor.connect('enter-event', | ||||
|                            Lang.bind(this, this._onMessageTrayEntered)); | ||||
|         this.actor.connect('leave-event', | ||||
|                            Lang.bind(this, this._onMessageTrayLeft)); | ||||
|         this._isShowing = false; | ||||
|         this.actor.show(); | ||||
|  | ||||
|         this._summary = new St.BoxLayout({ name: 'summary-mode' }); | ||||
|         this._summaryBin.child = this._summary; | ||||
|  | ||||
|         this._sources = {}; | ||||
|         this._icons = {}; | ||||
|     }, | ||||
|  | ||||
|     contains: function(source) { | ||||
|         return this._sources.hasOwnProperty(source.id); | ||||
|     }, | ||||
|  | ||||
|     add: function(source) { | ||||
|         if (this.contains(source)) { | ||||
|             log('Trying to re-add source ' + source.id); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let iconBox = new St.Bin({ reactive: true }); | ||||
|         iconBox.child = source.createIcon(ICON_SIZE); | ||||
|         this._summary.insert_actor(iconBox, 0); | ||||
|         this._icons[source.id] = iconBox; | ||||
|         this._sources[source.id] = source; | ||||
|  | ||||
|         iconBox.connect('button-release-event', Lang.bind(this, | ||||
|             function () { | ||||
|                 source.clicked(); | ||||
|             })); | ||||
|  | ||||
|         source.connect('destroy', Lang.bind(this, | ||||
|             function () { | ||||
|                 this.remove(source); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     remove: function(source) { | ||||
|         if (!this.contains(source)) | ||||
|             return; | ||||
|  | ||||
|         this._summary.remove_actor(this._icons[source.id]); | ||||
|         delete this._icons[source.id]; | ||||
|         delete this._sources[source.id]; | ||||
|     }, | ||||
|  | ||||
|     getSource: function(id) { | ||||
|         return this._sources[id]; | ||||
|     }, | ||||
|  | ||||
|     _onMessageTrayEntered: function() { | ||||
|         // Don't hide the message tray after a timeout if the user has moved the mouse over it. | ||||
|         // We might have a timeout in place if the user moved the mouse away from the message tray for a very short period of time | ||||
|         // or if we are showing a notification. | ||||
|         if (this._hideTimeoutId > 0) | ||||
|             Mainloop.source_remove(this._hideTimeoutId); | ||||
|  | ||||
|         if (this._isShowing) | ||||
|             return; | ||||
|  | ||||
|         // If the message tray was not already showing, we'll show it in the summary mode. | ||||
|         this._summaryBin.show(); | ||||
|         this._show(); | ||||
|     }, | ||||
|  | ||||
|     _onMessageTrayLeft: function() { | ||||
|         if (!this._isShowing) | ||||
|             return; | ||||
|  | ||||
|         // We wait just a little before hiding the message tray in case the user will quickly move the mouse back over it. | ||||
|         this._hideTimeoutId = Mainloop.timeout_add(MESSAGE_TRAY_TIMEOUT * 1000, Lang.bind(this, this._hide)); | ||||
|     }, | ||||
|  | ||||
|     _show: function() { | ||||
|         this._isShowing = true; | ||||
|         let primary = global.get_primary_monitor(); | ||||
|         Tweener.addTween(this.actor, | ||||
|                          { y: primary.height - this.actor.height, | ||||
|                            time: ANIMATION_TIME, | ||||
|                            transition: "easeOutQuad" | ||||
|                          }); | ||||
|     }, | ||||
|  | ||||
|     _hide: function() { | ||||
|         this._hideTimeoutId = 0; | ||||
|  | ||||
|         let primary = global.get_primary_monitor(); | ||||
|  | ||||
|         Tweener.addTween(this.actor, | ||||
|                          { y: primary.height - 1, | ||||
|                            time: ANIMATION_TIME, | ||||
|                            transition: "easeOutQuad", | ||||
|                            onComplete: this._hideComplete, | ||||
|                            onCompleteScope: this | ||||
|                          }); | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _hideComplete: function() { | ||||
|         this._isShowing = false; | ||||
|         this._summaryBin.hide(); | ||||
|         this._notificationBox.actor.hide(); | ||||
|         if (this._notificationQueue.length > 0) | ||||
|             this.showNotification(this._notificationQueue.shift()); | ||||
|     }, | ||||
|  | ||||
|     showNotification: function(notification) { | ||||
|         if (this._isShowing) { | ||||
|             this._notificationQueue.push(notification); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._notificationBox.setContent(notification); | ||||
|  | ||||
|         this._notificationBox.actor.x = Math.round((this.actor.width - this._notificationBox.actor.width) / 2); | ||||
|         this._notificationBox.actor.show(); | ||||
|  | ||||
|         // Because we set up the timeout before we do the animation, we add ANIMATION_TIME to NOTIFICATION_TIMEOUT, so that | ||||
|         // NOTIFICATION_TIMEOUT represents the time the notifiation is fully shown. | ||||
|         this._hideTimeoutId = Mainloop.timeout_add((NOTIFICATION_TIMEOUT + ANIMATION_TIME) * 1000, Lang.bind(this, this._hide)); | ||||
|  | ||||
|         this._show(); | ||||
|      } | ||||
| }; | ||||
							
								
								
									
										218
									
								
								js/ui/notificationDaemon.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								js/ui/notificationDaemon.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | ||||
| /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ | ||||
|  | ||||
| const DBus = imports.dbus; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Mainloop = imports.mainloop; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| let nextNotificationId = 1; | ||||
|  | ||||
| const NotificationDaemonIface = { | ||||
|     name: 'org.freedesktop.Notifications', | ||||
|     methods: [{ name: 'Notify', | ||||
|                 inSignature: 'susssasa{sv}i', | ||||
|                 outSignature: 'u' | ||||
|               }, | ||||
|               { name: 'CloseNotification', | ||||
|                 inSignature: 'u', | ||||
|                 outSignature: '' | ||||
|               }, | ||||
|               { name: 'GetCapabilities', | ||||
|                 inSignature: '', | ||||
|                 outSignature: 'as' | ||||
|               }, | ||||
|               { name: 'GetServerInformation', | ||||
|                 inSignature: '', | ||||
|                 outSignature: 'ssss' | ||||
|               }], | ||||
|     signals: [{ name: 'NotificationClosed', | ||||
|                 inSignature: 'uu' }, | ||||
|               { name: 'ActionInvoked', | ||||
|                 inSignature: 'us' }] | ||||
| }; | ||||
|  | ||||
| const NotificationClosedReason = { | ||||
|     EXPIRED: 1, | ||||
|     DISMISSED: 2, | ||||
|     APP_CLOSED: 3, | ||||
|     UNDEFINED: 4 | ||||
| }; | ||||
|  | ||||
| const Urgency = { | ||||
|     LOW: 0, | ||||
|     NORMAL: 1, | ||||
|     CRITICAL: 2 | ||||
| }; | ||||
|  | ||||
| function NotificationDaemon() { | ||||
|     this._init(); | ||||
| } | ||||
|  | ||||
| NotificationDaemon.prototype = { | ||||
|     _init: function() { | ||||
|         DBus.session.exportObject('/org/freedesktop/Notifications', this); | ||||
|  | ||||
|         this._everAcquiredName = false; | ||||
|         DBus.session.acquire_name('org.freedesktop.Notifications', | ||||
|                                   // We pass MANY_INSTANCES so that if | ||||
|                                   // notification-daemon is running, we'll | ||||
|                                   // get queued behind it and then get the | ||||
|                                   // name after killing it below | ||||
|                                   DBus.MANY_INSTANCES, | ||||
|                                   Lang.bind(this, this._acquiredName), | ||||
|                                   Lang.bind(this, this._lostName)); | ||||
|     }, | ||||
|  | ||||
|     _acquiredName: function() { | ||||
|         this._everAcquiredName = true; | ||||
|     }, | ||||
|  | ||||
|     _lostName: function() { | ||||
|         if (this._everAcquiredName) | ||||
|             log('Lost name org.freedesktop.Notifications!'); | ||||
|         else if (GLib.getenv('GNOME_SHELL_NO_REPLACE')) | ||||
|             log('Failed to acquire org.freedesktop.Notifications'); | ||||
|         else { | ||||
|             log('Failed to acquire org.freedesktop.Notifications; trying again'); | ||||
|  | ||||
|             // kill the notification-daemon. pkill is more portable | ||||
|             // than killall, but on Linux at least it won't match if | ||||
|             // you pass more than 15 characters of the process name... | ||||
|             // However, if you use the "-f" flag to match the entire | ||||
|             // command line, it will work, but we have to be careful | ||||
|             // in that case that we don't match "gedit | ||||
|             // notification-daemon.c" or whatever... | ||||
|             let p = new Shell.Process({ args: ['pkill', '-f', | ||||
|                                                '^([^ ]*/)?(notification-daemon|notify-osd)$']}); | ||||
|             p.run(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _sourceId: function(id) { | ||||
|         return 'notification-' + id; | ||||
|     }, | ||||
|  | ||||
|     Notify: function(appName, replacesId, icon, summary, body, | ||||
|                      actions, hints, timeout) { | ||||
|         let id, source = null; | ||||
|  | ||||
|         if (replacesId != 0) { | ||||
|             id = replacesId; | ||||
|             source = Main.messageTray.getSource(this._sourceId(id)); | ||||
|             // source may be null if the current source was destroyed | ||||
|             // right as the client sent the new notification | ||||
|         } | ||||
|  | ||||
|         if (source == null) { | ||||
|             id = nextNotificationId++; | ||||
|  | ||||
|             source = new Source(this._sourceId(id), icon, hints); | ||||
|             Main.messageTray.add(source); | ||||
|  | ||||
|             source.connect('clicked', Lang.bind(this, | ||||
|                 function() { | ||||
|                     source.destroy(); | ||||
|                     this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED); | ||||
|                 })); | ||||
|         } | ||||
|  | ||||
|         summary = GLib.markup_escape_text(summary, -1); | ||||
|         if (body) | ||||
|             source.notify('<b>' + summary + '</b>: ' + body); | ||||
|         else | ||||
|             source.notify('<b>' + summary + '</b>'); | ||||
|         return id; | ||||
|     }, | ||||
|  | ||||
|     CloseNotification: function(id) { | ||||
|         let source = Main.messageTray.getSource(this._sourceId(id)); | ||||
|         if (source) | ||||
|             source.destroy(); | ||||
|         this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED); | ||||
|     }, | ||||
|  | ||||
|     GetCapabilities: function() { | ||||
|         return [ | ||||
|             // 'actions', | ||||
|             'body', | ||||
|             // 'body-hyperlinks', | ||||
|             // 'body-images', | ||||
|             'body-markup', | ||||
|             // 'icon-multi', | ||||
|             'icon-static' | ||||
|             // 'sound', | ||||
|         ]; | ||||
|     }, | ||||
|  | ||||
|     GetServerInformation: function() { | ||||
|         return [ | ||||
|             'GNOME Shell', | ||||
|             'GNOME', | ||||
|             '0.1', // FIXME, get this from somewhere | ||||
|             '1.0' | ||||
|         ]; | ||||
|     }, | ||||
|  | ||||
|     _emitNotificationClosed: function(id, reason) { | ||||
|         DBus.session.emit_signal('/org/freedesktop/Notifications', | ||||
|                                  'org.freedesktop.Notifications', | ||||
|                                  'NotificationClosed', 'uu', | ||||
|                                  [id, reason]); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface); | ||||
|  | ||||
| function Source(sourceId, icon, hints) { | ||||
|     this._init(sourceId, icon, hints); | ||||
| } | ||||
|  | ||||
| Source.prototype = { | ||||
|     __proto__:  MessageTray.Source.prototype, | ||||
|  | ||||
|     _init: function(sourceId, icon, hints) { | ||||
|         MessageTray.Source.prototype._init.call(this, sourceId); | ||||
|  | ||||
|         hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true); | ||||
|  | ||||
|         this._icon = icon; | ||||
|         this._iconData = hints.icon_data; | ||||
|         this._urgency = hints.urgency; | ||||
|     }, | ||||
|  | ||||
|     createIcon: function(size) { | ||||
|         let textureCache = Shell.TextureCache.get_default(); | ||||
|  | ||||
|         if (this._icon) { | ||||
|             if (this._icon.substr(0, 7) == 'file://') | ||||
|                 return textureCache.load_uri_async(this._icon, size, size); | ||||
|             else if (this._icon[0] == '/') { | ||||
|                 let uri = GLib.filename_to_uri(this._icon, null); | ||||
|                 return textureCache.load_uri_async(uri, size, size); | ||||
|             } else | ||||
|                 return textureCache.load_icon_name(this._icon, size); | ||||
|         } else if (this._iconData) { | ||||
|             let [width, height, rowStride, hasAlpha, | ||||
|                  bitsPerSample, nChannels, data] = this._iconData; | ||||
|             return textureCache.load_from_raw(data, data.length, hasAlpha, | ||||
|                                               width, height, rowStride, size); | ||||
|         } else { | ||||
|             let stockIcon; | ||||
|             switch (this._urgency) { | ||||
|                 case Urgency.LOW: | ||||
|                 case Urgency.NORMAL: | ||||
|                     stockIcon = 'gtk-dialog-info'; | ||||
|                     break; | ||||
|                 case Urgency.CRITICAL: | ||||
|                     stockIcon = 'gtk-dialog-error'; | ||||
|                     break; | ||||
|             } | ||||
|             return textureCache.load_icon_name(stockIcon, size); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										5
									
								
								src/gnome-shell.in
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										5
									
								
								src/gnome-shell.in
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -357,8 +357,9 @@ try: | ||||
|     shell = None | ||||
|     if options.xephyr: | ||||
|         xephyr = start_xephyr() | ||||
|         # This makes us not grab the org.gnome.Panel name | ||||
|         os.environ['GNOME_SHELL_NO_REPLACE_PANEL'] = '1' | ||||
|         # This makes us not grab the org.gnome.Panel or | ||||
|         # org.freedesktop.Notifications D-Bus names | ||||
|         os.environ['GNOME_SHELL_NO_REPLACE'] = '1' | ||||
|         shell = start_shell() | ||||
|     else: | ||||
|         xephyr = None | ||||
|   | ||||
| @@ -751,7 +751,7 @@ shell_global_grab_dbus_service (ShellGlobal *global) | ||||
|    * unless a special environment variable is passed.  The environment | ||||
|    * variable is used by the gnome-shell (no --replace) launcher in | ||||
|    * Xephyr */ | ||||
|   if (!g_getenv ("GNOME_SHELL_NO_REPLACE_PANEL")) | ||||
|   if (!g_getenv ("GNOME_SHELL_NO_REPLACE")) | ||||
|     { | ||||
|       if (!dbus_g_proxy_call (bus, "RequestName", &error, G_TYPE_STRING, | ||||
|                               "org.gnome.Panel", G_TYPE_UINT, | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #define GNOME_DESKTOP_USE_UNSTABLE_API | ||||
| #include <libgnomeui/gnome-desktop-thumbnail.h> | ||||
| #include <string.h> | ||||
| #include <glib.h> | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
| @@ -17,6 +18,7 @@ typedef struct | ||||
|   GIcon *icon; | ||||
|   gchar *uri; | ||||
|   gchar *thumbnail_uri; | ||||
|   gchar *checksum; | ||||
|  | ||||
|   /* This one is common to all */ | ||||
|   guint size; | ||||
| @@ -51,6 +53,8 @@ cache_key_hash (gconstpointer a) | ||||
|     base_hash = g_str_hash (akey->uri); | ||||
|   else if (akey->thumbnail_uri) | ||||
|     base_hash = g_str_hash (akey->thumbnail_uri); | ||||
|   else if (akey->checksum) | ||||
|     base_hash = g_str_hash (akey->checksum); | ||||
|   else | ||||
|     g_assert_not_reached (); | ||||
|   return base_hash + 31*akey->size; | ||||
| @@ -76,6 +80,8 @@ cache_key_equal (gconstpointer a, | ||||
|     return strcmp (akey->uri, bkey->uri) == 0; | ||||
|   else if (akey->thumbnail_uri && bkey->thumbnail_uri) | ||||
|     return strcmp (akey->thumbnail_uri, bkey->thumbnail_uri) == 0; | ||||
|   else if (akey->checksum && bkey->checksum) | ||||
|     return strcmp (akey->checksum, bkey->checksum) == 0; | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
| @@ -89,6 +95,7 @@ cache_key_dup (CacheKey *key) | ||||
|     ret->icon = g_object_ref (key->icon); | ||||
|   ret->uri = g_strdup (key->uri); | ||||
|   ret->thumbnail_uri = g_strdup (key->thumbnail_uri); | ||||
|   ret->checksum = g_strdup (key->checksum); | ||||
|   ret->size = key->size; | ||||
|   return ret; | ||||
| } | ||||
| @@ -101,6 +108,7 @@ cache_key_destroy (gpointer a) | ||||
|     g_object_unref (akey->icon); | ||||
|   g_free (akey->uri); | ||||
|   g_free (akey->thumbnail_uri); | ||||
|   g_free (akey->checksum); | ||||
|   g_free (akey); | ||||
| } | ||||
|  | ||||
| @@ -318,7 +326,8 @@ on_image_size_prepared (GdkPixbufLoader *pixbuf_loader, | ||||
| } | ||||
|  | ||||
| static GdkPixbuf * | ||||
| impl_load_pixbuf_file (const char     *uri, | ||||
| impl_load_pixbuf_data (const guchar   *data, | ||||
|                        gsize           size, | ||||
|                        int             available_width, | ||||
|                        int             available_height, | ||||
|                        GError        **error) | ||||
| @@ -326,22 +335,10 @@ impl_load_pixbuf_file (const char     *uri, | ||||
|   GdkPixbufLoader *pixbuf_loader = NULL; | ||||
|   GdkPixbuf *rotated_pixbuf = NULL; | ||||
|   GdkPixbuf *pixbuf; | ||||
|   GFile *file = NULL; | ||||
|   char *contents = NULL; | ||||
|   gsize size; | ||||
|   gboolean success; | ||||
|   Dimensions available_dimensions; | ||||
|   int width_before_rotation, width_after_rotation; | ||||
|  | ||||
|   file = g_file_new_for_uri (uri); | ||||
|  | ||||
|   success = g_file_load_contents (file, NULL, &contents, &size, NULL, error); | ||||
|  | ||||
|   if (!success) | ||||
|     { | ||||
|       goto out; | ||||
|     } | ||||
|  | ||||
|   pixbuf_loader = gdk_pixbuf_loader_new (); | ||||
|  | ||||
|   available_dimensions.width = available_width; | ||||
| @@ -349,10 +346,7 @@ impl_load_pixbuf_file (const char     *uri, | ||||
|   g_signal_connect (pixbuf_loader, "size-prepared", | ||||
|                     G_CALLBACK (on_image_size_prepared), &available_dimensions); | ||||
|  | ||||
|   success = gdk_pixbuf_loader_write (pixbuf_loader, | ||||
|                                      (const guchar *) contents, | ||||
|                                      size, | ||||
|                                      error); | ||||
|   success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error); | ||||
|   if (!success) | ||||
|     goto out; | ||||
|   success = gdk_pixbuf_loader_close (pixbuf_loader, error); | ||||
| @@ -384,10 +378,7 @@ impl_load_pixbuf_file (const char     *uri, | ||||
|       g_signal_connect (pixbuf_loader, "size-prepared", | ||||
|                         G_CALLBACK (on_image_size_prepared), &available_dimensions); | ||||
|  | ||||
|       success = gdk_pixbuf_loader_write (pixbuf_loader, | ||||
|                                          (const guchar *) contents, | ||||
|                                          size, | ||||
|                                          error); | ||||
|       success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error); | ||||
|       if (!success) | ||||
|         goto out; | ||||
|  | ||||
| @@ -401,14 +392,36 @@ impl_load_pixbuf_file (const char     *uri, | ||||
|     } | ||||
|  | ||||
| out: | ||||
|   g_free (contents); | ||||
|   if (file) | ||||
|     g_object_unref (file); | ||||
|   if (pixbuf_loader) | ||||
|     g_object_unref (pixbuf_loader); | ||||
|   return rotated_pixbuf; | ||||
| } | ||||
|  | ||||
| static GdkPixbuf * | ||||
| impl_load_pixbuf_file (const char     *uri, | ||||
|                        int             available_width, | ||||
|                        int             available_height, | ||||
|                        GError        **error) | ||||
| { | ||||
|   GdkPixbuf *pixbuf = NULL; | ||||
|   GFile *file; | ||||
|   char *contents = NULL; | ||||
|   gsize size; | ||||
|  | ||||
|   file = g_file_new_for_uri (uri); | ||||
|   if (g_file_load_contents (file, NULL, &contents, &size, NULL, error)) | ||||
|     { | ||||
|       pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size, | ||||
|                                       available_width, available_height, | ||||
|                                       error); | ||||
|     } | ||||
|  | ||||
|   g_object_unref (file); | ||||
|   g_free (contents); | ||||
|  | ||||
|   return pixbuf; | ||||
| } | ||||
|  | ||||
| static GdkPixbuf * | ||||
| impl_load_thumbnail (ShellTextureCache *cache, | ||||
|                      const char        *uri, | ||||
| @@ -655,6 +668,7 @@ typedef struct { | ||||
|   gboolean thumbnail; | ||||
|   char *mimetype; | ||||
|   GtkRecentInfo *recent_info; | ||||
|   char *checksum; | ||||
|   GIcon *icon; | ||||
|   GtkIconInfo *icon_info; | ||||
|   guint width; | ||||
| @@ -1137,6 +1151,128 @@ shell_texture_cache_load_uri_sync (ShellTextureCache *cache, | ||||
|   return CLUTTER_ACTOR (texture); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * shell_texture_cache_load_from_data: | ||||
|  * @cache: The texture cache instance | ||||
|  * @data: Image data in PNG, GIF, etc format | ||||
|  * @len: length of @data | ||||
|  * @size: Size in pixels to use for the resulting texture | ||||
|  * @error: Return location for error | ||||
|  * | ||||
|  * Synchronously creates an image from @data. The image is scaled down | ||||
|  * to fit the available width and height dimensions, but the image is | ||||
|  * never scaled up beyond its actual size. The pixbuf is rotated | ||||
|  * according to the associated orientation setting. | ||||
|  * | ||||
|  * Return value: (transfer none): A new #ClutterActor with the image data loaded if it was | ||||
|  *               generated succesfully, %NULL otherwise | ||||
|  */ | ||||
| ClutterActor * | ||||
| shell_texture_cache_load_from_data (ShellTextureCache *cache, | ||||
|                                     const guchar      *data, | ||||
|                                     gsize              len, | ||||
|                                     int                size, | ||||
|                                     GError           **error) | ||||
| { | ||||
|   ClutterTexture *texture; | ||||
|   CoglHandle texdata; | ||||
|   GdkPixbuf *pixbuf; | ||||
|   CacheKey key; | ||||
|   gchar *checksum; | ||||
|  | ||||
|   texture = create_default_texture (cache); | ||||
|   clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); | ||||
|  | ||||
|   checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len); | ||||
|  | ||||
|   memset (&key, 0, sizeof(key)); | ||||
|   key.size = size; | ||||
|   key.checksum = checksum; | ||||
|  | ||||
|   texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key); | ||||
|   if (texdata == NULL) | ||||
|     { | ||||
|       pixbuf = impl_load_pixbuf_data (data, len, size, size, error); | ||||
|       if (!pixbuf) | ||||
|         { | ||||
|           g_object_unref (texture); | ||||
|           return NULL; | ||||
|         } | ||||
|  | ||||
|       texdata = pixbuf_to_cogl_handle (pixbuf); | ||||
|       g_object_unref (pixbuf); | ||||
|  | ||||
|       set_texture_cogl_texture (texture, texdata); | ||||
|  | ||||
|       g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata); | ||||
|     } | ||||
|  | ||||
|   g_free (key.checksum); | ||||
|  | ||||
|   set_texture_cogl_texture (texture, texdata); | ||||
|   return CLUTTER_ACTOR (texture); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * shell_texture_cache_load_from_raw: | ||||
|  * @cache: a #ShellTextureCache | ||||
|  * @data: raw pixel data | ||||
|  * @len: the length of @data | ||||
|  * @has_alpha: whether @data includes an alpha channel | ||||
|  * @width: width in pixels of @data | ||||
|  * @height: width in pixels of @data | ||||
|  * @rowstride: rowstride of @data | ||||
|  * @size: size of icon to return | ||||
|  * | ||||
|  * Creates (or retrieves from cache) an icon based on raw pixel data. | ||||
|  * | ||||
|  * Return value: (transfer none): a new #ClutterActor displaying a | ||||
|  * pixbuf created from @data and the other parameters. | ||||
|  **/ | ||||
| ClutterActor * | ||||
| shell_texture_cache_load_from_raw (ShellTextureCache *cache, | ||||
|                                    const guchar      *data, | ||||
|                                    gsize              len, | ||||
|                                    gboolean           has_alpha, | ||||
|                                    int                width, | ||||
|                                    int                height, | ||||
|                                    int                rowstride, | ||||
|                                    int                size, | ||||
|                                    GError           **error) | ||||
| { | ||||
|   ClutterTexture *texture; | ||||
|   CoglHandle texdata; | ||||
|   CacheKey key; | ||||
|   gchar *checksum; | ||||
|  | ||||
|   texture = create_default_texture (cache); | ||||
|   clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); | ||||
|  | ||||
|   /* In theory, two images of different size could have the same | ||||
|    * pixel data. We ignore that theory. | ||||
|    */ | ||||
|   checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len); | ||||
|  | ||||
|   memset (&key, 0, sizeof(key)); | ||||
|   key.size = size; | ||||
|   key.checksum = checksum; | ||||
|  | ||||
|   texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key); | ||||
|   if (texdata == NULL) | ||||
|     { | ||||
|       texdata = cogl_texture_new_from_data (width, height, COGL_TEXTURE_NONE, | ||||
|                                             has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, | ||||
|                                             COGL_PIXEL_FORMAT_ANY, | ||||
|                                             rowstride, data); | ||||
|       g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata); | ||||
|     } | ||||
|  | ||||
|   g_free (key.checksum); | ||||
|  | ||||
|   set_texture_cogl_texture (texture, texdata); | ||||
|   return CLUTTER_ACTOR (texture); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * shell_texture_cache_load_thumbnail: | ||||
|  * @cache: | ||||
|   | ||||
| @@ -80,6 +80,21 @@ ClutterActor *shell_texture_cache_load_uri_sync (ShellTextureCache *cache, | ||||
|                                                  int                available_height, | ||||
|                                                  GError           **error); | ||||
|  | ||||
| ClutterActor *shell_texture_cache_load_from_data (ShellTextureCache *cache, | ||||
|                                                   const guchar      *data, | ||||
|                                                   gsize              len, | ||||
|                                                   int                size, | ||||
|                                                   GError           **error); | ||||
| ClutterActor *shell_texture_cache_load_from_raw  (ShellTextureCache *cache, | ||||
|                                                   const guchar      *data, | ||||
|                                                   gsize              len, | ||||
|                                                   gboolean           has_alpha, | ||||
|                                                   int                width, | ||||
|                                                   int                height, | ||||
|                                                   int                rowstride, | ||||
|                                                   int                size, | ||||
|                                                   GError           **error); | ||||
|  | ||||
| gboolean shell_texture_cache_pixbuf_equal (ShellTextureCache *cache, GdkPixbuf *a, GdkPixbuf *b); | ||||
|  | ||||
| #endif /* __SHELL_TEXTURE_CACHE_H__ */ | ||||
|   | ||||
| @@ -1418,3 +1418,36 @@ st_box_layout_get_n_children  (StBoxLayout *self) | ||||
| { | ||||
|   return g_list_length (self->priv->children); | ||||
| } | ||||
|  | ||||
| void | ||||
| st_box_layout_move_child (StBoxLayout  *self, | ||||
|                           ClutterActor *actor, | ||||
|                           int           pos) | ||||
| { | ||||
|   StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (self)->priv; | ||||
|  | ||||
|   GList *item = NULL; | ||||
|  | ||||
|   item = g_list_find (priv->children, actor); | ||||
|  | ||||
|   if (item == NULL) | ||||
|     { | ||||
|       g_warning ("Actor of type '%s' is not a child of the StBoxLayout container", | ||||
|                  g_type_name (G_OBJECT_TYPE (actor))); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   priv->children = g_list_delete_link (priv->children, item); | ||||
|   priv->children = g_list_insert (priv->children, actor, pos); | ||||
|   clutter_actor_queue_relayout ((ClutterActor*) self); | ||||
| } | ||||
|  | ||||
| void | ||||
| st_box_layout_insert_actor (StBoxLayout  *self, | ||||
|                             ClutterActor *actor, | ||||
|                             int           pos) | ||||
| { | ||||
|   clutter_container_add_actor((ClutterContainer*) self, actor); | ||||
|   st_box_layout_move_child(self, actor, pos); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -95,6 +95,14 @@ void     st_box_layout_destroy_children (StBoxLayout *box); | ||||
|  | ||||
| guint    st_box_layout_get_n_children  (StBoxLayout *box); | ||||
|  | ||||
| void     st_box_layout_move_child (StBoxLayout  *self, | ||||
|                                    ClutterActor *actor, | ||||
|                                    int           pos); | ||||
|  | ||||
| void     st_box_layout_insert_actor (StBoxLayout  *self, | ||||
|                                      ClutterActor *actor, | ||||
|                                      int           pos); | ||||
|  | ||||
| G_END_DECLS | ||||
|  | ||||
| #endif /* _ST_BOX_LAYOUT_H */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user