~ubuntu-branches/debian/stretch/insubstantial/stretch

« back to all changes in this revision

Viewing changes to laf-widget/src/main/java/org/pushingpixels/lafwidget/menu/MenuSearchWidget.java

  • Committer: Package Import Robot
  • Author(s): Felix Natter
  • Date: 2016-01-18 20:58:45 UTC
  • Revision ID: package-import@ubuntu.com-20160118205845-crbmrkda61qsi5qa
Tags: upstream-7.3+dfsg2
ImportĀ upstreamĀ versionĀ 7.3+dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2005-2010 Laf-Widget Kirill Grouchnikov. All Rights Reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions are met:
 
6
 *
 
7
 *  o Redistributions of source code must retain the above copyright notice,
 
8
 *    this list of conditions and the following disclaimer.
 
9
 *
 
10
 *  o Redistributions in binary form must reproduce the above copyright notice,
 
11
 *    this list of conditions and the following disclaimer in the documentation
 
12
 *    and/or other materials provided with the distribution.
 
13
 *
 
14
 *  o Neither the name of Laf-Widget Kirill Grouchnikov nor the names of
 
15
 *    its contributors may be used to endorse or promote products derived
 
16
 *    from this software without specific prior written permission.
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
19
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 
20
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
21
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
22
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
25
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
26
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 
27
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 
28
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
 */
 
30
package org.pushingpixels.lafwidget.menu;
 
31
 
 
32
import java.awt.*;
 
33
import java.awt.event.*;
 
34
import java.beans.PropertyChangeEvent;
 
35
import java.beans.PropertyChangeListener;
 
36
import java.util.*;
 
37
 
 
38
import javax.swing.*;
 
39
 
 
40
import org.pushingpixels.lafwidget.*;
 
41
 
 
42
/**
 
43
 * Adds menu search panel to menu bars.
 
44
 * 
 
45
 * @author Kirill Grouchnikov
 
46
 */
 
47
public class MenuSearchWidget extends LafWidgetAdapter<JMenuBar> implements
 
