/**
* This file is a part of the Cairo-Dock project
*
* Copyright : (C) see the 'copyright' file.
* E-mail    : see the 'copyright' file.
*
* 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 3
* 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, see <http://www.gnu.org/licenses/>.
*/

#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <glib/gstdio.h>
#include <gtk/gtk.h>

#include <cairo.h>

#include "gldi-config.h"
#include "cairo-dock-image-buffer.h"
#include "cairo-dock-config.h"
#include "cairo-dock-data-renderer.h"  // cairo_dock_reload_data_renderer_on_icon/cairo_dock_refresh_data_renderer
#include "cairo-dock-icon-factory.h"
#include "cairo-dock-icon-facility.h"
#include "cairo-dock-separator-manager.h"  // gldi_automatic_separators_add_in_list
#include "cairo-dock-launcher-manager.h"
#include "cairo-dock-applet-manager.h"
#include "cairo-dock-stack-icon-manager.h"
#include "cairo-dock-class-icon-manager.h"
#include "cairo-dock-class-manager-priv.h"  // cairo_dock_update_class_subdock_name
#include "cairo-dock-backends-manager.h"
#include "cairo-dock-desktop-manager.h"
#include "cairo-dock-log.h"
#include "cairo-dock-keyfile-utilities.h"
#include "cairo-dock-themes-manager.h"  // cairo_dock_add_conf_file
#include "cairo-dock-draw.h"
#include "cairo-dock-draw-opengl.h"
#include "cairo-dock-animations.h"
#include "cairo-dock-container-priv.h"
#include "cairo-dock-keybinder.h"
#include "cairo-dock-indicator-manager.h"  // myIndicatorsParam.bUseClassIndic
#include "cairo-dock-style-manager.h"
#include "cairo-dock-opengl.h"
#include "cairo-dock-dock-visibility.h"
#include "cairo-dock-dock-facility.h"
#include "cairo-dock-dock-manager.h"
#include "cairo-dock-dock-priv.h" // also includes dock-factory
#include "cairo-dock-dialog-manager.h" // myDialogObjectMgr
#include "cairo-dock-windows-manager.h"


// public (manager, config, data)
CairoDocksParam myDocksParam;
GldiManager myDocksMgr;
GldiObjectManager myDockObjectMgr;
CairoDockImageBuffer g_pVisibleZoneBuffer;
CairoDock *g_pMainDock = NULL;  // pointeur sur le dock principal.
CairoDockHidingEffect *g_pHidingBackend = NULL;
CairoDockHidingEffect *g_pKeepingBelowBackend = NULL;

// dependencies
extern gchar *g_cConfFile;
extern gchar *g_cCurrentThemePath;
extern gboolean g_bUseOpenGL;
extern CairoDockGLConfig g_openglConfig;

// private
static GHashTable *s_hDocksTable = NULL;  // table des docks existant.
static GList *s_pRootDockList = NULL;
static gboolean s_bQuickHide = FALSE;
static gboolean s_bKeepAbove = FALSE;
static GldiShortkey *s_pPopupBinding = NULL;  // option 'pop up on shortkey'
static gboolean s_bResetAll = FALSE;

static gboolean _get_root_dock_config (CairoDock *pDock);
static void _synchronize_sub_docks_orientation (CairoDock *pDock, gboolean bUpdateDockSize);
static void _set_dock_orientation (CairoDock *pDock, CairoDockPositionType iScreenBorder);
static void _remove_root_dock_config (const gchar *cDockName);

  /////////////
 // MANAGER //
/////////////


void cairo_dock_force_docks_above (void)
{
	if (g_pMainDock != NULL)
	{
		cd_warning ("this function must be called before any dock is created.");
		return;
	}
	s_bKeepAbove = TRUE;
}


// UNLOAD //
static gboolean _free_one_dock (G_GNUC_UNUSED const gchar *cDockName, CairoDock *pDock, G_GNUC_UNUSED gpointer data)
{
	gldi_object_unref (GLDI_OBJECT(pDock));
	return TRUE;
}
void cairo_dock_reset_docks_table (void)
{
	// need to free this first as calls from reset_object might end up accessing elements in it
	// (specifically screen edge monitoring would re-check based on this whether to keep polling)
	g_list_free (s_pRootDockList);
	s_pRootDockList = NULL;
	
	s_bResetAll = TRUE;
	g_hash_table_foreach_remove (s_hDocksTable, (GHRFunc) _free_one_dock, NULL);
	g_pMainDock = NULL;
	s_bResetAll = FALSE;
}


static void _make_sub_dock (CairoDock *pDock, CairoDock *pParentDock, const gchar *cRendererName)
{
	//\__________________ set sub-dock flag
	pDock->iRefCount = 1;
	gtk_window_set_title (GTK_WINDOW (pDock->container.pWidget), "cairo-dock-sub");
	
	//\__________________ set the orientation relatively to the parent dock
	pDock->container.bIsHorizontal = pParentDock->container.bIsHorizontal;
	pDock->container.bDirectionUp = pParentDock->container.bDirectionUp;
	pDock->iNumScreen = pParentDock->iNumScreen;
	pDock->iScreenReq = pParentDock->iScreenReq;
	
	//\__________________ set a renderer
	cairo_dock_set_renderer (pDock, cRendererName);
	
	//\__________________ update the icons size and the ratio.
	double fPrevRatio = pDock->container.fRatio;
	pDock->container.fRatio = MIN (pDock->container.fRatio, myBackendsParam.fSubDockSizeRatio);
	pDock->iIconSize = pParentDock->iIconSize;
	
	Icon *icon;
	GList *ic;
	pDock->fFlatDockWidth = - myIconsParam.iIconGap;
	for (ic = pDock->icons; ic != NULL; ic = ic->next)
	{
		icon = ic->data;
		cairo_dock_icon_set_requested_size (icon, 0, 0);  // no request
		icon->fWidth = icon->fHeight = 0;  /// useful ?...
		cairo_dock_set_icon_size_in_dock (pDock, icon);
		pDock->fFlatDockWidth += icon->fWidth + myIconsParam.iIconGap;
	}
	pDock->iMaxIconHeight *= pDock->container.fRatio / fPrevRatio;
	
	//\__________________ remove any input shape
	if (pDock->pShapeBitmap != NULL)
	{
		cairo_region_destroy (pDock->pShapeBitmap);
		pDock->pShapeBitmap = NULL;
		if (pDock->iInputState != CAIRO_DOCK_INPUT_ACTIVE)
		{
			cairo_dock_set_input_shape_active (pDock);
			pDock->iInputState = CAIRO_DOCK_INPUT_ACTIVE;
		}
	}
	
	//\__________________ hide the dock
	pDock->bAutoHide = FALSE;
	gtk_widget_hide (pDock->container.pWidget);
}
void gldi_dock_make_subdock (CairoDock *pDock, CairoDock *pParentDock, const gchar *cRendererName)
{
	//g_print ("%s (%s)\n", __func__, cRendererName);
	if (pDock->iRefCount == 0)  // il devient un sous-dock.
	{
		//\__________________ make it a sub-dock.
		if (pParentDock == NULL)
			pParentDock = g_pMainDock;
		_make_sub_dock (pDock, pParentDock, cRendererName);
		cairo_dock_update_dock_size (pDock);
		
		//\__________________ remove from main-dock land
		_remove_root_dock_config (pDock->cDockName);  // just in case.
		
		s_pRootDockList = g_list_remove (s_pRootDockList, pDock);
		
		gldi_dock_set_visibility (pDock, CAIRO_DOCK_VISI_KEEP_ABOVE);  // si la visibilite n'avait pas ete mise (sub-dock), ne fera rien (vu que la visibilite par defaut est KEEP_ABOVE).
	}
}



static void _find_similar_root_dock (CairoDock *pDock, gpointer *data)
{
	CairoDock *pDock0 = data[0];
	if (pDock == pDock0)
		data[2] = GINT_TO_POINTER (TRUE);
	if (data[2])
		return;
	if (pDock->container.bIsHorizontal == pDock0->container.bIsHorizontal
		&& pDock->container.bDirectionUp == pDock0->container.bDirectionUp)
	{
		int *i = data[1];
		*i = *i + 1;
	}
}
gchar *gldi_dock_get_readable_name (CairoDock *pDock)
{
	g_return_val_if_fail (pDock != NULL, NULL);
	gchar *cUserName = NULL;
	if (pDock->iRefCount == 0)
	{
		int i = 0;
		gpointer data[3] = {pDock, &i, NULL};
		GList *d = g_list_last (s_pRootDockList);
		for (;d != NULL; d = d->prev)  // parse the list from the end to get the main dock first (docks are prepended).
			_find_similar_root_dock (d->data, data);
		const gchar *cPosition;
		if (pDock->container.bIsHorizontal)
		{
			if (pDock->container.bDirectionUp)
				cPosition = _("Bottom dock");
			else
				cPosition = _("Top dock");
		}
		else
		{
			if (pDock->container.bDirectionUp)
				cPosition = _("Right dock");
			else
				cPosition = _("Left dock");
		}
		if (i > 0)
			cUserName = g_strdup_printf ("%s (%d)", cPosition, i+1);
		else
			cUserName = g_strdup (cPosition);
	}
	
	return cUserName;
}

CairoDock *gldi_dock_get (const gchar *cDockName)
{
	if (cDockName == NULL)
		return NULL;
	return g_hash_table_lookup (s_hDocksTable, cDockName);
}

static gboolean _cairo_dock_search_icon_from_subdock (G_GNUC_UNUSED gchar *cDockName, CairoDock *pDock, gpointer *data)
{
	if (pDock == data[0])
		return FALSE;
	Icon **pIconFound = data[1];
	CairoDock **pDockFound = data[2];
	Icon *icon = cairo_dock_get_icon_with_subdock (pDock->icons, data[0]);
	if (icon != NULL)
	{
		*pIconFound = icon;
		if (pDockFound != NULL)
			*pDockFound = pDock;
		return TRUE;
	}
	else
		return FALSE;
}
Icon *cairo_dock_search_icon_pointing_on_dock (CairoDock *pDock, CairoDock **pParentDock)  // pParentDock peut etre NULL.
{
	if (pDock == NULL || pDock->bIsMainDock)  // par definition. On n'utilise pas iRefCount, car si on est en train de detruire un dock, sa reference est deja decrementee. C'est dommage mais c'est comme ca.
		return NULL;
	Icon *pPointingIcon = NULL;
	gpointer data[3] = {pDock, &pPointingIcon, pParentDock};
	g_hash_table_find (s_hDocksTable, (GHRFunc)_cairo_dock_search_icon_from_subdock, data);
	return pPointingIcon;
}

gchar *cairo_dock_get_unique_dock_name (const gchar *cPrefix)
{
	const gchar *cNamepattern = (cPrefix != NULL && *cPrefix != '\0' && strcmp (cPrefix, "cairo-dock") != 0 ? cPrefix : "dock");
	GString *sNameString = g_string_new (cNamepattern);
	
	int i = 2;
	while (g_hash_table_lookup (s_hDocksTable, sNameString->str) != NULL)
	{
		g_string_printf (sNameString, "%s-%d", cNamepattern, i);
		i ++;
	}
	
	return g_string_free (sNameString, FALSE);
}

gboolean cairo_dock_check_unique_subdock_name (Icon *pIcon)
{
	cd_debug ("%s (%s)", __func__, pIcon->cName);
	gchar *cUniqueName = cairo_dock_get_unique_dock_name (pIcon->cName);
	if (pIcon->cName == NULL || strcmp (pIcon->cName, cUniqueName) != 0)
	{
		g_free (pIcon->cName);
		pIcon->cName = cUniqueName;
		cd_debug (" cName <- %s", cUniqueName);
		return TRUE;
	}
	return FALSE;
}


