/*
 * Copyright 2015-2017 Matthew Aguirre
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tros.torgo.swing;

import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseWheelEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import org.tros.utils.logging.Logger;
import org.tros.utils.logging.Logging;

/**
 *
 * @author matta
 */
public abstract class ZoomableComponent {

    private static final Logger LOGGER = Logging.getLogFactory().getLogger(ZoomableComponent.class);

    public ZoomableComponent(JComponent component) {
        this(component, component);
    }

    public ZoomableComponent(JComponent component, JComponent popupMenuContainer) {
        Runnable increase = () -> {
            LOGGER.verbose("increase");
            zoomIn();
        };

        Runnable decrease = () -> {
            LOGGER.verbose("decrease");
            zoomOut();
        };

        Runnable reset = () -> {
            LOGGER.verbose("reset");
            zoomReset();
        };

        component.addMouseWheelListener((MouseWheelEvent mwe) -> {
            if ((mwe.getModifiers() & KeyEvent.CTRL_MASK) == KeyEvent.CTRL_MASK) {
                if (mwe.getPreciseWheelRotation() < 0) {
                    increase.run();
                } else {
                    decrease.run();
                }
            }
        });
        JMenuItem jmi1 = new JMenuItem("Zoom In");
        jmi1.addActionListener((ActionEvent ae) -> {
            increase.run();
        });
        JMenuItem jmi2 = new JMenuItem("Zoom Out");
        jmi2.addActionListener((ActionEvent ae) -> {
            decrease.run();
        });
        JMenuItem jmi3 = new JMenuItem("Zoom Reset");
        jmi3.addActionListener((ActionEvent ae) -> {
            reset.run();
        });
        jmi1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, InputEvent.CTRL_MASK));
        jmi2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_MASK));
        jmi3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, InputEvent.CTRL_MASK));
        SwingUtilities.invokeLater(() -> {
            JPopupMenu popupMenu;
            try {
                //HACK: for RSyntaxTextArea
                Method method = popupMenuContainer.getClass().getMethod("getPopupMenu");
                popupMenu = (JPopupMenu) method.invoke(popupMenuContainer);
            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                popupMenu = popupMenuContainer.getComponentPopupMenu();
            }
            popupMenu = popupMenu == null ? new JPopupMenu() : popupMenu;
            popupMenu.add(jmi1);
            popupMenu.add(jmi2);
            popupMenu.add(jmi3);
            popupMenuContainer.setComponentPopupMenu(popupMenu);
        });
    }

    protected abstract void zoomIn();

    protected abstract void zoomOut();

    protected abstract void zoomReset();
}

@@ -725,24 +725,95 @@
 t.KeyEvent;%0A
+import java.awt.event.MouseEvent;%0Aimport java.awt.event.MouseListener;%0A
 import java.
@@ -924,16 +924,66 @@
 Method;%0A
+import java.util.concurrent.atomic.AtomicBoolean;%0A
 import j
@@ -1100,43 +1100,8 @@
 ke;%0A
-import javax.swing.SwingUtilities;%0A
 impo
@@ -2925,43 +2925,167 @@
     
-SwingUtilities.invokeLater(() -%3E %7B%0A
+final AtomicBoolean added = new AtomicBoolean(false);%0A        final Runnable r = () -%3E %7B%0A%0A            if (!added.get()) %7B%0A                added.set(true);%0A    
     
@@ -3130,14 +3130,22 @@
     
+ 
+   
 try %7B%0A
+    
     
@@ -3200,16 +3200,20 @@
         
+  
+  
 Method m
@@ -3285,32 +3285,36 @@
                 
+    
 popupMenu = (JPo
@@ -3369,16 +3369,20 @@
         
+   
+ 
 %7D catch 
@@ -3518,32 +3518,36 @@
                 
+    
 popupMenu = popu
@@ -3582,24 +3582,28 @@
 opupMenu();%0A
+    
             
@@ -3608,32 +3608,36 @@
   %7D%0A            
+    
 popupMenu = popu
@@ -3686,32 +3686,36 @@
 nu;%0A            
+    
 popupMenu.add(jm
@@ -3723,32 +3723,36 @@
 1);%0A            
+ 
+   
 popupMenu.add(jm
@@ -3756,16 +3756,20 @@
 (jmi2);%0A
+    
         
@@ -3797,32 +3797,36 @@
 3);%0A            
+  
+  
 popupMenuContain
@@ -3862,16 +3862,951 @@
 pMenu);%0A
+            %7D%0A        %7D;%0A        popupMenuContainer.addMouseListener(new MouseListener() %7B%0A            @Override%0A            public void mouseClicked(MouseEvent me) %7B%0A                r.run();%0A                component.removeMouseListener(this);%0A            %7D%0A%0A            @Override%0A            public void mousePressed(MouseEvent me) %7B%0A                r.run();%0A                component.removeMouseListener(this);%0A            %7D%0A%0A            @Override%0A            public void mouseReleased(MouseEvent me) %7B%0A                r.run();%0A                component.removeMouseListener(this);%0A            %7D%0A%0A            @Override%0A            public void mouseEntered(MouseEvent me) %7B%0A                r.run();%0A                component.removeMouseListener(this);%0A            %7D%0A%0A            @Override%0A            public void mouseExited(MouseEvent me) %7B%0A                r.run();%0A                component.removeMouseListener(this);%0A            %7D%0A
         

Fix for testing zoomable