48
                Resettable {
 
49
        /**
 
50
         * Boolean flag to prevent infinite loop. Maybe need to use something more
 
51
         * elegant.
 
52
         */
 
53
        private boolean inEvent = false;
 
54
 
 
55
        /**
 
56
         * Listens on changes to the component orientation.
 
57
         */
 
58
        protected PropertyChangeListener propertyListener;
 
59
 
 
60
        /**
 
61
         * The associated search panel.
 
62
         */
 
63
        private SearchPanel searchPanel;
 
64
 
 
65
        /**
 
66
         * Panel for searching the menus.
 
67
         * 
 
68
         * @author Kirill Grouchnikov
 
69
         */
 
70
        private class SearchPanel extends JPanel {
 
71
                /**
 
72
                 * Toggle button for showing / hiding search controls.
 
73
                 */
 
74
                private JToggleButton searchButton;
 
75
 
 
76
                /**
 
77
                 * Text field for entering search string.
 
78
                 */
 
79
                private JTextField searchStringField;
 
80
 
 
81
                // /**
 
82
                // * The associated menu bar.
 
83
                // */
 
84
                // private JMenuBar menuBar;
 
85
                //
 
86
                /**
 
87
                 * The result buttons. Key is {@link Integer}, value is {@link JButton}.
 
88
                 */
 
89
                private Map<Integer, JButton> resultButtons;
 
90
 
 
91
                /**
 
92
                 * Simple constructor.
 
93
                 * 
 
94
                 * @param menuBar
 
95
                 *            The associated menu bar.
 
96
                 */
 
97
                public SearchPanel(final JMenuBar menuBar) {
 
98
                        // this.menuBar = menuBar;
 
99
                        this.setLayout(new SearchResultsLayout(this));
 
100
 
 
101
                        // Search button (toggle) with tooltip.
 
102
                        LafWidgetSupport support = LafWidgetRepository.getRepository()
 
103
                                        .getLafSupport();
 
104
                        int iconDim = support.getLookupIconSize();
 
105
                        int buttonDim = support.getLookupButtonSize();
 
106
                        Icon searchIcon = (support == null) ? LafWidgetUtilities
 
107
                                        .getSearchIcon(iconDim, jcomp.getComponentOrientation()
 
108
                                                        .isLeftToRight()) : support.getSearchIcon(iconDim,
 
109
                                        jcomp.getComponentOrientation());
 
110
                        this.searchButton = new JToggleButton(searchIcon);
 
111
                        this.searchButton.setPreferredSize(new Dimension(buttonDim,
 
112
                                        buttonDim));
 
113
                        ResourceBundle bundle = LafWidgetUtilities
 
114
                                        .getResourceBundle(menuBar);
 
115
                        this.searchButton.setToolTipText(bundle
 
116
                                        .getString("Tooltip.menuSearchButton"));
 
117
                        this.searchButton.setFocusable(false);
 
118
                        if (support != null)
 
119
                                support.markButtonAsFlat(this.searchButton);
 
120
                        this.add(this.searchButton);
 
121
 
 
122
                        // Add action listener on the toggle button. Based on the
 
123
                        // state of the toggle button, the search field and result buttons
 
124
                        // will be set visible or invisible.
 
125
                        this.searchButton.addActionListener(new ActionListener() {
 
126
                                @Override
 
127
                public void actionPerformed(ActionEvent e) {
 
128
                                        SwingUtilities.invokeLater(new Runnable() {
 
129
                                                @Override
 
130
                        public void run() {
 
131
                                                        boolean toShow = SearchPanel.this.searchButton
 
132
                                                                        .isSelected();
 
133
                                                        SearchPanel.this.searchStringField
 
134
                                                                        .setVisible(toShow);
 
135
                                                        SearchPanel.this.searchStringField.requestFocus();
 
136
                                                        for (JButton resultButton : SearchPanel.this.resultButtons
 
137
                                                                        .values()) {
 
138
                                                                resultButton.setVisible(toShow);
 
139
                                                        }
 
140
                                                        SearchPanel.this.repaint();
 
141
                                                        SearchPanel.this.revalidate();
 
142
                                                }
 
143
                                        });
 
144
                                }
 
145
                        });
 
146
                        // add mouse listener to remove the search panel on mouse
 
147
                        // click when CTRL button is pressed.
 
148
                        this.searchButton.addMouseListener(new MouseAdapter() {
 
149
                                @Override
 
150
                                public void mousePressed(MouseEvent e) {
 
151
                                        if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) {
 
152
                                                SwingUtilities.invokeLater(new Runnable() {
 
153
                                                        @Override
 
154
                            public void run() {
 
155
                                                                SearchPanel.this.removeAll();
 
156
                                                                SearchPanel.this.repaint();
 
157
                                                                jcomp.revalidate();
 
158
                                                        }
 
159
                                                });
 
160
                                        }
 
161
                                }
 
162
                        });
 
163
 
 
164
                        // Search field.
 
165
                        this.searchStringField = new JTextField();
 
166
                        this.searchStringField.setColumns(10);
 
167
                        this.add(this.searchStringField);
 
168
                        this.searchStringField.setVisible(false);
 
169
                        this.searchStringField.setToolTipText(bundle
 
170
                                        .getString("Tooltip.menuSearchField"));
 
171
 
 
172
                        // Map to hold the result buttons (need for the icon reset
 
173
                        // on theme change and layout manager).
 
174
                        this.resultButtons = new HashMap<Integer, JButton>();
 
175
                        this.searchStringField.addActionListener(new ActionListener() {
 
176
                                @Override
 
177
                public void actionPerformed(ActionEvent e) {
 
178
                                        String searchString = SearchPanel.this.searchStringField
 
179
                                                        .getText().toLowerCase();
 
180
                                        // See if there is at least one non-white space character.
 
181
                                        // This is fix for bug 54
 
182
                                        if (searchString.trim().length() == 0) {
 
183
                                                return;
 
184
                                        }
 
185
 
 
186
                                        // remove all old buttons
 
187
                                        for (JButton toRemove : SearchPanel.this.resultButtons
 
188
                                                        .values()) {
 
189
                                                SearchPanel.this.remove(toRemove);
 
190
                                        }
 
191
                                        SearchPanel.this.resultButtons.clear();
 
192
                                        // find all matching menu items / menus
 
193
                                        LinkedList<SearchResult> searchResults = SearchPanel.this
 
194
                                                        .findOccurences(searchString);
 
195
                                        int count = 0;
 
196
                                        for (SearchResult searchResult : searchResults) {
 
197
                                                // show only first 16 results.
 
198
                                                if (count == 16)
 
199
                                                        break;
 
200
                                                // create new button with binary icon
 
201
                                                LafWidgetSupport support = LafWidgetRepository
 
202
                                                                .getRepository().getLafSupport();
 
203
                                                Icon markerIcon = support.getNumberIcon(count + 1);
 
204
                                                JButton resultButton = new JButton(markerIcon);
 
205
                                                // set action listener (to show the menu).
 
206
                                                resultButton
 
207
                                                                .addActionListener(new SearchResultListener(
 
208
                                                                                searchResult));
 
209
                                                // check if the path to the menu (item) has
 
210
                                                // only enabled items.
 
211
                                                resultButton.setEnabled(searchResult.isEnabled());
 
212
                                                SearchPanel.this.add(resultButton);
 
213
                                                SearchPanel.this.resultButtons.put(count + 1, resultButton);
 
214
                                                resultButton.setToolTipText("<html><body><b>"
 
215
                                                                + searchResult.toString()
 
216
                                                                + "</b><br>"
 
217
                                                                + LafWidgetUtilities.getResourceBundle(menuBar)
 
218
                                                                                .getString("Tooltip.menuSearchTooltip")
 
219
                                                                + "</html>");
 
220
                                                if (support != null)
 
221
                                                        support.markButtonAsFlat(resultButton);
 
222
                                                count++;
 
223
                                        }
 
224
                                        SearchPanel.this.repaint();
 
225
                                        jcomp.revalidate();
 
226
                                }
 
227
                        });
 
228
                }
 
229
 
 
230
                /**
 
231
                 * Returns all occurences of the specified string in the menus and menu
 
232
                 * items of the associated menu bar.
 
233
                 * 
 
234
                 * @param searchPattern
 
235
                 *            Pattern to search (no wildcards yet).
 
236
                 * @return All occurences of the specified string in the menus and menu
 
237
                 *         items of the associated menu bar.
 
238
                 */
 
239
                private LinkedList<SearchResult> findOccurences(String searchPattern) {
 
240
                        LinkedList<SearchResult> result = new LinkedList<SearchResult>();
 
241
 
 
242
                        LinkedList<JMenu> currentPath = new LinkedList<JMenu>();
 
243
 
 
244
                        for (int i = 0; i < jcomp.getComponentCount(); i++) {
 
245
                                Component component = jcomp.getComponent(i);
 
246
                                if (component instanceof JMenu) {
 
247
                                        JMenu menu = (JMenu) component;
 
248
                                        this.checkMenu(currentPath, menu, searchPattern, result);
 
249
                                }
 
250
                        }
 
251
 
 
252
                        return result;
 
253
                }
 
254
 
 
255
                /**
 
256
                 * Recursively scans the specified menu (item) and updates the list that
 
257
                 * contains all occurences of the specified string in the contained
 
258
                 * menus and menu items.
 
259
                 * 
 
260
                 * @param currentPath
 
261
                 *            The path to the current menu (item). Contains
 
262
                 *            {@link JMenu}s.
 
263
                 * @param menuItem
 
264
                 *            The menu (item) itself that is being tested.
 
265
                 * @param searchPattern
 
266
                 *            Pattern to search (no wildcards yet).
 
267
                 * @param matchingResults
 
268
                 *            All occurences of the specified string up until now. After
 
269
                 *            <code>this</code> function returns, will also contain
 
270
                 *            all occurences of the specified string in the contained
 
271
                 *            menu (item)s. Contains {@link SearchResult}s.
 
272
                 */
 
273
                private void checkMenu(LinkedList<JMenu> currentPath,
 
274
                                JMenuItem menuItem, String searchPattern,
 
275
                                LinkedList<SearchResult> matchingResults) {
 
276
                        String menuItemText = menuItem.getText();
 
277
                        if (menuItemText.toLowerCase().indexOf(searchPattern) >= 0) {
 
278
                                matchingResults.addLast(new SearchResult(jcomp, currentPath,
 
279
                                                menuItem));
 
280
                        }
 
281
                        if (menuItem instanceof JMenu) {
 
282
                                JMenu menu = (JMenu) menuItem;
 
283
                                currentPath.addLast(menu);
 
284
                                for (int i = 0; i < menu.getMenuComponentCount(); i++) {
 
285
                                        Component menuComponent = menu.getMenuComponent(i);
 
286
                                        if (menuComponent instanceof JMenuItem) {
 
287
                                                JMenuItem childItem = (JMenuItem) menuComponent;
 
288
                                                this.checkMenu(currentPath, childItem, searchPattern,
 
289
                                                                matchingResults);
 
290
                                        }
 
291
                                }
 
292
                                currentPath.removeLast();
 
293
                        }
 
294
                }
 
295
 
 
296
                /*
 
297
                 * (non-Javadoc)
 
298
                 * 
 
299
                 * @see java.awt.Component#setVisible(boolean)
 
300
                 */
 
301
                @Override
 
302
                public void setVisible(boolean aFlag) {
 
303
                        super.setVisible(aFlag);
 
304
                        if (aFlag)
 
305
                                this.searchStringField.requestFocus();
 
306
                }
 
307
        }
 
