2 * To change this template, choose Tools | Templates
3 * and open the template in the editor.
6 package twitter.gui.component;
9 * このプログラムは http://terai.xrea.jp/Swing/DnDTabbedPane.html のものを使わせていただきました.
12 //-*- mode:java; encoding:utf8n; coding:utf-8 -*-
13 // vim:set fileencoding=utf-8:
14 //http://terai.xrea.jp/Swing/DnDTabbedPane.html
16 import java.awt.datatransfer.*;
17 import java.awt.dnd.*;
18 import java.awt.event.*;
19 import java.awt.image.*;
21 import twitter.gui.action.TweetMainAction;
23 public class DnDTabbedPane extends JTabbedPane {
24 private static final int LINEWIDTH = 3;
25 private static final String NAME = "tweetTab";
26 private final GhostGlassPane glassPane = new GhostGlassPane();
27 private final Rectangle lineRect = new Rectangle();
28 private final Color lineColor = new Color(0, 100, 255);
29 private int dragTabIndex = -1;
30 private final Icon icon;
31 private TweetMainAction mainAction = null;
37 public void setMainAction(TweetMainAction mainAction ) {
38 this.mainAction = mainAction;
41 private void clickArrowButton(String actionKey) {
42 ActionMap map = getActionMap();
44 Action action = map.get(actionKey);
45 if (action != null && action.isEnabled()) {
46 action.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null, 0, 0));
50 private static Rectangle rBackward = new Rectangle();
51 private static Rectangle rForward = new Rectangle();
52 private static int rwh = 20;
53 private static int buttonsize = 30; //xxx magic number of scroll button size
55 private void autoScrollTest(Point glassPt) {
56 Rectangle r = getTabAreaBounds();
57 int tabPlacement = getTabPlacement();
58 if(tabPlacement==TOP || tabPlacement==BOTTOM) {
59 rBackward.setBounds(r.x, r.y, rwh, r.height);
60 rForward.setBounds(r.x+r.width-rwh-buttonsize, r.y, rwh+buttonsize, r.height);
61 }else if(tabPlacement==LEFT || tabPlacement==RIGHT) {
62 rBackward.setBounds(r.x, r.y, r.width, rwh);
63 rForward.setBounds(r.x, r.y+r.height-rwh-buttonsize, r.width, rwh+buttonsize);
65 if(rBackward.contains(glassPt)) {
66 //System.out.println(new java.util.Date() + "Backward");
67 clickArrowButton("scrollTabsBackwardAction");
68 }else if(rForward.contains(glassPt)) {
69 //System.out.println(new java.util.Date() + "Forward");
70 clickArrowButton("scrollTabsForwardAction");
77 private static class CloseTabIcon implements Icon {
80 public CloseTabIcon() {
85 public void paintIcon(Component c, Graphics g, int x, int y) {
87 g.setColor(Color.BLACK);
88 g.drawLine(4, 4, 11, 11);
89 g.drawLine(4, 5, 10, 11);
90 g.drawLine(5, 4, 11, 10);
91 g.drawLine(11, 4, 4, 11);
92 g.drawLine(11, 5, 5, 11);
93 g.drawLine(10, 4, 4, 10);
96 @Override public int getIconWidth() {
99 @Override public int getIconHeight() {
102 // public Rectangle getBounds() {
103 // return new Rectangle(0, 0, width, height);
107 public DnDTabbedPane() {
109 final DragSourceListener dsl = new DragSourceListener() {
110 public void dragEnter(DragSourceDragEvent e) {
111 e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
113 public void dragExit(DragSourceEvent e) {
114 e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
115 lineRect.setRect(0,0,0,0);
116 glassPane.setPoint(new Point(-1000,-1000));
119 public void dragOver(DragSourceDragEvent e) {
120 Point glassPt = e.getLocation();
121 SwingUtilities.convertPointFromScreen(glassPt, glassPane);
122 int targetIdx = getTargetTabIndex(glassPt);
123 //if(getTabAreaBounds().contains(tabPt) && targetIdx>=0 &&
124 if(getTabAreaBounds().contains(glassPt) && targetIdx>=0 &&
125 targetIdx!=dragTabIndex && targetIdx!=dragTabIndex+1) {
126 e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
127 glassPane.setCursor(DragSource.DefaultMoveDrop);
129 e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
130 glassPane.setCursor(DragSource.DefaultMoveNoDrop);
133 public void dragDropEnd(DragSourceDropEvent e) {
134 lineRect.setRect(0,0,0,0);
136 glassPane.setVisible(false);
138 glassPane.setVisible(false);
139 glassPane.setImage(null);
142 public void dropActionChanged(DragSourceDragEvent e) {}
144 final Transferable t = new Transferable() {
145 private final DataFlavor FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME);
146 public Object getTransferData(DataFlavor flavor) {
147 return DnDTabbedPane.this;
149 public DataFlavor[] getTransferDataFlavors() {
150 DataFlavor[] f = new DataFlavor[1];
154 public boolean isDataFlavorSupported(DataFlavor flavor) {
155 return flavor.getHumanPresentableName().equals(NAME);
158 final DragGestureListener dgl = new DragGestureListener() {
159 public void dragGestureRecognized(DragGestureEvent e) {
160 if(getTabCount()<=1) return;
161 Point tabPt = e.getDragOrigin();
162 dragTabIndex = indexAtLocation(tabPt.x, tabPt.y);
163 //"disabled tab problem".
164 if(dragTabIndex<0 || !isEnabledAt(dragTabIndex)) return;
165 initGlassPane(e.getComponent(), e.getDragOrigin());
167 e.startDrag(DragSource.DefaultMoveDrop, t, dsl);
168 }catch(InvalidDnDOperationException idoe) {
169 idoe.printStackTrace();
173 new DropTarget(glassPane, DnDConstants.ACTION_COPY_OR_MOVE, new CDropTargetListener(), true);
174 new DragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
177 this.icon = new CloseTabIcon();
180 this.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
189 public void addTab(String title, final Component content) {
190 JPanel tab = new JPanel(new BorderLayout());
191 tab.setOpaque(false);
192 JLabel label = new JLabel(title);
193 label.setBorder(BorderFactory.createEmptyBorder(0,0,0,4));
194 JButton button = new JButton(icon);
195 //button.setBorderPainted(false);
196 //button.setFocusPainted(false);
197 //button.setContentAreaFilled(false);
198 button.setPreferredSize(new Dimension(icon.getIconWidth(),
199 icon.getIconHeight()));
200 button.addActionListener(new ActionListener() {
201 public void actionPerformed(ActionEvent e) {
202 int index = indexOfComponent(content);
204 if( mainAction != null ) {
205 mainAction.actionRemoveTabbedTable( index );
209 tab.add(label, BorderLayout.WEST);
210 tab.add(button, BorderLayout.EAST);
211 tab.setBorder(BorderFactory.createEmptyBorder(2,1,1,1));
212 super.addTab(title, content);
213 setTabComponentAt(getTabCount()-1, tab);
216 class CDropTargetListener implements DropTargetListener{
217 public void dragEnter(DropTargetDragEvent e) {
218 if(isDragAcceptable(e)) e.acceptDrag(e.getDropAction());
221 public void dragExit(DropTargetEvent e) {}
222 public void dropActionChanged(DropTargetDragEvent e) {}
224 private Point pt_ = new Point();
225 public void dragOver(final DropTargetDragEvent e) {
226 Point pt = e.getLocation();
227 if(getTabPlacement()==JTabbedPane.TOP || getTabPlacement()==JTabbedPane.BOTTOM) {
228 initTargetLeftRightLine(getTargetTabIndex(pt));
230 initTargetTopBottomLine(getTargetTabIndex(pt));
233 glassPane.setPoint(pt);
235 if(!pt_.equals(pt)) glassPane.repaint();
240 public void drop(DropTargetDropEvent e) {
241 if(isDropAcceptable(e)) {
242 convertTab(dragTabIndex, getTargetTabIndex(e.getLocation()));
243 e.dropComplete(true);
245 e.dropComplete(false);
249 public boolean isDragAcceptable(DropTargetDragEvent e) {
250 Transferable t = e.getTransferable();
251 if(t==null) return false;
252 DataFlavor[] f = e.getCurrentDataFlavors();
253 if(t.isDataFlavorSupported(f[0]) && dragTabIndex>=0) {
258 public boolean isDropAcceptable(DropTargetDropEvent e) {
259 Transferable t = e.getTransferable();
260 if(t==null) return false;
261 DataFlavor[] f = t.getTransferDataFlavors();
262 if(t.isDataFlavorSupported(f[0]) && dragTabIndex>=0) {
269 private boolean hasGhost = true;
270 public void setPaintGhost(boolean flag) {
273 public boolean hasGhost() {
276 private boolean isPaintScrollArea = true;
277 public void setPaintScrollArea(boolean flag) {
278 isPaintScrollArea = flag;
280 public boolean isPaintScrollArea() {
281 return isPaintScrollArea;
284 private int getTargetTabIndex(Point glassPt) {
285 Point tabPt = SwingUtilities.convertPoint(glassPane, glassPt, DnDTabbedPane.this);
286 boolean isTB = getTabPlacement()==JTabbedPane.TOP || getTabPlacement()==JTabbedPane.BOTTOM;
287 for(int i=0;i<getTabCount();i++) {
288 Rectangle r = getBoundsAt(i);
289 if(isTB) r.setRect(r.x-r.width/2, r.y, r.width, r.height);
290 else r.setRect(r.x, r.y-r.height/2, r.width, r.height);
291 if(r.contains(tabPt)) return i;
293 Rectangle r = getBoundsAt(getTabCount()-1);
294 if(isTB) r.setRect(r.x+r.width/2, r.y, r.width, r.height);
295 else r.setRect(r.x, r.y+r.height/2, r.width, r.height);
296 return r.contains(tabPt)?getTabCount():-1;
298 private void convertTab(int prev, int next) {
299 if(next<0 || prev==next) {
302 Component cmp = getComponentAt(prev);
303 Component tab = getTabComponentAt(prev);
304 String str = getTitleAt(prev);
305 Icon icon = getIconAt(prev);
306 String tip = getToolTipTextAt(prev);
307 boolean flg = isEnabledAt(prev);
308 int tgtindex = prev>next ? next : next-1;
310 insertTab(str, icon, cmp, tip, tgtindex);
311 setEnabledAt(tgtindex, flg);
312 //When you drag'n'drop a disabled tab, it finishes enabled and selected.
313 //pointed out by dlorde
314 if(flg) setSelectedIndex(tgtindex);
316 //I have a component in all tabs (jlabel with an X to close the tab) and when i move a tab the component disappear.
317 //pointed out by Daniel Dario Morales Salas
318 setTabComponentAt(tgtindex, tab);
321 private void initTargetLeftRightLine(int next) {
322 if(next<0 || dragTabIndex==next || next-dragTabIndex==1) {
323 lineRect.setRect(0,0,0,0);
325 Rectangle r = SwingUtilities.convertRectangle(this, getBoundsAt(0), glassPane);
326 lineRect.setRect(r.x-LINEWIDTH/2,r.y,LINEWIDTH,r.height);
328 Rectangle r = SwingUtilities.convertRectangle(this, getBoundsAt(next-1), glassPane);
329 lineRect.setRect(r.x+r.width-LINEWIDTH/2,r.y,LINEWIDTH,r.height);
332 private void initTargetTopBottomLine(int next) {
333 if(next<0 || dragTabIndex==next || next-dragTabIndex==1) {
334 lineRect.setRect(0,0,0,0);
336 Rectangle r = SwingUtilities.convertRectangle(this, getBoundsAt(0), glassPane);
337 lineRect.setRect(r.x,r.y-LINEWIDTH/2,r.width,LINEWIDTH);
339 Rectangle r = SwingUtilities.convertRectangle(this, getBoundsAt(next-1), glassPane);
340 lineRect.setRect(r.x,r.y+r.height-LINEWIDTH/2,r.width,LINEWIDTH);
344 private void initGlassPane(Component c, Point tabPt) {
345 getRootPane().setGlassPane(glassPane);
347 Rectangle rect = getBoundsAt(dragTabIndex);
348 BufferedImage image = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
349 Graphics g = image.getGraphics();
351 rect.x = rect.x<0?0:rect.x;
352 rect.y = rect.y<0?0:rect.y;
353 image = image.getSubimage(rect.x,rect.y,rect.width,rect.height);
354 glassPane.setImage(image);
356 Point glassPt = SwingUtilities.convertPoint(c, tabPt, glassPane);
357 glassPane.setPoint(glassPt);
358 glassPane.setVisible(true);
361 private Rectangle getTabAreaBounds() {
362 Rectangle tabbedRect = getBounds();
363 //pointed out by daryl. NullPointerException: i.e. addTab("Tab",null)
364 //Rectangle compRect = getSelectedComponent().getBounds();
365 Component comp = getSelectedComponent();
367 while(comp==null && idx<getTabCount()) comp = getComponentAt(idx++);
368 Rectangle compRect = (comp==null)?new Rectangle():comp.getBounds();
369 int tabPlacement = getTabPlacement();
370 if(tabPlacement==TOP) {
371 tabbedRect.height = tabbedRect.height - compRect.height;
372 }else if(tabPlacement==BOTTOM) {
373 tabbedRect.y = tabbedRect.y + compRect.y + compRect.height;
374 tabbedRect.height = tabbedRect.height - compRect.height;
375 }else if(tabPlacement==LEFT) {
376 tabbedRect.width = tabbedRect.width - compRect.width;
377 }else if(tabPlacement==RIGHT) {
378 tabbedRect.x = tabbedRect.x + compRect.x + compRect.width;
379 tabbedRect.width = tabbedRect.width - compRect.width;
381 tabbedRect.grow(2, 2);
384 class GhostGlassPane extends JPanel {
385 private final AlphaComposite composite;
386 private Point location = new Point(0, 0);
387 private BufferedImage draggingGhost = null;
388 public GhostGlassPane() {
390 composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
391 //http://bugs.sun.com/view_bug.do?bug_id=6700748
394 public void setImage(BufferedImage draggingGhost) {
395 this.draggingGhost = draggingGhost;
397 public void setPoint(Point location) {
398 this.location = location;
400 public void paintComponent(Graphics g) {
401 Graphics2D g2 = (Graphics2D) g;
402 g2.setComposite(composite);
403 if(isPaintScrollArea() && getTabLayoutPolicy()==SCROLL_TAB_LAYOUT) {
404 g2.setPaint(Color.RED);
408 if(draggingGhost != null) {
409 double xx = location.getX() - (draggingGhost.getWidth(this) /2d);
410 double yy = location.getY() - (draggingGhost.getHeight(this)/2d);
411 g2.drawImage(draggingGhost, (int)xx, (int)yy , null);
413 if(dragTabIndex>=0) {
414 g2.setPaint(lineColor);