1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt;
  27 
  28 import java.awt.peer.TaskbarPeer;
  29 import sun.awt.SunToolkit;
  30 
  31 /**
  32  * The {@code Taskbar} class allows a Java application to interact with 
  33  * the system task area (taskbar, Dock, etc.).
  34  * 
  35  * <p>
  36  * There are a variety of interactions depending on the current platform such as
  37  * displaying progress of some task, appending user-specified menu to the application
  38  * icon context menu, etc.
  39  * 
  40  * @implNote Linux support is currently limited to Unity. However to make these 
  41  * features work on Unity, the app should be run from a .desktop file with 
  42  * specified {@code java.desktop.appName} system property set to this .desktop
  43  * file name:
  44  * {@code Exec=java -Djava.desktop.appName=MyApp.desktop -jar /path/to/myapp.jar}
  45  * see <a href="https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles">
  46  * https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles</a>
  47  */
  48 
  49 public class Taskbar {
  50     
  51     /**
  52      * List of provided features.
  53      */
  54     public static enum Feature {
  55 
  56         /**
  57          * @see #setIconBadge(java.lang.String) 
  58          */
  59         ICON_BADGE_TEXT,
  60 
  61         /**
  62          * @see #setIconBadge(java.lang.String) 
  63          */
  64         ICON_BADGE_NUMBER,
  65 
  66         /**
  67          * @see #setWindowIconBadge(java.awt.Window, java.awt.Image) 
  68          */
  69         ICON_BADGE_IMAGE_WINDOW,
  70 
  71         /**
  72          * @see #setIconImage(java.awt.Image) 
  73          */
  74         ICON_IMAGE,
  75 
  76         /**
  77          * @see #setMenu(java.awt.PopupMenu) 
  78          * @see #getMenu() 
  79          */
  80         MENU,
  81 
  82         /**
  83          * @see #setWindowProgressState(java.awt.Window, int) 
  84          */
  85         PROGRESS_STATE_WINDOW,
  86 
  87         /**
  88          * @see #setProgressValue(int) 
  89          */
  90         PROGRESS_VALUE,
  91 
  92         /**
  93          * @see #setWindowProgressValue(java.awt.Window, int)  
  94          */
  95         PROGRESS_VALUE_WINDOW,
  96 
  97         /**
  98          * @see #requestUserAttention(boolean, boolean) 
  99          */
 100         USER_ATTENTION,
 101 
 102         /**
 103          * @see #requestWindowUserAttention(java.awt.Window) 
 104          */
 105         USER_ATTENTION_WINDOW
 106     }
 107 
 108     /**
 109      * Stops displaying the progress.
 110      */
 111     public static final int STATE_OFF = 0x0;
 112 
 113     /**
 114      * The progress indicator displays with normal color and determinate mode.
 115      */
 116     public static final int STATE_NORMAL = 0x1;
 117 
 118     /**
 119      * Shows progress as paused, progress can be resumed by the user.
 120      * Switches to the determinate display.
 121      */
 122     public static final int STATE_PAUSED = 0x2;
 123 
 124     /**
 125      * The progress indicator displays activity without specifying what 
 126      * proportion of the progress is complete.
 127      */
 128     public static final int STATE_INDETERMINATE = 0x3;
 129 
 130     /**
 131      * Shows that an error has occurred.
 132      * Switches to the determinate display.
 133      */
 134     public static final int STATE_ERROR = 0x4;
 135             
 136     private TaskbarPeer peer;
 137     
 138     /**
 139      * Tests whether a {@code Feature} is supported on the current platform.
 140      * @param feature the specified {@link Feature}
 141      * @return true if the specified feature is supported on the current platform
 142      */
 143     public boolean isSupported(Feature feature) {
 144         return peer.isSupported(feature);
 145     }
 146 
 147     /**
 148      * Checks if the feature type is supported.
 149      *
 150      * @param featureType the action type in question
 151      * @throws UnsupportedOperationException if the specified action type is not
 152      *         supported on the current platform
 153      */
 154     private void checkFeatureSupport(Feature featureType){
 155         if (!isSupported(featureType)) {
 156             throw new UnsupportedOperationException("The " + featureType.name()
 157                     + " feature is not supported on the current platform!");
 158         }
 159     }
 160     
 161     /**
 162      *  Calls to the security manager's <code>checkPermission</code> method with
 163      *  an <code>AWTPermission("showWindowWithoutWarningBanner")</code>
 164      *  permission.
 165      */
 166     private void checkAWTPermission(){
 167         SecurityManager sm = System.getSecurityManager();
 168         if (sm != null) {
 169             sm.checkPermission(new AWTPermission(
 170                     "showWindowWithoutWarningBanner"));
 171         }
 172     }
 173 
 174     private Taskbar() {
 175         Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
 176         if (defaultToolkit instanceof SunToolkit) {
 177             peer = ((SunToolkit) defaultToolkit).createTaskbarPeer(this);
 178         }
 179     }
 180     
 181     /**
 182      * Returns the <code>Taskbar</code> instance of the current
 183      * taskbar context.  On some platforms the Taskbar API may not be
 184      * supported; use the {@link #isTaskbarSupported} method to
 185      * determine if the current taskbar is supported.
 186      * @return the Taskbar instance
 187      * @throws HeadlessException if {@link
 188      * GraphicsEnvironment#isHeadless()} returns {@code true}
 189      * @throws UnsupportedOperationException if this class is not
 190      * supported on the current platform
 191      * @see #isTaskbarSupported()
 192      * @see java.awt.GraphicsEnvironment#isHeadless
 193      */
 194     public static synchronized Taskbar getTaskbar(){
 195         if (GraphicsEnvironment.isHeadless()) throw new HeadlessException();
 196 
 197         if (!Taskbar.isTaskbarSupported()) {
 198             throw new UnsupportedOperationException("Taskbar API is not " +
 199                                                     "supported on the current platform");
 200         }
 201 
 202         sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
 203         Taskbar taskbar = (Taskbar)context.get(Taskbar.class);
 204 
 205         if (taskbar == null) {
 206             taskbar = new Taskbar();
 207             context.put(Taskbar.class, taskbar);
 208         }
 209 
 210         return taskbar;
 211     }
 212     
 213     /**
 214      * Tests whether this class is supported on the current platform.
 215      * If it's supported, use {@link #getTaskbar()} to retrieve an
 216      * instance.
 217      *
 218      * @return <code>true</code> if this class is supported on the
 219      *         current platform; <code>false</code> otherwise
 220      * @see #getTaskbar()
 221      */
 222     public static boolean isTaskbarSupported(){
 223         Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
 224         if (defaultToolkit instanceof SunToolkit) {
 225             return ((SunToolkit)defaultToolkit).isTaskbarSupported();
 226         }
 227         return false;
 228     }
 229 
 230     /**
 231      * Requests user attention to this application.
 232      * 
 233      * Depending on the platform, this may be visually indicated by a bouncing 
 234      * or flashing icon in the task area. It may have no effect on an already active
 235      * application.
 236      * 
 237      * On some platforms (e.g. Mac OS) this effect may disappear upon app activation
 238      * and cannot be dismissed by setting {@code enabled} to false.
 239      * Other platforms may require an additional call
 240      * {@link #requestUserAttention} to dismiss this request
 241      * with {@code enabled} parameter set to false.
 242      * 
 243      * @param enabled disables this request if false
 244      * @param critical if this is an important request
 245      */
 246     public void requestUserAttention(final boolean enabled, final boolean critical) {
 247         checkAWTPermission();
 248         checkFeatureSupport(Feature.USER_ATTENTION);
 249         peer.requestUserAttention(enabled, critical);
 250     }
 251 
 252     /**
 253      * Requests user attention to the specified window until it is activated.
 254      * 
 255      * On an already active window requesting attention does nothing.
 256      * 
 257      * @param w window
 258      */
 259     public void requestWindowUserAttention(Window w) {
 260         checkAWTPermission();
 261         checkFeatureSupport(Feature.USER_ATTENTION_WINDOW);
 262         peer.requestWindowUserAttention(w);
 263     }
 264 
 265     /**
 266      * Attaches the contents of the provided PopupMenu to the application icon
 267      * in the task area.
 268      *
 269      * @param menu the PopupMenu to attach to this application
 270      */
 271     public void setMenu(final PopupMenu menu) {
 272         checkAWTPermission();
 273         checkFeatureSupport(Feature.MENU);
 274         peer.setMenu(menu);
 275     }
 276 
 277     /**
 278      * Gets PopupMenu used to add items to this application's icon in system task area. 
 279      * 
 280      * @return the PopupMenu 
 281      */
 282     public PopupMenu getMenu() {
 283         checkAWTPermission();
 284         checkFeatureSupport(Feature.MENU);
 285         return peer.getMenu();
 286     }
 287 
 288     /**
 289      * Changes this application's icon to the provided image.
 290      *
 291      * @param image to change
 292      */
 293     public void setIconImage(final Image image) {
 294         checkAWTPermission();
 295         checkFeatureSupport(Feature.ICON_IMAGE);
 296         peer.setIconImage(image);
 297     }
 298 
 299     /**
 300      * Obtains an image of this application's icon.
 301      *
 302      * @return an image of this application's icon
 303      */
 304     public Image getIconImage() {
 305         checkAWTPermission();
 306         checkFeatureSupport(Feature.ICON_IMAGE);
 307         return peer.getIconImage();
 308     }
 309 
 310     /**
 311      * Affixes a small system-provided badge to this application's icon.
 312      * Usually a number. 
 313      * 
 314      * Some platforms do not support string values and accept only integer
 315      * values. In this case, pass an integer represented as a string as parameter. 
 316      * This can be tested by {@code Feature.ICON_BADGE_STRING} and 
 317      * {@code Feature.ICON_BADGE_NUMBER}.
 318      * 
 319      * Passing {@code null} as parameter hides the badge. 
 320      * @param badge label to affix to the icon
 321      */
 322     public void setIconBadge(final String badge) {
 323         checkAWTPermission();
 324         checkFeatureSupport(Feature.ICON_BADGE_NUMBER);
 325         peer.setIconBadge(badge);
 326     }
 327 
 328     /**
 329      * Affixes a small badge to this application's icon in the task area
 330      * for the specified window.
 331      * It may be disabled by system settings.
 332      *
 333      * @param w window to update 
 334      * @param badge image to affix to the icon
 335      */
 336     public void setWindowIconBadge(Window w, final Image badge) {
 337         checkAWTPermission();
 338         checkFeatureSupport(Feature.ICON_BADGE_IMAGE_WINDOW);
 339         if (w != null) {
 340             peer.setWindowIconBadge(w, badge);
 341         }
 342     }
 343 
 344 
 345     /**
 346      * Affixes a small system-provided progress bar to this application's icon.
 347      *
 348      * @param value from 0 to 100, other to disable progress indication
 349      */
 350     public void setProgressValue(int value) {
 351         checkAWTPermission();
 352         checkFeatureSupport(Feature.PROGRESS_VALUE);
 353         peer.setProgressValue(value);
 354     }
 355 
 356     /**
 357      * Displays progress for specified window.
 358      *
 359      * @param w window to update
 360      * @param value from 0 to 100, other to disable progress indication
 361      */
 362     public void setWindowProgressValue(Window w, int value) {
 363         checkAWTPermission();
 364         checkFeatureSupport(Feature.PROGRESS_VALUE_WINDOW);
 365         if (w != null) {
 366             peer.setWindowProgressValue(w, value);
 367         }
 368     }
 369 
 370     /**
 371      * Sets a progress state for a specified window.
 372      *
 373      * @param w window
 374      * @param state to change to
 375      * @see #STATE_OFF
 376      * @see #STATE_NORMAL
 377      * @see #STATE_PAUSED
 378      * @see #STATE_INDETERMINATE
 379      * @see #STATE_ERROR
 380      */
 381     public void setWindowProgressState(Window w, int state) {
 382         checkAWTPermission();
 383         checkFeatureSupport(Feature.PROGRESS_STATE_WINDOW);
 384         if (state < STATE_OFF && state > STATE_ERROR) {
 385             throw new IllegalArgumentException("Invalid state value");
 386         }
 387         if (w != null) {
 388             peer.setWindowProgressState(w, state);
 389         }
 390     }
 391     
 392     private native void initIDs();
 393 }