summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/viewer.c254
-rw-r--r--docs/CREDITS1
2 files changed, 210 insertions, 45 deletions
diff --git a/apps/plugins/viewer.c b/apps/plugins/viewer.c
index 9e590776ee..f58abdea53 100644
--- a/apps/plugins/viewer.c
+++ b/apps/plugins/viewer.c
@@ -128,10 +128,11 @@ static unsigned char *word_mode_str[] = {"wrap", "chop", "words"};
128enum { 128enum {
129 NORMAL=0, 129 NORMAL=0,
130 JOIN, 130 JOIN,
131 REFLOW,
131 EXPAND, 132 EXPAND,
132 LINE_MODES 133 LINE_MODES
133} line_mode = 0; 134} line_mode = 0;
134static unsigned char *line_mode_str[] = {"normal", "join", "expand", "lines"}; 135static unsigned char *line_mode_str[] = {"normal", "join", "reflow", "expand", "lines"};
135 136
136enum { 137enum {
137 NARROW=0, 138 NARROW=0,
@@ -158,8 +159,10 @@ static unsigned char *page_mode_str[] = {"don't overlap", "overlap", "pages"};
158 159
159static unsigned char buffer[BUFFER_SIZE + 1]; 160static unsigned char buffer[BUFFER_SIZE + 1];
160static unsigned char line_break[] = {0,0x20,'-',9,0xB,0xC}; 161static unsigned char line_break[] = {0,0x20,'-',9,0xB,0xC};
161static int display_columns; /* number of columns on the display */ 162static int display_columns; /* number of (pixel) columns on the display */
162static int display_lines; /* number of lines on the display */ 163static int display_lines; /* number of lines on the display */
164static int draw_columns; /* number of (pixel) columns available for text */
165static int par_indent_spaces; /* number of spaces to indent first paragraph */
163static int fd; 166static int fd;
164static long file_size; 167static long file_size;
165static bool mac_text; 168static bool mac_text;
@@ -172,6 +175,21 @@ static unsigned char *next_screen_to_draw_ptr;
172static unsigned char *next_line_ptr; 175static unsigned char *next_line_ptr;
173static struct plugin_api* rb; 176static struct plugin_api* rb;
174 177
178static unsigned char glyph_width[256];
179
180#define ADVANCE_COUNTERS(c) do { width += glyph_width[c]; k++; } while(0)
181#define LINE_IS_FULL ((k>MAX_COLUMNS-1) || (width > draw_columns))
182static unsigned char* crop_at_width(const unsigned char* p)
183{
184 int k,width;
185
186 k=width=0;
187 while (!LINE_IS_FULL)
188 ADVANCE_COUNTERS(p[k]);
189
190 return (unsigned char*) p+k-1;
191}
192
175static unsigned char* find_first_feed(const unsigned char* p, int size) 193static unsigned char* find_first_feed(const unsigned char* p, int size)
176{ 194{
177 int i; 195 int i;
@@ -198,7 +216,7 @@ static unsigned char* find_last_space(const unsigned char* p, int size)
198{ 216{
199 int i, j, k; 217 int i, j, k;
200 218
201 k = line_mode==JOIN? 0:1; 219 k = (line_mode==JOIN) || (line_mode==REFLOW) ? 0:1;
202 220
203 for (i=size-1; i>=0; i--) 221 for (i=size-1; i>=0; i--)
204 for (j=k; j < (int) sizeof(line_break); j++) 222 for (j=k; j < (int) sizeof(line_break); j++)
@@ -208,72 +226,99 @@ static unsigned char* find_last_space(const unsigned char* p, int size)
208 return NULL; 226 return NULL;
209} 227}
210 228
211static unsigned char* find_next_line(const unsigned char* cur_line) 229static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
212{ 230{
213 const unsigned char *next_line = NULL; 231 const unsigned char *next_line = NULL;
214 int size, i, j, k, chop_len, search_len, spaces, newlines, draw_columns; 232 int size, i, j, k, width, search_len, spaces, newlines;
233 bool first_chars;
215 unsigned char c; 234 unsigned char c;
216 235
236 if (is_short != NULL)
237 *is_short = true;
238
217 if BUFFER_OOB(cur_line) 239 if BUFFER_OOB(cur_line)
218 return NULL; 240 return NULL;
219 241
220#ifdef HAVE_LCD_BITMAP
221 draw_columns = need_scrollbar? display_columns-1: display_columns;
222#else
223 draw_columns = display_columns;
224#endif
225
226 if (view_mode == WIDE) { 242 if (view_mode == WIDE) {
227 search_len = chop_len = MAX_WIDTH; 243 search_len = MAX_WIDTH;
228 } 244 }
229 else { /* view_mode == NARROW */ 245 else { /* view_mode == NARROW */
230 chop_len = draw_columns; 246 search_len = crop_at_width(cur_line) - cur_line;
231 search_len = chop_len + 1;
232 } 247 }
233 248
234 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len; 249 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
235 250
236 if (line_mode == JOIN) { 251 if ((line_mode == JOIN) || (line_mode == REFLOW)) {
237 /* Need to scan ahead and possibly increase search_len and size, 252 /* Need to scan ahead and possibly increase search_len and size,
238 or possibly set next_line at second hard return in a row. */ 253 or possibly set next_line at second hard return in a row. */
239 next_line = NULL; 254 next_line = NULL;
240 for (j=k=spaces=newlines=0; j < size; j++) { 255 first_chars=true;
241 if (k == MAX_COLUMNS) 256 for (j=k=width=spaces=newlines=0; ; j++) {
257 if (BUFFER_OOB(cur_line+j))
258 return NULL;
259 if (LINE_IS_FULL) {
260 size = search_len = j;
242 break; 261 break;
262 }
243 263
244 c = cur_line[j]; 264 c = cur_line[j];
245 switch (c) { 265 switch (c) {
246 case ' ': 266 case ' ':
247 spaces++; 267 if (line_mode == REFLOW) {
268 if (newlines > 0) {
269 size = j;
270 next_line = cur_line + size;
271 return (unsigned char*) next_line;
272 }
273 if (j==0) /* i=1 is intentional */
274 for (i=0; i<par_indent_spaces; i++)
275 ADVANCE_COUNTERS(' ');
276 }
277 if (!first_chars) spaces++;
248 break; 278 break;
249 279
250 case 0: 280 case 0:
251 if (newlines > 0) { 281 if (newlines > 0) {
252 size = j; 282 size = j;
253 next_line = cur_line + size - spaces - 1; 283 next_line = cur_line + size - spaces;
254 if (next_line != cur_line) 284 if (next_line != cur_line)
255 return (unsigned char*) next_line; 285 return (unsigned char*) next_line;
256 break; 286 break;
257 } 287 }
288
258 newlines++; 289 newlines++;
259 size += spaces; 290 size += spaces -1;
260 if (BUFFER_OOB(cur_line+size) || size > 2*search_len) 291 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
261 return NULL; 292 return NULL;
262
263 search_len = size; 293 search_len = size;
264 spaces = 0; 294 spaces = first_chars? 0:1;
265 k++;
266 break; 295 break;
267 296
268 default: 297 default:
269 newlines = 0; 298 if (line_mode==JOIN || newlines>0) {
270 while (spaces) { 299 while (spaces) {
271 spaces--; 300 spaces--;
272 k++; 301 ADVANCE_COUNTERS(' ');
273 if (k == MAX_COLUMNS - 1) 302 if (LINE_IS_FULL) {
303 size = search_len = j;
274 break; 304 break;
275 } 305 }
276 k++; 306 }
307 newlines=0;
308 } else if (spaces) {
309 /* REFLOW, multiple spaces between words: count only
310 * one. If more are needed, they will be added
311 * while drawing. */
312 search_len = size;
313 spaces=0;
314 ADVANCE_COUNTERS(' ');
315 if (LINE_IS_FULL) {
316 size = search_len = j;
317 break;
318 }
319 }
320 first_chars = false;
321 ADVANCE_COUNTERS(c);
277 break; 322 break;
278 } 323 }
279 } 324 }
@@ -289,7 +334,7 @@ static unsigned char* find_next_line(const unsigned char* cur_line)
289 next_line = find_last_space(cur_line, size); 334 next_line = find_last_space(cur_line, size);
290 335
291 if (next_line == NULL) 336 if (next_line == NULL)
292 next_line = cur_line + chop_len; 337 next_line = crop_at_width(cur_line);
293 else 338 else
294 if (word_mode == WRAP) 339 if (word_mode == WRAP)
295 for (i=0; 340 for (i=0;
@@ -317,6 +362,9 @@ static unsigned char* find_next_line(const unsigned char* cur_line)
317 if (BUFFER_OOB(next_line)) 362 if (BUFFER_OOB(next_line))
318 return NULL; 363 return NULL;
319 364
365 if (is_short)
366 *is_short = false;
367
320 return (unsigned char*) next_line; 368 return (unsigned char*) next_line;
321} 369}
322 370
@@ -342,7 +390,7 @@ static unsigned char* find_prev_line(const unsigned char* cur_line)
342 points from where they wrap when scrolling down. 390 points from where they wrap when scrolling down.
343 If buffer is at top of file, start at top of buffer. */ 391 If buffer is at top of file, start at top of buffer. */
344 392
345 if (line_mode == JOIN) 393 if ((line_mode == JOIN) || (line_mode == REFLOW))
346 prev_line = p = NULL; 394 prev_line = p = NULL;
347 else 395 else
348 prev_line = p = find_last_feed(buffer, cur_line-buffer-1); 396 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
@@ -356,7 +404,7 @@ static unsigned char* find_prev_line(const unsigned char* cur_line)
356 /* Wrap downwards until too far, then use the one before. */ 404 /* Wrap downwards until too far, then use the one before. */
357 while (p < cur_line && p != NULL) { 405 while (p < cur_line && p != NULL) {
358 prev_line = p; 406 prev_line = p;
359 p = find_next_line(prev_line); 407 p = find_next_line(prev_line, NULL);
360 } 408 }
361 409
362 if (BUFFER_OOB(prev_line)) 410 if (BUFFER_OOB(prev_line))
@@ -477,11 +525,14 @@ static void viewer_scrollbar(void) {
477static void viewer_draw(int col) 525static void viewer_draw(int col)
478{ 526{
479 int i, j, k, line_len, resynch_move, spaces, left_col=0; 527 int i, j, k, line_len, resynch_move, spaces, left_col=0;
528 int width, extra_spaces, indent_spaces, spaces_per_word;
529 bool multiple_spacing, line_is_short;
480 unsigned char *line_begin; 530 unsigned char *line_begin;
481 unsigned char *line_end; 531 unsigned char *line_end;
482 unsigned char c; 532 unsigned char c;
483 unsigned char scratch_buffer[MAX_COLUMNS + 1]; 533 unsigned char scratch_buffer[MAX_COLUMNS + 1];
484 534
535
485 /* If col==-1 do all calculations but don't display */ 536 /* If col==-1 do all calculations but don't display */
486 if (col != -1) { 537 if (col != -1) {
487#ifdef HAVE_LCD_BITMAP 538#ifdef HAVE_LCD_BITMAP
@@ -499,7 +550,7 @@ static void viewer_draw(int col)
499 break; /* Happens after display last line at BUFFER_EOF() */ 550 break; /* Happens after display last line at BUFFER_EOF() */
500 551
501 line_begin = line_end; 552 line_begin = line_end;
502 line_end = find_next_line(line_begin); 553 line_end = find_next_line(line_begin, &line_is_short);
503 554
504 if (line_end == NULL) { 555 if (line_end == NULL) {
505 if (BUFFER_EOF()) { 556 if (BUFFER_EOF()) {
@@ -524,7 +575,7 @@ static void viewer_draw(int col)
524 if (i > 0) 575 if (i > 0)
525 next_line_ptr -= resynch_move; 576 next_line_ptr -= resynch_move;
526 577
527 line_end = find_next_line(line_begin); 578 line_end = find_next_line(line_begin, NULL);
528 if (line_end == NULL) /* Should not really happen */ 579 if (line_end == NULL) /* Should not really happen */
529 break; 580 break;
530 } 581 }
@@ -536,6 +587,8 @@ static void viewer_draw(int col)
536 line_begin++; 587 line_begin++;
537 if (word_mode == CHOP) 588 if (word_mode == CHOP)
538 line_end++; 589 line_end++;
590 else
591 line_len--;
539 } 592 }
540 for (j=k=spaces=0; j < line_len; j++) { 593 for (j=k=spaces=0; j < line_len; j++) {
541 if (k == MAX_COLUMNS) 594 if (k == MAX_COLUMNS)
@@ -561,13 +614,102 @@ static void viewer_draw(int col)
561 break; 614 break;
562 } 615 }
563 } 616 }
617
564 if (col != -1) 618 if (col != -1)
565 if (k > col) { 619 if (k > col) {
566 scratch_buffer[k] = 0; 620 scratch_buffer[k] = 0;
567 rb->lcd_puts(left_col, i, scratch_buffer + col); 621 rb->lcd_puts(left_col, i, scratch_buffer + col);
568 } 622 }
569 } 623 }
570 else { 624 else if (line_mode == REFLOW) {
625 if (line_begin[0] == 0) {
626 line_begin++;
627 if (word_mode == CHOP)
628 line_end++;
629 else
630 line_len--;
631 }
632
633 indent_spaces = 0;
634 if (!line_is_short) {
635 multiple_spacing = false;
636 for (j=width=spaces=0; j < line_len; j++) {
637 c = line_begin[j];
638 switch (c) {
639 case ' ':
640 case 0:
641 if ((j==0) && (word_mode==WRAP))
642 /* special case: indent the paragraph,
643 * don't count spaces */
644 indent_spaces = par_indent_spaces;
645 else if (!multiple_spacing)
646 spaces++;
647 multiple_spacing = true;
648 break;
649 default:
650 multiple_spacing = false;
651 width += glyph_width[c];
652 k++;
653 break;
654 }
655 }
656 if (multiple_spacing) spaces--;
657
658 if (spaces) {
659 /* total number of spaces to insert between words */
660 extra_spaces = (draw_columns-width) / glyph_width[' ']
661 - indent_spaces;
662 /* number of spaces between each word*/
663 spaces_per_word = extra_spaces / spaces;
664 /* number of words with n+1 spaces (to fill up) */
665 extra_spaces = extra_spaces % spaces;
666 if (spaces_per_word > 2) { /* too much spacing is awful */
667 spaces_per_word = 3;
668 extra_spaces = 0;
669 }
670 } else { /* this doesn't matter much... no spaces anyway */
671 spaces_per_word = extra_spaces = 0;
672 }
673 } else { /* end of a paragraph: don't fill line */
674 spaces_per_word = 1;
675 extra_spaces = 0;
676 }
677
678 multiple_spacing = false;
679 for (j=k=spaces=0; j < line_len; j++) {
680 if (k == MAX_COLUMNS)
681 break;
682
683 c = line_begin[j];
684 switch (c) {
685 case ' ':
686 case 0:
687 if (j==0 && word_mode==WRAP) { /* indent paragraph */
688 for (j=0; j<par_indent_spaces; j++)
689 scratch_buffer[k++] = ' ';
690 j=0;
691 }
692 else if (!multiple_spacing) {
693 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
694 scratch_buffer[k++] = ' ';
695 spaces++;
696 }
697 multiple_spacing = true;
698 break;
699 default:
700 scratch_buffer[k++] = c;
701 multiple_spacing = false;
702 break;
703 }
704 }
705
706 if (col != -1)
707 if (k > col) {
708 scratch_buffer[k] = 0;
709 rb->lcd_puts(left_col, i, scratch_buffer + col);
710 }
711 }
712 else { /* line_mode != JOIN && line_mode != REFLOW */
571 if (col != -1) 713 if (col != -1)
572 if (line_len > col) { 714 if (line_len > col) {
573 c = line_end[0]; 715 c = line_end[0];
@@ -636,20 +778,38 @@ static void init_need_scrollbar(void) {
636 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */ 778 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
637 viewer_draw(-1); 779 viewer_draw(-1);
638 need_scrollbar = NEED_SCROLLBAR(); 780 need_scrollbar = NEED_SCROLLBAR();
781 draw_columns = need_scrollbar? display_columns-glyph_width['o'] : display_columns;
782 par_indent_spaces = draw_columns/(5*glyph_width[' ']);
639} 783}
640#endif 784#endif
641 785
642static bool viewer_init(char* file) 786static bool viewer_init(char* file)
643{ 787{
644#ifdef HAVE_LCD_BITMAP 788#ifdef HAVE_LCD_BITMAP
645 int w,h; 789 int idx, ch;
646 790 struct font *pf;
647 rb->lcd_getstringsize("o", &w, &h); 791
648 display_lines = LCD_HEIGHT / h; 792 pf = rb->font_get(FONT_UI);
649 display_columns = LCD_WIDTH / w; 793 if (pf->width != NULL)
794 { /* variable pitch font -- fill structure from font width data */
795 ch = pf->defaultchar - pf->firstchar;
796 rb->memset(glyph_width, pf->width[ch], 256);
797 idx = pf->firstchar;
798 rb->memcpy(&glyph_width[idx], pf->width, pf->size);
799 idx += pf->size;
800 rb->memset(&glyph_width[idx], pf->width[ch], 256-idx);
801 }
802 else /* fixed pitch font -- same width for all glyphs */
803 rb->memset(glyph_width, pf->maxwidth, 256);
804
805 display_lines = LCD_HEIGHT / pf->height;
806 display_columns = LCD_WIDTH;
650#else 807#else
808 /* REAL fixed pitch :) all chars use up 1 cell */
651 display_lines = 2; 809 display_lines = 2;
652 display_columns = 11; 810 draw_columns = display_columns = 11;
811 par_indent_spaces = 2;
812 rb->memset(glyph_width, 1, 256);
653#endif 813#endif
654 /********************* 814 /*********************
655 * (Could re-initialize settings here, if you 815 * (Could re-initialize settings here, if you
@@ -755,14 +915,18 @@ enum plugin_status plugin_start(struct plugin_api* api, void* file)
755 break; 915 break;
756 916
757 case VIEWER_MODE_LINE: 917 case VIEWER_MODE_LINE:
758 /* Line-paragraph mode: NORMAL, JOIN or EXPAND */ 918 /* Line-paragraph mode: NORMAL, JOIN, REFLOW or EXPAND */
759 if (++line_mode == LINE_MODES) 919 if (++line_mode == LINE_MODES)
760 line_mode = 0; 920 line_mode = 0;
761 921
762 if (view_mode == WIDE) 922 if (view_mode == WIDE) {
763 if (line_mode == JOIN) 923 if (line_mode == JOIN)
764 if (++line_mode == LINE_MODES) 924 if (++line_mode == LINE_MODES)
765 line_mode = 0; 925 line_mode = 0;
926 if (line_mode == REFLOW)
927 if (++line_mode == LINE_MODES)
928 line_mode = 0;
929 }
766 930
767#ifdef HAVE_LCD_BITMAP 931#ifdef HAVE_LCD_BITMAP
768 init_need_scrollbar(); 932 init_need_scrollbar();
@@ -778,10 +942,10 @@ enum plugin_status plugin_start(struct plugin_api* api, void* file)
778 942
779 case VIEWER_MODE_WIDTH: 943 case VIEWER_MODE_WIDTH:
780 /* View-width mode: NARROW or WIDE */ 944 /* View-width mode: NARROW or WIDE */
781 if (line_mode == JOIN) 945 if ((line_mode == JOIN) || (line_mode == REFLOW))
782 rb->splash(HZ, true, "(no %s %s)", 946 rb->splash(HZ, true, "(no %s %s)",
783 view_mode_str[WIDE], 947 view_mode_str[WIDE],
784 line_mode_str[JOIN]); 948 line_mode_str[line_mode]);
785 else 949 else
786 if (++view_mode == VIEW_MODES) 950 if (++view_mode == VIEW_MODES)
787 view_mode = 0; 951 view_mode = 0;
@@ -839,7 +1003,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* file)
839 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT: 1003 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
840 if (view_mode == WIDE) { 1004 if (view_mode == WIDE) {
841 /* Screen left */ 1005 /* Screen left */
842 col -= display_columns; 1006 col -= draw_columns/glyph_width['o'];
843 col = col_limit(col); 1007 col = col_limit(col);
844 } 1008 }
845 else { /* view_mode == NARROW */ 1009 else { /* view_mode == NARROW */
@@ -854,7 +1018,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* file)
854 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT: 1018 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
855 if (view_mode == WIDE) { 1019 if (view_mode == WIDE) {
856 /* Screen right */ 1020 /* Screen right */
857 col += display_columns; 1021 col += draw_columns/glyph_width['o'];
858 col = col_limit(col); 1022 col = col_limit(col);
859 } 1023 }
860 else { /* view_mode == NARROW */ 1024 else { /* view_mode == NARROW */
diff --git a/docs/CREDITS b/docs/CREDITS
index 3aeb7bba5c..61350b8db2 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -110,3 +110,4 @@ Tomas Salfischberger
110Miika Pekkarinen 110Miika Pekkarinen
111Tapio Karppinen 111Tapio Karppinen
112Richard Ottó O'Brien 112Richard Ottó O'Brien
113Luca Burelli