diff dwtx/novocode/ishell/internal/TitleBar.d @ 190:df4e66472aff

novocode line endings, indention
author Frank Benoit <benoit@tionex.de>
date Sun, 26 Oct 2008 15:04:41 +0100
parents 71ca5bcf2307
children c3583c6ec027
line wrap: on
line diff
--- a/dwtx/novocode/ishell/internal/TitleBar.d	Sun Oct 26 14:57:25 2008 +0100
+++ b/dwtx/novocode/ishell/internal/TitleBar.d	Sun Oct 26 15:04:41 2008 +0100
@@ -1,567 +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;
-
-import dwt.dwthelper.utils;
-
-
-/**
- * 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;
-    }
-  }
-}
+/*******************************************************************************
+ * 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;
+
+import dwt.dwthelper.utils;
+
+
+/**
+ * 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;
+        }
+    }
+}