static int gtextfield_mouse(GGadget *g, GEvent *event) {
    GTextField *gt = (GTextField *) g;
    GListField *ge = (GListField *) g;
    unichar_t *end=NULL, *end1, *end2;
    int i=0,ll,curlen;

    if ( gt->hidden_cursor ) {
	GDrawSetCursor(gt->g.base,gt->old_cursor);
	gt->hidden_cursor = false;
	_GWidget_ClearGrabGadget(g);
    }
    if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
return( false );
    if ( event->type == et_crossing )
return( false );
    if ( gt->completionfield && ((GCompletionField *) gt)->choice_popup!=NULL &&
	    event->type==et_mousedown )
	GCompletionDestroy((GCompletionField *) gt);
    if (( gt->listfield && event->u.mouse.x>=ge->buttonrect.x &&
	    event->u.mouse.x<ge->buttonrect.x+ge->buttonrect.width &&
	    event->u.mouse.y>=ge->buttonrect.y &&
	    event->u.mouse.y<ge->buttonrect.y+ge->buttonrect.height ) ||
	( gt->listfield && ge->popup!=NULL ))
return( glistfield_mouse(ge,event));
    if ( gt->numericfield && event->u.mouse.x>=ge->buttonrect.x &&
	    event->u.mouse.x<ge->buttonrect.x+ge->buttonrect.width &&
	    event->u.mouse.y>=ge->buttonrect.y &&
	    event->u.mouse.y<ge->buttonrect.y+ge->buttonrect.height )
return( gnumericfield_mouse(gt,event));
    if (( event->type==et_mouseup || event->type==et_mousedown ) &&
	    (event->u.mouse.button>=4 && event->u.mouse.button<=7)) {
	int isv = event->u.mouse.button<=5;
	if ( event->u.mouse.state&ksm_shift ) isv = !isv;
	if ( isv && gt->vsb!=NULL )
return( GGadgetDispatchEvent(&gt->vsb->g,event));
	else if ( !isv && gt->hsb!=NULL )
return( GGadgetDispatchEvent(&gt->hsb->g,event));
	else
return( true );
    }

    if ( gt->pressed==NULL && event->type == et_mousemove && g->popup_msg!=NULL &&
	    GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y))
	GGadgetPreparePopup(g->base,g->popup_msg);

    curlen = u_strlen(gt->text);

    if ( event->type == et_mousedown || gt->pressed ) {
	i = (event->u.mouse.y-g->inner.y)/gt->fh + gt->loff_top;
	if ( i<0 ) i = 0;
	if ( !gt->multi_line ) i = 0;
	if ( i>=gt->lcnt ) {
	    end = gt->text+curlen;
	    i = gt->lcnt - 1;
	    if (i < 0) i = 0; // Can lcnt ever be 0?
	} else
	    end = GTextFieldGetPtFromPos(gt,i,event->u.mouse.x);
    }

    if ( event->type == et_mousedown ) {
	int end8;
	if ( event->u.mouse.button==3 &&
		GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
	    GTFPopupMenu(gt,event);
return( true );
	}

	ll = gt->lines8[i+1]==-1?-1:gt->lines8[i+1]-gt->lines8[i]-1;
	GDrawLayoutInit(gt->g.base,gt->utf8_text+gt->lines8[i],ll,NULL);
	end8 = GDrawLayoutXYToIndex(gt->g.base,event->u.mouse.x-g->inner.x+gt->xoff_left,0);
	end1 = end2 = gt->text + gt->lines[i] + utf82u_index(end8,gt->utf8_text+gt->lines8[i]);

	gt->wordsel = gt->linesel = false;
	if ( event->u.mouse.button==1 && event->u.mouse.clicks>=3 ) {
	    gt->sel_start = gt->lines[i]; gt->sel_end = gt->lines[i+1];
	    if ( gt->sel_end==-1 ) gt->sel_end = curlen;
	    gt->wordsel = false; gt->linesel = true;
	} else if ( event->u.mouse.button==1 && event->u.mouse.clicks==2 ) {
	    gt->sel_start = gt->sel_end = gt->sel_base = end-gt->text;
	    gt->wordsel = true;
	    GTextFieldSelectWords(gt,gt->sel_base);
	} else if ( end1-gt->text>=gt->sel_start && end2-gt->text<gt->sel_end &&
		gt->sel_start!=gt->sel_end &&
		event->u.mouse.button==1 ) {
	    gt->drag_and_drop = true;
	    if ( !gt->hidden_cursor )
		gt->old_cursor = GDrawGetCursor(gt->g.base);
	    GDrawSetCursor(gt->g.base,ct_draganddrop);
	} else if ( /*event->u.mouse.button!=3 &&*/ !(event->u.mouse.state&ksm_shift) ) {
	    if ( event->u.mouse.button==1 )
		GTextFieldGrabPrimarySelection(gt);
	    gt->sel_start = gt->sel_end = gt->sel_base = end-gt->text;
	} else if ( end-gt->text>gt->sel_base ) {
	    gt->sel_start = gt->sel_base;
	    gt->sel_end = end-gt->text;
	} else {
	    gt->sel_start = gt->sel_base = gt->sel_end = end-gt->text;
	}

	if ( gt->pressed==NULL )
	    gt->pressed = GDrawRequestTimer(gt->g.base,200,100,NULL);
	if ( gt->sel_start > curlen )	/* Ok to have selection at end, but beyond is an error */
	    fprintf( stderr, "About to crash\n" );
	_ggadget_redraw(g);
return( true );
    } else if ( gt->pressed && (event->type == et_mousemove || event->type == et_mouseup )) {
	int refresh = true;

	if ( gt->drag_and_drop ) {
	    refresh = GTextFieldDoDrop(gt,event,end-gt->text);
	    // curlen may be inaccurate now, but we recalculate after this guard set.
	} else if ( gt->linesel ) {
	    int j;
	    for ( j=i; j>0 && gt->text[gt->lines[j]-1] != '\n'; --j )
		;
	    gt->sel_start = gt->lines[j];
	    for ( j=i+1; gt->lines[j]!=-1 && gt->text[gt->lines[j]-1] != '\n'; ++j )
		;
	    gt->sel_end = gt->lines[j]!=-1 ? gt->lines[j]-1 : curlen;
	} else if ( gt->wordsel )
	    GTextFieldSelectWords(gt,end-gt->text);
	else if ( event->u.mouse.button!=2 ) {
	    int e = end-gt->text;
	    if ( e>gt->sel_base ) {
		gt->sel_start = gt->sel_base; gt->sel_end = e;
	    } else {
		gt->sel_start = e; gt->sel_end = gt->sel_base;
	    }
	}
	if ( event->type==et_mouseup ) {
	    GDrawCancelTimer(gt->pressed); gt->pressed = NULL;
	    if ( event->u.mouse.button==2 )
		GTextFieldPaste(gt,sn_primary);
	    if ( gt->sel_start==gt->sel_end )
		GTextField_Show(gt,gt->sel_start);
	    GTextFieldChanged(gt,-1);
	    if ( gt->sel_start<gt->sel_end && _GDraw_InsCharHook!=NULL && !gt->donthook )
		(_GDraw_InsCharHook)(GDrawGetDisplayOfWindow(gt->g.base),
			gt->text[gt->sel_start]);
	}
	if ( gt->sel_end > u_strlen(gt->text) )
	    fprintf( stderr, "About to crash\n" );
	if ( refresh )
	    _ggadget_redraw(g);
return( true );
    }
return( false );
}