foobar-widget.c 26.5 KB
Newer Older
1
/* GNOME panel: foobar widGET
2 3
 * Copyright 1999,2000 Helix Code, Inc.
 * Copyright 2000 Eazel, Inc.
4 5 6 7 8
 *
 * Author: Jacob Berkman
 *
 */

9
/* since BASEP_IS_WIDGET() is used throughout, it makes life easier if we
10 11 12 13
 * have a GtkWindow of our own.
 */

#include <config.h>
14
#include <unistd.h>
Michael Meeks's avatar
Michael Meeks committed
15
#include <string.h>
16

17
#include <libgnome/libgnome.h>
18 19
#include <gdk/gdkkeysyms.h>

20 21 22
/* Yes, yes I know, now bugger off ... */
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include <libwnck/libwnck.h>
23

24 25 26 27 28 29 30 31
#include "foobar-widget.h"

#include "menu.h"
#include "menu-util.h"
#include "session.h"
#include "panel-widget.h"
#include "xstuff.h"
#include "basep-widget.h"
32
#include "panel-config-global.h"
33
#include "panel-util.h"
34
#include "drawer-widget.h"
35
#include "gnome-run.h"
36
#include "multiscreen-stuff.h"
37
#include "panel-marshal.h"
38

39
#define ICON_SIZE 20
40

41
extern GlobalConfig global_config;
42
extern GSList *panel_list;
43

44 45
extern GtkTooltips *panel_tooltips;

46 47 48 49 50 51
static void foobar_widget_class_init	(FoobarWidgetClass *klass);
static void foobar_widget_instance_init (FoobarWidget *foo);
static void foobar_widget_realize	(GtkWidget *w);
static void foobar_widget_destroy	(GtkObject *o);
static void foobar_widget_size_allocate	(GtkWidget *w,
					 GtkAllocation *alloc);
52 53 54 55
static gboolean foobar_leave_notify	(GtkWidget *widget,
					 GdkEventCrossing *event);
static gboolean foobar_enter_notify	(GtkWidget *widget,
					 GdkEventCrossing *event);
56
static void foobar_widget_move_focus_out   (FoobarWidget *foo);
57
static void append_task_menu (FoobarWidget *foo, GtkMenuShell *menu_bar);
58
static void setup_task_menu (FoobarWidget *foo);
59

60
static GList *foobars = NULL;
61

62
static GtkWindowClass *foobar_widget_parent_class = NULL;
63

64
enum {
65
	MOVE_FOCUS_OUT_SIGNAL,
66 67 68 69 70
	WIDGET_LAST_SIGNAL
};

static guint foobar_widget_signals[WIDGET_LAST_SIGNAL] = { 0 };

Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
71
GType
72
foobar_widget_get_type (void)
73
{
74 75 76 77 78 79 80 81 82 83 84 85 86
	static GType object_type = 0;

	if (object_type == 0) {
		static const GTypeInfo object_info = {
                    sizeof (FoobarWidgetClass),
                    (GBaseInitFunc)         NULL,
                    (GBaseFinalizeFunc)     NULL,
                    (GClassInitFunc)        foobar_widget_class_init,
                    NULL,                   /* class_finalize */
                    NULL,                   /* class_data */
                    sizeof (FoobarWidget),
                    0,                      /* n_preallocs */
                    (GInstanceInitFunc)     foobar_widget_instance_init
87 88
		};

89 90
		object_type = g_type_register_static (GTK_TYPE_WINDOW, "FoobarWidget", &object_info, 0);
        	foobar_widget_parent_class = g_type_class_ref (GTK_TYPE_WINDOW);
91 92 93

	}

94
	return object_type;
95 96 97 98 99
}

static void
foobar_widget_class_init (FoobarWidgetClass *klass)
{
100
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
101
	GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
102
	GtkBindingSet *binding_set;
103

104 105
	object_class->destroy = foobar_widget_destroy;

106
	widget_class->realize = foobar_widget_realize;
107
	widget_class->size_allocate = foobar_widget_size_allocate;
108 109
	widget_class->enter_notify_event = foobar_enter_notify;
	widget_class->leave_notify_event = foobar_leave_notify;
110

111
	klass->move_focus_out = foobar_widget_move_focus_out;
112

113 114 115 116 117 118 119
	gtk_rc_parse_string ("style \"panel-foobar-menubar-style\"\n"
			     "{\n"
			     "GtkMenuBar::shadow-type = none\n"
			     "GtkMenuBar::internal-padding = 0\n"
			     "}\n"
			     "widget \"*.panel-foobar-menubar\" style \"panel-foobar-menubar-style\"");

120 121
	foobar_widget_signals[MOVE_FOCUS_OUT_SIGNAL] =
		g_signal_new	("move_focus_out",
122 123
				G_TYPE_FROM_CLASS (object_class),
				G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
124
				G_STRUCT_OFFSET (FoobarWidgetClass, move_focus_out),				NULL,
125 126 127 128 129 130 131 132
                                NULL,
                                panel_marshal_VOID__VOID,
				G_TYPE_NONE,
				0);

	binding_set = gtk_binding_set_by_class (object_class);
	gtk_binding_entry_add_signal (binding_set,
                                      GDK_Tab, GDK_CONTROL_MASK,
133
                                      "move_focus_out", 0);
134 135
	gtk_binding_entry_add_signal (binding_set,
                                      GDK_KP_Tab, GDK_CONTROL_MASK,
136
                                      "move_focus_out", 0);
137 138
	gtk_binding_entry_add_signal (binding_set,
                                      GDK_ISO_Left_Tab, GDK_CONTROL_MASK,
139
                                      "move_focus_out", 0);
140 141


142 143 144
}

