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

Annotation of /sargasso2/sargasso.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 77 - (hide annotations) (download) (as text)
Tue Sep 26 21:31:28 2006 UTC (17 years, 7 months ago) by james
File MIME type: text/x-csrc
File size: 30196 byte(s)
Rewrite of Sargasso in C.

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

  ViewVC Help
Powered by ViewVC 1.1.26