/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */

/* AbiWord
 * Copyright (C) 1998 AbiSource, Inc.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <gdk/gdkkeysyms.h>
#include "ut_endian.h"
#include "xap_UnixApp.h"
#include "xap_UnixFontManager.h"
#include "xap_UnixFont.h"
#include "xap_UnixNullGraphics.h"

#include "gr_UnixGraphics.h"
#include "gr_UnixImage.h"
#include "ut_sleep.h"
#include "xap_Strings.h"

#include "ut_debugmsg.h"
#include "ut_assert.h"
#include "ut_misc.h"
#include "ut_string.h"
#include "xap_UnixDialogHelper.h"
#include "ut_wctomb.h"
#include "xap_EncodingManager.h"
#include "ut_OverstrikingChars.h"
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>

#ifndef FT_ENCODING_ADOBE_CUSTOM
#define FT_ENCODING_ADOBE_CUSTOM ft_encoding_adobe_custom
#endif

static const UT_uint32 adobeDUni[/*202*/][2] =
	{
		{0x0020,0x0020},
		{0x0021,0x2701},
		{0x0022,0x2702},
		{0x0023,0x2703},
		{0x0024,0x2704},
		{0x0025,0x260E},
		{0x0026,0x2706},
		{0x0027,0x2707},
		{0x0028,0x2708},
		{0x0029,0x2709},
		{0x002A,0x261B},
		{0x002B,0x261E},
		{0x002C,0x270C},
		{0x002D,0x270D},
		{0x002E,0x270E},
		{0x002F,0x270F},
		{0x0030,0x2710},
		{0x0031,0x2711},
		{0x0032,0x2712},
		{0x0033,0x2713},
		{0x0034,0x2714},
		{0x0035,0x2715},
		{0x0036,0x2716},
		{0x0037,0x2717},
		{0x0038,0x2718},
		{0x0039,0x2719},
		{0x003A,0x271A},
		{0x003B,0x271B},
		{0x003C,0x271C},
		{0x003D,0x271D},
		{0x003E,0x271E},
		{0x003F,0x271F},
		{0x0040,0x2720},
		{0x0041,0x2721},
		{0x0042,0x2722},
		{0x0043,0x2723},
		{0x0044,0x2724},
		{0x0045,0x2725},
		{0x0046,0x2726},
		{0x0047,0x2727},
		{0x0048,0x2605},
		{0x0049,0x2729},
		{0x004A,0x272A},
		{0x004B,0x272B},
		{0x004C,0x272C},
		{0x004D,0x272D},
		{0x004E,0x272E},
		{0x004F,0x272F},
		{0x0050,0x2730},
		{0x0051,0x2731},
		{0x0052,0x2732},
		{0x0053,0x2733},
		{0x0054,0x2734},
		{0x0055,0x2735},
		{0x0056,0x2736},
		{0x0057,0x2737},
		{0x0058,0x2738},
		{0x0059,0x2739},
		{0x005A,0x273A},
		{0x005B,0x273B},
		{0x005C,0x273C},
		{0x005D,0x273D},
		{0x005E,0x273E},
		{0x005F,0x273F},
		{0x0060,0x2740},
		{0x0061,0x2741},
		{0x0062,0x2742},
		{0x0063,0x2743},
		{0x0064,0x2744},
		{0x0065,0x2745},
		{0x0066,0x2746},
		{0x0067,0x2747},
		{0x0068,0x2748},
		{0x0069,0x2749},
		{0x006A,0x274A},
		{0x006B,0x274B},
		{0x006C,0x25CF},
		{0x006D,0x274D},
		{0x006E,0x25A0},
		{0x006F,0x274F},
		{0x0070,0x2750},
		{0x0071,0x2751},
		{0x0072,0x2752},
		{0x0073,0x25B2},
		{0x0074,0x25BC},
		{0x0075,0x25C6},
		{0x0076,0x2756},
		{0x0077,0x25D7},
		{0x0078,0x2758},
		{0x0079,0x2759},
		{0x007A,0x275A},
		{0x007B,0x275B},
		{0x007C,0x275C},
		{0x007D,0x275D},
		{0x007E,0x275E},
		{0x0080,0xF8D7},
		{0x0081,0xF8D8},
		{0x0082,0xF8D9},
		{0x0083,0xF8DA},
		{0x0084,0xF8DB},
		{0x0085,0xF8DC},
		{0x0086,0xF8DD},
		{0x0087,0xF8DE},
		{0x0088,0xF8DF},
		{0x0089,0xF8E0},
		{0x008A,0xF8E1},
		{0x008B,0xF8E2},
		{0x008C,0xF8E3},
		{0x008D,0xF8E4},
		{0x00A1,0x2761},
		{0x00A2,0x2762},
		{0x00A3,0x2763},
		{0x00A4,0x2764},
		{0x00A5,0x2765},
		{0x00A6,0x2766},
		{0x00A7,0x2767},
		{0x00A8,0x2663},
		{0x00A9,0x2666},
		{0x00AA,0x2665},
		{0x00AB,0x2660},
		{0x00AC,0x2460},
		{0x00AD,0x2461},
		{0x00AE,0x2462},
		{0x00AF,0x2463},
		{0x00B0,0x2464},
		{0x00B1,0x2465},
		{0x00B2,0x2466},
		{0x00B3,0x2467},
		{0x00B4,0x2468},
		{0x00B5,0x2469},
		{0x00B6,0x2776},
		{0x00B7,0x2777},
		{0x00B8,0x2778},
		{0x00B9,0x2779},
		{0x00BA,0x277A},
		{0x00BB,0x277B},
		{0x00BC,0x277C},
		{0x00BD,0x277D},
		{0x00BE,0x277E},
		{0x00BF,0x277F},
		{0x00C0,0x2780},
		{0x00C1,0x2781},
		{0x00C2,0x2782},
		{0x00C3,0x2783},
		{0x00C4,0x2784},
		{0x00C5,0x2785},
		{0x00C6,0x2786},
		{0x00C7,0x2787},
		{0x00C8,0x2788},
		{0x00C9,0x2789},
		{0x00CA,0x278A},
		{0x00CB,0x278B},
		{0x00CC,0x278C},
		{0x00CD,0x278D},
		{0x00CE,0x278E},
		{0x00CF,0x278F},
		{0x00D0,0x2790},
		{0x00D1,0x2791},
		{0x00D2,0x2792},
		{0x00D3,0x2793},
		{0x00D4,0x2794},
		{0x00D5,0x2192},
		{0x00D6,0x2194},
		{0x00D7,0x2195},
		{0x00D8,0x2798},
		{0x00D9,0x2799},
		{0x00DA,0x279A},
		{0x00DB,0x279B},
		{0x00DC,0x279C},
		{0x00DD,0x279D},
		{0x00DE,0x279E},
		{0x00DF,0x279F},
		{0x00E0,0x27A0},
		{0x00E1,0x27A1},
		{0x00E2,0x27A2},
		{0x00E3,0x27A3},
		{0x00E4,0x27A4},
		{0x00E5,0x27A5},
		{0x00E6,0x27A6},
		{0x00E7,0x27A7},
		{0x00E8,0x27A8},
		{0x00E9,0x27A9},
		{0x00EA,0x27AA},
		{0x00EB,0x27AB},
		{0x00EC,0x27AC},
		{0x00ED,0x27AD},
		{0x00EE,0x27AE},
		{0x00EF,0x27AF},
		{0x00F1,0x27B1},
		{0x00F2,0x27B2},
		{0x00F3,0x27B3},
		{0x00F4,0x27B4},
		{0x00F5,0x27B5},
		{0x00F6,0x27B6},
		{0x00F7,0x27B7},
		{0x00F8,0x27B8},
		{0x00F9,0x27B9},
		{0x00FA,0x27BA},
		{0x00FB,0x27BB},
		{0x00FC,0x27BC},
		{0x00FD,0x27BD},
		{0x00FE,0x27BE},
		{255,100000}
	};

