1 2 #ifdef ENABLE_QUICKSTART_APPLET 3 4 #include <unotools/moduleoptions.hxx> 5 6 #include <unotools/dynamicmenuoptions.hxx> 7 8 #include <gtk/gtk.h> 9 #include <glib.h> 10 #include <eggtray/eggtrayicon.h> 11 #include <vos/mutex.hxx> 12 #include <vcl/bitmapex.hxx> 13 #include <vcl/bmpacc.hxx> 14 #include <sfx2/app.hxx> 15 #ifndef _SFX_APP_HRC 16 #include "app.hrc" 17 #endif 18 #ifndef __SHUTDOWNICON_HXX__ 19 #define USE_APP_SHORTCUTS 20 #include "shutdownicon.hxx" 21 #endif 22 23 // Cut/paste from vcl/inc/svids.hrc 24 #define SV_ICON_SMALL_START 25000 25 26 #define SV_ICON_ID_OFFICE 1 27 #define SV_ICON_ID_TEXT 2 28 #define SV_ICON_ID_SPREADSHEET 4 29 #define SV_ICON_ID_DRAWING 6 30 #define SV_ICON_ID_PRESENTATION 8 31 #define SV_ICON_ID_DATABASE 14 32 #define SV_ICON_ID_FORMULA 15 33 #define SV_ICON_ID_TEMPLATE 16 34 35 using namespace ::rtl; 36 using namespace ::osl; 37 38 static ResMgr *pVCLResMgr; 39 static EggTrayIcon *pTrayIcon; 40 static GtkWidget *pExitMenuItem = NULL; 41 static GtkWidget *pOpenMenuItem = 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 egg_tray_icon_cancel_message (pTrayIcon, 1 ); 70 ShutdownIcon::getInstance()->terminateDesktop(); 71 plugin_shutdown_sys_tray(); 72 } 73 74 static void menu_deactivate_cb( GtkWidget *pMenu ) 75 { 76 gtk_menu_popdown( GTK_MENU( pMenu ) ); 77 } 78 79 static GdkPixbuf * ResIdToPixbuf( sal_uInt16 nResId ) 80 { 81 ResId aResId( SV_ICON_SMALL_START + nResId, *pVCLResMgr ); 82 BitmapEx aIcon( aResId ); 83 Bitmap pInSalBitmap = aIcon.GetBitmap(); 84 AlphaMask pInSalAlpha = aIcon.GetAlpha(); 85 86 BitmapReadAccess* pSalBitmap = pInSalBitmap.AcquireReadAccess(); 87 BitmapReadAccess* pSalAlpha = pInSalAlpha.AcquireReadAccess(); 88 89 g_return_val_if_fail( pSalBitmap != NULL, NULL ); 90 91 Size aSize( pSalBitmap->Width(), pSalBitmap->Height() ); 92 g_return_val_if_fail( Size( pSalAlpha->Width(), pSalAlpha->Height() ) == aSize, NULL ); 93 94 int nX, nY; 95 guchar *pPixbufData = ( guchar * )g_malloc( 4 * aSize.Width() * aSize.Height() ); 96 guchar *pDestData = pPixbufData; 97 98 for( nY = 0; nY < pSalBitmap->Height(); nY++ ) 99 { 100 for( nX = 0; nX < pSalBitmap->Width(); nX++ ) 101 { 102 BitmapColor aPix; 103 aPix = pSalBitmap->GetPixel( nY, nX ); 104 pDestData[0] = aPix.GetRed(); 105 pDestData[1] = aPix.GetGreen(); 106 pDestData[2] = aPix.GetBlue(); 107 if (pSalAlpha) 108 { 109 aPix = pSalAlpha->GetPixel( nY, nX ); 110 pDestData[3] = 255 - aPix.GetIndex(); 111 } 112 else 113 pDestData[3] = 255; 114 pDestData += 4; 115 } 116 } 117 118 pInSalBitmap.ReleaseAccess( pSalBitmap ); 119 if( pSalAlpha ) 120 pInSalAlpha.ReleaseAccess( pSalAlpha ); 121 122 return gdk_pixbuf_new_from_data( pPixbufData, 123 GDK_COLORSPACE_RGB, sal_True, 8, 124 aSize.Width(), aSize.Height(), 125 aSize.Width() * 4, 126 (GdkPixbufDestroyNotify) g_free, 127 NULL ); 128 } 129 130 extern "C" { 131 static void oustring_delete (gpointer data, 132 GClosure * /* closure */) 133 { 134 OUString *pURL = (OUString *) data; 135 delete pURL; 136 } 137 } 138 139 static void add_item( GtkMenuShell *pMenuShell, const char *pAsciiURL, 140 OUString *pOverrideLabel, 141 sal_uInt16 nResId, GCallback pFnCallback ) 142 { 143 OUString *pURL = new OUString (OStringToOUString( pAsciiURL, 144 RTL_TEXTENCODING_UTF8 )); 145 OString aLabel; 146 if (pOverrideLabel) 147 aLabel = OUStringToOString (*pOverrideLabel, RTL_TEXTENCODING_UTF8); 148 else 149 { 150 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance(); 151 aLabel = OUStringToOString (pShutdownIcon->GetUrlDescription( *pURL ), 152 RTL_TEXTENCODING_UTF8); 153 } 154 155 GdkPixbuf *pPixbuf= ResIdToPixbuf( nResId ); 156 GtkWidget *pImage = gtk_image_new_from_pixbuf( pPixbuf ); 157 g_object_unref( G_OBJECT( pPixbuf ) ); 158 159 GtkWidget *pMenuItem = gtk_image_menu_item_new_with_label( aLabel ); 160 gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage ); 161 g_signal_connect_data( pMenuItem, "activate", pFnCallback, pURL, 162 oustring_delete, GConnectFlags(0)); 163 164 gtk_menu_shell_append( pMenuShell, pMenuItem ); 165 } 166 167 // Unbelievably nasty 168 using namespace ::com::sun::star::uno; 169 using namespace ::com::sun::star::task; 170 using namespace ::com::sun::star::lang; 171 using namespace ::com::sun::star::beans; 172 173 static void add_ugly_db_item( GtkMenuShell *pMenuShell, const char *pAsciiURL, 174 sal_uInt16 nResId, GCallback pFnCallback ) 175 { 176 SvtDynamicMenuOptions aOpt; 177 Sequence < Sequence < PropertyValue > > aMenu = aOpt.GetMenu( E_NEWMENU ); 178 for ( sal_Int32 n=0; n<aMenu.getLength(); n++ ) 179 { 180 ::rtl::OUString aURL; 181 ::rtl::OUString aDescription; 182 Sequence < PropertyValue >& aEntry = aMenu[n]; 183 for ( sal_Int32 m=0; m<aEntry.getLength(); m++ ) 184 { 185 if ( aEntry[m].Name.equalsAsciiL( "URL", 3 ) ) 186 aEntry[m].Value >>= aURL; 187 if ( aEntry[m].Name.equalsAsciiL( "Title", 5 ) ) 188 aEntry[m].Value >>= aDescription; 189 } 190 191 if ( aURL.equalsAscii( BASE_URL ) && aDescription.getLength() ) 192 { 193 add_item (pMenuShell, pAsciiURL, &aDescription, nResId, pFnCallback); 194 break; 195 } 196 } 197 } 198 199 static GtkWidget * 200 add_image_menu_item( GtkMenuShell *pMenuShell, 201 const gchar *stock_id, 202 rtl::OUString aLabel, 203 GCallback activate_cb ) 204 { 205 OString aUtfLabel = rtl::OUStringToOString (aLabel, RTL_TEXTENCODING_UTF8 ); 206 207 GtkWidget *pImage; 208 pImage = gtk_image_new_from_stock( stock_id, GTK_ICON_SIZE_MENU ); 209 210 GtkWidget *pMenuItem; 211 pMenuItem = gtk_image_menu_item_new_with_label( aUtfLabel ); 212 gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( pMenuItem ), pImage ); 213 214 gtk_menu_shell_append( pMenuShell, pMenuItem ); 215 g_signal_connect( pMenuItem, "activate", activate_cb, NULL); 216 217 return pMenuItem; 218 } 219 220 static void populate_menu( GtkWidget *pMenu ) 221 { 222 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance(); 223 GtkMenuShell *pMenuShell = GTK_MENU_SHELL( pMenu ); 224 SvtModuleOptions aModuleOptions; 225 226 if ( aModuleOptions.IsWriter() ) 227 add_item (pMenuShell, WRITER_URL, NULL, 228 SV_ICON_ID_TEXT, G_CALLBACK( open_url_cb )); 229 230 if ( aModuleOptions.IsCalc() ) 231 add_item (pMenuShell, CALC_URL, NULL, 232 SV_ICON_ID_SPREADSHEET, G_CALLBACK( open_url_cb )); 233 234 if ( aModuleOptions.IsImpress() ) 235 add_item (pMenuShell, IMPRESS_URL, NULL, 236 SV_ICON_ID_PRESENTATION, G_CALLBACK( open_url_cb )); 237 238 if ( aModuleOptions.IsDraw() ) 239 add_item (pMenuShell, DRAW_URL, NULL, 240 SV_ICON_ID_DRAWING, G_CALLBACK( open_url_cb )); 241 242 if ( aModuleOptions.IsDataBase() ) 243 add_ugly_db_item (pMenuShell, BASE_URL, 244 SV_ICON_ID_DATABASE, G_CALLBACK( open_url_cb )); 245 246 if ( aModuleOptions.IsMath() ) 247 add_item (pMenuShell, MATH_URL, NULL, 248 SV_ICON_ID_FORMULA, G_CALLBACK( open_url_cb )); 249 250 OUString aULabel = pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE ); 251 add_item (pMenuShell, "dummy", &aULabel, 252 SV_ICON_ID_TEMPLATE, G_CALLBACK( open_template_cb )); 253 254 OString aLabel; 255 GtkWidget *pMenuItem; 256 257 pMenuItem = gtk_separator_menu_item_new(); 258 gtk_menu_shell_append( pMenuShell, pMenuItem ); 259 260 pOpenMenuItem = add_image_menu_item 261 (pMenuShell, GTK_STOCK_OPEN, 262 pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN ), 263 G_CALLBACK( open_file_cb )); 264 265 pMenuItem = gtk_separator_menu_item_new(); 266 gtk_menu_shell_append( pMenuShell, pMenuItem ); 267 268 (void) 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 } 293 294 extern "C" { 295 static void 296 layout_menu( GtkMenu *menu, 297 gint *x, gint *y, gboolean *push_in, 298 gpointer ) 299 { 300 GtkRequisition req; 301 GtkWidget *ebox = GTK_BIN( pTrayIcon )->child; 302 303 gtk_widget_size_request( GTK_WIDGET( menu ), &req ); 304 gdk_window_get_origin( ebox->window, x, y ); 305 306 (*x) += ebox->allocation.x; 307 (*y) += ebox->allocation.y; 308 309 if (*y >= gdk_screen_get_height (gtk_widget_get_screen (ebox)) / 2) 310 (*y) -= req.height; 311 else 312 (*y) += ebox->allocation.height; 313 314 *push_in = sal_True; 315 } 316 } 317 318 static gboolean display_menu_cb( GtkWidget *, 319 GdkEventButton *event, GtkWidget *pMenu ) 320 { 321 if (event->button == 2) 322 return sal_False; 323 324 #ifdef TEMPLATE_DIALOG_MORE_POLISHED 325 if (event->button == 1 && 326 event->type == GDK_2BUTTON_PRESS) 327 { 328 open_template_cb( NULL ); 329 return sal_True; 330 } 331 if (event->button == 3) 332 { 333 ... as below ... 334 #endif 335 336 refresh_menu( pMenu ); 337 338 gtk_menu_popup( GTK_MENU( pMenu ), NULL, NULL, 339 layout_menu, NULL, 0, event->time ); 340 341 return sal_True; 342 } 343 344 extern "C" { 345 static gboolean 346 show_at_idle( gpointer ) 347 { 348 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 349 gtk_widget_show_all( GTK_WIDGET( pTrayIcon ) ); 350 return sal_False; 351 } 352 } 353 354 void SAL_DLLPUBLIC_EXPORT plugin_init_sys_tray() 355 { 356 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 357 358 if( !g_type_from_name( "GdkDisplay" ) ) 359 return; 360 361 OString aLabel; 362 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance(); 363 364 aLabel = rtl::OUStringToOString ( 365 pShutdownIcon->GetResString( STR_QUICKSTART_TIP ), 366 RTL_TEXTENCODING_UTF8 ); 367 368 pTrayIcon = egg_tray_icon_new( aLabel ); 369 370 GtkWidget *pParent = gtk_event_box_new(); 371 GtkTooltips *pTooltips = gtk_tooltips_new(); 372 gtk_tooltips_set_tip( GTK_TOOLTIPS( pTooltips ), pParent, aLabel, NULL ); 373 374 GtkWidget *pIconImage = gtk_image_new(); 375 gtk_container_add( GTK_CONTAINER( pParent ), pIconImage ); 376 377 pVCLResMgr = CREATEVERSIONRESMGR( vcl ); 378 379 GdkPixbuf *pPixbuf = ResIdToPixbuf( SV_ICON_ID_OFFICE ); 380 gtk_image_set_from_pixbuf( GTK_IMAGE( pIconImage ), pPixbuf ); 381 g_object_unref( pPixbuf ); 382 383 GtkWidget *pMenu = gtk_menu_new(); 384 g_signal_connect (pMenu, "deactivate", 385 G_CALLBACK (menu_deactivate_cb), NULL); 386 g_signal_connect( pParent, "button_press_event", 387 G_CALLBACK( display_menu_cb ), pMenu ); 388 gtk_container_add( GTK_CONTAINER( pTrayIcon ), pParent ); 389 390 // Show at idle to avoid artefacts at startup 391 g_idle_add (show_at_idle, (gpointer) pTrayIcon); 392 393 // disable shutdown 394 pShutdownIcon->SetVeto( true ); 395 pShutdownIcon->addTerminateListener(); 396 } 397 398 void SAL_DLLPUBLIC_EXPORT plugin_shutdown_sys_tray() 399 { 400 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 401 if( !pTrayIcon ) 402 return; 403 gtk_widget_destroy( GTK_WIDGET( pTrayIcon ) ); 404 pTrayIcon = NULL; 405 pExitMenuItem = NULL; 406 pOpenMenuItem = NULL; 407 } 408 409 #endif // ENABLE_QUICKSTART_APPLET 410