spmenu/libs/arg.c
speedie b59f851fdc Rewrite the way moving around works
It seemed like a good thing at first to combine all the movement stuff
in a single function, however as soon as you want to move multiple lines
at once it becomes very difficult to do that, especially if you aren't
familiar with C.

This commit splits it into 4 separate functions, where the argument
(arg->i) is how many times we move. This means it is now possible to
choose how many lines to move without even editing the function itself,
just through keybinds. This also makes the "fastmove" keybindings
redundant.

Note that calcoffsets() and drawmenu() after that must be called AFTER the for loop added with this commit, otherwise
you can see the selection moving from each line to the next which is
rather ugly.
2023-03-20 18:05:53 +01:00

484 lines
7.2 KiB
C

void
moveleft(const Arg *arg)
{
struct item *tmpsel;
int i, offscreen = 0;
int argu = arg->i ? arg->i : 1;
if (columns > 1) {
if (!sel)
return;
tmpsel = sel;
for (i = 0; i < lines; i++) {
if (!tmpsel->left || tmpsel->left->right != tmpsel) {
if (offscreen)
drawmenu();
return;
}
if (tmpsel == curr)
offscreen = 1;
tmpsel = tmpsel->left;
}
sel = tmpsel;
if (offscreen) {
for (int j = 0; j < argu; j++) {
curr = prev;
}
}
drawmenu();
calcoffsets();
}
if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
cursor = nextrune(-1);
drawmenu();
}
}
void
moveright(const Arg *arg)
{
struct item *tmpsel;
int i, offscreen = 0;
int argu = arg->i ? arg->i : 1;
if (columns > 1) {
if (!sel)
return;
tmpsel = sel;
for (i = 0; i < lines; i++) {
if (!tmpsel->right || tmpsel->right->left != tmpsel) {
if (offscreen)
drawmenu();
return;
}
tmpsel = tmpsel->right;
if (tmpsel == next)
offscreen = 1;
}
sel = tmpsel;
if (offscreen) {
for (int j = 0; j < argu; j++)
curr = next;
}
calcoffsets();
}
drawmenu();
if (text[cursor] != '\0') {
cursor = nextrune(+1);
drawmenu();
}
}
void
movedown(const Arg *arg)
{
struct item *tmpsel;
int i, offscreen = 0;
int argu = arg->i ? arg->i : 1;
for (int j = 0; j < argu; j++) {
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
}
}
calcoffsets();
drawmenu();
}
void
moveup(const Arg *arg)
{
struct item *tmpsel;
int i, offscreen = 0;
int argu = arg->i ? arg->i : 1;
for (int j = 0; j < argu; j++) {
if (sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev;
}
}
calcoffsets();
drawmenu();
}
void
complete(const Arg *arg)
{
if (!sel) return;
strncpy(text, sel->text, sizeof text - 1);
text[sizeof text - 1] = '\0';
cursor = strlen(text);
match();
drawmenu();
}
void
movenext(const Arg *arg)
{
if (!next)
return;
sel = curr = next;
calcoffsets();
drawmenu();
}
void
moveprev(const Arg *arg)
{
if (!prev)
return;
sel = curr = prev;
calcoffsets();
drawmenu();
}
void
movestart(const Arg *arg)
{
if (sel == matches) {
cursor = 0;
drawmenu();
return;
}
sel = curr = matches;
calcoffsets();
drawmenu();
}
void
moveend(const Arg *arg)
{
if (text[cursor] != '\0') {
cursor = strlen(text);
drawmenu();
return;
}
if (next) {
curr = matchend;
calcoffsets();
curr = prev;
calcoffsets();
while (next && (curr = curr->right))
calcoffsets();
}
sel = matchend;
drawmenu();
}
void
paste(const Arg *arg)
{
int clipboard;
if (arg->i == 1) {
clipboard = XA_PRIMARY;
} else {
clipboard = clip;
}
XConvertSelection(dpy, clipboard, utf8, utf8, win, CurrentTime);
return;
}
void
viewhist(const Arg *arg)
{
int i;
if (histfile) {
if (!backup_items) {
backup_items = items;
items = calloc(histsz + 1, sizeof(struct item));
if (!items) {
die("spmenu: cannot allocate memory");
}
for (i = 0; i < histsz; i++) {
items[i].text = history[i];
}
} else {
free(items);
items = backup_items;
backup_items = NULL;
}
}
match();
drawmenu();
}
void
deleteword(const Arg *arg)
{
if (cursor == 0)
return;
while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
insert(NULL, nextrune(-1) - cursor);
while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
insert(NULL, nextrune(-1) - cursor);
drawmenu();
}
void
moveword(const Arg *arg)
{
if (arg->i < 0) { // move cursor to the start of the word
while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
cursor = nextrune(-1);
} else { // move cursor to the end of the word
while (text[cursor] && strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
while (text[cursor] && !strchr(worddelimiters, text[cursor]))
cursor = nextrune(+1);
}
drawmenu();
}
void
movecursor(const Arg *arg)
{
cursor = nextrune(arg->i);
drawmenu();
}
void
backspace(const Arg *arg)
{
if (cursor == 0)
return;
insert(NULL, nextrune(-1) - cursor);
drawmenu();
}
void
selectitem(const Arg *arg)
{
char *selection;
if (sel) {
selection = sel->text;
} else {
selection = text;
}
if (!selection)
return;
puts(selection);
savehistory(selection);
cleanup();
exit(0);
}
void
navhistory(const Arg *arg)
{
navigatehistfile(arg->i);
drawmenu();
}
void
restoresel(const Arg *arg)
{
text[cursor] = '\0';
match();
drawmenu();
}
void
clear(const Arg *arg)
{
insert(NULL, 0 - cursor);
drawmenu();
}
void
quit(const Arg *arg)
{
cleanup();
exit(1);
}
void
savehistory(char *input)
{
unsigned int i;
FILE *fp;
if (!histfile ||
0 == maxhist ||
0 == strlen(input)) {
goto out;
}
fp = fopen(histfile, "w");
if (!fp) {
die("failed to open %s", histfile);
}
for (i = histsz < maxhist ? 0 : histsz - maxhist; i < histsz; i++) {
if (0 >= fprintf(fp, "%s\n", history[i])) {
die("failed to write to %s", histfile);
}
}
if (histsz == 0 || !histnodup || (histsz > 0 && strcmp(input, history[histsz-1]) != 0)) {
if (0 >= fputs(input, fp)) {
die("failed to write to %s", histfile);
}
}
if (fclose(fp)) {
die("failed to close file %s", histfile);
}
out:
for (i = 0; i < histsz; i++) {
free(history[i]);
}
free(history);
}
void
setimgsize(const Arg *arg)
{
#if USEIMAGE
setimagesize(imagewidth + arg->i, imageheight + arg->i);
#endif
}
void
flipimg(const Arg *arg)
{
#if USEIMAGE
if (!image) return;
flip = flip ? 0 : arg->i ? 1 : 2;
drawmenu();
#endif
}
void
setimgpos(const Arg *arg)
{
#if USEIMAGE
if (!image || hideimage) return;
if (imageposition < 3) {
imageposition += arg->i;
} else {
imageposition = 0;
}
drawmenu();
#endif
}
void
setimggaps(const Arg *arg)
{
#if USEIMAGE
imagegaps += arg->i;
if (!image || hideimage) return;
if (imagegaps < 0)
imagegaps = 0;
// limitation to make sure we have a reasonable gap size
if (imagegaps > imagewidth / 2)
imagegaps -= arg->i;
drawmenu();
#endif
}
void
rotateimg(const Arg *arg)
{
#if USEIMAGE
if (!image || hideimage) return;
rotation += arg->i ? arg->i : 1;
drawmenu();
#endif
}
void
toggleimg(const Arg *arg)
{
#if USEIMAGE
hideimage = !hideimage;
drawmenu();
#endif
}
void
defaultimg(const Arg *arg)
{
#if USEIMAGE
if (hideimage || !image) return;
if (imagew) {
imagewidth = imagew;
imageheight = imageh;
imagegaps = imageg;
}
drawmenu();
#endif
}
void
setlines(const Arg *arg)
{
lines += arg->i;
if (lines < 0) lines = 0;
match();
resizeclient();
drawmenu();
}
void
setcolumns(const Arg *arg)
{
columns += arg->i;
if (columns < 0) columns = 0;
match();
resizeclient();
drawmenu();
}