001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.sql.framework.ui.graph.impl;
043:
044: import java.awt.Color;
045: import java.awt.Graphics2D;
046: import java.awt.Insets;
047: import java.awt.Point;
048: import java.awt.Rectangle;
049: import java.util.ArrayList;
050:
051: import javax.swing.DefaultListModel;
052: import javax.swing.ListModel;
053: import javax.swing.event.ListDataEvent;
054: import javax.swing.event.ListDataListener;
055:
056: import org.netbeans.modules.sql.framework.ui.graph.ListAreaCellRenderer;
057:
058: import com.nwoods.jgo.JGoBrush;
059: import com.nwoods.jgo.JGoDrawable;
060: import com.nwoods.jgo.JGoObject;
061: import com.nwoods.jgo.JGoPen;
062: import com.nwoods.jgo.JGoRectangle;
063: import com.nwoods.jgo.JGoScrollBar;
064: import com.nwoods.jgo.JGoView;
065:
066: /**
067: * @author Ritesh Adval
068: * @version $Revision$
069: */
070: public class ListArea extends CanvasArea {
071:
072: private static final JGoPen DEFAULT_PEN = JGoPen
073: .makeStockPen(Color.lightGray);
074:
075: private ListModel dataModel;
076:
077: private ListAreaCellRenderer cellRenderer;
078:
079: private ListDataAdapter listDataAdapter;
080:
081: private ArrayList cells;
082:
083: private JGoRectangle columnRect;
084:
085: private int vSpacing = 1;
086:
087: private JGoPen linePen = null;
088:
089: //constants for first , last visible row
090: //by default set to 0 so that first row is visible
091: private int firstVisibleRow = 0;
092: private int lastVisibleRow = -1;
093: //vertical scrollbar
094: private JGoScrollBar vScrollBar;
095: private int vScrollBarGap = 0;
096:
097: //this is the size of the scrollbar
098: private static int scrollBarSize = 14;
099: private int prefferedWidth = -1;
100:
101: private ArrayList areaList = new ArrayList();
102:
103: private boolean drawBoundingRect = true;
104:
105: private boolean drawLines = true;
106:
107: private boolean showScrollBar = true;
108:
109: /** Creates a new instance of BasicListArea */
110: public ListArea() {
111:
112: listDataAdapter = new ListDataAdapter();
113: if (drawBoundingRect) {
114: columnRect = new JGoRectangle();
115: columnRect.setSelectable(false);
116: columnRect.setResizable(false);
117: columnRect.setPen(JGoPen.lightGray);
118: columnRect.setBrush(JGoBrush.makeStockBrush(Color.WHITE));
119: addObjectAtTail(columnRect);
120: }
121: //add scrollbar
122: vScrollBar = new JGoScrollBar();
123: vScrollBar.setVertical(true);
124: vScrollBar.setSelectable(false);
125: addObjectAtTail(vScrollBar);
126:
127: this .setSelectable(false);
128: this .setResizable(false);
129: this .setDraggable(true);
130:
131: //set the insets around column
132: //this.insets = new Insets(1, 5, 0, 10);
133:
134: // have a default cell renderer
135: cellRenderer = new DefaultListAreaRenderer();
136:
137: }
138:
139: /**
140: * create an instance of list area
141: *
142: * @param listData list data values
143: */
144: public ListArea(Object[] listData) {
145: this ();
146:
147: DefaultListModel model = new DefaultListModel();
148:
149: for (int i = 0; i < listData.length; i++) {
150: model.addElement(listData[i]);
151: }
152: setModel(model);
153:
154: }
155:
156: /**
157: * set the list model
158: *
159: * @param model list model
160: */
161: public void setModel(ListModel model) {
162: ListModel oldModel = dataModel;
163: this .dataModel = model;
164: if (oldModel != null) {
165: oldModel.removeListDataListener(listDataAdapter);
166: }
167: dataModel.addListDataListener(listDataAdapter);
168:
169: //by setting the model we need to also set the renderer for objects
170: //in the model
171: for (int i = 0; i < dataModel.getSize(); i++) {
172: Object val = dataModel.getElementAt(i);
173: ListAreaCellRenderer listCellRenderer = getCellRenderer(i);
174: JGoObject cell = listCellRenderer.getListAreaCellRenderer(
175: this , val, i, false, false);
176:
177: areaList.add(cell);
178: this .addObjectAtTail(cell);
179: }
180:
181: }
182:
183: /**
184: * get the list model
185: *
186: * @return list model
187: */
188: public ListModel getModel() {
189: return dataModel;
190: }
191:
192: /**
193: * set the default cell renderer
194: *
195: * @param cellRenderer cell renderer
196: */
197: public void setCellRenderer(ListAreaCellRenderer cellRenderer) {
198: this .cellRenderer = cellRenderer;
199: }
200:
201: /**
202: * get the default cell renderer
203: *
204: * @return cell renderer
205: */
206: public ListAreaCellRenderer getCellRenderer() {
207: return cellRenderer;
208: }
209:
210: /**
211: * get cell renderer at an index
212: *
213: * @param row row index
214: * @return cell renderer
215: */
216: protected ListAreaCellRenderer getCellRenderer(int row) {
217: return getCellRenderer();
218: }
219:
220: /**
221: * get the cell renderer component at an index
222: *
223: * @param row row index
224: * @return cell renderer
225: */
226: public JGoObject getCellRendererComponent(int row) {
227: if ((row >= areaList.size()) || row < 0) {
228: return null;
229: }
230: return (JGoObject) areaList.get(row);
231: }
232:
233: /**
234: * set whether to draw lines after each cell in this list
235: *
236: * @param drawLines whether to draw lines
237: */
238: public void setDrawLines(boolean drawLines) {
239: this .drawLines = drawLines;
240: }
241:
242: /**
243: * get whether list draws line after each cell
244: *
245: * @return drawlines
246: */
247: public boolean isDrawLines() {
248: return this .drawLines;
249: }
250:
251: /**
252: * get value at a particular point
253: *
254: * @param loc location
255: * @return cell value
256: */
257: public Object getValueAt(Point loc) {
258: ListModel model = getModel();
259:
260: if (model == null) {
261: return null;
262: }
263:
264: for (int i = 0; i < model.getSize(); i++) {
265: Object val = model.getElementAt(i);
266: JGoObject renderer = getCellRendererComponent(i);
267: if (renderer != null
268: && renderer.getBoundingRect().contains(loc)) {
269: return val;
270: }
271: }
272:
273: return null;
274: }
275:
276: /**
277: * get the vertical scrollbar
278: *
279: * @return vertical scroll bar
280: */
281: public JGoScrollBar getVerticalScrollBar() {
282: return vScrollBar;
283: }
284:
285: /**
286: * set the gap of vertical scrollbar from the edge of this area
287: *
288: * @param gap gap
289: */
290: public void setVerticalScrollBarGapFromEdge(int gap) {
291: vScrollBarGap = gap;
292: }
293:
294: public void setShowScrollBar(boolean showScrollBar) {
295: this .showScrollBar = showScrollBar;
296: }
297:
298: public boolean isShowScrollBar() {
299: return this .showScrollBar;
300: }
301:
302: /**
303: * since setVisible() doesn't automatically call setVisible() on all the children, we
304: * need to do this manually to handle the scroll bar
305: *
306: * @param bFlag boolean
307: */
308: public void setVisible(boolean bFlag) {
309: super .setVisible(bFlag);
310: if (getVerticalScrollBar() != null && isShowScrollBar()) {
311: getVerticalScrollBar().setVisible(bFlag);
312: if (bFlag) {
313: this .addObjectAtTail(getVerticalScrollBar());
314: } else {
315: this .removeObject(getVerticalScrollBar());
316: }
317: }
318: }
319:
320: /**
321: * updates the vertical scroll bar
322: */
323: public void updateVerticalScrollBar() {
324: JGoScrollBar bar = getVerticalScrollBar();
325: if (bar != null) {
326:
327: if (!this .isShowScrollBar()) {
328: bar.setVisible(false);
329: return;
330: }
331:
332: if (getFirstVisibleRow() == 0
333: && getLastVisibleRow() == getRowCount() - 1) {
334: bar.setVisible(false);
335: scrollBarSize = 0;
336: } else {
337: bar.setVisible(true);
338: bar.setValues(getFirstVisibleRow(), getLastVisibleRow()
339: - getFirstVisibleRow() + 1, 0, getRowCount(),
340: 1, Math.max(getLastVisibleRow()
341: - getFirstVisibleRow(), 1));
342:
343: scrollBarSize = 14;
344: //set the height so that only visible rows can be dispalced
345: //this avoids extra space at the end of scroll bar
346: //any parent should listen to change in child geometry
347: if (this .getParent() instanceof BasicListArea) {
348: ((BasicListArea) this .getParent())
349: .adjustHeight(this );
350: }
351: }
352: }
353: }
354:
355: /**
356: * this gets the notification from the JGoScrollBar when the scroll bar value,
357: * representing the first visible index, has changed
358: *
359: * @param hint event hint
360: * @param prevInt previous integer value
361: * @param prevVal previous object value
362: */
363: public void update(int hint, int prevInt, Object prevVal) {
364: if (hint == JGoScrollBar.ChangedScrollBarValue) {
365: if (getVerticalScrollBar() != null) {
366: // optimization: assume area doesn't change when scrolling items
367: setFirstVisibleRow(getVerticalScrollBar().getValue());
368: }
369: } else {
370: super .update(hint, prevInt, prevVal);
371: }
372: }
373:
374: /**
375: * get the graph rectangle
376: *
377: * @return graph rectangle
378: */
379: public JGoObject getRect() {
380: return columnRect;
381: }
382:
383: /**
384: * get the number of rows in this column area
385: *
386: * @return number of rows in this column
387: */
388: public int getRowCount() {
389: if (dataModel == null) {
390: return -1;
391: }
392: return dataModel.getSize();
393: }
394:
395: /**
396: * add a new cell in the column area int val value to represent in a cell
397: *
398: * @param row row
399: * @param val value
400: */
401: public void addItem(int row, Object val) {
402: }
403:
404: /**
405: * get the cell at an index
406: *
407: * @param row row
408: * @return cell area
409: */
410: public CellArea getCellAt(int row) {
411: if (row <= cells.size() - 1) {
412: return (CellArea) cells.get(row);
413: }
414:
415: return null;
416: }
417:
418: /**
419: * set the preffered width
420: *
421: * @param width preffered width
422: */
423: public void setPrefferedWidth(int width) {
424: this .prefferedWidth = width;
425: }
426:
427: /**
428: * get the preffered width
429: *
430: * @return preffered width
431: */
432: public int getPrefferedWidth() {
433: return prefferedWidth;
434: }
435:
436: /**
437: * get maximum width of this area
438: *
439: * @return max width
440: */
441: public int getMaximumWidth() {
442: int maxWidth = getInsets().left + getInsets().right;
443:
444: int w = 0;
445:
446: for (int i = 0; i < getModel().getSize(); i++) {
447: JGoObject renderer = getCellRendererComponent(i);
448: if (renderer != null) {
449: int rendererWidth = renderer.getWidth();
450: if (w < rendererWidth) {
451: w = rendererWidth;
452: }
453: }
454: }
455:
456: maxWidth += w;
457:
458: return maxWidth;
459: }
460:
461: /**
462: * get the maximum height of this area
463: *
464: * @return max height
465: */
466: public int getMaximumHeight() {
467: int maxHeight = getInsets().top + getInsets().bottom;
468:
469: for (int i = 0; i < getModel().getSize(); i++) {
470: JGoObject renderer = getCellRendererComponent(i);
471: if (renderer != null) {
472: int rendererHeight = renderer.getHeight();
473: maxHeight += rendererHeight;
474: maxHeight += vSpacing;
475: }
476: }
477: //remove one extra vspace
478: maxHeight -= vSpacing;
479:
480: return maxHeight;
481: }
482:
483: /**
484: * get first visible row index
485: *
486: * @return first visible row index
487: */
488: public int getFirstVisibleRow() {
489: return firstVisibleRow;
490: }
491:
492: /**
493: * set the first visible row
494: *
495: * @param rowIdx index of first visible row
496: */
497: public void setFirstVisibleRow(int rowIdx) {
498: int oldIndex = firstVisibleRow;
499:
500: if (rowIdx >= 0 && rowIdx <= getRowCount()
501: && oldIndex != rowIdx) {
502: firstVisibleRow = rowIdx;
503: }
504: }
505:
506: /**
507: * get the height of all visible rows in this column
508: *
509: * @return height of visible rows
510: */
511: public int getVisibleRowHeights() {
512: int rowHeights = 0;
513: for (int i = this .getFirstVisibleRow(); i <= this
514: .getLastVisibleRow(); i++) {
515: JGoObject renderer = getCellRendererComponent(i);
516: if (renderer != null) {
517: rowHeights += renderer.getHeight()
518: + getVerticalSpacing();
519: }
520: }
521:
522: rowHeights += insets.top + insets.bottom - getVerticalSpacing();
523:
524: return rowHeights;
525: }
526:
527: /**
528: * get the last visible row of this list area
529: *
530: * @return list visible row
531: */
532: public int getLastVisibleRow() {
533: return lastVisibleRow;
534: }
535:
536: /**
537: * set the last visible row
538: *
539: * @param rowIdx index of first visible row
540: */
541: public void setLastVisibleRow(int rowIdx) {
542: int oldIndex = lastVisibleRow;
543:
544: if (rowIdx >= 0 && rowIdx <= getRowCount()
545: && oldIndex != rowIdx) {
546: lastVisibleRow = rowIdx;
547: }
548: }
549:
550: /**
551: * get the vertical spacing
552: *
553: * @return vertical spacing
554: */
555: public int getVerticalSpacing() {
556: return vSpacing;
557: }
558:
559: /**
560: * set the vertical spacing between cells of this list area
561: *
562: * @param newspace new vertical space
563: */
564: public void setVerticalSpacing(int newspace) {
565: int oldSpacing = vSpacing;
566: if (oldSpacing != newspace) {
567: vSpacing = newspace;
568: }
569: }
570:
571: /**
572: * get the line pen
573: *
574: * @return line pen
575: */
576: public JGoPen getLinePen() {
577: return (linePen != null) ? linePen : DEFAULT_PEN;
578: }
579:
580: /**
581: * set line pen for drawing border
582: *
583: * @param pen pen
584: */
585: public void setLinePen(JGoPen pen) {
586: JGoPen oldPen = linePen;
587: if (oldPen != pen) {
588: linePen = pen;
589: layoutChildren();
590: }
591: }
592:
593: /**
594: * set the out of scroll cell bounds
595: *
596: * @param rect rect
597: */
598: public void setOutOfScrollCellBounds(Rectangle rect) {
599: }
600:
601: /**
602: * paint this area
603: *
604: * @param g Graphics2D
605: * @param view view
606: */
607: public void paint(Graphics2D g, JGoView view) {
608: super .paint(g, view);
609:
610: if (!drawLines) {
611: return;
612: }
613:
614: int penwidth = 0;
615: if (getLinePen() != null) {
616: penwidth = getLinePen().getWidth();
617: }
618: if (penwidth == 0) {
619: return;
620: }
621: JGoObject r = getRect();
622: if (r == null) {
623: return; // not yet initialized
624: }
625: Insets insets1 = getInsets();
626:
627: int rectleft = r.getLeft();
628: int recttop = r.getTop();
629: int rectwidth = r.getWidth();
630: int rectheight = r.getHeight();
631:
632: int top = recttop + insets1.top;
633: int height = rectheight - insets1.top - insets1.bottom;
634:
635: int limit = 0;
636: limit = height;
637:
638: int s = 0; // height/width of visible items so far
639: for (int i = getFirstVisibleRow(); i < getLastVisibleRow(); i++) {
640:
641: JGoObject cell = this .getCellRendererComponent(i);
642: int h = cell.getHeight();
643: s += h;
644: int sep = Math.max(penwidth, getVerticalSpacing());
645: if (s + sep <= limit) {
646: JGoDrawable.drawLine(g, getLinePen(), rectleft, top + s
647: + sep / 2, rectleft + rectwidth, top + s + sep
648: / 2);
649: }
650: s += sep;
651: }
652:
653: }
654:
655: /**
656: * layout all of this children of this column area
657: */
658: public void layoutChildren() {
659: columnRect.setPen(getLinePen());
660:
661: if (drawBoundingRect) {
662: columnRect.setBoundingRect(this .getBoundingRect());
663: }
664:
665: //get the bounding rectangle of this column area
666: int x = getLeft() + insets.left;
667: int y = getTop() + insets.top;
668: int width = getWidth() - insets.left - insets.right;
669: int height = getHeight() - insets.top - insets.bottom;
670:
671: int oldLastVisibleRow = lastVisibleRow;
672:
673: int cellWidth = width;
674:
675: int nextCellDeltaTop = 0;
676:
677: //row count
678: int cnt = 0;
679: for (int i = 0; i < dataModel.getSize(); i++) {
680: JGoObject cell = (JGoObject) areaList.get(i);
681:
682: if (cnt < getFirstVisibleRow()) {
683: cell.setVisible(false);
684:
685: cell.setBoundingRect(x, y + nextCellDeltaTop,
686: cellWidth, cell.getHeight());
687: cnt++;
688: continue;
689: }
690:
691: //if cell is going out of the height of this
692: //area then we mark it invisible
693: //if (nextCellTop > (y + height)) {
694: if (nextCellDeltaTop + cell.getHeight() > height) {
695:
696: cell.setVisible(false);
697: cell.setBoundingRect(x, y + nextCellDeltaTop,
698: cellWidth, cell.getHeight());
699: } else {
700: cell.setVisible(true);
701: lastVisibleRow = cnt;
702: cell.setBoundingRect(x, y + nextCellDeltaTop,
703: cellWidth, cell.getHeight());
704: nextCellDeltaTop += cell.getHeight()
705: + getVerticalSpacing();
706:
707: }
708: cnt++;
709: }
710:
711: //layout vertical scrollbar
712: JGoScrollBar sbar = getVerticalScrollBar();
713:
714: if (sbar != null) {
715: sbar.setBoundingRect(x + width - scrollBarSize
716: - vScrollBarGap, y, scrollBarSize, height);
717:
718: if (oldLastVisibleRow != lastVisibleRow) {
719: updateVerticalScrollBar();
720: }
721: }
722: }
723:
724: class ListDataAdapter implements ListDataListener {
725:
726: /**
727: * Sent when the contents of the list has changed in a way that's too complex to
728: * characterize with the previous methods. For example, this is sent when an item
729: * has been replaced. Index0 and index1 bracket the change.
730: *
731: * @param e a <code>ListDataEvent</code> encapsulating the event information
732: */
733: public void contentsChanged(ListDataEvent e) {
734: }
735:
736: /**
737: * Sent after the indices in the index0,index1 interval have been inserted in the
738: * data model. The new interval includes both index0 and index1.
739: *
740: * @param e a <code>ListDataEvent</code> encapsulating the event information
741: */
742: public void intervalAdded(ListDataEvent e) {
743: //list model has new items so get renderer for them and add it
744: for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
745: Object val = getModel().getElementAt(i);
746:
747: ListAreaCellRenderer listCellRenderer = getCellRenderer(i);
748: JGoObject cell = listCellRenderer
749: .getListAreaCellRenderer(ListArea.this , val, i,
750: false, false);
751:
752: //set location of the cell otherwise it will be 0 0 causing
753: //width and height of listarea to start from 0,0 till current list
754: //are x, y position see computeBoundingRect() for how it is calculated
755:
756: cell.setLocation(ListArea.this .getLocation());
757: areaList.add(cell);
758: addObjectAtTail(cell);
759: }
760: }
761:
762: /**
763: * Sent after the indices in the index0,index1 interval have been removed from the
764: * data model. The interval includes both index0 and index1.
765: *
766: * @param e a <code>ListDataEvent</code> encapsulating the event information
767: */
768: public void intervalRemoved(ListDataEvent e) {
769: //list model has some items removed so remove renderer for them
770: for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
771: JGoObject obj = (JGoObject) areaList.get(i);
772: areaList.remove(i);
773: removeObject(obj);
774: }
775:
776: }
777:
778: }
779: }
|