view pidgin/plugins/win32/winprefs/gtkappbar.c @ 31534:a8cc50c2279f

Remove trailing whitespace
author Richard Laager <rlaager@wiktel.com>
date Tue, 04 Jan 2011 06:55:30 +0000
parents 8998ca47e6d0
children
line wrap: on
line source

/*
 * purple - WinPurple Options Plugin
 *
 * File: gtkappbar.c
 * Date: August 2, 2003
 * Description: Appbar functionality for Windows GTK+ applications
 *
 * Copyright (C) 2003, Herman Bloggs <hermanator12002@yahoo.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 *
 */
/*
 *  TODO:
 *  - Move 'App on top' feature from Trans plugin to here
 *  - Bug: Multiple Show/Hide Desktop calls causes client area to disappear
 */
#include <windows.h>
#include <winver.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk/gdkwin32.h>
#include "gtkappbar.h"
#include "debug.h"

#define APPBAR_CALLBACK 	WM_USER + 1010

typedef HMONITOR WINAPI purple_MonitorFromPoint(POINT, DWORD);
typedef HMONITOR WINAPI purple_MonitorFromWindow(HWND, DWORD);
typedef BOOL WINAPI purple_GetMonitorInfo(HMONITOR, LPMONITORINFO);

static void gtk_appbar_do_dock(GtkAppBar *ab, UINT side);

/* Retrieve the rectangular display area from the specified monitor
 * Return TRUE if successful, otherwise FALSE
 */
static gboolean
get_rect_from_monitor(HMODULE hmod, HMONITOR monitor, RECT *rect) {
	purple_GetMonitorInfo *the_GetMonitorInfo;
	MONITORINFO info;

	if (!(the_GetMonitorInfo = (purple_GetMonitorInfo*)
		GetProcAddress(hmod, "GetMonitorInfoA"))) {
		return FALSE;
	}

	info.cbSize = sizeof(info);
	if (!the_GetMonitorInfo(monitor, &info)) {
		return FALSE;
	}

	CopyRect(rect, &(info.rcMonitor));

	return TRUE;
}

/**
 * This will only work on Win98+ and Win2K+
 * Return TRUE if successful, otherwise FALSE
 */
static gboolean
get_rect_at_point_multimonitor(POINT pt, RECT *rect) {
	HMODULE hmod;
	purple_MonitorFromPoint *the_MonitorFromPoint;
	HMONITOR monitor;

	if (!(hmod = GetModuleHandle("user32"))) {
		return FALSE;
	}

	if (!(the_MonitorFromPoint = (purple_MonitorFromPoint*)
		GetProcAddress(hmod, "MonitorFromPoint"))) {
		return FALSE;
	}

	monitor =
		the_MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);

	return get_rect_from_monitor(hmod, monitor, rect);
}

/**
 * This will only work on Win98+ and Win2K+
 * Return TRUE if successful, otherwise FALSE
 */
static gboolean
get_rect_of_window_multimonitor(HWND window, RECT *rect) {
	HMODULE hmod;
	purple_MonitorFromWindow *the_MonitorFromWindow;
	HMONITOR monitor;

	if (!(hmod = GetModuleHandle("user32"))) {
		return FALSE;
	}

	if (!(the_MonitorFromWindow = (purple_MonitorFromWindow*)
		GetProcAddress(hmod, "MonitorFromWindow"))) {
		return FALSE;
	}

	monitor =
		the_MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);

	return get_rect_from_monitor(hmod, monitor, rect);
}

/*
 * Fallback if cannot get the RECT from the monitor directly
 */
static void get_default_workarea(RECT *rect) {
	if (!SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, FALSE)) {
		/* I don't think this will ever happen */
		rect->left = 0;
		rect->top = 0;
		rect->bottom = GetSystemMetrics(SM_CYSCREEN);
		rect->right = GetSystemMetrics(SM_CXSCREEN);
	}
}

/* Retrieve the rectangle of the active work area at a point */
static void get_rect_at_point(POINT pt, RECT *rc) {
	if (!get_rect_at_point_multimonitor(pt, rc)) {
		get_default_workarea(rc);
	}
}

