~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to form/src/org/netbeans/modules/form/editors/IconEditor.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
 
 
42
package org.netbeans.modules.form.editors;
 
43
 
 
44
import java.awt.Component;
 
45
import java.beans.*;
 
46
import java.util.*;
 
47
import java.io.File;
 
48
import java.io.IOException;
 
49
import java.net.MalformedURLException;
 
50
import java.net.URISyntaxException;
 
51
import java.net.URL;
 
52
import java.util.logging.Level;
 
53
import java.util.logging.Logger;
 
54
import javax.imageio.ImageIO;
 
55
import javax.swing.*;
 
56
 
 
57
import org.openide.explorer.propertysheet.editors.XMLPropertyEditor;
 
58
import org.openide.filesystems.FileObject;
 
59
import org.openide.ErrorManager;
 
60
import org.openide.filesystems.FileUtil;
 
61
import org.netbeans.api.java.classpath.ClassPath;
 
62
import org.netbeans.modules.form.FormModel;
 
63
import org.netbeans.modules.form.FormAwareEditor;
 
64
import org.netbeans.modules.form.FormDesignValue;
 
65
import org.netbeans.modules.form.FormDesignValueAdapter;
 
66
import org.netbeans.modules.form.FormEditor;
 
67
import org.netbeans.modules.form.FormProperty;
 
68
 
 
69
/**
 
70
 * PropertyEditor for Icons. Depends on existing DataObject for images.
 
71
 * Images must be represented by some DataObject which returns itself
 
72
 * as cookie, and has image file as a primary file. File extensions
 
73
 * for images is specified in isImage method.
 
74
 *
 
75
 * @author Jan Jancura, Jan Stola, Tomas Pavek
 
76
 */
 
77
public class IconEditor extends PropertyEditorSupport
 
78
                        implements XMLPropertyEditor, FormAwareEditor
 
