/* If scroll_only is FALSE, move up one line. If scroll_only is TRUE, * scroll up one line without scrolling the cursor. */ void do_up(bool scroll_only) { size_t was_column = xplustabs(); /* If we're at the top of the file, or if scroll_only is TRUE and * the top of the file is onscreen, get out. */ if (openfile->current == openfile->fileage || (scroll_only && openfile->edittop == openfile->fileage)) return; assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno); /* Move the current line of the edit window up. */ openfile->current = openfile->current->prev; openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); /* When the cursor was on the first line of the edit window (or when just * scrolling without moving the cursor), scroll the edit window up -- one * line if we're in smooth scrolling mode, and half a page otherwise. */ if (openfile->current->next == openfile->edittop || scroll_only) edit_scroll(UPWARD, (ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 : editwinrows / 2 + 1); /* If the lines weren't already redrawn, see if they need to be. */ if (openfile->current_y > 0) { /* Redraw the prior line if it was horizontally scrolled. */ if (need_horizontal_scroll(was_column, 0)) update_line(openfile->current->next, 0); /* Redraw the current line if it needs to be horizontally scrolled. */ if (need_horizontal_scroll(0, xplustabs())) update_line(openfile->current, openfile->current_x); } }
/* Move down one page. */ void do_page_down(void) { int i; /* If there's less than a page of text left on the screen, put the * cursor at the beginning of the last line of the file, and then * update the edit window. */ if (openfile->current->lineno + editwinrows - 2 >= openfile->filebot->lineno) { do_last_line(); return; } /* If we're not in smooth scrolling mode, put the cursor at the * beginning of the top line of the edit window, as Pico does. */ #ifndef NANO_TINY if (!ISSET(SMOOTH_SCROLL)) { #endif openfile->current = openfile->edittop; openfile->placewewant = 0; #ifndef NANO_TINY } #endif for (i = editwinrows - 2; i > 0 && openfile->current != openfile->filebot; i--) openfile->current = openfile->current->next; openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); /* Scroll the edit window down a page. */ edit_scroll(DOWN_DIR, editwinrows - 2); }
/* If scroll_only is FALSE, move down one line. If scroll_only is TRUE, * scroll down one line without scrolling the cursor. */ void do_down( #ifndef NANO_TINY bool scroll_only #else void #endif ) { bool onlastline = FALSE; /* If we're at the bottom of the file, get out. */ if (openfile->current == openfile->filebot) return; assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno); /* Move the current line of the edit window down. */ openfile->current = openfile->current->next; openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); if (ISSET(SOFTWRAP)) { if (openfile->current->lineno - openfile->edittop->lineno >= maxrows) onlastline = TRUE; } /* If scroll_only is FALSE and if we're on the first line of the * edit window, scroll the edit window down one line if we're in * smooth scrolling mode, or down half a page if we're not. If * scroll_only is TRUE, scroll the edit window down one line * unconditionally. */ if (onlastline || openfile->current_y == editwinrows - 1 #ifndef NANO_TINY || scroll_only #endif ) { edit_scroll(DOWN_DIR, #ifndef NANO_TINY (ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 : #endif editwinrows / 2 + 1); edit_refresh_needed = TRUE; } /* If we're above the last line of the edit window, update the line * we were on before and the line we're on now. The former needs to * be redrawn if we're not on the first page, and the latter needs * to be drawn unconditionally. */ if (ISSET(SOFTWRAP) || openfile->current_y < editwinrows - 1) { if (need_vertical_update(0)) update_line(openfile->current->prev, 0); update_line(openfile->current, openfile->current_x); } }
/* If scroll_only is FALSE, move up one line. If scroll_only is TRUE, * scroll up one line without scrolling the cursor. */ void do_up( #ifndef NANO_TINY bool scroll_only #else void #endif ) { /* If we're at the top of the file, or if scroll_only is TRUE and * the top of the file is onscreen, get out. */ if (openfile->current == openfile->fileage #ifndef NANO_TINY || (scroll_only && openfile->edittop == openfile->fileage) #endif ) return; assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno); /* Move the current line of the edit window up. */ openfile->current = openfile->current->prev; openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); /* If scroll_only is FALSE and if we're on the first line of the * edit window, scroll the edit window up one line if we're in * smooth scrolling mode, or up half a page if we're not. If * scroll_only is TRUE, scroll the edit window up one line * unconditionally. */ if (openfile->current_y == 0 || (ISSET(SOFTWRAP) && openfile->edittop->lineno == openfile->current->next->lineno) #ifndef NANO_TINY || scroll_only #endif ) edit_scroll(UP_DIR, #ifndef NANO_TINY (ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 : #endif editwinrows / 2 + 1); /* If we're below the first line of the edit window, update the * line we were on before and the line we're on now. The former * needs to be redrawn if we're not on the first page, and the * latter needs to be drawn unconditionally. */ if (openfile->current_y > 0) { if (need_vertical_update(0)) update_line(openfile->current->next, 0); update_line(openfile->current, openfile->current_x); } }
/* Move down one page. */ void do_page_down(void) { int i, mustmove; /* If the cursor is less than a page away from the bottom of the file, * put it at the end of the last line. */ if (openfile->current->lineno + maxrows - 2 >= openfile->filebot->lineno) { do_last_line(); return; } /* If we're not in smooth scrolling mode, put the cursor at the * beginning of the top line of the edit window, as Pico does. */ if (!ISSET(SMOOTH_SCROLL)) { openfile->current = openfile->edittop; openfile->placewewant = openfile->current_y = 0; } mustmove = (maxrows < 3) ? 1 : maxrows - 2; for (i = mustmove; i > 0 && openfile->current != openfile->filebot; i--) { openfile->current = openfile->current->next; #ifdef DEBUG fprintf(stderr, "paging down: moving to line %lu\n", (unsigned long)openfile->current->lineno); #endif } openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); /* Scroll the edit window down a page. */ adjust_viewport(STATIONARY); refresh_needed = TRUE; }
/* Handle a mouse click on the statusbar prompt or the shortcut list. */ int do_statusbar_mouse(void) { int mouse_x, mouse_y; int retval = get_mouseinput(&mouse_x, &mouse_y, TRUE); /* We can click on the statusbar window text to move the cursor. */ if (retval == 0 && wmouse_trafo(bottomwin, &mouse_y, &mouse_x, FALSE)) { size_t start_col; assert(prompt != NULL); start_col = strlenpt(prompt) + 2; /* Move to where the click occurred. */ if (mouse_x >= start_col && mouse_y == 0) { size_t pww_save = statusbar_pww; statusbar_x = actual_x(answer, get_statusbar_page_start(start_col, start_col + statusbar_xplustabs()) + mouse_x - start_col); statusbar_pww = statusbar_xplustabs(); if (need_statusbar_update(pww_save)) update_statusbar_line(answer, statusbar_x); } } return retval; }
/* Repaint the statusbar when getting a character in * get_prompt_string(). The statusbar text line will be displayed * starting with curranswer[index]. */ void update_statusbar_line(const char *curranswer, size_t index) { size_t start_col, page_start; char *expanded; assert(prompt != NULL && index <= strlen(curranswer)); start_col = strlenpt(prompt) + 2; index = strnlenpt(curranswer, index); page_start = get_statusbar_page_start(start_col, start_col + index); if (interface_color_pair[TITLE_BAR].bright) wattron(bottomwin, A_BOLD); wattron(bottomwin, interface_color_pair[TITLE_BAR].pairnum); blank_statusbar(); mvwaddnstr(bottomwin, 0, 0, prompt, actual_x(prompt, COLS - 2)); waddch(bottomwin, ':'); waddch(bottomwin, (page_start == 0) ? ' ' : '$'); expanded = display_string(curranswer, page_start, COLS - start_col - 1, FALSE); waddstr(bottomwin, expanded); free(expanded); wattroff(bottomwin, A_BOLD); wattroff(bottomwin, interface_color_pair[TITLE_BAR].pairnum); statusbar_pww = statusbar_xplustabs(); reset_statusbar_cursor(); wnoutrefresh(bottomwin); }
/* Write a shortcut key to the help area at the bottom of the window. * keystroke is e.g. "^G" and desc is e.g. "Get Help". We are careful * to write at most len characters, even if len is very small and * keystroke and desc are long. Note that waddnstr(,,(size_t)-1) adds * the whole string! We do not bother padding the entry with blanks. */ void onekey(const std::string& keystroke, const std::string& desc, size_t len) { size_t keystroke_len = keystroke.length() + 1; set_color(bottomwin, interface_colors[KEY_COMBO]); waddnstr(bottomwin, keystroke.c_str(), actual_x(keystroke.c_str(), len)); clear_color(bottomwin, interface_colors[KEY_COMBO]); if (len > keystroke_len) { len -= keystroke_len; } else { len = 0; } if (len > 0) { waddch(bottomwin, ' '); waddnstr(bottomwin, desc.c_str(), actual_x(desc.c_str(), len)); } }
/* Move up one page. */ void do_page_up(void) { int i, skipped = 0; /* If there's less than a page of text left on the screen, put the * cursor at the beginning of the first line of the file, and then * update the edit window. */ if (openfile->current->lineno == 1 || ( #ifndef NANO_TINY !ISSET(SOFTWRAP) && #endif openfile->current->lineno <= editwinrows - 2)) { do_first_line(); return; } /* If we're not in smooth scrolling mode, put the cursor at the * beginning of the top line of the edit window, as Pico does. */ #ifndef NANO_TINY if (!ISSET(SMOOTH_SCROLL)) { #endif openfile->current = openfile->edittop; openfile->placewewant = openfile->current_y = 0; #ifndef NANO_TINY } #endif for (i = editwinrows - 2; i - skipped > 0 && openfile->current != openfile->fileage; i--) { openfile->current = openfile->current->prev; #ifndef NANO_TINY if (ISSET(SOFTWRAP) && openfile->current) { skipped += strlenpt(openfile->current->data) / COLS; #ifdef DEBUG fprintf(stderr, "do_page_up: i = %d, skipped = %d based on line %ld len %lu\n", i, skipped, (long)openfile->current->lineno, (unsigned long)strlenpt(openfile->current->data)); #endif } #endif } openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); #ifdef DEBUG fprintf(stderr, "do_page_up: openfile->current->lineno = %lu, skipped = %d\n", (unsigned long)openfile->current->lineno, skipped); #endif /* Scroll the edit window up a page. */ edit_update(NONE); }
/* Indicate on the statusbar that the string at str was not found by the * last search. */ void not_found_msg(const char *str) { char *disp; int numchars; assert(str != NULL); disp = display_string(str, 0, (COLS / 2) + 1, FALSE); numchars = actual_x(disp, mbstrnlen(disp, COLS / 2)); statusbar(_("\"%.*s%s\" not found"), numchars, disp, (disp[numchars] == '\0') ? "" : "..."); free(disp); }
/* Move up one page. */ void do_page_up(void) { int i, mustmove, skipped = 0; /* If the cursor is less than a page away from the top of the file, * put it at the beginning of the first line. */ if (openfile->current->lineno == 1 || (!ISSET(SOFTWRAP) && openfile->current->lineno <= editwinrows - 2)) { do_first_line(); return; } /* If we're not in smooth scrolling mode, put the cursor at the * beginning of the top line of the edit window, as Pico does. */ if (!ISSET(SMOOTH_SCROLL)) { openfile->current = openfile->edittop; openfile->placewewant = openfile->current_y = 0; } mustmove = (editwinrows < 3) ? 1 : editwinrows - 2; for (i = mustmove; i - skipped > 0 && openfile->current != openfile->fileage; i--) { openfile->current = openfile->current->prev; #ifndef NANO_TINY if (ISSET(SOFTWRAP) && openfile->current) { skipped += strlenpt(openfile->current->data) / editwincols; #ifdef DEBUG fprintf(stderr, "paging up: i = %d, skipped = %d based on line %ld len %lu\n", i, skipped, (long)openfile->current->lineno, (unsigned long)strlenpt(openfile->current->data)); #endif } #endif } openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); /* Scroll the edit window up a page. */ adjust_viewport(STATIONARY); refresh_needed = TRUE; }
/* Move down one page. */ void do_page_down(void) { int i; /* If there's less than a page of text left on the screen, put the * cursor at the beginning of the last line of the file, and then * update the edit window. */ if (openfile->current->lineno + maxrows - 2 >= openfile->filebot->lineno) { do_last_line(); return; } /* If we're not in smooth scrolling mode, put the cursor at the * beginning of the top line of the edit window, as Pico does. */ #ifndef NANO_TINY if (!ISSET(SMOOTH_SCROLL)) { #endif openfile->current = openfile->edittop; openfile->placewewant = openfile->current_y = 0; #ifndef NANO_TINY } #endif for (i = maxrows - 2; i > 0 && openfile->current != openfile->filebot; i--) { openfile->current = openfile->current->next; #ifdef DEBUG fprintf(stderr, "do_page_down: moving to line %lu\n", (unsigned long) openfile->current->lineno); #endif } openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); /* Scroll the edit window down a page. */ edit_update(NONE); }
/* Highlight the current word being replaced or spell checked. We * expect word to have tabs and control characters expanded. */ void do_replace_highlight(bool highlight, const char *word) { size_t y = xplustabs(), word_len = strlenpt(word); y = get_page_start(y) + COLS - y; /* Now y is the number of columns that we can display on this * line. */ assert(y > 0); if (word_len > y) { y--; } reset_cursor(); wnoutrefresh(edit); if (highlight) { wattron(edit, highlight_attribute); } /* This is so we can show zero-length matches. */ if (word_len == 0) { waddch(edit, ' '); } else { waddnstr(edit, word, actual_x(word, y)); } if (word_len > y) { waddch(edit, '$'); } if (highlight) { wattroff(edit, highlight_attribute); } }
void titlebar(const char *path) { int space = COLS; /* The space we have available for display. */ size_t verlen = strlenpt(PACKAGE_STRING) + 1; /* The length of the version message in columns, plus one for * padding. */ const char *prefix; /* "DIR:", "File:", or "New Buffer". Goes before filename. */ size_t prefixlen; /* The length of the prefix in columns, plus one for padding. */ const char *state; /* "Modified", "View", or "". Shows the state of this * buffer. */ ssize_t statelen = 0; /* The length of the state in columns, or the length of * "Modified" if the state is blank and we're not in the file * browser. */ char *exppath = NULL; /* The filename, expanded for display. */ bool newfie = false; /* Do we say "New Buffer"? */ bool dots = false; /* Do we put an ellipsis before the path? */ set_color(topwin, interface_colors[TITLE_BAR]); blank_titlebar(); /* space has to be at least 4: two spaces before the version message, * at least one character of the version message, and one space * after the version message. */ if (space < 4) { space = 0; } else { /* Limit verlen to 1/3 the length of the screen in columns, * minus three columns for spaces. */ if (verlen > (COLS / 3) - 3) { verlen = (COLS / 3) - 3; } } if (space >= 4) { /* Add a space after the version message, and account for both * it and the two spaces before it. */ mvwaddnstr(topwin, 0, 2, PACKAGE_STRING, actual_x(PACKAGE_STRING, verlen)); verlen += 3; /* Account for the full length of the version message. */ space -= verlen; } /* Don't display the state if we're in the file browser. */ if (path != NULL) { state = ""; } else { state = openfile->modified ? _("Modified") : ISSET(VIEW_MODE) ? _("View") : ""; } statelen = strlenpt((*state == '\0' && path == NULL) ? _("Modified") : state); /* If possible, add a space before state. */ if (space > 0 && statelen < space) { statelen++; } else { goto the_end; } /* path should be a directory if we're in the file browser. */ if (path != NULL) { prefix = _("DIR:"); } else { if (openfile->filename[0] == '\0') { prefix = _("New Buffer"); newfie = true; } else { prefix = _("File:"); } } prefixlen = strnlenpt(prefix, space - statelen) + 1; /* If newfie is false, add a space after prefix. */ if (!newfie && prefixlen + statelen < space) { prefixlen++; } /* If we're not in the file browser, set path to the current * filename. */ if (path == NULL) { path = openfile->filename.c_str(); } /* Account for the full lengths of the prefix and the state. */ if (space >= prefixlen + statelen) { space -= prefixlen + statelen; } else { space = 0; } /* space is now the room we have for the filename. */ if (!newfie) { size_t lenpt = strlenpt(path), start_col; /* Don't set dots to true if we have fewer than eight columns * (i.e. one column for padding, plus seven columns for a * filename). */ dots = (space >= 8 && lenpt >= space); if (dots) { start_col = lenpt - space + 3; space -= 3; } else { start_col = 0; } exppath = display_string(path, start_col, space, false); } /* If dots is true, we will display something like "File: * ...ename". */ if (dots) { mvwaddnstr(topwin, 0, verlen - 1, prefix, actual_x(prefix, prefixlen)); if (space <= -3 || newfie) { goto the_end; } waddch(topwin, ' '); waddnstr(topwin, "...", space + 3); if (space <= 0) { goto the_end; } waddstr(topwin, exppath); } else { size_t exppathlen = newfie ? 0 : strlenpt(exppath); /* The length of the expanded filename. */ /* There is room for the whole filename, so we center it. */ mvwaddnstr(topwin, 0, verlen + ((space - exppathlen) / 3), prefix, actual_x(prefix, prefixlen)); if (!newfie) { waddch(topwin, ' '); waddstr(topwin, exppath); } } the_end: free(exppath); if (state[0] != '\0') { if (statelen >= COLS - 1) { mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS)); } else { assert(COLS - statelen - 1 >= 0); mvwaddnstr(topwin, 0, COLS - statelen - 1, state, actual_x(state, statelen)); } } clear_color(topwin, interface_colors[TITLE_BAR]); wnoutrefresh(topwin); reset_cursor(); wnoutrefresh(edit); }
/* Ask a question on the statusbar. The prompt will be stored in the * static prompt, which should be NULL initially, and the answer will be * stored in the answer global. Returns -1 on aborted enter, -2 on a * blank string, and 0 otherwise, the valid shortcut key caught. * curranswer is any editable text that we want to put up by default, * and refresh_func is the function we want to call to refresh the edit * window. * * The allow_tabs parameter indicates whether we should allow tabs to be * interpreted. The allow_files parameter indicates whether we should * allow all files (as opposed to just directories) to be tab * completed. */ int do_prompt(bool allow_tabs, #ifndef DISABLE_TABCOMP bool allow_files, #endif int menu, const char *curranswer, #ifndef DISABLE_HISTORIES linestruct **history_list, #endif void (*refresh_func)(void), const char *msg, ...) { va_list ap; int retval; functionptrtype func; #ifndef DISABLE_TABCOMP bool list = FALSE; #endif /* prompt has been freed and set to NULL unless the user resized * while at the statusbar prompt. */ free(prompt); prompt = charalloc(((COLS - 4) * mb_cur_max()) + 1); bottombars(menu); va_start(ap, msg); vsnprintf(prompt, (COLS - 4) * mb_cur_max(), msg, ap); va_end(ap); null_at(&prompt, actual_x(prompt, COLS - 4)); func = get_prompt_string(&retval, allow_tabs, #ifndef DISABLE_TABCOMP allow_files, &list, #endif curranswer, #ifndef DISABLE_HISTORIES history_list, #endif refresh_func); free(prompt); prompt = NULL; /* We're done with the prompt, so save the statusbar cursor * position. */ old_statusbar_x = statusbar_x; old_pww = statusbar_pww; /* If we left the prompt via Cancel or Enter, set the return value * properly. */ if (func == do_cancel) retval = -1; else if (func == do_enter_void) retval = (*answer == '\0') ? -2 : 0; blank_statusbar(); wnoutrefresh(bottomwin); #ifdef DEBUG fprintf(stderr, "answer = \"%s\"\n", answer); #endif #ifndef DISABLE_TABCOMP /* If we've done tab completion, there might be a list of filename * matches on the edit window at this point. Make sure that they're * cleared off. */ if (list) refresh_func(); #endif return retval; }
int do_dictionary_prompt(bool all, const char *msg, char *str) { s_list l; s_node *p; int ok = -2, width = 16; const char *yesstr; /* String of Yes characters accepted. */ const char *nostr; /* Same for No. */ const char *allstr; /* And All, surprise! */ int u; char **thisarray = (char **)malloc(sizeof(char)*5); for(u = 0; u < 5; u++) thisarray[u] = (char *)malloc(sizeof(char)*16); thisarray = thisfunc(str); int oldmenu = currmenu; char but[16]; int i, j = 0; int count = 0; int t = 0; int bs = 0; char buf[16]; strcpy(buf, str); int tp; int we = 0, wc = 0; tp = typepos(); assert(msg != NULL); s_init(&l); u = 0; while(u < 5) { s_append(&l, thisarray[u]); u++; } /*Now partial matched list is prepared*/ p = l.head; if(str == NULL){ count = 0; while(p){ count++; p = p->next; } } //count=5; p = l.head; do { int kbinput; functionptrtype func; i = 0; #ifndef DISABLE_MOUSE int mouse_x, mouse_y; #endif if (!ISSET(NO_HELP)) { char shortstr[3]; /* Temporary string for (translated) " Y", " N" and " A". */ if (COLS < 32) width = COLS / 2; /* Clear the shortcut list from the bottom of the screen. */ blank_bottombars(); /* Now show the ones for "Yes", "No", "Cancel" and maybe "All". */ if (all) { shortstr[1] = allstr[0]; wmove(bottomwin, 1, width); onekey(shortstr, _("All"), width); } for(i = 0; i < count && i < 5; i++){ sprintf(but, "%d", i + 1); if(i == 0) wmove(bottomwin, 1, 0); else wmove(bottomwin, 1, i * width); if(p){ onekey(but, p->word, width); p = p->next; } j++; } wmove(bottomwin, 1, i * width); onekey("^C", _("Cancel"), width); } if (interface_color_pair[TITLE_BAR].bright) wattron(bottomwin, A_BOLD); wattron(bottomwin, interface_color_pair[TITLE_BAR].pairnum); blank_statusbar(); mvwaddnstr(bottomwin, 0, 0, msg, actual_x(msg, COLS - 1)); wattroff(bottomwin, A_BOLD); wattroff(bottomwin, interface_color_pair[TITLE_BAR].pairnum); /* Refresh edit window and statusbar before getting input. */ wnoutrefresh(edit); wnoutrefresh(bottomwin); currmenu = MYESNO; kbinput = get_kbinput(bottomwin); #ifndef NANO_TINY if (kbinput == KEY_WINCH) continue; #endif /*49 is 1, 50 is 2 and so on*/ func = func_from_key(&kbinput); if (func == do_cancel){ ok = 0; return ok; } if(kbinput == 's'){ if(i == 5) { continue; } } if(kbinput <= '0' || kbinput > '0' + count) return -1; if(kbinput > '0' && kbinput <= '0' + count){ p = l.head; i = kbinput - '0' - 1; if(j > 5) t = j - 5 + i; else t = i; while(t--) if(p){ p = p->next; } if(tp == 1 || tp == 2){ bs = strlen(str); while(bs--) do_backspace(); } if(tp == 6 || tp == 7){ if(str != NULL){ we = openfile->current_x; while(openfile->current->data[we] != ' ' && openfile->current->data[we] != '\00'){ we++; wc++; do_right(); } we--; while(openfile->current->data[we] != ' ' && openfile->current->data[we] != '\00'){ do_backspace(); we--; } } } do_output(p->word, strlen(p->word), FALSE); return tp; } else if (func == total_refresh) { total_redraw(); continue; } else { /* Look for the kbinput in the Yes, No and (optionally) * All strings. */ if (strchr(yesstr, kbinput) != NULL) ok = 1; else if (strchr(nostr, kbinput) != NULL) ok = 0; else if (all && strchr(allstr, kbinput) != NULL) ok = 2; } i = 0; p = l.head; } while (ok == -2); currmenu = oldmenu; return ok; }
/* edit_draw() takes care of the job of actually painting a line into * the edit window. fileptr is the line to be painted, at row line of * the window. converted is the actual string to be written to the * window, with tabs and control characters replaced by strings of * regular characters. start is the column number of the first * character of this page. That is, the first character of converted * corresponds to character number actual_x(fileptr->data, start) of the * line. */ void edit_draw(filestruct *fileptr, const char *converted, int line, size_t start) { size_t startpos = actual_x(fileptr->data, start); /* The position in fileptr->data of the leftmost character * that displays at least partially on the window. */ size_t endpos = actual_x(fileptr->data, start + COLS - 1) + 1; /* The position in fileptr->data of the first character that is * completely off the window to the right. * * Note that endpos might be beyond the null terminator of the * string. */ assert(openfile != openfiles.end() && fileptr != NULL && converted != NULL); assert(strlenpt(converted) <= COLS); /* Just paint the string in any case (we'll add color or reverse on * just the text that needs it). */ mvwaddstr(edit, line, 0, converted); /* Tell ncurses to really redraw the line without trying to optimize * for what it thinks is already there, because it gets it wrong in * the case of a wide character in column zero. */ #ifndef USE_SLANG wredrawln(edit, line, 1); #endif /* If color syntaxes are available and turned on, we need to display * them. */ if (!openfile->colorstrings.empty() && !ISSET(NO_COLOR_SYNTAX)) { /* Set up multi-line color data for this line if it's not yet calculated */ if (fileptr->multidata.empty() && openfile->syntax && openfile->syntax->nmultis > 0) { fileptr->multidata.resize(openfile->syntax->nmultis, -1); // assume that '-1' applies until we know otherwise } for (auto tmpcolor : openfile->colorstrings) { int x_start; /* Starting column for mvwaddnstr. Zero-based. */ int paintlen = 0; /* Number of chars to paint on this line. There are * COLS characters on a whole line. */ size_t index; /* Index in converted where we paint. */ regmatch_t startmatch; /* Match position for start_regex. */ regmatch_t endmatch; /* Match position for end_regex. */ if (tmpcolor->bright) { wattron(edit, A_BOLD); } if (tmpcolor->underline) { wattron(edit, A_UNDERLINE); } wattron(edit, COLOR_PAIR(tmpcolor->pairnum)); /* Two notes about regexec(). A return value of zero means * that there is a match. Also, rm_eo is the first * non-matching character after the match. */ /* First case,tmpcolor is a single-line expression. */ if (tmpcolor->end == NULL) { size_t k = 0; /* We increment k by rm_eo, to move past the end of the * last match. Even though two matches may overlap, we * want to ignore them, so that we can highlight e.g. C * strings correctly. */ while (k < endpos) { /* Note the fifth parameter to regexec(). It says * not to match the beginning-of-line character * unless k is zero. If regexec() returns * REG_NOMATCH, there are no more matches in the * line. */ if (regexec(tmpcolor->start, &fileptr->data[k], 1, &startmatch, (k == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH) { break; } /* Translate the match to the beginning of the * line. */ startmatch.rm_so += k; startmatch.rm_eo += k; /* Skip over a zero-length regex match. */ if (startmatch.rm_so == startmatch.rm_eo) { startmatch.rm_eo++; } else if (startmatch.rm_so < endpos && startmatch.rm_eo > startpos) { x_start = (startmatch.rm_so <= startpos) ? 0 : strnlenpt(fileptr->data, startmatch.rm_so) - start; index = actual_x(converted, x_start); paintlen = actual_x(converted + index, strnlenpt(fileptr->data, startmatch.rm_eo) - start - x_start); assert(0 <= x_start && 0 <= paintlen); mvwaddnstr(edit, line, x_start, converted + index, paintlen); } k = startmatch.rm_eo; } } else if (!fileptr->multidata.empty() && fileptr->multidata[tmpcolor->id] != CNONE) { /* This is a multi-line regex. There are two steps. * First, we have to see if the beginning of the line is * colored by a start on an earlier line, and an end on * this line or later. * * We find the first line before fileptr matching the * start. If every match on that line is followed by an * end, then go to step two. Otherwise, find the next * line after start_line matching the end. If that line * is not before fileptr, then paint the beginning of * this line. */ const filestruct *start_line = fileptr->prev; /* The first line before fileptr matching start. */ regoff_t start_col; /* Where it starts in that line. */ const filestruct *end_line; short md = fileptr->multidata[tmpcolor->id]; if (md == -1) { fileptr->multidata[tmpcolor->id] = CNONE; /* until we find out otherwise */ } else if (md == CNONE) { unset_formatting(tmpcolor); continue; } else if (md == CWHOLELINE) { mvwaddnstr(edit, line, 0, converted, -1); unset_formatting(tmpcolor); continue; } else if (md == CBEGINBEFORE) { regexec(tmpcolor->end, fileptr->data, 1, &endmatch, 0); paintlen = actual_x(converted, strnlenpt(fileptr->data, endmatch.rm_eo) - start); mvwaddnstr(edit, line, 0, converted, paintlen); unset_formatting(tmpcolor); continue; } while (start_line != NULL && regexec(tmpcolor->start, start_line->data, 1, &startmatch, 0) == REG_NOMATCH) { /* If there is an end on this line, there is no need * to look for starts on earlier lines. */ if (regexec(tmpcolor->end, start_line->data, 0, NULL, 0) == 0) { goto step_two; } start_line = start_line->prev; } /* If the found start has been qualified as an end earlier, believe it and skip to the next step. */ if (start_line != NULL && !start_line->multidata.empty() && start_line->multidata[tmpcolor->id] == CBEGINBEFORE) { goto step_two; } /* Skip over a zero-length regex match. */ if (startmatch.rm_so == startmatch.rm_eo) { startmatch.rm_eo++; } else { /* No start found, so skip to the next step. */ if (start_line == NULL) { goto step_two; } /* Now start_line is the first line before fileptr * containing a start match. Is there a start on * this line not followed by an end on this line? */ start_col = 0; while (true) { start_col += startmatch.rm_so; startmatch.rm_eo -= startmatch.rm_so; if (regexec(tmpcolor->end, start_line->data + start_col + startmatch.rm_eo, 0, NULL, (start_col + startmatch.rm_eo == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH) { /* No end found after this start. */ break; } start_col++; if (regexec(tmpcolor->start, start_line->data + start_col, 1, &startmatch, REG_NOTBOL) == REG_NOMATCH) { /* No later start on this line. */ goto step_two; } } /* Indeed, there is a start not followed on this * line by an end. */ /* We have already checked that there is no end * before fileptr and after the start. Is there an * end after the start at all? We don't paint * unterminated starts. */ end_line = fileptr; while (end_line != NULL && regexec(tmpcolor->end, end_line->data, 1, &endmatch, 0) == REG_NOMATCH) { end_line = end_line->next; } /* No end found, or it is too early. */ if (end_line == NULL || (end_line == fileptr && endmatch.rm_eo <= startpos)) { goto step_two; } /* Now paint the start of fileptr. If the start of * fileptr is on a different line from the end, * paintlen is -1, meaning that everything on the * line gets painted. Otherwise, paintlen is the * expanded location of the end of the match minus * the expanded location of the beginning of the * page. */ if (end_line != fileptr) { paintlen = -1; fileptr->multidata[tmpcolor->id] = CWHOLELINE; } else { paintlen = actual_x(converted, strnlenpt(fileptr->data, endmatch.rm_eo) - start); fileptr->multidata[tmpcolor->id] = CBEGINBEFORE; } mvwaddnstr(edit, line, 0, converted, paintlen); /* If the whole line has been painted, don't bother looking for any more starts. */ if (paintlen < 0) { continue; } step_two: /* Second step, we look for starts on this line. */ start_col = 0; while (start_col < endpos) { if (regexec(tmpcolor->start, fileptr->data + start_col, 1, &startmatch, (start_col == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH || start_col + startmatch.rm_so >= endpos) { /* No more starts on this line. */ break; } /* Translate the match to be relative to the * beginning of the line. */ startmatch.rm_so += start_col; startmatch.rm_eo += start_col; x_start = (startmatch.rm_so <= startpos) ? 0 : strnlenpt(fileptr->data, startmatch.rm_so) - start; index = actual_x(converted, x_start); if (regexec(tmpcolor->end, fileptr->data + startmatch.rm_eo, 1, &endmatch, (startmatch.rm_eo == 0) ? 0 : REG_NOTBOL) == 0) { /* Translate the end match to be relative to the beginning of the line. */ endmatch.rm_so += startmatch.rm_eo; endmatch.rm_eo += startmatch.rm_eo; /* There is an end on this line. But does * it appear on this page, and is the match * more than zero characters long? */ if (endmatch.rm_eo > startpos && endmatch.rm_eo > startmatch.rm_so) { paintlen = actual_x(converted + index, strnlenpt(fileptr->data, endmatch.rm_eo) - start - x_start); assert(0 <= x_start && x_start < COLS); mvwaddnstr(edit, line, x_start, converted + index, paintlen); if (paintlen > 0) { fileptr->multidata[tmpcolor->id] = CSTARTENDHERE; } } } else { /* There is no end on this line. But we * haven't yet looked for one on later * lines. */ end_line = fileptr->next; while (end_line != NULL && regexec(tmpcolor->end, end_line->data, 0, NULL, 0) == REG_NOMATCH) { end_line = end_line->next; } if (end_line != NULL) { assert(0 <= x_start && x_start < COLS); mvwaddnstr(edit, line, x_start, converted + index, -1); /* We painted to the end of the line, so * don't bother checking any more * starts. */ fileptr->multidata[tmpcolor->id] = CENDAFTER; break; } } start_col = startmatch.rm_so + 1; } } } unset_formatting(tmpcolor); } } /* If the mark is on, we need to display it. */ if (openfile->mark_set && (fileptr->lineno <= openfile->mark_begin->lineno || fileptr->lineno <= openfile->current->lineno) && (fileptr->lineno >= openfile->mark_begin->lineno || fileptr->lineno >= openfile->current->lineno)) { /* fileptr is at least partially selected. */ const filestruct *top; /* Either current or mark_begin, whichever is first. */ size_t top_x; /* current_x or mark_begin_x, corresponding to top. */ const filestruct *bot; size_t bot_x; int x_start; /* Starting column for mvwaddnstr(). Zero-based. */ int paintlen; /* Number of characters to paint on this line. There are * COLS characters on a whole line. */ size_t index; /* Index in converted where we paint. */ mark_order(&top, &top_x, &bot, &bot_x, NULL); if (top->lineno < fileptr->lineno || top_x < startpos) { top_x = startpos; } if (bot->lineno > fileptr->lineno || bot_x > endpos) { bot_x = endpos; } /* The selected bit of fileptr is on this page. */ if (top_x < endpos && bot_x > startpos) { assert(startpos <= top_x); /* x_start is the expanded location of the beginning of the * mark minus the beginning of the page. */ x_start = strnlenpt(fileptr->data, top_x) - start; /* If the end of the mark is off the page, paintlen is -1, * meaning that everything on the line gets painted. * Otherwise, paintlen is the expanded location of the end * of the mark minus the expanded location of the beginning * of the mark. */ if (bot_x >= endpos) { paintlen = -1; } else paintlen = strnlenpt(fileptr->data, bot_x) - (x_start + start); /* If x_start is before the beginning of the page, shift * paintlen x_start characters to compensate, and put * x_start at the beginning of the page. */ if (x_start < 0) { paintlen += x_start; x_start = 0; } assert(x_start >= 0 && x_start <= strlen(converted)); index = actual_x(converted, x_start); if (paintlen > 0) { paintlen = actual_x(converted + index, paintlen); } set_color(edit, interface_colors[FUNCTION_TAG]); mvwaddnstr(edit, line, x_start, converted + index, paintlen); clear_color(edit, interface_colors[FUNCTION_TAG]); } } }
/* Ask a simple Yes/No (and optionally All) question, specified in msg, * on the statusbar. Return 1 for Yes, 0 for No, 2 for All (if all is * TRUE when passed in), and -1 for Cancel. */ int do_yesno_prompt(bool all, const char *msg) { int ok = -2, width = 16; const char *yesstr; /* String of Yes characters accepted. */ const char *nostr; /* Same for No. */ const char *allstr; /* And All, surprise! */ int oldmenu = currmenu; assert(msg != NULL); /* yesstr, nostr, and allstr are strings of any length. Each string * consists of all single-byte characters accepted as valid * characters for that value. The first value will be the one * displayed in the shortcuts. */ /* TRANSLATORS: For the next three strings, if possible, specify * the single-byte shortcuts for both your language and English. * For example, in French: "OoYy" for "Oui". */ yesstr = _("Yy"); nostr = _("Nn"); allstr = _("Aa"); do { int kbinput; functionptrtype func; #ifndef DISABLE_MOUSE int mouse_x, mouse_y; #endif if (!ISSET(NO_HELP)) { char shortstr[3]; /* Temporary string for (translated) " Y", " N" and " A". */ if (COLS < 32) width = COLS / 2; /* Clear the shortcut list from the bottom of the screen. */ blank_bottombars(); /* Now show the ones for "Yes", "No", "Cancel" and maybe "All". */ sprintf(shortstr, " %c", yesstr[0]); wmove(bottomwin, 1, 0); onekey(shortstr, _("Yes"), width); if (all) { shortstr[1] = allstr[0]; wmove(bottomwin, 1, width); onekey(shortstr, _("All"), width); } shortstr[1] = nostr[0]; wmove(bottomwin, 2, 0); onekey(shortstr, _("No"), width); wmove(bottomwin, 2, width); onekey("^C", _("Cancel"), width); } if (interface_color_pair[TITLE_BAR].bright) wattron(bottomwin, A_BOLD); wattron(bottomwin, interface_color_pair[TITLE_BAR].pairnum); blank_statusbar(); mvwaddnstr(bottomwin, 0, 0, msg, actual_x(msg, COLS - 1)); wattroff(bottomwin, A_BOLD); wattroff(bottomwin, interface_color_pair[TITLE_BAR].pairnum); /* Refresh edit window and statusbar before getting input. */ wnoutrefresh(edit); wnoutrefresh(bottomwin); currmenu = MYESNO; kbinput = get_kbinput(bottomwin); #ifndef NANO_TINY if (kbinput == KEY_WINCH) continue; #endif func = func_from_key(&kbinput); if (func == do_cancel) ok = -1; #ifndef DISABLE_MOUSE else if (kbinput == KEY_MOUSE) { /* We can click on the Yes/No/All shortcut list to * select an answer. */ if (get_mouseinput(&mouse_x, &mouse_y, FALSE) == 0 && wmouse_trafo(bottomwin, &mouse_y, &mouse_x, FALSE) && mouse_x < (width * 2) && mouse_y > 0) { int x = mouse_x / width; /* Calculate the x-coordinate relative to the * two columns of the Yes/No/All shortcuts in * bottomwin. */ int y = mouse_y - 1; /* Calculate the y-coordinate relative to the * beginning of the Yes/No/All shortcuts in * bottomwin, i.e. with the sizes of topwin, * edit, and the first line of bottomwin * subtracted out. */ assert(0 <= x && x <= 1 && 0 <= y && y <= 1); /* x == 0 means they clicked Yes or No. y == 0 * means Yes or All. */ ok = -2 * x * y + x - y + 1; if (ok == 2 && !all) ok = -2; } } #endif /* !DISABLE_MOUSE */ else if (func == total_refresh) { total_redraw(); continue; } else { /* Look for the kbinput in the Yes, No and (optionally) * All strings. */ if (strchr(yesstr, kbinput) != NULL) ok = 1; else if (strchr(nostr, kbinput) != NULL) ok = 0; else if (all && strchr(allstr, kbinput) != NULL) ok = 2; } } while (ok == -2); currmenu = oldmenu; return ok; }
/* If scroll_only is FALSE, move down one line. If scroll_only is TRUE, * scroll down one line without scrolling the cursor. */ void do_down(bool scroll_only) { #ifndef NANO_TINY int amount = 0, enough; filestruct *topline; #endif size_t was_column = xplustabs(); /* If we're at the bottom of the file, get out. */ if (openfile->current == openfile->filebot) return; assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno); assert(openfile->current->next != NULL); #ifndef NANO_TINY if (ISSET(SOFTWRAP)) { /* Compute the number of lines to scroll. */ amount = strlenpt(openfile->current->data) / editwincols - xplustabs() / editwincols + strlenpt(openfile->current->next->data) / editwincols + openfile->current_y - editwinrows + 2; topline = openfile->edittop; /* Reduce the amount when there are overlong lines at the top. */ for (enough = 1; enough < amount; enough++) { amount -= strlenpt(topline->data) / editwincols; if (amount > 0) topline = topline->next; if (amount < enough) { amount = enough; break; } } } #endif /* Move to the next line in the file. */ openfile->current = openfile->current->next; openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); /* If scroll_only is FALSE and if we're on the last line of the * edit window, scroll the edit window down one line if we're in * smooth scrolling mode, or down half a page if we're not. If * scroll_only is TRUE, scroll the edit window down one line * unconditionally. */ #ifndef NANO_TINY if (openfile->current_y == editwinrows - 1 || amount > 0 || scroll_only) { if (amount < 1 || scroll_only) amount = 1; edit_scroll(DOWNWARD, (ISSET(SMOOTH_SCROLL) || scroll_only) ? amount : editwinrows / 2 + 1); if (ISSET(SOFTWRAP)) { refresh_needed = TRUE; return; } } #else if (openfile->current_y == editwinrows - 1) edit_scroll(DOWNWARD, editwinrows / 2 + 1); #endif /* If the lines weren't already redrawn, see if they need to be. */ if (openfile->current_y < editwinrows - 1) { /* Redraw the prior line if it was horizontally scrolled. */ if (need_horizontal_scroll(was_column, 0)) update_line(openfile->current->prev, 0); /* Redraw the current line if it needs to be horizontally scrolled. */ if (need_horizontal_scroll(0, xplustabs())) update_line(openfile->current, openfile->current_x); } }
/* Go to the specified line and column, or ask for them if interactive * is TRUE. Save the x-coordinate and y-coordinate if save_pos is TRUE. * Update the screen afterwards if allow_update is TRUE. Note that both * the line and column numbers should be one-based. */ void do_gotolinecolumn(ssize_t line, ssize_t column, bool use_answer, bool interactive, bool save_pos, bool allow_update) { if (interactive) { char *ans = mallocstrcpy(NULL, answer); functionptrtype func; /* Ask for the line and column. */ int i = do_prompt(FALSE, #ifndef DISABLE_TABCOMP TRUE, #endif MGOTOLINE, use_answer ? ans : "", #ifndef DISABLE_HISTORIES NULL, #endif /* TRANSLATORS: This is a prompt. */ edit_refresh, _("Enter line number, column number")); free(ans); /* Cancel, or Enter with blank string. */ if (i < 0) { statusbar(_("Cancelled")); display_main_list(); return; } func = func_from_key(&i); if (func == gototext_void) { /* Keep answer up on the statusbar. */ search_init(TRUE, TRUE); do_search(); return; } /* Do a bounds check. Display a warning on an out-of-bounds * line or column number only if we hit Enter at the statusbar * prompt. */ if (!parse_line_column(answer, &line, &column) || line < 1 || column < 1) { if (i == 0) statusbar(_("Invalid line or column number")); display_main_list(); return; } } else { if (line < 1) line = openfile->current->lineno; if (column < 1) column = openfile->placewewant + 1; } for (openfile->current = openfile->fileage; openfile->current != openfile->filebot && line > 1; line--) openfile->current = openfile->current->next; openfile->current_x = actual_x(openfile->current->data, column - 1); openfile->placewewant = column - 1; /* Put the top line of the edit window in range of the current line. * If save_pos is TRUE, don't change the cursor position when doing * it. */ edit_update(save_pos ? NONE : CENTER); /* If allow_update is TRUE, update the screen. */ if (allow_update) { edit_refresh(); display_main_list(); } }
char *display_string(const char *buf, size_t start_col, size_t len, bool dollars) { size_t start_index; /* Index in buf of the first character shown. */ size_t column; /* Screen column that start_index corresponds to. */ size_t alloc_len; /* The length of memory allocated for converted. */ char *converted; /* The string we return. */ size_t index; /* Current position in converted. */ char *buf_mb; int buf_mb_len; /* If dollars is true, make room for the "$" at the end of the * line. */ if (dollars && len > 0 && strlenpt(buf) > start_col + len) { len--; } if (len == 0) { return mallocstrcpy(NULL, ""); } buf_mb = charalloc(mb_cur_max()); start_index = actual_x(buf, start_col); column = strnlenpt(buf, start_index); assert(column <= start_col); /* Make sure there's enough room for the initial character, whether * it's a multibyte control character, a non-control multibyte * character, a tab character, or a null terminator. Rationale: * * multibyte control character followed by a null terminator: * 1 byte ('^') + mb_cur_max() bytes + 1 byte ('\0') * multibyte non-control character followed by a null terminator: * mb_cur_max() bytes + 1 byte ('\0') * tab character followed by a null terminator: * mb_cur_max() bytes + (tabsize - 1) bytes + 1 byte ('\0') * * Since tabsize has a minimum value of 1, it can substitute for 1 * byte above. */ alloc_len = (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE; converted = charalloc(alloc_len); index = 0; if (buf[start_index] != '\0' && buf[start_index] != '\t' && (column < start_col || (dollars && column > 0))) { /* We don't display all of buf[start_index] since it starts to * the left of the screen. */ buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL); if (is_cntrl_mbchar(buf_mb)) { if (column < start_col) { char *ctrl_buf_mb = charalloc(mb_cur_max()); int ctrl_buf_mb_len, i; ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb, &ctrl_buf_mb_len); for (i = 0; i < ctrl_buf_mb_len; i++) { converted[index++] = ctrl_buf_mb[i]; } start_col += mbwidth(ctrl_buf_mb); free(ctrl_buf_mb); start_index += buf_mb_len; } } else if (using_utf8() && mbwidth(buf_mb) == 2) { if (column >= start_col) { converted[index++] = ' '; start_col++; } converted[index++] = ' '; start_col++; start_index += buf_mb_len; } } while (buf[start_index] != '\0') { buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL); /* Make sure there's enough room for the next character, whether * it's a multibyte control character, a non-control multibyte * character, a tab character, or a null terminator. */ if (index + mb_cur_max() + tabsize + 1 >= alloc_len - 1) { alloc_len += (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE; converted = charealloc(converted, alloc_len); } /* If buf contains a tab character, interpret it. */ if (*buf_mb == '\t') { if (ISSET(WHITESPACE_DISPLAY)) { int i; for (i = 0; i < whitespace_len[0]; i++) { converted[index++] = whitespace[i]; } } else { converted[index++] = ' '; } start_col++; while (start_col % tabsize != 0) { converted[index++] = ' '; start_col++; } } else if (is_cntrl_mbchar(buf_mb)) { /* If buf contains a control character, interpret it. */ char *ctrl_buf_mb = charalloc(mb_cur_max()); int ctrl_buf_mb_len, i; converted[index++] = '^'; start_col++; ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb, &ctrl_buf_mb_len); for (i = 0; i < ctrl_buf_mb_len; i++) { converted[index++] = ctrl_buf_mb[i]; } start_col += mbwidth(ctrl_buf_mb); free(ctrl_buf_mb); /* If buf contains a space character, interpret it. */ } else if (*buf_mb == ' ') { if (ISSET(WHITESPACE_DISPLAY)) { int i; for (i = whitespace_len[0]; i < whitespace_len[0] + whitespace_len[1]; i++) { converted[index++] = whitespace[i]; } } else { converted[index++] = ' '; } start_col++; } else { /* If buf contains a non-control character, interpret it. If buf * contains an invalid multibyte sequence, display it as such. */ char *nctrl_buf_mb = charalloc(mb_cur_max()); int nctrl_buf_mb_len, i; /* Make sure an invalid sequence-starter byte is properly * terminated, so that it doesn't pick up lingering bytes * of any previous content. */ null_at(&buf_mb, buf_mb_len); nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb, &nctrl_buf_mb_len); for (i = 0; i < nctrl_buf_mb_len; i++) { converted[index++] = nctrl_buf_mb[i]; } start_col += mbwidth(nctrl_buf_mb); free(nctrl_buf_mb); } start_index += buf_mb_len; } free(buf_mb); assert(alloc_len >= index + 1); /* Null-terminate converted. */ converted[index] = '\0'; /* Make sure converted takes up no more than len columns. */ index = actual_x(converted, len); null_at(&converted, index); return converted; }
/* If scroll_only is FALSE, move down one line. If scroll_only is TRUE, * scroll down one line without scrolling the cursor. */ void do_down( #ifndef NANO_TINY bool scroll_only #else void #endif ) { #ifndef NANO_TINY int amount = 0, enough; filestruct *topline; #endif /* If we're at the bottom of the file, get out. */ if (openfile->current == openfile->filebot || !openfile->current->next) return; assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno); /* Move the current line of the edit window down. */ openfile->current = openfile->current->next; openfile->current_x = actual_x(openfile->current->data, openfile->placewewant); #ifndef NANO_TINY if (ISSET(SOFTWRAP)) { /* Compute the amount to scroll. */ amount = (strlenpt(openfile->current->data) / COLS + openfile->current_y + 2 + strlenpt(openfile->current->prev->data) / COLS - editwinrows); topline = openfile->edittop; /* Reduce the amount when there are overlong lines at the top. */ for (enough = 1; enough < amount; enough++) { if (amount <= strlenpt(topline->data) / COLS) { amount = enough; break; } amount -= strlenpt(topline->data) / COLS; topline = topline->next; } } #endif /* If scroll_only is FALSE and if we're on the last line of the * edit window, scroll the edit window down one line if we're in * smooth scrolling mode, or down half a page if we're not. If * scroll_only is TRUE, scroll the edit window down one line * unconditionally. */ if (openfile->current_y == editwinrows - 1 #ifndef NANO_TINY || amount > 0 || scroll_only #endif ) { #ifndef NANO_TINY if (amount < 1 || scroll_only) amount = 1; #endif edit_scroll(DOWNWARD, #ifndef NANO_TINY (ISSET(SMOOTH_SCROLL) || scroll_only) ? amount : #endif editwinrows / 2 + 1); edit_refresh_needed = TRUE; } /* If we're above the last line of the edit window, update the line * we were on before and the line we're on now. The former needs to * be redrawn if we're not on the first page, and the latter needs * to be drawn unconditionally. */ if (openfile->current_y < editwinrows - 1 #ifndef NANO_TINY || ISSET(SOFTWRAP) #endif ) { if (need_screen_update(0)) update_line(openfile->current->prev, 0); update_line(openfile->current, openfile->current_x); } }