forked from speedie/spmenu
b59f851fdc
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.
484 lines
7.2 KiB
C
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();
|
|
}
|