static GtkWidget *
145
pixmap_menu_item_new (const char *text, const char *try_file, gboolean force_image)
146 147 148 149
{
	GtkWidget *item;
	GtkWidget *label;

Martin Baulig's avatar
Martin Baulig committed
150
	item = gtk_image_menu_item_new ();
151

152 153
	panel_load_menu_image_deferred (item, try_file, NULL /* fallback */,
					force_image);
154 155 156 157 158 159 160 161 162 163

	if (text) {
		label = gtk_label_new (text);
		gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
		gtk_container_add (GTK_CONTAINER (item), label);
	}

	return item;
}

164 165 166 167
static gboolean
foobar_leave_notify (GtkWidget *widget,
		     GdkEventCrossing *event)
{
168 169
	if (GTK_WIDGET_CLASS (foobar_widget_parent_class)->leave_notify_event)
		GTK_WIDGET_CLASS (foobar_widget_parent_class)->leave_notify_event (widget,
170 171 172 173 174 175 176 177 178
								     event);

	return FALSE;
}

static gboolean
foobar_enter_notify (GtkWidget *widget,
		     GdkEventCrossing *event)
{
179 180
	if (GTK_WIDGET_CLASS (foobar_widget_parent_class)->enter_notify_event)
		GTK_WIDGET_CLASS (foobar_widget_parent_class)->enter_notify_event (widget,
181 182 183 184 185 186 187 188
								     event);

	if (global_config.autoraise)
		gdk_window_raise (widget->window);

	return FALSE;
}

189 190
static void
append_actions_menu (GtkWidget *menu_bar)
191
{
192
	GtkWidget *menu, *item;
193

194
	menu = panel_menu_new ();
195

196
	item = pixmap_menu_item_new (_("Run Program..."), "gnome-run.png",
197
				     FALSE /* force_image */);
198 199 200 201 202 203 204 205 206 207 208 209 210 211
	gtk_tooltips_set_tip (panel_tooltips, item,
			      _("Run applications, if you know the "
				"correct command to type in"),
			      NULL);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
	g_signal_connect (G_OBJECT (item), "activate",
			  G_CALLBACK (show_run_dialog), 0);
	setup_internal_applet_drag (item, "RUN:NEW");

	/* FIXME: search */
	/* FIXME: shutdown or reboot */

	if (panel_is_program_in_path  ("xscreensaver")) {
		item = pixmap_menu_item_new (_("Lock Display"), 
212 213
					     "gnome-lockscreen.png",
					     FALSE /* force_image */);
214 215 216 217 218 219 220 221
		gtk_tooltips_set_tip (panel_tooltips, item,
				      _("Protect your computer from "
					"unauthorized use"),
				      NULL);
		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
		g_signal_connect (G_OBJECT (item), "activate",
				  G_CALLBACK (panel_lock), 0);
		setup_internal_applet_drag(item, "LOCK:NEW");
222 223
	}

224 225
	item = pixmap_menu_item_new (_("Log Out"), "gnome-term-night.png",
				     FALSE /* force_image */);
226 227 228 229 230 231 232 233 234 235
	gtk_tooltips_set_tip (panel_tooltips, item,
			      _("Quit from the GNOME desktop"),
			      NULL);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
	g_signal_connect (G_OBJECT (item), "activate",
			  G_CALLBACK (panel_quit), 0);
	setup_internal_applet_drag (item, "LOGOUT:NEW");

	item = gtk_menu_item_new_with_label (_("Actions"));
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
236
	gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), item);
237

238
	panel_stretch_events_to_toplevel (item, PANEL_STRETCH_TOP);
239 240
}

241
void
242
foobar_widget_update_winhints (FoobarWidget *foo)
243
{
244
	GtkWidget *w = GTK_WIDGET (foo);
245

246 247
	gtk_window_set_decorated (GTK_WINDOW (w), FALSE);
	gtk_window_stick (GTK_WINDOW (w));
248

249
	xstuff_set_wmspec_dock_hints (w->window, FALSE /* autohide */);
250 251
}