308
 
 
309
        /**
 
310
         * Listener on the <code>search result</code> button. The action itself -
 
311
         * show the associated menu path to the menu item that contains the string
 
312
         * that has been specified during the search.
 
313
         * 
 
314
         * @author Kirill Grouchnikov
 
315
         */
 
316
        private static class SearchResultListener implements ActionListener {
 
317
                /**
 
318
                 * The associated search result.
 
319
                 */
 
320
                private SearchResult searchResult;
 
321
 
 
322
                /**
 
323
                 * Simple constructor.
 
324
                 * 
 
325
                 * @param searchResult
 
326
                 *            The associated search result.
 
327
                 */
 
328
                public SearchResultListener(SearchResult searchResult) {
 
329
                        super();
 
330
                        this.searchResult = searchResult;
 
331
                }
 
332
 
 
333
                /*
 
334
                 * (non-Javadoc)
 
335
                 * 
 
336
                 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
 
337
                 */
 
338
                @Override
 
339
        public void actionPerformed(ActionEvent e) {
 
340
                        // start opening the menus
 
341
                        MenuElement[] menuElements = this.searchResult.menuElements;
 
342
                        MenuSelectionManager.defaultManager().setSelectedPath(menuElements);
 
343
                }
 
344
        }
 
345
 
 
346
        /**
 
347
         * Single result of menu search.
 
348
         * 
 
349
         * @author Kirill Grouchnikov
 
350
         */
 
