You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by sk...@apache.org on 2015/12/07 16:31:44 UTC

[5/8] cayenne git commit: cleanup

http://git-wip-us.apache.org/repos/asf/cayenne/blob/b444b1df/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/PathChooserComboBoxCellEditor.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/PathChooserComboBoxCellEditor.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/PathChooserComboBoxCellEditor.java
new file mode 100644
index 0000000..ae4a639
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/PathChooserComboBoxCellEditor.java
@@ -0,0 +1,192 @@
+/*****************************************************************
+ *   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.apache.cayenne.modeler.util;
+
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.util.combo.AutoCompletion;
+import org.apache.commons.lang.StringUtils;
+
+import javax.swing.AbstractCellEditor;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JTable;
+import javax.swing.table.TableCellEditor;
+import javax.swing.text.JTextComponent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * This class used as cell editor, when you need to
+ * choose path in comboBox and use autocompletion.
+ */
+public abstract class PathChooserComboBoxCellEditor extends AbstractCellEditor implements TableCellEditor {
+
+    protected JComboBox comboBoxPathChooser;
+    protected int previousEmbeddedLevel = 0;
+    protected EntityTreeModel treeModel;
+    protected int row;
+
+    protected abstract void enterPressed(JTable table);
+
+    protected abstract EntityTreeModel createTreeModelForComboBox(int indexInTable);
+
+    protected abstract Object getCurrentNodeToInitializeCombo(CayenneTableModel model, int row);
+
+    protected abstract String getPathToInitializeCombo(CayenneTableModel model, int row);
+
+    protected void initializeCombo(CayenneTableModel model, int row, final JTable table) {
+        Object currentNode = getCurrentNodeToInitializeCombo(model, row);
+        String dbAttributePath = getPathToInitializeCombo(model, row);
+        List<String> nodeChildren = getChildren(currentNode, dbAttributePath);
+        comboBoxPathChooser = Application.getWidgetFactory().createComboBox(
+                nodeChildren,
+                false);
+        comboBoxPathChooser.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyReleased(KeyEvent event) {
+                if (event.getKeyCode() == KeyEvent.VK_ENTER) {
+                    enterPressed(table);
+                    return;
+                }
+                parsePathString(event.getKeyChar());
+            }
+        });
+        AutoCompletion.enable(comboBoxPathChooser, false, true);
+    }
+
+    private void setComboModelAccordingToPath(String pathString) {
+        List<String> currentNodeChildren = new ArrayList<>();
+        currentNodeChildren.add(pathString);
+        currentNodeChildren.addAll(getChildren(getCurrentNode(pathString), pathString));
+        comboBoxPathChooser.setModel(new DefaultComboBoxModel(currentNodeChildren.toArray()));
+        comboBoxPathChooser.showPopup();
+        comboBoxPathChooser.setPopupVisible(true);
+    }
+
+    protected void parsePathString(char lastEnteredCharacter) {
+        JTextComponent editorComponent = (JTextComponent) (comboBoxPathChooser).getEditor().getEditorComponent();
+
+        String pathString = editorComponent.getText();
+        if (pathString != null && pathString.isEmpty()) {
+            setComboModelAccordingToPath("");
+            previousEmbeddedLevel = StringUtils.countMatches(pathString, ".");
+            return;
+        }
+
+        if (lastEnteredCharacter == '.') {
+            processDotEntered();
+            previousEmbeddedLevel = StringUtils.countMatches(pathString, ".");
+            return;
+        }
+
+        int currentEmbeddedLevel = StringUtils.countMatches(pathString, ".");
+        if (previousEmbeddedLevel != currentEmbeddedLevel) {
+            previousEmbeddedLevel = currentEmbeddedLevel;
+            List<String> currentNodeChildren = new ArrayList<>();
+            String[] pathStrings = pathString.split(Pattern.quote("."));
+            String lastStringInPath = pathStrings[pathStrings.length - 1];
+            String saveDbAttributePath = pathString;
+            pathString = pathString.replaceAll(lastStringInPath + "$", "");
+            currentNodeChildren.addAll(getChildren(getCurrentNode(pathString), pathString));
+            comboBoxPathChooser.setModel(new DefaultComboBoxModel(currentNodeChildren.toArray()));
+            editorComponent.setText(saveDbAttributePath);
+            return;
+        }
+    }
+
+    private void processDotEntered() {
+        JTextComponent editorComponent = (JTextComponent) (comboBoxPathChooser).getEditor().getEditorComponent();
+
+        String dbAttributePath = editorComponent.getText();
+        if (".".equals(dbAttributePath)) {
+            setComboModelAccordingToPath("");
+            return;
+        }
+        char secondFromEndCharacter = dbAttributePath.charAt(dbAttributePath.length() - 2);
+        if (secondFromEndCharacter == '.') {
+            // two dots entered one by one , we replace it by one dot
+            editorComponent.setText(dbAttributePath.substring(0, dbAttributePath.length() - 1));
+            return;
+        }
+        String[] pathStrings = dbAttributePath.split(Pattern.quote("."));
+        String lastStringInPath = pathStrings[pathStrings.length - 1];
+
+        //we will check if lastStringInPath is correct name of DbAttribute or DbRelationship
+        //for appropriate previous node in path. if it is not we won't add entered dot to dbAttributePath
+        String dbAttributePathForPreviousNode;
+        if (pathStrings.length == 1) {
+            //previous root is treeModel.getRoot()
+            dbAttributePathForPreviousNode = "";
+        } else {
+            dbAttributePathForPreviousNode = dbAttributePath.replace('.' + lastStringInPath, "");
+        }
+        List<String> potentialVariantsToChoose = getChildren(getCurrentNode(dbAttributePathForPreviousNode), "");
+        if (potentialVariantsToChoose.contains(lastStringInPath)) {
+            setComboModelAccordingToPath(dbAttributePath);
+        } else {
+            editorComponent.setText(dbAttributePath.substring(0, dbAttributePath.length() - 1));
+        }
+        previousEmbeddedLevel = StringUtils.countMatches(dbAttributePath, ".");
+    }
+
+    /**
+     * find current node by dbAttributePath
+     *
+     * @param pathString
+     * @return last node in dbAttributePath which matches DbRelationship or DbAttribute
+     */
+    protected Object getCurrentNode(String pathString) {
+        //case for new attribute
+        if (pathString == null || pathString.isEmpty()) {
+            return treeModel.getRoot();
+        }
+        String[] pathStrings = pathString.split(Pattern.quote("."));
+        Object root = treeModel.getRoot();
+        for (String rootChildText : pathStrings) {
+            for (int j = 0; j < treeModel.getChildCount(root); j++) {
+                Object child = treeModel.getChild(root, j);
+                String objectName = ModelerUtil.getObjectName(child);
+                if (objectName.equals(rootChildText)) {
+                    root = child;
+                    break;
+                }
+            }
+        }
+        return root;
+    }
+
+    /**
+     * @param node       for which we will find children
+     * @param pathString string which will be added to each child to make right autocomplete
+     * @return list with children , which will be used to autocomplete
+     */
+    protected List<String> getChildren(Object node, String pathString) {
+        List<String> currentNodeChildren = new ArrayList<>();
+        for (int j = 0; j < treeModel.getChildCount(node); j++) {
+            Object child = treeModel.getChild(node, j);
+            String relationshipName = ModelerUtil.getObjectName(child);
+            currentNodeChildren.add(pathString + relationshipName);
+        }
+        return currentNodeChildren;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/b444b1df/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/ProjectUtil.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/ProjectUtil.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/ProjectUtil.java
index 20aaf3d..fc7735d 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/ProjectUtil.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/ProjectUtil.java
@@ -19,11 +19,6 @@
 
 package org.apache.cayenne.modeler.util;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
 import org.apache.cayenne.configuration.DataChannelDescriptor;
 import org.apache.cayenne.configuration.DataNodeDescriptor;
 import org.apache.cayenne.map.Attribute;
@@ -48,6 +43,11 @@ import org.apache.cayenne.query.EJBQLQuery;
 import org.apache.cayenne.query.Query;
 import org.apache.cayenne.util.Util;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Provides utility methods to perform various manipulations with project objects.
  */
@@ -198,7 +198,7 @@ public class ProjectUtil {
         }
     }
 