252 253 254
static void
foobar_widget_realize (GtkWidget *w)
{
255 256 257
	gtk_window_set_wmclass (GTK_WINDOW (w),
				"panel_window", "Panel");

258 259
	if (GTK_WIDGET_CLASS (foobar_widget_parent_class)->realize)
		GTK_WIDGET_CLASS (foobar_widget_parent_class)->realize (w);
260

261
	foobar_widget_update_winhints (FOOBAR_WIDGET (w));
262
	xstuff_set_no_group_and_no_input (w->window);
263 264

	setup_task_menu (FOOBAR_WIDGET (w));
265 266 267 268 269 270

	xstuff_set_wmspec_strut (w->window,
				 0 /* left */,
				 0 /* right */,
				 w->allocation.height /* top */,
				 0 /* bottom */);
271 272
}

273
static void
274
programs_menu_to_display (GtkWidget *menu)
275
{
276
	if (menu_need_reread (menu)) {
277 278
		int flags;

279 280 281 282 283 284 285
		while (GTK_MENU_SHELL (menu)->children)
			gtk_widget_destroy (GTK_MENU_SHELL (menu)->children->data);
		flags = MAIN_MENU_SYSTEM;
		if (got_kde_menus ())
			flags |= MAIN_MENU_KDE_SUB;
		if (got_distro_menus ())
			flags |= MAIN_MENU_DISTRIBUTION_SUB;
286
		create_root_menu (menu, TRUE, flags, FALSE, FALSE /* run_item */);
287 288
	}
}
289

290 291 292
static void
set_the_task_submenu (FoobarWidget *foo, GtkWidget *item)
{
293
	foo->task_menu = gtk_menu_new ();
294 295
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), foo->task_menu);
	/*g_message ("setting...");*/
296 297 298 299 300
	g_object_set_data (G_OBJECT (foo->task_menu),
			   "menu_panel", foo->panel);
	g_signal_connect (G_OBJECT (foo->task_menu), "show",
			  G_CALLBACK (panel_make_sure_menu_within_screen),
			  NULL);
301 302 303
	g_signal_connect (G_OBJECT (foo->task_menu), "show",
			  G_CALLBACK (our_gtk_menu_position),
			  NULL);
304 305 306
}

static void
307
focus_window (GtkWidget *w, WnckWindow *window)
308
{
309 310 311
	WnckScreen *screen = wnck_screen_get (0 /* FIXME screen number */);
	WnckWorkspace *wspace = wnck_screen_get_active_workspace (screen);

312 313
	if (wspace != NULL)
		wnck_window_move_to_workspace (window, wspace);
314 315 316 317 318
	if (wnck_window_is_minimized (window)) 
		wnck_window_unminimize (window);
	wnck_window_activate (window);
}

319
/* No need to unref, in fact do NOT unref the return */
320 321 322 323 324 325
static GdkPixbuf *
get_default_image (void)
{
	static GdkPixbuf *pixbuf = NULL;
	static gboolean looked   = FALSE;

326 327 328 329 330 331
	if ( ! looked) {
		pixbuf = panel_make_menu_icon ("gnome-tasklist.png",
					       /* evil fallback huh? */
					       "apple-red.png",
					       20 /* size */,
					       NULL /* long_operation */);
332 333 334 335 336

		looked = TRUE;
	}

	return pixbuf;
337 338 339
}

static void
340
add_window (WnckWindow *window, FoobarWidget *foo)
341 342 343 344
{
	GtkWidget *item, *label;
	char *title = NULL;
	int slen;
Martin Baulig's avatar
Martin Baulig committed
345
	GtkWidget *image = NULL;
346 347 348 349
	GdkPixbuf *pb;
	const char *name;
	WnckScreen *screen = wnck_screen_get (0 /* FIXME screen number */);
	WnckWorkspace *wspace = wnck_screen_get_active_workspace (screen);
350

351
	g_assert (foo->windows != NULL);
352

353
	if (wnck_window_is_skip_tasklist (window))
354
		return;
355 356 357 358 359

	name = wnck_window_get_name (window);

	if (name != NULL) {
		slen = strlen (name);
360
		if (slen > 443)
361
			title = g_strdup_printf ("%.420s...%s", name, name + slen - 20);
362
		else
363
			title = g_strdup (name);
364 365 366 367 368 369
	} else {
		/* Translators: Task with no name, should not really happen, so
		 * this should signal that the panel is confused by this task
		 * (thus question marks) */
		title = g_strdup (_("???"));
	}
370

371
	if (wnck_window_is_minimized (window)) {
372
		char *tmp = title;
373 374
		title = g_strdup_printf ("[%s]", title);
		g_free (tmp);
375 376
	}
	
Martin Baulig's avatar
Martin Baulig committed
377
	item = gtk_image_menu_item_new ();
378 379 380 381
	pb = wnck_window_get_mini_icon (window);
	if (pb == NULL)
		pb = get_default_image ();
	if (pb != NULL) {
382 383 384 385 386
		double pix_x, pix_y;
		pix_x = gdk_pixbuf_get_width (pb);
		pix_y = gdk_pixbuf_get_height (pb);
		if (pix_x > ICON_SIZE || pix_y > ICON_SIZE) {
			double greatest;
387
			GdkPixbuf *scaled;
388 389

			greatest = pix_x > pix_y ? pix_x : pix_y;
390 391 392 393 394
			scaled = gdk_pixbuf_scale_simple (pb,
							  (ICON_SIZE / greatest) * pix_x,
							  (ICON_SIZE / greatest) * pix_y,
							  GDK_INTERP_BILINEAR);
			image = gtk_image_new_from_pixbuf (scaled);
395
			g_object_unref (G_OBJECT (scaled));
396 397
		} else {
			image = gtk_image_new_from_pixbuf (pb);
398
		}
399
		gtk_widget_show (image);
Martin Baulig's avatar
Martin Baulig committed
400 401
		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
					       GTK_WIDGET (image));
402 403
	}