351
        private static class SearchResult {
 
352
                /**
 
353
                 * Path to the menu (item). The first element is always {@link JMenuBar},
 
354
                 * and after each {@link JMenu} there is it's
 
355
                 * {@link JMenu#getPopupMenu()}.
 
356
                 */
 
357
                private MenuElement[] menuElements;
 
358
 
 
359
                /**
 
360
                 * Simple constructor.
 
361
                 * 
 
362
                 * @param menuBar
 
363
                 *            The main menu bar.
 
364
                 * @param menuPath
 
365
                 *            The menus leading to the matching entry. Contains
 
366
                 *            {@link JMenu}s.
 
367
                 * @param menuLeaf
 
368
                 *            The menu (item) that matches the search pattern string.
 
369
                 */
 
370
                public SearchResult(JMenuBar menuBar, LinkedList<JMenu> menuPath,
 
371
                                JMenuItem menuLeaf) {
 
372
                        int count = 1;
 
373
                        if (menuPath != null)
 
374
                                count += 2 * menuPath.size();
 
375
                        if (menuLeaf != null)
 
376
                                count++;
 
377
                        this.menuElements = new MenuElement[count];
 
378
                        count = 0;
 
379
 
 
380
                        // the first element is the menu bar itself
 
381
                        this.menuElements[count++] = menuBar;
 
382
                        if (menuPath != null) {
 
383
                                for (JMenu menu : menuPath) {
 
384
                                        // JMenu menu = (JMenu) it.next();
 
385
                                        this.menuElements[count++] = menu;
 
386
                                        // important - don't forget the popup menu of the menu
 
387
                                        this.menuElements[count++] = menu.getPopupMenu();
 
388
                                }
 
389
                        }
 
390
                        if (menuLeaf != null)
 
391
                                this.menuElements[count] = menuLeaf;
 
392
                }
 
393
 
 
394
                /**
 
395
                 * Returns the path to the menu (item).
 
396
                 * 
 
397
                 * @return Path to the menu (item). The first element is always
 
398
                 *         {@link JMenuBar}, and after each {@link JMenu} there is it's
 
399
                 *         {@link JMenu#getPopupMenu()}.
 
400
                 */
 
401
                public MenuElement[] getMenuElements() {
 
402
                        return this.menuElements;
 
403
                }
 
404
 
 
405
                /*
 
406
                 * (non-Javadoc)
 
407
                 * 
 
408
                 * @see java.lang.Object#toString()
 
409
                 */
 
410
                @Override
 
411
                public String toString() {
 
412
                        StringBuffer sb = new StringBuffer();
 
413
                        if (this.menuElements != null) {
 
414
                                String sep = "";
 
415
                                for (int i = 0; i < this.menuElements.length; i++) {
 
416
                                        MenuElement menuElem = this.menuElements[i];
 
417
                                        if (menuElem instanceof JMenuItem) {
 
418
                                                sb.append(sep);
 
419
                                                sep = " -> ";
 
420
                                                sb.append(((JMenuItem) menuElem).getText());
 
421
                                        }
 
422
                                }
 
423
                        }
 
424
                        return sb.toString();
 
425
                }
 
426
 
 
427
                /**
 
428
                 * Checks that all entries leading to the associated menu (item) are
 
429
                 * enabled.
 
430
                 * 
 
431
                 * @return <code>true</code> if all entries leading to the associated
 
432
                 *         menu (item) are enabled, <code>false</code> otherwise.
 
433
                 */
 
434
                public boolean isEnabled() {
 
435
                        // all parts must be enabled
 
436
                        for (int i = 0; i < this.menuElements.length; i++) {
 
437
                                MenuElement menuElem = this.menuElements[i];
 
438
                                if (menuElem instanceof JMenuItem) {
 
439
                                        JMenuItem menuItem = (JMenuItem) menuElem;
 
440
                                        if (!menuItem.isEnabled())
 
441
                                                return false;
 
442
                                }
 
443
                        }
 
444
                        return true;
 
445
                }
 
446
        }
 
447
 
 
448
        /**
 
449
         * Returns the number of menu items under the specified menu item.
 
450
         * 
 
451
         * @param menuItem
 
452
         *            The root menu item.
 
453
         * @return The number of menu items under the specified menu item.
 
454
         */
 
455
        private static int getMenuItemCount(JMenuItem menuItem) {
 
456
                int result = 1;
 
457
 
 
458
                if (menuItem instanceof JMenu) {
 
459
                        JMenu menu = (JMenu) menuItem;
 
460
                        for (int i = 0; i < menu.getMenuComponentCount(); i++) {
 
461
                                Component child = menu.getMenuComponent(i);
 
462
                                if (child instanceof JMenuItem)
 
463
                                        result += MenuSearchWidget
 
464
                                                        .getMenuItemCount((JMenuItem) child);
 
465
                        }
 
466
                }
 
467
 
 
468
                return result;
 
469
        }
 
470
 
 
471
        /**
 
472
         * Returns the number of menu items under the specified menu bar.
 
473
         * 
 
474
         * @param menuBar
 
475
         *            The root menu bar.
 
476
         * @return The number of menu items under the specified menu bar.
 
477
         */
 
