comparison 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
comparison
equal deleted inserted replaced
187:293a2f22f944 188:e3780acbbf80
1 /*******************************************************************************
2 * Copyright (c) 2005 Stefan Zeiger and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.novocode.com/legal/epl-v10.html
7 *
8 * Contributors:
9 * Stefan Zeiger (szeiger@novocode.com) - initial API and implementation
10 *******************************************************************************/
11
12 module dwtx.novocode.ishell.internal.TitleBar;
13
14 import dwt.dwthelper.utils;
15 import dwt.dwthelper.Runnable;
16 import dwtx.dwtxhelper.Timer;
17 import dwtx.dwtxhelper.TimerTask;
18
19 import dwt.DWT;
20 import dwt.graphics.Color;
21 import dwt.graphics.Font;
22 import dwt.graphics.FontData;
23 import dwt.graphics.GC;
24 import dwt.graphics.Image;
25 import dwt.graphics.ImageData;
26 import dwt.graphics.PaletteData;
27 import dwt.graphics.Point;
28 import dwt.graphics.Rectangle;
29 import dwt.graphics.RGB;
30 import dwt.widgets.Canvas;
31 import dwt.widgets.Display;
32 import dwt.widgets.Event;
33 import dwt.widgets.Listener;
34 import dwt.widgets.Menu;
35 import dwt.widgets.MenuItem;
36 import dwt.widgets.Shell;
37
38 import dwtx.novocode.ishell.DesktopForm;
39 import dwtx.novocode.ishell.InternalShell;
40
41 alias char[] String;
42
43
44 /**
45 * A title bar for an InternalShell.
46 *
47 * @author Stefan Zeiger (szeiger@novocode.com)
48 * @since Jan 21, 2005
49 * @version $Id: TitleBar.java 342 2005-07-09 20:37:13 +0000 (Sat, 09 Jul 2005) szeiger $
50 */
51
52 class TitleBar : Canvas
53 {
54 private static const long UPDATE_DELAY = 25;
55 private static const int MINIMUM_GRAB_AREA = 2;
56 private static const String ELLIPSIS = "...";
57 private static const int LEFT_PADDING = 2;
58 private static const int RIGHT_PADDING = 2;
59 private static const int IMAGE_SIZE = 16;
60 private static const int TOOL_SIZE = 14;
61 private static const int TOP_PADDING = 1;
62 private static const int BOTTOM_PADDING = 1;
63
64 private int mouseDownOffsetX, mouseDownOffsetY, snapBackX, snapBackY;
65 private bool cancelled;
66 private /**volatile*/ long lastUpdate;
67 private Timer timer;
68 private TimerTask timerTask;
69 private InternalShell ishell;
70 private DesktopForm desktop;
71 private String text;
72 private Image image;
73 private bool styleClose, styleMax, styleTool, styleMin;
74 private Image closeImage, restoreImage, maximizeImage, minimizeImage;
75 private MenuItem restoreItem, closeItem, maximizeItem;
76 private Menu defaultPopup;
77 private Point minGrabSize;
78 private Shell shell;
79 private Color gradStartColor, gradEndColor, textColor, inactiveGradStartColor, inactiveGradEndColor, inactiveTextColor;
80 private Listener activateListener, deactivateListener;
81
82
83 this(InternalShell parent, int style)
84 {
85 super(parent, checkStyle(style));
86 this.timer = new Timer(true);
87 this.minGrabSize = new Point(MINIMUM_GRAB_AREA, MINIMUM_GRAB_AREA);
88 this.ishell = parent;
89 this.desktop = cast(DesktopForm)ishell.getParent();
90 this.styleClose = (style & DWT.CLOSE) !is 0;
91 this.styleMax = (style & DWT.MAX) !is 0;
92 this.styleMin = (style & DWT.MIN) !is 0;
93 this.styleTool = (style & DWT.TOOL) !is 0;
94
95 Display display = getDisplay();
96 shell = getShell();
97
98 gradStartColor = display.getSystemColor(DWT.COLOR_TITLE_BACKGROUND);
99 gradEndColor = display.getSystemColor(DWT.COLOR_TITLE_BACKGROUND_GRADIENT);
100 textColor = display.getSystemColor(DWT.COLOR_TITLE_FOREGROUND);
101 inactiveGradStartColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND);
102 inactiveGradEndColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
103 inactiveTextColor = display.getSystemColor(DWT.COLOR_TITLE_INACTIVE_FOREGROUND);
104
105 GC gc = new GC(this);
106 int imgHeight = gc.getFontMetrics().getHeight()-1;
107 if(imgHeight%2 is 0) imgHeight--;
108 gc.dispose();
109
110 closeImage = createMenuImage(IMAGE_TYPE_CLOSE, imgHeight);
111 restoreImage = createMenuImage(IMAGE_TYPE_RESTORE, imgHeight);
112 maximizeImage = createMenuImage(IMAGE_TYPE_MAXIMIZE, imgHeight);
113 minimizeImage = createMenuImage(IMAGE_TYPE_MINIMIZE, imgHeight);
114
115 setFont(createTitleFont(getFont(), styleTool));
116
117 defaultPopup = new Menu(this);
118
119 restoreItem = new MenuItem(defaultPopup, DWT.PUSH);
120 restoreItem.setText("&Restore");
121 restoreItem.setImage(restoreImage);
122 restoreItem.addListener(DWT.Selection, dgListener(&restoreListener));
123
124 MenuItem minimizeItem = new MenuItem(defaultPopup, DWT.PUSH);
125 minimizeItem.setText("Mi&nimize");
126 minimizeItem.setEnabled(styleMin);
127 minimizeItem.setImage(minimizeImage);
128 minimizeItem.addListener(DWT.Selection, dgListener(&minimizeListener));
129
130 maximizeItem = new MenuItem(defaultPopup, DWT.PUSH);
131 maximizeItem.setText("Ma&ximize");
132 maximizeItem.setImage(maximizeImage);
133 maximizeItem.addListener(DWT.Selection, dgListener(&maximizeListener));
134
135 new MenuItem(defaultPopup, DWT.SEPARATOR);
136
137 closeItem = new MenuItem(defaultPopup, DWT.PUSH);
138 closeItem.setText("&Close");
139 closeItem.setEnabled(styleClose);
140 closeItem.setImage(closeImage);
141 closeItem.addListener(DWT.Selection, dgListener(&closeListener));
142
143 addListener(DWT.Paint, dgListener(&onPaint));
144 addListener(DWT.MouseDown, dgListener(&onMouseDown));
145 addListener(DWT.MenuDetect, dgListener(&onMenuDetect));
146 addListener(DWT.MouseDoubleClick, dgListener(&onMouseDoubleClick));
147 addListener(DWT.MouseMove, dgListener(&onMouseMove));
148 addListener(DWT.MouseUp, dgListener(&onMouseUp));
149
150 activateListener = dgListener(&onActivateListener);
151 deactivateListener = dgListener(&onDeactivateListener);
152 shell.addListener(DWT.Activate, activateListener);
153 shell.addListener(DWT.Deactivate, deactivateListener);
154
155 addListener(DWT.Dispose, dgListener(&onDispose));
156 }
157
158
159 private void restoreListener(Event event)
160 {
161 ishell.setMaximized(false);
162 }
163
164
165 private void minimizeListener(Event event)
166 {
167 ishell.setMinimized(true);
168 }
169
170
171 private void maximizeListener(Event event)
172 {
173 ishell.setMaximized(true);
174 }
175
176
177 private void closeListener(Event event)
178 {
179 ishell.close();
180 }
181
182
183 private void onPaint(Event event)
184 {
185 Rectangle r = getClientArea();
186 if(r.width is 0 || r.height is 0) return;
187
188 bool active = (shell is display.getActiveShell() && ishell.isActiveShell());
189
190 GC gc = event.gc;
191 gc.setForeground(active ? gradStartColor : inactiveGradStartColor);
192 gc.setBackground(active ? gradEndColor : inactiveGradEndColor);
193 gc.fillGradientRectangle(r.x, r.y, r.width, r.height, false);
194
195 int textLeftPadding = LEFT_PADDING;
196 if(image !is null)
197 {
198 Rectangle imageBounds = image.getBounds();
199 if(imageBounds.width > IMAGE_SIZE || imageBounds.height > IMAGE_SIZE)
200 gc.drawImage(image, 0, 0, imageBounds.width, imageBounds.height, LEFT_PADDING, TOP_PADDING, IMAGE_SIZE, IMAGE_SIZE);
201 else
202 gc.drawImage(image, LEFT_PADDING + (IMAGE_SIZE-imageBounds.width)/2, (r.height-imageBounds.height)/2);
203 textLeftPadding += IMAGE_SIZE + LEFT_PADDING;
204 }
205
206 if(text !is null && text.length() > 0)
207 {
208 gc.setForeground(active ? textColor : inactiveTextColor);
209 String s = text;
210 int availableWidth = r.width - textLeftPadding - RIGHT_PADDING;
211 if(gc.textExtent(s, DWT.DRAW_TRANSPARENT).x > availableWidth)
212 {
213 int ellipsisWidth = gc.textExtent(ELLIPSIS, DWT.DRAW_TRANSPARENT).x;
214 while(s.length() > 0)
215 {
216 s = s.substring(0, s.length()-1);
217 if(gc.textExtent(s, DWT.DRAW_TRANSPARENT).x + ellipsisWidth <= availableWidth)
218 {
219 s ~= ELLIPSIS;
220 break;
221 }
222 }
223 setToolTipText(text);
224 }
225 else setToolTipText(null);
226 if(s.length() > 0) gc.drawString(s, textLeftPadding, (r.height-gc.getFontMetrics().getHeight())/2, true);
227 }
228 else setToolTipText(null);
229 }
230
231
232 private void onMouseDown(Event event)
233 {
234 if(event.button is 1)
235 {
236 if(image !is null && event.x < LEFT_PADDING + IMAGE_SIZE)
237 {
238 cancelled = true;
239 // left-clicking on the image always shows the default popup menu
240 instrumentDefaultPopup(true);
241 defaultPopup.setLocation(toDisplay(0, getSize().y));
242 defaultPopup.setVisible(true);
243 }
244 else
245 {
246 mouseDownOffsetX = event.x;
247 mouseDownOffsetY = event.y;
248 Point p = ishell.getLocation();
249 snapBackX = p.x;
250 snapBackY = p.y;
251 cancelled = false;
252 }
253 }
254 else if(event.button is 3)
255 {
256 if((event.stateMask & DWT.BUTTON1) !is 0 && snapBackX !is Integer.MIN_VALUE && snapBackY !is Integer.MIN_VALUE)
257 {
258 ishell.setLocation(snapBackX, snapBackY);
259 snapBackX = Integer.MIN_VALUE;
260 snapBackY = Integer.MIN_VALUE;
261 cancelled = true;
262 }
263 else
264 {
265 }
266 }
267 }
268
269
270 private void onMenuDetect(Event event)
271 {
272 event.doit = false;
273 Menu m = getMenu();
274 if(m is null || m.isDisposed())
275 {
276 m = defaultPopup;
277 instrumentDefaultPopup(false);
278 }
279 m.setLocation(event.x, event.y);
280 m.setVisible(true);
281 }
282
283
284 private void onMouseDoubleClick(Event event)
285 {
286 if(event.button is 1)
287 {
288 if(image !is null && event.x < LEFT_PADDING + IMAGE_SIZE)
289 {
290 if(styleClose) ishell.close();
291 }
292 else
293 {
294 if(styleMax) ishell.setMaximized(!ishell.getMaximized());
295 }
296 cancelled = true;
297 }
298 }
299
300
301 private void onMouseMove(Event event)
302 {
303 if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0 && !ishell.getMaximized())
304 {
305 if(timerTask !is null)
306 {
307 timerTask.cancel();
308 timerTask = null;
309 }
310 long now = System.currentTimeMillis();
311 if(lastUpdate + UPDATE_DELAY < now)
312 {
313 performMove(event);
314 lastUpdate = now;
315 }
316 else
317 {
318 timerTask = new class() TimerTask
319 {
320 public void run()
321 {
322 TimerTask executingTask = this;
323 event.display.asyncExec(new class() Runnable
324 {
325 public void run()
326 {
327 if(executingTask !is timerTask) return;
328 performMove(event);
329 }
330 });
331 }
332 };
333 timer.schedule(timerTask, UPDATE_DELAY);
334 }
335 }
336 }
337
338
339 private void onMouseUp(Event event)
340 {
341 if(ishell.getMaximized()) return;
342 if(image is null || event.x >= LEFT_PADDING + IMAGE_SIZE)
343 {
344 if(timerTask !is null)
345 {
346 timerTask.cancel();
347 timerTask = null;
348 }
349 if(!cancelled && (event.stateMask & DWT.BUTTON1) !is 0)
350 {
351 performMove(event);
352 }
353 }
354 }
355
356
357 private void onActivateListener(Event event)
358 {
359 redraw();
360 }
361
362
363 private void onDeactivateListener(Event event)
364 {
365 redraw();
366 }
367
368
369 private void onDispose(Event event)
370 {
371 timer.cancel();
372 shell.removeListener(DWT.Activate, activateListener);
373 shell.removeListener(DWT.Deactivate, deactivateListener);
374 closeImage.dispose();
375 maximizeImage.dispose();
376 restoreImage.dispose();
377 minimizeImage.dispose();
378 defaultPopup.dispose();
379 }
380
381
382 private void performMove(Event event)
383 {
384 Point p = ishell.getLocation();
385 int newX = p.x + event.x - mouseDownOffsetX;
386 int newY = p.y + event.y - mouseDownOffsetY;
387
388 // Make sure that the minimum grab area stays visible
389 Rectangle deskCA = desktop.getClientArea();
390 Rectangle bounds = getBounds();
391 newX = Math.min(Math.max(newX, deskCA.x-bounds.x-bounds.width+MINIMUM_GRAB_AREA), deskCA.x-bounds.x+deskCA.width-minGrabSize.x);
392 newY = Math.min(Math.max(newY, deskCA.y-bounds.y-bounds.height+MINIMUM_GRAB_AREA), deskCA.y-bounds.y+deskCA.height-MINIMUM_GRAB_AREA);
393
394 if(newX !is p.x || newY !is p.y) ishell.setLocation(newX, newY);
395 }
396
397
398 public Point getMinGrabSize()
399 {
400 return minGrabSize;
401 }
402
403
404 public Point computeSize(int wHint, int hHint, bool changed)
405 {
406 checkWidget();
407 if(wHint is DWT.DEFAULT) wHint = 50;
408 if(hHint is DWT.DEFAULT)
409 {
410 GC gc = new GC(this);
411 hHint = gc.getFontMetrics().getHeight();
412 hHint = Math.max(hHint, styleTool ? TOOL_SIZE : IMAGE_SIZE);
413 hHint += TOP_PADDING + BOTTOM_PADDING;
414 gc.dispose();
415 }
416 return new Point(wHint, hHint);
417 }
418
419
420 private static int checkStyle(int style)
421 {
422 //int mask = DWT.SHADOW_IN | DWT.FLAT;
423 //style &= mask;
424 style = DWT.NO_FOCUS;
425 return style;
426 }
427
428
429 public bool setFocus()
430 {
431 checkWidget();
432 return false;
433 }
434
435
436 public bool isReparentable ()
437 {
438 checkWidget();
439 return false;
440 }
441
442
443 public void setText(String text)
444 {
445 checkWidget();
446 this.text = text;
447 redraw();
448 }
449
450
451 public String getText() { return text; }
452
453
454 public void setImage(Image image)
455 {
456 checkWidget();
457 if(styleTool) return;
458 this.image = image;
459 minGrabSize.x = MINIMUM_GRAB_AREA;
460 if(image !is null) minGrabSize.x += LEFT_PADDING + IMAGE_SIZE;
461 redraw();
462 }
463
464
465 public Image getImage() { return image; }
466
467
468 private Font createTitleFont(Font f, bool tool)
469 {
470 FontData[] fds = f.getFontData();
471 foreach(fd; fds)
472 {
473 fd.setStyle(fd.getStyle() | DWT.BOLD);
474 if(tool) fd.setHeight(cast(int)(fd.getHeight()*0.9));
475 }
476 return new Font(getDisplay(), fds);
477 }
478
479
480 private void instrumentDefaultPopup(bool onImage)
481 {
482 restoreItem.setEnabled(styleMax && ishell.getMaximized());
483 maximizeItem.setEnabled(styleMax && !ishell.getMaximized());
484 MenuItem def = null;
485 if(onImage)
486 {
487 if(styleClose) def = closeItem;
488 }
489 else if(styleMax)
490 {
491 def = ishell.getMaximized() ? restoreItem : maximizeItem;
492 }
493 defaultPopup.setDefaultItem(def);
494 }
495
496
497 private static const int IMAGE_TYPE_CLOSE = 1;
498 private static const int IMAGE_TYPE_MAXIMIZE = 2;
499 private static const int IMAGE_TYPE_RESTORE = 3;
500 private static const int IMAGE_TYPE_MINIMIZE = 4;
501
502
503 private Image createMenuImage(int type, int height)
504 {
505 final Point size = new Point(height, height);
506 final int imgWidth = height + height/2;
507 final Color fg = getForeground();
508 final Color white = getDisplay().getSystemColor(DWT.COLOR_WHITE);
509 final RGB blackRGB = new RGB(0,0,0);
510
511 ImageData id = new ImageData(imgWidth, size.y, 1, new PaletteData([ blackRGB, fg.getRGB() ]));
512 ImageData maskid = new ImageData(imgWidth, size.y, 1, new PaletteData([ blackRGB, white.getRGB() ]));
513
514 Image img = new Image(getDisplay(), id);
515 GC gc = new GC(img);
516 gc.setForeground(fg);
517 drawMenuImage(gc, size, type);
518 gc.dispose();
519
520 Image maskimg = new Image(getDisplay(), maskid);
521 gc = new GC(maskimg);
522 gc.setForeground(white);
523 drawMenuImage(gc, size, type);
524 gc.dispose();
525
526 Image transp = new Image(getDisplay(), img.getImageData(), maskimg.getImageData());
527 img.dispose();
528 maskimg.dispose();
529 return transp;
530 }
531
532
533 private void drawMenuImage(GC gc, Point size, int type)
534 {
535 switch(type)
536 {
537 case IMAGE_TYPE_CLOSE:
538 gc.drawLine(1, 1, size.x-2, size.y-2);
539 gc.drawLine(2, 1, size.x-2, size.y-3);
540 gc.drawLine(1, 2, size.x-3, size.y-2);
541 gc.drawLine(1, size.y-2, size.x-2, 1);
542 gc.drawLine(1, size.y-3, size.x-3, 1);
543 gc.drawLine(2, size.y-2, size.x-2, 2);
544 break;
545
546 case IMAGE_TYPE_RESTORE:
547 gc.drawRectangle(0, 4, size.x-4, size.y-6);
548 gc.drawLine(1, 5, size.x-5, 5);
549 gc.drawLine(2, 1, size.x-2, 1);
550 gc.drawLine(2, 2, size.x-2, 2);
551 gc.drawPoint(2, 3);
552 gc.drawLine(size.x-2, 3, size.x-2, size.y-5);
553 gc.drawPoint(size.x-3, size.y-5);
554 break;
555
556 case IMAGE_TYPE_MAXIMIZE:
557 gc.drawRectangle(0, 0, size.x-2, size.y-2);
558 gc.drawLine(1, 1, size.x-3, 1);
559 break;
560
561 case IMAGE_TYPE_MINIMIZE:
562 gc.drawLine(1, size.y-2, size.x-4, size.y-2);
563 gc.drawLine(1, size.y-3, size.x-4, size.y-3);
564 break;
565 }
566 }
567 }