//<copyright>
//
// Copyright (c) 1993-1996
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
//
//</copyright>


//<file>
//
// Name: bubblewindow.C
//
// Purpose: creates a popup window with bubble style and shading around the bubble
//
// $Id: bubblewindow.C,v 1.11 1996/10/22 10:37:07 jschipf Exp $
//
// Created: 5 Sept 95 Peter Kogler
//
// Modified: 13 Sept 95 Peter Kogler
//
// Description:
//
// BubbleWindow creates a popup window, which ist not under the control of the window
// manager, and the background is transparent.
// BubbleWindow shows its contained glyph in a bubble, which may be round or squary
// and can show in every direction (up ,down, left, right). The bubble has a shadowed
// outline, the frame of the bubble and the inner background of the bubble can be
// colored in any appropriate color. The size of the frame of the bubble can be set.
// In this implementation BubbleWindow pops up near the mouse coordinates in the right
// direction.
//
// $Log: bubblewindow.C,v $
// Revision 1.11  1996/10/22 10:37:07  jschipf
// remoed some memory leaks (foreground, background, brush) and potential
// bugs (first ref and than unref)
//
// Revision 1.10  1996/04/09 15:32:49  bheide
// changed OS/string.h -> hypger/OS/string.h
//
// Revision 1.9  1996/03/13 16:31:27  bmarsch
// Changed default bump_x
//
// Revision 1.8  1996/02/21 14:32:32  bmarsch
// Further beautified default bubble window
//
// Revision 1.6  1996/02/12 14:30:15  pkogler
// Made shape of BubbleWindows configurable (Xdefaults bump_x, bump_y,
// distance_x, distance_y)
//
// Revision 1.1  1996/02/08 15:02:10  pkogler
// Initial revision
//
// Revision 1.5  1996/01/19 14:50:30  bmarsch
// Corrected placing of window when it doesn't fit on the screen
//
//</file>

#include <InterViews/session.h>

// include interviews libraries
#include <hyperg/OS/string.h>
#include <IV-X11/xdisplay.h>
#include <IV-X11/xcanvas.h>
#include <InterViews/brush.h>
#include <InterViews/canvas.h>
#include <InterViews/color.h>
#include <InterViews/glyph.h>
#include <InterViews/layout.h>
#include <InterViews/style.h>
#include <IV-look/kit.h>

// include everything else
#include "bubblewindow.h"

//<function>
//
// Prototype: BubbleWindow
//            (
//              Glyph* glyph
//            );
//
// Purpose: Creates a BubbleWindow with a contained glyph to be showed.
//
// Description:
//
// Creates a BubbleWindow with a contained glyph glyph to be showed. The Glyph has its
// own style (background, color and so on). Default values are set.
//
//</function>
BubbleWindow::BubbleWindow(Glyph* glyph) : PopupWindow(LayoutKit::instance()->margin(glyph,21.0f))
{
  // ref glyph
  p_glyph_=glyph;
  Resource::ref(p_glyph_);
  // begin a new style
  WidgetKit& widget=*WidgetKit::instance();
  widget.begin_style("BubbleWindow");
  widget.style()->attribute("double_buffered","off");
  style(widget.style());
  // define standard window shape and direction
  shape(OVAL);
  direction(RIGHT,DOWN);
  // get shape attribute
  String shapestring;
  if (widget.style()->find_attribute("shape",shapestring))
  {
    if (shapestring=="oval")
      shape(OVAL);
    if (shapestring=="rectangle")
      shape(RECTANGLE);
  }
  // define standard brush
  Coord brushcoord;
  // get brush attribute
  p_brush_=nil;
  brush(new Brush(1.0f));
  if (widget.style()->find_attribute("brush",brushcoord))
  {
    brush(new Brush(brushcoord));
  }
  // get bump attributes
  Coord bump_x;
  if (!widget.style()->find_attribute("bump_x",bump_x))
    bump_x=0.04f;
  Coord bump_y;
  if (!widget.style()->find_attribute("bump_y",bump_y))
    bump_y=0.25f;
  // get distance attributes
  Coord distance_x;
  if (!widget.style()->find_attribute("distance_x",distance_x))
    distance_x=0.15f;
  Coord distance_y;
  if (!widget.style()->find_attribute("distance_y",distance_y))
    distance_y=0.15f;
  bump(bump_x,bump_y,distance_x,distance_y);
  // define standard colors
  String colorstring;
  // get foreground color attribute 
  p_foreground_color_=nil;
  foreground(new Color(0.0f,0.0f,0.0f));
  if (widget.style()->find_attribute("foreground",colorstring))
  {
    const Color* color=
      Color::lookup(Session::instance()->default_display(),colorstring);
    if (color)
      foreground(color);
  }
  // get background color attribute
  p_background_color_=nil;
  background(new Color(0.99f,0.99f,0.0f));
  if (widget.style()->find_attribute("background",colorstring))
  {
    const Color* color=
      Color::lookup(Session::instance()->default_display(),colorstring);
    if (color)
      background(color);
  }
  // set shadow color
  p_shadow_color_=new Color(0.3f,0.3f,0.3f,0.5f);
  Resource::ref(p_shadow_color_);
  // end the new style
  widget.end_style();
}

