Logo Search packages:      
Sourcecode: tcd version File versions  Download package

user-interface.c

/*-
 * This file implements the user interface for tcd, a tiny CD player.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cd-utils.h"
#include "cddb.h"
#include "user-interface.h"
#include "ui.h"

static struct coords screen, play_status, track_list, cpanel, info;

static void update_coords(void)
{
    int maxy, maxx;
    getmaxyx(stdscr, maxy, maxx);
    set_coords(&screen, 0, 0, maxy, maxx);
    set_coords(&play_status, 1, 1, 8, 28);
    set_coords(&track_list, play_status.bottom + 1, 1, screen.bottom - 1,
               28);
    set_coords(&info, screen.bottom - 5, play_status.right + 1,
               screen.bottom - 1, screen.right - 1);
    set_coords(&cpanel, 1, play_status.right + 1, info.top,
               screen.right - 1);
}

static int is_playable(struct tcd_state *state)
{
    const CDstatus st = state->cdrom->status;
    return (st == CD_STOPPED || st == CD_PAUSED || st == CD_PLAYING);
}

static const char *play_method(PlayMethod pm)
{
    switch (pm) {
    case REPEAT_CD:
        return "Repeat CD";
    case REPEAT_TRK:
        return "Repeat Track";
    case NORMAL:
        return "Normal";
    }
    return "(Unknown)";
}

static const char *play_status_str(struct tcd_state *state)
{
    switch (state->cdrom->status) {
    case CD_TRAYEMPTY:
        return "No Audio";
    case CD_PLAYING:
        return "Playing";
    case CD_PAUSED:
        return "Paused";
    case CD_STOPPED:
        return "Stopped";
    case CD_ERROR:
        return "Error";
    }
    return "(Unknown)";
}

static void phelp(int y, int x, char key, const char *func, int enabled)
{
    const int text_attr = (enabled) ? C_YELLOW | A_BOLD : A_DIM;
    const int maxwidth = cpanel.width - (x + 4);
    y += cpanel.top, x += cpanel.left;
    attron(text_attr);
    mvprintw(y, x, "[%c]", key);
    attroff(text_attr);
    mvaddlstr(y, x + 4, func, maxwidth);
}

static void draw_tracklist(struct tcd_state *state)
{
    const int base_x = track_list.left + 2, base_y = track_list.top + 1;
    const int delta_x = 12, delta_y = 1;
    const int tracks_x = 2, tracks_y = track_list.height - 1;
    int i, x, y, track_first = 0, track_last;
    
    if (!CD_INDRIVE(state->cdrom->status)) {
      return;
    }
    track_last = state->cdrom->numtracks - 1;

    if (track_last + 1 > tracks_x * tracks_y) {
        track_first = state->cdrom->cur_track - tracks_x * tracks_y / 2;
        track_last = state->cdrom->cur_track + tracks_x * tracks_y / 2 - 1;
        if (track_first < 0) {
            track_last -= track_first;
            track_first -= track_first;
        }
        if (track_last > state->cdrom->numtracks - 1) {
            track_first -= track_last - (state->cdrom->numtracks - 1);
            track_last -= track_last - (state->cdrom->numtracks - 1);
        }
    }

    for (i = track_first, x = 0, y = 0; i <= track_last; i++) {
        int cd_min, cd_sec, cd_frm;
        int is_current = (state->cdrom->status == CD_PLAYING
                          && state->cdrom->cur_track == i);
        if (is_current) {
            attron(A_BOLD);
        }
        FRAMES_TO_MSF(state->cdrom->track[i].length, &cd_min, &cd_sec,
                      &cd_frm);
        mvprintw(base_y + y * delta_y, base_x + x * delta_x,
                 "%02u - %02u:%02u", 1 + i, cd_min, cd_sec);
        if (is_current) {
            attroff(A_BOLD);
        }
        if (y < tracks_y - 1) {
            y++;
        } else {
            y = 0;
            if (x < tracks_x - 1) {
                x++;
            } else {
                break;
            }
        }
    }
}

static void draw_play_status(struct tcd_state *state)
{
    const struct coords *const ps = &play_status;
    const int left1 = play_status.left + 1;
    const int left2 = play_status.left + 13;
    const int width1 = left2 - left1;
    const int width2 = play_status.width - 13;
    int cd_min, cd_sec, ti_min, ti_sec, dummy;

    mvaddlstr(ps->top + 1, left1, "Status:", width1);
    mvaddlstr(ps->top + 2, left1, "Play mode:", width1);
    mvaddlstr(ps->top + 1, left2, play_status_str(state), width2);
    mvaddlstr(ps->top + 2, left2, play_method(state->play_method), width2);

    if (!CD_INDRIVE(state->cdrom->status)) {
      return;
    }
    mvaddlstr(ps->top + 3, left1, "Track:", width1);
    mvaddlstr(ps->top + 4, left1, "Time:", width1);
    mvaddlstr(ps->top + 5, left1, "CD:", width1);
    FRAMES_TO_MSF(cd_length(state->cdrom), &cd_min, &cd_sec, &dummy);
    FRAMES_TO_MSF(state->cdrom->cur_frame, &ti_min, &ti_sec, &dummy);
    mvprintw(ps->top + 3, left2, "%d of %d", 1 + state->cdrom->cur_track,
             state->cdrom->numtracks);
    mvprintw(ps->top + 4, left2, "%02d:%02d", ti_min, ti_sec);
    mvprintw(ps->top + 5, left2, "%02d:%02d", cd_min, cd_sec);
}

static void draw_control_panel(struct tcd_state *state)
{
    const int playable = is_playable(state);
    phelp(1, 1, 'P', "- Start playing", playable);
    phelp(2, 1, 'U', "- Pause or restart", playable);
    phelp(3, 1, 'E', "- Eject CDROM",
          state->cdrom->status != CD_TRAYEMPTY);
    phelp(4, 1, 'S', "- Stop playing", playable);
    phelp(5, 1, 'M', "- Change play mode", 1);
    phelp(1, 26, '-', "- Previous track", playable);
    phelp(2, 26, '+', "- Next track", playable);
    phelp(3, 26, 'G', "- Go to track", playable);
    phelp(4, 26, ']', "- Skip ahead", playable);
    phelp(5, 26, '[', "- Skip back", playable);
    phelp(7, 1, 'T', "- Edit track database", playable);
    phelp(8, 1, 'Q', "- Quit", 1);
}

static void draw_info(struct tcd_state *state)
{
    const int left1 = info.left + 1;
    const int left2 = info.left + 10;
    const int width1 = left2 - left1;
    const int width2 = info.width - 10;
    mvaddlstr(info.top + 1, left1, "Title:", width1);
    mvaddlstr(info.top + 2, left1, "Track:", width1);
    attron(C_RED | A_BOLD);
    mvaddlstr(info.top + 1, left2, state->cd_info.disc_title, width2);
    mvaddlstr(info.top + 2, left2,
              state->cd_info.trk[state->cdrom->cur_track].name, width2);
    attroff(C_RED | A_BOLD);
}

static void describe(struct coords *cbox, const char *descr)
{
    mvaddlstr(cbox->top - 1, cbox->left + 1, descr, cbox->width - 3);
}

static void draw_screen(struct tcd_state *state)
{
    attron(C_BLUE);
    box(stdscr, ACS_VLINE, ACS_HLINE);
    mvvsplit(screen.top, play_status.right, screen.height);
    mvhsplit(info.top - 1, info.left - 1, info.width + 2);
    mvhsplit(play_status.bottom, play_status.left - 1,
             play_status.width + 2);
    attroff(C_BLUE);

    attron(C_BLUE | A_BOLD);
    describe(&cpanel, "Control Panel");
    describe(&info, "Disc Information");
    describe(&track_list, "Track List");
    attroff(C_BLUE | A_BOLD);

    attron(C_WHITE | A_BOLD);
    mvaddlstr(screen.top, screen.left + 2, PACKAGE_STRING,
              screen.width - 4);
    attroff(C_WHITE | A_BOLD);

    draw_play_status(state);
    draw_tracklist(state);
    draw_control_panel(state);
    draw_info(state);
}

extern int get_i_track(void)
{
    char tmp[5];
    if (ui_inputbox(tmp, sizeof(tmp), "Go to which track?") != -1) {
        return atoi(tmp) - 1;
    }
    return -1;
}

extern void tcd_ui_update(struct tcd_state *state)
{
    wrefresh(stdscr);
    werase(stdscr);
    update_coords();
    draw_screen(state);
    wrefresh(stdscr);
}

Generated by  Doxygen 1.6.0   Back to index