/* Retrieve the rectangle of the active work area of a window*/
static void get_rect_of_window(HWND window, RECT *rc) {
	if (!get_rect_of_window_multimonitor(window, rc)) {
		get_default_workarea(rc);
	}
}

static void get_window_normal_rc(HWND hwnd, RECT *rc) {
         WINDOWPLACEMENT wplc;
         GetWindowPlacement(hwnd, &wplc);
         CopyRect(rc, &wplc.rcNormalPosition);
}
#if 0
static void print_rect(RECT *rc) {
        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "RECT: L:%ld R:%ld T:%ld B:%ld\n",
                   rc->left, rc->right, rc->top, rc->bottom);
}
#endif
/** Set the window style to be the "Tool Window" style - small header, no min/max buttons */
static void set_toolbar(HWND hwnd, gboolean val) {
	LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);

        if(val && !(style & WS_EX_TOOLWINDOW))
                style |= WS_EX_TOOLWINDOW;
        else if(!val && style & WS_EX_TOOLWINDOW)
                style &= ~WS_EX_TOOLWINDOW;
        else
                return;
        SetWindowLong(hwnd, GWL_EXSTYLE, style);
	SetWindowPos(hwnd, 0, 0, 0, 0, 0,
		SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

/*	This really should be the following, but SWP_FRAMECHANGED strangely causes initermittent problems "Show Desktop" done more than once.
 *	Not having SWP_FRAMECHANGED *should* cause the Style not to be applied, but i haven't noticed any problems
 *			SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
 */
}
/** Register the window as an appbar */
static gboolean gtk_appbar_register(GtkAppBar *ab, HWND hwnd) {
	APPBARDATA abd;

	abd.cbSize = sizeof(APPBARDATA);
	abd.hWnd = hwnd;
	abd.uCallbackMessage = APPBAR_CALLBACK;

	ab->registered = SHAppBarMessage(ABM_NEW, &abd);

	return ab->registered;
}
/** Unregister the window as an appbar */
static gboolean gtk_appbar_unregister(GtkAppBar *ab, HWND hwnd) {
	APPBARDATA abd;

        if(!ab->registered)
                return TRUE;

	abd.cbSize = sizeof(APPBARDATA);
	abd.hWnd = hwnd;

	SHAppBarMessage(ABM_REMOVE, &abd); /** This always returns TRUE */

	ab->registered = FALSE;

	ab->docked = FALSE;
	ab->undocking = FALSE;
	ab->docking = FALSE;

	return TRUE;
}

static void gtk_appbar_querypos(GtkAppBar *ab, HWND hwnd, RECT rcWorkspace) {
	APPBARDATA abd;
	guint iWidth = 0;

	if(!ab->registered)
		gtk_appbar_register(ab, hwnd);

	abd.hWnd = hwnd;
	abd.cbSize = sizeof(APPBARDATA);
	abd.uEdge = ab->side;

	iWidth = ab->docked_rect.right - ab->docked_rect.left;

	abd.rc.top = rcWorkspace.top;
	abd.rc.bottom = rcWorkspace.bottom;
	switch (abd.uEdge)
	{
		case ABE_LEFT:
			abd.rc.left = rcWorkspace.left;
			abd.rc.right = rcWorkspace.left + iWidth;
			break;

		case ABE_RIGHT:
			abd.rc.right = rcWorkspace.right;
			abd.rc.left = rcWorkspace.right - iWidth;
			break;
	}

        /* Ask the system for the screen space */
	SHAppBarMessage(ABM_QUERYPOS, &abd);

	switch (abd.uEdge)
	{
		case ABE_LEFT:
                        abd.rc.right = abd.rc.left + iWidth;
			break;

		case ABE_RIGHT:
			abd.rc.left = abd.rc.right - iWidth;
			break;
	}

	CopyRect(&(ab->docked_rect), &abd.rc);
}
/* Actually set the size and screen location of the appbar */
static void gtk_appbar_setpos(GtkAppBar *ab, HWND hwnd) {
        APPBARDATA abd;

        if(!ab->registered)
                gtk_appbar_register(ab, hwnd);

	abd.hWnd = hwnd;
	abd.cbSize = sizeof(APPBARDATA);
        CopyRect(&abd.rc, &(ab->docked_rect));
	abd.uEdge = ab->side;

	SHAppBarMessage(ABM_SETPOS, &abd);
}
/** Let any callbacks know that we have docked or undocked */
static void gtk_appbar_dispatch_dock_cbs(GtkAppBar *ab, gboolean val) {
        GSList *lst = ab->dock_cbs;

        while(lst) {
                GtkAppBarDockCB dock_cb = lst->data;
                dock_cb(val);
                lst = lst->next;
        }
}

