Code

hscroll: reimplemented the hscroll library
authorMax Kellermann <max@duempel.org>
Tue, 20 Oct 2009 06:00:13 +0000 (08:00 +0200)
committerMax Kellermann <max@duempel.org>
Tue, 20 Oct 2009 06:00:13 +0000 (08:00 +0200)
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
src/hscroll.h
src/screen_browser.c
src/screen_queue.c
src/song_paint.c
src/song_paint.h
src/status_bar.c
src/status_bar.h

index ace5bde9e4b4876cc33c98b7cf9ae719c768b6fc..32988d0fdf6b1b895415079a7610e56577413114 100644 (file)
@@ -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);
+}
index d266c8a335d8b50e46a424ad5d8f97c0196e2eaa..b3047b5c4d5d39bdc39c6462b4024b2bbb7ce53d 100644 (file)
 
 #include <glib.h>
 
+#ifdef HAVE_NCURSESW_NCURSES_H
+#include <ncursesw/ncurses.h>
+#else
+#include <ncurses.h>
+#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
index 89c84abedcb76f3867061940b9ad206d43ff311b..52d76f1a7e5bb2a22799872813c113445b4e7708 100644 (file)
@@ -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:
index 02978eab5fd291b890b770fc77ef19cc0059c5ef..c5be09af82a7751f2d0670f3eb10c43a3d0f09b1 100644 (file)
@@ -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);
 }
 
index 5b7966be18bb877c9476c507365efb8cf59015f4..0e2e74194383f3fb25f19ce3cf1c8377f3cddb04 100644 (file)
@@ -21,6 +21,8 @@
 #include "paint.h"
 #include "strfsong.h"
 #include "utils.h"
+#include "hscroll.h"
+#include "charset.h"
 #include "config.h"
 
 #include <mpd/client.h>
@@ -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
 }
index c4581aafc3ebd664b7de1a3ff91f3b04225216f0..9136fba89c875a884aa8786dac9f475cde326f16 100644 (file)
@@ -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
index 657008c4eb892c7432b55b67a7cb61de453ee04b..00724e3aae062952b1a9ab96ca74368f0c6de862 100644 (file)
@@ -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);
index bfbb2c04654103add9649b071f788be65457f4bc..c4a54708cbf706c3b762441240d6f41c2c2c7c9b 100644 (file)
@@ -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;