naev 0.10.4
news.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include <stdint.h>
11#include <stdlib.h>
12
13#include "naev.h"
16#include "news.h"
17
18#include "array.h"
19#include "faction.h"
20#include "log.h"
21#include "nlua.h"
22#include "nlua_diff.h"
23#include "nlua_faction.h"
24#include "nlua_misn.h"
25#include "nlua_var.h"
26#include "nluadef.h"
27#include "nstring.h"
28#include "ntime.h"
29#include "nxml.h"
30#include "nxml_lua.h"
31#include "space.h"
32#include "toolkit.h"
33
34#define NEWS_MAX_LENGTH 8192
36/*
37 * News stack.
38 */
40static int next_id = 0;
45static char buf[NEWS_MAX_LENGTH];
46static int len;
47
48static unsigned int news_tick = 0;
49static int news_drag = 0;
50static double news_pos = 0.;
52static char **news_lines = NULL;
54static double textlength = 0.;
55
59static int largestID;
60
61/*
62 * Prototypes
63 */
64static void news_render( double bx, double by, double w, double h, void *data );
65static void news_focusLose( unsigned int wid, const char* wgtname );
66static int news_mouse( unsigned int wid, SDL_Event *event, double mx, double my,
67 double w, double h, double rx, double ry, void *data );
68static int news_parseArticle( xmlNodePtr parent );
69int news_saveArticles( xmlTextWriterPtr writer ); /* externed in save.c */
70int news_loadArticles( xmlNodePtr parent ); /* externed in load.c */
71static void clear_newslines (void);
72
73static int news_cmp( const void *p1, const void *p2 )
74{
75 const news_t *n1, *n2;
76 int diff;
77 n1 = (const news_t*) p1;
78 n2 = (const news_t*) p2;
79 diff = n1->priority - n2->priority;
80 if (diff != 0)
81 return diff;
82 if (n1->date < n2->date)
83 return +1;
84 else if (n1->date > n2->date)
85 return -1;
86 return n1->id - n2->id;
87}
88
100int news_add( const char *title, const char *content,
101 const char *faction, const char *tag,
102 ntime_t date, ntime_t date_to_rm, int priority )
103{
104 news_t *n;
105 int id = ++next_id;
106
107 if (news_list==NULL)
109 n = &array_grow( &news_list );
110 memset( n, 0, sizeof(news_t) );
111 n->id = id;
112 n->title = strdup( title );
113 n->desc = strdup( content );
114 n->faction = strdup( faction );
115 if (tag != NULL)
116 n->tag = strdup( tag );
117 n->date = date;
118 n->date_to_rm = date_to_rm;
119 n->priority = priority;
120
121 /* Sort it! */
122 qsort( news_list, array_size(news_list), sizeof(news_t), news_cmp );
123
124 return id;
125}
126
130int news_init (void)
131{
132 /* init news list with dummy article */
133 if (news_list != NULL)
134 news_exit();
135
137 news_lines = array_create( char* );
139
140 return 0;
141}
142
146void news_exit (void)
147{
148 if (news_list == NULL)
149 return;
150
151 for (int i=0; i<array_size(news_list); i++) {
152 news_t *n = &news_list[i];
153
154 free(n->faction);
155 free(n->title);
156 free(n->desc);
157 free(n->tag);
158 }
160 news_list = NULL;
161
162 for (int i=0; i<array_size(news_lines); i++)
163 free(news_lines[i]);
166 news_lines = NULL;
167 news_restores = NULL;
168 textlength = 0;
169
170 news_list = NULL;
171}
172
176news_t* news_get( int id )
177{
178 for (int i=0; i<array_size(news_list); i++) {
179 news_t *n = &news_list[i];
180 if (n->id == id)
181 return n;
182 }
183 return NULL;
184}
185
186void news_free( news_t *n )
187{
188 free( n->title );
189 free( n->desc );
190 free( n->faction );
191 free( n->tag );
192}
193
194void news_rm( int id )
195{
196 news_t *n = news_get( id );
197 if (n==NULL)
198 return;
199 news_free( n );
200 array_erase( &news_list, &n[0], &n[1] );
201}
202
209int *generate_news( int faction )
210{
211 const char* fname;
212 ntime_t curtime = ntime_get();
213 int p = 0;
214 const char **tags;
215
216 fname = (faction >= 0) ? faction_name( faction ) : NULL;
217
218 /* First pass to remove old articles. */
219 for (int i=array_size(news_list)-1; i>=0; i--) {
220 news_t *n = &news_list[i];
221
222 /* if the article is due for removal */
223 if (n->date_to_rm <= curtime)
224 news_rm( n->id );
225 }
226
227 /* Put all acceptable news into buf */
228 tags = (faction >= 0) ? faction_tags( faction ) : NULL;
229 for (int i=0; i<array_size(news_list); i++) {
230 news_t *n = &news_list[i];
231 int match_tag = 0;
232
233 /* Check to see if matches tag. */
234 if (tags != NULL) {
235 for (int j=0; j<array_size(tags); j++) {
236 if (strcasecmp( tags[j], n->faction)==0) {
237 match_tag = 1;
238 break;
239 }
240 }
241 }
242
243 /* if article is okay */
244 if (match_tag || ((fname != NULL) && (strcasecmp(n->faction, fname) == 0))) {
245 if (n->date && (n->date != 0)) {
246 char *article_time = ntime_pretty( n->date, 1 );
247 p += scnprintf( buf+p, NEWS_MAX_LENGTH-p,
248 " %s \n"
249 "%s: %s#0\n\n", n->title, article_time, n->desc );
250 free( article_time );
251 }
252 else {
253 p += scnprintf( buf+p, NEWS_MAX_LENGTH-p,
254 " %s \n"
255 "%s#0\n\n", n->title, n->desc );
256 }
257 }
258 }
259
260 if (p == 0)
261 p = scnprintf(buf, NEWS_MAX_LENGTH, "\n\n%s\n\n\n", _("No news is available."));
262
263 len = MIN( p, NEWS_MAX_LENGTH );
264
265 return 0;
266}
267
277void news_widget( unsigned int wid, int x, int y, int w, int h )
278{
280
281 /* Safe defaults. */
282 news_pos = h/3;
283 news_tick = SDL_GetTicks();
284
285 clear_newslines();
286
287 /* Now load up the text. */
288 gl_printLineIteratorInit( &iter, NULL, buf, w-40 );
289
290 while (gl_printLineIteratorNext( &iter )) {
291 /* Copy the line. */
292 array_push_back( &news_lines, strndup( &buf[iter.l_begin], iter.l_end - iter.l_begin ) );
293 if (array_size( news_restores ) == 0)
295 else {
298 array_push_back( &news_restores, restore );
299 }
300 }
301
302 /* Create the custom widget. */
303 window_addCust( wid, x, y, w, h, "cstNews", 1, news_render, news_mouse, NULL, news_focusLose, NULL );
304 window_canFocusWidget( wid, "cstNews", 0 );
305}
306
307/* clears newslines for bar text, for when taking off */
308void clear_newslines (void)
309{
310 for (int i=0; i<array_size(news_lines); i++)
311 free(news_lines[i]);
312
315}
316
320static void news_focusLose( unsigned int wid, const char* wgtname )
321{
322 (void) wid;
323 (void) wgtname;
324 news_drag = 0;
325}
326
340static int news_mouse( unsigned int wid, SDL_Event *event, double mx, double my,
341 double w, double h, double rx, double ry, void *data )
342{
343 (void) data;
344 (void) rx;
345
346 switch (event->type) {
347 case SDL_MOUSEWHEEL:
348 /* Must be in bounds. */
349 if ((mx < 0.) || (mx > w) || (my < 0.) || (my > h))
350 return 0;
351
352 if (event->wheel.y > 0)
353 news_pos -= h/3.;
354 else if (event->wheel.y < 0)
355 news_pos += h/3.;
356 return 1;
357
358 case SDL_MOUSEBUTTONDOWN:
359 /* Must be in bounds. */
360 if ((mx < 0.) || (mx > w) || (my < 0.) || (my > h))
361 return 0;
362 window_setFocus( wid, "cstNews" );
363
364 news_drag = 1;
365 return 1;
366
367 case SDL_MOUSEBUTTONUP:
368 news_drag = 0;
369 break;
370
371 case SDL_MOUSEMOTION:
372 if (news_drag)
373 news_pos -= ry;
374 break;
375 }
376
377 return 0;
378}
379
389static void news_render( double bx, double by, double w, double h, void *data )
390{
391 (void) data;
392 int s, m, p;
393 unsigned int t;
394 double y, dt;
395
396 t = SDL_GetTicks();
397
398 /* Calculate offset. */
399 if (!news_drag) {
400 dt = (double)(t-news_tick)/1000.;
401 news_pos += dt * 25.;
402 }
403 news_tick = t;
404
405 /* Make sure user isn't silly and drags it to negative values. */
406 if (news_pos < 0.)
407 news_pos = 0.;
408
409 /* background */
410 gl_renderRect( bx, by, w, h, &cBlack );
411
412 /* Render the text. */
413 p = (int)ceil( news_pos / (news_font->h + 5.));
414 m = (int)ceil( h / (news_font->h + 5.));
415 if (p > array_size( news_lines ) + m + 1) {
416 news_pos = 0.;
417 return;
418 }
419
420 /* Get positions to make sure inbound. */
421 s = MAX(0, p - m);
422 p = MIN(p + 1, array_size( news_lines ) - 1);
423
424 /* Get start position. */
425 y = news_pos - s * (news_font->h+5.);
426
427 /* Draw loop. */
428 for (int i=s; i<p; i++) {
431 bx+10, by+y, &cFontGreen, -1., news_lines[i] );
432
433 /* Increment line and position. */
434 y -= news_font->h + 5.;
435 }
436}
437
438/*
439 * @brief saves all current articles
440 * @return 0 on success
441 */
442int news_saveArticles( xmlTextWriterPtr writer )
443{
444 xmlw_startElem(writer, "news");
445
446 for (int i=0; i<array_size(news_list); i++) {
447 const char *ntitle, *ndesc;
448 news_t *n = &news_list[i];
449
450 if (n->title == NULL || n->desc==NULL || n->faction == NULL)
451 continue;
452
453 xmlw_startElem(writer, "article");
454
455 ntitle = n->title;
456 ndesc = n->desc;
457
458 xmlw_attr(writer, "title", "%s", ntitle);
459 xmlw_attr(writer, "desc", "%s", ndesc);
460 xmlw_attr(writer, "faction", "%s", n->faction);
461 xmlw_attr(writer, "date", "%"PRIi64, n->date);
462 xmlw_attr(writer, "date_to_rm", "%"PRIi64, n->date_to_rm);
463 xmlw_attr(writer, "id", "%i", n->id);
464 xmlw_attr(writer, "priority", "%i", n->priority);
465
466 if (n->tag != NULL)
467 xmlw_attr(writer, "tag", "%s", n->tag);
468
469 xmlw_endElem(writer); /* "article" */
470 }
471
472 xmlw_endElem(writer); /* "news" */
473
474 return 0;
475}
476
483int news_loadArticles( xmlNodePtr parent )
484{
485 news_tick = 0;
486
487 xmlNodePtr node;
488
489 largestID = 0;
490
491 news_exit();
492 news_init();
493
494 /* Get and parse news/articles */
495 node = parent->xmlChildrenNode;
496 do {
497 if (xml_isNode(node, "news"))
498 if (news_parseArticle( node ) < 0)
499 return -1;
500 } while (xml_nextNode(node));
501
503
504 return 0;
505}
506
513static int news_parseArticle( xmlNodePtr parent )
514{
515 xmlNodePtr node;
516
517 node = parent->xmlChildrenNode;
518
519#define NEWS_READ(elem, s) \
520xmlr_attr_strd(node, s, elem); \
521if (elem == NULL) { WARN(_("Event is missing '%s', skipping."), s); goto cleanup; }
522
523 do {
524 char *title, *desc, *faction, *tag, *buff;
525 int priority;
526 ntime_t date, date_to_rm;
527
528 if (!xml_isNode(node, "article"))
529 continue;
530
531 /* Reset parameters. */
532 title = NULL;
533 desc = NULL;
534 faction = NULL;
535
536 NEWS_READ(title, "title");
537 NEWS_READ(desc, "desc");
538 NEWS_READ(faction, "faction");
539
540 NEWS_READ(buff, "date");
541 date = atoll(buff);
542 free(buff);
543
544 NEWS_READ(buff, "date_to_rm");
545 date_to_rm = atoll(buff);
546 free(buff);
547
548 NEWS_READ(buff, "id");
549 next_id = atoi(buff);
550 free(buff);
551
552 /* Older versions won't have priority. */
553 xmlr_attr_strd( node, "priority", buff );
554 priority = (buff==NULL) ? 5 : atoi(buff);
555 free(buff);
556
557 /* Read optional tag. */
558 tag = NULL;
559 xmlr_attr_strd( node, "tag", tag );
560
562
563 /* make the article*/
564 news_add( title, desc, faction, tag, date, date_to_rm, priority );
565 free( tag );
566
567cleanup:
568 free(title);
569 free(desc);
570 free(faction);
571 } while (xml_nextNode(node));
572#undef NEWS_READ
573
574 return 0;
575}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition: array.h:158
#define array_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
Definition: array.h:112
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition: array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition: array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition: array.h:119
#define array_back(ptr_array)
Returns the last element in the array.
Definition: array.h:216
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition: array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition: array.h:93
const char * faction_name(int f)
Gets a factions "real" (internal) name.
Definition: faction.c:304
const char ** faction_tags(int f)
Gets the tags the faction has.
Definition: faction.c:412
void gl_printRestoreInit(glFontRestore *restore)
Initializes a restore structure.
Definition: font.c:395
int gl_printLineIteratorNext(glPrintLineIterator *iter)
Updates iter with the next line's information.
Definition: font.c:525
glFont gl_defFont
Definition: font.c:153
int gl_printMidRaw(const glFont *ft_font, int width, double x, double y, const glColour *c, double outlineR, const char *text)
Displays text centered in position and width.
Definition: font.c:787
void gl_printRestore(const glFontRestore *restore)
Restores last colour from a restore structure.
Definition: font.c:404
void gl_printLineIteratorInit(glPrintLineIterator *iter, const glFont *ft_font, const char *text, int width)
Initialize an iterator object for breaking text into lines.
Definition: font.c:505
void gl_printStore(glFontRestore *restore, const char *text)
Stores the colour information from a piece of text.
Definition: font.c:441
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition: naev.h:40
#define MAX(x, y)
Definition: naev.h:39
static glFont * news_font
Definition: news.c:51
news_t * news_list
Definition: news.c:39
static int next_id
Definition: news.c:40
static void news_focusLose(unsigned int wid, const char *wgtname)
Called when it's de-focused.
Definition: news.c:320
static char ** news_lines
Definition: news.c:52
int news_loadArticles(xmlNodePtr parent)
Loads the player's active articles from a save, initilizes news.
Definition: news.c:483
int news_add(const char *title, const char *content, const char *faction, const char *tag, ntime_t date, ntime_t date_to_rm, int priority)
makes a new article and puts it into the list
Definition: news.c:100
news_t * news_get(int id)
gets the article with id ID, else NULL
Definition: news.c:176
static void news_render(double bx, double by, double w, double h, void *data)
Renders a news widget.
Definition: news.c:389
static glFontRestore * news_restores
Definition: news.c:53
static double news_pos
Definition: news.c:50
static unsigned int news_tick
Definition: news.c:48
void news_exit(void)
Kills the old news thread.
Definition: news.c:146
int * generate_news(int faction)
Generates news from newslist from specific faction AND Generic news.
Definition: news.c:209
#define NEWS_MAX_LENGTH
Definition: news.c:34
static int largestID
Definition: news.c:59
void news_widget(unsigned int wid, int x, int y, int w, int h)
Creates a news widget.
Definition: news.c:277
int news_init(void)
Initiate news linked list with a stack.
Definition: news.c:130
static int news_mouse(unsigned int wid, SDL_Event *event, double mx, double my, double w, double h, double rx, double ry, void *data)
News widget mouse event handler.
Definition: news.c:340
static int news_drag
Definition: news.c:49
static int news_parseArticle(xmlNodePtr parent)
Parses articles.
Definition: news.c:513
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
Definition: nstring.c:178
char * strndup(const char *s, size_t n)
Return a pointer to a new string, which is a duplicate of the string s (or, if necessary,...
Definition: nstring.c:94
ntime_t ntime_get(void)
Gets the current time.
Definition: ntime.c:108
char * ntime_pretty(ntime_t t, int d)
Gets the time in a pretty human readable format.
Definition: ntime.c:173
void gl_renderRect(double x, double y, double w, double h, const glColour *c)
Renders a rectangle.
Definition: opengl_render.c:90
Evil hack to allow restoring, yes it makes me cry myself to sleep.
Definition: font.h:27
Represents a font in memory.
Definition: font.h:16
int h
Definition: font.h:18
The state of a line iteration. This matches the process of rendering text into an on-screen box: An e...
Definition: font.h:40
size_t l_end
Definition: font.h:45
Represents a news article.
Definition: news.h:16
int priority
Definition: news.h:18
int id
Definition: news.h:17
ntime_t date
Definition: news.h:25
void window_setFocus(unsigned int wid, const char *wgtname)
Sets the focused widget in a window.
Definition: toolkit.c:2460
void window_canFocusWidget(unsigned int wid, const char *name, int canfocus)
Allows or disallows focusing a widget.
Definition: toolkit.c:523