/*
 * seln.c -- print the primary selection from the server.  If the
 * selection is in a text subwindow, then print information about
 * the line number(s) the selection spans and the indexes of the
 * bytes within the textsw's text stream.  This simple program
 * may not be sufficient for general usage -- see comments in
 * get_selection() comments below.
 */
#include <stdio.h>
#include <xview/xview.h>
#include <xview/textsw.h>
#include <xview/panel.h>
#include <xview/server.h>
#include <xview/seln.h>

Xv_Server       server;
Textsw          textsw;

char *get_selection();

main(argc, argv)
char *argv[];
{
    Frame       frame;
    Panel       panel;
    void        print_seln(), exit();

    xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);

    frame = (Frame) xv_create(NULL, FRAME,
                              FRAME_LABEL,            argv[0],
                              NULL);
    panel = (Panel) xv_create(frame, PANEL,
                              WIN_WIDTH,              WIN_EXTEND_TO_EDGE,
                              NULL);
    (void) xv_create(panel, PANEL_BUTTON,
                     PANEL_LABEL_STRING,     "Quit",
                     PANEL_NOTIFY_PROC,      exit,
                     NULL);
    (void) xv_create(panel, PANEL_BUTTON,
                     PANEL_LABEL_STRING,     "Get Selection",
                     PANEL_NOTIFY_PROC,      print_seln,
                     NULL);
    window_fit(panel);

    textsw = (Textsw)xv_create(frame, TEXTSW,
                               WIN_X,                  0,
                               WIN_BELOW,              panel,
                               WIN_ROWS,               10,
                               WIN_COLUMNS,            80,
                               TEXTSW_FILE_CONTENTS,   "/etc/passwd",
                               NULL);
    window_fit(frame);

    server = (Xv_Server)xv_get(xv_get(frame, XV_SCREEN), SCREEN_SERVER);

    xv_main_loop(frame);
}

void
print_seln()
{
    char *text = get_selection();

    if(text)
        printf("---selection---\n%s\n---end seln---\n", text);
}

/*
 * Get the selection using selection_ask().  Note that if the
 * selection is bigger than about 2K, the whole selection will
 * not be gotten with one call, thus this method of getting the
 * selection may not be sufficient.
 */
char *
get_selection()
{
    long                sel_lin_num, lines_selected;
    Textsw_index        first, last;
    Seln_holder         holder;
    Seln_result         result;
    int                 len;
    Seln_request       *request;
    static char         selection_buf[BUFSIZ];
    register char      *ptr;

    /* get the holder of the primary selection */
    holder = selection_inquire(server, SELN_PRIMARY);

    /* If the selection occurs in the text subwindow, print lots
     * of info about the selection.
     */
    if(seln_holder_same_client(&holder, (char *)textsw))
    {
        /* ask for information from the selection service */
        request = selection_ask(server, &holder,
                                /* get the index of the first and last chars in seln */
                                SELN_REQ_FIRST,             NULL,
                                SELN_REQ_LAST,              NULL,
                                /* get the actual selection bytes */
                                SELN_REQ_CONTENTS_ASCII,    NULL,
                                /* fool the textsw to think entire lines are selected */
                                SELN_REQ_FAKE_LEVEL,        SELN_LEVEL_LINE,
                                /* line numbers of beginning and ending of the seln */
                                SELN_REQ_FIRST_UNIT,        NULL,
                                SELN_REQ_LAST_UNIT,         NULL,
                                NULL);
        /* set the ptr to beginning of data -- SELN_REQ_FIRST */
        ptr = request->data;
        /* "first" is data succeeding SELN_REQ_FIRST -- skip attr */
        first = *(Textsw_index *)(ptr += sizeof(SELN_REQ_FIRST));
        ptr += sizeof(Textsw_index); /* skip over value of "first" */
        /* "last" is data succeeding SELN_REQ_LAST -- skip attr */
        last  = *(Textsw_index *)(ptr += sizeof(SELN_REQ_LAST));
        ptr += sizeof(Textsw_index); /* skip over value of "last" */

        /* advance pointer past SELN_REQ_CONTENTS_ASCII */
        ptr += sizeof(SELN_REQ_CONTENTS_ASCII);
        len = strlen(ptr); /* length of string in request */
        (void) strcpy(selection_buf, ptr);

        /*
         * advance pointer past length of string.  If the string
         * length isn't aligned to a 4-byte boundary, add the
         * difference in bytes -- then advance pointer passed "value".
         */
        if(len % 4)
            len = len + (4 - (len % 4));

        ptr += len + sizeof(Seln_attribute); /* skip over "value" */

        /* advance past SELN_REQ_FAKE_LEVEL, SELN_LEVEL_LINE */
        ptr += sizeof(SELN_REQ_FAKE_LEVEL) + sizeof(SELN_LEVEL_LINE);

        sel_lin_num = *(long *)(ptr += sizeof(SELN_REQ_FIRST_UNIT));
        ptr += sizeof(long);
        lines_selected = *(long *)(ptr += sizeof(SELN_REQ_LAST_UNIT));
        ptr += sizeof(long);

        /* hack to workaround bug with SELN_REQ_LAST_UNIT always
         * returning -1.  We have to count the line numbers ourselves.
         */
        if(lines_selected < 0)
        {
            register char *p;
            lines_selected++;

            for(p = selection_buf; *p; p++)
                if(*p == '\n')
                    lines_selected++;
        }

        printf("index in textsw: %d-%d, line number(s) = %d-%d\n",
               first + 1, last + 1, sel_lin_num + 1,
               sel_lin_num + lines_selected + 1);
    }
    else
    {
        /* the selection is not in the text subwindow */
        request = selection_ask(server, &holder,
                                SELN_REQ_CONTENTS_ASCII, NULL,
                                NULL);

        if(request->status != SELN_SUCCESS)
        {
            printf("selection_ask() returns %d\n", request->status);
            return "";
        }

        (void) strcpy(selection_buf,
                      request->data + sizeof(SELN_REQ_CONTENTS_ASCII));
    }

    return selection_buf;
}
