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 
64 static void open_url_cb( GtkWidget *, gpointer data )
65 {
66 	ShutdownIcon::OpenURL( *(OUString *)data,
67 						   OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
68 }
69 
70 static void open_file_cb( GtkWidget * )
71 {
72 	if ( !ShutdownIcon::bModalMode )
73 		ShutdownIcon::FileOpen();
74 }
75 
76 static void open_template_cb( GtkWidget * )
77 {
78 	if ( !ShutdownIcon::bModalMode )
79 		ShutdownIcon::FromTemplate();
80 }
81 
82 static void systray_disable_cb()
83 {
84 	ShutdownIcon::SetAutostart( false );
85 	ShutdownIcon::terminateDesktop();
86 }
87 
88 static void exit_quickstarter_cb( GtkWidget * )
89 {
90 	ShutdownIcon::getInstance()->terminateDesktop();
91 	plugin_shutdown_sys_tray();
92 }
93 
94 static void menu_deactivate_cb( GtkWidget *pMenu )
95 {
96 	gtk_menu_popdown( GTK_MENU( pMenu ) );
97 }
98 
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" {
151 static void oustring_delete (gpointer  data,
152 							 GClosure * /* closure */)
153 {
154 	OUString *pURL = (OUString *) data;
155 	delete pURL;
156 }
157 }
158 
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 );
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 
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 *
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;
228 	pImage = gtk_image_new_from_stock( stock_id, GTK_ICON_SIZE_MENU );
229 
230 	GtkWidget *pMenuItem;
231 	pMenuItem = gtk_image_menu_item_new_with_label( aUtfLabel );
232 	gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage );
233 
234 	gtk_menu_shell_append( pMenuShell, pMenuItem );
235 	g_signal_connect( pMenuItem, "activate", activate_cb, NULL);
236 
237 	return pMenuItem;
238 }
239 
240 static void populate_menu( GtkWidget *pMenu )
241 {
242 	ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
243 	GtkMenuShell *pMenuShell = GTK_MENU_SHELL( pMenu );
244 	SvtModuleOptions aModuleOptions;
245 
246 	if ( aModuleOptions.IsWriter() )
247 		add_item (pMenuShell, WRITER_URL, NULL,
248 				  SV_ICON_ID_TEXT, G_CALLBACK( open_url_cb ));
249 
250 	if ( aModuleOptions.IsCalc() )
251 		add_item (pMenuShell, CALC_URL, NULL,
252 				  SV_ICON_ID_SPREADSHEET, G_CALLBACK( open_url_cb ));
253 
254 	if ( aModuleOptions.IsImpress() )
255 		add_item (pMenuShell, IMPRESS_URL, NULL,
256 				  SV_ICON_ID_PRESENTATION, G_CALLBACK( open_url_cb ));
257 
258 	if ( aModuleOptions.IsDraw() )
259 		add_item (pMenuShell, DRAW_URL, NULL,
260 				  SV_ICON_ID_DRAWING, G_CALLBACK( open_url_cb ));
261 
262     if ( aModuleOptions.IsDataBase() )
263 		add_ugly_db_item (pMenuShell, BASE_URL,
264 						  SV_ICON_ID_DATABASE, G_CALLBACK( open_url_cb ));
265 
266 	if ( aModuleOptions.IsMath() )
267 		add_item (pMenuShell, MATH_URL, NULL,
268 				  SV_ICON_ID_FORMULA, G_CALLBACK( open_url_cb ));
269 
270 	OUString aULabel = pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE );
271 	add_item (pMenuShell, "dummy", &aULabel,
272 			  SV_ICON_ID_TEMPLATE, G_CALLBACK( open_template_cb ));
273 
274 	OString aLabel;
275 	GtkWidget *pMenuItem;
276 
277 	pMenuItem = gtk_separator_menu_item_new();
278 	gtk_menu_shell_append( pMenuShell, pMenuItem );
279 
280 	pOpenMenuItem = add_image_menu_item
281         (pMenuShell, GTK_STOCK_OPEN,
282 		 pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN ),
283 		 G_CALLBACK( open_file_cb ));
284 
285 
286 	pMenuItem = gtk_separator_menu_item_new();
287 	gtk_menu_shell_append( pMenuShell, pMenuItem );
288 
289 	pDisableMenuItem = add_image_menu_item
290         ( pMenuShell, GTK_STOCK_CLOSE,
291 		  pShutdownIcon->GetResString( STR_QUICKSTART_PRELAUNCH_UNX ),
292 		  G_CALLBACK( systray_disable_cb ) );
293 
294 	pMenuItem = gtk_separator_menu_item_new();
295 	gtk_menu_shell_append( pMenuShell, pMenuItem );
296 
297 	pExitMenuItem = add_image_menu_item
298         ( pMenuShell, GTK_STOCK_QUIT,
299 		  pShutdownIcon->GetResString( STR_QUICKSTART_EXIT ),
300 		  G_CALLBACK( exit_quickstarter_cb ) );
301 
302 	gtk_widget_show_all( pMenu );
303 }
304 
305 static void refresh_menu( GtkWidget *pMenu )
306 {
307 	if (!pExitMenuItem)
308 		populate_menu( pMenu );
309 
310 	bool bModal = ShutdownIcon::bModalMode;
311 	gtk_widget_set_sensitive( pExitMenuItem, !bModal);
312 	gtk_widget_set_sensitive( pOpenMenuItem, !bModal);
313     gtk_widget_set_sensitive( pDisableMenuItem, !bModal);
314 }
315 
316 static void activate_cb( GtkStatusIcon *status_icon,
317                          gpointer pMenu )
318 {
319     refresh_menu( GTK_WIDGET( pMenu ) );
320 
321     gtk_menu_popup( GTK_MENU( pMenu ), NULL, NULL,
322                     gtk_status_icon_position_menu,
323                     status_icon, 0, gtk_get_current_event_time() );
324 }
325 
326 static void popup_menu_cb(GtkStatusIcon *status_icon,
327                           guint button,
328                           guint activate_time,
329                           gpointer pMenu)
330 {
331 	if (button == 2)
332 		return;
333 
334 	refresh_menu( GTK_WIDGET( pMenu ) );
335 
336 	gtk_menu_popup( GTK_MENU( pMenu ), NULL, NULL,
337                     gtk_status_icon_position_menu,
338                     status_icon, button, activate_time );
339 }
340 
341 void SAL_DLLPUBLIC_EXPORT plugin_init_sys_tray()
342 {
343 	::vos::OGuard aGuard( Application::GetSolarMutex() );
344 
345 	if( !g_type_from_name( "GdkDisplay" ) )
346 		return;
347 
348     ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
349     if ( !pShutdownIcon )
350         return;
351 
352     pTrayIcon = gtk_status_icon_new();
353     pVCLResMgr = CREATEVERSIONRESMGR( vcl );
354 
355     if ( !pTrayIcon || !pVCLResMgr )
356         return;
357 
358     // disable shutdown
359     pShutdownIcon->SetVeto( true );
360     pShutdownIcon->addTerminateListener();
361 
362 	OString aLabel;
363 
364 	aLabel = rtl::OUStringToOString (
365 			pShutdownIcon->GetResString( STR_QUICKSTART_TIP ),
366 			RTL_TEXTENCODING_UTF8 );
367 
368 	GdkPixbuf *pPixbuf = ResIdToPixbuf( SV_ICON_LARGE_START + SV_ICON_ID_OFFICE );
369     g_object_set( G_OBJECT( pTrayIcon ),
370                   "pixbuf", pPixbuf,
371                   "title", aLabel.getStr(),/* Since 2.18 */
372                   "tooltip-text", aLabel.getStr(), /* Since 2.16 */
373                   NULL );
374 	g_object_unref( pPixbuf );
375 
376 	// gtk_status_icon_set_tooltip_text is available since 2.16
377     // so use instead deprecated gtk_status_icon_set_tooltip
378    gtk_status_icon_set_tooltip( pTrayIcon, aLabel.getStr() );
379 
380 	GtkWidget *pMenu = gtk_menu_new();
381 
382 	// Signal "button-press-event" is available since 2.14
383     // Use "activate" and "popup-menu" instead
384     g_signal_connect( pTrayIcon, "activate",
385                       G_CALLBACK( activate_cb ), pMenu );
386     g_signal_connect( pTrayIcon, "popup-menu",
387                       G_CALLBACK( popup_menu_cb ), pMenu );
388 
389     g_signal_connect( pMenu, "deactivate",
390                       G_CALLBACK (menu_deactivate_cb), NULL);
391 }
392 
393 void SAL_DLLPUBLIC_EXPORT plugin_shutdown_sys_tray()
394 {
395 	::vos::OGuard aGuard( Application::GetSolarMutex() );
396 	if( !pTrayIcon )
397 		return;
398 	g_object_unref( pTrayIcon );
399 	pTrayIcon = NULL;
400 	pExitMenuItem = NULL;
401 	pOpenMenuItem = NULL;
402     pDisableMenuItem = NULL;
403 }
404 
405 #endif // ENABLE_QUICKSTART_APPLET
406