404
	label = gtk_label_new (title);
405 406 407 408
	g_free (title);

	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
	gtk_container_add (GTK_CONTAINER (item), label);
409
	g_hash_table_insert (foo->windows, window, item);
410
	g_signal_connect (G_OBJECT (item), "activate", 
411 412
			  G_CALLBACK (focus_window),
			  window);
413
	gtk_widget_show_all (item);
414
	
415
	if (wspace == wnck_window_get_workspace (window)) {
Martin Baulig's avatar
Martin Baulig committed
416
		gtk_menu_shell_prepend (GTK_MENU_SHELL (foo->task_menu), item);
417
	} else {
Martin Baulig's avatar
Martin Baulig committed
418
		gtk_menu_shell_append (GTK_MENU_SHELL (foo->task_menu), item);
419
	}
420 421 422 423 424 425
}

static void
create_task_menu (GtkWidget *w, gpointer data)
{
	FoobarWidget *foo = FOOBAR_WIDGET (data);
426 427
	GList *list;
	GtkWidget *separator;
428 429
	WnckScreen *screen = wnck_screen_get (0 /* FIXME screen number */);
	GList *windows = wnck_screen_get_windows (screen);
430

431
	/* g_message ("creating..."); */
432
	foo->windows = g_hash_table_new (g_direct_hash, g_direct_equal);
433

434
	separator = add_menu_separator (foo->task_menu);
435

436
	g_list_foreach (windows, (GFunc)add_window, foo);
437

438 439 440 441 442 443 444 445 446
	list = g_list_last (GTK_MENU_SHELL (foo->task_menu)->children);

	if (list != NULL &&
	    separator == list->data) {
		/* if the separator is the last item wipe it.
		 * We leave it as the first though */
		gtk_widget_destroy (separator);
	}

447 448 449 450 451 452 453 454 455 456 457 458 459
	list = g_list_last (GTK_MENU_SHELL (foo->task_menu)->children);
	if (list == NULL) {
		GtkWidget *item;
		item = gtk_image_menu_item_new_with_label (_("No windows open"));
		gtk_widget_set_sensitive (item, FALSE);
		gtk_widget_show_all (item);
	
		gtk_menu_shell_append (GTK_MENU_SHELL (foo->task_menu), item);
	}

	if (GTK_WIDGET_VISIBLE (foo->task_menu))
		our_gtk_menu_position (GTK_MENU (foo->task_menu));

460
	/* Owen: don't read the next line */
461 462 463 464
#if 0
	GTK_MENU_SHELL (foo->task_menu)->active = 1;
	our_gtk_menu_position (GTK_MENU (foo->task_menu));
#endif
465 466 467 468 469 470 471 472
}

static void
destroy_task_menu (GtkWidget *w, gpointer data)
{
	FoobarWidget *foo = FOOBAR_WIDGET (data);
	/*g_message ("removing...");*/
	gtk_menu_item_remove_submenu (GTK_MENU_ITEM (w));
473 474
	g_hash_table_destroy (foo->windows);
	foo->windows = NULL;
475 476
	set_the_task_submenu (foo, w);
}
477 478