void gldi_dock_rename (CairoDock *pDock, const gchar *cNewName)
{
	// ensure everything is ok
	g_return_if_fail (pDock != NULL && cNewName != NULL);
	g_return_if_fail (g_hash_table_lookup (s_hDocksTable, cNewName) == NULL);
	cd_debug("%s (%s -> %s)", __func__, pDock->cDockName, cNewName);
	
	// if it's a class subdock, first update the dock name in the class
	cairo_dock_update_class_subdock_name (pDock, cNewName);
	
	// rename in the hash table
	g_hash_table_remove (s_hDocksTable, pDock->cDockName);
	g_free (pDock->cDockName);
	pDock->cDockName = g_strdup (cNewName);
	g_hash_table_insert (s_hDocksTable, pDock->cDockName, pDock);
	
	// rename in each icons
	GList* ic;
	Icon *icon;
	for (ic = pDock->icons; ic != NULL; ic = ic->next)
	{
		icon = ic->data;
		gldi_theme_icon_write_container_name_in_conf_file (icon, cNewName);
		g_free (icon->cParentDockName);
		icon->cParentDockName = g_strdup (cNewName);
	}
}


void gldi_docks_foreach (GHFunc pFunction, gpointer data)
{
	g_hash_table_foreach (s_hDocksTable, pFunction, data);
}

void gldi_docks_foreach_root (GFunc pFunction, gpointer data)
{
	g_list_foreach (s_pRootDockList, pFunction, data);
}

typedef void (*CairoDockSimpleCallback) (CairoDock *pDock);

static void _simple_cb (void *obj, void *data)
{
	CairoDock *pDock = (CairoDock*)obj;
	CairoDockSimpleCallback fun = (CairoDockSimpleCallback)data;
	fun (pDock);
}

static void _gldi_docks_foreach_root_no_data (CairoDockSimpleCallback pFunction)
{
	g_list_foreach (s_pRootDockList, _simple_cb, (void*)pFunction);
}

static void _gldi_icons_foreach_in_dock (G_GNUC_UNUSED gchar *cDockName, CairoDock *pDock, gpointer *data)
{
	GldiIconFunc pFunction = data[0];
	gpointer pUserData = data[1];
	GList *ic = pDock->icons, *next_ic;
	while (ic != NULL)
	{
		next_ic = ic->next;  // the function below may remove the current icon
		pFunction ((Icon*)ic->data, pUserData);
		ic = next_ic;
	}
}
void gldi_icons_foreach_in_docks (GldiIconFunc pFunction, gpointer pUserData)
{
	gpointer data[2] = {pFunction, pUserData};
	g_hash_table_foreach (s_hDocksTable, (GHFunc) _gldi_icons_foreach_in_dock, data);
}




static void _reload_buffers_in_dock (CairoDock *pDock, gboolean bRecursive, gboolean bUpdateIconSize)
{
	//g_print ("************%s (%d, %d)\n", __func__, pDock->bIsMainDock, bRecursive);
	if (bUpdateIconSize && pDock->bGlobalIconSize)
		pDock->iIconSize = myIconsParam.iIconWidth;
	
	// for each icon, reload its buffer (size may change).
	Icon* icon;
	GList* ic;
	for (ic = pDock->icons; ic != NULL; ic = ic->next)
	{
		icon = ic->data;
		
		if (CAIRO_DOCK_IS_APPLET (icon))  // for an applet, we need to let the module know that the size or the theme has changed, so that it can reload its private buffers.
		{
			gldi_object_reload (GLDI_OBJECT(icon->pModuleInstance), FALSE);
		}
		else
		{
			if (bUpdateIconSize)
			{
				cairo_dock_icon_set_requested_size (icon, 0, 0);
				cairo_dock_set_icon_size_in_dock (pDock, icon);
			}
			
			if (bUpdateIconSize && cairo_dock_get_icon_data_renderer (icon) != NULL)  // we need to reload the DataRenderer to use the new size
			{
				cairo_dock_load_icon_buffers (icon, CAIRO_CONTAINER (pDock));  // the DataRenderer uses the ImageBuffer's size on loading, so we need to load it now
				cairo_dock_reload_data_renderer_on_icon (icon, CAIRO_CONTAINER (pDock));
			}
			else
			{
				cairo_dock_trigger_load_icon_buffers (icon);
			}
		}
		
		if (bRecursive && icon->pSubDock != NULL)  // we handle the sub-dock for applets too, so that they don't need to care.
		{
			if (bUpdateIconSize)
				icon->pSubDock->iIconSize = pDock->iIconSize;
			_reload_buffers_in_dock (icon->pSubDock, bRecursive, bUpdateIconSize);
		}
	}
	
	if (bUpdateIconSize)
	{
		cairo_dock_update_dock_size (pDock);
	}
	gtk_widget_queue_draw (pDock->container.pWidget);
}

static void _reload_buffer_in_one_dock (CairoDock *pDock, gpointer data)
{
	_reload_buffers_in_dock (pDock, TRUE, GPOINTER_TO_INT (data));
}
static void _cairo_dock_draw_one_subdock_icon (G_GNUC_UNUSED const gchar *cDockName, CairoDock *pDock, G_GNUC_UNUSED gpointer data)
{
	Icon *icon;
	GList *ic;
	for (ic = pDock->icons; ic != NULL; ic = ic->next)
	{
		icon = ic->data;
		if (icon->pSubDock != NULL
		&& (GLDI_OBJECT_IS_STACK_ICON (icon) || CAIRO_DOCK_IS_MULTI_APPLI (icon) || GLDI_OBJECT_IS_APPLET_ICON (icon))
		&& (icon->iSubdockViewType != 0
			|| (CAIRO_DOCK_IS_MULTI_APPLI (icon) && !myIndicatorsParam.bUseClassIndic))
		/**&& icon->iSidRedrawSubdockContent == 0*/)  // icone de sous-dock ou de classe ou d'applets.
		{
			cairo_dock_trigger_redraw_subdock_content_on_icon (icon);
		}
	}
}
void cairo_dock_reload_buffers_in_all_docks (gboolean bUpdateIconSize)
{
	g_list_foreach (s_pRootDockList, (GFunc)_reload_buffer_in_one_dock, GINT_TO_POINTER (bUpdateIconSize));  // we load the root docks first, so that sub-docks can have the correct icon size.
	
	// now that all icons in sub-docks are drawn, redraw icons pointing to a sub-dock
	g_hash_table_foreach (s_hDocksTable, (GHFunc)_cairo_dock_draw_one_subdock_icon, NULL);
}



static void _cairo_dock_set_one_dock_view_to_default (G_GNUC_UNUSED gchar *cDockName, CairoDock *pDock, gpointer data)
{
	//g_print ("%s (%s)\n", __func__, cDockName);
	int iDockType = GPOINTER_TO_INT (data);
	if (iDockType == 0 || (iDockType == 1 && pDock->iRefCount == 0) || (iDockType == 2 && pDock->iRefCount != 0))
	{
		cairo_dock_set_default_renderer (pDock);
		cairo_dock_update_dock_size (pDock);
	}
}
void cairo_dock_set_all_views_to_default (int iDockType)
{
	//g_print ("%s ()\n", __func__);
	g_hash_table_foreach (s_hDocksTable, (GHFunc) _cairo_dock_set_one_dock_view_to_default, GINT_TO_POINTER (iDockType));
}


  //////////////////////
 // ROOT DOCK CONFIG //
//////////////////////

int cairo_dock_convert_icon_size_to_pixels (GldiIconSizeEnum s, double *fMaxScale, double *fReflectSize, int *iIconGap)
{
	int iIconSize;
	switch (s)
	{
		case ICON_DEFAULT:
		default:
			iIconSize = myIconsParam.iIconWidth;
			*fMaxScale = 1 + myIconsParam.fAmplitude;
			*iIconGap = myIconsParam.iIconGap;
			*fReflectSize = myIconsParam.fReflectHeightRatio;
		break;
		case ICON_TINY:
			iIconSize = ICON_SIZE_TINY;
			*fMaxScale = 2;
			*iIconGap = 4;
			*fReflectSize = .4;
		break;
		case ICON_VERY_SMALL:
			iIconSize = ICON_SIZE_VERY_SMALL;
			*fMaxScale = 1.8;
			*iIconGap = 4;
			*fReflectSize = .4;
		break;
		case ICON_SMALL:
			iIconSize = ICON_SIZE_SMALL;
			*fMaxScale = 1.8;
			*iIconGap = 4;
			*fReflectSize = .4;
		break;
		case ICON_MEDIUM:
			iIconSize = ICON_SIZE_MEDIUM;
			*fMaxScale = 1.6;
			*iIconGap = 3;
			*fReflectSize = .5;
		break;
		case ICON_BIG:
			iIconSize = ICON_SIZE_BIG;
			*fMaxScale = 1.5;
			*iIconGap = 2;
			*fReflectSize = .6;
		break;
		case ICON_HUGE:
			iIconSize = ICON_SIZE_HUGE;
			*fMaxScale = 1.3;
			*iIconGap = 2;
			*fReflectSize = .6;
		break;
	}
	return iIconSize;
}

GldiIconSizeEnum cairo_dock_convert_icon_size_to_enum (int iIconSize)
{
	GldiIconSizeEnum s = ICON_DEFAULT;
	if (iIconSize <= ICON_SIZE_TINY+2)
		s = ICON_TINY;
	else if (iIconSize <= ICON_SIZE_VERY_SMALL+2)
		s = ICON_VERY_SMALL;
	else if (iIconSize >= ICON_SIZE_HUGE-2)
		s = ICON_HUGE;
	else if (iIconSize > ICON_SIZE_MEDIUM)
		s = ICON_BIG;
	else
	{
		if (myIconsParam.fAmplitude >= 2 || iIconSize <= ICON_SIZE_SMALL)
			s = ICON_SMALL;
		else
			s = ICON_MEDIUM;  // moyennes.
	}
	return s;
}

static void _update_dock_screen_num (CairoDock *pDock)
{
	pDock->iNumScreen = pDock->iScreenReq;
	if (pDock->iNumScreen < 0 || pDock->iNumScreen >= g_desktopGeometry.iNbScreens)
		pDock->iNumScreen = 0;
}

