1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23 #ifdef ENABLE_QUICKSTART_APPLET
24
25 #include <unotools/moduleoptions.hxx>
26 #include <unotools/dynamicmenuoptions.hxx>
27
28 #include <gtk/gtk.h>
29 #include <glib.h>
30 #include <vos/mutex.hxx>
31 #include <vcl/bitmapex.hxx>
32 #include <vcl/bmpacc.hxx>
33 #include <sfx2/app.hxx>
34 #ifndef _SFX_APP_HRC
35 #include "app.hrc"
36 #endif
37 #ifndef __SHUTDOWNICON_HXX__
38 #define USE_APP_SHORTCUTS
39 #include "shutdownicon.hxx"
40 #endif
41
42 // Cut/paste from vcl/inc/svids.hrc
43 #define SV_ICON_LARGE_START 24000
44 #define SV_ICON_SMALL_START 25000
45
46 #define SV_ICON_ID_OFFICE 1
47 #define SV_ICON_ID_TEXT 2
48 #define SV_ICON_ID_SPREADSHEET 4
49 #define SV_ICON_ID_DRAWING 6
50 #define SV_ICON_ID_PRESENTATION 8
51 #define SV_ICON_ID_DATABASE 14
52 #define SV_ICON_ID_FORMULA 15
53 #define SV_ICON_ID_TEMPLATE 16
54
55 using namespace ::rtl;
56 using namespace ::osl;
57
58 static ResMgr *pVCLResMgr;
59 static GtkStatusIcon *pTrayIcon;
60 static GtkWidget *pExitMenuItem = NULL;
61 static GtkWidget *pOpenMenuItem = NULL;
62 static GtkWidget *pDisableMenuItem = NULL;
63
open_url_cb(GtkWidget *,gpointer data)64 static void open_url_cb( GtkWidget *, gpointer data )
65 {
66 ShutdownIcon::OpenURL( *(OUString *)data,
67 OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
68 }
69
open_file_cb(GtkWidget *)70 static void open_file_cb( GtkWidget * )
71 {
72 if ( !ShutdownIcon::bModalMode )
73 ShutdownIcon::FileOpen();
74 }
75
open_template_cb(GtkWidget *)76 static void open_template_cb( GtkWidget * )
77 {
78 if ( !ShutdownIcon::bModalMode )
79 ShutdownIcon::FromTemplate();
80 }
81
systray_disable_cb()82 static void systray_disable_cb()
83 {
84 ShutdownIcon::SetAutostart( false );
85 ShutdownIcon::terminateDesktop();
86 }
87
exit_quickstarter_cb(GtkWidget *)88 static void exit_quickstarter_cb( GtkWidget * )
89 {
90 plugin_shutdown_sys_tray();
91 ShutdownIcon::getInstance()->terminateDesktop();
92 }
93
menu_deactivate_cb(GtkWidget * pMenu)94 static void menu_deactivate_cb( GtkWidget *pMenu )
95 {
96 gtk_menu_popdown( GTK_MENU( pMenu ) );
97 }
98
ResIdToPixbuf(sal_uInt16 nResId)99 static GdkPixbuf * ResIdToPixbuf( sal_uInt16 nResId )
100 {
101 ResId aResId( nResId, *pVCLResMgr );
102 BitmapEx aIcon( aResId );
103 Bitmap pInSalBitmap = aIcon.GetBitmap();
104 AlphaMask pInSalAlpha = aIcon.GetAlpha();
105
106 BitmapReadAccess* pSalBitmap = pInSalBitmap.AcquireReadAccess();
107 BitmapReadAccess* pSalAlpha = pInSalAlpha.AcquireReadAccess();
108
109 g_return_val_if_fail( pSalBitmap != NULL, NULL );
110
111 Size aSize( pSalBitmap->Width(), pSalBitmap->Height() );
112 g_return_val_if_fail( Size( pSalAlpha->Width(), pSalAlpha->Height() ) == aSize, NULL );
113
114 int nX, nY;
115 guchar *pPixbufData = ( guchar * )g_malloc( 4 * aSize.Width() * aSize.Height() );
116 guchar *pDestData = pPixbufData;
117
118 for( nY = 0; nY < pSalBitmap->Height(); nY++ )
119 {
120 for( nX = 0; nX < pSalBitmap->Width(); nX++ )
121 {
122 BitmapColor aPix;
123 aPix = pSalBitmap->GetPixel( nY, nX );
124 pDestData[0] = aPix.GetRed();
125 pDestData[1] = aPix.GetGreen();
126 pDestData[2] = aPix.GetBlue();
127 if (pSalAlpha)
128 {
129 aPix = pSalAlpha->GetPixel( nY, nX );
130 pDestData[3] = 255 - aPix.GetIndex();
131 }
132 else
133 pDestData[3] = 255;
134 pDestData += 4;
135 }
136 }
137
138 pInSalBitmap.ReleaseAccess( pSalBitmap );
139 if( pSalAlpha )
140 pInSalAlpha.ReleaseAccess( pSalAlpha );
141
142 return gdk_pixbuf_new_from_data( pPixbufData,
143 GDK_COLORSPACE_RGB, sal_True, 8,
144 aSize.Width(), aSize.Height(),
145 aSize.Width() * 4,
146 (GdkPixbufDestroyNotify) g_free,
147 NULL );
148 }
149
150 extern "C" {
oustring_delete(gpointer data,GClosure *)151 static void oustring_delete (gpointer data,
152 GClosure * /* closure */)
153 {
154 OUString *pURL = (OUString *) data;
155 delete pURL;
156 }
157 }
158
add_item(GtkMenuShell * pMenuShell,const char * pAsciiURL,OUString * pOverrideLabel,sal_uInt16 nResId,GCallback pFnCallback)159 static void add_item( GtkMenuShell *pMenuShell, const char *pAsciiURL,
160 OUString *pOverrideLabel,
161 sal_uInt16 nResId, GCallback pFnCallback )
162 {
163 OUString *pURL = new OUString (OStringToOUString( pAsciiURL,
164 RTL_TEXTENCODING_UTF8 ));
165 OString aLabel;
166 if (pOverrideLabel)
167 aLabel = OUStringToOString (*pOverrideLabel, RTL_TEXTENCODING_UTF8);
168 else
169 {
170 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
171 aLabel = OUStringToOString (pShutdownIcon->GetUrlDescription( *pURL ),
172 RTL_TEXTENCODING_UTF8);
173 }
174
175 GdkPixbuf *pPixbuf= ResIdToPixbuf( SV_ICON_SMALL_START + nResId );
176 GtkWidget *pImage = gtk_image_new_from_pixbuf( pPixbuf );
177 g_object_unref( G_OBJECT( pPixbuf ) );
178
179 GtkWidget* pMenuItem = gtk_image_menu_item_new_with_label( aLabel.getStr() );
180 gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage );
181 g_signal_connect_data( pMenuItem, "activate", pFnCallback, pURL,
182 oustring_delete, GConnectFlags(0));
183
184 gtk_menu_shell_append( pMenuShell, pMenuItem );
185 }
186
187 // Unbelievably nasty
188 using namespace ::com::sun::star::uno;
189 using namespace ::com::sun::star::task;
190 using namespace ::com::sun::star::lang;
191 using namespace ::com::sun::star::beans;
192
add_ugly_db_item(GtkMenuShell * pMenuShell,const char * pAsciiURL,sal_uInt16 nResId,GCallback pFnCallback)193 static void add_ugly_db_item( GtkMenuShell *pMenuShell, const char *pAsciiURL,
194 sal_uInt16 nResId, GCallback pFnCallback )
195 {
196 SvtDynamicMenuOptions aOpt;
197 Sequence < Sequence < PropertyValue > > aMenu = aOpt.GetMenu( E_NEWMENU );
198 for ( sal_Int32 n=0; n<aMenu.getLength(); n++ )
199 {
200 ::rtl::OUString aURL;
201 ::rtl::OUString aDescription;
202 Sequence < PropertyValue >& aEntry = aMenu[n];
203 for ( sal_Int32 m=0; m<aEntry.getLength(); m++ )
204 {
205 if ( aEntry[m].Name.equalsAsciiL( "URL", 3 ) )
206 aEntry[m].Value >>= aURL;
207 if ( aEntry[m].Name.equalsAsciiL( "Title", 5 ) )
208 aEntry[m].Value >>= aDescription;
209 }
210
211 if ( aURL.equalsAscii( BASE_URL ) && aDescription.getLength() )
212 {
213 add_item (pMenuShell, pAsciiURL, &aDescription, nResId, pFnCallback);
214 break;
215 }
216 }
217 }
218
219 static GtkWidget *
add_image_menu_item(GtkMenuShell * pMenuShell,const gchar * stock_id,rtl::OUString aLabel,GCallback activate_cb)220 add_image_menu_item( GtkMenuShell *pMenuShell,
221 const gchar *stock_id,
222 rtl::OUString aLabel,
223 GCallback activate_cb )
224 {
225 OString aUtfLabel = rtl::OUStringToOString (aLabel, RTL_TEXTENCODING_UTF8 );
226
227 GtkWidget* pImage = gtk_image_new_from_stock( stock_id, GTK_ICON_SIZE_MENU );
228
229 GtkWidget* pMenuItem = gtk_image_menu_item_new_with_label( aUtfLabel.getStr() );
230 gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage );
231
232 gtk_menu_shell_append( pMenuShell, pMenuItem );
233 g_signal_connect( pMenuItem, "activate", activate_cb, NULL);
234
235 return pMenuItem;
236 }
237
populate_menu(GtkWidget * pMenu)238 static void populate_menu( GtkWidget *pMenu )
239 {
240 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
241 GtkMenuShell *pMenuShell = GTK_MENU_SHELL( pMenu );
242 SvtModuleOptions aModuleOptions;
243
244 if ( aModuleOptions.IsWriter() )
245 add_item (pMenuShell, WRITER_URL, NULL,
246 SV_ICON_ID_TEXT, G_CALLBACK( open_url_cb ));
247
248 if ( aModuleOptions.IsCalc() )
249 add_item (pMenuShell, CALC_URL, NULL,
250 SV_ICON_ID_SPREADSHEET, G_CALLBACK( open_url_cb ));
251
252 if ( aModuleOptions.IsImpress() )
253 add_item (pMenuShell, IMPRESS_URL, NULL,
254 SV_ICON_ID_PRESENTATION, G_CALLBACK( open_url_cb ));
255
256 if ( aModuleOptions.IsDraw() )
257 add_item (pMenuShell, DRAW_URL, NULL,
258 SV_ICON_ID_DRAWING, G_CALLBACK( open_url_cb ));
259
260 if ( aModuleOptions.IsDataBase() )
261 add_ugly_db_item (pMenuShell, BASE_URL,
262 SV_ICON_ID_DATABASE, G_CALLBACK( open_url_cb ));
263
264 if ( aModuleOptions.IsMath() )
265 add_item (pMenuShell, MATH_URL, NULL,
266 SV_ICON_ID_FORMULA, G_CALLBACK( open_url_cb ));
267
268 OUString aULabel = pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE );
269 add_item (pMenuShell, "dummy", &aULabel,
270 SV_ICON_ID_TEMPLATE, G_CALLBACK( open_template_cb ));
271
272 OString aLabel;
273 GtkWidget *pMenuItem;
274
275 pMenuItem = gtk_separator_menu_item_new();
276 gtk_menu_shell_append( pMenuShell, pMenuItem );
277
278 pOpenMenuItem = add_image_menu_item
279 (pMenuShell, GTK_STOCK_OPEN,
280 pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN ),
281 G_CALLBACK( open_file_cb ));
282
283
284 pMenuItem = gtk_separator_menu_item_new();
285 gtk_menu_shell_append( pMenuShell, pMenuItem );
286
287 pDisableMenuItem = add_image_menu_item
288 ( pMenuShell, GTK_STOCK_CLOSE,
289 pShutdownIcon->GetResString( STR_QUICKSTART_PRELAUNCH_UNX ),
290 G_CALLBACK( systray_disable_cb ) );
291
292 pMenuItem = gtk_separator_menu_item_new();
293 gtk_menu_shell_append( pMenuShell, pMenuItem );
294
295 pExitMenuItem = add_image_menu_item
296 ( pMenuShell, GTK_STOCK_QUIT,
297 pShutdownIcon->GetResString( STR_QUICKSTART_EXIT ),
298 G_CALLBACK( exit_quickstarter_cb ) );
299
300 gtk_widget_show_all( pMenu );
301 }
302
refresh_menu(GtkWidget * pMenu)303 static void refresh_menu( GtkWidget *pMenu )
304 {
305 if (!pExitMenuItem)
306 populate_menu( pMenu );
307
308 bool bModal = ShutdownIcon::bModalMode;
309 gtk_widget_set_sensitive( pExitMenuItem, !bModal);
310 gtk_widget_set_sensitive( pOpenMenuItem, !bModal);
311 gtk_widget_set_sensitive( pDisableMenuItem, !bModal);
312 }
313
activate_cb(GtkStatusIcon * status_icon,gpointer pMenu)314 static void activate_cb( GtkStatusIcon *status_icon,
315 gpointer pMenu )
316 {
317 refresh_menu( GTK_WIDGET( pMenu ) );
318
319 gtk_menu_popup( GTK_MENU( pMenu ), NULL, NULL,
320 gtk_status_icon_position_menu,
321 status_icon, 0, gtk_get_current_event_time() );
322 }
323
popup_menu_cb(GtkStatusIcon * status_icon,guint button,guint activate_time,gpointer pMenu)324 static void popup_menu_cb(GtkStatusIcon *status_icon,
325 guint button,
326 guint activate_time,
327 gpointer pMenu)
328 {
329 if (button == 2)
330 return;
331
332 refresh_menu( GTK_WIDGET( pMenu ) );
333
334 gtk_menu_popup( GTK_MENU( pMenu ), NULL, NULL,
335 gtk_status_icon_position_menu,
336 status_icon, button, activate_time );
337 }
338
plugin_init_sys_tray()339 void SAL_DLLPUBLIC_EXPORT plugin_init_sys_tray()
340 {
341 ::vos::OGuard aGuard( Application::GetSolarMutex() );
342
343 if( !g_type_from_name( "GdkDisplay" ) )
344 return;
345
346 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
347 if ( !pShutdownIcon )
348 return;
349
350 pTrayIcon = gtk_status_icon_new();
351 pVCLResMgr = CREATEVERSIONRESMGR( vcl );
352
353 if ( !pTrayIcon || !pVCLResMgr )
354 return;
355
356 // disable shutdown
357 pShutdownIcon->SetVeto( true );
358 pShutdownIcon->addTerminateListener();
359
360 OString aLabel;
361
362 aLabel = rtl::OUStringToOString (
363 pShutdownIcon->GetResString( STR_QUICKSTART_TIP ),
364 RTL_TEXTENCODING_UTF8 );
365
366 GdkPixbuf *pPixbuf = ResIdToPixbuf( SV_ICON_LARGE_START + SV_ICON_ID_OFFICE );
367 g_object_set( G_OBJECT( pTrayIcon ),
368 "pixbuf", pPixbuf,
369 "title", aLabel.getStr(),/* Since 2.18 */
370 "tooltip-text", aLabel.getStr(), /* Since 2.16 */
371 NULL );
372 g_object_unref( pPixbuf );
373
374 // gtk_status_icon_set_tooltip_text is available since 2.16
375 // so use instead deprecated gtk_status_icon_set_tooltip
376 gtk_status_icon_set_tooltip( pTrayIcon, aLabel.getStr() );
377
378 GtkWidget *pMenu = gtk_menu_new();
379
380 // Signal "button-press-event" is available since 2.14
381 // Use "activate" and "popup-menu" instead
382 g_signal_connect( pTrayIcon, "activate",
383 G_CALLBACK( activate_cb ), pMenu );
384 g_signal_connect( pTrayIcon, "popup-menu",
385 G_CALLBACK( popup_menu_cb ), pMenu );
386
387 g_signal_connect( pMenu, "deactivate",
388 G_CALLBACK (menu_deactivate_cb), NULL);
389 }
390
plugin_shutdown_sys_tray()391 void SAL_DLLPUBLIC_EXPORT plugin_shutdown_sys_tray()
392 {
393 ::vos::OGuard aGuard( Application::GetSolarMutex() );
394 if( !pTrayIcon )
395 return;
396 g_object_unref( pTrayIcon );
397 pTrayIcon = NULL;
398 pExitMenuItem = NULL;
399 pOpenMenuItem = NULL;
400 pDisableMenuItem = NULL;
401 }
402
403 #endif // ENABLE_QUICKSTART_APPLET
404