001    /* steelme theme manager for Java
002     * Copyright (C) 2000 - 2004 T.J. Willis
003     * 
004     * This program is free software; you can redistribute it and/or
005     * modify it under the terms of the GNU General Public License
006     * as published by the Free Software Foundation; either version 2
007     * of the License, or (at your option) any later version.
008     *      
009     * This program is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012     * GNU General Public License for more details.
013     *          
014     * You should have received a copy of the GNU General Public License
015     * along with this program; if not, write to the Free Software
016     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
017     *  
018     * $Header: /cvsroot/steelme/steelme/src/net/sourceforge/steelme/SteelmeTheme.java,v 1.5 2004/05/15 16:11:47 tj_willis Exp $
019     */
020    package net.sourceforge.steelme;
021    
022    import java.awt.*;
023    import java.awt.event.*;
024    import javax.swing.*;
025    import javax.swing.plaf.*;
026    import javax.swing.plaf.metal.*;
027    import java.io.*;
028    import java.util.*;
029    import org.apache.log4j.*;
030    
031    /**
032     * A MetalTheme with persistence and public accessor methods.
033     *
034     * @author <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a>
035     * @version 1.0
036     */
037    public class SteelmeTheme
038                            extends DefaultMetalTheme
039                            //implements ActionListener
040    {
041            private ColorUIResource p1,p2,p3;
042            private ColorUIResource s1,s2,s3;
043            private ColorUIResource t1,t2;
044            private ColorUIResource w,b;
045            private FontUIResource font;
046            private Properties prop;
047            private String myName;
048            private Component cc;
049            private static Category cat
050            = Category.getInstance(SteelmeTheme.class.getName());
051    
052            /**
053             * Creates a new <code>SteelmeTheme</code> instance.
054             *
055             * @param ip1 a <code>Color</code> value
056             * @param ip2 a <code>Color</code> value
057             * @param ip3 a <code>Color</code> value
058             * @param is1 a <code>Color</code> value
059             * @param is2 a <code>Color</code> value
060             * @param is3 a <code>Color</code> value
061             * @param iif a <code>Font</code> value
062             * @param it1 a <code>Color</code> value
063             * @param it2 a <code>Color</code> value
064             * @param iw a <code>Color</code> value
065             * @param ib a <code>Color</code> value
066             */
067            public SteelmeTheme(Color ip1, Color ip2, Color ip3, Color is1, Color is2, Color is3, Font iif, Color it1, Color it2, Color iw, Color ib) {
068                    super();
069                    prop = new Properties();
070                    setPrimary1(ip1);
071                    setPrimary2(ip2);
072                    setPrimary3(ip3);
073                    setSecondary1(is1);
074                    setSecondary2(is2);
075                    setSecondary3(is3);
076                    setFont(iif);
077                    setText1(it1);
078                    setText2(it2);
079                    setWhite(iw);
080                    setBlack(ib);
081                    setName("Untitled");
082            }
083    
084            /**
085             * Creates a new <code>SteelmeTheme</code> instance which copies
086             * the attributes of DefaultMetalTheme.
087             *
088             */
089            public SteelmeTheme () {
090                    this(new DefaultMetalTheme());
091            }
092    
093            /**
094             * Creates a new <code>SteelmeTheme</code> instance which copies
095             * the attributes of the given MetalTheme.
096             *
097             * @param orig a <code>MetalTheme</code> value
098             */
099            public SteelmeTheme(MetalTheme orig)
100            {
101                    super();
102                    prop = new Properties();
103                    //DefaultMetalTheme mt = new DefaultMetalTheme();
104    
105                    this.setPrimary1(orig.getSeparatorForeground()); // NPE here
106                    this.setPrimary2(orig.getDesktopColor());
107                    this.setPrimary3(orig.getPrimaryControl());
108                    this.setSecondary1(orig.getControlDarkShadow());
109                    this.setSecondary2(orig.getControlShadow());
110                    this.setSecondary3(orig.getMenuBackground());
111                    this.setFont(orig.getUserTextFont());
112                    this.setText1( orig.getControlTextColor());
113                    this.setText2( orig.getControl());
114                    this.setWhite(orig.getControl());
115                    this.setBlack(orig.getControlTextColor());
116                    this.setName(orig.getName());
117            }
118    
119    
120            /**
121             * Creates a new <code>SteelmeTheme</code> instance with
122             * attributes loaded from the specified file.
123             *
124             * @param themeFile a <code>File</code> value
125             */
126            public SteelmeTheme(File themeFile) {
127                    prop = new Properties();
128                    //targets = new Vector();
129                    initColors();
130                    try {
131                            FileInputStream fis = new FileInputStream(themeFile);
132                            loadProperties(fis);
133                    } catch (Exception ex) {
134                            cat.warn("Exception loading properties file: "+themeFile,ex);
135                    }
136            }
137    
138    
139            /**
140             * Applies this theme to the Root component of the given component.
141             *
142             * @param who a <code>Component</code> value
143             */
144            public void applyTheme(Component who) {
145    
146                    Component rrt = javax.swing.SwingUtilities.getRoot(who);
147                    //cat.debug("root component (1): " + rrt);
148                    JRootPane rr2 = null;
149                    if( rrt instanceof JFrame){
150                            JFrame frm = (JFrame) rrt;
151                            rr2 = frm.getRootPane();
152                    } else if (rrt instanceof JComponent) {
153                            JComponent comp = (JComponent)rrt;
154                            rr2 = comp.getRootPane();
155                    } else {
156                            cat.warn("Setting theme on non-lightweight component: " + rrt);
157                            return;
158                    }
159                    try {
160                            MetalLookAndFeel.setCurrentTheme(this);
161                            UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
162                            SwingUtilities.updateComponentTreeUI(rr2);
163                    } catch (Exception e) {
164                            cat.warn("Error applying theme",e);
165                    }
166            }
167    
168    
169            /**
170             * Returns this theme's name.
171             *
172             * @return a <code>String</code> value
173             */
174            public String getName() {
175                    return myName;
176            }
177    
178            /**
179             * Sets this theme's name.
180             *
181             * @param n a <code>String</code> value
182             */
183            public void setName(String name) {
184                    //super.setName(n);
185                    myName = name;
186                    prop.put("name",name);
187    
188            }
189    
190    
191            /**
192             * Describe <code>getPrimary1</code> method here.
193             *
194             * @return a <code>ColorUIResource</code> value
195             */
196            protected  ColorUIResource getPrimary1() {
197                    return p1;
198            }
199    
200            /**
201             * Describe <code>getPrimary2</code> method here.
202             *
203             * @return a <code>ColorUIResource</code> value
204             */
205            protected  ColorUIResource getPrimary2() {
206                    return p2;
207            }
208            /**
209             * Describe <code>getPrimary3</code> method here.
210             *
211             * @return a <code>ColorUIResource</code> value
212             */
213            protected  ColorUIResource getPrimary3() {
214                    return p3;
215            }
216            /**
217             * Describe <code>getSecondary1</code> method here.
218             *
219             * @return a <code>ColorUIResource</code> value
220             */
221            protected  ColorUIResource getSecondary1() {
222                    return s1;
223            }
224            /**
225             * Describe <code>getSecondary2</code> method here.
226             *
227             * @return a <code>ColorUIResource</code> value
228             */
229            protected  ColorUIResource getSecondary2() {
230                    return s2;
231            }
232            /**
233             * Describe <code>getSecondary3</code> method here.
234             *
235             * @return a <code>ColorUIResource</code> value
236             */
237            protected  ColorUIResource getSecondary3() {
238                    return s3;
239            }
240    
241            /**
242             * Describe <code>getP1</code> method here.
243             *
244             * @return a <code>ColorUIResource</code> value
245             */
246            public  ColorUIResource getP1() {
247                    return p1;
248            }
249    
250            /**
251             * Describe <code>getP2</code> method here.
252             *
253             * @return a <code>ColorUIResource</code> value
254             */
255            public  ColorUIResource getP2() {
256                    return p2;
257            }
258            /**
259             * Describe <code>getP3</code> method here.
260             *
261             * @return a <code>ColorUIResource</code> value
262             */
263            public  ColorUIResource getP3() {
264                    return p3;
265            }
266            /**
267             * Describe <code>getS1</code> method here.
268             *
269             * @return a <code>ColorUIResource</code> value
270             */
271            public  ColorUIResource getS1() {
272                    return s1;
273            }
274            /**
275             * Describe <code>getS2</code> method here.
276             *
277             * @return a <code>ColorUIResource</code> value
278             */
279            public  ColorUIResource getS2() {
280                    return s2;
281            }
282            /**
283             * Describe <code>getS3</code> method here.
284             *
285             * @return a <code>ColorUIResource</code> value
286             */
287            public  ColorUIResource getS3() {
288                    return s3;
289            }
290            /**
291             * Describe <code>getT1</code> method here.
292             *
293             * @return a <code>ColorUIResource</code> value
294             */
295            public  ColorUIResource getT1() {
296                    return t1;
297            }
298            /**
299             * Describe <code>getT2</code> method here.
300             *
301             * @return a <code>ColorUIResource</code> value
302             */
303            public  ColorUIResource getT2() {
304                    return t2;
305            }
306            /**
307             * Describe <code>getW</code> method here.
308             *
309             * @return a <code>ColorUIResource</code> value
310             */
311            public  ColorUIResource getW() {
312                    return w;
313            }
314            /**
315             * Describe <code>getB</code> method here.
316             *
317             * @return a <code>ColorUIResource</code> value
318             */
319            public  ColorUIResource getB() {
320                    return b;
321            }
322    
323            /**
324             * Describe <code>getWhite</code> method here.
325             *
326             * @return a <code>ColorUIResource</code> value
327             */
328            protected  ColorUIResource getWhite() {
329                    return w;
330            }
331            /**
332             * Describe <code>getBlack</code> method here.
333             *
334             * @return a <code>ColorUIResource</code> value
335             */
336            protected  ColorUIResource getBlack() {
337                    return b;
338            }
339    
340            /**
341             * Describe <code>getControlTextFont</code> method here.
342             *
343             * @return a <code>FontUIResource</code> value
344             */
345            public FontUIResource getControlTextFont() {
346                    return font;
347            }
348    
349            /**
350             * Describe <code>getMenuTextFont</code> method here.
351             *
352             * @return a <code>FontUIResource</code> value
353             */
354            public FontUIResource getMenuTextFont() {
355                    return font;
356            }
357    
358            /**
359             * Describe <code>getSubTextFont</code> method here.
360             *
361             * @return a <code>FontUIResource</code> value
362             */
363            public FontUIResource getSubTextFont() {
364                    return font;
365            }
366            /**
367             * Describe <code>getSystemTextFont</code> method here.
368             *
369             * @return a <code>FontUIResource</code> value
370             */
371            public FontUIResource getSystemTextFont() {
372                    return font;
373            }
374            /**
375             * Describe <code>getUserTextFont</code> method here.
376             *
377             * @return a <code>FontUIResource</code> value
378             */
379            public FontUIResource getUserTextFont() {
380                    return font;
381            }
382            /**
383             * Describe <code>getWindowTitleFont</code> method here.
384             *
385             * @return a <code>FontUIResource</code> value
386             */
387            public FontUIResource getWindowTitleFont() {
388                    return font;
389            }
390    
391            /**
392             * Describe <code>getSystemTextColor</code> method here.
393             *
394             * @return a <code>ColorUIResource</code> value
395             */
396            public ColorUIResource getSystemTextColor() {
397                    return t1;
398            }
399            /**
400             * Describe <code>getUserTextColor</code> method here.
401             *
402             * @return a <code>ColorUIResource</code> value
403             */
404            public ColorUIResource getUserTextColor()   {
405                    return t1;
406            }
407            /**
408             * Describe <code>getControlTextColor</code> method here.
409             *
410             * @return a <code>ColorUIResource</code> value
411             */
412            public ColorUIResource getControlTextColor()  {
413                    return t1;
414            }
415    
416            /**
417             * Describe <code>getHighlightedTextColor</code> method here.
418             *
419             * @return a <code>ColorUIResource</code> value
420             */
421            public ColorUIResource getHighlightedTextColor()  {
422                    return t2;
423            }
424            /**
425             * Describe <code>getInactiveControlTextColor</code> method here.
426             *
427             * @return a <code>ColorUIResource</code> value
428             */
429            public ColorUIResource getInactiveControlTextColor()  {
430                    return t2;
431            }
432            /**
433             * Describe <code>getInactiveSystemTextColor</code> method here.
434             *
435             * @return a <code>ColorUIResource</code> value
436             */
437            public ColorUIResource getInactiveSystemTextColor()  {
438                    return t2;
439            }
440    
441            /**
442             * Describe <code>setPrimary1</code> method here.
443             *
444             * @param x a <code>Color</code> value
445             */
446            public void setPrimary1(Color x) {
447                    p1=new ColorUIResource(x);
448                    prop.put("primary1",getColorString(x));
449            }
450    
451            /**
452             * Describe <code>setPrimary2</code> method here.
453             *
454             * @param x a <code>Color</code> value
455             */
456            public void setPrimary2(Color x) {
457                    p2=new ColorUIResource(x);
458                    prop.put("primary2",getColorString(x));
459            }
460            /**
461             * Describe <code>setPrimary3</code> method here.
462             *
463             * @param x a <code>Color</code> value
464             */
465            public void setPrimary3(Color x) {
466                    p3=new ColorUIResource(x);
467                    prop.put("primary3",getColorString(x));
468            }
469            /**
470             * Describe <code>setSecondary1</code> method here.
471             *
472             * @param x a <code>Color</code> value
473             */
474            public void setSecondary1(Color x) {
475                    s1=new ColorUIResource(x);
476                    prop.put("secondary1",getColorString(x));
477            }
478            /**
479             * Describe <code>setSecondary2</code> method here.
480             *
481             * @param x a <code>Color</code> value
482             */
483            public void setSecondary2(Color x) {
484                    s2=new ColorUIResource(x);
485                    prop.put("secondary2",getColorString(x));
486            }
487            /**
488             * Describe <code>setSecondary3</code> method here.
489             *
490             * @param x a <code>Color</code> value
491             */
492            public void setSecondary3(Color x) {
493                    s3 =new ColorUIResource( x);
494                    prop.put("secondary3",getColorString(x));
495            }
496            /**
497             * Describe <code>setText1</code> method here.
498             *
499             * @param x a <code>Color</code> value
500             */
501            public void setText1(Color x) {
502                    t1=new ColorUIResource(x);
503                    prop.put("text1",getColorString(x));
504            }
505            /**
506             * Describe <code>setText2</code> method here.
507             *
508             * @param x a <code>Color</code> value
509             */
510            public void setText2(Color x) {
511                    t2=new ColorUIResource(x);
512                    prop.put("text2",getColorString(x));
513            }
514    
515            /**
516             * Describe <code>setWhite</code> method here.
517             *
518             * @param x a <code>Color</code> value
519             */
520            public void setWhite(final Color white) {
521                    w=new ColorUIResource(white);
522                    prop.put("white",getColorString(white));
523            }
524            /**
525             * Describe <code>setBlack</code> method here.
526             *
527             * @param x a <code>Color</code> value
528             */
529            public void setBlack(final Color black) {
530                    b=new ColorUIResource(black);
531                    prop.put("black",getColorString(black));
532            }
533    
534            /**
535             * Describe <code>setFont</code> method here.
536             *
537             * @param f a <code>Font</code> value
538             */
539            public void setFont( final Font fnt) {
540                    font = new FontUIResource(fnt);
541                    prop.put("fontName",fnt.getName());
542                    //FIXME: these Integer conversions aren't good
543                    prop.put("fontSize",new Integer(fnt.getSize()).toString());
544                    prop.put("fontStyle",new Integer(fnt.getStyle()).toString());
545            }
546    
547            /**
548             * pass an inputstream pointing to a properties file.
549             * Colors will be initialized to be the same as the DefaultMetalTheme,
550             * and then any colors provided in the properties file will override that.
551             * @param stream an <code>InputStream</code> value
552             */
553            public SteelmeTheme( InputStream stream ) {
554                    initColors();
555                    loadProperties(stream);
556            }
557    
558            /**
559              * Initialize all colors to be the same as the DefaultMetalTheme.
560              */
561            private void initColors() {
562                    p1 = super.getPrimary1();
563                    p2 = super.getPrimary2();
564                    p3 = super.getPrimary3();
565    
566                    s1 = super.getSecondary1();
567                    s2 = super.getSecondary2();
568                    s3 = super.getSecondary3();
569    
570                    t1 = super.getBlack();
571                    t2 = super.getWhite();
572    
573                    w = super.getWhite();
574                    b = super.getBlack();
575                    Font fnt= new Font("Default",Font.PLAIN,12);
576                    font = new FontUIResource(fnt);
577            }
578    
579    
580            /**
581             * Saves the file to resources/themes/ThemeName.theme.
582             *
583             */
584            public void save() {
585                    // FIXME: this should throw the IOException
586                    try {
587                            String tname = getName();
588                            File fil = new File("resources/themes/" + tname+".theme");
589                            FileOutputStream fos = new FileOutputStream(fil);
590                            prop.store(fos,"This is my title");
591                    } catch (Exception x) {
592                            cat.warn("Error saving theme: " + getName(),x);
593                    }
594            }
595    
596            /**
597             * Describe <code>save</code> method here.
598             *
599             * @param t an <code>OutputStream</code> value
600             */
601            public void save(OutputStream stream) {
602                    //FIXME: this should throw the IOException
603                    try {
604                            prop.store(stream,"This is my title");
605                    } catch (Exception ex) {
606                            cat.warn("Error saving theme: "+getName(),ex);
607                    }
608            }
609    
610            private ColorUIResource getColorUIResourceByName(String name)
611            {
612                    Object colorString = prop.get(name);
613                    ColorUIResource color = null;
614                    if (colorString != null) {
615                            color = parseColor(colorString.toString());
616                    }
617                    return color;
618            }
619            /**
620              * Load the theme name and colors from the properties file
621              * Items not defined in the properties file are ignored
622              */
623            private void loadProperties(InputStream stream) {
624                    // FIXME: this should throw the IOException
625                    try {
626                            prop.load(stream);
627                    } catch (IOException e) {
628                            cat.warn("Error loading theme",e);
629                    }
630    
631                    Object tempName = prop.get("name");
632                    if (tempName != null) {
633                            setName(tempName.toString());
634                    }
635    
636                    Object colorString = null;
637                    ColorUIResource co = getColorUIResourceByName("primary1");
638                    if(co!=null) setPrimary1(co);
639                    co = getColorUIResourceByName("primary2");
640                    if(co!=null) setPrimary2(co);
641                    co = getColorUIResourceByName("primary3");
642                    if(co!=null) setPrimary3(co);
643                    co = getColorUIResourceByName("secondary1");
644                    if(co!=null) setSecondary1(co);
645                    co = getColorUIResourceByName("secondary2");
646                    if(co!=null) setSecondary2(co);
647                    co = getColorUIResourceByName("secondary3");
648                    if(co!=null) setSecondary3(co);
649                    co = getColorUIResourceByName("text1");
650                    if(co!=null) setText1(co);
651                    co = getColorUIResourceByName("text2");
652                    if(co!=null) setText2(co);
653                    co = getColorUIResourceByName("white");
654                    if(co!=null) setWhite(co);
655                    co = getColorUIResourceByName("black");
656                    if(co!=null) setBlack(co);
657    
658    
659                    Font theFont = null;
660                    Object fff = prop.get("fontName");
661                    // setFontName(fff.toString());
662                    String fontName = null;
663                    if (fff!=null)
664                            fontName = fff.toString();
665                    if(fontName!=null) {
666                            int size = 12;
667                            int style = Font.PLAIN;
668    
669    
670                            Object sss = prop.get("fontSize");
671                            String fontSizeString = null;
672                            if(sss!=null){
673                                    fontSizeString = sss.toString();
674                            }
675    
676                            if (fontSizeString!=null) {
677                                    try {
678                                            size = Integer.parseInt(fontSizeString);
679                                    } catch (Exception x) {
680                                            cat.warn("Error parsing font size " + fontSizeString,x);
681                                    }
682                            }
683                            Object fSty = prop.get("fontStyle");
684                            String fontStyleString = null;
685                            if(fSty!=null){
686                                    fontStyleString = fSty.toString();
687                            }
688                            if (fontStyleString!=null) {
689                                    try {
690                                            style = Integer.parseInt(fontStyleString);
691                                    } catch (Exception x) {
692                                            cat.warn("Error parsing font style " + fontStyleString,x);
693                                    }
694                            }
695                            theFont = new Font(fontName,style,size);
696                            setFont(theFont);//new FontUIResource(theFont);
697                    } else {
698                            theFont = new Font("Default",Font.PLAIN,12);
699                            setFont(theFont);//font = new FontUIResource(theFont);
700                    }
701            }
702    
703            /**
704            * parse a comma delimited list of 3 strings into a ColorUIResource.
705            * For example, "255,0,0" would give a new ColorUIResource(255,0,0).
706            */
707            private static ColorUIResource parseColor(String string) {
708                    int red = 0;
709                    int green = 0;
710                    int blue = 0;
711                    try {
712                            StringTokenizer st = new StringTokenizer(string, ",");
713    
714                            red = Integer.parseInt(st.nextToken());
715                            green = Integer.parseInt(st.nextToken());
716                            blue = Integer.parseInt(st.nextToken());
717    
718                    } catch (Exception ex) {
719                            cat.warn("Couldn't parse color :" + string,ex);
720                    }
721    
722                    return new ColorUIResource(red, green, blue);
723            }
724    
725            /**
726            * Convert a color to 3 integers separated by commas with values from
727            * 0..255 which represent it's red, green, and blue components.
728            *
729            * @param c a <code>Color</code> value
730            * @return a <code>String</code> value
731                      */
732            protected static String getColorString(Color color) {
733                    if(color==null){
734                            return "0,0,0";
735                    }
736                    return color.getRed() + "," + color.getGreen() + "," + color.getBlue();
737            }
738    
739            /**
740             * Sets the component to update when the theme is applied.
741             *
742             * @param cx a <code>Component</code> value
743             */
744            public void setTarget(Component target) {
745                    cc = target;
746            }
747    }
748    
749