//<function>
//
// Prototype: ~BubbleWindow
//            (
//            );
//
// Purpose: Destructor.
//
// Description:
//
// Destructor.
//
//</function>
BubbleWindow::~BubbleWindow()
{
  // unref glyph
  Resource::unref(p_glyph_);
  // unref shadow
  Resource::unref(p_shadow_color_);
  Resource::unref(p_foreground_color_);
  Resource::unref(p_background_color_);
  Resource::unref(p_brush_);
}

//<function>
//
// Prototype: void shape
//            (
//              BubbleWindow::Shape
//            );
//
// Purpose: Defines if the outline of the bubble ist rounded or squary.
//
// Description:
//
// Defines if the outline of the bubble ist rounded or squary. BubbleWindow::RECTANGLE
// defines squary bubble window. BubbleWindow::OVAL defines round bubble window.
//
//</function>
void BubbleWindow::shape(Shape bubble_shape)
{
  bubble_shape_=int(bubble_shape);
}

//<function>
//
// Prototype: void bump
//            (
//              Coord bump_x,Coord bump_y,Coord distance_x,Coord distance_y
//            );
//
// Purpose: Defines the outlinegeometry of the bubble if it is rounded.
//
// Description:
//
// Defines the outlinegeometry of the bubble if it is rounded or squary.
// BubbleWindow::OVAL defines round bubble window.
//
//</function>
void BubbleWindow::bump(Coord bump_x,Coord bump_y,Coord distance_x,Coord distance_y)
{
  bump_x_=bump_x;
  bump_y_=bump_y;
  distance_x_=distance_x;
  distance_y_=distance_y;
}

//<function>
//
// Prototype: void direction
//            (
//              BubbleWindow::Direction,
//              BubbleWindow::Direction
//            );
//
// Purpose: Defines the direction how to show the bubble.
//
// Description:
// 
// Defines the direction how to show the bubble (first parameter BubbleWindow::RIGHT
// or BubbleWindow::LEFT, second parameter BubbleWindow::UP or BubbleWindow::DOWN).
// The bubble is drawn from the mouseposition in this direction.  
//
//</function>
void BubbleWindow::direction(Direction align_x,Direction align_y)
{
  align_x_=int(align_x);
  align_y_=int(align_y);
}

//<function>
//
// Prototype: void brush
//            (
//              Brush*
//            );
//
// Purpose: Defines the brushstroke of the frame of the bubble.
//
// Description:
//
// Defines the brushstroke of the frame of the bubble. can be dashed and so on.
//
//</function>
void BubbleWindow::brush(Brush* brush)
{
  Resource::ref(brush);
  Resource::unref(p_brush_);
  p_brush_=brush;
}

//<function>
//
// Prototype: void background
//            (
//              Color*
//            );
//
// Purpose: Defines the background color of the bubble.
//
// Description:
//
// Defines the background color of the bubble. Only the color from the frame of
// the bubble to the glyph is altered. 
//
//</function>
void BubbleWindow::background(const Color* background_color)
{
  Resource::ref(background_color);
  Resource::unref(p_background_color_);
  p_background_color_=background_color;
}

//<function>
//
// Prototype: void foreground
//            (
//              Color*
//            );
//
// Purpose: Defines the foreground (frame) color of the bubble.
//
// Description:
//
// Defines the foreground (frame) color of the bubble.
//
//</function>
void BubbleWindow::foreground(const Color* foreground_color)
{
  Resource::ref(foreground_color);
  Resource::unref(p_foreground_color_);
  p_foreground_color_=foreground_color;
}

