/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */

/*
 * Porting to KDE(2) was done by
 *
 *  (C) 2000 by Thomas Capricelli <orzel@freehackers.org>
 *
 *  Please visit http://freehackers.org/kvim for other vim- or
 *  kde-related coding.
 *
 *  $Id: gui_kde_x11.cc,v 1.11 2005/03/15 22:39:20 vimboss Exp $
 *
 */

#include <qscrollbar.h>
#include <qcstring.h>
#include <qdatetime.h>
#include <qcursor.h>
#include <qfontmetrics.h>
#include <qpaintdevice.h>
#include <qclipboard.h>
#include <qregexp.h>
#include <kaboutkde.h>
#include <kiconloader.h>
#include <kfontdialog.h>
#include <kmessagebox.h>
#include <dcopclient.h>
#include <kwin.h>
#include <kmenubar.h>
#include <kconfig.h>
#if (QT_VERSION>=300)
#include <qnamespace.h>
#include <ktip.h>
#endif
#include <qpopupmenu.h>
#include <qpainter.h>
#include <qtextcodec.h>
#include <qfontmetrics.h>
#include <qfont.h>


#include "gui_kde_wid.h"


extern "C" {
#include "vim.h"
#include "version.h"
}

#include <stdio.h>

/*
 * global variable for KDE, we can't put them in Gui, cause there are C++ types
 */
VimMainWindow	*vmw = 0;
SBPool		*sbpool = 0;
QString		*argServerName = 0;

#ifdef FEAT_MOUSESHAPE
/* The last set mouse pointer shape is remembered, to be used when it goes
 * from hidden to not hidden. */
static int last_shape = 0;
#endif

/*
 * Arguments handled by KDE internally.
 */

#if QT_VERSION>=300
static int	tip = 0;    // 1 no dialog, 0 use it if enabled in conf,
			    // 2 force the tip
#endif
static int	reverse = 0; // 0 bg : white, 1 : bg : black
QString		*startfont;
QSize		*startsize;
static int	gui_argc = 0;
static char	**gui_argv = NULL;

/*
 * Parse the GUI related command-line arguments.  Any arguments used are
 * deleted from argv, and *argc is decremented accordingly.  This is called
 * when vim is started, whether or not the GUI has been started.
 */
    void
gui_mch_prepare(int *argc, char **argv)// {{{
{
    // copy args for KDE/Qt
    gui_argc = 0;

    // this one is not really good as all options are not for KDE/Qt ...
    gui_argv = (char **)lalloc((long_u)(*argc * sizeof(char *)), FALSE);
    if (gui_argv == NULL)
	return;
    gui_argv[gui_argc++] = argv[0];

    int found = 0;
    for (int i = 1; i < *argc ; i++)
    {
	if (found != 2)
	    found = 0;
	else
	{
	    found = 0;
	    // remove from the list of argv
	    if (--*argc > i)
	    {
		mch_memmove(&argv[i], &argv[i + 1],
			(*argc - i) * sizeof(char *));
	    }
	    i--;
	    continue;
	}

	if (strcmp(argv[i], "--servername") == 0)
	{
	    argServerName = new QString(argv[i+1]); // to get the serverName now
	}
#if QT_VERSION>+300
	if (strcmp(argv[i], "-tip") == 0 )
	{
	    tip = 2;
	    found = 1;
	}
	if (strcmp(argv[i], "-notip") == 0 )
	{
	    tip = 1;
	    found = 1;
	}
#endif
	if (strcmp(argv[i], "-black") == 0 )
	{
	    reverse = 1;
	    found = 1;
	}
	/* replaced by -black */
	/*		if (strcmp(argv[i], "-rv") == 0 )
	 *		{
	 reverse = 1;
	 found = 1;
	 }*/
	if (strcmp(argv[i], "-font") == 0 || strcmp(argv[i], "-fn") == 0)
	{
	    startfont = new QString(argv[i+1]);
	    found = 2;
	}
	if (strcmp(argv[i], "-geometry") == 0 || strcmp(argv[i], "-geom") == 0)
	{
	    found = 2;
	    QString text(argv[i + 1]);
	    QStringList list = QStringList::split(QChar('x'), text);
	    startsize = new QSize(list[0].toInt(), list[1].toInt());
	}
	if (strcmp(argv[i], "-display") == 0) //XXX: this does not work,
	    // too many -display options in main.c !
	    // ask Bram ...
	    {
		gui_argv[gui_argc++] = strdup("--display");
		gui_argv[gui_argc++] = argv[i+1];
		found = 0;
	    }
	if (strcmp(argv[i], "--display") == 0 )
	{
	    gui_argv[gui_argc++] = argv[i];
	    gui_argv[gui_argc++] = argv[i+1];
	    found = 2;
	}
	//KDE/Qt options with no args
	if (strcmp(argv[i], "--help-kde") == 0
		|| strcmp(argv[i], "--help-qt") == 0
		|| strcmp(argv[i], "--help-all") == 0
		|| strcmp(argv[i], "--reverse") == 0
		|| strcmp(argv[i], "--author") == 0
		//	|| strcmp(argv[i], "--version") == 0 //disabled we need these for kcmvim
		//	|| strcmp(argv[i], "-v") == 0
		|| strcmp(argv[i], "--license") == 0
		|| strcmp(argv[i], "--cmap") == 0
		|| strcmp(argv[i], "--nograb") == 0
		|| strcmp(argv[i], "--dograb") == 0
		|| strcmp(argv[i], "--sync") == 0
		|| strcmp(argv[i], "--noxim") == 0
		|| strcmp(argv[i], "--nocrashhandler") == 0
		|| strcmp(argv[i], "--waitforwm") == 0
	   )
	{
	    gui_argv[gui_argc++] = argv[i];
	    found = 1;
	}
	//this outputs KDE and Vim versions :)
	if (strcmp(argv[i], "--version") == 0
		|| strcmp(argv[i], "-v") == 0)
	{
	    gui_argv[gui_argc++] = argv[i];
	}


	// KDE/Qt options with one arg
	if (strcmp(argv[i], "--session") == 0
		|| strcmp(argv[i], "--ncols") == 0
		|| strcmp(argv[i], "--bg") == 0
		|| strcmp(argv[i], "--background") == 0
		|| strcmp(argv[i], "--fg") == 0
		|| strcmp(argv[i], "--foreground") == 0
		|| strcmp(argv[i], "--btn") == 0
		|| strcmp(argv[i], "--name") == 0
		|| strcmp(argv[i], "--title") == 0
		|| strcmp(argv[i], "--inputstyle") == 0
		|| strcmp(argv[i], "--im") == 0
		|| strcmp(argv[i], "--caption") == 0
		|| strcmp(argv[i], "--icon") == 0
		|| strcmp(argv[i], "--miniicon") == 0
		|| strcmp(argv[i], "--config") == 0
		|| strcmp(argv[i], "--dcopserver") == 0
		|| strcmp(argv[i], "--style") == 0
		|| strcmp(argv[i], "--geometry") == 0
		|| strcmp(argv[i], "--smkey") == 0
		|| strcmp(argv[i], "-smkey") == 0
		|| strcmp(argv[i], "-session") == 0
	   )
	{
	    gui_argv[gui_argc++] = argv[i];
	    gui_argv[gui_argc++] = argv[i + 1];
	    found = 2;
	}

	// remove from the list of argv
	if (found >= 1 && --*argc > i)
	{
	    mch_memmove(&argv[i], &argv[i + 1], (*argc - i) * sizeof(char *));
	    i--;
	}
    }
    KCmdLineArgs::init(gui_argc, gui_argv, argv[0], I18N_NOOP("KVim"),
			      I18N_NOOP("Vim inside KDE"), VIM_VERSION_SHORT);
}// }}}

