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 // 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
1ea8c92000-05-25Jonas Wallden // icon-src -- icon reference // icon-data -- inline icon data
67bff71999-11-15Jonas Wallden // align -- left|center|right text alignment
1ea8c92000-05-25Jonas Wallden // align-icon -- left|center-before|center-after|right icon 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.
5b97a62000-06-01Kenneth Johansson constant cvs_version = "$Id: gbutton.pike,v 1.49 2000/06/01 12:19:52 kuntri 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";
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  TAGDOCUMENTATION
5422622000-02-08Martin Nilsson #ifdef manual
5b97a62000-06-01Kenneth Johansson constant imagecache=#"
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <h2>Image cache attributes</h2> All examples are made for the &lt;cimg&gt; tag.
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <attr name='format' value='gif|jpeg|png|avs|gmp|bd|hrz|ilbm|psx|pnm|ps|pvr|tga|tiff|wbf|xbm|xpm' default='gif'> The format to encode the image to. The formats available are: <table> <tr><td>gif</td><td>Graphics Interchange Format (might be missing in your roxen)</td></tr> <tr><td>jpeg</td><td>Joint Photography Expert Group image compression</td></tr> <tr><td>png</td><td>Portable Networks Graphics</td></tr> <tr><td>avs</td><td></td></tr> <tr><td>bmp</td><td>Windows BitMaP file</td></tr> <tr><td>gd</td><td></td></tr> <tr><td>hrz</td><td>HRZ is (was?) used for amatuer radio slow-scan TV.</td></tr> <tr><td>ilbm</td><td></td></tr> <tr><td>pcx</td><td>Zsoft PCX file format (PC / DOS)</td></tr> <tr><td>pnm</td><td>Portable AnyMap</td></tr> <tr><td>ps</td><td>Adobe PostScript file</td></tr> <tr><td>pvr</td><td>Pover VR (dreamcast image)</td></tr> <tr><td>tga</td><td>TrueVision Targa (PC / DOS)</td></tr> <tr><td>tiff</td><td>Tag Image File Format</td></tr> <tr><td>wbf</td><td>WAP Bitmap File</td></tr> <tr><td>xbm</td><td>XWindows Bitmap File</td></tr> <tr><td>xpm</td><td>XWindows Pixmap File</td></tr> </table> <ex> <cimg src='internal-roxen-robodog' format='png'/> </ex> <ex> <cimg src='internal-roxen-robodog' format='gif'/> </ex> </attr>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <attr name='quant' value='number' default='format dependant'> The number of colors to quantizize the image to. <p> Default for gif is 255(+1 transparent), for most other formats (except black and white) is it unlimited.</p>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <ex> <cimg src='internal-roxen-robodog' quant='100'/> </ex> <ex> <cimg src='internal-roxen-robodog' quant='10'/> </ex> <ex> <cimg src='internal-roxen-robodog' quant='2'/> </ex>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson </attr>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <h1>Color/alpha attributes</h1>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <attr name='dither' value='none|random|floyd-steinberg' default='none'> Choose the dithering method. <table> <tr><td>none</td><td>No dithering is performed at all.</td></tr> <tr><td>random</td><td>Random scatter dither. Not visually pleasing, but it is useful for very high resolution printing.</td></tr> <tr><td>floyd-steinberg</td><td>Error diffusion dithering. Usually the best dithering method.</td></tr> </table>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <ex> <cimg src='internal-roxen-robodog' dither='random' quant='10'/> </ex> <ex> <cimg src='internal-roxen-robodog' dither='floyd-steinberg' quant='10'/> </ex> </attr>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <attr name='true-alpha'> If present, render a real alpha channel instead of on/off alpha. If the file format only supports on/off alpha, the alpha channel is dithered using a floyd-steinberg dither.
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <ex> <cimg src='internal-roxen-robodog' opaque-value='20'/> </ex> <ex> <cimg src='internal-roxen-robodog' opaque-value='20' true-alpha='1'/> </ex> </attr>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <attr name='background-color' value='color' default='taken from the page'> The color to render the image against. <ex> <cimg src='internal-roxen-robodog' background-color='black' opaque-value='50'/> </ex> </attr>
67bff71999-11-15Jonas Wallden 
5b97a62000-06-01Kenneth Johansson <attr name='opaque-value' value='percentage' default='100'> The transparency value to use, 100 is fully opaque, and 0 is fully transparent. </attr> <attr name='cs-rgb-hsv' value='0|1' default='0'> Perform rgb to hsv colorspace conversion. <ex> <cimg src='internal-roxen-robodog' cs-rgb-hsv='1'/> </ex> </attr> <attr name='gamma' value='number' default='1.0'> Perform gamma adjustment. <ex> <cimg src='internal-roxen-robodog' gamma='0.1'/> </ex> <ex> <cimg src='internal-roxen-robodog' gamma='0.5'/> </ex> <ex> <cimg src='internal-roxen-robodog' gamma='1.0'/> </ex> <ex> <cimg src='internal-roxen-robodog' gamma='2.0'/> </ex> <ex> <cimg src='internal-roxen-robodog' gamma='8.0'/> </ex> </attr> <attr name='cs-grey' value='0|1' default='0'> Perform rgb to greyscale colorspace conversion. <ex> <cimg src='internal-roxen-robodog' cs-grey='1'/> </ex> </attr> <attr name='cs-invert' value='0|1' default='0'> Invert all colors <ex> <cimg src='internal-roxen-robodog' cs-invert='1'/> </ex> </attr> <attr name='cs-hsv-rgb' value='0|1' default='0'> Perform hsv to rgb colorspace conversion. <ex> <cimg src='internal-roxen-robodog' cs-hsv-rgb='1'/> </ex> <ex> <cimg src='internal-roxen-robodog' cs-grey='1' cs-hsv-rgb='1' cs-rgb-hsv='1'/> </ex> <ex> <cimg src='internal-roxen-robodog' cs-hsv-rgb='1' cs-invert='1' cs-rgb-hsv='1/> </ex> </attr> <h1>Transform attributes</h1> <attr name='rotate-cw' value='degree' default='0'> Rotate the image clock-wise. <ex> <cimg src='internal-roxen-robodog' rotate-cw='20'/> </ex> <ex> <cimg src='internal-roxen-robodog' rotate-cw='90'/> </ex> </attr> <attr name='rotate-ccw' value='degree' default='0'> Rotate the image counter clock-wise. <ex> <cimg src='internal-roxen-robodog' rotate-ccw='20'/> </ex> <ex> <cimg src='internal-roxen-robodog' rotate-ccw='90'/> </ex> </attr> <attr name='rotate-unit' value='rad|deg|ndeg|part' default='deg'> Select the unit to use while rotating. <table> <tr><td>rad</td><td>Radians</td></tr> <tr><td>deg</td><td>Degrees</td></tr> <tr><td>ndeg</td><td>'New' degrees (400 for each full rotation)</td></tr> <tr><td>part</td><td>0 - 1.0 (1.0 == full rotation)</td></tr> </table> <ex> <cimg src='internal-roxen-robodog' rotate-ccw='1.2' rotate-unit='rad'/> </ex> <ex> <cimg src='internal-roxen-robodog' rotate-ccw='20' rotate-unit='deg'/> </ex> <ex> <cimg src='internal-roxen-robodog' rotate-ccw='0.2' rotate-unit='part'/> </ex> </attr> <attr name='mirror-x' value='0|1' default='0'> Mirror the image around the X-axis. <ex> <cimg src='internal-roxen-robodog' mirror-x='1'/> </ex> </attr> <attr name='mirror-y' value='0|1' default='0'> Mirror the image around the Y-axis. <ex> <cimg src='internal-roxen-robodog' mirror-y='1'/> </ex> </attr> <attr name='scale' value='fact' default='1.0'> Scale fact times. (0.5 -> half size, 2.0 -> double size) <ex> <cimg src='internal-roxen-robodog' scale='0.5'/> </ex> <ex> <cimg src='internal-roxen-robodog' scale='1.2'/> </ex> </attr> <attr name='scale' value='x,y'> Scale to the exact size x,y. If either of X or Y is zero, the image is scaled to the specified width or hight, and the value that is zero is scaled in proportion to the other value. <ex> <cimg src='internal-roxen-robodog' scale='10,40'/> </ex> <ex> <cimg src='internal-roxen-robodog' scale='100,0'/> </ex> <ex> <cimg src='internal-roxen-robodog' scale='0,10'/> </ex> <ex> <cimg src='internal-roxen-robodog' scale='100,10'/> </ex> </attr> <attr name='max-width' value='xsize'> If width is larger than 'xsize', scale width to 'xsize' while keeping aspect. <ex> <cimg src='internal-roxen-robodog' max-width='300'/> </ex> <ex> <cimg src='internal-roxen-robodog' max-width='100'/> </ex> </attr> <attr name='max-height' value='ysize'> If width is larger than 'ysize', scale width to 'ysize' while keeping aspect. <ex> <cimg src='internal-roxen-robodog' max-height='300'/> </ex> <ex> <cimg src='internal-roxen-robodog' max-height='30'/> </ex> </attr> <attr name='x-offset' value='pixels' default='0'> Cut n pixels from the beginning of the X scale. <ex> <cimg src='internal-roxen-robodog' x-offset='10'/> </ex> <ex> <cimg src='internal-roxen-robodog' x-offset='50'/> </ex> </attr> <attr name='y-offset' value='pixels' default='0'> Cut n pixels from the beginning of the Y scale. <ex> <cimg src='internal-roxen-robodog' y-offset='10'/> </ex> <ex> <cimg src='internal-roxen-robodog' y-offset='30'/> </ex> </attr> <attr name='x-size' value='pixels' default='whole image'> Keep n pixels from the beginning of the X scale. <ex> <cimg src='internal-roxen-robodog' x-size='100'/> </ex> </attr> <attr name='y-size' value='pixels' default='whole image'> Keep n pixels from the beginning of the Y scale. <ex> <cimg src='internal-roxen-robodog' y-size='30'/> </ex> <ex> <cimg src='internal-roxen-robodog' y-size='100'/> </ex> </attr> <attr name=crop value='x0,y0-x1,y1' default='whole image'> Crop the image by specifying the pixel coordinates. <ex> <cimg src='internal-roxen-robodog' crop='50,00-150,20'/> </ex> <ex> <cimg src='internal-roxen-robodog' crop='50,28-150,92'/> </ex> <ex> <cimg src='internal-roxen-robodog' crop='0,0-200,20'/> </ex> <ex> <cimg src='internal-roxen-robodog' crop='0,28-200,92'/> </ex> </attr> <h1>Format specific attributes</h1> <attr name='jpeg-quality' value='percentage' default='75'> Set the quality on the output jpeg image. <ex> <cimg src='internal-roxen-robodog' format='jpeg' jpeg-quality='100'/> </ex> <ex> <cimg src='internal-roxen-robodog' format='jpeg' jpeg-quality='30'/> </ex> <ex> <cimg src='internal-roxen-robodog' format='jpeg' jpeg-quality='1'/> </ex> </attr> <attr name='jpeg-optimize' value='0|1' default='1'> If 0, do not generate optimal tables. Somewhat faster, but produces bigger files. </attr> <attr name='jpeg-progressive=' value='0|1' default='0'> Generate progressive jpeg images. </attr> <attr name='jpeg-smooth' value='0-100' default='0'> Smooth the image while compressing it. This produces smaller files, but might undo the effects of dithering. <ex> <cimg src='internal-roxen-robodog' format='jpeg' jpeg-quality='10' jpeg-smooth='0'/> </ex> <ex> <cimg src='internal-roxen-robodog' format='jpeg' jpeg-quality='10' jpeg-smooth='100'/> </ex> </attr> <attr name='bmp-bpp' value='1,4,8,24' default='24'> Force this number of bits per pixel for bmp images. </attr> <attr name='bmp-windows' value='0|1' default='1'> Windows or OS/2 mode, default is 1. (windows mode) </attr> <attr name='bmp-rle' value='0|1' default='0'> RLE 'compress' the BMP image. </attr> <attr name='gd-alpha_index' value='color' default='0'> Color in the colormap to make transparent for GD-images with alpha channel. </attr> <attr name='pcx-raw' value='1|0' default='0'> If 1, do not RLE encode the PCX image. </attr> <attr name='pcx-dpy' value='0-10000000.0' default='75.0'> Resolution, in pixels per inch. </attr> <attr name='pcx-xdpy' value='0-10000000.0' default='75.0'> Resolution, in pixels per inch. </attr> <attr name='pcx-ydpy' value='0-10000000.0' default='75. Resolution, in pixels per inch. </attr> <attr name='pcx-xoffset' value='0-imagexsize-2' default='0'> Offset from start of image data to image content for PCX images. Unused by most programs. </attr> <attr name='pcx-yoffset' value='0-imageysize-2' default='0'> Offset from start of image data to image content for PCX images. Unused by most programs. </attr> <attr name='tga-raw' value='1|0' default='0'> If 1, do not RLE encode the Targa image. </attr> <attr name='ps-dpi' value='0-10000000.0' default='75.0'> Dots per inch for the resulting postscript file. </attr>"; constant gbuttonattr=#" <attr name='pagebgcolor' value='color'> </attr> <attr name='bgcolor' value='color'> Background color inside and outside button. <ex> <gbutton bgcolor='blue'>Background</gbutton> </ex> </attr> <attr name='bordercolor' value='color'> Button border color. <ex> <gbutton bordercolor='red'>Border</gbutton> </ex> </attr> <attr name='textcolor' value='color'> Button text color <ex> <gbutton bordercolor='red' textcolor='green'> Text</gbutton> </ex> </attr> <attr name=frame-image value='path'> 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 Roxen documentation; Web Site Creator/Graphical tags section. <ex> <gbutton frame-image='internal-roxen-tabframe'>foo</gbutton> </ex> </attr> <attr name='border_image' value='path'> Use this image as border. </attr> <attr name='alt' value='string'> Alternative button and alt text. </attr> <attr name='href' value='uri'> Button URI. </attr> <attr name='textstyle' value='normal|condensed'> Set to <att>normal</att> or <att>condensed</att> to alter text style. </attr> <attr name='width' value=''> Minimum button width. </attr> <attr name='align' value='left|center|right'> Set text alignment. There are some alignment restrictions: when text alignment is either <att>left</att> or <att>right</att>, icons must also be aligned <att>left</att> or <att>right</att>. </attr> <attr name='state' value='enabled|disabled'> Set to <att>enabled</att> or <att>disabled</att> to select button state. </attr> <attr name='icon-src' value='URI'> Fetch the icon from this URI. </attr> <attr name='icon-data' value=''> Inline icon data. </attr> <attr name='align-icon' value='left|center-before|center-after|right'> Set icon alignment. <table> <tr><td>left</td><td>Place icon on the left side of the text.</td></tr> <tr><td>center-before</td><td>Center the icon before the text.Requires the <att>align='center'</att> attribute.</td></tr> <tr><td>center-after</td><td>Center the icon after the text. Requires the <att>align='center'</att> attribute.</td></tr> <tr><td>right</td><td>Place icon on the right side of the text.</td></tr> <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> <attr name='font' value=''> </attr> <attr name='extra-layers' value='[''],[first|last],[selected|unselected],[background|mask|frame|left|right]'> </attr> <attr name='extra-left-layers' value='[''],[first|last],[selected|unselected],[background|mask|frame|left|right]'> </attr> <attr name='extra-right-layers' value='[''],[first|last],[selected|unselected],[background|mask|frame|left|right]'> </attr> <attr name='extra-background-layers' value='[''],[first|last],[selected|unselected],[background|mask|frame|left|right]'> </attr> <attr name='extra-mask-layers' value='[''],[first|last],[selected|unselected],[background|mask|frame|left|right]'> </attr> <attr name='extra-frame-layers' value='[''],[first|last],[selected|unselected],[background|mask|frame|left|right]'> <ex> <gbutton border_image='gbutton.xcf' alt='foo'>bu</gbutton> </ex> <ex> <gbutton alt='Edit' bgcolor='#aeaeae' extra-background-layers='unselected background,last unselected background,last background' extra-frame-layers='unselected frame,last unselected frame,last frame' extra-layers='unselected,last unselected,last' extra-left-layers='unselected left,last unselected left,last left' extra-mask-layers='unselected mask,last unselected mask,last mask' extra-right-layers='unselected right,last unselected right,last right' > </gbutton> </ex> </attr> <!-- <table> <tr><td>A</td><td>B</td><td>C</td></tr> <tr><td>''</td><td>''</td><td>''</td></tr> <tr><td>'first'</td><td>'selected'</td><td>'background'</td></tr> <tr><td>'last'</td><td>'unselected'</td><td>'mask'</td></tr> <tr><td></td><td></td><td>'frame'</td></tr> <tr><td></td><td></td><td>'left'</td></tr> <tr><td></td><td></td><td>'right'</td></tr> </table> -->"; constant tagdoc=(["gbutton":#"<desc cont><short>Creates graphical buttons.</short> </desc>" +gbuttonattr +imagecache, "gbutton-url":#"<desc><short>Generates an URI to the button.</short> <tag>gbutton-url</tag> takes the same attributes as <tag>gbutton</tag> including the image cache attributes.</desc>" +gbuttonattr +imagecache, ]);
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 }
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;
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 )
f29b142000-03-24Per Hedbor  set_image( roxen.load_layers("/internal-roxen-gbutton", id) );
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 = ({}); 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();
6ff8722000-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)) { 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
6ff8722000-05-24Jonas Wallden  req_width = (text_img && 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":
1ea8c92000-05-25Jonas Wallden  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":
1ea8c92000-05-25Jonas Wallden  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 ) {
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 ) ) frame = Image.lay( l+({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; frame = stretch_layer( frame, left, right, req_width ); if (mask != frame) mask = stretch_layer( mask, left, right, req_width );
e0fda72000-04-04Marcus Comstedt  array(Image.Layer) button_layers = ({ Image.Layer( Image.Image(req_width, frame->ysize(), args->bg),
8a98622000-04-04Per Hedbor  mask->alpha()->scale(req_width,frame->ysize())),
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 
8a98622000-04-04Per Hedbor  button_layers += ({ frame });
12493c2000-02-22Per Hedbor  frame->set_mode( "value" ); 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,
12493c2000-02-22Per Hedbor  "yoffset":(frame->ysize()-icon->img->ysize())/2,
1a54422000-02-21Per Hedbor  ]) )});
f6ea612000-02-07Per Hedbor 
67bff71999-11-15Jonas Wallden  // Draw text
3c24b62000-01-19Fredrik Noring  if(text_img)
1a54422000-02-21Per Hedbor  button_layers += ({ Image.Layer(([
12493c2000-02-22Per Hedbor  "alpha_value":(args->dim ? 0.5 : 1.0),
78bca32000-02-28Jonas Wallden  "image":text_img->color(0,0,0)->invert()->color(@args->txt),
735b932000-02-24Per Hedbor  "alpha":text_img,
1a54422000-02-21Per Hedbor  "xoffset":txt_x, "yoffset":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 ) {
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)}); }
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 }); 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 ); }
185bd42000-02-21Per Hedbor  q->set_offset( 0, 0 );
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 }); if( sizeof( l ) ) { object q = Image.lay( l );
a76b0a2000-02-22Per Hedbor  q->set_offset( button_layers[0]->xsize()+ button_layers[0]->xoffset(),0);
1a54422000-02-21Per Hedbor  button_layers += ({ q });
fff48a2000-02-21Per Hedbor  } }
5321c02000-02-08Per Hedbor 
1a54422000-02-21Per Hedbor  button_layers = ({Image.lay( button_layers )});
185bd42000-02-21Per Hedbor  // fix transparency (somewhat) if( !equal( args->pagebg, args->bg ) )
8a98622000-04-04Per Hedbor  return button_layers + ({ Image.Layer(([ "fill":args->pagebg, "alpha":button_layers[0]->alpha()->invert(), ])) });
1a54422000-02-21Per Hedbor  return button_layers;
67bff71999-11-15Jonas Wallden } mapping find_internal(string f, RequestID id) { return button_cache->http_file_answer(f, id); }
c421c62000-03-14Martin Nilsson class ButtonFrame { inherit RXML.Frame;
f29b142000-03-24Per Hedbor  array mk_url(RequestID id) {
c421c62000-03-14Martin Nilsson  string fi = (args["frame-image"]||id->misc->defines["gbutton-frame-image"]); if( fi ) fi = fix_relative( fi, id );
1ea8c92000-05-25Jonas Wallden  // Harmonize some attribute names to RXML standards... 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; m_delete(args, "icon-src"); m_delete(args, "icon-data"); m_delete(args, "align-icon");
c421c62000-03-14Martin Nilsson  mapping new_args = ([ "pagebg" :parse_color(args->pagebgcolor || 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 "txt" : parse_color(args->textcolor || id->misc->defines->theme_bgcolor || id->misc->defines->fgcolor || "#000000"), // Text color "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 && fix_relative(args->icon_src, id), // Icon URL "icd" : args->icon_data, // Inline icon data "ica" : args->align_icon || "left", // Icon alignment "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"], ]); new_args->quant = args->quant || 128; foreach(glob("*-*", indices(args)), string n) new_args[n] = args[n]; string img_src = query_internal_location() + button_cache->store( ({ new_args, content }), id); return ({ img_src, new_args }); } } class TagGButtonURL { inherit RXML.Tag; constant name = "gbutton-url"; 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 
c421c62000-03-14Martin Nilsson class TagGButtom { inherit RXML.Tag; constant name = "gbutton"; RXML.Type content_type = RXML.t_text(RXML.PXml); class Frame { inherit ButtonFrame; array do_return(RequestID id) {
1ea8c92000-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 ]);
1ea8c92000-05-25Jonas Wallden  if (img_align) img_attrs->align = img_align;
c421c62000-03-14Martin Nilsson  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; }
dbc2722000-03-24Johan Sundström  result = make_tag("img", img_attrs);
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) if(has_value("target/onmousedown/onmouseup/onclick/ondblclick/onmouseout/" "onmouseover/onkeypress/onkeyup/onkeydown"/"/", lower_case(arg))) a_attrs[arg] = args[arg]; result = make_container("a", a_attrs, result); }
c421c62000-03-14Martin Nilsson  return 0; } }
67bff71999-11-15Jonas Wallden }