static gboolean _get_root_dock_config (CairoDock *pDock)
{
	g_return_val_if_fail (pDock != NULL, FALSE);
	if (pDock->iRefCount > 0)
		return FALSE;
	
	if (pDock->bIsMainDock)  // the main dock doesn't have a config file, it uses the global params (that we already got from the Dock-manager)
	{
		pDock->iGapX = myDocksParam.iGapX;
		pDock->iGapY = myDocksParam.iGapY;
		
		pDock->fAlign = myDocksParam.fAlign;
		
		pDock->iScreenReq = myDocksParam.iScreenReq;
		_update_dock_screen_num (pDock);
		
		_set_dock_orientation (pDock, myDocksParam.iScreenBorder);  // do it after all position parameters have been set; it sets the sub-docks orientation too.
		
		gldi_dock_set_visibility (pDock, myDocksParam.iVisibility);
		
		pDock->bGlobalIconSize = TRUE;  // default icon size
		
		pDock->bGlobalBg = TRUE;  // default background
		// pDock->cRendererName and pDock->iIconSize stay at 0
		
		pDock->bExtendedMode = myDocksParam.bExtendedMode;
		
		return TRUE;
	}
	
	//\______________ On verifie la presence du fichier de conf associe.
	//g_print ("%s (%s)\n", __func__, pDock->cDockName);
	gchar *cConfFilePath = g_strdup_printf ("%s/%s.conf", g_cCurrentThemePath, pDock->cDockName);
	if (! g_file_test (cConfFilePath, G_FILE_TEST_EXISTS))  // pas encore de fichier de conf pour ce dock.
	{
		pDock->container.bIsHorizontal = g_pMainDock->container.bIsHorizontal;
		pDock->container.bDirectionUp = g_pMainDock->container.bDirectionUp;
		pDock->fAlign = g_pMainDock->fAlign;
		
		g_free (cConfFilePath);
		return FALSE;
	}
	
	//\______________ On ouvre le fichier de conf.
	GKeyFile *pKeyFile = cairo_dock_open_key_file (cConfFilePath);
	if (pKeyFile == NULL)
	{
		cd_warning ("wrong conf file (%s) !", cConfFilePath);
		g_free (cConfFilePath);
		return FALSE;
	}
	
	//\______________ Position.
	gboolean bFlushConfFileNeeded = FALSE;
	pDock->iGapX = cairo_dock_get_integer_key_value (pKeyFile, "Behavior", "x gap", &bFlushConfFileNeeded, 0, "Position", NULL);
	pDock->iGapY = cairo_dock_get_integer_key_value (pKeyFile, "Behavior", "y gap", &bFlushConfFileNeeded, 0, "Position", NULL);
	
	pDock->fAlign = cairo_dock_get_double_key_value (pKeyFile, "Behavior", "alignment", &bFlushConfFileNeeded, 0.5, "Position", NULL);
	
	pDock->iScreenReq = cairo_dock_get_integer_key_value (pKeyFile, "Behavior", "num_screen", &bFlushConfFileNeeded, GLDI_DEFAULT_SCREEN, "Position", NULL);
	_update_dock_screen_num (pDock);
	
	CairoDockPositionType iScreenBorder = cairo_dock_get_integer_key_value (pKeyFile, "Behavior", "screen border", &bFlushConfFileNeeded, 0, "Position", NULL);
	_set_dock_orientation (pDock, iScreenBorder);  // do it after all position parameters have been set; it sets the sub-docks orientation too.
	
	//\______________ Visibility.
	CairoDockVisibility iVisibility = cairo_dock_get_integer_key_value (pKeyFile, "Behavior", "visibility", &bFlushConfFileNeeded, FALSE, "Position", NULL);
	gldi_dock_set_visibility (pDock, iVisibility);
	
	//\______________ Icons size.
	int s = cairo_dock_get_integer_key_value (pKeyFile, "Appearance", "icon size", &bFlushConfFileNeeded, ICON_DEFAULT, NULL, NULL);  // ICON_DEFAULT <=> same as main dock
	double fMaxScale, fReflectSize;
	int iIconGap;
	pDock->iIconSize = cairo_dock_convert_icon_size_to_pixels (s, &fMaxScale, &fReflectSize, &iIconGap);
	pDock->bGlobalIconSize = (s == ICON_DEFAULT);
	
	//\______________ View.
	g_free (pDock->cRendererName);
	pDock->cRendererName = cairo_dock_get_string_key_value (pKeyFile, "Appearance", "main dock view", &bFlushConfFileNeeded, NULL, "Views", NULL);
	
	//\______________ Background.
	int iBgType = cairo_dock_get_integer_key_value (pKeyFile, "Appearance", "fill bg", &bFlushConfFileNeeded, 0, NULL, NULL);
	
	pDock->bGlobalBg = (iBgType == 0);
	
	if (!pDock->bGlobalBg)
	{
		if (iBgType == 1)
		{
			gchar *cBgImage = cairo_dock_get_string_key_value (pKeyFile, "Appearance", "background image", &bFlushConfFileNeeded, NULL, NULL, NULL);
			g_free (pDock->cBgImagePath);
			if (cBgImage != NULL)
			{
				pDock->cBgImagePath = cairo_dock_search_image_s_path (cBgImage);
				g_free (cBgImage);
			}
			else
				pDock->cBgImagePath = NULL;
			
			pDock->bBgImageRepeat = cairo_dock_get_boolean_key_value (pKeyFile, "Appearance", "repeat image", &bFlushConfFileNeeded, FALSE, NULL, NULL);
		}
		// on recupere la couleur tout le temps pour avoir un plan B.
		GldiColor couleur = {{.7, .7, 1., .7}};
		cairo_dock_get_color_key_value (pKeyFile, "Appearance", "stripes color dark", &bFlushConfFileNeeded, &pDock->fBgColorDark, &couleur, NULL, NULL);
		
		GldiColor couleur2 = {{.7, .9, .7, .4}};
		cairo_dock_get_color_key_value (pKeyFile, "Appearance", "stripes color bright", &bFlushConfFileNeeded, &pDock->fBgColorBright, &couleur2, NULL, NULL);
	}
	
	pDock->bExtendedMode = cairo_dock_get_boolean_key_value (pKeyFile, "Appearance", "extended", &bFlushConfFileNeeded, FALSE, NULL, NULL);
	
	//\______________ On met a jour le fichier de conf.
	if (! bFlushConfFileNeeded)
		bFlushConfFileNeeded = cairo_dock_conf_file_needs_update (pKeyFile, GLDI_VERSION);
	if (bFlushConfFileNeeded)
	{
		//g_print ("update %s conf file\n", pDock->cDockName);
		cairo_dock_upgrade_conf_file (cConfFilePath, pKeyFile, GLDI_SHARE_DATA_DIR"/"CAIRO_DOCK_MAIN_DOCK_CONF_FILE);
	}
	
	g_key_file_free (pKeyFile);
	g_free (cConfFilePath);
	return TRUE;
}

static void _remove_root_dock_config (const gchar *cDockName)
{
	if (! cDockName || strcmp (cDockName, "cairo-dock") == 0)
		return ;
	gchar *cConfFilePath = g_strdup_printf ("%s/%s.conf", g_cCurrentThemePath, cDockName);
	if (g_file_test (cConfFilePath, G_FILE_TEST_EXISTS))
	{
		cairo_dock_delete_conf_file (cConfFilePath);
	}
	g_free (cConfFilePath);
}

void gldi_dock_add_conf_file_for_name (const gchar *cDockName)
{
	// on cree le fichier de conf a partir du template.
	cd_debug ("%s (%s)", __func__, cDockName);
	gchar *cConfFilePath = g_strdup_printf ("%s/%s.conf", g_cCurrentThemePath, cDockName);
	cairo_dock_add_conf_file (GLDI_SHARE_DATA_DIR"/"CAIRO_DOCK_MAIN_DOCK_CONF_FILE, cConfFilePath);
	
	// on placera le nouveau dock a l'oppose du main dock, meme ecran et meme visibilite.
	cairo_dock_update_conf_file (cConfFilePath,
		G_TYPE_INT, "Behavior", "screen border",
		(g_pMainDock->container.bIsHorizontal ?
			(g_pMainDock->container.bDirectionUp ? 1 : 0) :
			(g_pMainDock->container.bDirectionUp ? 3 : 2)),
		G_TYPE_INT, "Behavior", "visibility",
		g_pMainDock->iVisibility,
		G_TYPE_INT, "Behavior", "num_screen",
		g_pMainDock->iScreenReq,
		G_TYPE_INVALID);
	g_free (cConfFilePath);
}

gchar *gldi_dock_add_conf_file (void)
{
	// on genere un nom unique.
	gchar *cValidDockName = cairo_dock_get_unique_dock_name (CAIRO_DOCK_MAIN_DOCK_NAME);  // meme nom que le main dock avec un numero en plus, plus facile pour les reperer.
	
	// on genere un fichier de conf pour ce nom.
	gldi_dock_add_conf_file_for_name (cValidDockName);
	
	return cValidDockName;
}


void gldi_docks_redraw_all_root (void)
{
	_gldi_docks_foreach_root_no_data ((CairoDockSimpleCallback)cairo_dock_redraw_container);
}


static void _reposition_one_root_dock (G_GNUC_UNUSED const gchar *cDockName, CairoDock *pDock, gpointer data)
{
	if (pDock->iRefCount == 0 && ! (data && pDock->bIsMainDock))
	{
		if (g_desktopGeometry.iNbScreens == 0 || g_desktopGeometry.Xscreen.width == 0 ||
			g_desktopGeometry.Xscreen.height == 0)
		{
			cd_debug ("hiding dock: %p", pDock);
			gtk_widget_hide (pDock->container.pWidget);
			return;
		}
		
		cd_debug ("showing dock: %p, iNumScreen: %d", pDock, pDock->iNumScreen);
		if (!pDock->bIsMainDock)
			_get_root_dock_config (pDock);  // relit toute la conf.
		gldi_container_set_screen (CAIRO_CONTAINER (pDock), pDock->iNumScreen); // this is a no-op on X11 (move_resize_dock () takes care of it below)
		cairo_dock_update_dock_size (pDock);  // la taille max du dock depend de la taille de l'ecran, donc on recalcule son ratio.
		cairo_dock_move_resize_dock (pDock);
		gtk_widget_show (pDock->container.pWidget);
		gtk_widget_queue_draw (pDock->container.pWidget);
		_synchronize_sub_docks_orientation (pDock, TRUE);
		gldi_dock_visibility_refresh (pDock);
	}
}
static void _reposition_root_docks (gboolean bExceptMainDock)
{
	g_hash_table_foreach (s_hDocksTable, (GHFunc)_reposition_one_root_dock, GINT_TO_POINTER (bExceptMainDock));
}

void gldi_subdock_synchronize_orientation (CairoDock *pSubDock, CairoDock *pDock, gboolean bUpdateDockSize)
{
	if (pSubDock->container.bDirectionUp != pDock->container.bDirectionUp)
	{
		pSubDock->container.bDirectionUp = pDock->container.bDirectionUp;
		bUpdateDockSize = TRUE;
	}
	if (pSubDock->container.bIsHorizontal != pDock->container.bIsHorizontal)
	{
		pSubDock->container.bIsHorizontal = pDock->container.bIsHorizontal;
		bUpdateDockSize = TRUE;
	}
	pSubDock->iScreenReq = pDock->iScreenReq;
	if (pSubDock->iNumScreen != pDock->iNumScreen)
	{
		pSubDock->iNumScreen = pDock->iNumScreen;
		bUpdateDockSize = TRUE;
	}
	
	if (bUpdateDockSize)
	{
		cairo_dock_update_dock_size (pSubDock);
	}
	
	_synchronize_sub_docks_orientation (pSubDock, bUpdateDockSize);
}

static void _synchronize_sub_docks_orientation (CairoDock *pDock, gboolean bUpdateDockSize)
{
	GList* ic;
	Icon *icon;
	for (ic = pDock->icons; ic != NULL; ic = ic->next)
	{
		icon = ic->data;
		if (icon->pSubDock != NULL)
		{
			gldi_subdock_synchronize_orientation (icon->pSubDock, pDock, bUpdateDockSize);  // recursively synchronize all children (no need to check for loops, as it shouldn't occur... if it does, then the problem is to fix upstream).
		}
	}
}

static void _set_dock_orientation (CairoDock *pDock, CairoDockPositionType iScreenBorder)
{
	switch (iScreenBorder)
	{
		case CAIRO_DOCK_BOTTOM :
			pDock->container.bIsHorizontal = CAIRO_DOCK_HORIZONTAL;
			pDock->container.bDirectionUp = TRUE;
		break;
		case CAIRO_DOCK_TOP :
			pDock->container.bIsHorizontal = CAIRO_DOCK_HORIZONTAL;
			pDock->container.bDirectionUp = FALSE;
		break;
		case CAIRO_DOCK_RIGHT :
			pDock->container.bIsHorizontal = CAIRO_DOCK_VERTICAL;
			pDock->container.bDirectionUp = TRUE;
		break;
		case CAIRO_DOCK_LEFT :
			pDock->container.bIsHorizontal = CAIRO_DOCK_VERTICAL;
			pDock->container.bDirectionUp = FALSE;
		break;
		case CAIRO_DOCK_INSIDE_SCREEN :
		case CAIRO_DOCK_NB_POSITIONS :
		break;
	}
	_synchronize_sub_docks_orientation (pDock, FALSE);
}


  ////////////////
 // VISIBILITY //
////////////////

static void _cairo_dock_quick_hide_one_root_dock (G_GNUC_UNUSED const gchar *cDockName, CairoDock *pDock, G_GNUC_UNUSED gpointer data)
{
	if (pDock->iRefCount == 0)
	{
		pDock->bAutoHide = TRUE;
		gldi_dock_leave_synthetic (pDock);
	}
}
void cairo_dock_quick_hide_all_docks (void)
{
	if (! s_bQuickHide)
	{
		s_bQuickHide = TRUE;
		g_hash_table_foreach (s_hDocksTable, (GHFunc) _cairo_dock_quick_hide_one_root_dock, NULL);
		gldi_container_update_polling_screen_edge ();
	}
}

static void _cairo_dock_stop_quick_hide_one_root_dock (G_GNUC_UNUSED const gchar *cDockName, CairoDock *pDock, G_GNUC_UNUSED gpointer data)
{
	if (pDock->iRefCount == 0 && ! pDock->bTemporaryHidden && pDock->bAutoHide && pDock->iVisibility != CAIRO_DOCK_VISI_AUTO_HIDE)
	{
		pDock->bAutoHide = FALSE;
		
		if (! pDock->container.bInside)  // on le fait re-apparaitre.
		{
			cairo_dock_start_showing (pDock);  // l'input shape sera mise lors du configure.
		}
	}
}
void cairo_dock_stop_quick_hide (void)
{
	if (s_bQuickHide)
	{
		s_bQuickHide = FALSE;
		g_hash_table_foreach (s_hDocksTable, (GHFunc) _cairo_dock_stop_quick_hide_one_root_dock, NULL);
		gldi_container_update_polling_screen_edge ();
	}
}