/****************************************************************************
 * Focus handlers:
 */

/*
 * Initialises time intervals for the cursor blinking
 */
    void
gui_mch_set_blinking(long waittime, long on, long off)//{{{
{
    gui.w->set_blink_time(waittime, on, off);
}//}}}

/*
 * Stop the cursor blinking.  Show the cursor if it wasn't shown.
 */
    void
gui_mch_stop_blink()//{{{
{
    gui.w->stop_cursor_blinking();
}//}}}

/*
 * Start the cursor blinking.  If it was already blinking, this restarts the
 * waiting time and shows the cursor.
 */
    void
gui_mch_start_blink()//{{{
{
    gui.w->start_cursor_blinking();
}//}}}

#ifdef FEAT_MZSCHEME
    void
mzscheme_kde_start_timer()//{{{
{
    gui.w->enable_mzscheme_threads();
}//}}}
    void
mzscheme_kde_stop_timer()//{{{
{
    gui.w->disable_mzscheme_threads();
}//}}}
#endif

/*
 * Check if the GUI can be started.  Called before gvimrc is sourced.
 * Return OK or FAIL.
 */
    int
gui_mch_init_check(void)//{{{
{
    gui.dpy = qt_xdisplay();
    return OK;
}//}}}

/*
 * Initialise the X GUI.  Create all the windows, set up all the call-backs etc.
 * Returns OK for success, FAIL when the GUI can't be started.
 */
    int
gui_mch_init()//{{{
{
    (void) new KApplication();
    KApplication::kApplication()->dcopClient()->registerAs(
				 KApplication::kApplication()->name(), false);
    //    dbf("%s %s", KGlobal::locale()->language().latin1(), KLocale::defaultLanguage().latin1());

    vmw = new VimMainWindow("KVim", 0);
    vmw->setFrameBorderWidth(0);
    kapp->setMainWidget(vmw);
    kapp->setTopWidget(vmw);

    sbpool = new SBPool;

#if QT_VERSION>=300
    vmw->connect(kapp->clipboard(), SIGNAL(selectionChanged()),
				     vmw, SLOT(clipboard_selection_update()));
#endif
    vmw->connect(kapp->clipboard(), SIGNAL(dataChanged()),
					  vmw, SLOT(clipboard_data_update()));
    clip_lose_selection(&clip_plus);
    clip_lose_selection(&clip_star);

    gui.in_focus = FALSE; // will be updated

    if (reverse)
    {
	gui.def_norm_pixel = gui_get_color((char_u *)"White");
	gui.def_back_pixel = gui_get_color((char_u *)"Black");
#if QT_VERSION>=300
	gui.w->setEraseColor(QColor(Qt::black));
#else
	gui.w->setBackgroundColor(QColor(Qt::black));
#endif
    }
    else
    {
	gui.def_norm_pixel = gui_get_color((char_u *)"Black");
	gui.def_back_pixel = gui_get_color((char_u *)"White");
#if QT_VERSION>=300
	gui.w->setEraseColor(QColor(Qt::white));
#else
	gui.w->setBackgroundColor(QColor(Qt::white));
#endif
    }

    gui.norm_pixel = gui.def_norm_pixel;
    gui.back_pixel = gui.def_back_pixel;

    gui.border_width  = 1;
    gui.border_offset = 1;//gui.border_width;
    gui.scrollbar_width = SB_DEFAULT_WIDTH;
    gui.scrollbar_height = SB_DEFAULT_WIDTH;

    //gui.menu_height = vmw->menuBar()->height()+1;
    //gui.toolbar_height = vmw->toolBar()->height();

    return OK;
}//}}}


