/[james]/sargasso2/sargasso.c
ViewVC logotype

Contents of /sargasso2/sargasso.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 79 - (show annotations) (download) (as text)
Sat Sep 30 21:35:19 2006 UTC (18 years, 2 months ago) by james
File MIME type: text/x-csrc
File size: 32203 byte(s)
Make fonts configurable.

1 /*
2 * This file is part of Sargasso, http://zamez.org/sargasso
3 * Licensed under the GNU General Public License,
4 * http://www.opensource.org/licenses/gpl-license
5 * Copyright 2006 James Bursa <james@zamez.org>
6 */
7
8 #include <assert.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <oslib/colourtrans.h>
14 #include <oslib/osfile.h>
15 #include <oslib/uri.h>
16 #include <oslib/wimp.h>
17 #include <oslib/wimpspriteop.h>
18 #include <rufl.h>
19 #include "feed.h"
20 #include "netsurf/utils/log.h"
21
22
23 #define MAX_LINES 20
24 #define MARGIN 10
25 #define FEEDS_READ "Choices:Sargasso.Feeds"
26 #define FEEDS_WRITE "<Choices$Write>.Sargasso.Feeds"
27 #define CHOICES_READ "Choices:Sargasso.Choices"
28 #define CHOICES_WRITE "<Choices$Write>.Sargasso.Choices"
29
30 typedef void (*click_callback)(unsigned int i);
31
32 struct paragraph {
33 int x0, y0, x1, y1;
34 int background, colour;
35 const char *font_family;
36 rufl_style font_style;
37 unsigned int font_size;
38 click_callback click;
39 unsigned int click_i;
40 unsigned int lines;
41 const char *text[MAX_LINES + 1];
42 struct paragraph *next;
43 };
44
45 bool quit = false;
46 int interval = 30 * 60;
47 os_t last_update = 0;
48
49 wimp_t task;
50 wimp_w info_window, main_window, feed_window, add_feed_window,
51 warning_window = 0, choices_window;
52 osspriteop_area *sprites;
53 struct paragraph *main_window_paragraphs = 0;
54 struct paragraph *feed_window_paragraphs = 0;
55 unsigned int current_feed = 0;
56 char font_headings[100] = "Homerton";
57 char font_summaries[100] = "NewHall";
58 char font_links[100] = "Homerton";
59
60 #define ICON_FLAGS (wimp_ICON_TEXT | \
61 (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT))
62 wimp_MENU(4) iconbar_menu = { { "Sargasso" }, wimp_COLOUR_BLACK,
63 wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_BLACK, wimp_COLOUR_WHITE,
64 200, 44, 0,
65 { { 0, 0, ICON_FLAGS, { "Info" } },
66 { 0, 0, ICON_FLAGS, { "Choices..." } },
67 { 0, 0, ICON_FLAGS, { "Update feeds" } },
68 { wimp_MENU_LAST, 0, ICON_FLAGS, { "Quit" } } } };
69 wimp_MENU(2) main_menu = { { "Sargasso" }, wimp_COLOUR_BLACK,
70 wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_BLACK, wimp_COLOUR_WHITE,
71 200, 44, 0,
72 { { 0, 0, ICON_FLAGS, { "Add feed..." } },
73 { wimp_MENU_LAST, 0, ICON_FLAGS, { "Remove feed" } } } };
74 wimp_menu *current_menu;
75 unsigned int current_removing = 0;
76 wimp_i current_font_menu;
77
78 const char *default_feeds[] = {
79 "http://news.google.co.uk/?output=rss",
80 "http://www.ft.com/rss/home/uk",
81 "http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml",
82 "http://rss.cnn.com/rss/edition.rss",
83 "http://feeds.chicagotribune.com/chicagotribune/news/",
84 "http://www.drobe.co.uk/rss.php",
85 "http://www.theregister.co.uk/feeds/latest.rdf",
86 "http://www.snackspot.org.uk/rss/rss.xml",
87 "http://www.mode7games.com/blog/?feed=rss2",
88 "http://cia.navi.cx/stats/project/NetSurf/.rss",
89 };
90
91 void gui_init(void);
92 void gui_quit(void);
93 wimp_w create_window(const char *name);
94 void gui_poll(void);
95 void mouse_click(wimp_w w, wimp_i i, int x, int y,
96 wimp_mouse_state buttons);
97 void set_pointer(const char *name, int x, int y);
98 void reset_pointer(void);
99 void key_pressed(wimp_key *key);
100 void menu_selection(wimp_selection *selection);
101 void open_window(wimp_w w);
102 void close_window(wimp_w w);
103 void open_menu(wimp_menu *menu, int x, int y);
104 void update_main_window(void);
105 void set_extent(wimp_w w, int y);
106 void click_main_window(unsigned int i);
107 void update_feed_window(unsigned int i);
108 void click_feed_link(unsigned int i);
109 void click_item_link(unsigned int j);
110 struct paragraph *add_paragraph(struct paragraph **paragraph_list,
111 int x0, int y0, int x1, int background, int colour,
112 const char *font_family, rufl_style font_style,
113 unsigned int font_size, const char *text,
114 click_callback click, unsigned int click_i);
115 void free_paragraph_list(struct paragraph **paragraph_list);
116 void redraw_window(wimp_draw *redraw, struct paragraph *paragraphs);
117 void fill_rectangle(int x0, int y0, int x1, int y1, int colour);
118 void choices_save(void);
119 void choices_load(void);
120 void die(const char *error);
121 void warn(const char *warning);
122 char *get_icon_string(wimp_w w, wimp_i i);
123 void set_icon_string(wimp_w w, wimp_i i, const char *text);
124
125
126 int main(int argc, char *argv[])
127 {
128 char error[200];
129 struct stat s;
130
131 /* memdebug_memdebug("memdump"); */
132
133 if (!feed_init())
134 die(feed_error);
135
136 interval = (30 + time(0) % 20) * 60;
137 choices_load();
138 gui_init();
139
140 if (stat(FEEDS_READ, &s)) {
141 warn("Welcome to Sargasso! A selection of feeds have been "
142 "added. To add more feeds, use the main menu.");
143 for (unsigned int i = 0; i != sizeof default_feeds /
144 sizeof default_feeds[0]; i++)
145 feed_add(default_feeds[i]);
146 feed_list_save(FEEDS_WRITE);
147 } else if (!feed_list_load(FEEDS_READ)) {
148 snprintf(error, sizeof error, "Unable to load feed list: %s",
149 feed_error);
150 warn(error);
151 }
152
153 update_main_window();
154 open_window(main_window);
155
156 last_update = os_read_monotonic_time();
157
158 while (!quit)
159 gui_poll();
160
161 gui_quit();
162 feed_quit();
163
164 return 0;
165 }
166
167
168 void gui_init(void)
169 {
170 rufl_code code;
171 const wimp_MESSAGE_LIST(1) messages = { { message_QUIT } };
172 int size;
173 fileswitch_object_type obj_type;
174 wimp_icon_create icon = {
175 wimp_ICON_BAR_RIGHT,
176 { { 0, 0, 68, 68 },
177 wimp_ICON_SPRITE | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
178 (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT),
179 { "!sargasso" } } };
180 os_error *error;
181
182 code = rufl_init();
183 if (code != rufl_OK) {
184 LOG(("rufl_init: %i", code));
185 die("Failed to initialise Unicode font library");
186 }
187
188 error = xwimp_initialise(wimp_VERSION_RO3, "Sargasso",
189 (const wimp_message_list *) &messages, 0, &task);
190 if (error) {
191 LOG(("xwimp_initialise: 0x%x: %s",
192 error->errnum, error->errmess));
193 die(error->errmess);
194 }
195
196 error = xosfile_read_stamped_no_path("<Sargasso$Dir>.Sprites",
197 &obj_type, 0, 0, &size, 0, 0);
198 if (error) {
199 LOG(("xosfile_read_stamped_no_path: 0x%x: %s",
200 error->errnum, error->errmess));
201 die(error->errmess);
202 }
203 if (obj_type != fileswitch_IS_FILE)
204 die("Sprites file missing");
205 sprites = malloc(size + 4);
206 if (!sprites)
207 die("Out of memory");
208 sprites->size = size + 4;
209 sprites->sprite_count = 0;
210 sprites->first = 16;
211 sprites->used = 16;
212 error = xosspriteop_load_sprite_file(osspriteop_USER_AREA,
213 sprites, "<Sargasso$Dir>.Sprites");
214 if (error) {
215 LOG(("xosspriteop_load_sprite_file: 0x%x: %s",
216 error->errnum, error->errmess));
217 die(error->errmess);
218 }
219
220 error = xwimp_create_icon(&icon, 0);
221 if (error) {
222 LOG(("xwimp_create_icon: 0x%x: %s",
223 error->errnum, error->errmess));
224 die(error->errmess);
225 }
226
227 error = xwimp_open_template("<Sargasso$Dir>.Templates");
228 if (error) {
229 LOG(("xwimp_open_template: 0x%x: %s",
230 error->errnum, error->errmess));
231 die(error->errmess);
232 }
233
234 info_window = create_window("info");
235 main_window = create_window("main");
236 feed_window = create_window("main");
237 add_feed_window = create_window("add_feed");
238 warning_window = create_window("warning");
239 choices_window = create_window("choices");
240
241 iconbar_menu.entries[0].sub_menu = (wimp_menu *) info_window;
242
243 error = xwimp_close_template();
244 if (error) {
245 LOG(("xwimp_close_template: 0x%x: %s",
246 error->errnum, error->errmess));
247 die(error->errmess);
248 }
249 }
250
251
252 void gui_quit(void)
253 {
254 free_paragraph_list(&main_window_paragraphs);
255 free_paragraph_list(&feed_window_paragraphs);
256 rufl_quit();
257 }
258
259
260 wimp_w create_window(const char *name)
261 {
262 char name_buf[12];
263 int window_size;
264 int data_size;
265 wimp_window *window;
266 char *data;
267 wimp_w w;
268 os_error *error;
269
270 assert(strlen(name) < 12);
271
272 strncpy(name_buf, name, sizeof name_buf);
273
274 error = xwimp_load_template(wimp_GET_SIZE, 0, 0, wimp_NO_FONTS,
275 name_buf, 0, &window_size, &data_size, 0);
276 if (error) {
277 LOG(("xwimp_load_template: 0x%x: %s",
278 error->errnum, error->errmess));
279 die(error->errmess);
280 }
281
282 window = malloc(window_size);
283 data = malloc(data_size);
284 if (!window || !data)
285 die("Out of memory");
286
287 error = xwimp_load_template(window, data, data + data_size,
288 wimp_NO_FONTS, name_buf, 0, 0, 0, 0);
289 if (error) {
290 LOG(("xwimp_load_template: 0x%x: %s",
291 error->errnum, error->errmess));
292 die(error->errmess);
293 }
294
295 error = xwimp_create_window(window, &w);
296 if (error) {
297 LOG(("xwimp_create_window: 0x%x: %s",
298 error->errnum, error->errmess));
299 die(error->errmess);
300 }
301
302 return w;
303 }
304
305
306 void gui_poll(void)
307 {
308 static bool in_window = false;
309 os_t t;
310 wimp_block block;
311 wimp_event_no event;
312 wimp_pointer pointer;
313 osbool more;
314 os_error *error;
315
316 t = os_read_monotonic_time();
317
318 error = xwimp_poll_idle(0,
319 &block,
320 feed_work_needed || in_window ? t + 5 : t + 100,
321 0, &event);
322 if (error) {
323 LOG(("xwimp_poll: 0x%x: %s", error->errnum, error->errmess));
324 warn(error->errmess);
325 }
326
327 switch (event) {
328 case wimp_NULL_REASON_CODE:
329 if (feed_work_needed) {
330 if (feed_work()) {
331 update_main_window();
332 if (feed_count && feeds[current_feed].updated)
333 update_feed_window(current_feed);
334 }
335 }
336
337 t = os_read_monotonic_time();
338 if (last_update + interval * 100 <= t) {
339 LOG(("updating"));
340 last_update = t;
341 feed_update();
342 }
343
344 error = xwimp_get_pointer_info(&pointer);
345 if (error) {
346 LOG(("xwimp_get_pointer_info: 0x%x: %s",
347 error->errnum, error->errmess));
348 warn(error->errmess);
349 }
350 mouse_click(pointer.w, pointer.i,
351 pointer.pos.x, pointer.pos.y, 0);
352 break;
353
354 case wimp_REDRAW_WINDOW_REQUEST:
355 error = xwimp_redraw_window(&block.redraw, &more);
356 if (error) {
357 LOG(("xwimp_redraw_window: 0x%x: %s",
358 error->errnum, error->errmess));
359 break;
360 }
361 while (more) {
362 if (block.redraw.w == main_window)
363 redraw_window(&block.redraw,
364 main_window_paragraphs);
365 else if (block.redraw.w == feed_window)
366 redraw_window(&block.redraw,
367 feed_window_paragraphs);
368 error = xwimp_get_rectangle(&block.redraw, &more);
369 if (error) {
370 LOG(("xwimp_get_rectangle: 0x%x: %s",
371 error->errnum, error->errmess));
372 break;
373 }
374 }
375 break;
376
377 case wimp_OPEN_WINDOW_REQUEST:
378 error = xwimp_open_window(&block.open);
379 if (error) {
380 LOG(("xwimp_open_window: 0x%x: %s",
381 error->errnum, error->errmess));
382 warn(error->errmess);
383 }
384 break;
385
386 case wimp_CLOSE_WINDOW_REQUEST:
387 error = xwimp_close_window(block.close.w);
388 if (error) {
389 LOG(("xwimp_close_window: 0x%x: %s",
390 error->errnum, error->errmess));
391 warn(error->errmess);
392 }
393 break;
394
395 case wimp_POINTER_LEAVING_WINDOW:
396 reset_pointer();
397 in_window = false;
398 break;
399
400 case wimp_POINTER_ENTERING_WINDOW:
401 in_window = true;
402 break;
403
404 case wimp_MOUSE_CLICK:
405 mouse_click(block.pointer.w, block.pointer.i,
406 block.pointer.pos.x, block.pointer.pos.y,
407 block.pointer.buttons);
408 break;
409
410 case wimp_KEY_PRESSED:
411 key_pressed(&block.key);
412 break;
413
414 case wimp_MENU_SELECTION:
415 menu_selection(&block.selection);
416 break;
417
418 case wimp_USER_MESSAGE:
419 case wimp_USER_MESSAGE_RECORDED:
420 switch (block.message.action) {
421 case message_QUIT:
422 quit = true;
423 break;
424 }
425 break;
426 }
427 }
428
429
430 void mouse_click(wimp_w w, wimp_i i, int x, int y,
431 wimp_mouse_state buttons)
432 {
433 wimp_window_state state;
434 int wx, wy;
435 struct paragraph *p = 0;
436 char minutes[10];
437 int mins;
438 os_error *error;
439
440 if (w == wimp_ICON_BAR) {
441 if (buttons == wimp_CLICK_MENU)
442 open_menu((wimp_menu *) &iconbar_menu,
443 x - 64, 96 + 44 * 4);
444 else if (buttons == wimp_CLICK_SELECT ||
445 buttons == wimp_CLICK_ADJUST)
446 open_window(main_window);
447
448 } else if (w == main_window || w == feed_window) {
449 state.w = w;
450 error = xwimp_get_window_state(&state);
451 if (error) {
452 LOG(("xwimp_get_window_state: 0x%x: %s",
453 error->errnum, error->errmess));
454 warn(error->errmess);
455 }
456 wx = x - (state.visible.x0 - state.xscroll);
457 wy = -(y - (state.visible.y1 - state.yscroll));
458 if (w == main_window)
459 p = main_window_paragraphs;
460 else
461 p = feed_window_paragraphs;
462 for (; p; p = p->next) {
463 if (p->x0 <= wx && wx <= p->x1 &&
464 p->y0 <= wy && wy <= p->y1 &&
465 p->click)
466 break;
467 }
468 if (p && buttons == wimp_CLICK_SELECT)
469 p->click(p->click_i);
470 else if (buttons == wimp_CLICK_MENU &&
471 w == main_window) {
472 if (p)
473 main_menu.entries[1].icon_flags &=
474 ~wimp_ICON_SHADED;
475 else
476 main_menu.entries[1].icon_flags |=
477 wimp_ICON_SHADED;
478 open_menu((wimp_menu *) &main_menu, x - 64, y);
479 current_removing = p ? p->click_i : 0;
480 } else if (buttons == 0) {
481 if (p)
482 set_pointer("ptr_point", 6, 0);
483 else
484 reset_pointer();
485 }
486
487 } else if (w == add_feed_window && buttons) {
488 if (i == 1) {
489 feed_add(get_icon_string(add_feed_window, 0));
490 update_main_window();
491 if (!feed_list_save(FEEDS_WRITE))
492 warn(feed_error);
493 if (buttons == wimp_CLICK_SELECT)
494 close_window(add_feed_window);
495 } else if (i == 2) {
496 close_window(add_feed_window);
497 }
498
499 } else if (w == warning_window && buttons) {
500 if (i == 1)
501 close_window(warning_window);
502
503 } else if (w == choices_window && buttons) {
504 if (i == 6) {
505 mins = atoi(get_icon_string(choices_window, 1));
506 if (mins < 15) {
507 mins = 15;
508 warn("To avoid overloading feed providers, "
509 "the minimum update interval "
510 "is 15 minutes.");
511 }
512 interval = mins * 60;
513 choices_save();
514 if (buttons == wimp_CLICK_SELECT)
515 close_window(choices_window);
516 } else if (i == 5) {
517 close_window(choices_window);
518 } else if (i == 2 || i == 3) {
519 mins = atoi(get_icon_string(choices_window, 1));
520 if ((i == 2 && buttons == wimp_CLICK_SELECT) ||
521 (i == 3 && buttons == wimp_CLICK_ADJUST))
522 mins -= 3;
523 else
524 mins += 3;
525 if (mins < 15)
526 mins = 15;
527 else if (999 < mins)
528 mins = 999;
529 snprintf(minutes, sizeof minutes, "%i", mins);
530 set_icon_string(choices_window, 1, minutes);
531 xwimp_set_caret_position(choices_window, 1,
532 0, 0, -1, strlen(minutes));
533 } else if (i == 7 || i == 8 ||
534 i == 10 || i == 11 ||
535 i == 13 || i == 14) {
536 if (i == 8 || i == 11 || i == 14)
537 i--;
538 wimp_window_state state;
539 wimp_icon_state icon_state;
540 state.w = w;
541 xwimp_get_window_state(&state);
542 icon_state.w = w;
543 icon_state.i = i;
544 xwimp_get_icon_state(&icon_state);
545 open_menu((wimp_menu *) rufl_family_menu,
546 state.visible.x0 + icon_state.icon.extent.x1,
547 state.visible.y1 + icon_state.icon.extent.y1);
548 current_font_menu = i;
549 }
550 }
551 }
552
553
554 void set_pointer(const char *name, int x, int y)
555 {
556 os_error *error;
557 error = xosspriteop_set_pointer_shape(osspriteop_USER_AREA, sprites,
558 (osspriteop_id) name, 1, x, y, 0, 0);
559 if (error) {
560 LOG(("xosspriteop_set_pointer_shape: 0x%x: %s",
561 error->errnum, error->errmess));
562 warn(error->errmess);
563 }
564 }
565
566
567 void reset_pointer(void)
568 {
569 os_error *error;
570 error = xwimpspriteop_set_pointer_shape("ptr_default", 1, 0, 0, 0, 0);
571 if (error) {
572 LOG(("xwimpspriteop_set_pointer_shape: 0x%x: %s",
573 error->errnum, error->errmess));
574 warn(error->errmess);
575 }
576 }
577
578
579 void key_pressed(wimp_key *key)
580 {
581 os_error *error;
582
583 if (key->w == add_feed_window) {
584 if (key->c == wimp_KEY_ESCAPE) {
585 mouse_click(key->w, 2, 0, 0, wimp_CLICK_SELECT);
586 return;
587 } else if (key->c == wimp_KEY_RETURN) {
588 mouse_click(key->w, 1, 0, 0, wimp_CLICK_SELECT);
589 return;
590 }
591
592 } else if (key->w == choices_window) {
593 if (key->c == wimp_KEY_ESCAPE) {
594 mouse_click(key->w, 5, 0, 0, wimp_CLICK_SELECT);
595 return;
596 } else if (key->c == wimp_KEY_RETURN) {
597 mouse_click(key->w, 6, 0, 0, wimp_CLICK_SELECT);
598 return;
599 } else if (key->c == wimp_KEY_DOWN) {
600 mouse_click(key->w, 2, 0, 0, wimp_CLICK_SELECT);
601 return;
602 } else if (key->c == wimp_KEY_UP) {
603 mouse_click(key->w, 3, 0, 0, wimp_CLICK_SELECT);
604 return;
605 }
606
607 } else if (key->w == warning_window) {
608 if (key->c == wimp_KEY_ESCAPE ||
609 key->c == wimp_KEY_RETURN) {
610 close_window(warning_window);
611 }
612 }
613
614 error = xwimp_process_key(key->c);
615 if (error) {
616 LOG(("xwimp_process_key: 0x%x: %s",
617 error->errnum, error->errmess));
618 warn(error->errmess);
619 }
620 }
621
622
623 void menu_selection(wimp_selection *selection)
624 {
625 char minutes[10];
626 wimp_pointer pointer;
627
628 xwimp_get_pointer_info(&pointer);
629
630 if (current_menu == (wimp_menu *) &iconbar_menu) {
631 switch (selection->items[0]) {
632 case 1:
633 snprintf(minutes, sizeof minutes, "%i", interval / 60);
634 set_icon_string(choices_window, 1, minutes);
635 set_icon_string(choices_window, 8, font_headings);
636 set_icon_string(choices_window, 11, font_summaries);
637 set_icon_string(choices_window, 14, font_links);
638 open_window(choices_window);
639 xwimp_set_caret_position(choices_window, 1,
640 0, 0, -1, strlen(minutes));
641 break;
642 case 2:
643 last_update = os_read_monotonic_time();
644 feed_update();
645 break;
646 case 3:
647 quit = true;
648 break;
649 }
650
651 } else if (current_menu == (wimp_menu *) &main_menu) {
652 switch (selection->items[0]) {
653 case 0:
654 set_icon_string(add_feed_window, 0, "http://");
655 open_window(add_feed_window);
656 xwimp_set_caret_position(add_feed_window, 0,
657 0, 0, -1, 7);
658 break;
659 case 1:
660 feed_remove(current_removing);
661 update_main_window();
662 close_window(feed_window);
663 if (!feed_list_save(FEEDS_WRITE))
664 warn(feed_error);
665 break;
666 }
667 } else if (current_menu == (wimp_menu *) rufl_family_menu) {
668 char *font = ((wimp_menu *) rufl_family_menu)->
669 entries[selection->items[0]].data.indirected_text.text;
670 if (current_font_menu == 7)
671 strncpy(font_headings, font, sizeof font_headings);
672 else if (current_font_menu == 10)
673 strncpy(font_summaries, font, sizeof font_summaries);
674 else if (current_font_menu == 13)
675 strncpy(font_links, font, sizeof font_links);
676 set_icon_string(choices_window, 8, font_headings);
677 set_icon_string(choices_window, 11, font_summaries);
678 set_icon_string(choices_window, 14, font_links);
679 update_main_window();
680 if (feed_count)
681 update_feed_window(current_feed);
682 }
683
684 if (pointer.buttons == wimp_CLICK_ADJUST)
685 xwimp_create_menu(current_menu, pointer.pos.x, pointer.pos.y);
686 }
687
688
689 void open_window(wimp_w w)
690 {
691 const os_VDU_VAR_LIST(5) vars = { { os_MODEVAR_XWIND_LIMIT,
692 os_MODEVAR_YWIND_LIMIT, os_MODEVAR_XEIG_FACTOR,
693 os_MODEVAR_YEIG_FACTOR, os_VDUVAR_END_LIST } };
694 int vals[4];
695 int width, height;
696 wimp_window_state state;
697 int dx, dy;
698 os_error *error;
699
700 error = xos_read_vdu_variables((const os_vdu_var_list *) &vars, vals);
701 if (error) {
702 LOG(("xos_read_vdu_variables: 0x%x: %s",
703 error->errnum, error->errmess));
704 warn(error->errmess);
705 return;
706 }
707 width = (vals[0] + 1) << vals[2];
708 height = (vals[1] + 1) << vals[3];
709
710 state.w = w;
711 error = xwimp_get_window_state(&state);
712 if (error) {
713 LOG(("xwimp_get_window_state: 0x%x: %s",
714 error->errnum, error->errmess));
715 warn(error->errmess);
716 return;
717 }
718
719 dx = (state.visible.x1 - state.visible.x0) / 2;
720 dy = (state.visible.y1 - state.visible.y0) / 2;
721 state.visible.x0 = width / 2 - dx;
722 state.visible.y0 = height / 2 - dy;
723 state.visible.x1 = width / 2 + dx;
724 state.visible.y1 = height / 2 + dy;
725 state.xscroll = 0;
726 state.yscroll = 0;
727 state.next = wimp_TOP;
728
729 error = xwimp_open_window((wimp_open *) &state);
730 if (error) {
731 LOG(("xwimp_open_window: 0x%x: %s",
732 error->errnum, error->errmess));
733 warn(error->errmess);
734 }
735 }
736
737
738 void close_window(wimp_w w)
739 {
740 os_error *error;
741
742 error = xwimp_close_window(w);
743 if (error) {
744 LOG(("xwimp_close_window: 0x%x: %s",
745 error->errnum, error->errmess));
746 warn(error->errmess);
747 }
748 }
749
750
751 void open_menu(wimp_menu *menu, int x, int y)
752 {
753 os_error *error;
754
755 error = xwimp_create_menu(menu, x, y);
756 if (error) {
757 LOG(("xwimp_create_menu: 0x%x: %s",
758 error->errnum, error->errmess));
759 warn(error->errmess);
760 }
761 current_menu = menu;
762 }
763
764
765 void update_main_window(void)
766 {
767 static char *status = 0;
768 int y = 0;
769 int y1;
770 unsigned int i;
771 unsigned int j;
772 unsigned int new_items;
773 rufl_style style;
774 struct paragraph *p;
775
776 free_paragraph_list(&main_window_paragraphs);
777 free(status);
778
779 status = malloc(feed_count * 40);
780 if (!status) {
781 LOG(("out of memory"));
782 return;
783 }
784
785 for (i = 0; i != feed_count; i++) {
786 new_items = 0;
787 for (j = 0; j != feeds[i].item_count; j++)
788 if (feeds[i].item[j].new_item)
789 new_items++;
790 style = new_items ? rufl_WEIGHT_900 : rufl_WEIGHT_400;
791
792 p = add_paragraph(&main_window_paragraphs, 0, y, 700,
793 0xffaa99, 0x000000,
794 font_headings, style, 200,
795 feeds[i].title ? (char *) feeds[i].title :
796 feeds[i].url,
797 click_main_window, i);
798 y1 = p->y1;
799
800 switch (feeds[i].status) {
801 case FEED_NEW:
802 case FEED_FETCHING:
803 case FEED_UPDATE:
804 snprintf(status + i * 40, 40, "Fetching");
805 break;
806 case FEED_OK:
807 if (new_items)
808 snprintf(status + i * 40, 40,
809 "%i items (%i new)",
810 feeds[i].item_count,
811 new_items);
812 else
813 snprintf(status + i * 40, 40,
814 "%i items",
815 feeds[i].item_count);
816 break;
817 case FEED_ERROR:
818 snprintf(status + i * 40, 40, "Failed");
819 break;
820 }
821 p = add_paragraph(&main_window_paragraphs, 700, y, 1000,
822 0xffaa99, 0x000000,
823 font_headings, style, 200,
824 status + i * 40,
825 click_main_window, i);
826 y = p->y1 = y1;
827
828 if (feeds[i].error) {
829 p = add_paragraph(&main_window_paragraphs, 0, y, 1000,
830 0, 0x0000a0,
831 font_summaries, style, 200,
832 feeds[i].error,
833 click_main_window, i);
834 y = p->y1;
835 }
836
837 if (feeds[i].description) {
838 p = add_paragraph(&main_window_paragraphs, 0, y, 1000,
839 0, 0x000000,
840 font_summaries, rufl_WEIGHT_400, 200,
841 feeds[i].description,
842 click_main_window, i);
843 y = p->y1;
844 }
845
846 y += MARGIN;
847 }
848
849 set_extent(main_window, y);
850 }
851
852
853 void set_extent(wimp_w w, int y)
854 {
855 os_box extent = { 0, 0, 1000, 0 };
856 wimp_window_state state;
857 os_error *error;
858
859 extent.y0 = -y;
860 error = xwimp_set_extent(w, &extent);
861 if (error) {
862 LOG(("xwimp_set_extent: 0x%x: %s",
863 error->errnum, error->errmess));
864 warn(error->errmess);
865 }
866
867 state.w = w;
868 error = xwimp_get_window_state(&state);
869 if (error) {
870 LOG(("xwimp_get_window_state: 0x%x: %s",
871 error->errnum, error->errmess));
872 warn(error->errmess);
873 }
874
875 if (state.flags & wimp_WINDOW_OPEN) {
876 error = xwimp_close_window(w);
877 if (error) {
878 LOG(("xwimp_close_window: 0x%x: %s",
879 error->errnum, error->errmess));
880 warn(error->errmess);
881 }
882
883 /*state.visible.y0 = 100;
884 state.visible.y1 = 100 + y;*/
885 error = xwimp_open_window((wimp_open *) &state);
886 if (error) {
887 LOG(("xwimp_open_window: 0x%x: %s",
888 error->errnum, error->errmess));
889 warn(error->errmess);
890 }
891 }
892 }
893
894
895 void click_main_window(unsigned int i)
896 {
897 unsigned int j;
898
899 update_feed_window(i);
900 open_window(feed_window);
901
902 for (j = 0; j != feeds[i].item_count; j++)
903 feeds[i].item[j].new_item = false;
904 update_main_window();
905 }
906
907
908 void update_feed_window(unsigned int i)
909 {
910 int y = 0;
911 int y1;
912 unsigned int j;
913 struct paragraph *p;
914 rufl_style style;
915
916 current_feed = i;
917
918 free_paragraph_list(&feed_window_paragraphs);
919
920 p = add_paragraph(&feed_window_paragraphs, 0, y, 1000,
921 0xffaa99, 0x000000,
922 font_headings, rufl_WEIGHT_400, 200,
923 feeds[i].title ? (char *) feeds[i].title : feeds[i].url,
924 0, 0);
925 y = p->y1;
926
927 if (feeds[i].link) {
928 p = add_paragraph(&feed_window_paragraphs, 0, y, 1000,
929 0, 0xff0000,
930 font_links, rufl_WEIGHT_400, 160,
931 feeds[i].link,
932 click_feed_link, i);
933 y = p->y1;
934 }
935 if (feeds[i].pub_date) {
936 p = add_paragraph(&feed_window_paragraphs, 0, y, 1000,
937 0, 0x0060ee,
938 font_summaries, rufl_WEIGHT_400 | rufl_SLANTED,
939 160,
940 feeds[i].pub_date,
941 0, 0);
942 y = p->y1;
943 }
944 if (feeds[i].description) {
945 p = add_paragraph(&feed_window_paragraphs, 0, y, 1000,
946 0, 0x000000,
947 font_summaries, rufl_WEIGHT_400, 200,
948 feeds[i].description,
949 0, 0);
950 y = p->y1;
951 }
952 if (feeds[i].copyright) {
953 p = add_paragraph(&feed_window_paragraphs, 0, y, 1000,
954 0, 0x666666,
955 font_summaries, rufl_WEIGHT_400 | rufl_SLANTED, 160,
956 feeds[i].copyright,
957 0, 0);
958 y = p->y1;
959 }
960
961 y += MARGIN;
962
963 for (j = 0; j != feeds[i].item_count; j++) {
964 style = feeds[i].item[j].new_item ? rufl_WEIGHT_900 :
965 rufl_WEIGHT_400;
966
967 p = add_paragraph(&feed_window_paragraphs, 0, y, 1000,
968 0xa0dddd, 0x000000,
969 font_headings, style, 200,
970 feeds[i].item[j].title ?
971 (char *) feeds[i].item[j].title : "",
972 0, j);
973 y1 = y = p->y1;
974
975 if (feeds[i].item[j].pub_date) {
976 p = add_paragraph(&feed_window_paragraphs, 0, y, 420,
977 0, 0x0060ee,
978 font_summaries,
979 rufl_WEIGHT_400 | rufl_SLANTED, 160,
980 feeds[i].item[j].pub_date,
981 0, j);
982 if (y1 < p->y1)
983 y1 = p->y1;
984 }
985 if (feeds[i].item[j].author) {
986 p = add_paragraph(&feed_window_paragraphs, 420, y, 720,
987 0, 0x666666,
988 font_summaries,
989 rufl_WEIGHT_400 | rufl_SLANTED, 160,
990 feeds[i].item[j].author,
991 0, j);
992 if (y1 < p->y1)
993 y1 = p->y1;
994 }
995 if (feeds[i].item[j].category) {
996 p = add_paragraph(&feed_window_paragraphs, 720, y, 1000,
997 0, 0xee6000,
998 font_summaries, rufl_WEIGHT_400, 160,
999 feeds[i].item[j].category,
1000 0, j);
1001 if (y1 < p->y1)
1002 y1 = p->y1;
1003 }
1004 y = y1;
1005
1006 if (feeds[i].item[j].link) {
1007 p = add_paragraph(&feed_window_paragraphs, 0, y, 1000,
1008 0, 0xff0000,
1009 font_links, rufl_WEIGHT_400, 160,
1010 feeds[i].item[j].link,
1011 click_item_link, j);
1012 y = p->y1;
1013 }
1014
1015 if (feeds[i].item[j].description) {
1016 p = add_paragraph(&feed_window_paragraphs, 0, y, 1000,
1017 0, 0x000000,
1018 font_summaries, rufl_WEIGHT_400, 180,
1019 feeds[i].item[j].description,
1020 0, j);
1021 y = p->y1;
1022 }
1023
1024 y += MARGIN;
1025 }
1026
1027 set_extent(feed_window, y);
1028 }
1029
1030
1031 void click_feed_link(unsigned int i)
1032 {
1033 os_error *error;
1034 error = xuri_dispatch(0, feeds[i].link, task, 0, 0, 0);
1035 if (error) {
1036 LOG(("xuri_dispatch: 0x%x: %s",
1037 error->errnum, error->errmess));
1038 }
1039 }
1040
1041
1042 void click_item_link(unsigned int j)
1043 {
1044 os_error *error;
1045 error = xuri_dispatch(0, feeds[current_feed].item[j].link, task,
1046 0, 0, 0);
1047 if (error) {
1048 LOG(("xuri_dispatch: 0x%x: %s",
1049 error->errnum, error->errmess));
1050 }
1051 }
1052
1053
1054 struct paragraph *add_paragraph(struct paragraph **paragraph_list,
1055 int x0, int y0, int x1, int background, int colour,
1056 const char *font_family, rufl_style font_style,
1057 unsigned int font_size, const char *text,
1058 click_callback click, unsigned int click_i)
1059 {
1060 struct paragraph *p;
1061 const char *t;
1062 size_t len = strlen(text);
1063 unsigned int lines = 0;
1064 size_t char_offset;
1065 int actual_x;
1066 rufl_code code;
1067
1068 p = malloc(sizeof *p);
1069 if (!p)
1070 return 0;
1071
1072 p->x0 = x0;
1073 p->y0 = y0;
1074 p->x1 = x1;
1075 p->background = background;
1076 p->colour = colour;
1077 p->font_family = font_family;
1078 p->font_style = font_style;
1079 p->font_size = font_size;
1080 p->click = click;
1081 p->click_i = click_i;
1082 p->lines = 0;
1083 p->text[0] = text;
1084
1085 t = text;
1086 while (len && lines != MAX_LINES) {
1087 code = rufl_split(font_family, font_style, font_size, t, len,
1088 x1 - x0 - MARGIN - MARGIN,
1089 &char_offset, &actual_x);
1090 if (code != rufl_OK) {
1091 LOG(("rufl_split: %i", code));
1092 break;
1093 }
1094 if (char_offset != len) {
1095 size_t space;
1096 for (space = char_offset; space && t[space] != ' ';
1097 space--)
1098 continue;
1099 if (space)
1100 char_offset = space + 1;
1101 }
1102 len -= char_offset;
1103 t += char_offset;
1104 p->text[++lines] = t;
1105 }
1106
1107 p->y1 = p->y0 + MARGIN + lines * font_size * 0.2 + MARGIN;
1108 p->lines = lines;
1109
1110 p->next = *paragraph_list;
1111 *paragraph_list = p;
1112
1113 return p;
1114 }
1115
1116
1117 void free_paragraph_list(struct paragraph **paragraph_list)
1118 {
1119 struct paragraph *p = *paragraph_list;
1120 struct paragraph *next;
1121
1122 while (p) {
1123 next = p->next;
1124 free(p);
1125 p = next;
1126 }
1127
1128 *paragraph_list = 0;
1129 }
1130
1131
1132 void redraw_window(wimp_draw *redraw, struct paragraph *paragraphs)
1133 {
1134 int ox = redraw->box.x0 - redraw->xscroll;
1135 int oy = redraw->box.y1 - redraw->yscroll;
1136 int clip_x0 = redraw->clip.x0 - ox;
1137 int clip_y0 = oy - redraw->clip.y1;
1138 int clip_x1 = redraw->clip.x1 - ox;
1139 int clip_y1 = oy - redraw->clip.y0;
1140 struct paragraph *p;
1141 unsigned int i;
1142 os_error *error;
1143 rufl_code code;
1144
1145 for (p = paragraphs; p; p = p->next) {
1146 if (p->x1 < clip_x0 || clip_x1 < p->x0 ||
1147 p->y1 < clip_y0 || clip_y1 < p->y0)
1148 continue;
1149 if (p->background)
1150 fill_rectangle(ox + p->x0, oy - p->y1,
1151 ox + p->x1, oy - p->y0,
1152 p->background);
1153 for (i = 0; i != p->lines; i++) {
1154 error = xcolourtrans_set_font_colours(font_CURRENT,
1155 p->background << 8, p->colour << 8,
1156 14, 0, 0, 0);
1157 if (error) {
1158 LOG(("xcolourtrans_set_font_colours: 0x%x: %s",
1159 error->errnum, error->errmess));
1160 return;
1161 }
1162 code = rufl_paint(p->font_family, p->font_style,
1163 p->font_size, p->text[i],
1164 p->text[i + 1] - p->text[i],
1165 ox + p->x0 + MARGIN,
1166 oy - p->y0 - MARGIN -
1167 i * p->font_size * 0.2 -
1168 p->font_size * 0.15,
1169 rufl_BLEND_FONT);
1170 if (code != rufl_OK) {
1171 LOG(("rufl_paint: %i", code));
1172 return;
1173 }
1174 }
1175 }
1176 }
1177
1178
1179 void fill_rectangle(int x0, int y0, int x1, int y1, int colour)
1180 {
1181 os_error *error;
1182
1183 error = xcolourtrans_set_gcol(colour << 8, colourtrans_USE_ECFS,
1184 os_ACTION_OVERWRITE, 0, 0);
1185 if (error) {
1186 LOG(("xcolourtrans_set_gcol: 0x%x: %s",
1187 error->errnum, error->errmess));
1188 return;
1189 }
1190
1191 error = xos_plot(os_MOVE_TO, x0, y0);
1192 if (error) {
1193 LOG(("xos_plot: 0x%x: %s", error->errnum, error->errmess));
1194 return;
1195 }
1196
1197 error = xos_plot(os_PLOT_RECTANGLE | os_PLOT_TO, x1, y1);
1198 if (error) {
1199 LOG(("xos_plot: 0x%x: %s", error->errnum, error->errmess));
1200 return;
1201 }
1202 }
1203
1204
1205 void choices_save(void)
1206 {
1207 FILE *choices;
1208 char error[200];
1209
1210 choices = fopen(CHOICES_WRITE, "w");
1211 if (!choices) {
1212 LOG(("fopen: %i: %s", errno, strerror(errno)));
1213 snprintf(error, sizeof error, "Unable to save choices: %s",
1214 strerror(errno));
1215 warn(error);
1216 return;
1217 }
1218 fprintf(choices, "interval: %i\n", interval);
1219 fprintf(choices, "font_headings: %s\n", font_headings);
1220 fprintf(choices, "font_summaries: %s\n", font_summaries);
1221 fprintf(choices, "font_links: %s\n", font_links);
1222 fclose(choices);
1223 }
1224
1225
1226 void choices_load(void)
1227 {
1228 FILE *choices;
1229 char error[200];
1230 char s[100];
1231
1232 choices = fopen(CHOICES_READ, "r");
1233 if (!choices) {
1234 LOG(("fopen: %i: %s", errno, strerror(errno)));
1235 if (errno != ENOENT) {
1236 snprintf(error, sizeof error,
1237 "Unable to read choices: %s",
1238 strerror(errno));
1239 warn(error);
1240 }
1241 return;
1242 }
1243 while (fgets(s, sizeof s, choices)) {
1244 sscanf(s, "interval: %i", &interval);
1245 sscanf(s, "font_headings: %s", font_headings);
1246 sscanf(s, "font_summaries: %s", font_summaries);
1247 sscanf(s, "font_links: %s", font_links);
1248 }
1249 fclose(choices);
1250 }
1251
1252
1253 /**
1254 * Display an error and exit.
1255 */
1256
1257 void die(const char *error)
1258 {
1259 os_error warn_error;
1260
1261 warn_error.errnum = 1;
1262 strncpy(warn_error.errmess, error, sizeof warn_error.errmess - 1);
1263 warn_error.errmess[sizeof warn_error.errmess - 1] = '\0';
1264 xwimp_report_error_by_category(&warn_error,
1265 wimp_ERROR_BOX_OK_ICON |
1266 wimp_ERROR_BOX_GIVEN_CATEGORY |
1267 wimp_ERROR_BOX_CATEGORY_ERROR <<
1268 wimp_ERROR_BOX_CATEGORY_SHIFT,
1269 "Sargasso", "!sargasso",
1270 (osspriteop_area *) 1, 0, 0);
1271 exit(EXIT_FAILURE);
1272 }
1273
1274
1275 void warn(const char *warning)
1276 {
1277 os_error error;
1278
1279 LOG(("%s", warning));
1280
1281 if (warning_window) {
1282 set_icon_string(warning_window, 0, warning);
1283 open_window(warning_window);
1284 xwimp_set_caret_position(warning_window, wimp_ICON_WINDOW,
1285 -100, -100, 1, 0);
1286 xos_bell();
1287 } else {
1288 strncpy(error.errmess, warning, sizeof error.errmess);
1289 error.errmess[sizeof error.errmess - 1] = 0;
1290 xwimp_report_error_by_category(&error,
1291 wimp_ERROR_BOX_OK_ICON |
1292 wimp_ERROR_BOX_GIVEN_CATEGORY |
1293 wimp_ERROR_BOX_CATEGORY_ERROR <<
1294 wimp_ERROR_BOX_CATEGORY_SHIFT,
1295 "Sargasso", "!sargasso",
1296 (osspriteop_area *) 1, 0, 0);
1297 }
1298 }
1299
1300
1301 char *get_icon_string(wimp_w w, wimp_i i)
1302 {
1303 wimp_icon_state icon_state = {w, i};
1304 os_error *error;
1305
1306 error = xwimp_get_icon_state(&icon_state);
1307 if (error) {
1308 LOG(("xwimp_get_icon_state: 0x%x: %s",
1309 error->errnum, error->errmess));
1310 return "";
1311 }
1312
1313 return icon_state.icon.data.indirected_text.text;
1314 }
1315
1316
1317 void set_icon_string(wimp_w w, wimp_i i, const char *text)
1318 {
1319 wimp_icon_state icon_state = {w, i};
1320 unsigned int size;
1321 os_error *error;
1322
1323 error = xwimp_get_icon_state(&icon_state);
1324 if (error) {
1325 LOG(("xwimp_get_icon_state: 0x%x: %s",
1326 error->errnum, error->errmess));
1327 return;
1328 }
1329 size = (unsigned int) icon_state.icon.data.indirected_text.size;
1330
1331 if (!strncmp(icon_state.icon.data.indirected_text.text, text, size))
1332 return;
1333
1334 strncpy(icon_state.icon.data.indirected_text.text, text, size);
1335 icon_state.icon.data.indirected_text.text[size - 1] = '\0';
1336
1337 xwimp_set_icon_state(w, i, 0, 0);
1338 }

  ViewVC Help
Powered by ViewVC 1.1.26