67bff71999-11-15Jonas Wallden // Button module. Generates graphical buttons for use in Roxen config // interface, Roxen SiteBuilder and other places. //
979f4d2000-03-16Martin Nilsson // Copyright © 1999-2000 Roxen IS. Author: Jonas Walldén, <jonasw@roxen.com>
67bff71999-11-15Jonas Wallden  // Usage: // // <gbutton // bgcolor -- background color inside/outside button // textcolor -- button text color // href -- button URL // alt -- alternative button alt text // border -- image border
0b91941999-11-16Jonas Wallden // state -- enabled|disabled button state // textstyle -- normal|consensed text
dc73752000-05-25Jonas Wallden // icon-src -- icon reference // icon-data -- inline icon data
67bff71999-11-15Jonas Wallden // align -- left|center|right text alignment
dc73752000-05-25Jonas Wallden // align-icon -- left|center-before|center-after|right icon alignment
6cf7e52000-08-07Jonas Wallden // valign-icon -- above|middle|below icon vertical alignment
67bff71999-11-15Jonas Wallden // >Button text</gbutton> // // Alignment restriction: when text alignment is either left or right, icons // must also be aligned left or right.
9b03652001-03-07Kenneth Johansson constant cvs_version = "$Id: gbutton.pike,v 1.77 2001/03/07 13:40:43 kuntri Exp $";
67bff71999-11-15Jonas Wallden constant thread_safe = 1; #include <module.h> inherit "module"; roxen.ImageCache button_cache;
b3281f2000-09-10Martin Nilsson constant module_type = MODULE_TAG;
c043f82000-02-10Martin Nilsson constant module_name = "GButton";
76fd092000-04-06Mattias Wingstedt constant module_doc = "Provides the <tt>&lt;gbutton&gt;</tt> tag that is used to draw graphical " "buttons.";
2e0a351999-12-09Martin Nilsson 
7390582000-06-01Martin Nilsson mapping tagdocumentation() { Stdio.File file=Stdio.File(); if(!file->open(__FILE__,"r")) return 0; string doc=compile_string("#define manual\n"+file->read())->gbuttonattr; string imagecache=button_cache->documentation();
67bff71999-11-15Jonas Wallden 
9b03652001-03-07Kenneth Johansson  return ([ "gbutton":#"<desc cont='cont'><p><short> Creates graphical buttons.</short></p> </desc>"
67bff71999-11-15Jonas Wallden 
7390582000-06-01Martin Nilsson  +doc +imagecache,
67bff71999-11-15Jonas Wallden 
9b03652001-03-07Kenneth Johansson "gbutton-url":#"<desc cont='cont'><p><short> Generates an URI to the button.</short> <tag>gbutton-url</tag> takes the same attributes as <xref href='gbutton.tag' /> including the image cache attributes.</p> </desc>"
67bff71999-11-15Jonas Wallden 
7390582000-06-01Martin Nilsson  +doc +imagecache, ]); }
67bff71999-11-15Jonas Wallden 
7390582000-06-01Martin Nilsson #ifdef manual constant gbuttonattr=#"
9b03652001-03-07Kenneth Johansson <attr name='pagebgcolor' value='color'><p></p>
7390582000-06-01Martin Nilsson  </attr>
9b03652001-03-07Kenneth Johansson <attr name='bgcolor' value='color'><p> Background color inside and outside button.</p>
7390582000-06-01Martin Nilsson <ex> <gbutton bgcolor='lightblue'>Background</gbutton> </ex> </attr>
67bff71999-11-15Jonas Wallden 
9b03652001-03-07Kenneth Johansson <attr name='textcolor' value='color'><p> Button text color.</p>
7390582000-06-01Martin Nilsson <ex>
87ba132000-06-05Jonas Wallden <gbutton textcolor='#ff6600'>Text</gbutton>
7390582000-06-01Martin Nilsson </ex> </attr>
67bff71999-11-15Jonas Wallden 
9b03652001-03-07Kenneth Johansson <attr name='frame-image' value='path'><p>
7390582000-06-01Martin Nilsson  Use this XCF-image as a frame for the button. The image is required to have at least the following layers: background, mask and frame. More information on how to create frame images can be found in the
9b03652001-03-07Kenneth Johansson  Roxen documentation; Web Site Creator/Graphical tags section.</p>
7390582000-06-01Martin Nilsson <ex> <gbutton frame-image='internal-roxen-tabframe'>foo</gbutton> </ex> </attr>
67bff71999-11-15Jonas Wallden 
9b03652001-03-07Kenneth Johansson <attr name='alt' value='string'><p> Alternative button and alt text.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='href' value='uri'><p> Button URI.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='textstyle' value='normal|condensed'><p> Set to <att>normal</att> or <att>condensed</att> to alter text style.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='width' value=''><p> Minimum button width.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='align' value='left|center|right'><p>
7390582000-06-01Martin Nilsson  Set text alignment. There are some alignment restrictions: when text alignment is either <att>left</att> or <att>right</att>, icons must
9b03652001-03-07Kenneth Johansson  also be aligned <att>left</att> or <att>right</att>.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='state' value='enabled|disabled'><p> Set to <att>enabled</att> or <att>disabled</att> to select button state.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='icon-src' value='URI'><p> Fetch the icon from this URI.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='icon-data' value=''><p> Inline icon data.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='align-icon' value='left|center-before|center-after|right'><p> Set icon alignment.</p>
7390582000-06-01Martin Nilsson 
9b03652001-03-07Kenneth Johansson <xtable> <row><c>left</c><c>Place icon on the left side of the text.</c></row> <row><c>center-before</c><c>Center the icon before the text. Requires the <att>align='center'</att> attribute.</c></row> <row><c>center-after</c><c>Center the icon after the text. Requires the <att>align='center'</att> attribute.</c></row> <row><c>right</c><c>Place icon on the right side of the text.</c></row> </xtable>
7390582000-06-01Martin Nilsson  <ex> <gbutton width='150' align-icon='center-before' icon-src='internal-roxen-help'>Roxen 2.0</gbutton> </ex> <ex> <gbutton width='150' align='center' align-icon='center-after' icon-src='internal-roxen-help'>Roxen 2.0</gbutton> </ex> </attr>
9b03652001-03-07Kenneth Johansson <attr name='valign-icon' value='above|middle|below'><p>
6363fa2000-08-15Jonas Wallden  Set icon vertical alignment. Requires three horizontal guidelines in the frame image. If set to <att>above</att> the icon is placed between the first and second guidelines and the text between the second and third ones. If set to <att>below</att> the placement is reversed. Default value is
9b03652001-03-07Kenneth Johansson  <att>middle</att>.</p>
7390582000-06-01Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson <attr name='font' value='fontname'><p></p>
67bff71999-11-15Jonas Wallden 
9b03652001-03-07Kenneth Johansson </attr>";
2e0a351999-12-09Martin Nilsson #endif
67bff71999-11-15Jonas Wallden 
8a98622000-04-04Per Hedbor function TIMER( function f ) { #if 0 return lambda(mixed ... args) { int h = gethrtime(); mixed res; werror("Drawing ... "); res = f( @args ); werror(" %.1fms\n", (gethrtime()-h)/1000000.0 ); return res; }; #endif return f; }
67bff71999-11-15Jonas Wallden void start() {
8a98622000-04-04Per Hedbor  button_cache = roxen.ImageCache("gbutton", TIMER(draw_button));
67bff71999-11-15Jonas Wallden }
f049812000-07-24Martin Nilsson string status() { array s=button_cache->status();
482ba72000-12-30Per Hedbor  return sprintf("<b>Images in cache:</b> %d images<br />\n" "<b>Cache size:</b> %s", s[0], Roxen.sizetostring(s[1]));
f049812000-07-24Martin Nilsson } mapping(string:function) query_action_buttons() { return ([ "Clear cache":flush_cache ]); } void flush_cache() { button_cache->flush(); }
fde9082000-02-08Per Hedbor Image.Layer layer_slice( Image.Layer l, int from, int to ) { return Image.Layer( ([ "image":l->image()->copy( from,0, to-1, l->ysize()-1 ), "alpha":l->alpha()->copy( from,0, to-1, l->ysize()-1 ), ]) ); } Image.Layer stretch_layer( Image.Layer o, int x1, int x2, int w ) { Image.Layer l, m, r; int leftovers = w - (x1 + (o->xsize()-x2) ); object oo = o; l = layer_slice( o, 0, x1 );
3ef6452000-02-08Jonas Wallden  m = layer_slice( o, x1+1, x2-1 );
fde9082000-02-08Per Hedbor  r = layer_slice( o, x2, o->xsize() ); m->set_image( m->image()->scale( leftovers, l->ysize() ), m->alpha()->scale( leftovers, l->ysize() )); l->set_offset( 0,0 ); m->set_offset( x1,0 ); r->set_offset( w-r->xsize(),0 ); o = Image.lay( ({ l, m, r }) );
e0fda72000-04-04Marcus Comstedt  o->set_mode( oo->mode() ); o->set_alpha_value( oo->alpha_value() );
fde9082000-02-08Per Hedbor  return o; }
67bff71999-11-15Jonas Wallden 
1a54422000-02-21Per Hedbor array(Image.Layer) draw_button(mapping args, string text, object id)
67bff71999-11-15Jonas Wallden {
fde9082000-02-08Per Hedbor  Image.Image text_img;
67bff71999-11-15Jonas Wallden  mapping icon;
fde9082000-02-08Per Hedbor  Image.Layer background; Image.Layer frame; Image.Layer mask;
6cf7e52000-08-07Jonas Wallden  int left, right, top, middle, bottom; /* offsets */
2bf4842000-12-21Per Hedbor  int req_width, noframe;
7169752000-02-02Per Hedbor 
3d3a472000-02-08Per Hedbor  mapping ll = ([]);
fff48a2000-02-21Per Hedbor  void set_image( array layers )
e76d2c2000-02-03Per Hedbor  {
fff48a2000-02-21Per Hedbor  foreach( layers||({}), object l )
fde9082000-02-08Per Hedbor  {
e2a3de2000-10-17Per Hedbor  if(!l->get_misc_value( "name" ) ) // Hm. Probably PSD
fff48a2000-02-21Per Hedbor  continue;
e2a3de2000-10-17Per Hedbor 
fff48a2000-02-21Per Hedbor  ll[lower_case(l->get_misc_value( "name" ))] = l; switch( lower_case(l->get_misc_value( "name" )) )
fde9082000-02-08Per Hedbor  { case "background": background = l; break; case "frame": frame = l; break; case "mask": mask = l; break; } }
fff48a2000-02-21Per Hedbor  };
5dac1c2000-01-10Martin Nilsson 
fff48a2000-02-21Per Hedbor  if( args->border_image ) set_image( roxen.load_layers(args->border_image, id) );
2bf4842000-12-21Per Hedbor 
fff48a2000-02-21Per Hedbor  // otherwise load default images
2bf4842000-12-21Per Hedbor  if ( !frame && !background && !mask )
e2a3de2000-10-17Per Hedbor  {
76dcb72000-10-18Martin Stjernholm  string data = Stdio.read_file("roxen-images/gbutton.xcf"); if (!data) error ("Failed to load default frame image " "(roxen-images/gbutton.xcf): " + strerror (errno())); mixed err = catch { set_image(Image.XCF.decode_layers(data));
e2a3de2000-10-17Per Hedbor  }; if( !frame )
76dcb72000-10-18Martin Stjernholm  if (err) { catch (err[0] = "Failed to decode default frame image " "(roxen-images/gbutton.xcf): " + err[0]); throw (err); } else error("Failed to decode default frame image " "(roxen-images/gbutton.xcf).\n");
e2a3de2000-10-17Per Hedbor  }
fff48a2000-02-21Per Hedbor 
2bf4842000-12-21Per Hedbor  if( !frame ) { noframe = 1; frame = background || mask; // for sizes offsets et.al. }
fff48a2000-02-21Per Hedbor  // Translate frame image to 0,0 (left layers are most likely to the // left of the frame image) int x0 = frame->xoffset(); int y0 = frame->yoffset(); if( x0 || y0 ) foreach( values( ll ), object l ) { int x = l->xoffset(); int y = l->yoffset(); l->set_offset( x-x0, y-y0 ); }
7169752000-02-02Per Hedbor 
fde9082000-02-08Per Hedbor  if( !mask ) mask = frame;
e76d2c2000-02-03Per Hedbor 
fde9082000-02-08Per Hedbor  array x = ({}); array y = ({});
2bf4842000-12-21Per Hedbor 
fde9082000-02-08Per Hedbor  foreach( frame->get_misc_value( "image_guides" ), object g )
2bf4842000-12-21Per Hedbor  if( g->vertical ) x += ({ g->pos - x0 }); else y += ({ g->pos - y0 });
fff48a2000-02-21Per Hedbor  sort( y ); sort( x );
e76d2c2000-02-03Per Hedbor 
fde9082000-02-08Per Hedbor  if(sizeof( x ) < 2) x = ({ 5, frame->xsize()-5 });
fff48a2000-02-21Per Hedbor 
fde9082000-02-08Per Hedbor  if(sizeof( y ) < 2) y = ({ 2, frame->ysize()-2 });
e76d2c2000-02-03Per Hedbor 
6cf7e52000-08-07Jonas Wallden  left = x[0]; right = x[-1]; top = y[0]; middle = y[1]; bottom = y[-1];
fde9082000-02-08Per Hedbor  right = frame->xsize()-right;
6cf7e52000-08-07Jonas Wallden  // Text height depends on which guides we should align to int text_height; switch (args->icva) { case "above": text_height = bottom - middle; break; case "below": text_height = middle - top; break; default: case "middle": text_height = bottom - top; break; }
7169752000-02-02Per Hedbor 
67bff71999-11-15Jonas Wallden  // Get icon if (args->icn) icon = roxen.low_load_image(args->icn, id); else if (args->icd) icon = roxen.low_decode_image(args->icd);
fde9082000-02-08Per Hedbor  int i_width = icon && icon->img->xsize();
6cf7e52000-08-07Jonas Wallden  int i_height = icon && icon->img->ysize();
4e63f12000-05-24Jonas Wallden  int i_spc = i_width && sizeof(text) && 5;
7169752000-02-02Per Hedbor 
67bff71999-11-15Jonas Wallden  // Generate text
fde9082000-02-08Per Hedbor  if (sizeof(text)) {
300d982000-12-11Per Hedbor  int os, dir; Font button_font; int th = text_height; do { button_font = resolve_font( args->font+" "+th ); text_img = button_font->write(text); os = text_img->ysize(); if( !dir )
e59f912000-12-11Per Hedbor  if( os < text_height ) dir = 1; else if( os > text_height ) dir =-1; if( dir > 0 && os > text_height ) break; else if( dir < 0 && os < text_height ) dir = 1; else if( os == text_height ) break; th += dir; } while( (text_img->ysize() - text_height)
300d982000-12-11Per Hedbor  && (th>0 && th<text_height*2)); // fonts that can not be scaled.
e59f912000-12-11Per Hedbor  if( abs(text_img->ysize() - text_height)>2 )
1597b42000-12-11Martin Nilsson  text_img = text_img->scale(0, text_height );
300d982000-12-11Per Hedbor  else { int o = text_img->ysize() - text_height; top -= o; middle -= o/2; }
3c24b62000-01-19Fredrik Noring  if (args->cnd) text_img = text_img->scale((int) round(text_img->xsize() * 0.8), text_img->ysize()); }
7169752000-02-02Per Hedbor 
fde9082000-02-08Per Hedbor  int t_width = text_img && text_img->xsize();
6cf7e52000-08-07Jonas Wallden  // Compute text and icon placement. Only incorporate icon width/spacing if // it's placed inline with the text. req_width = t_width + left + right; if ((args->icva || "middle") == "middle") req_width += i_width + i_spc;
67bff71999-11-15Jonas Wallden  if (args->wi && (req_width < args->wi)) req_width = args->wi;
fde9082000-02-08Per Hedbor 
6cf7e52000-08-07Jonas Wallden  int icn_x, icn_y, txt_x, txt_y; // Are text and icon lined up or on separate lines? switch (args->icva) { case "above": case "below": // Note: This requires _three_ guidelines! Icon and text can only be // horizontally centered icn_x = left + (req_width - right - left - i_width) / 2; txt_x = left + (req_width - right - left - t_width) / 2; if (args->icva == "above") { txt_y = middle; icn_y = top + (middle - top - i_height) / 2; } else { txt_y = top; icn_y = middle + (bottom - middle - i_height) / 2;
67bff71999-11-15Jonas Wallden  } break; default: case "middle":
6cf7e52000-08-07Jonas Wallden  // Center icon vertically on same line as text icn_y = icon && (frame->ysize() - icon->img->ysize()) / 2; txt_y = top; switch (args->al)
fde9082000-02-08Per Hedbor  {
6cf7e52000-08-07Jonas Wallden  case "left": // Allow icon alignment: left, right switch (args->ica) { case "left": icn_x = left; txt_x = icn_x + i_width + i_spc; break; default: case "right": txt_x = left; icn_x = req_width - right - i_width; break; }
67bff71999-11-15Jonas Wallden  break;
6cf7e52000-08-07Jonas Wallden  default: case "center": case "middle": // Allow icon alignment: // left, center, center-before, center-after, right switch (args->ica) { case "left": icn_x = left; txt_x = (req_width - right - left - i_width - i_spc - t_width) / 2; txt_x += icn_x + i_width + i_spc; break; default: case "center": case "center_before": case "center-before": icn_x = (req_width - i_width - i_spc - t_width) / 2; txt_x = icn_x + i_width + i_spc; break; case "center_after": case "center-after": txt_x = (req_width - i_width - i_spc - t_width) / 2; icn_x = txt_x + t_width + i_spc; break; case "right": icn_x = req_width - right - i_width; txt_x = left + (icn_x - i_spc - t_width) / 2; break; } break; case "right": // Allow icon alignment: left, right switch (args->ica) { default: case "left": icn_x = left; txt_x = req_width - right - t_width; break; case "right": icn_x = req_width - right - i_width; txt_x = icn_x - i_spc - t_width; break; } break;
67bff71999-11-15Jonas Wallden  } break; }
f6ea612000-02-07Per Hedbor 
5321c02000-02-08Per Hedbor  if( args->extra_frame_layers ) {
8a98622000-04-04Per Hedbor  array l = ({ });
5321c02000-02-08Per Hedbor  foreach( args->extra_frame_layers/",", string q ) l += ({ ll[q] }); l-=({ 0 });
8a98622000-04-04Per Hedbor  if( sizeof( l ) )
2bf4842000-12-21Per Hedbor  frame = Image.lay( l+(noframe?({}):({frame})) );
5321c02000-02-08Per Hedbor  }
f6ea612000-02-07Per Hedbor 
5321c02000-02-08Per Hedbor  if( args->extra_mask_layers ) { array l = ({ }); foreach( args->extra_mask_layers/",", string q ) l += ({ ll[q] }); l-=({ 0 }); if( sizeof( l ) )
fff48a2000-02-21Per Hedbor  { if( mask ) l = ({ mask })+l;
5321c02000-02-08Per Hedbor  mask = Image.lay( l );
fff48a2000-02-21Per Hedbor  }
5321c02000-02-08Per Hedbor  } right = frame->xsize()-right; if (mask != frame)
e95d462000-12-16Per Hedbor  { Image.Image i = mask->image(); Image.Image m = mask->alpha(); int x0 = -mask->xoffset(); int y0 = -mask->yoffset(); int x1 = frame->xsize()-1+x0; int y1 = frame->ysize()-1+y0; i = i->copy(x0,y0, x1,y1); if( m ) m = m->copy(x0,y0, x1,y1); mask->set_image( i, m );
5321c02000-02-08Per Hedbor  mask = stretch_layer( mask, left, right, req_width );
e95d462000-12-16Per Hedbor  }
2bf4842000-12-21Per Hedbor  if( frame != background ) frame = stretch_layer( frame, left, right, req_width );
e0fda72000-04-04Marcus Comstedt  array(Image.Layer) button_layers = ({ Image.Layer( Image.Image(req_width, frame->ysize(), args->bg),
e95d462000-12-16Per Hedbor  mask->alpha()->copy(0,0,req_width-1,frame->ysize()-1)),
e0fda72000-04-04Marcus Comstedt  });
8a98622000-04-04Per Hedbor  if( args->extra_background_layers || background)
fde9082000-02-08Per Hedbor  {
8a98622000-04-04Per Hedbor  array l = ({ background }); foreach( (args->extra_background_layers||"")/","-({""}), string q ) l += ({ ll[q] }); l-=({ 0 }); foreach( l, object ll ) { if( args->dim ) ll->set_alpha_value( 0.3 ); button_layers += ({ stretch_layer( ll, left, right, req_width ) }); }
fde9082000-02-08Per Hedbor  }
12493c2000-02-22Per Hedbor 
2bf4842000-12-21Per Hedbor  if( !noframe ) { button_layers += ({ frame }); frame->set_mode( "value" ); }
12493c2000-02-22Per Hedbor  if( args->dim ) {
1ac32d2000-02-10Jonas Wallden  // Adjust dimmed border intensity to the background int bg_value = Image.Color(@args->bg)->hsv()[2]; int dim_high, dim_low; if (bg_value < 128) { dim_low = max(bg_value - 64, 0); dim_high = dim_low + 128; } else { dim_high = min(bg_value + 64, 255); dim_low = dim_high - 128; }
12493c2000-02-22Per Hedbor  frame->set_image(frame->image()-> modify_by_intensity( 1, 1, 1, ({ dim_low, dim_low, dim_low }), ({ dim_high, dim_high, dim_high })), frame->alpha());
1ac32d2000-02-10Jonas Wallden  }
1a54422000-02-21Per Hedbor 
fde9082000-02-08Per Hedbor  // Draw icon. if (icon)
1a54422000-02-21Per Hedbor  button_layers += ({ Image.Layer( ([
12493c2000-02-22Per Hedbor  "alpha_value":(args->dim ? 0.3 : 1.0),
1a54422000-02-21Per Hedbor  "image":icon->img,
12493c2000-02-22Per Hedbor  "alpha":icon->alpha,
1a54422000-02-21Per Hedbor  "xoffset":icn_x,
6cf7e52000-08-07Jonas Wallden  "yoffset":icn_y
1a54422000-02-21Per Hedbor  ]) )});
f6ea612000-02-07Per Hedbor 
67bff71999-11-15Jonas Wallden  // Draw text
3c24b62000-01-19Fredrik Noring  if(text_img)
2bf4842000-12-21Per Hedbor  { float ta = args->txtalpha?args->txtalpha:1.0; button_layers += ({ Image.Layer(([ "mode":args->txtmode, "image":text_img->color(0,0,0)->invert()->color(@args->txt), "alpha":(text_img*(args->dim?0.5*ta:ta)), "xoffset":txt_x, "yoffset":txt_y, ])) }); }
7169752000-02-02Per Hedbor 
fff48a2000-02-21Per Hedbor  // 'plain' extra layers are added on top of everything else
5321c02000-02-08Per Hedbor  if( args->extra_layers ) {
1a54422000-02-21Per Hedbor  array q = map(args->extra_layers/",", lambda(string q) { return ll[q]; } )-({0});
8a98622000-04-04Per Hedbor  foreach( q, object ll ) { if( args->dim ) ll->set_alpha_value( 0.3 ); button_layers += ({stretch_layer(ll,left,right,req_width)});
e95d462000-12-16Per Hedbor  button_layers[-1]->set_offset( 0, button_layers[0]->ysize()- button_layers[-1]->ysize() );
8a98622000-04-04Per Hedbor  }
5321c02000-02-08Per Hedbor  }
1a54422000-02-21Per Hedbor  button_layers -= ({ 0 });
fff48a2000-02-21Per Hedbor  // left layers are added to the left of the image, and the mask is // extended using their mask. There is no corresponding 'mask' layers // for these, but that is not a problem most of the time. if( args->extra_left_layers ) { array l = ({ }); foreach( args->extra_left_layers/",", string q ) l += ({ ll[q] }); l-=({ 0 });
e95d462000-12-16Per Hedbor  l->set_offset( 0, 0 );
fff48a2000-02-21Per Hedbor  if( sizeof( l ) ) { object q = Image.lay( l );
1a54422000-02-21Per Hedbor  foreach( button_layers, object b ) { int x = b->xoffset(); int y = b->yoffset(); b->set_offset( x+q->xsize(), y ); }
e95d462000-12-16Per Hedbor  q->set_offset( 0, button_layers[0]->ysize()-q->ysize() );
1a54422000-02-21Per Hedbor  button_layers += ({ q });
fff48a2000-02-21Per Hedbor  } } // right layers are added to the right of the image, and the mask is // extended using their mask. There is no corresponding 'mask' layers // for these, but that is not a problem most of the time. if( args->extra_right_layers ) { array l = ({ }); foreach( args->extra_right_layers/",", string q ) l += ({ ll[q] }); l-=({ 0 });
e95d462000-12-16Per Hedbor  l->set_offset( 0, 0 );
fff48a2000-02-21Per Hedbor  if( sizeof( l ) ) { object q = Image.lay( l );
a76b0a2000-02-22Per Hedbor  q->set_offset( button_layers[0]->xsize()+
e95d462000-12-16Per Hedbor  button_layers[0]->xoffset(), button_layers[0]->ysize()-q->ysize());
1a54422000-02-21Per Hedbor  button_layers += ({ q });
fff48a2000-02-21Per Hedbor  } }
5321c02000-02-08Per Hedbor 
fcb9512000-09-19Per Hedbor // if( !equal( args->pagebg, args->bg ) ) // { // FIXME: fix transparency (somewhat) // this version totally destroys the alpha channel of the image, // but that's sort of the intention. The reason is that // the png images are generated without alpha.
d123332000-11-17Anders Johansson  if (args->format == "png") return ({ Image.Layer(([ "fill":args->pagebg, ])) }) + button_layers; else return button_layers;
fcb9512000-09-19Per Hedbor // }
67bff71999-11-15Jonas Wallden } mapping find_internal(string f, RequestID id) { return button_cache->http_file_answer(f, id); }
350dfa2001-02-02Per Hedbor mapping __stat_cache = ([ ]); int get_file_stat( string f, RequestID id ) { if( __stat_cache[ f ] ) return __stat_cache[ f ]; call_out( m_delete, 10, __stat_cache, f ); return __stat_cache[ f ] = (id->conf->stat_file( f,id ) || file_stat( f ) || ({ 0,0,0,0 }))[ST_MTIME]; }
67bff71999-11-15Jonas Wallden 
c421c62000-03-14Martin Nilsson class ButtonFrame { inherit RXML.Frame;
f29b142000-03-24Per Hedbor  array mk_url(RequestID id) {
350dfa2001-02-02Per Hedbor // int t = gethrtime();
6cf7e52000-08-07Jonas Wallden  string fi = (args["frame-image"] || id->misc->defines["gbutton-frame-image"]);
c421c62000-03-14Martin Nilsson  if( fi )
bf956c2000-05-01Martin Nilsson  fi = Roxen.fix_relative( fi, id );
dc73752000-05-25Jonas Wallden  // Harmonize some attribute names to RXML standards...
350dfa2001-02-02Per Hedbor  args->icon_src = args["icon-src"] || args->icon_src; args->icon_data = args["icon-data"] || args->icon_data; args->align_icon = args["align-icon"] || args->align_icon;
6cf7e52000-08-07Jonas Wallden  args->valign_icon = args["valign-icon"] || args->valign_icon;
dc73752000-05-25Jonas Wallden  m_delete(args, "icon-src"); m_delete(args, "icon-data"); m_delete(args, "align-icon");
350dfa2001-02-02Per Hedbor  mapping new_args = ([ "pagebg" :parse_color(args->pagebgcolor || id->misc->defines->theme_bgcolor || id->misc->defines->bgcolor || args->bgcolor || "#eeeeee"), // _page_ bg color "bg" : parse_color(args->bgcolor ||
c421c62000-03-14Martin Nilsson  id->misc->defines->theme_bgcolor || id->misc->defines->bgcolor ||
350dfa2001-02-02Per Hedbor  "#eeeeee"), // Background color "txt" : parse_color(args->textcolor || id->misc->defines->theme_bgcolor || id->misc->defines->fgcolor || "#000000"), // Text color "txtalpha": (args->textalpha?(float)args->textalpha:1.0), "txtmode": (args->textmode||"normal"), "cnd" : (args->condensed || // Condensed text (lower_case(args->textstyle || "") == "condensed")), "wi" : (int) args->width, // Min button width "al" : args->align || "left", // Text alignment "dim" : (args->dim || // Button dimming (< "dim", "disabled" >)[lower_case(args->state || "")]), "icn" : args->icon_src && Roxen.fix_relative(args->icon_src, id), // Icon URL "icd" : args->icon_data, // Inline icon data "ica" : lower_case(args->align_icon || "left"), // Icon alignment "icva": lower_case(args->valign_icon || "middle"),// Vertical align "font": (args->font||id->misc->defines->font|| roxen->query("default_font")), "border_image":fi, "extra_layers":args["extra-layers"], "extra_left_layers":args["extra-left-layers"], "extra_right_layers":args["extra-right-layers"], "extra_background_layers":args["extra-background-layers"], "extra_mask_layers":args["extra-mask-layers"], "extra_frame_layers":args["extra-frame-layers"], "scale":args["scale"], "format":args["format"], "gamma":args["gamma"], "crop":args["crop"], ]);
e4b4b72000-11-21Per Hedbor  if( fi )
350dfa2001-02-02Per Hedbor  new_args->stat = get_file_stat( fi, id );
e4b4b72000-11-21Per Hedbor 
c421c62000-03-14Martin Nilsson  new_args->quant = args->quant || 128; foreach(glob("*-*", indices(args)), string n) new_args[n] = args[n];
350dfa2001-02-02Per Hedbor  // if( new_args->stat && (fn = id->conf->real_file( fi, id ) ) ) // Roxen.add_cache_stat_callback( id, fn, new_args->stat ); string fn; // werror("mkurl took %dµs\n", gethrtime()-t ); // t = gethrtime();
c421c62000-03-14Martin Nilsson  string img_src =
db053e2000-12-05Martin Nilsson  query_absolute_internal_location(id) +
c421c62000-03-14Martin Nilsson  button_cache->store( ({ new_args, content }), id);
350dfa2001-02-02Per Hedbor // werror("argcache->store took %dµs\n", gethrtime()-t );
c421c62000-03-14Martin Nilsson  return ({ img_src, new_args }); } } class TagGButtonURL { inherit RXML.Tag; constant name = "gbutton-url";
2d2e182000-08-15Martin Stjernholm  constant flags = RXML.FLAG_DONT_REPORT_ERRORS;
c421c62000-03-14Martin Nilsson  RXML.Type content_type = RXML.t_text(RXML.PXml); class Frame { inherit ButtonFrame; array do_return(RequestID id) { result=mk_url(id)[0]; return 0; }
67bff71999-11-15Jonas Wallden  }
c421c62000-03-14Martin Nilsson }
5dac1c2000-01-10Martin Nilsson 
25d1762000-08-12Martin Stjernholm class TagGButton {
c421c62000-03-14Martin Nilsson  inherit RXML.Tag; constant name = "gbutton";
2d2e182000-08-15Martin Stjernholm  constant flags = RXML.FLAG_DONT_REPORT_ERRORS;
c421c62000-03-14Martin Nilsson  RXML.Type content_type = RXML.t_text(RXML.PXml); class Frame { inherit ButtonFrame; array do_return(RequestID id) {
dc73752000-05-25Jonas Wallden  // Peek at img-align and remove it so it won't be copied by "*-*" glob // in mk_url(). string img_align = args["img-align"]; m_delete(args, "img-align");
c421c62000-03-14Martin Nilsson  [string img_src, mapping new_args]=mk_url(id); mapping img_attrs = ([ "src" : img_src, "alt" : args->alt || content, "border" : args->border, "hspace" : args->hspace, "vspace" : args->vspace ]);
dc73752000-05-25Jonas Wallden  if (img_align) img_attrs->align = img_align;
ff08aa2001-01-12Anders Johansson  if (mapping size = button_cache->metadata( ({ new_args, content }), id, 1)) {
c421c62000-03-14Martin Nilsson  // Image in cache (1 above prevents generation on-the-fly, i.e. // first image will lack sizes). img_attrs->width = size->xsize; img_attrs->height = size->ysize; }
0023a62000-08-22Martin Nilsson  result = Roxen.make_tag("img", img_attrs, !args->noxml);
dbc2722000-03-24Johan Sundström 
c421c62000-03-14Martin Nilsson  // Make button clickable if not dimmed
dbc2722000-03-24Johan Sundström  if(args->href && !new_args->dim) {
c421c62000-03-14Martin Nilsson  mapping a_attrs = ([ "href" : args->href ]);
dbc2722000-03-24Johan Sundström  foreach(indices(args), string arg)
6cf7e52000-08-07Jonas Wallden  if(has_value("target/onmousedown/onmouseup/onclick/ondblclick/" "onmouseout/onmouseover/onkeypress/onkeyup/" "onkeydown" / "/", lower_case(arg)))
dbc2722000-03-24Johan Sundström  a_attrs[arg] = args[arg];
bf956c2000-05-01Martin Nilsson  result = Roxen.make_container("a", a_attrs, result);
dbc2722000-03-24Johan Sundström  }
c421c62000-03-14Martin Nilsson  return 0; } }
67bff71999-11-15Jonas Wallden }