/* Adds decorations like ellipsis to the output. Returns actual align type used * for the column (might not match col->info.align). */ static AlignType decorate_output(const column_t *col, char buf[], size_t max_line_width) { const size_t len = get_width_on_screen(buf); const size_t max_col_width = calculate_max_width(col, len, max_line_width); const int too_long = len > max_col_width; AlignType result; if(!too_long) { return (col->info.align == AT_RIGHT ? AT_RIGHT : AT_LEFT); } if(col->info.align == AT_LEFT || (col->info.align == AT_DYN && len <= max_col_width)) { const size_t truncate_pos = utf8_strsnlen(buf, max_col_width); buf[truncate_pos] = '\0'; result = AT_LEFT; } else { int extra_spaces; const size_t truncate_pos = utf8_strsnlen(buf, len - max_col_width); const char *new_beginning = buf + truncate_pos; extra_spaces = 0; while(get_width_on_screen(new_beginning) > max_col_width) { ++extra_spaces; new_beginning += utf8_chrw(new_beginning); } memmove(buf + extra_spaces, new_beginning, strlen(new_beginning) + 1); if(extra_spaces != 0) { memset(buf, ' ', extra_spaces); } assert(get_width_on_screen(buf) == max_col_width && "Column isn't filled."); result = AT_RIGHT; } if(col->info.cropping == CT_ELLIPSIS) { add_ellipsis(result, buf); } return result; }
/* Adds ellipsis to the string in buf not changing enlarging its length (at most * three first or last characters are replaced). */ static void add_ellipsis(AlignType align, char buf[]) { const size_t len = get_width_on_screen(buf); const size_t dot_count = MIN(len, MAX_ELLIPSIS_DOT_COUNT); if(align == AT_LEFT) { const size_t width_limit = len - dot_count; const size_t pos = utf8_strsnlen(buf, width_limit); memset(buf + pos, '.', dot_count); buf[pos + dot_count] = '\0'; } else { const char *new_beginning = buf; size_t skipped = 0; while(skipped < dot_count) { skipped += utf8_chrsw(new_beginning); new_beginning += utf8_chrw(new_beginning); } memmove(buf + dot_count, new_beginning, strlen(new_beginning) + 1); memset(buf, '.', dot_count); } }
void columns_format_line(columns_t *cols, const void *data, size_t max_line_width) { char prev_col_buf[1024 + 1]; size_t prev_col_start = 0UL; prev_col_buf[0] = '\0'; size_t i; size_t prev_col_end = 0; recalculate_if_needed(cols, max_line_width); for(i = 0U; i < cols->count; ++i) { /* Use big buffer to hold whole item so there will be no issues with right * aligned fields. */ char col_buffer[sizeof(prev_col_buf)]; char full_column[sizeof(prev_col_buf)]; size_t cur_col_start; AlignType align; const column_t *const col = &cols->list[i]; col->func(col->info.column_id, data, sizeof(col_buffer), col_buffer); strcpy(full_column, col_buffer); align = decorate_output(col, col_buffer, max_line_width); cur_col_start = calculate_start_pos(col, col_buffer, align); /* Ensure that we are not trying to draw current column in the middle of a * character inside previous column. */ if(prev_col_end > cur_col_start) { const size_t prev_col_max_width = (cur_col_start > prev_col_start) ? (cur_col_start - prev_col_start) : 0UL; const size_t break_point = utf8_strsnlen(prev_col_buf, prev_col_max_width); prev_col_buf[break_point] = '\0'; fill_gap_pos(data, prev_col_start + get_width_on_screen(prev_col_buf), cur_col_start); } else { fill_gap_pos(data, prev_col_end, cur_col_start); } print_func(data, col->info.column_id, col_buffer, cur_col_start, align, full_column); prev_col_end = cur_col_start + get_width_on_screen(col_buffer); /* Store information about the current column for usage on the next * iteration. */ strcpy(prev_col_buf, col_buffer); prev_col_start = cur_col_start; } fill_gap_pos(data, prev_col_end, max_line_width); }
TEST(get_real_string_width_in_the_middle_b, IF(utf8_locale)) { #define ENDING "丝刀" const char utf8_str[] = "师从螺" ENDING; const char utf8_end[] = ENDING; #undef ENDING const size_t expected_len = strlen(utf8_str) - strlen(utf8_end); const size_t calculated_len = utf8_strsnlen(utf8_str, 7); assert_int_equal(expected_len, calculated_len); }
/* Retrieves beginning of the text that should be displayed. Returns the * beginning. */ static const char * get_text_beginning(void) { int skipped = 0; const char *text_piece = text + data[curr_line][2]; /* Skip invisible virtual lines (those that are above current one). */ while(skipped < curr_vline - data[curr_line][0]) { text_piece += utf8_strsnlen(text_piece, viewport_width); ++skipped; } return text_piece; }
/* Formats status line in the "old way" (before introduction of 'statusline' * option). */ static void update_stat_window_old(FileView *view, int lazy_redraw) { const dir_entry_t *const entry = &view->dir_entry[view->list_pos]; char name_buf[160*2 + 1]; char perm_buf[26]; char size_buf[56]; char id_buf[52]; int x; int cur_x; size_t print_width; char *filename; x = getmaxx(stdscr); wresize(stat_win, 1, x); wbkgdset(stat_win, COLOR_PAIR(cfg.cs.pair[STATUS_LINE_COLOR]) | cfg.cs.color[STATUS_LINE_COLOR].attr); filename = get_current_file_name(view); print_width = utf8_strsnlen(filename, 20 + MAX(0, x - 83)); snprintf(name_buf, MIN(sizeof(name_buf), print_width + 1), "%s", filename); friendly_size_notation(entry->size, sizeof(size_buf), size_buf); get_uid_string(entry, 0, sizeof(id_buf), id_buf); if(id_buf[0] != '\0') strcat(id_buf, ":"); get_gid_string(entry, 0, sizeof(id_buf) - strlen(id_buf), id_buf + strlen(id_buf)); #ifndef _WIN32 get_perm_string(perm_buf, sizeof(perm_buf), entry->mode); #else snprintf(perm_buf, sizeof(perm_buf), "%s", attr_str_long(entry->attrs)); #endif werase(stat_win); cur_x = 2; checked_wmove(stat_win, 0, cur_x); wprint(stat_win, name_buf); cur_x += 22; if(x > 83) cur_x += x - 83; mvwaddstr(stat_win, 0, cur_x, size_buf); cur_x += 12; mvwaddstr(stat_win, 0, cur_x, perm_buf); cur_x += 11; snprintf(name_buf, sizeof(name_buf), "%d %s filtered", view->filtered, (view->filtered == 1) ? "file" : "files"); if(view->filtered > 0) mvwaddstr(stat_win, 0, x - (strlen(name_buf) + 2), name_buf); if(cur_x + strlen(id_buf) + 1 > x - (strlen(name_buf) + 2)) break_at(id_buf, ':'); if(cur_x + strlen(id_buf) + 1 > x - (strlen(name_buf) + 2)) id_buf[0] = '\0'; mvwaddstr(stat_win, 0, cur_x, id_buf); if(lazy_redraw) { wnoutrefresh(stat_win); } else { wrefresh(stat_win); } }
/* Formats status line in the "old way" (before introduction of 'statusline' * option). */ static void update_stat_window_old(view_t *view, int lazy_redraw) { const dir_entry_t *const curr = get_current_entry(view); char name_buf[160*2 + 1]; char perm_buf[26]; char size_buf[64]; char id_buf[52]; int x; int cur_x; size_t print_width; char *filename; if(curr == NULL || fentry_is_fake(curr)) { werase(stat_win); refresh_window(stat_win, lazy_redraw); return; } x = getmaxx(stdscr); wresize(stat_win, 1, x); ui_set_bg(stat_win, &cfg.cs.color[STATUS_LINE_COLOR], cfg.cs.pair[STATUS_LINE_COLOR]); filename = get_current_file_name(view); print_width = utf8_strsnlen(filename, 20 + MAX(0, x - 83)); copy_str(name_buf, MIN(sizeof(name_buf), print_width + 1), filename); friendly_size_notation(fentry_get_size(view, curr), sizeof(size_buf), size_buf); get_uid_string(curr, 0, sizeof(id_buf), id_buf); if(id_buf[0] != '\0') strcat(id_buf, ":"); get_gid_string(curr, 0, sizeof(id_buf) - strlen(id_buf), id_buf + strlen(id_buf)); #ifndef _WIN32 get_perm_string(perm_buf, sizeof(perm_buf), curr->mode); #else copy_str(perm_buf, sizeof(perm_buf), attr_str_long(curr->attrs)); #endif werase(stat_win); cur_x = 2; checked_wmove(stat_win, 0, cur_x); wprint(stat_win, name_buf); cur_x += 22; if(x > 83) cur_x += x - 83; mvwaddstr(stat_win, 0, cur_x, size_buf); cur_x += 12; mvwaddstr(stat_win, 0, cur_x, perm_buf); cur_x += 11; snprintf(name_buf, sizeof(name_buf), "%d %s filtered", view->filtered, (view->filtered == 1) ? "file" : "files"); if(view->filtered > 0) mvwaddstr(stat_win, 0, x - (strlen(name_buf) + 2), name_buf); if(cur_x + strlen(id_buf) + 1 > x - (strlen(name_buf) + 2)) break_at(id_buf, ':'); if(cur_x + strlen(id_buf) + 1 > x - (strlen(name_buf) + 2)) id_buf[0] = '\0'; mvwaddstr(stat_win, 0, cur_x, id_buf); refresh_window(stat_win, lazy_redraw); }