changedUpdate exception
[fedora-idea.git] / platform / platform-impl / src / com / intellij / ui / TabbedPaneWrapper.java
blobfad4918474e413f443266df06a1a470c55335783
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ui;
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.actionSystem.DataProvider;
20 import com.intellij.openapi.actionSystem.IdeActions;
21 import com.intellij.openapi.application.ex.ApplicationEx;
22 import com.intellij.openapi.application.ex.ApplicationManagerEx;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
25 import com.intellij.ui.tabs.JBTabs;
26 import com.intellij.util.IJSwingUtilities;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
30 import javax.swing.*;
31 import javax.swing.event.ChangeListener;
32 import java.awt.*;
33 import java.awt.event.MouseListener;
35 /**
36 * @author Anton Katilin
37 * @author Vladimir Kondratyev
39 public class TabbedPaneWrapper {
40 private static final PrevNextActionsDescriptor DEFAULT_SHORTCUTS = new PrevNextActionsDescriptor(IdeActions.ACTION_NEXT_TAB, IdeActions.ACTION_PREVIOUS_TAB);
41 protected TabbedPane myTabbedPane;
42 protected JComponent myTabbedPaneHolder;
44 private TabFactory myFactory;
46 protected TabbedPaneWrapper(boolean construct) {
47 if (construct) {
48 init(SwingConstants.TOP, DEFAULT_SHORTCUTS, new JTabbedPaneFactory(this));
52 public TabbedPaneWrapper(@NotNull Disposable parentDisposable) {
53 this(SwingConstants.TOP, DEFAULT_SHORTCUTS, parentDisposable);
56 /**
57 * Creates tabbed pane wrapper with specified tab placement
59 * @param tabPlacement tab placement. It one of the <code>SwingConstants.TOP</code>,
60 * <code>SwingConstants.LEFT</code>, <code>SwingConstants.BOTTOM</code> or
61 * <code>SwingConstants.RIGHT</code>.
63 public TabbedPaneWrapper(int tabPlacement, PrevNextActionsDescriptor installKeyboardNavigation, @NotNull Disposable parentDisposable) {
64 final TabFactory factory;
65 if (SwingConstants.BOTTOM == tabPlacement || SwingConstants.TOP == tabPlacement) {
66 factory = new JBTabsFactory(this, null, parentDisposable);
67 } else {
68 factory = new JTabbedPaneFactory(this);
71 init(tabPlacement, installKeyboardNavigation, factory);
74 void init(int tabPlacement, PrevNextActionsDescriptor installKeyboardNavigation, TabFactory tabbedPaneFactory) {
75 myFactory = tabbedPaneFactory;
77 myTabbedPane = createTabbedPane(tabPlacement);
78 myTabbedPane.putClientProperty(TabbedPaneWrapper.class, myTabbedPane);
79 myTabbedPane.setKeyboardNavigation(installKeyboardNavigation);
81 myTabbedPaneHolder = createTabbedPaneHolder();
82 myTabbedPaneHolder.add(myTabbedPane.getComponent(), BorderLayout.CENTER);
83 myTabbedPaneHolder.setFocusCycleRoot(true);
84 myTabbedPaneHolder.setFocusTraversalPolicy(new _MyFocusTraversalPolicy());
86 assertIsDispatchThread();
89 public boolean isDisposed() {
90 return myTabbedPane != null && myTabbedPane.isDisposed();
93 private void assertIsDispatchThread() {
94 final ApplicationEx application = ApplicationManagerEx.getApplicationEx();
95 if (application != null){
96 application.assertIsDispatchThread(myTabbedPane.getComponent());
100 public final void addChangeListener(final ChangeListener listener){
101 assertIsDispatchThread();
102 myTabbedPane.addChangeListener(listener);
105 public final void removeChangeListener(final ChangeListener listener){
106 assertIsDispatchThread();
107 myTabbedPane.removeChangeListener(listener);
110 protected TabbedPaneHolder createTabbedPaneHolder() {
111 return myFactory.createTabbedPaneHolder();
114 public final JComponent getComponent() {
115 assertIsDispatchThread();
116 return myTabbedPaneHolder;
120 * @see javax.swing.JTabbedPane#addTab(java.lang.String, javax.swing.Icon, java.awt.Component, java.lang.String)
122 public final synchronized void addTab(final String title, final Icon icon, final JComponent component, final String tip) {
123 insertTab(title, icon, component, tip, myTabbedPane.getTabCount());
126 public final synchronized void addTab(final String title, final JComponent component) {
127 insertTab(title, null, component, null, myTabbedPane.getTabCount());
130 public synchronized void insertTab(final String title, final Icon icon, final JComponent component, final String tip, final int index) {
131 myTabbedPane.insertTab(title, icon, createTabWrapper(component), tip, index);
134 protected TabWrapper createTabWrapper(JComponent component) {
135 return myFactory.createTabWrapper(component);
138 protected TabbedPane createTabbedPane(final int tabPlacement) {
139 return myFactory.createTabbedPane(tabPlacement);
143 * @see javax.swing.JTabbedPane#setTabPlacement
145 public final void setTabPlacement(final int tabPlacement) {
146 assertIsDispatchThread();
147 myTabbedPane.setTabPlacement(tabPlacement);
150 public final void addMouseListener(final MouseListener listener) {
151 assertIsDispatchThread();
152 myTabbedPane.addMouseListener(listener);
155 public final synchronized int getSelectedIndex() {
156 return myTabbedPane.getSelectedIndex();
160 * @see javax.swing.JTabbedPane#getSelectedComponent()
162 public final synchronized JComponent getSelectedComponent() {
163 // Workaround for JDK 6 bug
164 final TabWrapper tabWrapper = myTabbedPane.getTabCount() > 0 ? (TabWrapper)myTabbedPane.getSelectedComponent():null;
165 return tabWrapper != null ? tabWrapper.getComponent() : null;
168 public final void setSelectedIndex(final int index) {
169 setSelectedIndex(index, true);
172 public final void setSelectedIndex(final int index, boolean requestFocus) {
173 assertIsDispatchThread();
175 final boolean hadFocus = IJSwingUtilities.hasFocus2(myTabbedPaneHolder);
176 myTabbedPane.setSelectedIndex(index);
177 if (hadFocus && requestFocus) {
178 myTabbedPaneHolder.requestFocus();
182 public final void setSelectedComponent(final JComponent component){
183 assertIsDispatchThread();
185 final int index=indexOfComponent(component);
186 if(index==-1){
187 throw new IllegalArgumentException("component not found in tabbed pane wrapper");
189 setSelectedIndex(index);
192 public final synchronized void removeTabAt(final int index) {
193 assertIsDispatchThread();
195 final boolean hadFocus = IJSwingUtilities.hasFocus2(myTabbedPaneHolder);
196 final TabWrapper wrapper = getWrapperAt(index);
197 try {
198 myTabbedPane.removeTabAt(index);
199 if (myTabbedPane.getTabCount() == 0) {
200 // to clear BasicTabbedPaneUI.visibleComponent field
201 myTabbedPane.revalidate();
203 if (hadFocus) {
204 myTabbedPaneHolder.requestFocus();
207 finally {
208 wrapper.dispose();
212 public final synchronized int getTabCount() {
213 return myTabbedPane.getTabCount();
216 public final Color getForegroundAt(final int index){
217 assertIsDispatchThread();
218 return myTabbedPane.getForegroundAt(index);
222 * @see javax.swing.JTabbedPane#setForegroundAt(int, java.awt.Color)
224 public final void setForegroundAt(final int index,final Color color){
225 assertIsDispatchThread();
226 myTabbedPane.setForegroundAt(index,color);
230 * @see javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)
232 public final synchronized JComponent getComponentAt(final int i) {
233 return getWrapperAt(i).getComponent();
236 private TabWrapper getWrapperAt(final int i) {
237 return (TabWrapper)myTabbedPane.getComponentAt(i);
240 public final void setTitleAt(final int index, final String title) {
241 assertIsDispatchThread();
242 myTabbedPane.setTitleAt(index, title);
245 public final void setToolTipTextAt(final int index, final String toolTipText) {
246 assertIsDispatchThread();
247 myTabbedPane.setToolTipTextAt(index, toolTipText);
251 * @see javax.swing.JTabbedPane#setComponentAt(int, java.awt.Component)
253 public final synchronized void setComponentAt(final int index, final JComponent component) {
254 assertIsDispatchThread();
255 myTabbedPane.setComponentAt(index, createTabWrapper(component));
259 * @see javax.swing.JTabbedPane#setIconAt(int, javax.swing.Icon)
261 public final void setIconAt(final int index, final Icon icon) {
262 assertIsDispatchThread();
263 myTabbedPane.setIconAt(index, icon);
266 public final void setEnabledAt(final int index, final boolean enabled) {
267 assertIsDispatchThread();
268 myTabbedPane.setEnabledAt(index, enabled);
272 * @see javax.swing.JTabbedPane#indexOfComponent(java.awt.Component)
274 public final synchronized int indexOfComponent(final JComponent component) {
275 for (int i=0; i < myTabbedPane.getTabCount(); i++) {
276 final JComponent c = getWrapperAt(i).getComponent();
277 if (c == component) {
278 return i;
281 return -1;
285 * @see javax.swing.JTabbedPane#getTabLayoutPolicy
287 public final synchronized int getTabLayoutPolicy(){
288 return myTabbedPane.getTabLayoutPolicy();
292 * @see javax.swing.JTabbedPane#setTabLayoutPolicy
294 public final synchronized void setTabLayoutPolicy(final int policy){
295 myTabbedPane.setTabLayoutPolicy(policy);
296 final int index=myTabbedPane.getSelectedIndex();
297 if(index!=-1){
298 myTabbedPane.scrollTabToVisible(index);
303 * @deprecated Keyboard navigation is installed/deinstalled automatically. This method does nothing now.
305 public final void installKeyboardNavigation(){
309 * @deprecated Keyboard navigation is installed/deinstalled automatically. This method does nothing now.
311 public final void uninstallKeyboardNavigation(){
314 public final String getTitleAt(final int i) {
315 return myTabbedPane.getTitleAt(i);
318 public void setSelectedTitle(@Nullable final String title) {
319 if (title == null) return;
321 for (int i = 0; i < myTabbedPane.getTabCount(); i++) {
322 final String each = myTabbedPane.getTitleAt(i);
323 if (title.equals(each)) {
324 myTabbedPane.setSelectedIndex(i);
325 break;
330 @Nullable
331 public String getSelectedTitle() {
332 return getSelectedIndex() < 0 ? null : getTitleAt(getSelectedIndex());
335 public void removeAll() {
336 myTabbedPane.removeAll();
339 public static final class TabWrapper extends JPanel implements DataProvider{
340 private JComponent myComponent;
342 boolean myCustomFocus = true;
344 public TabWrapper(@NotNull final JComponent component) {
345 super(new BorderLayout());
346 myComponent = component;
347 add(component, BorderLayout.CENTER);
351 * Make possible to search down for DataProviders
353 public Object getData(final String dataId) {
354 if(myComponent instanceof DataProvider){
355 return ((DataProvider)myComponent).getData(dataId);
356 } else {
357 return null;
361 public JComponent getComponent() {
362 return myComponent;
366 * TabWrappers are never reused so we can fix the leak in some LAF's TabbedPane UI by cleanuping ourselves.
368 public void dispose() {
369 if (myComponent != null) {
370 remove(myComponent);
371 myComponent = null;
375 public boolean requestDefaultFocus() {
376 if (!myCustomFocus) return super.requestDefaultFocus();
377 if (myComponent == null) return false; // Just in case someone requests the focus when we're already removed from the Swing tree.
378 final JComponent preferredFocusedComponent = IdeFocusTraversalPolicy.getPreferredFocusedComponent(myComponent);
379 if (preferredFocusedComponent != null) {
380 if (!preferredFocusedComponent.requestFocusInWindow()) {
381 preferredFocusedComponent.requestFocus();
383 return true;
384 } else {
385 return myComponent.requestDefaultFocus();
389 public void requestFocus() {
390 if (!myCustomFocus) {
391 super.requestFocus();
392 } else {
393 requestDefaultFocus();
397 public boolean requestFocusInWindow() {
398 if (!myCustomFocus) return super.requestFocusInWindow();
399 return requestDefaultFocus();
403 private final class _MyFocusTraversalPolicy extends IdeFocusTraversalPolicy{
404 public final Component getDefaultComponentImpl(final Container focusCycleRoot) {
405 final JComponent component=getSelectedComponent();
406 if(component!=null){
407 return IdeFocusTraversalPolicy.getPreferredFocusedComponent(component, this);
408 }else{
409 return null;
414 protected static class TabbedPaneHolder extends JPanel {
416 private final TabbedPaneWrapper myWrapper;
418 protected TabbedPaneHolder(TabbedPaneWrapper wrapper) {
419 super(new BorderLayout());
420 myWrapper = wrapper;
423 public boolean requestDefaultFocus() {
424 final JComponent preferredFocusedComponent = IdeFocusTraversalPolicy.getPreferredFocusedComponent(myWrapper.myTabbedPane.getComponent());
425 if (preferredFocusedComponent != null) {
426 if (!preferredFocusedComponent.requestFocusInWindow()) {
427 preferredFocusedComponent.requestFocus();
429 return true;
430 } else {
431 return super.requestDefaultFocus();
435 public final void requestFocus() {
436 requestDefaultFocus();
439 public final boolean requestFocusInWindow() {
440 return requestDefaultFocus();
443 public void updateUI() {
444 super.updateUI();
445 if (myWrapper != null) {
446 myWrapper.myTabbedPane.updateUI();
451 public static TabbedPaneWrapper get(JTabbedPane tabs) {
452 return (TabbedPaneWrapper)tabs.getClientProperty(TabbedPaneWrapper.class);
455 private interface TabFactory {
456 TabbedPane createTabbedPane(int tabPlacement);
457 TabbedPaneHolder createTabbedPaneHolder();
458 TabWrapper createTabWrapper(JComponent component);
461 private static class JTabbedPaneFactory implements TabFactory {
462 private final TabbedPaneWrapper myWrapper;
464 private JTabbedPaneFactory(TabbedPaneWrapper wrapper) {
465 myWrapper = wrapper;
468 public TabbedPane createTabbedPane(int tabPlacement) {
469 return new TabbedPaneImpl(tabPlacement);
472 public TabbedPaneHolder createTabbedPaneHolder() {
473 return new TabbedPaneHolder(myWrapper);
476 public TabWrapper createTabWrapper(JComponent component) {
477 return new TabWrapper(component);
481 private static class JBTabsFactory implements TabFactory {
483 private final Project myProject;
484 private final Disposable myParent;
485 private final TabbedPaneWrapper myWrapper;
487 private JBTabsFactory(TabbedPaneWrapper wrapper, Project project, @NotNull Disposable parent) {
488 myWrapper = wrapper;
489 myProject = project;
490 myParent = parent;
493 public TabbedPane createTabbedPane(int tabPlacement) {
494 return new JBTabsPaneImpl(myProject, tabPlacement, myParent);
497 public TabbedPaneHolder createTabbedPaneHolder() {
498 return new TabbedPaneHolder(myWrapper) {
499 @Override
500 public boolean requestDefaultFocus() {
501 getTabs().requestFocus();
502 return true;
508 public TabWrapper createTabWrapper(JComponent component) {
509 final TabWrapper tabWrapper = new TabWrapper(component);
510 tabWrapper.myCustomFocus = false;
511 return tabWrapper;
514 public JBTabs getTabs() {
515 return ((JBTabsPaneImpl)myWrapper.myTabbedPane).getTabs();
518 public void dispose() {
522 public static class AsJBTabs extends TabbedPaneWrapper {
523 public AsJBTabs(@Nullable Project project, int tabPlacement, PrevNextActionsDescriptor installKeyboardNavigation, @NotNull Disposable parent) {
524 super(false);
525 init(tabPlacement, installKeyboardNavigation, new JBTabsFactory(this, project, parent));
528 public JBTabs getTabs() {
529 return ((JBTabsPaneImpl)myTabbedPane).getTabs();
533 public static class AsJTabbedPane extends TabbedPaneWrapper {
534 public AsJTabbedPane(int tabPlacement) {
535 super(false);
536 init(tabPlacement, DEFAULT_SHORTCUTS, new JTabbedPaneFactory(this));