Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/clui/tinput.c

    r79ae36dd r9be9c4d  
    11/*
    2  * Copyright (c) 2010 Jiri Svoboda
     2 * Copyright (c) 2011 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    2727 */
    2828
     29#include <sort.h>
    2930#include <stdio.h>
    3031#include <stdlib.h>
     
    4243#include <tinput.h>
    4344
     45#define LIN_TO_COL(ti, lpos) ((lpos) % ((ti)->con_cols))
     46#define LIN_TO_ROW(ti, lpos) ((lpos) / ((ti)->con_cols))
     47
    4448/** Seek direction */
    4549typedef enum {
     
    6165static void tinput_post_seek(tinput_t *, bool);
    6266
     67static void tinput_console_set_lpos(tinput_t *ti, unsigned lpos)
     68{
     69        console_set_pos(ti->console, LIN_TO_COL(ti, lpos),
     70            LIN_TO_ROW(ti, lpos));
     71}
     72
    6373/** Create a new text input field. */
    6474tinput_t *tinput_new(void)
     
    6676        tinput_t *ti;
    6777       
    68         ti = malloc(sizeof(tinput_t));
     78        ti = calloc(1, sizeof(tinput_t));
    6979        if (ti == NULL)
    7080                return NULL;
     
    7787void tinput_destroy(tinput_t *ti)
    7888{
     89        if (ti->prompt != NULL)
     90                free(ti->prompt);
    7991        free(ti);
     92}
     93
     94static void tinput_display_prompt(tinput_t *ti)
     95{
     96        tinput_console_set_lpos(ti, ti->prompt_coord);
     97
     98        console_set_style(ti->console, STYLE_EMPHASIS);
     99        printf("%s", ti->prompt);
     100        console_flush(ti->console);
     101        console_set_style(ti->console, STYLE_NORMAL);
    80102}
    81103
     
    88110        tinput_sel_get_bounds(ti, &sa, &sb);
    89111       
    90         console_set_pos(ti->console, (ti->col0 + start) % ti->con_cols,
    91             ti->row0 + (ti->col0 + start) / ti->con_cols);
     112        tinput_console_set_lpos(ti, ti->text_coord + start);
    92113        console_set_style(ti->console, STYLE_NORMAL);
    93114       
     
    134155static void tinput_position_caret(tinput_t *ti)
    135156{
    136         console_set_pos(ti->console, (ti->col0 + ti->pos) % ti->con_cols,
    137             ti->row0 + (ti->col0 + ti->pos) / ti->con_cols);
    138 }
    139 
    140 /** Update row0 in case the screen could have scrolled. */
     157        tinput_console_set_lpos(ti, ti->text_coord + ti->pos);
     158}
     159
     160/** Update text_coord, prompt_coord in case the screen could have scrolled. */
    141161static void tinput_update_origin(tinput_t *ti)
    142162{
    143         sysarg_t width = ti->col0 + ti->nc;
    144         sysarg_t rows = (width / ti->con_cols) + 1;
    145        
    146         /* Update row0 if the screen scrolled. */
    147         if (ti->row0 + rows > ti->con_rows)
    148                 ti->row0 = ti->con_rows - rows;
     163        unsigned end_coord = ti->text_coord + ti->nc;
     164        unsigned end_row = LIN_TO_ROW(ti, end_coord);
     165
     166        unsigned scroll_rows;
     167
     168        /* Update coords if the screen scrolled. */
     169        if (end_row >= ti->con_rows) {
     170                scroll_rows = end_row - ti->con_rows + 1;
     171                ti->text_coord -= ti->con_cols * scroll_rows;
     172                ti->prompt_coord -= ti->con_cols * scroll_rows;
     173        }
     174}
     175
     176static void tinput_jump_after(tinput_t *ti)
     177{
     178        tinput_console_set_lpos(ti, ti->text_coord + ti->nc);
     179        console_flush(ti->console);
     180        putchar('\n');
     181}
     182
     183static int tinput_display(tinput_t *ti)
     184{
     185        sysarg_t col0, row0;
     186       
     187        if (console_get_pos(ti->console, &col0, &row0) != EOK)
     188                return EIO;
     189       
     190        ti->prompt_coord = row0 * ti->con_cols + col0;
     191        ti->text_coord = ti->prompt_coord + str_length(ti->prompt);
     192
     193        tinput_display_prompt(ti);
     194        tinput_display_tail(ti, 0, 0);
     195        tinput_position_caret(ti);
     196
     197        return EOK;
    149198}
    150199
     
    154203                return;
    155204       
    156         sysarg_t new_width = ti->col0 + ti->nc + 1;
     205        unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + 1;
    157206        if (new_width % ti->con_cols == 0) {
    158207                /* Advancing to new line. */
     
    185234                return;
    186235       
    187         sysarg_t new_width = ti->col0 + ti->nc + ilen;
    188         sysarg_t new_height = (new_width / ti->con_cols) + 1;
     236        unsigned new_width = LIN_TO_COL(ti, ti->text_coord) + ti->nc + ilen;
     237        unsigned new_height = (new_width / ti->con_cols) + 1;
    189238        if (new_height >= ti->con_rows) {
    190239                /* Disallow text longer than 1 page for now. */
     
    511560}
    512561
     562/** Compare two entries in array of completions. */
     563static int compl_cmp(void *va, void *vb, void *arg)
     564{
     565        const char *a = *(const char **) va;
     566        const char *b = *(const char **) vb;
     567
     568        return str_cmp(a, b);
     569}
     570
     571static size_t common_pref_len(const char *a, const char *b)
     572{
     573        size_t i;
     574        size_t a_off, b_off;
     575        wchar_t ca, cb;
     576
     577        i = 0;
     578        a_off = 0;
     579        b_off = 0;
     580
     581        while (true) {
     582                ca = str_decode(a, &a_off, STR_NO_LIMIT);
     583                cb = str_decode(b, &b_off, STR_NO_LIMIT);
     584
     585                if (ca == '\0' || cb == '\0' || ca != cb)
     586                        break;
     587                ++i;
     588        }
     589
     590        return i;
     591}
     592
     593static void tinput_text_complete(tinput_t *ti)
     594{
     595        void *state;
     596        size_t cstart;
     597        char *ctmp;
     598        char **compl;           /* Array of completions */
     599        size_t compl_len;       /* Current length of @c compl array */
     600        size_t cnum;
     601        size_t i;
     602        int rc;
     603
     604        if (ti->compl_ops == NULL)
     605                return;
     606
     607        /*
     608         * Obtain list of all possible completions (growing array).
     609         */
     610
     611        rc = (*ti->compl_ops->init)(ti->buffer, ti->pos, &cstart, &state);
     612        if (rc != EOK)
     613                return;
     614
     615        cnum = 0;
     616
     617        compl_len = 1;
     618        compl = malloc(compl_len * sizeof(char *));
     619        if (compl == NULL) {
     620                printf("Error: Out of memory.\n");
     621                return;
     622        }
     623
     624        while (true) {
     625                rc = (*ti->compl_ops->get_next)(state, &ctmp);
     626                if (rc != EOK)
     627                        break;
     628
     629                if (cnum >= compl_len) {
     630                        /* Extend array */
     631                        compl_len = 2 * compl_len;
     632                        compl = realloc(compl, compl_len * sizeof(char *));
     633                        if (compl == NULL) {
     634                                printf("Error: Out of memory.\n");
     635                                break;
     636                        }
     637                }
     638
     639                compl[cnum] = str_dup(ctmp);
     640                if (compl[cnum] == NULL) {
     641                        printf("Error: Out of memory.\n");
     642                        break;
     643                }
     644                cnum++;
     645        }
     646
     647        (*ti->compl_ops->fini)(state);
     648
     649        if (cnum > 1) {
     650                /*
     651                 * More than one match. Determine maximum common prefix.
     652                 */
     653                size_t cplen;
     654
     655                cplen = str_length(compl[0]);
     656                for (i = 1; i < cnum; i++)
     657                        cplen = min(cplen, common_pref_len(compl[0], compl[i]));
     658
     659                /* Compute how many bytes we should skip. */
     660                size_t istart = str_lsize(compl[0], ti->pos - cstart);
     661
     662                if (cplen > istart) {
     663                        /* Insert common prefix. */
     664
     665                        /* Copy remainder of common prefix. */
     666                        char *cpref = str_ndup(compl[0] + istart,
     667                            str_lsize(compl[0], cplen - istart));
     668
     669                        /* Insert it. */
     670                        tinput_insert_string(ti, cpref);
     671                        free(cpref);
     672                } else {
     673                        /* No common prefix. Sort and display all entries. */
     674
     675                        qsort(compl, cnum, sizeof(char *), compl_cmp, NULL);
     676
     677                        tinput_jump_after(ti);
     678                        for (i = 0; i < cnum; i++)
     679                                printf("%s\n", compl[i]);
     680                        tinput_display(ti);
     681                }
     682        } else if (cnum == 1) {
     683                /*
     684                 * We have exactly one match. Insert it.
     685                 */
     686
     687                /* Compute how many bytes of completion string we should skip. */
     688                size_t istart = str_lsize(compl[0], ti->pos - cstart);
     689
     690                /* Insert remainder of completion string at current position. */
     691                tinput_insert_string(ti, compl[0] + istart);
     692        }
     693
     694        for (i = 0; i < cnum; i++)
     695                free(compl[i]);
     696        free(compl);
     697}
     698
    513699/** Initialize text input field.
    514700 *
     
    521707        ti->hpos = 0;
    522708        ti->history[0] = NULL;
     709}
     710
     711/** Set prompt string.
     712 *
     713 * @param ti            Text input
     714 * @param prompt        Prompt string
     715 *
     716 * @return              EOK on success, ENOMEM if out of memory.
     717 */
     718int tinput_set_prompt(tinput_t *ti, const char *prompt)
     719{
     720        if (ti->prompt != NULL)
     721                free(ti->prompt);
     722       
     723        ti->prompt = str_dup(prompt);
     724        if (ti->prompt == NULL)
     725                return ENOMEM;
     726       
     727        return EOK;
     728}
     729
     730/** Set completion ops.
     731 *
     732 * Set pointer to completion ops structure that will be used for text
     733 * completion.
     734 */
     735void tinput_set_compl_ops(tinput_t *ti, tinput_compl_ops_t *compl_ops)
     736{
     737        ti->compl_ops = compl_ops;
    523738}
    524739
     
    539754                return EIO;
    540755       
    541         if (console_get_pos(ti->console, &ti->col0, &ti->row0) != EOK)
    542                 return EIO;
    543        
    544756        ti->pos = 0;
    545757        ti->sel_start = 0;
     
    549761        ti->exit_clui = false;
    550762       
     763        if (tinput_display(ti) != EOK)
     764                return EIO;
     765       
    551766        while (!ti->done) {
    552767                console_flush(ti->console);
     
    714929                tinput_history_seek(ti, -1);
    715930                break;
     931        case KC_TAB:
     932                tinput_text_complete(ti);
     933                break;
    716934        default:
    717935                break;
Note: See TracChangeset for help on using the changeset viewer.