OSDN Git Service

タブのレイアウトポリシーをJTabbedPane.SCROLL_TAB_LAYOUTに変更
[nt-manager/nt-manager.git] / src / twitter / gui / component / DnDTabbedPane.java
1 /*
2  * To change this template, choose Tools | Templates
3  * and open the template in the editor.
4  */
5
6 package twitter.gui.component;
7
8 /**
9  * このプログラムは http://terai.xrea.jp/Swing/DnDTabbedPane.html のものを使わせていただきました.
10  */
11
12 //-*- mode:java; encoding:utf8n; coding:utf-8 -*-
13 // vim:set fileencoding=utf-8:
14 //http://terai.xrea.jp/Swing/DnDTabbedPane.html
15 import java.awt.*;
16 import java.awt.datatransfer.*;
17 import java.awt.dnd.*;
18 import java.awt.event.*;
19 import java.awt.image.*;
20 import javax.swing.*;
21 import twitter.gui.action.TweetMainAction;
22
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;
32
33     /**
34      * 
35      * @param mainAction
36      */
37     public void setMainAction(TweetMainAction mainAction ) {
38         this.mainAction = mainAction;
39     }
40
41     private void clickArrowButton(String actionKey) {
42         ActionMap map = getActionMap();
43         if(map != null) {
44             Action action = map.get(actionKey);
45             if (action != null && action.isEnabled()) {
46                 action.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null, 0, 0));
47             }
48         }
49     }
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
54
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);
64         }
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");
71         }
72     }
73
74     /**
75      * ばつボタン
76      */
77     private static class CloseTabIcon implements Icon {
78         private int width;
79         private int height;
80         public CloseTabIcon() {
81             width  = 16;
82             height = 16;
83         }
84         @Override
85         public void paintIcon(Component c, Graphics g, int x, int y) {
86             g.translate(x, 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);
94             g.translate(-x, -y);
95         }
96         @Override public int getIconWidth() {
97             return width;
98         }
99         @Override public int getIconHeight() {
100             return height;
101         }
102 //         public Rectangle getBounds() {
103 //             return new Rectangle(0, 0, width, height);
104 //         }
105     }
106
107     public DnDTabbedPane() {
108         super();
109         final DragSourceListener dsl = new DragSourceListener() {
110             public void dragEnter(DragSourceDragEvent e) {
111                 e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
112             }
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));
117                 glassPane.repaint();
118             }
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);
128                 }else{
129                     e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
130                     glassPane.setCursor(DragSource.DefaultMoveNoDrop);
131                 }
132             }
133             public void dragDropEnd(DragSourceDropEvent e) {
134                 lineRect.setRect(0,0,0,0);
135                 dragTabIndex = -1;
136                 glassPane.setVisible(false);
137                 if(hasGhost()) {
138                     glassPane.setVisible(false);
139                     glassPane.setImage(null);
140                 }
141             }
142             public void dropActionChanged(DragSourceDragEvent e) {}
143         };
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;
148             }
149             public DataFlavor[] getTransferDataFlavors() {
150                 DataFlavor[] f = new DataFlavor[1];
151                 f[0] = this.FLAVOR;
152                 return f;
153             }
154             public boolean isDataFlavorSupported(DataFlavor flavor) {
155                 return flavor.getHumanPresentableName().equals(NAME);
156             }
157         };
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());
166                 try{
167                     e.startDrag(DragSource.DefaultMoveDrop, t, dsl);
168                 }catch(InvalidDnDOperationException idoe) {
169                     idoe.printStackTrace();
170                 }
171             }
172         };
173         new DropTarget(glassPane, DnDConstants.ACTION_COPY_OR_MOVE, new CDropTargetListener(), true);
174         new DragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
175
176         //アイコン初期化
177         this.icon = new CloseTabIcon();
178
179         //スクロールポリシー
180         this.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
181     }
182
183     /**
184      * 
185      * @param title
186      * @param content
187      */
188     @Override
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);
203                 //削除時のイベント
204                 if( mainAction != null ) {
205                     mainAction.actionRemoveTabbedTable( index );
206                 }
207             }
208         });
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);
214     }
215
216     class CDropTargetListener implements DropTargetListener{
217         public void dragEnter(DropTargetDragEvent e) {
218             if(isDragAcceptable(e)) e.acceptDrag(e.getDropAction());
219             else e.rejectDrag();
220         }
221         public void dragExit(DropTargetEvent e) {}
222         public void dropActionChanged(DropTargetDragEvent e) {}
223
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));
229             }else{
230                 initTargetTopBottomLine(getTargetTabIndex(pt));
231             }
232             if(hasGhost()) {
233                 glassPane.setPoint(pt);
234             }
235             if(!pt_.equals(pt)) glassPane.repaint();
236             pt_ = pt;
237             autoScrollTest(pt);
238         }
239
240         public void drop(DropTargetDropEvent e) {
241             if(isDropAcceptable(e)) {
242                 convertTab(dragTabIndex, getTargetTabIndex(e.getLocation()));
243                 e.dropComplete(true);
244             }else{
245                 e.dropComplete(false);
246             }
247             repaint();
248         }
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) {
254                 return true;
255             }
256             return false;
257         }
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) {
263                 return true;
264             }
265             return false;
266         }
267     }
268
269     private boolean hasGhost = true;
270     public void setPaintGhost(boolean flag) {
271         hasGhost = flag;
272     }
273     public boolean hasGhost() {
274         return hasGhost;
275     }
276     private boolean isPaintScrollArea = true;
277     public void setPaintScrollArea(boolean flag) {
278         isPaintScrollArea = flag;
279     }
280     public boolean isPaintScrollArea() {
281         return isPaintScrollArea;
282     }
283
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;
292         }
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;
297     }
298     private void convertTab(int prev, int next) {
299         if(next<0 || prev==next) {
300             return;
301         }
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;
309         remove(prev);
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);
315
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);
319     }
320
321     private void initTargetLeftRightLine(int next) {
322         if(next<0 || dragTabIndex==next || next-dragTabIndex==1) {
323             lineRect.setRect(0,0,0,0);
324         }else if(next==0) {
325             Rectangle r = SwingUtilities.convertRectangle(this, getBoundsAt(0), glassPane);
326             lineRect.setRect(r.x-LINEWIDTH/2,r.y,LINEWIDTH,r.height);
327         }else{
328             Rectangle r = SwingUtilities.convertRectangle(this, getBoundsAt(next-1), glassPane);
329             lineRect.setRect(r.x+r.width-LINEWIDTH/2,r.y,LINEWIDTH,r.height);
330         }
331     }
332     private void initTargetTopBottomLine(int next) {
333         if(next<0 || dragTabIndex==next || next-dragTabIndex==1) {
334             lineRect.setRect(0,0,0,0);
335         }else if(next==0) {
336             Rectangle r = SwingUtilities.convertRectangle(this, getBoundsAt(0), glassPane);
337             lineRect.setRect(r.x,r.y-LINEWIDTH/2,r.width,LINEWIDTH);
338         }else{
339             Rectangle r = SwingUtilities.convertRectangle(this, getBoundsAt(next-1), glassPane);
340             lineRect.setRect(r.x,r.y+r.height-LINEWIDTH/2,r.width,LINEWIDTH);
341         }
342     }
343
344     private void initGlassPane(Component c, Point tabPt) {
345         getRootPane().setGlassPane(glassPane);
346         if(hasGhost()) {
347             Rectangle rect = getBoundsAt(dragTabIndex);
348             BufferedImage image = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
349             Graphics g = image.getGraphics();
350             c.paint(g);
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);
355         }
356         Point glassPt = SwingUtilities.convertPoint(c, tabPt, glassPane);
357         glassPane.setPoint(glassPt);
358         glassPane.setVisible(true);
359     }
360
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();
366         int idx = 0;
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;
380         }
381         tabbedRect.grow(2, 2);
382         return tabbedRect;
383     }
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() {
389             setOpaque(false);
390             composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
391             //http://bugs.sun.com/view_bug.do?bug_id=6700748
392             //setCursor(null);
393         }
394         public void setImage(BufferedImage draggingGhost) {
395             this.draggingGhost = draggingGhost;
396         }
397         public void setPoint(Point location) {
398             this.location = location;
399         }
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);
405                 g2.fill(rBackward);
406                 g2.fill(rForward);
407             }
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);
412             }
413             if(dragTabIndex>=0) {
414                 g2.setPaint(lineColor);
415                 g2.fill(lineRect);
416             }
417         }
418     }
419 }