static const UT_uint32 adobeSUni[/*185*/][2] =
	{
		{32,32},
		{33,33},
		{34,8704},
		{35,35},
		{36,8707},
		{37,37},
		{38,38},
		{39,8715},
		{40,40},
		{41,41},
		{42,8727},
		{43,43},
		{44,44},
		{45,8722},
		{46,46},
		{47,47},
		{48,48},
		{49,49},
		{50,50},
		{51,51},
		{52,52},
		{53,53},
		{54,54},
		{55,55},
		{56,56},
		{57,57},
		{58,58},
		{59,59},
		{60,60},
		{61,61},
		{62,62},
		{63,63},
		{64,8773},
		{65,913},
		{66,914},
		{67,935},
		{68,8710},
		{69,917},
		{70,934},
		{71,915},
		{72,919},
		{73,921},
		{74,977},
		{75,922},
		{76,923},
		{77,924},
		{78,925},
		{79,927},
		{80,928},
		{81,920},
		{82,929},
		{83,931},
		{84,932},
		{85,933},
		{86,962},
		{87,8486},
		{88,926},
		{89,936},
		{90,918},
		{91,91},
		{92,8756},
		{93,93},
		{94,8869},
		{95,95},
		{96,63717},
		{97,945},
		{98,946},
		{99,967},
		{100,948},
		{101,949},
		{102,966},
		{103,947},
		{104,951},
		{105,953},
		{106,981},
		{107,954},
		{108,955},
		{109,181},
		{110,957},
		{111,959},
		{112,960},
		{113,952},
		{114,961},
		{115,963},
		{116,964},
		{117,965},
		{119,969},
		{120,958},
		{121,968},
		{122,950},
		{123,123},
		{124,124},
		{125,125},
		{126,8764},
		{163,8804},
		{164,8260},
		{165,8734},
		{166,402},
		{167,9827},
		{168,9830},
		{169,9829},
		{170,9824},
		{171,8596},
		{172,8592},
		{173,8593},
		{174,8594},
		{175,8595},
		{176,176},
		{177,177},
		{179,8805},
		{180,215},
		{181,8733},
		{182,8706},
		{183,8226},
		{184,247},
		{185,8800},
		{186,8801},
		{187,8776},
		{188,8230},
		{189,63718},
		{190,63719},
		{191,8629},
		{192,8501},
		{193,8465},
		{194,8476},
		{195,8472},
		{196,8855},
		{197,8853},
		{198,8709},
		{199,8745},
		{200,8746},
		{201,8835},
		{202,8839},
		{203,8836},
		{204,8834},
		{205,8838},
		{206,8712},
		{207,8713},
		{208,8736},
		{209,8711},
		{210,0},
		{211,63193},
		{212,63195},
		{213,8719},
		{214,8730},
		{215,8901},
		{216,172},
		{217,8743},
		{218,8744},
		{219,8660},
		{220,8656},
		{221,8657},
		{222,8658},
		{223,8659},
		{224,9674},
		{225,9001},
		{226,0},
		{227,63721},
		{228,63722},
		{229,8721},
		{230,63723},
		{231,63724},
		{232,63725},
		{233,63726},
		{234,63727},
		{235,63728},
		{236,63729},
		{237,63730},
		{238,0},
		{239,63732},
		{241,9002},
		{242,8747},
		{243,8992},
		{244,63733},
		{245,8993},
		{246,63734},
		{247,63735},
		{248,63736},
		{249,63737},
		{250,63738},
		{251,63739},
		{252,63740},
		{253,63741},
		{254,63742},
		{255,100000}
	};

extern UT_uint32 adobeToUnicode(UT_uint32 iAdobe)
{
	UT_uint32 low = adobeSUni[0][0];
	UT_uint32 high = adobeSUni[183][0];
	if(iAdobe < low)
	{
		return iAdobe;
	}
	if(iAdobe > high)
	{
		return iAdobe;
	}
	UT_sint32 slow = static_cast<UT_sint32>(iAdobe) - 72;
	if(slow < 0)
	{ 
		slow = 0;
	}
	while(adobeSUni[slow][0] != iAdobe && slow < 255)
	{
		xxx_UT_DEBUGMSG(("char at %d is %d value %d \n",slow,adobeSUni[slow][0],adobeSUni[slow][1]));
		slow++;
	}
	xxx_UT_DEBUGMSG(("Input %d return %d \n",iAdobe,adobeSUni[slow][1]));
	if(slow > 255)
	{
		return iAdobe;
	}
	return adobeSUni[slow][1];
}

extern UT_uint32 adobeDingbatsToUnicode(UT_uint32 iAdobe)
{
	
#if 1
	UT_uint32 low = adobeDUni[0][0];
	UT_uint32 high = adobeDUni[202][0];
	if(iAdobe < low)
	{
		return iAdobe;
	}
	if(iAdobe > high)
	{
		return iAdobe;
	}
	UT_sint32 slow = static_cast<UT_sint32>(iAdobe) - 32;
	if(slow < 0)
	{ 
		slow = 0;
	}
	while(adobeDUni[slow][0] != iAdobe && slow < 255)
	{
		xxx_UT_DEBUGMSG(("char at %d is %d value %d \n",slow,adobeDUni[slow][0],adobeSUni[slow][1]));
		slow++;
	}
	xxx_UT_DEBUGMSG(("Input %d return %d \n",iAdobe,adobeDUni[slow][1]));
	if(slow > 255)
	{
		return iAdobe;
	}
	return adobeDUni[slow][1];
#endif
}

const char* GR_UnixGraphics::findNearestFont(const char* pszFontFamily,
											 const char* pszFontStyle,
											 const char* pszFontVariant,
											 const char* pszFontWeight,
											 const char* pszFontStretch,
											 const char* pszFontSize)
{
	xxx_UT_DEBUGMSG(("Find Font findNearestFont for %s \n",pszFontFamily));
	XAP_UnixFont* pUnixFont = XAP_UnixFontManager::pFontManager->findNearestFont(pszFontFamily, pszFontStyle,
																				 pszFontVariant, pszFontWeight,
																				 pszFontStretch, pszFontSize);
	xxx_UT_DEBUGMSG(("Return Font name %s \n",pUnixFont->getName()));
	//	UT_ASSERT(0);
	return pUnixFont->getName();
}

//
// Below this size we use GDK fonts. Above it we use metric info.
//
#define MAX_ABI_GDK_FONT_SIZE 200
#define FALLBACK_FONT_SIZE 12

UT_uint32 				GR_UnixGraphics::s_iInstanceCount = 0;

GR_UnixGraphics::GR_UnixGraphics(GdkWindow * win, XAP_UnixFontManager * fontManager, XAP_App * app):m_iLineWidth(tlu(1))
{
	m_pApp = app;
	m_pWin = win;
	m_pFontManager = fontManager;
	m_pFont = NULL;
	m_pSingleByteFont = NULL;
	m_pMultiByteFont = NULL;
	m_pFontGUI = NULL;
	s_iInstanceCount++;
	m_pColormap = gdk_rgb_get_colormap();

	//
	// Martin's attempt to make double buffering work.with xft
	//
	m_iXoff = 0;
	m_iYoff = 0;
	GdkDrawable * realDraw;
	gdk_window_get_internal_paint_info (m_pWin, &realDraw,&m_iXoff,&m_iYoff);
    m_iXoff = tlu(m_iXoff); m_iYoff = tlu(m_iYoff);
	m_pGC = gdk_gc_new(realDraw);
	m_pXORGC = gdk_gc_new(realDraw);
	m_pVisual = GDK_VISUAL_XVISUAL( gdk_drawable_get_visual(realDraw));
	m_Drawable = gdk_x11_drawable_get_xid(realDraw);

	m_pXftFontL = NULL;
	m_pXftFontD = NULL;
	m_Colormap = GDK_COLORMAP_XCOLORMAP(m_pColormap);
	m_pXftDraw = XftDrawCreate(GDK_DISPLAY(), m_Drawable, m_pVisual, m_Colormap);
	gdk_gc_set_function(m_pXORGC, GDK_XOR);

 	GdkColor clrWhite;
	clrWhite.red = clrWhite.green = clrWhite.blue = 65535;
	gdk_colormap_alloc_color (m_pColormap, &clrWhite, FALSE, TRUE);
	gdk_gc_set_foreground(m_pXORGC, &clrWhite);

 	GdkColor clrBlack;
	clrBlack.red = clrBlack.green = clrBlack.blue = 0;
	gdk_colormap_alloc_color (m_pColormap, &clrBlack, FALSE, TRUE);
	gdk_gc_set_foreground(m_pGC, &clrBlack);

	m_XftColor.color.red = clrBlack.red;
	m_XftColor.color.green = clrBlack.green;
	m_XftColor.color.blue = clrBlack.blue;
	m_XftColor.color.alpha = 0xffff;
	m_XftColor.pixel = clrBlack.pixel;

	// I only want to set CAP_NOT_LAST, but the call takes all
	// arguments (and doesn't have a default value).  Set the
	// line attributes to not draw the last pixel.

	// We force the line width to be zero because the CAP_NOT_LAST
	// stuff does not seem to work correctly when the width is set
	// to one.

	gdk_gc_set_line_attributes(m_pGC,   0,GDK_LINE_SOLID,GDK_CAP_NOT_LAST,GDK_JOIN_MITER);
	gdk_gc_set_line_attributes(m_pXORGC,0,GDK_LINE_SOLID,GDK_CAP_NOT_LAST,GDK_JOIN_MITER);

	// Set GraphicsExposes so that XCopyArea() causes an expose on
	// obscured regions rather than just tiling in the default background.
	gdk_gc_set_exposures(m_pGC,1);
	gdk_gc_set_exposures(m_pXORGC,1);

	m_cs = GR_Graphics::GR_COLORSPACE_COLOR;
	m_cursor = GR_CURSOR_INVALID;
	setCursor(GR_CURSOR_DEFAULT);
	m_bIsSymbol = false;
	m_bIsDingbat = false;

	if (m_pFontManager)
		m_pFallBackFontHandle = new XAP_UnixFontHandle(m_pFontManager->getDefaultFont(),
													   FALLBACK_FONT_SIZE);
	else
		m_pFallBackFontHandle = NULL;
}