static void
479
set_das_pixmap (FoobarWidget *foo, WnckWindow *window)
480
{
481 482
	GdkPixbuf *pb;
	if ( ! GTK_WIDGET_REALIZED (foo))
483 484
		return;

Martin Baulig's avatar
Martin Baulig committed
485 486 487
	if (foo->task_image != NULL)
		gtk_widget_destroy (GTK_WIDGET (foo->task_image));
	foo->task_image = NULL;
488

489 490 491 492 493 494 495
	foo->icon_window = window;

	pb = NULL;
	if (window != NULL)
		pb = wnck_window_get_mini_icon (window);
	if (pb == NULL)
		pb = get_default_image ();
496

497
	if (pb != NULL) {
498 499 500 501 502
		double pix_x, pix_y;
		pix_x = gdk_pixbuf_get_width (pb);
		pix_y = gdk_pixbuf_get_height (pb);
		if (pix_x > ICON_SIZE || pix_y > ICON_SIZE) {
			double greatest;
503
			GdkPixbuf *scaled;
504 505

			greatest = pix_x > pix_y ? pix_x : pix_y;
506 507 508 509 510
			scaled = gdk_pixbuf_scale_simple (pb,
							  (ICON_SIZE / greatest) * pix_x,
							  (ICON_SIZE / greatest) * pix_y,
							  GDK_INTERP_BILINEAR);
			foo->task_image = gtk_image_new_from_pixbuf (scaled);
511
			g_object_unref (G_OBJECT (scaled));
512 513
		} else {
			foo->task_image = gtk_image_new_from_pixbuf (pb);
514
		}
515
		gtk_widget_show (foo->task_image);
516 517

		gtk_container_add (GTK_CONTAINER (foo->task_bin),
Martin Baulig's avatar
Martin Baulig committed
518
				   GTK_WIDGET (foo->task_image));
519 520 521 522
	}
}

static void
523
append_task_menu (FoobarWidget *foo, GtkMenuShell *menu_bar)
524
{
525
	foo->task_item = gtk_menu_item_new ();
526
	gtk_widget_show (foo->task_item);
527 528

	foo->task_bin = gtk_alignment_new (0.3, 0.5, 0.0, 0.0);
529
	gtk_widget_set_size_request (foo->task_bin, 25, 20);
530 531 532
	gtk_widget_show (foo->task_bin);
	gtk_container_add (GTK_CONTAINER (foo->task_item), foo->task_bin);

533
	gtk_menu_shell_append (menu_bar, foo->task_item);
534

535 536
	panel_stretch_events_to_toplevel (
		foo->task_item, PANEL_STRETCH_TOP | PANEL_STRETCH_RIGHT);
537 538 539 540 541
}

static void
icon_changed (WnckWindow *window, FoobarWidget *foo)
{
542 543
	if (foo->icon_window == window)
		set_das_pixmap (foo, window);
544 545 546 547 548
}

static void
bind_window_changes (WnckWindow *window, FoobarWidget *foo)
{
549 550 551 552
	panel_signal_connect_while_alive (G_OBJECT (window), "icon_changed",
					  G_CALLBACK (icon_changed),
					  foo,
					  G_OBJECT (foo));
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
	/* XXX: do we care about names changing? */
}

/* focused window changed */
static void
active_window_changed (WnckScreen *screen,
		       FoobarWidget *foo)
{
	WnckWindow *window = wnck_screen_get_active_window (screen);

	/* icon might have changed */
	if (foo->icon_window != window)
		set_das_pixmap (foo, window);
}

/* window added */
static void
window_opened (WnckScreen *screen,
	       WnckWindow *window,
	       FoobarWidget *foo)
{
	if (foo->windows != NULL)
		add_window (window, foo);
	bind_window_changes (window, foo);
}
/* window removed */
static void
window_closed (WnckScreen *screen,
	       WnckWindow *window,
	       FoobarWidget *foo)
{
	if (window == foo->icon_window)
		set_das_pixmap (foo, NULL);
	if (foo->windows != NULL) {
		GtkWidget *item;
		item = g_hash_table_lookup (foo->windows, window);
		if (item != NULL) {
			g_hash_table_remove (foo->windows, window);
			gtk_widget_hide (item);
592
			gtk_menu_reposition (GTK_MENU (item->parent));
593 594 595 596 597
		} else {
			g_warning ("Could not find item for task '%s'",
				   sure_string (wnck_window_get_name (window)));
		}
	}
598 599 600 601 602
}

static void
setup_task_menu (FoobarWidget *foo)
{
603
	GList *windows, *li;
604
	WnckScreen *screen;
605 606
	g_assert (foo->task_item != NULL);

607 608 609 610
	g_signal_connect (G_OBJECT (foo->task_item), "select",
			  G_CALLBACK (create_task_menu), foo);
	g_signal_connect (G_OBJECT (foo->task_item), "deselect",
			  G_CALLBACK (destroy_task_menu), foo);
611 612 613

	set_the_task_submenu (foo, foo->task_item);

614 615
	screen = wnck_screen_get (0 /* FIXME screen number */);

616
	/* setup the pixmap to the focused task */
617
	windows = wnck_screen_get_windows (screen);
618 619 620
	for (li = windows; li != NULL; li = li->next) {
		if (wnck_window_is_active (li->data)) {
			set_das_pixmap  (foo, li->data);
621 622 623
			break;
		}
	}
624

625
	/* if no focused task found, then just set it to default */
626
	if (li == NULL)
627 628
		set_das_pixmap  (foo, NULL);

629 630
	g_list_foreach (windows, (GFunc)bind_window_changes, foo);

631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
	panel_signal_connect_while_alive (G_OBJECT (screen),
					  "active_window_changed",
					  G_CALLBACK (active_window_changed),
					  foo,
					  G_OBJECT (foo));
	panel_signal_connect_while_alive (G_OBJECT (screen),
					  "window_opened",
					  G_CALLBACK (window_opened),
					  foo,
					  G_OBJECT (foo));
	panel_signal_connect_while_alive (G_OBJECT (screen),
					  "window_closed",
					  G_CALLBACK (window_closed),
					  foo,
					  G_OBJECT (foo));
646

647 648
}

