xref: /titanic_41/usr/src/cmd/krb5/kadmin/gui/visualrt/sunsoft/jws/visual/rt/awt/TextView.java (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * ident	"%Z%%M%	%I%	%E% SMI"
24  *
25  * Copyright (c) 2000 by Sun Microsystems, Inc.
26  * All rights reserved.
27  */
28 
29 /*
30  *        Copyright (C) 1996  Active Software, Inc.
31  *                  All rights reserved.
32  *
33  * @(#) TextView.java 1.29 - last change made 08/12/97
34  */
35 
36 package sunsoft.jws.visual.rt.awt;
37 
38 import sunsoft.jws.visual.rt.base.Global;
39 
40 import java.awt.*;
41 import java.util.Vector;
42 import java.util.Hashtable;
43 
44 public class TextView extends VJCanvas implements Scrollable {
45     private static final int RIGHT_MOUSE = 4;
46 
47     protected Vector items;
48 
49     protected int fontHeight, lineWidth, lineHeight;
50     protected FontMetrics fontMetrics;
51     protected int minrows = 10;
52     protected int mincolumns = 15;
53     protected int minWidth = 0;
54 
55     static protected final int textIndent = 6;
56     static protected final int textBorder = 2;
57     static protected final int viewBorder = 0;
58     static protected final int viewIPad = 2;
59 
60     private int selected[] = new int[0];
61     private int numSelected;
62 
63     private int scrollx = 0;
64     private int scrolly = 0;
65     private Image buffer;
66     private boolean multipleSelections;
67     private Hashtable stringWidthTable;
68 
69     private boolean menuMode;
70     private CLChoice menuChoice;
71     boolean menuDrag;
72 
73     private boolean gotEventInside;
74     private int prevMenuY;
75 
TextView()76     public TextView() {
77         stringWidthTable = new Hashtable();
78         // why was the background hardcoded to white?
79         // setBackground(Color.white);
80     }
81 
82     //
83     // Accessor methods.  These are forwarded from the TextList class.
84     //
setMinimumRows(int num)85     public void setMinimumRows(int num) {
86         minrows = num;
87     }
88 
getMinimumRows()89     public int getMinimumRows() {
90         return minrows;
91     }
92 
setMinimumColumns(int num)93     public void setMinimumColumns(int num) {
94         mincolumns = num;
95     }
96 
getMinimumColumns()97     public int getMinimumColumns() {
98         return mincolumns;
99     }
100 
getRows()101     public int getRows() {
102         if (lineHeight == 0)
103             return 0;
104 
105         Dimension size = size();
106         int h = size.height - (viewBorder + viewIPad);
107         return ((h+lineHeight-1)/lineHeight);
108     }
109 
updateView()110     public void updateView() {
111         for (int i = 0; i < numSelected; i++) {
112             if (selected[i] >= items.size()) {
113                 shift(selected, i+1, numSelected, -1);
114                 numSelected--;
115             }
116         }
117 
118         cacheMinWidth();
119         repaint();
120     }
121 
shift(int[] data, int offset, int length, int shift)122     private void shift(int[] data, int offset, int length, int shift) {
123         System.arraycopy(data, offset, data, offset+shift,
124 			 length-offset);
125     }
126 
select(int index)127     public void select(int index) {
128         if (index >= items.size())
129             return;
130         if (index < -1)
131             return;
132 
133         if (!multipleSelections) {
134             if (index == -1) {
135                 if (numSelected != 0) {
136                     numSelected = 0;
137                     repaint();
138                 }
139             } else {
140                 if (numSelected == 0) {
141                     selected = ensureCapacity(selected, numSelected, 1);
142                     numSelected = 1;
143                     selected[0] = -1;
144                 }
145                 if (selected[0] != index) {
146                     selected[0] = index;
147                     repaint();
148                 }
149             }
150         } else {
151             if (index == -1)
152                 return;
153 
154             boolean inserted = false;
155             for (int i = 0; i < numSelected; i++) {
156                 if (index == selected[i]) {
157                     inserted = true;
158                     break;
159                 } else if (index < selected[i]) {
160                     inserted = true;
161                     selected = ensureCapacity(selected,
162 					      numSelected, numSelected+1);
163                     shift(selected, i, numSelected, 1);
164                     selected[i] = index;
165                     numSelected++;
166                     repaint();
167                     break;
168                 }
169             }
170 
171             if (!inserted) {
172                 selected = ensureCapacity(selected, numSelected,
173 					  numSelected+1);
174                 selected[numSelected] = index;
175                 numSelected++;
176                 repaint();
177             }
178         }
179     }
180 
select(Object item)181     public void select(Object item) {
182         if (item != null)
183             select(items.indexOf(item));
184     }
185 
deselect(int index)186     public void deselect(int index) {
187         if (index < 0 || index >= items.size())
188             return;
189 
190         for (int i = 0; i < numSelected; i++) {
191             if (selected[i] == index) {
192                 shift(selected, i+1, numSelected, -1);
193                 numSelected--;
194                 repaint();
195                 break;
196             }
197         }
198     }
199 
deselectAll()200     public void deselectAll() {
201         if (numSelected != 0) {
202             numSelected = 0;
203             repaint();
204         }
205     }
206 
isSelected(int index)207     public boolean isSelected(int index) {
208         for (int i = 0; i < numSelected; i++) {
209             if (selected[i] == index)
210                 return true;
211         }
212 
213         return false;
214     }
215 
setMultipleSelections(boolean v)216     public void setMultipleSelections(boolean v) {
217         multipleSelections = v;
218     }
219 
allowsMultipleSelections()220     public boolean allowsMultipleSelections() {
221         return multipleSelections;
222     }
223 
getSelectedIndex()224     public int getSelectedIndex() {
225         if (numSelected == 0)
226             return -1;
227         else
228             return selected[0];
229     }
230 
getSelectedIndexes()231     public int[] getSelectedIndexes() {
232         int[] data = new int[numSelected];
233         System.arraycopy(selected, 0, data, 0, numSelected);
234         return data;
235     }
236 
getSelectedItem()237     public Object getSelectedItem() {
238         if (numSelected == 0)
239             return null;
240         else
241             return items.elementAt(selected[0]);
242     }
243 
getSelectedItems()244     public Object[] getSelectedItems() {
245         Object[] data = new Object[numSelected];
246         for (int i = 0; i < numSelected; i++)
247             data[i] = items.elementAt(selected[i]);
248         return data;
249     }
250 
ensureCapacity(int[] elementData, int elementCount, int minCapacity)251     private int[] ensureCapacity(int[] elementData, int elementCount,
252 				 int minCapacity) {
253         int oldCapacity = elementData.length;
254         if (minCapacity > oldCapacity) {
255             int oldData[] = elementData;
256             int newCapacity = oldCapacity * 2;
257             if (newCapacity < minCapacity) {
258                 newCapacity = minCapacity;
259             }
260             elementData = new int[newCapacity];
261             System.arraycopy(oldData, 0, elementData, 0, elementCount);
262         }
263 
264         return elementData;
265     }
266 
267     //
268     // Package private accessor methods
269     //
items(Vector items)270     protected void items(Vector items) {
271         this.items = items;
272     }
273 
274     //
275     // Component methods
276     //
minimumSize()277     public Dimension minimumSize() {
278         int bd = getBD();
279         return new Dimension(minWidth + bd, (minrows * lineHeight)
280 			     + bd);
281     }
282 
preferredSize()283     public Dimension preferredSize() {
284         return minimumSize();
285     }
286 
287     //
288     // Scrollable methods
289     //
scrollX(int x)290     public void scrollX(int x) {
291         scrollx = x;
292         repaint();
293     }
294 
scrollY(int y)295     public void scrollY(int y) {
296         scrolly = y;
297         repaint();
298     }
299 
scrollSize()300     public Dimension scrollSize() {
301         return new Dimension(minWidth, items.size()*lineHeight);
302     }
303 
viewSize(Dimension size)304     public Dimension viewSize(Dimension size) {
305         int bd = getBD();
306         size.width -= bd;
307         size.height -= bd;
308         return size;
309     }
310 
lineHeight()311     public int lineHeight() {
312         return lineHeight;
313     }
314 
getBD()315     private int getBD() {
316         return 2 * (viewBorder + viewIPad);
317     }
318 
319     //
320     // Event handling for selections
321     //
mouseDown(Event e, int x, int y)322     public boolean mouseDown(Event e, int x, int y) {
323         //
324         // On Windows95, we sometimes get bogus
325         // mouseDown events.  This happens
326         // when the TextView is being used as a
327         // menu for the CLChoice component.
328         // The user presses the mouse over the CLChoice
329         // item in the list, causing
330         // the menu to be mapped.  Then, without
331         // releasing the mouse, the user
332         // drags the mouse into the menu.  This
333         // sometimes causes a bogus
334         // mouseDown event to be sent to the menu /* JSTYLED */
335 	// (actually to the TextView
336         // inside the menu).
337         //
338         // The workaround is to ignore mouseDown
339         // events that occur before there
340         // has been either a mouseDrag or a
341         // mouseMove event.  This check is only
342         // made if menuChoice is not null /* JSTYLED */
343 	// (indicating that this TextView is being
344         // used as a CLChoice menu).
345         //
346         if (menuChoice != null && !menuDrag)
347             return true;
348 
349         selectY(e, true);
350 
351         menuMode = false;
352         menuChoice = null;
353         menuDrag = false;
354 
355         return true;
356     }
357 
mouseDrag(Event e, int x, int y)358     public boolean mouseDrag(Event e, int x, int y) {
359         // Workaround for bug observed on WindowsNT
360         // where you get spurious
361         // mouse drag events when pressing the mouse.
362         // The spurious event
363         // has coordinates x=-1 and y=-1.
364         if (!Global.isWindows() || e.y != -1) {
365             if (menuMode) {
366                 menuDrag = true;
367                 menuEvent(e);
368             } else if (!multipleSelections) {
369                 selectY(e, true);
370             }
371         }
372 
373         return true;
374     }
375 
mouseUp(Event e, int x, int y)376     public boolean mouseUp(Event e, int x, int y) {
377         if (menuMode)
378             menuEvent(e);
379 
380         return true;
381     }
382 
mouseMove(Event e, int x, int y)383     public boolean mouseMove(Event e, int x, int y) {
384         if (menuMode) {
385             menuDrag = true;
386             menuEvent(e);
387             return true;
388         } else {
389             return false;
390         }
391     }
392 
selectY(Event e, boolean doPost)393     private void selectY(Event e, boolean doPost) {
394         int evtX = e.x;
395         int evtY = e.y + scrolly - (viewBorder + viewIPad);
396         int index = evtY/lineHeight;
397         int size = items.size();
398         int id;
399 
400         if (size == 0)
401             return;
402         if (index >= size)
403             index = size-1;
404         if (index < 0)
405             index = 0;
406 
407         if (multipleSelections) {
408             if (isSelected(index)) {
409                 id = Event.LIST_DESELECT;
410                 deselect(index);
411             } else {
412                 id = Event.LIST_SELECT;
413                 select(index);
414             }
415 
416             if (doPost) {
417                 Event evt = new Event(getParent(), id,
418 				      items.elementAt(index));
419                 if (menuChoice != null)
420                     menuChoice.handleEvent(evt);
421                 else
422                     postEvent(evt);
423             }
424         } else {
425             id = Event.LIST_SELECT;
426 
427             //
428             // Ignore double-clicks on Windows because
429             // they are sent spuriously.
430             //
431             if ((e.clickCount == 2 && !Global.isWindows()) ||
432 		e.modifiers == RIGHT_MOUSE) {
433                 id = Event.ACTION_EVENT;
434             }
435 
436             if (!isSelected(index)) {
437                 select(index);
438                 repaint();
439                 if (doPost) {
440                     Event evt = new Event(getParent(), id,
441 					  items.elementAt(index));
442                     if (menuChoice != null)
443                         menuChoice.handleEvent(evt);
444                     else
445                         postEvent(evt);
446                 }
447             } else if (e.id == Event.MOUSE_DOWN ||
448 		       e.id == Event.MOUSE_UP) {
449                 if (doPost) {
450                     Event evt = new Event(getParent(), id,
451 					  items.elementAt(index));
452                     if (menuChoice != null)
453                         menuChoice.handleEvent(evt);
454                     else
455                         postEvent(evt);
456                 }
457             }
458         }
459     }
460 
461     //
462     // Painting
463     //
reshape(int x, int y, int width, int height)464     public void reshape(int x, int y, int width, int height) {
465         super.reshape(x, y, width, height);
466         cacheLineWidth();
467 
468         if (width <= 0 || height <= 0)
469             return;
470 
471         // Create the image used for double-buffering
472         if (buffer == null ||
473 	    (width != buffer.getWidth(this) ||
474 	    height != buffer.getHeight(this)))
475 	    buffer = createImage(width, height);
476     }
477 
update(Graphics g)478     public void update(Graphics g) {
479         paint(g);
480     }
481 
paint(Graphics g)482     public void paint(Graphics g) {
483         if (buffer == null)
484             return;
485 
486         g = buffer.getGraphics();
487         g.setFont(getFont());
488 
489         Dimension d = size();
490 
491         g.setColor(getBackground());
492         g.fillRect(0, 0, d.width, d.height);
493 
494         if (isEnabled())
495             g.setColor(getForeground());
496         else
497             g.setColor(getBackground().darker());
498         drawItems(g);
499 
500         g.setColor(getBackground());
501         drawBorder(g);
502 
503         g = getGraphics();
504         g.drawImage(buffer, 0, 0, this);
505     }
506 
drawItems(Graphics g)507     private void drawItems(Graphics g) {
508         Dimension d = size();
509         int size = items.size();
510 
511         int viewTop, viewBottom, lineTop, lineBottom;
512         int bd = viewBorder + viewIPad;
513         int yoff;
514 
515         viewTop = scrolly;
516         viewBottom = scrolly + d.height;
517 
518         for (int i = 0; i < size; i++) {
519             lineTop = i*lineHeight;
520             lineBottom = lineTop + lineHeight;
521 
522             if (lineTop > viewBottom || lineBottom < viewTop)
523                 continue;
524 
525             yoff = lineTop - viewTop + bd;
526             drawLine(g, i, -scrollx+bd, yoff);
527         }
528     }
529 
drawLine(Graphics g, int index, int xoff, int yoff)530     protected void drawLine(Graphics g, int index, int xoff, int yoff) {
531         String name = (String)items.elementAt(index);
532 
533         int x = textIndent;
534         int y = (lineHeight + fontHeight)/2 - 1;
535 
536         if (isSelected(index)) {
537             g.setColor(new Color(0, 0, 128));
538             g.fillRect(xoff, yoff, lineWidth, lineHeight);
539             g.setColor(Color.white);
540         }
541 
542         // Useful for pixel debugging
543         // g.drawRect(xoff, yoff, lineWidth-1, lineHeight-1);
544 
545         g.drawString(name, x+xoff, y+yoff);
546 
547         if (isSelected(index)) {
548             g.setColor(getForeground());
549         }
550     }
551 
drawBorder(Graphics g)552     private void drawBorder(Graphics g) {
553         Dimension size = size();
554 
555         for (int i = 0; i < viewIPad; i++)
556             g.drawRect(viewBorder+i, viewBorder+i,
557 		       size.width-1-2*(i+viewBorder),
558 		       size.height-1-2*(i+viewBorder));
559     }
560 
addNotify()561     public void addNotify() {
562         super.addNotify();
563         cacheAll();
564     }
565 
setFont(Font f)566     public void setFont(Font f) {
567         super.setFont(f);
568 
569         stringWidthTable.clear();
570         if (getPeer() != null)
571             cacheAll();
572     }
573 
cacheAll()574     private void cacheAll() {
575         cacheLineHeight();
576         cacheMinWidth();
577     }
578 
579     //
580     // Need to call this when the list of items
581     // changes, the font changes,
582     // or the mincolumns changes.  The mincolumns
583     // change should be followed
584     // by a call to updateView for the change
585     // to take effect.  It is only
586     // necessary to call updateView if addNotify
587     // has not yet been called.
588     //
cacheMinWidth()589     protected void cacheMinWidth() {
590         minWidth = mincolumns * getStringWidth(/* NOI18N */"0");
591 
592         int count = items.size();
593         for (int i = 0; i < count; i++)
594             minWidth = Math.max(minWidth,
595 				getStringWidth((String)items.elementAt(i)));
596 
597         minWidth += textIndent * 2;
598         cacheLineWidth();
599     }
600 
getStringWidth(String s)601     protected int getStringWidth(String s) {
602         if (fontMetrics == null)
603             return 0;
604 
605         Integer val = (Integer)stringWidthTable.get(s);
606         if (val == null) {
607             val = new Integer(fontMetrics.stringWidth(s));
608             stringWidthTable.put(s, val);
609         }
610 
611         return val.intValue();
612     }
613 
614     //
615     // Need to call this when the size
616     // changes and when the minWidth changes.
617     //
cacheLineWidth()618     protected void cacheLineWidth() {
619         Dimension size = size();
620         int bd = getBD();
621         lineWidth = Math.max(minWidth, size.width-bd);
622     }
623 
624     //
625     // Need to call this when the font changes.
626     //
cacheLineHeight()627     protected void cacheLineHeight() {
628         lineHeight = 0;
629         Graphics g = getGraphics();
630         if (g == null)
631             return;
632 
633         Font f = getFont();
634         if (f == null)
635             return;
636 
637         fontMetrics = g.getFontMetrics(f);
638         fontHeight = fontMetrics.getMaxAscent();
639 
640         lineHeight = fontHeight + 2*textBorder;
641     }
642 
643     //
644     // Methods used by CLChoice
645     //
646 
menuMode(CLChoice choice)647     void menuMode(CLChoice choice) {
648         menuChoice = choice;
649         menuMode = true;
650         menuDrag = false;
651         gotEventInside = false;
652         prevMenuY = 0;
653     }
654 
menuEvent(Event e)655     private void menuEvent(Event e) {
656         if (checkBounds(e)) {
657             selectY(e, (e.id == Event.MOUSE_UP));
658 
659             // Auto-scrolling
660             int bd = getBD();
661             if (e.id != Event.MOUSE_MOVE &&
662 		((e.y < bd && e.y < prevMenuY) ||
663 		 (e.y > (size().height-bd) && e.y > prevMenuY))) {
664                 ((TextList)getParent()).makeVisible(getSelectedIndex());
665             }
666 
667             prevMenuY = e.y;
668         }
669     }
670 
checkBounds(Event e)671     private boolean checkBounds(Event e) {
672         if (!gotEventInside) {
673             Dimension d = size();
674             int bd = getBD();
675             if (e.x >= bd && e.y >= bd &&
676 		e.x <= (d.width-bd) && e.y <= (d.height-bd))
677 		gotEventInside = true;
678         }
679 
680         return gotEventInside;
681     }
682 }
683