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

Contents of /sargasso2/sargasso.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 83 - (show annotations) (download) (as text)
Mon Jul 6 01:59:16 2009 UTC (15 years, 5 months ago) by james
File MIME type: text/x-csrc
File size: 32378 byte(s)
Move rufl_init after wimp_initialise so that multi-tasking scanning is used. Fix LOG macro.

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

  ViewVC Help
Powered by ViewVC 1.1.26