478
        public static int getMenuItemCount(JMenuBar menuBar) {
 
479
                int result = 0;
 
480
 
 
481
                for (int i = 0; i < menuBar.getMenuCount(); i++) {
 
482
                        JMenu menu = menuBar.getMenu(i);
 
483
                        if (menu != null) {
 
484
                                result += MenuSearchWidget.getMenuItemCount(menu);
 
485
                        }
 
486
                }
 
487
 
 
488
                return result;
 
489
        }
 
490
 
 
491
        // /**
 
492
        // * Hides search panels recursively on the specified component.
 
493
        // *
 
494
        // * @param comp
 
495
        // * Component.
 
496
        // * @param toRepaint
 
497
        // * Indication whether the relevant menu bars should be repainted.
 
498
        // */
 
499
        // private static void hideSearchPanels(Component comp, final boolean toRepaint)
 
500
        // {
 
501
        // if (comp instanceof JFrame) {
 
502
        // JFrame jf = (JFrame) comp;
 
503
        // if (jf.getRootPane() != null) {
 
504
        // JMenuBar menuBar = jf.getJMenuBar();
 
505
        // if (menuBar != null) {
 
506
        // for (int j = 0; j < menuBar.getComponentCount(); j++) {
 
507
        // if (menuBar.getComponent(j) instanceof SearchPanel) {
 
508
        // SearchPanel sPanel = (SearchPanel) menuBar
 
509
        // .getComponent(j);
 
510
        // menuBar.remove(sPanel);
 
511
        // if (toRepaint)
 
512
        // menuBar.repaint();
 
513
        // break;
 
514
        // }
 
515
        // }
 
516
        // }
 
517
        // }
 
518
        // }
 
519
        //
 
520
        // if (comp instanceof JInternalFrame) {
 
521
        // JInternalFrame jif = (JInternalFrame) comp;
 
522
        // if (jif.getRootPane() != null) {
 
523
        // JMenuBar menuBar = jif.getJMenuBar();
 
524
        // if (menuBar != null) {
 
525
        // for (int j = 0; j < menuBar.getComponentCount(); j++) {
 
526
        // if (menuBar.getComponent(j) instanceof SearchPanel) {
 
527
        // SearchPanel sPanel = (SearchPanel) menuBar
 
528
        // .getComponent(j);
 
529
        // menuBar.remove(sPanel);
 
530
        // if (toRepaint)
 
531
        // menuBar.repaint();
 
532
        // break;
 
533
        // }
 
534
        // }
 
535
        // }
 
536
        // }
 
537
        // }
 
538
        //
 
539
        // if (comp instanceof Container) {
 
540
        // Container cont = (Container) comp;
 
541
        // for (int i = 0; i < cont.getComponentCount(); i++) {
 
542
        // Component child = cont.getComponent(i);
 
543
        // if (child instanceof JDesktopIcon)
 
544
        // child = ((JDesktopIcon) child).getInternalFrame();
 
545
        // hideSearchPanels(child, toRepaint);
 
546
        // }
 
547
        // }
 
548
        // }
 
549
        //
 
550
        // /**
 
551
        // * Hides search panels on all menu bars (both JFrames and JInternalFrames).
 
552
        // *
 
553
        // * @param toRepaint
 
554
        // * Indication whether the relevant menu bars should be repainted.
 
555
        // */
 
556
        // public static void hideSearchPanels(final boolean toRepaint) {
 
557
        // SwingUtilities.invokeLater(new Runnable() {
 
558
        // public void run() {
 
559
        // Frame[] frames = Frame.getFrames();
 
560
        // for (int i = 0; i < frames.length; i++) {
 
561
        // hideSearchPanels(frames[i], toRepaint);
 
562
        // }
 
563
        // };
 
564
        // });
 
565
        // }
 
566
 
 
567
        // /**
 
568
        // * Shows search panels on all descendant internal frames of the specified
 
569
        // * component.
 
570
        // *
 
571
        // * @param comp
 
572
        // * A component.
 
573
        // */
 
574
        // protected static void showSearchPanels(Component comp) {
 
575
        // if (comp instanceof JDesktopPane) {
 
576
        // JDesktopPane desktop = (JDesktopPane) comp;
 
577
        // JInternalFrame[] iFrames = desktop.getAllFrames();
 
578
        // for (int i = 0; i < iFrames.length; i++) {
 
579
        // JInternalFrame jif = iFrames[i];
 
580
        // if (jif.getRootPane() != null) {
 
581
        // JMenuBar menuBar = jif.getJMenuBar();
 
582
        // if (menuBar != null)
 
583
        // SwingUtilities.updateComponentTreeUI(menuBar);
 
584
        // }
 
585
        // }
 
586
        // return;
 
587
        // }
 
588
        // if (comp instanceof Container) {
 
589
        // Container cont = (Container) comp;
 
590
        // for (int i = 0; i < cont.getComponentCount(); i++) {
 
591
        // MenuSearchWidget.showSearchPanels(cont.getComponent(i));
 
592
        // }
 
593
        // }
 
594
        // }
 
595
        //
 
596
        // /**
 
597
        // * Shows search panels on all menu bars (both JFrames and JInternalFrames).
 
598
        // */
 
