KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > emf > common > ui > viewer > ExtendedTableTreeViewer


1 /**
2  * <copyright>
3  *
4  * Copyright (c) 2002-2004 IBM Corporation and others.
5  * All rights reserved. This program and the accompanying materials
6  * are made available under the terms of the Eclipse Public License v1.0
7  * which accompanies this distribution, and is available at
8  * http://www.eclipse.org/legal/epl-v10.html
9  *
10  * Contributors:
11  * IBM - Initial API and implementation
12  *
13  * </copyright>
14  *
15  * $Id: ExtendedTableTreeViewer.java,v 1.2 2005/06/08 06:24:33 nickb Exp $
16  */

17 package org.eclipse.emf.common.ui.viewer;
18
19
20 import java.util.Iterator JavaDoc;
21 import java.util.LinkedList JavaDoc;
22
23 import org.eclipse.jface.viewers.TableTreeViewer;
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.custom.TableTree;
26 import org.eclipse.swt.custom.TableTreeItem;
27 import org.eclipse.swt.events.PaintEvent;
28 import org.eclipse.swt.events.PaintListener;
29 import org.eclipse.swt.graphics.GC;
30 import org.eclipse.swt.graphics.Image;
31 import org.eclipse.swt.graphics.Point;
32 import org.eclipse.swt.graphics.Rectangle;
33 import org.eclipse.swt.widgets.Composite;
34 import org.eclipse.swt.widgets.Control;
35 import org.eclipse.swt.widgets.Display;
36 import org.eclipse.swt.widgets.Item;
37 import org.eclipse.swt.widgets.Table;
38 import org.eclipse.swt.widgets.TableItem;
39 import org.eclipse.swt.widgets.Widget;
40
41
42 /**
43  * This class extends a TableTreeViewer to draw images and tree lines in the tree column.
44  */

