diff --git a/php.nette2/src/org/netbeans/modules/php/nette2/Nette2FrameworkProvider.java b/php.nette2/src/org/netbeans/modules/php/nette2/Nette2FrameworkProvider.java --- a/php.nette2/src/org/netbeans/modules/php/nette2/Nette2FrameworkProvider.java +++ b/php.nette2/src/org/netbeans/modules/php/nette2/Nette2FrameworkProvider.java @@ -42,11 +42,17 @@ package org.netbeans.modules.php.nette2; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import org.netbeans.modules.php.api.framework.BadgeIcon; import org.netbeans.modules.php.api.phpmodule.PhpModule; import org.netbeans.modules.php.api.phpmodule.PhpModuleProperties; @@ -97,9 +103,9 @@ if (!result) { FileObject sourceDirectory = phpModule.getSourceDirectory(); if (sourceDirectory != null) { - FileObject bootstrap = sourceDirectory.getFileObject(Constants.COMMON_BOOTSTRAP_PATH); + FileObject bootstrap = getFileObject(sourceDirectory, Constants.COMMON_BOOTSTRAP_PATH); result = bootstrap != null && !bootstrap.isFolder() && bootstrap.isValid(); - FileObject config = sourceDirectory.getFileObject(Constants.COMMON_CONFIG_PATH); + FileObject config = getFileObject(sourceDirectory, Constants.COMMON_CONFIG_PATH); result = result && config != null && config.isFolder() && config.isValid(); } } @@ -181,4 +187,30 @@ return new Nette2CustomizerExtender(phpModule); } + /** + * Try to get a FileObject with correct filename case. See bug 238679. + * + * @param parent Parent FileObject. + * @param relPath Relative path, separated by slashes. + */ + private FileObject getFileObject(FileObject parent, String relPath) { + File parentFile = FileUtil.toFile(parent); + if (parentFile != null) { + String nativePath = relPath.replace('/', File.separatorChar); + try { + Path filePath = parentFile.toPath().resolve(nativePath); + if (!Files.exists(filePath)) { + return null; + } + Path realPath = filePath.toRealPath(LinkOption.NOFOLLOW_LINKS); + return FileUtil.toFileObject(realPath.toFile()); + } catch (IOException ex) { + Logger.getLogger(Nette2FrameworkProvider.class.getName()).log( + Level.FINE, null, ex); + return null; + } + } else { + return null; + } + } } diff --git a/php.symfony2/src/org/netbeans/modules/php/symfony2/commands/Symfony2Script.java b/php.symfony2/src/org/netbeans/modules/php/symfony2/commands/Symfony2Script.java --- a/php.symfony2/src/org/netbeans/modules/php/symfony2/commands/Symfony2Script.java +++ b/php.symfony2/src/org/netbeans/modules/php/symfony2/commands/Symfony2Script.java @@ -47,6 +47,9 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -117,7 +120,22 @@ // perhaps deleted app dir? fallback to default and let it fail later... return null; } - return appDir.getFileObject(SCRIPT_NAME); + File appDirFile = FileUtil.toFile(appDir); // #238679 + if (appDirFile != null) { + try { + Path filePath = appDirFile.toPath().resolve(SCRIPT_NAME); + if (!Files.exists(filePath)) { + return null; + } + Path realPath = filePath.toRealPath(LinkOption.NOFOLLOW_LINKS); + return FileUtil.toFileObject(realPath.toFile()); + } catch (IOException ex) { + LOGGER.log(Level.FINE, null, ex); + return null; + } + } else { + return null; + } } /** diff --git a/spi.debugger.ui/src/org/netbeans/modules/debugger/ui/views/debugging/DebuggingViewComponent.java b/spi.debugger.ui/src/org/netbeans/modules/debugger/ui/views/debugging/DebuggingViewComponent.java --- a/spi.debugger.ui/src/org/netbeans/modules/debugger/ui/views/debugging/DebuggingViewComponent.java +++ b/spi.debugger.ui/src/org/netbeans/modules/debugger/ui/views/debugging/DebuggingViewComponent.java @@ -146,6 +146,7 @@ private Preferences preferences = NbPreferences.forModule(getClass()).node("debugging"); // NOI18N private PreferenceChangeListener prefListener; private SessionsComboBoxListener sessionsComboListener; + private VisibleTreePosition visibleTreePosition = null; private transient ImageIcon resumeIcon; private transient ImageIcon focusedResumeIcon; @@ -265,6 +266,17 @@ treeScrollBar.addAdjustmentListener(this); setSuspendTableVisible(preferences.getBoolean(FiltersDescriptor.SHOW_SUSPEND_TABLE, true)); + + mainScrollPane.getVerticalScrollBar().addAdjustmentListener( + new AdjustmentListener() { + + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + if (e.getValueIsAdjusting()) { + storeScrollPosition(); + } + } + }); } /** This method is called from within the constructor to @@ -577,8 +589,8 @@ } if (currentThread != null) { DVThread thread = threadMadeCurrentRef != null ? threadMadeCurrentRef.get() : null; - if (thread == currentThread) { - threadToScrollRef = new WeakReference(thread); + if (thread != currentThread) { + threadToScrollRef = new WeakReference(currentThread); } } refreshView(); @@ -682,9 +694,32 @@ @Override public void treeExpanded(TreeExpansionEvent event) { + TreePath path = event.getPath(); + checkIfWeShouldScrollToCurrentThread(path); refreshView(); } + /** + * Check whether we should scroll to the current thread. + * + * @param path Path of node that has been just expanded, or that has new + * children. + */ + private void checkIfWeShouldScrollToCurrentThread(TreePath path) { + Node node = Visualizer.findNode(path.getLastPathComponent()); + if (node == null) { + return; + } + DVThread dvThread = node.getLookup().lookup(DVThread.class); + DVThread currentThread; + synchronized (lock) { + currentThread = (debugger != null) ? debugger.getCurrentThread() : null; + } + if (currentThread != null && currentThread == dvThread) { + threadToScrollRef = new WeakReference(dvThread); + } + } + @Override public void treeCollapsed(TreeExpansionEvent event) { refreshView(); @@ -697,6 +732,8 @@ @Override public void treeNodesInserted(TreeModelEvent e) { + TreePath path = e.getTreePath(); + checkIfWeShouldScrollToCurrentThread(path); refreshView(); } @@ -856,6 +893,87 @@ setRootContext(compoundModel, de); } + /** + * Store first visible node and its offset. Called when the view is scrolled + * by the user. + */ + private void storeScrollPosition() { + + JTree tree = getJTree(); + if (tree != null) { + int scrollTop = mainScrollPane.getViewport().getViewPosition().y; + int row = tree.getRowForLocation(tree.getRowBounds(0).x + 1, + scrollTop); + if (row >= 0) { + TreePath path = tree.getPathForRow(row); + if (path != null) { + int offset = tree.getRowBounds(row).y - scrollTop; + visibleTreePosition = new VisibleTreePosition( + path, offset); + return; + } + } + } + visibleTreePosition = null; + } + + /** + * Store visible offset of an important node. Called when the view is + * scrolled by ViewRefresher + * + * @param path Path to a node that should stay visible at the same position. + */ + private void storeScrollPosition(TreePath path) { + JTree tree = getJTree(); + if (tree != null && path != null) { + int row = tree.getRowForPath(path); + if (row > 0) { + int scrollTop = mainScrollPane.getViewport().getViewPosition().y; + int offset = tree.getRowBounds(row).y - scrollTop; + visibleTreePosition = new VisibleTreePosition( + path, offset); + return; + } + } + visibleTreePosition = null; + } + + private JTree getJTree() { + DebugTreeView treeView1 = getTreeView(); + if (treeView1 != null) { + JTree tree = treeView1.getTree(); + if (tree != null && tree.getRowCount() > 0) { + return tree; + } + } + return null; + } + + /** + * Restore stored scroll position. + */ + private void restoreScrollPosition() { + if (visibleTreePosition != null) { + JTree tree = getJTree(); + if (tree != null) { + int row = tree.getRowForPath(visibleTreePosition.getPath()); + if (row != -1) { + Rectangle bounds = tree.getRowBounds(row); + if (bounds != null) { + int scrollY = bounds.y - visibleTreePosition.getOffset(); + JViewport viewport = mainScrollPane.getViewport(); + Rectangle rect = viewport.getViewRect(); + rect.y = scrollY; + if (!rect.isEmpty()) { + ((JComponent) viewport.getView()).scrollRectToVisible( + rect); + } + } + } + } + } + } + // ************************************************************************** // inner classes // ************************************************************************** @@ -893,7 +1011,6 @@ DVThread threadToScroll = threadToScrollRef != null ? threadToScrollRef.get() : null; threadToScrollRef = null; int scrollStart = -1, scrollEnd = -1; - boolean pathToScrollSearching = false; int mainPanelHeight = 0; int treeViewWidth = 0; @@ -904,6 +1021,7 @@ Object currentObject = null; int currentSY = 0; int height = 0; + TreePath scrollPath = null; if (tView != null) { for (TreePath path : tView.getVisiblePaths()) { @@ -916,9 +1034,10 @@ height = rect != null ? (int) Math.round(rect.getHeight()) : 0; if (dvThread != null || dvThreadGroup != null) { - pathToScrollSearching = dvThread == threadToScroll; - if (pathToScrollSearching) { + if (dvThread == threadToScroll) { scrollStart = mainPanelHeight; + scrollEnd = scrollStart + height; + scrollPath = path; } if (currentObject != null) { addPanels(currentObject, isCurrent, isAtBreakpoint, isInDeadlock, @@ -946,8 +1065,8 @@ leftBarHeight += height; sy += height; - if (pathToScrollSearching) { - scrollEnd = mainPanelHeight; + if (scrollPath != null && scrollPath.equals(path.getParentPath())) { + scrollEnd += height; } } // for } // if @@ -977,7 +1096,10 @@ Rectangle aRect = new Rectangle(0, scrollStart, 1, aRectHeight); if (!aRect.isEmpty()) { ((JComponent)viewport.getView()).scrollRectToVisible(aRect); + storeScrollPosition(scrollPath); } + } else { + restoreScrollPosition(); } } @@ -1279,4 +1401,27 @@ } + private class VisibleTreePosition { + + final TreePath path; + final int offset; + + public VisibleTreePosition(TreePath path, int offset) { + this.path = path; + this.offset = offset; + } + + public TreePath getPath() { + return path; + } + + public int getOffset() { + return offset; + } + + @Override + public String toString() { + return "VisibleTreePosition[" + path + ", " + offset + "]"; //NOI18N + } + } }