blob: ae48abfe05f14693218a4c0c054f1a760fa40f1d [file] [log] [blame]
/* 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$
*
*/
#include <assert.h>
#include <qpainter.h>
#include <qevent.h>
#include <qpushbutton.h>
#include <qscrollbar.h>
#include <qlayout.h>
#include <qclipboard.h>
#include <qdragobject.h>
#include <qstrlist.h>
#include <qmenubar.h>
#include <qtextcodec.h>
#if QT_VERSION>=300
#include <qptrlist.h>
#include <ktip.h>
#endif
#include <kglobal.h>
#include <kconfig.h>
#include <kaboutapplication.h>
#include <dcopclient.h>
#include <kaboutkde.h>
#include <kbugreport.h>
#include <kurldrag.h>
#include <kmenubar.h>
#include <ktoolbar.h>
#include <kstandarddirs.h>
#include "gui_kde_widget.h"
#include <qxembed.h>
extern "C" {
#include "version.h"
}
// Pixmap for dialog
#ifdef FEAT_GUI_DIALOG
# include "../../pixmaps/alert.xpm"
# include "../../pixmaps/error.xpm"
# include "../../pixmaps/generic.xpm"
# include "../../pixmaps/info.xpm"
# include "../../pixmaps/quest.xpm"
#endif
/**
* Keycodes recognized by vim.
*/
struct special_key {//{{{
int qtkey;
char_u code0;
char_u code1;
} special_keys[] =
{
{ Qt::Key_Up, 'k', 'u' },
{ Qt::Key_Down, 'k', 'd' },
{ Qt::Key_Left, 'k', 'l' },
{ Qt::Key_Right, 'k', 'r' },
{ Qt::Key_F1, 'k', '1' },
{ Qt::Key_F2, 'k', '2' },
{ Qt::Key_F3, 'k', '3' },
{ Qt::Key_F4, 'k', '4' },
{ Qt::Key_F5, 'k', '5' },
{ Qt::Key_F6, 'k', '6' },
{ Qt::Key_F7, 'k', '7' },
{ Qt::Key_F8, 'k', '8' },
{ Qt::Key_F9, 'k', '9' },
{ Qt::Key_F10, 'k', ';' },
{ Qt::Key_F11, 'F', '1' },
{ Qt::Key_F12, 'F', '2' },
{ Qt::Key_F13, 'F', '3' },
{ Qt::Key_F14, 'F', '4' },
{ Qt::Key_F15, 'F', '5' },
{ Qt::Key_F16, 'F', '6' },
{ Qt::Key_F17, 'F', '7' },
{ Qt::Key_F18, 'F', '8' },
{ Qt::Key_F19, 'F', '9' },
{ Qt::Key_F20, 'F', 'A' },
{ Qt::Key_F21, 'F', 'B' },
{ Qt::Key_F22, 'F', 'C' },
{ Qt::Key_F23, 'F', 'D' },
{ Qt::Key_F24, 'F', 'E' },
{ Qt::Key_F25, 'F', 'F' },
{ Qt::Key_F26, 'F', 'G' },
{ Qt::Key_F27, 'F', 'H' },
{ Qt::Key_F28, 'F', 'I' },
{ Qt::Key_F29, 'F', 'J' },
{ Qt::Key_F30, 'F', 'K' },
{ Qt::Key_F31, 'F', 'L' },
{ Qt::Key_F32, 'F', 'M' },
{ Qt::Key_F33, 'F', 'N' },
{ Qt::Key_F34, 'F', 'O' },
{ Qt::Key_F35, 'F', 'P' },
{ Qt::Key_Help, '%', '1' },
// { Qt::Key_Undo, '&', '8' }, <= hmmm ?
{ Qt::Key_BackSpace, 'k', 'b' },
{ Qt::Key_Insert, KS_EXTRA, KE_KINS },
{ Qt::Key_Delete, KS_EXTRA, KE_KDEL },
{ Qt::Key_Home, 'K', '1' },
{ Qt::Key_End, 'K', '4' },
{ Qt::Key_Prior, 'K', '3' },
{ Qt::Key_Next, 'K', '5' },
{ Qt::Key_Print, '%', '9' },
{ Qt::Key_Plus, 'K', '6'},
{ Qt::Key_Minus, 'K', '7'},
{ Qt::Key_Slash, 'K', '8'},
{ Qt::Key_multiply, 'K', '9'},
{ Qt::Key_Enter, 'K', 'A'},
{ Qt::Key_Period, 'K', 'B'},
{ Qt::Key_0, 'K', 'C'},
{ Qt::Key_1, 'K', 'D'},
{ Qt::Key_2, 'K', 'E'},
{ Qt::Key_3, 'K', 'F'},
{ Qt::Key_4, 'K', 'G'},
{ Qt::Key_5, 'K', 'H'},
{ Qt::Key_6, 'K', 'I'},
{ Qt::Key_7, 'K', 'J'},
{ Qt::Key_8, 'K', 'K'},
{ Qt::Key_9, 'K', 'L'},
/* End of list marker: */
{ 0, 0, 0 }
};//}}}
#ifdef FEAT_CLIENTSERVER
typedef int (*QX11EventFilter) (XEvent*);
extern QX11EventFilter qt_set_x11_event_filter (QX11EventFilter filter);
static QX11EventFilter oldFilter = 0;
static int kvim_x11_event_filter( XEvent* e);
#endif
void gui_keypress(QKeyEvent *e);
/*
* Return OK if the key with the termcap name "name" is supported.
*/
int
gui_mch_haskey(char_u * name)//{{{
{
for (int i=0; special_keys[i].qtkey != 0; i++)
if (name[0] == special_keys[i].code0 &&
name[1] == special_keys[i].code1)
return OK;
return FAIL;
}//}}}
/*
* custom Frame for drawing ...
*/
void VimWidget::paintEvent( QPaintEvent *e)//{{{
{
QRect r = e->rect();
gui_redraw(r.x(), r.y(), r.width(), r.height() );
}//}}}
void VimWidget::draw_string(int x, int y, QString s, int len, int flags)//{{{
{
gui.current_font->setBold( flags & DRAW_BOLD );
gui.current_font->setUnderline( flags & DRAW_UNDERL );
gui.current_font->setItalic(flags & DRAW_ITALIC);
painter->setBackgroundMode( flags & DRAW_TRANSP ? Qt::TransparentMode : Qt::OpaqueMode);
painter->setFont( *(gui.current_font) );
painter->drawText( x, y, s, len);
}//}}}
void VimWidget::mousePressEvent(QMouseEvent *event)//{{{
{
int button=0;
int modifiers=0;
ButtonState state = event->state();
ButtonState buttons = event->button();
//Look at button states
if(buttons & QMouseEvent::LeftButton) {
button|=MOUSE_LEFT;
}
if(buttons & QMouseEvent::RightButton) {
button|=MOUSE_RIGHT;
}
if(buttons & QMouseEvent::MidButton) {
button|=MOUSE_MIDDLE;
}
//Look for keyboard modifiers
if(state & QMouseEvent::ShiftButton) {
modifiers|=MOUSE_SHIFT;
}
if(state & QMouseEvent::ControlButton){
modifiers|=MOUSE_CTRL;
}
if(state & QMouseEvent::AltButton){
modifiers|=MOUSE_ALT;
}
gui_send_mouse_event(button,event->x(),event->y(),FALSE,modifiers);
#if QT_VERSION>=300
QByteArray params;
QDataStream stream(params, IO_WriteOnly);
stream << kapp->dcopClient()->appId() << button << modifiers << gui.row << gui.col;
kapp->dcopClient()->emitDCOPSignal("mousePEvent(QCString,int,int,int,int)", params);
#endif
event->accept();
}//}}}
#if defined(FEAT_SESSION)
void VimMainWindow::saveGlobalProperties (KConfig *conf)
{
//we write a mksession file to a file written in the user's ~/.kde/share/config/
//the name of the file in saved in 'conf'
//when restoring app, we source this file
#if 0 //disabled for release
QString filename = KGlobal::dirs()->localkdedir() + KGlobal::dirs()->kde_default("config") + kapp->randomString(10);
QString cmd("mksession ");
cmd+=filename;
do_cmdline_cmd((char_u*)cmd.latin1());
conf->writePathEntry("sessionfile", filename);
conf->sync();
#endif
}
void VimMainWindow::readGlobalProperties (KConfig *conf)
{
#if 0
QString filename = conf->readPathEntry("sessionfile");
if (filename.isNull()) return;
QString cmd("source ");
cmd+=filename;
do_cmdline_cmd((char_u*)cmd.latin1());
#endif
}
#endif
void VimMainWindow::wheelEvent (QWheelEvent *event)//{{{
{
ButtonState state = event->state();
int button=0;
int modifiers=0;
if (event->delta()>0)
button|=MOUSE_4;
else button|=MOUSE_5;
if(state & ShiftButton)
modifiers|=MOUSE_SHIFT;
if(state & ControlButton)
modifiers|=MOUSE_CTRL;
if(state & AltButton)
modifiers|=MOUSE_ALT;
gui_send_mouse_event(button,event->x(),event->y(),FALSE,modifiers);
#if QT_VERSION>=300
QByteArray params;
QDataStream stream(params, IO_WriteOnly);
stream << kapp->dcopClient()->appId() << button << modifiers << gui.row << gui.col;
kapp->dcopClient()->emitDCOPSignal("mouseWhlEvent(QCString, int, int,int,int)", params);
#endif
event->accept();
}//}}}
void VimWidget::mouseDoubleClickEvent(QMouseEvent *event)//{{{
{
ButtonState state = event->state();
ButtonState buttons = event->button();
int modifiers=0;
int button=0;
//Look at button states
if(buttons & LeftButton)
button|=MOUSE_LEFT;
if(buttons & RightButton)
button|=MOUSE_RIGHT;
if(buttons & MidButton)
button|=MOUSE_MIDDLE;
//Look for keyboard modifiers
if(state & ShiftButton)
modifiers|=MOUSE_SHIFT;
if(state & ControlButton)
modifiers|=MOUSE_CTRL;
if(state & AltButton)
modifiers|=MOUSE_ALT;
gui_send_mouse_event(button,event->x(),event->y(),TRUE,modifiers);
#if QT_VERSION>=300
QByteArray params;
QDataStream stream(params, IO_WriteOnly);
stream << kapp->dcopClient()->appId() << button << modifiers << gui.row << gui.col;
kapp->dcopClient()->emitDCOPSignal("mouseDblClickEvent(QCString, int, int,int,int)", params);
#endif
event->accept();
}//}}}
void VimWidget::mouseMoveEvent(QMouseEvent *event){//{{{
ButtonState state = event->state();
int modifiers=0;
int button=0;
gui_mch_mousehide(FALSE);
//Look at button states
//warning: we use state here, this is important !
if(state & QMouseEvent::LeftButton || state & QMouseEvent::RightButton || state & QMouseEvent::MidButton)
button|=MOUSE_DRAG;
//Look for keyboard modifiers
if(state & ShiftButton)
modifiers|=MOUSE_SHIFT;
if(state & ControlButton)
modifiers|=MOUSE_CTRL;
if(state & AltButton)
modifiers|=MOUSE_ALT;
if (button!=MOUSE_DRAG)
gui_mouse_moved(event->x(),event->y());
else
gui_send_mouse_event(MOUSE_DRAG,event->x(),event->y(),FALSE,modifiers);
}//}}}
void VimWidget::mouseReleaseEvent(QMouseEvent *event)//{{{
{
ButtonState state = event->state();
int modifiers=0;
//Look for keyboard modifiers
if(state & ShiftButton)
modifiers|=MOUSE_SHIFT;
if(state & ControlButton)
modifiers|=MOUSE_CTRL;
if(state & AltButton)
modifiers|=MOUSE_ALT;
gui_send_mouse_event(MOUSE_RELEASE,event->x(),event->y(),FALSE,modifiers);
event->accept();
}//}}}
/*
* The main widget (everything but toolbar/menubar)
*/
VimWidget::VimWidget( QWidget *parent, const char *name, WFlags f )//{{{
:QWidget(parent, name, f)
,DCOPObject("KVim")
#ifdef FEAT_MZSCHEME
,mzscheme_timer_id(-1)
#endif
{
//to be able to show/hide the cursor when moving the mouse
setMouseTracking(true);
painter=new QPainter(this);
setKeyCompression(true);
setFocusPolicy( QWidget::StrongFocus );
setAcceptDrops(TRUE); // DND
blink_state = BLINK_NONE;
blink_on_time = 700;
blink_off_time = 400;
blink_wait_time = 250;
connect( &blink_timer, SIGNAL( timeout() ), SLOT( blink_cursor() ));
connect( &wait_timer, SIGNAL( timeout() ), SLOT ( wait_timeout() ));
}//}}}
void VimWidget::execNormal(QString command)//{{{
{
QString cmd("execute 'normal ");
cmd+=command;
cmd+="'";
QCString unistring = vmw->codec->fromUnicode(cmd);
do_cmdline_cmd((char_u *)(const char*)unistring);
gui_update_screen();
}//}}}
void VimWidget::execInsert(QString command)//{{{
{
QString cmd("execute 'normal i");
cmd+=command;
cmd+="'";
QCString unistring = vmw->codec->fromUnicode(cmd);
do_cmdline_cmd((char_u *)(const char*)unistring);
gui_update_screen();
}//}}}
void VimWidget::execRaw(QString command)//{{{
{
QString cmd("execute '");
cmd+=command;
cmd+="'";
QCString unistring = vmw->codec->fromUnicode(cmd);
do_cmdline_cmd((char_u *)(const char*)unistring);
gui_update_screen();
}//}}}
void VimWidget::execCmd(QString command)//{{{
{
QCString unistring = vmw->codec->fromUnicode(command);
do_cmdline_cmd((char_u *)(const char*)unistring);
gui_update_screen();
}//}}}
QString VimWidget::eval(QString expr)//{{{
{
#ifdef FEAT_EVAL
QCString unistring = vmw->codec->fromUnicode(expr);
QString val((const char *)eval_to_string((char_u *)(const char*)unistring,NULL));
return val;
#else
return QString::null;
#endif
}//}}}
void VimWidget::wait(long wtime)//{{{
{
if ( wait_timer.isActive() ) wait_timer.stop();
wait_done = false;
wait_timer.start( wtime, true);
}//}}}
void VimWidget::wait_timeout() //{{{
{
wait_done = true;
}//}}}
void VimWidget::dragEnterEvent (QDragEnterEvent *e)//{{{
{
#if (defined(FEAT_WINDOWS) && defined(HAVE_DROP_FILE)) || defined(PROTO)
e->accept(QUriDrag::canDecode(e));
#else
e->ignore();
#endif
}//}}}
void VimWidget::dropEvent (QDropEvent *e) // {{{
{
#if (defined(FEAT_WINDOWS) && defined(HAVE_DROP_FILE)) || defined(PROTO)
QStrList urls;
char_u **fnames;
int redo_dirs = FALSE;
int i;
int n;
int nfiles;
int url = FALSE;
/* Count how many items there may be and normalize delimiters. */
if (QUriDrag::decode(e, urls)) {
n = urls.count();
fnames = (char_u **)lalloc((n+1) * sizeof(char_u *), TRUE);
nfiles = 0;
#if QT_VERSION>=300
QPtrListIterator<char> it(urls);
for( ; it.current(); ++it ) {
KURL u(*it);
#else
for (i=0;i<urls.count();++i) {
KURL u(urls.at(i));
#endif
if ( !u.isLocalFile() )
url = TRUE;
else {
fnames[nfiles] = (char_u *)strdup((const char *)u.path());
++nfiles;
}
}
/* Real files (i.e. not http and not ftp) */
if (url == FALSE)
{
if (nfiles == 1)
{
if (mch_isdir(fnames[0]))
{
/* Handle dropping a directory on Vim. */
if (mch_chdir((char *)fnames[0]) == 0)
{
free(fnames[0]);
fnames[0] = NULL;
redo_dirs = TRUE;
}
}
} else {
/* Ignore any directories */
for (i = 0; i < nfiles; ++i)
{
if (mch_isdir(fnames[i]))
{
vim_free(fnames[i]);
fnames[i] = NULL;
}
}
}
if (0)
{
/* Shift held down, change to first file's directory */
if (fnames[0] != NULL && vim_chdirfile(fnames[0]) == OK)
redo_dirs = TRUE;
} else {
char_u dirname[MAXPATHL];
char_u *s;
if (mch_dirname(dirname, MAXPATHL) == OK)
for (i = 0; i < nfiles; ++i)
if (fnames[i] != NULL)
{
s = shorten_fname(fnames[i], dirname);
if (s != NULL && (s = vim_strsave(s)) != NULL)
{
vim_free(fnames[i]);
fnames[i] = s;
}
}
}
}
/* Handle the drop, :edit or :split to get to the file */
handle_drop(nfiles, fnames, FALSE);
if (redo_dirs)
shorten_fnames(TRUE);
}
/* Update the screen display */
update_screen(NOT_VALID);
#ifdef FEAT_MENU
gui_update_menus(0);
#endif
setcursor();
out_flush();
gui_update_cursor(FALSE, FALSE);
gui_mch_flush();
#endif
} // }}}
void VimWidget::keyPressEvent( QKeyEvent *e ) // {{{
{
gui_keypress(e);
} // }}}
void gui_keypress(QKeyEvent *e) { // {{{
int key = (int)e->key();
int modifiers = 0,i;
uchar string[256],string2[256];
uchar *s,*d;
Qt::ButtonState state = e->state();
QCString unistring = vmw->codec->fromUnicode(e->text());
if (unistring.length()>0)
strncpy((char*)string, (const char*)unistring,unistring.length());
string[unistring.length()] = 0;
int len=unistring.length();
// ignore certain keys
if (key == Qt::Key_Shift || key == Qt::Key_Alt || key == Qt::Key_Control || key == Qt::Key_Meta
|| key == Qt::Key_CapsLock || key == Qt::Key_NumLock || key == Qt::Key_ScrollLock ) {
e->ignore();
return;
}
#ifdef FEAT_MBYTE
if (input_conv.vc_type != CONV_NONE)
{
mch_memmove(string2, string, len);
len = convert_input(string2, len, sizeof(string2));
s = string2;
}
else
#endif
s = string;
d = string;
for (i = 0; i < len; ++i)
{
*d++ = s[i];
if (d[-1] == CSI && d + 2 < string + sizeof(string))
{
/* Turn CSI into K_CSI. */
*d++ = KS_EXTRA;
*d++ = (int)KE_CSI;
}
}
len = d - string;
// change shift-tab (backtab) into S_TAB
if ( key == Qt::Key_BackTab && state & Qt::ShiftButton) {
key = Qt::Key_Tab;
}
// Change C-@ and C-2 in NUL ? Gtk does this
if ( (key == Qt::Key_2 || key == Qt::Key_At)
&& state & Qt::ControlButton ) {
string[0] = NUL;
len = 1;
}
else if (len == 0 && (key == Qt::Key_Space || key == Qt::Key_Tab))
{
/* When there are modifiers, these keys get zero length; we need the
* original key here to be able to add a modifier below. */
string[0] = (key & 0xff);
len = 1;
}
/* Check for Alt/Meta key (Mod1Mask), but not for a BS, DEL or character
* that already has the 8th bit set.
* Don't do this for <S-M-Tab>, that should become K_S_TAB with ALT. */
if (len == 1
&& (key != Qt::Key_BackSpace && key != Qt::Key_Delete)
&& (string[0] & 0x80) == 0
&& (state & Qt::AltButton)
&& !(key == Qt::Key_Tab && (state & Qt::ShiftButton)))
{
string[0] |= 0x80;
#ifdef FEAT_MBYTE
if (enc_utf8) // convert to utf-8
{
string[1] = string[0] & 0xbf;
string[0] = ((unsigned)string[0] >> 6) + 0xc0;
if (string[1] == CSI)
{
string[2] = KS_EXTRA;
string[3] = (int)KE_CSI;
len = 4;
}
else
len = 2;
}
#endif
}
/* Check for special keys, making sure BS and DEL are recognised. */
if (len == 0 || key == Qt::Key_BackSpace || key == Qt::Key_Delete)
{
while (special_keys[i].qtkey != 0 && special_keys[i].qtkey != key ) i++;
if (special_keys[i].qtkey != 0) {
string[0] = CSI;
string[1] = special_keys[i].code0;
string[2] = special_keys[i].code1;
len = -3;
}
/*
for (i = 0; special_keys[i].qtkey != 0 ; i++)
{
if (special_keys[i].qtkey == key ) {
string[0] = CSI;
string[1] = special_keys[i].code0;
string[2] = special_keys[i].code1;
len = -3;
break;
}
}*/
}
if (len == 0) {
//no need to dump that, that's a QT problem, we can't do anything
//dbf("Unrecognised Key : %X %s", key, e->text().latin1());
e->ignore();
return;
}
/* Special keys (and a few others) may have modifiers */
if (len == -3 || key == Qt::Key_Space || key == Qt::Key_Tab ||
key == Qt::Key_Return || key == Qt::Key_Enter ||
key == Qt::Key_Escape) {
modifiers = 0;
if (state & Qt::ShiftButton) modifiers |= MOD_MASK_SHIFT;
if (state & Qt::ControlButton) modifiers |= MOD_MASK_CTRL;
if (state & Qt::AltButton) modifiers |= MOD_MASK_ALT;
/*
* For some keys a shift modifier is translated into another key
* code. Do we need to handle the case where len != 1 and
* string[0] != CSI?
*/
if (len == -3)
key = TO_SPECIAL(string[1], string[2]);
else
key = string[0];
key = simplify_key(key, &modifiers);
if (key == CSI) key=K_CSI;
if (IS_SPECIAL(key)) {
string[0] = CSI;
string[1] = K_SECOND(key);
string[2] = K_THIRD(key);
len = 3;
} else {
string[0] = key;
len = 1;
}
if (modifiers!=0) {
uchar string2[10];
string2[0] = CSI;
string2[1] = KS_MODIFIER;
string2[2] = modifiers;
add_to_input_buf(string2, 3);
}
} /* special keys */
if (len == 1 && ((string[0] == Ctrl_C && ctrl_c_interrupts)
|| (string[0] == intr_char && intr_char != Ctrl_C)))
{
trash_input_buf();
got_int = TRUE;
}
add_to_input_buf(string, len);
if (p_mh) {
gui_mch_mousehide(TRUE);
}
//DCOP Embedding stuff
//if we are here then the user has type something in the window, thus we can easily imagine that :
// 1 - text has changed (emit textChanged())
// 2 - characters were interactively inserted (emit charactersInteractivelyInserted())
// 3 - cursor position has changed ( emit cursorPositionChanged() )
// 4 - selection has changed ? dunno yet //XXX
// 5 - undo changed too ? (each character typed in makes the undo changes anyway)
// conclusion : this makes a lot of things to send to the vim kpart, maybe too much
// for now i'll just send : keyboardEvent to the kpart with the event string as parameter,
// with current current position
// i'll do the same for mouseEvents
#if QT_VERSION>=300
QByteArray params;
QDataStream stream(params, IO_WriteOnly);
stream << kapp->dcopClient()->appId() << unistring << gui.row << gui.col;
kapp->dcopClient()->emitDCOPSignal("keyboardEvent(QCString, QCString,int,int)", params);
#endif
e->ignore();
} // }}}
#ifdef FEAT_CLIENTSERVER
void VimWidget::serverActivate(WId id) //{{{
{
if (serverName == NULL && serverDelayedStartName != NULL) {
commWindow = id;
(void)serverRegisterName(qt_xdisplay(), serverDelayedStartName);
} else {
serverChangeRegisteredWindow( qt_xdisplay(), id );
}
}//}}}
#endif
#ifdef FEAT_XIM
void VimWidget::imStartEvent(QIMEvent *e) {
e->accept();
}
void VimWidget::imEndEvent(QIMEvent *e) {
uchar string[256];
QCString unistring = vmw->codec->fromUnicode(e->text());
if (unistring.length()>0)
strncpy((char*)string, (const char*)unistring,unistring.length());
string[unistring.length()] = 0;
int len=unistring.length();
add_to_input_buf(string, len);
e->accept();
}
void VimWidget::imComposeEvent(QIMEvent *e) {
//i should do something here, displaying the text somewhere ... (status area ?)
e->accept();
}
#endif
void VimMainWindow::lock()
{
locked=true;
}
void VimMainWindow::unlock()
{
locked=false;
}
bool VimMainWindow::isLocked()
{
return locked;
}
// ->resize VimWidget if not locked
//
void VimMainWindow::resizeEvent ( QResizeEvent *e ) //{{{
{
if ( vmw->isLocked() ) return;
//remove toolbar and menubar height
int height = e->size().height();
int width = e->size().width();
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
height = ( ((int)(height/gui.char_height))*gui.char_height );
if (!vmw->isLocked()) gui_resize_shell(width,height);
}//}}}
void VimWidget::focusInEvent( QFocusEvent * fe ) // {{{
{
gui_focus_change(true);
if (blink_state == BLINK_NONE)
gui_mch_start_blink();
} // }}}
void VimWidget::focusOutEvent( QFocusEvent * fe )//{{{
{
gui_focus_change(false);
if (blink_state != BLINK_NONE)
gui_mch_stop_blink();
}//}}}
void VimWidget::set_blink_time( long wait, long on, long off)//{{{
{
blink_wait_time = wait;
blink_on_time = on;
blink_off_time = off;
}//}}}
void VimWidget::start_cursor_blinking()//{{{
{
if (blink_timer.isActive()) blink_timer.stop();
/* Only switch blinking on if none of the times is zero */
if (blink_wait_time && blink_on_time && blink_off_time && gui.in_focus) {
blink_state = BLINK_ON;
gui_update_cursor(TRUE, FALSE);
// The first blink appears after wait_time
blink_timer.start( blink_wait_time, true);
}
}//}}}
void VimWidget::blink_cursor()//{{{
{
if (blink_state == BLINK_ON) {
// set cursor off
gui_undraw_cursor();
blink_state = BLINK_OFF;
blink_timer.start( blink_off_time, true);
} else {
// set cursor on
gui_update_cursor(TRUE, FALSE);
blink_state = BLINK_ON;
blink_timer.start( blink_on_time, true);
}
}//}}}
void VimWidget::stop_cursor_blinking()//{{{
{
if (blink_timer.isActive()) blink_timer.stop();
if (blink_state == BLINK_OFF)
gui_update_cursor(TRUE, FALSE);
blink_state = BLINK_NONE;
}//}}}
#ifdef FEAT_MZSCHEME
void VimWidget::timerEvent( QTimerEvent * evnt)//{{{
{
if (evnt->timerId() == mzscheme_timer_id)
timer_proc();
}//}}}
void VimWidget::enable_mzscheme_threads()//{{{
{
mzscheme_timer_id = startTimer(p_mzq);
}//}}}
void VimWidget::disable_mzscheme_threads()//{{{
{
killTimer(mzscheme_timer_id);
}//}}}
#endif
void VimWidget::flash()//{{{
{
QPainter p(this);
p.setRasterOp(Qt::XorROP);
p.fillRect(geometry(),QColor(0xFF,0xFF,0xFF));
p.flush();
//FIXME: Make this a little smarter. Maybe add a timer or something
usleep(19000);
p.fillRect(geometry(),QColor(0xFF,0xFF,0xFF));
p.flush();
p.end();
}//}}}
/*
* The main Window
*/
VimMainWindow::VimMainWindow ( const char *name , WFlags f)//{{{
:KMainWindow(0L, name,f)
{
#ifdef FEAT_CLIENTSERVER
oldFilter = qt_set_x11_event_filter( kvim_x11_event_filter );
#endif
if (echo_wid_arg== 1) {
fprintf(stderr, "WID: %ld\n", (long)winId());
fflush(stderr);
}
w = new VimWidget(this, "main vim widget");
gui.w = w;
setFocusProxy(w);
w->setFocus();
have_tearoff=0;
finddlg=new KEdFind (this,0,false);
repldlg=new KEdReplace (this,0,false);
QObject::connect( finddlg, SIGNAL(search()), this, SLOT(slotSearch()) );
QObject::connect( repldlg, SIGNAL(find()), this, SLOT(slotFind()) );
QObject::connect( repldlg, SIGNAL(replace()), this, SLOT(slotReplace()) );
QObject::connect( repldlg, SIGNAL(replaceAll()), this, SLOT(slotReplaceAll()) );
#ifdef FEAT_TOOLBAR
connect(toolBar(), SIGNAL(clicked(int)), this, SLOT(menu_activated(int)));
#endif
#ifdef FEAT_CLIENTSERVER
w->serverActivate(winId());
if (serverName!=NULL)
kapp->dcopClient()->registerAs(QCString((const char*)serverName),false);
else if (serverDelayedStartName!=NULL)
kapp->dcopClient()->registerAs(QCString((const char*)serverDelayedStartName),false);
else if (argServerName!=NULL)
kapp->dcopClient()->registerAs(argServerName->utf8(),false);
#else
if (argServerName!=NULL)
kapp->dcopClient()->registerAs(argServerName->utf8(),false);
#endif
QXEmbed::initialize();
}//{{{
bool VimMainWindow::queryClose()//{{{
{
gui_shell_closed();
return true;
}//}}}
bool VimMainWindow::queryExit()//{{{
{
return true;
}//}}}
void VimMainWindow::menu_activated(int dx)//{{{
{
#ifdef FEAT_MENU
if (!dx) { // tearoff
return;
}
gui_mch_set_foreground();
gui_menu_cb((VimMenu *) dx);
#endif
}//}}}
void VimMainWindow::clipboard_selection_update(){//{{{
if(kapp->clipboard()->ownsSelection()) {
clip_own_selection(&clip_star);
} else {
clip_lose_selection(&clip_star);
}
}//}}}
void VimMainWindow::clipboard_data_update(){//{{{
#if QT_VERSION>=300
if (kapp->clipboard()->ownsClipboard()) {
clip_own_selection(&clip_plus);
} else {
clip_lose_selection(&clip_plus);
}
#else
if (kapp->clipboard()->ownsSelection()) {
clip_own_selection(&clip_star);
} else {
clip_lose_selection(&clip_star);
}
#endif
}//}}}
void VimMainWindow::slotSearch()//{{{
{
QString find_text;
bool direction_down = TRUE;
bool casesensitive = TRUE;
int flags = FRD_FINDNEXT;
find_text = finddlg->getText();
direction_down = !(finddlg->get_direction());
casesensitive = finddlg->case_sensitive();
// if (casesensitive) find_text = "\\C" + find_text;
// else find_text = "\\c" + find_text;
if (casesensitive) flags|=FRD_MATCH_CASE;
QCString unistring = vmw->codec->fromUnicode(find_text);
gui_do_findrepl(flags, (char_u *)(const char *)unistring, NULL,(int)direction_down);
}//}}}
void VimMainWindow::slotFind()//{{{
{
QString find_text;
bool direction_down=TRUE;
bool casesensitive = TRUE;
int flags = FRD_R_FINDNEXT;
find_text=repldlg->getText();
direction_down = !(repldlg->get_direction());
casesensitive = repldlg->case_sensitive();
// if (casesensitive) find_text = "\\C" + find_text;
// else find_text = "\\c" + find_text;
if (casesensitive) flags|=FRD_MATCH_CASE;
QCString unistring = vmw->codec->fromUnicode(find_text);
gui_do_findrepl(flags, (char_u *)(const char *)unistring, NULL,(int)direction_down);
}//}}}
void VimMainWindow::slotReplace()//{{{
{
QString find_text;
QString repl_text;
bool direction_down=TRUE;
bool casesensitive = TRUE;
int flags = FRD_REPLACE;
find_text=repldlg->getText();
repl_text=repldlg->getReplaceText();
direction_down = !(repldlg->get_direction());
//if (casesensitive) find_text = "\\C" + find_text;
//else find_text = "\\c" + find_text;
if (casesensitive) flags|=FRD_MATCH_CASE;
QCString unistring = vmw->codec->fromUnicode(find_text);
QCString unistring2 = vmw->codec->fromUnicode(repl_text);
gui_do_findrepl(flags, (char_u *)(const char *)unistring,(char_u *)(const char*)unistring2,(int)direction_down);
}//}}}
void VimMainWindow::slotReplaceAll()//{{{
{
QString find_text;
QString repl_text;
bool direction_down=TRUE;
bool casesensitive = TRUE;
int flags = FRD_REPLACEALL;
find_text=repldlg->getText();
repl_text=repldlg->getReplaceText();
direction_down = !(repldlg->get_direction());
casesensitive = repldlg->case_sensitive();
// if (casesensitive) find_text = "\\C" + find_text;
// else find_text = "\\c" + find_text;
if (casesensitive) flags|=FRD_MATCH_CASE;
QCString unistring = vmw->codec->fromUnicode(find_text);
QCString unistring2 = vmw->codec->fromUnicode(repl_text);
gui_do_findrepl(flags, (char_u *)(const char *)unistring,(char_u *)(const char*)unistring2,(int)direction_down);
}//}}}
void VimMainWindow::showAboutKDE()
{
KAboutKDE *kde = new KAboutKDE(this);
kde->show();
}
void VimMainWindow::showAboutApplication()//{{{
{
KAboutData *aboutData = new KAboutData (
"kvim"
, I18N_NOOP("KVim")
, VIM_VERSION_SHORT
, I18N_NOOP("Vim in a KDE interface")
, 0
, "(c) Vim Team, \":help credits\" for more infos.\nType \":help iccf\" to see how you can help the children in Uganda"
, 0l
, "http://freehackers.org/kvim"
, "kvim-dev@freenux.org"
);
aboutData->addAuthor("Bram Moolenaar",
I18N_NOOP("Main vim author"),
"Bram@vim.org",
"http://www.vim.org/");
aboutData->addAuthor("Thomas Capricelli",
I18N_NOOP("KDE porting"),
"orzel@freehackers.org",
"http://orzel.freehackers.org");
aboutData->addAuthor("Philippe Fremy",
I18N_NOOP("KDE porting"),
"pfremy@chez.com",
"http://www.freehackers.org/kvim");
aboutData->addAuthor("Mark Westcott",
I18N_NOOP("Qtopia porting, maintainer of the Qtopia part"),
"mark@houseoffish.org",
"http://houseoffish.org");
aboutData->addAuthor("Mickael Marchand",
I18N_NOOP("KDE porting, maintainer"),
"marchand@kde.org",
"http://freenux.org");
aboutData->addAuthor("Many other people",
I18N_NOOP("type :help credits for more infos")
);
aboutData->addCredit("Vince Negri",
I18N_NOOP("Antialiasing support, Color fixes"),
"vnegri@asl-electronics.co.uk");
aboutData->addCredit("Malte Starostik",
I18N_NOOP("Patch for performance improvement"),
"malte@kde.org");
aboutData->addCredit("Mark Stosberg",
I18N_NOOP("Provided a FreeBSD box to debug KVim on BSD"),
"mark@summersault.com"
);
aboutData->addCredit("Henrik Skott",
I18N_NOOP("Font patch when KDE not configured"),
"henrik.skott@hem.utfors.se"
);
aboutData->addCredit("Kailash Sethuraman",
I18N_NOOP("NetBSD configure/compilation fixes")
);
aboutData->setLicenseText(
"KVim as an extension of Vim follows Vim license : \n\
Vim is Charityware. You can use and copy it as much as you like, but you are\n\
encouraged to make a donation to orphans in Uganda. Please read the file\n\
runtime/doc/uganda.txt for details.\n\
\n\
There are no restrictions on distributing an unmodified copy of Vim. Parts of\n\
Vim may also be distributed, but this text must always be included. You are\n\
allowed to include executables that you made from the unmodified Vim sources,\n\
your own usage examples and Vim scripts.\n\
\n\
If you distribute a modified version of Vim, you are encouraged to send the\n\
maintainer a copy, including the source code. Or make it available to the\n\
maintainer through ftp; let him know where it can be found. If the number of\n\
changes is small (e.g., a modified Makefile) e-mailing the diffs will do.\n\
When the maintainer asks for it (in any way) you must make your changes,\n\
including source code, available to him.\n\
\n\
The maintainer reserves the right to include any changes in the official\n\
version of Vim. This is negotiable. You are not allowed to distribute a\n\
modified version of Vim when you are not willing to make the source code\n\
available to the maintainer.\n\
\n\
The current maintainer is Bram Moolenaar <Bram@vim.org>. If this changes, it\n\
will be announced in appropriate places (most likely www.vim.org and\n\
comp.editors). When it is completely impossible to contact the maintainer,\n\
the obligation to send him modified source code ceases.\n\
\n\
It is not allowed to remove these restrictions from the distribution of the\n\
Vim sources or parts of it. These restrictions may also be used for previous\n\
Vim releases instead of the text that was included with it.");
KAboutApplication *about = new KAboutApplication(aboutData);
about->show();
}//}}}
void VimMainWindow::showTipOfTheDay() {
#if QT_VERSION>=300
KTipDialog::showTip (vmw,QString::null,true);
#endif
}
void VimMainWindow::buffersToolbar() {
}
void VimMainWindow::showBugReport() {
KBugReport *bug= new KBugReport(this,true);
bug->show();
}
/*
* Vim Dialog
*
* Returns:
* 0: Cancel
* 1- : nb of the pressed button
*/
VimDialog::VimDialog (int type, /* type of dialog *///{{{
char_u * title, /* title of dialog */
char_u * message, /* message text */
char_u * buttons, /* names of buttons */
int def_but, /* default button */
char_u *textfield ) /* input field */
:QDialog(vmw, "vim generic dialog", true), // true is for "modal"
mapper(this, "dialog signal mapper")
{
/*
* Create Icon
*/
char ** icon_data;
switch (type) {
case VIM_GENERIC:
icon_data = generic_xpm;
break;
case VIM_ERROR:
icon_data = error_xpm;
break;
case VIM_WARNING:
icon_data = alert_xpm;
break;
case VIM_INFO:
icon_data = info_xpm;
break;
case VIM_QUESTION:
icon_data = quest_xpm;
break;
default:
icon_data = generic_xpm;
};
QLabel * icon = new QLabel( this );
icon->setPixmap( QPixmap( (const char **) icon_data ) );
icon->setFixedSize( icon->sizeHint() );
QLabel * text = new QLabel( (const char *)message, this );
text->setAlignment( AlignHCenter | AlignVCenter | ExpandTabs );
QStringList buttonText = QStringList::split( DLG_BUTTON_SEP, (char *) buttons);
int butNb = buttonText.count();
/*
* Layout
*/
QVBoxLayout * vly = new QVBoxLayout( this, 5, 5 );
QHBoxLayout * hly1 = new QHBoxLayout( vly, 5);
hly1->addWidget( icon );
hly1->addWidget( text );
QHBoxLayout * hly3 = new QHBoxLayout ( vly , 5);
if (textfield!=NULL) {
entry = new QLineEdit((const char *)textfield,this);
entry->setText((const char *)textfield);
hly3->addWidget( entry );
ret=textfield;
} else entry=NULL;
QHBoxLayout * hly2 = new QHBoxLayout( vly, 15);
QString s;
QPushButton * pushButton = 0L;
for( int i=0; i<butNb; i++) {
s = buttonText[i];
pushButton = new QPushButton(s, this );
if (s.find('&') != -1) {
pushButton->setAccel( s.at(s.find('&')+1).latin1() );
}
hly2->addWidget( pushButton );
if (i == def_but-1) {
pushButton->setDefault( true );
pushButton->setAutoDefault( true );
setResult( i+1 );
}
connect(pushButton, SIGNAL(clicked()), &mapper, SLOT(map()));
mapper.setMapping(pushButton, i+1);
}
connect( &mapper, SIGNAL(mapped(int)), this, SLOT(done(int)));
setCaption((const char *) title);
vly->activate();
}//}}}
void VimDialog::done(int r) {
if (entry!=NULL) {
if (r) {
QCString unistring=vmw->codec->fromUnicode(entry->text());
STRCPY(ret,(const char*)unistring);
} else
*ret=NUL;
}
QDialog::done(r);
}
/*
* ScrollBar pool handling
*/
SBPool::SBPool(void)//{{{
:mapper(this, "SBPool signal mapper")
{
connect(&mapper, SIGNAL(mapped(int)), this, SLOT(sbUsed(int)));
}//}}}
void SBPool::create(GuiScrollbar * sb, int orient)//{{{
{
switch(orient) {
case SBAR_HORIZ:
sb->w = new QScrollBar(QScrollBar::Horizontal, vmw);
break;
case SBAR_VERT:
sb->w = new QScrollBar(QScrollBar::Vertical, vmw);
break;
default:
sb->w = 0;
return;
}
connect(sb->w, SIGNAL(valueChanged(int)), &mapper, SLOT(map()));
mapper.setMapping(sb->w, (int)sb);
}//}}}
void SBPool::sbUsed(int who)//{{{
{
GuiScrollbar *sb = (GuiScrollbar*)who;
gui_drag_scrollbar( sb, sb->w->value(), FALSE);
}//}}}
void SBPool::destroy(GuiScrollbar * sb)//{{{
{
if (!sb->w) return;
delete sb->w;
sb->w = 0;
}//}}}
#ifdef FEAT_CLIENTSERVER
static int kvim_x11_event_filter( XEvent* e)//{{{
{
if (e->xproperty.type == PropertyNotify
&& e->xproperty.atom == commProperty
&& e->xproperty.window == commWindow
&& e->xproperty.state == PropertyNewValue ) {
serverEventProc(qt_xdisplay(), e);
}
if (oldFilter) return oldFilter( e );
return FALSE;
}//}}}
#endif
//add some QT 3 fonts usefull functions
#if QT_VERSION<300
QString KVimUtils::toString(QFont *f)
{
QStringList l;
l.append(f->family());
l.append(QString::number(f->pointSize()));
l.append(QString::number(f->pixelSize()));
l.append(QString::number((int)f->styleHint()));
l.append(QString::number(f->weight()));
l.append(QString::number((int)f->italic()));
l.append(QString::number((int)f->underline()));
l.append(QString::number((int)f->strikeOut()));
l.append(QString::number((int)f->fixedPitch()));
l.append(QString::number((int)f->rawMode()));
return l.join(",");
}
bool KVimUtils::fromString(QFont *f, QString descrip)
{
QStringList l(QStringList::split(',', descrip));
int count = l.count();
if (count != 10 && count != 9) {
return FALSE;
}
f->setFamily(l[0]);
f->setPointSize(l[1].toInt());
if ( count == 9 ) {
f->setStyleHint((QFont::StyleHint) l[2].toInt());
f->setWeight(l[3].toInt());
f->setItalic(l[4].toInt());
f->setUnderline(l[5].toInt());
f->setStrikeOut(l[6].toInt());
f->setFixedPitch(l[7].toInt());
f->setRawMode(l[8].toInt());
} else {
f->setPixelSize(l[2].toInt());
f->setStyleHint((QFont::StyleHint) l[3].toInt());
f->setWeight(l[4].toInt());
f->setItalic(l[5].toInt());
f->setUnderline(l[6].toInt());
f->setStrikeOut(l[7].toInt());
f->setFixedPitch(l[8].toInt());
f->setRawMode(l[9].toInt());
}
return TRUE;
}
#endif
QString KVimUtils::convertEncodingName(QString name)
{
if (name.startsWith("ucs") || name.startsWith("utf-16")) return QString("utf16");
if (name=="cp950") return QString("Big5");
return QString();
}