gboolean cairo_dock_entrance_is_allowed (CairoDock *pDock)
{
	//!! currently this function always returns TRUE, bEntranceDisabled is never set to TRUE
	return (! pDock->bEntranceDisabled);
}

void cairo_dock_activate_temporary_auto_hide (CairoDock *pDock)
{
	if (pDock->iRefCount == 0 && ! pDock->bTemporaryHidden && pDock->iVisibility != CAIRO_DOCK_VISI_AUTO_HIDE)
	{
		pDock->bAutoHide = TRUE;
		pDock->bTemporaryHidden = TRUE;
		if (!pDock->container.bInside)  // on ne declenche pas le cachage lorsque l'on change par exemple de bureau via le switcher ou un clic sur une appli.
		{
			gldi_dock_leave_synthetic (pDock);  // un cairo_dock_start_hiding ne cacherait pas les sous-docks.
		}
		gldi_container_update_polling_screen_edge ();
	}
}

void cairo_dock_deactivate_temporary_auto_hide (CairoDock *pDock)
{
	//g_print ("%s ()\n", __func__);
	if (pDock->iRefCount == 0 && pDock->bTemporaryHidden && ! s_bQuickHide)
	{
		pDock->bTemporaryHidden = FALSE;
		pDock->bAutoHide = FALSE;
		
		if (! pDock->container.bInside)  // on le fait re-apparaitre.
		{
			cairo_dock_start_showing (pDock);
		}
		gldi_container_update_polling_screen_edge ();
	}
}


static gboolean _cairo_dock_hide_back_dock (CairoDock *pDock)
{
	//g_print ("hide back\n");
	if (pDock->iVisibility == CAIRO_DOCK_VISI_KEEP_BELOW && ! pDock->container.bInside)
		cairo_dock_pop_down (pDock);
	else if (pDock->bAutoHide)
		cairo_dock_start_hiding (pDock);
	pDock->iSidHideBack = 0;
	return FALSE;
}
static gboolean _cairo_dock_unhide_dock_delayed (CairoDock *pDock)
{
	//g_print ("%s (%d, %d)\n", __func__, pDock->container.bInside, pDock->iInputState);
	if (pDock->container.bInside && pDock->iInputState != CAIRO_DOCK_INPUT_HIDDEN && !pDock->bIsBelow)  // already inside and reachable (caution) => no need to show it again.
	{
		pDock->iSidUnhideDelayed = 0;
		return FALSE;
	}
	
	//g_print ("let's show this dock (%d)\n", pDock->bIsMainDock);
	if (pDock->bAutoHide)
		cairo_dock_start_showing (pDock);
	if (pDock->iVisibility == CAIRO_DOCK_VISI_KEEP_BELOW)
		cairo_dock_pop_up (pDock);
	
	if (pDock->iSidHideBack == 0)  // on se recachera dans 2s si on n'est pas entre dans le dock entre-temps.
		pDock->iSidHideBack = g_timeout_add (2000, (GSourceFunc) _cairo_dock_hide_back_dock, (gpointer) pDock);
	pDock->iSidUnhideDelayed = 0;
	return FALSE;
}

void cairo_dock_unhide_dock_delayed (CairoDock *pDock, int iDelay)
{
	if (iDelay != 0)  // on programme une apparition.
	{
		if (pDock->iSidUnhideDelayed == 0)
			pDock->iSidUnhideDelayed = g_timeout_add (iDelay, (GSourceFunc) _cairo_dock_unhide_dock_delayed, (gpointer) pDock);
	}
	else  // on montre le dock tout de suite.
	{
		if (pDock->iSidUnhideDelayed == 0)
			pDock->iSidUnhideDelayed = g_idle_add ((GSourceFunc) _cairo_dock_unhide_dock_delayed, (gpointer)pDock);
	}
}

void gldi_dock_set_visibility (CairoDock *pDock, CairoDockVisibility iVisibility)
{
	// check that our backend supports recalling a hidden dock
	if (!gldi_container_can_poll_screen_edge ())
	{
		// disallow hiding the dock in this case
		if (iVisibility != CAIRO_DOCK_VISI_RESERVE &&
			iVisibility != CAIRO_DOCK_VISI_KEEP_BELOW &&
			iVisibility != CAIRO_DOCK_VISI_KEEP_ABOVE)
				iVisibility = CAIRO_DOCK_VISI_KEEP_ABOVE;
	}
	
	//\_______________ jeu de parametres.
	gboolean bReserveSpace = (iVisibility == CAIRO_DOCK_VISI_RESERVE);
	gboolean bKeepBelow = (iVisibility == CAIRO_DOCK_VISI_KEEP_BELOW);
	gboolean bAutoHideOnOverlap = (iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP);
	gboolean bAutoHideOnAnyOverlap = (iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP_ANY);
	gboolean bAutoHide = (iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE);
	gboolean bShortKey = (iVisibility == CAIRO_DOCK_VISI_SHORTKEY);
	
	gboolean bReserveSpace0 = (pDock->iVisibility == CAIRO_DOCK_VISI_RESERVE);
	gboolean bKeepBelow0 = (pDock->iVisibility == CAIRO_DOCK_VISI_KEEP_BELOW);
	gboolean bAutoHideOnOverlap0 = (pDock->iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP);
	gboolean bAutoHideOnAnyOverlap0 = (pDock->iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP_ANY);
	gboolean bAutoHide0 = (pDock->iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE);
	gboolean bShortKey0 = (pDock->iVisibility == CAIRO_DOCK_VISI_SHORTKEY);
	
	pDock->iVisibility = iVisibility;
	
	//\_______________ changement dans le Reserve Space.
	if (bReserveSpace != bReserveSpace0)
		cairo_dock_reserve_space_for_dock (pDock, bReserveSpace);
	
	//\_______________ changement dans le Keep below.
	if (bKeepBelow != bKeepBelow0)
	{
		if (bKeepBelow)
			cairo_dock_pop_down (pDock);
		else
			cairo_dock_pop_up (pDock);
	}
	
	//\_______________ changement dans l'Auto-Hide
	if (bAutoHideOnOverlap != bAutoHideOnOverlap0 ||
		bAutoHideOnAnyOverlap != bAutoHideOnAnyOverlap0 ||
		bAutoHide != bAutoHide0)
	{
		pDock->bTemporaryHidden = pDock->bAutoHide;  // needed to use the following function
		gldi_dock_visibility_refresh (pDock); // will update any event listeners as needed
		if (bAutoHide)
		{
			pDock->bTemporaryHidden = FALSE;
			pDock->bAutoHide = TRUE;
			cairo_dock_start_hiding (pDock);
		}
		else if (! (bAutoHideOnAnyOverlap || bAutoHideOnOverlap))
		{
			// all three are false, we should show the dock now
			pDock->bTemporaryHidden = FALSE;
			pDock->bAutoHide = FALSE;
			cairo_dock_start_showing (pDock);
		}
	}
	
	//\_______________ shortkey
	if (pDock->bIsMainDock)
	{
		if (bShortKey)  // option is enabled.
		{
			if (s_pPopupBinding && gldi_shortkey_could_grab (s_pPopupBinding))  // a shortkey has been registered and grabbed to show/hide the dock => hide the dock.
			{
				gtk_widget_hide (pDock->container.pWidget);
			}
			else  // bind couldn't be done (no shortkey or couldn't grab it).
			{
				// g_print ("bind couldn't be done (no shortkey or couldn't grab it).\n");
				pDock->iVisibility = CAIRO_DOCK_VISI_KEEP_ABOVE;
			}
		}
		else if (bShortKey0)  // option is now disabled => show the dock.
		{
			_reposition_root_docks (FALSE);  // FALSE => tous.
		}
	}
	
	//\_______________ on arrete/demarre la scrutation des bords.
	gldi_container_update_polling_screen_edge ();
}


  /////////////////
 /// CALLBACKS ///
/////////////////

static gboolean _autohide_after_shortkey (CairoDock *pDock)
{
	if (pDock->iVisibility == CAIRO_DOCK_VISI_SHORTKEY && gldi_container_is_visible (CAIRO_CONTAINER (pDock)) && ! pDock->container.bInside)
		gtk_widget_hide (pDock->container.pWidget);
	pDock->iSidHideBack = 0;
	return FALSE;
}
static void _show_dock_at_mouse (CairoDock *pDock, G_GNUC_UNUSED gpointer data)
{
	if (pDock->iVisibility != CAIRO_DOCK_VISI_SHORTKEY)
		return;
	
	if (gldi_container_is_visible (CAIRO_CONTAINER (pDock)))  // already visible -> hide (toggle)
	{
		gtk_widget_hide (pDock->container.pWidget);
	}
	else  // invisible -> show
	{
		// calculate the position where the dock will be shown (under the mouse)
		gldi_container_update_mouse_position (CAIRO_CONTAINER (pDock));
	
		int W = gldi_dock_get_screen_width (pDock), H = gldi_dock_get_screen_height (pDock);
		int iScreenOffsetX = gldi_dock_get_screen_offset_x (pDock), iScreenOffsetY = gldi_dock_get_screen_offset_y (pDock);
		///pDock->iGapX = pDock->container.iWindowPositionX + iMouseX - g_desktopGeometry.iScreenWidth[pDock->container.bIsHorizontal] * pDock->fAlign;
		///pDock->iGapY = (pDock->container.bDirectionUp ? g_desktopGeometry.iScreenHeight[pDock->container.bIsHorizontal] - (pDock->container.iWindowPositionY + iMouseY) : pDock->container.iWindowPositionY + iMouseY);
		pDock->iGapX = pDock->container.iWindowPositionX + pDock->container.iMouseX - (W - pDock->container.iWidth) * pDock->fAlign - pDock->container.iWidth/2 - iScreenOffsetX;
		pDock->iGapY = (pDock->container.bDirectionUp ? H - (pDock->container.iWindowPositionY + pDock->container.iMouseY) : pDock->container.iWindowPositionY + pDock->container.iMouseY) - iScreenOffsetY;
		cd_debug (" => %d;%d", g_pMainDock->iGapX, g_pMainDock->iGapY);
	
		int iNewPositionX, iNewPositionY;
		cairo_dock_get_window_position_at_balance (pDock,
			pDock->container.iWidth, pDock->container.iHeight,
			&iNewPositionX, &iNewPositionY);
		cd_debug (" ==> %d;%d", iNewPositionX, iNewPositionY);
		if (iNewPositionX < 0)
			iNewPositionX = 0;
		else if (iNewPositionX + pDock->container.iWidth > W)
			iNewPositionX = W - pDock->container.iWidth;
	
		if (iNewPositionY < 0)
			iNewPositionY = 0;
		else if (iNewPositionY + pDock->container.iHeight > H)
			iNewPositionY = H - pDock->container.iHeight;
		
		// show the dock
		gtk_window_move (GTK_WINDOW (pDock->container.pWidget),
			(pDock->container.bIsHorizontal ? iNewPositionX : iNewPositionY),
			(pDock->container.bIsHorizontal ? iNewPositionY : iNewPositionX));
		gtk_widget_show (pDock->container.pWidget);
		
		// schedule a hiding in a few seconds
		if (pDock->iSidHideBack != 0)
			g_source_remove (pDock->iSidHideBack);
		pDock->iSidHideBack = g_timeout_add (3000, (GSourceFunc) _autohide_after_shortkey, (gpointer) pDock);
	}
}
static void _raise_from_shortcut (G_GNUC_UNUSED const char *cKeyShortcut, G_GNUC_UNUSED gpointer data)
{
	// g_print ("shortkey\n");
	gldi_docks_foreach_root ((GFunc)_show_dock_at_mouse, NULL);
}

static unsigned int s_sidDesktopGeom = 0;

static gboolean _reposition_root_docks_idle (G_GNUC_UNUSED void* dummy)
{
	s_sidDesktopGeom = 0;
	
	// update which screen the main dock should be shown since its config will not be reloaded
	_update_dock_screen_num (g_pMainDock);
	
	_reposition_root_docks (FALSE);  // FALSE <=> main dock included
	return G_SOURCE_REMOVE;
}