static GdkFilterReturn wnd_moving(GtkAppBar *ab, GdkXEvent *xevent) {
        MSG *msg = (MSG*)xevent;
        POINT cp;
        RECT *rc = (RECT*)msg->lParam;
	RECT monRect;
        int side = -1;
	long dockAreaWidth = 0;

        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "wnd_moving\n");

        GetCursorPos(&cp);
	get_rect_at_point(cp, &monRect);

	dockAreaWidth = (monRect.right - monRect.left) / 10;
        /* Which part of the screen are we in ? */
	if (cp.x > (monRect.right - dockAreaWidth)) {
                side = ABE_RIGHT;
	} else if (cp.x < (monRect.left + dockAreaWidth)) {
                side = ABE_LEFT;
	}

        if(!ab->docked) {
                if( (side == ABE_RIGHT || side == ABE_LEFT) ) {
                        if( !ab->docking ) {
                                ab->side = side;
                                GetWindowRect(msg->hwnd, &(ab->docked_rect));
				gtk_appbar_querypos(ab, msg->hwnd, monRect);

                                /* save pre-docking height */
                                ab->undocked_height = rc->bottom - rc->top;
                                ab->docking = TRUE;
                        }
                }
                else
                        ab->docking = FALSE;
        }
        else if(side < 0) {
                gtk_appbar_unregister(ab, msg->hwnd);
		ab->undocking = TRUE;
                rc->bottom = rc->top + ab->undocked_height;
        }

        return GDK_FILTER_CONTINUE;
}

static GdkFilterReturn wnd_sizing(GtkAppBar *ab, GdkXEvent *xevent) {
        MSG *msg = (MSG*)xevent;

        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "wnd_sizing\n");
        if(ab->docked) {
                RECT *rc = (RECT*)msg->lParam;
                if(ab->side == ABE_LEFT && msg->wParam == WMSZ_RIGHT) {
                        ab->docked_rect.right = rc->right;
                        gtk_appbar_setpos(ab, msg->hwnd);
                }
                else if(ab->side == ABE_RIGHT && msg->wParam == WMSZ_LEFT) {
                        ab->docked_rect.left = rc->left;
                        gtk_appbar_setpos(ab, msg->hwnd);
                }
                return GDK_FILTER_REMOVE;
        }
        return GDK_FILTER_CONTINUE;
}
/** Notify the system that the appbar has been activated */
static GdkFilterReturn wnd_activate(GtkAppBar *ab, GdkXEvent *xevent) {
	if (ab->registered) {
		APPBARDATA abd;
		MSG *msg = (MSG*)xevent;
		purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "wnd_activate\n");

		abd.hWnd = msg->hwnd;
		abd.cbSize = sizeof(APPBARDATA);

		SHAppBarMessage(ABM_ACTIVATE, &abd);
	}
	return GDK_FILTER_CONTINUE;
}

static void show_hide(GtkAppBar *ab, gboolean hide) {
	purple_debug_info("gtkappbar", "show_hide(%d)\n", hide);

	if (hide) {
		purple_debug_info("gtkappbar", "hidden\n");
		gtk_appbar_unregister(ab, GDK_WINDOW_HWND(ab->win->window));
		ab->docked = TRUE;
		ab->iconized = TRUE;
	} else {
		ab->iconized = FALSE;
		purple_debug_info("gtkappbar", "shown\n");
		ab->docked = FALSE;
		gtk_appbar_do_dock(ab, ab->side);
	}

}