/*
 * Called when the foreground or background color has been changed.
 */
    void
gui_mch_new_colors()//{{{
{
    QColor rgb;
    rgb.setRgb(gui.back_pixel);
#if QT_VERSION>=300
    gui.w->setEraseColor(rgb);
#else
    gui.w->setBackgroundColor(rgb);
#endif
}//}}}

/*
 * Open the GUI window which was created by a call to gui_mch_init().
 */
    int
gui_mch_open()//{{{
{
    gui.dpy = qt_xdisplay();
    set_normal_colors();

    /* Check that none of the colors are the same as the background color */
    gui_check_colors();

    /* Get the colors for the highlight groups (gui_check_colors() might have
     * changed them).
     */
    highlight_gui_started();    /* re-init colors and fonts */
#ifdef FEAT_MENU
    vmw->w->menu = new QPopupMenu(vmw);

#if QT_VERSION>=300
    vmw->w->menu->insertItem(SmallIcon("ktip"), i18n("&Tip of the day..."),
						vmw, SLOT(showTipOfTheDay()));
    vmw->w->menu->insertSeparator();
#endif
    if (vmw->have_tearoff)
	vmw->w->menu->insertTearOffHandle(0, 0);
    vmw->w->menu->insertItem(i18n("&Report Bug ..."),
						  vmw, SLOT(showBugReport()));
    vmw->w->menu->insertSeparator();
    vmw->w->menu->insertItem(SmallIcon("kvim"), i18n("&About KVim..."),
					   vmw, SLOT(showAboutApplication()));
    vmw->w->menu->insertItem(SmallIcon("about_kde"), i18n("About &KDE..."),
						   vmw, SLOT(showAboutKDE()));
    vmw->menuBar()->insertItem("&KVim", vmw->w->menu);
#endif
    if (startfont != NULL)
	gui_mch_init_font((char_u*)startfont->latin1(), FALSE);

    if (startsize != NULL)
	vmw->resize(startsize->width(), startsize->height());

    gui_mch_update_codec();

    if (kapp->isRestored())
	if (KMainWindow::canBeRestored(1))
	    vmw->restore(1);

    vmw->show();
#if QT_VERSION>=300
    if (tip == 2)
	KTipDialog::showTip(vmw, QString::null, true);
    else if (tip == 0)
	KTipDialog::showTip(vmw);
#endif

    return OK;
}//}}}

    void
gui_mch_exit(int rc)//{{{
{
    kapp->quit();
}//}}}

/*
 * Get the position of the top left corner of the window.
 */
    int
gui_mch_get_winpos(int *x, int *y)//{{{
{
    *x = vmw->x();
    *y = vmw->y();
    return OK;
}//}}}

/*
 * Set the position of the top left corner of the window to the given
 * coordinates.
 */
    void
gui_mch_set_winpos(int x, int y)//{{{
{
    vmw->move(x, y);
}//}}}

/*
 * Set the windows size.
 * ->resize VimWidget
 * ->resize vmw (block any events generated from here)
 */
    void
gui_mch_set_shellsize(int width, int height,//{{{
		int min_width, int min_height,
		int base_width, int base_height)
{
    //resize VimWidget
    vmw->w->resize(width, height);

    //resize vmw
    int vheight, vwidth;
    vheight = height;
    vwidth = width;

    if (gui.which_scrollbars[SBAR_LEFT])
	vwidth += gui.scrollbar_width;
    if (gui.which_scrollbars[SBAR_RIGHT])
	vwidth += gui.scrollbar_width;
    if (gui.which_scrollbars[SBAR_BOTTOM])
	vheight += gui.scrollbar_height;

    if (vmw->menuBar()->isVisible() && vmw->menuBar()->isEnabled()
#if QT_VERSION>=300
	    && !vmw->menuBar()->isTopLevelMenu()
#endif
       )
	vheight += vmw->menuBar()->height();
#ifdef FEAT_TOOLBAR
    if (vmw->toolBar()->isVisible() && vmw->toolBar()->isEnabled()
	    && (vmw->toolBar()->barPos() == KToolBar::Top
		|| vmw->toolBar()->barPos() == KToolBar::Bottom))
	vheight += vmw->toolBar()->height();

    if (vmw->toolBar()->isVisible() && vmw->toolBar()->isEnabled()
	    && (vmw->toolBar()->barPos() == KToolBar::Left
		|| vmw->toolBar()->barPos() == KToolBar::Right))
	vwidth += vmw->toolBar()->width();
#endif
    vmw->lock();
    vmw->resize(vwidth, vheight);
    gui_mch_update();
    //size should be nearly perfect, update baseSize and sizeIncrement
    vmw->setBaseSize(base_width, vmw->menuBar()->height() + 1
			    + vmw->toolBar()->height() + gui.char_height * 2);
    vmw->setSizeIncrement((( int )(gui.char_width / 2) * 2), gui.char_height);
    vmw->unlock();
}//}}}


