/* * Copyright (C) 2008 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Neil Jagdish Patel * */ #if HAVE_CONFIG_H #include #endif #include "launcher-app.h" #include #include #include #include #include #include #include #include #include #include "clutter-focus-manager.h" #include "launcher-background.h" #include "launcher-catbar.h" #include "launcher-config.h" #include "launcher-defines.h" #include "launcher-iconview.h" #include "launcher-link-editor.h" #include "launcher-menu.h" #include "launcher-shortcut.h" #include "launcher-sidebar.h" #include "launcher-startup.h" #include "launcher-util.h" #include "launcher-wm.h" #define LINK_WATCH_DIR "/tmp/webfav" G_DEFINE_TYPE (LauncherApp, launcher_app, CLUTTER_TYPE_GROUP); #define LAUNCHER_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ LAUNCHER_TYPE_APP, \ LauncherAppPrivate)) struct _LauncherAppPrivate { gboolean windowed; LauncherWm *wm; LauncherMenu *menu; ClutterActor *focus_manager; ClutterActor *bg; ClutterActor *shadow; ClutterActor *window; ClutterActor *ubuntu; ClutterActor *catbar; ClutterActor *iconviewbg; ClutterActor *iconview; ClutterActor *sidebar; ClutterActor *places; LauncherMenuCategory *current_cat; LauncherMenuCategory *places_cat; ClutterTimeline *time; ClutterEffectTemplate *temp; GFile *watch_dir; GFileMonitor *dir_monitor; }; /* Callbacks */ static void on_new_category (LauncherCatbar *catbar, LauncherMenuCategory *category, LauncherApp *app) { LauncherAppPrivate *priv; LauncherConfig *cfg = launcher_config_get_default (); g_return_if_fail (LAUNCHER_IS_APP (app)); g_return_if_fail (category); priv = app->priv; priv->current_cat = category; if (priv->places && priv->current_cat != priv->places_cat) launcher_shortcut_set_active (LAUNCHER_SHORTCUT (priv->places), FALSE); if (CLUTTER_IS_ACTOR (priv->iconview)) { clutter_effect_fade (priv->temp, priv->iconview, 0, (ClutterEffectCompleteFunc)clutter_actor_destroy, NULL); } priv->iconview = launcher_iconview_new (); clutter_container_add_actor (CLUTTER_CONTAINER (app), priv->iconview); clutter_actor_set_position (priv->iconview, clutter_actor_get_x (priv->window)+cfg->bar_width-3, PANEL_HEIGHT*2.3+PADDING); clutter_actor_show (priv->iconview); clutter_focus_manager_insert_group(CLUTTER_FOCUS_MANAGER(priv->focus_manager), CLUTTER_FOCUS_GROUP (priv->iconview), 1); launcher_iconview_set_category (LAUNCHER_ICONVIEW (priv->iconview), category); } static gboolean on_scroll_event (ClutterActor *actor, ClutterScrollEvent *event, LauncherApp *app) { g_return_val_if_fail (LAUNCHER_IS_APP (app), FALSE); if (!LAUNCHER_IS_ICONVIEW (app->priv->iconview)) return FALSE; return launcher_iconview_scroll_event (actor, event, LAUNCHER_ICONVIEW (app->priv->iconview) ); } static gboolean eat_event (ClutterActor *stage, ClutterEvent *event, LauncherApp *app) { gint opacity = clutter_actor_get_opacity (CLUTTER_ACTOR (app)); if (opacity == 255) { g_signal_handlers_disconnect_by_func (stage, eat_event, app); clutter_ungrab_pointer (); return FALSE; } return TRUE; } static void ungrab (ClutterActor *actor, LauncherApp *app) { clutter_ungrab_pointer (); g_signal_handlers_disconnect_by_func (clutter_stage_get_default (), eat_event, app); } static void show_launcher (LauncherWm *wm, LauncherApp *app) { LauncherAppPrivate *priv; g_return_if_fail (LAUNCHER_IS_APP (app)); priv = app->priv; clutter_effect_fade (priv->temp, CLUTTER_ACTOR (app), 255, (ClutterEffectCompleteFunc)ungrab, app); } static void hide_launcher (LauncherWm *wm, WnckWindow *window, LauncherApp *app) { LauncherAppPrivate *priv; g_return_if_fail (LAUNCHER_IS_APP (app)); priv = app->priv; if (priv->windowed) return; clutter_effect_fade (priv->temp, CLUTTER_ACTOR (app), 50, NULL, NULL); clutter_grab_pointer (clutter_stage_get_default ()); g_signal_connect (clutter_stage_get_default (), "captured-event", G_CALLBACK (eat_event), app); } /* * External link watching */ static gchar * save_favion (const gchar *path) { #define ICON_DIR ".config/netbook-remix--launcher/icons" GFile *from; GFile *to; gchar *new_path, *temp, *leaf; from = g_file_new_for_path (path); temp = g_filename_display_basename (path); leaf = g_strdup_printf ("%ld-%s", time (NULL), temp); new_path = g_build_filename (g_get_home_dir (), ICON_DIR, leaf, NULL); to = g_file_new_for_path (new_path); g_file_move (from, to, 0, NULL, NULL, NULL, NULL); g_free (temp); g_free (leaf); g_object_unref (from); g_object_unref (to); return new_path; } static void on_link_reponse (GtkWidget *dialog, gint res, LauncherApp *app) { if (!LAUNCHER_IS_APP (app)) { gtk_widget_destroy (dialog); return; } if (res == GTK_RESPONSE_YES) { gchar *name = NULL, *exec = NULL, *icon_name = NULL, *real_icon; g_object_get (dialog, "link-name", &name, "link-exec", &exec, "link-icon", &icon_name, NULL); real_icon = save_favion (icon_name); launcher_util_add_favorite (name, "Favorites", exec, real_icon); g_free (name); g_free (exec); g_free (icon_name); g_free (real_icon); } gtk_widget_destroy (dialog); } static void on_watch_dir_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event, LauncherApp *app) { LauncherAppPrivate *priv; gchar *link_path; g_return_if_fail (LAUNCHER_IS_APP (app)); priv = app->priv; if (event != G_FILE_MONITOR_EVENT_CREATED) return; link_path = g_file_get_path (file); if (link_path && strstr (link_path, ".desktop")) { GKeyFile *file; file = g_key_file_new (); if (g_key_file_load_from_file (file, link_path, 0, NULL)) { GtkWidget *dialog; gchar *icon_path; icon_path = g_build_filename (LINK_WATCH_DIR, g_key_file_get_string (file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL), NULL); dialog = launcher_link_editor_new (); g_object_set (dialog, "title", _("Add Shortcut"), "link-name", g_key_file_get_string (file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL), "link-exec", g_key_file_get_string (file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, NULL), "link-icon", icon_path, NULL); gtk_dialog_add_buttons (GTK_DIALOG (dialog), "gtk-cancel", GTK_RESPONSE_CANCEL, GTK_STOCK_APPLY, GTK_RESPONSE_YES, NULL); g_signal_connect (dialog, "response", G_CALLBACK (on_link_reponse), app); launcher_util_present_window (GTK_WINDOW (dialog)); g_free (icon_path); } g_key_file_free (file); } g_free (link_path); } static void watch_links (LauncherApp *app) { LauncherAppPrivate *priv = app->priv; GFile *dir; g_mkdir_with_parents (LINK_WATCH_DIR, 0700); dir = priv->watch_dir = g_file_new_for_path (LINK_WATCH_DIR); priv->dir_monitor = g_file_monitor_directory (dir, 0, NULL, NULL); g_signal_connect (priv->dir_monitor, "changed", G_CALLBACK (on_watch_dir_changed), app); } static gboolean launch_dist_page (ClutterActor *actor, ClutterButtonEvent *event) { const gchar *url; gchar *exec; if (event->button != 1) return FALSE; url = clutter_actor_get_name (actor); exec = g_strdup_printf ("%s %s", XDG_OPEN, url); gdk_spawn_command_line_on_screen (gdk_screen_get_default (), exec, NULL); g_free (exec); return FALSE; } static void load_dist_logo (LauncherApp *app) { #define DIST_DIR "/apps/netbook-launcher" #define DIST_IMG DIST_DIR"/dist_image" #define DIST_URL DIST_DIR"/dist_url" LauncherAppPrivate *priv = app->priv; LauncherConfig *cfg = launcher_config_get_default (); GConfClient *client = gconf_client_get_default (); ClutterActor *tex; GdkPixbuf *logo; gint width, height; const gchar *imageurl; const gchar *url; width = cfg->bar_width; height = cfg->shortcut_height * 1.1; height *= 1; gconf_client_add_dir (client, DIST_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL); imageurl = gconf_client_get_string (client, DIST_IMG, NULL); url = gconf_client_get_string (client, DIST_URL, NULL); if (!imageurl) return; logo = gdk_pixbuf_new_from_file_at_scale (imageurl, width, height, TRUE, NULL); if (logo) { tex = launcher_util_texture_new_from_pixbuf (logo); clutter_container_add_actor (CLUTTER_CONTAINER (app), tex); clutter_actor_set_anchor_point_from_gravity (tex, CLUTTER_GRAVITY_CENTER); clutter_actor_set_position (tex, clutter_actor_get_x (priv->window) + (width/2), CSH() - PADDING*2 - (height/2)); clutter_actor_set_name (tex, url); clutter_actor_set_reactive (tex, TRUE); clutter_actor_show (tex); if (url) { g_signal_connect (tex, "button-release-event", G_CALLBACK (launch_dist_page), NULL); } g_object_unref (logo); } g_object_unref (client); } static gboolean deactivate_shortcut (LauncherShortcut *shortcut) { launcher_shortcut_set_active (LAUNCHER_SHORTCUT (shortcut), FALSE); return FALSE; } static void on_quit_clicked (LauncherShortcut *shortcut) { gdk_spawn_command_line_on_screen (gdk_screen_get_default (), "gnome-session-save --kill --gui", NULL); g_timeout_add_seconds (2, (GSourceFunc)deactivate_shortcut, shortcut); } static void on_places_clicked (LauncherShortcut *shortcut, LauncherApp *app) { static LauncherMenuCategory *cat = NULL; GList *places, *p; if (cat == app->priv->current_cat) return; if (cat) { GList *a; for (a = cat->applications; a; a = a->next) { LauncherMenuApplication *app = a->data; g_free (app->name); g_free (app->exec); g_slice_free (LauncherMenuApplication, app); } g_list_free (cat->applications); cat->applications = NULL; } else { cat = app->priv->places_cat = g_slice_new0 (LauncherMenuCategory); } places = launcher_sidebar_get_places (LAUNCHER_SIDEBAR (launcher_sidebar_get_default ())); for (p = places; p; p = p->next) { LauncherShortcut *shortcut = p->data; LauncherMenuApplication *app; GVolume *volume; if (!LAUNCHER_IS_SHORTCUT (shortcut)) continue; app = g_slice_new0 (LauncherMenuApplication); app->name = g_strdup (launcher_shortcut_get_label (shortcut)); app->pixbuf = launcher_shortcut_get_icon (shortcut); app->exec = g_strdup (clutter_actor_get_name (CLUTTER_ACTOR (shortcut))); app->can_fav = FALSE; volume = g_object_get_data (G_OBJECT (shortcut), "volume"); if (G_IS_VOLUME (volume)) app->volume = volume; cat->applications = g_list_append (cat->applications, app); } launcher_catbar_deactivate (LAUNCHER_CATBAR (app->priv->catbar)); on_new_category (NULL, cat, app); g_list_free (places); } static void on_sidebar_shortcuts_changed (LauncherSidebar *sidebar, LauncherApp *app) { app->priv->current_cat = NULL; on_places_clicked (LAUNCHER_SHORTCUT (app->priv->places), app); } /* GObject stuff */ static void launcher_app_finalize (GObject *object) { LauncherAppPrivate *priv; priv = LAUNCHER_APP_GET_PRIVATE (object); g_object_unref (priv->wm); g_object_unref (priv->menu); G_OBJECT_CLASS (launcher_app_parent_class)->finalize (object); } static void launcher_app_class_init (LauncherAppClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); obj_class->finalize = launcher_app_finalize; g_type_class_add_private (obj_class, sizeof (LauncherAppPrivate)); } static void launcher_app_init (LauncherApp *app) { LauncherAppPrivate *priv; ClutterActor *stage = clutter_stage_get_default (); LauncherConfig *cfg = launcher_config_get_default (); ClutterActor *shortcut; const gchar *winmode; priv = app->priv = LAUNCHER_APP_GET_PRIVATE (app); winmode = g_getenv ("LAUNCHER_WINDOWED"); priv->windowed = winmode ? atoi (winmode) : FALSE; priv->places = NULL; priv->places_cat = NULL; /* The desktop background */ priv->bg = launcher_background_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->bg); clutter_actor_set_size (priv->bg, CSW(), CSH()); clutter_actor_set_position (priv->bg, 0, 0); clutter_actor_show (priv->bg); /* Main container group */ clutter_container_add_actor (CLUTTER_CONTAINER (stage), CLUTTER_ACTOR (app)); clutter_actor_set_position (CLUTTER_ACTOR (app), 0, 0); clutter_actor_set_size (CLUTTER_ACTOR (app), CSW(), CSH()); clutter_actor_show (CLUTTER_ACTOR (app)); /* Focus server */ priv->focus_manager = clutter_focus_manager_get_default (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->focus_manager); stage = CLUTTER_ACTOR (app); /* Shadow priv->shadow = launcher_util_texture_new_from_file (PKGDATADIR"/shadow.png"); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->shadow); clutter_actor_set_position (priv->shadow, 0, PANEL_HEIGHT); if (!priv->windowed) clutter_actor_show (priv->shadow); */ /* Window */ priv->window = launcher_util_texture_new_from_file (PKGDATADIR"/window.svg"); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->window); clutter_actor_set_opacity (priv->window, 0); priv->window = tidy_texture_frame_new (CLUTTER_TEXTURE (priv->window), 30, 30, 30, 30); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->window); clutter_actor_set_size (priv->window, cfg->win_width, cfg->win_height); clutter_actor_set_position (priv->window, (CSW()-cfg->win_width)/2, PANEL_HEIGHT + 5); clutter_actor_show (priv->window); /* Ubuntu logo */ priv->ubuntu = launcher_util_texture_new_from_file (PKGDATADIR"/ubuntu.png"); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->ubuntu); clutter_actor_set_anchor_point_from_gravity (priv->ubuntu, CLUTTER_GRAVITY_NORTH); clutter_actor_set_position (priv->ubuntu, CSW()/2, PANEL_HEIGHT + PADDING + PANEL_HEIGHT/4); clutter_actor_show (priv->ubuntu); /* Init the WM interface */ priv->wm = launcher_wm_get_default (); g_signal_connect (priv->wm, "show-windec", G_CALLBACK (hide_launcher), app); g_signal_connect (priv->wm, "hide-windec", G_CALLBACK (show_launcher), app); /* Application menu */ priv->menu = launcher_menu_get_default (); /* Iconviewbg */ priv->iconviewbg = launcher_util_texture_new_from_file (PKGDATADIR"/iconviewbg.png"); clutter_actor_set_opacity (priv->iconviewbg, 0); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->iconviewbg); priv->iconviewbg = tidy_texture_frame_new (CLUTTER_TEXTURE (priv->iconviewbg), 30, 30, 30, 30); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->iconviewbg); clutter_actor_set_size (priv->iconviewbg, cfg->iconview_width, cfg->win_height-(PANEL_HEIGHT*1.3+PADDING)); clutter_actor_set_position (priv->iconviewbg, clutter_actor_get_x (priv->window)+cfg->bar_width-3, PANEL_HEIGHT*2.3+PADDING+2); clutter_actor_show (priv->iconviewbg); clutter_actor_set_reactive (CLUTTER_ACTOR (priv->iconviewbg), TRUE); g_signal_connect (priv->iconviewbg, "scroll-event", G_CALLBACK (on_scroll_event), app); /* Sidebar */ if (!cfg->is_portrait) { priv->sidebar = launcher_sidebar_get_default (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->sidebar); clutter_actor_set_position (priv->sidebar, clutter_actor_get_x (priv->window) +CAW(priv->window)-cfg->bar_width, PANEL_HEIGHT*2.3+PADDING); clutter_actor_show (priv->sidebar); clutter_focus_manager_insert_group (CLUTTER_FOCUS_MANAGER(priv->focus_manager), CLUTTER_FOCUS_GROUP (priv->sidebar), 1); } /* Category bar */ priv->catbar = launcher_catbar_get_default (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), priv->catbar); clutter_actor_set_position (priv->catbar, clutter_actor_get_x (priv->window), PANEL_HEIGHT*2.3+PADDING); clutter_actor_show (priv->catbar); g_signal_connect (priv->catbar, "category-selected", G_CALLBACK (on_new_category), app); clutter_focus_manager_insert_group(CLUTTER_FOCUS_MANAGER(priv->focus_manager), CLUTTER_FOCUS_GROUP (priv->catbar), 0); /* dist logo */ if (!cfg->is_portrait) { load_dist_logo (app); } else { shortcut = launcher_shortcut_new ("gtk-quit", _("Quit..."), FALSE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), shortcut); clutter_actor_set_position (shortcut, 0, cfg->win_height - (cfg->shortcut_height)); clutter_actor_show_all (shortcut); g_signal_connect (shortcut, "clicked", G_CALLBACK (on_quit_clicked), NULL); shortcut = priv->places = launcher_shortcut_new ("folder", _("Places"), FALSE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), shortcut); clutter_actor_set_position (shortcut, 0, cfg->win_height - (cfg->shortcut_height*2)); clutter_actor_show_all (shortcut); g_signal_connect (shortcut, "clicked", G_CALLBACK (on_places_clicked), app); g_signal_connect (launcher_sidebar_get_default (), "shortcuts-changed", G_CALLBACK (on_sidebar_shortcuts_changed), app); } on_new_category (NULL, launcher_menu_get_categories (priv->menu)->data, app); priv->time = clutter_timeline_new_for_duration (MID_TIME); priv->temp = clutter_effect_template_new (priv->time, clutter_sine_inc_func); watch_links (app); } ClutterActor * launcher_app_get_default (void) { static ClutterActor *app = NULL; if (!app) app = g_object_new (LAUNCHER_TYPE_APP, NULL); return app; }