#include "view.h" #include "../utils.h" #include "gui/gui.h" #include <stdlib.h> #include <pthread.h> #include <string.h> struct textlist { view common; gui *g; widget *w; int maxsize; int cursize; float refresh_rate; int autoscroll; pthread_mutex_t lock; list * volatile to_append; }; static void _append(struct textlist *this, char *s, int *deleted) { if (this->cursize == this->maxsize) { text_list_del_silent(this->g, this->w, 0); this->cursize--; (*deleted)++; } text_list_add_silent(this->g, this->w, s, -1, FOREGROUND_COLOR); this->cursize++; } static void *textlist_thread(void *_this) { struct textlist *this = _this; int dirty; int deleted; int visible_lines, start_line, number_of_lines; while (1) { if (pthread_mutex_lock(&this->lock)) abort(); dirty = this->to_append == NULL ? 0 : 1; deleted = 0; while (this->to_append != NULL) { char *s = this->to_append->data; this->to_append = list_remove_head(this->to_append); _append(this, s, &deleted); free(s); } if (pthread_mutex_unlock(&this->lock)) abort(); if (dirty) { text_list_state(this->g, this->w, &visible_lines, &start_line, &number_of_lines); if (this->autoscroll) start_line = number_of_lines - visible_lines; else start_line -= deleted; if (start_line < 0) start_line = 0; text_list_set_start_line(this->g, this->w, start_line); /* this call is not necessary, but if things change in text_list... */ widget_dirty(this->g, this->w); } sleepms(1000/this->refresh_rate); } return 0; } static void clear(view *this) { /* TODO */ } static void append(view *_this, char *s) { struct textlist *this = (struct textlist *)_this; char *dup; if (pthread_mutex_lock(&this->lock)) abort(); dup = strdup(s); if (dup == NULL) abort(); this->to_append = list_append(this->to_append, dup); if (pthread_mutex_unlock(&this->lock)) abort(); } static void scroll(void *private, gui *g, char *notification, widget *w, void *notification_data) { struct textlist *this = private; int visible_lines; int start_line; int number_of_lines; int new_line; int inc; text_list_state(g, w, &visible_lines, &start_line, &number_of_lines); inc = 10; if (inc > visible_lines - 2) inc = visible_lines - 2; if (inc < 1) inc = 1; if (!strcmp(notification, "scrollup")) inc = -inc; new_line = start_line + inc; if (new_line > number_of_lines - visible_lines) new_line = number_of_lines - visible_lines; if (new_line < 0) new_line = 0; text_list_set_start_line(g, w, new_line); if (new_line + visible_lines < number_of_lines) this->autoscroll = 0; else this->autoscroll = 1; } static void click(void *private, gui *g, char *notification, widget *w, void *notification_data) { struct textlist *this = private; int *d = notification_data; int button = d[1]; if (button == 1) this->autoscroll = 1 - this->autoscroll; } view *new_textlist(int maxsize, float refresh_rate, gui *g, widget *w) { struct textlist *ret = calloc(1, sizeof(struct textlist)); if (ret == NULL) abort(); ret->common.clear = clear; ret->common.append = (void (*)(view *, ...))append; ret->cursize = 0; ret->maxsize = maxsize; ret->refresh_rate = refresh_rate; ret->g = g; ret->w = w; ret->autoscroll = 1; if (pthread_mutex_init(&ret->lock, NULL)) abort(); register_notifier(g, "scrollup", w, scroll, ret); register_notifier(g, "scrolldown", w, scroll, ret); register_notifier(g, "click", w, click, ret); new_thread(textlist_thread, ret); return (view *)ret; }