/** Notify the system that the appbar's position has changed */
static GdkFilterReturn wnd_poschanged(GtkAppBar *ab, GdkXEvent *xevent) {
	if (ab->registered) {
		APPBARDATA abd;
		MSG *msg = (MSG*)xevent;

		purple_debug(PURPLE_DEBUG_MISC, "gtkappbar", "wnd_poschanged\n");

		abd.hWnd = msg->hwnd;
		abd.cbSize = sizeof(APPBARDATA);

		SHAppBarMessage(ABM_WINDOWPOSCHANGED, &abd);

	}
	return GDK_FILTER_CONTINUE;
}
/** The window is about to change */
static GdkFilterReturn wnd_poschanging(GtkAppBar *ab, GdkXEvent *xevent) {
        MSG *msg = (MSG*)xevent;
        WINDOWPOS *wpos = (WINDOWPOS*)msg->lParam;

        purple_debug(PURPLE_DEBUG_MISC, "gtkappbar", "wnd_poschanging\n");

        if(ab->docked || ab->docking) {
                wpos->x = ab->docked_rect.left;
                wpos->y = ab->docked_rect.top;
                wpos->cx = ab->docked_rect.right - ab->docked_rect.left;
                wpos->cy = ab->docked_rect.bottom - ab->docked_rect.top;
                if(IsIconic(msg->hwnd))
                        set_toolbar(msg->hwnd, FALSE);
                /*return GDK_FILTER_REMOVE;*/
        }

	if (ab->docked) {
		if (ab->iconized && wpos->flags & SWP_SHOWWINDOW)
			show_hide(ab, FALSE);
		else if (!ab->iconized && wpos->flags & SWP_HIDEWINDOW)
			show_hide(ab, TRUE);
	}

        return GDK_FILTER_CONTINUE;
}

static GdkFilterReturn wnd_exitsizemove(GtkAppBar *ab, GdkXEvent *xevent) {
        MSG *msg = (MSG*)xevent;

        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "wnd_exitsizemove\n");
        if(ab->docking) {
		gtk_appbar_setpos(ab, msg->hwnd);
		ab->docking = FALSE;
		ab->docked = TRUE;
		ShowWindow(msg->hwnd, SW_HIDE);
		set_toolbar(msg->hwnd, TRUE);
		ShowWindow(msg->hwnd, SW_SHOW);
		gtk_appbar_dispatch_dock_cbs(ab, TRUE);
	} else if(ab->undocking) {
		ShowWindow(msg->hwnd, SW_HIDE);
		set_toolbar(msg->hwnd, FALSE);
		ShowWindow(msg->hwnd, SW_SHOW);
		gtk_appbar_dispatch_dock_cbs(ab, FALSE);
		ab->undocking = FALSE;
        }

        return GDK_FILTER_CONTINUE;
}

static GdkFilterReturn wnd_showwindow(GtkAppBar *ab, GdkXEvent *xevent) {
	MSG *msg = (MSG*)xevent;

	purple_debug_info("gtkappbar", "wnd_showwindow\n");
	if(msg->wParam && ab->docked) {
		show_hide(ab, FALSE);
	} else if(!msg->wParam && ab->docked) {
		show_hide(ab, TRUE);
	}
	return GDK_FILTER_CONTINUE;
}

/** The window's size has changed */
static GdkFilterReturn wnd_size(GtkAppBar *ab, GdkXEvent *xevent) {
        MSG *msg = (MSG*)xevent;

        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "wnd_size\n");

        if(msg->wParam == SIZE_MINIMIZED) {
                purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "Minimize\n");
                if(ab->docked) {
                        gtk_appbar_unregister(ab, GDK_WINDOW_HWND(ab->win->window));
                        ab->docked = TRUE;
                }
        }
        else if(msg->wParam == SIZE_RESTORED) {
                purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "Restore\n");
		if (!ab->iconized && ab->docked) {
                        gtk_appbar_do_dock(ab, ab->side);
                }
        }
        return GDK_FILTER_CONTINUE;
}