bool GR_UnixGraphics::isDingbat(void) const
{
	return m_bIsDingbat;
}


bool GR_UnixGraphics::isSymbol(void) const
{
	return m_bIsSymbol;
}

GR_UnixGraphics::GR_UnixGraphics(GdkPixmap * win, XAP_UnixFontManager * fontManager, XAP_App * app, bool bUseDrawable):m_iLineWidth(tlu(1))
{
	m_pApp = app;
	m_pWin = static_cast<GdkWindow *>(win);
	m_pFontManager = fontManager;
	m_pFont = NULL;
	m_pSingleByteFont = NULL;
	m_pMultiByteFont = NULL;
	m_pFontGUI = NULL;
	s_iInstanceCount++;
	m_pColormap = gdk_rgb_get_colormap();


	//
	// Martin's attempt to make double buffering work.with xft
	//
	m_iXoff = 0;
	m_iYoff = 0;
	GdkDrawable * realDraw = static_cast<GdkDrawable *>(win);
	if(bUseDrawable)
	{
		realDraw = static_cast<GdkDrawable *>(win);
	}

    m_iXoff = tlu(m_iXoff); m_iYoff = tlu(m_iYoff);
	m_pGC = gdk_gc_new(realDraw);
	m_pXORGC = gdk_gc_new(realDraw);
	m_pVisual = GDK_VISUAL_XVISUAL( gdk_drawable_get_visual(realDraw));
	m_Drawable = gdk_x11_drawable_get_xid(realDraw);

	m_pXftFontL = NULL;
	m_pXftFontD = NULL;
	m_Colormap = GDK_COLORMAP_XCOLORMAP(m_pColormap);
	m_pXftDraw = XftDrawCreate(GDK_DISPLAY(), m_Drawable, m_pVisual, m_Colormap);
	gdk_gc_set_function(m_pXORGC, GDK_XOR);

 	GdkColor clrWhite;
	clrWhite.red = clrWhite.green = clrWhite.blue = 65535;
	gdk_colormap_alloc_color (m_pColormap, &clrWhite, FALSE, TRUE);
	gdk_gc_set_foreground(m_pXORGC, &clrWhite);

 	GdkColor clrBlack;
	clrBlack.red = clrBlack.green = clrBlack.blue = 0;
	gdk_colormap_alloc_color (m_pColormap, &clrBlack, FALSE, TRUE);
	gdk_gc_set_foreground(m_pGC, &clrBlack);

	m_XftColor.color.red = clrBlack.red;
	m_XftColor.color.green = clrBlack.green;
	m_XftColor.color.blue = clrBlack.blue;
	m_XftColor.color.alpha = 0xffff;
	m_XftColor.pixel = clrBlack.pixel;

	// I only want to set CAP_NOT_LAST, but the call takes all
	// arguments (and doesn't have a default value).  Set the
	// line attributes to not draw the last pixel.

	// We force the line width to be zero because the CAP_NOT_LAST
	// stuff does not seem to work correctly when the width is set
	// to one.

	gdk_gc_set_line_attributes(m_pGC,   0,GDK_LINE_SOLID,GDK_CAP_NOT_LAST,GDK_JOIN_MITER);
	gdk_gc_set_line_attributes(m_pXORGC,0,GDK_LINE_SOLID,GDK_CAP_NOT_LAST,GDK_JOIN_MITER);

	// Set GraphicsExposes so that XCopyArea() causes an expose on
	// obscured regions rather than just tiling in the default background.
	gdk_gc_set_exposures(m_pGC,1);
	gdk_gc_set_exposures(m_pXORGC,1);

	m_cs = GR_Graphics::GR_COLORSPACE_COLOR;
	m_cursor = GR_CURSOR_INVALID;
	setCursor(GR_CURSOR_DEFAULT);
	m_bIsSymbol = false;
	m_bIsDingbat = false;

	if (m_pFontManager)
		m_pFallBackFontHandle = new XAP_UnixFontHandle(m_pFontManager->getDefaultFont(),
													   FALLBACK_FONT_SIZE);
	else
		m_pFallBackFontHandle = NULL;
}

GR_UnixGraphics::~GR_UnixGraphics()
{
	DELETEP(m_pFontGUI);

	s_iInstanceCount--;

	/* WARNING: Don't use XftDrawDestroy.  XftDrawDestroy will also destroy the drawable */
	if (m_pXftDraw)
		free(m_pXftDraw);

	delete m_pFallBackFontHandle;

	UT_VECTOR_PURGEALL(UT_Rect*, m_vSaveRect);

	// purge saved pixbufs
	for (UT_uint32 i = 0; i < m_vSaveRectBuf.size (); i++)
		{
			GdkPixbuf * pix = static_cast<GdkPixbuf *>(m_vSaveRectBuf.getNthItem (i));
			g_object_unref (G_OBJECT (pix));
		}

	g_object_unref (G_OBJECT(m_pGC));
	g_object_unref (G_OBJECT(m_pXORGC));
}

bool GR_UnixGraphics::queryProperties(GR_Graphics::Properties gp) const
{
	switch (gp)
	{
		case DGP_SCREEN:
		case DGP_OPAQUEOVERLAY:
			return true;
		case DGP_PAPER:
			return false;
		default:
			UT_ASSERT(0);
			return false;
	}
}

void GR_UnixGraphics::setZoomPercentage(UT_uint32 iZoom)
{
	GR_Graphics::setZoomPercentage (iZoom); // chain up

	// If we have a font at all then fetch a device font suitable for the new
	// zoom level.
	if (m_pFont)
		m_pXftFontD = m_pFont->getDeviceXftFont(iZoom);
	DELETEP(m_pFontGUI);
	m_pFontGUI = NULL;
	m_pFontGUI = static_cast<XAP_UnixFontHandle*>(getGUIFont());
}

static GdkCapStyle mapCapStyle ( GR_Graphics::CapStyle in )
{
	switch ( in )
    {
		case GR_Graphics::CAP_ROUND :
			return GDK_CAP_ROUND ;
		case GR_Graphics::CAP_PROJECTING :
			return GDK_CAP_PROJECTING ;
		case GR_Graphics::CAP_BUTT :
		default:
			return GDK_CAP_BUTT ;
    }
}

static GdkLineStyle mapLineStyle ( GdkGC				  * pGC, 
								   GR_Graphics::LineStyle 	in, 
								   gint 					iWidth )
{
	iWidth = iWidth == 0 ? 1 : iWidth;
	switch ( in )
    {
		case GR_Graphics::LINE_ON_OFF_DASH :
			{
				gint8 dash_list[2] = { 4*iWidth, 4*iWidth };
				gdk_gc_set_dashes(pGC, 0, dash_list, 2);
			}
			return GDK_LINE_ON_OFF_DASH ;
		case GR_Graphics::LINE_DOUBLE_DASH :
			{
				gint8 dash_list[2] = { 4*iWidth, 4*iWidth };
				gdk_gc_set_dashes(pGC, 0, dash_list, 2);
			}
			return GDK_LINE_DOUBLE_DASH ;
		case GR_Graphics::LINE_DOTTED:
			{
				/* strange but 1/3 ratio looks dotted */
				gint8 dash_list[2] = { iWidth, 3*iWidth };
				gdk_gc_set_dashes(pGC, 0, dash_list, 2);
			}
			return GDK_LINE_ON_OFF_DASH;
		case GR_Graphics::LINE_SOLID :
		default:
			return GDK_LINE_SOLID ;
    }
}