599
        // public static void showSearchPanels() {
 
600
        // SwingUtilities.invokeLater(new Runnable() {
 
601
        // public void run() {
 
602
        // Frame[] frames = Frame.getFrames();
 
603
        // for (int i = 0; i < frames.length; i++) {
 
604
        // Frame frame = frames[i];
 
605
        //
 
606
        // if (frame instanceof JFrame) {
 
607
        // JFrame jf = (JFrame) frame;
 
608
        // if (jf.getRootPane() != null) {
 
609
        // JMenuBar menuBar = jf.getJMenuBar();
 
610
        // if (menuBar != null)
 
611
        // SwingUtilities.updateComponentTreeUI(menuBar);
 
612
        // }
 
613
        // }
 
614
        // // fix for defect 134 - menubars on internal frames
 
615
        // MenuSearchWidget.showSearchPanels(frame);
 
616
        // }
 
617
        // };
 
618
        // });
 
619
        // }
 
620
 
 
621
        /*
 
622
         * (non-Javadoc)
 
623
         * 
 
624
         * @see org.pushingpixels.lafwidget.LafWidgetAdapter#installUI()
 
625
         */
 
626
        @Override
 
627
        public void installUI() {
 
628
                final LafWidgetSupport lafSupport = LafWidgetRepository.getRepository()
 
629
                                .getLafSupport();
 
630
                this.searchPanel = new SearchPanel(this.jcomp);
 
631
                this.jcomp.add(searchPanel, this.jcomp.getComponentCount());
 
632
                this.searchPanel.setVisible(lafSupport.toInstallMenuSearch(this.jcomp));
 
633
                // NewMenuSearchWidget.panels.put(this.jcomp, searchPanel);
 
634
                // toAddListener = true;
 
635
                // }
 
636
 
 
637
                // if (toAddListener) {
 
638
                // need to add a container listener that will move a newly added
 
639
                // JMenu one entry before the last (so that our search panel
 
640
                // will always be the last).
 
641
                this.jcomp.addContainerListener(new ContainerAdapter() {
 
642
                        @Override
 
643
                        public void componentAdded(ContainerEvent e) {
 
644
                                if (!(e.getChild() instanceof JMenu))
 
645
                                        return;
 
646
                                if (!inEvent) {
 
647
                                        inEvent = true;
 
648
                                        Component removed = null;
 
649
                                        for (int i = 0; i < MenuSearchWidget.this.jcomp
 
650
                                                        .getComponentCount(); i++) {
 
651
                                                if (MenuSearchWidget.this.jcomp.getComponent(i) instanceof SearchPanel) {
 
652
                                                        removed = MenuSearchWidget.this.jcomp
 
653
                                                                        .getComponent(i);
 
654
                                                        break;
 
655
                                                }
 
656
                                        }
 
657
                                        if (removed != null) {
 
658
                                                MenuSearchWidget.this.jcomp.remove(removed);
 
659
                                                MenuSearchWidget.this.jcomp
 
660
                                                                .add(removed, MenuSearchWidget.this.jcomp
 
661
                                                                                .getComponentCount());
 
662
                                                // Show search panel only if the LAF-specific
 
663
                                                // support requests this
 
664
                                                if (lafSupport.toInstallMenuSearch(jcomp))
 
665
                                                        removed.setVisible(true);
 
666
                                                else
 
667
                                                        removed.setVisible(false);
 
668
                                        }
 
669
                                        inEvent = false;
 
670
                                }
 
671
                        }
 
672
                });
 
673
                // }
 
674
 
 
675
                // SearchPanel sp = (SearchPanel)
 
676
                // NewMenuSearchWidget.panels.get(this.jcomp);
 
677
                // if (sp != null) {
 
678
                searchPanel.applyComponentOrientation(this.jcomp
 
679
                                .getComponentOrientation());
 
680
                // }
 
681
        }
 
682
 
 
683
        /*
 
684
         * (non-Javadoc)
 
685
         * 
 
686
         * @see org.pushingpixels.lafwidget.LafWidgetAdapter#uninstallUI()
 
687
         */
 
688
        @Override
 
689
        public void uninstallUI() {
 
690
                this.jcomp.remove(this.searchPanel);
 
691
                super.uninstallUI();
 
692
        }
 
693
 
 
694
        /*
 
695
         * (non-Javadoc)
 
696
         * 
 
697
         * @see org.pushingpixels.lafwidget.LafWidgetAdapter#installListeners()
 
698
         */
 
699
        @Override
 
