diff dwtx/novocode/ishell/internal/TitleBar.d @ 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
children 71ca5bcf2307
line wrap: on
line diff
--- /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;
+    }
+  }
+}