649
static void
650
foobar_widget_instance_init (FoobarWidget *foo)
651
{
652
	char *path;
653
	GtkWindow *window = GTK_WINDOW (foo);
654
	GtkWidget *bufmap;
655
	GtkWidget *menu_bar, *bar;
656
	GtkWidget *menu, *menuitem;
657
	GtkWidget *align;
658
	gint flags;
659

660 661
	foo->screen = 0;

662
	foo->windows    = NULL;
663 664
	foo->task_item  = NULL;
	foo->task_menu  = NULL;
Martin Baulig's avatar
Martin Baulig committed
665
	foo->task_image = NULL;
666
	foo->task_bin   = NULL;
667
	foo->icon_window = NULL;
668

669
	foo->compliant_wm = xstuff_is_compliant_wm ();
670 671 672 673
	if(foo->compliant_wm)
		GTK_WINDOW(foo)->type = GTK_WINDOW_TOPLEVEL;
	else
		GTK_WINDOW(foo)->type = GTK_WINDOW_POPUP;
674 675 676 677

	window->allow_shrink = TRUE;
	window->allow_grow   = TRUE;

678 679
	g_signal_connect (G_OBJECT (foo), "delete_event",
			  G_CALLBACK (gtk_true), NULL);
680

681 682 683
	gtk_window_move (GTK_WINDOW (foo),
			 multiscreen_x (foo->screen),
			 multiscreen_y (foo->screen));
684 685 686
	g_object_set (G_OBJECT (foo),
		      "width_request", (int)multiscreen_width (foo->screen),
		      NULL);
687

688
	foo->ebox = gtk_event_box_new ();
689
	foo->hbox = gtk_hbox_new (FALSE, 0);
690
	gtk_container_add(GTK_CONTAINER(foo->ebox), foo->hbox);
691

692 693 694 695 696 697 698 699
	path = panel_pixmap_discovery ("panel-corner-left.png", FALSE /* fallback */);
	if (path != NULL) {
		bufmap = gtk_image_new_from_file (path);
		g_free (path);
		align = gtk_alignment_new (0.0, 0.0, 1.0, 0.0);
		gtk_container_add (GTK_CONTAINER (align), bufmap);
		gtk_box_pack_start (GTK_BOX (foo->hbox), align, FALSE, FALSE, 0);
	}
700 701

	menu_bar = gtk_menu_bar_new ();	
702 703
	gtk_widget_set_name (menu_bar,
			     "panel-foobar-menubar");
704
	
705
	menuitem = pixmap_menu_item_new (_("Applications"),
706 707
					 "gnome-logo-icon-transparent.png",
					 TRUE /* force_image */);
708 709 710 711 712 713
	flags = MAIN_MENU_SYSTEM;
	if (got_kde_menus ())
		flags |= MAIN_MENU_KDE_SUB;
	if (got_distro_menus ())
		flags |= MAIN_MENU_DISTRIBUTION_SUB;

714
	menu = create_root_menu (NULL, TRUE, flags, FALSE, FALSE /* run_item */);
715
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
716
	gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), menuitem);
717 718 719
	g_signal_connect (G_OBJECT (menu), "show",
			  G_CALLBACK (programs_menu_to_display),
			  NULL);
720 721
	foo->programs = menu;

722
	/* Strech the applications menu to the corner */
723 724
	panel_stretch_events_to_toplevel (
		menuitem, PANEL_STRETCH_TOP | PANEL_STRETCH_LEFT);
725 726

	append_actions_menu (menu_bar);
727

728 729 730 731
	gtk_box_pack_start (GTK_BOX (foo->hbox), menu_bar, FALSE, FALSE, 0);
	
	
	/* panel widget */
732
	foo->panel = panel_widget_new (NULL, FALSE, GTK_ORIENTATION_HORIZONTAL,
Mark McLoughlin's avatar
Mark McLoughlin committed
733
				       PANEL_SIZE_X_SMALL, PANEL_BACK_NONE,
734
				       NULL, FALSE, FALSE, FALSE, NULL);
