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
.openapi
.ui
;
18 import com
.intellij
.openapi
.Disposable
;
19 import com
.intellij
.openapi
.actionSystem
.AnAction
;
20 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
21 import com
.intellij
.openapi
.actionSystem
.CustomShortcutSet
;
22 import com
.intellij
.openapi
.actionSystem
.ShortcutSet
;
23 import com
.intellij
.openapi
.application
.ApplicationManager
;
24 import com
.intellij
.openapi
.fileChooser
.FileChooser
;
25 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptor
;
26 import com
.intellij
.openapi
.keymap
.KeymapUtil
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.openapi
.util
.Disposer
;
29 import com
.intellij
.openapi
.util
.SystemInfo
;
30 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
31 import com
.intellij
.openapi
.vfs
.VirtualFile
;
32 import com
.intellij
.ui
.UIBundle
;
33 import com
.intellij
.util
.ui
.update
.LazyUiDisposable
;
34 import org
.jetbrains
.annotations
.NotNull
;
35 import org
.jetbrains
.annotations
.Nullable
;
39 import java
.awt
.event
.ActionEvent
;
40 import java
.awt
.event
.ActionListener
;
41 import java
.awt
.event
.KeyEvent
;
44 public class ComponentWithBrowseButton
<Comp
extends JComponent
> extends JPanel
implements Disposable
{
45 private final Comp myComponent
;
46 private final FixedSizeButton myBrowseButton
;
47 private boolean myButtonEnabled
= true;
49 public ComponentWithBrowseButton(Comp component
, ActionListener browseActionListener
) {
50 super(new BorderLayout(SystemInfo
.isMac?
0 : 2, 0));
51 myComponent
= component
;
52 add(myComponent
, BorderLayout
.CENTER
);
54 myBrowseButton
=new FixedSizeButton(myComponent
);
55 if (browseActionListener
!= null)
56 myBrowseButton
.addActionListener(browseActionListener
);
57 add(myBrowseButton
, BorderLayout
.EAST
);
59 myBrowseButton
.setToolTipText(UIBundle
.message("component.with.browse.button.browse.button.tooltip.text"));
61 // FixedSizeButton isn't focusable but it should be selectable via keyboard.
62 if (ApplicationManager
.getApplication() != null) { // avoid crash at design time
63 new MyDoClickAction(myBrowseButton
).registerShortcut(myComponent
);
67 public final Comp
getChildComponent() {
71 public void setTextFieldPreferredWidth(final int charCount
) {
72 final Comp comp
= getChildComponent();
73 Dimension size
= comp
.getPreferredSize();
74 FontMetrics fontMetrics
= comp
.getFontMetrics(comp
.getFont());
75 size
.width
= fontMetrics
.charWidth('a') * charCount
;
76 comp
.setPreferredSize(size
);
77 final Dimension preferredSize
= myBrowseButton
.getPreferredSize();
78 setPreferredSize(new Dimension(size
.width
+ preferredSize
.width
+ 2, preferredSize
.height
+ 2));
81 public void setEnabled(boolean enabled
) {
82 super.setEnabled(enabled
);
83 myBrowseButton
.setEnabled(enabled
&& myButtonEnabled
);
84 myComponent
.setEnabled(enabled
);
87 public void setButtonEnabled(boolean buttonEnabled
) {
88 myButtonEnabled
= buttonEnabled
;
89 setEnabled(isEnabled());
92 public void setButtonIcon(Icon icon
) {
93 myBrowseButton
.setIcon(icon
);
97 * Adds specified <code>listener</code> to the browse button.
99 public void addActionListener(ActionListener listener
){
100 myBrowseButton
.addActionListener(listener
);
103 public void removeActionListener(ActionListener listener
) {
104 myBrowseButton
.removeActionListener(listener
);
107 public void addBrowseFolderListener(String title
, String description
, @Nullable Project project
,
108 FileChooserDescriptor fileChooserDescriptor
,
109 TextComponentAccessor
<Comp
> accessor
) {
110 addBrowseFolderListener(title
, description
, project
, fileChooserDescriptor
, accessor
, true);
113 public void addBrowseFolderListener(String title
, String description
, @Nullable Project project
,
114 FileChooserDescriptor fileChooserDescriptor
,
115 TextComponentAccessor
<Comp
> accessor
, boolean autoRemoveOnHide
) {
116 addBrowseFolderListener(project
, new BrowseFolderActionListener
<Comp
>(title
, description
, this, project
, fileChooserDescriptor
, accessor
), autoRemoveOnHide
);
119 public void addBrowseFolderListener(@Nullable Project project
, final BrowseFolderActionListener
<Comp
> actionListener
) {
120 addBrowseFolderListener(project
, actionListener
, true);
123 public void addBrowseFolderListener(@Nullable Project project
, final BrowseFolderActionListener
<Comp
> actionListener
, boolean autoRemoveOnHide
) {
124 if (autoRemoveOnHide
) {
125 new LazyUiDisposable
<ComponentWithBrowseButton
<Comp
>>(null, this, this) {
126 protected void initialize(@NotNull Disposable parent
, @NotNull ComponentWithBrowseButton
<Comp
> child
, @Nullable Project project
) {
127 addActionListener(actionListener
);
128 Disposer
.register(child
, new Disposable() {
129 public void dispose() {
130 removeActionListener(actionListener
);
136 addActionListener(actionListener
);
140 public void dispose() {
143 public FixedSizeButton
getButton() {
144 return myBrowseButton
;
148 * Do not use this class directly it is public just to hack other implementation of controls similar to TextFieldWithBrowseButton.
150 public static final class MyDoClickAction
extends AnAction
{
151 private final FixedSizeButton myBrowseButton
;
152 private JComponent myFocusedComponent
;
153 public MyDoClickAction(FixedSizeButton browseButton
) {
154 myBrowseButton
= browseButton
;
157 public void actionPerformed(AnActionEvent e
){
158 myBrowseButton
.doClick();
159 SwingUtilities
.invokeLater(new Runnable() {
161 if (myFocusedComponent
!= null){
162 myFocusedComponent
.requestFocus();
169 public void registerShortcut(JComponent textField
) {
170 ShortcutSet shiftEnter
= new CustomShortcutSet(KeyStroke
.getKeyStroke(KeyEvent
.VK_ENTER
, KeyEvent
.SHIFT_DOWN_MASK
));
171 registerCustomShortcutSet(shiftEnter
, textField
);
172 myBrowseButton
.setToolTipText(KeymapUtil
.getShortcutsText(shiftEnter
.getShortcuts()));
173 myFocusedComponent
= textField
;
176 public static void addTo(FixedSizeButton browseButton
, JComponent aComponent
) {
177 new MyDoClickAction(browseButton
).registerShortcut(aComponent
);
181 public static class BrowseFolderActionListener
<T
extends JComponent
> implements ActionListener
{
182 private final String myTitle
;
183 private final String myDescription
;
184 private final ComponentWithBrowseButton
<T
> myTextComponent
;
185 private final TextComponentAccessor
<T
> myAccessor
;
186 private final Project myProject
;
187 private final FileChooserDescriptor myFileChooserDescriptor
;
189 public BrowseFolderActionListener(String title
, String description
, ComponentWithBrowseButton
<T
> textField
, @Nullable Project project
, FileChooserDescriptor fileChooserDescriptor
, TextComponentAccessor
<T
> accessor
) {
191 myDescription
= description
;
192 myTextComponent
= textField
;
194 myFileChooserDescriptor
= fileChooserDescriptor
;
195 myAccessor
= accessor
;
198 public void actionPerformed(ActionEvent e
){
199 FileChooserDescriptor fileChooserDescriptor
= (FileChooserDescriptor
)myFileChooserDescriptor
.clone();
200 if (myTitle
!= null) {
201 fileChooserDescriptor
.setTitle(myTitle
);
203 if (myDescription
!= null) {
204 fileChooserDescriptor
.setDescription(myDescription
);
206 VirtualFile initialFile
= getInitialFile();
207 VirtualFile
[] files
= doChoose(fileChooserDescriptor
, initialFile
);
208 if (files
!= null && files
.length
!= 0) {
209 onFileChoosen(files
[0]);
214 protected VirtualFile
getInitialFile() {
215 String directoryName
= getComponentText();
216 if (directoryName
.length() == 0) return null;
217 directoryName
= directoryName
.replace(File
.separatorChar
, '/');
218 VirtualFile path
= LocalFileSystem
.getInstance().findFileByPath(directoryName
);
219 while (path
== null && directoryName
.length() > 0) {
220 int pos
= directoryName
.lastIndexOf('/');
222 directoryName
= directoryName
.substring(0, pos
-1);
223 path
= LocalFileSystem
.getInstance().findFileByPath(directoryName
);
228 protected String
getComponentText() {
229 return myAccessor
.getText(myTextComponent
.getChildComponent()).trim();
232 protected void onFileChoosen(VirtualFile chosenFile
) {
233 myAccessor
.setText(myTextComponent
.getChildComponent(), chosenFile
.getPresentableUrl());
236 private VirtualFile
[] doChoose(FileChooserDescriptor fileChooserDescriptor
, VirtualFile initialFile
) {
237 if (myProject
== null) {
238 return FileChooser
.chooseFiles(myTextComponent
, fileChooserDescriptor
, initialFile
);
241 return FileChooser
.chooseFiles(myProject
, fileChooserDescriptor
, initialFile
);
246 public final void requestFocus() {
247 myComponent
.requestFocus();
250 public final void setNextFocusableComponent(Component aComponent
) {
251 myComponent
.setNextFocusableComponent(aComponent
);
254 private KeyEvent myCurrentEvent
= null;
255 protected final boolean processKeyBinding(KeyStroke ks
, KeyEvent e
, int condition
, boolean pressed
) {
256 if (condition
== WHEN_FOCUSED
&& myCurrentEvent
!= e
)
259 myComponent
.dispatchEvent(e
);
262 myCurrentEvent
= null;
264 if (e
.isConsumed()) return true;
265 return super.processKeyBinding(ks
, e
, condition
, pressed
);