1 /*
2  * Copyright (c) 2010, 2011 Ryan Flannery <ryan.flannery@gmail.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "uinterface.h"
18 
19 /* the global user interface object */
20 uinterface ui;
21 
22 /*****************************************************************************
23  * scrollable window stuff
24  ****************************************************************************/
25 
26 swindow*
swindow_new(int h,int w,int y,int x)27 swindow_new(int h, int w, int y, int x)
28 {
29    swindow *swin = malloc(sizeof(swindow));
30    if (swin == NULL)
31       err(1, "swindow_new failed to allocate swin");
32 
33    swin->w        = w;
34    swin->h        = h;
35    swin->voffset  = 0;
36    swin->hoffset  = 0;
37    swin->crow     = 0;
38    swin->nrows    = 0;
39    swin->cwin     = newwin(h, w, y, x);
40    if (swin->cwin == NULL)
41       errx(1, "swindow_new: failed to create window");
42 
43    return swin;
44 }
45 
46 void
swindow_resize(swindow * win,int h,int w,int y,int x)47 swindow_resize(swindow *win, int h, int w, int y, int x)
48 {
49    wresize(win->cwin, h, w);
50    mvwin(win->cwin, y, x);
51 
52    win->w = w;
53    win->h = h;
54 
55    if (win->crow >= h)
56       win->crow = h - 1;
57 }
58 
59 void
swindow_free(swindow * win)60 swindow_free(swindow *win)
61 {
62    delwin(win->cwin);
63    free(win);
64 }
65 
66 void
swindow_scroll(swindow * win,Direction d,int n)67 swindow_scroll(swindow *win, Direction d, int n)
68 {
69    switch (d) {
70    case UP:
71       win->voffset -= n;
72       if (win->voffset < 0)
73          win->voffset = 0;
74       break;
75 
76    case DOWN:
77       win->voffset += n;
78       if (win->voffset >= win->nrows - win->h)
79          win->voffset = win->nrows - win->h;
80       if (win->voffset < 0)
81          win->voffset = 0;
82       break;
83 
84    case LEFT:
85       win->hoffset -= n;
86       if (win->hoffset < 0)
87          win->hoffset = 0;
88       break;
89 
90    case RIGHT:
91       win->hoffset += n;
92       /* NOTE: "overflow" here is handled elsewhere.
93        * see input_handlers.c, scroll_col()
94        */
95       break;
96 
97    default:
98       err(1, "swindow_scroll: bad direction");
99    }
100 }
101 
102 /*****************************************************************************
103  * UI Create / Resize / Destroy Functions
104  ****************************************************************************/
105 
106 void
ui_init(int library_width)107 ui_init(int library_width)
108 {
109    int lines, cols;
110 
111    /* ncurses init */
112    initscr();
113    raw();
114    noecho();
115    nonl();
116    keypad(stdscr, TRUE);
117    start_color();
118    curs_set(0);
119    ESCDELAY = 0;
120    refresh();
121 
122    getmaxyx(stdscr, lines, cols);
123 
124    /* setup width of library window */
125    ui.lwidth = library_width;
126    ui.lhide = false;
127 
128    /* create the player and command windows */
129    ui.player  = newwin(1, cols, 0,         0);
130    ui.command = newwin(1, cols, lines - 1, 0);
131    if (ui.player == NULL || ui.command == NULL)
132       errx(1, "ui_init: failed to create player/command windows");
133 
134    /* and the rest */
135    ui.library  = swindow_new(lines - 3, ui.lwidth, 2, 0);
136    ui.playlist = swindow_new(lines - 3, cols - ui.lwidth - 1, 2, ui.lwidth + 1);
137 
138    ui.active = ui.library;
139 }
140 
141 bool
ui_is_init()142 ui_is_init()
143 {
144    return isendwin() == TRUE;
145 }
146 
147 void
ui_destroy()148 ui_destroy()
149 {
150    /* destroy each window (this also free()'s the mem for each window) */
151    delwin(ui.player);
152    delwin(ui.command);
153    swindow_free(ui.playlist);
154    if (ui.library != NULL)
155       swindow_free(ui.library);
156 
157    /* reset other properties of the ui to sane 'empty' defaults */
158    ui.player = NULL;
159    ui.command = NULL;
160    ui.library = NULL;
161    ui.playlist = NULL;
162    ui.active = NULL;
163 
164    /* end ncurses */
165    endwin();
166 }
167 
168 void
ui_resize()169 ui_resize()
170 {
171    struct winsize ws;
172 
173    /* get new dimensions and check for changes */
174    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
175       err(1, "ui_resize: ioctl failed");
176 
177    /* can we even handle the new display size?  if not, just exit */
178    if (ws.ws_col < ui.lwidth + 2) {
179       endwin();
180       errx(1, "ui_resize: not enough columns to render vitunes nicely");
181    }
182    if (ws.ws_row < 4) {
183       endwin();
184       errx(1, "ui_resize: not enough rows to render vitunes nicely");
185    }
186 
187    /* resize ncurses */
188    resizeterm(ws.ws_row, ws.ws_col);
189 
190    /* resize player & command windows */
191    wresize(ui.player,  1, ws.ws_col);
192    wresize(ui.command, 1, ws.ws_col);
193 
194    /* resize library and playlist windows */
195    if (ui.library->cwin == NULL)
196       swindow_resize(ui.playlist, ws.ws_row - 3, ws.ws_col, 2, 0);
197    else {
198       swindow_resize(ui.library,  ws.ws_row - 3, ui.lwidth, 2, 0);
199       swindow_resize(ui.playlist, ws.ws_row - 3, ws.ws_col - ui.lwidth - 1, 2, ui.lwidth + 1);
200    }
201 
202    /* move the command window to the new bottom row */
203    mvwin(ui.command, ws.ws_row - 1, 0);
204 
205 }
206 
207 void
ui_hide_library()208 ui_hide_library()
209 {
210    int w, h;
211 
212    /* if already hidden, nothing to do */
213    if (ui.library->cwin == NULL) return;
214 
215    /* remove library window */
216    delwin(ui.library->cwin);
217    ui.library->cwin = NULL;
218 
219    /* resize & move playlist window */
220    getmaxyx(stdscr, h, w);
221    swindow_resize(ui.playlist, h - 3, w, 2, 0);
222 }
223 
224 void
ui_unhide_library()225 ui_unhide_library()
226 {
227    int w, h;
228 
229    /* if not hidden, nothing to do */
230    if (ui.library->cwin != NULL) return;
231 
232    getmaxyx(stdscr, h, w);
233 
234    /* create library window */
235    ui.library->cwin = newwin(h - 3, ui.lwidth, 2, 0);
236    if (ui.library->cwin == NULL)
237       errx(1, "ui_unhide_library: failed to create newwin");
238 
239    /* resize & move playlist window */
240    swindow_resize(ui.playlist, h - 3, w - ui.lwidth - 1, 2, ui.lwidth + 1);
241 }
242 
243 void
ui_clear()244 ui_clear()
245 {
246    wclear(ui.player);
247    wclear(ui.command);
248    wclear(ui.library->cwin);
249    wclear(ui.playlist->cwin);
250    clear();
251 
252    wrefresh(ui.player);
253    wrefresh(ui.command);
254    wrefresh(ui.library->cwin);
255    wrefresh(ui.playlist->cwin);
256    refresh();  /* XXX needed? */
257 }
258