735 736 737 738 739
	PANEL_WIDGET (foo->panel)->panel_parent = GTK_WIDGET (foo);
	PANEL_WIDGET (foo->panel)->drop_widget = GTK_WIDGET (foo);

	gtk_container_add (GTK_CONTAINER (foo->hbox), foo->panel);

740
	g_object_set_data (G_OBJECT (menu_bar), "menu_panel", foo->panel);
741

742 743 744 745 746 747 748 749
	path = panel_pixmap_discovery ("panel-corner-right.png", FALSE /* fallback */);
	if (path != NULL) {
		bufmap = gtk_image_new_from_file (path);
		g_free (path);
		align = gtk_alignment_new (1.0, 0.0, 1.0, 0.0);
		gtk_container_add (GTK_CONTAINER (align), bufmap);
		gtk_box_pack_end (GTK_BOX (foo->hbox), align, FALSE, FALSE, 0);
	}
750

751
	bar = menu_bar = gtk_menu_bar_new ();
752 753
	gtk_widget_set_name (menu_bar,
			     "panel-foobar-menubar");
754

755
	append_task_menu (foo, GTK_MENU_SHELL (bar));
756 757 758


	gtk_box_pack_end (GTK_BOX (foo->hbox), menu_bar, FALSE, FALSE, 0);
759 760
	gtk_container_add (GTK_CONTAINER (foo), foo->ebox);
	gtk_widget_show_all (foo->ebox);
761 762 763
}

static void
764
queue_panel_resize (gpointer data, gpointer user_data)
765 766 767 768 769 770 771 772 773 774
{
	PanelData *pd = data;
	GtkWidget *panel;

	g_assert (pd);

	panel = pd->panel;

	g_return_if_fail (GTK_IS_WIDGET (panel));

775
	if (!DRAWER_IS_WIDGET (panel) && !FOOBAR_IS_WIDGET (panel))
776 777 778
		gtk_widget_queue_resize (panel);
}

779
static void
780
foobar_widget_destroy (GtkObject *o)
781
{
782 783
	FoobarWidget *foo = FOOBAR_WIDGET (o);

784
	foobars = g_list_remove (foobars, foo);
785

786
	g_slist_foreach (panel_list, queue_panel_resize, NULL);
787

788 789 790
	if (foo->windows != NULL)
		g_hash_table_destroy (foo->windows);
	foo->windows = NULL;
791

792 793
	if (GTK_OBJECT_CLASS (foobar_widget_parent_class)->destroy)
		GTK_OBJECT_CLASS (foobar_widget_parent_class)->destroy (o);
794 795
}

796
static void
797
foobar_widget_size_allocate (GtkWidget *w, GtkAllocation *alloc)
798
{
799 800
	if (GTK_WIDGET_CLASS (foobar_widget_parent_class)->size_allocate)
		GTK_WIDGET_CLASS (foobar_widget_parent_class)->size_allocate (w, alloc);
801

802
	if (GTK_WIDGET_REALIZED (w)) {
803 804 805 806 807 808 809
		FoobarWidget *foo = FOOBAR_WIDGET (w);
		xstuff_set_pos_size (w->window,
				     multiscreen_x (foo->screen),
				     multiscreen_y (foo->screen),
				     alloc->width,
				     alloc->height);

810
		g_slist_foreach (panel_list, queue_panel_resize, NULL);
811
		basep_border_queue_recalc (foo->screen);
812 813 814 815 816 817

		xstuff_set_wmspec_strut (w->window,
					 0 /* left */,
					 0 /* right */,
					 alloc->height /* top */,
					 0 /* bottom */);
818
	}
819 820
}

821
GtkWidget *
822
foobar_widget_new (const char *panel_id, int screen)
823
{
824 825
	FoobarWidget *foo;

826
	g_return_val_if_fail (screen >=0, NULL);
827 828 829

	if (foobar_widget_exists (screen))
		return NULL;
830

831
	foo = g_object_new (FOOBAR_TYPE_WIDGET, NULL);
832

833
	if (panel_id)
834
		panel_widget_set_id (PANEL_WIDGET (foo->panel), panel_id);
835

836
	foo->screen = screen;
837 838 839
	gtk_window_move (GTK_WINDOW (foo),
			 multiscreen_x (foo->screen),
			 multiscreen_y (foo->screen));
840 841 842
	g_object_set (G_OBJECT (foo),
		      "width_request", (int)multiscreen_width (foo->screen),
		      NULL);
843

844
	foobars = g_list_prepend (foobars, foo);
845

846
	return GTK_WIDGET (foo);
847 848 849
}

gboolean
850
foobar_widget_exists (int screen)
851
{
852
	GList *li;
853

854 855 856 857 858 859 860
	for (li = foobars; li != NULL; li = li->next) {
		FoobarWidget *foo = li->data;

		if (foo->screen == screen)
			return TRUE;
	}
	return FALSE;
861 862
}

