| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.intellij.openapi.util.registry; |
| |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.util.PropertiesComponent; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.application.ApplicationInfo; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ModalityState; |
| import com.intellij.openapi.application.ex.ApplicationEx; |
| import com.intellij.openapi.application.impl.LaterInvocator; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.ui.ShadowAction; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.ui.*; |
| import com.intellij.ui.table.JBTable; |
| import com.intellij.util.PlatformIcons; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.ListSelectionEvent; |
| import javax.swing.event.ListSelectionListener; |
| import javax.swing.table.AbstractTableModel; |
| import javax.swing.table.TableCellEditor; |
| import javax.swing.table.TableCellRenderer; |
| import javax.swing.table.TableColumn; |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.KeyAdapter; |
| import java.awt.event.KeyEvent; |
| import java.awt.image.BufferedImage; |
| import java.util.*; |
| import java.util.List; |
| |
| /** |
| * @author Kirill Kalishev |
| * @author Konstantin Bulenkov |
| */ |
| public class RegistryUi implements Disposable { |
| private static final String RECENT_PROPERTIES_KEY = "RegistryRecentKeys"; |
| |
| private final JBTable myTable; |
| private final JTextArea myDescriptionLabel; |
| |
| private final JPanel myContent = new JPanel(); |
| |
| private static final Icon RESTART_ICON = PlatformIcons.CHECK_ICON; |
| private final RestoreDefaultsAction myRestoreDefaultsAction; |
| private final MyTableModel myModel; |
| |
| public RegistryUi() { |
| myContent.setLayout(new BorderLayout(UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP)); |
| |
| myModel = new MyTableModel(); |
| myTable = new JBTable(myModel); |
| myTable.setCellSelectionEnabled(true); |
| myTable.setEnableAntialiasing(true); |
| final MyRenderer r = new MyRenderer(); |
| |
| final TableColumn c0 = myTable.getColumnModel().getColumn(0); |
| c0.setCellRenderer(r); |
| c0.setMaxWidth(RESTART_ICON.getIconWidth() + 12); |
| c0.setMinWidth(RESTART_ICON.getIconWidth() + 12); |
| c0.setHeaderValue(null); |
| |
| final TableColumn c1 = myTable.getColumnModel().getColumn(1); |
| c1.setCellRenderer(r); |
| c1.setHeaderValue("Key"); |
| |
| final TableColumn c2 = myTable.getColumnModel().getColumn(2); |
| c2.setCellRenderer(r); |
| c2.setHeaderValue("Value"); |
| c2.setCellEditor(new MyEditor()); |
| myTable.setStriped(true); |
| |
| myDescriptionLabel = new JTextArea(3, 50); |
| myDescriptionLabel.setEditable(false); |
| final JScrollPane label = ScrollPaneFactory.createScrollPane(myDescriptionLabel); |
| final JPanel descriptionPanel = new JPanel(new BorderLayout()); |
| descriptionPanel.add(label, BorderLayout.CENTER); |
| descriptionPanel.setBorder(IdeBorderFactory.createTitledBorder("Description", false)); |
| |
| myContent.add(ScrollPaneFactory.createScrollPane(myTable), BorderLayout.CENTER); |
| myContent.add(descriptionPanel, BorderLayout.SOUTH); |
| |
| myTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { |
| @Override |
| public void valueChanged(@NotNull ListSelectionEvent e) { |
| if (e.getValueIsAdjusting()) return; |
| |
| final int selected = myTable.getSelectedRow(); |
| if (selected != -1) { |
| final RegistryValue value = myModel.getRegistryValue(selected); |
| String desc = value.getDescription(); |
| if (value.isRestartRequired()) { |
| String required = " Requires IDE restart."; |
| if (desc.endsWith(".")) { |
| desc += required; |
| } else { |
| desc += "." + required; |
| } |
| } |
| myDescriptionLabel.setText(desc); |
| } else { |
| myDescriptionLabel.setText(null); |
| } |
| } |
| }); |
| |
| myRestoreDefaultsAction = new RestoreDefaultsAction(); |
| |
| final DefaultActionGroup tbGroup = new DefaultActionGroup(); |
| tbGroup.add(new EditAction()); |
| tbGroup.add(new RevertAction()); |
| |
| final ActionToolbar tb = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, tbGroup, true); |
| tb.setTargetComponent(myTable); |
| |
| myContent.add(tb.getComponent(), BorderLayout.NORTH); |
| final TableSpeedSearch search = new TableSpeedSearch(myTable); |
| search.setComparator(new SpeedSearchComparator(false)); |
| myTable.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(@NotNull KeyEvent e) { |
| if (e.getKeyCode() == KeyEvent.VK_SPACE) { |
| int row = myTable.getSelectedRow(); |
| RegistryValue rv = myModel.getRegistryValue(row); |
| if (rv.isBoolean()) { |
| rv.setValue(!rv.asBoolean()); |
| keyChanged(rv.getKey()); |
| for (int i : new int[]{0, 1, 2}) myModel.fireTableCellUpdated(row, i); |
| revaliateActions(); |
| if (search.isPopupActive()) search.hidePopup(); |
| } |
| } |
| } |
| }); |
| } |
| |
| private class RevertAction extends AnAction { |
| |
| private RevertAction() { |
| new ShadowAction(this, ActionManager.getInstance().getAction("EditorDelete"), myTable); |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| e.getPresentation().setEnabled(!myTable.isEditing() && myTable.getSelectedRow() >= 0); |
| e.getPresentation().setText("Revert to Default"); |
| e.getPresentation().setIcon(AllIcons.General.Remove); |
| |
| if (e.getPresentation().isEnabled()) { |
| final RegistryValue rv = myModel.getRegistryValue(myTable.getSelectedRow()); |
| e.getPresentation().setEnabled(rv.isChangedFromDefault()); |
| } |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| final RegistryValue rv = myModel.getRegistryValue(myTable.getSelectedRow()); |
| rv.resetToDefault(); |
| myModel.fireTableCellUpdated(myTable.getSelectedRow(), 0); |
| myModel.fireTableCellUpdated(myTable.getSelectedRow(), 1); |
| myModel.fireTableCellUpdated(myTable.getSelectedRow(), 2); |
| revaliateActions(); |
| } |
| } |
| |
| private class EditAction extends AnAction { |
| private EditAction() { |
| new ShadowAction(this, ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE), myTable); |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| e.getPresentation().setEnabled(!myTable.isEditing() && myTable.getSelectedRow() >= 0); |
| e.getPresentation().setText("Edit"); |
| e.getPresentation().setIcon(AllIcons.Actions.EditSource); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| startEditingAtSelection(); |
| } |
| } |
| |
| private void startEditingAtSelection() { |
| myTable.editCellAt(myTable.getSelectedRow(), 2); |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| if (myTable.isEditing()) { |
| myTable.getEditorComponent().requestFocus(); |
| } |
| } |
| }); |
| } |
| |
| private static class MyTableModel extends AbstractTableModel { |
| |
| private final List<RegistryValue> myAll; |
| |
| private MyTableModel() { |
| myAll = Registry.getAll(); |
| final List<String> recent = getRecent(); |
| |
| Collections.sort(myAll, new Comparator<RegistryValue>() { |
| @Override |
| public int compare(@NotNull RegistryValue o1, @NotNull RegistryValue o2) { |
| final String key1 = o1.getKey(); |
| final String key2 = o2.getKey(); |
| final int i1 = recent.indexOf(key1); |
| final int i2 = recent.indexOf(key2); |
| final boolean c1 = i1 != -1; |
| final boolean c2 = i2 != -1; |
| if (c1 && !c2) return -1; |
| if (!c1 && c2) return 1; |
| if (c1 && c2) return i1 - i2; |
| return key1.compareToIgnoreCase(key2); |
| } |
| }); |
| } |
| |
| public void fireChanged() { |
| fireTableDataChanged(); |
| } |
| |
| @Override |
| public int getRowCount() { |
| return myAll.size(); |
| } |
| |
| @Override |
| public int getColumnCount() { |
| return 3; |
| } |
| |
| @Override |
| public Object getValueAt(int rowIndex, int columnIndex) { |
| RegistryValue value = getRegistryValue(rowIndex); |
| switch (columnIndex) { |
| case 0: |
| return ""; |
| case 1: |
| return value.getKey(); |
| case 2: |
| return value.asString(); |
| default: |
| return value; |
| } |
| } |
| |
| private RegistryValue getRegistryValue(final int rowIndex) { |
| return myAll.get(rowIndex); |
| } |
| |
| @Override |
| public boolean isCellEditable(int rowIndex, int columnIndex) { |
| return columnIndex == 2; |
| } |
| } |
| |
| private static List<String> getRecent() { |
| String value = PropertiesComponent.getInstance().getValue(RECENT_PROPERTIES_KEY); |
| return StringUtil.isEmpty(value) ? new ArrayList<String>(0) : StringUtil.split(value, "="); |
| } |
| |
| private static void keyChanged(String key) { |
| final List<String> recent = getRecent(); |
| recent.remove(key); |
| recent.add(0, key); |
| final String newValue = StringUtil.join(recent, "="); |
| PropertiesComponent.getInstance().setValue(RECENT_PROPERTIES_KEY, newValue); |
| } |
| |
| public boolean show() { |
| DialogWrapper dialog = new DialogWrapper(true) { |
| { |
| setTitle("Registry"); |
| setModal(true); |
| init(); |
| revaliateActions(); |
| } |
| |
| private AbstractAction myCloseAction; |
| |
| @Override |
| protected JComponent createCenterPanel() { |
| return myContent; |
| } |
| |
| @Override |
| protected void dispose() { |
| super.dispose(); |
| RegistryUi.this.dispose(); |
| } |
| |
| @Override |
| protected String getDimensionServiceKey() { |
| return "Registry"; |
| } |
| |
| |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| return myTable; |
| } |
| |
| @NotNull |
| @Override |
| protected Action[] createActions() { |
| return new Action[]{myRestoreDefaultsAction, myCloseAction}; |
| } |
| |
| @Override |
| protected void createDefaultActions() { |
| super.createDefaultActions(); |
| myCloseAction = new AbstractAction("Close") { |
| @Override |
| public void actionPerformed(@NotNull ActionEvent e) { |
| processClose(); |
| doOKAction(); |
| } |
| }; |
| myCloseAction.putValue(DialogWrapper.DEFAULT_ACTION, true); |
| } |
| |
| @Override |
| public void doCancelAction() { |
| final TableCellEditor cellEditor = myTable.getCellEditor(); |
| if (cellEditor != null) { |
| cellEditor.stopCellEditing(); |
| } |
| processClose(); |
| super.doCancelAction(); |
| } |
| }; |
| |
| return dialog.showAndGet(); |
| } |
| |
| private void processClose() { |
| if (Registry.getInstance().isRestartNeeded()) { |
| final ApplicationEx app = (ApplicationEx) ApplicationManager.getApplication(); |
| final ApplicationInfo info = ApplicationInfo.getInstance(); |
| |
| final int r = Messages.showOkCancelDialog(myContent, "You need to restart " + info.getVersionName() + " for the changes to take effect", "Restart Required", |
| app.isRestartCapable() ? "Restart Now" : "Shutdown Now", |
| app.isRestartCapable() ? "Restart Later": "Shutdown Later" |
| , Messages.getQuestionIcon()); |
| |
| |
| if (r == Messages.OK) { |
| LaterInvocator.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| app.restart(true); |
| } |
| }, ModalityState.NON_MODAL); |
| } |
| } |
| } |
| |
| private void restoreDefaults() { |
| final int r = Messages.showYesNoDialog(myContent, "Are you sure you want to revert registry settings to default values?", "Revert To Defaults", Messages.getQuestionIcon()); |
| if (r == Messages.YES) { |
| Registry.getInstance().restoreDefaults(); |
| myModel.fireChanged(); |
| revaliateActions(); |
| } |
| } |
| |
| private void revaliateActions() { |
| myRestoreDefaultsAction.setEnabled(!Registry.getInstance().isInDefaultState()); |
| } |
| |
| @Override |
| public void dispose() { |
| } |
| |
| private static class MyRenderer implements TableCellRenderer { |
| |
| private final JLabel myLabel = new JLabel(); |
| |
| @NotNull |
| @Override |
| public Component getTableCellRendererComponent(@NotNull JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
| final RegistryValue v = ((MyTableModel)table.getModel()).getRegistryValue(row); |
| myLabel.setIcon(null); |
| myLabel.setText(null); |
| myLabel.setHorizontalAlignment(SwingConstants.LEFT); |
| Color fg = isSelected ? table.getSelectionForeground() : table.getForeground(); |
| Color bg = isSelected ? table.getSelectionBackground() : table.getBackground(); |
| |
| if (v != null) { |
| switch (column) { |
| case 0: |
| myLabel.setIcon(v.isRestartRequired() ? RESTART_ICON : null); |
| myLabel.setHorizontalAlignment(SwingConstants.CENTER); |
| break; |
| case 1: |
| myLabel.setText(v.getKey()); |
| break; |
| case 2: |
| if (v.asColor(null) != null) { |
| myLabel.setIcon(createColoredIcon(v.asColor(null))); |
| } else if (v.isBoolean()) { |
| final JCheckBox box = new JCheckBox(); |
| box.setSelected(v.asBoolean()); |
| box.setBackground(bg); |
| return box; |
| } else { |
| myLabel.setText(v.asString()); |
| } |
| } |
| |
| myLabel.setOpaque(true); |
| |
| myLabel.setFont(myLabel.getFont().deriveFont(v.isChangedFromDefault() ? Font.BOLD : Font.PLAIN)); |
| myLabel.setForeground(fg); |
| myLabel.setBackground(bg); |
| } |
| |
| return myLabel; |
| } |
| } |
| |
| private static final Map<Color, Icon> icons_cache = new HashMap<Color, Icon>(); |
| private static Icon createColoredIcon(Color color) { |
| Icon icon = icons_cache.get(color); |
| if (icon != null) return icon; |
| final BufferedImage image = GraphicsEnvironment.getLocalGraphicsEnvironment() |
| .getDefaultScreenDevice().getDefaultConfiguration() |
| .createCompatibleImage(16, 16, Transparency.TRANSLUCENT); |
| final Graphics g = image.getGraphics(); |
| g.setColor(color); |
| g.fillRect(0, 0, 16, 16); |
| g.dispose(); |
| icon = new ImageIcon(image); |
| icons_cache.put(color, icon); |
| return icon; |
| } |
| |
| private class MyEditor extends AbstractCellEditor implements TableCellEditor { |
| |
| private final JTextField myField = new JTextField(); |
| private final JCheckBox myCheckBox = new JCheckBox(); |
| private RegistryValue myValue; |
| |
| @Override |
| @Nullable |
| public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { |
| myValue = ((MyTableModel)table.getModel()).getRegistryValue(row); |
| if (myValue.asColor(null) != null) { |
| final Color color = ColorChooser.chooseColor(table, "Choose color", myValue.asColor(Color.WHITE)); |
| if (color != null) { |
| myValue.setValue(color.getRed() + "," + color.getGreen() + "," + color.getBlue()); |
| keyChanged(myValue.getKey()); |
| } |
| return null; |
| } else if (myValue.isBoolean()) { |
| myCheckBox.setSelected(myValue.asBoolean()); |
| myCheckBox.setBackground(table.getBackground()); |
| return myCheckBox; |
| } else { |
| myField.setText(myValue.asString()); |
| myField.setBorder(null); |
| myField.selectAll(); |
| return myField; |
| } |
| } |
| |
| @Override |
| public boolean stopCellEditing() { |
| if (myValue != null) { |
| if (myValue.isBoolean()) { |
| myValue.setValue(myCheckBox.isSelected()); |
| } else { |
| myValue.setValue(myField.getText().trim()); |
| } |
| keyChanged(myValue.getKey()); |
| } |
| revaliateActions(); |
| return super.stopCellEditing(); |
| } |
| |
| @Override |
| public Object getCellEditorValue() { |
| return myValue; |
| } |
| } |
| |
| private class RestoreDefaultsAction extends AbstractAction { |
| public RestoreDefaultsAction() { |
| super("Restore Defaults"); |
| } |
| |
| @Override |
| public void actionPerformed(@NotNull ActionEvent e) { |
| restoreDefaults(); |
| } |
| } |
| } |