static gboolean _on_screen_geometry_changed (G_GNUC_UNUSED gpointer data, gboolean bSizeHasChanged)
{
	if (bSizeHasChanged && !s_sidDesktopGeom)
		s_sidDesktopGeom = g_idle_add (_reposition_root_docks_idle, NULL);
	return GLDI_NOTIFICATION_LET_PASS;
}

static gboolean _on_new_dialog (G_GNUC_UNUSED gpointer data, CairoDialog *pDialog)
{
	//\________________ hide sub-dock or label that would overlap it
	Icon *pIcon = pDialog->pIcon;
	if (! pIcon)
		return GLDI_NOTIFICATION_LET_PASS;
	
	if (pIcon->pSubDock)  // un sous-dock par-dessus le dialogue est tres genant.
	{
		gldi_dock_leave_synthetic (pIcon->pSubDock);
	}
	
	if (! gldi_container_use_new_positioning_code ())
	{
		GldiContainer *pContainer = cairo_dock_get_icon_container (pIcon);
		if (CAIRO_DOCK_IS_DOCK (pContainer) && cairo_dock_get_icon_max_scale (pIcon) < 1.01)  // view without zoom, the dialog is stuck to the icon, and therefore is under the label, so hide this one.
		{
			if (pIcon->iHideLabel == 0)
				gtk_widget_queue_draw (pContainer->pWidget);
			pIcon->iHideLabel ++;
		}
	}
	
	return GLDI_NOTIFICATION_LET_PASS;
}

static gboolean _render_dock_notification (G_GNUC_UNUSED gpointer pUserData, CairoDock *pDock, cairo_t *pCairoContext)
{
	if (pCairoContext)  // cairo
	{
		if (pDock->fHideOffset != 0 && g_pHidingBackend != NULL && g_pHidingBackend->pre_render)
			g_pHidingBackend->pre_render (pDock, pDock->fHideOffset, pCairoContext);
	
		if (pDock->iFadeCounter != 0 && g_pKeepingBelowBackend != NULL && g_pKeepingBelowBackend->pre_render)
			g_pKeepingBelowBackend->pre_render (pDock, (double) pDock->iFadeCounter / myBackendsParam.iHideNbSteps, pCairoContext);
		
		/// TODO: see if it's ok to not use the optimized rendering any more...
		/// if not, we can probably get the clip on the cairo context
		pDock->pRenderer->render (pCairoContext, pDock);
		
		if (pDock->fHideOffset != 0 && g_pHidingBackend != NULL && g_pHidingBackend->post_render)
			g_pHidingBackend->post_render (pDock, pDock->fHideOffset, pCairoContext);
	
		if (pDock->iFadeCounter != 0 && g_pKeepingBelowBackend != NULL && g_pKeepingBelowBackend->post_render)
			g_pKeepingBelowBackend->post_render (pDock, (double) pDock->iFadeCounter / myBackendsParam.iHideNbSteps, pCairoContext);
	}
	else  // opengl
	{
		if (pDock->fHideOffset != 0 && g_pHidingBackend != NULL && g_pHidingBackend->pre_render_opengl)
			g_pHidingBackend->pre_render_opengl (pDock, pDock->fHideOffset);
		
		if (pDock->iFadeCounter != 0 && g_pKeepingBelowBackend != NULL && g_pKeepingBelowBackend->pre_render_opengl)
			g_pKeepingBelowBackend->pre_render_opengl (pDock, (double) pDock->iFadeCounter / myBackendsParam.iHideNbSteps);
		
		pDock->pRenderer->render_opengl (pDock);
		
		if (pDock->fHideOffset != 0 && g_pHidingBackend != NULL && g_pHidingBackend->post_render_opengl)
			g_pHidingBackend->post_render_opengl (pDock, pDock->fHideOffset);
		
		if (pDock->iFadeCounter != 0 && g_pKeepingBelowBackend != NULL && g_pKeepingBelowBackend->post_render_opengl)
			g_pKeepingBelowBackend->post_render_opengl (pDock, (double) pDock->iFadeCounter / myBackendsParam.iHideNbSteps);
	}
	return GLDI_NOTIFICATION_LET_PASS;
}

static gboolean _on_leave_dock (G_GNUC_UNUSED gpointer data, CairoDock *pDock, G_GNUC_UNUSED gboolean *bStartAnimation)
{
	//g_print ("%s (%d, %d)\n", __func__, pDock->iRefCount, pDock->bHasModalWindow);
	
	//\_______________ On lance l'animation du dock.
	if (pDock->iRefCount == 0)
	{
		//g_print ("%s (auto-hide:%d)\n", __func__, pDock->bAutoHide);
		if (pDock->bAutoHide)
		{
			///pDock->fFoldingFactor = (myBackendsParam.bAnimateOnAutoHide ? 0.001 : 0.);
			cairo_dock_start_hiding (pDock);
		}
	}
	else if (pDock->icons != NULL)
	{
		pDock->fFoldingFactor = (myDocksParam.bAnimateSubDock ? 0.001 : 0.);
		Icon *pIcon = cairo_dock_search_icon_pointing_on_dock (pDock, NULL);
		//g_print ("'%s' se replie\n", pIcon?pIcon->cName:"none");
		gldi_object_notify (pIcon, NOTIFICATION_UNFOLD_SUBDOCK, pIcon);
	}
	//g_print ("start shrinking\n");
	cairo_dock_start_shrinking (pDock);  // on commence a faire diminuer la taille des icones.
	return GLDI_NOTIFICATION_LET_PASS;
}

static void _update_removing_inserting_icon_size (Icon *icon)
{
	icon->fInsertRemoveFactor *= .85;
	if (icon->fInsertRemoveFactor > 0)
	{
		if (icon->fInsertRemoveFactor < CAIRO_DOCK_ICON_INSERT_REMOVE_THRESHOLD)
			icon->fInsertRemoveFactor = CAIRO_DOCK_ICON_INSERT_REMOVE_THRESHOLD;
	}
	else if (icon->fInsertRemoveFactor < 0)
	{
		if (icon->fInsertRemoveFactor > -CAIRO_DOCK_ICON_INSERT_REMOVE_THRESHOLD)
			icon->fInsertRemoveFactor = -CAIRO_DOCK_ICON_INSERT_REMOVE_THRESHOLD;
	}
}

static gboolean on_update_inserting_removing_icon (G_GNUC_UNUSED gpointer pUserData, Icon *pIcon, CairoDock *pDock, gboolean *bContinueAnimation)
{
	if (pIcon->iGlideDirection != 0)
	{
		pIcon->fGlideOffset += pIcon->iGlideDirection * .1;
		if (fabs (pIcon->fGlideOffset) > .99)
		{
			pIcon->fGlideOffset = pIcon->iGlideDirection;
			pIcon->iGlideDirection = 0;
		}
		else if (fabs (pIcon->fGlideOffset) < .01)
		{
			pIcon->fGlideOffset = 0;
			pIcon->iGlideDirection = 0;
		}
		*bContinueAnimation = TRUE;
		cairo_dock_redraw_container (CAIRO_CONTAINER (pDock));
	}
	
	if (pIcon->fInsertRemoveFactor != 0) // the icon is being inserted/removed
	{
		_update_removing_inserting_icon_size (pIcon);
		if (fabs (pIcon->fInsertRemoveFactor) > CAIRO_DOCK_ICON_INSERT_REMOVE_THRESHOLD)  // the animation is not yet finished
		{
			cairo_dock_mark_icon_as_inserting_removing (pIcon);
			*bContinueAnimation = TRUE;
		}
		cairo_dock_redraw_container (CAIRO_CONTAINER (pDock));
	}
	return GLDI_NOTIFICATION_LET_PASS;
}

static gboolean on_insert_remove_icon (G_GNUC_UNUSED gpointer pUserData, Icon *pIcon, G_GNUC_UNUSED CairoDock *pDock)
{
	if (pIcon->fInsertRemoveFactor == 0)  // animation not needed.
		return GLDI_NOTIFICATION_LET_PASS;
	
	cairo_dock_mark_icon_as_inserting_removing (pIcon);  // On prend en charge le dessin de l'icone pendant sa phase d'insertion/suppression.
	
	return GLDI_NOTIFICATION_LET_PASS;
}

static gboolean on_stop_inserting_removing_icon (G_GNUC_UNUSED gpointer pUserData, Icon *pIcon)
{
	pIcon->fGlideOffset = 0;
	pIcon->iGlideDirection = 0;
	return GLDI_NOTIFICATION_LET_PASS;
}


  //////////////////
 /// GET CONFIG ///
//////////////////

