1 /*
2  * Copyright 2013 Ole Loots <ole@monochrom.net>
3  *
4  * This file is part of NetSurf, http://www.netsurf-browser.org/
5  *
6  * NetSurf is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * NetSurf is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Module Description:
19  *
20  * AES Object tree tools.
21  *
22  */
23 
24 #include <assert.h>
25 #include "gemtk.h"
26 
gemtk_obj_get_text(OBJECT * tree,short idx)27 char *gemtk_obj_get_text(OBJECT * tree, short idx)
28 {
29     static char p[]="";
30 
31     switch (tree[idx].ob_type & 0x00FF) {
32     case G_BUTTON:
33     case G_STRING:
34     case G_TITLE:
35         return( tree[idx].ob_spec.free_string);
36     case G_TEXT:
37     case G_BOXTEXT:
38     case G_FTEXT:
39     case G_FBOXTEXT:
40         return (tree[idx].ob_spec.tedinfo->te_ptext);
41     case G_ICON:
42     case G_CICON:
43         return (tree[idx].ob_spec.iconblk->ib_ptext);
44         break;
45 
46     default:
47         break;
48     }
49     return (p);
50 }
51 
52 /*
53 static void set_text(OBJECT *obj, short idx, char * text, int len)
54 {
55     char spare[255];
56 
57     if( len > 254 )
58         len = 254;
59     if( text != NULL ) {
60         strncpy(spare, text, 254);
61     } else {
62         strcpy(spare, "");
63     }
64 
65     set_string(obj, idx, spare);
66 }
67 */
68 
gemtk_obj_set_str_safe(OBJECT * tree,short idx,const char * txt)69 char gemtk_obj_set_str_safe(OBJECT * tree, short idx, const char *txt)
70 {
71     char spare[204];
72     short type = 0;
73     short maxlen = 0;
74 
75 
76     type = (tree[idx].ob_type & 0xFF);
77     if (type == G_FTEXT || type == G_FBOXTEXT) {
78         TEDINFO *ted = ((TEDINFO *)get_obspec(tree, idx));
79         maxlen = ted->te_tmplen+1;
80         if (maxlen > 200) {
81             maxlen = 200;
82         } else if (maxlen < 0) {
83             maxlen = 0;
84         }
85     } else {
86         assert((type == G_FTEXT) || (type == G_FBOXTEXT));
87     }
88 
89     snprintf(spare, maxlen, "%s", txt);
90     set_string(tree, idx, spare);
91 
92     return(0);
93 }
94 
gemtk_obj_get_tree(int idx)95 OBJECT *gemtk_obj_get_tree(int idx)
96 {
97 
98     OBJECT *tree;
99 
100     rsrc_gaddr(R_TREE, idx, &tree);
101 
102     return tree;
103 }
104 
gemtk_obj_is_inside(OBJECT * tree,short obj,GRECT * area)105 bool gemtk_obj_is_inside(OBJECT * tree, short obj, GRECT *area)
106 {
107     GRECT obj_screen;
108     bool ret = false;
109 
110     objc_offset(tree, obj, &obj_screen.g_x, &obj_screen.g_y);
111     obj_screen.g_w = tree[obj].ob_width;
112     obj_screen.g_h = tree[obj].ob_height;
113 
114     ret = RC_WITHIN(&obj_screen, area);
115 
116     return(ret);
117 }
118 
gemtk_obj_screen_rect(OBJECT * tree,short obj)119 GRECT * gemtk_obj_screen_rect(OBJECT * tree, short obj)
120 {
121     static GRECT obj_screen;
122 
123     get_objframe(tree, obj, &obj_screen);
124 
125     return(&obj_screen);
126 }
127 
128 
gemtk_obj_mouse_sprite(OBJECT * tree,int index)129 void gemtk_obj_mouse_sprite(OBJECT *tree, int index)
130 {
131     MFORM mform;
132     int dum;
133 
134     if ((tree[index].ob_type & 0xFF) != G_ICON)
135         return;
136 
137     dum = tree[index].ob_spec.iconblk->ib_char;
138     mform . mf_nplanes = 1;
139     mform . mf_fg = (dum>>8)&0x0F;
140     mform . mf_bg = dum>>12;
141     mform . mf_xhot = 0; /* to prevent the mform to "jump" on the */
142     mform . mf_yhot = 0; /* screen (zebulon rules!) */
143 
144     for( dum = 0; dum<16; dum ++) {
145         mform . mf_mask[dum] = tree[index].ob_spec.iconblk->ib_pmask[dum];
146         mform . mf_data[dum] = tree[index].ob_spec.iconblk->ib_pdata[dum];
147     }
148     graf_mouse(USER_DEF, &mform);
149 }
150 
151 
152 /*
153  * gemtk_obj_tree_copy
154  *
155  * Copy a complete object-tree including all substructures (optional).
156  *
157  * CAUTION: The object-tree *must* have the LASTOB-flag (0x20) set in
158  * it's physically last member.
159  *
160  * BUG: Up to now tree_copy won't copy the color-icon-structure,
161  * because I'm too lazy ;) Maybe I'll do that one day. If you need it
162  * urgently, contact me and force me to work... Btw, this doesn't mean
163  * that G_CICONs won't be copied at all, but the copied tree will
164  * share the CICONBLKs with the original.
165  *
166  * Input:
167  * tree: Pointer to tree which should be copied
168  * what: Specifies what substructures should be copied, too (see the
169  *       C_xxx-definitions in tree-copy.h for details)
170  *
171  * Output:
172  * NULL: Tree couldn't be copied (due to lack of memory)
173  * otherwise: Pointer to copied tree, use free to dealloc it's memory
174  */
gemtk_obj_tree_copy(OBJECT * tree)175 OBJECT *gemtk_obj_tree_copy(OBJECT *tree)
176 {
177     int16_t	i, objects;
178     size_t	to_malloc, size;
179     OBJECT	*new_tree;
180     char	*area;
181 
182     /* Calculate the number of bytes we need for the new tree */
183     to_malloc = (size_t) 0;
184     for (i = 0;;) {
185 
186         /* Size of the OBJECT-structure itself */
187         to_malloc += sizeof(OBJECT);
188 
189         switch (tree[i].ob_type & 0xff)	{
190         case G_TEXT:
191         case G_BOXTEXT:
192         case G_FTEXT:
193         case G_FBOXTEXT:
194             /* Size of a TEDINFO-structure */
195             to_malloc += sizeof(TEDINFO);
196 
197             /* Sizes of the strings in the TEDINFO-structure */
198             to_malloc += (size_t)tree[i].ob_spec.tedinfo->te_txtlen;
199             to_malloc += (size_t)tree[i].ob_spec.tedinfo->te_txtlen;
200             to_malloc += (size_t)tree[i].ob_spec.tedinfo->te_tmplen;
201             break;
202 
203         case G_IMAGE:
204 
205 			/* Size of the BITBLK-structure */
206 			to_malloc += sizeof(BITBLK);
207 
208 			/* Size of the image-data in the BITBLK-structure */
209 			to_malloc += (size_t)((int32_t)tree[i].ob_spec.bitblk->bi_wb *
210                     (int32_t)tree[i].ob_spec.bitblk->bi_hl);
211 
212             break;
213 
214         case G_USERDEF:
215                 /* Size of the USERBLK-structure */
216                 to_malloc += sizeof(USERBLK);
217             break;
218 
219         case G_BUTTON:
220         case G_STRING:
221         case G_TITLE:
222 			/* Size of the string (with one null character at the end) */
223 			to_malloc += strlen(tree[i].ob_spec.free_string) + 1L;
224             break;
225 
226         case G_ICON:
227 			/* Size of the ICONBLK-structure */
228             to_malloc += sizeof(BITBLK);
229 
230             /* Sizes of icon-data, icon-mask and icon-text */
231             to_malloc += (size_t)((int32_t)tree[i].ob_spec.iconblk->ib_wicon *
232 									(int32_t)tree[i].ob_spec.iconblk->ib_hicon /
233 									4L + 1L +
234 									(int32_t)strlen(tree[i].ob_spec.iconblk->ib_ptext));
235 
236             break;
237         }
238 
239         /* If the size is odd, make it even */
240         if ((long)to_malloc & 1)
241             to_malloc++;
242 
243         /* Exit if we've reached the last object in the tree */
244         if (tree[i].ob_flags & OF_LASTOB)
245             break;
246 
247         i++;
248     }
249 
250     objects = i + 1;
251 
252     /* If there's not enough memory left for the new tree, return NULL */
253     if ((new_tree = (OBJECT *)calloc(1, to_malloc)) == NULL) {
254         return(NULL);
255     }
256 
257     /*
258      * area contains a pointer to the area where we copy the structures to
259      */
260     area = (char *)((int32_t)new_tree+(int32_t)objects*(int32_t)sizeof(OBJECT));
261 
262     for (i = 0; i < objects; i++) {
263 
264         /* Copy the contents of the OBJECT-structure */
265         new_tree[i] = tree[i];
266 
267         /* This was added to assure true copies of the object type */
268         new_tree[i].ob_type = tree[i].ob_type;
269 
270         switch (tree[i].ob_type & 0xff) {
271         case G_TEXT:
272         case G_BOXTEXT:
273         case G_FTEXT:
274         case G_FBOXTEXT:
275 
276 			/* Copy the contents of the TEDINFO-structure */
277 			*(TEDINFO *)area = *tree[i].ob_spec.tedinfo;
278 			new_tree[i].ob_spec.tedinfo = (TEDINFO *)area;
279 			area += sizeof(TEDINFO);
280 
281 			/* Copy the strings in the TEDINFO-structure */
282 			strncpy(area, tree[i].ob_spec.tedinfo->te_ptext,
283 					tree[i].ob_spec.tedinfo->te_txtlen);
284 			new_tree[i].ob_spec.tedinfo->te_ptext = area;
285 			area += tree[i].ob_spec.tedinfo->te_txtlen;
286 			strncpy(area, tree[i].ob_spec.tedinfo->te_ptmplt,
287 					tree[i].ob_spec.tedinfo->te_tmplen);
288 			new_tree[i].ob_spec.tedinfo->te_ptmplt = area;
289 			area += tree[i].ob_spec.tedinfo->te_tmplen;
290 			strncpy(area, tree[i].ob_spec.tedinfo->te_pvalid,
291 					tree[i].ob_spec.tedinfo->te_txtlen);
292 			new_tree[i].ob_spec.tedinfo->te_pvalid = area;
293 			area += tree[i].ob_spec.tedinfo->te_txtlen;
294 
295             break;
296 
297         case G_IMAGE:
298 
299 			/* Copy the contents of the BITBLK-structure */
300 			*(BITBLK *)area = *tree[i].ob_spec.bitblk;
301 			new_tree[i].ob_spec.bitblk = (BITBLK *)area;
302 			area += sizeof(BITBLK);
303 
304 			/* Copy the image-data */
305 			size = (size_t)((int32_t)tree[i].ob_spec.bitblk->bi_wb *
306 							(int32_t)tree[i].ob_spec.bitblk->bi_hl);
307 			memcpy(area, tree[i].ob_spec.bitblk->bi_pdata, size);
308 			new_tree[i].ob_spec.bitblk->bi_pdata = (int16_t *)area;
309 			area += size;
310 
311             break;
312 
313         case G_USERDEF:
314 
315 			/* Copy the contents of the USERBLK-structure */
316 			*(USERBLK *)area = *tree[i].ob_spec.userblk;
317 			new_tree[i].ob_spec.userblk = (USERBLK *)area;
318 			area += sizeof(USERBLK);
319 
320             break;
321 
322         case G_BUTTON:
323         case G_STRING:
324         case G_TITLE:
325 
326 			/* Copy the string */
327 			size = strlen(tree[i].ob_spec.free_string) + 1L;
328 			strcpy(area, tree[i].ob_spec.free_string);
329 			new_tree[i].ob_spec.free_string = area;
330 			area += size;
331 
332             break;
333 
334         case G_ICON:
335 
336 			/* Copy the contents of the ICONBLK-structure */
337 			*(ICONBLK *)area = *tree[i].ob_spec.iconblk;
338 			new_tree[i].ob_spec.iconblk = (ICONBLK *)area;
339 			area += sizeof(ICONBLK);
340 
341 			size = (size_t)((int32_t)tree[i].ob_spec.iconblk->ib_wicon *
342 							(int32_t)tree[i].ob_spec.iconblk->ib_hicon /
343 							8L);
344 			/* Copy the mask-data */
345 			memcpy(area, tree[i].ob_spec.iconblk->ib_pmask, size);
346 			new_tree[i].ob_spec.iconblk->ib_pmask =	(int16_t *)area;
347 			area += size;
348 
349 			/* Copy the icon-data */
350 			memcpy(area, tree[i].ob_spec.iconblk->ib_pdata, size);
351 			new_tree[i].ob_spec.iconblk->ib_pdata = (int16_t *)area;
352 			area += size;
353 			size = strlen(tree[i].ob_spec.iconblk->ib_ptext) + 1L;
354 
355 			/* Copy the icon-string */
356 			strcpy(area, tree[i].ob_spec.iconblk->ib_ptext);
357 			new_tree[i].ob_spec.iconblk->ib_ptext = area;
358 			area += size;
359 
360             break;
361         }
362 
363         /* Assure that area contains an even address */
364         if ((int32_t)area & 1)
365             area++;
366     }
367 
368     return(new_tree);
369 }
370 
371 /***
372  * Create a simple OBJECT tree from a list of strings, to be used with menu_popup
373  * OBJECT ownership is passed to caller.
374  * Call gemtk_obj_destroy_popup_tree once the popup isn't needed anymore.
375  *
376  * \param items A list of string to be used as items
377  * \param nitems The number of items in the list
378  * \param selected The text of the selected item
379  * \param horizontal Set to true to render the tree horizontally
380  * \param max_width -1: autodetect width
381  * \param max_heigth -1: autodetect height (set to postive value when using a vertical scrolling popup)
382  * \return a pointer to the new OBJECT tree
383  */
gemtk_obj_create_popup_tree(const char ** items,int nitems,char * selected,bool horizontal,int max_width,int max_height)384 OBJECT * gemtk_obj_create_popup_tree(const char **items, int nitems,
385                                      char * selected, bool horizontal,
386                                      int max_width, int max_height)
387 {
388     OBJECT * popup = NULL;
389     int box_width = 0;
390     int box_height = 0;
391     int char_width = 10;
392     int char_height = 16;
393     int item_height;   // height of each item
394 
395     assert(items != NULL);
396 
397     item_height = char_height;
398 
399     /* Allocate room for n items and the root G_BOX: */
400     popup = calloc(nitems+1, sizeof(OBJECT));
401     assert(popup != null);
402 
403     for (int i=0; i<nitems; i++) {
404 
405         int len = strlen(items[i]);
406 
407         if (horizontal && (max_width<1)) {
408             box_width += (len * char_width)+4;
409         }
410         else if (!horizontal){
411             /* Detect max width, used for vertical rendering: */
412             if(len*char_width > box_width){
413                 box_width = (len+2) * char_width;
414             }
415             //if (max_height < 1) {
416                 box_height += item_height;
417             //}
418         }
419     }
420 
421     if (max_width>0){
422         box_width = max_width;
423     }
424 
425     if (horizontal) {
426         box_height = item_height;
427     }
428     else if(max_height > 0){
429         // TODO: validate max_height, shrink values larger than screen height
430         //box_height = max_height;
431     }
432 
433 /*
434     printf("popup height: %d, popup width: %d\n", box_height, box_width);
435 */
436 
437     popup[0].ob_next = -1;	/**< object's next sibling */
438     popup[0].ob_head = 1; 	/**< head of object's children */
439     popup[0].ob_tail = nitems; 	/**< tail of object's children */
440     popup[0].ob_type = G_BOX; 	/**< type of object */
441     popup[0].ob_flags = OF_FL3DBAK;	/**< flags */
442     popup[0].ob_state = OS_NORMAL;	/**< state */
443     popup[0].ob_spec.index = (long) 16650496L; 	/**< object-specific data */
444     popup[0].ob_x = 0; 		/**< upper left corner of object */
445     popup[0].ob_y = 0; 		/**< upper left corner of object */
446     popup[0].ob_width = box_width;	/**< width of obj */
447     popup[0].ob_height = box_height;
448 
449 
450 
451     /* Add items to popup: */
452     int xpos = 0, ypos = 0;
453 
454     for (int i=0; i<nitems; i++) {
455 
456         int state = OS_NORMAL;
457         int flags = OF_NONE;
458         int item_width;
459         char * string = calloc(1, strlen(items[i])+3);
460 
461         snprintf(string, strlen(items[i])+3, "  %s", items[i]);
462 
463         if (selected != NULL) {
464             if (strcmp(selected, items[i]) == 0) {
465                 state |= OS_CHECKED;
466             }
467         }
468 
469         if (i == nitems-1) {
470             flags |= OF_LASTOB;
471         }
472 
473         item_width = (horizontal) ? ((int)strlen(items[i])*char_width)+2 : box_width;
474 /*
475         printf("addin popup item \"%s\" (w: %d, h: %d, flags: %x) at %d/%d\n", items[i],
476                item_width, item_height, flags,
477                xpos, ypos);
478 */
479 
480         popup[i+1].ob_next = ((flags&OF_LASTOB) != 0) ? 0 : i+2;	/**< object's next sibling */
481         popup[i+1].ob_head = -1; 	/**< head of object's children */
482         popup[i+1].ob_tail = -1; 	/**< tail of object's children */
483         popup[i+1].ob_type = G_STRING; 	/**< type of object */
484         popup[i+1].ob_flags = flags;	/**< flags */
485         popup[i+1].ob_state = state;	/**< state */
486         popup[i+1].ob_spec.free_string = string; 	/**< object-specific data  */
487         popup[i+1].ob_x = xpos; 		/**< upper left corner of object */
488         popup[i+1].ob_y = ypos; 		/**< upper left corner of object */
489         popup[i+1].ob_width = item_width;	/**< width of obj */
490         popup[i+1].ob_height = item_height;
491 
492         if (horizontal) {
493             xpos += item_width;
494         }
495         else {
496             ypos += item_height;
497         }
498 
499     }
500 
501     return(popup);
502 }
503 
504 /***
505  * Free memory of an OBJECT tree created with gemtk_obj_create_popup_tree.
506  *
507  * \param popup The tree to destroy
508  */
gemtk_obj_destroy_popup_tree(OBJECT * popup)509 void gemtk_obj_destroy_popup_tree(OBJECT * popup)
510 {
511     int i=0;
512 
513     while (1) {
514         if (popup[i].ob_type == G_STRING) {
515             free(popup[i+1].ob_spec.free_string);
516         }
517         if((popup[i].ob_flags & OF_LASTOB) != OF_LASTOB){
518             break;
519         }
520         i++;
521     }
522 
523     free(popup);
524 }
525