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