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

Contents of /sargasso2/sargasso.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 81 - (show annotations) (download) (as text)
Sun Nov 26 16:57:32 2006 UTC (17 years, 4 months ago) by james
File MIME type: text/x-csrc
File size: 32300 byte(s)
Create Choices.Sargasso directory.

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

  ViewVC Help
Powered by ViewVC 1.1.26