//<function>
//
// Prototype: void map
//            (
//              void
//            );
//
// Purpose: Use this to map the window.
//
// Description:
//
// Use this to map the window.
//
//</function>
void BubbleWindow::map(void)
{
  // position window at mousecoordinates
  Display* display=Session::instance()->default_display(); 
  if (display)
  {
    int root_x;
    int root_y;
    int child_x;
    int child_y;
    unsigned int buttons;
    XWindow root,child;
    // get mousecoordinates
    XQueryPointer
    (
      display->rep()->display_,
      display->rep()->root_,
      &root,
      &child,
      &child_x,
      &child_y,
      &root_x,
      &root_y,
      &buttons
    );
    root_y=display->rep()->pheight_-root_y;
    // align the window appropriate
    Coord align_x=0.0f;
    Coord align_y=0.0f;
    if (align_x_)
      align_x=1.0f;
    if (align_y_) 
      align_y=1.0f;
    // test if window reaches out of display
    WindowRep& window=*rep();   
    // get extension of window
    window.glyph_->request(window.shape_);
    Coord x=window.shape_.x_requirement().natural()+10.0f;
    Coord y=window.shape_.y_requirement().natural()+10.0f;
    // test where window reaches edge of display, alter alignment
    if (align_x_)
    {
      if ((display->to_coord(root_x)-x)<0)
      {
        align_x=0.0f;
        align_x_=0;
      }
    }
    else
    {      
      if ((display->to_coord(root_x)+x)>(display->width()))
      {        
        align_x=1.0f;
        align_x_=1;
      }
    }
    if (align_y_)
    {
      if ((display->to_coord(root_y)-y)<0)
      {
        align_y=0.0f;
        align_y_=0;
      }
    }
    else
    {
      if ((display->to_coord(root_y)+y)>(display->height()))
      {
        align_y=1.0f;
        align_y_=1;
      }
    }
    // place window appropriate      
    if (!align_y_)
      root_y+=2;
    else
      root_y-=2;
    place(display->to_coord(root_x),display->to_coord(root_y));
    align(align_x,align_y);
    // map the window
    PopupWindow::map();
  }
}

