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

Contents of /sargasso2/sargasso.c

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /*
2 * This file is part of Sargasso, http://zamez.org/sargasso
3 * Licensed under the GNU General Public License,
4 * http://www.opensource.org/licenses/gpl-license
5 * Copyright 2006 James Bursa <james@zamez.org>
6 */
7
8 #include <assert.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <oslib/colourtrans.h>
14 #include <oslib/osfile.h>
15 #include <oslib/uri.h>
16 #include <oslib/wimp.h>
17 #include <oslib/wimpspriteop.h>
18 #include <rufl.h>
19 #include "feed.h"
20 #include "netsurf/utils/log.h"
21
22
23 #define MAX_LINES 20
24 #define MARGIN 10
25 #define FEEDS_READ "Choices:Sargasso.Feeds"
26 #define FEEDS_WRITE "<Choices$Write>.Sargasso.Feeds"
27 #define CHOICES_READ "Choices:Sargasso.Choices"
28 #define CHOICES_WRITE "<Choices$Write>.Sargasso.Choices"
29
30 typedef void (*click_callback)(unsigned int i);
31
32 struct paragraph {
33 int x0, y0, x1, y1;
34 int background, colour;
35 const char *font_family;
36 rufl_style font_style;
37 unsigned int font_size;
38 click_callback click;
39 unsigned int click_i;
40 unsigned int lines;
41 const char *text[MAX_LINES + 1];
42 struct paragraph *next;
43 };
44
45 bool quit = false;
46 int interval = 30 * 60;
47 os_t last_update = 0;
48
49 wimp_t task;
50 wimp_w info_window, main_window, feed_window, add_feed_window,
51 warning_window = 0, choices_window;
52 osspriteop_area *sprites;
53 struct paragraph *main_window_paragraphs = 0;
54 struct paragraph *feed_window_paragraphs = 0;
55 unsigned int current_feed = 0;
56
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