/*
 * The screen size is used to make sure the initial window doesn't get bigger
 * then the screen.  This subtracts some room for menubar, toolbar and window
 * decorations.
 */
    void
gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)//{{{
{
    *screen_w = kapp->desktop()->width();
    *screen_h = kapp->desktop()->height();
}//}}}

#if defined(FEAT_MENU) || defined(PROTO)
    void
gui_mch_enable_menu(int showit)//{{{
{
    if (showit)
	vmw->menuBar()->show();
    else
	vmw->menuBar()->hide();
    vmw->resize(vmw->width(), vmw->height());
}//}}}
#endif


#if defined(FEAT_TOOLBAR) || defined(PROTO)
    void
gui_mch_show_toolbar(int showit)//{{{
{
    if (showit)
	vmw->toolBar()->show();
    else
	vmw->toolBar()->hide();
    vmw->resize(vmw->width(), vmw->height());
}//}}}
#endif

/*
 * Put up a font dialog and return the selected font name in allocated memory.
 * "oldval" is the previous value.
 * Return NULL when cancelled.
 */

    char_u *
gui_mch_font_dialog(char_u *oldval)//{{{
{
    QFont myFont(vmw->w->font());
    if (gui.fontname)
	gui.fontname = NULL;

    int result = KFontDialog::getFont(myFont, true);
    if (result != KFontDialog::Accepted)
	return NULL;

    //	myFont.setFixedPitch(true);
#if QT_VERSION>=300
    QString n = myFont.toString();
#else
    QString n = KVimUtils::toString(&myFont);
#endif
    n.replace(QRegExp(","), "/");
    gui.fontname = (char_u *)strdup((const char *)n);
    n.replace(QRegExp(" "), "\\ ");
    n = QString("To set this font as your default font for KVim, edit your ~/.gvimrc file and add the following lines : \nif has(\"gui_kde\")\nset guifont=")+n+QString("\nendif");// \n OR \n use the control center of KDE and choose the correct fixed font");

    //display a message box which explains how to save your font settings
    KMessageBox::information(vmw, n, "Font Selection", "kvimselectfont");

    return vim_strsave(gui.fontname);
}//}}}

/*
 * Initialise vim to use the font with the given name.
 * Return FAIL if the font could not be loaded, OK otherwise.
 */
    int
gui_mch_init_font(char_u * font_name, int fontset)//{{{
{
    QString fontname;
    GuiFont font = NULL;

    if (font_name == NULL)
    {
#if 0
#if QT_VERSION>=300
	KConfig *base = KGlobal::config();
#else
	KConfigBase *base = KGlobal::config();
#endif
	base->setGroup("General");
	if (!base->hasKey("fixed"))
	{
	    KMessageBox::error(KApplication::kApplication()->mainWidget(),"Cannot load default fixed font\n\nConfigure fonts in KDE Control Center.\n(Just click 'Choose...', 'OK' and then 'Apply')");
	    return FAIL;
	}
#if QT_VERSION>=300
	QString f = base->readEntry("fixed");
#else
	QFont ft = base->readFontEntry("fixed", NULL);
	QString f = KVimUtils::toString(&ft);
#endif
	font_name = (char_u*)strdup(f.latin1()); //latin1 ?
#else
	font_name = (char_u*)strdup("misc-fixed/10/-1/5/50/0/0/0/1/0");
#endif
    }
    fontname = (const char *)font_name;
    /*	fontname.replace(QRegExp("/"), ",");
	font = new QFont();
	font->fromString( fontname );
	*/
#ifdef FEAT_XFONTSET
    if (fontset)
	font = gui_mch_get_fontset(font_name, TRUE, TRUE);
    if (font == NULL)
#endif
	font = gui_mch_get_font(font_name, FALSE);

    if (font == NULL)
	return FAIL;
    if (fontname.contains('*') && fontname.contains('-'))
	return FAIL;

    gui_mch_free_font(gui.norm_font);
#ifdef FEAT_XFONTSET
    gui_mch_free_fontset(gui.fontset);
    gui.fontset = NOFONTSET;
    if (fontset)
    {
	gui.fontset = font;
	gui.norm_font = NOFONT;
    }
    else
#endif
	gui.norm_font = font;

    /* Compute the width of the character cell.  Some fonts include
     * double-width characters.  Use the width of ASCII characters to find
     * out if this is so. */
    QFontMetrics f(*font);
    int width_max = 0;
    for (char c = 32; c < 127; c++)
	if (width_max < f.width((QChar)c))
	    width_max = f.width((QChar)c);
    if (width_max <= f.maxWidth() / 2)
	width_max = f.maxWidth() / 2;
    gui.char_width  = width_max;
    gui.char_height = f.height() + p_linespace;
    gui.char_ascent = f.ascent() + p_linespace;

    //check values, just to make sure and avoid a crash
    if (gui.char_width <= 0)
	gui.char_width = 8;
    if (gui.char_height <= 0)
	gui.char_height = 1;

    hl_set_font_name(font_name);

    vmw->w->setFont(*font);

    return OK;
}//}}}

    GuiFont
