/** * Created by IntelliJ IDEA. User: lex Date: Jun 4, 2003 Time: 12:45:56 PM To change this template * use Options | File Templates. */ public abstract class DebuggerStateManager { private final EventDispatcher<DebuggerContextListener> myEventDispatcher = EventDispatcher.create(DebuggerContextListener.class); public abstract DebuggerContextImpl getContext(); public abstract void setState( DebuggerContextImpl context, DebuggerSession.State state, DebuggerSession.Event event, String description); // we allow add listeners inside DebuggerContextListener.changeEvent public void addListener(DebuggerContextListener listener) { myEventDispatcher.addListener(listener); } // we allow remove listeners inside DebuggerContextListener.changeEvent public void removeListener(DebuggerContextListener listener) { myEventDispatcher.removeListener(listener); } protected void fireStateChanged(DebuggerContextImpl newContext, DebuggerSession.Event event) { myEventDispatcher.getMulticaster().changeEvent(newContext, event); } }
@Override public <F extends Facet> void addListener( final Class<F> facetClass, final FacetPointerListener<F> listener) { EventDispatcher<FacetPointerListener> dispatcher = myDispatchers.get(facetClass); if (dispatcher == null) { dispatcher = EventDispatcher.create(FacetPointerListener.class); myDispatchers.put(facetClass, dispatcher); } dispatcher.addListener(listener); }
/** * @author yole * @since 28.11.2006 */ public class MultipleChangeListBrowser extends ChangesBrowser { private final ChangeListChooser myChangeListChooser; private final ChangeListListener myChangeListListener = new MyChangeListListener(); private final boolean myShowingAllChangeLists; private final EventDispatcher<SelectedListChangeListener> myDispatcher = EventDispatcher.create(SelectedListChangeListener.class); private final ChangesBrowserExtender myExtender; private final Disposable myParentDisposable; private final Runnable myRebuildListListener; private Collection<Change> myAllChanges; private Map<Change, LocalChangeList> myChangeListsMap; private boolean myInRebuildList; // todo terrible constructor public MultipleChangeListBrowser( Project project, List<? extends ChangeList> changeLists, List<Change> changes, Disposable parentDisposable, ChangeList initialListSelection, boolean capableOfExcludingChanges, boolean highlightProblems, Runnable rebuildListListener, @Nullable Runnable inclusionListener, AnAction... additionalActions) { super( project, changeLists, changes, initialListSelection, capableOfExcludingChanges, highlightProblems, inclusionListener, MyUseCase.LOCAL_CHANGES, null); myParentDisposable = parentDisposable; myRebuildListListener = rebuildListListener; myChangeListChooser = new ChangeListChooser(changeLists); myHeaderPanel.add(myChangeListChooser, BorderLayout.EAST); myShowingAllChangeLists = Comparing.haveEqualElements( changeLists, ChangeListManager.getInstance(project).getChangeLists()); ChangeListManager.getInstance(myProject).addChangeListListener(myChangeListListener); myExtender = new Extender(project, this, additionalActions); ActionManager actionManager = ActionManager.getInstance(); final AnAction moveAction = actionManager.getAction(IdeActions.MOVE_TO_ANOTHER_CHANGE_LIST); actionManager.addAnActionListener( new AnActionListener.Adapter() { @Override public void afterActionPerformed( AnAction action, DataContext dataContext, AnActionEvent event) { if (moveAction.equals(action)) { rebuildList(); } } }, myParentDisposable); } @Override protected void setInitialSelection( List<? extends ChangeList> changeLists, List<Change> changes, ChangeList initialListSelection) { myAllChanges = new ArrayList<Change>(); mySelectedChangeList = initialListSelection; for (ChangeList list : changeLists) { if (list instanceof LocalChangeList) { myAllChanges.addAll(list.getChanges()); if (initialListSelection == null) { for (Change c : list.getChanges()) { if (changes.contains(c)) { mySelectedChangeList = list; break; } } } } } if (mySelectedChangeList == null) { for (ChangeList list : changeLists) { if (list instanceof LocalChangeList && ((LocalChangeList) list).isDefault()) { mySelectedChangeList = list; break; } } if (mySelectedChangeList == null && !changeLists.isEmpty()) { mySelectedChangeList = changeLists.get(0); } } } @Override public void dispose() { ChangeListManager.getInstance(myProject).removeChangeListListener(myChangeListListener); } public Collection<Change> getAllChanges() { return myAllChanges; } public ChangesBrowserExtender getExtender() { return myExtender; } public void addSelectedListChangeListener(SelectedListChangeListener listener) { myDispatcher.addListener(listener); } private void setSelectedList(final ChangeList list) { mySelectedChangeList = list; rebuildList(); myDispatcher.getMulticaster().selectedListChanged(); } @Override public void rebuildList() { if (myInRebuildList) return; try { myInRebuildList = true; if (myChangesToDisplay == null) { // changes set not fixed === local changes final ChangeListManager manager = ChangeListManager.getInstance(myProject); myChangeListsMap = new HashMap<Change, LocalChangeList>(); final List<LocalChangeList> lists = manager.getChangeListsCopy(); Collection<Change> allChanges = new ArrayList<Change>(); for (LocalChangeList list : lists) { final Collection<Change> changes = list.getChanges(); allChanges.addAll(changes); for (Change change : changes) { myChangeListsMap.put(change, list); } } myAllChanges = allChanges; // refresh selected list also updateListsInChooser(); } super.rebuildList(); if (myRebuildListListener != null) { myRebuildListListener.run(); } } finally { myInRebuildList = false; } } @Override public List<Change> getCurrentDisplayedChanges() { if (myChangesToDisplay == null) { return sortChanges(filterBySelectedChangeList(myAllChanges)); } return super.getCurrentDisplayedChanges(); } @NotNull public List<Change> getCurrentIncludedChanges() { return filterBySelectedChangeList(myViewer.getIncludedChanges()); } @NotNull public Collection<Change> getChangesIncludedInAllLists() { return myViewer.getIncludedChanges(); } private List<Change> filterBySelectedChangeList(final Collection<Change> changes) { List<Change> filtered = new ArrayList<Change>(); for (Change change : changes) { if (Comparing.equal(getList(change), mySelectedChangeList)) { filtered.add(change); } } return filtered; } private ChangeList getList(final Change change) { return myChangeListsMap.get(change); } @Override protected void buildToolBar(final DefaultActionGroup toolBarGroup) { super.buildToolBar(toolBarGroup); EmptyAction.registerWithShortcutSet( IdeActions.MOVE_TO_ANOTHER_CHANGE_LIST, CommonShortcuts.getMove(), myViewer); toolBarGroup.add(ActionManager.getInstance().getAction(IdeActions.MOVE_TO_ANOTHER_CHANGE_LIST)); } @Override protected List<AnAction> createDiffActions() { List<AnAction> actions = super.createDiffActions(); actions.add(new MoveAction()); return actions; } private void updateListsInChooser() { Runnable runnable = new Runnable() { public void run() { if (myChangeListChooser != null && myShowingAllChangeLists) { myChangeListChooser.updateLists( ChangeListManager.getInstance(myProject).getChangeListsCopy()); } } }; if (SwingUtilities.isEventDispatchThread()) { runnable.run(); } else { ApplicationManager.getApplication() .invokeLater(runnable, ModalityState.stateForComponent(this)); } } private static class Extender implements ChangesBrowserExtender { private final Project myProject; private final MultipleChangeListBrowser myBrowser; private final AnAction[] myAdditionalActions; private Extender( final Project project, final MultipleChangeListBrowser browser, AnAction[] additionalActions) { myProject = project; myBrowser = browser; myAdditionalActions = additionalActions; } public void addToolbarActions(final DialogWrapper dialogWrapper) { final Icon icon = AllIcons.Actions.Refresh; if (myBrowser.myChangesToDisplay == null) { myBrowser.addToolbarAction( new AnAction("Refresh Changes") { @Override public void actionPerformed(AnActionEvent e) { myBrowser.rebuildList(); } @Override public void update(AnActionEvent e) { e.getPresentation().setIcon(icon); } }); } RollbackDialogAction rollback = new RollbackDialogAction(); EmptyAction.setupAction(rollback, IdeActions.CHANGES_VIEW_ROLLBACK, myBrowser); myBrowser.addToolbarAction(rollback); final EditSourceForDialogAction editSourceAction = new EditSourceForDialogAction(myBrowser); editSourceAction.registerCustomShortcutSet(CommonShortcuts.getEditSource(), myBrowser); myBrowser.addToolbarAction(editSourceAction); myBrowser.addToolbarAction( ActionManager.getInstance().getAction("Vcs.CheckinProjectToolbar")); final List<AnAction> actions = AdditionalLocalChangeActionsInstaller.calculateActions( myProject, myBrowser.getAllChanges()); if (actions != null) { for (AnAction action : actions) { myBrowser.addToolbarAction(action); } } if (myAdditionalActions != null && myAdditionalActions.length > 0) { for (AnAction action : myAdditionalActions) { myBrowser.addToolbarAction(action); } } } public void addSelectedListChangeListener(final SelectedListChangeListener listener) { myBrowser.addSelectedListChangeListener(listener); } public Collection<AbstractVcs> getAffectedVcses() { final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject); final Set<AbstractVcs> vcses = new HashSet<AbstractVcs>(Arrays.asList(vcsManager.getAllActiveVcss())); final Set<AbstractVcs> result = new HashSet<AbstractVcs>(); for (Change change : myBrowser.myAllChanges) { if (vcses.isEmpty()) break; final AbstractVcs vcs = ChangesUtil.getVcsForChange(change, myBrowser.myProject); if (vcs != null) { result.add(vcs); vcses.remove(vcs); } } return result; } public List<Change> getCurrentIncludedChanges() { return myBrowser.getCurrentIncludedChanges(); } } private class ChangeListChooser extends JPanel { private static final int MAX_LEN = 35; private final JComboBox myChooser; public ChangeListChooser(List<? extends ChangeList> lists) { super(new BorderLayout(4, 2)); myChooser = new JComboBox(); //noinspection unchecked myChooser.setRenderer( new ColoredListCellRendererWrapper<LocalChangeList>() { @Override protected void doCustomize( JList list, LocalChangeList value, int index, boolean selected, boolean hasFocus) { if (value != null) { String name = value.getName().trim(); if (name.length() > MAX_LEN) { name = name.substring(0, MAX_LEN - 3) + "..."; } append( name, value.isDefault() ? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES); } } }); myChooser.addItemListener( new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { final LocalChangeList changeList = (LocalChangeList) myChooser.getSelectedItem(); setSelectedList(changeList); myChooser.setToolTipText(changeList == null ? "" : (changeList.getName())); } } }); updateLists(lists); myChooser.setEditable(false); add(myChooser, BorderLayout.CENTER); JLabel label = new JLabel(VcsBundle.message("commit.dialog.changelist.label")); label.setLabelFor(myChooser); add(label, BorderLayout.WEST); } public void updateLists(List<? extends ChangeList> lists) { //noinspection unchecked myChooser.setModel(new DefaultComboBoxModel(lists.toArray())); myChooser.setEnabled(lists.size() > 1); if (lists.contains(mySelectedChangeList)) { myChooser.setSelectedItem(mySelectedChangeList); } else { if (myChooser.getItemCount() > 0) { myChooser.setSelectedIndex(0); } } mySelectedChangeList = (ChangeList) myChooser.getSelectedItem(); } } private class MyChangeListListener extends ChangeListAdapter { public void changeListAdded(ChangeList list) { updateListsInChooser(); } } private class MoveAction extends MoveChangesToAnotherListAction { @Override protected boolean isEnabled(AnActionEvent e) { Change change = e.getData(VcsDataKeys.CURRENT_CHANGE); if (change == null) return false; return super.isEnabled(e); } public void actionPerformed(AnActionEvent e) { Change change = e.getData(VcsDataKeys.CURRENT_CHANGE); askAndMove(myProject, Collections.singletonList(change), null); } } }
public class BreakpointManager { private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.breakpoints.BreakpointManager"); @NonNls private static final String MASTER_BREAKPOINT_TAGNAME = "master_breakpoint"; @NonNls private static final String SLAVE_BREAKPOINT_TAGNAME = "slave_breakpoint"; @NonNls private static final String DEFAULT_SUSPEND_POLICY_ATTRIBUTE_NAME = "default_suspend_policy"; @NonNls private static final String DEFAULT_CONDITION_STATE_ATTRIBUTE_NAME = "default_condition_enabled"; @NonNls private static final String RULES_GROUP_NAME = "breakpoint_rules"; private static final String CONVERTED_PARAM = "converted"; private final Project myProject; private final Map<XBreakpoint, Breakpoint> myBreakpoints = new HashMap<XBreakpoint, Breakpoint>(); // breakpoints storage, access should be synchronized @Nullable private List<Breakpoint> myBreakpointsListForIteration = null; // another list for breakpoints iteration, unsynchronized access ok private final Map<String, String> myUIProperties = new LinkedHashMap<String, String>(); // private final Map<Key<? extends Breakpoint>, BreakpointDefaults> myBreakpointDefaults = new // LinkedHashMap<Key<? extends Breakpoint>, BreakpointDefaults>(); private final EventDispatcher<BreakpointManagerListener> myDispatcher = EventDispatcher.create(BreakpointManagerListener.class); private final StartupManager myStartupManager; private void update(@NotNull List<BreakpointWithHighlighter> breakpoints) { final TIntHashSet intHash = new TIntHashSet(); for (BreakpointWithHighlighter breakpoint : breakpoints) { SourcePosition sourcePosition = breakpoint.getSourcePosition(); breakpoint.reload(); if (breakpoint.isValid()) { if (sourcePosition == null || breakpoint.getSourcePosition().getLine() != sourcePosition.getLine()) { fireBreakpointChanged(breakpoint); } if (intHash.contains(breakpoint.getLineIndex())) { remove(breakpoint); } else { intHash.add(breakpoint.getLineIndex()); } } else { remove(breakpoint); } } } private void remove(final BreakpointWithHighlighter breakpoint) { DebuggerInvocationUtil.invokeLater( myProject, new Runnable() { @Override public void run() { removeBreakpoint(breakpoint); } }); } public BreakpointManager( @NotNull Project project, @NotNull StartupManager startupManager, @NotNull DebuggerManagerImpl debuggerManager) { myProject = project; myStartupManager = startupManager; debuggerManager .getContextManager() .addListener( new DebuggerContextListener() { private DebuggerSession myPreviousSession; @Override public void changeEvent(@NotNull DebuggerContextImpl newContext, int event) { if (newContext.getDebuggerSession() != myPreviousSession || event == DebuggerSession.EVENT_DETACHED) { updateBreakpointsUI(); myPreviousSession = newContext.getDebuggerSession(); } } }); } public void init() { XBreakpointManager manager = XDebuggerManager.getInstance(myProject).getBreakpointManager(); manager.addBreakpointListener( new XBreakpointListener() { @Override public void breakpointAdded(@NotNull XBreakpoint xBreakpoint) { if (isJavaType(xBreakpoint)) { onBreakpointAdded(xBreakpoint); } } @Override public void breakpointRemoved(@NotNull XBreakpoint xBreakpoint) { onBreakpointRemoved(xBreakpoint); } @Override public void breakpointChanged(@NotNull XBreakpoint xBreakpoint) { Breakpoint breakpoint = myBreakpoints.get(xBreakpoint); if (breakpoint != null) { fireBreakpointChanged(breakpoint); } } }); } private XBreakpointManager getXBreakpointManager() { return XDebuggerManager.getInstance(myProject).getBreakpointManager(); } public void editBreakpoint(final Breakpoint breakpoint, final Editor editor) { DebuggerInvocationUtil.swingInvokeLater( myProject, new Runnable() { @Override public void run() { XBreakpoint xBreakpoint = breakpoint.myXBreakpoint; if (xBreakpoint instanceof XLineBreakpointImpl) { RangeHighlighter highlighter = ((XLineBreakpointImpl) xBreakpoint).getHighlighter(); if (highlighter != null) { GutterIconRenderer renderer = highlighter.getGutterIconRenderer(); if (renderer != null) { DebuggerSupport.getDebuggerSupport(JavaDebuggerSupport.class) .getEditBreakpointAction() .editBreakpoint(myProject, editor, breakpoint.myXBreakpoint, renderer); } } } } }); } // @NotNull // public BreakpointDefaults getBreakpointDefaults(Key<? extends Breakpoint> category) { // BreakpointDefaults defaults = myBreakpointDefaults.get(category); // if (defaults == null) { // defaults = new BreakpointDefaults(); // } // return defaults; // } public void setBreakpointDefaults( Key<? extends Breakpoint> category, BreakpointDefaults defaults) { Class typeCls = null; if (LineBreakpoint.CATEGORY.toString().equals(category.toString())) { typeCls = JavaLineBreakpointType.class; } else if (MethodBreakpoint.CATEGORY.toString().equals(category.toString())) { typeCls = JavaMethodBreakpointType.class; } else if (FieldBreakpoint.CATEGORY.toString().equals(category.toString())) { typeCls = JavaFieldBreakpointType.class; } else if (ExceptionBreakpoint.CATEGORY.toString().equals(category.toString())) { typeCls = JavaExceptionBreakpointType.class; } if (typeCls != null) { XBreakpointType<XBreakpoint<?>, ?> type = XDebuggerUtil.getInstance().findBreakpointType(typeCls); ((XBreakpointManagerImpl) getXBreakpointManager()) .getBreakpointDefaults(type) .setSuspendPolicy(Breakpoint.transformSuspendPolicy(defaults.getSuspendPolicy())); } // myBreakpointDefaults.put(category, defaults); } @Nullable public RunToCursorBreakpoint addRunToCursorBreakpoint( Document document, int lineIndex, final boolean ignoreBreakpoints) { return RunToCursorBreakpoint.create(myProject, document, lineIndex, ignoreBreakpoints); } @Nullable public StepIntoBreakpoint addStepIntoBreakpoint(@NotNull BreakpointStepMethodFilter filter) { return StepIntoBreakpoint.create(myProject, filter); } @Nullable public LineBreakpoint addLineBreakpoint(Document document, int lineIndex) { ApplicationManager.getApplication().assertIsDispatchThread(); if (!LineBreakpoint.canAddLineBreakpoint(myProject, document, lineIndex)) { return null; } XLineBreakpoint xLineBreakpoint = addXLineBreakpoint(JavaLineBreakpointType.class, document, lineIndex); LineBreakpoint breakpoint = LineBreakpoint.create(myProject, xLineBreakpoint); if (breakpoint == null) { return null; } addBreakpoint(breakpoint); return breakpoint; } // @Nullable // public FieldBreakpoint addFieldBreakpoint(Field field, ObjectReference object) { // ApplicationManager.getApplication().assertIsDispatchThread(); // final FieldBreakpoint fieldBreakpoint = FieldBreakpoint.create(myProject, field, object, // null); // if (fieldBreakpoint != null) { // addBreakpoint(fieldBreakpoint); // } // return fieldBreakpoint; // } @Nullable public FieldBreakpoint addFieldBreakpoint(@NotNull Document document, int offset) { PsiField field = FieldBreakpoint.findField(myProject, document, offset); if (field == null) { return null; } int line = document.getLineNumber(offset); if (document.getLineNumber(field.getNameIdentifier().getTextOffset()) < line) { return null; } return addFieldBreakpoint(document, line, field.getName()); } @Nullable public FieldBreakpoint addFieldBreakpoint(Document document, int lineIndex, String fieldName) { ApplicationManager.getApplication().assertIsDispatchThread(); XLineBreakpoint xBreakpoint = addXLineBreakpoint(JavaFieldBreakpointType.class, document, lineIndex); FieldBreakpoint fieldBreakpoint = FieldBreakpoint.create(myProject, fieldName, xBreakpoint); if (fieldBreakpoint != null) { addBreakpoint(fieldBreakpoint); } return fieldBreakpoint; } @NotNull public ExceptionBreakpoint addExceptionBreakpoint( @NotNull final String exceptionClassName, final String packageName) { ApplicationManager.getApplication().assertIsDispatchThread(); final JavaExceptionBreakpointType type = (JavaExceptionBreakpointType) XDebuggerUtil.getInstance().findBreakpointType(JavaExceptionBreakpointType.class); return ApplicationManager.getApplication() .runWriteAction( new Computable<ExceptionBreakpoint>() { @Override public ExceptionBreakpoint compute() { XBreakpoint<JavaExceptionBreakpointProperties> xBreakpoint = XDebuggerManager.getInstance(myProject) .getBreakpointManager() .addBreakpoint( type, new JavaExceptionBreakpointProperties(exceptionClassName, packageName)); ExceptionBreakpoint breakpoint = new ExceptionBreakpoint( myProject, exceptionClassName, packageName, xBreakpoint); addBreakpoint(breakpoint); if (LOG.isDebugEnabled()) { LOG.debug("ExceptionBreakpoint Added"); } return breakpoint; } }); } @Nullable public MethodBreakpoint addMethodBreakpoint(Document document, int lineIndex) { ApplicationManager.getApplication().assertIsDispatchThread(); XLineBreakpoint xBreakpoint = addXLineBreakpoint(JavaMethodBreakpointType.class, document, lineIndex); MethodBreakpoint breakpoint = MethodBreakpoint.create(myProject, xBreakpoint); if (breakpoint == null) { return null; } XDebugSessionImpl.NOTIFICATION_GROUP .createNotification( "Method breakpoints may dramatically slow down debugging", MessageType.WARNING) .notify(myProject); addBreakpoint(breakpoint); return breakpoint; } private <B extends XBreakpoint<?>> XLineBreakpoint addXLineBreakpoint( Class<? extends XBreakpointType<B, ?>> typeCls, Document document, final int lineIndex) { final XBreakpointType<B, ?> type = XDebuggerUtil.getInstance().findBreakpointType(typeCls); final VirtualFile file = FileDocumentManager.getInstance().getFile(document); return ApplicationManager.getApplication() .runWriteAction( new Computable<XLineBreakpoint>() { @Override public XLineBreakpoint compute() { return XDebuggerManager.getInstance(myProject) .getBreakpointManager() .addLineBreakpoint( (XLineBreakpointType) type, file.getUrl(), lineIndex, ((XLineBreakpointType) type).createBreakpointProperties(file, lineIndex)); } }); } @Nullable public WildcardMethodBreakpoint addMethodBreakpoint(String classPattern, String methodName) { ApplicationManager.getApplication().assertIsDispatchThread(); WildcardMethodBreakpoint breakpoint = WildcardMethodBreakpoint.create(myProject, classPattern, methodName, null); if (breakpoint == null) { return null; } addBreakpoint(breakpoint); return breakpoint; } /** @return null if not found or a breakpoint object */ @NotNull public List<BreakpointWithHighlighter> findBreakpoints( final Document document, final int offset) { LinkedList<BreakpointWithHighlighter> result = new LinkedList<BreakpointWithHighlighter>(); ApplicationManager.getApplication().assertIsDispatchThread(); for (final Breakpoint breakpoint : getBreakpoints()) { if (breakpoint instanceof BreakpointWithHighlighter && ((BreakpointWithHighlighter) breakpoint).isAt(document, offset)) { result.add((BreakpointWithHighlighter) breakpoint); } } return result; } @NotNull public List<BreakpointWithHighlighter> findBreakpoints( @NotNull Document document, @NotNull TextRange textRange) { ApplicationManager.getApplication().assertIsDispatchThread(); List<BreakpointWithHighlighter> result = new ArrayList<BreakpointWithHighlighter>(); int startLine = document.getLineNumber(textRange.getStartOffset()); int endLine = document.getLineNumber(textRange.getEndOffset()) + 1; TextRange lineRange = new TextRange(startLine, endLine); for (final Breakpoint breakpoint : getBreakpoints()) { if (breakpoint instanceof BreakpointWithHighlighter && lineRange.contains(((BreakpointWithHighlighter) breakpoint).getLineIndex())) { result.add((BreakpointWithHighlighter) breakpoint); } } return result; } /** @param category breakpoint category, null if the category does not matter */ @Nullable public <T extends BreakpointWithHighlighter> T findBreakpoint( final Document document, final int offset, @Nullable final Key<T> category) { for (final Breakpoint breakpoint : getBreakpoints()) { if (breakpoint instanceof BreakpointWithHighlighter && ((BreakpointWithHighlighter) breakpoint).isAt(document, offset)) { if (category == null || category.equals(breakpoint.getCategory())) { //noinspection CastConflictsWithInstanceof,unchecked return (T) breakpoint; } } } return null; } @Nullable public static Breakpoint findBreakpoint(@NotNull XBreakpoint xBreakpoint) { Project project = ((XBreakpointBase) xBreakpoint).getProject(); BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager(); return breakpointManager.myBreakpoints.get(xBreakpoint); } private List<Element> myOriginalBreakpointsNodes = new ArrayList<Element>(); public void readExternal(@NotNull final Element parentNode) { // save old breakpoints for (Element element : parentNode.getChildren()) { myOriginalBreakpointsNodes.add(element.clone()); } if (myProject.isOpen()) { doRead(parentNode); } else { myStartupManager.registerPostStartupActivity( new Runnable() { @Override public void run() { doRead(parentNode); } }); } } private void doRead(@NotNull final Element parentNode) { ApplicationManager.getApplication() .runReadAction( new Runnable() { @Override @SuppressWarnings({"HardCodedStringLiteral"}) public void run() { final Map<String, Breakpoint> nameToBreakpointMap = new THashMap<String, Breakpoint>(); try { final List groups = parentNode.getChildren(); for (final Object group1 : groups) { final Element group = (Element) group1; if (group.getName().equals(RULES_GROUP_NAME)) { continue; } // skip already converted if (group.getAttribute(CONVERTED_PARAM) != null) { continue; } final String categoryName = group.getName(); final Key<Breakpoint> breakpointCategory = BreakpointCategory.lookup(categoryName); final String defaultPolicy = group.getAttributeValue(DEFAULT_SUSPEND_POLICY_ATTRIBUTE_NAME); final boolean conditionEnabled = Boolean.parseBoolean( group.getAttributeValue( DEFAULT_CONDITION_STATE_ATTRIBUTE_NAME, "true")); setBreakpointDefaults( breakpointCategory, new BreakpointDefaults(defaultPolicy, conditionEnabled)); Element anyExceptionBreakpointGroup; if (!AnyExceptionBreakpoint.ANY_EXCEPTION_BREAKPOINT.equals( breakpointCategory)) { // for compatibility with previous format anyExceptionBreakpointGroup = group.getChild( AnyExceptionBreakpoint.ANY_EXCEPTION_BREAKPOINT.toString()); // final BreakpointFactory factory = // BreakpointFactory.getInstance(breakpointCategory); // if (factory != null) { for (Element breakpointNode : group.getChildren("breakpoint")) { // Breakpoint breakpoint = factory.createBreakpoint(myProject, // breakpointNode); Breakpoint breakpoint = createBreakpoint(categoryName, breakpointNode); breakpoint.readExternal(breakpointNode); nameToBreakpointMap.put(breakpoint.getDisplayName(), breakpoint); } // } } else { anyExceptionBreakpointGroup = group; } if (anyExceptionBreakpointGroup != null) { final Element breakpointElement = group.getChild("breakpoint"); if (breakpointElement != null) { XBreakpointManager manager = XDebuggerManager.getInstance(myProject).getBreakpointManager(); JavaExceptionBreakpointType type = (JavaExceptionBreakpointType) XDebuggerUtil.getInstance() .findBreakpointType(JavaExceptionBreakpointType.class); XBreakpoint<JavaExceptionBreakpointProperties> xBreakpoint = manager.getDefaultBreakpoint(type); Breakpoint breakpoint = createJavaBreakpoint(xBreakpoint); breakpoint.readExternal(breakpointElement); addBreakpoint(breakpoint); } } } } catch (InvalidDataException ignored) { } final Element rulesGroup = parentNode.getChild(RULES_GROUP_NAME); if (rulesGroup != null) { final List<Element> rules = rulesGroup.getChildren("rule"); for (Element rule : rules) { // skip already converted if (rule.getAttribute(CONVERTED_PARAM) != null) { continue; } final Element master = rule.getChild(MASTER_BREAKPOINT_TAGNAME); if (master == null) { continue; } final Element slave = rule.getChild(SLAVE_BREAKPOINT_TAGNAME); if (slave == null) { continue; } final Breakpoint masterBreakpoint = nameToBreakpointMap.get(master.getAttributeValue("name")); if (masterBreakpoint == null) { continue; } final Breakpoint slaveBreakpoint = nameToBreakpointMap.get(slave.getAttributeValue("name")); if (slaveBreakpoint == null) { continue; } boolean leaveEnabled = "true".equalsIgnoreCase(rule.getAttributeValue("leaveEnabled")); XDependentBreakpointManager dependentBreakpointManager = ((XBreakpointManagerImpl) getXBreakpointManager()) .getDependentBreakpointManager(); dependentBreakpointManager.setMasterBreakpoint( slaveBreakpoint.myXBreakpoint, masterBreakpoint.myXBreakpoint, leaveEnabled); // addBreakpointRule(new EnableBreakpointRule(BreakpointManager.this, // masterBreakpoint, slaveBreakpoint, leaveEnabled)); } } DebuggerInvocationUtil.invokeLater( myProject, new Runnable() { @Override public void run() { updateBreakpointsUI(); } }); } }); myUIProperties.clear(); final Element props = parentNode.getChild("ui_properties"); if (props != null) { final List children = props.getChildren("property"); for (Object child : children) { Element property = (Element) child; final String name = property.getAttributeValue("name"); final String value = property.getAttributeValue("value"); if (name != null && value != null) { myUIProperties.put(name, value); } } } } private Breakpoint createBreakpoint(String category, Element breakpointNode) throws InvalidDataException { XBreakpoint xBreakpoint = null; if (category.equals(LineBreakpoint.CATEGORY.toString())) { xBreakpoint = createXLineBreakpoint(JavaLineBreakpointType.class, breakpointNode); } else if (category.equals(MethodBreakpoint.CATEGORY.toString())) { if (breakpointNode.getAttribute("url") != null) { xBreakpoint = createXLineBreakpoint(JavaMethodBreakpointType.class, breakpointNode); } else { xBreakpoint = createXBreakpoint(JavaWildcardMethodBreakpointType.class, breakpointNode); } } else if (category.equals(FieldBreakpoint.CATEGORY.toString())) { xBreakpoint = createXLineBreakpoint(JavaFieldBreakpointType.class, breakpointNode); } else if (category.equals(ExceptionBreakpoint.CATEGORY.toString())) { xBreakpoint = createXBreakpoint(JavaExceptionBreakpointType.class, breakpointNode); } if (xBreakpoint == null) { throw new IllegalStateException("Unknown breakpoint category " + category); } return myBreakpoints.get(xBreakpoint); } private <B extends XBreakpoint<?>> XBreakpoint createXBreakpoint( Class<? extends XBreakpointType<B, ?>> typeCls, Element breakpointNode) throws InvalidDataException { final XBreakpointType<B, ?> type = XDebuggerUtil.getInstance().findBreakpointType(typeCls); return ApplicationManager.getApplication() .runWriteAction( new Computable<XBreakpoint>() { @Override public XBreakpoint compute() { return XDebuggerManager.getInstance(myProject) .getBreakpointManager() .addBreakpoint((XBreakpointType) type, type.createProperties()); } }); } private <B extends XBreakpoint<?>> XLineBreakpoint createXLineBreakpoint( Class<? extends XBreakpointType<B, ?>> typeCls, Element breakpointNode) throws InvalidDataException { final String url = breakpointNode.getAttributeValue("url"); VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(url); if (vFile == null) { throw new InvalidDataException( DebuggerBundle.message("error.breakpoint.file.not.found", url)); } final Document doc = FileDocumentManager.getInstance().getDocument(vFile); if (doc == null) { throw new InvalidDataException( DebuggerBundle.message("error.cannot.load.breakpoint.file", url)); } final int line; try { //noinspection HardCodedStringLiteral line = Integer.parseInt(breakpointNode.getAttributeValue("line")); } catch (Exception e) { throw new InvalidDataException("Line number is invalid for breakpoint"); } return addXLineBreakpoint(typeCls, doc, line); } // used in Fabrique public synchronized void addBreakpoint(@NotNull Breakpoint breakpoint) { myBreakpoints.put(breakpoint.myXBreakpoint, breakpoint); myBreakpointsListForIteration = null; breakpoint.updateUI(); RequestManagerImpl.createRequests(breakpoint); myDispatcher.getMulticaster().breakpointsChanged(); if (breakpoint instanceof MethodBreakpoint || breakpoint instanceof WildcardMethodBreakpoint) { XDebugSessionImpl.NOTIFICATION_GROUP .createNotification( "Method breakpoints may dramatically slow down debugging", MessageType.WARNING) .notify(myProject); } } private synchronized void onBreakpointAdded(XBreakpoint xBreakpoint) { Breakpoint breakpoint = createJavaBreakpoint(xBreakpoint); addBreakpoint(breakpoint); } public void removeBreakpoint(@Nullable final Breakpoint breakpoint) { if (breakpoint == null) { return; } ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { getXBreakpointManager().removeBreakpoint(breakpoint.myXBreakpoint); } }); } private synchronized void onBreakpointRemoved(@Nullable final XBreakpoint xBreakpoint) { ApplicationManager.getApplication().assertIsDispatchThread(); if (xBreakpoint == null) { return; } Breakpoint breakpoint = myBreakpoints.remove(xBreakpoint); if (breakpoint != null) { // updateBreakpointRules(breakpoint); myBreakpointsListForIteration = null; // we delete breakpoints inside release, so gutter will not fire events to deleted breakpoints breakpoint.delete(); RequestManagerImpl.deleteRequests(breakpoint); myDispatcher.getMulticaster().breakpointsChanged(); } } public void writeExternal(@NotNull final Element parentNode) { // restore old breakpoints for (Element group : myOriginalBreakpointsNodes) { if (group.getAttribute(CONVERTED_PARAM) == null) { group.setAttribute(CONVERTED_PARAM, "true"); } group.detach(); } parentNode.addContent(myOriginalBreakpointsNodes); // ApplicationManager.getApplication().runReadAction(new Runnable() { // @Override // public void run() { // removeInvalidBreakpoints(); // final Map<Key<? extends Breakpoint>, Element> categoryToElementMap = new THashMap<Key<? // extends Breakpoint>, Element>(); // for (Key<? extends Breakpoint> category : myBreakpointDefaults.keySet()) { // final Element group = getCategoryGroupElement(categoryToElementMap, category, // parentNode); // final BreakpointDefaults defaults = getBreakpointDefaults(category); // group.setAttribute(DEFAULT_SUSPEND_POLICY_ATTRIBUTE_NAME, // String.valueOf(defaults.getSuspendPolicy())); // group.setAttribute(DEFAULT_CONDITION_STATE_ATTRIBUTE_NAME, // String.valueOf(defaults.isConditionEnabled())); // } // // don't store invisible breakpoints // for (Breakpoint breakpoint : getBreakpoints()) { // if (breakpoint.isValid() && // (!(breakpoint instanceof BreakpointWithHighlighter) || // ((BreakpointWithHighlighter)breakpoint).isVisible())) { // writeBreakpoint(getCategoryGroupElement(categoryToElementMap, // breakpoint.getCategory(), parentNode), breakpoint); // } // } // final AnyExceptionBreakpoint anyExceptionBreakpoint = getAnyExceptionBreakpoint(); // final Element group = getCategoryGroupElement(categoryToElementMap, // anyExceptionBreakpoint.getCategory(), parentNode); // writeBreakpoint(group, anyExceptionBreakpoint); // // final Element rules = new Element(RULES_GROUP_NAME); // parentNode.addContent(rules); // //for (EnableBreakpointRule myBreakpointRule : myBreakpointRules) { // // writeRule(myBreakpointRule, rules); // //} // } // }); // // final Element uiProperties = new Element("ui_properties"); // parentNode.addContent(uiProperties); // for (final String name : myUIProperties.keySet()) { // Element property = new Element("property"); // uiProperties.addContent(property); // property.setAttribute("name", name); // property.setAttribute("value", myUIProperties.get(name)); // } } // @SuppressWarnings({"HardCodedStringLiteral"}) // private static void writeRule(@NotNull final EnableBreakpointRule enableBreakpointRule, // @NotNull Element element) { // Element rule = new Element("rule"); // if (enableBreakpointRule.isLeaveEnabled()) { // rule.setAttribute("leaveEnabled", Boolean.toString(true)); // } // element.addContent(rule); // writeRuleBreakpoint(rule, MASTER_BREAKPOINT_TAGNAME, // enableBreakpointRule.getMasterBreakpoint()); // writeRuleBreakpoint(rule, SLAVE_BREAKPOINT_TAGNAME, // enableBreakpointRule.getSlaveBreakpoint()); // } // @SuppressWarnings({"HardCodedStringLiteral"}) private static void writeRuleBreakpoint(@NotNull // final Element element, final String tagName, @NotNull final Breakpoint breakpoint) { // Element master = new Element(tagName); // element.addContent(master); // master.setAttribute("name", breakpoint.getDisplayName()); // } // @SuppressWarnings({"HardCodedStringLiteral"}) // private static void writeBreakpoint(@NotNull final Element group, @NotNull final Breakpoint // breakpoint) { // Element breakpointNode = new Element("breakpoint"); // group.addContent(breakpointNode); // try { // breakpoint.writeExternal(breakpointNode); // } // catch (WriteExternalException e) { // LOG.error(e); // } // } private static <T extends Breakpoint> Element getCategoryGroupElement( @NotNull final Map<Key<? extends Breakpoint>, Element> categoryToElementMap, @NotNull final Key<T> category, @NotNull final Element parentNode) { Element group = categoryToElementMap.get(category); if (group == null) { group = new Element(category.toString()); categoryToElementMap.put(category, group); parentNode.addContent(group); } return group; } private void removeInvalidBreakpoints() { ArrayList<Breakpoint> toDelete = new ArrayList<Breakpoint>(); for (Breakpoint breakpoint : getBreakpoints()) { if (!breakpoint.isValid()) { toDelete.add(breakpoint); } } for (final Breakpoint aToDelete : toDelete) { removeBreakpoint(aToDelete); } } /** * @return breakpoints of one of the category: LINE_BREAKPOINTS, EXCEPTION_BREAKPOINTS, * FIELD_BREAKPOINTS, METHOD_BREAKPOINTS */ public <T extends Breakpoint> Breakpoint[] getBreakpoints(@NotNull final Key<T> category) { ApplicationManager.getApplication().assertIsDispatchThread(); removeInvalidBreakpoints(); final ArrayList<Breakpoint> breakpoints = new ArrayList<Breakpoint>(); for (Breakpoint breakpoint : getBreakpoints()) { if (category.equals(breakpoint.getCategory())) { breakpoints.add(breakpoint); } } return breakpoints.toArray(new Breakpoint[breakpoints.size()]); } @NotNull public synchronized List<Breakpoint> getBreakpoints() { if (myBreakpointsListForIteration == null) { myBreakpointsListForIteration = new ArrayList<Breakpoint>(myBreakpoints.size()); XBreakpoint<?>[] xBreakpoints = ApplicationManager.getApplication() .runReadAction( new Computable<XBreakpoint<?>[]>() { public XBreakpoint<?>[] compute() { return getXBreakpointManager().getAllBreakpoints(); } }); for (XBreakpoint<?> xBreakpoint : xBreakpoints) { if (isJavaType(xBreakpoint)) { Breakpoint breakpoint = myBreakpoints.get(xBreakpoint); if (breakpoint == null) { breakpoint = createJavaBreakpoint(xBreakpoint); myBreakpoints.put(xBreakpoint, breakpoint); } } } myBreakpointsListForIteration.addAll(myBreakpoints.values()); } return myBreakpointsListForIteration; } private boolean isJavaType(XBreakpoint xBreakpoint) { return xBreakpoint.getType() instanceof JavaBreakpointType; } private Breakpoint createJavaBreakpoint(XBreakpoint xBreakpoint) { if (xBreakpoint.getType() instanceof JavaBreakpointType) { return ((JavaBreakpointType) xBreakpoint.getType()) .createJavaBreakpoint(myProject, xBreakpoint); } throw new IllegalStateException("Unsupported breakpoint type:" + xBreakpoint.getType()); } // interaction with RequestManagerImpl public void disableBreakpoints(@NotNull final DebugProcessImpl debugProcess) { final List<Breakpoint> breakpoints = getBreakpoints(); if (!breakpoints.isEmpty()) { final RequestManagerImpl requestManager = debugProcess.getRequestsManager(); for (Breakpoint breakpoint : breakpoints) { breakpoint.markVerified(requestManager.isVerified(breakpoint)); requestManager.deleteRequest(breakpoint); } SwingUtilities.invokeLater( new Runnable() { @Override public void run() { updateBreakpointsUI(); } }); } } public void enableBreakpoints(final DebugProcessImpl debugProcess) { final List<Breakpoint> breakpoints = getBreakpoints(); if (!breakpoints.isEmpty()) { for (Breakpoint breakpoint : breakpoints) { breakpoint.markVerified(false); // clean cached state breakpoint.createRequest(debugProcess); } SwingUtilities.invokeLater( new Runnable() { @Override public void run() { updateBreakpointsUI(); } }); } } public void applyThreadFilter( @NotNull final DebugProcessImpl debugProcess, @Nullable ThreadReference newFilterThread) { final RequestManagerImpl requestManager = debugProcess.getRequestsManager(); final ThreadReference oldFilterThread = requestManager.getFilterThread(); if (Comparing.equal(newFilterThread, oldFilterThread)) { // the filter already added return; } requestManager.setFilterThread(newFilterThread); if (newFilterThread == null || oldFilterThread != null) { final List<Breakpoint> breakpoints = getBreakpoints(); for (Breakpoint breakpoint : breakpoints) { if (LineBreakpoint.CATEGORY.equals(breakpoint.getCategory()) || MethodBreakpoint.CATEGORY.equals(breakpoint.getCategory())) { requestManager.deleteRequest(breakpoint); breakpoint.createRequest(debugProcess); } } } else { // important! need to add filter to _existing_ requests, otherwise Requestor->Request mapping // will be lost // and debugger trees will not be restored to original state abstract class FilterSetter<T extends EventRequest> { void applyFilter(@NotNull final List<T> requests, final ThreadReference thread) { for (T request : requests) { try { final boolean wasEnabled = request.isEnabled(); if (wasEnabled) { request.disable(); } addFilter(request, thread); if (wasEnabled) { request.enable(); } } catch (InternalException e) { LOG.info(e); } } } protected abstract void addFilter(final T request, final ThreadReference thread); } final EventRequestManager eventRequestManager = requestManager.getVMRequestManager(); new FilterSetter<BreakpointRequest>() { @Override protected void addFilter( @NotNull final BreakpointRequest request, final ThreadReference thread) { request.addThreadFilter(thread); } }.applyFilter(eventRequestManager.breakpointRequests(), newFilterThread); new FilterSetter<MethodEntryRequest>() { @Override protected void addFilter( @NotNull final MethodEntryRequest request, final ThreadReference thread) { request.addThreadFilter(thread); } }.applyFilter(eventRequestManager.methodEntryRequests(), newFilterThread); new FilterSetter<MethodExitRequest>() { @Override protected void addFilter( @NotNull final MethodExitRequest request, final ThreadReference thread) { request.addThreadFilter(thread); } }.applyFilter(eventRequestManager.methodExitRequests(), newFilterThread); } } public void updateAllRequests() { ApplicationManager.getApplication().assertIsDispatchThread(); List<Breakpoint> breakpoints = getBreakpoints(); for (Breakpoint breakpoint : breakpoints) { fireBreakpointChanged(breakpoint); } } public void updateBreakpointsUI() { ApplicationManager.getApplication().assertIsDispatchThread(); for (Breakpoint breakpoint : getBreakpoints()) { breakpoint.updateUI(); } } public void reloadBreakpoints() { ApplicationManager.getApplication().assertIsDispatchThread(); for (Breakpoint breakpoint : getBreakpoints()) { breakpoint.reload(); } } public void addBreakpointManagerListener(@NotNull BreakpointManagerListener listener) { myDispatcher.addListener(listener); } public void removeBreakpointManagerListener(@NotNull BreakpointManagerListener listener) { myDispatcher.removeListener(listener); } private boolean myAllowMulticasting = true; private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); public void fireBreakpointChanged(Breakpoint breakpoint) { breakpoint.reload(); breakpoint.updateUI(); RequestManagerImpl.updateRequests(breakpoint); if (myAllowMulticasting) { // can be invoked from non-AWT thread myAlarm.cancelAllRequests(); final Runnable runnable = new Runnable() { @Override public void run() { myAlarm.addRequest( new Runnable() { @Override public void run() { myDispatcher.getMulticaster().breakpointsChanged(); } }, 100); } }; if (ApplicationManager.getApplication().isDispatchThread()) { runnable.run(); } else { SwingUtilities.invokeLater(runnable); } } } public void setBreakpointEnabled(@NotNull final Breakpoint breakpoint, final boolean enabled) { if (breakpoint.isEnabled() != enabled) { breakpoint.setEnabled(enabled); // fireBreakpointChanged(breakpoint); // breakpoint.updateUI(); } } public void addBreakpointRule(@NotNull EnableBreakpointRule rule) { // rule.init(); // myBreakpointRules.add(rule); } public boolean removeBreakpointRule(@NotNull EnableBreakpointRule rule) { // final boolean removed = myBreakpointRules.remove(rule); // if (removed) { // rule.dispose(); // } // return removed; return false; } public boolean removeBreakpointRule(@NotNull Breakpoint slaveBreakpoint) { // for (final EnableBreakpointRule rule : myBreakpointRules) { // if (slaveBreakpoint.equals(rule.getSlaveBreakpoint())) { // removeBreakpointRule(rule); // return true; // } // } return false; } // private void updateBreakpointRules(@NotNull Breakpoint removedBreakpoint) { // for (Iterator<EnableBreakpointRule> it = myBreakpointRules.iterator(); it.hasNext();) { // final EnableBreakpointRule rule = it.next(); // if (removedBreakpoint.equals(rule.getMasterBreakpoint()) || // removedBreakpoint.equals(rule.getSlaveBreakpoint())) { // it.remove(); // } // } // } // copied from XDebugSessionImpl processDependencies public void processBreakpointHit(@NotNull final Breakpoint breakpoint) { XDependentBreakpointManager dependentBreakpointManager = ((XBreakpointManagerImpl) getXBreakpointManager()).getDependentBreakpointManager(); XBreakpoint xBreakpoint = breakpoint.myXBreakpoint; if (!dependentBreakpointManager.isMasterOrSlave(xBreakpoint)) { return; } List<XBreakpoint<?>> breakpoints = dependentBreakpointManager.getSlaveBreakpoints(xBreakpoint); for (final XBreakpoint<?> slaveBreakpoint : breakpoints) { DebuggerInvocationUtil.invokeLater( myProject, new Runnable() { @Override public void run() { slaveBreakpoint.setEnabled(true); } }); } if (dependentBreakpointManager.getMasterBreakpoint(xBreakpoint) != null && !dependentBreakpointManager.isLeaveEnabled(xBreakpoint)) { DebuggerInvocationUtil.invokeLater( myProject, new Runnable() { @Override public void run() { breakpoint.setEnabled(false); } }); // myDebuggerManager.getBreakpointManager().getLineBreakpointManager().queueBreakpointUpdate(breakpoint); } } public void setInitialBreakpointsState() { // myAllowMulticasting = false; // for (final EnableBreakpointRule myBreakpointRule : myBreakpointRules) { // myBreakpointRule.init(); // } // myAllowMulticasting = true; // if (!myBreakpointRules.isEmpty()) { // IJSwingUtilities.invoke(new Runnable() { // @Override // public void run() { // myDispatcher.getMulticaster().breakpointsChanged(); // } // }); // } } @Nullable public Breakpoint findMasterBreakpoint(@NotNull Breakpoint dependentBreakpoint) { XDependentBreakpointManager dependentBreakpointManager = ((XBreakpointManagerImpl) getXBreakpointManager()).getDependentBreakpointManager(); return myBreakpoints.get( dependentBreakpointManager.getMasterBreakpoint(dependentBreakpoint.myXBreakpoint)); } @Nullable public EnableBreakpointRule findBreakpointRule(@NotNull Breakpoint dependentBreakpoint) { // for (final EnableBreakpointRule rule : myBreakpointRules) { // if (dependentBreakpoint.equals(rule.getSlaveBreakpoint())) { // return rule; // } // } return null; } public String getProperty(String name) { return myUIProperties.get(name); } public String setProperty(String name, String value) { return myUIProperties.put(name, value); } }
@State( name = "DebuggerManager", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) public class DebuggerManagerImpl extends DebuggerManagerEx implements PersistentStateComponent<Element> { private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerManagerImpl"); private final Project myProject; private final HashMap<ProcessHandler, DebuggerSession> mySessions = new HashMap<ProcessHandler, DebuggerSession>(); private final BreakpointManager myBreakpointManager; private final List<NameMapper> myNameMappers = ContainerUtil.createLockFreeCopyOnWriteList(); private final List<Function<DebugProcess, PositionManager>> myCustomPositionManagerFactories = new ArrayList<Function<DebugProcess, PositionManager>>(); private final EventDispatcher<DebuggerManagerListener> myDispatcher = EventDispatcher.create(DebuggerManagerListener.class); private final MyDebuggerStateManager myDebuggerStateManager = new MyDebuggerStateManager(); private final DebuggerContextListener mySessionListener = new DebuggerContextListener() { @Override public void changeEvent(DebuggerContextImpl newContext, DebuggerSession.Event event) { final DebuggerSession session = newContext.getDebuggerSession(); if (event == DebuggerSession.Event.PAUSE && myDebuggerStateManager.myDebuggerSession != session) { // if paused in non-active session; switch current session myDebuggerStateManager.setState( newContext, session != null ? session.getState() : DebuggerSession.State.DISPOSED, event, null); return; } if (myDebuggerStateManager.myDebuggerSession == session) { myDebuggerStateManager.fireStateChanged(newContext, event); } if (event == DebuggerSession.Event.ATTACHED) { myDispatcher.getMulticaster().sessionAttached(session); } else if (event == DebuggerSession.Event.DETACHED) { myDispatcher.getMulticaster().sessionDetached(session); } else if (event == DebuggerSession.Event.DISPOSE) { dispose(session); if (myDebuggerStateManager.myDebuggerSession == session) { myDebuggerStateManager.setState( DebuggerContextImpl.EMPTY_CONTEXT, DebuggerSession.State.DISPOSED, DebuggerSession.Event.DISPOSE, null); } } } }; @NonNls private static final String DEBUG_KEY_NAME = "idea.xdebug.key"; @Override public void addClassNameMapper(final NameMapper mapper) { myNameMappers.add(mapper); } @Override public void removeClassNameMapper(final NameMapper mapper) { myNameMappers.remove(mapper); } @Override public String getVMClassQualifiedName(@NotNull final PsiClass aClass) { for (NameMapper nameMapper : myNameMappers) { final String qName = nameMapper.getQualifiedName(aClass); if (qName != null) { return qName; } } return aClass.getQualifiedName(); } @Override public void addDebuggerManagerListener(DebuggerManagerListener listener) { myDispatcher.addListener(listener); } @Override public void removeDebuggerManagerListener(DebuggerManagerListener listener) { myDispatcher.removeListener(listener); } public DebuggerManagerImpl( Project project, StartupManager startupManager, EditorColorsManager colorsManager) { myProject = project; myBreakpointManager = new BreakpointManager(myProject, startupManager, this); if (!project.isDefault()) { colorsManager.addEditorColorsListener( new EditorColorsListener() { @Override public void globalSchemeChange(EditorColorsScheme scheme) { getBreakpointManager().updateBreakpointsUI(); } }, project); } } @Override public DebuggerSession getSession(DebugProcess process) { ApplicationManager.getApplication().assertIsDispatchThread(); for (final DebuggerSession debuggerSession : getSessions()) { if (process == debuggerSession.getProcess()) return debuggerSession; } return null; } @Override public Collection<DebuggerSession> getSessions() { synchronized (mySessions) { final Collection<DebuggerSession> values = mySessions.values(); return values.isEmpty() ? Collections.<DebuggerSession>emptyList() : new ArrayList<DebuggerSession>(values); } } @Override public void disposeComponent() {} @Override public void initComponent() {} @Override public void projectClosed() {} @Override public void projectOpened() { myBreakpointManager.init(); } @Nullable @Override public Element getState() { Element state = new Element("state"); myBreakpointManager.writeExternal(state); return state; } @Override public void loadState(Element state) { myBreakpointManager.readExternal(state); } public void writeExternal(Element element) throws WriteExternalException { myBreakpointManager.writeExternal(element); } @Override @Nullable public DebuggerSession attachVirtualMachine(@NotNull DebugEnvironment environment) throws ExecutionException { ApplicationManager.getApplication().assertIsDispatchThread(); final DebugProcessEvents debugProcess = new DebugProcessEvents(myProject); debugProcess.addDebugProcessListener( new DebugProcessAdapter() { @Override public void processAttached(final DebugProcess process) { process.removeDebugProcessListener(this); for (Function<DebugProcess, PositionManager> factory : myCustomPositionManagerFactories) { final PositionManager positionManager = factory.fun(process); if (positionManager != null) { process.appendPositionManager(positionManager); } } for (PositionManagerFactory factory : Extensions.getExtensions(PositionManagerFactory.EP_NAME, myProject)) { final PositionManager manager = factory.createPositionManager(debugProcess); if (manager != null) { process.appendPositionManager(manager); } } } @Override public void processDetached(final DebugProcess process, final boolean closedByUser) { debugProcess.removeDebugProcessListener(this); } @Override public void attachException( final RunProfileState state, final ExecutionException exception, final RemoteConnection remoteConnection) { debugProcess.removeDebugProcessListener(this); } }); DebuggerSession session = DebuggerSession.create(environment.getSessionName(), debugProcess, environment); ExecutionResult executionResult = session.getProcess().getExecutionResult(); if (executionResult == null) { return null; } session.getContextManager().addListener(mySessionListener); getContextManager() .setState( DebuggerContextUtil.createDebuggerContext( session, session.getContextManager().getContext().getSuspendContext()), session.getState(), DebuggerSession.Event.CONTEXT, null); final ProcessHandler processHandler = executionResult.getProcessHandler(); synchronized (mySessions) { mySessions.put(processHandler, session); } if (!(processHandler instanceof RemoteDebugProcessHandler)) { // add listener only to non-remote process handler: // on Unix systems destroying process does not cause VMDeathEvent to be generated, // so we need to call debugProcess.stop() explicitly for graceful termination. // RemoteProcessHandler on the other hand will call debugProcess.stop() as a part of // destroyProcess() and detachProcess() implementation, // so we shouldn't add the listener to avoid calling stop() twice processHandler.addProcessListener( new ProcessAdapter() { @Override public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) { final DebugProcessImpl debugProcess = getDebugProcess(event.getProcessHandler()); if (debugProcess != null) { // if current thread is a "debugger manager thread", stop will execute synchronously // it is KillableColoredProcessHandler responsibility to terminate VM debugProcess.stop( willBeDestroyed && !(event.getProcessHandler() instanceof KillableColoredProcessHandler)); // wait at most 10 seconds: the problem is that debugProcess.stop() can hang if // there are troubles in the debuggee // if processWillTerminate() is called from AWT thread debugProcess.waitFor() will // block it and the whole app will hang if (!DebuggerManagerThreadImpl.isManagerThread()) { if (SwingUtilities.isEventDispatchThread()) { ProgressManager.getInstance() .runProcessWithProgressSynchronously( new Runnable() { @Override public void run() { ProgressManager.getInstance() .getProgressIndicator() .setIndeterminate(true); debugProcess.waitFor(10000); } }, "Waiting For Debugger Response", false, debugProcess.getProject()); } else { debugProcess.waitFor(10000); } } } } }); } myDispatcher.getMulticaster().sessionCreated(session); return session; } @Override public DebugProcessImpl getDebugProcess(final ProcessHandler processHandler) { synchronized (mySessions) { DebuggerSession session = mySessions.get(processHandler); return session != null ? session.getProcess() : null; } } @SuppressWarnings("UnusedDeclaration") @Nullable public DebuggerSession getDebugSession(final ProcessHandler processHandler) { synchronized (mySessions) { return mySessions.get(processHandler); } } @Override public void addDebugProcessListener( final ProcessHandler processHandler, final DebugProcessListener listener) { DebugProcessImpl debugProcess = getDebugProcess(processHandler); if (debugProcess != null) { debugProcess.addDebugProcessListener(listener); } else { processHandler.addProcessListener( new ProcessAdapter() { @Override public void startNotified(ProcessEvent event) { DebugProcessImpl debugProcess = getDebugProcess(processHandler); if (debugProcess != null) { debugProcess.addDebugProcessListener(listener); } processHandler.removeProcessListener(this); } }); } } @Override public void removeDebugProcessListener( final ProcessHandler processHandler, final DebugProcessListener listener) { DebugProcessImpl debugProcess = getDebugProcess(processHandler); if (debugProcess != null) { debugProcess.removeDebugProcessListener(listener); } else { processHandler.addProcessListener( new ProcessAdapter() { @Override public void startNotified(ProcessEvent event) { DebugProcessImpl debugProcess = getDebugProcess(processHandler); if (debugProcess != null) { debugProcess.removeDebugProcessListener(listener); } processHandler.removeProcessListener(this); } }); } } @Override public boolean isDebuggerManagerThread() { return DebuggerManagerThreadImpl.isManagerThread(); } @Override @NotNull public String getComponentName() { return "DebuggerManager"; } @Override public BreakpointManager getBreakpointManager() { return myBreakpointManager; } @Override public DebuggerContextImpl getContext() { return getContextManager().getContext(); } @Override public DebuggerStateManager getContextManager() { return myDebuggerStateManager; } @Override public void registerPositionManagerFactory( final Function<DebugProcess, PositionManager> factory) { myCustomPositionManagerFactories.add(factory); } @Override public void unregisterPositionManagerFactory( final Function<DebugProcess, PositionManager> factory) { myCustomPositionManagerFactories.remove(factory); } private static boolean hasWhitespace(String string) { int length = string.length(); for (int i = 0; i < length; i++) { if (Character.isWhitespace(string.charAt(i))) { return true; } } return false; } /* Remoting */ private static void checkTargetJPDAInstalled(JavaParameters parameters) throws ExecutionException { final Sdk jdk = parameters.getJdk(); if (jdk == null) { throw new ExecutionException(DebuggerBundle.message("error.jdk.not.specified")); } final JavaSdkVersion version = JavaSdk.getInstance().getVersion(jdk); String versionString = jdk.getVersionString(); if (version == JavaSdkVersion.JDK_1_0 || version == JavaSdkVersion.JDK_1_1) { throw new ExecutionException( DebuggerBundle.message("error.unsupported.jdk.version", versionString)); } if (SystemInfo.isWindows && version == JavaSdkVersion.JDK_1_2) { final VirtualFile homeDirectory = jdk.getHomeDirectory(); if (homeDirectory == null || !homeDirectory.isValid()) { throw new ExecutionException( DebuggerBundle.message("error.invalid.jdk.home", versionString)); } //noinspection HardCodedStringLiteral File dllFile = new File( homeDirectory.getPath().replace('/', File.separatorChar) + File.separator + "bin" + File.separator + "jdwp.dll"); if (!dllFile.exists()) { GetJPDADialog dialog = new GetJPDADialog(); dialog.show(); throw new ExecutionException(DebuggerBundle.message("error.debug.libraries.missing")); } } } /** for Target JDKs versions 1.2.x - 1.3.0 the Classic VM should be used for debugging */ private static boolean shouldForceClassicVM(Sdk jdk) { if (SystemInfo.isMac) { return false; } if (jdk == null) return false; String version = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VERSION); if (version != null) { if (version.compareTo("1.4") >= 0) { return false; } if (version.startsWith("1.2") && SystemInfo.isWindows) { return true; } version += ".0"; if (version.startsWith("1.3.0") && SystemInfo.isWindows) { return true; } if ((version.startsWith("1.3.1_07") || version.startsWith("1.3.1_08")) && SystemInfo.isWindows) { return false; // fixes bug for these JDKs that it cannot start with -classic option } } return DebuggerSettings.getInstance().FORCE_CLASSIC_VM; } @SuppressWarnings({"HardCodedStringLiteral"}) public static RemoteConnection createDebugParameters( final JavaParameters parameters, final boolean debuggerInServerMode, int transport, final String debugPort, boolean checkValidity) throws ExecutionException { if (checkValidity) { checkTargetJPDAInstalled(parameters); } final boolean useSockets = transport == DebuggerSettings.SOCKET_TRANSPORT; String address = ""; if (StringUtil.isEmptyOrSpaces(debugPort)) { try { address = DebuggerUtils.getInstance().findAvailableDebugAddress(useSockets); } catch (ExecutionException e) { if (checkValidity) { throw e; } } } else { address = debugPort; } final TransportServiceWrapper transportService = TransportServiceWrapper.getTransportService(useSockets); final String debugAddress = debuggerInServerMode && useSockets ? "127.0.0.1:" + address : address; String debuggeeRunProperties = "transport=" + transportService.transportId() + ",address=" + debugAddress; if (debuggerInServerMode) { debuggeeRunProperties += ",suspend=y,server=n"; } else { debuggeeRunProperties += ",suspend=n,server=y"; } if (hasWhitespace(debuggeeRunProperties)) { debuggeeRunProperties = "\"" + debuggeeRunProperties + "\""; } final String _debuggeeRunProperties = debuggeeRunProperties; ApplicationManager.getApplication() .runReadAction( new Runnable() { @Override @SuppressWarnings({"HardCodedStringLiteral"}) public void run() { JavaSdkUtil.addRtJar(parameters.getClassPath()); final Sdk jdk = parameters.getJdk(); final boolean forceClassicVM = shouldForceClassicVM(jdk); final boolean forceNoJIT = shouldForceNoJIT(jdk); final String debugKey = System.getProperty(DEBUG_KEY_NAME, "-Xdebug"); final boolean needDebugKey = shouldAddXdebugKey(jdk) || !"-Xdebug".equals(debugKey) /*the key is non-standard*/; if (forceClassicVM || forceNoJIT || needDebugKey || !isJVMTIAvailable(jdk)) { parameters .getVMParametersList() .replaceOrPrepend("-Xrunjdwp:", "-Xrunjdwp:" + _debuggeeRunProperties); } else { // use newer JVMTI if available parameters.getVMParametersList().replaceOrPrepend("-Xrunjdwp:", ""); parameters .getVMParametersList() .replaceOrPrepend( "-agentlib:jdwp=", "-agentlib:jdwp=" + _debuggeeRunProperties); } if (forceNoJIT) { parameters .getVMParametersList() .replaceOrPrepend("-Djava.compiler=", "-Djava.compiler=NONE"); parameters.getVMParametersList().replaceOrPrepend("-Xnoagent", "-Xnoagent"); } if (needDebugKey) { parameters.getVMParametersList().replaceOrPrepend(debugKey, debugKey); } else { // deliberately skip outdated parameter because it can disable full-speed // debugging for some jdk builds // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6272174 parameters.getVMParametersList().replaceOrPrepend("-Xdebug", ""); } parameters .getVMParametersList() .replaceOrPrepend("-classic", forceClassicVM ? "-classic" : ""); } }); return new RemoteConnection(useSockets, "127.0.0.1", address, debuggerInServerMode); } private static boolean shouldForceNoJIT(Sdk jdk) { if (DebuggerSettings.getInstance().DISABLE_JIT) { return true; } if (jdk != null) { final String version = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VERSION); if (version != null && (version.startsWith("1.2") || version.startsWith("1.3"))) { return true; } } return false; } private static boolean shouldAddXdebugKey(Sdk jdk) { if (jdk == null) { return true; // conservative choice } if (DebuggerSettings.getInstance().DISABLE_JIT) { return true; } // if (ApplicationManager.getApplication().isUnitTestMode()) { // need this in unit tests to avoid false alarms when comparing actual output with expected // output // return true; // } final String version = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VERSION); return version == null || // version.startsWith("1.5") || version.startsWith("1.4") || version.startsWith("1.3") || version.startsWith("1.2") || version.startsWith("1.1") || version.startsWith("1.0"); } private static boolean isJVMTIAvailable(Sdk jdk) { if (jdk == null) { return false; // conservative choice } final String version = JdkUtil.getJdkMainAttribute(jdk, Attributes.Name.IMPLEMENTATION_VERSION); if (version == null) { return false; } return !(version.startsWith("1.4") || version.startsWith("1.3") || version.startsWith("1.2") || version.startsWith("1.1") || version.startsWith("1.0")); } public static RemoteConnection createDebugParameters( final JavaParameters parameters, GenericDebuggerRunnerSettings settings, boolean checkValidity) throws ExecutionException { return createDebugParameters( parameters, settings.LOCAL, settings.getTransport(), settings.getDebugPort(), checkValidity); } private static class MyDebuggerStateManager extends DebuggerStateManager { private DebuggerSession myDebuggerSession; @Override public DebuggerContextImpl getContext() { return myDebuggerSession == null ? DebuggerContextImpl.EMPTY_CONTEXT : myDebuggerSession.getContextManager().getContext(); } @Override public void setState( final DebuggerContextImpl context, DebuggerSession.State state, DebuggerSession.Event event, String description) { ApplicationManager.getApplication().assertIsDispatchThread(); myDebuggerSession = context.getDebuggerSession(); if (myDebuggerSession != null) { myDebuggerSession.getContextManager().setState(context, state, event, description); } else { fireStateChanged(context, event); } } } private void dispose(DebuggerSession session) { ProcessHandler processHandler = session.getProcess().getProcessHandler(); synchronized (mySessions) { DebuggerSession removed = mySessions.remove(processHandler); LOG.assertTrue(removed != null); myDispatcher.getMulticaster().sessionRemoved(session); } } }
public SvnLineCommand( Project project, File workingDirectory, @NotNull SvnCommandName commandName) { super(project, workingDirectory, commandName); myLineListeners = EventDispatcher.create(LineProcessEventListener.class); }
/** @author nik */ public class JpsLibraryTableImpl implements LibraryTable, Disposable { private final JpsLibrariesModel myModel; private final EventDispatcher<Listener> myDispatcher = EventDispatcher.create(Listener.class); private final String myTableLevel; private LibraryTablePresentation myPresentation; public JpsLibraryTableImpl(JpsLibraryCollection libraryCollection, String level) { myTableLevel = level; myModel = new JpsLibrariesModel(libraryCollection); } @NotNull @Override public Library[] getLibraries() { return myModel.getLibraries(); } @NotNull @Override public Iterator<Library> getLibraryIterator() { return myModel.getLibraryIterator(); } @Override public Library getLibraryByName(@NotNull String name) { return myModel.getLibraryByName(name); } @Override public void addListener(Listener listener) { myDispatcher.addListener(listener); } @Override public void addListener(Listener listener, Disposable parentDisposable) { myDispatcher.addListener(listener, parentDisposable); } @Override public void removeListener(Listener listener) { myDispatcher.removeListener(listener); } @Override public Library createLibrary() { return createLibrary(null); } @Override public Library createLibrary(@NonNls String name) { final ModifiableModel model = getModifiableModel(); final Library library = model.createLibrary(name); model.commit(); return library; } @Override public void removeLibrary(@NotNull Library library) { final ModifiableModel model = getModifiableModel(); model.removeLibrary(library); model.commit(); } @Override public void dispose() { for (Library library : getLibraries()) { Disposer.dispose(library); } } @Override public ModifiableModel getModifiableModel() { return new JpsLibrariesModel(myModel.myJpsLibraries); } @Override public boolean isEditable() { return true; } @Override public String getTableLevel() { return myTableLevel; } @Override public LibraryTablePresentation getPresentation() { return myPresentation; } private class JpsLibrariesModel implements LibraryTableBase.ModifiableModelEx { private final JpsLibraryCollection myJpsLibraries; private final List<JpsLibraryDelegate> myLibraries; private JpsLibrariesModel(JpsLibraryCollection libraryCollection) { myLibraries = new ArrayList<JpsLibraryDelegate>(); myJpsLibraries = libraryCollection; for (JpsLibrary library : libraryCollection.getLibraries()) { myLibraries.add(new JpsLibraryDelegate(library, JpsLibraryTableImpl.this)); } } @Override public Library createLibrary(String name) { return createLibrary(name, null); } @Override public Library createLibrary(String name, @Nullable PersistentLibraryKind type) { throw new UnsupportedOperationException( "'createLibrary' not implemented in " + getClass().getName()); } @NotNull @Override public Iterator<Library> getLibraryIterator() { return Collections.<Library>unmodifiableList(myLibraries).iterator(); } @Override public void removeLibrary(@NotNull Library library) { throw new UnsupportedOperationException(); } @NotNull @Override public Library[] getLibraries() { return myLibraries.toArray(new Library[myLibraries.size()]); } @Override public Library getLibraryByName(@NotNull String name) { for (JpsLibraryDelegate library : myLibraries) { if (name.equals(library.getName())) { return library; } } return null; } @Override public void commit() { throw new UnsupportedOperationException(); } @Override public boolean isChanged() { return false; } } }
public class FontOptions extends JPanel implements OptionsPanel { private static final FontInfoRenderer RENDERER = new FontInfoRenderer() { @Override protected AntialiasingType getAntialiasingType() { return UISettings.getShadowInstance().EDITOR_AA_TYPE; } }; private final EventDispatcher<ColorAndFontSettingsListener> myDispatcher = EventDispatcher.create(ColorAndFontSettingsListener.class); @NotNull private final ColorAndFontOptions myOptions; @NotNull private final JTextField myEditorFontSizeField = new JTextField(4); @NotNull private final JTextField myLineSpacingField = new JTextField(4); private final FontComboBox myPrimaryCombo = new FontComboBox(); private final JCheckBox myUseSecondaryFontCheckbox = new JCheckBox(ApplicationBundle.message("secondary.font")); private final JCheckBox myEnableLigaturesCheckbox = new JCheckBox(ApplicationBundle.message("use.ligatures")); private final JLabel myLigaturesInfoLinkLabel; private final FontComboBox mySecondaryCombo = new FontComboBox(); @NotNull private final JBCheckBox myOnlyMonospacedCheckBox = new JBCheckBox(ApplicationBundle.message("checkbox.show.only.monospaced.fonts")); private boolean myIsInSchemeChange; public FontOptions(ColorAndFontOptions options) { this(options, ApplicationBundle.message("group.editor.font")); } protected FontOptions(@NotNull ColorAndFontOptions options, final String title) { setLayout(new MigLayout("ins 0, gap 5, flowx")); Insets borderInsets = new Insets( IdeBorderFactory.TITLED_BORDER_TOP_INSET, IdeBorderFactory.TITLED_BORDER_LEFT_INSET, 0, IdeBorderFactory.TITLED_BORDER_RIGHT_INSET); setBorder(IdeBorderFactory.createTitledBorder(title, false, borderInsets)); myOptions = options; add(myOnlyMonospacedCheckBox, "sgx b, sx 2"); add(new JLabel(ApplicationBundle.message("primary.font")), "newline, ax right"); add(myPrimaryCombo, "sgx b"); add(new JLabel(ApplicationBundle.message("editbox.font.size")), "gapleft 20"); add(myEditorFontSizeField); add(new JLabel(ApplicationBundle.message("editbox.line.spacing")), "gapleft 20"); add(myLineSpacingField); add( new JLabel( ApplicationBundle.message("label.fallback.fonts.list.description"), MessageType.INFO.getDefaultIcon(), SwingConstants.LEFT), "newline, sx 5"); add(myUseSecondaryFontCheckbox, "newline, ax right"); add(mySecondaryCombo, "sgx b"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); myEnableLigaturesCheckbox.setBorder(null); panel.add(myEnableLigaturesCheckbox); myLigaturesInfoLinkLabel = new LinkLabel<Void>( ApplicationBundle.message("ligatures.more.info"), null, new LinkListener<Void>() { @Override public void linkSelected(LinkLabel aSource, Void aLinkData) { BrowserUtil.browse( "https://confluence.jetbrains.com/display/IDEADEV/Support+for+Ligatures+in+Editor"); } }); myLigaturesInfoLinkLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); panel.add(myLigaturesInfoLinkLabel); add(panel, "newline, sx 2"); myOnlyMonospacedCheckBox.setBorder(null); myUseSecondaryFontCheckbox.setBorder(null); mySecondaryCombo.setEnabled(false); myOnlyMonospacedCheckBox.setSelected( EditorColorsManager.getInstance().isUseOnlyMonospacedFonts()); myOnlyMonospacedCheckBox.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { EditorColorsManager.getInstance() .setUseOnlyMonospacedFonts(myOnlyMonospacedCheckBox.isSelected()); myPrimaryCombo.setMonospacedOnly(myOnlyMonospacedCheckBox.isSelected()); mySecondaryCombo.setMonospacedOnly(myOnlyMonospacedCheckBox.isSelected()); } }); myPrimaryCombo.setMonospacedOnly(myOnlyMonospacedCheckBox.isSelected()); myPrimaryCombo.setRenderer(RENDERER); mySecondaryCombo.setMonospacedOnly(myOnlyMonospacedCheckBox.isSelected()); mySecondaryCombo.setRenderer(RENDERER); myUseSecondaryFontCheckbox.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { mySecondaryCombo.setEnabled(myUseSecondaryFontCheckbox.isSelected()); syncFontFamilies(); } }); ItemListener itemListener = new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { syncFontFamilies(); } } }; myPrimaryCombo.addItemListener(itemListener); mySecondaryCombo.addItemListener(itemListener); ActionListener actionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { syncFontFamilies(); } }; myPrimaryCombo.addActionListener(actionListener); mySecondaryCombo.addActionListener(actionListener); myEditorFontSizeField .getDocument() .addDocumentListener( new DocumentAdapter() { @Override public void textChanged(DocumentEvent event) { if (myIsInSchemeChange || !SwingUtilities.isEventDispatchThread()) return; String selectedFont = myPrimaryCombo.getFontName(); if (selectedFont != null) { FontPreferences fontPreferences = getFontPreferences(); fontPreferences.register(selectedFont, getFontSizeFromField()); } updateDescription(true); } }); myEditorFontSizeField.addKeyListener( new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() != KeyEvent.VK_UP && e.getKeyCode() != KeyEvent.VK_DOWN) return; boolean up = e.getKeyCode() == KeyEvent.VK_UP; try { int value = Integer.parseInt(myEditorFontSizeField.getText()); value += (up ? 1 : -1); value = Math.min( OptionsConstants.MAX_EDITOR_FONT_SIZE, Math.max(OptionsConstants.MIN_EDITOR_FONT_SIZE, value)); myEditorFontSizeField.setText(String.valueOf(value)); } catch (NumberFormatException ignored) { } } }); myLineSpacingField .getDocument() .addDocumentListener( new DocumentAdapter() { @Override public void textChanged(DocumentEvent event) { if (myIsInSchemeChange) return; float lineSpacing = getLineSpacingFromField(); if (getLineSpacing() != lineSpacing) { setCurrentLineSpacing(lineSpacing); } updateDescription(true); } }); myLineSpacingField.addKeyListener( new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() != KeyEvent.VK_UP && e.getKeyCode() != KeyEvent.VK_DOWN) return; boolean up = e.getKeyCode() == KeyEvent.VK_UP; try { float value = Float.parseFloat(myLineSpacingField.getText()); value += (up ? 1 : -1) * .1F; value = Math.min( OptionsConstants.MAX_EDITOR_LINE_SPACING, Math.max(OptionsConstants.MIN_EDITOR_LINE_SPACING, value)); myLineSpacingField.setText(String.format(Locale.ENGLISH, "%.1f", value)); } catch (NumberFormatException ignored) { } } }); myEnableLigaturesCheckbox.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { getFontPreferences().setUseLigatures(myEnableLigaturesCheckbox.isSelected()); } }); } private int getFontSizeFromField() { try { return Math.min( OptionsConstants.MAX_EDITOR_FONT_SIZE, Math.max( OptionsConstants.MIN_EDITOR_FONT_SIZE, Integer.parseInt(myEditorFontSizeField.getText()))); } catch (NumberFormatException e) { return OptionsConstants.DEFAULT_EDITOR_FONT_SIZE; } } private float getLineSpacingFromField() { try { return Math.min( OptionsConstants.MAX_EDITOR_LINE_SPACING, Math.max( OptionsConstants.MIN_EDITOR_LINE_SPACING, Float.parseFloat(myLineSpacingField.getText()))); } catch (NumberFormatException e) { return OptionsConstants.DEFAULT_EDITOR_LINE_SPACING; } } private void syncFontFamilies() { if (myIsInSchemeChange) { return; } FontPreferences fontPreferences = getFontPreferences(); fontPreferences.clearFonts(); String primaryFontFamily = myPrimaryCombo.getFontName(); String secondaryFontFamily = mySecondaryCombo.isEnabled() ? mySecondaryCombo.getFontName() : null; int fontSize = getFontSizeFromField(); if (primaryFontFamily != null) { if (!FontPreferences.DEFAULT_FONT_NAME.equals(primaryFontFamily)) { fontPreferences.addFontFamily(primaryFontFamily); } fontPreferences.register(primaryFontFamily, JBUI.scale(fontSize)); } if (secondaryFontFamily != null) { if (!FontPreferences.DEFAULT_FONT_NAME.equals(secondaryFontFamily)) { fontPreferences.addFontFamily(secondaryFontFamily); } fontPreferences.register(secondaryFontFamily, JBUI.scale(fontSize)); } updateDescription(true); } public static void showReadOnlyMessage(JComponent parent, final boolean sharedScheme) { if (!sharedScheme) { Messages.showMessageDialog( parent, ApplicationBundle.message("error.readonly.scheme.cannot.be.modified"), ApplicationBundle.message("title.cannot.modify.readonly.scheme"), Messages.getInformationIcon()); } else { Messages.showMessageDialog( parent, ApplicationBundle.message("error.shared.scheme.cannot.be.modified"), ApplicationBundle.message("title.cannot.modify.readonly.scheme"), Messages.getInformationIcon()); } } @Override public void updateOptionsList() { myIsInSchemeChange = true; myLineSpacingField.setText(Float.toString(getLineSpacing())); FontPreferences fontPreferences = getFontPreferences(); List<String> fontFamilies = fontPreferences.getEffectiveFontFamilies(); myPrimaryCombo.setFontName(fontPreferences.getFontFamily()); boolean isThereSecondaryFont = fontFamilies.size() > 1; myUseSecondaryFontCheckbox.setSelected(isThereSecondaryFont); mySecondaryCombo.setFontName(isThereSecondaryFont ? fontFamilies.get(1) : null); myEditorFontSizeField.setText( String.valueOf(fontPreferences.getSize(fontPreferences.getFontFamily()))); boolean readOnly = ColorAndFontOptions.isReadOnly(myOptions.getSelectedScheme()); myPrimaryCombo.setEnabled(!readOnly); mySecondaryCombo.setEnabled(isThereSecondaryFont && !readOnly); myOnlyMonospacedCheckBox.setEnabled(!readOnly); myLineSpacingField.setEnabled(!readOnly); myEditorFontSizeField.setEnabled(!readOnly); myUseSecondaryFontCheckbox.setEnabled(!readOnly); myEnableLigaturesCheckbox.setEnabled(!readOnly); myLigaturesInfoLinkLabel.setEnabled(!readOnly); myEnableLigaturesCheckbox.setSelected(fontPreferences.useLigatures()); myIsInSchemeChange = false; } @NotNull protected FontPreferences getFontPreferences() { return getCurrentScheme().getFontPreferences(); } protected float getLineSpacing() { return getCurrentScheme().getLineSpacing(); } protected void setCurrentLineSpacing(float lineSpacing) { getCurrentScheme().setLineSpacing(lineSpacing); } @Override @Nullable public Runnable showOption(final String option) { return null; } @Override public void applyChangesToScheme() {} @Override public void selectOption(final String typeToSelect) {} protected EditorColorsScheme getCurrentScheme() { return myOptions.getSelectedScheme(); } public boolean updateDescription(boolean modified) { EditorColorsScheme scheme = myOptions.getSelectedScheme(); if (modified && (ColorAndFontOptions.isReadOnly(scheme) || ColorSettingsUtil.isSharedScheme(scheme))) { showReadOnlyMessage(this, ColorSettingsUtil.isSharedScheme(scheme)); return false; } myDispatcher.getMulticaster().fontChanged(); return true; } @Override public void addListener(ColorAndFontSettingsListener listener) { myDispatcher.addListener(listener); } @Override public JPanel getPanel() { return this; } @Override public Set<String> processListOptions() { return new HashSet<String>(); } }
/** @author Eugene Zhuravlev Date: Dec 29, 2003 */ @Deprecated public class ModuleTypeStep extends ModuleWizardStep { private final JPanel myPanel; private final JRadioButton myRbCreateNewModule; private final JRadioButton myRbImportModule; private final FieldPanel myModulePathFieldPanel; private final JList myTypesList; private final JEditorPane myModuleDescriptionPane; private ModuleType myModuleType = StdModuleTypes.JAVA; private Runnable myDoubleClickAction = null; final EventDispatcher<UpdateListener> myEventDispatcher = EventDispatcher.create(UpdateListener.class); private final ButtonGroup myButtonGroup; public static interface UpdateListener extends EventListener { void moduleTypeSelected(ModuleType type); void importModuleOptionSelected(boolean selected); } public ModuleTypeStep(boolean createNewProject) { myPanel = new JPanel(new GridBagLayout()); myPanel.setBorder(BorderFactory.createEtchedBorder()); myModuleDescriptionPane = new JEditorPane(); myModuleDescriptionPane.setContentType(UIUtil.HTML_MIME); myModuleDescriptionPane.addHyperlinkListener( new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { try { BrowserUtil.launchBrowser(e.getURL().toString()); } catch (IllegalThreadStateException ex) { // it's nnot a problem } } } }); myModuleDescriptionPane.setEditable(false); final ModuleType[] allModuleTypes = ModuleTypeManager.getInstance().getRegisteredTypes(); myTypesList = new JList(allModuleTypes); myTypesList.setSelectionModel(new PermanentSingleSelectionModel()); myTypesList.setCellRenderer(new ModuleTypesListCellRenderer()); myTypesList.addListSelectionListener( new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } final ModuleType typeSelected = (ModuleType) myTypesList.getSelectedValue(); myModuleType = typeSelected; //noinspection HardCodedStringLiteral myModuleDescriptionPane.setText( "<html><body><font face=\"verdana\" size=\"-1\">" + typeSelected.getDescription() + "</font></body></html>"); myEventDispatcher.getMulticaster().moduleTypeSelected(typeSelected); } }); myTypesList.setSelectedIndex(0); myTypesList.addMouseListener( new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { if (myDoubleClickAction != null) { if (myTypesList.getSelectedValue() != null) { myDoubleClickAction.run(); } } } } }); myRbCreateNewModule = new JRadioButton(IdeBundle.message("radio.create.new.module"), true); myRbImportModule = new JRadioButton(IdeBundle.message("radio.import.existing.module")); myButtonGroup = new ButtonGroup(); myButtonGroup.add(myRbCreateNewModule); myButtonGroup.add(myRbImportModule); ModulesRbListener listener = new ModulesRbListener(); myRbCreateNewModule.addItemListener(listener); myRbImportModule.addItemListener(listener); JTextField tfModuleFilePath = new JTextField(); final String productName = ApplicationNamesInfo.getInstance().getProductName(); myModulePathFieldPanel = createFieldPanel( tfModuleFilePath, IdeBundle.message("label.path.to.module.file", productName), new BrowseFilesListener( tfModuleFilePath, IdeBundle.message("prompt.select.module.file.to.import", productName), null, new ModuleFileChooserDescriptor())); myModulePathFieldPanel.setEnabled(false); if (createNewProject) { final JLabel moduleTypeLabel = new JLabel(IdeBundle.message("label.select.module.type")); moduleTypeLabel.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD)); myPanel.add(moduleTypeLabel, LABEL_CONSTRAINT); } else { myPanel.add( myRbCreateNewModule, new GridBagConstraints( 0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(8, 10, 8, 10), 0, 0)); } final JLabel descriptionLabel = new JLabel(IdeBundle.message("label.description")); descriptionLabel.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD)); myPanel.add( descriptionLabel, new GridBagConstraints( 1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); final JScrollPane typesListScrollPane = ScrollPaneFactory.createScrollPane(myTypesList); final Dimension preferredSize = calcTypeListPreferredSize(allModuleTypes); typesListScrollPane.setPreferredSize(preferredSize); typesListScrollPane.setMinimumSize(preferredSize); myPanel.add( typesListScrollPane, new GridBagConstraints( 0, GridBagConstraints.RELATIVE, 1, 1, 0.2, (createNewProject ? 1.0 : 0.0), GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, createNewProject ? 10 : 30, 0, 10), 0, 0)); final JScrollPane descriptionScrollPane = ScrollPaneFactory.createScrollPane(myModuleDescriptionPane); descriptionScrollPane.setPreferredSize( new Dimension(preferredSize.width * 3, preferredSize.height)); myPanel.add( descriptionScrollPane, new GridBagConstraints( 1, GridBagConstraints.RELATIVE, 1, 1, 0.8, (createNewProject ? 1.0 : 0.0), GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 10), 0, 0)); if (!createNewProject) { myPanel.add( myRbImportModule, new GridBagConstraints( 0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(16, 10, 0, 10), 0, 0)); myPanel.add( myModulePathFieldPanel, new GridBagConstraints( 0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(8, 30, 0, 10), 0, 0)); } } private Dimension calcTypeListPreferredSize(final ModuleType[] allModuleTypes) { int width = 0; int height = 0; final FontMetrics fontMetrics = myTypesList.getFontMetrics(myTypesList.getFont()); final int fontHeight = fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent(); for (final ModuleType type : allModuleTypes) { final Icon icon = type.getBigIcon(); final int iconHeight = icon != null ? icon.getIconHeight() : 0; final int iconWidth = icon != null ? icon.getIconWidth() : 0; height += Math.max(iconHeight, fontHeight) + 6; width = Math.max(width, iconWidth + fontMetrics.stringWidth(type.getName()) + 10); } return new Dimension(width, height); } public String getHelpId() { return "project.creatingModules.page1"; } public void setModuleListDoubleClickAction(Runnable runnable) { myDoubleClickAction = runnable; } public JComponent getComponent() { return myPanel; } public Icon getIcon() { return ICON; } public boolean validate() { if (myRbImportModule.isSelected()) { final String path = myModulePathFieldPanel.getText().trim(); if (path.length() == 0) { Messages.showErrorDialog( IdeBundle.message( "error.please.specify.path.to.module.file", ApplicationNamesInfo.getInstance().getProductName()), IdeBundle.message("title.module.file.path.not.specified")); myModulePathFieldPanel.getTextField().requestFocus(); return false; } final File file = new File(path); if (!file.exists()) { Messages.showErrorDialog( IdeBundle.message("error.module.file.does.not.exist"), IdeBundle.message("title.module.file.does.not.exist")); myModulePathFieldPanel.getTextField().requestFocus(); return false; } if (!StdFileTypes.IDEA_MODULE.equals( FileTypeManager.getInstance().getFileTypeByFileName(file.getName()))) { Messages.showErrorDialog( IdeBundle.message( "error.module.not.iml", path, ApplicationNamesInfo.getInstance().getProductName()), IdeBundle.message("title.incorrect.file.type")); myModulePathFieldPanel.getTextField().requestFocus(); return false; } } return true; } public boolean isNextButtonEnabled() { return !myRbImportModule.isSelected(); } public boolean isCreateNewModule() { return myRbCreateNewModule.isSelected(); } public boolean isImportExistingModule() { return myRbImportModule.isSelected(); } public String getModuleFilePath() { return myModulePathFieldPanel.getText().trim().replace(File.separatorChar, '/'); } public ModuleType getModuleType() { return myModuleType; } public void addUpdateListener(UpdateListener listener) { myEventDispatcher.addListener(listener); } public void removeUpdateListener(UpdateListener listener) { myEventDispatcher.removeListener(listener); } public void updateDataModel() {} public JComponent getPreferredFocusedComponent() { return myTypesList; } private class ModulesRbListener implements ItemListener { public void itemStateChanged(ItemEvent e) { final JComponent toFocus; ButtonModel selection = myButtonGroup.getSelection(); setControlsEnabled(selection); if (selection == myRbCreateNewModule.getModel()) { toFocus = myTypesList; myEventDispatcher.getMulticaster().importModuleOptionSelected(false); } else if (selection == myRbImportModule.getModel()) { // import existing toFocus = myModulePathFieldPanel.getTextField(); myEventDispatcher.getMulticaster().importModuleOptionSelected(true); } else { toFocus = null; } if (toFocus != null) { SwingUtilities.invokeLater( new Runnable() { public void run() { toFocus.requestFocus(); } }); } } } private void setControlsEnabled(ButtonModel selection) { boolean newModuleEnabled = selection == myRbCreateNewModule.getModel(); myTypesList.setEnabled(newModuleEnabled); myModuleDescriptionPane.setEnabled(newModuleEnabled); boolean importModuleEnabled = selection == myRbImportModule.getModel(); myModulePathFieldPanel.setEnabled(importModuleEnabled); } private static class ModuleFileChooserDescriptor extends FileChooserDescriptor { public ModuleFileChooserDescriptor() { super(true, false, false, false, false, false); setHideIgnored(false); } public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { final boolean isVisible = super.isFileVisible(file, showHiddenFiles); if (!isVisible || file.isDirectory()) { return isVisible; } return StdFileTypes.IDEA_MODULE.equals(FileTypeManager.getInstance().getFileTypeByFile(file)); } } private static class ModuleTypesListCellRenderer extends DefaultListCellRenderer { public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { final Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); final ModuleType moduleType = (ModuleType) value; setIcon(moduleType.getBigIcon()); setDisabledIcon(moduleType.getBigIcon()); setText(moduleType.getName()); return rendererComponent; } } private static class PermanentSingleSelectionModel extends DefaultListSelectionModel { public PermanentSingleSelectionModel() { super.setSelectionMode(SINGLE_SELECTION); } public final void setSelectionMode(int selectionMode) {} public final void removeSelectionInterval(int index0, int index1) {} } }
/** @author Dmitry Avdeev */ @State( name = "TaskManager", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) public class TaskManagerImpl extends TaskManager implements ProjectComponent, PersistentStateComponent<TaskManagerImpl.Config>, ChangeListDecorator { private static final Logger LOG = Logger.getInstance("#com.intellij.tasks.impl.TaskManagerImpl"); private static final DecimalFormat LOCAL_TASK_ID_FORMAT = new DecimalFormat("LOCAL-00000"); public static final Comparator<Task> TASK_UPDATE_COMPARATOR = new Comparator<Task>() { public int compare(Task o1, Task o2) { int i = Comparing.compare(o2.getUpdated(), o1.getUpdated()); return i == 0 ? Comparing.compare(o2.getCreated(), o1.getCreated()) : i; } }; private static final Convertor<Task, String> KEY_CONVERTOR = new Convertor<Task, String>() { @Override public String convert(Task o) { return o.getId(); } }; static final String TASKS_NOTIFICATION_GROUP = "Task Group"; private final Project myProject; private final WorkingContextManager myContextManager; private final Map<String, Task> myIssueCache = Collections.synchronizedMap(new LinkedHashMap<String, Task>()); private final Map<String, LocalTask> myTasks = Collections.synchronizedMap( new LinkedHashMap<String, LocalTask>() { @Override public LocalTask put(String key, LocalTask task) { LocalTask result = super.put(key, task); if (size() > myConfig.taskHistoryLength) { ArrayList<LocalTask> list = new ArrayList<LocalTask>(values()); Collections.sort(list, TASK_UPDATE_COMPARATOR); for (LocalTask oldest : list) { if (!oldest.isDefault()) { remove(oldest); break; } } } return result; } }); @NotNull private LocalTask myActiveTask = createDefaultTask(); private Timer myCacheRefreshTimer; private volatile boolean myUpdating; private final Config myConfig = new Config(); private final ChangeListAdapter myChangeListListener; private final ChangeListManager myChangeListManager; private final List<TaskRepository> myRepositories = new ArrayList<TaskRepository>(); private final EventDispatcher<TaskListener> myDispatcher = EventDispatcher.create(TaskListener.class); private Set<TaskRepository> myBadRepositories = new ConcurrentHashSet<TaskRepository>(); public TaskManagerImpl( Project project, WorkingContextManager contextManager, final ChangeListManager changeListManager) { myProject = project; myContextManager = contextManager; myChangeListManager = changeListManager; myChangeListListener = new ChangeListAdapter() { @Override public void changeListRemoved(ChangeList list) { LocalTask task = getAssociatedTask((LocalChangeList) list); if (task != null) { for (ChangeListInfo info : task.getChangeLists()) { if (Comparing.equal(info.id, ((LocalChangeList) list).getId())) { info.id = ""; } } } } @Override public void defaultListChanged(ChangeList oldDefaultList, ChangeList newDefaultList) { final LocalTask associatedTask = getAssociatedTask((LocalChangeList) newDefaultList); if (associatedTask != null && !getActiveTask().equals(associatedTask)) { ApplicationManager.getApplication() .invokeLater( new Runnable() { public void run() { activateTask(associatedTask, true); } }, myProject.getDisposed()); } } }; } @Override public TaskRepository[] getAllRepositories() { return myRepositories.toArray(new TaskRepository[myRepositories.size()]); } public <T extends TaskRepository> void setRepositories(List<T> repositories) { Set<TaskRepository> set = new HashSet<TaskRepository>(myRepositories); set.removeAll(repositories); myBadRepositories.removeAll(set); // remove all changed reps myIssueCache.clear(); myRepositories.clear(); myRepositories.addAll(repositories); reps: for (T repository : repositories) { if (repository.isShared() && repository.getUrl() != null) { List<TaskProjectConfiguration.SharedServer> servers = getProjectConfiguration().servers; TaskRepositoryType type = repository.getRepositoryType(); for (TaskProjectConfiguration.SharedServer server : servers) { if (repository.getUrl().equals(server.url) && type.getName().equals(server.type)) { continue reps; } } TaskProjectConfiguration.SharedServer server = new TaskProjectConfiguration.SharedServer(); server.type = type.getName(); server.url = repository.getUrl(); servers.add(server); } } } @Override public void removeTask(LocalTask task) { if (task.isDefault()) return; if (myActiveTask.equals(task)) { activateTask(myTasks.get(LocalTaskImpl.DEFAULT_TASK_ID), true); } myTasks.remove(task.getId()); myDispatcher.getMulticaster().taskRemoved(task); myContextManager.removeContext(task); } @Override public void addTaskListener(TaskListener listener) { myDispatcher.addListener(listener); } @Override public void removeTaskListener(TaskListener listener) { myDispatcher.removeListener(listener); } @NotNull @Override public LocalTask getActiveTask() { return myActiveTask; } @Nullable @Override public LocalTask findTask(String id) { return myTasks.get(id); } @NotNull @Override public List<Task> getIssues(@Nullable final String query) { return getIssues(query, true); } @Override public List<Task> getIssues(@Nullable final String query, final boolean forceRequest) { return getIssues(query, 50, 0, forceRequest, true, new EmptyProgressIndicator()); } @Override public List<Task> getIssues( @Nullable String query, int max, long since, boolean forceRequest, final boolean withClosed, @NotNull final ProgressIndicator cancelled) { List<Task> tasks = getIssuesFromRepositories(query, max, since, forceRequest, cancelled); if (tasks == null) return getCachedIssues(withClosed); myIssueCache.putAll(ContainerUtil.newMapFromValues(tasks.iterator(), KEY_CONVERTOR)); return ContainerUtil.filter( tasks, new Condition<Task>() { @Override public boolean value(final Task task) { return withClosed || !task.isClosed(); } }); } @Override public List<Task> getCachedIssues() { return getCachedIssues(true); } @Override public List<Task> getCachedIssues(final boolean withClosed) { return ContainerUtil.filter( myIssueCache.values(), new Condition<Task>() { @Override public boolean value(final Task task) { return withClosed || !task.isClosed(); } }); } @Nullable @Override public Task updateIssue(@NotNull String id) { for (TaskRepository repository : getAllRepositories()) { if (repository.extractId(id) == null) { continue; } try { Task issue = repository.findTask(id); if (issue != null) { LocalTask localTask = findTask(id); if (localTask != null) { localTask.updateFromIssue(issue); return localTask; } return issue; } } catch (Exception e) { LOG.info(e); } } return null; } @Override public List<LocalTask> getLocalTasks() { return getLocalTasks(true); } @Override public List<LocalTask> getLocalTasks(final boolean withClosed) { synchronized (myTasks) { return ContainerUtil.filter( myTasks.values(), new Condition<LocalTask>() { @Override public boolean value(final LocalTask task) { return withClosed || !isLocallyClosed(task); } }); } } @Override public LocalTask addTask(Task issue) { LocalTaskImpl task = issue instanceof LocalTaskImpl ? (LocalTaskImpl) issue : new LocalTaskImpl(issue); addTask(task); return task; } @Override public LocalTaskImpl createLocalTask(@NotNull String summary) { return createTask(LOCAL_TASK_ID_FORMAT.format(myConfig.localTasksCounter++), summary); } private static LocalTaskImpl createTask(@NotNull String id, @NotNull String summary) { LocalTaskImpl task = new LocalTaskImpl(id, summary); Date date = new Date(); task.setCreated(date); task.setUpdated(date); return task; } @Override public LocalTask activateTask(@NotNull final Task origin, boolean clearContext) { LocalTask activeTask = getActiveTask(); if (origin.equals(activeTask)) return activeTask; saveActiveTask(); if (clearContext) { myContextManager.clearContext(); } myContextManager.restoreContext(origin); final LocalTask task = doActivate(origin, true); return restoreVcsContext(task); } private LocalTask restoreVcsContext(LocalTask task) { if (!isVcsEnabled()) return task; List<ChangeListInfo> changeLists = task.getChangeLists(); if (!changeLists.isEmpty()) { ChangeListInfo info = changeLists.get(0); LocalChangeList changeList = myChangeListManager.getChangeList(info.id); if (changeList == null) { changeList = myChangeListManager.addChangeList(info.name, info.comment); info.id = changeList.getId(); } myChangeListManager.setDefaultChangeList(changeList); } List<BranchInfo> branches = task.getBranches(false); VcsTaskHandler.TaskInfo info = fromBranches(branches); VcsTaskHandler[] handlers = VcsTaskHandler.getAllHandlers(myProject); for (VcsTaskHandler handler : handlers) { handler.switchToTask(info); } return task; } private static VcsTaskHandler.TaskInfo fromBranches(List<BranchInfo> branches) { MultiMap<String, String> map = new MultiMap<String, String>(); for (BranchInfo branch : branches) { map.putValue(branch.name, branch.repository); } return new VcsTaskHandler.TaskInfo(map); } public void createBranch(LocalTask task, LocalTask previousActive, String name) { VcsTaskHandler[] handlers = VcsTaskHandler.getAllHandlers(myProject); for (VcsTaskHandler handler : handlers) { VcsTaskHandler.TaskInfo info = handler.getActiveTask(); if (previousActive != null) { addBranches(previousActive, info, false); } addBranches(task, info, true); addBranches(task, handler.startNewTask(name), false); } } public void mergeBranch(LocalTask task) { VcsTaskHandler.TaskInfo original = fromBranches(task.getBranches(true)); VcsTaskHandler.TaskInfo feature = fromBranches(task.getBranches(false)); VcsTaskHandler[] handlers = VcsTaskHandler.getAllHandlers(myProject); for (VcsTaskHandler handler : handlers) { handler.closeTask(feature, original); } } private static void addBranches(LocalTask task, VcsTaskHandler.TaskInfo info, boolean original) { List<BranchInfo> branchInfos = BranchInfo.fromTaskInfo(info, original); for (BranchInfo branchInfo : branchInfos) { task.addBranch(branchInfo); } } private void saveActiveTask() { myContextManager.saveContext(myActiveTask); myActiveTask.setUpdated(new Date()); } private LocalTask doActivate(Task origin, boolean explicitly) { final LocalTaskImpl task = origin instanceof LocalTaskImpl ? (LocalTaskImpl) origin : new LocalTaskImpl(origin); if (explicitly) { task.setUpdated(new Date()); } myActiveTask.setActive(false); task.setActive(true); addTask(task); if (task.isIssue()) { StartupManager.getInstance(myProject) .runWhenProjectIsInitialized( new Runnable() { public void run() { ProgressManager.getInstance() .run( new com.intellij.openapi.progress.Task.Backgroundable( myProject, "Updating " + task.getId()) { public void run(@NotNull ProgressIndicator indicator) { updateIssue(task.getId()); } }); } }); } LocalTask oldActiveTask = myActiveTask; boolean isChanged = !task.equals(oldActiveTask); myActiveTask = task; if (isChanged) { myDispatcher.getMulticaster().taskDeactivated(oldActiveTask); myDispatcher.getMulticaster().taskActivated(task); } return task; } private void addTask(LocalTaskImpl task) { myTasks.put(task.getId(), task); myDispatcher.getMulticaster().taskAdded(task); } @Override public boolean testConnection(final TaskRepository repository) { TestConnectionTask task = new TestConnectionTask("Test connection") { public void run(@NotNull ProgressIndicator indicator) { indicator.setText("Connecting to " + repository.getUrl() + "..."); indicator.setFraction(0); indicator.setIndeterminate(true); try { myConnection = repository.createCancellableConnection(); if (myConnection != null) { Future<Exception> future = ApplicationManager.getApplication().executeOnPooledThread(myConnection); while (true) { try { myException = future.get(100, TimeUnit.MILLISECONDS); return; } catch (TimeoutException ignore) { try { indicator.checkCanceled(); } catch (ProcessCanceledException e) { myException = e; myConnection.cancel(); return; } } catch (Exception e) { myException = e; return; } } } else { try { repository.testConnection(); } catch (Exception e) { LOG.info(e); myException = e; } } } catch (Exception e) { myException = e; } } }; ProgressManager.getInstance().run(task); Exception e = task.myException; if (e == null) { myBadRepositories.remove(repository); Messages.showMessageDialog( myProject, "Connection is successful", "Connection", Messages.getInformationIcon()); } else if (!(e instanceof ProcessCanceledException)) { String message = e.getMessage(); if (e instanceof UnknownHostException) { message = "Unknown host: " + message; } if (message == null) { LOG.error(e); message = "Unknown error"; } Messages.showErrorDialog(myProject, StringUtil.capitalize(message), "Error"); } return e == null; } @SuppressWarnings({"unchecked"}) @NotNull public Config getState() { myConfig.tasks = ContainerUtil.map( myTasks.values(), new Function<Task, LocalTaskImpl>() { public LocalTaskImpl fun(Task task) { return new LocalTaskImpl(task); } }); myConfig.servers = XmlSerializer.serialize(getAllRepositories()); return myConfig; } @SuppressWarnings({"unchecked"}) public void loadState(Config config) { XmlSerializerUtil.copyBean(config, myConfig); myTasks.clear(); for (LocalTaskImpl task : config.tasks) { addTask(task); } myRepositories.clear(); Element element = config.servers; List<TaskRepository> repositories = loadRepositories(element); myRepositories.addAll(repositories); } public static ArrayList<TaskRepository> loadRepositories(Element element) { ArrayList<TaskRepository> repositories = new ArrayList<TaskRepository>(); for (TaskRepositoryType repositoryType : TaskRepositoryType.getRepositoryTypes()) { for (Object o : element.getChildren()) { if (((Element) o).getName().equals(repositoryType.getName())) { try { @SuppressWarnings({"unchecked"}) TaskRepository repository = (TaskRepository) XmlSerializer.deserialize((Element) o, repositoryType.getRepositoryClass()); if (repository != null) { repository.setRepositoryType(repositoryType); repositories.add(repository); } } catch (XmlSerializationException e) { // ignore LOG.error(e.getMessage()); } } } } return repositories; } public void projectOpened() { TaskProjectConfiguration projectConfiguration = getProjectConfiguration(); servers: for (TaskProjectConfiguration.SharedServer server : projectConfiguration.servers) { if (server.type == null || server.url == null) { continue; } for (TaskRepositoryType<?> repositoryType : TaskRepositoryType.getRepositoryTypes()) { if (repositoryType.getName().equals(server.type)) { for (TaskRepository repository : myRepositories) { if (!repositoryType.equals(repository.getRepositoryType())) { continue; } if (server.url.equals(repository.getUrl())) { continue servers; } } TaskRepository repository = repositoryType.createRepository(); repository.setUrl(server.url); myRepositories.add(repository); } } } myContextManager.pack(200, 50); // make sure the task is associated with default changelist LocalTask defaultTask = findTask(LocalTaskImpl.DEFAULT_TASK_ID); LocalChangeList defaultList = myChangeListManager.findChangeList(LocalChangeList.DEFAULT_NAME); if (defaultList != null && defaultTask != null) { ChangeListInfo listInfo = new ChangeListInfo(defaultList); if (!defaultTask.getChangeLists().contains(listInfo)) { defaultTask.addChangelist(listInfo); } } // remove already not existing changelists from tasks changelists for (LocalTask localTask : getLocalTasks()) { for (Iterator<ChangeListInfo> iterator = localTask.getChangeLists().iterator(); iterator.hasNext(); ) { final ChangeListInfo changeListInfo = iterator.next(); if (myChangeListManager.getChangeList(changeListInfo.id) == null) { iterator.remove(); } } } myChangeListManager.addChangeListListener(myChangeListListener); } private TaskProjectConfiguration getProjectConfiguration() { return ServiceManager.getService(myProject, TaskProjectConfiguration.class); } public void projectClosed() {} @NotNull public String getComponentName() { return "Task Manager"; } public void initComponent() { if (!ApplicationManager.getApplication().isUnitTestMode()) { myCacheRefreshTimer = UIUtil.createNamedTimer( "TaskManager refresh", myConfig.updateInterval * 60 * 1000, new ActionListener() { public void actionPerformed(ActionEvent e) { if (myConfig.updateEnabled && !myUpdating) { updateIssues(null); } } }); myCacheRefreshTimer.setInitialDelay(0); StartupManager.getInstance(myProject) .registerPostStartupActivity( new Runnable() { public void run() { myCacheRefreshTimer.start(); } }); } // make sure that the default task is exist LocalTask defaultTask = findTask(LocalTaskImpl.DEFAULT_TASK_ID); if (defaultTask == null) { defaultTask = createDefaultTask(); addTask(defaultTask); } // search for active task LocalTask activeTask = null; final List<LocalTask> tasks = getLocalTasks(); Collections.sort(tasks, TASK_UPDATE_COMPARATOR); for (LocalTask task : tasks) { if (activeTask == null) { if (task.isActive()) { activeTask = task; } } else { task.setActive(false); } } if (activeTask == null) { activeTask = defaultTask; } myActiveTask = activeTask; doActivate(myActiveTask, false); myDispatcher.getMulticaster().taskActivated(myActiveTask); } private static LocalTaskImpl createDefaultTask() { return new LocalTaskImpl(LocalTaskImpl.DEFAULT_TASK_ID, "Default task"); } public void disposeComponent() { if (myCacheRefreshTimer != null) { myCacheRefreshTimer.stop(); } myChangeListManager.removeChangeListListener(myChangeListListener); } public void updateIssues(final @Nullable Runnable onComplete) { TaskRepository first = ContainerUtil.find( getAllRepositories(), new Condition<TaskRepository>() { public boolean value(TaskRepository repository) { return repository.isConfigured(); } }); if (first == null) { myIssueCache.clear(); if (onComplete != null) { onComplete.run(); } return; } myUpdating = true; if (ApplicationManager.getApplication().isUnitTestMode()) { doUpdate(onComplete); } else { ApplicationManager.getApplication() .executeOnPooledThread( new Runnable() { public void run() { doUpdate(onComplete); } }); } } private void doUpdate(@Nullable Runnable onComplete) { try { List<Task> issues = getIssuesFromRepositories( null, myConfig.updateIssuesCount, 0, false, new EmptyProgressIndicator()); if (issues == null) return; synchronized (myIssueCache) { myIssueCache.clear(); for (Task issue : issues) { myIssueCache.put(issue.getId(), issue); } } // update local tasks synchronized (myTasks) { for (Map.Entry<String, LocalTask> entry : myTasks.entrySet()) { Task issue = myIssueCache.get(entry.getKey()); if (issue != null) { entry.getValue().updateFromIssue(issue); } } } } finally { if (onComplete != null) { onComplete.run(); } myUpdating = false; } } @Nullable private List<Task> getIssuesFromRepositories( @Nullable String request, int max, long since, boolean forceRequest, @NotNull final ProgressIndicator cancelled) { List<Task> issues = null; for (final TaskRepository repository : getAllRepositories()) { if (!repository.isConfigured() || (!forceRequest && myBadRepositories.contains(repository))) { continue; } try { Task[] tasks = repository.getIssues(request, max, since, cancelled); myBadRepositories.remove(repository); if (issues == null) issues = new ArrayList<Task>(tasks.length); if (!repository.isSupported(TaskRepository.NATIVE_SEARCH) && request != null) { List<Task> filteredTasks = TaskSearchSupport.filterTasks(request, ContainerUtil.list(tasks)); ContainerUtil.addAll(issues, filteredTasks); } else { ContainerUtil.addAll(issues, tasks); } } catch (ProcessCanceledException ignored) { // OK } catch (Exception e) { String reason = ""; // Fix to IDEA-111810 if (e.getClass() == Exception.class) { // probably contains some message meaningful to end-user reason = e.getMessage(); } //noinspection InstanceofCatchParameter if (e instanceof SocketTimeoutException) { LOG.warn("Socket timeout from " + repository); } else { LOG.warn("Cannot connect to " + repository, e); } myBadRepositories.add(repository); if (forceRequest) { notifyAboutConnectionFailure(repository, reason); } } } return issues; } private void notifyAboutConnectionFailure(final TaskRepository repository, String details) { Notifications.Bus.register(TASKS_NOTIFICATION_GROUP, NotificationDisplayType.BALLOON); String content = "<p><a href=\"\">Configure server...</a></p>"; if (!StringUtil.isEmpty(details)) { content = "<p>" + details + "</p>" + content; } Notifications.Bus.notify( new Notification( TASKS_NOTIFICATION_GROUP, "Cannot connect to " + repository.getUrl(), content, NotificationType.WARNING, new NotificationListener() { public void hyperlinkUpdate( @NotNull Notification notification, @NotNull HyperlinkEvent event) { TaskRepositoriesConfigurable configurable = new TaskRepositoriesConfigurable(myProject); ShowSettingsUtil.getInstance().editConfigurable(myProject, configurable); if (!ArrayUtil.contains(repository, getAllRepositories())) { notification.expire(); } } }), myProject); } @Override public boolean isVcsEnabled() { return ProjectLevelVcsManager.getInstance(myProject).getAllActiveVcss().length > 0; } @Override public AbstractVcs getActiveVcs() { AbstractVcs[] vcss = ProjectLevelVcsManager.getInstance(myProject).getAllActiveVcss(); if (vcss.length == 0) return null; for (AbstractVcs vcs : vcss) { if (vcs.getType() == VcsType.distributed) { return vcs; } } return vcss[0]; } @Override public boolean isLocallyClosed(final LocalTask localTask) { if (isVcsEnabled()) { List<ChangeListInfo> lists = localTask.getChangeLists(); if (lists.isEmpty()) return true; for (ChangeListInfo list : lists) { if (StringUtil.isEmpty(list.id)) { return true; } } } return false; } @Nullable @Override public LocalTask getAssociatedTask(LocalChangeList list) { for (LocalTask task : getLocalTasks()) { for (ChangeListInfo changeListInfo : task.getChangeLists()) { if (changeListInfo.id.equals(list.getId())) { return task; } } } return null; } @Override public void trackContext(LocalChangeList changeList) { ChangeListInfo changeListInfo = new ChangeListInfo(changeList); String changeListName = changeList.getName(); LocalTaskImpl task = createLocalTask(changeListName); task.addChangelist(changeListInfo); addTask(task); if (changeList.isDefault()) { activateTask(task, false); } } @Override public void disassociateFromTask(LocalChangeList changeList) { ChangeListInfo changeListInfo = new ChangeListInfo(changeList); for (LocalTask localTask : getLocalTasks()) { if (localTask.getChangeLists().contains(changeListInfo)) { localTask.removeChangelist(changeListInfo); } } } public void decorateChangeList( LocalChangeList changeList, ColoredTreeCellRenderer cellRenderer, boolean selected, boolean expanded, boolean hasFocus) { LocalTask task = getAssociatedTask(changeList); if (task != null && task.isIssue()) { cellRenderer.setIcon(task.getIcon()); } } public void createChangeList(LocalTask task, String name) { String comment = TaskUtil.getChangeListComment(task); createChangeList(task, name, comment); } private void createChangeList(LocalTask task, String name, @Nullable String comment) { LocalChangeList changeList = myChangeListManager.findChangeList(name); if (changeList == null) { changeList = myChangeListManager.addChangeList(name, comment); } else { final LocalTask associatedTask = getAssociatedTask(changeList); if (associatedTask != null) { associatedTask.removeChangelist(new ChangeListInfo(changeList)); } changeList.setComment(comment); } task.addChangelist(new ChangeListInfo(changeList)); myChangeListManager.setDefaultChangeList(changeList); } public String getChangelistName(Task task) { if (task.isIssue() && myConfig.changelistNameFormat != null) { return TaskUtil.formatTask(task, myConfig.changelistNameFormat); } return task.getSummary(); } public String suggestBranchName(Task task) { if (task.isIssue() && StringUtil.isNotEmpty(task.getNumber())) { return task.getId().replace(' ', '-'); } else { String summary = task.getSummary(); List<String> words = StringUtil.getWordsIn(summary); String[] strings = ArrayUtil.toStringArray(words); return StringUtil.join(strings, 0, Math.min(2, strings.length), "-"); } } @TestOnly public ChangeListAdapter getChangeListListener() { return myChangeListListener; } public static class Config { @Property(surroundWithTag = false) @AbstractCollection(surroundWithTag = false, elementTag = "task") public List<LocalTaskImpl> tasks = new ArrayList<LocalTaskImpl>(); public int localTasksCounter = 1; public int taskHistoryLength = 50; public boolean updateEnabled = true; public int updateInterval = 20; public int updateIssuesCount = 100; // create task options public boolean clearContext = true; public boolean createChangelist = true; public boolean createBranch = true; // close task options public boolean closeIssue = true; public boolean commitChanges = true; public boolean mergeBranch = true; public boolean saveContextOnCommit = true; public boolean trackContextForNewChangelist = false; public boolean markAsInProgress = false; public String changelistNameFormat = "{id} {summary}"; public boolean searchClosedTasks = false; @Tag("servers") public Element servers = new Element("servers"); } private abstract class TestConnectionTask extends com.intellij.openapi.progress.Task.Modal { protected Exception myException; @Nullable protected TaskRepository.CancellableConnection myConnection; public TestConnectionTask(String title) { super(TaskManagerImpl.this.myProject, title, true); } @Override public void onCancel() { if (myConnection != null) { myConnection.cancel(); } } } }
/** @author peter */ public final class DomManagerImpl extends DomManager { private static final Key<Object> MOCK = Key.create("MockElement"); static final Key<WeakReference<DomFileElementImpl>> CACHED_FILE_ELEMENT = Key.create("CACHED_FILE_ELEMENT"); static final Key<DomFileDescription> MOCK_DESCRIPTION = Key.create("MockDescription"); static final SemKey<FileDescriptionCachedValueProvider> FILE_DESCRIPTION_KEY = SemKey.createKey("FILE_DESCRIPTION_KEY"); static final SemKey<DomInvocationHandler> DOM_HANDLER_KEY = SemKey.createKey("DOM_HANDLER_KEY"); static final SemKey<IndexedElementInvocationHandler> DOM_INDEXED_HANDLER_KEY = DOM_HANDLER_KEY.subKey("DOM_INDEXED_HANDLER_KEY"); static final SemKey<CollectionElementInvocationHandler> DOM_COLLECTION_HANDLER_KEY = DOM_HANDLER_KEY.subKey("DOM_COLLECTION_HANDLER_KEY"); static final SemKey<CollectionElementInvocationHandler> DOM_CUSTOM_HANDLER_KEY = DOM_HANDLER_KEY.subKey("DOM_CUSTOM_HANDLER_KEY"); static final SemKey<AttributeChildInvocationHandler> DOM_ATTRIBUTE_HANDLER_KEY = DOM_HANDLER_KEY.subKey("DOM_ATTRIBUTE_HANDLER_KEY"); private final EventDispatcher<DomEventListener> myListeners = EventDispatcher.create(DomEventListener.class); private final GenericValueReferenceProvider myGenericValueReferenceProvider = new GenericValueReferenceProvider(); private final Project myProject; private final SemService mySemService; private final ConverterManager myConverterManager; private final DomApplicationComponent myApplicationComponent; private long myModificationCount; private boolean myChanging; public DomManagerImpl( Project project, SemService semService, ConverterManager converterManager, DomApplicationComponent appComponent) { myProject = project; mySemService = semService; myConverterManager = converterManager; myApplicationComponent = appComponent; final PomModel pomModel = PomManager.getModel(project); pomModel.addModelListener( new PomModelListener() { public void modelChanged(PomModelEvent event) { if (myChanging) return; final XmlChangeSet changeSet = (XmlChangeSet) event.getChangeSet(pomModel.getModelAspect(XmlAspect.class)); if (changeSet != null) { for (XmlFile file : changeSet.getChangedFiles()) { DomFileElementImpl<DomElement> element = getCachedFileElement(file); if (element != null) { fireEvent(new DomEvent(element, false)); } } } } public boolean isAspectChangeInteresting(PomModelAspect aspect) { return aspect instanceof XmlAspect; } }, project); Runnable setupVfsListeners = new Runnable() { public void run() { final VirtualFileAdapter listener = new VirtualFileAdapter() { private final List<DomEvent> myDeletionEvents = new SmartList<DomEvent>(); public void contentsChanged(VirtualFileEvent event) { if (event.isFromSave()) return; processVfsChange(event.getFile()); } public void fileCreated(VirtualFileEvent event) { processVfsChange(event.getFile()); } public void beforeFileDeletion(final VirtualFileEvent event) { if (!myProject.isDisposed()) { beforeFileDeletion(event.getFile()); } } private void beforeFileDeletion(final VirtualFile file) { if (file.isDirectory() && file instanceof NewVirtualFile) { for (final VirtualFile child : ((NewVirtualFile) file).getCachedChildren()) { beforeFileDeletion(child); } return; } if (file.isValid() && StdFileTypes.XML.equals(file.getFileType())) { final PsiFile psiFile = getCachedPsiFile(file); if (psiFile instanceof XmlFile) { Collections.addAll( myDeletionEvents, recomputeFileElement((XmlFile) psiFile)); } } } public void fileDeleted(VirtualFileEvent event) { if (!myDeletionEvents.isEmpty()) { if (!myProject.isDisposed()) { for (DomEvent domEvent : myDeletionEvents) { fireEvent(domEvent); } } myDeletionEvents.clear(); } } public void propertyChanged(VirtualFilePropertyEvent event) { final VirtualFile file = event.getFile(); if (!file.isDirectory() && VirtualFile.PROP_NAME.equals(event.getPropertyName())) { processVfsChange(file); } } }; VirtualFileManager.getInstance().addVirtualFileListener(listener, myProject); } }; StartupManager startupManager = StartupManager.getInstance(project); if (!((StartupManagerEx) startupManager).startupActivityPassed()) { startupManager.registerStartupActivity(setupVfsListeners); } else { setupVfsListeners.run(); } } private void processVfsChange(final VirtualFile file) { processFileOrDirectoryChange(file); } public long getPsiModificationCount() { return PsiManager.getInstance(getProject()).getModificationTracker().getModificationCount(); } public <T extends DomInvocationHandler> void cacheHandler( SemKey<T> key, XmlElement element, T handler) { mySemService.setCachedSemElement(key, element, handler); } private void processFileChange(final VirtualFile file) { if (StdFileTypes.XML != file.getFileType()) return; processFileChange(getCachedPsiFile(file)); } private PsiFile getCachedPsiFile(VirtualFile file) { return ((PsiManagerEx) PsiManager.getInstance(myProject)) .getFileManager() .getCachedPsiFile(file); } private void processFileChange(final PsiFile file) { if (file != null && StdFileTypes.XML.equals(file.getFileType()) && file instanceof XmlFile) { for (final DomEvent event : recomputeFileElement((XmlFile) file)) { fireEvent(event); } } } static DomEvent[] recomputeFileElement(final XmlFile file) { final DomFileElementImpl oldElement = getCachedFileElement(file); return oldElement == null ? DomEvent.EMPTY_ARRAY : new DomEvent[] {new DomEvent(oldElement, false)}; } private void processDirectoryChange(final VirtualFile directory) { for (final VirtualFile file : directory.getChildren()) { processFileOrDirectoryChange(file); } } private void processFileOrDirectoryChange(final VirtualFile file) { if (!ProjectFileIndex.SERVICE.getInstance(myProject).isInContent(file)) return; if (!file.isDirectory()) { processFileChange(file); } else { processDirectoryChange(file); } } @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass"}) public static DomManagerImpl getDomManager(Project project) { return (DomManagerImpl) DomManager.getDomManager(project); } public void addDomEventListener(DomEventListener listener, Disposable parentDisposable) { myListeners.addListener(listener, parentDisposable); } public final ConverterManager getConverterManager() { return myConverterManager; } public final void addPsiReferenceFactoryForClass( Class clazz, PsiReferenceFactory psiReferenceFactory) { myGenericValueReferenceProvider.addReferenceProviderForClass(clazz, psiReferenceFactory); } public final ModelMerger createModelMerger() { return new ModelMergerImpl(); } final void fireEvent(DomEvent event) { if (mySemService.isInsideAtomicChange()) return; myModificationCount++; myListeners.getMulticaster().eventOccured(event); } public final DomGenericInfo getGenericInfo(final Type type) { return myApplicationComponent.getStaticGenericInfo(type); } @Nullable public static DomInvocationHandler getDomInvocationHandler(DomElement proxy) { if (proxy instanceof DomFileElement) { return null; } if (proxy instanceof DomInvocationHandler) { return (DomInvocationHandler) proxy; } final InvocationHandler handler = AdvancedProxy.getInvocationHandler(proxy); if (handler instanceof StableInvocationHandler) { final DomElement element = ((StableInvocationHandler<DomElement>) handler).getWrappedElement(); return element == null ? null : getDomInvocationHandler(element); } if (handler instanceof DomInvocationHandler) { return (DomInvocationHandler) handler; } return null; } @NotNull public static DomInvocationHandler getNotNullHandler(DomElement proxy) { DomInvocationHandler handler = getDomInvocationHandler(proxy); if (handler == null) { throw new AssertionError("null handler for " + proxy); } return handler; } public static StableInvocationHandler getStableInvocationHandler(Object proxy) { return (StableInvocationHandler) AdvancedProxy.getInvocationHandler(proxy); } public DomApplicationComponent getApplicationComponent() { return myApplicationComponent; } public final Project getProject() { return myProject; } @NotNull public final <T extends DomElement> DomFileElementImpl<T> getFileElement( final XmlFile file, final Class<T> aClass, String rootTagName) { //noinspection unchecked if (file.getUserData(MOCK_DESCRIPTION) == null) { file.putUserData(MOCK_DESCRIPTION, new MockDomFileDescription<T>(aClass, rootTagName, file)); mySemService.clearCache(); } final DomFileElementImpl<T> fileElement = getFileElement(file); assert fileElement != null; return fileElement; } @SuppressWarnings({"unchecked"}) @NotNull final <T extends DomElement> FileDescriptionCachedValueProvider<T> getOrCreateCachedValueProvider( final XmlFile xmlFile) { return mySemService.getSemElement(FILE_DESCRIPTION_KEY, xmlFile); } public final Set<DomFileDescription> getFileDescriptions(String rootTagName) { return myApplicationComponent.getFileDescriptions(rootTagName); } public final Set<DomFileDescription> getAcceptingOtherRootTagNameDescriptions() { return myApplicationComponent.getAcceptingOtherRootTagNameDescriptions(); } @NotNull @NonNls public final String getComponentName() { return getClass().getName(); } final void runChange(Runnable change) { final boolean b = setChanging(true); try { change.run(); } finally { setChanging(b); } } final boolean setChanging(final boolean changing) { boolean oldChanging = myChanging; if (changing) { assert !oldChanging; } myChanging = changing; return oldChanging; } @Nullable public final <T extends DomElement> DomFileElementImpl<T> getFileElement(XmlFile file) { if (file == null) return null; if (!(file.getFileType() instanceof DomSupportEnabled)) return null; final VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile != null && virtualFile.isDirectory()) return null; return this.<T>getOrCreateCachedValueProvider(file).getFileElement(); } @Nullable static <T extends DomElement> DomFileElementImpl<T> getCachedFileElement(@NotNull XmlFile file) { WeakReference<DomFileElementImpl> ref = file.getUserData(CACHED_FILE_ELEMENT); return ref == null ? null : ref.get(); } @Nullable public final <T extends DomElement> DomFileElementImpl<T> getFileElement( XmlFile file, Class<T> domClass) { final DomFileDescription description = getDomFileDescription(file); if (description != null && myApplicationComponent.assignabilityCache.isAssignable( domClass, description.getRootElementClass())) { return getFileElement(file); } return null; } @Nullable public final DomElement getDomElement(final XmlTag element) { if (myChanging) return null; final DomInvocationHandler handler = getDomHandler(element); return handler != null ? handler.getProxy() : null; } @Nullable public GenericAttributeValue getDomElement(final XmlAttribute attribute) { if (myChanging) return null; final AttributeChildInvocationHandler handler = mySemService.getSemElement(DOM_ATTRIBUTE_HANDLER_KEY, attribute); return handler == null ? null : (GenericAttributeValue) handler.getProxy(); } @Nullable public DomInvocationHandler getDomHandler(final XmlElement tag) { if (tag == null) return null; List<DomInvocationHandler> cached = mySemService.getCachedSemElements(DOM_HANDLER_KEY, tag); if (cached != null && !cached.isEmpty()) { return cached.get(0); } return mySemService.getSemElement(DOM_HANDLER_KEY, tag); } @Nullable public AbstractDomChildrenDescription findChildrenDescription( @NotNull final XmlTag tag, @NotNull final DomElement parent) { return findChildrenDescription(tag, getDomInvocationHandler(parent)); } static AbstractDomChildrenDescription findChildrenDescription( final XmlTag tag, final DomInvocationHandler parent) { final DomGenericInfoEx info = parent.getGenericInfo(); return info.findChildrenDescription( parent, tag.getLocalName(), tag.getNamespace(), false, tag.getName()); } public final boolean isDomFile(@Nullable PsiFile file) { return file instanceof XmlFile && getFileElement((XmlFile) file) != null; } @Nullable public final DomFileDescription<?> getDomFileDescription(PsiElement element) { if (element instanceof XmlElement) { final PsiFile psiFile = element.getContainingFile(); if (psiFile instanceof XmlFile) { return getDomFileDescription((XmlFile) psiFile); } } return null; } public final <T extends DomElement> T createMockElement( final Class<T> aClass, final Module module, final boolean physical) { final XmlFile file = (XmlFile) PsiFileFactory.getInstance(myProject) .createFileFromText("a.xml", StdFileTypes.XML, "", (long) 0, physical); file.putUserData(MOCK_ELEMENT_MODULE, module); file.putUserData(MOCK, new Object()); return getFileElement( file, aClass, "I_sincerely_hope_that_nobody_will_have_such_a_root_tag_name") .getRootElement(); } public final boolean isMockElement(DomElement element) { return DomUtil.getFile(element).getUserData(MOCK) != null; } public final <T extends DomElement> T createStableValue(final Factory<T> provider) { return createStableValue( provider, new Condition<T>() { public boolean value(T t) { return t.isValid(); } }); } public final <T> T createStableValue(final Factory<T> provider, final Condition<T> validator) { final T initial = provider.create(); assert initial != null; final StableInvocationHandler handler = new StableInvocationHandler<T>(initial, provider, validator); final Set<Class> intf = new HashSet<Class>(); ContainerUtil.addAll(intf, initial.getClass().getInterfaces()); intf.add(StableElement.class); //noinspection unchecked return (T) AdvancedProxy.createProxy( initial.getClass().getSuperclass(), intf.toArray(new Class[intf.size()]), handler); } public final <T extends DomElement> void registerFileDescription( final DomFileDescription<T> description, Disposable parentDisposable) { registerFileDescription(description); Disposer.register( parentDisposable, new Disposable() { public void dispose() { getFileDescriptions(description.getRootTagName()).remove(description); getAcceptingOtherRootTagNameDescriptions().remove(description); } }); } public final void registerFileDescription(final DomFileDescription description) { mySemService.clearCache(); myApplicationComponent.registerFileDescription(description); } @NotNull public final DomElement getResolvingScope(GenericDomValue element) { final DomFileDescription description = DomUtil.getFileElement(element).getFileDescription(); return description.getResolveScope(element); } @Nullable public final DomElement getIdentityScope(DomElement element) { final DomFileDescription description = DomUtil.getFileElement(element).getFileDescription(); return description.getIdentityScope(element); } public TypeChooserManager getTypeChooserManager() { return myApplicationComponent.getTypeChooserManager(); } public long getModificationCount() { return myModificationCount + PsiManager.getInstance(myProject) .getModificationTracker() .getOutOfCodeBlockModificationCount(); } public void performAtomicChange(@NotNull Runnable change) { mySemService.performAtomicChange(change); if (!mySemService.isInsideAtomicChange()) { myModificationCount++; } } public SemService getSemService() { return mySemService; } }
/** * @author Anton Katilin * @author Vladimir Kondratyev */ public final class WindowManagerImpl extends WindowManagerEx implements ApplicationComponent, NamedJDOMExternalizable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.WindowManagerImpl"); private static boolean ourAlphaModeLibraryLoaded; @NonNls private static final String FOCUSED_WINDOW_PROPERTY_NAME = "focusedWindow"; @NonNls private static final String X_ATTR = "x"; @NonNls private static final String FRAME_ELEMENT = "frame"; @NonNls private static final String Y_ATTR = "y"; @NonNls private static final String WIDTH_ATTR = "width"; @NonNls private static final String HEIGHT_ATTR = "height"; @NonNls private static final String EXTENDED_STATE_ATTR = "extended-state"; private Boolean myAlphaModeSupported = null; private final EventDispatcher<WindowManagerListener> myEventDispatcher = EventDispatcher.create(WindowManagerListener.class); static { initialize(); } @SuppressWarnings({"HardCodedStringLiteral"}) private static void initialize() { try { System.loadLibrary("jawt"); ourAlphaModeLibraryLoaded = true; } catch (Throwable exc) { ourAlphaModeLibraryLoaded = false; } } /** Union of bounds of all available default screen devices. */ private final Rectangle myScreenBounds; private final CommandProcessor myCommandProcessor; private final WindowWatcher myWindowWatcher; /** That is the default layout. */ private final DesktopLayout myLayout; private final HashMap<Project, IdeFrameImpl> myProject2Frame; private final HashMap<Project, Set<JDialog>> myDialogsToDispose; /** * This members is needed to read frame's bounds from XML. <code>myFrameBounds</code> can be * <code>null</code>. */ private Rectangle myFrameBounds; private int myFrameExtendedState; private final WindowAdapter myActivationListener; private final ApplicationInfoEx myApplicationInfoEx; private final DataManager myDataManager; private final ActionManagerEx myActionManager; private final UISettings myUiSettings; /** * invoked by reflection * * @param dataManager * @param applicationInfoEx * @param actionManager * @param uiSettings */ public WindowManagerImpl( DataManager dataManager, ApplicationInfoEx applicationInfoEx, ActionManagerEx actionManager, UISettings uiSettings, MessageBus bus) { myApplicationInfoEx = applicationInfoEx; myDataManager = dataManager; myActionManager = actionManager; myUiSettings = uiSettings; if (myDataManager instanceof DataManagerImpl) { ((DataManagerImpl) myDataManager).setWindowManager(this); } final Application application = ApplicationManager.getApplication(); if (!application.isUnitTestMode()) { Disposer.register( application, new Disposable() { @Override public void dispose() { disposeRootFrame(); } }); } myCommandProcessor = new CommandProcessor(); myWindowWatcher = new WindowWatcher(); final KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); keyboardFocusManager.addPropertyChangeListener(FOCUSED_WINDOW_PROPERTY_NAME, myWindowWatcher); if (Patches.SUN_BUG_ID_4218084) { keyboardFocusManager.addPropertyChangeListener( FOCUSED_WINDOW_PROPERTY_NAME, new SUN_BUG_ID_4218084_Patch()); } myLayout = new DesktopLayout(); myProject2Frame = new HashMap<Project, IdeFrameImpl>(); myDialogsToDispose = new HashMap<Project, Set<JDialog>>(); myFrameExtendedState = Frame.NORMAL; // Calculate screen bounds. Rectangle screenBounds = new Rectangle(); if (!application.isHeadlessEnvironment()) { final GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); final GraphicsDevice[] devices = env.getScreenDevices(); for (final GraphicsDevice device : devices) { screenBounds = screenBounds.union(device.getDefaultConfiguration().getBounds()); } } myScreenBounds = screenBounds; myActivationListener = new WindowAdapter() { public void windowActivated(WindowEvent e) { Window activeWindow = e.getWindow(); if (activeWindow instanceof IdeFrameImpl) { // must be proceedDialogDisposalQueue(((IdeFrameImpl) activeWindow).getProject()); } } }; bus.connect() .subscribe( AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() { @Override public void appClosing() { // save fullscreen window states if (isFullScreenSupportedInCurrentOS() && GeneralSettings.getInstance().isReopenLastProject()) { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); if (openProjects.length > 0) { WindowManagerEx wm = WindowManagerEx.getInstanceEx(); for (Project project : openProjects) { IdeFrameImpl frame = wm.getFrame(project); if (frame != null) { frame.storeFullScreenStateIfNeeded(); } } } } } }); if (UIUtil.hasLeakingAppleListeners()) { UIUtil.addAwtListener( new AWTEventListener() { @Override public void eventDispatched(AWTEvent event) { if (event.getID() == ContainerEvent.COMPONENT_ADDED) { if (((ContainerEvent) event).getChild() instanceof JViewport) { UIUtil.removeLeakingAppleListeners(); } } } }, AWTEvent.CONTAINER_EVENT_MASK, application); } } @NotNull public IdeFrameImpl[] getAllProjectFrames() { final Collection<IdeFrameImpl> ideFrames = myProject2Frame.values(); return ideFrames.toArray(new IdeFrameImpl[ideFrames.size()]); } @Override public JFrame findVisibleFrame() { IdeFrameImpl[] frames = getAllProjectFrames(); if (frames.length > 0) return frames[0]; return (JFrame) WelcomeFrame.getInstance(); } @Override public void addListener(final WindowManagerListener listener) { myEventDispatcher.addListener(listener); } public void removeListener(final WindowManagerListener listener) { myEventDispatcher.removeListener(listener); } public final Rectangle getScreenBounds() { return myScreenBounds; } @Override public boolean isFullScreen(@NotNull Frame frame) { return frame instanceof IdeFrameImpl && ((IdeFrameImpl) frame).isInFullScreen(); } @Override public Rectangle getScreenBounds(@NotNull Project project) { final GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); final Point onScreen = getFrame(project).getLocationOnScreen(); final GraphicsDevice[] devices = environment.getScreenDevices(); for (final GraphicsDevice device : devices) { final Rectangle bounds = device.getDefaultConfiguration().getBounds(); if (bounds.contains(onScreen)) { return bounds; } } return null; } public final boolean isInsideScreenBounds(final int x, final int y, final int width) { return x >= myScreenBounds.x + 50 - width && y >= myScreenBounds.y - 50 && x <= myScreenBounds.x + myScreenBounds.width - 50 && y <= myScreenBounds.y + myScreenBounds.height - 50; } public final boolean isInsideScreenBounds(final int x, final int y) { return myScreenBounds.contains(x, y); } public final boolean isAlphaModeSupported() { if (myAlphaModeSupported == null) { myAlphaModeSupported = calcAlphaModelSupported(); } return myAlphaModeSupported.booleanValue(); } private static boolean calcAlphaModelSupported() { if (AWTUtilitiesWrapper.isTranslucencyAPISupported()) { return AWTUtilitiesWrapper.isTranslucencySupported(AWTUtilitiesWrapper.TRANSLUCENT); } try { return WindowUtils.isWindowAlphaSupported(); } catch (Throwable e) { return false; } } public final void setAlphaModeRatio(final Window window, final float ratio) { if (!window.isDisplayable() || !window.isShowing()) { throw new IllegalArgumentException( "window must be displayable and showing. window=" + window); } if (ratio < 0.0f || ratio > 1.0f) { throw new IllegalArgumentException("ratio must be in [0..1] range. ratio=" + ratio); } if (!isAlphaModeSupported() || !isAlphaModeEnabled(window)) { return; } setAlphaMode(window, ratio); } private static void setAlphaMode(Window window, float ratio) { try { if (SystemInfo.isMacOSLeopard) { if (window instanceof JWindow) { ((JWindow) window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); } else if (window instanceof JDialog) { ((JDialog) window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); } else if (window instanceof JFrame) { ((JFrame) window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); } } else if (AWTUtilitiesWrapper.isTranslucencySupported(AWTUtilitiesWrapper.TRANSLUCENT)) { AWTUtilitiesWrapper.setWindowOpacity(window, 1.0f - ratio); } else { WindowUtils.setWindowAlpha(window, 1.0f - ratio); } } catch (Throwable e) { LOG.debug(e); } } public void setWindowMask(final Window window, @Nullable final Shape mask) { try { if (AWTUtilitiesWrapper.isTranslucencySupported(AWTUtilitiesWrapper.PERPIXEL_TRANSPARENT)) { AWTUtilitiesWrapper.setWindowShape(window, mask); } else { WindowUtils.setWindowMask(window, mask); } } catch (Throwable e) { LOG.debug(e); } } @Override public void setWindowShadow(Window window, WindowShadowMode mode) { if (window instanceof JWindow) { JRootPane root = ((JWindow) window).getRootPane(); root.putClientProperty( "Window.shadow", mode == WindowShadowMode.DISABLED ? Boolean.FALSE : Boolean.TRUE); root.putClientProperty("Window.style", mode == WindowShadowMode.SMALL ? "small" : null); } } public void resetWindow(final Window window) { try { if (!isAlphaModeSupported()) return; setWindowMask(window, null); setAlphaMode(window, 0f); setWindowShadow(window, WindowShadowMode.NORMAL); } catch (Throwable e) { LOG.debug(e); } } public final boolean isAlphaModeEnabled(final Window window) { if (!window.isDisplayable() || !window.isShowing()) { throw new IllegalArgumentException( "window must be displayable and showing. window=" + window); } return isAlphaModeSupported(); } public final void setAlphaModeEnabled(final Window window, final boolean state) { if (!window.isDisplayable() || !window.isShowing()) { throw new IllegalArgumentException( "window must be displayable and showing. window=" + window); } } public void hideDialog(JDialog dialog, Project project) { if (project == null) { dialog.dispose(); } else { IdeFrameImpl frame = getFrame(project); if (frame.isActive()) { dialog.dispose(); } else { queueForDisposal(dialog, project); dialog.setVisible(false); } } } @Override public void adjustContainerWindow(Component c, Dimension oldSize, Dimension newSize) { if (c == null) return; Window wnd = SwingUtilities.getWindowAncestor(c); if (wnd instanceof JWindow) { JBPopup popup = (JBPopup) ((JWindow) wnd).getRootPane().getClientProperty(JBPopup.KEY); if (popup != null) { if (oldSize.height < newSize.height) { Dimension size = popup.getSize(); size.height += (newSize.height - oldSize.height); popup.setSize(size); popup.moveToFitScreen(); } } } } public final void disposeComponent() {} public final void initComponent() {} public final void doNotSuggestAsParent(final Window window) { myWindowWatcher.doNotSuggestAsParent(window); } public final void dispatchComponentEvent(final ComponentEvent e) { myWindowWatcher.dispatchComponentEvent(e); } @Nullable public final Window suggestParentWindow(@Nullable final Project project) { return myWindowWatcher.suggestParentWindow(project); } @Nullable public final StatusBar getStatusBar(final Project project) { if (!myProject2Frame.containsKey(project)) { return null; } final IdeFrameImpl frame = getFrame(project); LOG.assertTrue(frame != null); return frame.getStatusBar(); } @Override public StatusBar getStatusBar(@NotNull Component c) { return getStatusBar(c, null); } @Override public StatusBar getStatusBar(@NotNull Component c, @Nullable Project project) { Component parent = UIUtil.findUltimateParent(c); if (parent instanceof IdeFrame) { return ((IdeFrame) parent).getStatusBar().findChild(c); } IdeFrame frame = findFrameFor(project); if (frame != null) { return frame.getStatusBar().findChild(c); } assert false : "Cannot find status bar for " + c; return null; } public IdeFrame findFrameFor(@Nullable final Project project) { IdeFrame frame = null; if (project != null) { frame = project.isDefault() ? WelcomeFrame.getInstance() : getFrame(project); } else { Container eachParent = getMostRecentFocusedWindow(); while (eachParent != null) { if (eachParent instanceof IdeFrame) { frame = (IdeFrame) eachParent; break; } eachParent = eachParent.getParent(); } if (frame == null) { frame = tryToFindTheOnlyFrame(); } } return frame; } private static IdeFrame tryToFindTheOnlyFrame() { IdeFrame candidate = null; final Frame[] all = Frame.getFrames(); for (Frame each : all) { if (each instanceof IdeFrame) { if (candidate == null) { candidate = (IdeFrame) each; } else { candidate = null; break; } } } return candidate; } public final IdeFrameImpl getFrame(@Nullable final Project project) { // no assert! otherwise WindowWatcher.suggestParentWindow fails for default project // LOG.assertTrue(myProject2Frame.containsKey(project)); return myProject2Frame.get(project); } public IdeFrame getIdeFrame(@Nullable final Project project) { if (project != null) { return getFrame(project); } final Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); final Component parent = UIUtil.findUltimateParent(window); if (parent instanceof IdeFrame) return (IdeFrame) parent; final Frame[] frames = Frame.getFrames(); for (Frame each : frames) { if (each instanceof IdeFrame) { return (IdeFrame) each; } } return null; } public void showFrame() { final IdeFrameImpl frame = new IdeFrameImpl( myApplicationInfoEx, myActionManager, myUiSettings, myDataManager, ApplicationManager.getApplication()); myProject2Frame.put(null, frame); if (myFrameBounds == null || !ScreenUtil.isVisible( myFrameBounds)) { // avoid situations when IdeFrame is out of all screens Rectangle rect = ScreenUtil.getScreenRectangle(0, 0); int yParts = rect.height / 6; int xParts = rect.width / 5; myFrameBounds = new Rectangle(xParts, yParts, xParts * 3, yParts * 4); } frame.setBounds(myFrameBounds); frame.setVisible(true); frame.setExtendedState(myFrameExtendedState); } public final IdeFrameImpl allocateFrame(final Project project) { LOG.assertTrue(!myProject2Frame.containsKey(project)); final IdeFrameImpl frame; if (myProject2Frame.containsKey(null)) { frame = myProject2Frame.get(null); myProject2Frame.remove(null); myProject2Frame.put(project, frame); frame.setProject(project); } else { frame = new IdeFrameImpl( (ApplicationInfoEx) ApplicationInfo.getInstance(), ActionManagerEx.getInstanceEx(), UISettings.getInstance(), DataManager.getInstance(), ApplicationManager.getApplication()); final Rectangle bounds = ProjectFrameBounds.getInstance(project).getBounds(); if (bounds != null) { frame.setBounds(bounds); } else if (myFrameBounds != null) { frame.setBounds(myFrameBounds); } frame.setExtendedState(myFrameExtendedState); frame.setProject(project); myProject2Frame.put(project, frame); frame.setVisible(true); } frame.addWindowListener(myActivationListener); myEventDispatcher.getMulticaster().frameCreated(frame); return frame; } private void proceedDialogDisposalQueue(Project project) { Set<JDialog> dialogs = myDialogsToDispose.get(project); if (dialogs == null) return; for (JDialog dialog : dialogs) { dialog.dispose(); } myDialogsToDispose.put(project, null); } private void queueForDisposal(JDialog dialog, Project project) { Set<JDialog> dialogs = myDialogsToDispose.get(project); if (dialogs == null) { dialogs = new HashSet<JDialog>(); myDialogsToDispose.put(project, dialogs); } dialogs.add(dialog); } public final void releaseFrame(final IdeFrameImpl frame) { myEventDispatcher.getMulticaster().beforeFrameReleased(frame); final Project project = frame.getProject(); LOG.assertTrue(project != null); frame.removeWindowListener(myActivationListener); proceedDialogDisposalQueue(project); frame.setProject(null); frame.setTitle(null); frame.setFileTitle(null, null); myProject2Frame.remove(project); Disposer.dispose(frame.getStatusBar()); frame.dispose(); } public final void disposeRootFrame() { if (myProject2Frame.size() == 1) { final IdeFrameImpl rootFrame = myProject2Frame.remove(null); if (rootFrame != null) { // disposing last frame if quitting rootFrame.dispose(); } } } public final Window getMostRecentFocusedWindow() { return myWindowWatcher.getFocusedWindow(); } public final Component getFocusedComponent(@NotNull final Window window) { return myWindowWatcher.getFocusedComponent(window); } @Nullable public final Component getFocusedComponent(@Nullable final Project project) { return myWindowWatcher.getFocusedComponent(project); } /** Private part */ public final CommandProcessor getCommandProcessor() { return myCommandProcessor; } public final String getExternalFileName() { return "window.manager"; } public final void readExternal(final Element element) { final Element frameElement = element.getChild(FRAME_ELEMENT); if (frameElement != null) { myFrameBounds = loadFrameBounds(frameElement); try { myFrameExtendedState = Integer.parseInt(frameElement.getAttributeValue(EXTENDED_STATE_ATTR)); if ((myFrameExtendedState & Frame.ICONIFIED) > 0) { myFrameExtendedState = Frame.NORMAL; } } catch (NumberFormatException ignored) { myFrameExtendedState = Frame.NORMAL; } } final Element desktopElement = element.getChild(DesktopLayout.TAG); if (desktopElement != null) { myLayout.readExternal(desktopElement); } } private static Rectangle loadFrameBounds(final Element frameElement) { Rectangle bounds = new Rectangle(); try { bounds.x = Integer.parseInt(frameElement.getAttributeValue(X_ATTR)); } catch (NumberFormatException ignored) { return null; } try { bounds.y = Integer.parseInt(frameElement.getAttributeValue(Y_ATTR)); } catch (NumberFormatException ignored) { return null; } try { bounds.width = Integer.parseInt(frameElement.getAttributeValue(WIDTH_ATTR)); } catch (NumberFormatException ignored) { return null; } try { bounds.height = Integer.parseInt(frameElement.getAttributeValue(HEIGHT_ATTR)); } catch (NumberFormatException ignored) { return null; } return bounds; } public final void writeExternal(final Element element) { // Save frame bounds final Element frameElement = new Element(FRAME_ELEMENT); element.addContent(frameElement); final Project[] projects = ProjectManager.getInstance().getOpenProjects(); final Project project; if (projects.length > 0) { project = projects[projects.length - 1]; } else { project = null; } final IdeFrameImpl frame = getFrame(project); if (frame != null) { int extendedState = frame.getExtendedState(); if (SystemInfo.isMacOSLion && frame.getPeer() instanceof FramePeer) { // frame.state is not updated by jdk so get it directly from peer extendedState = ((FramePeer) frame.getPeer()).getState(); } boolean usePreviousBounds = extendedState == Frame.MAXIMIZED_BOTH || isFullScreenSupportedInCurrentOS() && WindowManagerEx.getInstanceEx().isFullScreen(frame); Rectangle rectangle = usePreviousBounds ? myFrameBounds : frame.getBounds(); if (rectangle == null) { // frame is out of the screen? rectangle = ScreenUtil.getScreenRectangle(0, 0); } frameElement.setAttribute(X_ATTR, Integer.toString(rectangle.x)); frameElement.setAttribute(Y_ATTR, Integer.toString(rectangle.y)); frameElement.setAttribute(WIDTH_ATTR, Integer.toString(rectangle.width)); frameElement.setAttribute(HEIGHT_ATTR, Integer.toString(rectangle.height)); frameElement.setAttribute(EXTENDED_STATE_ATTR, Integer.toString(extendedState)); // Save default layout final Element layoutElement = new Element(DesktopLayout.TAG); element.addContent(layoutElement); myLayout.writeExternal(layoutElement); } } public final DesktopLayout getLayout() { return myLayout; } public final void setLayout(final DesktopLayout layout) { myLayout.copyFrom(layout); } @NotNull public final String getComponentName() { return "WindowManager"; } /** * We cannot clear selected menu path just by changing of focused window. Under Windows LAF * focused window changes sporadically when user clickes on menu item or submenu. The problem is * that all popups under Windows LAF always has native window ancestor. This window isn't * focusable but by mouse click focused window changes in this manner: InitialFocusedWindow->null * null->InitialFocusedWindow To fix this problem we use alarm to accumulate such focus events. */ private static final class SUN_BUG_ID_4218084_Patch implements PropertyChangeListener { private final Alarm myAlarm; private Window myInitialFocusedWindow; private Window myLastFocusedWindow; private final Runnable myClearSelectedPathRunnable; public SUN_BUG_ID_4218084_Patch() { myAlarm = new Alarm(); myClearSelectedPathRunnable = new Runnable() { public void run() { if (myInitialFocusedWindow != myLastFocusedWindow) { MenuSelectionManager.defaultManager().clearSelectedPath(); } } }; } public void propertyChange(final PropertyChangeEvent e) { if (myAlarm.getActiveRequestCount() == 0) { myInitialFocusedWindow = (Window) e.getOldValue(); final MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath(); if (selectedPath.length == 0) { // there is no visible popup return; } Component firstComponent = null; for (final MenuElement menuElement : selectedPath) { final Component component = menuElement.getComponent(); if (component instanceof JMenuBar) { firstComponent = component; break; } else if (component instanceof JPopupMenu) { firstComponent = ((JPopupMenu) component).getInvoker(); break; } } if (firstComponent == null) { return; } final Window window = SwingUtilities.getWindowAncestor(firstComponent); if (window != myInitialFocusedWindow) { // focused window doesn't have popup return; } } myLastFocusedWindow = (Window) e.getNewValue(); myAlarm.cancelAllRequests(); myAlarm.addRequest(myClearSelectedPathRunnable, 150); } } public WindowWatcher getWindowWatcher() { return myWindowWatcher; } public void setFullScreen(IdeFrameImpl frame, boolean fullScreen) { if (!isFullScreenSupportedInCurrentOS() || frame.isInFullScreen() == fullScreen) return; try { if (SystemInfo.isMacOSLion) { frame.getFrameDecorator().toggleFullScreen(fullScreen); return; } if (SystemInfo.isWindows) { GraphicsDevice device = ScreenUtil.getScreenDevice(frame.getBounds()); if (device == null) return; try { frame.getRootPane().putClientProperty(ScreenUtil.DISPOSE_TEMPORARY, Boolean.TRUE); if (fullScreen) { frame.getRootPane().putClientProperty("oldBounds", frame.getBounds()); } frame.dispose(); frame.setUndecorated(fullScreen); } finally { if (fullScreen) { frame.setBounds(device.getDefaultConfiguration().getBounds()); } else { Object o = frame.getRootPane().getClientProperty("oldBounds"); if (o instanceof Rectangle) { frame.setBounds((Rectangle) o); } } frame.setVisible(true); frame.getRootPane().putClientProperty(ScreenUtil.DISPOSE_TEMPORARY, null); } } } finally { frame.storeFullScreenStateIfNeeded(fullScreen); } } }
@SuppressWarnings({"SSBasedInspection"}) public class LaterInvocator { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.application.impl.LaterInvocator"); private static final boolean DEBUG = LOG.isDebugEnabled(); private static final Object LOCK = new Object(); private static final IdeEventQueue ourEventQueue = IdeEventQueue.getInstance(); private static final FrequentEventDetector ourFrequentEventDetector = new FrequentEventDetector(1009, 100); private LaterInvocator() {} private static class RunnableInfo { @NotNull private final Runnable runnable; @NotNull private final ModalityState modalityState; @NotNull private final Condition<?> expired; @NotNull private final ActionCallback callback; public RunnableInfo( @NotNull Runnable runnable, @NotNull ModalityState modalityState, @NotNull Condition<?> expired, @NotNull ActionCallback callback) { this.runnable = runnable; this.modalityState = modalityState; this.expired = expired; this.callback = callback; } @Override @NonNls public String toString() { return "[runnable: " + runnable + "; state=" + modalityState + (expired.value(null) ? "; expired" : "") + "] "; } } private static final List<Object> ourModalEntities = ContainerUtil.createLockFreeCopyOnWriteList(); private static final List<RunnableInfo> ourQueue = new ArrayList<RunnableInfo>(); // protected by LOCK private static volatile int ourQueueSkipCount = 0; // optimization private static final FlushQueue ourFlushQueueRunnable = new FlushQueue(); private static final Stack<AWTEvent> ourEventStack = new Stack<AWTEvent>(); // guarded by RUN_LOCK private static final EventDispatcher<ModalityStateListener> ourModalityStateMulticaster = EventDispatcher.create(ModalityStateListener.class); private static final List<RunnableInfo> ourForcedFlushQueue = new ArrayList<RunnableInfo>(); public static void addModalityStateListener( @NotNull ModalityStateListener listener, @NotNull Disposable parentDisposable) { if (!ourModalityStateMulticaster.getListeners().contains(listener)) { ourModalityStateMulticaster.addListener(listener, parentDisposable); } } @NotNull static ModalityStateEx modalityStateForWindow(@NotNull Window window) { int index = ourModalEntities.indexOf(window); if (index < 0) { Window owner = window.getOwner(); if (owner == null) { return (ModalityStateEx) ApplicationManager.getApplication().getNoneModalityState(); } ModalityStateEx ownerState = modalityStateForWindow(owner); if (window instanceof Dialog && ((Dialog) window).isModal()) { return ownerState.appendEntity(window); } return ownerState; } List<Object> result = new ArrayList<Object>(); for (Object entity : ourModalEntities) { if (entity instanceof Window || entity instanceof ProgressIndicator && ((ProgressIndicator) entity).isModal()) { result.add(entity); } } return new ModalityStateEx(result.toArray()); } @NotNull static ActionCallback invokeLater(@NotNull Runnable runnable, @NotNull Condition<?> expired) { ModalityState modalityState = ModalityState.defaultModalityState(); return invokeLater(runnable, modalityState, expired); } @NotNull static ActionCallback invokeLater( @NotNull Runnable runnable, @NotNull ModalityState modalityState) { return invokeLater(runnable, modalityState, Conditions.FALSE); } @NotNull static ActionCallback invokeLater( @NotNull Runnable runnable, @NotNull ModalityState modalityState, @NotNull Condition<?> expired) { ourFrequentEventDetector.eventHappened(); final ActionCallback callback = new ActionCallback(); RunnableInfo runnableInfo = new RunnableInfo(runnable, modalityState, expired, callback); synchronized (LOCK) { ourQueue.add(runnableInfo); } requestFlush(); return callback; } static void invokeAndWait( @NotNull final Runnable runnable, @NotNull ModalityState modalityState) { LOG.assertTrue(!isDispatchThread()); final Semaphore semaphore = new Semaphore(); semaphore.down(); final Ref<Throwable> exception = Ref.create(); Runnable runnable1 = new Runnable() { @Override public void run() { try { runnable.run(); } catch (Throwable e) { exception.set(e); } finally { semaphore.up(); } } @Override @NonNls public String toString() { return "InvokeAndWait[" + runnable + "]"; } }; invokeLater(runnable1, modalityState); semaphore.waitFor(); if (!exception.isNull()) { throw new RuntimeException(exception.get()); } } public static void enterModal(@NotNull Object modalEntity) { LOG.assertTrue(isDispatchThread(), "enterModal() should be invoked in event-dispatch thread"); if (LOG.isDebugEnabled()) { LOG.debug("enterModal:" + modalEntity); } ourModalityStateMulticaster.getMulticaster().beforeModalityStateChanged(true); ourModalEntities.add(modalEntity); } public static void leaveModal(@NotNull Object modalEntity) { LOG.assertTrue(isDispatchThread(), "leaveModal() should be invoked in event-dispatch thread"); if (LOG.isDebugEnabled()) { LOG.debug("leaveModal:" + modalEntity); } //noinspection StatementWithEmptyBody while (ourFlushQueueRunnable.runNextEvent()) ; ourModalityStateMulticaster.getMulticaster().beforeModalityStateChanged(false); boolean removed = ourModalEntities.remove(modalEntity); LOG.assertTrue(removed, modalEntity); cleanupQueueForModal(modalEntity); ourQueueSkipCount = 0; requestFlush(); } private static void cleanupQueueForModal(@NotNull final Object modalEntity) { synchronized (LOCK) { for (Iterator<RunnableInfo> iterator = ourQueue.iterator(); iterator.hasNext(); ) { RunnableInfo runnableInfo = iterator.next(); if (runnableInfo.modalityState instanceof ModalityStateEx) { ModalityStateEx stateEx = (ModalityStateEx) runnableInfo.modalityState; if (stateEx.contains(modalEntity)) { ourForcedFlushQueue.add(runnableInfo); iterator.remove(); } } } } } @TestOnly static void leaveAllModals() { ourModalEntities.clear(); ourQueueSkipCount = 0; requestFlush(); } @NotNull public static Object[] getCurrentModalEntities() { ApplicationManager.getApplication().assertIsDispatchThread(); // TODO! // LOG.assertTrue(IdeEventQueue.getInstance().isInInputEvent() || isInMyRunnable()); return ArrayUtil.toObjectArray(ourModalEntities); } public static boolean isInModalContext() { LOG.assertTrue(isDispatchThread()); return !ourModalEntities.isEmpty(); } private static boolean isDispatchThread() { return ApplicationManager.getApplication().isDispatchThread(); } private static void requestFlush() { if (FLUSHER_SCHEDULED.compareAndSet(false, true)) { SwingUtilities.invokeLater(ourFlushQueueRunnable); } } /** * There might be some requests in the queue, but ourFlushQueueRunnable might not be scheduled * yet. In these circumstances {@link EventQueue#peekEvent()} default implementation would return * null, and {@link UIUtil#dispatchAllInvocationEvents()} would stop processing events too early * and lead to spurious test failures. * * @see IdeEventQueue#peekEvent() */ public static boolean ensureFlushRequested() { if (getNextEvent(false) != null) { SwingUtilities.invokeLater(ourFlushQueueRunnable); return true; } return false; } @Nullable private static RunnableInfo getNextEvent(boolean remove) { synchronized (LOCK) { if (!ourForcedFlushQueue.isEmpty()) { final RunnableInfo toRun = remove ? ourForcedFlushQueue.remove(0) : ourForcedFlushQueue.get(0); if (!toRun.expired.value(null)) { return toRun; } else { toRun.callback.setDone(); } } ModalityState currentModality; if (ourModalEntities.isEmpty()) { Application application = ApplicationManager.getApplication(); currentModality = application == null ? ModalityState.NON_MODAL : application.getNoneModalityState(); } else { currentModality = new ModalityStateEx(ourModalEntities.toArray()); } while (ourQueueSkipCount < ourQueue.size()) { RunnableInfo info = ourQueue.get(ourQueueSkipCount); if (info.expired.value(null)) { ourQueue.remove(ourQueueSkipCount); info.callback.setDone(); continue; } if (!currentModality.dominates(info.modalityState)) { if (remove) { ourQueue.remove(ourQueueSkipCount); } return info; } ourQueueSkipCount++; } return null; } } private static final AtomicBoolean FLUSHER_SCHEDULED = new AtomicBoolean(false); private static final Object RUN_LOCK = new Object(); private static class FlushQueue implements Runnable { @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private RunnableInfo myLastInfo; @Override public void run() { FLUSHER_SCHEDULED.set(false); if (runNextEvent()) { requestFlush(); } } private boolean runNextEvent() { final RunnableInfo lastInfo = getNextEvent(true); myLastInfo = lastInfo; if (lastInfo != null) { synchronized (RUN_LOCK) { // necessary only because of switching to our own event queue AWTEvent event = ourEventQueue.getTrueCurrentEvent(); ourEventStack.push(event); int stackSize = ourEventStack.size(); try { lastInfo.runnable.run(); lastInfo.callback.setDone(); } catch (ProcessCanceledException ignored) { } catch (Throwable t) { LOG.error(t); } finally { LOG.assertTrue(ourEventStack.size() == stackSize); ourEventStack.pop(); if (!DEBUG) myLastInfo = null; } } } return lastInfo != null; } @Override public String toString() { return "LaterInvocator.FlushQueue" + (myLastInfo == null ? "" : " lastInfo=" + myLastInfo); } } @TestOnly public static List<RunnableInfo> getLaterInvocatorQueue() { synchronized (LOCK) { return ContainerUtil.newArrayList(ourQueue); } } }
public class SModelRootEntry implements ModelRootEntry { private SModelRoot myModelRoot; private EventDispatcher<ModelRootEntryListener> myEventDispatcher = EventDispatcher.create(ModelRootEntryListener.class); @Override public ModelRoot getModelRoot() { return myModelRoot; } @Override public void setModelRoot(ModelRoot modelRoot) { if (!(modelRoot instanceof SModelRoot)) throw new ClassCastException( "Can't convert " + modelRoot.getClass().getCanonicalName() + " to " + SModelRoot.class.getCanonicalName()); myModelRoot = (SModelRoot) modelRoot; } @Override public String getDetailsText() { final StringBuilder messageText = new StringBuilder(); messageText.append("<html>"); messageText.append("Type : ").append(myModelRoot.getType()).append("<br>"); messageText .append("Manager : ") .append( myModelRoot.getModelRoot().getManager() != null ? myModelRoot.getModelRoot().getManager().getClassName() : "Default") .append("<br>"); messageText.append("Path : ").append(myModelRoot.getPath()).append("<br>"); return messageText.toString(); } @Override public boolean isValid() { String path = myModelRoot.getPath(); return (new java.io.File(path != null ? path : "")).exists(); } @Override public ModelRootEntryEditor getEditor() { return new SModelRootEntryEditor(myModelRoot); } @Override public void addModelRootEntryListener(ModelRootEntryListener listener) { myEventDispatcher.addListener(listener); } private class SModelRootEntryEditor implements ModelRootEntryEditor { private JPanel myTreePanel; private ComboBox myComboBox; private SModelRoot myModelRoot; public SModelRootEntryEditor(SModelRoot modelRoot) { myModelRoot = modelRoot; } @Override public JComponent createComponent() { JPanel panel = new JPanel(new GridLayoutManager(2, 1)); List<ModelRootManager> managers = ManagerTableCellEditor.getManagers(); managers.remove(null); final ModelRootManager empty = new ModelRootManager("", "Default"); managers.add(empty); myComboBox = new ComboBox(managers.toArray(), 10); myComboBox.setRenderer( new DefaultListCellRenderer() { @Override public Component getListCellRendererComponent( JList list, Object value, int index, boolean selected, boolean focus) { ModelRootManager manager = ((ModelRootManager) value); String managerName = NameUtil.shortNameFromLongName(manager.getClassName()); return super.getListCellRendererComponent(list, managerName, index, selected, focus); } }); panel.add( myComboBox, new GridConstraints( 0, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null)); myComboBox.setSelectedItem( myModelRoot.getModelRoot().getManager() == null ? empty : myModelRoot.getModelRoot().getManager()); myComboBox.addItemListener( new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { ModelRootManager manager = (ModelRootManager) e.getItem(); myModelRoot.getModelRoot().setManager(manager.equals(empty) ? null : manager); myEventDispatcher.getMulticaster().fireDataChanged(); } } }); myTreePanel = new JPanel(new BorderLayout()); updateTree(); final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTreePanel); panel.add( scrollPane, new GridConstraints( 1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null)); return panel; } private void updateTree() { FileSystemTreeImpl fileSystemTree = new FileSystemTreeImpl( null, new FileChooserDescriptor(true, true, true, true, true, false)); AbstractTreeUi ui = fileSystemTree.getTreeBuilder().getUi(); String path = myModelRoot.getPath() == null ? "" : myModelRoot.getPath(); VirtualFile virtualFile = VirtualFileManager.getInstance() .findFileByUrl(VirtualFileManager.constructUrl("file", path)); if (myModelRoot.getModule() != null && (virtualFile == null || path.isEmpty())) { if (myModelRoot.getModule() instanceof AbstractModule) { virtualFile = VirtualFileManager.getInstance() .findFileByUrl( VirtualFileManager.constructUrl( "file", ((AbstractModule) myModelRoot.getModule()) .getModuleSourceDir() .getPath())); } } if (virtualFile != null) fileSystemTree.select(virtualFile, null); fileSystemTree.addListener( new Listener() { @Override public void selectionChanged(List<VirtualFile> selection) { if (selection.size() > 0) { myModelRoot.setPath(FileUtil.getCanonicalPath(selection.get(0).getPath())); myEventDispatcher.getMulticaster().fireDataChanged(); } } }, new Disposable() { @Override public void dispose() {} }); myTreePanel.removeAll(); myTreePanel.add(ui.getTree(), BorderLayout.CENTER); ui.scrollSelectionToVisible(null, true); } } }
/** The handler that is based on per-line processing of the text. */ public class LineHandler extends TextHandler { /** the partial line from stdout stream */ private final StringBuilder myStdoutLine = new StringBuilder(); /** the partial line from stderr stream */ private final StringBuilder myStderrLine = new StringBuilder(); /** Line listeners */ private final EventDispatcher<LineHandlerListener> myLineListeners = EventDispatcher.create(LineHandlerListener.class); /** * A constructor * * @param project a project * @param directory a process directory * @param command a command to execute */ @SuppressWarnings({"WeakerAccess"}) public LineHandler(@NotNull Project project, @NotNull File directory, @NotNull Command command) { super(project, directory, command); } /** * A constructor * * @param project a project * @param vcsRoot a process directory * @param command a command to execute */ public LineHandler( @NotNull final Project project, @NotNull final VirtualFile vcsRoot, @NotNull final Command command) { super(project, vcsRoot, command); } /** {@inheritDoc} */ protected void processTerminated(final int exitCode) { // force newline if (myStdoutLine.length() != 0) { onTextAvailable("\n\r", ProcessOutputTypes.STDOUT); } else if (!isStderrSuppressed() && myStderrLine.length() != 0) { onTextAvailable("\n\r", ProcessOutputTypes.STDERR); } } /** * Add listener * * @param listener a listener to add */ public void addLineListener(LineHandlerListener listener) { super.addListener(listener); myLineListeners.addListener(listener); } /** {@inheritDoc} */ protected void onTextAvailable(final String text, final Key outputType) { Iterator<String> lines = splitText(text).iterator(); if (ProcessOutputTypes.STDOUT == outputType) { notifyLines(outputType, lines, myStdoutLine); } else if (ProcessOutputTypes.STDERR == outputType) { notifyLines(outputType, lines, myStderrLine); } } /** * Notify listeners for each complete line. Note that in the case of stderr, the last line is * saved. * * @param outputType output type * @param lines line iterator * @param lineBuilder a line builder */ private void notifyLines( final Key outputType, final Iterator<String> lines, final StringBuilder lineBuilder) { /* while(lines.hasNext()) { String line = lines.next(); //lineBuilder.append(line); notifyLine(line, outputType); } */ if (!lines.hasNext()) return; if (lineBuilder.length() > 0) { lineBuilder.append(lines.next()); if (lines.hasNext()) { // line is complete final String line = lineBuilder.toString(); notifyLine(line, outputType); lineBuilder.setLength(0); } } while (true) { String line = lines.next(); if (lines.hasNext()) { notifyLine(line, outputType); } else { if (line.length() > 0) { lineBuilder.append(line); } break; } } } /** * Notify single line * * @param line a line to notify * @param outputType output type */ private void notifyLine(final String line, final Key outputType) { String trimmed = trimLineSeparator(line); // if line ends with return, then it is a progress line, ignore it if (myVcs != null && !"\r".equals(line.substring(trimmed.length()))) { if (outputType == ProcessOutputTypes.STDOUT && !isStdoutSuppressed()) { myVcs.showMessages(trimmed); } else if (outputType == ProcessOutputTypes.STDERR && !isStderrSuppressed()) { myVcs.showErrorMessages(trimmed); } } myLineListeners.getMulticaster().onLineAvailable(trimmed, outputType); } /** * Trim line separator from new line if it presents * * @param line a line to process * @return a trimmed line */ private static String trimLineSeparator(String line) { int n = line.length(); if (n == 0) { return line; } char ch = line.charAt(n - 1); if (ch == '\n' || ch == '\r') { n--; } else { return line; } if (n > 0) { char ch2 = line.charAt(n - 1); if ((ch2 == '\n' || ch2 == '\r') && ch2 != ch) { n--; } } return line.substring(0, n); } /** * Split text into lines. New line characters are treated as separators. So if the text starts * with newline, empty string will be the first element, if the text ends with new line, the empty * string will be the last element. The returned lines will be substrings of the text argument. * The new line characters are included into the line text. * * @param text a text to split * @return a list of elements (note that there are always at least one element) */ private static List<String> splitText(String text) { int startLine = 0; int i = 0; int n = text.length(); ArrayList<String> rc = new ArrayList<String>(); while (i < n) { switch (text.charAt(i)) { case '\n': i++; if (i < n && text.charAt(i) == '\r') { i++; } rc.add(text.substring(startLine, i)); startLine = i; break; case '\r': i++; if (i < n && text.charAt(i) == '\n') { i++; } rc.add(text.substring(startLine, i)); startLine = i; break; default: i++; } } if (startLine == text.length()) { // still add empty line or previous line wouldn't be treated as completed rc.add(""); } else { rc.add(text.substring(startLine, i)); } return rc; } }
@SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"}) public class ApplicationImpl extends ComponentManagerImpl implements ApplicationEx { private static final Logger LOG = Logger.getInstance("#com.intellij.application.impl.ApplicationImpl"); private final ModalityState MODALITY_STATE_NONE = ModalityState.NON_MODAL; private final ModalityInvokator myInvokator = new ModalityInvokatorImpl(); private final EventDispatcher<ApplicationListener> myDispatcher = EventDispatcher.create(ApplicationListener.class); private boolean myTestModeFlag; private final boolean myHeadlessMode; private final boolean myCommandLineMode; private final boolean myIsInternal; private final String myName; private final ReentrantWriterPreferenceReadWriteLock myActionsLock = new ReentrantWriterPreferenceReadWriteLock(); private final Stack<Runnable> myWriteActionsStack = new Stack<Runnable>(); // accessed from EDT only, no need to sync private volatile Runnable myExceptionalThreadWithReadAccessRunnable; private int myInEditorPaintCounter = 0; private long myStartTime = 0; private boolean myDoNotSave = false; private volatile boolean myDisposeInProgress = false; private int myRestartCode = 0; private volatile int myExitCode = 0; private final AtomicBoolean mySaveSettingsIsInProgress = new AtomicBoolean(false); @SuppressWarnings({"UseOfArchaicSystemPropertyAccessors"}) private static final int ourDumpThreadsOnLongWriteActionWaiting = Integer.getInteger(System.getProperty("dump.threads.on.long.write.action.waiting"), 0); private final ExecutorService ourThreadExecutorsService = new ThreadPoolExecutor( 3, Integer.MAX_VALUE, 5 * 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() { int i; public Thread newThread(Runnable r) { final Thread thread = new Thread(r, "ApplicationImpl pooled thread " + i++) { public void interrupt() { if (LOG.isDebugEnabled()) { LOG.debug("Interrupted worker, will remove from pool"); } super.interrupt(); } public void run() { try { super.run(); } catch (Throwable t) { if (LOG.isDebugEnabled()) { LOG.debug("Worker exits due to exception", t); } } } }; thread.setPriority(Thread.NORM_PRIORITY - 1); return thread; } }); private boolean myIsFiringLoadingEvent = false; @NonNls private static final String WAS_EVER_SHOWN = "was.ever.shown"; private Boolean myActive; private static final ThreadLocal<Integer> ourEdtSafe = new ThreadLocal<Integer>(); protected void boostrapPicoContainer() { super.boostrapPicoContainer(); getPicoContainer() .registerComponentImplementation( IComponentStore.class, StoresFactory.getApplicationStoreClass()); getPicoContainer().registerComponentImplementation(ApplicationPathMacroManager.class); } @Override @NotNull public synchronized IApplicationStore getStateStore() { return (IApplicationStore) super.getStateStore(); } public ApplicationImpl( boolean isInternal, boolean isUnitTestMode, boolean isHeadless, boolean isCommandLine, @NotNull String appName) { super(null); getPicoContainer().registerComponentInstance(Application.class, this); CommonBundle.assertKeyIsFound = isUnitTestMode; if ((isInternal || isUnitTestMode) && !Comparing.equal("off", System.getProperty("idea.disposer.debug"))) { Disposer.setDebugMode(true); } myStartTime = System.currentTimeMillis(); myName = appName; ApplicationManagerEx.setApplication(this); PluginsFacade.INSTANCE = new PluginsFacade() { public IdeaPluginDescriptor getPlugin(PluginId id) { return PluginManager.getPlugin(id); } public IdeaPluginDescriptor[] getPlugins() { return PluginManager.getPlugins(); } }; if (!isUnitTestMode && !isHeadless) { Toolkit.getDefaultToolkit().getSystemEventQueue().push(IdeEventQueue.getInstance()); if (Patches.SUN_BUG_ID_6209673) { RepaintManager.setCurrentManager(new IdeRepaintManager()); } IconLoader.activate(); } myIsInternal = isInternal; myTestModeFlag = isUnitTestMode; myHeadlessMode = isHeadless; myCommandLineMode = isCommandLine; loadApplicationComponents(); if (myTestModeFlag) { registerShutdownHook(); } if (!isUnitTestMode && !isHeadless) { Disposer.register(this, Disposer.newDisposable(), "ui"); StartupUtil.addExternalInstanceListener( new Consumer<List<String>>() { @Override public void consume(final List<String> args) { invokeLater( new Runnable() { @Override public void run() { final Project project = CommandLineProcessor.processExternalCommandLine(args); final IdeFrame frame; if (project != null) { frame = WindowManager.getInstance().getIdeFrame(project); } else { frame = WindowManager.getInstance().getAllFrames()[0]; } ((IdeFrameImpl) frame).requestFocus(); } }); } }); } final String s = System.getProperty("jb.restart.code"); if (s != null) { try { myRestartCode = Integer.parseInt(s); } catch (NumberFormatException ignore) { } } } private void registerShutdownHook() { ShutDownTracker .getInstance(); // Necessary to avoid creating an instance while already shutting down. ShutDownTracker.getInstance() .registerShutdownTask( new Runnable() { public void run() { if (isDisposed() || isDisposeInProgress()) { return; } try { SwingUtilities.invokeAndWait( new Runnable() { public void run() { ApplicationManagerEx.setApplication(ApplicationImpl.this); try { saveAll(); } finally { disposeSelf(); } } }); } catch (InterruptedException e) { LOG.error(e); } catch (InvocationTargetException e) { LOG.error(e); } } }); } private boolean disposeSelf() { myDisposeInProgress = true; final CommandProcessor commandProcessor = CommandProcessor.getInstance(); final Ref<Boolean> canClose = new Ref<Boolean>(Boolean.TRUE); for (final Project project : ProjectManagerEx.getInstanceEx().getOpenProjects()) { try { commandProcessor.executeCommand( project, new Runnable() { public void run() { canClose.set(ProjectUtil.closeAndDispose(project)); } }, ApplicationBundle.message("command.exit"), null); } catch (Throwable e) { LOG.error(e); } if (!canClose.get()) { myDisposeInProgress = false; return false; } } Disposer.dispose(this); Disposer.assertIsEmpty(); return true; } @NotNull public String getName() { return myName; } public boolean holdsReadLock() { return myActionsLock.isReadLockAcquired(); } @Override protected void handleInitComponentError( final Throwable ex, final boolean fatal, final String componentClassName) { if (PluginManager.isPluginClass(componentClassName)) { LOG.error(ex); PluginId pluginId = PluginManager.getPluginByClassName(componentClassName); @NonNls final String errorMessage = "Plugin " + pluginId.getIdString() + " failed to initialize and will be disabled:\n" + ex.getMessage() + "\nPlease restart " + ApplicationNamesInfo.getInstance().getFullProductName() + "."; PluginManager.disablePlugin(pluginId.getIdString()); if (!myHeadlessMode) { JOptionPane.showMessageDialog(null, errorMessage); } else { //noinspection UseOfSystemOutOrSystemErr System.out.println(errorMessage); } System.exit(1); } else if (fatal) { LOG.error(ex); @NonNls final String errorMessage = "Fatal error initializing class " + componentClassName + ":\n" + ex.toString() + "\nComplete error stacktrace was written to idea.log"; if (!myHeadlessMode) { JOptionPane.showMessageDialog(null, errorMessage); } else { //noinspection UseOfSystemOutOrSystemErr System.out.println(errorMessage); } } super.handleInitComponentError(ex, fatal, componentClassName); } private void loadApplicationComponents() { final IdeaPluginDescriptor[] plugins = PluginManager.getPlugins(); for (IdeaPluginDescriptor plugin : plugins) { if (PluginManager.shouldSkipPlugin(plugin)) continue; loadComponentsConfiguration(plugin.getAppComponents(), plugin, false); } } protected MutablePicoContainer createPicoContainer() { return Extensions.getRootArea().getPicoContainer(); } public boolean isInternal() { return myIsInternal; } public boolean isUnitTestMode() { return myTestModeFlag; } public void setUnitTestMode(boolean testModeFlag) { myTestModeFlag = testModeFlag; } public boolean isHeadlessEnvironment() { return myHeadlessMode; } public boolean isCommandLine() { return myCommandLineMode; } public IdeaPluginDescriptor getPlugin(PluginId id) { return PluginsFacade.INSTANCE.getPlugin(id); } public IdeaPluginDescriptor[] getPlugins() { return PluginsFacade.INSTANCE.getPlugins(); } public Future<?> executeOnPooledThread(@NotNull final Runnable action) { return ourThreadExecutorsService.submit( new Runnable() { public void run() { try { action.run(); } catch (ProcessCanceledException e) { // ignore } catch (Throwable t) { LOG.error(t); } finally { Thread.interrupted(); // reset interrupted status } } }); } @Override public <T> Future<T> executeOnPooledThread(@NotNull final Callable<T> action) { return ourThreadExecutorsService.submit( new Callable<T>() { public T call() { try { return action.call(); } catch (ProcessCanceledException e) { // ignore } catch (Throwable t) { LOG.error(t); } finally { Thread.interrupted(); // reset interrupted status } return null; } }); } private static Thread ourDispatchThread = null; public boolean isDispatchThread() { return EventQueue.isDispatchThread(); } @NotNull public ModalityInvokator getInvokator() { return myInvokator; } public void invokeLater(@NotNull final Runnable runnable) { myInvokator.invokeLater(runnable); } public void invokeLater(@NotNull final Runnable runnable, @NotNull final Condition expired) { myInvokator.invokeLater(runnable, expired); } public void invokeLater(@NotNull final Runnable runnable, @NotNull final ModalityState state) { myInvokator.invokeLater(runnable, state); } public void invokeLater( @NotNull final Runnable runnable, @NotNull final ModalityState state, @NotNull final Condition expired) { myInvokator.invokeLater(runnable, state, expired); } public void load(String path) throws IOException, InvalidDataException { getStateStore().setOptionsPath(path); getStateStore().setConfigPath(PathManager.getConfigPath()); myIsFiringLoadingEvent = true; try { fireBeforeApplicationLoaded(); } finally { myIsFiringLoadingEvent = false; } loadComponentRoamingTypes(); try { getStateStore().load(); } catch (StateStorage.StateStorageException e) { throw new IOException(e.getMessage()); } } @Override protected <T> T getComponentFromContainer(final Class<T> interfaceClass) { if (myIsFiringLoadingEvent) { return null; } return super.getComponentFromContainer(interfaceClass); } private static void loadComponentRoamingTypes() { ExtensionPoint<RoamingTypeExtensionPointBean> point = Extensions.getRootArea().getExtensionPoint("com.intellij.ComponentRoamingType"); final RoamingTypeExtensionPointBean[] componentRoamingTypes = point.getExtensions(); for (RoamingTypeExtensionPointBean object : componentRoamingTypes) { assert object.componentName != null; assert object.roamingType != null; final RoamingType type = RoamingType.valueOf(object.roamingType); assert type != null; ComponentRoamingManager.getInstance().setRoamingType(object.componentName, type); } } private void fireBeforeApplicationLoaded() { ExtensionPoint<ApplicationLoadListener> point = Extensions.getRootArea().getExtensionPoint("com.intellij.ApplicationLoadListener"); final ApplicationLoadListener[] objects = point.getExtensions(); for (ApplicationLoadListener object : objects) { object.beforeApplicationLoaded(this); } } public void dispose() { fireApplicationExiting(); disposeComponents(); ourThreadExecutorsService.shutdownNow(); super.dispose(); } private final Object lock = new Object(); private void makeChangesVisibleToEDT() { synchronized (lock) { lock.hashCode(); } } public boolean runProcessWithProgressSynchronously( @NotNull final Runnable process, @NotNull String progressTitle, boolean canBeCanceled, Project project) { return runProcessWithProgressSynchronously( process, progressTitle, canBeCanceled, project, null); } public boolean runProcessWithProgressSynchronously( @NotNull final Runnable process, @NotNull final String progressTitle, final boolean canBeCanceled, @Nullable final Project project, final JComponent parentComponent) { return runProcessWithProgressSynchronously( process, progressTitle, canBeCanceled, project, parentComponent, null); } public boolean runProcessWithProgressSynchronously( @NotNull final Runnable process, @NotNull final String progressTitle, final boolean canBeCanceled, @Nullable final Project project, final JComponent parentComponent, final String cancelText) { assertIsDispatchThread(); if (myExceptionalThreadWithReadAccessRunnable != null || ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment()) { try { ProgressManager.getInstance().runProcess(process, new EmptyProgressIndicator()); } catch (ProcessCanceledException e) { // ok to ignore. return false; } return true; } final ProgressWindow progress = new ProgressWindow(canBeCanceled, false, project, parentComponent, cancelText); progress.setTitle(progressTitle); try { myExceptionalThreadWithReadAccessRunnable = process; final boolean[] threadStarted = {false}; SwingUtilities.invokeLater( new Runnable() { public void run() { if (myExceptionalThreadWithReadAccessRunnable != process) { LOG.error( "myExceptionalThreadWithReadAccessRunnable != process, process = " + myExceptionalThreadWithReadAccessRunnable); } executeOnPooledThread( new Runnable() { public void run() { if (myExceptionalThreadWithReadAccessRunnable != process) { LOG.error( "myExceptionalThreadWithReadAccessRunnable != process, process = " + myExceptionalThreadWithReadAccessRunnable); } final boolean old = setExceptionalThreadWithReadAccessFlag(true); LOG.assertTrue(isReadAccessAllowed()); try { ProgressManager.getInstance().runProcess(process, progress); } catch (ProcessCanceledException e) { progress.cancel(); // ok to ignore. } catch (RuntimeException e) { progress.cancel(); throw e; } finally { setExceptionalThreadWithReadAccessFlag(old); makeChangesVisibleToEDT(); } } }); threadStarted[0] = true; } }); progress.startBlocking(); LOG.assertTrue(threadStarted[0]); LOG.assertTrue(!progress.isRunning()); } finally { myExceptionalThreadWithReadAccessRunnable = null; makeChangesVisibleToEDT(); } return !progress.isCanceled(); } public boolean isInModalProgressThread() { if (myExceptionalThreadWithReadAccessRunnable == null || !isExceptionalThreadWithReadAccess()) { return false; } ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); return progressIndicator.isModal() && ((ProgressIndicatorEx) progressIndicator).isModalityEntered(); } public void invokeAndWait(@NotNull Runnable runnable, @NotNull ModalityState modalityState) { if (isDispatchThread()) { LOG.error("invokeAndWait must not be called from event queue thread"); runnable.run(); return; } if (isExceptionalThreadWithReadAccess()) { // OK if we're in exceptional thread. LaterInvocator.invokeAndWait(runnable, modalityState); return; } if (myActionsLock.isReadLockAcquired()) { LOG.error("Calling invokeAndWait from read-action leads to possible deadlock."); } LaterInvocator.invokeAndWait(runnable, modalityState); } @NotNull public ModalityState getCurrentModalityState() { Object[] entities = LaterInvocator.getCurrentModalEntities(); return entities.length > 0 ? new ModalityStateEx(entities) : getNoneModalityState(); } @NotNull public ModalityState getModalityStateForComponent(@NotNull Component c) { Window window = c instanceof Window ? (Window) c : SwingUtilities.windowForComponent(c); if (window == null) return getNoneModalityState(); // ? return LaterInvocator.modalityStateForWindow(window); } @NotNull public ModalityState getDefaultModalityState() { if (EventQueue.isDispatchThread()) { return getCurrentModalityState(); } else { ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); return progress == null ? getNoneModalityState() : progress.getModalityState(); } } @NotNull public ModalityState getNoneModalityState() { return MODALITY_STATE_NONE; } public long getStartTime() { return myStartTime; } public long getIdleTime() { return IdeEventQueue.getInstance().getIdleTime(); } public void exit() { exit(false); } public void exit(final boolean force) { if (!force && getDefaultModalityState() != ModalityState.NON_MODAL) { return; } Runnable runnable = new Runnable() { public void run() { if (!force) { if (!showConfirmation()) { saveAll(); myExitCode = 0; return; } } getMessageBus().syncPublisher(AppLifecycleListener.TOPIC).appClosing(); FileDocumentManager.getInstance().saveAllDocuments(); saveSettings(); if (!canExit()) { myExitCode = 0; return; } final boolean success = disposeSelf(); if (!success || isUnitTestMode()) { myExitCode = 0; return; } System.exit(myExitCode); } }; if (!isDispatchThread()) { invokeLater(runnable, ModalityState.NON_MODAL); } else { runnable.run(); } } private static boolean showConfirmation() { final boolean hasUnsafeBgTasks = ProgressManager.getInstance().hasUnsafeProgressIndicator(); final ConfirmExitDialog confirmExitDialog = new ConfirmExitDialog(hasUnsafeBgTasks); if (confirmExitDialog.isToBeShown()) { confirmExitDialog.show(); if (!confirmExitDialog.isOK()) { return false; } } else { confirmExitDialog.close(DialogWrapper.OK_EXIT_CODE); } return true; } private boolean canExit() { for (ApplicationListener applicationListener : myDispatcher.getListeners()) { if (!applicationListener.canExitApplication()) { return false; } } ProjectManagerEx projectManager = (ProjectManagerEx) ProjectManager.getInstance(); Project[] projects = projectManager.getOpenProjects(); for (Project project : projects) { if (!projectManager.canClose(project)) { return false; } } return true; } public void runReadAction(@NotNull final Runnable action) { /** * if we are inside read action, do not try to acquire read lock again since it will deadlock if * there is a pending writeAction see {@link * com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock#allowReader()} */ if (isReadAccessAllowed()) { action.run(); return; } LOG.assertTrue( !Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing readAction"); try { myActionsLock.readLock().acquire(); } catch (InterruptedException e) { throw new RuntimeInterruptedException(e); } try { action.run(); } finally { myActionsLock.readLock().release(); } } private static final ThreadLocal<Boolean> exceptionalThreadWithReadAccessFlag = new ThreadLocal<Boolean>(); private static boolean isExceptionalThreadWithReadAccess() { Boolean flag = exceptionalThreadWithReadAccessFlag.get(); return flag == Boolean.TRUE; } public static boolean setExceptionalThreadWithReadAccessFlag(boolean flag) { boolean old = isExceptionalThreadWithReadAccess(); if (flag) { exceptionalThreadWithReadAccessFlag.set(Boolean.TRUE); } else { exceptionalThreadWithReadAccessFlag.remove(); } return old; } public <T> T runReadAction(@NotNull final Computable<T> computation) { final Ref<T> ref = Ref.create(null); runReadAction( new Runnable() { public void run() { ref.set(computation.compute()); } }); return ref.get(); } public void runWriteAction(@NotNull final Runnable action) { assertCanRunWriteAction(); ActivityTracker.getInstance().inc(); fireBeforeWriteActionStart(action); final AtomicBoolean stopped = new AtomicBoolean(false); if (ourDumpThreadsOnLongWriteActionWaiting > 0) { executeOnPooledThread( new Runnable() { @Override public void run() { while (!stopped.get()) { try { Thread.sleep(ourDumpThreadsOnLongWriteActionWaiting); if (!stopped.get()) { PerformanceWatcher.getInstance().dumpThreads(true); } } catch (InterruptedException ignored) { } } } }); } LOG.assertTrue( myActionsLock.isWriteLockAcquired(Thread.currentThread()) || !Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing writeAction"); try { myActionsLock.writeLock().acquire(); } catch (InterruptedException e) { throw new RuntimeInterruptedException(e); } stopped.set(true); try { myWriteActionsStack.push(action); fireWriteActionStarted(action); action.run(); } finally { try { fireWriteActionFinished(action); myWriteActionsStack.pop(); } finally { myActionsLock.writeLock().release(); } } } public <T> T runWriteAction(@NotNull final Computable<T> computation) { final Ref<T> ref = Ref.create(null); runWriteAction( new Runnable() { public void run() { ref.set(computation.compute()); } }); return ref.get(); } public <T> T getCurrentWriteAction(@Nullable Class<T> actionClass) { assertCanRunWriteAction(); for (int i = myWriteActionsStack.size() - 1; i >= 0; i--) { Runnable action = myWriteActionsStack.get(i); if (actionClass == null || ReflectionCache.isAssignable(actionClass, action.getClass())) return (T) action; } return null; } public void assertReadAccessAllowed() { if (myHeadlessMode) return; if (!isReadAccessAllowed()) { LOG.error( "Read access is allowed from event dispatch thread or inside read-action only (see com.intellij.openapi.application.Application.runReadAction())", "Current thread: " + describe(Thread.currentThread()), "Our dispatch thread:" + describe(ourDispatchThread), "SystemEventQueueThread: " + describe(getEventQueueThread())); } } @NonNls private static String describe(Thread o) { if (o == null) return "null"; return o.toString() + " " + System.identityHashCode(o); } @Nullable private static Thread getEventQueueThread() { EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); try { Method method = EventQueue.class.getDeclaredMethod("getDispatchThread"); method.setAccessible(true); return (Thread) method.invoke(eventQueue); } catch (Exception e1) { // ok } return null; } public boolean isReadAccessAllowed() { Thread currentThread = Thread.currentThread(); return ourDispatchThread == currentThread || isExceptionalThreadWithReadAccess() || myActionsLock.isReadLockAcquired() || myActionsLock.isWriteLockAcquired() || isDispatchThread(); } public void assertReadAccessToDocumentsAllowed() { /* TODO Thread currentThread = Thread.currentThread(); if (ourDispatchThread != currentThread) { if (myExceptionalThreadWithReadAccess == currentThread) return; if (myActionsLock.isReadLockAcquired(currentThread)) return; if (myActionsLock.isWriteLockAcquired(currentThread)) return; if (isDispatchThread(currentThread)) return; LOG.error( "Read access is allowed from event dispatch thread or inside read-action only (see com.intellij.openapi.application.Application.runReadAction())"); } */ } private static void assertCanRunWriteAction() { assertIsDispatchThread("Write access is allowed from event dispatch thread only"); } public void assertIsDispatchThread() { assertIsDispatchThread("Access is allowed from event dispatch thread only."); } private static void assertIsDispatchThread(String message) { if (ShutDownTracker.isShutdownHookRunning()) return; final Thread currentThread = Thread.currentThread(); if (ourDispatchThread == currentThread) return; if (EventQueue.isDispatchThread()) { ourDispatchThread = currentThread; } if (ourDispatchThread == currentThread) return; Integer safeCounter = ourEdtSafe.get(); if (safeCounter != null && safeCounter > 0) return; LOG.error( message, "Current thread: " + describe(Thread.currentThread()), "Our dispatch thread:" + describe(ourDispatchThread), "SystemEventQueueThread: " + describe(getEventQueueThread())); } public void runEdtSafeAction(@NotNull Runnable runnable) { Integer value = ourEdtSafe.get(); if (value == null) { value = Integer.valueOf(0); } ourEdtSafe.set(value + 1); try { runnable.run(); } finally { int newValue = ourEdtSafe.get() - 1; ourEdtSafe.set(newValue >= 1 ? newValue : null); } } public void assertIsDispatchThread(@Nullable final JComponent component) { if (component == null) return; Thread curThread = Thread.currentThread(); if (ourDispatchThread == curThread) { return; } if (Boolean.TRUE.equals(component.getClientProperty(WAS_EVER_SHOWN))) { assertIsDispatchThread(); } else { final JRootPane root = component.getRootPane(); if (root != null) { component.putClientProperty(WAS_EVER_SHOWN, Boolean.TRUE); assertIsDispatchThread(); } } } public void assertTimeConsuming() { if (myTestModeFlag || myHeadlessMode || ShutDownTracker.isShutdownHookRunning()) return; LOG.assertTrue( !isDispatchThread(), "This operation is time consuming and must not be called on EDT"); } public boolean tryRunReadAction(@NotNull Runnable action) { /** * if we are inside read action, do not try to acquire read lock again since it will deadlock if * there is a pending writeAction see {@link * com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock#allowReader()} */ boolean mustAcquire = !isReadAccessAllowed(); if (mustAcquire) { LOG.assertTrue( myTestModeFlag || !Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing readAction"); try { if (!myActionsLock.readLock().attempt(0)) return false; } catch (InterruptedException e) { throw new RuntimeInterruptedException(e); } } try { action.run(); } finally { if (mustAcquire) { myActionsLock.readLock().release(); } } return true; } public boolean tryToApplyActivationState(boolean active, Window window) { final Component frame = UIUtil.findUltimateParent(window); if (frame instanceof IdeFrame) { final IdeFrame ideFrame = (IdeFrame) frame; if (isActive() != active) { myActive = Boolean.valueOf(active); System.setProperty("idea.active", Boolean.valueOf(myActive).toString()); if (active) { myDispatcher.getMulticaster().applicationActivated(ideFrame); } else { myDispatcher.getMulticaster().applicationDeactivated(ideFrame); } return true; } } return false; } public boolean isActive() { if (isUnitTestMode()) return true; if (myActive == null) { Window active = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); return active != null; } return myActive; } public void assertWriteAccessAllowed() { LOG.assertTrue( isWriteAccessAllowed(), "Write access is allowed inside write-action only (see com.intellij.openapi.application.Application.runWriteAction())"); } public boolean isWriteAccessAllowed() { return myActionsLock.isWriteLockAcquired(Thread.currentThread()); } public void editorPaintStart() { myInEditorPaintCounter++; } public void editorPaintFinish() { myInEditorPaintCounter--; LOG.assertTrue(myInEditorPaintCounter >= 0); } public void addApplicationListener(@NotNull ApplicationListener l) { myDispatcher.addListener(l); } public void addApplicationListener(@NotNull ApplicationListener l, @NotNull Disposable parent) { myDispatcher.addListener(l, parent); } public void removeApplicationListener(@NotNull ApplicationListener l) { myDispatcher.removeListener(l); } private void fireApplicationExiting() { myDispatcher.getMulticaster().applicationExiting(); } private void fireBeforeWriteActionStart(Runnable action) { myDispatcher.getMulticaster().beforeWriteActionStart(action); } private void fireWriteActionStarted(Runnable action) { myDispatcher.getMulticaster().writeActionStarted(action); } private void fireWriteActionFinished(Runnable action) { myDispatcher.getMulticaster().writeActionFinished(action); } public void _saveSettings() { // public for testing purposes if (mySaveSettingsIsInProgress.compareAndSet(false, true)) { try { doSave(); } catch (final Throwable ex) { if (isUnitTestMode()) { System.out.println("Saving application settings failed"); ex.printStackTrace(); } else { LOG.info("Saving application settings failed", ex); invokeLater( new Runnable() { public void run() { if (ex instanceof PluginException) { final PluginException pluginException = (PluginException) ex; PluginManager.disablePlugin(pluginException.getPluginId().getIdString()); Messages.showMessageDialog( "The plugin " + pluginException.getPluginId() + " failed to save settings and has been disabled. Please restart " + ApplicationNamesInfo.getInstance().getFullProductName(), CommonBundle.getErrorTitle(), Messages.getErrorIcon()); } else { Messages.showMessageDialog( ApplicationBundle.message( "application.save.settings.error", ex.getLocalizedMessage()), CommonBundle.getErrorTitle(), Messages.getErrorIcon()); } } }); } } finally { mySaveSettingsIsInProgress.set(false); } } } public void saveSettings() { if (!myDoNotSave && !isUnitTestMode() && !isHeadlessEnvironment()) { _saveSettings(); } } public void saveAll() { if (myDoNotSave || isUnitTestMode() || isHeadlessEnvironment()) return; FileDocumentManager.getInstance().saveAllDocuments(); Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); for (Project openProject : openProjects) { ProjectEx project = (ProjectEx) openProject; project.save(); } saveSettings(); } public void doNotSave() { myDoNotSave = true; } public boolean isDoNotSave() { return myDoNotSave; } public <T> T[] getExtensions(final ExtensionPointName<T> extensionPointName) { return Extensions.getRootArea().getExtensionPoint(extensionPointName).getExtensions(); } public boolean isDisposeInProgress() { return myDisposeInProgress; } public boolean isRestartCapable() { return SystemInfo.isWindows || SystemInfo.isMacOSSnowLeopard || myRestartCode > 0; } public void restart() { if (SystemInfo.isWindows) { Win32Restarter.restart(); } else if (SystemInfo.isMacOSSnowLeopard) { MacRestarter.restart(); } else if (myRestartCode > 0) { myExitCode = myRestartCode; exit(true); } else { exit(true); } } public boolean isSaving() { if (getStateStore().isSaving()) return true; Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); for (Project openProject : openProjects) { ProjectEx project = (ProjectEx) openProject; if (project.getStateStore().isSaving()) return true; } return false; } @Override public String toString() { return "Application"; } }
protected class MyContentEntryEditor extends ContentEntryEditor { private final EventDispatcher<ChangeListener> myEventDispatcher = EventDispatcher.create(ChangeListener.class); public MyContentEntryEditor( String contentEntryUrl, List<ModuleSourceRootEditHandler<?>> handlers) { super(contentEntryUrl, handlers); } @Override protected ModifiableRootModel getModel() { return PyContentEntriesEditor.this.getModel(); } public void addListener(ChangeListener changeListener) { myEventDispatcher.addListener(changeListener); } public void removeListener(ChangeListener changeListener) { myEventDispatcher.removeListener(changeListener); } @Override protected ContentRootPanel createContentRootPane() { return new MyContentRootPanel(); } @Override public void deleteContentFolder(ContentEntry contentEntry, ContentFolder folder) { for (PyRootTypeProvider provider : myRootTypeProviders) { if (provider.isMine(folder)) { removeRoot(contentEntry, folder.getUrl(), provider); return; } } super.deleteContentFolder(contentEntry, folder); } public void removeRoot( @Nullable ContentEntry contentEntry, String folder, PyRootTypeProvider provider) { if (contentEntry == null) { contentEntry = getContentEntry(); } VirtualFilePointer root = getRoot(provider, folder); if (root != null) { provider.removeRoot(contentEntry, root, getModel()); fireUpdate(); } } public void fireUpdate() { myEventDispatcher.getMulticaster().stateChanged(new ChangeEvent(this)); update(); } public VirtualFilePointer getRoot(PyRootTypeProvider provider, @NotNull final String url) { for (VirtualFilePointer filePointer : provider.getRoots().get(getContentEntry())) { if (Comparing.equal(filePointer.getUrl(), url)) { return filePointer; } } return null; } public void addRoot(PyRootTypeProvider provider, @NotNull final VirtualFilePointer root) { provider.getRoots().putValue(getContentEntry(), root); fireUpdate(); } protected class MyContentRootPanel extends ContentRootPanel { public MyContentRootPanel() { super(MyContentEntryEditor.this, getEditHandlers()); } @Override @NotNull protected ContentEntryImpl getContentEntry() { //noinspection ConstantConditions return (ContentEntryImpl) MyContentEntryEditor.this.getContentEntry(); } @Override protected void addFolderGroupComponents() { super.addFolderGroupComponents(); for (PyRootTypeProvider provider : myRootTypeProviders) { MultiMap<ContentEntry, VirtualFilePointer> roots = provider.getRoots(); if (!roots.get(getContentEntry()).isEmpty()) { final JComponent sourcesComponent = createFolderGroupComponent( provider.getName() + " Folders", provider.createFolders(getContentEntry()), provider.getColor(), null); this.add( sourcesComponent, new GridBagConstraints( 0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0)); } } } } }
/** @author Eugene Zhuravlev Date: Oct 4, 2003 Time: 6:29:56 PM */ @SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"}) public abstract class ModuleEditor implements Place.Navigator, Disposable { private static final Logger LOG = Logger.getInstance(ModuleEditor.class); private static final ExtensionPointName<ModuleConfigurableEP> MODULE_CONFIGURABLES = ExtensionPointName.create("com.intellij.moduleConfigurable"); public static final String SELECTED_EDITOR_NAME = "selectedEditor"; private final Project myProject; private JPanel myGenericSettingsPanel; private ModifiableRootModel myModifiableRootModel; // important: in order to correctly update OrderEntries UI use // corresponding proxy for the model private final ModulesProvider myModulesProvider; private String myName; private final Module myModule; protected final List<ModuleConfigurationEditor> myEditors = new ArrayList<>(); private ModifiableRootModel myModifiableRootModelProxy; private final EventDispatcher<ChangeListener> myEventDispatcher = EventDispatcher.create(ChangeListener.class); @NonNls private static final String METHOD_COMMIT = "commit"; private boolean myEditorsInitialized; protected History myHistory; public ModuleEditor(Project project, ModulesProvider modulesProvider, @NotNull Module module) { myProject = project; myModulesProvider = modulesProvider; myModule = module; myName = module.getName(); } public void init(History history) { myHistory = history; for (ModuleConfigurationEditor each : myEditors) { if (each instanceof ModuleElementsEditor) { ((ModuleElementsEditor) each).setHistory(myHistory); } } restoreSelectedEditor(); } public abstract ProjectFacetsConfigurator getFacetsConfigurator(); protected abstract JComponent createCenterPanel(); @Nullable public abstract ModuleConfigurationEditor getSelectedEditor(); public abstract void selectEditor(String displayName); protected abstract void restoreSelectedEditor(); @Nullable public abstract ModuleConfigurationEditor getEditor(@NotNull String displayName); protected abstract void disposeCenterPanel(); public interface ChangeListener extends EventListener { void moduleStateChanged(ModifiableRootModel moduleRootModel); } public void addChangeListener(ChangeListener listener) { myEventDispatcher.addListener(listener); } public void removeChangeListener(ChangeListener listener) { myEventDispatcher.removeListener(listener); } @Nullable public Module getModule() { final Module[] all = myModulesProvider.getModules(); for (Module each : all) { if (each == myModule) return myModule; } return myModulesProvider.getModule(myName); } public ModifiableRootModel getModifiableRootModel() { if (myModifiableRootModel == null) { final Module module = getModule(); if (module != null) { myModifiableRootModel = ((ModuleRootManagerImpl) ModuleRootManager.getInstance(module)) .getModifiableModel(new UIRootConfigurationAccessor(myProject)); } } return myModifiableRootModel; } public OrderEntry[] getOrderEntries() { if (myModifiableRootModel == null) { // do not clone all model if not necessary return ModuleRootManager.getInstance(getModule()).getOrderEntries(); } else { return myModifiableRootModel.getOrderEntries(); } } public ModifiableRootModel getModifiableRootModelProxy() { if (myModifiableRootModelProxy == null) { final ModifiableRootModel rootModel = getModifiableRootModel(); if (rootModel != null) { myModifiableRootModelProxy = (ModifiableRootModel) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {ModifiableRootModel.class}, new ModifiableRootModelInvocationHandler(rootModel)); } } return myModifiableRootModelProxy; } public ModuleRootModel getRootModel() { if (myModifiableRootModel != null) { return getModifiableRootModelProxy(); } return ModuleRootManager.getInstance(myModule); } public boolean isModified() { for (ModuleConfigurationEditor moduleElementsEditor : myEditors) { if (moduleElementsEditor.isModified()) { return true; } } return false; } private void createEditors(@Nullable Module module) { if (module == null) return; ModuleConfigurationState state = createModuleConfigurationState(); for (ModuleConfigurationEditorProvider provider : collectProviders(module)) { ModuleConfigurationEditor[] editors = provider.createEditors(state); if (editors.length > 0 && provider instanceof ModuleConfigurationEditorProviderEx && ((ModuleConfigurationEditorProviderEx) provider).isCompleteEditorSet()) { myEditors.clear(); ContainerUtil.addAll(myEditors, editors); break; } else { ContainerUtil.addAll(myEditors, editors); } } for (Configurable moduleConfigurable : ServiceKt.getComponents(module, Configurable.class)) { reportDeprecatedModuleEditor(moduleConfigurable.getClass()); myEditors.add(new ModuleConfigurableWrapper(moduleConfigurable)); } for (ModuleConfigurableEP extension : module.getExtensions(MODULE_CONFIGURABLES)) { if (extension.canCreateConfigurable()) { Configurable configurable = extension.createConfigurable(); if (configurable != null) { reportDeprecatedModuleEditor(configurable.getClass()); myEditors.add(new ModuleConfigurableWrapper(configurable)); } } } } private static Set<Class<?>> ourReportedDeprecatedClasses = new HashSet<>(); private static void reportDeprecatedModuleEditor(Class<?> aClass) { if (ourReportedDeprecatedClasses.add(aClass)) { LOG.warn( aClass.getName() + " uses deprecated way to register itself as a module editor. " + ModuleConfigurationEditorProvider.class.getName() + " extension point should be used instead"); } } private static ModuleConfigurationEditorProvider[] collectProviders(@NotNull Module module) { List<ModuleConfigurationEditorProvider> result = new ArrayList<>(); result.addAll(ServiceKt.getComponents(module, ModuleConfigurationEditorProvider.class)); for (ModuleConfigurationEditorProvider component : result) { reportDeprecatedModuleEditor(component.getClass()); } ContainerUtil.addAll( result, Extensions.getExtensions(ModuleConfigurationEditorProvider.EP_NAME, module)); return result.toArray(new ModuleConfigurationEditorProvider[result.size()]); } public ModuleConfigurationState createModuleConfigurationState() { return new ModuleConfigurationStateImpl(myProject, myModulesProvider) { @Override public ModifiableRootModel getRootModel() { return getModifiableRootModelProxy(); } @Override public FacetsProvider getFacetsProvider() { return getFacetsConfigurator(); } }; } private JPanel createPanel() { getModifiableRootModel(); // initialize model if needed getModifiableRootModelProxy(); myGenericSettingsPanel = new ModuleEditorPanel(); createEditors(getModule()); final JComponent component = createCenterPanel(); myGenericSettingsPanel.add(component, BorderLayout.CENTER); myEditorsInitialized = true; return myGenericSettingsPanel; } public JPanel getPanel() { if (myGenericSettingsPanel == null) { myGenericSettingsPanel = createPanel(); } return myGenericSettingsPanel; } public void moduleCountChanged() { updateOrderEntriesInEditors(false); } private void updateOrderEntriesInEditors(boolean forceInitEditors) { if (getModule() != null) { // module with attached module libraries was deleted if (myEditorsInitialized || forceInitEditors) { getPanel(); // init editor if needed for (final ModuleConfigurationEditor myEditor : myEditors) { myEditor.moduleStateChanged(); } } myEventDispatcher.getMulticaster().moduleStateChanged(getModifiableRootModelProxy()); } } public void updateCompilerOutputPathChanged(String baseUrl, String moduleName) { if (myGenericSettingsPanel == null) return; // wasn't initialized yet for (final ModuleConfigurationEditor myEditor : myEditors) { if (myEditor instanceof ModuleElementsEditor) { ((ModuleElementsEditor) myEditor).moduleCompileOutputChanged(baseUrl, moduleName); } } } @Override public void dispose() { try { for (final ModuleConfigurationEditor myEditor : myEditors) { myEditor.disposeUIResources(); } myEditors.clear(); disposeCenterPanel(); if (myModifiableRootModel != null) { myModifiableRootModel.dispose(); } myGenericSettingsPanel = null; } finally { myModifiableRootModel = null; myModifiableRootModelProxy = null; } } public ModifiableRootModel apply() throws ConfigurationException { try { for (ModuleConfigurationEditor editor : myEditors) { editor.saveData(); editor.apply(); } return myModifiableRootModel; } finally { myModifiableRootModel = null; myModifiableRootModelProxy = null; } } public void canApply() throws ConfigurationException { for (ModuleConfigurationEditor editor : myEditors) { if (editor instanceof ModuleElementsEditor) { ((ModuleElementsEditor) editor).canApply(); } } } public String getName() { return myName; } private class ModifiableRootModelInvocationHandler implements InvocationHandler, ProxyDelegateAccessor { private final ModifiableRootModel myDelegateModel; @NonNls private final Set<String> myCheckedNames = new HashSet<>( Arrays.asList( "addOrderEntry", "addLibraryEntry", "addInvalidLibrary", "addModuleOrderEntry", "addInvalidModuleEntry", "removeOrderEntry", "setSdk", "inheritSdk", "inheritCompilerOutputPath", "setExcludeOutput", "replaceEntryOfType", "rearrangeOrderEntries")); ModifiableRootModelInvocationHandler(ModifiableRootModel model) { myDelegateModel = model; } @Override public Object invoke(Object object, Method method, Object[] params) throws Throwable { final boolean needUpdate = myCheckedNames.contains(method.getName()); try { final Object result = method.invoke(myDelegateModel, unwrapParams(params)); if (result instanceof LibraryTable) { return Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {LibraryTable.class}, new LibraryTableInvocationHandler((LibraryTable) result)); } return result; } catch (InvocationTargetException e) { throw e.getCause(); } finally { if (needUpdate) { updateOrderEntriesInEditors(true); } } } @Override public Object getDelegate() { return myDelegateModel; } } private class LibraryTableInvocationHandler implements InvocationHandler, ProxyDelegateAccessor { private final LibraryTable myDelegateTable; @NonNls private final Set<String> myCheckedNames = new HashSet<>(Arrays.asList("removeLibrary" /*,"createLibrary"*/)); LibraryTableInvocationHandler(LibraryTable table) { myDelegateTable = table; } @Override public Object invoke(Object object, Method method, Object[] params) throws Throwable { final boolean needUpdate = myCheckedNames.contains(method.getName()); try { final Object result = method.invoke(myDelegateTable, unwrapParams(params)); if (result instanceof Library) { return Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {result instanceof LibraryEx ? LibraryEx.class : Library.class}, new LibraryInvocationHandler((Library) result)); } else if (result instanceof LibraryTable.ModifiableModel) { return Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {LibraryTableBase.ModifiableModel.class}, new LibraryTableModelInvocationHandler((LibraryTable.ModifiableModel) result)); } if (result instanceof Library[]) { Library[] libraries = (Library[]) result; for (int idx = 0; idx < libraries.length; idx++) { Library library = libraries[idx]; libraries[idx] = (Library) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] { library instanceof LibraryEx ? LibraryEx.class : Library.class }, new LibraryInvocationHandler(library)); } } return result; } catch (InvocationTargetException e) { throw e.getCause(); } finally { if (needUpdate) { updateOrderEntriesInEditors(true); } } } @Override public Object getDelegate() { return myDelegateTable; } } private class LibraryInvocationHandler implements InvocationHandler, ProxyDelegateAccessor { private final Library myDelegateLibrary; LibraryInvocationHandler(Library delegateLibrary) { myDelegateLibrary = delegateLibrary; } @Override public Object invoke(Object object, Method method, Object[] params) throws Throwable { try { final Object result = method.invoke(myDelegateLibrary, unwrapParams(params)); if (result instanceof LibraryEx.ModifiableModelEx) { return Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {LibraryEx.ModifiableModelEx.class}, new LibraryModifiableModelInvocationHandler((LibraryEx.ModifiableModelEx) result)); } return result; } catch (InvocationTargetException e) { throw e.getCause(); } } @Override public Object getDelegate() { return myDelegateLibrary; } } private class LibraryModifiableModelInvocationHandler implements InvocationHandler, ProxyDelegateAccessor { private final Library.ModifiableModel myDelegateModel; LibraryModifiableModelInvocationHandler(Library.ModifiableModel delegateModel) { myDelegateModel = delegateModel; } @Override public Object invoke(Object object, Method method, Object[] params) throws Throwable { final boolean needUpdate = METHOD_COMMIT.equals(method.getName()); try { return method.invoke(myDelegateModel, unwrapParams(params)); } catch (InvocationTargetException e) { throw e.getCause(); } finally { if (needUpdate) { updateOrderEntriesInEditors(true); } } } @Override public Object getDelegate() { return myDelegateModel; } } private class LibraryTableModelInvocationHandler implements InvocationHandler, ProxyDelegateAccessor { private final LibraryTable.ModifiableModel myDelegateModel; LibraryTableModelInvocationHandler(LibraryTable.ModifiableModel delegateModel) { myDelegateModel = delegateModel; } @Override public Object invoke(Object object, Method method, Object[] params) throws Throwable { final boolean needUpdate = METHOD_COMMIT.equals(method.getName()); try { Object result = method.invoke(myDelegateModel, unwrapParams(params)); if (result instanceof Library[]) { Library[] libraries = (Library[]) result; for (int idx = 0; idx < libraries.length; idx++) { Library library = libraries[idx]; libraries[idx] = (Library) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {LibraryEx.class}, new LibraryInvocationHandler(library)); } } if (result instanceof Library) { result = Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {LibraryEx.class}, new LibraryInvocationHandler((Library) result)); } return result; } catch (InvocationTargetException e) { throw e.getCause(); } finally { if (needUpdate) { updateOrderEntriesInEditors(true); } } } @Override public Object getDelegate() { return myDelegateModel; } } public interface ProxyDelegateAccessor { Object getDelegate(); } private static Object[] unwrapParams(Object[] params) { if (params == null || params.length == 0) { return params; } final Object[] unwrappedParams = new Object[params.length]; for (int idx = 0; idx < params.length; idx++) { Object param = params[idx]; if (param != null && Proxy.isProxyClass(param.getClass())) { final InvocationHandler invocationHandler = Proxy.getInvocationHandler(param); if (invocationHandler instanceof ProxyDelegateAccessor) { param = ((ProxyDelegateAccessor) invocationHandler).getDelegate(); } } unwrappedParams[idx] = param; } return unwrappedParams; } @Nullable public String getHelpTopic() { if (myEditors.isEmpty()) { return null; } final ModuleConfigurationEditor selectedEditor = getSelectedEditor(); return selectedEditor != null ? selectedEditor.getHelpTopic() : null; } public void setModuleName(final String name) { myName = name; } private class ModuleEditorPanel extends JPanel implements DataProvider { public ModuleEditorPanel() { super(new BorderLayout()); } @Override public Object getData(String dataId) { if (LangDataKeys.MODULE_CONTEXT.is(dataId)) { return getModule(); } return null; } } @Override public void setHistory(final History history) {} }
/** * @author Eugene Belyaev * @author Vladimir Kondratyev */ public final class InternalDecorator extends JPanel implements Queryable, DataProvider { private Project myProject; private WindowInfoImpl myInfo; private final ToolWindowImpl myToolWindow; private final MyDivider myDivider; private final EventDispatcher<InternalDecoratorListener> myDispatcher = EventDispatcher.create(InternalDecoratorListener.class); /* * Actions */ private final TogglePinnedModeAction myToggleAutoHideModeAction; private final ToggleDockModeAction myToggleDockModeAction; private final ToggleFloatingModeAction myToggleFloatingModeAction; private final ToggleWindowedModeAction myToggleWindowedModeAction; private final ToggleSideModeAction myToggleSideModeAction; private final ToggleContentUiTypeAction myToggleContentUiTypeAction; private ActionGroup myAdditionalGearActions; /** Catches all event from tool window and modifies decorator's appearance. */ @NonNls private static final String HIDE_ACTIVE_WINDOW_ACTION_ID = "HideActiveWindow"; @NonNls public static final String TOGGLE_PINNED_MODE_ACTION_ID = "TogglePinnedMode"; @NonNls public static final String TOGGLE_DOCK_MODE_ACTION_ID = "ToggleDockMode"; @NonNls public static final String TOGGLE_FLOATING_MODE_ACTION_ID = "ToggleFloatingMode"; @NonNls public static final String TOGGLE_WINDOWED_MODE_ACTION_ID = "ToggleWindowedMode"; @NonNls public static final String TOGGLE_SIDE_MODE_ACTION_ID = "ToggleSideMode"; @NonNls private static final String TOGGLE_CONTENT_UI_TYPE_ACTION_ID = "ToggleContentUiTypeMode"; private ToolWindowHeader myHeader; private ActionGroup myToggleToolbarGroup; InternalDecorator( final Project project, @NotNull WindowInfoImpl info, final ToolWindowImpl toolWindow) { super(new BorderLayout()); myProject = project; myToolWindow = toolWindow; myToolWindow.setDecorator(this); myDivider = new MyDivider(); myToggleFloatingModeAction = new ToggleFloatingModeAction(); myToggleWindowedModeAction = new ToggleWindowedModeAction(); myToggleSideModeAction = new ToggleSideModeAction(); myToggleDockModeAction = new ToggleDockModeAction(); myToggleAutoHideModeAction = new TogglePinnedModeAction(); myToggleContentUiTypeAction = new ToggleContentUiTypeAction(); myToggleToolbarGroup = ToggleToolbarAction.createToggleToolbarGroup(myProject, myToolWindow); myHeader = new ToolWindowHeader( toolWindow, info, new Producer<ActionGroup>() { @Override public ActionGroup produce() { return /*createGearPopupGroup()*/ createPopupGroup(true); } }) { @Override protected boolean isActive() { return isFocused(); } @Override protected void hideToolWindow() { fireHidden(); } @Override protected void toolWindowTypeChanged(ToolWindowType type) { fireTypeChanged(type); } @Override protected void sideHidden() { fireHiddenSide(); } }; init(); apply(info); } public boolean isFocused() { IdeFocusManager fm = IdeFocusManager.getInstance(myProject); Component component = fm.getFocusedDescendantFor(myToolWindow.getComponent()); if (component != null) return true; Component owner = fm.getLastFocusedFor(WindowManager.getInstance().getIdeFrame(myProject)); return owner != null && SwingUtilities.isDescendingFrom(owner, myToolWindow.getComponent()); } /** Applies specified decoration. */ public final void apply(@NotNull WindowInfoImpl info) { if (Comparing.equal(myInfo, info) || myProject == null || myProject.isDisposed()) { return; } myInfo = info; // Anchor final ToolWindowAnchor anchor = myInfo.getAnchor(); if (info.isSliding()) { myDivider.invalidate(); if (ToolWindowAnchor.TOP == anchor) { add(myDivider, BorderLayout.SOUTH); } else if (ToolWindowAnchor.LEFT == anchor) { add(myDivider, BorderLayout.EAST); } else if (ToolWindowAnchor.BOTTOM == anchor) { add(myDivider, BorderLayout.NORTH); } else if (ToolWindowAnchor.RIGHT == anchor) { add(myDivider, BorderLayout.WEST); } myDivider.setPreferredSize(new Dimension(0, 0)); } else { // docked and floating windows don't have divider remove(myDivider); } validate(); repaint(); // Push "apply" request forward if (myInfo.isFloating() && myInfo.isVisible()) { final FloatingDecorator floatingDecorator = (FloatingDecorator) SwingUtilities.getAncestorOfClass(FloatingDecorator.class, this); if (floatingDecorator != null) { floatingDecorator.apply(myInfo); } } myToolWindow.getContentUI().setType(myInfo.getContentUiType()); setBorder(new InnerPanelBorder(myToolWindow)); } @Nullable @Override public Object getData(@NonNls String dataId) { if (PlatformDataKeys.TOOL_WINDOW.is(dataId)) { return myToolWindow; } return null; } final void addInternalDecoratorListener(InternalDecoratorListener l) { myDispatcher.addListener(l); } final void removeInternalDecoratorListener(InternalDecoratorListener l) { myDispatcher.removeListener(l); } final void dispose() { removeAll(); Disposer.dispose(myHeader); myHeader = null; myProject = null; } private void fireAnchorChanged(ToolWindowAnchor anchor) { myDispatcher.getMulticaster().anchorChanged(this, anchor); } private void fireAutoHideChanged(boolean autoHide) { myDispatcher.getMulticaster().autoHideChanged(this, autoHide); } /** Fires event that "hide" button has been pressed. */ final void fireHidden() { myDispatcher.getMulticaster().hidden(this); } /** Fires event that "hide" button has been pressed. */ final void fireHiddenSide() { myDispatcher.getMulticaster().hiddenSide(this); } /** Fires event that user performed click into the title bar area. */ final void fireActivated() { myDispatcher.getMulticaster().activated(this); } private void fireTypeChanged(ToolWindowType type) { myDispatcher.getMulticaster().typeChanged(this, type); } final void fireResized() { myDispatcher.getMulticaster().resized(this); } private void fireSideStatusChanged(boolean isSide) { myDispatcher.getMulticaster().sideStatusChanged(this, isSide); } private void fireContentUiTypeChanges(ToolWindowContentUiType type) { myDispatcher.getMulticaster().contentUiTypeChanges(this, type); } private void init() { enableEvents(AWTEvent.COMPONENT_EVENT_MASK); final JPanel contentPane = new JPanel(new BorderLayout()); contentPane.add(myHeader, BorderLayout.NORTH); JPanel innerPanel = new JPanel(new BorderLayout()); JComponent toolWindowComponent = myToolWindow.getComponent(); innerPanel.add(toolWindowComponent, BorderLayout.CENTER); final NonOpaquePanel inner = new NonOpaquePanel(innerPanel); inner.setBorder(new EmptyBorder(-1, 0, 0, 0)); contentPane.add(inner, BorderLayout.CENTER); add(contentPane, BorderLayout.CENTER); if (SystemInfo.isMac) { setBackground(new JBColor(Gray._200, Gray._90)); } // Add listeners registerKeyboardAction( new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { ToolWindowManager.getInstance(myProject).activateEditorComponent(); } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); } public void setTitleActions(AnAction[] actions) { myHeader.setAdditionalTitleActions(actions); } private class InnerPanelBorder implements Border { private final ToolWindow myWindow; private InnerPanelBorder(ToolWindow window) { myWindow = window; } @Override public void paintBorder( final Component c, final Graphics g, final int x, final int y, final int width, final int height) { if (UIUtil.isUnderDarcula()) { g.setColor(Gray._40); doPaintBorder(c, g, x, y, width, height); } else { g.setColor(UIUtil.getPanelBackground()); doPaintBorder(c, g, x, y, width, height); g.setColor(Gray._155); doPaintBorder(c, g, x, y, width, height); } } private void doPaintBorder(Component c, Graphics g, int x, int y, int width, int height) { Insets insets = getBorderInsets(c); if (insets.top > 0) { UIUtil.drawLine(g, x, y + insets.top - 1, x + width - 1, y + insets.top - 1); UIUtil.drawLine(g, x, y + insets.top, x + width - 1, y + insets.top); } if (insets.left > 0) { UIUtil.drawLine(g, x, y, x, y + height); UIUtil.drawLine(g, x + 1, y, x + 1, y + height); } if (insets.right > 0) { UIUtil.drawLine(g, x + width - 1, y + insets.top, x + width - 1, y + height); UIUtil.drawLine(g, x + width, y + insets.top, x + width, y + height); } if (insets.bottom > 0) { UIUtil.drawLine(g, x, y + height - 1, x + width, y + height - 1); UIUtil.drawLine(g, x, y + height, x + width, y + height); } } @Override public Insets getBorderInsets(final Component c) { if (myProject == null) return new Insets(0, 0, 0, 0); ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject); if (!(toolWindowManager instanceof ToolWindowManagerImpl) || !((ToolWindowManagerImpl) toolWindowManager).isToolWindowRegistered(myInfo.getId()) || myWindow.getType() == ToolWindowType.FLOATING) { return new Insets(0, 0, 0, 0); } ToolWindowAnchor anchor = myWindow.getAnchor(); Component component = myWindow.getComponent(); Container parent = component.getParent(); while (parent != null) { if (parent instanceof Splitter) { Splitter splitter = (Splitter) parent; boolean isFirst = splitter.getFirstComponent() == component; boolean isVertical = splitter.isVertical(); return new Insets( 0, anchor == ToolWindowAnchor.RIGHT || (!isVertical && !isFirst) ? 1 : 0, (isVertical && isFirst) ? 1 : 0, anchor == ToolWindowAnchor.LEFT || (!isVertical && isFirst) ? 1 : 0); } component = parent; parent = component.getParent(); } return new Insets( 0, anchor == ToolWindowAnchor.RIGHT ? 1 : 0, anchor == ToolWindowAnchor.TOP ? 1 : 0, anchor == ToolWindowAnchor.LEFT ? 1 : 0); } @Override public boolean isBorderOpaque() { return false; } } public final ActionGroup createPopupGroup() { return createPopupGroup(false); } public final ActionGroup createPopupGroup(boolean skipHideAction) { final DefaultActionGroup group = createGearPopupGroup(); if (!ToolWindowId.PREVIEW.equals(myInfo.getId())) { group.add(myToggleContentUiTypeAction); } final DefaultActionGroup moveGroup = new DefaultActionGroup(UIBundle.message("tool.window.move.to.action.group.name"), true); final ToolWindowAnchor anchor = myInfo.getAnchor(); if (anchor != ToolWindowAnchor.TOP) { final AnAction topAction = new ChangeAnchorAction( UIBundle.message("tool.window.move.to.top.action.name"), ToolWindowAnchor.TOP); moveGroup.add(topAction); } if (anchor != ToolWindowAnchor.LEFT) { final AnAction leftAction = new ChangeAnchorAction( UIBundle.message("tool.window.move.to.left.action.name"), ToolWindowAnchor.LEFT); moveGroup.add(leftAction); } if (anchor != ToolWindowAnchor.BOTTOM) { final AnAction bottomAction = new ChangeAnchorAction( UIBundle.message("tool.window.move.to.bottom.action.name"), ToolWindowAnchor.BOTTOM); moveGroup.add(bottomAction); } if (anchor != ToolWindowAnchor.RIGHT) { final AnAction rightAction = new ChangeAnchorAction( UIBundle.message("tool.window.move.to.right.action.name"), ToolWindowAnchor.RIGHT); moveGroup.add(rightAction); } group.add(moveGroup); DefaultActionGroup resize = new DefaultActionGroup(ActionsBundle.groupText("ResizeToolWindowGroup"), true); resize.add(new ResizeToolWindowAction.Left(myToolWindow, this)); resize.add(new ResizeToolWindowAction.Right(myToolWindow, this)); resize.add(new ResizeToolWindowAction.Up(myToolWindow, this)); resize.add(new ResizeToolWindowAction.Down(myToolWindow, this)); resize.add(ActionManager.getInstance().getAction("MaximizeToolWindow")); group.add(resize); if (!skipHideAction) { group.addSeparator(); group.add(new HideAction()); } return group; } private DefaultActionGroup createGearPopupGroup() { final DefaultActionGroup group = new DefaultActionGroup(); if (myAdditionalGearActions != null) { addSorted(group, myAdditionalGearActions); group.addSeparator(); } group.addAction(myToggleToolbarGroup).setAsSecondary(true); if (myInfo.isDocked()) { group.add(myToggleAutoHideModeAction); group.add(myToggleDockModeAction); group.add(myToggleFloatingModeAction); group.add(myToggleWindowedModeAction); group.add(myToggleSideModeAction); } else if (myInfo.isFloating()) { group.add(myToggleAutoHideModeAction); group.add(myToggleFloatingModeAction); group.add(myToggleWindowedModeAction); } else if (myInfo.isWindowed()) { group.add(myToggleFloatingModeAction); group.add(myToggleWindowedModeAction); } else if (myInfo.isSliding()) { if (!ToolWindowId.PREVIEW.equals(myInfo.getId())) { group.add(myToggleDockModeAction); } group.add(myToggleFloatingModeAction); group.add(myToggleWindowedModeAction); group.add(myToggleSideModeAction); } return group; } private static void addSorted(DefaultActionGroup main, ActionGroup group) { final AnAction[] children = group.getChildren(null); boolean hadSecondary = false; for (AnAction action : children) { if (group.isPrimary(action)) { main.add(action); } else { hadSecondary = true; } } if (hadSecondary) { main.addSeparator(); for (AnAction action : children) { if (!group.isPrimary(action)) { main.addAction(action).setAsSecondary(true); } } } String separatorText = group.getTemplatePresentation().getText(); if (children.length > 0 && !StringUtil.isEmpty(separatorText)) { main.addAction(new Separator(separatorText), Constraints.FIRST); } } /** @return tool window associated with the decorator. */ final ToolWindowImpl getToolWindow() { return myToolWindow; } /** @return last window info applied to the decorator. */ @NotNull final WindowInfoImpl getWindowInfo() { return myInfo; } public int getHeaderHeight() { return myHeader.getPreferredSize().height; } @Override protected final void processComponentEvent(final ComponentEvent e) { super.processComponentEvent(e); if (ComponentEvent.COMPONENT_RESIZED == e.getID()) { fireResized(); } } private final class ChangeAnchorAction extends AnAction implements DumbAware { private final ToolWindowAnchor myAnchor; public ChangeAnchorAction(final String title, final ToolWindowAnchor anchor) { super(title); myAnchor = anchor; } @Override public final void actionPerformed(@NotNull final AnActionEvent e) { fireAnchorChanged(myAnchor); } } private final class TogglePinnedModeAction extends ToggleAction implements DumbAware { public TogglePinnedModeAction() { copyFrom(ActionManager.getInstance().getAction(TOGGLE_PINNED_MODE_ACTION_ID)); } @Override public final boolean isSelected(final AnActionEvent event) { return !myInfo.isAutoHide(); } @Override public final void setSelected(final AnActionEvent event, final boolean flag) { fireAutoHideChanged(!myInfo.isAutoHide()); } } private final class ToggleDockModeAction extends ToggleAction implements DumbAware { public ToggleDockModeAction() { copyFrom(ActionManager.getInstance().getAction(TOGGLE_DOCK_MODE_ACTION_ID)); } @Override public final boolean isSelected(final AnActionEvent event) { return myInfo.isDocked(); } @Override public final void setSelected(final AnActionEvent event, final boolean flag) { if (myInfo.isDocked()) { fireTypeChanged(ToolWindowType.SLIDING); } else if (myInfo.isSliding()) { fireTypeChanged(ToolWindowType.DOCKED); } } } private final class ToggleFloatingModeAction extends ToggleAction implements DumbAware { public ToggleFloatingModeAction() { copyFrom(ActionManager.getInstance().getAction(TOGGLE_FLOATING_MODE_ACTION_ID)); } @Override public final boolean isSelected(final AnActionEvent event) { return myInfo.isFloating(); } @Override public final void setSelected(final AnActionEvent event, final boolean flag) { if (myInfo.isFloating()) { fireTypeChanged(myInfo.getInternalType()); } else { fireTypeChanged(ToolWindowType.FLOATING); } } } private final class ToggleWindowedModeAction extends ToggleAction implements DumbAware { public ToggleWindowedModeAction() { copyFrom(ActionManager.getInstance().getAction(TOGGLE_WINDOWED_MODE_ACTION_ID)); } @Override public final boolean isSelected(final AnActionEvent event) { return myInfo.isWindowed(); } @Override public final void setSelected(final AnActionEvent event, final boolean flag) { if (myInfo.isWindowed()) { fireTypeChanged(myInfo.getInternalType()); } else { fireTypeChanged(ToolWindowType.WINDOWED); } } @Override public void update(@NotNull AnActionEvent e) { super.update(e); if (SystemInfo.isMac) { e.getPresentation().setEnabledAndVisible(false); } } } private final class ToggleSideModeAction extends ToggleAction implements DumbAware { public ToggleSideModeAction() { copyFrom(ActionManager.getInstance().getAction(TOGGLE_SIDE_MODE_ACTION_ID)); } @Override public final boolean isSelected(final AnActionEvent event) { return myInfo.isSplit(); } @Override public final void setSelected(final AnActionEvent event, final boolean flag) { fireSideStatusChanged(flag); } @Override public void update(@NotNull final AnActionEvent e) { super.update(e); } } private final class HideAction extends AnAction implements DumbAware { @NonNls public static final String HIDE_ACTIVE_WINDOW_ACTION_ID = InternalDecorator.HIDE_ACTIVE_WINDOW_ACTION_ID; public HideAction() { copyFrom(ActionManager.getInstance().getAction(HIDE_ACTIVE_WINDOW_ACTION_ID)); getTemplatePresentation().setText(UIBundle.message("tool.window.hide.action.name")); } @Override public final void actionPerformed(@NotNull final AnActionEvent e) { fireHidden(); } @Override public final void update(@NotNull final AnActionEvent event) { final Presentation presentation = event.getPresentation(); presentation.setEnabled(myInfo.isVisible()); } } private final class ToggleContentUiTypeAction extends ToggleAction implements DumbAware { private boolean myHadSeveralContents; private ToggleContentUiTypeAction() { copyFrom(ActionManager.getInstance().getAction(TOGGLE_CONTENT_UI_TYPE_ACTION_ID)); } @Override public void update(@NotNull AnActionEvent e) { myHadSeveralContents = myHadSeveralContents || myToolWindow.getContentManager().getContentCount() > 1; super.update(e); e.getPresentation().setVisible(myHadSeveralContents); } @Override public boolean isSelected(AnActionEvent e) { return myInfo.getContentUiType() == ToolWindowContentUiType.COMBO; } @Override public void setSelected(AnActionEvent e, boolean state) { fireContentUiTypeChanges( state ? ToolWindowContentUiType.COMBO : ToolWindowContentUiType.TABBED); } } private final class MyDivider extends JPanel { private boolean myDragging; private Point myLastPoint; private Disposable myDisposable; private IdeGlassPane myGlassPane; private final MouseAdapter myListener = new MyMouseAdapter(); @Override public void addNotify() { super.addNotify(); myGlassPane = IdeGlassPaneUtil.find(this); myDisposable = Disposer.newDisposable(); myGlassPane.addMouseMotionPreprocessor(myListener, myDisposable); myGlassPane.addMousePreprocessor(myListener, myDisposable); } @Override public void removeNotify() { super.removeNotify(); if (myDisposable != null && !Disposer.isDisposed(myDisposable)) { Disposer.dispose(myDisposable); } } boolean isInDragZone(MouseEvent e) { final Point p = SwingUtilities.convertMouseEvent(e.getComponent(), e, this).getPoint(); return Math.abs(myInfo.getAnchor().isHorizontal() ? p.y : p.x) < 6; } private class MyMouseAdapter extends MouseAdapter { private void updateCursor(MouseEvent e) { if (isInDragZone(e)) { myGlassPane.setCursor(MyDivider.this.getCursor(), MyDivider.this); e.consume(); } } @Override public void mousePressed(MouseEvent e) { myDragging = isInDragZone(e); updateCursor(e); } @Override public void mouseClicked(MouseEvent e) { updateCursor(e); } @Override public void mouseReleased(MouseEvent e) { updateCursor(e); myDragging = false; } @Override public void mouseMoved(MouseEvent e) { updateCursor(e); } @Override public void mouseDragged(MouseEvent e) { if (!myDragging) return; MouseEvent event = SwingUtilities.convertMouseEvent(e.getComponent(), e, MyDivider.this); final ToolWindowAnchor anchor = myInfo.getAnchor(); final Point point = event.getPoint(); final Container windowPane = InternalDecorator.this.getParent(); myLastPoint = SwingUtilities.convertPoint(MyDivider.this, point, windowPane); myLastPoint.x = Math.min(Math.max(myLastPoint.x, 0), windowPane.getWidth()); myLastPoint.y = Math.min(Math.max(myLastPoint.y, 0), windowPane.getHeight()); final Rectangle bounds = InternalDecorator.this.getBounds(); if (anchor == ToolWindowAnchor.TOP) { InternalDecorator.this.setBounds(0, 0, bounds.width, myLastPoint.y); } else if (anchor == ToolWindowAnchor.LEFT) { InternalDecorator.this.setBounds(0, 0, myLastPoint.x, bounds.height); } else if (anchor == ToolWindowAnchor.BOTTOM) { InternalDecorator.this.setBounds( 0, myLastPoint.y, bounds.width, windowPane.getHeight() - myLastPoint.y); } else if (anchor == ToolWindowAnchor.RIGHT) { InternalDecorator.this.setBounds( myLastPoint.x, 0, windowPane.getWidth() - myLastPoint.x, bounds.height); } InternalDecorator.this.validate(); e.consume(); } } @NotNull @Override public Cursor getCursor() { final boolean isVerticalCursor = myInfo.isDocked() ? myInfo.getAnchor().isSplitVertically() : myInfo.getAnchor().isHorizontal(); return isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR); } } @Override public void putInfo(@NotNull Map<String, String> info) { info.put("toolWindowTitle", myToolWindow.getTitle()); final Content selection = myToolWindow.getContentManager().getSelectedContent(); if (selection != null) { info.put("toolWindowTab", selection.getTabName()); } } public void setAdditionalGearActions(@Nullable ActionGroup additionalGearActions) { myAdditionalGearActions = additionalGearActions; } }
/** @author Robert F. Beeger ([email protected]) */ @State( name = "Osmorc", storages = {@Storage(file = "$PROJECT_FILE$")}) public class ProjectSettings implements PersistentStateComponent<ProjectSettings> { private EventDispatcher<ProjectSettingsListener> dispatcher = EventDispatcher.create(ProjectSettingsListener.class); @Nullable public String getBundlesOutputPath() { return _bundlesOutputPath; } public void setBundlesOutputPath(@Nullable String _bundlesOutputPath) { this._bundlesOutputPath = _bundlesOutputPath; } @NotNull public static String getDefaultBundlesOutputPath(Project project) { CompilerProjectExtension instance = CompilerProjectExtension.getInstance(project); if (instance != null) { final VirtualFilePointer compilerOutput = instance.getCompilerOutputPointer(); if (compilerOutput != null) { return VfsUtil.urlToPath(compilerOutput.getUrl()) + "/bundles"; } } // this actually should never happen (only in tests) return FileUtil.getTempDirectory(); } public static ProjectSettings getInstance(Project project) { return ServiceManager.getService(project, ProjectSettings.class); } @Nullable public String getFrameworkInstanceName() { return _frameworkInstanceName; } public void setFrameworkInstanceName(@Nullable String frameworkInstanceName) { _frameworkInstanceName = frameworkInstanceName; dispatcher.getMulticaster().projectSettingsChanged(); } @NotNull public ProjectSettings getState() { return this; } public void loadState(@NotNull ProjectSettings state) { XmlSerializerUtil.copyBean(state, this); } public void setCreateFrameworkInstanceModule(boolean selected) { _createFrameworkInstanceModule = selected; dispatcher.getMulticaster().projectSettingsChanged(); } public boolean isCreateFrameworkInstanceModule() { return _createFrameworkInstanceModule; } public void setDefaultManifestFileLocation(@NotNull String defaultManifestFileLocation) { _defaultManifestFileLocation = defaultManifestFileLocation; if (_defaultManifestFileLocation.equals("META-INF")) { // we specify full names, so to work with older projects, we have to convert this _defaultManifestFileLocation = "META-INF/MANIFEST.MF"; } dispatcher.getMulticaster().projectSettingsChanged(); } public void addProjectSettingsListener(ProjectSettingsListener listener) { dispatcher.addListener(listener); } public void removeProjectSettingsListener(ProjectSettingsListener listener) { dispatcher.removeListener(listener); } @NotNull public String getDefaultManifestFileLocation() { return _defaultManifestFileLocation; } private @Nullable String _frameworkInstanceName; private boolean _createFrameworkInstanceModule; private @NotNull String _defaultManifestFileLocation = "META-INF/MANIFEST.MF"; private @Nullable String _bundlesOutputPath; public interface ProjectSettingsListener extends EventListener { void projectSettingsChanged(); } }
@State( name = "UISettings", storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/ui.lnf.xml")}) public class UISettings extends SimpleModificationTracker implements PersistentStateComponent<UISettings>, ExportableApplicationComponent { /** Not tabbed pane. */ public static final int TABS_NONE = 0; public static UISettings getInstance() { return ApplicationManager.getApplication().getComponent(UISettings.class); } /** * Use this method if you are not sure whether the application is initialized. * * @return persisted UISettings instance or default values. */ public static UISettings getShadowInstance() { Application application = ApplicationManager.getApplication(); return application != null ? getInstance() : new UISettings(); } @Property(filter = FontFilter.class) public String FONT_FACE; @Property(filter = FontFilter.class) public int FONT_SIZE; public int RECENT_FILES_LIMIT = 50; public int CONSOLE_COMMAND_HISTORY_LIMIT = 300; public int EDITOR_TAB_LIMIT = 10; public int EDITOR_TAB_TITLE_LIMIT = 100; public boolean ANIMATE_WINDOWS = true; public int ANIMATION_SPEED = 2000; // Pixels per second public boolean SHOW_TOOL_WINDOW_NUMBERS = true; public boolean HIDE_TOOL_STRIPES = true; public boolean WIDESCREEN_SUPPORT = false; public boolean LEFT_HORIZONTAL_SPLIT = false; public boolean RIGHT_HORIZONTAL_SPLIT = false; public boolean SHOW_EDITOR_TOOLTIP = true; public boolean SHOW_MEMORY_INDICATOR = false; public boolean ALLOW_MERGE_BUTTONS = true; public boolean SHOW_MAIN_TOOLBAR = false; public boolean SHOW_STATUS_BAR = true; public boolean SHOW_NAVIGATION_BAR = true; public boolean ALWAYS_SHOW_WINDOW_BUTTONS = false; public boolean CYCLE_SCROLLING = true; public boolean SCROLL_TAB_LAYOUT_IN_EDITOR = false; public boolean SHOW_CLOSE_BUTTON = true; public int EDITOR_TAB_PLACEMENT = 1; public boolean HIDE_KNOWN_EXTENSION_IN_TABS = false; public boolean SHOW_ICONS_IN_QUICK_NAVIGATION = true; public boolean CLOSE_NON_MODIFIED_FILES_FIRST = false; public boolean ACTIVATE_MRU_EDITOR_ON_CLOSE = false; public boolean ACTIVATE_RIGHT_EDITOR_ON_CLOSE = false; public boolean ANTIALIASING_IN_EDITOR = true; public boolean MOVE_MOUSE_ON_DEFAULT_BUTTON = false; public boolean ENABLE_ALPHA_MODE = false; public int ALPHA_MODE_DELAY = 1500; public float ALPHA_MODE_RATIO = 0.5f; public int MAX_CLIPBOARD_CONTENTS = 5; public boolean OVERRIDE_NONIDEA_LAF_FONTS = false; public boolean SHOW_ICONS_IN_MENUS = true; public boolean DISABLE_MNEMONICS = SystemInfo.isMac; // IDEADEV-33409, should be disabled by default on MacOS public boolean DISABLE_MNEMONICS_IN_CONTROLS = false; public boolean USE_SMALL_LABELS_ON_TABS = SystemInfo.isMac; public boolean SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY = false; public int MAX_LOOKUP_WIDTH2 = 500; public int MAX_LOOKUP_LIST_HEIGHT = 11; public boolean HIDE_NAVIGATION_ON_FOCUS_LOSS = true; public boolean DND_WITH_PRESSED_ALT_ONLY = false; public boolean FILE_COLORS_IN_PROJECT_VIEW = false; public boolean DEFAULT_AUTOSCROLL_TO_SOURCE = false; @Transient public boolean PRESENTATION_MODE = false; public int PRESENTATION_MODE_FONT_SIZE = 24; public boolean MARK_MODIFIED_TABS_WITH_ASTERISK = false; public boolean SHOW_TABS_TOOLTIPS = true; public boolean SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true; private final EventDispatcher<UISettingsListener> myDispatcher = EventDispatcher.create(UISettingsListener.class); public UISettings() { tweakPlatformDefaults(); setSystemFontFaceAndSize(); } private void tweakPlatformDefaults() { // TODO[anton] consider making all IDEs use the same settings if (PlatformUtilsCore.isAppCode()) { SCROLL_TAB_LAYOUT_IN_EDITOR = true; ACTIVATE_RIGHT_EDITOR_ON_CLOSE = true; SHOW_ICONS_IN_MENUS = false; } } /** * @deprecated use {@link UISettings#addUISettingsListener(com.intellij.ide.ui.UISettingsListener, * Disposable disposable)} instead. */ public void addUISettingsListener(UISettingsListener listener) { myDispatcher.addListener(listener); } public void addUISettingsListener( @NotNull final UISettingsListener listener, @NotNull Disposable parentDisposable) { myDispatcher.addListener(listener, parentDisposable); } /** Notifies all registered listeners that UI settings has been changed. */ public void fireUISettingsChanged() { incModificationCount(); myDispatcher.getMulticaster().uiSettingsChanged(this); ApplicationManager.getApplication() .getMessageBus() .syncPublisher(UISettingsListener.TOPIC) .uiSettingsChanged(this); } public void removeUISettingsListener(UISettingsListener listener) { myDispatcher.removeListener(listener); } private void setSystemFontFaceAndSize() { if (FONT_FACE == null || FONT_SIZE <= 0) { final Pair<String, Integer> fontData = getSystemFontFaceAndSize(); FONT_FACE = fontData.first; FONT_SIZE = fontData.second; } } private static Pair<String, Integer> getSystemFontFaceAndSize() { final Pair<String, Integer> fontData = UIUtil.getSystemFontData(); if (fontData != null) { return fontData; } return Pair.create("Dialog", 12); } public static class FontFilter implements SerializationFilter { public boolean accepts(Accessor accessor, Object bean) { UISettings settings = (UISettings) bean; return !hasDefaultFontSetting(settings); } } private static boolean hasDefaultFontSetting(final UISettings settings) { final Pair<String, Integer> fontData = getSystemFontFaceAndSize(); return fontData.first.equals(settings.FONT_FACE) && fontData.second.equals(settings.FONT_SIZE); } public UISettings getState() { return this; } public void loadState(UISettings object) { XmlSerializerUtil.copyBean(object, this); // Check tab placement in editor if (EDITOR_TAB_PLACEMENT != TABS_NONE && EDITOR_TAB_PLACEMENT != SwingConstants.TOP && EDITOR_TAB_PLACEMENT != SwingConstants.LEFT && EDITOR_TAB_PLACEMENT != SwingConstants.BOTTOM && EDITOR_TAB_PLACEMENT != SwingConstants.RIGHT) { EDITOR_TAB_PLACEMENT = SwingConstants.TOP; } // Check that alpha delay and ratio are valid if (ALPHA_MODE_DELAY < 0) { ALPHA_MODE_DELAY = 1500; } if (ALPHA_MODE_RATIO < 0.0f || ALPHA_MODE_RATIO > 1.0f) { ALPHA_MODE_RATIO = 0.5f; } setSystemFontFaceAndSize(); // 1. Sometimes system font cannot display standard ASCII symbols. If so we have // find any other suitable font withing "preferred" fonts first. boolean fontIsValid = isValidFont(new Font(FONT_FACE, Font.PLAIN, FONT_SIZE)); if (!fontIsValid) { @NonNls final String[] preferredFonts = {"dialog", "Arial", "Tahoma"}; for (String preferredFont : preferredFonts) { if (isValidFont(new Font(preferredFont, Font.PLAIN, FONT_SIZE))) { FONT_FACE = preferredFont; fontIsValid = true; break; } } // 2. If all preferred fonts are not valid in current environment // we have to find first valid font (if any) if (!fontIsValid) { String[] fontNames = UIUtil.getValidFontNames(false); if (fontNames.length > 0) { FONT_FACE = fontNames[0]; } } } if (MAX_CLIPBOARD_CONTENTS <= 0) { MAX_CLIPBOARD_CONTENTS = 5; } fireUISettingsChanged(); } private static final boolean DEFAULT_ALIASING = SystemProperties.getBooleanProperty("idea.use.default.antialiasing.in.editor", false); private static final boolean FORCE_USE_FRACTIONAL_METRICS = SystemProperties.getBooleanProperty("idea.force.use.fractional.metrics", false); public static void setupAntialiasing(final Graphics g) { if (DEFAULT_ALIASING) return; Graphics2D g2d = (Graphics2D) g; UISettings uiSettings = getInstance(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); if (!isRemoteDesktopConnected() && UIUtil.isRetina()) { g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } else { if (uiSettings == null || uiSettings.ANTIALIASING_IN_EDITOR) { Toolkit tk = Toolkit.getDefaultToolkit(); //noinspection HardCodedStringLiteral Map map = (Map) tk.getDesktopProperty("awt.font.desktophints"); if (map != null) { if (isRemoteDesktopConnected()) { g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); } else { g2d.addRenderingHints(map); } } else { g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } if (FORCE_USE_FRACTIONAL_METRICS) { g2d.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); } } else { g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); } } } /** @return true when Remote Desktop (i.e. Windows RDP) is connected */ // TODO[neuro]: move to UIUtil public static boolean isRemoteDesktopConnected() { if (System.getProperty("os.name").contains("Windows")) { final Map map = (Map) Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints"); return map != null && RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT.equals( map.get(RenderingHints.KEY_TEXT_ANTIALIASING)); } return false; } @NotNull @Override public File[] getExportFiles() { return new File[] {PathManager.getOptionsFile("ui.lnf")}; } @NotNull @Override public String getPresentableName() { return IdeBundle.message("ui.settings"); } @NonNls @NotNull @Override public String getComponentName() { return "UISettings"; } @Override public void initComponent() {} @Override public void disposeComponent() {} }
public class ClasspathPanelImpl extends JPanel implements ClasspathPanel { private static final Logger LOG = Logger.getInstance( "#com.intellij.openapi.roots.ui.configuration.classpath.ClasspathPanelImpl"); private final JBTable myEntryTable; private final ClasspathTableModel myModel; private final EventDispatcher<OrderPanelListener> myListeners = EventDispatcher.create(OrderPanelListener.class); private List<AddItemPopupAction<?>> myPopupActions = null; private AnActionButton myEditButton; private final ModuleConfigurationState myState; private AnActionButton myRemoveButton; public ClasspathPanelImpl(ModuleConfigurationState state) { super(new BorderLayout()); myState = state; myModel = new ClasspathTableModel(state, getStructureConfigurableContext()); myEntryTable = new JBTable(myModel) { @Override protected TableRowSorter<TableModel> createRowSorter(TableModel model) { return new DefaultColumnInfoBasedRowSorter(model) { @Override public void toggleSortOrder(int column) { if (isSortable(column)) { SortKey oldKey = ContainerUtil.getFirstItem(getSortKeys()); SortOrder oldOrder; if (oldKey == null || oldKey.getColumn() != column) { oldOrder = SortOrder.UNSORTED; } else { oldOrder = oldKey.getSortOrder(); } setSortKeys( Collections.singletonList(new SortKey(column, getNextSortOrder(oldOrder)))); } } }; } }; myEntryTable.setShowGrid(false); myEntryTable.setDragEnabled(false); myEntryTable.setIntercellSpacing(new Dimension(0, 0)); myEntryTable.setDefaultRenderer( ClasspathTableItem.class, new TableItemRenderer(getStructureConfigurableContext())); myEntryTable.setDefaultRenderer( Boolean.class, new ExportFlagRenderer(myEntryTable.getDefaultRenderer(Boolean.class))); JComboBox scopeEditor = new ComboBox(new EnumComboBoxModel<DependencyScope>(DependencyScope.class)); myEntryTable.setDefaultEditor(DependencyScope.class, new DefaultCellEditor(scopeEditor)); myEntryTable.setDefaultRenderer( DependencyScope.class, new ComboBoxTableRenderer<DependencyScope>(DependencyScope.values()) { @Override protected String getTextFor(@NotNull final DependencyScope value) { return value.getDisplayName(); } }); myEntryTable.setTransferHandler( new TransferHandler() { @Nullable @Override protected Transferable createTransferable(JComponent c) { OrderEntry entry = getSelectedEntry(); if (entry == null) return null; String text = entry.getPresentableName(); return new TextTransferable(text); } @Override public int getSourceActions(JComponent c) { return COPY; } }); myEntryTable .getSelectionModel() .setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); new SpeedSearchBase<JBTable>(myEntryTable) { @Override public int getSelectedIndex() { return myEntryTable.getSelectedRow(); } @Override protected int convertIndexToModel(int viewIndex) { return myEntryTable.convertRowIndexToModel(viewIndex); } @Override public Object[] getAllElements() { final int count = myModel.getRowCount(); Object[] elements = new Object[count]; for (int idx = 0; idx < count; idx++) { elements[idx] = myModel.getItem(idx); } return elements; } @Override public String getElementText(Object element) { return getCellAppearance( (ClasspathTableItem<?>) element, getStructureConfigurableContext(), false) .getText(); } @Override public void selectElement(Object element, String selectedText) { final int count = myModel.getRowCount(); for (int row = 0; row < count; row++) { if (element.equals(myModel.getItem(row))) { final int viewRow = myEntryTable.convertRowIndexToView(row); myEntryTable.getSelectionModel().setSelectionInterval(viewRow, viewRow); TableUtil.scrollSelectionToVisible(myEntryTable); break; } } } }; setFixedColumnWidth(ClasspathTableModel.EXPORT_COLUMN); setFixedColumnWidth(ClasspathTableModel.SCOPE_COLUMN); // leave space for combobox border myEntryTable.registerKeyboardAction( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final int[] selectedRows = myEntryTable.getSelectedRows(); boolean currentlyMarked = true; for (final int selectedRow : selectedRows) { final ClasspathTableItem<?> item = getItemAt(selectedRow); if (selectedRow < 0 || !item.isExportable()) { return; } currentlyMarked &= item.isExported(); } for (final int selectedRow : selectedRows) { getItemAt(selectedRow).setExported(!currentlyMarked); } myModel.fireTableDataChanged(); TableUtil.selectRows(myEntryTable, selectedRows); } }, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), WHEN_FOCUSED); myEditButton = new AnActionButton( ProjectBundle.message("module.classpath.button.edit"), null, IconUtil.getEditIcon()) { @Override public void actionPerformed(@NotNull AnActionEvent e) { doEdit(); } @Override public boolean isEnabled() { ClasspathTableItem<?> selectedItem = getSelectedItem(); return selectedItem != null && selectedItem.isEditable(); } @Override public boolean isDumbAware() { return true; } }; add(createTableWithButtons(), BorderLayout.CENTER); if (myEntryTable.getRowCount() > 0) { myEntryTable.getSelectionModel().setSelectionInterval(0, 0); } new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent e) { navigate(true); return true; } }.installOn(myEntryTable); DefaultActionGroup actionGroup = new DefaultActionGroup(); final AnAction navigateAction = new AnAction(ProjectBundle.message("classpath.panel.navigate.action.text")) { @Override public void actionPerformed(@NotNull AnActionEvent e) { navigate(false); } @Override public void update(@NotNull AnActionEvent e) { final Presentation presentation = e.getPresentation(); presentation.setEnabled(false); final OrderEntry entry = getSelectedEntry(); if (entry != null && entry.isValid()) { if (!(entry instanceof ModuleSourceOrderEntry)) { presentation.setEnabled(true); } } } }; navigateAction.registerCustomShortcutSet( ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE).getShortcutSet(), myEntryTable); actionGroup.add(myEditButton); actionGroup.add(myRemoveButton); actionGroup.add(navigateAction); actionGroup.add(new InlineModuleDependencyAction(this)); actionGroup.add(new MyFindUsagesAction()); actionGroup.add(new AnalyzeDependencyAction()); addChangeLibraryLevelAction(actionGroup, LibraryTablesRegistrar.PROJECT_LEVEL); addChangeLibraryLevelAction(actionGroup, LibraryTablesRegistrar.APPLICATION_LEVEL); addChangeLibraryLevelAction(actionGroup, LibraryTableImplUtil.MODULE_LEVEL); PopupHandler.installPopupHandler( myEntryTable, actionGroup, ActionPlaces.UNKNOWN, ActionManager.getInstance()); } @NotNull private static SortOrder getNextSortOrder(@NotNull SortOrder order) { switch (order) { case ASCENDING: return SortOrder.DESCENDING; case DESCENDING: return SortOrder.UNSORTED; case UNSORTED: default: return SortOrder.ASCENDING; } } private ClasspathTableItem<?> getItemAt(int selectedRow) { return myModel.getItem(myEntryTable.convertRowIndexToModel(selectedRow)); } private void addChangeLibraryLevelAction(DefaultActionGroup actionGroup, String tableLevel) { final LibraryTablePresentation presentation = LibraryEditingUtil.getLibraryTablePresentation(getProject(), tableLevel); actionGroup.add( new ChangeLibraryLevelInClasspathAction( this, presentation.getDisplayName(true), tableLevel)); } @Override @Nullable public OrderEntry getSelectedEntry() { ClasspathTableItem<?> item = getSelectedItem(); return item != null ? item.getEntry() : null; } @Nullable private ClasspathTableItem<?> getSelectedItem() { if (myEntryTable.getSelectedRowCount() != 1) return null; return getItemAt(myEntryTable.getSelectedRow()); } private void setFixedColumnWidth(final int columnIndex) { final TableColumn column = myEntryTable.getTableHeader().getColumnModel().getColumn(columnIndex); column.setResizable(false); column.setMaxWidth(column.getPreferredWidth()); } @Override public void navigate(boolean openLibraryEditor) { final OrderEntry entry = getSelectedEntry(); final ProjectStructureConfigurable rootConfigurable = ProjectStructureConfigurable.getInstance(myState.getProject()); if (entry instanceof ModuleOrderEntry) { Module module = ((ModuleOrderEntry) entry).getModule(); if (module != null) { rootConfigurable.select(module.getName(), null, true); } } else if (entry instanceof LibraryOrderEntry) { if (!openLibraryEditor && !((LibraryOrderEntry) entry) .getLibraryLevel() .equals(LibraryTableImplUtil.MODULE_LEVEL)) { rootConfigurable.select((LibraryOrderEntry) entry, true); } else { doEdit(); } } else if (entry instanceof JdkOrderEntry) { Sdk jdk = ((JdkOrderEntry) entry).getJdk(); if (jdk != null) { rootConfigurable.select(jdk, true); } } } private JComponent createTableWithButtons() { final boolean isAnalyzeShown = false; final ClasspathPanelAction removeAction = new ClasspathPanelAction(this) { @Override public void run() { removeSelectedItems(TableUtil.removeSelectedItems(myEntryTable)); } }; final AnActionButton analyzeButton = new AnActionButton( ProjectBundle.message("classpath.panel.analyze"), null, IconUtil.getAnalyzeIcon()) { @Override public void actionPerformed(@NotNull AnActionEvent e) { AnalyzeDependenciesDialog.show(getRootModel().getModule()); } }; // addButton.setShortcut(CustomShortcutSet.fromString("alt A", "INSERT")); // removeButton.setShortcut(CustomShortcutSet.fromString("alt DELETE")); // upButton.setShortcut(CustomShortcutSet.fromString("alt UP")); // downButton.setShortcut(CustomShortcutSet.fromString("alt DOWN")); final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myEntryTable); AnActionButtonUpdater moveUpDownUpdater = new AnActionButtonUpdater() { @Override public boolean isEnabled(AnActionEvent e) { for (RowSorter.SortKey key : myEntryTable.getRowSorter().getSortKeys()) { if (key.getSortOrder() != SortOrder.UNSORTED) { return false; } } return true; } }; decorator .setAddAction( new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { initPopupActions(); final JBPopup popup = JBPopupFactory.getInstance() .createListPopup( new BaseListPopupStep<AddItemPopupAction<?>>(null, myPopupActions) { @Override public Icon getIconFor(AddItemPopupAction<?> aValue) { return aValue.getIcon(); } @Override public boolean hasSubstep(AddItemPopupAction<?> selectedValue) { return selectedValue.hasSubStep(); } @Override public boolean isMnemonicsNavigationEnabled() { return true; } @Override public PopupStep onChosen( final AddItemPopupAction<?> selectedValue, final boolean finalChoice) { if (selectedValue.hasSubStep()) { return selectedValue.createSubStep(); } return doFinalStep( new Runnable() { @Override public void run() { selectedValue.execute(); } }); } @Override @NotNull public String getTextFor(AddItemPopupAction<?> value) { return "&" + value.getIndex() + " " + value.getTitle(); } }); popup.show(button.getPreferredPopupPoint()); } }) .setRemoveAction( new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { removeAction.actionPerformed(null); } }) .setRemoveActionUpdater( new AnActionButtonUpdater() { @Override public boolean isEnabled(AnActionEvent e) { final int[] selectedRows = myEntryTable.getSelectedRows(); for (final int selectedRow : selectedRows) { if (!getItemAt(selectedRow).isRemovable()) { return false; } } return selectedRows.length > 0; } }) .setMoveUpAction( new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { moveSelectedRows(-1); } }) .setMoveUpActionUpdater(moveUpDownUpdater) .setMoveUpActionName("Move Up (disabled if items are shown in sorted order)") .setMoveDownAction( new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { moveSelectedRows(+1); } }) .setMoveDownActionUpdater(moveUpDownUpdater) .setMoveDownActionName("Move Down (disabled if items are shown in sorted order)") .addExtraAction(myEditButton); if (isAnalyzeShown) { decorator.addExtraAction(analyzeButton); } final JPanel panel = decorator.createPanel(); myRemoveButton = ToolbarDecorator.findRemoveButton(panel); return panel; } private void doEdit() { final OrderEntry entry = getSelectedEntry(); if (!(entry instanceof LibraryOrderEntry)) return; final Library library = ((LibraryOrderEntry) entry).getLibrary(); if (library == null) { return; } final LibraryTable table = library.getTable(); final String tableLevel = table != null ? table.getTableLevel() : LibraryTableImplUtil.MODULE_LEVEL; final LibraryTablePresentation presentation = LibraryEditingUtil.getLibraryTablePresentation(getProject(), tableLevel); final LibraryTableModifiableModelProvider provider = getModifiableModelProvider(tableLevel); EditExistingLibraryDialog dialog = EditExistingLibraryDialog.createDialog( this, provider, library, myState.getProject(), presentation, getStructureConfigurableContext()); dialog.setContextModule(getRootModel().getModule()); dialog.show(); myEntryTable.repaint(); ModuleStructureConfigurable.getInstance(myState.getProject()).getTree().repaint(); } private void removeSelectedItems(final List removedRows) { if (removedRows.isEmpty()) { return; } for (final Object removedRow : removedRows) { final ClasspathTableItem<?> item = (ClasspathTableItem<?>) ((Object[]) removedRow)[ClasspathTableModel.ITEM_COLUMN]; final OrderEntry orderEntry = item.getEntry(); if (orderEntry == null) { continue; } getRootModel().removeOrderEntry(orderEntry); } final int[] selectedRows = myEntryTable.getSelectedRows(); myModel.fireTableDataChanged(); TableUtil.selectRows(myEntryTable, selectedRows); final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myState.getProject()).getContext(); context .getDaemonAnalyzer() .queueUpdate(new ModuleProjectStructureElement(context, getRootModel().getModule())); } @Override @NotNull public LibraryTableModifiableModelProvider getModifiableModelProvider( @NotNull String tableLevel) { if (LibraryTableImplUtil.MODULE_LEVEL.equals(tableLevel)) { final LibraryTable moduleLibraryTable = getRootModel().getModuleLibraryTable(); return new LibraryTableModifiableModelProvider() { @Override public LibraryTable.ModifiableModel getModifiableModel() { return moduleLibraryTable.getModifiableModel(); } }; } else { return getStructureConfigurableContext().createModifiableModelProvider(tableLevel); } } @Override public void runClasspathPanelAction(Runnable action) { try { disableModelUpdate(); action.run(); } finally { enableModelUpdate(); myEntryTable.requestFocus(); } } @Override public void addItems(List<ClasspathTableItem<?>> toAdd) { for (ClasspathTableItem<?> item : toAdd) { myModel.addRow(item); } TIntArrayList toSelect = new TIntArrayList(); for (int i = myModel.getRowCount() - toAdd.size(); i < myModel.getRowCount(); i++) { toSelect.add(myEntryTable.convertRowIndexToView(i)); } TableUtil.selectRows(myEntryTable, toSelect.toNativeArray()); TableUtil.scrollSelectionToVisible(myEntryTable); final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myState.getProject()).getContext(); context .getDaemonAnalyzer() .queueUpdate(new ModuleProjectStructureElement(context, getRootModel().getModule())); } @Override public ModifiableRootModel getRootModel() { return myState.getRootModel(); } @Override public Project getProject() { return myState.getProject(); } @Override public ModuleConfigurationState getModuleConfigurationState() { return myState; } @Override public JComponent getComponent() { return this; } public void rootsChanged() { forceInitFromModel(); } private void initPopupActions() { if (myPopupActions == null) { int actionIndex = 1; final List<AddItemPopupAction<?>> actions = new ArrayList<AddItemPopupAction<?>>(); final StructureConfigurableContext context = getStructureConfigurableContext(); actions.add(new AddNewModuleLibraryAction(this, actionIndex++, context)); actions.add( new AddLibraryDependencyAction( this, actionIndex++, ProjectBundle.message("classpath.add.library.action"), context)); actions.add(new AddModuleDependencyAction(this, actionIndex, context)); myPopupActions = actions; } } private StructureConfigurableContext getStructureConfigurableContext() { return ProjectStructureConfigurable.getInstance(myState.getProject()).getContext(); } private void enableModelUpdate() { myInsideChange--; } private void disableModelUpdate() { myInsideChange++; } public void addListener(OrderPanelListener listener) { myListeners.addListener(listener); } public void removeListener(OrderPanelListener listener) { myListeners.removeListener(listener); } private void moveSelectedRows(int increment) { LOG.assertTrue(increment == -1 || increment == 1); if (myEntryTable.isEditing()) { myEntryTable.getCellEditor().stopCellEditing(); } final ListSelectionModel selectionModel = myEntryTable.getSelectionModel(); for (int row = increment < 0 ? 0 : myModel.getRowCount() - 1; increment < 0 ? row < myModel.getRowCount() : row >= 0; row += increment < 0 ? +1 : -1) { if (selectionModel.isSelectedIndex(row)) { final int newRow = moveRow(row, increment); selectionModel.removeSelectionInterval(row, row); selectionModel.addSelectionInterval(newRow, newRow); } } Rectangle cellRect = myEntryTable.getCellRect(selectionModel.getMinSelectionIndex(), 0, true); myEntryTable.scrollRectToVisible(cellRect); myEntryTable.repaint(); } public void selectOrderEntry(@NotNull OrderEntry entry) { for (int row = 0; row < myModel.getRowCount(); row++) { final OrderEntry orderEntry = getItemAt(row).getEntry(); if (orderEntry != null && entry.getPresentableName().equals(orderEntry.getPresentableName())) { myEntryTable.getSelectionModel().setSelectionInterval(row, row); TableUtil.scrollSelectionToVisible(myEntryTable); } } IdeFocusManager.getInstance(myState.getProject()).requestFocus(myEntryTable, true); } private int moveRow(final int row, final int increment) { int newIndex = Math.abs(row + increment) % myModel.getRowCount(); myModel.exchangeRows(row, newIndex); return newIndex; } public void stopEditing() { TableUtil.stopEditing(myEntryTable); } private int myInsideChange = 0; public void initFromModel() { if (myInsideChange == 0) { forceInitFromModel(); } } public void forceInitFromModel() { Set<ClasspathTableItem<?>> oldSelection = new HashSet<ClasspathTableItem<?>>(); for (int i : myEntryTable.getSelectedRows()) { ContainerUtil.addIfNotNull(getItemAt(i), oldSelection); } myModel.clear(); myModel.init(); myModel.fireTableDataChanged(); TIntArrayList newSelection = new TIntArrayList(); for (int i = 0; i < myModel.getRowCount(); i++) { if (oldSelection.contains(getItemAt(i))) { newSelection.add(i); } } TableUtil.selectRows(myEntryTable, newSelection.toNativeArray()); } static CellAppearanceEx getCellAppearance( final ClasspathTableItem<?> item, final StructureConfigurableContext context, final boolean selected) { final OrderEntryAppearanceService service = OrderEntryAppearanceService.getInstance(); if (item instanceof InvalidJdkItem) { return service.forJdk(null, false, selected, true); } else { final OrderEntry entry = item.getEntry(); assert entry != null : item; return service.forOrderEntry(context.getProject(), entry, selected); } } private static class TableItemRenderer extends ColoredTableCellRenderer { private final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1); private StructureConfigurableContext myContext; public TableItemRenderer(StructureConfigurableContext context) { myContext = context; } @Override protected void customizeCellRenderer( JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { setPaintFocusBorder(false); setFocusBorderAroundIcon(true); setBorder(NO_FOCUS_BORDER); if (value instanceof ClasspathTableItem<?>) { final ClasspathTableItem<?> tableItem = (ClasspathTableItem<?>) value; getCellAppearance(tableItem, myContext, selected).customize(this); setToolTipText(tableItem.getTooltipText()); } } } private static class ExportFlagRenderer implements TableCellRenderer { private final TableCellRenderer myDelegate; private final JPanel myBlankPanel; public ExportFlagRenderer(TableCellRenderer delegate) { myDelegate = delegate; myBlankPanel = new JPanel(); } @Override public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (!table.isCellEditable(row, column)) { myBlankPanel.setBackground( isSelected ? table.getSelectionBackground() : table.getBackground()); return myBlankPanel; } return myDelegate.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column); } } private class MyFindUsagesAction extends FindUsagesInProjectStructureActionBase { private MyFindUsagesAction() { super(myEntryTable, myState.getProject()); } @Override protected boolean isEnabled() { return getSelectedElement() != null; } @Override protected ProjectStructureElement getSelectedElement() { final OrderEntry entry = getSelectedEntry(); if (entry instanceof LibraryOrderEntry) { final Library library = ((LibraryOrderEntry) entry).getLibrary(); if (library != null) { return new LibraryProjectStructureElement(getContext(), library); } } else if (entry instanceof ModuleOrderEntry) { final Module module = ((ModuleOrderEntry) entry).getModule(); if (module != null) { return new ModuleProjectStructureElement(getContext(), module); } } else if (entry instanceof JdkOrderEntry) { final Sdk jdk = ((JdkOrderEntry) entry).getJdk(); if (jdk != null) { return new SdkProjectStructureElement(getContext(), jdk); } } return null; } @Override protected RelativePoint getPointToShowResults() { Rectangle rect = myEntryTable.getCellRect(myEntryTable.getSelectedRow(), 1, false); Point location = rect.getLocation(); location.y += rect.height; return new RelativePoint(myEntryTable, location); } } private class AnalyzeDependencyAction extends AnAction { private AnalyzeDependencyAction() { super("Analyze This Dependency"); } @Override public void actionPerformed(@NotNull AnActionEvent e) { final OrderEntry selectedEntry = getSelectedEntry(); GlobalSearchScope targetScope; if (selectedEntry instanceof ModuleOrderEntry) { final Module module = ((ModuleOrderEntry) selectedEntry).getModule(); LOG.assertTrue(module != null); targetScope = GlobalSearchScope.moduleScope(module); } else { Library library = ((LibraryOrderEntry) selectedEntry).getLibrary(); LOG.assertTrue(library != null); targetScope = new LibraryScope(getProject(), library); } new AnalyzeDependenciesOnSpecifiedTargetHandler( getProject(), new AnalysisScope(myState.getRootModel().getModule()), targetScope) { @Override protected boolean shouldShowDependenciesPanel(List<DependenciesBuilder> builders) { for (DependenciesBuilder builder : builders) { for (Set<PsiFile> files : builder.getDependencies().values()) { if (!files.isEmpty()) { Messages.showInfoMessage( myProject, "Dependencies were successfully collected in \"" + ToolWindowId.DEPENDENCIES + "\" toolwindow", FindBundle.message("find.pointcut.applications.not.found.title")); return true; } } } if (Messages.showOkCancelDialog( myProject, "No code dependencies were found. Would you like to remove the dependency?", CommonBundle.getWarningTitle(), Messages.getWarningIcon()) == Messages.OK) { removeSelectedItems(TableUtil.removeSelectedItems(myEntryTable)); } return false; } }.analyze(); } @Override public void update(@NotNull AnActionEvent e) { final OrderEntry entry = getSelectedEntry(); e.getPresentation() .setVisible( entry instanceof ModuleOrderEntry && ((ModuleOrderEntry) entry).getModule() != null || entry instanceof LibraryOrderEntry && ((LibraryOrderEntry) entry).getLibrary() != null); } } }
/** Created with IntelliJ IDEA. User: Irina.Chernushina Date: 1/25/12 Time: 12:58 PM */ public abstract class SvnCommand { static final Logger LOG = Logger.getInstance(SvnCommand.class.getName()); private final File myConfigDir; private boolean myIsDestroyed; private int myExitCode; protected final GeneralCommandLine myCommandLine; private final File myWorkingDirectory; private Process myProcess; private OSProcessHandler myHandler; // TODO: Try to implement commands in a way that they manually indicate if they need full output - // to prevent situations // TODO: when large amount of data needs to be stored instead of just sequential processing. private CapturingProcessAdapter outputAdapter; private final Object myLock; private final EventDispatcher<ProcessEventListener> myListeners = EventDispatcher.create(ProcessEventListener.class); private final SvnCommandName myCommandName; public SvnCommand( File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) { this(workingDirectory, commandName, exePath, null); } public SvnCommand( File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath, @Nullable File configDir) { myCommandName = commandName; myLock = new Object(); myCommandLine = new GeneralCommandLine(); myWorkingDirectory = workingDirectory; myCommandLine.setExePath(exePath); myCommandLine.setWorkDirectory(workingDirectory); myConfigDir = configDir; if (configDir != null) { myCommandLine.addParameters("--config-dir", configDir.getPath()); } myCommandLine.addParameter(commandName.getName()); } public String[] getParameters() { synchronized (myLock) { return myCommandLine.getParametersList().getArray(); } } /** * Indicates if process was destroyed "manually" by command execution logic. * * @return */ public boolean isManuallyDestroyed() { return myIsDestroyed; } public void start() { synchronized (myLock) { checkNotStarted(); try { myProcess = myCommandLine.createProcess(); if (LOG.isDebugEnabled()) { LOG.debug(myCommandLine.toString()); } myHandler = new OSProcessHandler(myProcess, myCommandLine.getCommandLineString()); startHandlingStreams(); } catch (Throwable t) { myListeners.getMulticaster().startFailed(t); } } } private void startHandlingStreams() { final ProcessListener processListener = new ProcessListener() { public void startNotified(final ProcessEvent event) { // do nothing } public void processTerminated(final ProcessEvent event) { final int exitCode = event.getExitCode(); try { setExitCode(exitCode); SvnCommand.this.processTerminated(exitCode); } finally { listeners().processTerminated(exitCode); } } public void processWillTerminate( final ProcessEvent event, final boolean willBeDestroyed) { // do nothing } public void onTextAvailable(final ProcessEvent event, final Key outputType) { SvnCommand.this.onTextAvailable(event.getText(), outputType); } }; outputAdapter = new CapturingProcessAdapter(); myHandler.addProcessListener(outputAdapter); myHandler.addProcessListener(processListener); myHandler.startNotify(); } public String getOutput() { return outputAdapter.getOutput().getStdout(); } public String getErrorOutput() { return outputAdapter.getOutput().getStderr(); } /** * Wait for process termination * * @param timeout */ public boolean waitFor(int timeout) { checkStarted(); final OSProcessHandler handler; synchronized (myLock) { // TODO: This line seems to cause situation when exitCode is not set before // SvnLineCommand.runCommand() is finished. // TODO: Carefully analyze behavior (on all operating systems) and fix. if (myIsDestroyed) return true; handler = myHandler; } if (timeout == -1) { return handler.waitFor(); } else { return handler.waitFor(timeout); } } protected abstract void processTerminated(int exitCode); protected abstract void onTextAvailable(final String text, final Key outputType); public void cancel() { synchronized (myLock) { checkStarted(); destroyProcess(); } } protected void setExitCode(final int code) { synchronized (myLock) { myExitCode = code; } } public void addListener(final ProcessEventListener listener) { synchronized (myLock) { myListeners.addListener(listener); } } protected ProcessEventListener listeners() { synchronized (myLock) { return myListeners.getMulticaster(); } } public void addParameters(@NonNls @NotNull String... parameters) { synchronized (myLock) { checkNotStarted(); myCommandLine.addParameters(parameters); } } public void addParameters(List<String> parameters) { synchronized (myLock) { checkNotStarted(); myCommandLine.addParameters(parameters); } } public void destroyProcess() { synchronized (myLock) { if (!myIsDestroyed) { myIsDestroyed = true; myHandler.destroyProcess(); } } } public String getCommandText() { synchronized (myLock) { return myCommandLine.getCommandLineString(); } } public String getExePath() { synchronized (myLock) { return myCommandLine.getExePath(); } } /** * check that process is not started yet * * @throws IllegalStateException if process has been already started */ private void checkNotStarted() { if (isStarted()) { throw new IllegalStateException("The process has been already started"); } } /** * check that process is started * * @throws IllegalStateException if process has not been started */ protected void checkStarted() { if (!isStarted()) { throw new IllegalStateException("The process is not started yet"); } } /** @return true if process is started */ public boolean isStarted() { synchronized (myLock) { return myProcess != null; } } protected int getExitCode() { synchronized (myLock) { return myExitCode; } } protected File getWorkingDirectory() { return myWorkingDirectory; } public SvnCommandName getCommandName() { return myCommandName; } }