863 864 865 866
void
foobar_widget_force_menu_remake (void)
{
	FoobarWidget *foo;
867
	GList *li;
868

869 870
	for (li = foobars; li != NULL; li = li->next) {
		foo = FOOBAR_WIDGET(li->data);
871 872

		if (foo->programs != NULL)
873 874 875
			g_object_set_data (G_OBJECT (foo->programs),
					   "need_reread",
					   GINT_TO_POINTER (TRUE));
876
	}
877 878
}

879
gint
880
foobar_widget_get_height (int screen)
881
{
882
	GList *li;
883

884
	g_return_val_if_fail (screen >= 0, 0);
885

886 887 888 889 890 891 892
	for (li = foobars; li != NULL; li = li->next) {
		FoobarWidget *foo = FOOBAR_WIDGET(li->data);

		if (foo->screen == screen)
			return GTK_WIDGET (foo)->allocation.height;
	}
	return 0; 
893
}
894 895 896 897 898

static void
reparent_button_widgets(GtkWidget *w, gpointer data)
{
	GdkWindow *newwin = data;
899
	if (BUTTON_IS_WIDGET (w)) {
900
		GtkButton *button = GTK_BUTTON (w);
901 902 903
		/* we can just reparent them all to 0,0 as the next thing
		 * that will happen is a queue_resize and on size allocate
		 * they will be put into their proper place */
904
		gdk_window_reparent (button->event_window, newwin, 0, 0);
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
	}
}

void
foobar_widget_redo_window(FoobarWidget *foo)
{
	GtkWindow *window;
	GtkWidget *widget;
	GdkWindowAttr attributes;
	gint attributes_mask;
	GdkWindow *oldwin;
	GdkWindow *newwin;
	gboolean comp;

	comp = xstuff_is_compliant_wm();
920
	if (comp == foo->compliant_wm)
921 922 923 924 925 926
		return;

	window = GTK_WINDOW(foo);
	widget = GTK_WIDGET(foo);

	foo->compliant_wm = comp;
927
	if (foo->compliant_wm) {
928 929 930 931 932 933 934
		window->type = GTK_WINDOW_TOPLEVEL;
		attributes.window_type = GDK_WINDOW_TOPLEVEL;
	} else {
		window->type = GTK_WINDOW_POPUP;
		attributes.window_type = GDK_WINDOW_TEMP;
	}

935
	if (widget->window == NULL)
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
		return;

	/* this is mostly copied from gtkwindow.c realize method */
	attributes.title = window->title;
	attributes.wmclass_name = window->wmclass_name;
	attributes.wmclass_class = window->wmclass_class;
	attributes.width = widget->allocation.width;
	attributes.height = widget->allocation.height;
	attributes.wclass = GDK_INPUT_OUTPUT;
	attributes.visual = gtk_widget_get_visual (widget);
	attributes.colormap = gtk_widget_get_colormap (widget);
	attributes.event_mask = gtk_widget_get_events (widget);
	attributes.event_mask |= (GDK_EXPOSURE_MASK |
				  GDK_KEY_PRESS_MASK |
				  GDK_ENTER_NOTIFY_MASK |
				  GDK_LEAVE_NOTIFY_MASK |
				  GDK_FOCUS_CHANGE_MASK |
				  GDK_STRUCTURE_MASK);

	attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
	attributes_mask |= (window->title ? GDK_WA_TITLE : 0);
	attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0);
   
	oldwin = widget->window;

	newwin = gdk_window_new(NULL, &attributes, attributes_mask);
	gdk_window_set_user_data(newwin, window);

964
	xstuff_set_no_group_and_no_input (newwin);
965

966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
	/* reparent our main panel window */
	gdk_window_reparent(foo->ebox->window, newwin, 0, 0);
	/* reparent all the base event windows as they are also children of
	 * the foobar */
	gtk_container_foreach(GTK_CONTAINER(foo->panel),
			      reparent_button_widgets,
			      newwin);


	widget->window = newwin;

	gdk_window_set_user_data(oldwin, NULL);
	gdk_window_destroy(oldwin);

	widget->style = gtk_style_attach(widget->style, widget->window);
	gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);

	GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);

	gtk_widget_queue_resize(widget);

	foobar_widget_update_winhints (foo);

989 990 991 992 993 994
	xstuff_set_wmspec_strut (widget->window,
				 0 /* left */,
				 0 /* right */,
				 widget->allocation.height /* top */,
				 0 /* bottom */);

995 996 997 998
	gtk_drag_dest_set (widget, 0, NULL, 0, 0);

	gtk_widget_map(widget);
}
999 1000

static void
1001
foobar_widget_move_focus_out (FoobarWidget *foobar)
1002 1003 1004 1005 1006
{
 	panel_widget_focus (PANEL_WIDGET (foobar->panel));
}