static GdkJoinStyle mapJoinStyle ( GR_Graphics::JoinStyle in )
{
	switch ( in )
    {
		case GR_Graphics::JOIN_ROUND :
			return GDK_JOIN_ROUND ;
		case GR_Graphics::JOIN_BEVEL :
			return GDK_JOIN_BEVEL ;
		case GR_Graphics::JOIN_MITER :
		default:
			return GDK_JOIN_MITER ;
    }
}

void GR_UnixGraphics::setLineProperties ( double inWidth, 
										  GR_Graphics::JoinStyle inJoinStyle,
										  GR_Graphics::CapStyle inCapStyle,
										  GR_Graphics::LineStyle inLineStyle )
{
	gint iWidth = static_cast<gint>(tduD(inWidth));
	gdk_gc_set_line_attributes ( m_pGC, iWidth,
								 mapLineStyle ( m_pGC, inLineStyle, iWidth ),
								 mapCapStyle ( inCapStyle ),
								 mapJoinStyle ( inJoinStyle ) ) ;
	gdk_gc_set_line_attributes ( m_pXORGC, iWidth,
								 mapLineStyle ( m_pXORGC, inLineStyle, iWidth ), /* this was m_pGC before */
								 mapCapStyle ( inCapStyle ),
								 mapJoinStyle ( inJoinStyle ) ) ;
}

void GR_UnixGraphics::drawGlyph(UT_uint32 Char, UT_sint32 xoff, UT_sint32 yoff)
{
	UT_uint32 iChar = Char;
	if(isSymbol() && (iChar < 255)  && (iChar >= 32))
	{
		iChar = adobeToUnicode(Char);
		xxx_UT_DEBUGMSG(("DrawGlyph 1 Symbol remapped %d to %d \n",Char,iChar));
	}
	if(isDingbat())
	{
		FT_Face face = XftLockFace(m_pXftFontD);
		//
		// Only CUSTOM encoding gives non-zero indexes
		FT_Select_Charmap(face,FT_ENCODING_ADOBE_CUSTOM);
		iChar =  FT_Get_Char_Index(face,Char);
		XftUnlockFace (m_pXftFontD);
	}
	if(m_pXftFontD == NULL)
		return;
	// FIXME ascent in wrong unit

	UT_sint32 idy = _tduY(yoff);
	UT_sint32 idx = _tduX(xoff);

	XftDrawGlyphs(m_pXftDraw, &m_XftColor, m_pXftFontD, tdu(m_iXoff) +idx, tdu(m_pXftFontL->ascent * getResolution() / s_getDeviceResolution() + m_iYoff)+idy, &iChar, 1);
}

void GR_UnixGraphics::drawChars(const UT_UCSChar* pChars, int iCharOffset,
				int iLength, UT_sint32 xoff, UT_sint32 yoff,
				int * pCharWidths)
{
	xxx_UT_DEBUGMSG(("UnixGraphics:drawChars: m_bIsSymbol %d iLength %d \n",m_bIsSymbol,iLength));
	if (iLength == 0)
		return;
	if(m_pXftFontD == NULL)
		return;
	// FIXME shouldn't need to do this - plam
	UT_sint32 idy = _tduY(yoff);
	UT_sint32 idx = _tduX(xoff);

	//	UT_sint32 iAscent = m_pXftFontL->ascent * getResolution() / s_getDeviceResolution();
	UT_sint32 iAscent = getFontAscent();
	if (!pCharWidths)
	{
		if(!isSymbol() && !isDingbat())
		{
			XftDrawString32(m_pXftDraw, &m_XftColor, m_pXftFontD, tdu(m_iXoff) + idx, tdu(iAscent + m_iYoff)+idy,
							const_cast<XftChar32*> (pChars + iCharOffset), iLength);
		}
		else if(isSymbol())
		{
			xxx_UT_DEBUGMSG(("Doing draw symbols length %d offset %d \n",iLength,iCharOffset));
			UT_uint32 * uChars = new UT_uint32[iLength];
			for(UT_uint32 i = static_cast<UT_uint32>(iCharOffset); i< static_cast<UT_uint32>(iLength); i++)
			{
				uChars[i] = static_cast<UT_uint32>(pChars[iCharOffset + i]);
				if((uChars[i] < 255) && (uChars[i] >= 32))
				{
					uChars[i] = adobeToUnicode(uChars[i]);
					xxx_UT_DEBUGMSG(("drawchars: mapped %d to %d \n",pChars[i],uChars[i]));
				}
			}
			XftDrawString32(m_pXftDraw, &m_XftColor, m_pXftFontD, tdu(m_iXoff) + idx, tdu(iAscent + m_iYoff)+idy,
							const_cast<XftChar32*> (uChars), iLength);
			delete [] uChars;
		}
		else if(isDingbat())
		{
			xxx_UT_DEBUGMSG(("Doing draw Dingbat length %d offset %d \n",iLength,iCharOffset));
			FT_UInt * uChars = new FT_UInt[iLength];
			FT_Face face = XftLockFace(m_pXftFontD);
		//
		// Only CUSTOM encoding gives non-zero indexes
			FT_Select_Charmap(face,FT_ENCODING_ADOBE_CUSTOM);
			for(UT_uint32 i = static_cast<UT_uint32>(iCharOffset); i< static_cast<UT_uint32>(iLength); i++)
			{
				uChars[i] = static_cast<FT_UInt>(FT_Get_Char_Index(face,pChars[iCharOffset + i]));
			}
			XftUnlockFace (m_pXftFontD);
			XftDrawGlyphs(m_pXftDraw, &m_XftColor, m_pXftFontD, 
						  tdu(m_iXoff) +idx, tdu(iAscent + m_iYoff)+idy,
						  uChars, iLength);
			delete [] uChars;
		}
	}
	else
	{
		UT_uint32 uChar;
		XftCharSpec aCharSpec[256];
		XftCharSpec* pCharSpec = aCharSpec;
		XftGlyphSpec * pGlyphSpec = NULL;
		FT_Face face = NULL;

		UT_sint32 currentYoff = idy +tdu(iAscent);
		if(isDingbat())
		{
			face = XftLockFace(m_pXftFontD);
			//
			// Only CUSTOM encoding gives non-zero indexes
			FT_Select_Charmap(face,FT_ENCODING_ADOBE_CUSTOM);
			pGlyphSpec = new XftGlyphSpec[iLength];
		}
		if (iLength > 256)
			pCharSpec = new XftCharSpec[iLength];

		UT_uint32 xPos = xoff;
		for (int i = 0; i < iLength; ++i)
		{
			uChar = static_cast<UT_uint32>(pChars[i + iCharOffset]);
			if(isSymbol() && uChar < 255 && uChar >=32)
			{
				pCharSpec[i].ucs4 = static_cast<FT_UInt>(adobeToUnicode(uChar));
				xxx_UT_DEBUGMSG(("DrawGlyph 2 Symbol remapped %d to %d \n",uChar,pCharSpec[i].ucs4));
			}
			else if(isDingbat())
			{
				pGlyphSpec[i].glyph = static_cast<FT_UInt>(FT_Get_Char_Index(face,uChar));
				xxx_UT_DEBUGMSG(("DrawGlyph 2 remapped %d to %d \n",uChar,pGlyphSpec[i].glyph));

			}
			else
			{
				pCharSpec[i].ucs4 = static_cast<FT_UInt>(pChars[i + iCharOffset]);
			}
			// we may have to keep a remainder and tdu all through, if
			// it turns out that we don't fit in a short.
			// or we can keep it in an int array, then transfer to shorts.
			// that's probably better.
			idx = _tduX(xPos);
			if(!isDingbat())
			{
				pCharSpec[i].x = idx;
				pCharSpec[i].y = currentYoff;
				xxx_UT_DEBUGMSG(("xPos %d idx %d i %d \n",xPos,idx,i));
			}
			else
			{
				pGlyphSpec[i].x = idx;
				pGlyphSpec[i].y = currentYoff;
			}
			if (i < iLength - 1) {
				xPos += pCharWidths[iCharOffset+i];
			}
		}
		if(isDingbat())
		{
			XftDrawGlyphSpec (m_pXftDraw, &m_XftColor, m_pXftFontD, pGlyphSpec, iLength);
		}
		else
		{
			XftDrawCharSpec (m_pXftDraw, &m_XftColor, m_pXftFontD, pCharSpec, iLength);
		}
		if (pCharSpec != aCharSpec)
			delete[] pCharSpec;
		if(isDingbat())
		{
			XftUnlockFace (m_pXftFontD);
			delete [] pGlyphSpec;
		}
	}
}