gui_mch_get_font(char_u * name, int report_error)//{{{
{
    QString fontname((const char *)name);
    if (!gui.in_use || name == NULL)
	return NOFONT;
    if (fontname.contains('*') && fontname.contains('-'))
	return NOFONT; // XFLD names not allowed anymore
    QFont *myFont = new QFont();
    fontname.replace(QRegExp("/"), ",");
    //	myFont->setRawMode(TRUE);

#if QT_VERSION>=300
    myFont->fromString(fontname);
#else
    KVimUtils::fromString(myFont, fontname);
#endif
    myFont->setFixedPitch(true);
    if (!myFont->fixedPitch())
	dbf("Non fixed-width font");
    return (GuiFont) myFont;
}//}}}

#if defined(FEAT_EVAL) || defined(PROTO)
/*
 * Return the name of font "font" in allocated memory.
 * Don't know how to get the actual name, thus use the provided name.
 */
    char_u *
gui_mch_get_fontname(GuiFont font, char_u *name)//{{{
{
    if (name == NULL)
	return NULL;
    return vim_strsave(name);
}//}}}
#endif

/*
 * Set the current text font.
 * Since we create all GC on demand, we use just gui.current_font to
 * indicate the desired current font.
 */
    void
gui_mch_set_font(GuiFont font)//{{{
{
    gui.current_font = font;
    gui.w->painter->setFont(*(gui.current_font));
}//}}}

/*
 * If a font is not going to be used, free its structure.
 */
    void
gui_mch_free_font(GuiFont font)//{{{
{
    if (font)
	delete font; // this is a QFont , we can delete it :)
}//}}}

    GuiFontset
gui_mch_get_fontset(char_u *name, int report_error, int fixed_width)
{
    return (GuiFontset)gui_mch_get_font(name, report_error);
}

    void
gui_mch_set_fontset(GuiFontset fontset)
{
    gui_mch_set_font((GuiFont)fontset);
}

    void
gui_mch_free_fontset(GuiFontset fontset)
{
    if (fontset)
	delete fontset;
}

    void
gui_mch_settitle(char_u *title, char_u *icon)//{{{
{
    if (!gui.in_use)		/* can't do this when GUI not running */
	return;
    vmw->setPlainCaption((const char *)title);
    QPixmap p((const char *)icon);
    vmw->setIcon(p); //FIXME
}//}}}

/*
 * Return the Pixel value (color) for the given color name.  This routine was
 * pretty much taken from example code in the Silicon Graphics OSF/Motif
 * Programmer's Guide.
 * Return -1 for error.
 */
    guicolor_T
gui_mch_get_color(char_u * name)//{{{
{
    int i;
    static char *(vimnames[][2]) =
    {
	/* A number of colors that some X11 systems don't have */
	{"LightRed", "#FFA0A0"},
	{"LightGreen", "#80FF80"},
	{"LightMagenta", "#FFA0FF"},
	{"DarkCyan", "#008080"},
	{"DarkBlue", "#0000C0"},
	{"DarkRed", "#C00000"},
	{"DarkMagenta", "#C000C0"},
	{"DarkGrey", "#C0C0C0"},
	{NULL, NULL}
    };

    if (!gui.in_use)		/* can't do this when GUI not running */
	return (guicolor_T)(-1);

    QColor _color((const char *)name);

    if (_color.isValid())
    {
	// return (_color.red() << 16) + ((_color.green() << 8))
							 // + (_color.blue());
	return _color.rgb();
	// return (guicolor_T) _color.pixel();
    }

    /* add a few builtin names */
    for (i = 0;; ++i)
    {
	if (vimnames[i][0] == NULL)
	    return (guicolor_T)(-1);
	if (STRICMP(name, vimnames[i][0]) == 0)
	{
	    name = (char_u *) vimnames[i][1];
	    return gui_mch_get_color(name);
	}
    }

    return (guicolor_T)(-1); // dead code, should not be reached..
}//}}}

/*
 * Set the current text foreground color.
 */
    void
gui_mch_set_fg_color(guicolor_T color)//{{{
{
    QColor rgb;
    rgb.setRgb(color);
    gui.w->painter->setPen(rgb);
}//}}}

/*
 * Set the current text background color.
 */
    void
gui_mch_set_bg_color(guicolor_T color)//{{{
{
    QColor rgb;
    rgb.setRgb(color);
    gui.w->painter->setBackgroundColor(rgb);
}//}}}

/*
 * Set the current text special color.
 */
    void
gui_mch_set_sp_color(guicolor_T color)//{{{
{
    /* TODO */
}//}}}

/*
 * Use the blank mouse pointer or not.
 *
 * hide: TRUE = use blank ptr, FALSE = use parent ptr
 */
    void
gui_mch_mousehide(int hide)//{{{
{
    if (hide == gui.pointer_hidden)
	return;
    //#ifdef FEAT_MOUSESHAPE
    //	if (!hide) mch_set_mouse_shape(last_shape);
    //#else
# if (QT_VERSION<300)
    gui.w->setCursor((hide)?BlankCursor:ArrowCursor);
# else
    gui.w->setCursor((hide)?Qt::BlankCursor:Qt::ArrowCursor);
# endif
    //#endif
    gui.pointer_hidden = hide;
}//}}}

    void
