diff --git a/ide/editor.kit/nbproject/project.xml b/ide/editor.kit/nbproject/project.xml index 2d2b769dd485..8ac9979bb917 100644 --- a/ide/editor.kit/nbproject/project.xml +++ b/ide/editor.kit/nbproject/project.xml @@ -138,6 +138,12 @@ 1.21 + + org.netbeans.modules.svg + + 1.0 + + org.netbeans.modules.xml.core diff --git a/ide/svg/build.xml b/ide/svg/build.xml new file mode 100644 index 000000000000..bb0d0e752c64 --- /dev/null +++ b/ide/svg/build.xml @@ -0,0 +1,25 @@ + + + + Builds, tests, and runs the project org.netbeans.modules.svg + + diff --git a/ide/svg/licenseinfo.xml b/ide/svg/licenseinfo.xml new file mode 100644 index 000000000000..8abdf6d0d889 --- /dev/null +++ b/ide/svg/licenseinfo.xml @@ -0,0 +1,28 @@ + + + + + src/org/netbeans/modules/svg/resources/svgLogo.png + + + + diff --git a/ide/svg/manifest.mf b/ide/svg/manifest.mf new file mode 100644 index 000000000000..f8fce48879a4 --- /dev/null +++ b/ide/svg/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: false +OpenIDE-Module: org.netbeans.modules.svg +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/svg/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 diff --git a/ide/svg/nbproject/project.properties b/ide/svg/nbproject/project.properties new file mode 100644 index 000000000000..60729d846776 --- /dev/null +++ b/ide/svg/nbproject/project.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +javac.release=17 +javac.compilerargs=-Xlint -Xlint:-serial diff --git a/ide/svg/nbproject/project.xml b/ide/svg/nbproject/project.xml new file mode 100644 index 000000000000..ecda4b1643f1 --- /dev/null +++ b/ide/svg/nbproject/project.xml @@ -0,0 +1,165 @@ + + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.svg + + + org.netbeans.api.xml.ui + + + + 1 + 1.70 + + + + org.netbeans.core.ide + + + + 1 + 1.68 + + + + org.netbeans.core.multiview + + + + 1 + 1.71 + + + + org.netbeans.libs.jsvg + + + + 1.22 + + + + org.netbeans.modules.xml + + + + 1.60 + + + + org.netbeans.modules.xml.core + + + + 2 + 1.69 + + + + org.netbeans.spi.navigator + + + + 1 + 1.64 + + + + org.openide.awt + + + + 7.94 + + + + org.openide.dialogs + + + + 7.74 + + + + org.openide.filesystems + + + + 9.39 + + + + org.openide.loaders + + + + 7.96 + + + + org.openide.nodes + + + + 7.71 + + + + org.openide.util + + + + 9.34 + + + + org.openide.util.lookup + + + + 8.60 + + + + org.openide.util.ui + + + + 9.35 + + + + org.openide.windows + + + + 6.103 + + + + + + + diff --git a/ide/svg/src/org/netbeans/modules/svg/BackgroundMode.java b/ide/svg/src/org/netbeans/modules/svg/BackgroundMode.java new file mode 100644 index 000000000000..6bcb91b0f048 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/BackgroundMode.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg; + +/** + * + * @author Christian Lenz + */ +public enum BackgroundMode { + DEFAULT, BLACK, WHITE, TRANSPARENT, DARK_TRANSPARENT +} diff --git a/ide/svg/src/org/netbeans/modules/svg/Bundle.properties b/ide/svg/src/org/netbeans/modules/svg/Bundle.properties new file mode 100644 index 000000000000..9da8d38a378a --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/Bundle.properties @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +OpenIDE-Module-Name=SVG Support + +## SVGDataObject.java +CTL_SourceTabCaption=&Source +ERR_SVGDocument=SVG-Document is not available \ No newline at end of file diff --git a/ide/svg/src/org/netbeans/modules/svg/SVGDataObject.java b/ide/svg/src/org/netbeans/modules/svg/SVGDataObject.java new file mode 100644 index 000000000000..d916cd4c7b69 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/SVGDataObject.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg; + +import org.netbeans.core.spi.multiview.MultiViewElement; +import org.netbeans.core.spi.multiview.text.MultiViewEditorElement; +import org.netbeans.modules.xml.XMLDataObjectLook; +import org.netbeans.modules.xml.cookies.DataObjectCookieManager; +import org.netbeans.modules.xml.cookies.UpdateDocumentCookie; +import org.netbeans.modules.xml.sync.DataObjectSyncSupport; +import org.netbeans.modules.xml.sync.Synchronizator; +import org.netbeans.spi.xml.cookies.CheckXMLSupport; +import org.netbeans.spi.xml.cookies.DataObjectAdapters; +import org.netbeans.spi.xml.cookies.ValidateXMLSupport; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; +import org.openide.awt.HtmlBrowser; +import org.openide.cookies.ViewCookie; +import org.openide.filesystems.*; +import org.openide.loaders.*; +import org.openide.nodes.CookieSet; +import org.openide.nodes.Node; +import org.openide.util.*; +import org.openide.windows.TopComponent; +import org.xml.sax.InputSource; + +/** + * + * @author Christian Lenz + */ +@NbBundle.Messages({ + "LBL_SVG_LOADER=Files of SVG" +}) +@MIMEResolver.ExtensionRegistration( + displayName = "#LBL_SVG_LOADER", + mimeType = SVGDataObject.MIME_TYPE, + extension = {"svg", "SVG"}, + position = 21367 +) +@DataObject.Registration( + mimeType = SVGDataObject.MIME_TYPE, + iconBase = "org/netbeans/modules/svg/resources/svgLogo.png", + displayName = "#LBL_SVG_LOADER", + position = 300 +) +@ActionReferences({ + @ActionReference( + id = @ActionID(category = "System", id = "org.openide.actions.OpenAction"), + path = "Loaders/image/svg+xml/Actions", + position = 100 + ), + @ActionReference( + id = @ActionID(category = "System", id = "org.openide.actions.ViewAction"), + path = "Loaders/image/svg+xml/Actions", + position = 200, + separatorAfter = 400 + ), + @ActionReference( + id = @ActionID(category = "XML", id = "org.netbeans.modules.xml.tools.actions.CheckAction"), + path = "Loaders/image/svg+xml/Actions", + position = 600 + ), + @ActionReference( + id = @ActionID(category = "XML", id = "org.netbeans.modules.xml.tools.actions.ValidateAction"), + path = "Loaders/image/svg+xml/Actions", + position = 700, + separatorAfter = 750 + ), + @ActionReference( + id = @ActionID(category = "Edit", id = "org.openide.actions.CutAction"), + path = "Loaders/image/svg+xml/Actions", + position = 800 + ), + @ActionReference( + id = @ActionID(category = "Edit", id = "org.openide.actions.CopyAction"), + path = "Loaders/image/svg+xml/Actions", + position = 900 + ), + @ActionReference( + id = @ActionID(category = "Edit", id = "org.openide.actions.PasteAction"), + path = "Loaders/image/svg+xml/Actions", + position = 1000, + separatorAfter = 1100 + ), + @ActionReference( + id = @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"), + path = "Loaders/image/svg+xml/Actions", + position = 1200 + ), + @ActionReference( + id = @ActionID(category = "System", id = "org.openide.actions.RenameAction"), + path = "Loaders/image/svg+xml/Actions", + position = 1300, + separatorAfter = 1400 + ), + @ActionReference( + id = @ActionID(category = "System", id = "org.openide.actions.SaveAsTemplateAction"), + path = "Loaders/image/svg+xml/Actions", + position = 1500 + ), + @ActionReference( + id = @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"), + path = "Loaders/image/svg+xml/Actions", + position = 1550, + separatorAfter = 1600 + ), + @ActionReference( + id = @ActionID(category = "System", id = "org.openide.actions.ToolsAction"), + path = "Loaders/image/svg+xml/Actions", + position = 1700 + ), + @ActionReference( + id = @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"), + path = "Loaders/image/svg+xml/Actions", + position = 1800 + ) +}) +public final class SVGDataObject extends MultiDataObject implements XMLDataObjectLook, UpdateDocumentCookie { + + public static final String MIME_TYPE = "image/svg+xml"; + private final transient DataObjectCookieManager cookieManager; + private transient Synchronizator synchronizator; + + public SVGDataObject(final FileObject fo, MultiFileLoader loader) throws DataObjectExistsException { + super(fo, loader); + registerEditor(MIME_TYPE, true); + + CookieSet set = getCookieSet(); + cookieManager = new DataObjectCookieManager(this, getCookieSet()); + + // add check and validate cookies + InputSource is = DataObjectAdapters.inputSource(this); + set.add(new CheckXMLSupport(is, CheckXMLSupport.DOCUMENT_MODE)); + set.add(new ValidateXMLSupport(is)); + + CookieSet.Factory viewCookieFactory = new ViewCookieFactory(); + set.add(ViewCookie.class, viewCookieFactory); + } + + @Override + protected int associateLookup() { + return 1; + } + + @Override + public DataObjectCookieManager getCookieManager() { + return cookieManager; + } + + @Override + public synchronized Synchronizator getSyncInterface() { + if (synchronizator == null) { + synchronizator = new DataObjectSyncSupport(SVGDataObject.this); + } + + return synchronizator; + } + + @MultiViewElement.Registration( + displayName = "org.netbeans.modules.svg.Bundle#CTL_SourceTabCaption", + iconBase = "org/netbeans/modules/svg/resources/svgLogo.png", + persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED, + preferredID = "SVG", + mimeType = MIME_TYPE, + position = 1 + ) + public static MultiViewEditorElement createMultiViewEditorElement(Lookup context) { + return new MultiViewEditorElement(context); + } + + @Override + public void updateDocumentRoot() { + setModified(false); + getPrimaryFile(); + } + + private class ViewCookieFactory implements CookieSet.Factory { + + @Override + public Node.Cookie createCookie(Class klass) { + if (klass == ViewCookie.class) { + return new ViewSupport(org.netbeans.modules.svg.SVGDataObject.this.getPrimaryEntry()); + } else { + return null; + } + } + } + + private static final class ViewSupport implements ViewCookie { + + private final MultiDataObject.Entry primary; + + public ViewSupport(MultiDataObject.Entry primary) { + this.primary = primary; + } + + @Override + public void view() { + HtmlBrowser.URLDisplayer.getDefault().showURL(primary.getFile().toURL()); + } + + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/SVGPanel.java b/ide/svg/src/org/netbeans/modules/svg/SVGPanel.java new file mode 100644 index 000000000000..a1b498a579dd --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/SVGPanel.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg; + +import com.github.weisj.jsvg.SVGDocument; +import com.github.weisj.jsvg.SVGRenderingHints; +import java.awt.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.*; +import org.openide.util.NbBundle; + +/** + * + * @author Christian Lenz + */ +public class SVGPanel extends JPanel { + + private static final Logger LOG = Logger.getLogger(SVGViewerElement.class.getName()); + + private SVGDocument svgDocument; + private double scale = 1.0D; + private BackgroundMode bgMode = BackgroundMode.DEFAULT; + + public void setSVGDocument(SVGDocument doc) { + this.svgDocument = doc; + + repaint(); + } + + public void setScale(double scale) { + this.scale = scale; + + repaint(); + } + + public void setBackgroundMode(BackgroundMode mode) { + this.bgMode = mode; + + repaint(); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + Rectangle visibleRect = getVisibleRectangle(); + + switch (bgMode) { + case BLACK -> { + g.setColor(Color.BLACK); + g.fillRect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height); + } + case WHITE -> { + g.setColor(Color.WHITE); + g.fillRect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height); + } + + case TRANSPARENT, DARK_TRANSPARENT -> + Utils.drawChestTilePattern(g, visibleRect, 20, bgMode == BackgroundMode.DARK_TRANSPARENT); + case DEFAULT -> { + g.setColor(getBackground()); + g.fillRect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height); + } + } + + Graphics2D g2d = (Graphics2D) g.create(); + + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g2d.setRenderingHint(SVGRenderingHints.KEY_MASK_CLIP_RENDERING, SVGRenderingHints.VALUE_MASK_CLIP_RENDERING_ACCURACY); + + g2d.scale(scale, scale); + + try { + svgDocument.render(this, g2d); + } catch (Exception ex) { + LOG.log(Level.INFO, ex.getMessage()); + + drawErrorMesage(g); + } finally { + g2d.dispose(); + } + } + + /** + * Gets the visible portion of the panel. If this panel is inside a + * JViewport, it returns the visible part, otherwise, it returns the full + * panel size. + * + * @return The visible rectangle for the panel. + */ + private Rectangle getVisibleRectangle() { + JViewport viewport = getViewport(); + if (viewport != null) { + return viewport.getViewRect(); + } + + return new Rectangle(0, 0, getWidth(), getHeight()); + } + + /** + * Finds the parent JViewport, if any, to get the visible part of this + * panel. + * + * @return The JViewport that contains this panel, or null if none is found. + */ + private JViewport getViewport() { + Container parent = getParent(); + while (parent != null) { + if (parent instanceof JViewport) { + return (JViewport) parent; + } + + parent = parent.getParent(); + } + + return null; + } + + private void drawErrorMesage(Graphics g) { + g.setColor(Color.RED); + + FontMetrics fm = this.getFontMetrics(g.getFont()); + String errMessage = NbBundle.getMessage(SVGPanel.class, "ERR_SVGDocument"); + int stringWidth = fm.stringWidth(errMessage); + + g.drawString(errMessage, (this.getWidth() - stringWidth) / 2, this.getHeight() / 2); + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/SVGViewerElement.java b/ide/svg/src/org/netbeans/modules/svg/SVGViewerElement.java new file mode 100644 index 000000000000..70e1049a6ae5 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/SVGViewerElement.java @@ -0,0 +1,382 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg; + +import com.github.weisj.jsvg.SVGDocument; +import com.github.weisj.jsvg.geometry.size.FloatSize; +import com.github.weisj.jsvg.parser.SVGLoader; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; +import org.netbeans.core.spi.multiview.CloseOperationState; +import org.netbeans.core.spi.multiview.MultiViewElement; +import org.netbeans.core.spi.multiview.MultiViewElementCallback; +import org.netbeans.modules.svg.toolbar.SVGViewerToolbar; +import org.openide.awt.UndoRedo; +import org.openide.filesystems.FileChangeAdapter; +import org.openide.filesystems.FileChangeListener; +import org.openide.filesystems.FileEvent; +import org.openide.filesystems.FileObject; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; +import org.openide.windows.TopComponent; + +/** + * + * @author Christian Lenz + */ +@MultiViewElement.Registration( + displayName = "#LBL_SVGViewer", + iconBase = "org/netbeans/modules/svg/resources/svgLogo.png", + mimeType = "image/svg+xml", + persistenceType = TopComponent.PERSISTENCE_NEVER, + preferredID = "SVGViewer", + position = 1100 +) +@Messages("LBL_SVGViewer=Preview") +public class SVGViewerElement implements MultiViewElement { + + static { + // JSVG loader/parser is logging with SEVERE level which would land in the NB exception reporter + Logger.getLogger("com.github.weisj.jsvg").setLevel(Level.OFF); + } + + private static final Logger LOG = Logger.getLogger(SVGViewerElement.class.getName()); + + private final SVGDataObject dataObject; + private final Lookup lookup; + private transient JToolBar toolbar; + private transient JComponent component; + private transient MultiViewElementCallback callback; + + /** + * Component showing SVG. + */ + private transient JPanel viewer; + + private final SVGLoader svgLoader = new SVGLoader(); + private final SVGPanel svgPanel = new SVGPanel(); + private SVGDocument svgDocument = null; + + private int imageHeight = 0; + private int imageWidth = 0; + private long imageSize = -1; + + /** + * Scale of SVG. + */ + private double scale = 1.0D; + + /** + * Increase/decrease factor. + */ + private final double changeFactor = Math.sqrt(2.0D); + + private final FileChangeListener fcl = new FileChangeAdapter() { + @Override + public void fileChanged(FileEvent fe) { + updateView(); + } + }; + + @SuppressWarnings({"this-escape", "LeakingThisInConstructor"}) + public SVGViewerElement(Lookup lookup) { + this.dataObject = lookup.lookup(SVGDataObject.class); + this.lookup = new ProxyLookup( + Lookups.proxy(() -> dataObject.getLookup()), + Lookups.singleton(this) + ); + } + + @Override + public JComponent getVisualRepresentation() { + if (component == null) { + viewer = new JPanel(); + component = viewer; + } + + return component; + } + + @Override + public JComponent getToolbarRepresentation() { + if (toolbar == null) { + toolbar = new SVGViewerToolbar().createToolbar(this); + } + + return toolbar; + } + + @Override + public Action[] getActions() { + return callback.createDefaultActions(); + } + + @Override + public Lookup getLookup() { + return lookup; + } + + @Override + public void componentOpened() { + dataObject.getPrimaryFile().addFileChangeListener(fcl); + + updateView(); + } + + @Override + public void componentClosed() { + dataObject.getPrimaryFile().removeFileChangeListener(fcl); + } + + @Override + public void componentShowing() { + } + + @Override + public void componentHidden() { + } + + @Override + public void componentActivated() { + } + + @Override + public void componentDeactivated() { + } + + @Override + public UndoRedo getUndoRedo() { + return UndoRedo.NONE; + } + + @Override + public void setMultiViewCallback(MultiViewElementCallback callback) { + this.callback = callback; + } + + @Override + public CloseOperationState canCloseElement() { + return CloseOperationState.STATE_OK; + } + + private void addMouseWheelListenerToViewer(JScrollPane scrollPane) { + svgPanel.addMouseWheelListener(e -> { + double oldScale = scale; + + // Point in scrolled pane + Point visiblePoint = e.getPoint(); + + // "Picturepixel" + Point markedPoint = new Point( + (int) (visiblePoint.getX() / oldScale), + (int) (visiblePoint.getY() / oldScale)); + + int clicks = e.getWheelRotation(); + int clicks_abs = Math.abs(clicks); + for (int i = 0; i < clicks_abs; i++) { + if (clicks < 0) { + zoomIn(); + } else { + zoomOut(); + } + } + + double newScale = scale; + + Point markedPointInRealSpace = new Point( + (int) (markedPoint.getX() * newScale), + (int) (markedPoint.getY() * newScale) + ); + + Rectangle r = scrollPane.getViewport().getViewRect(); + + r.setLocation(markedPointInRealSpace); + r.translate(-r.width / 2, -r.height / 2); + + svgPanel.scrollRectToVisible(r); + }); + } + + private void showEmptyMessage(String message) { + JLabel label = new JLabel(message, SwingConstants.CENTER); + label.setForeground(Color.RED); + + viewer.removeAll(); + viewer.setLayout(new BorderLayout()); + viewer.add(label, BorderLayout.CENTER); + viewer.revalidate(); + viewer.repaint(); + } + + private void updateView() { + FileObject fo = dataObject.getPrimaryFile(); + + if (fo == null) { + return; + } + + if (viewer == null) { + return; + } + + svgDocument = svgLoader.load(fo.toURL()); + + if (svgDocument == null) { + showEmptyMessage(NbBundle.getMessage(SVGViewerElement.class, "ERR_SVGDocument")); + + return; + } + + FloatSize size = svgDocument.size(); + int width = (int) size.width; + int height = (int) size.height; + + imageWidth = width; + imageHeight = height; + + imageSize = fo.getSize(); + + if (width <= 0 || height <= 0) { + LOG.log(Level.WARNING, "Invalid SVG dimensions: width={0}, height={1}", new Object[]{width, height}); + + return; + } + + svgPanel.setPreferredSize(new Dimension(width, height)); + svgPanel.setSVGDocument(svgDocument); + + JScrollPane scrollPane = new JScrollPane(svgPanel); + + addMouseWheelListenerToViewer(scrollPane); + + viewer.removeAll(); + viewer.setLayout(new BorderLayout()); + viewer.add(scrollPane, BorderLayout.CENTER); + viewer.revalidate(); + viewer.repaint(); + } + + public float getImageSize() { + return imageSize; + } + + public float getImageWidth() { + return imageWidth; + } + + public float getImageHeight() { + return imageHeight; + } + + /** + * Perform zoom with specific proportion. + * + * @param fx numerator for scaled + * @param fy denominator for scaled + */ + public void customZoom(int fx, int fy) { + double oldScale = scale; + + scale = (double) fx / (double) fy; + if (!isNewSizeOK()) { + scale = oldScale; + + return; + } + + svgPanel.setScale(scale); + + resizePanel(); + } + + /** + * Draws zoom in scaled image. + */ + public void zoomIn() { + scale *= changeFactor; + + if (isNewSizeOK()) { + svgPanel.setScale(scale); + + resizePanel(); + } else { + scale /= changeFactor; + } + } + + /** + * Draws zoom out scaled image. + */ + public void zoomOut() { + scale /= changeFactor; + + if (isNewSizeOK()) { + svgPanel.setScale(scale); + + resizePanel(); + } else { + scale *= changeFactor; + } + } + + public void changeBackground(BackgroundMode bgMode) { + svgPanel.setBackgroundMode(bgMode); + + viewer.revalidate(); + viewer.repaint(); + } + + /** + * Resizes panel. + */ + private void resizePanel() { + int newWidth = (int) (svgDocument.size().getWidth() * scale); + int newHeight = (int) (svgDocument.size().getHeight() * scale); + + svgPanel.setBounds(0, 0, newWidth, newHeight); + svgPanel.setPreferredSize(new Dimension(newWidth, newHeight)); + + viewer.revalidate(); + viewer.repaint(); + } + + /** + * Tests new size of image. If image is smaller than minimum size(1x1) + * zooming will be not performed. + */ + private boolean isNewSizeOK() { + return (scale * svgDocument.size().getWidth() > 1) + && (scale * svgDocument.size().getHeight() > 1); + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/Utils.java b/ide/svg/src/org/netbeans/modules/svg/Utils.java new file mode 100644 index 000000000000..3cd6ce0e5181 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/Utils.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg; + +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * + * @author Christian Lenz + */ +public class Utils { + + /** + * Creates a pre-rendered chest like tile pattern as a BufferedImage. + * + * @param tileSize The size of each tile in the pattern. + * @param transparent Whether to add a transparent overlay to the pattern. + * @return The generated chest like pattern as a BufferedImage. + */ + public static BufferedImage createChestTilePattern(int tileSize, boolean transparent) { + int patternSize = tileSize * 2; + BufferedImage image = new BufferedImage(patternSize, patternSize, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = image.createGraphics(); + + drawSmallChestTilePattern(g2d, 0, 0, patternSize, patternSize, transparent); + + return image; + } + + /** + * Draws a repeating chest like tiled pattern within the specified visible + * rectangle. + * + * @param g The graphics object used for drawing. + * @param visibleRect The visible area to draw the pattern. + * @param tileSize The size of each tile in the pattern. + * @param transparent Whether to add a transparent overlay to the pattern. + */ + public static void drawChestTilePattern(Graphics g, Rectangle visibleRect, int tileSize, boolean transparent) { + BufferedImage backgroundImage = createChestTilePattern(tileSize, transparent); + int imgWidth = backgroundImage.getWidth(); + int imgHeight = backgroundImage.getHeight(); + + for (int y = visibleRect.y; y < visibleRect.y + visibleRect.height; y += imgHeight) { + for (int x = visibleRect.x; x < visibleRect.x + visibleRect.width; x += imgWidth) { + g.drawImage(backgroundImage, x, y, null); + } + } + } + + /** + * Draws a small chest like tile pattern on a specified area. + * + * @param g2d The graphics object used for drawing. + * @param x The x-coordinate of the top-left corner. + * @param y The y-coordinate of the top-left corner. + * @param width The width of the area to draw the pattern. + * @param height The height of the area to draw the pattern. + * @param transparentOverlay Whether to add a transparent overlay on top of + * the pattern. + */ + public static void drawSmallChestTilePattern(Graphics2D g2d, int x, int y, int width, int height, boolean transparentOverlay) { + int tileSize = width / 2; + + try { + for (int row = 0; row < 2; row++) { + for (int col = 0; col < 2; col++) { + if ((row + col) % 2 == 0) { + g2d.setColor(Color.LIGHT_GRAY); + } else { + g2d.setColor(Color.WHITE); + } + + g2d.fillRect(x + col * tileSize, y + row * tileSize, tileSize, tileSize); + } + } + + if (transparentOverlay) { + g2d.setColor(new Color(0, 0, 0, 128)); // Semi-transparent black overlay (50% opacity) + g2d.fillRect(x, y, width, height); + } + } finally { + g2d.dispose(); + } + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/navigation/Bundle.properties b/ide/svg/src/org/netbeans/modules/svg/navigation/Bundle.properties new file mode 100644 index 000000000000..f4ea92e6350f --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/navigation/Bundle.properties @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# ImageNavigatorPanel +Navigator_DisplayName=SVG preview +Navigator_DisplayHint=Thumbnail +ERR_IOFile=File is not available +ERR_DataObject=Can't find the data object +ERR_Thumbnail=Thumbnail is not available diff --git a/ide/svg/src/org/netbeans/modules/svg/navigation/SVGNavigatorPanel.java b/ide/svg/src/org/netbeans/modules/svg/navigation/SVGNavigatorPanel.java new file mode 100644 index 000000000000..614919106763 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/navigation/SVGNavigatorPanel.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg.navigation; + +import com.github.weisj.jsvg.SVGDocument; +import com.github.weisj.jsvg.parser.SVGLoader; +import java.util.Collection; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import org.netbeans.modules.svg.SVGDataObject; +import org.netbeans.modules.svg.SVGViewerElement; +import org.netbeans.spi.navigator.NavigatorPanel; +import org.openide.filesystems.FileChangeAdapter; +import org.openide.filesystems.FileChangeListener; +import org.openide.filesystems.FileEvent; +import org.openide.filesystems.FileObject; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectNotFoundException; +import org.openide.util.*; + +/** + * + * @author christian lenz + */ +@NavigatorPanel.Registrations({ + @NavigatorPanel.Registration(mimeType = "image/svg+xml", displayName = "#Navigator_DisplayName") +}) +public class SVGNavigatorPanel implements NavigatorPanel { + + static { + // JSVG loader/parser is logging with SEVERE level which would land in the NB exception reporter + Logger.getLogger("com.github.weisj.jsvg").setLevel(Level.OFF); + } + + private static final Logger LOG = Logger.getLogger(SVGViewerElement.class.getName()); + + /** + * holds UI of this panel + */ + private SVGPreviewPanel panelUI; + /** + * template for finding data in given context. Object used as example, + * replace with your own data source, for example JavaDataObject etc + */ + private static final Lookup.Template MY_DATA = new Lookup.Template<>(SVGDataObject.class); + /** + * current context to work on + */ + private Lookup.Result currentContext; + /** + * listener to context changes + */ + private LookupListener contextListener; + /** + * Listens for changes on image file. + */ + private FileChangeListener fileChangeListener; + private long lastSaveTime = -1; + private DataObject currentDataObject; + private static final RequestProcessor WORKER = new RequestProcessor(SVGNavigatorPanel.class.getName()); + + @Override + public String getDisplayName() { + return NbBundle.getMessage(SVGNavigatorPanel.class, "Navigator_DisplayName"); + } + + @Override + public String getDisplayHint() { + return NbBundle.getMessage(SVGNavigatorPanel.class, "Navigator_DisplayHint"); + } + + @Override + public JComponent getComponent() { + if (lastSaveTime == -1) { + lastSaveTime = System.currentTimeMillis(); + } + if (panelUI == null) { + panelUI = new SVGPreviewPanel(); + } + return panelUI; + } + + @Override + public void panelActivated(Lookup context) { + // lookup context and listen to result to get notified about context changes + currentContext = context.lookup(MY_DATA); + currentContext.addLookupListener(getContextListener()); + // get actual data and recompute content + Collection data = currentContext.allInstances(); + currentDataObject = getDataObject(data); + + if (currentDataObject == null) { + return; + } + + if (fileChangeListener == null) { + fileChangeListener = new SVGFileChangeAdapter(); + } + currentDataObject.getPrimaryFile().addFileChangeListener(fileChangeListener); + setNewContent(currentDataObject); + } + + @Override + public void panelDeactivated() { + currentContext.removeLookupListener(getContextListener()); + currentContext = null; + if (currentDataObject != null) { + currentDataObject.getPrimaryFile().removeFileChangeListener(fileChangeListener); + } + currentDataObject = null; + } + + @Override + public Lookup getLookup() { + // go with default activated Node strategy + return null; + } + + private void setNewContent(final DataObject dataObject) { + if (dataObject == null) { + return; + } + + WORKER.post(() -> { + FileObject fo = dataObject.getPrimaryFile(); + + if (fo == null) { + return; + } + + if (panelUI == null) { + getComponent(); + } + + SVGLoader svgLoader = new SVGLoader(); + SVGDocument svgDocument = svgLoader.load(fo.toURL()); + + SwingUtilities.invokeLater(() -> panelUI.setSVG(svgDocument)); + }); + } + + private DataObject getDataObject(Collection data) { + if (data.isEmpty()) { + return null; + } else { + return data.iterator().next(); + } + } + + /** + * Accessor for listener to context + */ + private LookupListener getContextListener() { + if (contextListener == null) { + contextListener = new ContextListener(); + } + return contextListener; + } + + /** + * Listens to changes of context and triggers proper action + */ + private class ContextListener implements LookupListener { + + @Override + public void resultChanged(LookupEvent ev) { + currentDataObject = getDataObject(currentContext.allInstances()); + setNewContent(currentDataObject); + } + } + + private class SVGFileChangeAdapter extends FileChangeAdapter { + + @Override + public void fileChanged(final FileEvent fe) { + if (fe.getTime() > lastSaveTime) { + lastSaveTime = System.currentTimeMillis(); + + // Refresh image viewer + SwingUtilities.invokeLater(() -> { + try { + currentDataObject = DataObject.find(fe.getFile()); + setNewContent(currentDataObject); + } catch (DataObjectNotFoundException ex) { + LOG.log(Level.INFO, NbBundle.getMessage(SVGNavigatorPanel.class, "ERR_DataObject")); + } + }); + } + } + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/navigation/SVGPreviewPanel.java b/ide/svg/src/org/netbeans/modules/svg/navigation/SVGPreviewPanel.java new file mode 100644 index 000000000000..ed0f70769ba1 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/navigation/SVGPreviewPanel.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg.navigation; + +import com.github.weisj.jsvg.SVGDocument; +import com.github.weisj.jsvg.SVGRenderingHints; +import com.github.weisj.jsvg.geometry.size.FloatSize; +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JPanel; +import javax.swing.UIManager; +import org.netbeans.modules.svg.SVGViewerElement; +import org.openide.awt.GraphicsUtils; +import org.openide.util.NbBundle; + +/** + * JPanel used for SVG preview in Navigator window + * + * @author christian lenz + */ +public class SVGPreviewPanel extends JPanel { + + private static final Logger LOG = Logger.getLogger(SVGViewerElement.class.getName()); + + private SVGDocument svgDocument; + private final int stringGapSize = 10; + private final Color background = UIManager.getColor("Table.background"); + private final Color foreground = UIManager.getColor("Table.foreground"); + + public void setSVG(SVGDocument svgDoc) { + this.svgDocument = svgDoc; + this.setBackground(background); + this.revalidate(); + this.repaint(); + } + + @Override + protected void paintComponent(Graphics g) { + GraphicsUtils.configureDefaultRenderingHints(g); + super.paintComponent(g); + + if (svgDocument != null) { + g.setColor(foreground); + + FloatSize size = svgDocument.size(); + int originalWidth = (int) size.getWidth(); + int originalHeight = (int) size.getHeight(); + String sizes = "Dimensions: " + originalWidth + " x " + originalHeight; + + g.drawString(sizes, (int) (this.getWidth() * 0.05), this.getHeight() - stringGapSize); + + int thumbnailMaxSize = 100; + + double widthRatio = (double) originalWidth / thumbnailMaxSize; + double heightRatio = (double) originalHeight / thumbnailMaxSize; + double ratio = 1.0; + + if (widthRatio > 1 || heightRatio > 1) { + ratio = widthRatio > heightRatio ? widthRatio : heightRatio; + } + + int scaledWidth = (int) (originalWidth / ratio); + int scaledHeight = (int) (originalHeight / ratio); + Graphics2D g2d = (Graphics2D) g.create((this.getWidth() - scaledWidth) / 2, (this.getHeight() - scaledHeight) / 2, scaledWidth, scaledHeight); + + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g2d.setRenderingHint(SVGRenderingHints.KEY_MASK_CLIP_RENDERING, SVGRenderingHints.VALUE_MASK_CLIP_RENDERING_ACCURACY); + + g2d.scale(1.0 / ratio, 1.0 / ratio); + + try { + svgDocument.render(this, g2d); + } catch (Exception ex) { + LOG.log(Level.INFO, ex.getMessage()); + + drawErrorMessage(g); + } finally { + g2d.dispose(); + } + } else { + drawErrorMessage(g); + } + } + + private void drawErrorMessage(Graphics g) { + g.setColor(Color.RED); + + FontMetrics fm = this.getFontMetrics(g.getFont()); + String errMessage = NbBundle.getMessage(SVGPreviewPanel.class, "ERR_Thumbnail"); + int stringWidth = fm.stringWidth(errMessage); + + g.drawString(errMessage, (this.getWidth() - stringWidth) / 2, this.getHeight() / 2); + g.dispose(); + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/resources/svgLogo.png b/ide/svg/src/org/netbeans/modules/svg/resources/svgLogo.png new file mode 100644 index 000000000000..bf0dde524692 Binary files /dev/null and b/ide/svg/src/org/netbeans/modules/svg/resources/svgLogo.png differ diff --git a/ide/svg/src/org/netbeans/modules/svg/resources/svgLogo.svg b/ide/svg/src/org/netbeans/modules/svg/resources/svgLogo.svg new file mode 100644 index 000000000000..d089234f8cbf --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/resources/svgLogo.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/BackgroundIcon.java b/ide/svg/src/org/netbeans/modules/svg/toolbar/BackgroundIcon.java new file mode 100644 index 000000000000..2f77670774fc --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/BackgroundIcon.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg.toolbar; + +import org.netbeans.modules.svg.BackgroundMode; + +import java.awt.*; +import javax.swing.*; +import org.netbeans.modules.svg.Utils; + +/** + * + * @author Christian Lenz + */ +public class BackgroundIcon implements Icon { + + private final BackgroundMode bgMode; + private final Color defaultColor; + + public BackgroundIcon(BackgroundMode bgMode, Color defaultColor) { + this.bgMode = bgMode; + this.defaultColor = defaultColor; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D g2d = (Graphics2D) g; + + int width = getIconWidth(); + int height = getIconHeight(); + + switch (bgMode) { + case BLACK -> { + g2d.setColor(Color.BLACK); + g2d.fillRect(x, y, width, height); + } + case WHITE -> { + g2d.setColor(Color.WHITE); + g2d.fillRect(x, y, width, height); + } + case TRANSPARENT, DARK_TRANSPARENT -> + Utils.drawSmallChestTilePattern(g2d, x, y, width, height, bgMode == BackgroundMode.DARK_TRANSPARENT); + case DEFAULT -> { + g2d.setColor(defaultColor); + g2d.fillRect(x, y, width, height); + } + } + } + + @Override + public int getIconWidth() { + return 16; + } + + @Override + public int getIconHeight() { + return 16; + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/Bundle.properties b/ide/svg/src/org/netbeans/modules/svg/toolbar/Bundle.properties new file mode 100644 index 000000000000..7ef4939154d7 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/Bundle.properties @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +LBL_CustomZoomAction=Custom Zoom... +MSG_InvalidValues=Invalid input values. + +ACSD_CustomZoomPanel=N/A +ACSD_Toolbar=N/A +ACSD_Out_BTN=N/A +ACSD_In_BTN=N/A + +ACSN_Toolbar=Zoom Toolbar + +# mnemonic for action "Zoom In" (the action's name is not displayed, only the mnemonic is used in the UI) +ACS_In_BTN_Mnem=I + +# mnemonic for action "Zoom Out" (the action's name is not displayed, only the mnemonic is used in the UI) +ACS_Out_BTN_Mnem=O + +ACS_EnlargeText=N/A +ACS_DecreaseText=N/A +ACS_Zoom_BTN=N/A +ACS_Background_BTN=N/A + +LBL_ChangeBackground=Use {0} Background + +# Labels for dimension and size +LBL_ImageDimensions={0}x{1} +LBL_ImageSizeBytes={0} Bytes +LBL_ImageSizeKb={0} KB +LBL_ImageSizeMb={0} MB \ No newline at end of file diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/SVGViewerToolbar.java b/ide/svg/src/org/netbeans/modules/svg/toolbar/SVGViewerToolbar.java new file mode 100644 index 000000000000..645bd230ba40 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/SVGViewerToolbar.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg.toolbar; + +import java.awt.Color; +import org.netbeans.modules.svg.toolbar.actions.CustomZoomAction; +import org.netbeans.modules.svg.toolbar.actions.ZoomInAction; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collection; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JToolBar; +import org.netbeans.modules.svg.BackgroundMode; +import org.netbeans.modules.svg.SVGViewerElement; +import org.netbeans.modules.svg.toolbar.actions.ZoomOutAction; +import org.openide.util.ImageUtilities; +import org.openide.util.NbBundle; +import org.openide.util.actions.SystemAction; + +/** + * + * @author Christian Lenz + */ +public class SVGViewerToolbar { + + /** + * collection of all buttons in the toolbar + */ + private final Collection toolbarButtons = new ArrayList<>(11); + private SVGViewerElement svgViewerElement; + private final DecimalFormat formatter = new DecimalFormat("#.##"); //NOI18N + private CustomZoomAction customZoomAction; + + public JToolBar createToolbar(SVGViewerElement svgViewerElement) { + this.svgViewerElement = svgViewerElement; + + JToolBar toolBar = new JToolBar(); + + toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE); //NOI18N + toolBar.setFloatable(false); + toolBar.setName(NbBundle.getMessage(SVGViewerToolbar.class, "ACSN_Toolbar")); + toolBar.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(SVGViewerToolbar.class, "ACSD_Toolbar")); + + JButton outButton = new JButton(SystemAction.get(ZoomOutAction.class)); + outButton.setIcon(ImageUtilities.loadImageIcon("org/netbeans/modules/image/zoomOut.gif", false)); + outButton.setToolTipText(NbBundle.getMessage(ZoomOutAction.class, "LBL_ZoomOut")); + outButton.setMnemonic(NbBundle.getMessage(SVGViewerToolbar.class, "ACS_Out_BTN_Mnem").charAt(0)); + outButton.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(SVGViewerToolbar.class, "ACSD_Out_BTN")); + outButton.setText(""); + toolBar.add(outButton); + toolbarButtons.add(outButton); + + JButton inButton = new JButton(SystemAction.get(ZoomInAction.class)); + inButton.setToolTipText(NbBundle.getMessage(ZoomInAction.class, "LBL_ZoomIn")); + inButton.setMnemonic(NbBundle.getMessage(SVGViewerToolbar.class, "ACS_In_BTN_Mnem").charAt(0)); + inButton.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(SVGViewerToolbar.class, "ACSD_In_BTN")); + inButton.setText(""); + toolBar.add(inButton); + toolbarButtons.add(inButton); + + toolBar.addSeparator(new Dimension(11, 0)); + + JButton button; + toolBar.add(button = createZoomButton(1, 1)); + toolbarButtons.add(button); + toolBar.addSeparator(new Dimension(11, 0)); + toolBar.add(button = createZoomButton(1, 3)); + toolbarButtons.add(button); + toolBar.add(button = createZoomButton(1, 5)); + toolbarButtons.add(button); + toolBar.add(button = createZoomButton(1, 7)); + toolbarButtons.add(button); + toolBar.addSeparator(new Dimension(11, 0)); + toolBar.add(button = createZoomButton(3, 1)); + toolbarButtons.add(button); + toolBar.add(button = createZoomButton(5, 1)); + toolbarButtons.add(button); + toolBar.add(button = createZoomButton(7, 1)); + toolbarButtons.add(button); + + toolBar.addSeparator(new Dimension(11, 0)); + + toolBar.add(button = createZoomButton()); + toolbarButtons.add(button); + + toolBar.addSeparator(new Dimension(11, 0)); + + // Backgrounds + toolBar.add(button = createCustomBackgroundButton(BackgroundMode.BLACK, Color.BLACK)); + toolbarButtons.add(button); + toolBar.add(button = createCustomBackgroundButton(BackgroundMode.WHITE, Color.WHITE)); + toolbarButtons.add(button); + toolBar.add(button = createCustomBackgroundButton(BackgroundMode.TRANSPARENT, Color.LIGHT_GRAY)); + toolbarButtons.add(button); + toolBar.add(button = createCustomBackgroundButton(BackgroundMode.DARK_TRANSPARENT, Color.DARK_GRAY)); + toolbarButtons.add(button); + toolBar.add(button = createCustomBackgroundButton(BackgroundMode.DEFAULT, Color.WHITE)); + toolbarButtons.add(button); + + // Image Dimension + toolBar.addSeparator(new Dimension(11, 0)); + toolBar.add(new JLabel(NbBundle.getMessage(SVGViewerToolbar.class, "LBL_ImageDimensions", svgViewerElement.getImageWidth(), svgViewerElement.getImageHeight()))); + + // Image File Size in KB, MB + if (svgViewerElement.getImageSize() != -1) { + toolBar.addSeparator(new Dimension(11, 0)); + double kb = 1024.0; + double mb = kb * kb; + final double size; + final String label; + if (svgViewerElement.getImageSize() >= mb) { + size = svgViewerElement.getImageSize() / mb; + label = "LBL_ImageSizeMb"; // NOI18N + } else if (svgViewerElement.getImageSize() >= kb) { + size = svgViewerElement.getImageSize() / kb; + label = "LBL_ImageSizeKb"; // NOI18N + } else { + size = svgViewerElement.getImageSize(); + label = "LBL_ImageSizeBytes"; //NOI18N + } + + toolBar.add(new JLabel(NbBundle.getMessage(SVGViewerToolbar.class, label, formatter.format(size)))); + } + for (JButton jb : toolbarButtons) { + jb.setFocusable(false); + } + return toolBar; + } + + /** + * Creates zoom button. + */ + private JButton createZoomButton(final int xf, final int yf) { + // PENDING buttons should have their own icons. + JButton button = new JButton("" + xf + ":" + yf); // NOI18N + if (xf < yf) { + button.setToolTipText(NbBundle.getMessage(ZoomOutAction.class, "LBL_ZoomOut") + " " + xf + " : " + yf); + } else { + button.setToolTipText(NbBundle.getMessage(ZoomInAction.class, "LBL_ZoomIn") + " " + xf + " : " + yf); + } + button.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(SVGViewerToolbar.class, "ACS_Zoom_BTN")); + button.addActionListener((ActionEvent evt) -> { + svgViewerElement.customZoom(xf, yf); + }); + + return button; + } + + private JButton createZoomButton() { + // PENDING buttons should have their own icons. + JButton button = new JButton(SystemAction.get(CustomZoomAction.class)); + button.setToolTipText(NbBundle.getMessage(CustomZoomAction.class, "LBL_CustomZoom")); + button.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(SVGViewerToolbar.class, "ACS_Zoom_BTN")); + + return button; + } + + /** + * Creates custom background button. + */ + private JButton createCustomBackgroundButton(BackgroundMode bgMode, Color color) { + final JButton button = new JButton(); // NOI18N + button.setIcon(new BackgroundIcon(bgMode, color)); + button.setToolTipText(NbBundle.getMessage(SVGViewerToolbar.class, "LBL_ChangeBackground", bgMode.name())); + button.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(SVGViewerToolbar.class, "ACS_Background_BTN")); + button.addActionListener((ActionEvent evt) -> { + svgViewerElement.changeBackground(bgMode); + }); + + return button; + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/Bundle.properties b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/Bundle.properties new file mode 100644 index 000000000000..438b6b046b85 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/Bundle.properties @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +## Toolbbar action labels +LBL_ZoomIn=Zoom In +LBL_ZoomOut=Zoom Out +LBL_CustomZoom=Customize Zoom +LBL_EnlargeFactor=&Enlarge Factor: +LBL_DecreaseFactor=&Decrease Factor: + +# CustomZoomAction +LBL_XtoY=X:Y +LBL_CustomZoomAction=Custom Zoom... +MSG_InvalidValues=Invalid input values. + +ACSD_CustomZoomPanel=N/A + +ACS_EnlargeText=N/A + +ACS_DecreaseText=N/A \ No newline at end of file diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomAction.java b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomAction.java new file mode 100644 index 000000000000..b71f2a7ba5ab --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomAction.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg.toolbar.actions; + +import java.awt.Dialog; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import org.netbeans.modules.svg.SVGViewerElement; + +import org.openide.*; +import org.openide.awt.ActionID; +import org.openide.awt.ActionRegistration; +import org.openide.util.actions.CallableSystemAction; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.windows.TopComponent; + +/** + * Action that can always be invoked and work procedurally. + * + * @author Christian Lenz + */ +@ActionID(id = "org.netbeans.modules.svg.toolbar.CustomZoomAction", category = "View") +@ActionRegistration(lazy = false, displayName = "#LBL_CustomZoom") +public class CustomZoomAction extends CallableSystemAction { + + /** + * Generated serial version UID. + */ + static final long serialVersionUID = 8247068408606777895L; + + /** + * Actually performs action. + */ + @Override + public void performAction() { + TopComponent currentComponent = TopComponent.getRegistry().getActivated(); + Lookup tcLookup = currentComponent != null ? currentComponent.getLookup() : null; + SVGViewerElement svgViewerElement = tcLookup != null ? tcLookup.lookup(SVGViewerElement.class) : null; + if (svgViewerElement != null) { + final Dialog[] dialogs = new Dialog[1]; + final CustomZoomPanel zoomPanel = new CustomZoomPanel(); + + zoomPanel.setEnlargeFactor(1); + zoomPanel.setDecreaseFactor(1); + + DialogDescriptor dd = new DialogDescriptor( + zoomPanel, + NbBundle.getMessage(CustomZoomAction.class, "LBL_CustomZoomAction"), + true, + DialogDescriptor.OK_CANCEL_OPTION, + DialogDescriptor.OK_OPTION, + new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + if (ev.getSource() == DialogDescriptor.OK_OPTION) { + int enlargeFactor; + int decreaseFactor; + + try { + enlargeFactor = zoomPanel.getEnlargeFactor(); + decreaseFactor = zoomPanel.getDecreaseFactor(); + } catch (NumberFormatException nfe) { + notifyInvalidInput(); + return; + } + + // Invalid values. + if (enlargeFactor == 0 || decreaseFactor == 0) { + notifyInvalidInput(); + return; + } + + svgViewerElement.customZoom(enlargeFactor, decreaseFactor); + dialogs[0].setVisible(false); + dialogs[0].dispose(); + } else { + dialogs[0].setVisible(false); + dialogs[0].dispose(); + } + } + + private void notifyInvalidInput() { + DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message( + NbBundle.getMessage(CustomZoomAction.class, "MSG_InvalidValues"), + NotifyDescriptor.ERROR_MESSAGE + )); + } + + } // End of annonymnous ActionListener. + ); + dialogs[0] = DialogDisplayer.getDefault().createDialog(dd); + dialogs[0].setVisible(true); + } + } + + /** + * Gets action name. Implements superclass abstract method. + */ + @Override + public String getName() { + return ""; + } + + /** + * Gets action help context. Implemenets superclass abstract method. + */ + @Override + public HelpCtx getHelpCtx() { + return new HelpCtx("org.netbeans.modules.svg.toolbar.CustomZoomAction"); + } + + /** + * Gets icon resource. Overrides superclass method. + */ + @Override + protected String iconResource() { + return "org/netbeans/modules/image/customZoom.gif"; // NOI18N + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomPanel.form b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomPanel.form new file mode 100644 index 000000000000..5d2623fe09c1 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomPanel.form @@ -0,0 +1,100 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomPanel.java b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomPanel.java new file mode 100644 index 000000000000..01221a6639c5 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/CustomZoomPanel.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg.toolbar.actions; + +import javax.swing.JPanel; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.PlainDocument; + +import org.openide.util.NbBundle; + +/** + * This class define a panel for "Custom zoom" dialog. + * + * @author Christian Lenz + */ +public class CustomZoomPanel extends JPanel { + + /** + * Creates new form CustomZoomPane + */ + public CustomZoomPanel() { + initComponents(); + initAccessibility(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + enlargeLabel = new javax.swing.JLabel(); + enlargeText = new javax.swing.JTextField(); + decreasingLabel = new javax.swing.JLabel(); + decreaseText = new javax.swing.JTextField(); + + setLayout(new java.awt.GridBagLayout()); + + enlargeLabel.setLabelFor(enlargeText); + org.openide.awt.Mnemonics.setLocalizedText(enlargeLabel, NbBundle.getBundle(CustomZoomPanel.class).getString("LBL_EnlargeFactor")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(12, 12, 0, 0); + add(enlargeLabel, gridBagConstraints); + + enlargeText.setDocument(new WholeNumberDocument()); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(12, 11, 0, 11); + add(enlargeText, gridBagConstraints); + + decreasingLabel.setLabelFor(decreaseText); + org.openide.awt.Mnemonics.setLocalizedText(decreasingLabel, NbBundle.getBundle(CustomZoomPanel.class).getString("LBL_DecreaseFactor")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(5, 12, 11, 0); + add(decreasingLabel, gridBagConstraints); + + decreaseText.setDocument(new WholeNumberDocument()); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 11, 11, 11); + add(decreaseText, gridBagConstraints); + }// //GEN-END:initComponents + + private void initAccessibility() { + getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(CustomZoomPanel.class).getString("ACSD_CustomZoomPanel")); + enlargeText.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(CustomZoomPanel.class).getString("ACS_EnlargeText")); + decreaseText.getAccessibleContext().setAccessibleDescription(NbBundle.getBundle(CustomZoomPanel.class).getString("ACS_DecreaseText")); + + } + + public int getEnlargeFactor() { + return Integer.parseInt(enlargeText.getText()); + } + + public void setEnlargeFactor(int enlargeFactor) { + enlargeText.setText("" + enlargeFactor); // NOI18N + } + + public int getDecreaseFactor() { + return Integer.parseInt(decreaseText.getText()); + } + + public void setDecreaseFactor(int decreaseFactor) { + decreaseText.setText("" + decreaseFactor); // NOI18N + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField decreaseText; + private javax.swing.JLabel decreasingLabel; + private javax.swing.JLabel enlargeLabel; + private javax.swing.JTextField enlargeText; + // End of variables declaration//GEN-END:variables + + /** + * Documnet which accepts only digit chars. + */ + private static class WholeNumberDocument extends PlainDocument { + + /** + * Overrides superclass method. + */ + @Override + public void insertString(int offs, String str, AttributeSet a) + throws BadLocationException { + char[] source = str.toCharArray(); + StringBuilder result = new StringBuilder(); + + for (int i = 0; i < source.length; i++) { + if (Character.isDigit(source[i])) { + result.append(source[i]); + } else { + if (Boolean.getBoolean("netbeans.debug.excpetions")) // NOI18N + { + System.err.println("Image: Trying insert non-digit in custom zoom action."); // NOI18N + } + } + } + + // There has to be some number added. + if (result.length() == 0) { + return; + } + + super.insertString(offs, result.toString(), a); + } + + } // End of nested class WholeNumberDocument. + +} diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/ZoomInAction.java b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/ZoomInAction.java new file mode 100644 index 000000000000..a7ed9b95f418 --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/ZoomInAction.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg.toolbar.actions; + +import org.netbeans.modules.svg.SVGViewerElement; +import org.openide.awt.ActionID; +import org.openide.awt.ActionRegistration; +import org.openide.util.actions.CallableSystemAction; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.windows.TopComponent; + +/** + * Action which zooms in of an svg. + * + * @author Christian Lenz + */ +@ActionID(id = "org.netbeans.modules.svg.toolbar.ZoomInAction", category = "View") +@ActionRegistration(lazy = false, displayName = "#LBL_ZoomIn") +public class ZoomInAction extends CallableSystemAction { + + /** + * Generated serial version UID. + */ + static final long serialVersionUID = -8705899978543961455L; + + /** + * Perform action. + */ + @Override + public void performAction() { + TopComponent currentComponent = TopComponent.getRegistry().getActivated(); + Lookup tcLookup = currentComponent != null ? currentComponent.getLookup() : null; + SVGViewerElement svgViewerElement = tcLookup != null ? tcLookup.lookup(SVGViewerElement.class) : null; + if (svgViewerElement != null) { + svgViewerElement.zoomIn(); + } + } + + /** + * Gets action name. Implements superclass abstract method. + */ + @Override + public String getName() { + return NbBundle.getMessage(ZoomInAction.class, "LBL_ZoomIn"); //NOI18N + } + + /** + * Gets action help context. Implemenets superclass abstract method. + */ + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + /** + * Overrides superclass method. + */ + @Override + public boolean isEnabled() { + return true; + } + + /** + * Gets icon resource. Overrides superclass method. + */ + @Override + protected String iconResource() { + return "org/netbeans/modules/image/zoomIn.gif"; // NOI18N + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/ZoomOutAction.java b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/ZoomOutAction.java new file mode 100644 index 000000000000..f9baeed009bf --- /dev/null +++ b/ide/svg/src/org/netbeans/modules/svg/toolbar/actions/ZoomOutAction.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.svg.toolbar.actions; + +import org.netbeans.modules.svg.SVGViewerElement; +import org.openide.awt.ActionID; +import org.openide.awt.ActionRegistration; +import org.openide.util.actions.CallableSystemAction; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.windows.TopComponent; + +/** + * Action which zooms out of an svg. + * + * @author Christian Lenz + */ +@ActionID(id = "org.netbeans.modules.svg.toolbar.ZoomOutAction", category = "View") +@ActionRegistration(lazy = false, displayName = "#LBL_ZoomOut") +public class ZoomOutAction extends CallableSystemAction { + + /** + * Generated serial version UID. + */ + static final long serialVersionUID = 1859897546585041051L; + + /** + * Peforms action. + */ + @Override + public void performAction() { + TopComponent currentComponent = TopComponent.getRegistry().getActivated(); + Lookup tcLookup = currentComponent != null ? currentComponent.getLookup() : null; + SVGViewerElement svgViewerElement = tcLookup != null ? tcLookup.lookup(SVGViewerElement.class) : null; + if (svgViewerElement != null) { + svgViewerElement.zoomOut(); + } + } + + /** + * Gets name of action. Implements superclass abstract method. + */ + @Override + public String getName() { + return NbBundle.getMessage(ZoomOutAction.class, "LBL_ZoomOut"); + } + + /** + * Gets help context for action. Implements superclass abstract method. + */ + @Override + public org.openide.util.HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + /** + * Overrides superclass method. + */ + @Override + public boolean isEnabled() { + return true; + } + + /** + * Gets icon resource. Overrides superclass method. + */ + @Override + protected String iconResource() { + return "org/netbeans/modules/image/zoomOut.gif"; // NOI18N + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/ide/xml/nbproject/project.xml b/ide/xml/nbproject/project.xml index 3661a55839f1..665103094ec9 100644 --- a/ide/xml/nbproject/project.xml +++ b/ide/xml/nbproject/project.xml @@ -333,6 +333,7 @@ + org.netbeans.modules.svg org.netbeans.modules.xml.schema org.netbeans.modules.xml.tax org.netbeans.modules.xml.tools @@ -349,4 +350,4 @@ - + \ No newline at end of file diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties index b9a79066f948..82f72697a5f1 100644 --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -493,6 +493,7 @@ nb.cluster.ide=\ spi.tasklist,\ spi.viewmodel,\ subversion,\ + svg,\ swing.validation,\ target.iterator,\ tasklist.kit,\