void GR_UnixGraphics::setFont(GR_Font * pFont)
{
	UT_ASSERT(pFont);
	if(pFont == NULL)
	{
		return;
	}
	XAP_UnixFontHandle * pUFont = static_cast<XAP_UnixFontHandle *> (pFont);

	// Sometimes we ask gr_UnixGraphics to build big (*BIG*) fonts only to
	// get the linear metrics of the font (in the so called "layout units").
	// Xft is not able to open fonts so big, so if we are called with such
	// a font, then we don't even try to open it.
	// IMO the code should not create a big GR_Font to get the linear metrics,
	// but just ask for the metrics with float precision, for instance.
	// I'm just taking here the shortest path to get Xft working...

	// this is probably caching done on the wrong level
	// TODO: turn this off when our text runs get a bit smarter

	// this probably is not safe. It was observed in the win32 build that
	// identity of font pointer does not imply identity of font, i.e.,
	// code like this
	// 
	//   f1 = new GR_Font();
	//   delete f1;
	//   f2 = new GR_Font(); /* different font altogether */
	//
	//   can result in f1 == f2 and since the allocation and
	//   deallocation of fonts happens outside of the graphics class,
	//   the chached m_pFont could well be pointing to
	//   a different font than intended (or something completely
	//   different. I am not sure whether this is or is not the case
	//   on Unix, really depends on where the font pointer comes from,
	//   so I will not meddle with this, but it needs to be
	//   investigated by someone who knows better -- Tomas
	
	// It's worse. Fonts can get deallocated in the cache but have
    // pointers to them in fp_Run.h. I can detect them here and prevent
	// crashes but I don't know
	// how to communicate this back to fp_Run and fully solve this right now 
	// - Martin

	//	XAP_UnixFont * pUnixFont = pUFont->getUnixFont();
	//  bool bDealloc = m_pFontManager->isDeallocated(pUnixFont);
	//  UT_ASSERT(!bDealloc);

	if(m_pFont && (pUFont->getUnixFont() == m_pFont->getUnixFont()) &&
	   (pUFont->getSize() == m_pFont->getSize()))
	{
		// although the new font is the same as the current set one,
		// make at least sure that we point our font pointer to the "new" one, 
		// as the caller of this function expects
		m_pFont = pUFont;
		return;
	}
	m_bIsSymbol = false;
	m_bIsDingbat = false;

	m_pFont = pUFont;
	char * szUnixFontName = UT_strdup(m_pFont->getUnixFont()->getName());
	const char * szLCFontName = UT_lowerString(szUnixFontName);

	if (szLCFontName)
	{
		if(strstr(szLCFontName,"symbol") != NULL)
		{
			if(strstr(szLCFontName,"star") != NULL)
				m_bIsSymbol = false;
			else
				m_bIsSymbol = true;
		}
		if(strstr(szLCFontName,"dingbat") != NULL)
			m_bIsDingbat = true;
		xxx_UT_DEBUGMSG(("Unix Font name is %s dingbat %d \n",szLCFontName,isDingbat()));
	}
	FREEP(szLCFontName);
	//	m_bIsSymbol = false;
	//  m_bIsDingbat = false;
	
	m_pXftFontL = m_pFont->getLayoutXftFont();
	m_pXftFontD = m_pFont->getDeviceXftFont(getZoomPercentage());
}

UT_uint32 GR_UnixGraphics::getFontHeight(GR_Font * fnt)
{
	return getFontAscent(fnt)+getFontDescent(fnt);
}

void GR_UnixGraphics::getCoverage(UT_NumberVector& coverage)
{
	m_pFont->getUnixFont()->getCoverage(coverage);
}

UT_uint32 GR_UnixGraphics::getFontHeight()
{
	if (!m_pFontManager)
		return 0;

	return getFontAscent()+getFontDescent();
}

UT_sint32 GR_UnixGraphics::measureUnRemappedChar(const UT_UCSChar c)
{
	// measureString() could be defined in terms of measureUnRemappedChar()
	// but its not (for presumed performance reasons).  Also, a difference
	// is that measureString() uses remapping to get past zero-width
	// character cells.

	UT_UCSChar newChar;
	double fWidth;

	if(!isSymbol() && !isDingbat())
	{
		newChar = c;
	}
	else if(isSymbol())
	{
		newChar = static_cast<UT_UCSChar>(adobeToUnicode(c));
		xxx_UT_DEBUGMSG(("Measure width of remappedd Symbol %x \n",newChar));
	}
	else
	{
		newChar = c;
	}
	// FIXME we should really be getting stuff fromt he font in layout units,
	// FIXME but we're not smart enough to do that yet

	fWidth = m_pFont->measureUnRemappedChar(newChar, m_pFont->getSize())
		* ((double)getResolution() / (double)s_getDeviceResolution());
	xxx_UT_DEBUGMSG(("char %d width = %d \n",newChar,rint(fWidth)));
	return static_cast<UT_uint32>(rint(fWidth));
}

UT_uint32 GR_UnixGraphics::s_getDeviceResolution(void)
{
	// this is hard-coded at 96 for X now, since 72 (which
	// most X servers return when queried for a resolution)
	// makes for tiny fonts on modern resolutions.

	return 72;
}

UT_uint32 GR_UnixGraphics::getDeviceResolution(void) const
{
	return s_getDeviceResolution();
}

void GR_UnixGraphics::getColor(UT_RGBColor& clr)
{
	clr = m_curColor;
}

void GR_UnixGraphics::setColor(const UT_RGBColor& clr)
{
	UT_ASSERT(m_pGC);
	GdkColor c;

	if (m_curColor == clr)
		return;

	m_curColor = clr;
	c.red = clr.m_red << 8;
	c.blue = clr.m_blu << 8;
	c.green = clr.m_grn << 8;

	_setColor(c);
}

void GR_UnixGraphics::_setColor(GdkColor & c)
{
	gint ret = gdk_colormap_alloc_color(m_pColormap, &c, FALSE, TRUE);

	UT_ASSERT(ret == TRUE);

	gdk_gc_set_foreground(m_pGC, &c);

	m_XftColor.color.red = c.red;
	m_XftColor.color.green = c.green;
	m_XftColor.color.blue = c.blue;
	m_XftColor.color.alpha = 0xffff;
	m_XftColor.pixel = c.pixel;
	
	/* Set up the XOR gc */
	gdk_gc_set_foreground(m_pXORGC, &c);
	gdk_gc_set_function(m_pXORGC, GDK_XOR);
}

GR_Font * GR_UnixGraphics::getGUIFont(void)
{
	if (!m_pFontManager)
		return NULL;

	if (!m_pFontGUI)
	{

		// get the font resource
		GtkStyle *tempStyle = gtk_style_new();
		const char *guiFontName = pango_font_description_get_family(tempStyle->font_desc);
		if (!guiFontName)
			guiFontName = "Times New Roman";
		XAP_UnixFont * font = static_cast<XAP_UnixFont *>(m_pFontManager->searchFont(guiFontName)); // this operation should never fail
		g_object_unref(G_OBJECT(tempStyle));
		UT_ASSERT(font);

		// bury it in a new font handle
		// Hardcoded GUI font size guaranteed to be 12 device units tall
		m_pFontGUI = new XAP_UnixFontHandle(font, static_cast<UT_uint32>(12*100.0/getZoomPercentage()));
		UT_ASSERT(m_pFontGUI);
	}

	return m_pFontGUI;
}

/**
 * Finds a font which match the family, style, variant, weight and size
 * asked.  It will do a fuzzy match to find the font (using the aliases
 * found in fonts.conf
 */
GR_Font * GR_UnixGraphics::_findFont(const char* pszFontFamily,
									 const char* pszFontStyle,
									 const char* pszFontVariant,
									 const char* pszFontWeight,
									 const char* pszFontStretch,
									 const char* pszFontSize)
{
	xxx_UT_DEBUGMSG(("Find Font _findFont for %s \n",pszFontFamily));
	XAP_UnixFont* pUnixFont = m_pFontManager->findNearestFont(pszFontFamily, pszFontStyle, pszFontVariant, pszFontWeight,
															  pszFontStretch, pszFontSize,this);

	// bury the pointer to our Unix font in a XAP_UnixFontHandle with the correct size.
	UT_uint32 iSize = static_cast<UT_uint32>(UT_convertToPoints(pszFontSize)*s_getDeviceResolution()/72.);

	XAP_UnixFontHandle* pFont = new XAP_UnixFontHandle(pUnixFont, iSize);
	UT_ASSERT(pFont);
	xxx_UT_DEBUGMSG(("Return Font name %s \n",pUnixFont->getName())); 
	return pFont;
}