static GdkFilterReturn wnd_nchittest(GtkAppBar *ab, GdkXEvent *xevent) {
        MSG *msg = (MSG*)xevent;

        if(ab->docked) {
                UINT ret = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);

                switch(ret) {
                case HTBOTTOM:
                case HTBOTTOMLEFT:
                case HTBOTTOMRIGHT:
                case HTTOP:
                case HTTOPLEFT:
                case HTTOPRIGHT:
                        return GDK_FILTER_REMOVE;
                case HTLEFT:
                        if(ab->side == ABE_LEFT)
                                return GDK_FILTER_REMOVE;
                        break;
                case HTRIGHT:
                        if(ab->side == ABE_RIGHT)
                                return GDK_FILTER_REMOVE;
                        break;
                }
        }
        return GDK_FILTER_CONTINUE;
}

#if 0
static GdkFilterReturn wnd_initmenupopup(GtkAppBar *ab, GdkXEvent *xevent) {
        MSG *msg = (MSG*)xevent;

        if(ab->docked && HIWORD(msg->lParam)) {
                HMENU sysmenu = GetSystemMenu(msg->hwnd, FALSE);
                purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "wnd_initpopupmenu: docked: %d ismenu: %d\n", ab->docked, IsMenu(sysmenu));
                if(EnableMenuItem(sysmenu, SC_MAXIMIZE, MF_BYCOMMAND|MF_GRAYED)<0)
                        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "SC_MAXIMIZE Menu item does not exist\n");
                if(EnableMenuItem(sysmenu, SC_MOVE, MF_BYCOMMAND|MF_GRAYED)<0)
                        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "SC_MOVE Menu item does not exist\n");
                return GDK_FILTER_CONTINUE;
        }
        else
                GetSystemMenu(msg->hwnd, TRUE);
        return GDK_FILTER_CONTINUE;
}
#endif

static GdkFilterReturn gtk_appbar_callback(GtkAppBar *ab, GdkXEvent *xevent) {
        MSG *msg = (MSG*)xevent;
	RECT orig, windowRect;

        switch (msg->wParam) {
        case ABN_STATECHANGE:
                purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "gtk_appbar_callback: ABN_STATECHANGE\n");
	        break;

        case ABN_FULLSCREENAPP:
                purple_debug(PURPLE_DEBUG_MISC, "gtkappbar", "gtk_appbar_callback: ABN_FULLSCREENAPP: %d\n", (BOOL)msg->lParam);
		if (!ab->iconized && ab->docked) {
		if ((BOOL)msg->lParam) {
			SetWindowPos(msg->hwnd, HWND_BOTTOM, 0, 0, 0, 0,
				SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
		} else {
			SetWindowPos(msg->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
				SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_FRAMECHANGED);
		}
		}

                break;
	case ABN_POSCHANGED:
		purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "gtk_appbar_callback: ABN_POSCHANGED\n");
		CopyRect(&orig, &(ab->docked_rect));
		get_rect_of_window(msg->hwnd, &windowRect);
		gtk_appbar_querypos(ab, msg->hwnd, windowRect);
		if (EqualRect(&orig, &(ab->docked_rect)) == 0) {
			MoveWindow(msg->hwnd, ab->docked_rect.left, ab->docked_rect.top,
				ab->docked_rect.right - ab->docked_rect.left,
				ab->docked_rect.bottom - ab->docked_rect.top, TRUE);
		}
                gtk_appbar_setpos(ab, msg->hwnd);
		break;
#if 0
	default:
		purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "gtk_appbar_callback: %d\n", msg->wParam);
#endif
        }
        return GDK_FILTER_CONTINUE;
}

static GdkFilterReturn gtk_appbar_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) {
	MSG *msg = (MSG*)xevent;

        /*printf("MSG: %s\n", message_to_string (msg->message));*/
        switch(msg->message) {
        case WM_EXITSIZEMOVE:
                return wnd_exitsizemove(data, xevent);
        case WM_WINDOWPOSCHANGING:
                return wnd_poschanging(data, xevent);
	case WM_WINDOWPOSCHANGED:
		return wnd_poschanged(data, xevent);
	case WM_ACTIVATE:
		return wnd_activate(data, xevent);
        case WM_SIZING:
                return wnd_sizing(data, xevent);
        case WM_MOVING:
                return wnd_moving(data, xevent);
        case WM_SHOWWINDOW:
                return wnd_showwindow(data, xevent);
        case WM_NCHITTEST:
                return wnd_nchittest(data, xevent);
#if 0
        case WM_INITMENUPOPUP:
                return wnd_initmenupopup(data, xevent);
#endif
        case WM_SIZE:
                return wnd_size(data, xevent);
        case APPBAR_CALLBACK:
                return gtk_appbar_callback(data, xevent);
#if 0
	default:
		purple_debug_info("gtkappbar", "gtk_appbar_event_filter %d\n", msg->message);
#endif
        }
        return GDK_FILTER_CONTINUE;
}