79
{
 
80
    /** Type constant for icons from URL. */
 
81
    public static final int TYPE_URL = 1;
 
82
    /** Type constant for icons from file. */
 
83
    public static final int TYPE_FILE = 2;
 
84
    /** Type constant for icons from classpath. */
 
85
    public static final int TYPE_CLASSPATH = 3;
 
86
 
 
87
    /**
 
88
     * Names of subfolders (relative to the form file) where we try to look for
 
89
     * images by default. If none exists, the folder of the form file is used.
 
90
     */
 
91
    private static final String[] DEFAULT_DIRS = { "resources", "resource", "images" }; // NOI18N
 
92
 
 
93
    /**
 
94
     * Resource name of the current package (uses / as separator but does not
 
95
     * contain the initial /). Short file names are resolved against this
 
96
     * package, and also the package content is offered in getTags().
 
97
     */
 
98
    private String currentPackage;
 
99
    private String[] currentFiles;
 
100
 
 
101
    //private FormModel formModel;
 
102
    private FileObject sourceFile;
 
103
    private boolean externalIconsAllowed = true;
 
104
 
 
105
    public void setValue(Object value) {
 
106
        if (sameValue(value, getValue()))
 
107
            return;
 
108
 
 
109
        NbImageIcon nbIcon;
 
110
        if (value instanceof NbImageIcon) {
 
111
            nbIcon = (NbImageIcon) value;
 
112
            if (nbIcon.getType() == TYPE_CLASSPATH) {
 
113
                setCurrentPackage(getResourcePackage(nbIcon.getName()));
 
114
            }
 
115
        }
 
116
        else {
 
117
            nbIcon = null;
 
118
            if (value == null && currentPackage == null)
 
119
                setCurrentPackage(getDefaultResourcePackage());
 
120
        }
 
121
        super.setValue(value);
 
122
 
 
123
        currentFiles = null; // hack - reset sometimes to read new folder content
 
124
    }
 
125
 
 
126
    private static boolean sameValue(Object val1, Object val2) {
 
127
        if (val1 == null && val2 == null)
 
128
            return true;
 
129
        if (val1 instanceof NbImageIcon && val2 instanceof NbImageIcon)
 
130
            return sameIcon((NbImageIcon)val1, (NbImageIcon)val2);
 
131
        return false;
 
132
    }
 
133
 
 
134
    private static boolean sameIcon(NbImageIcon nbIcon1, NbImageIcon nbIcon2) {
 
135
        return nbIcon1.getType() == nbIcon2.getType()
 
136
               && nbIcon1.getName().equals(nbIcon2.getName());
 
137
    }
 
138
 
 
139
    public String getAsText() {
 
140
        Object val = getValue();
 
141
        if (val instanceof NbImageIcon) {
 
142
            NbImageIcon nbIcon = (NbImageIcon) val;
 
143
            if (nbIcon.getType() == TYPE_CLASSPATH) {
 
144
                String resName = nbIcon.getName();
 
145
                if (currentPackage != null && resName.startsWith(currentPackage))
 
146
                    return resName.substring(currentPackage.length() + 1);
 
147
                else
 
148
                    return resName;
 
149
            }
 
150
            else return nbIcon.getName();
 
151
        }
 
152
        return ""; // NOI18N
 
153
    }
 
154
 
 
155
    public void setAsText(String string) throws IllegalArgumentException {
 
156
        setValue(createIconFromText(string));
 
157
    }
 
158
 
 
159
    public String getJavaInitializationString() {
 
160
        if (getValue() instanceof NbImageIcon) {
 
161
            NbImageIcon ii = (NbImageIcon)getValue();
 
162
            switch (ii.type) {
 
163
                case TYPE_URL: return
 
164
                "new javax.swing.JLabel() {\n" + // NOI18N
 
165
                "  public javax.swing.Icon getIcon() {\n" + // NOI18N
 
166
                "    try {\n" + // NOI18N
 
167
                "      return new javax.swing.ImageIcon(\n" + // NOI18N
 
168
                "        new java.net.URL(\"" + convert(ii.name) + "\")\n" + // NOI18N
 
169
                "      );\n" + // NOI18N
 
170
                "    } catch (java.net.MalformedURLException e) {\n" + // NOI18N
 
171
                "    }\n" + // NOI18N
 
172
                "    return null;\n" + // NOI18N
 
173
                "  }\n" + // NOI18N
 
174
                "}.getIcon()"; // NOI18N
 
175
                case TYPE_FILE: return
 
176
                    "*/\n\\1NOI18N*/\n\\0" // NOI18N
 
177
                    + "new javax.swing.ImageIcon(\"" + convert(ii.name) + "\")"; // NOI18N
 
178
                case TYPE_CLASSPATH: return
 
179
                    "*/\n\\1NOI18N*/\n\\0" // NOI18N
 
180
                    + "new javax.swing.ImageIcon(getClass().getResource(\"/" + ii.name + "\"))"; // NOI18N
 
181
                // */\n\\1 is a special code mark for line comment
 
182
                // */\n\\0 is a special code mark to indicate that a real code follows
 
183
            }
 
184
        }
 
185
        return "null"; // NOI18N
 
186
    }
 
187
 
 
188
    /**
 
189
     * Duplicates backslashes in the input string.
 
190
     * @param s string to duplicate backslashes in
 
191
     * @return string with duplicated backslashes
 
192
     */
 
193
    private static String convert(String s) {
 
194
        StringTokenizer st = new StringTokenizer(s, "\\"); // NOI18N
 
195
        StringBuilder sb = new StringBuilder();
 
196
        if (st.hasMoreElements()) {
 
197
            sb.append(st.nextElement());
 
198
            while (st.hasMoreElements())
 
199
                sb.append("\\\\").append(st.nextElement()); // NOI18N
 
200
        }
 
201
        return sb.toString();
 
202
    }
 
203
 
 
204
    public String[] getTags() {
 
205
        if (currentFiles == null)
 
206
            currentFiles = getAvailableFileNames();
 
207
        return currentFiles;
 
208
    }
 
209
    
 
210
    public boolean supportsCustomEditor() {
 
211
        return true;
 
212
    }
 
213
    
 
214
    public Component getCustomEditor() {
 
215
        CustomIconEditor customEditor = new CustomIconEditor(this);
 
216
        customEditor.setValue((NbImageIcon)getValue());
 
217
        currentFiles = null; // hack - reset sometimes to read new folder content
 
218
        return customEditor;
 
219
    }
 
220
 
 
221
    // FormAwareEditor implementation
 
222
    public void setContext(FormModel model, FormProperty prop) {
 
223
        if (model != null) { // might be null when loaded as constraints property of JTabbedPane's tab
 
224
            this.sourceFile = FormEditor.getFormDataObject(model).getPrimaryFile();
 
225
        }
 
226
        if (prop != null)
 
227
            prop.setValue("canEditAsText", true); // NOI18N
 
228
        if (currentPackage == null && sourceFile != null) {
 
229
            setCurrentPackage(getDefaultResourcePackage());
 
230
        }
 
231
    }
 
232
 
 
233
    // FormAwareEditor implementation
 
234
    public void updateFormVersionLevel() {
 
235
    }
 
236
 
 
237
    // -----
 
238
 
 
239
    FileObject getSourceFile() {
 
240
        return sourceFile;
 
241
    }
 
242
    
 
243
    public void setSourceFile(FileObject sourceFile) {
 
244
        this.sourceFile = sourceFile;
 
245
    }
 
246
    
 
247
    public void setExternalIconsAllowed(boolean externalIconsAllowed) {
 
248
        this.externalIconsAllowed = externalIconsAllowed;
 
249
    }
 
250
    public boolean isExternalIconsAllowed() {
 
251
        return this.externalIconsAllowed;
 
252
    }
 
253
 
 
254
    /**
 
255
     * Returns the "current folder" which is used to resolve short file names,
 
256
     * its content is offered via getTags(), and it is also selected in the
 
257
     * custom editor.
 
258
     * @return the current folder used to pick image files from preferentially
 
259
     */
 
260
    public FileObject getCurrentFolder() {
 
261
        if (currentPackage != null) {
 
262
            FileObject sourceFile = getSourceFile();
 
263
            FileObject folder = ClassPath.getClassPath(sourceFile, ClassPath.SOURCE)
 
264
                    .findResource(currentPackage);
 
265
            if (folder == null)
 
266
                folder = ClassPath.getClassPath(sourceFile, ClassPath.EXECUTE)
 
267
                    .findResource(currentPackage);
 
268
            return folder;
 
269
        }
 
270
        return null;
 
271
    }
 
272
 
 
273
    /**
 
274
     * Sets the "current folder" which is used to resolve short file names, its
 
275
     * content is offered via getTags(), and it is also selected in the custom
 
276
     * editor. The folder is kept as a resource name of the corresponding
 
277
     * package. It should be on the classpath of the source file's project.
 
278
     * It also gets set when a classpath-based icon is set (if null, it is
 
279
     * initially set to getDefaultResourceFolder).
 
280
     * @param folder the current preferred folder for images
 
281
     */
 
282
    public void setCurrentFolder(FileObject folder) {
 
283
        if (folder != null) {
 
284
            FileObject sourceFile = getSourceFile();
 
285
            FileObject root = ClassPath.getClassPath(sourceFile, ClassPath.SOURCE)
 
286
                    .findOwnerRoot(folder);
 
287
            if (root == null)
 
288
                root = ClassPath.getClassPath(sourceFile, ClassPath.EXECUTE)
 
289
                    .findOwnerRoot(folder);
 
290
            if (root != null)
 
291
                setCurrentPackage(FileUtil.getRelativePath(root, folder));
 
292
        }
 
293
        else setCurrentPackage(null);
 
294
    }
 
295
 
 
296
    private void setCurrentPackage(String pkg) {
 
297
        currentPackage = pkg;
 
298
        currentFiles = null;
 
299
    }
 
300
 
 
301
    /**
 
302
     * @param resName resource name of an image file
 
303
     * @return String representing the package of the resource
 
304
     */
 
305
    private String getResourcePackage(String resName) {
 
306
        int i = resName.lastIndexOf('/');
 
307
        if (i < 0) // default package? don't even try
 
308
            return null;
 
309
 
 
310
        return resName.substring(0, i);
 
311
    }
 
312
 
 
313
    FileObject getDefaultResourceFolder() {
 
314
        FileObject sourceFile = getSourceFile();
 
315
        for (String dir : DEFAULT_DIRS) {
 
316
            FileObject folder = sourceFile.getParent().getFileObject(dir);
 
317
            if (folder != null)
 
318
                return folder;
 
319
        }
 
320
        return sourceFile.getParent();
 
321
    }
 
322
 
 
323
    private String getDefaultResourcePackage() {
 
324
        FileObject folder = getDefaultResourceFolder();
 
325
        ClassPath cp = ClassPath.getClassPath(folder, ClassPath.SOURCE);
 
326
        FileObject root = cp.findOwnerRoot(folder);
 
327
        return FileUtil.getRelativePath(root, folder);
 
328
    }
 
329
 
 
330
    /**
 
331
     * @return names of files (without path) available in current folder
 
332
     */
 
333
    private String[] getAvailableFileNames() {
 
334
        FileObject folder = getCurrentFolder();
 
335
        if (folder != null) {
 
336
            List<String> list = new LinkedList<String>();
 
337
            for (FileObject fo : folder.getChildren()) {
 
338
                if (isImageFile(fo))
 
339
                    list.add(fo.getNameExt());
 
340
            }
 
341
            String[] fileNames = new String[list.size()];
 
342
            list.toArray(fileNames);
 
343
            Arrays.sort(fileNames);
 
344
            return fileNames;
 
345
        }
 
346
        return null;
 
347
    }
 
348
 
 
349
    static boolean isImageFile(FileObject fo) {
 
350
        return fo.isFolder() ? false : isImageFileName(fo.getNameExt());
 
351
    }
 
352
 
 
353
    static boolean isImageFileName(String name) {
 
354
        name = name.toLowerCase();
 
355
        return name.endsWith(".gif") || name.endsWith(".jpg") || name.endsWith(".png") // NOI18N
 
356
               || name.endsWith(".jpeg") || name.endsWith(".jpe"); // NOI18N
 
357
    }
 
358
 
 
359
    private NbImageIcon createIconFromText(String txt) {
 
360
        if (txt == null || "".equals(txt.trim())) // NOI18N
 
361
            return null;
 
362
 
 
363
        if (!txt.contains("/") && !txt.contains("\\") && !txt.contains(":")) { // NOI18N
 
364
             // just a file name within current folder 
 
365
            String pkg = currentPackage != null ? currentPackage : getDefaultResourcePackage();
 
366
            txt = pkg + "/" + txt; // NOI18N
 
367
        }
 
368
 
 
369
        NbImageIcon nbIcon = iconFromResourceName(txt);
 
370
        if (nbIcon != null)
 
371
            return nbIcon;
 
372
 
 
373
        nbIcon = iconFromURL(txt, true);
 
374
        if (nbIcon != null)
 
375
            return nbIcon;
 
376
 
 
377
        return iconFromFileName(txt);
 
378
    }
 
379
 
 
380
    private NbImageIcon iconFromResourceName(String resName) {
 
381
        FileObject srcFile = getSourceFile();
 
382
        ClassPath cp = ClassPath.getClassPath(srcFile, ClassPath.SOURCE);
 
383
        FileObject fo = cp.findResource(resName);
 
384
        if (fo == null) {
 
385
            cp = ClassPath.getClassPath(srcFile, ClassPath.EXECUTE);
 
386
            fo = cp.findResource(resName);
 
387
        }
 
388
        if (fo != null) {
 
389
            try {
 
390
                Icon icon = new ImageIcon(ImageIO.read(fo.getURL()));
 
391
                return new NbImageIcon(TYPE_CLASSPATH, resName, icon);
 
392
            } catch (IOException ex) { // should not happen
 
393
                Logger.getLogger(IconEditor.class.getName()).log(Level.WARNING, null, ex);
 
394
            }
 
395
        }
 
396
        return null;
 
397
    }
 
398
 
 
399
    private NbImageIcon iconFromURL(String urlString, boolean forceURL) {
 
400
        try { // try as URL
 
401
            URL url = new URL(urlString);
 
402
            try { // is it a local file?
 
403
                File f = new File(url.toURI());
 
404
                if (f.exists() && !forceURL) { // prefer definition as file
 
405
                    String fileName = f.getAbsolutePath();
 
406
                    try {
 
407
                        Icon icon = new ImageIcon(ImageIO.read(new File(fileName)));
 
408
                        return new NbImageIcon(TYPE_FILE, fileName, icon);
 
409
                    } catch (IOException ex) { // should not happen
 
410
                        Logger.getLogger(IconEditor.class.getName()).log(Level.WARNING, null, ex);
 
411
                    }
 
412
                }
 
413
            }
 
414
            catch (URISyntaxException ex) {}
 
415
 
 
416
            if (url != null) { // treat as url
 
417
                try {
 
418
                    Icon icon = new ImageIcon(ImageIO.read(url));
 
419
                    return new NbImageIcon(TYPE_URL, urlString, icon);
 
420
                } catch (IOException ex) { // should not happen
 
421
                    Logger.getLogger(IconEditor.class.getName()).log(Level.WARNING, null, ex);
 
422
                }
 
423
            }
 
424
        }
 
425
        catch (MalformedURLException ex) {}
 
426
 
 
427
        return null;
 
428
    }
 
429
 
 
430
    private NbImageIcon iconFromFileName(String fileName) {
 
431
        File file = new File(fileName);
 
432
        if (file.exists()) {
 
433
            try {
 
434
                Icon icon = new ImageIcon(ImageIO.read(file));
 
435
                return new NbImageIcon(TYPE_FILE, fileName, icon);
 
436
            } catch (IOException ex) {
 
437
                Logger.getLogger(IconEditor.class.getName()).log(Level.INFO, null, ex);
 
438
            }
 
439
        }
 
440
        return null;
 
441
    }
 
442
 
 
443
    /**
 
444
     * A wrapper class for an icon value. It is public to be accessible by the
 
445
     * resource-aware IconEditor in editors2 package.
 
446
     */
 
447
    public static class NbImageIcon extends FormDesignValueAdapter {
 
448
        /** Source type of the icon (TYPE_CLASSPATH, TYPE_FILE, TYPE_URL). */
 
449
        private int type;
 
450
        /** Name of the icon (can be resource name, file name, url string - according to type). */
 
451
        private String name;
 
452
        /** The icon itself. */
 
453
        private Icon icon;
 
454
        
 
455
        public NbImageIcon(int type, String name, Icon icon) {
 
456
            this.type = type;
 
457
            if (name.startsWith("/")) // NOI18N
 
458
                name = name.substring(1);
 
459
            this.name = name;
 
460
            this.icon = icon;
 
461
        }
 
462
 
 
463
        public int getType() {
 
464
            return type;
 
465
        }
 
466
 
 
467
        public String getName() {
 
468
            return name;
 
469
        }
 
470
 
 
471
        public Icon getIcon() {
 
472
            return icon;
 
473
        }
 
474
 
 
475
        // FormDesignValue implementation
 
476
        public Object getDesignValue() {
 
477
            return icon;
 
478
        }
 
479
 
 
480
        // FormDesignValue implementation
 
481
        public String getDescription() {
 
482
            return name;
 
483
        }
 
484
        
 
485
        // FormDesignValue implementation
 
486
        public FormDesignValue copy(FormProperty formProperty) {
 
487
            return new IconEditor.NbImageIcon(type, name, icon);   
 
488
        }
 
489
    }
 
490
 
 
491
    // -----
 
492
    // XMLPropertyEditor
 
493
 
 
494
    /** Root of the XML representation of the icon. */
 
495
    public static final String XML_IMAGE = "Image"; // NOI18N
 
496
    /** Attribute holding icon type. */
 
497
    public static final String ATTR_TYPE = "iconType"; // NOI18N
 
498
    /** Attribute holding icon name. */
 
499
    public static final String ATTR_NAME = "name"; // NOI18N
 
500
 
 
501
    public void readFromXML(org.w3c.dom.Node element) throws java.io.IOException {
 
502
        if (!XML_IMAGE.equals(element.getNodeName())) {
 
503
            throw new java.io.IOException();
 
504
        }
 
505
        org.w3c.dom.NamedNodeMap attributes = element.getAttributes();
 
506
        try {
 
507
            int type = Integer.parseInt(attributes.getNamedItem(ATTR_TYPE).getNodeValue());
 
508
            String name = attributes.getNamedItem(ATTR_NAME).getNodeValue();
 
509
            switch (type) {
 
510
                case 0:
 
511
                    setValue(null);
 
512
                    break;
 
513
                case TYPE_URL:
 
514
                    setValue(iconFromURL(name, false));
 
515
                    break;
 
516
                case TYPE_FILE:
 
517
                    setValue(iconFromFileName(name));
 
518
                    break;
 
519
                case TYPE_CLASSPATH:
 
520
                    if (name.startsWith("/")) // NOI18N
 
521
                        name = name.substring(1);
 
522
                    setValue(iconFromResourceName(name));
 
523
                    break;
 
524
            }
 
525
        } catch (NullPointerException e) {
 
526
            java.io.IOException ioe = new java.io.IOException();
 
527
            ErrorManager.getDefault().annotate(ioe, e);
 
528
            throw ioe;
 
529
        }
 
530
    }
 
531
 
 
532
    public org.w3c.dom.Node storeToXML(org.w3c.dom.Document doc) {
 
533
        org.w3c.dom.Element el = doc.createElement(XML_IMAGE);
 
534
        Object value = getValue();
 
535
        if (value instanceof NbImageIcon) {
 
536
            NbImageIcon ii = (NbImageIcon) value;
 
537
            String name = ii.getName();
 
538
            if (ii.getType() == TYPE_CLASSPATH && !name.startsWith("/")) // NOI18N
 
539
                name = "/" + name; // NOI18N
 
540
            el.setAttribute(ATTR_TYPE, Integer.toString(ii.type));
 
541
            el.setAttribute(ATTR_NAME, name);
 
542
        } else {
 
543
            el.setAttribute(ATTR_TYPE, "0"); // NOI18N
 
544
            el.setAttribute(ATTR_NAME, "null"); // NOI18N
 
545
        }
 
546
        return el;
 
547
    }
 
548
    
 
549
}