gui_mch_update_codec()
{
#ifdef FEAT_MBYTE
    if (!gui.in_use)
	return;
    vmw->codec = QTextCodec::codecForName((const char *)p_enc);
    if (vmw->codec == NULL)
	vmw->codec = QTextCodec::codecForName(
		KVimUtils::convertEncodingName(QString((const char*)p_enc)));
    if (vmw->codec == NULL)
	vmw->codec = QTextCodec::codecForLocale();
#else
    vmw->codec = QTextCodec::codecForLocale();
#endif
    if (vmw->codec == NULL)
	vmw->codec = QTextCodec::codecForName("ISO-8859-1"); //fallback
}

    void
gui_mch_draw_string(int row, int col, char_u * s, int len, int flags)//{{{
{
    QString text = vmw->codec->toUnicode((const char *)s, len);
    gui.w->draw_string(TEXT_X(col), TEXT_Y(row), text, text.length(), flags);
}//}}}

#if defined(FEAT_TITLE) || defined(PROTO)
/*
 * Return the text window-id and display.  Only required for X-based GUI's
 */
    int
gui_get_x11_windis(Window * win, Display ** dis)//{{{
{
    *win = /*vmw*/gui.w->winId();
    *dis = qt_xdisplay();
    return OK;
}//}}}
#endif

    void
gui_mch_beep()//{{{
{
    kapp->beep();
}//}}}

    void
gui_mch_flash(int msec)//{{{
{
    gui.w->flash();
}//}}}

/*
 * Invert a rectangle from row r, column c, for nr rows and nc columns.
 */
    void
gui_mch_invert_rectangle(int r, int c, int nr, int nc)//{{{
{
    bitBlt(gui.w,
	    FILL_X(c), FILL_Y(r),
	    gui.w,
	    FILL_X(c), FILL_Y(r),
	    (nc) * gui.char_width,
	    (nr) * gui.char_height,
	    Qt::NotROP,		// raster Operation
	    true);		// ignoreMask
}//}}}

/*
 * Iconify the GUI window.
 */
    void
gui_mch_iconify()//{{{
{
    vmw->showMinimized();
}//}}}

/*
 * Draw a cursor without focus.
 */
    void
gui_mch_draw_hollow_cursor(guicolor_T color)//{{{
{
    QPainter p(gui.w);
    p.setPen(color);

    p.drawRect(FILL_X(gui.col), FILL_Y(gui.row), gui.char_width - 1,
							 gui.char_height - 1);
    p.end();
}//}}}

/*
 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
 * color "color".
 */
    void
gui_mch_draw_part_cursor(int w, int h, guicolor_T color)//{{{
{
    QPainter p(gui.w);
    p.setPen(color);
    p.fillRect(
	    FILL_X(gui.col),
	    FILL_Y(gui.row) + gui.char_height - h + 1,
	    w, h - 2, QColor( color, color));
    p.drawRect(FILL_X(gui.col), FILL_Y(gui.row) + gui.char_height - h
						       + (int)p_linespace / 2,
	    w, h - (int)p_linespace);
}//}}}


/*
 * Catch up with any queued X11 events.  This may put keyboard input into the
 * input buffer, call resize call-backs, trigger timers etc.  If there is
 * nothing in the X11 event queue (& no timers pending), then we return
 * immediately.
 */
    void
gui_mch_update()//{{{
{
    kapp->processEvents();
}//}}}


/*
 * GUI input routine called by gui_wait_for_chars().  Waits for a character
 * from the keyboard.
 *  wtime == -1     Wait forever.
 *  wtime == 0      This should never happen.
 *  wtime > 0       Wait wtime milliseconds for a character.
 * Returns OK if a character was found to be available within the given time,
 * or FAIL otherwise.
 */
    int
gui_mch_wait_for_chars(long wtime)//{{{
{
    // malte@kde.org's  gift to KVim ;), thanks to him :) for this hard to find bug
    if (wtime > 0)
    {
	gui.w->wait(wtime);
	while (vim_is_input_buf_empty() && !gui.w->wait_done)
	    kapp->processOneEvent();
	return vim_is_input_buf_empty() ? FAIL : OK;
    }
    else
	while (vim_is_input_buf_empty())
	    kapp->processOneEvent();

    return OK;
}//}}}


/****************************************************************************
 * Output drawing routines.
 ****************************************************************************/


/* Flush any output to the screen */
    void
gui_mch_flush()//{{{
{
    kapp->flushX();
}//}}}

/*
 * Clear a rectangular region of the screen from text pos (row1, col1) to
 * (row2, col2) inclusive.
 */
    void
gui_mch_clear_block(int row1, int col1, int row2, int col2)//{{{
{
    gui.w->erase(FILL_X(col1), FILL_Y(row1),
	    (col2 - col1 + 1) * gui.char_width + (col2 == Columns - 1),
	    (row2 - row1 + 1) * gui.char_height);
}//}}}

    void
gui_mch_clear_all(void)//{{{
{
    gui.w->erase();
}//}}}


/*
 * Delete the given number of lines from the given row, scrolling up any
 * text further down within the scroll region.
 */
    void