700
        public void installListeners() {
 
701
                super.installListeners();
 
702
 
 
703
                this.propertyListener = new PropertyChangeListener() {
 
704
                        @Override
 
705
            public void propertyChange(final PropertyChangeEvent evt) {
 
706
                                if ("componentOrientation".equals(evt.getPropertyName())) {
 
707
                                        // final SearchPanel sp = (SearchPanel)
 
708
                                        // NewMenuSearchWidget.panels
 
709
                                        // .get(NewMenuSearchWidget.this.jcomp);
 
710
                                        SwingUtilities.invokeLater(new Runnable() {
 
711
                                                @Override
 
712
                        public void run() {
 
713
                                                        if (searchPanel != null) {
 
714
                                                                searchPanel
 
715
                                                                                .applyComponentOrientation((ComponentOrientation) evt
 
716
                                                                                                .getNewValue());
 
717
                                                        }
 
718
                                                        MenuSearchWidget.this.reset();
 
719
                                                }
 
720
                                        });
 
721
                                }
 
722
                                if ("locale".equals(evt.getPropertyName())) {
 
723
                                        SwingUtilities.invokeLater(new Runnable() {
 
724
                                                @Override
 
725
                        public void run() {
 
726
                                                        reset();
 
727
                                                }
 
728
                                        });
 
729
                                }
 
730
                        }
 
731
                };
 
732
                this.jcomp.addPropertyChangeListener(this.propertyListener);
 
733
        }
 
734
 
 
735
        /*
 
736
         * (non-Javadoc)
 
737
         * 
 
738
         * @see org.pushingpixels.lafwidget.LafWidgetAdapter#uninstallListeners()
 
739
         */
 
740
        @Override
 
741
        public void uninstallListeners() {
 
742
                this.jcomp.removePropertyChangeListener(this.propertyListener);
 
743
                this.propertyListener = null;
 
744
        }
 
745
 
 
746
        @Override
 
747
    public void reset() {
 
748
                LafWidgetSupport support = LafWidgetRepository.getRepository()
 
749
                                .getLafSupport();
 
750
                // SearchPanel searchPanel = (SearchPanel) NewMenuSearchWidget.panels
 
751
                // .get(this.jcomp);
 
752
                if (searchPanel == null)
 
753
                        return;
 
754
                for (Map.Entry<Integer, JButton> entry : searchPanel.resultButtons
 
755
                                .entrySet()) {
 
756
                        // Map.Entry entry = (Map.Entry) it.next();
 
757
                        int index = entry.getKey();
 
758
                        JButton button = entry.getValue();
 
759
 
 
760
                        Icon markerIcon = (support == null) ? LafWidgetUtilities
 
761
                                        .getHexaMarker(index) : support.getNumberIcon(index);
 
762
                        button.setIcon(markerIcon);
 
763
                }
 
764
                int iconDim = support.getLookupIconSize();
 
765
                Icon searchIcon = (support == null) ? LafWidgetUtilities.getSearchIcon(
 
766
                                iconDim, searchPanel.getComponentOrientation().isLeftToRight())
 
767
                                : support.getSearchIcon(iconDim, searchPanel
 
768
                                                .getComponentOrientation());
 
769
                searchPanel.searchButton.setIcon(searchIcon);
 
770
                ResourceBundle bundle = LafWidgetUtilities
 
771
                                .getResourceBundle(this.jcomp);
 
772
                searchPanel.searchButton.setToolTipText(bundle
 
773
                                .getString("Tooltip.menuSearchButton"));
 
774
                searchPanel.searchStringField.setToolTipText(bundle
 
775
                                .getString("Tooltip.menuSearchField"));
 
776
        }
 
777
 
 
778
        /**
 
779
         * Layout for the search panel. Note that {@link FlowLayout} is almost
 
780
         * perfect for us, but we need the following:
 
781
         * <ul>
 
782
         * <li>Minimum size to be 16*16 (for the search icon)
 
783
         * <li>When there isn't enough place for result buttons, they should
 
784
         * continue (even if they are unseen) and not flow to the next line.
 
785
         * </ul>
 
786
         * 
 
787
         * @author Kirill Grouchnikov
 
788
         */
 
