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