static gboolean get_config (GKeyFile *pKeyFile, CairoDocksParam *pDocksParam)
{
	gboolean bFlushConfFileNeeded = FALSE;
	
	CairoDocksParam *pBackground = pDocksParam;
	CairoDocksParam *pPosition = pDocksParam;
	CairoDocksParam *pAccessibility = pDocksParam;
	CairoDocksParam *pSystem = pDocksParam;
	
	// frame
	pBackground->iDockRadius = cairo_dock_get_integer_key_value (pKeyFile, "Background", "corner radius", &bFlushConfFileNeeded, 12, NULL, NULL);

	pBackground->iDockLineWidth = cairo_dock_get_integer_key_value (pKeyFile, "Background", "line width", &bFlushConfFileNeeded, 2, NULL, NULL);

	pBackground->iFrameMargin = cairo_dock_get_integer_key_value (pKeyFile, "Background", "frame margin", &bFlushConfFileNeeded, 2, NULL, NULL);

	GldiColor couleur = {{0., 0., 0.6, 0.4}};
	cairo_dock_get_color_key_value (pKeyFile, "Background", "line color", &bFlushConfFileNeeded, &pBackground->fLineColor, &couleur, NULL, NULL);

	pBackground->bRoundedBottomCorner = cairo_dock_get_boolean_key_value (pKeyFile, "Background", "rounded bottom corner", &bFlushConfFileNeeded, TRUE, NULL, NULL);
	
	// background image
	int iStyle = cairo_dock_get_integer_key_value (pKeyFile, "Background", "style", &bFlushConfFileNeeded, -1, NULL, NULL);  // -1 pour intercepter le cas ou la cle n'existe pas.
	if (iStyle == -1)  // old params < 3.4
	{
		iStyle = g_key_file_get_integer (pKeyFile, "Background", "fill bg", NULL);
		iStyle ++;
		g_key_file_set_integer (pKeyFile, "Background", "style", iStyle);
	}
	
	if (iStyle == 0)
	{
		pBackground->bUseDefaultColors = TRUE;
		pBackground->iDockRadius = myStyleParam.iCornerRadius;
		pBackground->iDockLineWidth = myStyleParam.iLineWidth;
	}
	else if (iStyle == 1)
	{
		gchar *cBgImage = (iStyle == 1 ? cairo_dock_get_string_key_value (pKeyFile, "Background", "background image", &bFlushConfFileNeeded, NULL, NULL, NULL) : NULL);
		if (cBgImage != NULL)
		{
			pBackground->cBackgroundImageFile = cairo_dock_search_image_s_path (cBgImage);
			g_free (cBgImage);
			pBackground->fBackgroundImageAlpha = cairo_dock_get_double_key_value (pKeyFile, "Background", "image alpha", &bFlushConfFileNeeded, 0.5, NULL, NULL);
			pBackground->bBackgroundImageRepeat = cairo_dock_get_boolean_key_value (pKeyFile, "Background", "repeat image", &bFlushConfFileNeeded, FALSE, NULL, NULL);
		}
	}
	
	// background gradation
	if (iStyle != 0 && pBackground->cBackgroundImageFile == NULL)
	{
		pBackground->iNbStripes = cairo_dock_get_integer_key_value (pKeyFile, "Background", "number of stripes", &bFlushConfFileNeeded, 10, NULL, NULL);
		
		if (pBackground->iNbStripes != 0)
		{
			pBackground->fStripesWidth = MAX (.01, MIN (.99, cairo_dock_get_double_key_value (pKeyFile, "Background", "stripes width", &bFlushConfFileNeeded, 0.2, NULL, NULL))) / pBackground->iNbStripes;
		}
		GldiColor couleur3 = {{.7, .7, 1., .7}};
		cairo_dock_get_color_key_value (pKeyFile, "Background", "stripes color dark", &bFlushConfFileNeeded, &pBackground->fStripesColorDark, &couleur3, NULL, NULL);
		
		GldiColor couleur2 = {{.7, .9, .7, .4}};
		cairo_dock_get_color_key_value (pKeyFile, "Background", "stripes color bright", &bFlushConfFileNeeded, &pBackground->fStripesColorBright, &couleur2, NULL, NULL);
		
		pBackground->fStripesAngle = cairo_dock_get_double_key_value (pKeyFile, "Background", "stripes angle", &bFlushConfFileNeeded, 90., NULL, NULL);
	}
	
	pAccessibility->bExtendedMode = cairo_dock_get_boolean_key_value (pKeyFile, "Background", "extended", &bFlushConfFileNeeded, FALSE, "Accessibility", NULL);
	
	// hidden bg
	GldiColor hcolor = {{.8, .8, .8, .5}};
	cairo_dock_get_color_key_value (pKeyFile, "Background", "hidden bg color", &bFlushConfFileNeeded, &pBackground->fHiddenBg, &hcolor, NULL, NULL);
	
	// position
	pPosition->iGapX = cairo_dock_get_integer_key_value (pKeyFile, "Position", "x gap", &bFlushConfFileNeeded, 0, NULL, NULL);
	pPosition->iGapY = cairo_dock_get_integer_key_value (pKeyFile, "Position", "y gap", &bFlushConfFileNeeded, 0, NULL, NULL);

	pPosition->iScreenBorder = cairo_dock_get_integer_key_value (pKeyFile, "Position", "screen border", &bFlushConfFileNeeded, 0, NULL, NULL);
	if (pPosition->iScreenBorder >= CAIRO_DOCK_NB_POSITIONS)
		pPosition->iScreenBorder = 0;

	pPosition->fAlign = cairo_dock_get_double_key_value (pKeyFile, "Position", "alignment", &bFlushConfFileNeeded, 0.5, NULL, NULL);
	
	pPosition->iScreenReq = GLDI_DEFAULT_SCREEN;
	if (g_key_file_has_key (pKeyFile, "Position", "num_screen", NULL))  // "num_screen" is the new key
		pPosition->iScreenReq = g_key_file_get_integer (pKeyFile, "Position", "num_screen", NULL);
	else
	{
		if (g_key_file_has_key (pKeyFile, "Position", "xinerama", NULL))  // "xinerama" and "num screen" old keys
		{
			if (g_key_file_get_boolean (pKeyFile," Position", "xinerama", NULL))  // xinerama was used -> set num-screen back
			{
				pPosition->iScreenReq = g_key_file_get_integer (pKeyFile, "Position", "num screen", NULL); // "num screen" was the old key
			}
		}
		g_key_file_set_integer (pKeyFile, "Position", "num_screen", pPosition->iScreenReq);
		bFlushConfFileNeeded = TRUE;
	}
	
	//\____________________ Visibilite
	int iVisibility = cairo_dock_get_integer_key_value (pKeyFile, "Accessibility", "visibility", &bFlushConfFileNeeded, -1, NULL, NULL);  // -1 pour pouvoir intercepter le cas ou la cle n'existe pas.
	
	gchar *cShortkey = cairo_dock_get_string_key_value (pKeyFile, "Accessibility", "raise shortcut", &bFlushConfFileNeeded, NULL, "Position", NULL);
	
	pAccessibility->cHideEffect = cairo_dock_get_string_key_value (pKeyFile, "Accessibility", "hide effect", &bFlushConfFileNeeded, NULL, NULL, NULL);
	
	if (iVisibility == -1)  // option nouvelle 2.1.3
	{
		if (g_key_file_get_boolean (pKeyFile, "Accessibility", "reserve space", NULL))
			iVisibility = CAIRO_DOCK_VISI_KEEP_ABOVE;
		else if (g_key_file_get_boolean (pKeyFile, "Accessibility", "pop-up", NULL))  // on force au nouveau mode.
		{
			iVisibility = CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP_ANY;
			pAccessibility->cHideEffect = g_strdup_printf ("Fade out");  // on force a "fade out" pour garder le meme effet.
			g_key_file_set_string (pKeyFile, "Accessibility", "hide effect", pAccessibility->cHideEffect);
		}
		else if (g_key_file_get_boolean (pKeyFile, "Accessibility", "auto-hide", NULL))
			iVisibility = CAIRO_DOCK_VISI_AUTO_HIDE;
		else if (g_key_file_get_boolean (pKeyFile, "Accessibility", "auto quick hide on max", NULL))
			iVisibility = CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP_ANY;
		else if (cShortkey)
		{
			iVisibility = CAIRO_DOCK_VISI_SHORTKEY;
			pAccessibility->cRaiseDockShortcut = cShortkey;
			cShortkey = NULL;
		}
		else
			iVisibility = CAIRO_DOCK_VISI_KEEP_ABOVE;
		
		g_key_file_set_integer (pKeyFile, "Accessibility", "visibility", iVisibility);
	}
	else
	{
		if (pAccessibility->cHideEffect == NULL)  // nouvelle option 2.2.0, cela a change l'ordre du menu.
		{
			// avant c'etait : KEEP_ABOVE, RESERVE, KEEP_BELOW, AUTO_HIDE,       HIDE_ON_MAXIMIZED,   SHORTKEY
			// mtn c'est     : KEEP_ABOVE, RESERVE, KEEP_BELOW, HIDE_ON_OVERLAP, HIDE_ON_OVERLAP_ANY, AUTO_HIDE, VISI_SHORTKEY,
			if (iVisibility == 2)  // on force au nouveau mode.
			{
				iVisibility = CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP_ANY;
				pAccessibility->cHideEffect = g_strdup_printf ("Fade out");  // on force a "fade out" pour garder le meme effet.
				g_key_file_set_integer (pKeyFile, "Accessibility", "visibility", iVisibility);
				g_key_file_set_string (pKeyFile, "Accessibility", "hide effect", pAccessibility->cHideEffect);
			}
			else if (iVisibility == 3)
			{
				iVisibility = CAIRO_DOCK_VISI_AUTO_HIDE;
				g_key_file_set_integer (pKeyFile, "Accessibility", "visibility", iVisibility);
			}
			else if (iVisibility == 4)
			{
				iVisibility = CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP_ANY;
				g_key_file_set_integer (pKeyFile, "Accessibility", "visibility", iVisibility);
			}
			else if (iVisibility == 5)
			{
				iVisibility = CAIRO_DOCK_VISI_SHORTKEY;
				g_key_file_set_integer (pKeyFile, "Accessibility", "visibility", iVisibility);
			}
		}
		if (iVisibility == CAIRO_DOCK_VISI_SHORTKEY)
		{
			pAccessibility->cRaiseDockShortcut = cShortkey;
			cShortkey = NULL;
		}
	}
	pAccessibility->iVisibility = iVisibility;
	g_free (cShortkey);
	if (pAccessibility->cHideEffect == NULL) 
	{
		pAccessibility->cHideEffect = g_strdup_printf ("Move down");
		g_key_file_set_string (pKeyFile, "Accessibility", "hide effect", pAccessibility->cHideEffect);
	}
	
	pAccessibility->iCallbackMethod = cairo_dock_get_integer_key_value (pKeyFile, "Accessibility", "callback", &bFlushConfFileNeeded, CAIRO_HIT_DOCK_PLACE, NULL, NULL);
	
	if (pAccessibility->iCallbackMethod == CAIRO_HIT_ZONE)
	{
		if (! g_key_file_has_key (pKeyFile, "Accessibility", "zone size", NULL))
		{
			pAccessibility->iZoneWidth = 100;
			pAccessibility->iZoneHeight = 10;
			int list[2] = {pAccessibility->iZoneWidth, pAccessibility->iZoneHeight};
			g_key_file_set_integer_list (pKeyFile, "Accessibility", "zone size", list, 2);
		}
		cairo_dock_get_size_key_value_helper (pKeyFile, "Accessibility", "zone ", bFlushConfFileNeeded, pAccessibility->iZoneWidth, pAccessibility->iZoneHeight);
		if (pAccessibility->iZoneWidth < 20)
			pAccessibility->iZoneWidth = 20;
		if (pAccessibility->iZoneHeight < 2)
			pAccessibility->iZoneHeight = 2;
		pAccessibility->cZoneImage = cairo_dock_get_string_key_value (pKeyFile, "Accessibility", "callback image", &bFlushConfFileNeeded, 0, "Background", NULL);
		pAccessibility->fZoneAlpha = 1.;  // on laisse l'utilisateur definir la transparence qu'il souhaite directement dans l'image.
	}
	
	//\____________________ Autres parametres.
	double fSensitivity = cairo_dock_get_double_key_value (pKeyFile, "Accessibility", "edge sensitivity", &bFlushConfFileNeeded, -1, NULL, NULL);  // replace "unhide delay"
	if (fSensitivity < 0)  // old param
	{
		int iUnhideDockDelay = g_key_file_get_integer (pKeyFile, "Accessibility", "unhide delay", NULL);
		fSensitivity = iUnhideDockDelay / 1500.;  // 0 -> 0 = sensitive, 1500 -> 1 = not sensitive
		g_key_file_set_double (pKeyFile, "Accessibility", "edge sensitivity", fSensitivity);
	}
	pAccessibility->iUnhideDockDelay = fSensitivity * 1000;  // so we decreased the old delay by 1.5, since we handle mouse movements better.
	
	//\____________________ sous-docks.
	pAccessibility->iLeaveSubDockDelay = cairo_dock_get_integer_key_value (pKeyFile, "Accessibility", "leaving delay", &bFlushConfFileNeeded, 330, "System", NULL);
	pAccessibility->iShowSubDockDelay = cairo_dock_get_integer_key_value (pKeyFile, "Accessibility", "show delay", &bFlushConfFileNeeded, 300, "System", NULL);
	if (!g_key_file_has_key (pKeyFile, "Accessibility", "show_on_click", NULL))
	{
		pAccessibility->bShowSubDockOnClick = cairo_dock_get_boolean_key_value (pKeyFile, "Accessibility", "show on click", &bFlushConfFileNeeded, FALSE, "System", NULL);
		g_key_file_set_integer (pKeyFile, "Accessibility", "show_on_click", pAccessibility->bShowSubDockOnClick ? 1 : 0);
		bFlushConfFileNeeded = TRUE;
	}
	else
		pAccessibility->bShowSubDockOnClick = (cairo_dock_get_integer_key_value (pKeyFile, "Accessibility", "show_on_click", &bFlushConfFileNeeded, 0, NULL, NULL) == 1);
	
	//\____________________ lock
	///pAccessibility->bLockAll = cairo_dock_get_boolean_key_value (pKeyFile, "Accessibility", "lock all", &bFlushConfFileNeeded, FALSE, NULL, NULL);
	pAccessibility->bLockIcons = pAccessibility->bLockAll || cairo_dock_get_boolean_key_value (pKeyFile, "Accessibility", "lock icons", &bFlushConfFileNeeded, FALSE, NULL, NULL);
	
	// system
	pSystem->bAnimateSubDock = cairo_dock_get_boolean_key_value (pKeyFile, "System", "animate subdocks", &bFlushConfFileNeeded, TRUE, "Sub-Docks", NULL);
	
	return bFlushConfFileNeeded;
}


  ////////////////////
 /// RESET CONFIG ///
////////////////////

static void reset_config (CairoDocksParam *pDocksParam)
{
	CairoDocksParam *pBackground = pDocksParam;
	CairoDocksParam *pAccessibility = pDocksParam;
	
	// background
	g_free (pBackground->cBackgroundImageFile);
	
	// accessibility
	g_free (pAccessibility->cRaiseDockShortcut);
	g_free (pAccessibility->cHideEffect);
	g_free (pAccessibility->cZoneImage);
}


  ////////////
 /// LOAD ///
////////////