gui_mch_delete_lines(int row, int num_lines)//{{{
{
    if (num_lines <= 0)
	return;

    if (row + num_lines > gui.scroll_region_bot)
    {
	/* Scrolled out of region, just blank the lines out */
	gui_clear_block(row, gui.scroll_region_left, gui.scroll_region_bot,
						     gui.scroll_region_right);
    }
    else
    {
	bitBlt(gui.w,
		FILL_X(gui.scroll_region_left), FILL_Y(row),
		gui.w,
		FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
		gui.char_width * (gui.scroll_region_right
					    - gui.scroll_region_left + 1) + 1,
		gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
		Qt::CopyROP,	    // raster Operation
		true);		    // ignoreMask

	/* Update gui.cursor_row if the cursor scrolled or copied over */
	if (gui.cursor_row >= row)
	{
	    if (gui.cursor_row < row + num_lines)
		gui.cursor_is_valid = FALSE;
	    else if (gui.cursor_row <= gui.scroll_region_bot)
		gui.cursor_row -= num_lines;
	}

	gui_clear_block(gui.scroll_region_bot - num_lines + 1,
		gui.scroll_region_left,
		gui.scroll_region_bot, gui.scroll_region_right);
    }
}//}}}

/*
 * Insert the given number of lines before the given row, scrolling down any
 * following text within the scroll region.
 */
    void
gui_mch_insert_lines(int row, int num_lines)//{{{
{
    if (num_lines <= 0)
	return;

    if (row + num_lines > gui.scroll_region_bot)
    {
	/* Scrolled out of region, just blank the lines out */
	gui_clear_block(row, gui.scroll_region_left, gui.scroll_region_bot,
						 gui.scroll_region_right - 1);
    }
    else
    {
	bitBlt(gui.w,
		FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
		gui.w,
		FILL_X(gui.scroll_region_left), FILL_Y(row),
		gui.char_width * ( gui.scroll_region_right
					   - gui.scroll_region_left + 1 ) + 1,
		gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
		Qt::CopyROP,	    // raster Operation
		true);		    // ignoreMask

	/* Update gui.cursor_row if the cursor scrolled or copied over */
	if (gui.cursor_row >= gui.row)
	{
	    if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
		gui.cursor_row += num_lines;
	    else if (gui.cursor_row <= gui.scroll_region_bot)
		gui.cursor_is_valid = FALSE;
	}

	gui_clear_block(row, gui.scroll_region_left, row + num_lines - 1,
						     gui.scroll_region_right);
    }
}//}}}

/*
 * X Selection stuff, for cutting and pasting text to other windows.
 */
    void
clip_mch_request_selection(VimClipboard *cbd)//{{{
{
#if QT_VERSION>=300
    if (cbd == &clip_star)
	kapp->clipboard()->setSelectionMode(true);
#endif
    QString selection = kapp->clipboard()->text();

    QCString unistring = vmw->codec->fromUnicode(selection);
    clip_yank_selection(MCHAR, (char_u *)(const char*)unistring,
					      (long)unistring.length(), cbd);
#if QT_VERSION>=300
    if (cbd == &clip_star)
	kapp->clipboard()->setSelectionMode(false);
#endif
}//}}}

    void
clip_mch_lose_selection(VimClipboard *cbd)//{{{
{
    // Don't need to do anything here
    gui_mch_update();
}//}}}

/*
 * Check whatever we allready own the selection.
 */
    int
clip_mch_own_selection(VimClipboard *cbd)//{{{
{
    if (kapp->clipboard()->ownsSelection())
	return OK;
    else
    {
#if QT_VERSION>=300
	kapp->clipboard()->setSelectionMode(true);
#endif
	return OK;
    }
}//}}}

/*
 * Send the current selection to the clipboard.
 */
    void
clip_mch_set_selection(VimClipboard *cbd)//{{{
{
    char_u *data;
    long_u length;

    clip_get_selection(cbd);
    if (clip_convert_selection(&data, &length, cbd) < 0)
	return;

    QString selection((const char *)data);
    // We must turncate the string because it is not
    // null terminated
    selection.truncate((uint) length);

#if QT_VERSION>=300
    if (cbd == &clip_star)
	kapp->clipboard()->setSelectionMode(true);
#endif
    kapp->clipboard()->setText(selection);
#if QT_VERSION>=300
    kapp->clipboard()->setSelectionMode(false);
#endif
}//}}}


#if defined(FEAT_MENU) || defined(PROTO)
/*
 * Make a menu item appear either active or not active (grey or not grey).
 */
    void
gui_mch_menu_grey(vimmenu_T * menu, int grey)//{{{
{
    if (!menu || !menu->parent || !menu->parent->widget)
	return;
    menu->parent->widget->setItemEnabled((long)menu, !grey);
    gui_mch_update();
}//}}}

/*
 * Make menu item hidden or not hidden.
 */
    void
gui_mch_menu_hidden(vimmenu_T * menu, int hidden)//{{{
{
    // FIXME: cannot be fixed AFAIK
    // it's hard to remove an item in a QPopupMenu
    gui_mch_menu_grey(menu, hidden);
}//}}}

/*
 * This is called after setting all the menus to grey/hidden or not.
 */
    void
gui_mch_draw_menubar()//{{{
{
    // nothing to do under kde
}//}}}
#endif

/*
 * Scrollbar stuff.
 */
    void
gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)//{{{
{
    if (!sb->w)
	return;
    int width = gui.w->width();
    int height = gui.w->height();
    int neww = vmw->width();
    int newh = vmw->height();

    if (gui.which_scrollbars[SBAR_LEFT])
	width += gui.scrollbar_width;
    if (gui.which_scrollbars[SBAR_RIGHT])
	width += gui.scrollbar_width;
    if (gui.which_scrollbars[SBAR_BOTTOM])
	height += gui.scrollbar_height;

    if (vmw->menuBar()->isVisible() && vmw->menuBar()->isEnabled()
#if QT_VERSION>=300
	    && !vmw->menuBar()->isTopLevelMenu()
#endif
       )
	height += vmw->menuBar()->height();
#ifdef FEAT_TOOLBAR
    if (vmw->toolBar()->isVisible() && vmw->toolBar()->isEnabled()
	    && (vmw->toolBar()->barPos() == KToolBar::Top
		|| vmw->toolBar()->barPos() == KToolBar::Bottom))
	height += vmw->toolBar()->height();

    if (vmw->toolBar()->isVisible() && vmw->toolBar()->isEnabled()
	    && (vmw->toolBar()->barPos() == KToolBar::Left
		|| vmw->toolBar()->barPos() == KToolBar::Right))
	width += vmw->toolBar()->width();
#endif
    if (abs(vmw->width() - width) > 5
			 && (sb->type == SBAR_LEFT || sb->type == SBAR_RIGHT))
	neww = width;
    if (abs(vmw->height() - height) > 5 && (sb->type == SBAR_BOTTOM))
	newh = height;

    if (flag)
	sb->w->show();
    else
	sb->w->hide();
    gui_mch_update();
    vmw->lock();
    vmw->resize(neww, newh);
    vmw->unlock();
    gui_mch_update();
}//}}}

/*
 * Return the RGB value of a pixel as "#RRGGBB".
 */
    long_u
gui_mch_get_rgb(guicolor_T pixel)//{{{
{
    //	QColor c(pixel, pixel);
    //	return (c.red() << 16) +  ((c.green() << 8)) + (c.blue());
    return pixel;
    // funny no ? it looks like with Qt we can always use directly the rgb
    // value (i hope i don't break colors again ;p)
}//}}}

/*
 * Get current mouse coordinates in text window.
 */
    void
gui_mch_getmouse(int *x, int *y)//{{{
{
    *x = vmw->mapFromGlobal(QCursor::pos()).x();
    *y = vmw->mapFromGlobal(QCursor::pos()).y();
}//}}}

    void
gui_mch_setmouse(int x, int y)//{{{
{
    QCursor::setPos(vmw->mapToGlobal(QPoint(x, y)));
}//}}}

#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
#if QT_VERSION>=300
static int mshape_ids[] = {//{{{
	Qt::ArrowCursor,		/* arrow */
	Qt::BlankCursor,		/* blank */
	Qt::IbeamCursor,		/* beam */
	Qt::SizeVerCursor,		/* updown */
	Qt::SplitHCursor,		/* udsizing */
	Qt::SizeHorCursor,		/* leftright */
	Qt::SizeHorCursor,		/* lrsizing */
	Qt::WaitCursor,			/* busy */
	Qt::ForbiddenCursor,		/* no */
	Qt::CrossCursor,		/* crosshair */
	Qt::PointingHandCursor,		/* hand1 */
	Qt::PointingHandCursor,		/* hand2 */
	Qt::ArrowCursor,		/* pencil */
	Qt::WhatsThisCursor,		/* question */
	Qt::ArrowCursor,		/* right-arrow */
	Qt::UpArrowCursor,		/* up-arrow */
	Qt::ArrowCursor			/* last one */
};//}}}
#else
static int mshape_ids[] = {//{{{
	ArrowCursor,		/* arrow */
	BlankCursor,		/* blank */
	IbeamCursor,		/* beam */
	SizeVerCursor,		/* updown */
	SplitHCursor,		/* udsizing */
	SizeHorCursor,		/* leftright */
	SizeHorCursor,		/* lrsizing */
	WaitCursor,		/* busy */
	ForbiddenCursor,	/* no */
	CrossCursor,		/* crosshair */
	PointingHandCursor,	/* hand1 */
	PointingHandCursor,	/* hand2 */
	ArrowCursor,		/* pencil */
	ArrowCursor,		/* question */
	ArrowCursor,		/* right-arrow */
	UpArrowCursor,		/* up-arrow */
	ArrowCursor		/* last one */
};//}}}
#endif

    void
mch_set_mouse_shape (int shape)//{{{
{
    int		   id;

    if (shape == MSHAPE_HIDE || gui.pointer_hidden)
#if QT_VERSION>=300
	gui.w->setCursor(Qt::BlankCursor);
#else
	gui.w->setCursor(BlankCursor);
#endif
    else
    {
	if (shape >= MSHAPE_NUMBERED)
	{
	    id = shape - MSHAPE_NUMBERED;
	    /*		if (id >= GDK_NUM_GLYPHS)
			id = GDK_LEFT_PTR;
			else
			id &= ~1;*/	/* they are always even (why?) */
	    id &= -1;
	}
	else
	    id = mshape_ids[shape];

	gui.w->setCursor(id);
    }
    if (shape != MSHAPE_HIDE)
	last_shape = shape;
}//}}}
#endif

    int
gui_mch_adjust_charsize ()//{{{
{
    QFont f(*(gui.current_font));
    QFontMetrics fm(f);
    gui.char_height = fm.height() + p_linespace;
    //gui.char_height = fm.ascent() + fm.descent() + p_linespace;
    gui.char_ascent = fm.ascent() + p_linespace / 2;

    return OK;
}//}}}

    void
gui_mch_set_foreground ()//{{{
{
    KWin::activateWindow(vmw->winId());
}//}}}