GR_Font* GR_UnixGraphics::getDefaultFont(UT_String& fontFamily)
{
	static XAP_UnixFontHandle fontHandle(m_pFontManager->getDefaultFont(), 12);
	fontFamily = fontHandle.getUnixFont()->getName();
	
	return &fontHandle;
}

UT_uint32 GR_UnixGraphics::getFontAscent(GR_Font * fnt)
{
	// should return a value in layout units, independent of the zoom.
	// ideally, the font should return something appropriate to us.

	UT_ASSERT(fnt);
	UT_ASSERT(m_pGC);

	XAP_UnixFontHandle * hndl = static_cast<XAP_UnixFontHandle *>(fnt);
	
	// FIXME we should really be getting stuff fromt he font in layout units,
	// FIXME but we're not smart enough to do that yet
    // we call s_getDeviceResolution() to avoid zoom
	return static_cast<UT_uint32>(hndl->getUnixFont()->getAscender(hndl->getSize()) * getResolution() / s_getDeviceResolution() ); // +0.5);
}

UT_uint32 GR_UnixGraphics::getFontAscent()
{
	return getFontAscent(m_pFont);
}

UT_uint32 GR_UnixGraphics::getFontDescent(GR_Font * fnt)
{
	UT_ASSERT(fnt);
	UT_ASSERT(m_pGC);

	XAP_UnixFontHandle * hndl = static_cast<XAP_UnixFontHandle *>(fnt);

	XAP_UnixFont* pFont = hndl->getUnixFont();
	// FIXME we should really be getting stuff fromt he font in layout units,
	// FIXME but we're not smart enough to do that yet
	return static_cast<UT_uint32>(pFont->getDescender(hndl->getSize()) * getResolution() / getDeviceResolution() );  // +0.5);
}

UT_uint32 GR_UnixGraphics::getFontDescent()
{
	return getFontDescent(m_pFont);
}


void GR_UnixGraphics::drawLine(UT_sint32 x1, UT_sint32 y1,
							   UT_sint32 x2, UT_sint32 y2)
{
	GdkGCValues gcV;
	gdk_gc_get_values(m_pGC, &gcV);
	
	UT_sint32 idx1 = _tduX(x1);
	UT_sint32 idx2 = _tduX(x2);

	UT_sint32 idy1 = _tduY(y1);
	UT_sint32 idy2 = _tduY(y2);
	
	gdk_draw_line(m_pWin, m_pGC, idx1, idy1, idx2, idy2);
}

void GR_UnixGraphics::setLineWidth(UT_sint32 iLineWidth)
{
	m_iLineWidth = tdu(iLineWidth);

	// Get the current values of the line attributes

	GdkGCValues cur_line_att;
	gdk_gc_get_values(m_pGC, &cur_line_att);
	GdkLineStyle cur_line_style = cur_line_att.line_style;
	GdkCapStyle   cur_cap_style = cur_line_att.cap_style;
	GdkJoinStyle  cur_join_style = cur_line_att.join_style;

	// Set the new line width
	gdk_gc_set_line_attributes(m_pGC,m_iLineWidth,cur_line_style,cur_cap_style,cur_join_style);

}

void GR_UnixGraphics::xorLine(UT_sint32 x1, UT_sint32 y1, UT_sint32 x2,
							  UT_sint32 y2)
{
	UT_sint32 idx1 = _tduX(x1);
	UT_sint32 idx2 = _tduX(x2);

	UT_sint32 idy1 = _tduY(y1);
	UT_sint32 idy2 = _tduY(y2);

	gdk_draw_line(m_pWin, m_pXORGC, idx1, idy1, idx2, idy2);
}

void GR_UnixGraphics::polyLine(UT_Point * pts, UT_uint32 nPoints)
{
	// see bug #303 for what this is about

	GdkPoint * points = static_cast<GdkPoint *>(calloc(nPoints, sizeof(GdkPoint)));
	UT_ASSERT(points);

	for (UT_uint32 i = 0; i < nPoints; i++)
	{
		UT_sint32 idx = _tduX(pts[i].x);
		points[i].x = idx;
		// It seems that Windows draws each pixel along the the Y axis
		// one pixel beyond where GDK draws it (even though both coordinate
		// systems start at 0,0 (?)).  Subtracting one clears this up so
		// that the poly line is in the correct place relative to where
		// the rest of GR_UnixGraphics:: does things (drawing text, clearing
		// areas, etc.).
		UT_sint32 idy1 = _tduY(pts[i].y);

		points[i].y = idy1 - 1;
	}

	gdk_draw_lines(m_pWin, m_pGC, points, nPoints);

	FREEP(points);
}

void GR_UnixGraphics::invertRect(const UT_Rect* pRect)
{
	UT_ASSERT(pRect);

	UT_sint32 idy = _tduY(pRect->top);
	UT_sint32 idx = _tduX(pRect->left);
	UT_sint32 idw = _tduR(pRect->width);
	UT_sint32 idh = _tduR(pRect->height);

	gdk_draw_rectangle(m_pWin, m_pXORGC, 1, idx, idy,
			   idw, idh);
}

void GR_UnixGraphics::setClipRect(const UT_Rect* pRect)
{
	m_pRect = pRect;
	if (pRect)
	{
		GdkRectangle r;
		UT_sint32 idy = _tduY(pRect->top);
		UT_sint32 idx = _tduX(pRect->left);
		//UT_sint32 idw = _tduR(pRect->width);
		//UT_sint32 idh = _tduR(pRect->height);
		r.x = idx;
		r.y = idy;
		r.width = _tduR(pRect->width);
		r.height = _tduR(pRect->height);

		gdk_gc_set_clip_rectangle(m_pGC, &r);
		gdk_gc_set_clip_rectangle(m_pXORGC, &r);
		Region region;
		XPoint points[4];
	
		points[0].x = r.x + tdu(m_iXoff);
		points[0].y = r.y + tdu(m_iYoff);
			
		points[1].x = r.x + r.width  + tdu(m_iXoff);
		points[1].y = r.y + tdu(m_iYoff);
			
		points[2].x = r.x + r.width  + tdu(m_iXoff);
		points[2].y = r.y + r.height + tdu(m_iYoff);
			
		points[3].x = r.x  + tdu(m_iXoff);
		points[3].y = r.y + r.height + tdu(m_iYoff);

		xxx_UT_DEBUGMSG(("Setting clipping rectangle: (%d, %d, %d, %d)\n", r.x, r.y, r.width, r.height));
		region = XPolygonRegion(points, 4, EvenOddRule);
		if (region)
		{
			XftDrawSetClip(m_pXftDraw, region);
			XDestroyRegion (region);
		}
	}
	else
	{
		gdk_gc_set_clip_rectangle(m_pGC, NULL);
		gdk_gc_set_clip_rectangle(m_pXORGC, NULL);

		xxx_UT_DEBUGMSG(("Setting clipping rectangle NULL\n"));
		XftDrawSetClip(m_pXftDraw, 0);
	}
}

void GR_UnixGraphics::fillRect(const UT_RGBColor& c, UT_sint32 x, UT_sint32 y,
							   UT_sint32 w, UT_sint32 h)
{
	// save away the current color, and restore it after we fill the rect
	GdkGCValues gcValues;
	GdkColor oColor;

	memset(&oColor, 0, sizeof(GdkColor));

	gdk_gc_get_values(m_pGC, &gcValues);

	oColor.pixel = gcValues.foreground.pixel;

	// get the new color
	GdkColor nColor;

	nColor.red = c.m_red << 8;
	nColor.blue = c.m_blu << 8;
	nColor.green = c.m_grn << 8;

	gdk_colormap_alloc_color(m_pColormap, &nColor, FALSE, TRUE);

	gdk_gc_set_foreground(m_pGC, &nColor);
	UT_sint32 idx = _tduX(x);
	UT_sint32 idy = _tduY(y);
	UT_sint32 idw = _tduR(w);
	UT_sint32 idh = _tduR(h);
 	gdk_draw_rectangle(m_pWin, m_pGC, 1, idx, idy, idw, idh);

	gdk_gc_set_foreground(m_pGC, &oColor);
}