static void _load_visible_zone (const gchar *cVisibleZoneImageFile, int iVisibleZoneWidth, int iVisibleZoneHeight, double fVisibleZoneAlpha)
{
	cairo_dock_unload_image_buffer (&g_pVisibleZoneBuffer);
	
	cairo_dock_load_image_buffer_full (&g_pVisibleZoneBuffer,
		cVisibleZoneImageFile,
		iVisibleZoneWidth,
		iVisibleZoneHeight,
		CAIRO_DOCK_FILL_SPACE,
		fVisibleZoneAlpha);
}
static void load (void)
{
	_load_visible_zone (myDocksParam.cZoneImage, myDocksParam.iZoneWidth, myDocksParam.iZoneHeight, myDocksParam.fZoneAlpha);
	
	g_pHidingBackend = cairo_dock_get_hiding_effect (myDocksParam.cHideEffect);
	
	if (g_pKeepingBelowBackend == NULL)  // pas d'option en config pour ca.
		g_pKeepingBelowBackend = cairo_dock_get_hiding_effect ("Fade out");
	
	// register a key binding
	if (myDocksParam.iVisibility == CAIRO_DOCK_VISI_SHORTKEY)  // register a key binding
	{
		if (s_pPopupBinding == NULL)
		{
			s_pPopupBinding = gldi_shortkey_new (myDocksParam.cRaiseDockShortcut,
				"Cairo-Dock",
				_("Pop up the main dock"),
				GLDI_SHARE_DATA_DIR"/"CAIRO_DOCK_ICON,
				g_cConfFile,
				"Accessibility",
				"raise shortcut",
				(CDBindkeyHandler) _raise_from_shortcut,
				NULL);
		}
		else
		{
			gldi_shortkey_rebind (s_pPopupBinding, myDocksParam.cRaiseDockShortcut, NULL);
		}
	}
}


  //////////////
 /// RELOAD ///
//////////////

static void _reload_bg (CairoDock *pDock, G_GNUC_UNUSED gpointer data)
{
	pDock->backgroundBuffer.iWidth ++;  // force the reload
	cairo_dock_trigger_load_dock_background (pDock);
}
static void _init_hiding (CairoDock *pDock, G_GNUC_UNUSED gpointer data)
{
	if (pDock->bIsShowing || pDock->bIsHiding)
	{
		g_pHidingBackend->init (pDock);
	}
}
static void reload (CairoDocksParam *pPrevDocksParam, CairoDocksParam *pDocksParam)
{
	CairoDocksParam *pBackground = pDocksParam;
	CairoDocksParam *pPosition = pDocksParam;
	CairoDocksParam *pAccessibility = pDocksParam;
	// CairoDocksParam *pViews = pDocksParam;
	// CairoDocksParam *pSystem = pDocksParam;
	CairoDocksParam *pPrevBackground = pPrevDocksParam;
	CairoDocksParam *pPrevPosition = pPrevDocksParam;
	CairoDocksParam *pPrevAccessibility = pPrevDocksParam;
	// CairoDocksParam *pPrevViews = pPrevDocksParam;
	// CairoDocksParam *pPrevSystem = pPrevDocksParam;
	CairoDock *pDock = g_pMainDock;
	
	// background
	gldi_docks_foreach_root ((GFunc)_reload_bg, NULL);
	
	// position
	if (pPosition->iScreenReq != pPrevPosition->iScreenReq)
	{
		pDock->iScreenReq = pPosition->iScreenReq;
		_update_dock_screen_num (pDock);
		gldi_container_set_screen (CAIRO_CONTAINER (pDock), pDock->iNumScreen);
		_reposition_root_docks (TRUE);  // on replace tous les docks racines sauf le main dock, puisque c'est fait apres.
	}
	
	CairoDockTypeHorizontality bWasHorizontal = pDock->container.bIsHorizontal;
	if (pPosition->iScreenBorder != pPrevPosition->iScreenBorder)
	{
		_set_dock_orientation (pDock, pPosition->iScreenBorder);
		_reload_buffers_in_dock (pDock, TRUE, FALSE);  // icons may have a different width and height, so changing the orientation will affect them.  also, stack-icons may be drawn differently according to the orientation (ex.: box).
	}
	pDock->bExtendedMode = pBackground->bExtendedMode;
	pDock->iGapX = pPosition->iGapX;
	pDock->iGapY = pPosition->iGapY;
	pDock->fAlign = pPosition->fAlign;
	
	if (pPosition->iScreenReq != pPrevPosition->iScreenReq
	|| pPosition->iScreenBorder != pPrevPosition->iScreenBorder  // if the orientation or the screen has changed, the available size may have changed too
	|| pPosition->iGapX != pPrevPosition->iGapX
	|| pPosition->iGapY != pPrevPosition->iGapY)
	{
		cairo_dock_update_dock_size (pDock);
		cairo_dock_move_resize_dock (pDock);
		if (bWasHorizontal != pDock->container.bIsHorizontal)
			pDock->container.iWidth --;  // la taille dans le referentiel du dock ne change pas meme si on change d'horizontalite, par contre la taille de la fenetre change. On introduit donc un biais ici pour forcer le configure-event a faire son travail, sinon ca fausse le redraw.
	}
	else if (pPosition->fAlign != pPrevPosition->fAlign  // need to update the input zone
	|| pPrevBackground->iDockLineWidth != pBackground->iDockLineWidth  // frame size has changed
	|| pPrevBackground->iFrameMargin != pBackground->iFrameMargin)  // idem
	{
		cairo_dock_update_dock_size (pDock);
	}
	
	///cairo_dock_calculate_dock_icons (pDock);
	
	gldi_docks_redraw_all_root ();  // the background is a global parameter
	
	// accessibility
	//\_______________ Shortkey.
	if (pAccessibility->iVisibility == CAIRO_DOCK_VISI_SHORTKEY)
	{
		if (s_pPopupBinding == NULL)
		{
			s_pPopupBinding = gldi_shortkey_new (myDocksParam.cRaiseDockShortcut,
				"Cairo-Dock",
				_("Pop up the main dock"),
				GLDI_SHARE_DATA_DIR"/"CAIRO_DOCK_ICON,
				g_cCurrentThemePath,
				"Accessibility",
				"raise shortcut",
				(CDBindkeyHandler) _raise_from_shortcut,
				NULL);
		}
		else
		{
			gldi_shortkey_rebind (s_pPopupBinding, myDocksParam.cRaiseDockShortcut, NULL);
		}
	}
	else
	{
		gldi_object_unref (GLDI_OBJECT(s_pPopupBinding));
		s_pPopupBinding = NULL;
	}
	
	//\_______________ Hiding effect.
	if (g_strcmp0 (pAccessibility->cHideEffect, pPrevAccessibility->cHideEffect) != 0)
	{
		g_pHidingBackend = cairo_dock_get_hiding_effect (pAccessibility->cHideEffect);
		if (g_pHidingBackend && g_pHidingBackend->init)
		{
			gldi_docks_foreach_root ((GFunc)_init_hiding, NULL);  // si le dock est en cours d'animation, comme le backend est nouveau, il n'a donc pas ete initialise au debut de l'animation => on le fait ici.
		}
	}
	
	//\_______________ Callback zone.
	if (g_strcmp0 (pAccessibility->cZoneImage, pPrevAccessibility->cZoneImage) != 0
	|| pAccessibility->iZoneWidth != pPrevAccessibility->iZoneWidth
	|| pAccessibility->iZoneHeight != pPrevAccessibility->iZoneHeight
	|| pAccessibility->fZoneAlpha != pPrevAccessibility->fZoneAlpha)
	{
		_load_visible_zone (pAccessibility->cZoneImage, pAccessibility->iZoneWidth, pAccessibility->iZoneHeight, pAccessibility->fZoneAlpha);
		
		gldi_docks_redraw_all_root ();
	}
	
	gldi_dock_set_visibility (pDock, pAccessibility->iVisibility);
}


  //////////////
 /// UNLOAD ///
//////////////

static void unload (void)
{
	cairo_dock_unload_image_buffer (&g_pVisibleZoneBuffer);
	
	gldi_container_update_polling_screen_edge ();
	s_bQuickHide = FALSE;
	
	gldi_object_unref (GLDI_OBJECT(s_pPopupBinding));
	s_pPopupBinding = NULL;
}


  ////////////
 /// INIT ///
////////////

static gboolean on_style_changed (G_GNUC_UNUSED gpointer data)
{
	cd_debug ("Docks: style change to %d", myDocksParam.bUseDefaultColors);
	if (myDocksParam.bUseDefaultColors)  // reload bg
	{
		cd_debug (" reload dock's bg...");
		
		gboolean bNeedUpdateSize = (myDocksParam.iDockLineWidth != myStyleParam.iLineWidth);  // frame size changed
		myDocksParam.iDockRadius = myStyleParam.iCornerRadius;
		myDocksParam.iDockLineWidth = myStyleParam.iLineWidth;
		
		if (bNeedUpdateSize)  // update docks size and background
			_gldi_docks_foreach_root_no_data (cairo_dock_update_dock_size);
		else  // update docks background for color change
			gldi_docks_foreach_root ((GFunc)_reload_bg, NULL);
	}
	return GLDI_NOTIFICATION_LET_PASS;
}

static void init (void)
{
	s_hDocksTable = g_hash_table_new_full (g_str_hash,
		g_str_equal,
		NULL,  // name of the dock (points directly to the dock)
		NULL);  // dock
	
	/**gldi_object_register_notification (&myDockObjectMgr,
		NOTIFICATION_RENDER,
		(GldiNotificationFunc) _render_dock_notification,
		GLDI_RUN_FIRST, NULL);*/
	gldi_object_register_notification (&myDockObjectMgr,
		NOTIFICATION_LEAVE_DOCK,
		(GldiNotificationFunc) _on_leave_dock,  // is a notification so that others can prevent a dock from hiding (ex Slide view)
		GLDI_RUN_FIRST, NULL);
	gldi_object_register_notification (&myDockObjectMgr,
		NOTIFICATION_INSERT_ICON,
		(GldiNotificationFunc) on_insert_remove_icon,
		GLDI_RUN_AFTER, NULL);
	gldi_object_register_notification (&myDockObjectMgr,
		NOTIFICATION_REMOVE_ICON,
		(GldiNotificationFunc) on_insert_remove_icon,
		GLDI_RUN_AFTER, NULL);
	gldi_object_register_notification (&myIconObjectMgr,
		NOTIFICATION_UPDATE_ICON,
		(GldiNotificationFunc) on_update_inserting_removing_icon,
		GLDI_RUN_AFTER, NULL);
	gldi_object_register_notification (&myIconObjectMgr,
		NOTIFICATION_STOP_ICON,
		(GldiNotificationFunc) on_stop_inserting_removing_icon,
		GLDI_RUN_AFTER, NULL);
	gldi_object_register_notification (&myDesktopMgr,
		NOTIFICATION_DESKTOP_GEOMETRY_CHANGED,
		(GldiNotificationFunc) _on_screen_geometry_changed,
		GLDI_RUN_FIRST, NULL);
	gldi_object_register_notification (&myDialogObjectMgr,
		NOTIFICATION_NEW,
		(GldiNotificationFunc) _on_new_dialog,
		GLDI_RUN_AFTER, NULL);
	gldi_object_register_notification (&myStyleMgr,
		NOTIFICATION_STYLE_CHANGED,
		(GldiNotificationFunc) on_style_changed,
		GLDI_RUN_AFTER, NULL);
	
	// On Wayland, this only works on KWin yet (other compositors do not
	// send us window positions).
	gldi_docks_visibility_start ();
}


  ///////////////
 /// MANAGER ///
///////////////

