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()));