Bug 628949 - Update visible region / glass regions after we paint. r=roc a=2.0.
[mozilla-central.git] / toolkit / system / gnome / nsAlertsIconListener.cpp
blob2793ead6b520654b31629cd2967e04e8d80acb15
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2008
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsAlertsIconListener.h"
39 #include "imgIContainer.h"
40 #include "imgILoader.h"
41 #include "imgIRequest.h"
42 #include "nsNetUtil.h"
43 #include "nsIImageToPixbuf.h"
44 #include "nsIStringBundle.h"
45 #include "nsIObserverService.h"
47 #include <gdk/gdk.h>
49 // Compatibility macro for <libnotify-0.7
50 #ifndef NOTIFY_CHECK_VERSION
51 #define NOTIFY_CHECK_VERSION(x,y,z) 0
52 #endif
54 static PRBool gHasActions = PR_FALSE;
56 static void notify_action_cb(NotifyNotification *notification,
57 gchar *action, gpointer user_data)
59 nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data);
60 alert->SendCallback();
63 static void notify_closed_marshal(GClosure* closure,
64 GValue* return_value,
65 guint n_param_values,
66 const GValue* param_values,
67 gpointer invocation_hint,
68 gpointer marshal_data)
70 NS_ABORT_IF_FALSE(n_param_values >= 1, "No object in params");
72 nsAlertsIconListener* alert =
73 static_cast<nsAlertsIconListener*>(closure->data);
74 alert->SendClosed();
75 NS_RELEASE(alert);
78 NS_IMPL_ISUPPORTS3(nsAlertsIconListener, imgIContainerObserver, imgIDecoderObserver, nsIObserver)
80 nsAlertsIconListener::nsAlertsIconListener()
81 : mLoadedFrame(PR_FALSE),
82 mHasQuit(PR_FALSE),
83 mNotification(NULL)
85 MOZ_COUNT_CTOR(nsAlertsIconListener);
87 nsCOMPtr<nsIObserverService> obsServ =
88 do_GetService("@mozilla.org/observer-service;1");
89 obsServ->AddObserver(this, "quit-application", PR_FALSE);
92 nsAlertsIconListener::~nsAlertsIconListener()
94 MOZ_COUNT_DTOR(nsAlertsIconListener);
96 if (mIconRequest)
97 mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
99 if (!mHasQuit) {
100 nsCOMPtr<nsIObserverService> obsServ =
101 do_GetService("@mozilla.org/observer-service;1");
102 obsServ->RemoveObserver(this, "quit-application");
106 NS_IMETHODIMP
107 nsAlertsIconListener::OnStartRequest(imgIRequest* aRequest)
109 return NS_OK;
112 NS_IMETHODIMP
113 nsAlertsIconListener::OnStartDecode(imgIRequest* aRequest)
115 return NS_OK;
119 NS_IMETHODIMP
120 nsAlertsIconListener::OnStartContainer(imgIRequest* aRequest,
121 imgIContainer* aContainer)
123 return NS_OK;
127 NS_IMETHODIMP
128 nsAlertsIconListener::OnStartFrame(imgIRequest* aRequest,
129 PRUint32 aFrame)
131 return NS_OK;
135 NS_IMETHODIMP
136 nsAlertsIconListener::OnDataAvailable(imgIRequest* aRequest,
137 PRBool aCurrentFrame,
138 const nsIntRect* aRect)
140 return NS_OK;
143 NS_IMETHODIMP
144 nsAlertsIconListener::OnStopContainer(imgIRequest* aRequest,
145 imgIContainer* aContainer)
147 return NS_OK;
151 NS_IMETHODIMP
152 nsAlertsIconListener::OnStopDecode(imgIRequest* aRequest,
153 nsresult status,
154 const PRUnichar* statusArg)
156 return NS_OK;
159 NS_IMETHODIMP
160 nsAlertsIconListener::FrameChanged(imgIContainer* aContainer,
161 const nsIntRect* aDirtyRect)
163 return NS_OK;
166 NS_IMETHODIMP
167 nsAlertsIconListener::OnStopRequest(imgIRequest* aRequest,
168 PRBool aIsLastPart)
170 PRUint32 imgStatus = imgIRequest::STATUS_ERROR;
171 nsresult rv = aRequest->GetImageStatus(&imgStatus);
172 NS_ENSURE_SUCCESS(rv, rv);
173 if (imgStatus == imgIRequest::STATUS_ERROR && !mLoadedFrame) {
174 // We have an error getting the image. Display the notification with no icon.
175 ShowAlert(NULL);
178 if (mIconRequest) {
179 mIconRequest->Cancel(NS_BINDING_ABORTED);
180 mIconRequest = nsnull;
182 return NS_OK;
185 NS_IMETHODIMP
186 nsAlertsIconListener::OnDiscard(imgIRequest *aRequest)
188 return NS_OK;
191 NS_IMETHODIMP
192 nsAlertsIconListener::OnStopFrame(imgIRequest* aRequest,
193 PRUint32 aFrame)
195 if (aRequest != mIconRequest)
196 return NS_ERROR_FAILURE;
198 if (mLoadedFrame)
199 return NS_OK; // only use one frame
201 nsCOMPtr<imgIContainer> image;
202 nsresult rv = aRequest->GetImage(getter_AddRefs(image));
203 if (NS_FAILED(rv))
204 return rv;
206 nsCOMPtr<nsIImageToPixbuf> imgToPixbuf =
207 do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1");
209 GdkPixbuf* imagePixbuf = imgToPixbuf->ConvertImageToPixbuf(image);
210 if (!imagePixbuf)
211 return NS_ERROR_FAILURE;
213 ShowAlert(imagePixbuf);
215 g_object_unref(imagePixbuf);
217 mLoadedFrame = PR_TRUE;
218 return NS_OK;
221 nsresult
222 nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
224 mNotification = notify_notification_new(mAlertTitle.get(),
225 mAlertText.get(),
226 NULL
227 // >=libnotify-0.7.0 has no support for attaching to widgets
228 #if !NOTIFY_CHECK_VERSION(0,7,0)
229 , NULL
230 #endif
233 if (!mNotification)
234 return NS_ERROR_OUT_OF_MEMORY;
236 if (aPixbuf)
237 notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);
239 NS_ADDREF(this);
240 if (mAlertHasAction) {
241 // What we put as the label doesn't matter here, if the action
242 // string is "default" then that makes the entire bubble clickable
243 // rather than creating a button.
244 notify_notification_add_action(mNotification, "default", "Activate",
245 notify_action_cb, this, NULL);
248 // Fedora 10 calls NotifyNotification "closed" signal handlers with a
249 // different signature, so a marshaller is used instead of a C callback to
250 // get the user_data (this) in a parseable format. |closure| is created
251 // with a floating reference, which gets sunk by g_signal_connect_closure().
252 GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
253 g_closure_set_marshal(closure, notify_closed_marshal);
254 mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE);
255 gboolean result = notify_notification_show(mNotification, NULL);
257 return result ? NS_OK : NS_ERROR_FAILURE;
260 nsresult
261 nsAlertsIconListener::StartRequest(const nsAString & aImageUrl)
263 if (mIconRequest) {
264 // Another icon request is already in flight. Kill it.
265 mIconRequest->Cancel(NS_BINDING_ABORTED);
266 mIconRequest = nsnull;
269 nsCOMPtr<nsIURI> imageUri;
270 NS_NewURI(getter_AddRefs(imageUri), aImageUrl);
271 if (!imageUri)
272 return ShowAlert(NULL);
274 nsCOMPtr<imgILoader> il(do_GetService("@mozilla.org/image/loader;1"));
275 if (!il)
276 return ShowAlert(NULL);
278 return il->LoadImage(imageUri, nsnull, nsnull, nsnull, this,
279 nsnull, nsIRequest::LOAD_NORMAL, nsnull, nsnull,
280 nsnull, getter_AddRefs(mIconRequest));
283 void
284 nsAlertsIconListener::SendCallback()
286 if (mAlertListener)
287 mAlertListener->Observe(NULL, "alertclickcallback", mAlertCookie.get());
290 void
291 nsAlertsIconListener::SendClosed()
293 if (mNotification) {
294 g_object_unref(mNotification);
295 mNotification = NULL;
297 if (mAlertListener)
298 mAlertListener->Observe(NULL, "alertfinished", mAlertCookie.get());
301 NS_IMETHODIMP
302 nsAlertsIconListener::Observe(nsISupports *aSubject, const char *aTopic,
303 const PRUnichar *aData) {
304 // We need to close any open notifications upon application exit, otherwise
305 // we will leak since libnotify holds a ref for us.
306 if (!nsCRT::strcmp(aTopic, "quit-application") && mNotification) {
307 g_signal_handler_disconnect(mNotification, mClosureHandler);
308 g_object_unref(mNotification);
309 mNotification = NULL;
310 mHasQuit = PR_TRUE;
311 Release(); // equivalent to NS_RELEASE(this)
313 return NS_OK;
316 nsresult
317 nsAlertsIconListener::InitAlertAsync(const nsAString & aImageUrl,
318 const nsAString & aAlertTitle,
319 const nsAString & aAlertText,
320 PRBool aAlertTextClickable,
321 const nsAString & aAlertCookie,
322 nsIObserver * aAlertListener)
324 if (!notify_is_initted()) {
325 // Give the name of this application to libnotify
326 nsCOMPtr<nsIStringBundleService> bundleService =
327 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
329 nsCAutoString appShortName;
330 if (bundleService) {
331 nsCOMPtr<nsIStringBundle> bundle;
332 bundleService->CreateBundle("chrome://branding/locale/brand.properties",
333 getter_AddRefs(bundle));
334 nsAutoString appName;
336 if (bundle) {
337 bundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
338 getter_Copies(appName));
339 appShortName = NS_ConvertUTF16toUTF8(appName);
340 } else {
341 NS_WARNING("brand.properties not present, using default application name");
342 appShortName.AssignLiteral("Mozilla");
344 } else {
345 appShortName.AssignLiteral("Mozilla");
348 if (!notify_init(appShortName.get()))
349 return NS_ERROR_FAILURE;
351 GList *server_caps = notify_get_server_caps();
352 if (server_caps) {
353 for (GList* cap = server_caps; cap != NULL; cap = cap->next) {
354 if (!strcmp((char*) cap->data, "actions")) {
355 gHasActions = PR_TRUE;
356 break;
359 g_list_foreach(server_caps, (GFunc)g_free, NULL);
360 g_list_free(server_caps);
364 if (!gHasActions && aAlertTextClickable)
365 return NS_ERROR_FAILURE; // No good, fallback to XUL
367 // Workaround for a libnotify bug - blank titles aren't dealt with
368 // properly so we use a space
369 if (aAlertTitle.IsEmpty()) {
370 mAlertTitle = NS_LITERAL_CSTRING(" ");
371 } else {
372 mAlertTitle = NS_ConvertUTF16toUTF8(aAlertTitle);
375 mAlertText = NS_ConvertUTF16toUTF8(aAlertText);
376 mAlertHasAction = aAlertTextClickable;
378 mAlertListener = aAlertListener;
379 mAlertCookie = aAlertCookie;
381 return StartRequest(aImageUrl);