1 /* Copyright (C) 2000-2004 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * $Id: button.pike,v 1.1 2008/03/31 13:39:57 exodusd Exp $
19 inherit "/kernel/module";
22 class button : public module{
29 Image.Layer layer_slice( Image.Layer l, int from, int to )
31 return Image.Layer( ([
32 "image":l->image()->copy( from,0, to-1, l->ysize()-1 ),
33 "alpha":l->alpha()->copy( from,0, to-1, l->ysize()-1 ),
37 Image.Layer stretch_layer( Image.Layer o, int x1, int x2, int w )
40 int leftovers = w - (x1 + (o->xsize()-x2) );
43 l = layer_slice( o, 0, x1 );
44 m = layer_slice( o, x1+1, x2-1 );
45 r = layer_slice( o, x2, o->xsize() );
47 m->set_image( m->image()->scale( leftovers, l->ysize() ),
48 m->alpha()->scale( leftovers, l->ysize() ));
51 m->set_offset( x1,0 );
52 r->set_offset( w-r->xsize(),0 );
53 o = Image.lay( ({ l, m, r }) );
54 o->set_mode( oo->mode() );
55 o->set_alpha_value( oo->alpha_value() );
59 mapping load_icon(string path)
61 werror("Loading Icon="+path+"\n");
65 array(Image.Layer) load_layers(string path)
67 object obj = _FILEPATH->path_to_object(path);
68 string data = obj->get_content();
69 return Image.decode_layers(data);
72 Image.Font resolve_font(string font)
74 object fontobj = Image.Font();
76 fontobj->load("server/modules/test.fnt");
80 array(Image.Layer)|mapping draw_button(mapping args, string text)
85 Image.Layer background;
89 int left, right, top, middle, bottom; /* offsets */
90 int req_width, noframe;
94 void set_image( array layers )
96 foreach( layers||({}), object l )
98 if(!l->get_misc_value( "name" ) ) // Hm. Probably PSD
101 ll[lower_case(l->get_misc_value( "name" ))] = l;
102 switch( lower_case(l->get_misc_value( "name" )) )
104 case "background": background = l; break;
105 case "frame": frame = l; break;
106 case "mask": mask = l; break;
111 if( args->border_image )
113 array(Image.Layer)|mapping tmp = load_layers(args->border_image);
115 if (tmp->error == 401)
118 error("GButton: Failed to load frame image: %O\n",
124 // otherwise load default images
125 if ( !frame && !background && !mask )
127 string data = Stdio.read_file("gbutton.xcf");
129 error ("Failed to load default frame image "
130 "(roxen-images/gbutton.xcf): " + strerror (errno()));
132 set_image(Image.XCF.decode_layers(data));
136 catch (err[0] = "Failed to decode default frame image "
137 "(gbutton.xcf): " + err[0]);
141 error("Failed to decode default frame image "
142 "(roxen-images/gbutton.xcf).\n");
148 frame = background || mask; // for sizes offsets et.al.
151 // Translate frame image to 0,0 (left layers are most likely to the
152 // left of the frame image)
153 int x0 = frame->xoffset();
154 int y0 = frame->yoffset();
156 foreach( values( ll ), object l )
158 int x = l->xoffset();
159 int y = l->yoffset();
160 l->set_offset( x-x0, y-y0 );
169 foreach( frame->get_misc_value( "image_guides" ), object g )
171 x += ({ g->pos - x0 });
173 y += ({ g->pos - y0 });
179 x = ({ 5, frame->xsize()-5 });
182 y = ({ 2, frame->ysize()-2 });
184 left = x[0]; right = x[-1]; top = y[0]; middle = y[1]; bottom = y[-1];
185 right = frame->xsize()-right;
187 // Text height depends on which guides we should align to
189 switch (args->icva) {
191 text_height = bottom - middle;
194 text_height = middle - top;
198 text_height = bottom - top;
204 icon = load_icon(args->icn);
206 icon = load_icon(args->icd);
208 int i_width = icon && icon->img->xsize();
209 int i_height = icon && icon->img->ysize();
210 int i_spc = i_width && sizeof(text) && 5;
216 Image.Font button_font;
217 int th = text_height;
220 button_font = resolve_font( args->font+" "+th );
221 text_img = button_font->write(text);
222 os = text_img->ysize();
224 if( os < text_height )
226 else if( os > text_height )
228 if( dir > 0 && os > text_height ) break;
229 else if( dir < 0 && os < text_height ) dir = 1;
230 else if( os == text_height ) break;
232 } while( (text_img->ysize() - text_height)
233 && (th>0 && th<text_height*2));
235 // fonts that can not be scaled.
236 if( abs(text_img->ysize() - text_height)>2 )
237 text_img = text_img->scale(0, text_height );
240 int o = text_img->ysize() - text_height;
245 text_img = text_img->scale((int) round(text_img->xsize() * 0.8),
250 int t_width = text_img && text_img->xsize();
252 // Compute text and icon placement. Only incorporate icon width/spacing if
253 // it's placed inline with the text.
254 req_width = t_width + left + right;
255 if ((args->icva || "middle") == "middle")
256 req_width += i_width + i_spc;
257 if (args->wi && (req_width < args->wi))
258 req_width = args->wi;
260 int icn_x, icn_y, txt_x, txt_y;
262 // Are text and icon lined up or on separate lines?
263 switch (args->icva) {
266 // Note: This requires _three_ guidelines! Icon and text can only be
267 // horizontally centered
268 icn_x = left + (req_width - right - left - i_width) / 2;
269 txt_x = left + (req_width - right - left - t_width) / 2;
270 if (args->icva == "above" || !text_height) {
272 icn_y = top + ((text_height ? middle : bottom) - top - i_height) / 2;
275 icn_y = middle + (bottom - middle - i_height) / 2;
281 // Center icon vertically on same line as text
282 icn_y = icon && (frame->ysize() - icon->img->ysize()) / 2;
288 // Allow icon alignment: left, right
293 txt_x = icn_x + i_width + i_spc;
298 icn_x = req_width - right - i_width;
306 // Allow icon alignment:
307 // left, center, center-before, center-after, right
312 txt_x = (req_width - right - left - i_width - i_spc - t_width) / 2;
313 txt_x += icn_x + i_width + i_spc;
317 case "center_before":
318 case "center-before":
319 icn_x = (req_width - i_width - i_spc - t_width) / 2;
320 txt_x = icn_x + i_width + i_spc;
324 txt_x = (req_width - i_width - i_spc - t_width) / 2;
325 icn_x = txt_x + t_width + i_spc;
328 icn_x = req_width - right - i_width;
329 txt_x = left + (icn_x - i_spc - t_width - left) / 2;
335 // Allow icon alignment: left, right
341 txt_x = req_width - right - t_width;
344 icn_x = req_width - right - i_width;
345 txt_x = icn_x - i_spc - t_width;
353 if( args->extra_frame_layers )
356 foreach( args->extra_frame_layers/",", string q )
360 frame = Image.lay( l+(noframe?({}):({frame})) );
363 if( args->extra_mask_layers )
366 foreach( args->extra_mask_layers/",", string q )
373 mask = Image.lay( l );
377 right = frame->xsize()-right;
380 Image.Image i = mask->image();
381 Image.Image m = mask->alpha();
382 int x0 = -mask->xoffset();
383 int y0 = -mask->yoffset();
384 int x1 = frame->xsize()-1+x0;
385 int y1 = frame->ysize()-1+y0;
387 i = i->copy(x0,y0, x1,y1);
389 m = m->copy(x0,y0, x1,y1);
390 mask->set_image( i, m );
391 mask = stretch_layer( mask, left, right, req_width );
393 if( frame != background )
394 frame = stretch_layer( frame, left, right, req_width );
395 array(Image.Layer) button_layers = ({
396 Image.Layer( Image.Image(req_width, frame->ysize(), args->bg),
397 mask->alpha()->copy(0,0,req_width-1,frame->ysize()-1)),
401 if( args->extra_background_layers || background)
403 array l = ({ background });
404 foreach( (args->extra_background_layers||"")/","-({""}), string q )
407 foreach( l, object ll )
410 ll->set_alpha_value( 0.3 );
411 button_layers += ({ stretch_layer( ll, left, right, req_width ) });
417 button_layers += ({ frame });
418 frame->set_mode( "value" );
423 // Adjust dimmed border intensity to the background
424 int bg_value = Image.Color(@args->bg)->hsv()[2];
425 int dim_high, dim_low;
426 if (bg_value < 128) {
427 dim_low = max(bg_value - 64, 0);
428 dim_high = dim_low + 128;
430 dim_high = min(bg_value + 64, 255);
431 dim_low = dim_high - 128;
433 frame->set_image(frame->image()->
434 modify_by_intensity( 1, 1, 1,
435 ({ dim_low, dim_low, dim_low }),
436 ({ dim_high, dim_high, dim_high })),
444 "alpha_value":(args->dim ? 0.3 : 1.0),
454 float ta = args->txtalpha?args->txtalpha:1.0;
458 "mode":args->txtmode,
459 "image":text_img->color(0,0,0)->invert()->color(@args->txt),
460 "alpha":(text_img*(args->dim?0.5*ta:ta)),
467 // 'plain' extra layers are added on top of everything else
468 if( args->extra_layers )
470 array q = map(args->extra_layers/",",
471 lambda(string q) { return ll[q]; } )-({0});
472 foreach( q, object ll )
475 ll->set_alpha_value( 0.3 );
476 button_layers += ({stretch_layer(ll,left,right,req_width)});
477 button_layers[-1]->set_offset( 0,
478 button_layers[0]->ysize()-
479 button_layers[-1]->ysize() );
483 button_layers -= ({ 0 });
484 // left layers are added to the left of the image, and the mask is
485 // extended using their mask. There is no corresponding 'mask' layers
486 // for these, but that is not a problem most of the time.
487 if( args->extra_left_layers )
490 foreach( args->extra_left_layers/",", string q )
493 l->set_offset( 0, 0 );
496 object q = Image.lay( l );
497 foreach( button_layers, object b )
499 int x = b->xoffset();
500 int y = b->yoffset();
501 b->set_offset( x+q->xsize(), y );
503 q->set_offset( 0, button_layers[0]->ysize()-q->ysize() );
504 button_layers += ({ q });
508 // right layers are added to the right of the image, and the mask is
509 // extended using their mask. There is no corresponding 'mask' layers
510 // for these, but that is not a problem most of the time.
511 if( args->extra_right_layers )
514 foreach( args->extra_right_layers/",", string q )
517 l->set_offset( 0, 0 );
520 object q = Image.lay( l );
521 q->set_offset( button_layers[0]->xsize()+
522 button_layers[0]->xoffset(),
523 button_layers[0]->ysize()-q->ysize());
524 button_layers += ({ q });
528 // if( !equal( args->pagebg, args->bg ) )
530 // FIXME: fix transparency (somewhat)
531 // this version totally destroys the alpha channel of the image,
532 // but that's sort of the intention. The reason is that
533 // the png images are generated without alpha.
534 if (args->format == "png")
535 return ({ Image.Layer(([ "fill":args->pagebg, ])) }) + button_layers;
537 return button_layers;
542 mixed tab(mapping args)
544 mapping gbutton_args = args;
548 if ( !stringp(args->frame_image) ) {
549 fimage = Stdio.read_file("tabframe.xcf");
550 gbutton_args["frame-image"] = fimage;
553 object fimg = _FILEPATH->path_to_object(args->frame_image);
554 fimage = fimg->get_content();
558 array(Image.Layer) button_layers;
559 if( args->selected ) {
560 //add_layers( gbutton_args, "selected" );
561 gbutton_args->bg = Colors.parse_color(args->selcolor || "white");
562 gbutton_args->txt = Colors.parse_color(args->seltextcolor || "black");
563 gbutton_args->txtmode = (args->textmode ||"normal");
564 button_layers = draw_button(gbutton_args, "Hello");
566 //add_layers( gbutton_args, "unselected" );
567 gbutton_args->bg = Colors.parse_color(args->dimcolor || "#003366");
568 gbutton_args->txt = Colors.parse_color(args->textcolor || "white");
569 gbutton_args->txtmode = (args->textmode ||"normal");
570 button_layers = draw_button(gbutton_args, args->text);
572 m_delete(gbutton_args, "selected");
573 m_delete(gbutton_args, "dimcolor");
574 m_delete(gbutton_args, "seltextcolor");
575 m_delete(gbutton_args, "selcolor");
576 m_delete(gbutton_args, "result");
577 return button_layers;
580 array execute(mapping args)
582 array layers = tab(args);
583 object img = Image.lay( layers );
584 string str = Image.PNG->encode(img->image());
585 return ({ str, "image/gif" });
588 string get_identifier() { return "buttons"; }
591 void main(int argc, array argv)
594 object img = Image.lay( layers );
595 Stdio.write_file("test.jpg", Image.GIF->encode(img->image()));