From 5acc319b99f1e179f81e0034ca6714954831467c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 20 Oct 2009 08:00:13 +0200 Subject: [PATCH] hscroll: reimplemented the hscroll library The hscroll object knows the location on the screen and the text. Upon initialization, it installs a GLib timer which updates the screen every second. --- src/hscroll.c | 83 ++++++++++++++++++++++++++++++++++++++++++++ src/hscroll.h | 78 +++++++++++++++++++++++++++++++++++++++++ src/screen_browser.c | 2 +- src/screen_queue.c | 77 +++++++++++++--------------------------- src/song_paint.c | 13 +++++-- src/song_paint.h | 5 ++- src/status_bar.c | 58 +++++++++++-------------------- src/status_bar.h | 1 - 8 files changed, 222 insertions(+), 95 deletions(-) diff --git a/src/hscroll.c b/src/hscroll.c index ace5bde..32988d0 100644 --- a/src/hscroll.c +++ b/src/hscroll.c @@ -49,3 +49,86 @@ strscroll(struct hscroll *hscroll, const char *str, const char *separator, g_free(tmp); return buf; } + +/** + * This timer scrolls the area by one and redraws. + */ +static gboolean +hscroll_timer_callback(gpointer data) +{ + struct hscroll *hscroll = data; + + hscroll_step(hscroll); + hscroll_draw(hscroll); + wrefresh(hscroll->w); + return true; +} + +void +hscroll_set(struct hscroll *hscroll, unsigned x, unsigned y, unsigned width, + const char *text) +{ + assert(hscroll != NULL); + assert(hscroll->w != NULL); + assert(text != NULL); + + if (hscroll->text != NULL && hscroll->x == x && hscroll->y == y && + hscroll->width == width && strcmp(hscroll->text, text) == 0) + /* no change, do nothing (and, most importantly, do + not reset the current offset!) */ + return; + + hscroll_clear(hscroll); + + hscroll->x = x; + hscroll->y = y; + hscroll->width = width; + + /* obtain the ncurses attributes and the current color, store + them */ + wattr_get(hscroll->w, &hscroll->attrs, &hscroll->pair, NULL); + + hscroll->text = g_strdup(text); + hscroll->offset = 0; + hscroll->source_id = g_timeout_add_seconds(1, hscroll_timer_callback, + hscroll); +} + +void +hscroll_clear(struct hscroll *hscroll) +{ + assert(hscroll != NULL); + + if (hscroll->text == NULL) + return; + + g_source_remove(hscroll->source_id); + + g_free(hscroll->text); + hscroll->text = NULL; +} + +void +hscroll_draw(struct hscroll *hscroll) +{ + attr_t old_attrs; + short old_pair; + char *p; + + assert(hscroll != NULL); + assert(hscroll->w != NULL); + assert(hscroll->text != NULL); + + /* set stored attributes and color */ + wattr_get(hscroll->w, &old_attrs, &old_pair, NULL); + wattr_set(hscroll->w, hscroll->attrs, hscroll->pair, NULL); + + /* scroll the string, and draw it */ + p = strscroll(hscroll, hscroll->text, hscroll->separator, + hscroll->width); + mvwaddstr(hscroll->w, hscroll->y, hscroll->x, p); + g_free(p); + + /* restore previous attributes and color */ + wattr_set(hscroll->w, old_attrs, old_pair, NULL); +} diff --git a/src/hscroll.h b/src/hscroll.h index d266c8a..b3047b5 100644 --- a/src/hscroll.h +++ b/src/hscroll.h @@ -22,8 +22,53 @@ #include +#ifdef HAVE_NCURSESW_NCURSES_H +#include +#else +#include +#endif + +/** + * This class is used to auto-scroll text which does not fit on the + * screen. Call hscroll_init() to initialize the object, + * hscroll_clear() to free resources, and hscroll_set() to begin + * scrolling. + */ struct hscroll { + WINDOW *w; + const char *separator; + + /** + * The bounding coordinates on the screen. + */ + unsigned x, y, width; + + /** + * ncurses attributes for drawing the text. + */ + attr_t attrs; + + /** + * ncurses colors for drawing the text. + */ + short pair; + + /** + * The scrolled text, in the current locale. + */ + char *text; + + /** + * The current scrolling offset. This is a character + * position, not a screen column. + */ gsize offset; + + /** + * The id of the timer which updates the scrolled area every + * second. + */ + guint source_id; }; static inline void @@ -42,4 +87,37 @@ char * strscroll(struct hscroll *hscroll, const char *str, const char *separator, unsigned width); +/** + * Initializes a #hscroll object allocated by the caller. + */ +static inline void +hscroll_init(struct hscroll *hscroll, WINDOW *w, const char *separator) +{ + hscroll->w = w; + hscroll->separator = separator; +} + +/** + * Sets a text to scroll. This installs a timer which redraws every + * second with the current window attributes. Call hscroll_clear() to + * disable it. This function automatically removes the old text. + */ +void +hscroll_set(struct hscroll *hscroll, unsigned x, unsigned y, unsigned width, + const char *text); + +/** + * Removes the text and the timer. It may be reused with + * hscroll_set(). + */ +void +hscroll_clear(struct hscroll *hscroll); + +/** + * Explicitly draws the scrolled text. Calling this function is only + * allowed if there is a text currently. + */ +void +hscroll_draw(struct hscroll *hscroll); + #endif diff --git a/src/screen_browser.c b/src/screen_browser.c index 89c84ab..52d76f1 100644 --- a/src/screen_browser.c +++ b/src/screen_browser.c @@ -551,7 +551,7 @@ screen_browser_paint_callback(WINDOW *w, unsigned i, case MPD_ENTITY_TYPE_SONG: paint_song_row(w, y, width, selected, highlight, - mpd_entity_get_song(entity)); + mpd_entity_get_song(entity), NULL); break; case MPD_ENTITY_TYPE_PLAYLIST: diff --git a/src/screen_queue.c b/src/screen_queue.c index 02978ea..c5be09a 100644 --- a/src/screen_queue.c +++ b/src/screen_queue.c @@ -56,10 +56,7 @@ typedef struct struct mpdclient *c; } completion_callback_data_t; -/* static struct hscroll hscroll; -static guint scroll_source_id; -*/ #endif static struct mpdclient_playlist *playlist; @@ -120,20 +117,6 @@ screen_queue_restore_selection(void) screen_queue_save_selection(); } -/* -#ifndef NCMPC_MINI -static gboolean -scroll_timer_callback(G_GNUC_UNUSED gpointer data) -{ - scroll_source_id = 0; - - hscroll_step(&hscroll); - screen_queue_repaint(); - return false; -} -#endif -*/ - static const char * screen_queue_lw_callback(unsigned idx, G_GNUC_UNUSED void *data) { @@ -147,41 +130,6 @@ screen_queue_lw_callback(unsigned idx, G_GNUC_UNUSED void *data) strfsong(songname, MAX_SONG_LENGTH, options.list_format, song); - /* -#ifndef NCMPC_MINI - if (idx == lw->selected) - { - if (options.scroll && utf8_width(songname) > (unsigned)COLS) { - static unsigned current_song; - char *tmp; - - if (current_song != lw->selected) { - hscroll_reset(&hscroll); - current_song = lw->selected; - } - - tmp = strscroll(&hscroll, songname, options.scroll_sep, - MAX_SONG_LENGTH); - g_strlcpy(songname, tmp, MAX_SONG_LENGTH); - g_free(tmp); - - if (scroll_source_id == 0) - scroll_source_id = - g_timeout_add(1000, - scroll_timer_callback, - NULL); - } else { - hscroll_reset(&hscroll); - - if (scroll_source_id != 0) { - g_source_remove(scroll_source_id); - scroll_source_id = 0; - } - } - } -#endif - */ - return songname; } @@ -463,6 +411,11 @@ static void screen_queue_init(WINDOW *w, int cols, int rows) { lw = list_window_init(w, cols, rows); + +#ifndef NCMPC_MINI + if (options.scroll) + hscroll_init(&hscroll, w, options.scroll_sep); +#endif } static gboolean @@ -510,6 +463,11 @@ screen_queue_close(void) g_source_remove(timer_hide_cursor_id); timer_hide_cursor_id = 0; } + +#ifndef NCMPC_MINI + if (options.scroll) + hscroll_clear(&hscroll); +#endif } static void @@ -541,20 +499,33 @@ screen_queue_paint_callback(WINDOW *w, unsigned i, bool selected, G_GNUC_UNUSED void *data) { const struct mpd_song *song; + struct hscroll *row_hscroll; assert(playlist != NULL); assert(i < playlist_length(playlist)); song = playlist_get(playlist, i); +#ifdef NCMPC_MINI + row_hscroll = NULL; +#else + row_hscroll = selected && options.scroll && lw->selected == i + ? &hscroll : NULL; +#endif + paint_song_row(w, y, width, selected, (int)mpd_song_get_id(song) == current_song_id, - song); + song, row_hscroll); } static void screen_queue_paint(void) { +#ifndef NCMPC_MINI + if (options.scroll) + hscroll_clear(&hscroll); +#endif + list_window_paint2(lw, screen_queue_paint_callback, NULL); } diff --git a/src/song_paint.c b/src/song_paint.c index 5b7966b..0e2e741 100644 --- a/src/song_paint.c +++ b/src/song_paint.c @@ -21,6 +21,8 @@ #include "paint.h" #include "strfsong.h" #include "utils.h" +#include "hscroll.h" +#include "charset.h" #include "config.h" #include @@ -31,7 +33,8 @@ void paint_song_row(WINDOW *w, G_GNUC_UNUSED unsigned y, unsigned width, - bool selected, bool highlight, const struct mpd_song *song) + bool selected, bool highlight, const struct mpd_song *song, + G_GNUC_UNUSED struct hscroll *hscroll) { char buffer[width * 4]; @@ -44,9 +47,15 @@ paint_song_row(WINDOW *w, G_GNUC_UNUSED unsigned y, unsigned width, char duration[32]; format_duration_short(duration, sizeof(duration), mpd_song_get_duration(song)); - wmove(w, y, width - strlen(duration) - 1); + width -= strlen(duration) + 1; + wmove(w, y, width); waddch(w, ' '); waddstr(w, duration); } + + if (hscroll != NULL && utf8_width(buffer) >= width) { + hscroll_set(hscroll, 0, y, width, buffer); + hscroll_draw(hscroll); + } #endif } diff --git a/src/song_paint.h b/src/song_paint.h index c4581aa..9136fba 100644 --- a/src/song_paint.h +++ b/src/song_paint.h @@ -29,6 +29,7 @@ #endif struct mpd_song; +struct hscroll; /** * Paints a song into a list window row. The cursor must be set to @@ -40,9 +41,11 @@ struct mpd_song; * @param selected true if the row is selected * @param highlight true if the row is highlighted * @param song the song object + * @param hscroll an optional hscroll object */ void paint_song_row(WINDOW *w, unsigned y, unsigned width, - bool selected, bool highlight, const struct mpd_song *song); + bool selected, bool highlight, const struct mpd_song *song, + struct hscroll *hscroll); #endif diff --git a/src/status_bar.c b/src/status_bar.c index 657008c..00724e3 100644 --- a/src/status_bar.c +++ b/src/status_bar.c @@ -42,8 +42,9 @@ status_bar_init(struct status_bar *p, unsigned width, int y, int x) p->message_source_id = 0; #ifndef NCMPC_MINI - hscroll_reset(&p->hscroll); - p->scroll_source_id = 0; + if (options.scroll) + hscroll_init(&p->hscroll, p->window.w, options.scroll_sep); + p->prev_status = NULL; p->prev_song = NULL; #endif @@ -54,24 +55,11 @@ status_bar_deinit(struct status_bar *p) { delwin(p->window.w); - if (p->message_source_id != 0) - g_source_remove(p->message_source_id); -} - #ifndef NCMPC_MINI -static gboolean -scroll_timer_callback(gpointer data) -{ - struct status_bar *p = data; - - p->scroll_source_id = 0; - - hscroll_step(&p->hscroll); - status_bar_paint(p, p->prev_status, p->prev_song); - doupdate(); - return false; -} + if (options.scroll) + hscroll_clear(&p->hscroll); #endif +} static gboolean status_bar_clear_message(gpointer data) @@ -214,28 +202,19 @@ status_bar_paint(struct status_bar *p, const struct mpd_status *status, /* scroll if the song name is to long */ #ifndef NCMPC_MINI if (options.scroll && utf8_width(songname) > (unsigned)width) { - char *tmp = strscroll(&p->hscroll, songname, - options.scroll_sep, width); - - g_strlcpy(songname, tmp, sizeof(songname)); - g_free(tmp); - - if (p->scroll_source_id == 0) - p->scroll_source_id = - g_timeout_add(1000, - scroll_timer_callback, - p); - } else if (p->scroll_source_id != 0) { - g_source_remove(p->scroll_source_id); - p->scroll_source_id = 0; + hscroll_set(&p->hscroll, x, 0, width, songname); + hscroll_draw(&p->hscroll); + } else { + if (options.scroll) + hscroll_clear(&p->hscroll); + mvwaddstr(w, 0, x, songname); } -#endif - //mvwaddnstr(w, 0, x, songname, width); +#else mvwaddstr(w, 0, x, songname); +#endif #ifndef NCMPC_MINI - } else if (p->scroll_source_id != 0) { - g_source_remove(p->scroll_source_id); - p->scroll_source_id = 0; + } else if (options.scroll) { + hscroll_clear(&p->hscroll); #endif } @@ -262,6 +241,11 @@ status_bar_message(struct status_bar *p, const char *msg) { WINDOW *w = p->window.w; +#ifndef NCMPC_MINI + if (options.scroll) + hscroll_clear(&p->hscroll); +#endif + wmove(w, 0, 0); wclrtoeol(w); colors_use(w, COLOR_STATUS_ALERT); diff --git a/src/status_bar.h b/src/status_bar.h index bfbb2c0..c4a5470 100644 --- a/src/status_bar.h +++ b/src/status_bar.h @@ -38,7 +38,6 @@ struct status_bar { #ifndef NCMPC_MINI struct hscroll hscroll; - guint scroll_source_id; const struct mpd_status *prev_status; const struct mpd_song *prev_song; -- 2.30.2