//<function>
//
// Prototype: void repair
//            (
//              void
//            );
//
// Purpose: Overloaded to draw the bubble. Do not use this.
//
// Description:
//
// Overloaded to draw the bubble. Do not use this.
//
//</function>
void BubbleWindow::repair(void)
{
  WindowRep& window=*rep();
  CanvasRep& canvas=*window.canvas_->rep();
  if (canvas.start_repair())
  { 
    // get coordinates of allocation
    Coord l=window.allocation_.left()+15.0f;
    Coord lr=l+6.0f;
    Coord r=window.allocation_.right()-15.0f;
    Coord rr=r-6.0f;
    Coord t=window.allocation_.top()-15.0f;
    Coord tr=t-6.0f;
    Coord b=window.allocation_.bottom()+15.0f;
    Coord br=b+6.0f;
    // calculate bumps
    Coord bx=(r-l)*bump_x_;
    if (bx<0.0f)
      bx*=-1.0f;
    Coord by=(t-b)*bump_y_;
    if (by<0.0f)
      by*=-1.0f;
    // calculate distances
    Coord dx=(r-l)*distance_x_;
    if (dx<0.0f)
      dx*=-1.0f;
    Coord dy=(t-b)*distance_y_;
    if (dy<0.0f)
      dy*=-1.0f;
    // drawing routines for bubbles
    if (bubble_shape_)
    {
      // round bubble right up
      if (!align_x_ && !align_y_)
      {
        // shadow
        window.canvas_->new_path();
        window.canvas_->move_to(l-9.0f,b-9.0f);
        window.canvas_->line_to(l+7.0f+4.0f,b-7.0f);
        window.canvas_->curve_to(r+7.0f,b-7.0f,l+dx+7.0f,b-by-7.0f,r-dx+7.0f,b-by-7.0f);
        window.canvas_->curve_to(r+7.0f,t-7.0f,r+bx+7.0f,b+dy-7.0f,r+bx+7.0f,t-dy-7.0f);
        window.canvas_->curve_to(l+7.0f,t-7.0f,r-dx+7.0f,t+by-7.0f,l+dx+7.0f,t+by-7.0f);
        window.canvas_->curve_to(l+7.0f,b-7.0f+4.0f,l-bx+7.0f,t-dy-7.0f,l-bx+7.0f,b+dy-7.0f);
        window.canvas_->line_to(l-9.0f,b-9.0f);
        window.canvas_->close_path();
        window.canvas_->fill(p_shadow_color_);
        // bubble
        window.canvas_->new_path();
        window.canvas_->move_to(l-9.0f,b-9.0f);
        window.canvas_->line_to(l+4.0f,b);
        window.canvas_->curve_to(r,b,l+dx,b-by,r-dx,b-by);
        window.canvas_->curve_to(r,t,r+bx,b+dy,r+bx,t-dy);
        window.canvas_->curve_to(l,t,r-dx,t+by,l+dx,t+by);
        window.canvas_->curve_to(l,b+4.0f,l-bx,t-dy,l-bx,b+dy);
        window.canvas_->line_to(l-9.0f,b-9.0f);
        window.canvas_->close_path();
        window.canvas_->fill(p_background_color_);
        window.canvas_->stroke(p_foreground_color_,p_brush_);
      }   
      // round bubble right down
      if (!align_x_ && align_y_)
      {
        // shadow
        window.canvas_->new_path();
        window.canvas_->move_to(l-9.0f,t+9.0f);
        window.canvas_->line_to(l+7.0f,t-7.0f-4.0f);
        window.canvas_->curve_to(l+7.0f,b-7.0f,l-bx+7.0f,t-dy-7.0f,l-bx+7.0f,b+dy-7.0f);
        window.canvas_->curve_to(r+7.0f,b-7.0f,l+dx+7.0f,b-by-7.0f,r-dx+7.0f,b-by-7.0f);
        window.canvas_->curve_to(r+7.0f,t-7.0f,r+bx+7.0f,b+dy-7.0f,r+bx+7.0f,t-dy-7.0f);
        window.canvas_->curve_to(l+7.0f+4.0f,t-7.0f,r-dx+7.0f,t+by-7.0f,l+dx+7.0f,t+by-7.0f);
        window.canvas_->line_to(l-9.0f,t+9.0f);
        window.canvas_->close_path();
        window.canvas_->fill(p_shadow_color_);
        // bubble
        window.canvas_->new_path();
        window.canvas_->move_to(l-9.0f,t+9.0f);
        window.canvas_->line_to(l,t-4.0f);
        window.canvas_->curve_to(l,b,l-bx,t-dy,l-bx,b+dy);
        window.canvas_->curve_to(r,b,l+dx,b-by,r-dx,b-by);
        window.canvas_->curve_to(r,t,r+bx,b+dy,r+bx,t-dy);
        window.canvas_->curve_to(l+4.0f,t,r-dx,t+by,l+dx,t+by);
        window.canvas_->line_to(l-9.0f,t+9.0f);
        window.canvas_->close_path();
        window.canvas_->fill(p_background_color_);
        window.canvas_->stroke(p_foreground_color_,p_brush_);
      }
      // round bubble left up
      if (align_x_ && !align_y_)
      {
        // shadow
        window.canvas_->new_path();
        window.canvas_->move_to(r+9.0f,b-9.0f);
        window.canvas_->line_to(r+7.0f,br-7.0f+4.0f);
        window.canvas_->curve_to(r+7.0f,t-7.0f,r+bx+7.0f,b+dy-7.0f,r+bx+7.0f,t-dy-7.0f);
        window.canvas_->curve_to(l+7.0f,t-7.0f,r-dx+7.0f,t+by-7.0f,l+dx+7.0f,t+by-7.0f);
        window.canvas_->curve_to(l+7.0f,b-7.0f,l-bx+7.0f,t-dy-7.0f,l-bx+7.0f,b+dy-7.0f);
        window.canvas_->curve_to(r+7.0f-4.0f,b-7.0f,l+dx+7.0f,b-by-7.0f,r-dx+7.0f,b-by-7.0f);
        window.canvas_->line_to(r+9.0f,b-9.0f);
        window.canvas_->close_path();
        window.canvas_->fill(p_shadow_color_);
        // bubble
        window.canvas_->new_path();
        window.canvas_->move_to(r+9.0f,b-9.0f);
        window.canvas_->line_to(r,b+4.0f);
        window.canvas_->curve_to(r,t,r+bx,b+dy,r+bx,t-dy);
        window.canvas_->curve_to(l,t,r-dx,t+by,l+dx,t+by);
        window.canvas_->curve_to(l,b,l-bx,t-dy,l-bx,b+dy);
        window.canvas_->curve_to(r-4.0f,b,l+dx,b-by,r-dx,b-by);
        window.canvas_->line_to(r+9.0f,b-9.0f);
        window.canvas_->close_path();
        window.canvas_->fill(p_background_color_);
        window.canvas_->stroke(p_foreground_color_,p_brush_);
      }
      // round bubble left down
      if (align_x_ && align_y_)
      {
        // shadow
        window.canvas_->new_path();
        window.canvas_->move_to(r+9.0f,t+9.0f);
        window.canvas_->line_to(r+7.0f-4.0f,t-7.0f);
        window.canvas_->curve_to(l+7.0f,t-7.0f,r-dx+7.0f,t+by-7.0f,l+dx+7.0f,t+by-7.0f);
        window.canvas_->curve_to(l+7.0f,b-7.0f,l-bx+7.0f,t-dy-7.0f,l-bx+7.0f,b+dy-7.0f);
        window.canvas_->curve_to(r+7.0f,b-7.0f,l+dx+7.0f,b-by-7.0f,r-dx+7.0f,b-by-7.0f);
        window.canvas_->curve_to(r+7.0f,t-7.0f-4.0f,r+bx+7.0f,b+dy-7.0f,r+bx+7.0f,t-dy-7.0f);
        window.canvas_->line_to(r+9.0f,t+9.0f);
        window.canvas_->close_path();
        window.canvas_->fill(p_shadow_color_);
        // bubble
        window.canvas_->new_path();
        window.canvas_->move_to(r+9.0f,t+9.0f);
        window.canvas_->line_to(r-4.0f,t);
        window.canvas_->curve_to(l,t,r-dx,t+by,l+dx,t+by);
        window.canvas_->curve_to(l,b,l-bx,t-dy,l-bx,b+dy);
        window.canvas_->curve_to(r,b,l+dx,b-by,r-dx,b-by);
        window.canvas_->curve_to(r,t-4.0f,r+bx,b+dy,r+bx,t-dy);
        window.canvas_->line_to(r+9.0f,t+9.0f);
        window.canvas_->close_path();
        window.canvas_->fill(p_background_color_);
        window.canvas_->stroke(p_foreground_color_,p_brush_);
      }
    }
    else
    {
      // square bubble right up
      if (!align_x_ && !align_y_)
      {
  	// shadow
  	window.canvas_->new_path();
  	window.canvas_->move_to(l-9.0f,b-9.0f);
  	window.canvas_->line_to(lr+9.0f,b-9.0f);
  	window.canvas_->line_to(rr+9.0f,b-9.0f);
  	window.canvas_->line_to(r+9.0f,br-9.0f);
  	window.canvas_->line_to(r+9.0f,tr-9.0f);
  	window.canvas_->line_to(rr+9.0f,t-9.0f);
  	window.canvas_->line_to(lr+9.0f,t-9.0f);
  	window.canvas_->line_to(l+9.0f,tr-9.0f);
  	window.canvas_->line_to(l+9.0f,br-9.0f);
  	window.canvas_->line_to(l-9.0f,b-9.0f);
  	window.canvas_->close_path();
  	window.canvas_->fill(p_shadow_color_);
  	// bubble
  	window.canvas_->new_path();
  	window.canvas_->move_to(l-9.0f,b-9.0f);
  	window.canvas_->line_to(lr,b);
  	window.canvas_->line_to(rr,b);
  	window.canvas_->line_to(r,br);
  	window.canvas_->line_to(r,tr);
  	window.canvas_->line_to(rr,t);
  	window.canvas_->line_to(lr,t);
  	window.canvas_->line_to(l,tr);
  	window.canvas_->line_to(l,br);
  	window.canvas_->line_to(l-9.0f,b-9.0f);
  	window.canvas_->close_path();
  	window.canvas_->fill(p_background_color_);
  	window.canvas_->stroke(p_foreground_color_,p_brush_);
      }   
      // square bubble right down
      if (!align_x_ && align_y_)
      {
  	// shadow
  	window.canvas_->new_path();
  	window.canvas_->move_to(l-9.0f,t+9.0f);
  	window.canvas_->line_to(l+9.0f,tr-9.0f);
  	window.canvas_->line_to(l+9.0f,br-9.0f);
  	window.canvas_->line_to(lr+9.0f,b-9.0f);
  	window.canvas_->line_to(rr+9.0f,b-9.0f);
  	window.canvas_->line_to(r+9.0f,br-9.0f);
  	window.canvas_->line_to(r+9.0f,tr-9.0f);
  	window.canvas_->line_to(rr+9.0f,t-9.0f);
  	window.canvas_->line_to(lr+9.0f,t-9.0f);
  	window.canvas_->line_to(l-9.0f,t+9.0f);
  	window.canvas_->close_path();
  	window.canvas_->fill(p_shadow_color_);
  	// bubble
  	window.canvas_->new_path();
  	window.canvas_->move_to(l-9.0f,t+9.0f);
  	window.canvas_->line_to(l,tr);
  	window.canvas_->line_to(l,br);
  	window.canvas_->line_to(lr,b);
  	window.canvas_->line_to(rr,b);
  	window.canvas_->line_to(r,br);
  	window.canvas_->line_to(r,tr);
  	window.canvas_->line_to(rr,t);
  	window.canvas_->line_to(lr,t);
  	window.canvas_->line_to(l-9.0f,t+9.0f);
  	window.canvas_->close_path();
  	window.canvas_->fill(p_background_color_);
  	window.canvas_->stroke(p_foreground_color_,p_brush_);
      }
      // square bubble left up
      if (align_x_ && !align_y_)
      {
  	// shadow
  	window.canvas_->new_path();
  	window.canvas_->move_to(r+9.0f,b-9.0f);
  	window.canvas_->line_to(r+9.0f,br-9.0f);
  	window.canvas_->line_to(r+9.0f,tr-9.0f);
  	window.canvas_->line_to(rr+9.0f,t-9.0f);
  	window.canvas_->line_to(lr+9.0f,t-9.0f);
  	window.canvas_->line_to(l+9.0f,tr-9.0f);
  	window.canvas_->line_to(l+9.0f,br-9.0f);
  	window.canvas_->line_to(lr+9.0f,b-9.0f);
  	window.canvas_->line_to(rr+9.0f,b-9.0f);
  	window.canvas_->line_to(r+9.0f,b-9.0f);
  	window.canvas_->close_path();
  	window.canvas_->fill(p_shadow_color_);
  	// bubble
  	window.canvas_->new_path();
  	window.canvas_->move_to(r+9.0f,b-9.0f);
  	window.canvas_->line_to(r,br);
  	window.canvas_->line_to(r,tr);
  	window.canvas_->line_to(rr,t);
  	window.canvas_->line_to(lr,t);
  	window.canvas_->line_to(l,tr);
  	window.canvas_->line_to(l,br);
  	window.canvas_->line_to(lr,b);
  	window.canvas_->line_to(rr,b);
  	window.canvas_->line_to(r+9.0f,b-9.0f);
  	window.canvas_->close_path();
  	window.canvas_->fill(p_background_color_);
  	window.canvas_->stroke(p_foreground_color_,p_brush_);
      }
      // square bubble left down
      if (align_x_ && align_y_)
      {
  	// shadow
  	window.canvas_->new_path();
  	window.canvas_->move_to(r+9.0f,t+9.0f);
  	window.canvas_->line_to(rr+9.0f,t-9.0f);
  	window.canvas_->line_to(lr+9.0f,t-9.0f);
  	window.canvas_->line_to(l+9.0f,tr-9.0f);
  	window.canvas_->line_to(l+9.0f,br-9.0f);
  	window.canvas_->line_to(lr+9.0f,b-9.0f);
  	window.canvas_->line_to(rr+9.0f,b-9.0f);
  	window.canvas_->line_to(r+9.0f,br-9.0f);
  	window.canvas_->line_to(r+9.0f,tr-9.0f);
  	window.canvas_->line_to(r+9.0f,t+9.0f);
  	window.canvas_->close_path();
  	window.canvas_->fill(p_shadow_color_);
  	// bubble
  	window.canvas_->new_path();
  	window.canvas_->move_to(r+9.0f,t+9.0f);
  	window.canvas_->line_to(rr,t);
  	window.canvas_->line_to(lr,t);
  	window.canvas_->line_to(l,tr);
  	window.canvas_->line_to(l,br);
  	window.canvas_->line_to(lr,b);
  	window.canvas_->line_to(rr,b);
  	window.canvas_->line_to(r,br);
  	window.canvas_->line_to(r,tr);
  	window.canvas_->line_to(r+9.0f,t+9.0f);
  	window.canvas_->close_path();
  	window.canvas_->fill(p_background_color_);
  	window.canvas_->stroke(p_foreground_color_,p_brush_);
      }
    }
    // draw the contained glyph
    window.glyph_->draw(window.canvas_,window.allocation_);
    canvas.finish_repair();
  }
}
