<commit_before>/*
 * (C)opyright MMIV-MMVI <NAME> <<EMAIL>beam at gmail dot com>
 * (C)opyright MMVI      <NAME> <a dot h dot vandijk at gmail dot com>
 * See LICENSE file for license details.
 */

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#include "blitz.h"

static Bool done = False;
static int ret = 0;
static char text[4096];
static Color selcolor;
static Color normcolor;

static Display *dpy;
static Window win;
static XRectangle mrect;
static int screen;
static char **allitem = nil;
static size_t nallitem = 0;
static size_t allitemsz = 0;
static char **item = nil;
static size_t itemsz = 0;
static size_t nitem = 0;
static int sel = -1;
static size_t nextoff = 0;
static size_t prevoff = 0;
static size_t curroff = 0;
static unsigned int cmdw = 0;
static Draw draw = { 0 };
static const int seek = 30;     /* 30px */

static void check_event(void);
static void draw_menu(void);
static void handle_kpress(XKeyEvent * e);

static char version[] = "wmiimenu - " VERSION ", (C)opyright MMIV-MMVI <NAME>\n";

static void
usage()
{
    fprintf(stderr, "%s", "usage: wmiimenu [-v]\n");
    exit(1);
}

static void
update_offsets()
{
	size_t i;
    unsigned int w = cmdw + 2 * seek;

    if(!nitem)
        return;

    for(i = curroff; i < nitem; i++) {
        w += XTextWidth(draw.font, item[i], strlen(item[i])) + mrect.height;
        if(w > mrect.width)
            break;
    }
	nextoff = i;

    w = cmdw + 2 * seek;
    for(i = curroff; i > 0; i--) {
        w += XTextWidth(draw.font, item[i], strlen(item[i])) + mrect.height;
        if(w > mrect.width)
            break;
    }
	prevoff = i;
}

static size_t 
update_items(char *pattern)
{
    size_t plen = strlen(pattern);
    int i;

	curroff = prevoff = nextoff = 0;
	sel = -1;

	for(i = 0; i < nitem; i++)
		item[i] = nil; /* faster than cext_array_detach */
	nitem = 0;

    for(i = 0; i < nallitem; i++) {
        if(!plen || !strncmp(pattern, allitem[i], plen))
		{
			item = (char **)cext_array_attach((void **)item, allitem[i],
											sizeof(char *), &itemsz);
            nitem++;
		}
    }
    for(i = 0; i < nallitem; i++) {
        if(plen && strncmp(pattern, allitem[i], plen)
			&& strstr(allitem[i], pattern))
		{
			item = (char **)cext_array_attach((void **)item, allitem[i],
											sizeof(char *), &itemsz);
            nitem++;
		}
    }
	if(nitem)
		sel = 0;
    
    update_offsets();
	return nitem;
}

/* creates draw structs for menu mode drawing */
static void
draw_menu()
{
    unsigned int i, offx = 0;

    draw.rect = mrect;
    draw.rect.x = 0;
    draw.rect.y = 0;
	draw.color = normcolor;
    blitz_drawlabelnoborder(dpy, &draw);

    /* print command */
    draw.align = WEST;
    draw.data = text;
    if(cmdw && nitem)
        draw.rect.width = cmdw;
    offx += draw.rect.width;
    blitz_drawlabelnoborder(dpy, &draw);

    draw.align = CENTER;
    if(nitem) {
        draw.color = normcolor;
        draw.data = prevoff < curroff ? "<" : nil;
        draw.rect.x = offx;
        draw.rect.width = seek;
        offx += draw.rect.width;
        blitz_drawlabelnoborder(dpy, &draw);

        /* determine maximum items */
        for(i = curroff; i < nextoff; i++) {
            draw.data = item[i];
            draw.rect.x = offx;
            draw.rect.width =
                XTextWidth(draw.font, draw.data, strlen(draw.data)) + mrect.height;
            offx += draw.rect.width;
            if(sel == i) {
				draw.color = selcolor;
                blitz_drawlabel(dpy, &draw);
            } else {
				draw.color = normcolor;
                blitz_drawlabelnoborder(dpy, &draw);
            }
        }

        draw.color = normcolor;
        draw.data = nitem > nextoff ? ">" : nil;
        draw.rect.x = mrect.width - seek;
        draw.rect.width = seek;
        blitz_drawlabelnoborder(dpy, &draw);
    }
    XCopyArea(dpy, draw.drawable, win, draw.gc, 0, 0, mrect.width, mrect.height, 0, 0);
    XSync(dpy, False);
}