static void gtk_appbar_do_dock(GtkAppBar *ab, UINT side) {
	RECT orig, windowRect;

        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "gtk_appbar_do_dock\n");

        if(!ab || !IsWindow(GDK_WINDOW_HWND(ab->win->window)))
                return;

        ab->side = side;
        get_window_normal_rc(GDK_WINDOW_HWND(ab->win->window), &(ab->docked_rect));
        CopyRect(&orig, &(ab->docked_rect));
	get_rect_of_window(GDK_WINDOW_HWND(ab->win->window), &windowRect);
	gtk_appbar_querypos(ab, GDK_WINDOW_HWND(ab->win->window), windowRect);
        if(EqualRect(&orig, &(ab->docked_rect)) == 0)
                MoveWindow(GDK_WINDOW_HWND(ab->win->window),
                           ab->docked_rect.left,
                           ab->docked_rect.top,
                           ab->docked_rect.right - ab->docked_rect.left,
                           ab->docked_rect.bottom - ab->docked_rect.top, TRUE);
        gtk_appbar_setpos(ab, GDK_WINDOW_HWND(ab->win->window));
        ab->docked = TRUE;
}

void gtk_appbar_dock(GtkAppBar *ab, UINT side) {
	HWND hwnd;

	g_return_if_fail(ab != NULL);

	hwnd = GDK_WINDOW_HWND(ab->win->window);

	g_return_if_fail(IsWindow(hwnd));

	ab->iconized = IsIconic(hwnd);

	if (!ab->docked && !ab->iconized)
		ShowWindow(hwnd, SW_HIDE);
	gtk_appbar_do_dock(ab, side);
	set_toolbar(hwnd, TRUE);
	if (!ab->iconized)
		ShowWindow(hwnd, SW_SHOW);
}

void gtk_appbar_add_dock_cb(GtkAppBar *ab, GtkAppBarDockCB dock_cb) {
        if(!ab)
                return;
        ab->dock_cbs = g_slist_prepend(ab->dock_cbs, dock_cb);
}

GtkAppBar *gtk_appbar_add(GtkWidget *win) {
        GtkAppBar *ab;

        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "gtk_appbar_add\n");

        if(!win)
                return NULL;
        ab = g_new0(GtkAppBar, 1);
        ab->win = win;

        /* init docking coords */
        get_window_normal_rc(GDK_WINDOW_HWND(win->window), &(ab->docked_rect));

        /* Add main window filter */
	gdk_window_add_filter(win->window,
                              gtk_appbar_event_filter,
                              ab);
        return ab;
}

void gtk_appbar_remove(GtkAppBar *ab) {
	HWND hwnd;
        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "gtk_appbar_remove\n");

        if(!ab)
                return;

	hwnd = GDK_WINDOW_HWND(ab->win->window);
        gdk_window_remove_filter(ab->win->window,
                                 gtk_appbar_event_filter,
                                 ab);
        if(ab->docked) {
                gtk_window_resize(GTK_WINDOW(ab->win),
                                  ab->docked_rect.right - ab->docked_rect.left,
                                  ab->undocked_height);
		if (!ab->iconized)
			ShowWindow(hwnd, SW_HIDE);
		set_toolbar(hwnd, FALSE);
		if (!ab->iconized)
			ShowWindow(hwnd, SW_SHOW);
        }
        gtk_appbar_unregister(ab, hwnd);

	while (ab->dock_cbs)
		ab->dock_cbs = g_slist_remove(ab->dock_cbs, ab->dock_cbs->data);

	g_free(ab);
}