public SearchBar(String defaultText, String tooltip, final Component defaultComponent) {
        this.defaultText = defaultText;
        this.searchPanel = new CQueryField();
        this.searchBar = this.searchPanel.getQueryField();
        searchBar.setToolTipText(tooltip);
        searchBar.setColumns(12);

        resetSearchBar();
        searchBar.addFocusListener(new FocusListener() {

            public void focusGained(FocusEvent e) {
                readySearchBar();
            }

            public void focusLost(FocusEvent e) {
                resetSearchBar();
            }
        });

        searchBar.addKeyListener(new KeyAdapter() {

            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    searchBar.setText("");
                    defaultComponent.requestFocusInWindow();
                }
            }
        });

        searchBar.getDocument().addDocumentListener(new DocumentListener() {

            public void changedUpdate(DocumentEvent e) {
                // This method intentionally left blank.
            }

            public void insertUpdate(DocumentEvent e) {
                //System.out.println("Called insertUpdate, offset = " + e.getOffset() + ", query length = " + searchBar.getText().length());
                if (searchBar.getText().equals(SearchBar.this.defaultText)) {
                    return;
                }
                // If the search term changed only at the beginning or end, then only
                // the blocks found already may change.  Remove unmatched blocks from
                // foundBlocks.
                if (e.getOffset() == 0 || e.getOffset() + e.getLength() == searchBar.getText().length()) {
                    performSearch(SearchRange.REMOVE_FROM_FOUND);
                } else {
                    // If the search term changed in the middle, then the blocks found and
                    // the blocks yet to be found may have changed.  Recheck all blocks.
                    performSearch(SearchRange.CHECK_ALL);
                }
            }

            public void removeUpdate(DocumentEvent e) {
                //System.out.println("Called removeUpdate, offset = " + e.getOffset() + ", query length = " + searchBar.getText().length());
                if (searchBar.getText().equals("")) {
                    performSearch(SearchRange.CHECK_ALL);
                } else if (e.getOffset() == 0 || e.getOffset() == searchBar.getText().length()) {
                    // If the search term changed only at the beginning or end, then
                    // the blocks found already do not change.  Check for additional blocks
                    // from the Worspace.
                    performSearch(SearchRange.ADD_FROM_NOT_FOUND);
                } else {
                    // If the search term changed in the middle, then the blocks found may have
                    // changed.  Recheck all blocks.
                    performSearch(SearchRange.CHECK_ALL);
                }
            }
        });

        // Repeat search periodically to refresh results in case elements change,
        // such as when a new block is dragged onto the Workspace and should be included in the results.
        searchUpdater = new Timer(SEARCH_UPDATER_DELAY, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                // Skip the update if the throttle is about to perform a search anyway.
                if (searchThrottle != null && !searchThrottle.isRunning()) {
                    searchRange = SearchRange.CHECK_ALL;
                    performSearchTimerHandler();
                }
            }
        });
        searchUpdater.start();
    }