static void
handle_kpress(XKeyEvent * e)
{
    KeySym ksym;
    char buf[32];
    int num;
    size_t len = strlen(text);

    buf[0] = 0;
    num = XLookupString(e, buf, sizeof(buf), &ksym, 0);

    if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
       || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
       || IsPrivateKeypadKey(ksym))
        return;

    /* first check if a control mask is omitted */
    if(e->state & ShiftMask) {
        if(ksym == XK_ISO_Left_Tab)
            ksym = XK_Left;
    } else if(e->state & ControlMask) {
        switch (ksym) {
        case XK_H:
        case XK_h:
            ksym = XK_BackSpace;
            break;
        case XK_I:
        case XK_i:
            ksym = XK_Tab;
            break;
        case XK_J:
        case XK_j:
            ksym = XK_Return;
            break;
        case XK_U:
        case XK_u:
            text[0] = 0;
            update_items(text);
            draw_menu();
            return;<commit_msg>add C-p and C-n as alternatives for Left and Right in wmiimenu (makes the menu completely C-* controllable).<commit_after>/*
 * (C)opyright MMIV-MMVI <NAME> <<EMAIL>beam at g<EMAIL> dot com>
 * (C)opyright MMVI      <NAME> <a dot h dot vandijk at gmail dot com>
 * See LICENSE file for license details.
 */

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#include "blitz.h"

static Bool done = False;
static int ret = 0;
static char text[4096];
static Color selcolor;
static Color normcolor;

static Display *dpy;
static Window win;
static XRectangle mrect;
static int screen;
static char **allitem = nil;
static size_t nallitem = 0;
static size_t allitemsz = 0;
static char **item = nil;
static size_t itemsz = 0;
static size_t nitem = 0;
static int sel = -1;
static size_t nextoff = 0;
static size_t prevoff = 0;
static size_t curroff = 0;
static unsigned int cmdw = 0;
static Draw draw = { 0 };
static const int seek = 30;     /* 30px */

static void check_event(void);
static void draw_menu(void);
static void handle_kpress(XKeyEvent * e);

static char version[] = "wmiimenu - " VERSION ", (C)opyright MMIV-MMVI <NAME>\n";

static void
usage()
{
    fprintf(stderr, "%s", "usage: wmiimenu [-v]\n");
    exit(1);
}

static void
update_offsets()
{
	size_t i;
    unsigned int w = cmdw + 2 * seek;

    if(!nitem)
        return;

    for(i = curroff; i < nitem; i++) {
        w += XTextWidth(draw.font, item[i], strlen(item[i])) + mrect.height;
        if(w > mrect.width)
            break;
    }
	nextoff = i;

    w = cmdw + 2 * seek;
    for(i = curroff; i > 0; i--) {
        w += XTextWidth(draw.font, item[i], strlen(item[i])) + mrect.height;
        if(w > mrect.width)
            break;
    }
	prevoff = i;
}

static size_t 
update_items(char *pattern)
{
    size_t plen = strlen(pattern);
    int i;

	curroff = prevoff = nextoff = 0;
	sel = -1;

	for(i = 0; i < nitem; i++)
		item[i] = nil; /* faster than cext_array_detach */
	nitem = 0;

    for(i = 0; i < nallitem; i++) {
        if(!plen || !strncmp(pattern, allitem[i], plen))
		{
			item = (char **)cext_array_attach((void **)item, allitem[i],
											sizeof(char *), &itemsz);
            nitem++;
		}
    }
    for(i = 0; i < nallitem; i++) {
        if(plen && strncmp(pattern, allitem[i], plen)
			&& strstr(allitem[i], pattern))
		{
			item = (char **)cext_array_attach((void **)item, allitem[i],
											sizeof(char *), &itemsz);
            nitem++;
		}
    }
	if(nitem)
		sel = 0;
    
    update_offsets();
	return nitem;
}

/* creates draw structs for menu mode drawing */
static void
draw_menu()
{
    unsigned int i, offx = 0;

    draw.rect = mrect;
    draw.rect.x = 0;
    draw.rect.y = 0;
	draw.color = normcolor;
    blitz_drawlabelnoborder(dpy, &draw);

    /* print command */
    draw.align = WEST;
    draw.data = text;
    if(cmdw && nitem)
        draw.rect.width = cmdw;
    offx += draw.rect.width;
    blitz_drawlabelnoborder(dpy, &draw);

    draw.align = CENTER;
    if(nitem) {
        draw.color = normcolor;
        draw.data = prevoff < curroff ? "<" : nil;
        draw.rect.x = offx;
        draw.rect.width = seek;
        offx += draw.rect.width;
        blitz_drawlabelnoborder(dpy, &draw);

        /* determine maximum items */
        for(i = curroff; i < nextoff; i++) {
            draw.data = item[i];
            draw.rect.x = offx;
            draw.rect.width =
                XTextWidth(draw.font, draw.data, strlen(draw.data)) + mrect.height;
            offx += draw.rect.width;
            if(sel == i) {
				draw.color = selcolor;
                blitz_drawlabel(dpy, &draw);
            } else {
				draw.color = normcolor;
                blitz_drawlabelnoborder(dpy, &draw);
            }
        }

        draw.color = normcolor;
        draw.data = nitem > nextoff ? ">" : nil;
        draw.rect.x = mrect.width - seek;
        draw.rect.width = seek;
        blitz_drawlabelnoborder(dpy, &draw);
    }
    XCopyArea(dpy, draw.drawable, win, draw.gc, 0, 0, mrect.width, mrect.height, 0, 0);
    XSync(dpy, False);
}

static void
handle_kpress(XKeyEvent * e)
{
    KeySym ksym;
    char buf[32];
    int num;
    size_t len = strlen(text);

    buf[0] = 0;
    num = XLookupString(e, buf, sizeof(buf), &ksym, 0);

    if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
       || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
       || IsPrivateKeypadKey(ksym))
        return;

    /* first check if a control mask is omitted */
    if(e->state & ControlMask) {
        switch (ksym) {
        case XK_H:
        case XK_h:
            ksym = XK_BackSpace;
            break;
        case XK_I:
        case XK_i:
            ksym = XK_Tab;
            break;
        case XK_J:
        case XK_j:
            ksym = XK_Return;
            break;
        case XK_N:
        case XK_n:
            ksym = XK_Right;
            break;
        case XK_P:
        case XK_p:
            ksym = XK_Left;
            break;
        case XK_U:
        case XK_u:
            text[0] = 0;
            update_items(text);
            draw_menu();
            return;
{'fraction_non_alphanumeric': 0.13924901565351003, 'fraction_numerical': 0.009987515605493134, 'mean_word_length': 2.1895865237366, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6366067', 'n_tokens_mistral': 4020, 'n_tokens_neox': 3810, 'n_words': 957}