/*
* 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;
}