789
        private class SearchResultsLayout implements LayoutManager {
 
790
                /**
 
791
                 * The associated search panel.
 
792
                 */
 
793
                private SearchPanel searchPanel;
 
794
 
 
795
                /**
 
796
                 * Simple constructor.
 
797
                 * 
 
798
                 * @param searchPanel
 
799
                 *            The associated search panel.
 
800
                 */
 
801
                public SearchResultsLayout(SearchPanel searchPanel) {
 
802
                        this.searchPanel = searchPanel;
 
803
                }
 
804
 
 
805
                /*
 
806
                 * (non-Javadoc)
 
807
                 * 
 
808
                 * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String,
 
809
                 *      java.awt.Component)
 
810
                 */
 
811
                @Override
 
812
        public void addLayoutComponent(String name, Component c) {
 
813
                }
 
814
 
 
815
                /*
 
816
                 * (non-Javadoc)
 
817
                 * 
 
818
                 * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component)
 
819
                 */
 
820
                @Override
 
821
        public void removeLayoutComponent(Component c) {
 
822
                }
 
823
 
 
824
                /*
 
825
                 * (non-Javadoc)
 
826
                 * 
 
827
                 * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container)
 
828
                 */
 
829
                @Override
 
830
        public Dimension preferredLayoutSize(Container c) {
 
831
                        if (this.searchPanel.searchButton.isSelected())
 
832
                                return c.getSize();
 
833
                        int buttonSize = LafWidgetRepository.getRepository()
 
834
                                        .getLafSupport().getLookupButtonSize();
 
835
                        return new Dimension(buttonSize, buttonSize);
 
836
                }
 
837
 
 
838
                /*
 
839
                 * (non-Javadoc)
 
840
                 * 
 
841
                 * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container)
 
842
                 */
 
843
                @Override
 
844
        public Dimension minimumLayoutSize(Container c) {
 
845
                        // enough for the search icon
 
846
                        int buttonSize = LafWidgetRepository.getRepository()
 
847
                                        .getLafSupport().getLookupButtonSize();
 
848
                        return new Dimension(buttonSize, buttonSize);
 
849
                }
 
850
 
 
851
                /*
 
852
                 * (non-Javadoc)
 
853
                 * 
 
854
                 * @see java.awt.LayoutManager#layoutContainer(java.awt.Container)
 
855
                 */
 
856
                @Override
 
857
        public void layoutContainer(Container c) {
 
858
                        int height = c.getHeight();
 
859
                        int width = c.getWidth();
 
860
 
 
861
                        if (!this.searchPanel.searchButton.isVisible())
 
862
                                return;
 
863
 
 
864
                        boolean leftToRight = jcomp.getComponentOrientation()
 
865
                                        .isLeftToRight();
 
866
 
 
867
                        if (leftToRight) {
 
868
                                // start from the toggle button
 
869
                                int x = 2;
 
870
                                int sbWidth = this.searchPanel.searchButton.getPreferredSize().width;
 
871
                                int sbHeight = this.searchPanel.searchButton.getPreferredSize().height;
 
872
                                this.searchPanel.searchButton.setBounds(x,
 
873
                                                (height - sbHeight) / 2, sbWidth, sbHeight);
 
874
 
 
875
                                x += (sbWidth + 4);
 
876
 
 
877
                                if (this.searchPanel.isVisible()) {
 
878
                                        // now - text field
 
879
                                        int tbWidth = this.searchPanel.searchStringField
 
880
                                                        .getPreferredSize().width;
 
881
                                        int tbHeight = this.searchPanel.searchStringField
 
882
                                                        .getPreferredSize().height;
 
883
                                        // make the text field fit in the available height
 
884
                                        tbHeight = Math.min(tbHeight, height - 2);
 
885
                                        this.searchPanel.searchStringField.setBounds(x,
 
886
                                                        (height - tbHeight) / 2, tbWidth, tbHeight);
 
887
 
 
888
                                        x += (tbWidth + 2);
 
889
 
 
890
                                        // result buttons
 
891
                                        int buttonCount = this.searchPanel.resultButtons.size();
 
892
                                        for (int i = 1; i <= buttonCount; i++) {
 
893
                                                JButton button = this.searchPanel.resultButtons.get(i);
 
894
                                                int bw = button.getPreferredSize().width;
 
895
                                                int bh = button.getPreferredSize().height;
 
896
 
 
897
                                                button.setBounds(x, (height - bh) / 2, bw, bh);
 
898
                                                x += (bw + 1);
 
899
                                        }
 
900
                                }
 
901
                        } else {
 
902
                                // start from the toggle button
 
903
                                int x = width - 2;
 
904
                                int sbWidth = this.searchPanel.searchButton.getPreferredSize().width;
 
905
                                int sbHeight = this.searchPanel.searchButton.getPreferredSize().height;
 
906
                                this.searchPanel.searchButton.setBounds(x - sbWidth,
 
907
                                                (height - sbHeight) / 2, sbWidth, sbHeight);
 
908
 
 
909
                                x -= (sbWidth + 4);
 
910
 
 
911
                                if (this.searchPanel.isVisible()) {
 
912
                                        // now - text field
 
913
                                        int tbWidth = this.searchPanel.searchStringField
 
914
                                                        .getPreferredSize().width;
 
915
                                        int tbHeight = this.searchPanel.searchStringField
 
916
                                                        .getPreferredSize().height;
 
917
                                        // make the text field fit in the available height
 
918
                                        tbHeight = Math.min(tbHeight, height - 2);
 
919
                                        this.searchPanel.searchStringField.setBounds(x - tbWidth,
 
920
                                                        (height - tbHeight) / 2, tbWidth, tbHeight);
 
921
 
 
922
                                        x -= (tbWidth + 2);
 
923
 
 
924
                                        // result buttons
 
925
                                        int buttonCount = this.searchPanel.resultButtons.size();
 
926
                                        for (int i = 1; i <= buttonCount; i++) {
 
927
                                                JButton button = this.searchPanel.resultButtons.get(i);
 
928
                                                int bw = button.getPreferredSize().width;
 
929
                                                int bh = button.getPreferredSize().height;
 
930
 
 
931
                                                button.setBounds(x - bw, (height - bh) / 2, bw, bh);
 
932
                                                x -= (bw + 1);
 
933
                                        }
 
934
                                }
 
935
                        }
 
936
                }
 
937
        }
 
938
 
 
939
        /*
 
940
         * (non-Javadoc)
 
941
         * 
 
942
         * @see org.pushingpixels.lafwidget.LafWidget#requiresCustomLafSupport()
 
943
         */
 
944
        @Override
 
945
    public boolean requiresCustomLafSupport() {
 
946
                return false;
 
947
        }
 
948
}