static void init_object (GldiObject *obj, gpointer attr)
{
	CairoDock *pDock = (CairoDock*)obj;
	CairoDockAttr *dattr = (CairoDockAttr*)attr;
	
	// check everything is ok
	g_return_if_fail (dattr != NULL && dattr->cDockName != NULL);
	
	if (g_hash_table_lookup (s_hDocksTable, dattr->cDockName) != NULL)
	{
		cd_warning ("a dock with the name '%s' is already registered", dattr->cDockName);
		return;
	}

	//\__________________ init layer-shell (if enabled) and also set parent; needs to happen before window is mapped
	if (dattr->bSubDock && gldi_container_use_new_positioning_code ())
	{
		CairoDock *pParentDock = dattr->pParentDock;
		if (pParentDock == NULL)
			pParentDock = g_pMainDock;
		gtk_window_set_transient_for (GTK_WINDOW (pDock->container.pWidget), GTK_WINDOW (pParentDock->container.pWidget));
		// need to set now, so that gldi_dock_init_internals () will not try to show this dock
		pDock->iRefCount = 1;
	}
	{
		gchar *cNamespace = (! dattr->bSubDock) ? g_strdup_printf ("cairo-dock-%s", dattr->cDockName) : NULL;
		gldi_container_init_layer (&(pDock->container), cNamespace);
		g_free (cNamespace);
	}
	
	//\__________________ init internals
	gldi_dock_init_internals (pDock);
	if (s_bKeepAbove)
		gtk_window_set_keep_above (GTK_WINDOW (pDock->container.pWidget), s_bKeepAbove);
	
	//\__________________ initialize its parameters (it's a root dock by default)
	pDock->cDockName = g_strdup (dattr->cDockName);
	pDock->iAvoidingMouseIconType = -1;
	pDock->fFlatDockWidth = - myIconsParam.iIconGap;
	pDock->fMagnitudeMax = 1.;
	pDock->fPostHideOffset = 1.;
	pDock->iInputState = CAIRO_DOCK_INPUT_AT_REST;  // le dock est cree au repos. La zone d'input sera mis en place lors du configure.
	pDock->iIconSize = myIconsParam.iIconWidth;  // by default
	
	gldi_object_register_notification (pDock,
		NOTIFICATION_RENDER,
		(GldiNotificationFunc) _render_dock_notification,
		GLDI_RUN_FIRST, NULL);  /// we connect here, to pass before the manager... try to avoid this hack...
	
	//\__________________ register the dock
	if (g_hash_table_size (s_hDocksTable) == 0)  // c'est le 1er.
	{
		pDock->bIsMainDock = TRUE;
		g_pMainDock = pDock;
	}
	g_hash_table_insert (s_hDocksTable, pDock->cDockName, pDock);
	
	//\__________________ set the icons.
	GList *pIconList = dattr->pIconList;
	
	gldi_automatic_separators_add_in_list (pIconList);
	
	pDock->icons = pIconList;  // set icons now, before we set the ratio and the renderer.
	Icon *icon;
	GList *ic;
	for (ic = pIconList; ic != NULL; ic = ic->next)
	{
		icon = ic->data;
		if (icon->cParentDockName == NULL)
			icon->cParentDockName = g_strdup (pDock->cDockName);
		cairo_dock_set_icon_container (icon, pDock);
	}
	
	//\__________________ 
	if (! dattr->bSubDock)
	{
		gtk_window_set_title (GTK_WINDOW (pDock->container.pWidget), "cairo-dock");
		
		//\__________________ register it as a main dock
		s_pRootDockList = g_list_prepend (s_pRootDockList, pDock);
	
		//\__________________ set additional params from its config file
		_get_root_dock_config (pDock);
		gldi_container_set_screen (CAIRO_CONTAINER (pDock), pDock->iNumScreen); // this is a no-op on X11 (absolute coords are used to move the dock)
	}
	else
	{
		gtk_window_set_title (GTK_WINDOW (pDock->container.pWidget), "cairo-dock-sub");
		
		//\__________________ set additional params from its parent dock.
		CairoDock *pParentDock = dattr->pParentDock;
		if (pParentDock == NULL)
			pParentDock = g_pMainDock;
		
		pDock->container.bIsHorizontal = pParentDock->container.bIsHorizontal;
		pDock->container.bDirectionUp = pParentDock->container.bDirectionUp;
		pDock->iScreenReq = pParentDock->iScreenReq;
		pDock->iNumScreen = pParentDock->iNumScreen;
		pDock->iIconSize = pParentDock->iIconSize;
		
		pDock->container.fRatio = myBackendsParam.fSubDockSizeRatio;
		
		//\__________________ hide the dock if needed
		if (!gldi_container_use_new_positioning_code ())
		{
			// in this case, dock was shown in gldi_dock_init_internals (), we need to hide again
			pDock->iRefCount = 1;
			gtk_widget_hide (pDock->container.pWidget);
		}
	}
	
	//\__________________ set a renderer (got from the conf, or the default one).
	if (dattr->cRendererName)
		cairo_dock_set_renderer (pDock, dattr->cRendererName);  /// merge both functions ?...
	else
		cairo_dock_set_default_renderer (pDock);
	
	//\__________________ load the icons.
	if (pIconList != NULL)
	{
		_reload_buffers_in_dock (pDock, FALSE, TRUE);  // idle reload; FALSE = not recursively, TRUE = compute icons size
	}
	
	cairo_dock_update_dock_size (pDock);
}

static void reset_object (GldiObject *obj)
{
	CairoDock *pDock = (CairoDock*)obj;
	
	// stop timers
	if (pDock->iSidUnhideDelayed != 0)
		g_source_remove (pDock->iSidUnhideDelayed);
	if (pDock->iSidHideBack != 0)
		g_source_remove (pDock->iSidHideBack);
	if (pDock->iSidMoveResize != 0)
		g_source_remove (pDock->iSidMoveResize);
	if (pDock->iSidLeaveDemand != 0)
		g_source_remove (pDock->iSidLeaveDemand);
	if (pDock->iSidUpdateWMIcons != 0)
		g_source_remove (pDock->iSidUpdateWMIcons);
	if (pDock->iSidLoadBg != 0)
		g_source_remove (pDock->iSidLoadBg);
	if (pDock->iSidDestroyEmptyDock != 0)
		g_source_remove (pDock->iSidDestroyEmptyDock);
	if (pDock->iSidTestMouseOutside != 0)
		g_source_remove (pDock->iSidTestMouseOutside);
	if (pDock->iSidUpdateDockSize != 0)
		g_source_remove (pDock->iSidUpdateDockSize);
	
	// free icons that are still present
	GList *icons = pDock->icons;
	pDock->icons = NULL;  // remove the icons first, to avoid any use of 'icons' in the 'destroy' callbacks.
	GList *ic;
	for (ic = icons; ic != NULL; ic = ic->next)
	{
		Icon *pIcon = ic->data;
		cairo_dock_set_icon_container (pIcon, NULL);  // optimisation, to avoid detaching the icon from the container (it also prevents from auto-destroying the dock when it becomes empty)
		if (pIcon->pSubDock != NULL && s_bResetAll)  // if we're deleting the whole table, we don't want the icon to destroy its sub-dock
			pIcon->pSubDock = NULL;
		gldi_object_unref (GLDI_OBJECT(pIcon));
	}
	g_list_free (icons);
	
	// if it's a sub-dock, ensure the main icon looses its sub-dock
	if (pDock->iRefCount > 0)
	{
		Icon *pPointedIcon = cairo_dock_search_icon_pointing_on_dock (pDock, NULL);
		if (pPointedIcon != NULL)
			pPointedIcon->pSubDock = NULL;
	}
	
	// unregister it (unless we are deleting the whole table when this is done at once in cairo_dock_reset_docks_table ())
	if (! s_bResetAll)
	{
		g_hash_table_remove (s_hDocksTable, pDock->cDockName);
		s_pRootDockList = g_list_remove (s_pRootDockList, pDock);
	}
	
	// stop the mouse scrutation + dock visibility polling
	if (pDock->iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP
	|| pDock->iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP_ANY
	|| pDock->iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE
	|| pDock->iVisibility == CAIRO_DOCK_VISI_KEEP_BELOW)
	{
		// whether the visibility backend should be stopped for this dock
		gboolean bStopVis = (pDock->iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP
			|| pDock->iVisibility == CAIRO_DOCK_VISI_AUTO_HIDE_ON_OVERLAP_ANY);
		/// this will ensure that screen edge polling will stop for this dock
		pDock->iVisibility = CAIRO_DOCK_VISI_KEEP_ABOVE;
		pDock->bAutoHide = FALSE;
		pDock->bIsBelow = FALSE;
		gldi_container_update_polling_screen_edge ();
		if (bStopVis) gldi_dock_visibility_refresh (pDock); // will stop polling and free any data
	}
	
	// free data
	if (pDock->pShapeBitmap != NULL)
		cairo_region_destroy (pDock->pShapeBitmap);
	
	if (pDock->pHiddenShapeBitmap != NULL)
		cairo_region_destroy (pDock->pHiddenShapeBitmap);
	
	if (pDock->pActiveShapeBitmap != NULL)
		cairo_region_destroy (pDock->pActiveShapeBitmap);
	
	if (pDock->pRenderer != NULL && pDock->pRenderer->free_data != NULL)
	{
		pDock->pRenderer->free_data (pDock);
	}
	
	g_free (pDock->cRendererName);
	g_free (pDock->cBgImagePath);
	cairo_dock_unload_image_buffer (&pDock->backgroundBuffer);
	if (pDock->iFboId != 0)
		glDeleteFramebuffersEXT (1, &pDock->iFboId);
	if (pDock->iRedirectedTexture != 0)
		_cairo_dock_delete_texture (pDock->iRedirectedTexture);
	g_free (pDock->cDockName);
}

static gboolean delete_object (GldiObject *obj)
{
	CairoDock *pDock = (CairoDock*)obj;
	if (pDock->bIsMainDock)  // can't delete the main dock
	{
		return FALSE;
	}
	
	// remove the conf file
	_remove_root_dock_config (pDock->cDockName);
	
	// delete all the icons
	GList *icons = pDock->icons;
	pDock->icons = NULL;  // remove the icons first, to avoid any use of 'icons' in the 'destroy' callbacks.
	GList *ic;
	for (ic = icons; ic != NULL; ic = ic->next)
	{
		Icon *pIcon = ic->data;
		cairo_dock_set_icon_container (pIcon, NULL);  // optimisation, to avoid detaching the icon from the container.
		gldi_object_delete (GLDI_OBJECT(pIcon));
	}
	///g_list_foreach (icons, (GFunc)gldi_object_delete, NULL);
	g_list_free (icons);
	
	return TRUE;
}

static GKeyFile* reload_object (GldiObject *obj, gboolean bReloadConf, G_GNUC_UNUSED GKeyFile *pKeyFile)
{
	CairoDock *pDock = (CairoDock*)obj;
	
	if (bReloadConf)  // maybe we should update the parameters that have the global value ?...
		_reposition_one_root_dock (NULL, pDock, GINT_TO_POINTER (TRUE)); // will call _get_root_dock_config () as needed
	
	cairo_dock_set_default_renderer (pDock);
	
	pDock->backgroundBuffer.iWidth ++;  // pour forcer le chargement du fond.
	_reload_buffers_in_dock (pDock, TRUE, TRUE);
	
	_cairo_dock_draw_one_subdock_icon (NULL, pDock, NULL);  // container-icons may be drawn differently according to the orientation (ex.: box). must be done after sub-docks are reloaded.
	
	gtk_widget_queue_draw (pDock->container.pWidget);
	return NULL;
}

void gldi_register_docks_manager (void)
{
	// Manager
	memset (&myDocksMgr, 0, sizeof (GldiManager));
	gldi_object_init (GLDI_OBJECT(&myDocksMgr), &myManagerObjectMgr, NULL);
	myDocksMgr.cModuleName   = "Docks";
	// interface
	myDocksMgr.init          = init;
	myDocksMgr.load          = load;
	myDocksMgr.unload        = unload;
	myDocksMgr.reload        = (GldiManagerReloadFunc)reload;
	myDocksMgr.get_config    = (GldiManagerGetConfigFunc)get_config;
	myDocksMgr.reset_config  = (GldiManagerResetConfigFunc)reset_config;
	// Config
	memset (&myDocksParam, 0, sizeof (CairoDocksParam));
	myDocksMgr.pConfig = (GldiManagerConfigPtr)&myDocksParam;
	myDocksMgr.iSizeOfConfig = sizeof (CairoDocksParam);
	// data
	memset (&g_pVisibleZoneBuffer, 0, sizeof (CairoDockImageBuffer));
	myDocksMgr.pData = (GldiManagerDataPtr)NULL;
	myDocksMgr.iSizeOfData = 0;
	
	// Object Manager
	memset (&myDockObjectMgr, 0, sizeof (GldiObjectManager));
	myDockObjectMgr.cName         = "Dock";
	myDockObjectMgr.iObjectSize   = sizeof (CairoDock);
	// interface
	myDockObjectMgr.init_object   = init_object;
	myDockObjectMgr.reset_object  = reset_object;
	myDockObjectMgr.delete_object = delete_object;
	myDockObjectMgr.reload_object = reload_object;
	// signals
	gldi_object_install_notifications (&myDockObjectMgr, NB_NOTIFICATIONS_DOCKS);
	// parent object
	gldi_object_set_manager (GLDI_OBJECT (&myDockObjectMgr), &myContainerObjectMgr);
}
