button._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2004 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
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.
7  *
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.
12  *
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
16  *
17  * $Id: button.pike,v 1.1 2008/03/31 13:39:57 exodusd Exp $
18  */
19 inherit "/kernel/module";
20 #include <database.h>
21 #include <macros.h>
22 class button : public module{
23 public:
24 
25 
26 
27 
28 
29 Image.Layer layer_slice( Image.Layer l, int from, int to )
30 {
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 ),
34  ]) );
35 }
36 
37 Image.Layer stretch_layer( Image.Layer o, int x1, int x2, int w )
38 {
39  Image.Layer l, m, r;
40  int leftovers = w - (x1 + (o->xsize()-x2) );
41  object oo = o;
42 
43  l = layer_slice( o, 0, x1 );
44  m = layer_slice( o, x1+1, x2-1 );
45  r = layer_slice( o, x2, o->xsize() );
46 
47  m->set_image( m->image()->scale( leftovers, l->ysize() ),
48  m->alpha()->scale( leftovers, l->ysize() ));
49 
50  l->set_offset( 0,0 );
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() );
56  return o;
57 }
58 
59 mapping load_icon(string path)
60 {
61  werror("Loading Icon="+path+"\n");
62 }
63 
64 
65 array(Image.Layer) load_layers(string path)
66 {
67  object obj = _FILEPATH->path_to_object(path);
68  string data = obj->get_content();
69  return Image.decode_layers(data);
70 }
71 
72 Image.Font resolve_font(string font)
73 {
74  object fontobj = Image.Font();
75 
76  fontobj->load("server/modules/test.fnt");
77  return fontobj;
78 }
79 
80 array(Image.Layer)|mapping draw_button(mapping args, string text)
81 {
82  Image.Image text_img;
83  mapping icon;
84 
85  Image.Layer background;
86  Image.Layer frame;
87  Image.Layer mask;
88 
89  int left, right, top, middle, bottom; /* offsets */
90  int req_width, noframe;
91 
92  mapping ll = ([]);
93 
94  void set_image( array layers )
95  {
96  foreach( layers||({}), object l )
97  {
98  if(!l->get_misc_value( "name" ) ) // Hm. Probably PSD
99  continue;
100 
101  ll[lower_case(l->get_misc_value( "name" ))] = l;
102  switch( lower_case(l->get_misc_value( "name" )) )
103  {
104  case "background": background = l; break;
105  case "frame": frame = l; break;
106  case "mask": mask = l; break;
107  }
108  }
109  };
110 
111  if( args->border_image )
112  {
113  array(Image.Layer)|mapping tmp = load_layers(args->border_image);
114  if (mappingp(tmp))
115  if (tmp->error == 401)
116  return tmp;
117  else
118  error("GButton: Failed to load frame image: %O\n",
119  args->border_image);
120  set_image( tmp );
121  }
122 
123 
124  // otherwise load default images
125  if ( !frame && !background && !mask )
126  {
127  string data = Stdio.read_file("gbutton.xcf");
128  if (!data)
129  error ("Failed to load default frame image "
130  "(roxen-images/gbutton.xcf): " + strerror (errno()));
131  mixed err = catch {
132  set_image(Image.XCF.decode_layers(data));
133  };
134  if( !frame )
135  if (err) {
136  catch (err[0] = "Failed to decode default frame image "
137  "(gbutton.xcf): " + err[0]);
138  throw (err);
139  }
140  else
141  error("Failed to decode default frame image "
142  "(roxen-images/gbutton.xcf).\n");
143  }
144 
145  if( !frame )
146  {
147  noframe = 1;
148  frame = background || mask; // for sizes offsets et.al.
149  }
150 
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();
155  if( x0 || y0 )
156  foreach( values( ll ), object l )
157  {
158  int x = l->xoffset();
159  int y = l->yoffset();
160  l->set_offset( x-x0, y-y0 );
161  }
162 
163  if( !mask )
164  mask = frame;
165 
166  array x = ({});
167  array y = ({});
168 
169  foreach( frame->get_misc_value( "image_guides" ), object g )
170  if( g->vertical )
171  x += ({ g->pos - x0 });
172  else
173  y += ({ g->pos - y0 });
174 
175  sort( y );
176  sort( x );
177 
178  if(sizeof( x ) < 2)
179  x = ({ 5, frame->xsize()-5 });
180 
181  if(sizeof( y ) < 2)
182  y = ({ 2, frame->ysize()-2 });
183 
184  left = x[0]; right = x[-1]; top = y[0]; middle = y[1]; bottom = y[-1];
185  right = frame->xsize()-right;
186 
187  // Text height depends on which guides we should align to
188  int text_height;
189  switch (args->icva) {
190  case "above":
191  text_height = bottom - middle;
192  break;
193  case "below":
194  text_height = middle - top;
195  break;
196  default:
197  case "middle":
198  text_height = bottom - top;
199  break;
200  }
201 
202  // Get icon
203  if (args->icn)
204  icon = load_icon(args->icn);
205  else if (args->icd)
206  icon = load_icon(args->icd);
207 
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;
211 
212  // Generate text
213  if (sizeof(text))
214  {
215  int os, dir;
216  Image.Font button_font;
217  int th = text_height;
218  do
219  {
220  button_font = resolve_font( args->font+" "+th );
221  text_img = button_font->write(text);
222  os = text_img->ysize();
223  if( !dir )
224  if( os < text_height )
225  dir = 1;
226  else if( os > text_height )
227  dir =-1;
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;
231  th += dir;
232  } while( (text_img->ysize() - text_height)
233  && (th>0 && th<text_height*2));
234 
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 );
238  else
239  {
240  int o = text_img->ysize() - text_height;
241  top -= o;
242  middle -= o/2;
243  }
244  if (args->cnd)
245  text_img = text_img->scale((int) round(text_img->xsize() * 0.8),
246  text_img->ysize());
247  } else
248  text_height = 0;
249 
250  int t_width = text_img && text_img->xsize();
251 
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;
259 
260  int icn_x, icn_y, txt_x, txt_y;
261 
262  // Are text and icon lined up or on separate lines?
263  switch (args->icva) {
264  case "above":
265  case "below":
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) {
271  txt_y = middle;
272  icn_y = top + ((text_height ? middle : bottom) - top - i_height) / 2;
273  } else {
274  txt_y = top;
275  icn_y = middle + (bottom - middle - i_height) / 2;
276  }
277  break;
278 
279  default:
280  case "middle":
281  // Center icon vertically on same line as text
282  icn_y = icon && (frame->ysize() - icon->img->ysize()) / 2;
283  txt_y = top;
284 
285  switch (args->al)
286  {
287  case "left":
288  // Allow icon alignment: left, right
289  switch (args->ica)
290  {
291  case "left":
292  icn_x = left;
293  txt_x = icn_x + i_width + i_spc;
294  break;
295  default:
296  case "right":
297  txt_x = left;
298  icn_x = req_width - right - i_width;
299  break;
300  }
301  break;
302 
303  default:
304  case "center":
305  case "middle":
306  // Allow icon alignment:
307  // left, center, center-before, center-after, right
308  switch (args->ica)
309  {
310  case "left":
311  icn_x = left;
312  txt_x = (req_width - right - left - i_width - i_spc - t_width) / 2;
313  txt_x += icn_x + i_width + i_spc;
314  break;
315  default:
316  case "center":
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;
321  break;
322  case "center_after":
323  case "center-after":
324  txt_x = (req_width - i_width - i_spc - t_width) / 2;
325  icn_x = txt_x + t_width + i_spc;
326  break;
327  case "right":
328  icn_x = req_width - right - i_width;
329  txt_x = left + (icn_x - i_spc - t_width - left) / 2;
330  break;
331  }
332  break;
333 
334  case "right":
335  // Allow icon alignment: left, right
336  switch (args->ica)
337  {
338  default:
339  case "left":
340  icn_x = left;
341  txt_x = req_width - right - t_width;
342  break;
343  case "right":
344  icn_x = req_width - right - i_width;
345  txt_x = icn_x - i_spc - t_width;
346  break;
347  }
348  break;
349  }
350  break;
351  }
352 
353  if( args->extra_frame_layers )
354  {
355  array l = ({ });
356  foreach( args->extra_frame_layers/",", string q )
357  l += ({ ll[q] });
358  l-=({ 0 });
359  if( sizeof( l ) )
360  frame = Image.lay( l+(noframe?({}):({frame})) );
361  }
362 
363  if( args->extra_mask_layers )
364  {
365  array l = ({ });
366  foreach( args->extra_mask_layers/",", string q )
367  l += ({ ll[q] });
368  l-=({ 0 });
369  if( sizeof( l ) )
370  {
371  if( mask )
372  l = ({ mask })+l;
373  mask = Image.lay( l );
374  }
375  }
376 
377  right = frame->xsize()-right;
378  if (mask != frame)
379  {
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;
386 
387  i = i->copy(x0,y0, x1,y1);
388  if( m )
389  m = m->copy(x0,y0, x1,y1);
390  mask->set_image( i, m );
391  mask = stretch_layer( mask, left, right, req_width );
392  }
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)),
398  });
399 
400 
401  if( args->extra_background_layers || background)
402  {
403  array l = ({ background });
404  foreach( (args->extra_background_layers||"")/","-({""}), string q )
405  l += ({ ll[q] });
406  l-=({ 0 });
407  foreach( l, object ll )
408  {
409  if( args->dim )
410  ll->set_alpha_value( 0.3 );
411  button_layers += ({ stretch_layer( ll, left, right, req_width ) });
412  }
413  }
414 
415  if( !noframe )
416  {
417  button_layers += ({ frame });
418  frame->set_mode( "value" );
419  }
420 
421  if( args->dim )
422  {
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;
429  } else {
430  dim_high = min(bg_value + 64, 255);
431  dim_low = dim_high - 128;
432  }
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 })),
437  frame->alpha());
438  }
439 
440  // Draw icon.
441  if (icon)
442  button_layers += ({
443  Image.Layer( ([
444  "alpha_value":(args->dim ? 0.3 : 1.0),
445  "image":icon->img,
446  "alpha":icon->alpha,
447  "xoffset":icn_x,
448  "yoffset":icn_y
449  ]) )});
450 
451  // Draw text
452  if(text_img)
453  {
454  float ta = args->txtalpha?args->txtalpha:1.0;
455  button_layers +=
456  ({
457  Image.Layer(([
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)),
461  "xoffset":txt_x,
462  "yoffset":txt_y,
463  ]))
464  });
465  }
466 
467  // 'plain' extra layers are added on top of everything else
468  if( args->extra_layers )
469  {
470  array q = map(args->extra_layers/",",
471  lambda(string q) { return ll[q]; } )-({0});
472  foreach( q, object ll )
473  {
474  if( args->dim )
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() );
480  }
481  }
482 
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 )
488  {
489  array l = ({ });
490  foreach( args->extra_left_layers/",", string q )
491  l += ({ ll[q] });
492  l-=({ 0 });
493  l->set_offset( 0, 0 );
494  if( sizeof( l ) )
495  {
496  object q = Image.lay( l );
497  foreach( button_layers, object b )
498  {
499  int x = b->xoffset();
500  int y = b->yoffset();
501  b->set_offset( x+q->xsize(), y );
502  }
503  q->set_offset( 0, button_layers[0]->ysize()-q->ysize() );
504  button_layers += ({ q });
505  }
506  }
507 
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 )
512  {
513  array l = ({ });
514  foreach( args->extra_right_layers/",", string q )
515  l += ({ ll[q] });
516  l-=({ 0 });
517  l->set_offset( 0, 0 );
518  if( sizeof( l ) )
519  {
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 });
525  }
526  }
527 
528 // if( !equal( args->pagebg, args->bg ) )
529 // {
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;
536  else
537  return button_layers;
538 // }
539 }
540 
541 
542 mixed tab(mapping args)
543 {
544  mapping gbutton_args = args;
545 
546 
547  string fimage;
548  if ( !stringp(args->frame_image) ) {
549  fimage = Stdio.read_file("tabframe.xcf");
550  gbutton_args["frame-image"] = fimage;
551  }
552  else {
553  object fimg = _FILEPATH->path_to_object(args->frame_image);
554  fimage = fimg->get_content();
555  }
556 
557 
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");
565  } else {
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);
571  }
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;
578 }
579 
580 array execute(mapping args)
581 {
582  array layers = tab(args);
583  object img = Image.lay( layers );
584  string str = Image.PNG->encode(img->image());
585  return ({ str, "image/gif" });
586 }
587 
588 string get_identifier() { return "buttons"; }
589 
590 #if 0
591 void main(int argc, array argv)
592 {
593 
594  object img = Image.lay( layers );
595  Stdio.write_file("test.jpg", Image.GIF->encode(img->image()));
596 }
597 #endif
598 
599 
600 };