45 public class ExtendedTableTreeViewer extends TableTreeViewer
46 {
47   public static final String JavaDoc ITEM_ID = "TableTreeItemID";
48
49   public ExtendedTableTreeViewer(TableTree tableTree)
50   {
51     super(tableTree);
52   }
53
54   public ExtendedTableTreeViewer(Composite parent)
55   {
56     super(parent);
57   }
58
59   public ExtendedTableTreeViewer(Composite parent, int style)
60   {
61     super(parent, style);
62   }
63
64   protected Item newItem(Widget parent, int flags, int index)
65   {
66     TableTreeItem item =
67       index >= 0 ?
68         parent instanceof TableTreeItem ?
69           new ExtendedTableTreeItem((TableTreeItem) parent, flags, index) :
70           new ExtendedTableTreeItem((TableTree) parent, flags, index) :
71         parent instanceof TableTreeItem ?
72           new ExtendedTableTreeItem((TableTreeItem) parent, flags) :
73           new ExtendedTableTreeItem((TableTree) parent, flags);
74
75     return item;
76   }
77
78   // We cache the dimensions of the expand/collapse icon as soon as we find
79
// them. They shouldn't ever change, but on GTK, they may be misreported
80
// as 0 when the mouse pointer moves down into or up out of a leaf item.
81
// Also on GTK, they're originally reported as 0, so we need the
82
// default-value hack.
83
// See Bugzilla 42434.
84
//
85
protected Point interactorSize = new Point(12, 12);
86   protected boolean interactorFound = false;
87
88   protected void hookControl(Control control)
89   {
90     super.hookControl(control);
91
92 /*
93     getTableTree().getTable().addPaintListener
94       (new PaintListener()
95        {
96          public void paintControl(PaintEvent event)
97          {
98            if (event.count > 0)
99            {
100              Thread.dumpStack();
101            }
102          }
103        });
104 */

105     getTableTree().getTable().addPaintListener
106       (new PaintListener()
107        {
108          protected boolean isStarted;
109          protected TableTreeItem firstTableTreeItem;
110          protected TableTreeItem lastTableTreeItem;
111          protected LinkedList JavaDoc chain;
112          protected int scrollX;
113
114          /**
115           * On GTK, PaintEvent.gc has its origin below the Table's header,
116           * instead of above it, as on other platforms. This adjusts for
117           * that, mutating and returning the given Rectangle.
118           * See Bugzilla 42416.
119           */

120          protected Rectangle fixForGC(Rectangle bounds)
121          {
122            if (isGTK() && bounds != null)
123            {
124              bounds.x -= 2;
125              bounds.y -= getTableTree().getTable().getHeaderHeight();
126            }
127            return bounds;
128          }
129
130          public void paintControl(PaintEvent event)
131          {
132            // System.out.println("Painting....." + event + " x=" + event.x + " y=" + event.y + " width=" + event.width + " height=" + event.height);
133
// if (true) return;
134

135            Table table = (Table)event.getSource();
136            TableItem[] items = table.getItems();
137
138            firstTableTreeItem = null;
139            lastTableTreeItem = null;
140
141            for (int i = table.getTopIndex(); i < items.length; i++)
142            {
143              TableItem tableItem = items[i];
144              ExtendedTableTreeItem tableTreeItem = (ExtendedTableTreeItem)tableItem.getData(ITEM_ID);
145              if (!tableTreeItem.isDisposed())
146              {
147                if (firstTableTreeItem == null)
148                {
149                  firstTableTreeItem = tableTreeItem;
150                }
151                lastTableTreeItem = tableTreeItem;
152   
153                if (!interactorFound)
154                {
155                  Rectangle bounds = tableItem.getImageBounds(0);
156                  if (bounds.width != 0)
157                  {
158                    interactorFound = true;
159                    interactorSize = new Point(bounds.width, bounds.height);
160                  }
161                }
162
163                Rectangle itemBounds = tableTreeItem.getBounds(0);
164                if (itemBounds != null)
165                {
166                  Image image = tableTreeItem.getFirstImage();
167                  if (image != null)
168                  {
169                    // Paint over the selected padding spaces with the
170
// background colour. On GTK, the whole item, not just
171
// the text, is selected, so we don't do this.
172
//
173
if (!isGTK())
174                    {
175                      // On Motif, selection color may be set as background.
176
//
177
Display display = tableItem.getDisplay();
178                      event.gc.setBackground(
179                        display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
180
181                      Rectangle bounds = tableTreeItem.getImageBounds(tableItem, 0);
182                      bounds.x += bounds.width;
183                      bounds.y = itemBounds.y;
184                      bounds.width = imagePaddingWidth - 1;
185                      bounds.height = itemBounds.height;
186                      event.gc.fillRectangle(fixForGC(bounds));
187                    }
188
189                    // Draw the extra first-column image.
190
//
191
Rectangle sourceBounds = image.getBounds();
192                    Rectangle targetBounds =
193                      fixForGC(tableTreeItem.getFirstImageBounds(tableItem));
194
195                    event.gc.drawImage(image, sourceBounds.x, sourceBounds.y, sourceBounds.width, sourceBounds.height, targetBounds.x, targetBounds.y, targetBounds.width, targetBounds.height);
196                  }
197
198                  // Stop if the next item will be out the event bounds.
199
// The event bounds values are also misaligned on GTK.
200
//
201
fixForGC(itemBounds);
202                  if (itemBounds.y + itemBounds.height > event.y + event.height)
203                  {
204
205                    break;
206                  }
207                }
208              }
209            }
210
211            // If the table is indenting, draw tree lines.
212
//
213
if (firstTableTreeItem != null && isIndenting())
214            {
215              isStarted = false;
216              chain = new LinkedList JavaDoc();
217              event.gc.setForeground(table.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
218              scrollX = items[0].getBounds(0).x;
219              paintLines(event.gc, getTableTree().getItems());
220            }
221          }
222
223          private boolean indenting = false;
224          private boolean indentingTested = false;
225          
226          /**
227           * Tests whether the table is drawing child items indented compared
228           * to their parents. This will return false while the tree is still
229           * totally collapsed.
230           */

231          protected boolean isIndenting()
232          {
233            if (!indentingTested)
234            {
235              TableItem[] items = getTableTree().getTable().getItems();
236              if (items.length > 1)
237              {
238                int x = items[0].getBounds(0).x;
239                for (int i = 1, len = items.length; i < len; i++)
240                {
241                  if (items[i].getBounds(0).x != x)
242                  {
243                    indenting = true;
244                  }
245
246                  TableTreeItem item = (TableTreeItem)items[i].getData(ITEM_ID);
247                  if (item.getParentItem() != null)
248                  {
249                    indentingTested = true;
250                    break;
251                  }
252                }
253              }
254            }
255            return indenting;
256          }
257
258          protected boolean paintLines(GC gc, TableTreeItem [] tableTreeItems)
259          {
260            int offset = interactorSize.x;
261            int indent = Math.min(6, (offset - 8) / 2);
262
263            if (tableTreeItems != null)
264            {
265              for (int i = 0; i < tableTreeItems.length; ++i)
266              {
267                TableTreeItem tableTreeItem = tableTreeItems[i];
268                if (!isStarted && tableTreeItem == firstTableTreeItem)
269                {
270                  isStarted = true;
271                }
272                if (isStarted)
273                {
274                  Rectangle bounds = tableTreeItem.getBounds(0);
275                  int x = 1 + scrollX;
276
277                  for (Iterator JavaDoc j = chain.iterator(); j.hasNext(); )
278                  {
279                    TableTreeItem ancestor = (TableTreeItem)j.next();
280                    if (ancestor != null)
281                    {
282                      gc.drawLine(x + offset/2, bounds.y, x + offset/2, bounds.y + bounds.height);
283                    }
284                    x += offset;
285                  }
286
287                  if (i + 1 == tableTreeItems.length)
288                  {
289                    if (i != 0 || !chain.isEmpty())
290                    {
291                      gc.drawLine
292                        (x + offset/2, bounds.y,
293                         x + offset/2, bounds.y + (tableTreeItem.getItemCount() > 0 ? indent - 1 : bounds.height/2));
294                    }
295                  }
296                  else
297                  {
298                    if (tableTreeItem.getItemCount() > 0)
299                    {
300                      gc.drawLine
301                        (x + offset/2, bounds.y, x + offset/2, bounds.y + indent - 1);
302                      gc.drawLine
303                        (x + offset/2, bounds.y + bounds.height - indent + 2, x + offset/2, bounds.y + bounds.height);
304                    }
305                    else
306                    {
307                      gc.drawLine(x + offset/2, bounds.y, x + offset/2, bounds.y + bounds.height);
308                    }
309                  }
310
311                  gc.drawLine
312                    (x + (tableTreeItem.getItemCount() > 0 ? offset - indent + 1 : offset/2), bounds.y + (bounds.height + 1)/2,
313                     x + offset + 2, bounds.y + (bounds.height + 1)/2);
314
315                }
316                if (tableTreeItem.getExpanded())
317                {
318                  chain.addLast(i + 1 == tableTreeItems.length ? null : tableTreeItem);
319                  if (!paintLines(gc, tableTreeItem.getItems()))
320                  {
321                    return false;
322                  }
323                  chain.removeLast();
324                }
325                if (isStarted && tableTreeItem == lastTableTreeItem)
326                {
327                  return false;
328                }
329              }
330            }
331
332            return true;
333          }
334        });
335   }
336
337   protected String JavaDoc imagePadding;
338   protected int imagePaddingWidth;
339
340   protected void createImagePadding(int width)
341   {
342     GC gc = new GC(getTableTree().getTable());
343     imagePadding = " ";
344     while ((imagePaddingWidth = gc.stringExtent(imagePadding).x) < width + 6)
345     {
346       imagePadding += " ";
347     }
348     gc.dispose();
349
350     TableItem [] tableItems = getTableTree().getTable().getItems();
351     for (int i = 0; i < tableItems.length; ++i)
352     {
353       TableTreeItem tableTreeItem = (TableTreeItem)tableItems[i].getData(ITEM_ID);
354       tableTreeItem.setText(0, tableTreeItem.getText(0));
355     }
356   }
357
358   /**
359    * Returns whether GTK is the current platform. Special treatment is
360    * needed for GTK in drawing on the table.
361    */

362   protected static boolean isGTK()
363   {
364     return "gtk".equals(SWT.getPlatform());
365   }
366
367   /**
368    * This is a convenient way to get image bound values that are corrected
369    * on GTK. If the given TableItem underlies an ExtendedTableTreeItem,
370    * getImageBounds() is called on that ExtendedTableTreeItem. Otherwise,
371    * it is called directory on the TableItem.
372    * See Bugzilla 42434.
373    */

374   public static Rectangle getImageBounds(TableItem tableItem, int column)
375   {
376     Object JavaDoc item = tableItem.getData(ITEM_ID);
377     return item instanceof ExtendedTableTreeItem ?
378       ((ExtendedTableTreeItem)item).getImageBounds(tableItem, column) :
379       tableItem.getImageBounds(column);
380   }
381
382   /**
383    * Centers the Rectangle vertically, within a surrounding space of
384    * the given height. The given Rectangle is changed and returned.
385    */

386   protected static Rectangle center(Rectangle bounds, int maxHeight)
387   {
388     if (bounds.height < maxHeight)
389     {
390       bounds.y += (maxHeight - bounds.height) / 2;
391     }
392     return bounds;
393   }
394
395   /**
396    * Scales the Rectangle, mainting its aspect, such that it fits within the
397    * given height. The given Rectangle is changed and returned.
398    */

399   protected static Rectangle scale(Rectangle bounds, int maxHeight)
400   {
401     if (bounds.height > maxHeight)
402     {
403       float sf = (float)bounds.width / (float)bounds.height;
404       bounds.width = Math.round(sf * maxHeight);
405       bounds.height = maxHeight;
406     }
407     return bounds;
408   }
409
410   public class ExtendedTableTreeItem extends TableTreeItem
411   {
412     protected Image firstImage;
413
414     public ExtendedTableTreeItem(TableTree parent, int style)
415     {
416       super(parent, style);
417     }
418
419     public ExtendedTableTreeItem(TableTree parent, int style, int index)
420     {
421       super(parent, style, index);
422     }
423
424     public ExtendedTableTreeItem(TableTreeItem parent, int style)
425     {
426       super(parent, style);
427     }
428
429     public ExtendedTableTreeItem(TableTreeItem parent, int style, int index)
430     {
431       super(parent, style, index);
432     }
433
434     public void setText(int index, String JavaDoc text)
435     {
436       // System.out.println("setting the text " + index + " " + text + " " + getImage(index));
437
if (index == 0 && imagePadding != null)
438       {
439         if (text != null && text.indexOf(imagePadding) == 0)
440         {
441           super.setText(0, text);
442         }
443         else
444         {
445           super.setText(0, imagePadding + text);
446         }
447       }
448       else
449       {
450         super.setText(index, text);
451       }
452     }
453
454     public String JavaDoc getText(int index)
455     {
456       String JavaDoc result = super.getText(index);
457       if (index == 0 && result != null && imagePadding != null && result.indexOf(imagePadding) == 0)
458       {
459         result = result.substring(imagePadding.length());
460       }
461
462       return result;
463     }
464
465     public void setImage(int index, Image image)
466     {
467       if (index == 0)
468       {
469         firstImage = image;
470         if (image != null && imagePadding == null)
471         {
472           createImagePadding(image.getBounds().width);
473         }
474       }
475       else
476       {
477         super.setImage(index, image);
478       }
479     }
480
481     /**
482      * Returns the additional first image, which would have been set
483      * by setImage(..., 0).
484      */

485     public Image getFirstImage()
486     {
487       return firstImage;
488     }
489
490     public int getImagePaddingWidth()
491     {
492       return imagePaddingWidth;
493     }
494
495     /**
496      * This is equivalent to TableItem.getImageBounds(), except that it
497      * gives corrected values on GTK.
498      * See Bugzilla 42434.
499      */

500     public Rectangle getImageBounds(int column)
501     {
502       return getImageBounds(getTableItem(), column);
503     }
504
505     /**
506      * Because getImageBounds() needs to obtain the underlying TableItem,
507      * this form is provided for speedy internal use when we've already
508      * got a handle on it.
509      */

510     private Rectangle getImageBounds(TableItem tableItem, int column)
511     {
512       if (isGTK())
513       {
514         Rectangle result = tableItem.getBounds(column);
515         int itemHeight = result.height;
516         
517         if (column == 0)
518         {
519           result.width = interactorSize.x;
520           result.height = interactorSize.y;
521         }
522         else
523         {
524           Image image = tableItem.getImage(column);
525           if (image == null)
526           {
527             result.width = 0;
528             result.height = 0;
529           }
530           else
531           {
532             Rectangle imageBounds = image.getBounds();
533             result.width = imageBounds.width;
534             result.height = imageBounds.height;
535           }
536         }
537         center(result, itemHeight);
538         result.x += 3;
539         
540         return result;
541       }
542       
543       return tableItem.getImageBounds(column);
544     }
545
546     /**
547      * Returns the bounds of the additional first image, which would have
548      * been set by setImage(..., 0).
549      */

550     public Rectangle getFirstImageBounds()
551     {
552       return getFirstImageBounds(getTableItem());
553     }
554
555     /**
556      * Because getFirstImageBounds() needs to obtain the underlying
557      * TableItem, this form is provided for speedy internal use when we've
558      * already got a handle on it.
559      */

560     private Rectangle getFirstImageBounds(TableItem tableItem)
561     {
562       Rectangle result = new Rectangle(0, 0, 0, 0);
563       
564       if (tableItem != null)
565       {
566         Rectangle itemBounds = tableItem.getBounds(0);
567         Rectangle interactorBounds = getImageBounds(tableItem, 0);
568
569         result.x = interactorBounds.x + interactorBounds.width + 5;
570         result.y = itemBounds.y;
571
572         if (firstImage != null)
573         {
574           Rectangle imageBounds = firstImage.getBounds();
575           result.width = imageBounds.width;
576           result.height = imageBounds.height;
577         }
578
579         scale(result, itemBounds.height);
580         center(result, itemBounds.height);
581       }
582
583       return result;
584     }
585
586     /**
587      * Returns the underlying TableItem.
588      */

589     protected TableItem getTableItem()
590     {
591       TableItem[] items = getTableTree().getTable().getItems();
592       for (int i = 0; i < items.length; i++)
593       {
594         if (items[i].getData(ITEM_ID) == this) return items[i];
595       }
596       return null;
597     }
598   }
599 }
600
Popular Tags