void GR_UnixGraphics::scroll(UT_sint32 dx, UT_sint32 dy)
{
	GR_CaretDisabler caretDisabler(getCaret());
	UT_sint32 oldDY = tdu(getPrevYOffset());
	UT_sint32 oldDX = tdu(getPrevXOffset());
	UT_sint32 newY = getPrevYOffset() + dy;
	UT_sint32 newX = getPrevXOffset() + dx;
	UT_sint32 ddx = -(tdu(newX) - oldDX);
	UT_sint32 ddy = -(tdu(newY) - oldDY);
	setPrevYOffset(newY);
	setPrevXOffset(newX);
	if(ddx == 0 && ddy == 0)
	{
		return;
	}
	UT_sint32 iddy = labs(ddy);
	bool bEnableSmooth = m_pApp->isSmoothScrollingEnabled();
	bEnableSmooth = bEnableSmooth && (iddy < 30) && (ddx == 0);
	if(bEnableSmooth)
	{
		if(ddy < 0)
		{
			UT_sint32 i = 0;
			for(i = 0; i< iddy; i++)
			{
				gdk_window_scroll(m_pWin,0,-1);
			}
		}
		else
		{
			UT_sint32 i = 0;
			for(i = 0; i< iddy; i++)
			{
				gdk_window_scroll(m_pWin,0,1);
			}
		}
	}
	else
	{
		gdk_window_scroll(m_pWin,ddx,ddy);
	}
	setExposePending(true);
}

void GR_UnixGraphics::scroll(UT_sint32 x_dest, UT_sint32 y_dest,
						  UT_sint32 x_src, UT_sint32 y_src,
						  UT_sint32 width, UT_sint32 height)
{
	GR_CaretDisabler caretDisabler(getCaret());
   	gdk_draw_drawable(m_pWin, m_pGC, m_pWin, tdu(x_src), tdu(y_src),
   				  tdu(x_dest), tdu(y_dest), tdu(width), tdu(height));
}

void GR_UnixGraphics::clearArea(UT_sint32 x, UT_sint32 y,
				UT_sint32 width, UT_sint32 height)
{
	if (width > 0)
	{
		static const UT_RGBColor clrWhite(255,255,255);
		fillRect(clrWhite, x, y, width, height);
	}
}

bool GR_UnixGraphics::startPrint(void)
{
	UT_ASSERT(0);
	return false;
}

bool GR_UnixGraphics::startPage(const char * /*szPageLabel*/, UT_uint32 /*pageNumber*/,
								bool /*bPortrait*/, UT_uint32 /*iWidth*/, UT_uint32 /*iHeight*/)
{
	UT_ASSERT(0);
	return false;
}

bool GR_UnixGraphics::endPrint(void)
{
	UT_ASSERT(0);
	return false;
}

// gdk-pixbuf based routines
/*!
 * Create a new image from the Raster rgba byte buffer defined by pBB.
 * The dimensions of iWidth and iHeight are in logical units but the image
 * doesn't scale if the resolution or zoom changes. Instead you must create
 * a new image.
 */
GR_Image* GR_UnixGraphics::createNewImage(const char* pszName, const UT_ByteBuf* pBB, UT_sint32 iWidth, UT_sint32 iHeight, GR_Image::GRType iType)
{
   	GR_Image* pImg = NULL;

	pImg = new GR_UnixImage(pszName);
	pImg->convertFromBuffer(pBB, tdu(iWidth), tdu(iHeight));
   	return pImg;
}

// a bit of voodoo since i'm not entirely sure what the
// alpha_threshold param means. I know it takes values 0 <= threshold <= 255
// and that values < than the alpha threshold are painted as 0s
// this seems to work for me, so I'm happy - Dom
#define ABI_ALPHA_THRESHOLD 100
/*!
 * Draw the specified image at the location specified in local units 
 * (xDest,yDest). xDest and yDest are in logical units.
 */
void GR_UnixGraphics::drawImage(GR_Image* pImg, UT_sint32 xDest, UT_sint32 yDest)
{
	UT_ASSERT(pImg);

   	GR_UnixImage * pUnixImage = static_cast<GR_UnixImage *>(pImg);

	GdkPixbuf * image = pUnixImage->getData();
	UT_return_if_fail(image);

   	UT_sint32 iImageWidth = pUnixImage->getDisplayWidth();
   	UT_sint32 iImageHeight = pUnixImage->getDisplayHeight();
	UT_sint32 idx = _tduX(xDest);
	UT_sint32 idy = _tduY(yDest);

	xDest = idx; yDest = idy;

	if (gdk_pixbuf_get_has_alpha (image))
		gdk_draw_pixbuf (m_pWin, NULL, image,
						 0, 0, xDest, yDest,
						 iImageWidth, iImageHeight,
						 GDK_RGB_DITHER_NORMAL,
						 0, 0);
	else
		gdk_draw_pixbuf (m_pWin, m_pGC, image,
						 0, 0, xDest, yDest,
						 iImageWidth, iImageHeight,
						 GDK_RGB_DITHER_NORMAL,
						 0, 0);
}

void GR_UnixGraphics::flush(void)
{
	gdk_flush();
}

void GR_UnixGraphics::setColorSpace(GR_Graphics::ColorSpace /* c */)
{
	// we only use ONE color space here now (GdkRGB's space)
	// and we don't let people change that on us.
	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
}

GR_Graphics::ColorSpace GR_UnixGraphics::getColorSpace(void) const
{
	return m_cs;
}

void GR_UnixGraphics::setCursor(GR_Graphics::Cursor c)
{
	if (m_cursor == c)
		return;

	m_cursor = c;

	GdkCursorType cursor_number;

	switch (c)
	{
	default:
		UT_ASSERT(UT_NOT_IMPLEMENTED);
		/*FALLTHRU*/
	case GR_CURSOR_DEFAULT:
		cursor_number = GDK_LEFT_PTR;
		break;

	case GR_CURSOR_IBEAM:
		cursor_number = GDK_XTERM;
		break;

	//I have changed the shape of the arrow so get a consistent
	//behaviour in the bidi build; I think the new arrow is better
	//for the purpose anyway

	case GR_CURSOR_RIGHTARROW:
		cursor_number = GDK_SB_RIGHT_ARROW; //GDK_ARROW;
		break;

	case GR_CURSOR_LEFTARROW:
		cursor_number = GDK_SB_LEFT_ARROW; //GDK_LEFT_PTR;
		break;

	case GR_CURSOR_IMAGE:
		cursor_number = GDK_FLEUR;
		break;

	case GR_CURSOR_IMAGESIZE_NW:
		cursor_number = GDK_TOP_LEFT_CORNER;
		break;

	case GR_CURSOR_IMAGESIZE_N:
		cursor_number = GDK_TOP_SIDE;
		break;

	case GR_CURSOR_IMAGESIZE_NE:
		cursor_number = GDK_TOP_RIGHT_CORNER;
		break;

	case GR_CURSOR_IMAGESIZE_E:
		cursor_number = GDK_RIGHT_SIDE;
		break;

	case GR_CURSOR_IMAGESIZE_SE:
		cursor_number = GDK_BOTTOM_RIGHT_CORNER;
		break;

	case GR_CURSOR_IMAGESIZE_S:
		cursor_number = GDK_BOTTOM_SIDE;
		break;

	case GR_CURSOR_IMAGESIZE_SW:
		cursor_number = GDK_BOTTOM_LEFT_CORNER;
		break;

	case GR_CURSOR_IMAGESIZE_W:
		cursor_number = GDK_LEFT_SIDE;
		break;

	case GR_CURSOR_LEFTRIGHT:
		cursor_number = GDK_SB_H_DOUBLE_ARROW;
		break;

	case GR_CURSOR_UPDOWN:
		cursor_number = GDK_SB_V_DOUBLE_ARROW;
		break;

	case GR_CURSOR_EXCHANGE:
		cursor_number = GDK_EXCHANGE;
		break;

	case GR_CURSOR_GRAB:
		cursor_number = GDK_HAND1;
		break;

	case GR_CURSOR_LINK:
		cursor_number = GDK_HAND2;
		break;

	case GR_CURSOR_WAIT:
		cursor_number = GDK_WATCH;
		break;

	case GR_CURSOR_HLINE_DRAG:
		cursor_number = GDK_SB_V_DOUBLE_ARROW;
		break;

	case GR_CURSOR_VLINE_DRAG:
		cursor_number = GDK_SB_H_DOUBLE_ARROW;
		break;

	case GR_CURSOR_CROSSHAIR:
		cursor_number = GDK_CROSSHAIR;
		break;

	case GR_CURSOR_DOWNARROW:
		cursor_number = GDK_SB_DOWN_ARROW;
		break;

	case GR_CURSOR_DRAGTEXT:
		cursor_number = GDK_TARGET;
		break;

	case GR_CURSOR_COPYTEXT:
		cursor_number = GDK_DRAPED_BOX;
		break;
	}
	xxx_UT_DEBUGMSG(("cursor set to %d  gdk %d \n",c,cursor_number));
	GdkCursor * cursor = gdk_cursor_new(cursor_number);
	gdk_window_set_cursor(m_pWin, cursor);
	gdk_cursor_unref(cursor);
}

