67bff71999-11-15Jonas Wallden // Button module. Generates graphical buttons for use in Roxen config // interface, Roxen SiteBuilder and other places. //
5dac1c2000-01-10Martin Nilsson // Copyright © 1999-2000 Idonex AB. Author: Jonas Walldén, <jonasw@idonex.se>
67bff71999-11-15Jonas Wallden  // Usage: // // <gbutton // bgcolor -- background color inside/outside button // bordercolor -- button border color // 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
67bff71999-11-15Jonas Wallden // icon_src -- icon reference // icon_data -- inline icon data // align -- left|center|right text alignment // align_icon -- left|center_before|center_after|right icon alignment // >Button text</gbutton> // // Alignment restriction: when text alignment is either left or right, icons // must also be aligned left or right.
fff48a2000-02-21Per Hedbor constant cvs_version = "$Id: gbutton.pike,v 1.29 2000/02/21 17:50:48 per Exp $";
67bff71999-11-15Jonas Wallden constant thread_safe = 1; #include <module.h> inherit "module"; inherit "roxenlib"; roxen.ImageCache button_cache;
c043f82000-02-10Martin Nilsson constant module_type = MODULE_PARSER; constant module_name = "GButton"; constant module_doc = "Provides the <tt>&lt;gbutton&gt;Title&lt;/gbutton&gt;</tt> " "tag for drawing graphical buttons.";
2e0a351999-12-09Martin Nilsson  TAGDOCUMENTATION
5422622000-02-08Martin Nilsson #ifdef manual
2e0a351999-12-09Martin Nilsson constant tagdoc=(["gbutton":"","gbutton-url":""]); /*
67bff71999-11-15Jonas Wallden  "<table border=0>" "<tr><td><b>bgcolor</b></td><td>Background color inside and " "outside button</td></tr>" "<tr><td><b>bordercolor</b></td><td>Button border color</td></tr>" "<tr><td><b>textcolor</b></td><td>Button text color</td></tr>" "<tr><td><b>href</b></td><td>Button URL</td></tr>" "<tr><td><b>alt</b></td><td>Alternative button alt text</td></tr>" "<tr><td><b>border</b></td><td>Image border</td></tr>"
0b91941999-11-16Jonas Wallden  "<tr><td><b>state</b></td><td>Set to <tt>enabled</tt> or " "<tt>disabled</tt> to select button state</td></tr>"
67bff71999-11-15Jonas Wallden 
0b91941999-11-16Jonas Wallden  "<tr><td><b>textstyle</b></td><td>Set to <tt>normal</tt> or " "<tt>condensed</tt> to alter text style.</td></tr>"
67bff71999-11-15Jonas Wallden  "<tr><td><b>icon_src</b></td><td>Icon reference</td></tr>" "<tr><td><b>icon_data</b></td><td>Inline icon data</td></tr>" "<tr><td><b>align</b></td><td>Text alignment: " "<tt>left|center|right</tt></td></tr>" "<tr><td><b>align_icon</b></td><td>Icon alignment: " "<tt>left|center_before|center_after|right</tt></td></tr>" "</table><p>" "There are some alignment restrictions: when text alignment is " "either <tt>left</tt> or <tt>right</tt>, icons must also be "
2e0a351999-12-09Martin Nilsson  "aligned <tt>left</tt> or <tt>right</tt>." */ #endif
67bff71999-11-15Jonas Wallden  void start() { button_cache = roxen.ImageCache("gbutton", draw_button); } mapping query_tag_callers() { return ([ ]); } mapping query_container_callers() {
3014c21999-11-15Per Hedbor  return ([ "gbutton" : tag_button, "gbutton-url" : tag_button ]);
67bff71999-11-15Jonas Wallden }
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 }) ); return o; }
67bff71999-11-15Jonas Wallden  object(Image.Image)|mapping draw_button(mapping args, string text, object id) {
fde9082000-02-08Per Hedbor  Image.Image text_img;
67bff71999-11-15Jonas Wallden  mapping icon;
7169752000-02-02Per Hedbor  object button_font = resolve_font( args->font );
fde9082000-02-08Per Hedbor  Image.Layer background; Image.Layer frame; Image.Layer mask; int left, right, top, bottom; /* offsets */ int req_width;
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  {
fff48a2000-02-21Per Hedbor  if(!l->get_misc_value( "name" ) ) // Hm. continue; 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) ); // otherwise load default images if ( !frame ) set_image( roxen.load_layers("roxen-images/gbutton.xcf", id) ); // 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 = ({}); foreach( frame->get_misc_value( "image_guides" ), object g )
8d387a2000-02-11Jonas Wallden  if( g->pos < 4096 ) if( g->vertical )
fff48a2000-02-21Per Hedbor  x += ({ g->pos-x0 });
8d387a2000-02-11Jonas Wallden  else
fff48a2000-02-21Per Hedbor  y += ({ g->pos-y0 }); 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 
fde9082000-02-08Per Hedbor  left = x[0]; right = x[-1]; top = y[0]; bottom = y[-1]; right = frame->xsize()-right; int text_height = bottom - top;
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();
3ef6452000-02-08Jonas Wallden  int i_spc = i_width ? 5 : 0;
7169752000-02-02Per Hedbor 
67bff71999-11-15Jonas Wallden  // Generate text
fde9082000-02-08Per Hedbor  if (sizeof(text)) { text_img = button_font->write(text)->scale(0, text_height );
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();
67bff71999-11-15Jonas Wallden  // Compute text and icon placement
3ef6452000-02-08Jonas Wallden  req_width = text_img->xsize() + left + right + i_width + i_spc;
944d9e2000-02-08Per Hedbor 
67bff71999-11-15Jonas Wallden  if (args->wi && (req_width < args->wi)) req_width = args->wi;
fde9082000-02-08Per Hedbor  int icn_x, txt_x;
944d9e2000-02-08Per Hedbor 
fde9082000-02-08Per Hedbor  switch (lower_case(args->al)) {
67bff71999-11-15Jonas Wallden  case "left": // Allow icon alignment: left, right
fde9082000-02-08Per Hedbor  switch (lower_case(args->ica)) { case "left": icn_x = left;
3ef6452000-02-08Jonas Wallden  txt_x = icn_x + i_width + i_spc;
fde9082000-02-08Per Hedbor  break; default: case "right": txt_x = left; icn_x = req_width - right - i_width; break;
67bff71999-11-15Jonas Wallden  } break; default: case "center": case "middle": // Allow icon alignment: left, center, center_before, center_after, right
fde9082000-02-08Per Hedbor  switch (lower_case(args->ica)) { case "left": icn_x = left;
3ef6452000-02-08Jonas Wallden  txt_x = (req_width - right - left - i_width - i_spc - t_width) / 2; txt_x += icn_x + i_width + i_spc;
fde9082000-02-08Per Hedbor  break; default: case "center": case "center_before":
3ef6452000-02-08Jonas Wallden  icn_x = (req_width - i_width - i_spc - t_width) / 2; txt_x = icn_x + i_width + i_spc;
fde9082000-02-08Per Hedbor  break; case "center_after":
3ef6452000-02-08Jonas Wallden  txt_x = (req_width - i_width - i_spc - t_width) / 2; icn_x = txt_x + t_width + i_spc;
fde9082000-02-08Per Hedbor  break; case "right": icn_x = req_width - right - i_width;
3ef6452000-02-08Jonas Wallden  txt_x = left + (icn_x - i_spc - t_width) / 2;
fde9082000-02-08Per Hedbor  break;
67bff71999-11-15Jonas Wallden  } break; case "right": // Allow icon alignment: left, right
fde9082000-02-08Per Hedbor  switch (lower_case(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;
3ef6452000-02-08Jonas Wallden  txt_x = icn_x - i_spc - t_width;
fde9082000-02-08Per Hedbor  break;
67bff71999-11-15Jonas Wallden  } break; }
f6ea612000-02-07Per Hedbor 
5321c02000-02-08Per Hedbor  if( args->extra_frame_layers ) { array l = ({ frame }); foreach( args->extra_frame_layers/",", string q ) l += ({ ll[q] }); l-=({ 0 });
fff48a2000-02-21Per Hedbor  if( sizeof( l ) > 1) frame = Image.lay( l );
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  } if( args->extra_background_layers )
3d3a472000-02-08Per Hedbor  { array l = ({ });
5321c02000-02-08Per Hedbor  foreach( args->extra_background_layers/",", string q )
3d3a472000-02-08Per Hedbor  l += ({ ll[q] }); l-=({ 0 });
5321c02000-02-08Per Hedbor  if( sizeof( l ) )
fff48a2000-02-21Per Hedbor  { if( background ) l = ({ background })+l;
5321c02000-02-08Per Hedbor  background = Image.lay( l );
fff48a2000-02-21Per Hedbor  }
3d3a472000-02-08Per Hedbor  }
5321c02000-02-08Per Hedbor  right = frame->xsize()-right; frame = stretch_layer( frame, left, right, req_width ); if (mask != frame) mask = stretch_layer( mask, left, right, req_width );
fde9082000-02-08Per Hedbor  if( background ) { if( !background->alpha() ) background->set_image( background->image(), Image.Image( background->xsize(), background->ysize(), ({255,255,255}) ) ); if( args->dim ) background->set_image(background->image(), background->alpha() * 0.3 ); background = stretch_layer( background, left, right, req_width ); }
f6ea612000-02-07Per Hedbor 
fde9082000-02-08Per Hedbor  Image.Image button = Image.Image(req_width, frame->ysize(), args->bg); button = button->rgb_to_hsv();
1ac32d2000-02-10Jonas Wallden  if( args->dim ) { // 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; } frame->set_image( frame->image()-> modify_by_intensity( 1, 1, 1, ({ dim_low, dim_low, dim_low }), ({ dim_high, dim_high, dim_high })),
fde9082000-02-08Per Hedbor  frame->alpha());
1ac32d2000-02-10Jonas Wallden  }
fde9082000-02-08Per Hedbor  object h = button*({255,0,0}); object s = button*({0,255,0}); object v = button*({0,0,255}); v->paste_mask( frame->image(), frame->alpha() ); button = Image.lay( ({ Image.Layer( h )->set_mode( "red" ), Image.Layer( s )->set_mode( "green" ), Image.Layer( v )->set_mode( "blue" ), }) )->image(); button = button->hsv_to_rgb(); // if there is a background, draw it. if( background ) button->paste_mask( background->image(), background->alpha() ); // fix transparency (somewhat) if( !equal( args->pagebg, args->bg ) ) button->paste_alpha_color( mask->alpha()->invert()->threshold( 200 ), args->pagebg ); // Draw icon. if (icon) { int icn_y = (button->ysize() - icon->img->ysize()) / 2;
7169752000-02-02Per Hedbor 
67bff71999-11-15Jonas Wallden  if (!icon->alpha)
7597c81999-11-24Per Hedbor  icon->alpha = icon->img->clone()->clear(({255,255,255}));
fde9082000-02-08Per Hedbor 
67bff71999-11-15Jonas Wallden  if (args->dim) icon->alpha *= 0.3;
fde9082000-02-08Per Hedbor 
67bff71999-11-15Jonas Wallden  button->paste_mask(icon->img, icon->alpha, icn_x, icn_y); }
f6ea612000-02-07Per Hedbor 
67bff71999-11-15Jonas Wallden  // Draw text if (args->dim) for (int i = 0; i < 3; i++) args->txt[i] = (args->txt[i] + args->bg[i]) / 2;
fde9082000-02-08Per Hedbor 
3c24b62000-01-19Fredrik Noring  if(text_img)
fde9082000-02-08Per Hedbor  button->paste_alpha_color(text_img, args->txt, txt_x, top);
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 ) { array l = ({ }); foreach( args->extra_layers/",", string q ) l += ({ ll[q] }); l-=({ 0 }); if( sizeof( l ) ) { object q = Image.lay( l ); q = stretch_layer( q, left, right, req_width ); button->paste_mask( q->image(), q->alpha() ); } }
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 }); if( sizeof( l ) ) { object q = Image.lay( l ); object b2 = Image.Image( button->xsize()+q->xsize(), button->ysize(), @args->pagebg ); b2->paste( button, q->xsize() ); b2->paste_mask( q->image(), q->alpha() ); button = b2; mask = Image.lay( ({q, mask->set_offset( q->xsize(),0 )}) ); } } // 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 }); if( sizeof( l ) ) { object q = Image.lay( l ); object b2 = Image.Image( button->xsize()+q->xsize(), button->ysize(), @args->pagebg ); b2->paste( button ); b2->paste_mask( q->image(), q->alpha(), button->xsize(), 0 ); button = b2; mask = Image.lay( ({q->set_offset( mask->xsize(),0), mask }) ); } }
5321c02000-02-08Per Hedbor 
fff48a2000-02-21Per Hedbor  return ([
fde9082000-02-08Per Hedbor  "img":button, "alpha":mask->alpha()->threshold( 40 ), ]);
67bff71999-11-15Jonas Wallden } mapping find_internal(string f, RequestID id) { return button_cache->http_file_answer(f, id); } string tag_button(string tag, mapping args, string contents, RequestID id) {
f6ea612000-02-07Per Hedbor  string fi = (args["frame-image"]||id->misc->defines["gbutton-frame-image"]); if( fi ) fi = fix_relative( fi, id );
67bff71999-11-15Jonas Wallden  mapping new_args = ([
f6ea612000-02-07Per Hedbor  "pagebg" :parse_color(id->misc->defines->theme_bgcolor || id->misc->defines->bgcolor || args->bgcolor || "#eeeeee"), // _page_ background color "bg" : parse_color(args->bgcolor || id->misc->defines->theme_bgcolor || id->misc->defines->bgcolor || "#eeeeee"), // Background color
5dac1c2000-01-10Martin Nilsson  "txt" : parse_color(args->textcolor || id->misc->defines->theme_bgcolor || id->misc->defines->fgcolor || "#000000"), // Text color
0b91941999-11-16Jonas Wallden  "cnd" : args->condensed || // Condensed text (lower_case(args->textstyle || "") == "condensed"),
67bff71999-11-15Jonas Wallden  "wi" : (int) args->width, // Min button width "al" : args->align || "left", // Text alignment
0b91941999-11-16Jonas Wallden  "dim" : args->dim || // Button dimming (< "dim", "disabled" >)[lower_case(args->state || "")],
67bff71999-11-15Jonas Wallden  "icn" : args->icon_src && fix_relative(args->icon_src, id), // Icon URL "icd" : args->icon_data, // Inline icon data
7169752000-02-02Per Hedbor  "ica" : args->align_icon || "left", // Icon alignment
f6ea612000-02-07Per Hedbor  "font": (args->font||id->misc->defines->font|| roxen->query("default_font")), "border_image":fi,
3d3a472000-02-08Per Hedbor  "extra_layers":args["extra-layers"],
fff48a2000-02-21Per Hedbor  "extra_left_layers":args["extra-left-layers"], "extra_right_layers":args["extra-right-layers"],
5321c02000-02-08Per Hedbor  "extra_background_layers":args["extra-background-layers"], "extra_mask_layers":args["extra-mask-layers"], "extra_frame_layers":args["extra-frame-layers"],
67bff71999-11-15Jonas Wallden  ]);
5dac1c2000-01-10Martin Nilsson 
95b3891999-11-15Jonas Wallden  new_args->quant = args->quant || 128;
67bff71999-11-15Jonas Wallden  foreach(glob("*-*", indices(args)), string n) new_args[n] = args[n];
95b3891999-11-15Jonas Wallden 
67bff71999-11-15Jonas Wallden  string img_src = query_internal_location() + button_cache->store( ({ new_args, contents }), id);
3014c21999-11-15Per Hedbor  if( tag == "gbutton-url" ) return img_src;
67bff71999-11-15Jonas Wallden  mapping img_attrs = ([ "src" : img_src, "alt" : args->alt || contents,
95b3891999-11-15Jonas Wallden  "border" : args->border, "hspace" : args->hspace, "vspace" : args->vspace ]);
7169752000-02-02Per Hedbor 
67bff71999-11-15Jonas Wallden  if (mapping size = button_cache->metadata(new_args, id, 1)) { // 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; }
5dac1c2000-01-10Martin Nilsson 
67bff71999-11-15Jonas Wallden  // Make button clickable if not dimmed
da94fd1999-11-16Jonas Wallden  if (args->href && !new_args->dim) { mapping a_attrs = ([ "href" : args->href ]); if (args->target) a_attrs->target = args->target; return make_container("a", a_attrs, make_tag("img", img_attrs)); } else
67bff71999-11-15Jonas Wallden  return make_tag("img", img_attrs); }