changeset 188:e3780acbbf80

Added ported sources from Novocode, thanks to WasserDragoon
author Frank Benoit <benoit@tionex.de>
date Sun, 26 Oct 2008 14:54:39 +0100
parents 293a2f22f944
children 71ca5bcf2307
files dwtx/novocode/CustomSeparator.d dwtx/novocode/ScaledImage.d dwtx/novocode/SizeBorder.d dwtx/novocode/SizeGrip.d dwtx/novocode/ishell/DesktopForm.d dwtx/novocode/ishell/InternalShell.d dwtx/novocode/ishell/internal/CustomDrawnButton.d dwtx/novocode/ishell/internal/DesktopListener.d dwtx/novocode/ishell/internal/TitleBar.d dwtx/novocode/ishell/internal/TitleBarButton.d
diffstat 10 files changed, 3032 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/CustomSeparator.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,139 @@
+/**
+        Original: com.novocode.naf.swt.custom.CustomSeparator
+***/
+
+/*******************************************************************************
+ * Copyright (c) 2004 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *******************************************************************************/
+
+module dwtx.novocode.CustomSeparator;
+
+import dwt.DWT;
+/**import dwt.events.PaintEvent;**/
+/**import dwt.events.PaintListener;**/
+import dwt.graphics.Color;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Canvas;
+import dwt.widgets.Composite;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+
+
+/**
+ * Instances of this class are non-native separator lines.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SHADOW_IN, SHADOW_OUT, SHADOW_NONE, HORIZONTAL, VERTICAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of SHADOW_IN, SHADOW_OUT and SHADOW_NONE may be specified.
+ * If neither ist specified, the default value SHADOW_IN is used. If SHADOW_NONE
+ * is specified, a single line is drawn with the control's foreground color.
+ * Only one of HORIZONTAL and VERTICAL may be specified. The default is VERTICAL.
+ * </p>
+ *
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Feb 12, 2004
+ * @version $Id: CustomSeparator.java 199 2004-10-08 13:20:36 +0000 (Fri, 08 Oct 2004) szeiger $
+ */
+
+class CustomSeparator : Canvas
+{
+  private int lineSize;
+  private int style;
+
+
+  this(Composite parent, int style)
+  {
+    super(parent, style = checkStyle (style));
+
+    this.style = style;
+
+    if((style & DWT.SHADOW_IN) !is 0 || (style & DWT.SHADOW_OUT) !is 0) lineSize = 2;
+    else lineSize = 1;
+
+    /**addPaintListener(new class() PaintListener
+    {
+      public void paintControl(PaintEvent event) { onPaint(event); }
+    });*/
+    addListener(DWT.Paint, dgListener(&onPaint));
+  }
+
+
+  private int checkStyle(int style)
+  {
+    int mask = DWT.SHADOW_IN | DWT.SHADOW_OUT| DWT.SHADOW_NONE | DWT.HORIZONTAL | DWT.VERTICAL;
+    style &= mask;
+    if((style & (DWT.SHADOW_IN | DWT.SHADOW_OUT| DWT.SHADOW_NONE)) is 0) style |= DWT.SHADOW_IN;
+    if((style & (DWT.HORIZONTAL | DWT.VERTICAL)) is 0) style |= DWT.VERTICAL;
+    return style;
+  }
+
+
+  public Point computeSize(int wHint, int hHint, bool changed)
+  {
+    checkWidget();
+    if(wHint is DWT.DEFAULT) wHint = lineSize;
+    if(hHint is DWT.DEFAULT) hHint = lineSize;
+    return new Point(wHint, hHint);
+  }
+  
+
+  public bool setFocus()
+  {
+    checkWidget();
+    return false;
+  }
+
+
+  private void onPaint(PaintEvent event)
+  {
+    Rectangle r = getClientArea();
+    if(r.width is 0 || r.height is 0) return;
+    bool horiz = ((style & DWT.HORIZONTAL) !is 0);
+    int mid = horiz ? r.y + (r.height/2) : r.x + (r.width/2);
+
+    Display disp = getDisplay();
+    event.gc.setLineWidth(1);
+
+    if((style & DWT.SHADOW_IN) !is 0)
+    {
+      Color shadow = disp.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
+      Color highlight = disp.getSystemColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+      event.gc.setForeground(shadow);
+      if(horiz) event.gc.drawLine(r.x, mid-1, r.x+r.width-1, mid-1);
+      else event.gc.drawLine(mid-1, r.y, mid-1, r.y+r.height-1);
+      event.gc.setForeground(highlight);
+      if(horiz) event.gc.drawLine(r.x, mid, r.x+r.width-1, mid);
+      else event.gc.drawLine(mid, r.y, mid, r.y+r.height-1);
+    }
+    else if((style & DWT.SHADOW_OUT) !is 0)
+    {
+      Color shadow = disp.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
+      Color highlight = disp.getSystemColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+      event.gc.setForeground(highlight);
+      if(horiz) event.gc.drawLine(r.x, mid-1, r.x+r.width-1, mid-1);
+      else event.gc.drawLine(mid-1, r.y, mid-1, r.y+r.height-1);
+      event.gc.setForeground(shadow);
+      if(horiz) event.gc.drawLine(r.x, mid, r.x+r.width-1, mid);
+      else event.gc.drawLine(mid, r.y, mid, r.y+r.height-1);
+    }
+    else
+    {
+      event.gc.setForeground(getForeground());
+      if(horiz) event.gc.drawLine(r.x, mid, r.x+r.width-1, mid);
+      else event.gc.drawLine(mid, r.y, mid, r.y+r.height-1);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/ScaledImage.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,271 @@
+/*******************************************************************************
+ * Copyright (c) 2004 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *     IBM Corporation - original SWT CLabel implementation on which this class is based
+ *******************************************************************************/
+
+module dwtx.novocode.ScaledImage;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Canvas;
+import dwt.widgets.Composite;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+
+
+/**
+ * An image / gradient component. Under development.
+ *
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Mar 21, 2005
+ * @version $Id: ScaledImage.java 346 2005-07-11 20:15:57 +0000 (Mon, 11 Jul 2005) szeiger $
+ */
+
+class ScaledImage : Canvas
+{
+  private const Rectangle DEFAULT_BOUNDS;
+  
+  public static const int IMAGE_PLACEMENT_STRETCH = 0;
+  public static const int IMAGE_PLACEMENT_TILE    = 1;
+
+  private Image image;
+
+  private Color[] gradientColors;
+
+  private int[] gradientPercents;
+
+  private bool gradientVertical;
+  
+  private int imagePlacement = IMAGE_PLACEMENT_STRETCH;
+
+
+  this(Composite parent, int style)
+  {
+    super(parent, style | DWT.NO_BACKGROUND);
+    this.DEFAULT_BOUNDS = new Rectangle(0, 0, 32, 32);
+
+    addListener(DWT.Paint, dgListener(&onPaint));
+  }
+
+
+  private void onPaint(Event event)
+  {
+        Rectangle rect = getClientArea();
+        GC gc = event.gc;
+        if(image is null
+          || image.getImageData().getTransparencyType() !is DWT.TRANSPARENCY_NONE)
+        {
+
+          if(gradientColors !is null)
+          {
+            // draw a gradient behind the text
+            Color oldBackground = gc.getBackground();
+            if(gradientColors.length is 1)
+            {
+              if(gradientColors[0] !is null) gc.setBackground(gradientColors[0]);
+              gc.fillRectangle(0, 0, rect.width, rect.height);
+            }
+            else
+            {
+              Color oldForeground = gc.getForeground();
+              Color lastColor = gradientColors[0];
+              if(lastColor is null) lastColor = oldBackground;
+              int pos = 0;
+              for(int i = 0; i < gradientPercents.length; ++i)
+              {
+                gc.setForeground(lastColor);
+                lastColor = gradientColors[i + 1];
+                if(lastColor is null) lastColor = oldBackground;
+                gc.setBackground(lastColor);
+                if(gradientVertical)
+                {
+                  int gradientHeight = (gradientPercents[i] * rect.height / 100)
+                    - pos;
+                  gc.fillGradientRectangle(0, pos, rect.width, gradientHeight,
+                    true);
+                  pos += gradientHeight;
+                }
+                else
+                {
+                  int gradientWidth = (gradientPercents[i] * rect.width / 100)
+                    - pos;
+                  gc.fillGradientRectangle(pos, 0, gradientWidth, rect.height,
+                    false);
+                  pos += gradientWidth;
+                }
+              }
+              if(gradientVertical && pos < rect.height)
+              {
+                gc.setBackground(getBackground());
+                gc.fillRectangle(0, pos, rect.width, rect.height - pos);
+              }
+              if(!gradientVertical && pos < rect.width)
+              {
+                gc.setBackground(getBackground());
+                gc.fillRectangle(pos, 0, rect.width - pos, rect.height);
+              }
+              gc.setForeground(oldForeground);
+            }
+            gc.setBackground(oldBackground);
+          }
+          else
+          {
+            if((getStyle() & DWT.NO_BACKGROUND) !is 0)
+            {
+              gc.setBackground(getBackground());
+              gc.fillRectangle(rect);
+            }
+          }
+
+        }
+        if(image !is null)
+        {
+          Rectangle ib = image.getBounds();
+          if(imagePlacement is IMAGE_PLACEMENT_TILE)
+          {
+            int maxStartX = rect.x + rect.width;
+            int maxStartY = rect.y + rect.height;
+            for(int x = rect.x; x < maxStartX; x += ib.width)
+              for(int y = rect.y; y < maxStartY; y += ib.height)
+                event.gc.drawImage(image, x, y);
+          }
+          else // IMAGE_PLACEMENT_STRETCH
+          {
+            event.gc.drawImage(image, ib.x, ib.y, ib.width, ib.height, rect.x,
+              rect.y, rect.width, rect.height);
+          }
+        }
+  }
+
+
+  public void setImage(Image image)
+  {
+    this.image = image;
+    redraw();
+  }
+  
+  
+  public void setImagePlacement(int imagePlacement)
+  {
+    this.imagePlacement = imagePlacement;
+    redraw();
+  }
+
+
+  public Point computeSize(int wHint, int hHint, bool changed)
+  {
+    checkWidget();
+    Rectangle ib = image !is null ? image.getBounds() : DEFAULT_BOUNDS;
+    if(wHint == DWT.DEFAULT) wHint = ib.width;
+    if(hHint == DWT.DEFAULT) hHint = ib.height;
+    return new Point(wHint, hHint);
+  }
+
+
+  public void setBackground(Color color)
+  {
+    super.setBackground(color);
+    // Are these settings the same as before?
+    if(color !is null && gradientColors is null && gradientPercents is null)
+    {
+      Color background = getBackground();
+      if(color is background)
+      {
+        return;
+      }
+    }
+    gradientColors = null;
+    gradientPercents = null;
+    redraw();
+  }
+
+
+  public void setBackground(Color[] colors, int[] percents)
+  {
+    setBackground(colors, percents, false);
+  }
+
+
+  public void setBackground(Color[] colors, int[] percents, bool vertical)
+  {
+    checkWidget();
+    if(colors !is null)
+    {
+      if(percents is null || percents.length !is colors.length - 1)
+      {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+      }
+      if(getDisplay().getDepth() < 15)
+      {
+        // Don't use gradients on low color displays
+        colors = [ colors[colors.length - 1] ];
+        percents = [];
+      }
+      for(int i = 0; i < percents.length; i++)
+      {
+        if(percents[i] < 0 || percents[i] > 100)
+        {
+          DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        }
+        if(i > 0 && percents[i] < percents[i - 1])
+        {
+          DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        }
+      }
+    }
+
+    // Are these settings the same as before?
+    Color background = getBackground();
+    if((gradientColors !is null) && (colors !is null)
+      && (gradientColors.length is colors.length))
+    {
+      bool same = false;
+      for(int i = 0; i < gradientColors.length; i++)
+      {
+        same = (gradientColors[i] is colors[i])
+          || ((gradientColors[i] is null) && (colors[i] is background))
+          || ((gradientColors[i] is background) && (colors[i] is null));
+        if(!same) break;
+      }
+      if(same)
+      {
+        for(int i = 0; i < gradientPercents.length; i++)
+        {
+          same = gradientPercents[i] is percents[i];
+          if(!same) break;
+        }
+      }
+      if(same && this.gradientVertical is vertical) return;
+    }
+    // Store the new settings
+    if(colors is null)
+    {
+      gradientColors = null;
+      gradientPercents = null;
+      gradientVertical = false;
+    }
+    else
+    {
+      gradientColors = new Color[colors.length];
+      for(int i = 0; i < colors.length; ++i)
+        gradientColors[i] = (colors[i] !is null) ? colors[i] : background;
+      gradientPercents = new int[percents.length];
+      for(int i = 0; i < percents.length; ++i)
+        gradientPercents[i] = percents[i];
+      gradientVertical = vertical;
+    }
+    // Refresh with the new settings
+    redraw();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/SizeBorder.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,406 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *******************************************************************************/
+
+module dwtx.novocode.SizeBorder;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import dwtx.dwtxhelper.Timer;
+import dwtx.dwtxhelper.TimerTask;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Cursor;
+import dwt.graphics.GC;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Canvas;
+import dwt.widgets.Composite;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Shell;
+
+
+/**
+ * A border for a resizable container. This border control usually fills the
+ * entire container, with a content pane above it (not covering the actual
+ * border area).
+ * 
+ * <p>If the style SWT.BORDER ist set, a beveled border (as used on Windows
+ * Classic window decorations) will be drawn. Without this style, no drawing
+ * is done.</p>
+ *
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Jan 21, 2005
+ * @version $Id: SizeBorder.java 321 2005-02-26 15:44:24 +0000 (Sat, 26 Feb 2005) szeiger $
+ */
+
+class SizeBorder : Canvas
+{
+  private static const long UPDATE_DELAY = 25;
+  
+  private static const int AREA_NONE = 0;
+  private static const int AREA_N    = 1;
+  private static const int AREA_S    = 2;
+  private static const int AREA_E    = 4;
+  private static const int AREA_W    = 8;
+  private static const int AREA_NW   = 9;
+  private static const int AREA_NE   = 5;
+  private static const int AREA_SE   = 6;
+  private static const int AREA_SW   = 10;
+  
+  private Rectangle snapBack;
+  private bool cancelled = true;
+  private /**volatile*/ long lastUpdate;
+  private Timer timer;
+  private TimerTask timerTask;
+  private Composite resizableParent;
+  private Point minSize, mouseDownOffset;
+  private int borderWidth = 4, cornerSize = 16;
+  private Display display;
+  private Cursor cursor, cursorNWSE, cursorNESW, cursorWE, cursorNS;
+  private int currentArea;
+  private Color highlightShadowColor, lightShadowColor, normalShadowColor,darkShadowColor;
+
+
+  this(Composite parent, int style)
+  {
+    this(parent, parent.getShell(), style);
+  }
+
+  
+  this(Composite parent, Composite resizableParent, int style)
+  {
+    super(parent, checkStyle (style));
+    this.timer = new Timer(true);
+    this.resizableParent = resizableParent;
+    this.display = getDisplay();
+
+    cursorNWSE = new Cursor(getDisplay(), DWT.CURSOR_SIZENWSE);
+    cursorNESW = new Cursor(getDisplay(), DWT.CURSOR_SIZENESW);
+    cursorWE = new Cursor(getDisplay(), DWT.CURSOR_SIZEWE);
+    cursorNS = new Cursor(getDisplay(), DWT.CURSOR_SIZENS);
+
+    addListener(DWT.Dispose, dgListener(&onDispose));
+
+    if((style & DWT.BORDER) !is 0)
+    {
+      highlightShadowColor = display.getSystemColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+      lightShadowColor = display.getSystemColor(DWT.COLOR_WIDGET_LIGHT_SHADOW);
+      normalShadowColor = display.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
+      darkShadowColor = display.getSystemColor(DWT.COLOR_WIDGET_DARK_SHADOW);
+  
+      addListener(DWT.Paint, dgListener(&onPaint));
+    }
+
+    addListener(DWT.MouseDown, dgListener(&onMouseDown));
+
+    addListener(DWT.MouseMove, dgListener(&onMouseMove));
+    
+    addListener(DWT.MouseUp, dgListener(&onMouseUp));
+    
+    addListener(DWT.Show, dgListener(&onShow));
+  }
+
+  private void onDispose(Event event)
+  {
+        cursorNWSE.dispose();
+        cursorNESW.dispose();
+        cursorWE.dispose();
+        cursorNS.dispose();
+
+        timer.cancel();
+  }
+
+
+  private void onPaint(Event event)
+  {
+          Rectangle r = getClientArea();
+          if(r.width is 0 || r.height is 0) return;
+          drawBevelRect(event.gc, r.x, r.y, r.width-1, r.height-1, lightShadowColor, darkShadowColor);
+          drawBevelRect(event.gc, r.x+1, r.y+1, r.width-3, r.height-3, highlightShadowColor, normalShadowColor);
+  }
+
+
+  private void onMouseDown(Event event)
+  {
+        if(event.button is 1)
+        {
+          currentArea = areaAtPoint(event.x, event.y);
+          if(currentArea is AREA_NONE) return;
+          if(cast(Shell)resizableParent !is null)
+            mouseDownOffset = toDisplay(event.x, event.y);
+          else
+            mouseDownOffset = display.map(/**SizeBorder.this*/getSizeBorder(), resizableParent.getParent(), event.x, event.y);
+          snapBack = resizableParent.getBounds();
+          cancelled = false;
+        }
+        else if(event.button is 3 && (event.stateMask & DWT.BUTTON1) !is 0) // chord click
+        {
+          if(snapBack !is null)
+          {
+            resizableParent.setBounds(snapBack);
+            snapBack = null;
+            cancelled = true;
+          }
+        }
+  }
+
+
+  private void onMouseMove(Event event)
+  {
+        if((event.stateMask & DWT.BUTTON1) is 0) updateCursor(areaAtPoint(event.x, event.y));
+
+        if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0)
+        {
+          if(timerTask !is null)
+          {
+            timerTask.cancel();
+            timerTask = null;
+          }
+          long now = System.currentTimeMillis();
+          if(lastUpdate + UPDATE_DELAY < now)
+          {
+            performResize(event);
+            lastUpdate = now;
+          }
+          else
+          {
+            timerTask = new class() TimerTask
+            {
+              public void run()
+              {
+                TimerTask executingTask = this;
+                event.display.asyncExec(new class() Runnable
+                {
+                  public void run()
+                  {
+                    if(executingTask !is timerTask) return;
+                    performResize(event);
+                  }
+                });
+              }
+            };
+            timer.schedule(timerTask, UPDATE_DELAY);
+          }
+        }
+  }
+
+
+  private void onMouseUp(Event event)
+  {
+        if(timerTask !is null)
+        {
+          timerTask.cancel();
+          timerTask = null;
+        }
+        if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0)
+        {
+          performResize(event);
+        }
+  }
+
+
+  private void onShow(Event event)
+  {
+        Point p = toControl(display.getCursorLocation());
+        updateCursor(areaAtPoint(p.x, p.y));
+  }
+
+
+  private SizeBorder getSizeBorder()
+  {
+      return this;
+  }
+
+  private static int checkStyle(int style)
+  {
+    //int mask = DWT.NONE;
+    //style &= mask;
+    style = DWT.NO_FOCUS;
+    return style;
+  }
+
+
+  private void performResize(Event event)
+  {
+    // Make sure we stay within the container parent's client area
+    Rectangle ca;
+    if(cast(Shell)resizableParent !is null) ca = getDisplay().getClientArea();
+    else ca = getDisplay().map(resizableParent.getParent(), null, resizableParent.getParent().getClientArea());
+    Point caOffset = toControl(ca.x, ca.y);
+    event.x = Math.max(Math.min(event.x, caOffset.x + ca.width - 1), caOffset.x);
+    event.y = Math.max(Math.min(event.y, caOffset.y + ca.height - 1), caOffset.y);
+
+    // Compute movement relative to position at MouseDown event
+    Point movement = (cast(Shell)resizableParent !is null)
+      ? toDisplay(event.x, event.y)
+      : display.map(this, resizableParent.getParent(), event.x, event.y);
+    movement.x -= mouseDownOffset.x;
+    movement.y -= mouseDownOffset.y;
+    
+    // Compute new size and position
+    int newW = snapBack.width, newH = snapBack.height, newX = snapBack.x, newY = snapBack.y;
+    if((currentArea & AREA_E) !is 0) newW += movement.x;
+    else if((currentArea & AREA_W) !is 0) { newW -= movement.x; newX += snapBack.width - newW; }
+    if((currentArea & AREA_S) !is 0) newH += movement.y;
+    else if((currentArea & AREA_N) !is 0) { newH -= movement.y; newY += snapBack.height - newH; }
+
+    // Do not go below the container's minimum size
+    int minW, minH;
+    if(minSize !is null) { minW = minSize.x; minH = minSize.y; }
+    else { minW = 0; minH = 0; }
+    int maxX = snapBack.x + snapBack.width - minW;
+    int maxY = snapBack.y + snapBack.height - minH;
+
+    newW = Math.max(minW, newW);
+    newH = Math.max(minH, newH);
+    newX = Math.min(maxX, newX);
+    newY = Math.min(maxY, newY);
+
+    resizableParent.setBounds(newX, newY, newW, newH);
+  }
+  
+  
+  private void updateCursor(int area)
+  {
+    Cursor c = null;
+    switch(area)
+    {
+      case AREA_N:  case AREA_S:  c = cursorNS;   break;
+      case AREA_W:  case AREA_E:  c = cursorWE;   break;
+      case AREA_NW: case AREA_SE: c = cursorNWSE; break;
+      case AREA_NE: case AREA_SW: c = cursorNESW; break;
+    }
+    if(cursor is c) return;
+    cursor = c;
+    setCursor(c);
+  }
+  
+  
+  private int areaAtPoint(int x, int y)
+  {
+    Point size = getSize();
+    if(x < borderWidth) // left edge
+    {
+      if(y < cornerSize) return AREA_NW;
+      else if(y >= size.y-cornerSize) return AREA_SW;
+      else return AREA_W;
+    }
+    else if(x >= size.x-borderWidth) // right edge
+    {
+      if(y >= size.y-cornerSize) return AREA_SE;
+      else if(y < cornerSize) return AREA_NE;
+      else return AREA_E;
+    }
+    else if(y < borderWidth) // top edge
+    {
+      if(x < cornerSize) return AREA_NW;
+      else if(x >= size.x-cornerSize) return AREA_NE;
+      else return AREA_N;
+    }
+    else if(y >= size.y-borderWidth) // bottom edge
+    {
+      if(x >= size.x-cornerSize) return AREA_SE;
+      else if(x < cornerSize) return AREA_SW;
+      else return AREA_S;
+    }
+    else return AREA_NONE;
+  }
+  
+  
+  public Point computeSize(int wHint, int hHint, bool changed)
+  {
+    checkWidget();
+    if(wHint == DWT.DEFAULT) wHint = 0;
+    if(hHint == DWT.DEFAULT) hHint = 0;
+    return new Point(wHint, hHint);
+  }
+
+
+  public bool setFocus()
+  {
+    checkWidget();
+    return false;
+  }
+
+
+  public bool isReparentable ()
+  {
+    checkWidget();
+    return false;
+  }
+
+
+  /**
+   * Set the allowed minimum size for the shell. The SizeGrip will
+   * not resize the shell to a smaller size.
+   * <p>
+   * Note: This does <em>not</em> affect other ways of resizing the shell,
+   * like using the size controls which are placed on the trimmings by
+   * the window manager.
+   * </p>
+   */
+
+  public void setMinimumShellSize(Point p)
+  {
+    checkWidget();
+    this.minSize = p;
+  }
+
+
+  /**
+   * Set the allowed minimum size for the shell. The SizeGrip will
+   * not resize the shell to a smaller size.
+   * <p>
+   * Note: This does <em>not</em> affect other ways of resizing the shell,
+   * like using the size controls which are placed on the trimmings by
+   * the window manager.
+   * </p>
+   */
+
+  public void setMinimumShellSize(int width, int height)
+  {
+    checkWidget();
+    this.minSize = new Point(width, height);
+  }
+  
+  
+  public void setBorderWidth(int width)
+  {
+    checkWidget();
+    borderWidth = width;
+    Point p = toControl(display.getCursorLocation());
+    updateCursor(areaAtPoint(p.x, p.y));
+  }
+  
+  
+  public void setCornerSize(int size)
+  {
+    checkWidget();
+    cornerSize = size;
+    Point p = toControl(display.getCursorLocation());
+    updateCursor(areaAtPoint(p.x, p.y));
+  }
+
+  
+  private static void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright)
+  {
+    gc.setForeground(bottomright);
+    gc.drawLine(x + w, y, x + w, y + h);
+    gc.drawLine(x, y + h, x + w, y + h);
+
+    gc.setForeground(topleft);
+    gc.drawLine(x, y, x + w - 1, y);
+    gc.drawLine(x, y, x, y + h - 1);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/SizeGrip.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,366 @@
+/*******************************************************************************
+ * Copyright (c) 2004 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+/++
+ + This code was take from http://www.novocode.com/swt/
+ +/
+module dwtx.novocode.SizeGrip;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import dwtx.dwtxhelper.Timer;
+import dwtx.dwtxhelper.TimerTask;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Cursor;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Canvas;
+import dwt.widgets.Composite;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Shell;
+
+
+/**
+ * A non-native size grip which looks (and almost feels) like the native Win32 size grip.
+ * <p>
+ * The SHADOW_IN style causes highlight lines to be drawn at the right and bottom border.
+ * This style should be used when placing the size grip on top of the bottom right corner
+ * of a FramedComposite with style SHADOW_IN. If the FLAT style is set, the size grip is
+ * drawn in a Windows XP style instead of the normal Windows Classic style.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SHADOW_IN, FLAT</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * </p><p>
+ * NOTE: The visibility of this widget is controlled by the "maximized" state of the
+ * shell. The size grip is hidden when the shell is maximized, even if it has been made
+ * visible by calling <code>setVisible(true)</code>. getVisible() always returns
+ * the value set with setVisible(). isVisible() returns the true visibility, as usual.
+ * </p>
+ *
+ * <p>New in 1.6: Smoother resizing for a more native look &amp; feel. The window size is
+ * not updated more than once every 25ms to reduce the number of unnecessary repaints.</p>
+ *
+ * <p>New in 1.8: You can specify a parent Composite other than the shell which will be
+ * resized by the SizeGrip.</p>
+ *
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Mar 8, 2004
+ * @version $Id: SizeGrip.java,v 1.9 2005/01/24 21:52:14 szeiger Exp $
+ */
+
+public final class SizeGrip : Canvas
+{
+    private static const int WIDTH  = 13;
+    private static const int HEIGHT = 13;
+    private static const long UPDATE_DELAY = 25;
+
+    private int mouseDownOffsetX, mouseDownOffsetY, snapBackX, snapBackY;
+    private bool cancelled;
+    private Cursor sizeCursor;
+    private Point minSize;
+    private bool userVisible = true;
+    private /+volatile+/ long lastUpdate;
+    private Timer timer;
+    private TimerTask timerTask;
+    private Composite resizableParent;
+
+
+    public this(Composite parent, int style)
+    {
+        this(parent, parent.getShell(), style);
+    }
+
+    private void onDispose( Event e ){
+        if(sizeCursor !is null) {
+            sizeCursor.dispose();
+            sizeCursor = null;
+        }
+    }
+    private void onPaint( Event event ){
+        Rectangle r = getClientArea();
+        if(r.width is 0 || r.height is 0) return;
+
+        Display disp = getDisplay();
+        Color shadow = disp.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
+        Color highlight = disp.getSystemColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+
+
+
+        event.gc.setLineWidth(1);
+
+        if((getStyle() & DWT.FLAT) !is 0) {
+            event.gc.setBackground(highlight);
+            event.gc.fillRectangle(r.width-3, r.height-3, 2, 2);
+            event.gc.fillRectangle(r.width-7, r.height-3, 2, 2);
+            event.gc.fillRectangle(r.width-11, r.height-3, 2, 2);
+            event.gc.fillRectangle(r.width-3, r.height-7, 2, 2);
+            event.gc.fillRectangle(r.width-7, r.height-7, 2, 2);
+            event.gc.fillRectangle(r.width-3, r.height-11, 2, 2);
+            event.gc.setBackground(shadow);
+            event.gc.fillRectangle(r.width-4, r.height-4, 2, 2);
+            event.gc.fillRectangle(r.width-8, r.height-4, 2, 2);
+            event.gc.fillRectangle(r.width-12, r.height-4, 2, 2);
+            event.gc.fillRectangle(r.width-4, r.height-8, 2, 2);
+            event.gc.fillRectangle(r.width-8, r.height-8, 2, 2);
+            event.gc.fillRectangle(r.width-4, r.height-12, 2, 2);
+            event.gc.setForeground(highlight);
+        }
+        else {
+            event.gc.setForeground(shadow);
+            event.gc.drawLine(r.width-3, r.height-2, r.width-2, r.height-3);
+            event.gc.drawLine(r.width-4, r.height-2, r.width-2, r.height-4);
+            event.gc.drawLine(r.width-7, r.height-2, r.width-2, r.height-7);
+            event.gc.drawLine(r.width-8, r.height-2, r.width-2, r.height-8);
+            event.gc.drawLine(r.width-11, r.height-2, r.width-2, r.height-11);
+            event.gc.drawLine(r.width-12, r.height-2, r.width-2, r.height-12);
+
+            event.gc.setForeground(highlight);
+            event.gc.drawLine(r.width-5, r.height-2, r.width-2, r.height-5);
+            event.gc.drawLine(r.width-9, r.height-2, r.width-2, r.height-9);
+            event.gc.drawLine(r.width-13, r.height-2, r.width-2, r.height-13);
+        }
+
+        if((getStyle() & DWT.SHADOW_IN) !is 0) {
+            if(event.width > WIDTH) event.gc.drawLine(0, r.height-1, r.width-14, r.height-1);
+            if(event.height > HEIGHT) event.gc.drawLine(r.width-1, 0, r.width-1, r.height-14);
+        }
+    }
+    private void onMouseDown( Event event ){
+        if(event.button is 1) {
+            mouseDownOffsetX = event.x;
+            mouseDownOffsetY = event.y;
+            Point p = resizableParent.getSize();
+            snapBackX = p.x;
+            snapBackY = p.y;
+            cancelled = false;
+            //System.out.println("x="+mouseDownOffsetX+", y="+mouseDownOffsetY);
+        }
+        else if(event.button is 3 && (event.stateMask & DWT.BUTTON1) !is 0) // chord click
+        {
+            if(snapBackX > 0 && snapBackY > 0)
+            {
+                resizableParent.setSize(snapBackX, snapBackY);
+                snapBackX = 0;
+                snapBackY = 0;
+                cancelled = true;
+            }
+        }
+    }
+    private void onMouseMove( Event event ){
+        if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0)
+        {
+            if(timerTask !is null)
+            {
+                timerTask.cancel();
+                timerTask = null;
+            }
+            long now = System.currentTimeMillis();
+
+            long lastUpdate_;
+            synchronized(this){
+                lastUpdate_ = lastUpdate;
+            }
+
+            if(lastUpdate_ + UPDATE_DELAY < now)
+            {
+                performResize(event);
+
+                synchronized(this){
+                    lastUpdate = now;
+                }
+
+            }
+            else
+            {
+                timerTask = new class() TimerTask
+                {
+                    public void run()
+                    {
+                        TimerTask executingTask = this;
+                        event.display.asyncExec( dgRunnable( (Event event_, TimerTask executingTask_ )
+                        {
+                            if(executingTask_ !is timerTask) return;
+                            performResize(event_);
+                        }, event, executingTask ));
+                    }
+                };
+                timer.schedule(timerTask, UPDATE_DELAY);
+            }
+        }
+    }
+    private void onMouseUp( Event event ){
+        if(timerTask !is null)
+        {
+            timerTask.cancel();
+            timerTask = null;
+        }
+        if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0)
+        {
+            performResize(event);
+        }
+    }
+    
+    public this(Composite parent, Composite resizableParent_, int style)
+    {
+        super(parent, style = checkStyle (style));
+        this.timer = new Timer(true);
+        this.resizableParent = resizableParent_;
+        setSize(WIDTH, HEIGHT);
+
+        sizeCursor = new Cursor(getDisplay(), DWT.CURSOR_SIZENWSE);
+        setCursor(sizeCursor);
+
+        addListener(DWT.Dispose, dgListener( &onDispose ));
+        addListener(DWT.Paint, dgListener( &onPaint ));
+        addListener(DWT.MouseDown, dgListener( &onMouseDown ));
+        addListener(DWT.MouseMove, dgListener( &onMouseMove ));
+        addListener(DWT.MouseUp, dgListener( &onMouseUp ));
+
+        Listener resizeListener = ( null !is cast(Shell)resizableParent) ? dgListener( &onResize ) : null;
+
+        if(resizeListener !is null) resizableParent.addListener(DWT.Resize, resizeListener);
+
+        addListener(DWT.Dispose, dgListener( &onDisposeResizeListener, resizeListener ));
+
+        updateVisibility();
+    }
+
+    private void onDisposeResizeListener(Event event, Listener resizeListener ) {
+        timer.cancel();
+        if(resizeListener !is null) resizableParent.removeListener(DWT.Resize, resizeListener);
+    }
+    private void onResize( Event event ){
+        updateVisibility();
+    }
+
+    private void performResize(Event event)
+    {
+        // Make sure we stay within the container parent's client area
+        Rectangle ca;
+        if(cast(Shell)resizableParent ) ca = getDisplay().getClientArea();
+        else ca = getDisplay().map(resizableParent.getParent(), null, resizableParent.getParent().getClientArea());
+        Point limit = toControl(ca.x + ca.width - 1, ca.y + ca.height - 1);
+        event.x = Math.min(event.x, limit.x);
+        event.y = Math.min(event.y, limit.y);
+
+        Point p = resizableParent.getSize();
+        int newX = p.x + event.x - mouseDownOffsetX;
+        int newY = p.y + event.y - mouseDownOffsetY;
+        if(minSize !is null)
+        {
+            newX = Math.max(minSize.x, newX);
+            newY = Math.max(minSize.y, newY);
+        }
+        if(newX !is p.x || newY !is p.y) resizableParent.setSize(newX, newY);
+    }
+
+
+    private void updateVisibility()
+    {
+        if( auto shell = cast(Shell)resizableParent )
+        {
+            bool vis = super.getVisible();
+            bool max = shell.getMaximized();
+            bool newVis = userVisible && !max;
+            if(vis !is newVis) super.setVisible(newVis);
+        }
+        else if(userVisible !is super.getVisible()) super.setVisible(userVisible);
+    }
+
+
+    public Point computeSize(int wHint, int hHint, bool changed)
+    {
+        checkWidget();
+        if(wHint is DWT.DEFAULT) wHint = WIDTH;
+        if(hHint is DWT.DEFAULT) hHint = HEIGHT;
+        return new Point(wHint, hHint);
+    }
+
+
+    private static int checkStyle(int style)
+    {
+        int mask = DWT.SHADOW_IN | DWT.FLAT;
+        style &= mask;
+        return style;
+    }
+
+
+    public bool setFocus()
+    {
+        checkWidget();
+        return false;
+    }
+
+
+    public bool isReparentable ()
+    {
+        checkWidget();
+        return false;
+    }
+
+
+    /**
+     * Set the allowed minimum size for the shell. The SizeGrip will
+     * not resize the shell to a smaller size.
+     * <p>
+     * Note: This does <em>not</em> affect other ways of resizing the shell,
+     * like using the size controls which are placed on the trimmings by
+     * the window manager.
+     * </p>
+     */
+
+    public void setMinimumShellSize(Point p)
+    {
+        checkWidget();
+        this.minSize = p;
+    }
+
+
+    /**
+     * Set the allowed minimum size for the shell. The SizeGrip will
+     * not resize the shell to a smaller size.
+     * <p>
+     * Note: This does <em>not</em> affect other ways of resizing the shell,
+     * like using the size controls which are placed on the trimmings by
+     * the window manager.
+     * </p>
+     */
+
+    public void setMinimumShellSize(int width, int height)
+    {
+        checkWidget();
+        this.minSize = new Point(width, height);
+    }
+
+
+    public bool getVisible()
+    {
+        checkWidget();
+        return userVisible;
+    }
+
+
+    public void setVisible(bool visible)
+    {
+        checkWidget();
+        userVisible = visible;
+        updateVisibility();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/ishell/DesktopForm.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,512 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *******************************************************************************/
+
+module dwtx.novocode.ishell.DesktopForm;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Shell;
+
+import dwtx.novocode.ishell.internal.DesktopListener;
+import dwtx.novocode.ishell.InternalShell;
+
+import tango.core.Array;
+
+
+/**
+ * A desktop which manages internal shells.
+ *
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Jan 21, 2005
+ * @version $Id: DesktopForm.java 344 2005-07-09 22:37:51 +0000 (Sat, 09 Jul 2005) szeiger $
+ */
+
+class DesktopForm : Composite
+{
+  private static const InternalShell[] EMPTY_INTERNALSHELL_ARRAY/** = new InternalShell[0]*/;
+  private static const int FIRST_SHELL_LOCATION = 32;
+  private static const int SHELL_LOCATION_OFFSET = 16;
+
+  private InternalShell activeShell;
+  private DesktopListener[] desktopListeners;
+  private InternalShell[] allShells;
+  private InternalShell[] visibleShells;
+  private int nextShellLocation = FIRST_SHELL_LOCATION;
+  private bool showMaximizedTitle;
+  private bool autoMaximize = true;
+  private bool enableCtrlTab = true;
+  private bool allowDeactivate;
+  private Shell shell;
+  private InternalShell ishell;
+  private Listener mouseDownFilter, focusInFilter, traverseFilter;
+
+
+  this(Composite parent, int style)
+  {
+    super(parent, style);
+    Display display = getDisplay();
+    shell = getShell();
+
+    Color bg = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND);
+    setBackground(bg);
+    int brightness = bg.getRed() + bg.getGreen() + bg.getBlue();
+    setForeground(display.getSystemColor(brightness > 400 ? DWT.COLOR_BLACK : DWT.COLOR_WHITE));
+    
+    addListener(DWT.Resize, dgListener(&onResize));
+    
+    mouseDownFilter = dgListener(&onMouseDownFilter);
+    focusInFilter = dgListener(&onFocusInFilter);
+    traverseFilter = dgListener(&onTraverseFilter);
+    
+    display.addFilter(DWT.MouseDown, mouseDownFilter);
+    display.addFilter(DWT.FocusIn, focusInFilter);
+    display.addFilter(DWT.Traverse, traverseFilter);
+    
+    addListener(DWT.Dispose, dgListener(&onDispose));
+  }
+  
+  
+  private void onResize(Event event)
+  {
+        Rectangle ca = getClientArea();
+        foreach(c; getChildren())
+        {
+          if(cast(InternalShell)c !is null)
+            (cast(InternalShell)c).desktopResized(ca);
+        }
+  }
+
+
+  private void onMouseDownFilter(Event event)
+  {
+        if(!(cast(Control)event.widget !is null)) return;
+        Control c = cast(Control)event.widget;
+        if(c.getShell() !is shell) return;
+        bool[] desktopHit = new bool[1];
+        InternalShell ishell = getInternalShell(c, desktopHit);
+        if(desktopHit[0] && allowDeactivate) activate(null);
+        if(ishell is null) return;
+        activate(ishell);
+  }
+
+
+  private void onFocusInFilter(Event event)
+  {
+        if(!(cast(Control)event.widget !is null)) return;
+        Control c = cast(Control)event.widget;
+        if(c.getShell() !is shell) return;
+        bool[] desktopHit = new bool[1];
+        ishell = getInternalShell(c, desktopHit);
+        if(desktopHit[0] && allowDeactivate) activate(null);
+        if(ishell is null) return;
+        ishell.focusControl = c;
+  }
+
+
+  private void onTraverseFilter(Event event)
+  {
+        if(!enableCtrlTab) return;
+        if(!event.doit) return; // don't steal traverse event if a control wants to handle it directly
+        if((event.stateMask & DWT.CTRL) is 0) return;
+        if(event.detail !is DWT.TRAVERSE_TAB_NEXT && event.detail !is DWT.TRAVERSE_TAB_PREVIOUS) return;
+        if(!(cast(Control)event.widget !is null)) return;
+        Control c = cast(Control)event.widget;
+        if(c.getShell() !is shell) return;
+        bool[] desktopHit = new bool[1];
+        InternalShell ishell = getInternalShell(c, desktopHit);
+        if(ishell !is null || desktopHit[0])
+        {
+          if(event.detail is DWT.TRAVERSE_TAB_NEXT) activateNextShell();
+          else activatePreviousShell();
+          event.doit = false;
+        }
+  }
+
+
+  private void onDispose(Event event)
+  {
+        display.removeFilter(DWT.MouseDown, mouseDownFilter);
+        display.removeFilter(DWT.FocusIn, focusInFilter);
+        display.removeFilter(DWT.Traverse, traverseFilter);
+  }
+
+
+  void manage(InternalShell ishell)
+  {
+    Rectangle bounds = getBounds();
+    if(nextShellLocation > bounds.height-100 || nextShellLocation > bounds.width-100)
+      nextShellLocation = FIRST_SHELL_LOCATION;
+    ishell.setLocation(bounds.x+nextShellLocation, bounds.y+nextShellLocation);
+    nextShellLocation += SHELL_LOCATION_OFFSET;
+
+    ishell.addListener(DWT.Dispose, dgListener(&onIshellDispose));
+    allShells ~= ishell;
+    if(ishell.isVisible()) visibleShells ~= ishell;
+    notifyDesktopListenersCreate(ishell);
+  }
+  
+  
+  private void onIshellDispose(Event event)
+  {
+        allShells.remove(ishell);
+        visibleShells.remove(ishell);
+        if(ishell is activeShell)
+        {
+          activateTopmostVisibleShellExcept(ishell);
+          if(autoMaximize && !hasVisibleMaximizedShell())
+            setAllVisibleMaximized(false);
+        }
+        notifyDesktopListenersDispose(ishell);
+  }
+
+
+  private InternalShell activateTopmostVisibleShellExcept(InternalShell except)
+  {
+    Control[] children = getChildren();
+    for(int i=0; i<children.length; i++)
+    {
+      Control c = children[i];
+      if(c is except) continue;
+      if(cast(InternalShell)c !is null && c.isVisible())
+      {
+        InternalShell ishell = cast(InternalShell)c;
+        activate(ishell);
+        return ishell;
+      }
+    }
+    activeShell = null;
+    notifyDesktopListenersActivate(null);
+    return null;
+  }
+  
+  
+  void activate(InternalShell ishell)
+  {
+    if(ishell is activeShell) return;
+    checkWidget();
+    if(ishell !is null)
+    {
+      if(!ishell.isVisible()) ishell.setVisible(true);
+      if((ishell.getStyle() & DWT.ON_TOP) !is 0)
+        ishell.moveAbove(null);
+      else
+      {
+        InternalShell firstRegular = getTopmostRegularShell();
+        if(firstRegular !is null && firstRegular !is ishell) ishell.moveAbove(firstRegular);
+        else
+        {
+          Control[] children = getChildren();
+          if(children.length > 0) ishell.moveAbove(children[0]);
+        }
+      }
+    }
+    InternalShell oldActiveShell = activeShell;
+    activeShell = ishell;
+    if(oldActiveShell !is null) oldActiveShell.redrawDecorationsAfterActivityChange();
+    if(ishell !is null)
+    {
+      if(activeShell.isVisible()) activeShell.redrawDecorationsAfterActivityChange();
+      setTabList(/**new Control[] { activeShell }*/[ activeShell ]);
+      activeShell.setFocus();
+    }
+    else
+    {
+      setTabList(/**new Control[] {}*/[]);
+      forceFocus();
+    }
+    notifyDesktopListenersActivate(ishell);
+  }
+  
+  
+  private InternalShell getTopmostRegularShell()
+  {
+    foreach(c; getChildren())
+    {
+      if(!(cast(InternalShell)c !is null)) continue;
+      if((c.getStyle() & DWT.ON_TOP) is 0) return cast(InternalShell)c;
+    }
+    return null;
+  }
+  
+  
+  private InternalShell getBottommostOnTopShell()
+  {
+    Control[] ch = getChildren();
+    for(int i=ch.length-1; i>=0; i--)
+    {
+      Control c = ch[i];
+      if(!(cast(InternalShell)c !is null)) continue;
+      if((c.getStyle() & DWT.ON_TOP) !is 0) return cast(InternalShell)c;
+    }
+    return null;
+  }
+  
+  
+  void shellVisibilityChanged(InternalShell ishell, bool visible)
+  {
+    if(visible)
+    {
+      if(!contains(visibleShells, ishell))
+      {
+        visibleShells ~= ishell;
+        if(autoMaximize && !ishell.getMaximized() && (ishell.getStyle() & DWT.MAX) !is 0 && hasVisibleMaximizedShell())
+          ishell.setMaximizedWithoutNotification(true);
+      }
+      if(ishell.getMaximized())
+        ishell.desktopResized(getClientArea());
+    }
+    else
+    {
+      visibleShells.remove(ishell);
+      if(ishell is activeShell)
+      {
+        activateTopmostVisibleShellExcept(ishell);
+        if(autoMaximize && !hasVisibleMaximizedShell())
+          setAllVisibleMaximized(false);
+      }
+    }
+  }
+  
+  
+  private InternalShell getInternalShell(Control c, bool[] desktopHit)
+  {
+    while(c !is null && c !is /**DesktopForm.*/this)
+    {
+      if(cast(InternalShell)c !is null && (cast(InternalShell)c).getParent() is this)
+        return cast(InternalShell)c;
+      c = c.getParent();
+    }
+    if(desktopHit !is null && c is /**DesktopForm.*/this) desktopHit[0] = true;
+    return null;
+  }
+  
+  
+  public InternalShell getActiveShell()
+  {
+    return activeShell;
+  }
+  
+  
+  public InternalShell[] getVisibleShells()
+  {
+    checkWidget();
+    return visibleShells;
+  }
+  
+  
+  public InternalShell[] getShells()
+  {
+    checkWidget();
+    return allShells;
+  }
+  
+  
+  public void setShowMaximizedTitle(bool b)
+  {
+    checkWidget();
+    showMaximizedTitle = b;
+    Rectangle ca = getClientArea();
+    foreach(c; getChildren())
+    {
+      if(cast(InternalShell)c !is null)
+        (cast(InternalShell)c).desktopResized(ca);
+    }
+  }
+
+
+  public bool getShowMaximizedTitle()
+  {
+    checkWidget();
+    return showMaximizedTitle;
+  }
+
+  
+  public void setAutoMaximize(bool b)
+  {
+    checkWidget();
+    autoMaximize = b;
+    bool hasMax = false;
+    foreach(ins; visibleShells)
+    {
+      if(ins.getMaximized())
+      {
+        hasMax = true;
+        break;
+      }
+    }
+    if(hasMax)
+    {
+      // Maximize all shells
+      foreach(ins; visibleShells)
+      {
+        if((ins.getStyle() & DWT.MAX) !is 0) ins.setMaximized(true);
+      }
+    }
+  }
+
+
+  public bool getAutoMaximize()
+  {
+    checkWidget();
+    return autoMaximize;
+  }
+  
+  
+  public void setEnableCtrlTab(bool b)
+  {
+    checkWidget();
+    this.enableCtrlTab = b;
+  }
+  
+  
+  public bool getEnableCtrlTab()
+  {
+    return enableCtrlTab;
+  }
+
+  
+  public void setAllowDeactivate(bool b)
+  {
+    checkWidget();
+    this.allowDeactivate = b;
+    if(!allowDeactivate && activeShell is null)
+      activateTopmostVisibleShellExcept(null);
+  }
+  
+  
+  public bool getAllowDeactivate()
+  {
+    return allowDeactivate;
+  }
+
+  
+  void shellMaximizedOrRestored(InternalShell ishell, bool maximized)
+  {
+    setAllVisibleMaximized(maximized);
+  }
+
+
+  private void setAllVisibleMaximized(bool maximized)
+  {
+    if(autoMaximize) // maximize or restore all shells
+    {
+      foreach(c; getChildren())
+      {
+        if(cast(InternalShell)c !is null)
+        {
+          InternalShell ishell = cast(InternalShell)c;
+          if((ishell.getStyle() & DWT.MAX) !is 0 && ishell.isVisible())
+            (cast(InternalShell)c).setMaximizedWithoutNotification(maximized);
+        }
+      }
+    }
+  }
+
+
+  private void activateNextShell()
+  {
+    if(activeShell is null)
+    {
+      activateTopmostVisibleShellExcept(null);
+      return;
+    }
+    if(visibleShells.length < 2) return;
+    InternalShell topReg = getTopmostRegularShell();
+    InternalShell botTop = getBottommostOnTopShell();
+    if((activeShell.getStyle() & DWT.ON_TOP) !is 0)
+    {
+      activeShell.moveBelow(botTop);
+      if(topReg !is null) activate(topReg);
+      else activateTopmostVisibleShellExcept(null);
+    }
+    else
+    {
+      activeShell.moveBelow(null);
+      activateTopmostVisibleShellExcept(null);
+    }
+  }
+
+
+  private void activatePreviousShell()
+  {
+    if(activeShell is null)
+    {
+      activateTopmostVisibleShellExcept(null);
+      return;
+    }
+    if(visibleShells.length < 2) return;
+    InternalShell topReg = getTopmostRegularShell();
+    InternalShell botTop = getBottommostOnTopShell();
+    if(activeShell is topReg && botTop !is null) activate(botTop);
+    else
+    {
+      Control[] ch = getChildren();
+      for(int i=ch.length-1; i>=0; i--)
+      {
+        if(cast(InternalShell)ch[i] !is null && ch[i].isVisible())
+        {
+          activate(cast(InternalShell)ch[i]);
+          break;
+        }
+      }
+    }
+  }
+
+
+  public void addDesktopListener(DesktopListener l)
+  {
+    desktopListeners ~= l;
+  }
+
+
+  public void removeDesktopListener(DesktopListener l)
+  {
+    desktopListeners.remove(l);
+  }
+
+
+  private void notifyDesktopListenersCreate(InternalShell ishell)
+  {
+    Event event = new Event();
+    event.widget = ishell;
+    foreach(l; desktopListeners) l.shellCreated(event);
+  }
+  
+  
+  private void notifyDesktopListenersDispose(InternalShell ishell)
+  {
+    Event event = new Event();
+    event.widget = ishell;
+    foreach(l; desktopListeners) l.shellDisposed(event);
+  }
+  
+  
+  private void notifyDesktopListenersActivate(InternalShell ishell)
+  {
+    Event event = new Event();
+    event.widget = ishell;
+    foreach(l; desktopListeners) l.shellActivated(event);
+  }
+  
+  
+  private bool hasVisibleMaximizedShell()
+  {
+    foreach(ins; visibleShells)
+      if(ins.getMaximized()) return true;
+    return false;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/ishell/InternalShell.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *******************************************************************************/
+
+module dwtx.novocode.ishell.InternalShell;
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.layout.FormAttachment;
+import dwt.layout.FormData;
+import dwt.layout.FormLayout;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Menu;
+
+import dwtx.novocode.SizeBorder;
+import dwtx.novocode.SizeGrip;
+import dwtx.novocode.ishell.DesktopForm;
+import dwtx.novocode.ishell.internal.TitleBar;
+import dwtx.novocode.ishell.internal.TitleBarButton;
+
+alias char[] String;
+
+
+/**
+ * An internal shell which can be placed on a DesktopForm.
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>RESIZE, CLOSE, MAX, ON_TOP, TOOL, NO_RADIO_GROUP</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * </p>
+ *
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Jan 21, 2005
+ * @version $Id: InternalShell.java 344 2005-07-09 22:37:51 +0000 (Sat, 09 Jul 2005) szeiger $
+ */
+
+// [TODO] Support styles NO_TRIM, BORDER, TITLE
+// [TODO] Separate "minimized" from "not visible"
+
+class InternalShell : Composite
+{
+  private static const int BORDER_SIZE = 4;
+
+  private Composite contentPane;
+  private TitleBar titleBar;
+  private SizeGrip sizeGrip;
+  private SizeBorder sizeBorder;
+  private int minWidth = 112;
+  private int minHeight;
+  private DesktopForm desktop;
+  private bool maximized;
+  private Rectangle pluralizedBounds;
+  private int titleHeight;
+  private int style;
+  private TitleBarButton closeButton, maxButton, minButton;
+
+  Control focusControl;
+
+  
+  this(DesktopForm parent, int style)
+  {
+    super(parent, checkStyle(style));
+    this.desktop = parent;
+    this.style = style;
+    setBackground(getDisplay().getSystemColor(DWT.COLOR_WIDGET_BACKGROUND));
+    FormLayout layout = new FormLayout();
+    setLayout(layout);
+    FormData fd;
+
+    titleBar = new TitleBar(this, style & (DWT.CLOSE | DWT.RESIZE | DWT.MAX | DWT.TOOL | DWT.MIN));
+    titleHeight = titleBar.computeSize(DWT.DEFAULT, DWT.DEFAULT, true).y;
+    
+    Control leftButton = null;
+
+    if((style & (DWT.CLOSE | DWT.MAX | DWT.MIN)) !is 0)
+    {
+      closeButton = new TitleBarButton(this, DWT.CLOSE);
+      if((style & DWT.CLOSE) is 0) closeButton.setEnabled(false);
+      closeButton.addListener(DWT.Selection, dgListener(&closeListener));
+      fd = new FormData(titleHeight, titleHeight);
+      if(leftButton !is null) fd.right = new FormAttachment(leftButton);
+      else fd.right = new FormAttachment(100, -BORDER_SIZE);
+      fd.top = new FormAttachment(0, BORDER_SIZE);
+      closeButton.setLayoutData(fd);
+      leftButton = closeButton;
+
+      if((style & (DWT.MAX|DWT.MIN)) !is 0)
+      {
+        maxButton = new TitleBarButton(this, DWT.MAX);
+        if((style & DWT.MAX) is 0) maxButton.setEnabled(false);
+        maxButton.addListener(DWT.Selection, dgListener(&maximizeListener));
+        fd = new FormData(titleHeight, titleHeight);
+        if(leftButton !is null) fd.right = new FormAttachment(leftButton);
+        else fd.right = new FormAttachment(100, -BORDER_SIZE);
+        fd.top = new FormAttachment(0, BORDER_SIZE);
+        maxButton.setLayoutData(fd);
+        leftButton = maxButton;
+
+        minButton = new TitleBarButton(this, DWT.MIN);
+        if((style & DWT.MIN) is 0) minButton.setEnabled(false);
+        minButton.addListener(DWT.Selection, dgListener(&minimizeListener));
+        fd = new FormData(titleHeight, titleHeight);
+        if(leftButton !is null) fd.right = new FormAttachment(leftButton);
+        else fd.right = new FormAttachment(100, -BORDER_SIZE);
+        fd.top = new FormAttachment(0, BORDER_SIZE);
+        minButton.setLayoutData(fd);
+        leftButton = minButton;
+      }
+    }
+
+    fd = new FormData();
+    fd.left = new FormAttachment(0, BORDER_SIZE);
+    if(leftButton !is null) fd.right = new FormAttachment(leftButton);
+    else fd.right = new FormAttachment(100, -BORDER_SIZE);
+    fd.top = new FormAttachment(0, BORDER_SIZE);
+    titleBar.setLayoutData(fd);
+    
+    contentPane = new Composite(this, DWT.NONE);
+    fd = new FormData();
+    fd.left = new FormAttachment(0, BORDER_SIZE);
+    fd.right = new FormAttachment(100, -BORDER_SIZE);
+    fd.top = new FormAttachment(titleBar, 1);
+    fd.bottom = new FormAttachment(100, -BORDER_SIZE);
+    contentPane.setLayoutData(fd);
+
+    sizeBorder = new SizeBorder(this, this, DWT.BORDER);
+    sizeBorder.setBorderWidth(BORDER_SIZE);
+    fd = new FormData();
+    fd.left = new FormAttachment(0);
+    fd.right = new FormAttachment(100);
+    fd.top = new FormAttachment(0);
+    fd.bottom = new FormAttachment(100);
+    sizeBorder.setLayoutData(fd);
+    
+    minHeight = titleHeight + 2*BORDER_SIZE;
+    sizeBorder.setMinimumShellSize(minWidth, minHeight);
+    sizeBorder.setCornerSize(titleHeight + BORDER_SIZE);
+    if((style & DWT.RESIZE) is 0) sizeBorder.setEnabled(false);
+    
+    setSize(320, 240);
+    setVisible(false);
+    
+    desktop.manage(this);
+  }
+
+
+  private void closeListener(Event event)
+  {
+      close();
+  }
+
+
+  private void maximizeListener(Event event)
+  {
+      setMaximized(!maximized);
+  }
+
+
+  private void minimizeListener(Event event)
+  {
+      setMinimized(true);
+  }
+
+
+  private static int checkStyle(int style)
+  {
+    int mask = DWT.NO_RADIO_GROUP;
+    style &= mask;
+    return style;
+  }
+
+
+  public int getStyle()
+  {
+    return style;
+  }
+
+
+  public Composite getContentPane() { return contentPane; }
+
+
+  public void setText(String s) { titleBar.setText(s); }
+
+  public String getText() { return titleBar.getText(); }
+
+
+  public void setCustomMenu(Menu menu) { titleBar.setMenu(menu); }
+
+  public Menu getCustomMenu() { return titleBar.getMenu(); }
+
+
+  public void setImage(Image image) { titleBar.setImage(image); }
+
+  public Image getImage() { return titleBar.getImage(); }
+
+
+  public void createSizeGrip(int style)
+  {
+    checkWidget();
+    if(sizeGrip !is null)
+      throw new DWTException("SizeGrip was already created");
+    if((this.style & DWT.RESIZE) is 0)
+      throw new DWTException("Cannot create SizeGrip for InternalShell without style RESIZE");
+    sizeGrip = new SizeGrip(this, this, style);
+    sizeGrip.setBackground(contentPane.getBackground());
+    sizeGrip.moveAbove(contentPane);
+    FormData fd = new FormData();
+    fd.right = new FormAttachment(100, -BORDER_SIZE);
+    fd.bottom = new FormAttachment(100, -BORDER_SIZE);
+    sizeGrip.setLayoutData(fd);
+    sizeGrip.setMinimumShellSize(minWidth, minHeight);
+    if(isVisible()) layout(true);
+  }
+  
+  
+  public Point computeSize(int wHint, int hHint, bool changed)
+  {
+    Point p = super.computeSize(wHint, hHint, changed);
+    if(p.x < minWidth) p.x = minWidth;
+    if(p.y < minHeight) p.y = minHeight;
+    return p;
+  }
+  
+  
+  public void setSize(int width, int height)
+  {
+    if(width < minWidth) width = minWidth;
+    if(height < minHeight) height = minHeight;
+    super.setSize(width, height);
+  }
+
+  
+  public void setBounds(int x, int y, int width, int height)
+  {
+    if(width < minWidth) width = minWidth;
+    if(height < minHeight) height = minHeight;
+    super.setBounds(x, y, width, height);
+  }
+
+
+  public void setMinimumSize(int width, int height)
+  {
+    checkWidget();
+    minWidth = width;
+    minHeight = height;
+    sizeGrip.setMinimumShellSize(minWidth, minHeight);
+    sizeBorder.setMinimumShellSize(minWidth, minHeight);
+    Point size = getSize();
+    if(size.x < minWidth || size.y < minHeight)
+      setSize(Math.max(minWidth, size.x), Math.max(minHeight, size.y));
+  }
+
+
+  public void close()
+  {
+    Event event = new Event();
+    notifyListeners(DWT.Close, event);
+    if(event.doit && !isDisposed()) dispose();
+  }
+  
+  
+  public void open()
+  {
+    desktop.activate(this);
+    setVisible(true);
+    setFocus();
+  }
+  
+  
+  public void setVisible(bool visible)
+  {
+    if(!visible) desktop.shellVisibilityChanged(this, false);
+    super.setVisible(visible);
+    if(visible) desktop.shellVisibilityChanged(this, true);
+  }
+  
+  
+  public void setActive()
+  {
+    desktop.activate(this);
+  }
+  
+  
+  public void setMaximized(bool maximized)
+  {
+    checkWidget();
+    if(this.maximized is maximized) return;
+    setMaximizedWithoutNotification(maximized);
+    desktop.shellMaximizedOrRestored(this, maximized);
+  }
+
+  
+  public void setMinimized(bool minimized)
+  {
+    checkWidget();
+    bool wasMaximized = maximized;
+    setVisible(!minimized);
+    maximized = wasMaximized;
+  }
+  
+  
+  public bool getMinimized()
+  {
+    return getVisible();
+  }
+
+  
+  void setMaximizedWithoutNotification(bool maximized)
+  {
+    if(this.maximized is maximized) return;
+    this.maximized = maximized;
+    if(maximized)
+    {
+      pluralizedBounds = getBounds();
+      desktopResized(desktop.getClientArea());
+    }
+    else
+    {
+      setBounds(pluralizedBounds.x,pluralizedBounds.y,pluralizedBounds.width,pluralizedBounds.height);
+    }
+    // Note: This method may be called in a Dispose event for this object
+    if(sizeGrip !is null && !sizeGrip.isDisposed()) sizeGrip.setVisible(!maximized);
+    if(!sizeBorder.isDisposed()) sizeBorder.setEnabled(!maximized && (style & DWT.RESIZE) !is 0);
+    if(maxButton !is null && !maxButton.isDisposed()) maxButton.redraw();
+  }
+
+  
+  public bool getMaximized()
+  {
+    checkWidget();
+    return maximized;
+  }
+  
+  
+  void redrawDecorationsAfterActivityChange()
+  {
+    // Note: This method may be called in a Dispose event for this object
+    if(!titleBar.isDisposed()) titleBar.redraw();
+    if(closeButton !is null && !closeButton.isDisposed()) closeButton.redraw();
+    if(maxButton !is null && !maxButton.isDisposed()) maxButton.redraw();
+    if(minButton !is null && !minButton.isDisposed()) minButton.redraw();
+  }
+  
+  
+  void desktopResized(Rectangle deskCA)
+  {
+    if(maximized)
+    {
+      int hideTitle = desktop.getShowMaximizedTitle() ? 0 : (titleHeight+1);
+      setBounds(deskCA.x - BORDER_SIZE,
+        deskCA.y - BORDER_SIZE - hideTitle,
+        deskCA.width + 2*BORDER_SIZE,
+        deskCA.height + 2*BORDER_SIZE + hideTitle);
+    }
+    else forceVisibleLocation(deskCA);
+  }
+  
+  
+  public bool setFocus()
+  {
+    if(focusControl !is null && focusControl !is this && !focusControl.isDisposed())
+      return focusControl.setFocus();
+    return super.setFocus();
+  }
+  
+  
+  public bool isActiveShell()
+  {
+    return desktop.getActiveShell() is this;
+  }
+  
+  
+  private void forceVisibleLocation(Rectangle deskCA)
+  {
+    Point p = getLocation();
+    Point minGrabSize = titleBar.getMinGrabSize();
+    int x = p.x, y = p.y;
+    int minX = minGrabSize.x + BORDER_SIZE, minY = minGrabSize.y + BORDER_SIZE;
+    x = Math.min(Math.max(x, deskCA.x+minY), deskCA.x+deskCA.width-minX);
+    y = Math.min(Math.max(y, deskCA.y+minY), deskCA.y+deskCA.height-minY);
+    if(x != p.x || y != p.y) setLocation(x, y);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/ishell/internal/CustomDrawnButton.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *******************************************************************************/
+
+module dwtx.novocode.ishell.internal.CustomDrawnButton;
+
+import dwt.DWT;
+import dwt.graphics.Point;
+import dwt.widgets.Canvas;
+import dwt.widgets.Composite;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+
+
+/**
+ * A simple button control which needs to be subclassed to draw a specific
+ * kind of button. This base class provides the event handling.
+ * 
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Jan 30, 2005
+ * @version $Id: CustomDrawnButton.java 320 2005-02-26 13:37:02 +0000 (Sat, 26 Feb 2005) szeiger $
+ */
+
+class CustomDrawnButton : Canvas
+{
+  private bool pressed;
+  private Display display;
+  private bool drawnMouseIn = false;
+
+
+  this(Composite parent, int style)
+  {
+    super(parent, style);
+    this.display = getDisplay();
+
+    addListener(DWT.Paint, dgListener(&paintListener));
+
+    addListener(DWT.MouseDown, dgListener(&onMouseDown));
+
+    addListener(DWT.MouseUp, dgListener(&onMouseUp));
+    
+    addListener(DWT.MouseMove, dgListener(&onMouseMove));
+  }
+  
+  
+  private void paintListener(Event event)
+  {
+        bool mouseIn = mouseIn();
+        onPaint(event, pressed && mouseIn);
+        drawnMouseIn = mouseIn;
+  }
+
+
+  private void onMouseDown(Event event)
+  {
+        if(event.button is 1)
+        {
+          pressed = true;
+          redraw();
+        }
+        else if(event.button is 3 && (event.stateMask & DWT.BUTTON1) !is 0) // chord click
+        {
+          pressed = false;
+          redraw();
+        }
+  }
+
+
+  private void onMouseUp(Event event)
+  {
+        if(pressed && (event.stateMask & DWT.BUTTON1) !is 0)
+        {
+          pressed = false;
+          if(mouseIn())
+          {
+            Event selectionEvent = new Event();
+            notifyListeners(DWT.Selection, selectionEvent);
+          }
+          if(!isDisposed()) redraw();
+        }
+  }
+
+
+  private void onMouseMove(Event event)
+  {
+        if(!pressed) return;
+        bool mouseIn = mouseIn();
+        if(mouseIn is drawnMouseIn) return;
+        redraw();
+  }
+
+
+  private bool mouseIn()
+  {
+    Point p = toControl(display.getCursorLocation());
+    if(p.x < -1 || p.y < -1) return false;
+    Point size = getSize();
+    return p.x <= size.x+1 && p.y <= size.y+1;
+  }
+
+
+  public Point computeSize(int wHint, int hHint, bool changed)
+  {
+    checkWidget();
+    if(wHint is DWT.DEFAULT) wHint = 0;
+    if(hHint is DWT.DEFAULT) hHint = 0;
+    return new Point(wHint, hHint);
+  }
+
+
+  public bool setFocus()
+  {
+    checkWidget();
+    return false;
+  }
+
+
+  public bool isReparentable ()
+  {
+    checkWidget();
+    return false;
+  }
+  
+  
+  protected abstract void onPaint(Event event, bool pressed);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/ishell/internal/DesktopListener.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *******************************************************************************/
+
+module dwtx.novocode.ishell.internal.DesktopListener;
+
+import dwt.internal.DWTEventListener;
+import dwt.widgets.Event;
+
+
+/**
+ * A listener which receives events when a change occurs on a DesktopForm.
+ *
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Jan 23, 2005
+ * @version $Id: DesktopListener.java 320 2005-02-26 13:37:02 +0000 (Sat, 26 Feb 2005) szeiger $
+ */
+
+interface DesktopListener : DWTEventListener
+{
+  public void shellCreated(Event event);
+  
+  public void shellDisposed(Event event);
+  
+  public void shellActivated(Event event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/ishell/internal/TitleBar.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,567 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *******************************************************************************/
+
+module dwtx.novocode.ishell.internal.TitleBar;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import dwtx.dwtxhelper.Timer;
+import dwtx.dwtxhelper.TimerTask;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.FontData;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwt.graphics.PaletteData;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.graphics.RGB;
+import dwt.widgets.Canvas;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Menu;
+import dwt.widgets.MenuItem;
+import dwt.widgets.Shell;
+
+import dwtx.novocode.ishell.DesktopForm;
+import dwtx.novocode.ishell.InternalShell;
+
+alias char[] String;
+
+
+/**
+ * A title bar for an InternalShell.
+ * 
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Jan 21, 2005
+ * @version $Id: TitleBar.java 342 2005-07-09 20:37:13 +0000 (Sat, 09 Jul 2005) szeiger $
+ */
+
+class TitleBar : Canvas
+{
+  private static const long UPDATE_DELAY = 25;
+  private static const int MINIMUM_GRAB_AREA = 2;
+  private static const String ELLIPSIS = "...";
+  private static const int LEFT_PADDING = 2;
+  private static const int RIGHT_PADDING = 2;
+  private static const int IMAGE_SIZE = 16;
+  private static const int TOOL_SIZE = 14;
+  private static const int TOP_PADDING = 1;
+  private static const int BOTTOM_PADDING = 1;
+
+  private int mouseDownOffsetX, mouseDownOffsetY, snapBackX, snapBackY;
+  private bool cancelled;
+  private /**volatile*/ long lastUpdate;
+  private Timer timer;
+  private TimerTask timerTask;
+  private InternalShell ishell;
+  private DesktopForm desktop;
+  private String text;
+  private Image image;
+  private bool styleClose, styleMax, styleTool, styleMin;
+  private Image closeImage, restoreImage, maximizeImage, minimizeImage;
+  private MenuItem restoreItem, closeItem, maximizeItem;
+  private Menu defaultPopup;
+  private Point minGrabSize;
+  private Shell shell;
+  private Color gradStartColor, gradEndColor, textColor, inactiveGradStartColor, inactiveGradEndColor, inactiveTextColor;
+  private Listener activateListener, deactivateListener;
+
+
+  this(InternalShell parent, int style)
+  {
+    super(parent, checkStyle(style));
+    this.timer = new Timer(true);
+    this.minGrabSize = new Point(MINIMUM_GRAB_AREA, MINIMUM_GRAB_AREA);
+    this.ishell = parent;
+    this.desktop = cast(DesktopForm)ishell.getParent();
+    this.styleClose = (style & DWT.CLOSE) !is 0;
+    this.styleMax = (style & DWT.MAX) !is 0;
+    this.styleMin = (style & DWT.MIN) !is 0;
+    this.styleTool = (style & DWT.TOOL) !is 0;
+
+    Display display = getDisplay();
+    shell = getShell();
+
+    gradStartColor = display.getSystemColor(DWT.COLOR_TITLE_BACKGROUND);
+    gradEndColor = display.getSystemColor(DWT.COLOR_TITLE_BACKGROUND_GRADIENT);
+    textColor = display.getSystemColor(DWT.COLOR_TITLE_FOREGROUND);
+    inactiveGradStartColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND);
+    inactiveGradEndColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
+    inactiveTextColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_FOREGROUND);
+
+    GC gc = new GC(this);
+    int imgHeight = gc.getFontMetrics().getHeight()-1;
+    if(imgHeight%2 is 0) imgHeight--;
+    gc.dispose();
+
+    closeImage = createMenuImage(IMAGE_TYPE_CLOSE, imgHeight);
+    restoreImage = createMenuImage(IMAGE_TYPE_RESTORE, imgHeight);
+    maximizeImage = createMenuImage(IMAGE_TYPE_MAXIMIZE, imgHeight);
+    minimizeImage = createMenuImage(IMAGE_TYPE_MINIMIZE, imgHeight);
+
+    setFont(createTitleFont(getFont(), styleTool));
+
+    defaultPopup = new Menu(this);
+
+    restoreItem = new MenuItem(defaultPopup, DWT.PUSH);
+    restoreItem.setText("&Restore");
+    restoreItem.setImage(restoreImage);
+    restoreItem.addListener(DWT.Selection, dgListener(&restoreListener));
+
+    MenuItem minimizeItem = new MenuItem(defaultPopup, DWT.PUSH);
+    minimizeItem.setText("Mi&nimize");
+    minimizeItem.setEnabled(styleMin);
+    minimizeItem.setImage(minimizeImage);
+    minimizeItem.addListener(DWT.Selection, dgListener(&minimizeListener));
+
+    maximizeItem = new MenuItem(defaultPopup, DWT.PUSH);
+    maximizeItem.setText("Ma&ximize");
+    maximizeItem.setImage(maximizeImage);
+    maximizeItem.addListener(DWT.Selection, dgListener(&maximizeListener));
+    
+    new MenuItem(defaultPopup, DWT.SEPARATOR);
+
+    closeItem = new MenuItem(defaultPopup, DWT.PUSH);
+    closeItem.setText("&Close");
+    closeItem.setEnabled(styleClose);
+    closeItem.setImage(closeImage);
+    closeItem.addListener(DWT.Selection, dgListener(&closeListener));
+
+    addListener(DWT.Paint, dgListener(&onPaint));
+    addListener(DWT.MouseDown, dgListener(&onMouseDown));
+    addListener(DWT.MenuDetect, dgListener(&onMenuDetect));
+    addListener(DWT.MouseDoubleClick, dgListener(&onMouseDoubleClick));
+    addListener(DWT.MouseMove, dgListener(&onMouseMove));
+    addListener(DWT.MouseUp, dgListener(&onMouseUp));
+
+    activateListener = dgListener(&onActivateListener);
+    deactivateListener = dgListener(&onDeactivateListener);
+    shell.addListener(DWT.Activate, activateListener);
+    shell.addListener(DWT.Deactivate, deactivateListener);
+    
+    addListener(DWT.Dispose, dgListener(&onDispose));
+  }
+
+
+  private void restoreListener(Event event)
+  {
+    ishell.setMaximized(false);
+  }
+
+
+  private void minimizeListener(Event event)
+  {
+    ishell.setMinimized(true);
+  }
+
+
+  private void maximizeListener(Event event)
+  {
+    ishell.setMaximized(true);
+  }
+
+
+  private void closeListener(Event event)
+  {
+    ishell.close();
+  }
+
+
+  private void onPaint(Event event)
+  {
+        Rectangle r = getClientArea();
+        if(r.width is 0 || r.height is 0) return;
+        
+        bool active = (shell is display.getActiveShell() && ishell.isActiveShell());
+
+        GC gc = event.gc;
+        gc.setForeground(active ? gradStartColor : inactiveGradStartColor);
+        gc.setBackground(active ? gradEndColor : inactiveGradEndColor);
+        gc.fillGradientRectangle(r.x, r.y, r.width, r.height, false);
+        
+        int textLeftPadding = LEFT_PADDING;
+        if(image !is null)
+        {
+          Rectangle imageBounds = image.getBounds();
+          if(imageBounds.width > IMAGE_SIZE || imageBounds.height > IMAGE_SIZE)
+            gc.drawImage(image, 0, 0, imageBounds.width, imageBounds.height, LEFT_PADDING, TOP_PADDING, IMAGE_SIZE, IMAGE_SIZE);
+          else
+            gc.drawImage(image, LEFT_PADDING + (IMAGE_SIZE-imageBounds.width)/2, (r.height-imageBounds.height)/2);
+          textLeftPadding += IMAGE_SIZE + LEFT_PADDING;
+        }
+        
+        if(text !is null && text.length() > 0)
+        {
+          gc.setForeground(active ? textColor : inactiveTextColor);
+          String s = text;
+          int availableWidth = r.width - textLeftPadding - RIGHT_PADDING;
+          if(gc.textExtent(s, DWT.DRAW_TRANSPARENT).x > availableWidth)
+          {
+            int ellipsisWidth = gc.textExtent(ELLIPSIS, DWT.DRAW_TRANSPARENT).x;
+            while(s.length() > 0)
+            {
+              s = s.substring(0, s.length()-1);
+              if(gc.textExtent(s, DWT.DRAW_TRANSPARENT).x + ellipsisWidth <= availableWidth)
+              {
+                s ~= ELLIPSIS;
+                break;
+              }
+            }
+            setToolTipText(text);
+          }
+          else setToolTipText(null);
+          if(s.length() > 0) gc.drawString(s, textLeftPadding, (r.height-gc.getFontMetrics().getHeight())/2, true);
+        }
+        else setToolTipText(null);
+  }
+
+
+  private void onMouseDown(Event event)
+  {
+        if(event.button is 1)
+        {
+          if(image !is null && event.x < LEFT_PADDING + IMAGE_SIZE)
+          {
+            cancelled = true;
+            // left-clicking on the image always shows the default popup menu
+            instrumentDefaultPopup(true);
+            defaultPopup.setLocation(toDisplay(0, getSize().y));
+            defaultPopup.setVisible(true);
+          }
+          else
+          {
+            mouseDownOffsetX = event.x;
+            mouseDownOffsetY = event.y;
+            Point p = ishell.getLocation();
+            snapBackX = p.x;
+            snapBackY = p.y;
+            cancelled = false;
+          }
+        }
+        else if(event.button is 3)
+        {
+          if((event.stateMask & DWT.BUTTON1) !is 0 && snapBackX !is Integer.MIN_VALUE && snapBackY !is Integer.MIN_VALUE)
+          {
+            ishell.setLocation(snapBackX, snapBackY);
+            snapBackX = Integer.MIN_VALUE;
+            snapBackY = Integer.MIN_VALUE;
+            cancelled = true;
+          }
+          else
+          {
+          }
+        }
+  }
+
+
+  private void onMenuDetect(Event event)
+  {
+        event.doit = false;
+        Menu m = getMenu();
+        if(m is null || m.isDisposed())
+        {
+          m = defaultPopup;
+          instrumentDefaultPopup(false);
+        }
+        m.setLocation(event.x, event.y);
+        m.setVisible(true);
+  }
+
+
+  private void onMouseDoubleClick(Event event)
+  {
+        if(event.button is 1)
+        {
+          if(image !is null && event.x < LEFT_PADDING + IMAGE_SIZE)
+          {
+            if(styleClose) ishell.close();
+          }
+          else
+          {
+            if(styleMax) ishell.setMaximized(!ishell.getMaximized());
+          }
+          cancelled = true;
+        }
+  }
+
+
+  private void onMouseMove(Event event)
+  {
+        if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0 && !ishell.getMaximized())
+        {
+          if(timerTask !is null)
+          {
+            timerTask.cancel();
+            timerTask = null;
+          }
+          long now = System.currentTimeMillis();
+          if(lastUpdate + UPDATE_DELAY < now)
+          {
+            performMove(event);
+            lastUpdate = now;
+          }
+          else
+          {
+            timerTask = new class() TimerTask
+            {
+              public void run()
+              {
+                TimerTask executingTask = this;
+                event.display.asyncExec(new class() Runnable
+                {
+                  public void run()
+                  {
+                    if(executingTask !is timerTask) return;
+                    performMove(event);
+                  }
+                });
+              }
+            };
+            timer.schedule(timerTask, UPDATE_DELAY);
+          }
+        }
+  }
+
+
+  private void onMouseUp(Event event)
+  {
+        if(ishell.getMaximized()) return;
+        if(image is null || event.x >= LEFT_PADDING + IMAGE_SIZE)
+        {
+          if(timerTask !is null)
+          {
+            timerTask.cancel();
+            timerTask = null;
+          }
+          if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0)
+          {
+            performMove(event);
+          }
+        }
+  }
+
+
+  private void onActivateListener(Event event)
+  {
+    redraw();
+  }
+
+
+  private void onDeactivateListener(Event event)
+  {
+    redraw();
+  }
+
+
+  private void onDispose(Event event)
+  {
+        timer.cancel();
+        shell.removeListener(DWT.Activate, activateListener);
+        shell.removeListener(DWT.Deactivate, deactivateListener);
+        closeImage.dispose();
+        maximizeImage.dispose();
+        restoreImage.dispose();
+        minimizeImage.dispose();
+        defaultPopup.dispose();
+  }
+
+
+  private void performMove(Event event)
+  {
+    Point p = ishell.getLocation();
+    int newX = p.x + event.x - mouseDownOffsetX;
+    int newY = p.y + event.y - mouseDownOffsetY;
+
+    // Make sure that the minimum grab area stays visible
+    Rectangle deskCA = desktop.getClientArea();
+    Rectangle bounds = getBounds();
+    newX = Math.min(Math.max(newX, deskCA.x-bounds.x-bounds.width+MINIMUM_GRAB_AREA), deskCA.x-bounds.x+deskCA.width-minGrabSize.x);
+    newY = Math.min(Math.max(newY, deskCA.y-bounds.y-bounds.height+MINIMUM_GRAB_AREA), deskCA.y-bounds.y+deskCA.height-MINIMUM_GRAB_AREA);
+
+    if(newX !is p.x || newY !is p.y) ishell.setLocation(newX, newY);
+  }
+  
+  
+  public Point getMinGrabSize()
+  {
+    return minGrabSize;
+  }
+
+
+  public Point computeSize(int wHint, int hHint, bool changed)
+  {
+    checkWidget();
+    if(wHint is DWT.DEFAULT) wHint = 50;
+    if(hHint is DWT.DEFAULT)
+    {
+      GC gc = new GC(this);
+      hHint = gc.getFontMetrics().getHeight();
+      hHint = Math.max(hHint, styleTool ?  TOOL_SIZE : IMAGE_SIZE);
+      hHint += TOP_PADDING + BOTTOM_PADDING;
+      gc.dispose();
+    }
+    return new Point(wHint, hHint);
+  }
+
+
+  private static int checkStyle(int style)
+  {
+    //int mask = DWT.SHADOW_IN | DWT.FLAT;
+    //style &= mask;
+    style = DWT.NO_FOCUS;
+    return style;
+  }
+
+
+  public bool setFocus()
+  {
+    checkWidget();
+    return false;
+  }
+
+
+  public bool isReparentable ()
+  {
+    checkWidget();
+    return false;
+  }
+  
+  
+  public void setText(String text)
+  {
+    checkWidget();
+    this.text = text;
+    redraw();
+  }
+  
+  
+  public String getText() { return text; }
+  
+  
+  public void setImage(Image image)
+  {
+    checkWidget();
+    if(styleTool) return;
+    this.image = image;
+    minGrabSize.x = MINIMUM_GRAB_AREA;
+    if(image !is null) minGrabSize.x += LEFT_PADDING + IMAGE_SIZE;
+    redraw();
+  }
+  
+  
+  public Image getImage() { return image; }
+
+
+  private Font createTitleFont(Font f, bool tool)
+  {
+    FontData[] fds = f.getFontData();
+    foreach(fd; fds)
+    {
+      fd.setStyle(fd.getStyle() | DWT.BOLD);
+      if(tool) fd.setHeight(cast(int)(fd.getHeight()*0.9));
+    }
+    return new Font(getDisplay(), fds);
+  }
+  
+  
+  private void instrumentDefaultPopup(bool onImage)
+  {
+    restoreItem.setEnabled(styleMax && ishell.getMaximized());
+    maximizeItem.setEnabled(styleMax && !ishell.getMaximized());
+    MenuItem def = null;
+    if(onImage)
+    {
+      if(styleClose) def = closeItem;
+    }
+    else if(styleMax)
+    {
+      def = ishell.getMaximized() ? restoreItem : maximizeItem;
+    }
+    defaultPopup.setDefaultItem(def);
+  }
+  
+  
+  private static const int IMAGE_TYPE_CLOSE    = 1;
+  private static const int IMAGE_TYPE_MAXIMIZE = 2;
+  private static const int IMAGE_TYPE_RESTORE  = 3;
+  private static const int IMAGE_TYPE_MINIMIZE = 4;
+
+
+  private Image createMenuImage(int type, int height)
+  {
+    final Point size = new Point(height, height);
+    final int imgWidth = height + height/2;
+    final Color fg = getForeground();
+    final Color white = getDisplay().getSystemColor(DWT.COLOR_WHITE);
+    final RGB blackRGB = new RGB(0,0,0);
+    
+    ImageData id = new ImageData(imgWidth, size.y, 1, new PaletteData([ blackRGB, fg.getRGB() ]));
+    ImageData maskid = new ImageData(imgWidth, size.y, 1, new PaletteData([ blackRGB, white.getRGB() ]));
+
+    Image img = new Image(getDisplay(), id);
+    GC gc = new GC(img);
+    gc.setForeground(fg);
+    drawMenuImage(gc, size, type);
+    gc.dispose();
+    
+    Image maskimg = new Image(getDisplay(), maskid);
+    gc = new GC(maskimg);
+    gc.setForeground(white);
+    drawMenuImage(gc, size, type);
+    gc.dispose();
+
+    Image transp = new Image(getDisplay(), img.getImageData(), maskimg.getImageData());
+    img.dispose();
+    maskimg.dispose();
+    return transp;
+  }
+
+
+  private void drawMenuImage(GC gc, Point size, int type)
+  {
+    switch(type)
+    {
+      case IMAGE_TYPE_CLOSE:
+        gc.drawLine(1, 1, size.x-2, size.y-2);
+        gc.drawLine(2, 1, size.x-2, size.y-3);
+        gc.drawLine(1, 2, size.x-3, size.y-2);
+        gc.drawLine(1, size.y-2, size.x-2, 1);
+        gc.drawLine(1, size.y-3, size.x-3, 1);
+        gc.drawLine(2, size.y-2, size.x-2, 2);
+        break;
+
+      case IMAGE_TYPE_RESTORE:
+        gc.drawRectangle(0, 4, size.x-4, size.y-6);
+        gc.drawLine(1, 5, size.x-5, 5);
+        gc.drawLine(2, 1, size.x-2, 1);
+        gc.drawLine(2, 2, size.x-2, 2);
+        gc.drawPoint(2, 3);
+        gc.drawLine(size.x-2, 3, size.x-2, size.y-5);
+        gc.drawPoint(size.x-3, size.y-5);
+        break;
+
+      case IMAGE_TYPE_MAXIMIZE:
+        gc.drawRectangle(0, 0, size.x-2, size.y-2);
+        gc.drawLine(1, 1, size.x-3, 1);
+        break;
+
+      case IMAGE_TYPE_MINIMIZE:
+        gc.drawLine(1, size.y-2, size.x-4, size.y-2);
+        gc.drawLine(1, size.y-3, size.x-4, size.y-3);
+        break;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/novocode/ishell/internal/TitleBarButton.d	Sun Oct 26 14:54:39 2008 +0100
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Stefan Zeiger and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.novocode.com/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
+ *******************************************************************************/
+
+module dwtx.novocode.ishell.internal.TitleBarButton;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.GC;
+import dwt.graphics.Point;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Shell;
+
+import dwtx.novocode.ishell.InternalShell;
+import dwtx.novocode.ishell.internal.CustomDrawnButton;
+
+
+/**
+ * A title bar button for an InternalShell.
+ *
+ * @author Stefan Zeiger (szeiger@novocode.com)
+ * @since Jan 30, 2005
+ * @version $Id: TitleBarButton.java 322 2005-02-26 20:31:26 +0000 (Sat, 26 Feb 2005) szeiger $
+ */
+
+class TitleBarButton : CustomDrawnButton
+{
+  private Color highlightShadowColor, lightShadowColor, normalShadowColor, darkShadowColor;
+  private Color gradEndColor, inactiveGradEndColor, widgetBackgroundColor, widgetForegroundColor;
+  private int style;
+  private Shell shell;
+  private Display display;
+  private InternalShell ishell;
+  private int leftOff, rightOff;
+  private Listener activateListener, deactivateListener;
+
+  this(InternalShell parent, int style)
+  {
+    super(parent, DWT.NO_FOCUS | DWT.NO_BACKGROUND);
+    this.style = style;
+    this.shell = getShell();
+    this.display = getDisplay();
+    this.ishell = parent;
+
+    highlightShadowColor = display.getSystemColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+    lightShadowColor = display.getSystemColor(DWT.COLOR_WIDGET_LIGHT_SHADOW);
+    normalShadowColor = display.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
+    darkShadowColor = display.getSystemColor(DWT.COLOR_WIDGET_DARK_SHADOW);
+    gradEndColor = display.getSystemColor(DWT.COLOR_TITLE_BACKGROUND_GRADIENT);
+    inactiveGradEndColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
+    widgetBackgroundColor = display.getSystemColor(DWT.COLOR_WIDGET_BACKGROUND);
+    widgetForegroundColor = display.getSystemColor(DWT.COLOR_WIDGET_FOREGROUND);
+    
+    if((style & (DWT.CLOSE | DWT.MAX)) !is 0) rightOff = 2;
+    else leftOff = 2;
+    
+    activateListener = dgListener(&onActivateListener);
+    deactivateListener = dgListener(&onDeactivateListener);
+    shell.addListener(DWT.Activate, activateListener);
+    shell.addListener(DWT.Deactivate, deactivateListener);
+    
+    addListener(DWT.Dispose, dgListener(&onDispose));
+  }
+
+
+  private void onActivateListener(Event event)
+  {
+      redraw();
+  }
+
+
+  private void onDeactivateListener(Event event)
+  {
+      redraw();
+  }
+
+
+  private void onDispose(Event event)
+  {
+        shell.removeListener(DWT.Activate, activateListener);
+        shell.removeListener(DWT.Deactivate, deactivateListener);
+  }
+
+
+  public int getStyle()
+  {
+    return style;
+  }
+
+  
+  protected void onPaint(Event event, bool pressed)
+  {
+    Point size = getSize();
+    bool active = (shell is display.getActiveShell() && ishell.isActiveShell());
+    GC gc = event.gc;
+
+    gc.setBackground(active ? gradEndColor : inactiveGradEndColor);
+    gc.fillRectangle(0, 0, size.x, size.y);
+    gc.setBackground(widgetBackgroundColor);
+    gc.fillRectangle(2, 4, size.x-4, size.y-6);
+
+    Color tloColor, tliColor, broColor, briColor;
+    int pOff;
+    if(pressed)
+    {
+      tloColor = darkShadowColor;
+      tliColor = normalShadowColor;
+      broColor = highlightShadowColor;
+      briColor = lightShadowColor;
+      pOff = 1;
+    }
+    else
+    {
+      tloColor = highlightShadowColor;
+      tliColor = lightShadowColor;
+      broColor = darkShadowColor;
+      briColor = normalShadowColor;
+      pOff = 0;
+    }
+
+    drawBevelRect(gc, leftOff, 2, size.x-1-leftOff-rightOff, size.y-5, tloColor, broColor);
+    drawBevelRect(gc, 1+leftOff, 3, size.x-3-leftOff-rightOff, size.y-7, tliColor, briColor);
+
+    if(isEnabled())
+    {
+      gc.setForeground(widgetForegroundColor);
+      drawImage(gc, size, pOff);
+    }
+    else
+    {
+      gc.setForeground(highlightShadowColor);
+      drawImage(gc, size, 1);
+      gc.setForeground(normalShadowColor);
+      drawImage(gc, size, 0);
+    }
+  }
+  
+  
+  private void drawImage(GC gc, Point size, int pOff)
+  {
+    if((style & DWT.CLOSE) !is 0) drawCloseImage(gc, size, pOff);
+    else if((style & DWT.MAX) !is 0)
+    {
+      if(ishell.getMaximized()) drawRestoreImage(gc, size, pOff);
+      else drawMaximizeImage(gc, size, pOff);
+    }
+    else if((style & DWT.MIN) !is 0) drawMinimizeImage(gc, size, pOff);
+  }
+
+
+  private static void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright)
+  {
+    gc.setForeground(bottomright);
+    gc.drawLine(x + w, y, x + w, y + h);
+    gc.drawLine(x, y + h, x + w, y + h);
+
+    gc.setForeground(topleft);
+    gc.drawLine(x, y, x + w - 1, y);
+    gc.drawLine(x, y, x, y + h - 1);
+  }
+  
+  
+  private void drawCloseImage(GC gc, Point size, int pOff)
+  {
+    gc.drawLine(pOff+leftOff+4, pOff+5, pOff+size.x-leftOff-rightOff-6, pOff+size.y-7);
+    gc.drawLine(pOff+leftOff+5, pOff+5, pOff+size.x-leftOff-rightOff-5, pOff+size.y-7);
+    gc.drawLine(pOff+leftOff+4, pOff+size.y-7, pOff+size.x-leftOff-rightOff-6, pOff+5);
+    gc.drawLine(pOff+leftOff+5, pOff+size.y-7, pOff+size.x-leftOff-rightOff-5, pOff+5);
+  }
+
+
+  private void drawRestoreImage(GC gc, Point size, int pOff)
+  {
+    gc.drawRectangle(pOff+leftOff+3, pOff+7, size.x-leftOff-rightOff-11, size.y-13);
+    gc.drawLine(pOff+leftOff+4, pOff+8, pOff+size.x-leftOff-rightOff-9, pOff+8);
+    gc.drawLine(pOff+leftOff+6, pOff+5, pOff+size.x-leftOff-rightOff-7, pOff+5);
+    gc.drawLine(pOff+leftOff+5, pOff+4, pOff+size.x-leftOff-rightOff-6, pOff+4);
+    gc.drawLine(pOff+size.x-leftOff-rightOff-7, pOff+size.y-9, pOff+size.x-leftOff-rightOff-6, pOff+size.y-9);
+    gc.drawLine(pOff+size.x-leftOff-rightOff-6, pOff+size.y-10, pOff+size.x-leftOff-rightOff-6, pOff+5);
+    gc.drawLine(pOff+leftOff+5, pOff+5, pOff+leftOff+5, pOff+6);
+  }
+
+
+  private void drawMaximizeImage(GC gc, Point size, int pOff)
+  {
+    gc.drawRectangle(pOff+leftOff+3, pOff+4, size.x-leftOff-rightOff-8, size.y-10);
+    gc.drawLine(pOff+leftOff+4, pOff+5, pOff+size.x-leftOff-rightOff-6, pOff+5);
+  }
+
+
+  private void drawMinimizeImage(GC gc, Point size, int pOff)
+  {
+    gc.drawLine(pOff+leftOff+4, pOff+size.y-6, pOff+size.x-leftOff-rightOff-5, pOff+size.y-6);
+    gc.drawLine(pOff+leftOff+4, pOff+size.y-7, pOff+size.x-leftOff-rightOff-5, pOff+size.y-7);
+  }
+}