void GR_UnixGraphics::createPixmapFromXPM( char ** pXPM,GdkPixmap *source,
										   GdkBitmap * mask)
{
	source
		= gdk_pixmap_colormap_create_from_xpm_d(m_pWin,NULL,
							&mask, NULL,
							pXPM);
}

GR_Graphics::Cursor GR_UnixGraphics::getCursor(void) const
{
	return m_cursor;
}

void GR_UnixGraphics::setColor3D(GR_Color3D c)
{
	UT_ASSERT(c < COUNT_3D_COLORS);
	_setColor(m_3dColors[c]);
}

bool GR_UnixGraphics::getColor3D(GR_Color3D name, UT_RGBColor &color)
{
	if (m_bHave3DColors) {
		color.m_red = m_3dColors[name].red >> 8;
		color.m_grn = m_3dColors[name].green >> 8;
		color.m_blu =m_3dColors[name].blue >> 8;
		return true;
	}
	return false;
}

void GR_UnixGraphics::init3dColors(GtkStyle * pStyle)
{
	m_3dColors[CLR3D_Foreground] = pStyle->text[GTK_STATE_NORMAL];
	m_3dColors[CLR3D_Background] = pStyle->bg[GTK_STATE_NORMAL];
	m_3dColors[CLR3D_BevelUp]    = pStyle->light[GTK_STATE_NORMAL];
	m_3dColors[CLR3D_BevelDown]  = pStyle->dark[GTK_STATE_NORMAL];
	m_3dColors[CLR3D_Highlight]  = pStyle->bg[GTK_STATE_PRELIGHT];

	m_bHave3DColors = true;
}

void GR_UnixGraphics::fillRect(GR_Color3D c, UT_sint32 x, UT_sint32 y, UT_sint32 w, UT_sint32 h)
{
	UT_ASSERT(c < COUNT_3D_COLORS);
	gdk_gc_set_foreground(m_pGC, &m_3dColors[c]);
	gdk_draw_rectangle(m_pWin, m_pGC, 1, tdu(x), tdu(y), tdu(w), tdu(h));
}

void GR_UnixGraphics::fillRect(GR_Color3D c, UT_Rect &r)
{
	UT_ASSERT(c < COUNT_3D_COLORS);
	fillRect(c,r.left,r.top,r.width,r.height);
}

void GR_UnixGraphics::polygon(UT_RGBColor& c,UT_Point *pts,UT_uint32 nPoints)
{
	// save away the current color, and restore it after we draw the polygon
	GdkGCValues gcValues;
	GdkColor oColor;

	memset(&oColor, 0, sizeof(GdkColor));

	gdk_gc_get_values(m_pGC, &gcValues);

	oColor.pixel = gcValues.foreground.pixel;

	// get the new color
	GdkColor nColor;

	nColor.red = c.m_red << 8;
	nColor.blue = c.m_blu << 8;
	nColor.green = c.m_grn << 8;

	gdk_colormap_alloc_color(m_pColormap, &nColor, FALSE, TRUE);

	gdk_gc_set_foreground(m_pGC, &nColor);

	GdkPoint* points = new GdkPoint[nPoints];
    UT_ASSERT(points);

    for (UT_uint32 i = 0;i < nPoints;i++){
		UT_sint32 idx = _tduX(pts[i].x);
        points[i].x = idx;
		UT_sint32 idy = _tduY(pts[i].y);
        points[i].y = idy;
    }
	gdk_draw_polygon(m_pWin, m_pGC, 1, points, nPoints);
	delete[] points;

	gdk_gc_set_foreground(m_pGC, &oColor);
}

//////////////////////////////////////////////////////////////////
// This is a static method in the GR_Font base class implemented
// in platform code.
//////////////////////////////////////////////////////////////////

void GR_Font::s_getGenericFontProperties(const char * /*szFontName*/,
										 FontFamilyEnum * pff,
										 FontPitchEnum * pfp,
										 bool * pbTrueType)
{
	// describe in generic terms the named font.

	// Note: most of the unix font handling code is in abi/src/af/xap/unix
	// Note: rather than in the graphics class.  i'm not sure this matters,
	// Note: but it is just different....

	// TODO add code to map the given font name into one of the
	// TODO enums in GR_Font and set *pff and *pft.

	*pff = FF_Unknown;
	*pfp = FP_Unknown;
	*pbTrueType = true;
}


/*!
 * Take a screenshot of the graphics and convert it to an image.
 */
GR_Image * GR_UnixGraphics::genImageFromRectangle(const UT_Rect &rec)
{
	UT_sint32 idx = _tduX(rec.left);
	UT_sint32 idy = _tduY(rec.top);
	UT_sint32 idw = _tduR(rec.width);
	UT_sint32 idh = _tduR(rec.height);
	UT_return_val_if_fail (idw > 0 && idh > 0 && idx >= 0 && idy >= 0, NULL);
	GdkColormap* cmp = gdk_colormap_get_system();
	GdkPixbuf * pix = gdk_pixbuf_get_from_drawable(NULL,
												   m_pWin,
												   cmp,
												   idx, idy, 0, 0,
												   idw, idh);
	
	UT_return_val_if_fail(pix, NULL);

	GR_UnixImage * pImg = new GR_UnixImage("ScreenShot");
	pImg->m_image = pix;
	pImg->setDisplaySize(idw,idh);
	return static_cast<GR_Image *>(pImg);
}

void GR_UnixGraphics::saveRectangle(UT_Rect & r, UT_uint32 iIndx)
{
	UT_Rect* oldR = NULL;	

	m_vSaveRect.setNthItem(iIndx, new UT_Rect(r),&oldR);
	if(oldR) {
		delete oldR;
	}

	GdkPixbuf * oldC = NULL;
	UT_sint32 idx = _tduX(r.left);
	UT_sint32 idy = _tduY(r.top);
	UT_sint32 idw = _tduR(r.width);
	UT_sint32 idh = _tduR(r.height);

	GdkPixbuf * pix = gdk_pixbuf_get_from_drawable(NULL,
												   m_pWin,
												   NULL,
												   idx, idy, 0, 0,
												   idw, idh);
	m_vSaveRectBuf.setNthItem(iIndx, pix, &oldC);

	if(oldC)
		g_object_unref (G_OBJECT (oldC));
}

void GR_UnixGraphics::restoreRectangle(UT_uint32 iIndx)
{
	UT_Rect * r = m_vSaveRect.getNthItem(iIndx);
	GdkPixbuf *p = m_vSaveRectBuf.getNthItem(iIndx);
	UT_sint32 idx = _tduX(r->left);
	UT_sint32 idy = _tduY(r->top);


	if (p && r)
		gdk_draw_pixbuf (m_pWin, NULL, p, 0, 0,
						 idx, idy,
						 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
}

void GR_UnixGraphics::_beginPaint ()
{
}

void GR_UnixGraphics::_endPaint ()
{
}


GR_Graphics *   GR_UnixGraphics::graphicsAllocator(GR_AllocInfo& allocInfo)
{

	if (allocInfo.getType() == GRID_UNIX_NULL)
		{
			return UnixNull_Graphics::graphicsAllocator(allocInfo);
		}
	GR_UnixAllocInfo &allocator = (GR_UnixAllocInfo&)allocInfo;
	if (allocator.m_win) 
		{
			return new GR_UnixGraphics(allocator.m_win, allocator.m_fontManager, XAP_App::getApp());
		}
	else 
		{
		return new GR_UnixGraphics(allocator.m_pixmap, allocator.m_fontManager, XAP_App::getApp(), allocator.m_usePixmap);
	}
}