-    
+
     /**
      * Changes the name of the embeddable attribute and all references to this embeddable attribute.
      */
@@ -207,7 +207,7 @@ public class ProjectUtil {
 
         attribute.setName(newName);
         Embeddable embeddable = attribute.getEmbeddable();
-        
+
         if (embeddable != null) {
             embeddable.removeAttribute(oldName);
             embeddable.addAttribute(attribute);
@@ -245,46 +245,15 @@ public class ProjectUtil {
             for (ObjAttribute att : entity.getAttributes()) {
 
                 // If flattenet atribute
-                if (att.getDbAttributePath() != null
-                        && att.getDbAttributePath().contains(".")) {
-                    String[] pathSplit = att.getDbAttributePath().split("\\.");
+                String dbAttributePath = att.getDbAttributePath();
+                if (dbAttributePath != null
+                        && dbAttributePath.contains(".")) {
+                    String[] pathSplit = dbAttributePath.split("\\.");
 
                     // If flattened attribute
                     if (pathSplit.length > 1) {
 
-                        DbEntity currentEnt = dbEnt;
-                        StringBuilder pathBuf = new StringBuilder();
-                        boolean isTruePath = true;
-
-                        if (currentEnt != null) {
-
-                            for (int j = 0; j < pathSplit.length; j++) {
-
-                                if (j == pathSplit.length - 1 && isTruePath) {
-                                    DbAttribute dbAttribute = (DbAttribute) currentEnt
-                                            .getAttribute(pathSplit[j]);
-                                    if (dbAttribute != null) {
-                                        pathBuf.append(dbAttribute.getName());
-                                    }
-                                    else {
-                                        isTruePath = false;
-                                    }
-                                }
-                                else if (isTruePath) {
-                                    DbRelationship dbRelationship = (DbRelationship) currentEnt
-                                            .getRelationship(pathSplit[j]);
-                                    if (dbRelationship != null) {
-                                        currentEnt = (DbEntity) dbRelationship
-                                                .getTargetEntity();
-                                        pathBuf.append(dbRelationship.getName());
-                                        pathBuf.append(".");
-                                    }
-                                    else {
-                                        isTruePath = false;
-                                    }
-                                }
-                            }
-                        }
+                        boolean isTruePath = isDbAttributePathCorrect(dbEnt, dbAttributePath);
 
                         if (!isTruePath) {
                             att.setDbAttributePath(null);
@@ -319,6 +288,34 @@ public class ProjectUtil {
     }
 
     /**
+     * check if path is correct. path is correct when he consist from <code>DbRelationship</code>
+     * objects, each <code>DbRelationship</code> object have  following <code>DbRelationship</code>
+     * object as a target, last component is <code>DbAttribute</code>
+     *
+     * @param currentEnt current db entity
+     * @param dbAttributePath path to check
+     * @return if path is correct return true
+     */
+    public static boolean isDbAttributePathCorrect(DbEntity currentEnt, String dbAttributePath) {
+        if (currentEnt == null) {
+            return true;
+        }
+
+        String[] pathSplit = dbAttributePath.split("\\.");
+
+        int size = pathSplit.length - 1;
+        for (int j = 0; j < size; j++) {
+            DbRelationship relationship = currentEnt.getRelationship(pathSplit[j]);
+            if (relationship == null) {
+                return false;
+            }
+            currentEnt = relationship.getTargetEntity();
+        }
+
+        return currentEnt.getAttribute(pathSplit[(size)]) != null;
+    }
+
+    /**
      * Clears all the mapping between this obj entity and its current db entity. Clears
      * mapping between entities, attributes and relationships.
      */
@@ -439,4 +436,4 @@ public class ProjectUtil {
         }
         return relationships;
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/b444b1df/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/SortButtonRenderer.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/SortButtonRenderer.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/SortButtonRenderer.java
index 524d8ba..595ba4d 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/SortButtonRenderer.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/SortButtonRenderer.java
@@ -18,51 +18,33 @@
  ****************************************************************/
 package org.apache.cayenne.modeler.util;
 
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Insets;
-import java.util.Hashtable;
-
 import javax.swing.BorderFactory;
-import javax.swing.JButton;
+import javax.swing.JLabel;
 import javax.swing.JTable;
-import javax.swing.border.MatteBorder;
+import javax.swing.border.Border;
 import javax.swing.table.TableCellRenderer;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.util.Hashtable;
 
-public class SortButtonRenderer extends JButton implements TableCellRenderer {
+public class SortButtonRenderer extends JLabel implements TableCellRenderer {
 
     public static final int NONE = 0;
     public static final int DOWN = 1;
     public static final int UP = 2;
 
-    private int pushedColumn;
     private Hashtable state;
-    private JButton downButton, upButton;
+    private JLabel downLabel , upLabel;
 
     public SortButtonRenderer() {
-        MatteBorder matteBorder = BorderFactory.createMatteBorder(0, 0, 1, 1, Color.gray);
-        setBorder(matteBorder);
-
-        pushedColumn = -1;
         state = new Hashtable();
 
-        setMargin(new Insets(0, 0, 0, 0));
-        setHorizontalTextPosition(CENTER);
-        setIcon(new BlankIcon());
+        downLabel = new JLabel();
+        downLabel.setIcon(new BevelArrowIcon(BevelArrowIcon.DOWN, false, false));
 
-        downButton = new JButton();
-
-        downButton.setBorder(matteBorder);
-        downButton.setMargin(new Insets(0, 0, 0, 0));
-        downButton.setHorizontalTextPosition(LEFT);
-        downButton.setIcon(new BevelArrowIcon(BevelArrowIcon.DOWN, false, false));
-        downButton.setPressedIcon(new BevelArrowIcon(BevelArrowIcon.DOWN, false, true));
-
-        upButton = new JButton();
-        upButton.setBorder(matteBorder);
-        upButton.setMargin(new Insets(0, 0, 0, 0));
-        upButton.setHorizontalTextPosition(LEFT);
-        upButton.setIcon(new BevelArrowIcon(BevelArrowIcon.UP, false, false));
+        upLabel = new JLabel();
+        upLabel.setIcon(new BevelArrowIcon(BevelArrowIcon.UP, false, false));
     }
 
     public Component getTableCellRendererComponent(
@@ -72,49 +54,51 @@ public class SortButtonRenderer extends JButton implements TableCellRenderer {
             boolean hasFocus,
             int row,
             int column) {
-        JButton button = this;
-        Object obj = state.get(new Integer(column));
+        JLabel  label = this;
+        Object obj = state.get(column);
 
         if (obj != null) {
-            if (((Integer) obj).intValue() == DOWN) {
-                button = downButton;
+            if ((Integer) obj == DOWN) {
+                label = downLabel;
             }
             else {
-                button = upButton;
+                label = upLabel;
             }
         }
-        button.setText((value == null) ? "" : value.toString());
-        return button;
-    }
-
-    public void setPressedColumn(int col) {
-        pushedColumn = col;
+        Border border = BorderFactory.createLineBorder(Color.GRAY, 1);
+        label.setText(' ' + ((value == null) ? "" : value.toString()));
+        label.setFont(new Font("Verdana", Font.BOLD , 13));
+        label.setHorizontalTextPosition(JLabel.LEFT);
+        label.setBorder(border);
+        label.setOpaque(true);
+        label.setBackground(new Color(204, 238, 255));
+        return label;
     }
 
     public void setSelectedColumn(int col, boolean isAscOrder) {
-        if (col < 0)
+        if (col < 0) {
             return;
+        }
         Integer value = null;
         //shows the direction of ordering
         if (isAscOrder) {
-            value = new Integer(DOWN);
-        }
-        else {
-            value = new Integer(UP);
+            value = DOWN;
+        } else {
+            value = UP;
         }
 
         state.clear();
-        state.put(new Integer(col), value);
+        state.put(col, value);
     }
 
     public int getState(int col) {
         int retValue;
-        Object obj = state.get(new Integer(col));
+        Object obj = state.get(col);
         if (obj == null) {
             retValue = NONE;
         }
         else {
-            if (((Integer) obj).intValue() == DOWN) {
+            if ((Integer) obj == DOWN) {
                 retValue = DOWN;
             }
             else {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/b444b1df/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/TableHeaderListener.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/TableHeaderListener.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/TableHeaderListener.java
index 29bf1a7..b665730 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/TableHeaderListener.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/TableHeaderListener.java
@@ -72,14 +72,12 @@ public class TableHeaderListener extends MouseAdapter {
     }
 
     public void mouseReleased(MouseEvent e) {
-        renderer.setPressedColumn(-1); // clear
         header.repaint();
     }
 
     public void sortByDefinedColumn(int col, int sortCol, boolean order) {
         CayenneTableModel model = (CayenneTableModel) table.getModel();
         if (model.isColumnSortable(sortCol)) {
-            renderer.setPressedColumn(col);
             renderer.setSelectedColumn(col, order);
             header.repaint();
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/b444b1df/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/combo/AutoCompletion.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/combo/AutoCompletion.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/combo/AutoCompletion.java
index 2e86251..9c8a371 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/combo/AutoCompletion.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/combo/AutoCompletion.java
@@ -19,15 +19,17 @@
 
 package org.apache.cayenne.modeler.util.combo;
 
+import javax.swing.JComboBox;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.text.JTextComponent;
+import java.awt.Component;
 import java.awt.event.FocusEvent;
 import java.awt.event.FocusListener;
 import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 
-import javax.swing.JComboBox;
-import javax.swing.SwingUtilities;
-import javax.swing.text.JTextComponent;
-
 /**
  * AutoCompletion class handles user input and suggests matching variants (see CAY-911)
  *
@@ -55,8 +57,8 @@ public class AutoCompletion implements FocusListener, KeyListener, Runnable {
     protected AutoCompletion(final JComboBox comboBox, boolean strict, boolean allowsUserValues) {
         this.comboBox = comboBox;
         textEditor = ((JTextComponent)comboBox.getEditor().getEditorComponent());
-        
-        this.allowsUserValues = allowsUserValues; 
+
+        this.allowsUserValues = allowsUserValues;
         
         suggestionList = new SuggestionList(comboBox, strict);
         
@@ -75,13 +77,17 @@ public class AutoCompletion implements FocusListener, KeyListener, Runnable {
      */
     public static void enable(JComboBox comboBox, boolean strict, boolean allowsUserValues) {
         comboBox.setEditable(true);
-        
+        KeyListener[] listeners = comboBox.getEditor().getEditorComponent().getListeners(KeyListener.class);
         comboBox.setEditor(new CustomTypeComboBoxEditor(comboBox, allowsUserValues));
-        
+        for (KeyListener listener : listeners) {
+            comboBox.getEditor().getEditorComponent().addKeyListener(listener);
+        }
+
+
         AutoCompletion ac = new AutoCompletion(comboBox, strict, allowsUserValues);
         comboBox.addFocusListener(ac);
         ac.textEditor.addKeyListener(ac);
-        
+
         //original keys would not work properly
         SwingUtilities.replaceUIActionMap(comboBox, null);
     }
@@ -100,23 +106,34 @@ public class AutoCompletion implements FocusListener, KeyListener, Runnable {
     }
 
     public void keyPressed(KeyEvent e) {
-        handleKeyPressed(comboBox, e);
+        handleKeyPressed(e);
     }
 
-    public void keyReleased(KeyEvent e) {}
+    public void keyReleased(KeyEvent e) {
+        if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE ||
+                e.getKeyCode() == KeyEvent.VK_ENTER) {
+
+            String text = textEditor.getText();
+            if (comboBox.isShowing()) {
+                suggestionList.hide();
+                suggestionList.filter(text);
+                suggestionList.show();
+            }
+        }
+    }
 
     public void keyTyped(KeyEvent e) {}
-    
+
     public void run() {
         String text = textEditor.getText();
-        
+
         //need to hide first because Swing incorrectly updates popups (getSize() returns
         //dimension not the same as seen on the screen)
         suggestionList.hide();
-        
+
         if (comboBox.isShowing()) {
             suggestionList.filter(text);
-            
+
             if (suggestionList.getItemCount() > 0) {
                 suggestionList.show();
             }
@@ -127,24 +144,31 @@ public class AutoCompletion implements FocusListener, KeyListener, Runnable {
      * Calculates next selection row, according to a pressed key and selects it.
      * This might affect either suggestion list or original popup
      */
-    private void handleKeyPressed(JComboBox comboBox, KeyEvent e) {
+    private void handleKeyPressed(KeyEvent e) {
         boolean suggest = suggestionList.isVisible();
-        
-        int sel, next, max;
-        
+
         if (suggest) {
-            sel = suggestionList.getSelectedIndex();
-            max = suggestionList.getItemCount() - 1;
+            processKeyPressedWhenSuggestionListIsVisible(e);
         }
         else {
-            sel = comboBox.getSelectedIndex();
-            max = comboBox.getItemCount() - 1;
+            processKeyPressedWhenSuggestionListIsInvisible(e);
         }
-        
+
+        //scroll doesn't work in suggestionList..so we will scroll manually
+        suggestionListScrolling();
+
+        textEditor.requestFocus();
+    }
+
+    private void   processKeyPressedWhenSuggestionListIsInvisible(KeyEvent e){
+        int sel = comboBox.getSelectedIndex();
+        int max = comboBox.getItemCount() - 1;
+
+        int next;
         switch (e.getKeyCode()) {
             case KeyEvent.VK_UP:
             case KeyEvent.VK_NUMPAD8:
-                next = sel - 1; 
+                next = sel - 1;
                 break;
             case KeyEvent.VK_DOWN:
             case KeyEvent.VK_NUMPAD2:
@@ -163,42 +187,71 @@ public class AutoCompletion implements FocusListener, KeyListener, Runnable {
                 next = max;
                 break;
             case KeyEvent.VK_ENTER:
-                if (suggest) {
-                    Object value = suggestionList.getSelectedValue();
-                    if (!allowsUserValues && value == null && suggestionList.getItemCount() > 0) {
-                        value = suggestionList.getItemAt(0);
-                    }
-                    
-                    //reset the item (value == null) only if user values are not supported
-                    if (value != null || !allowsUserValues) {
-                        comboBox.setSelectedItem(value);
-                    }
-                    suggestionList.hide();
-                }
                 return;
-                
             case KeyEvent.VK_ESCAPE:
-                if (suggest) {
-                    suggestionList.hide();
-                }
                 return;
-                
-            case KeyEvent.VK_CONTROL:
-            case KeyEvent.VK_ALT:
-            case KeyEvent.VK_SHIFT:
-                return;
-                
             default:
                 //invoke in end of AWT thread so that information in textEditor would update
                 SwingUtilities.invokeLater(this);
                 return;
         }
-        
-        /**
-         * Handle navigation keys
-         */
+        e.consume();
+        handleNavigationKeys(false,next,sel,max);
+    }
 
+    private void processKeyPressedWhenSuggestionListIsVisible(KeyEvent e){
+        int sel = suggestionList.getSelectedIndex();
+        int max = suggestionList.getItemCount() - 1;
+        int next;
+        switch (e.getKeyCode()) {
+            case KeyEvent.VK_UP:
+            case KeyEvent.VK_NUMPAD8:
+                next = sel - 1;
+                break;
+            case KeyEvent.VK_DOWN:
+            case KeyEvent.VK_NUMPAD2:
+                next = sel + 1;
+                break;
+            case KeyEvent.VK_PAGE_UP:
+                next = sel - comboBox.getMaximumRowCount();
+                break;
+            case KeyEvent.VK_PAGE_DOWN:
+                next = sel + comboBox.getMaximumRowCount();
+                break;
+            case KeyEvent.VK_HOME:
+                next = 0;
+                break;
+            case KeyEvent.VK_END:
+                next = max;
+                break;
+            case KeyEvent.VK_ENTER:
+                processEnterPressed();
+                return;
+            case KeyEvent.VK_ESCAPE:
+                suggestionList.hide();
+                return;
+            default:
+                //invoke in end of AWT thread so that information in textEditor would update
+                SwingUtilities.invokeLater(this);
+                return;
+        }
         e.consume();
+        handleNavigationKeys(true,next,sel,max);
+    }
+
+    private void processEnterPressed(){
+        Object value = suggestionList.getSelectedValue();
+        if (!allowsUserValues && value == null && suggestionList.getItemCount() > 0) {
+            value = suggestionList.getItemAt(0);
+        }
+        //reset the item (value == null) only if user values are not supported
+        if (value != null || !allowsUserValues) {
+            comboBox.setSelectedItem(value);
+        }
+        suggestionList.hide();
+    }
+
+    private void handleNavigationKeys(boolean suggest, int next, int sel, int max){
         if (!suggest && !comboBox.isPopupVisible()) {
             comboBox.setPopupVisible(true);
             return;
@@ -208,22 +261,33 @@ public class AutoCompletion implements FocusListener, KeyListener, Runnable {
             if (next < 0) {
                 next = 0;
             }
-        
+
             if (next > max) {
                 next = max;
             }
-            
+
             if (next != sel) {
                 if (suggest) {
                     suggestionList.setSelectedIndex(next);
-                }
-                else {
-                   comboBox.setPopupVisible(true);
-                   comboBox.setSelectedIndex(next);
+                } else {
+                    comboBox.setPopupVisible(true);
+                    comboBox.setSelectedIndex(next);
                 }
             }
-            
-            textEditor.requestFocus();
+        }
+    }
+
+    private void suggestionListScrolling(){
+        Component c = suggestionList.getComponent(0);
+        if (c instanceof JScrollPane) {
+            double height = suggestionList.getPreferredSize().getHeight();
+            int itemCount = suggestionList.getItemCount();
+            int selectedIndex = suggestionList.getSelectedIndex();
+            double scrollValue = Math.ceil(height*selectedIndex/itemCount);
+            JScrollPane scrollPane = (JScrollPane) c;
+            JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
+            scrollBar.setValue((int) scrollValue);
         }
     }
 }
+